diff --git a/.github/workflows/test.yml-template b/.github/workflows/test.yml-template new file mode 100644 index 0000000000..8b5743ecb4 --- /dev/null +++ b/.github/workflows/test.yml-template @@ -0,0 +1,29 @@ +name: Test + +on: + pull_request: + branches: [ master ] + +jobs: + build: + + runs-on: ubuntu-latest + + strategy: + matrix: + node-version: [20.x] + + steps: + - uses: actions/checkout@v2 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node-version }} + - run: npm install + - run: npm test + - name: Upload HTML report(backstop data) + if: ${{ always() }} + uses: actions/upload-artifact@v2 + with: + name: report + path: backstop_data diff --git a/backstopConfig.js b/backstopConfig.js deleted file mode 100644 index 8f0fd08bd3..0000000000 --- a/backstopConfig.js +++ /dev/null @@ -1,78 +0,0 @@ -'use strict'; -// https://github.com/garris/BackstopJS#advanced-scenarios - -const backstop = require('@mate-academy/backstop-config'); -const { basicScenario } = backstop; - -const basic = { - ...basicScenario, - label: 'Elementary test', - referenceUrl: basicScenario.referenceUrl + '/catalog/', -}; - -const config = { - ...backstop, - fileNameTemplate: '{scenarioLabel}_{viewportLabel}', - onBeforeScript: 'puppet/onBefore.js', - onReadyScript: 'puppet/onReady.js', - viewports: [ - { - name: '1024px', - width: 1024, - height: 768, - }, - { - name: '1200px', - width: 1200, - height: 768, - }, - ], - scenarios: [ - { - ...basic, - label: 'Entire document', - selectors: ['document'], - }, - { - ...basic, - label: 'Header tag', - selectors: ['header'], - }, - { - ...basic, - label: 'Nav tag', - selectors: ['nav'], - }, - { - ...basic, - label: 'Link with data-qa_hover', - selectors: ['[data-qa="nav-hover"]'], - hoverSelector: '[data-qa="nav-hover"]', - postInteractionWait: 1000, - }, - { - ...basic, - label: 'Link with class_is-active', - selectors: ['a.is-active'], - }, - { - ...basic, - label: 'Main tag', - selectors: ['main'], - }, - { - ...basic, - label: 'Card with data-qa_card', - selectors: ['[data-qa="card"]'], - }, - // { - // ...basic, - // label: 'Card with data-qa_card-hover', - // selectors: ['[data-qa="card"]'], - // hoverSelector: '[data-qa="card-hover"]', - // postInteractionWait: 1000, - // }, - ], -}; - -module.exports = config; diff --git a/backstop_data/engine_scripts/cookies.json b/backstop_data/engine_scripts/cookies.json index b59400d7e6..4123a5d695 100644 --- a/backstop_data/engine_scripts/cookies.json +++ b/backstop_data/engine_scripts/cookies.json @@ -9,6 +9,6 @@ "httpOnly": false, "secure": false, "session": false, - "sameSite": "no_restriction" + "sameSite": "Lax" } ] diff --git a/backstop_data/engine_scripts/puppet/clickAndHoverHelper.js b/backstop_data/engine_scripts/puppet/clickAndHoverHelper.js index 6c1e1b8841..703d3b89b2 100644 --- a/backstop_data/engine_scripts/puppet/clickAndHoverHelper.js +++ b/backstop_data/engine_scripts/puppet/clickAndHoverHelper.js @@ -1,9 +1,9 @@ module.exports = async (page, scenario) => { - var hoverSelector = scenario.hoverSelectors || scenario.hoverSelector; - var clickSelector = scenario.clickSelectors || scenario.clickSelector; - var keyPressSelector = scenario.keyPressSelectors || scenario.keyPressSelector; - var scrollToSelector = scenario.scrollToSelector; - var postInteractionWait = scenario.postInteractionWait; // selector [str] | ms [int] + const hoverSelector = scenario.hoverSelectors || scenario.hoverSelector; + const clickSelector = scenario.clickSelectors || scenario.clickSelector; + const keyPressSelector = scenario.keyPressSelectors || scenario.keyPressSelector; + const scrollToSelector = scenario.scrollToSelector; + const postInteractionWait = scenario.postInteractionWait; // selector [str] | ms [int] if (keyPressSelector) { for (const keyPressSelectorItem of [].concat(keyPressSelector)) { @@ -27,7 +27,9 @@ module.exports = async (page, scenario) => { } if (postInteractionWait) { - await new Promise(resolve => setTimeout(resolve, postInteractionWait)); + await new Promise(resolve => { + setTimeout(resolve, postInteractionWait); + }); } if (scrollToSelector) { diff --git a/backstop_data/engine_scripts/puppet/loadCookies.js b/backstop_data/engine_scripts/puppet/loadCookies.js deleted file mode 100644 index db848a7cc7..0000000000 --- a/backstop_data/engine_scripts/puppet/loadCookies.js +++ /dev/null @@ -1,29 +0,0 @@ -var fs = require('fs'); - -module.exports = async (page, scenario) => { - var cookies = []; - var cookiePath = scenario.cookiePath; - - // READ COOKIES FROM FILE IF EXISTS - if (fs.existsSync(cookiePath)) { - cookies = JSON.parse(fs.readFileSync(cookiePath)); - } - - // MUNGE COOKIE DOMAIN - cookies = cookies.map(cookie => { - cookie.url = 'https://' + cookie.domain; - delete cookie.domain; - return cookie; - }); - - // SET COOKIES - const setCookies = async () => { - return Promise.all( - cookies.map(async (cookie) => { - await page.setCookie(cookie); - }) - ); - }; - await setCookies(); - console.log('Cookie state restored with:', JSON.stringify(cookies, null, 2)); -}; diff --git a/javascript.js b/javascript.js new file mode 100644 index 0000000000..ced058317f --- /dev/null +++ b/javascript.js @@ -0,0 +1,43 @@ +const products = [ + { + imgSrc: 'images/imac.jpeg', + altText: 'Apple iMac', + title: 'APPLE A1419 iMac 27" Retina 5K Monoblock (MNED2UA/A)', + code: '195434', + reviews: 5, + price: '$2,199', + }, + // Другие карточки +]; + +const productContainer = document.getElementById('product-container'); + +products.forEach((product) => { + const card = document.createElement('div'); + card.className = 'card'; + card.setAttribute('data-qa', 'card'); + + card.innerHTML = ` + ${product.altText} +

${product.title}

+

Product code: ${product.code}

+
+
+ + + + + +
+

Reviews: ${product.reviews}

+
+
+

Price:

+

${product.price}

+
+ Buy + `; + + productContainer.appendChild(card); +}); + diff --git a/layout_catalog b/layout_catalog new file mode 160000 index 0000000000..83bec888ef --- /dev/null +++ b/layout_catalog @@ -0,0 +1 @@ +Subproject commit 83bec888ef2fffa085187a9c48e5452e4326e7eb diff --git a/package-lock.json b/package-lock.json index 6dd164c6e0..5525878405 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,10 +14,11 @@ "@mate-academy/backstop-config": "latest", "@mate-academy/bemlint": "latest", "@mate-academy/linthtml-config": "latest", - "@mate-academy/scripts": "^1.8.6", + "@mate-academy/scripts": "^1.9.12", "@mate-academy/stylelint-config": "latest", "@parcel/transformer-sass": "^2.12.0", - "backstopjs": "6.3.23", + "backstopjs": "^6.3.25", + "http-server": "^14.1.1", "jest": "^29.7.0", "parcel": "^2.12.0", "prettier": "^3.3.2", @@ -1212,10 +1213,11 @@ "dev": true }, "node_modules/@mate-academy/scripts": { - "version": "1.8.6", - "resolved": "https://registry.npmjs.org/@mate-academy/scripts/-/scripts-1.8.6.tgz", - "integrity": "sha512-b4om/whj4G9emyi84ORE3FRZzCRwRIesr8tJHXa8EvJdOaAPDpzcJ8A0sFfMsWH9NUOVmOwkBtOXDu5eZZ00Ig==", + "version": "1.9.12", + "resolved": "https://registry.npmjs.org/@mate-academy/scripts/-/scripts-1.9.12.tgz", + "integrity": "sha512-/OcmxMa34lYLFlGx7Ig926W1U1qjrnXbjFJ2TzUcDaLmED+A5se652NcWwGOidXRuMAOYLPU2jNYBEkKyXrFJA==", "dev": true, + "license": "MIT", "dependencies": { "@octokit/rest": "^17.11.2", "@types/get-port": "^4.2.0", @@ -4694,10 +4696,11 @@ } }, "node_modules/backstopjs": { - "version": "6.3.23", - "resolved": "https://registry.npmjs.org/backstopjs/-/backstopjs-6.3.23.tgz", - "integrity": "sha512-GRSth3jEWI0goJ5ETA+D6QsK7ZZi0+qvZ0AAroGoLODCb20Y7NG3gc0Egnf4Qdrqg4D+seJHVjfZwqm3nuYqZg==", + "version": "6.3.25", + "resolved": "https://registry.npmjs.org/backstopjs/-/backstopjs-6.3.25.tgz", + "integrity": "sha512-jy0dxlk45tItXLcj9zjRTCyCa6D27M9OMK5kM8To0ELLclKhI/dWn/igUTMBBMJXe4Kql+CGyDRErMtTv2+40Q==", "dev": true, + "license": "MIT", "dependencies": { "@mirzazeyrek/node-resemble-js": "^1.2.1", "chalk": "^4.1.2", @@ -4722,7 +4725,7 @@ "backstop": "cli/index.js" }, "engines": { - "node": ">=16.0.0 <=20.x.x", + "node": ">=16.0.0", "npm": ">=8.0.0" } }, @@ -4837,6 +4840,26 @@ } ] }, + "node_modules/basic-auth": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", + "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "5.1.2" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/basic-auth/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT" + }, "node_modules/basic-ftp": { "version": "5.0.5", "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.5.tgz", @@ -5393,6 +5416,16 @@ "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", "dev": true }, + "node_modules/corser": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/corser/-/corser-2.0.1.tgz", + "integrity": "sha512-utCYNzRSQIZNPIcGZdQc92UVJYAhtGAteCFg0yRaFm8f0P+CPtyGyHXJcGXnffjCybUCEx3FQ2G7U3/o9eIkVQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, "node_modules/cosmiconfig": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.0.tgz", @@ -6147,6 +6180,13 @@ "node": ">= 0.6" } }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true, + "license": "MIT" + }, "node_modules/execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", @@ -6487,6 +6527,27 @@ "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", "dev": true }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -6838,6 +6899,16 @@ "node": ">= 0.4" } }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "license": "MIT", + "bin": { + "he": "bin/he" + } + }, "node_modules/hosted-git-info": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", @@ -6868,6 +6939,19 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, + "node_modules/html-encoding-sniffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", + "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-encoding": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", @@ -7012,6 +7096,21 @@ "node": ">= 0.8" } }, + "node_modules/http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/http-proxy-agent": { "version": "7.0.2", "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", @@ -7025,6 +7124,51 @@ "node": ">= 14" } }, + "node_modules/http-server": { + "version": "14.1.1", + "resolved": "https://registry.npmjs.org/http-server/-/http-server-14.1.1.tgz", + "integrity": "sha512-+cbxadF40UXd9T01zUHgA+rlo2Bg1Srer4+B4NwIHdaGxAGGv59nYRnGGDJ9LBk7alpS0US+J+bLLdQOOkJq4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "basic-auth": "^2.0.1", + "chalk": "^4.1.2", + "corser": "^2.0.1", + "he": "^1.2.0", + "html-encoding-sniffer": "^3.0.0", + "http-proxy": "^1.18.1", + "mime": "^1.6.0", + "minimist": "^1.2.6", + "opener": "^1.5.1", + "portfinder": "^1.0.28", + "secure-compare": "3.0.1", + "union": "~0.5.0", + "url-join": "^4.0.1" + }, + "bin": { + "http-server": "bin/http-server" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/http-server/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, "node_modules/https-proxy-agent": { "version": "7.0.5", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", @@ -9168,6 +9312,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/opener": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", + "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", + "dev": true, + "license": "(WTFPL OR MIT)", + "bin": { + "opener": "bin/opener-bin.js" + } + }, "node_modules/opn": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/opn/-/opn-6.0.0.tgz", @@ -10271,6 +10425,13 @@ "node": ">=0.10.0" } }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "dev": true, + "license": "MIT" + }, "node_modules/resolve": { "version": "1.22.8", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", @@ -10438,6 +10599,13 @@ "node": ">=14.0.0" } }, + "node_modules/secure-compare": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/secure-compare/-/secure-compare-3.0.1.tgz", + "integrity": "sha512-AckIIV90rPDcBcglUwXPF3kg0P0qmPsPXAj6BBEENQE1p5yA1xfmDJzfi1Tappj37Pv2mVbKpL3Z1T+Nn7k1Qw==", + "dev": true, + "license": "MIT" + }, "node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -11456,6 +11624,18 @@ "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", "dev": true }, + "node_modules/union": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/union/-/union-0.5.0.tgz", + "integrity": "sha512-N6uOhuW6zO95P3Mel2I2zMsbsanvvtgn6jVqJv4vbVcz/JN0OkL9suomjQGmWtxJQXOCqUJvquc1sMeNz/IwlA==", + "dev": true, + "dependencies": { + "qs": "^6.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/universal-user-agent": { "version": "7.0.2", "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.2.tgz", @@ -11511,6 +11691,13 @@ "browserslist": ">= 4.21.0" } }, + "node_modules/url-join": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", + "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", + "dev": true, + "license": "MIT" + }, "node_modules/urlpattern-polyfill": { "version": "10.0.0", "resolved": "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-10.0.0.tgz", @@ -11619,6 +11806,32 @@ "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", "dev": true }, + "node_modules/whatwg-encoding": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", + "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-encoding/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", diff --git a/package.json b/package.json index 34398805fa..0c8f327f2a 100644 --- a/package.json +++ b/package.json @@ -22,10 +22,11 @@ "@mate-academy/backstop-config": "latest", "@mate-academy/bemlint": "latest", "@mate-academy/linthtml-config": "latest", - "@mate-academy/scripts": "^1.8.6", + "@mate-academy/scripts": "^1.9.12", "@mate-academy/stylelint-config": "latest", "@parcel/transformer-sass": "^2.12.0", - "backstopjs": "6.3.23", + "backstopjs": "^6.3.25", + "http-server": "^14.1.1", "jest": "^29.7.0", "parcel": "^2.12.0", "prettier": "^3.3.2", diff --git a/readme.md b/readme.md index 874ca70d97..42d0c455bb 100644 --- a/readme.md +++ b/readme.md @@ -9,7 +9,7 @@ Create an HTML page with a catalog. Develop semantic page structure as shown on - add `data-qa="card-hover"` (not just `hover`) to the link `Buy` inside the first card - nav links color is not `black` anymore (nav links should have `#060b35` color) - add the class `is-active` to the first link (`Apple`) in the navigation -- use `
` tag for cards container +- use `
` tag for cards container - use the grid for cards with different numbers of columns: - 1 for the smaller screens - 2 starting at `488px` @@ -33,8 +33,8 @@ This is possible because [we use the Parcel library](https://en.parceljs.org/scs ## Checklist ❗️ Replace `` with your GitHub username and copy the links to the `Pull Request` description: -- [DEMO LINK](https://.github.io/layout_catalog/) -- [TEST REPORT LINK](https://.github.io/layout_catalog/report/html_report/) +- [DEMO LINK](https://.github.io/layout_catalog/) +- [TEST REPORT LINK](https://.github.io/layout_catalog/report/html_report/) ❗️ Copy this `Checklist` to the `Pull Request` description after links, and put `- [x]` before each point after you checked it. diff --git a/src/index.html b/src/index.html index 9cff78eeb7..cefc1a4553 100644 --- a/src/index.html +++ b/src/index.html @@ -17,11 +17,125 @@ /> - -

Catalog

+
+ + +
+ +
+
+ Product Image +

Product Name

+

Product code: 195434

+
+
+

Reviews: 5

+
+
+

Price:

+

$2,199

+
+ + Buy + +
+
+ + diff --git a/src/scripts/main.js b/src/scripts/main.js index ad9a93a7c1..be8ed6faf7 100644 --- a/src/scripts/main.js +++ b/src/scripts/main.js @@ -1 +1,18 @@ -'use strict'; +// Динамическое добавление звезд на основе количества отзывов +document.querySelectorAll('.stars').forEach(starsContainer => { + const reviewCount = parseInt(starsContainer.dataset.reviews, 10) || 0; + for (let i = 0; i < 5; i++) { + const star = document.createElement('span'); + star.className = i < reviewCount ? 'stars__star stars__star--filled' : 'stars__star'; + starsContainer.appendChild(star); + } +}); + +// Обработчик событий для кнопки "Buy" +document.querySelectorAll('.card__button').forEach(button => { + button.addEventListener('click', function(event) { + event.preventDefault(); + // Логика обработки кнопки "Buy", например, открытие модального окна или переход на страницу покупки + window.location.href = '/buy/product-id'; // или другой путь к странице покупки + }); +}); diff --git a/src/styles/blocks/body.scss b/src/styles/blocks/body.scss new file mode 100644 index 0000000000..7719bc5bfa --- /dev/null +++ b/src/styles/blocks/body.scss @@ -0,0 +1,8 @@ +* { + box-sizing: border-box; +} + +body { + margin: 0; + padding: 0; +} diff --git a/src/styles/blocks/cards.scss b/src/styles/blocks/cards.scss new file mode 100644 index 0000000000..63a71c50d8 --- /dev/null +++ b/src/styles/blocks/cards.scss @@ -0,0 +1,45 @@ +@import '../utils/variables'; + +.card { + width: 200px; + height: 408px; + padding: 32px 16px 16px; + display: flex; + flex-direction: column; + background-color: #fff; + border: 1px solid #f3f3f3; + border-radius: 5px; + font-family: Roboto, sans-serif; + transition: transform 300ms; + + &:hover { + transform: scale(1.05); + } + + &__img { + width: 160px; + height: 134px; + display: block; + margin: 0 auto; + } + + &__title { + font-size: 12px; + font-weight: 500; + color: $normal-color; + } + + &__button { + background-color: $active-color; + color: #fff; + text-transform: uppercase; + text-align: center; + padding: 10px; + border-radius: 5px; + transition: opacity 0.3s; + + &:hover { + opacity: 0.8; + } + } +} diff --git a/src/styles/blocks/header.scss b/src/styles/blocks/header.scss new file mode 100644 index 0000000000..570a2e9e55 --- /dev/null +++ b/src/styles/blocks/header.scss @@ -0,0 +1,7 @@ +.header { + width: 100%; + padding: 0 50px; + display: flex; + align-items: center; + justify-content: space-between; +} diff --git a/src/styles/blocks/logo.scss b/src/styles/blocks/logo.scss new file mode 100644 index 0000000000..b4e0fc49b7 --- /dev/null +++ b/src/styles/blocks/logo.scss @@ -0,0 +1,9 @@ +.logo { + width: 40px; + height: 40px; + + &__img { + width: 100%; + height: 100%; + } +} diff --git a/src/styles/blocks/main.scss b/src/styles/blocks/main.scss new file mode 100644 index 0000000000..a16ba17910 --- /dev/null +++ b/src/styles/blocks/main.scss @@ -0,0 +1,25 @@ +.main { + display: grid; + grid-template-columns: 200px; + gap: 46px 48px; + padding: 50px 40px; + justify-content: center; + + @media (min-width: 488px) { + & { + grid-template-columns: repeat(2, 200px); + } + } + + @media (min-width: 768px) { + & { + grid-template-columns: repeat(3, 200px); + } + } + + @media (min-width: 1024px) { + & { + grid-template-columns: repeat(4, 200px); + } + } +} diff --git a/src/styles/blocks/nav.scss b/src/styles/blocks/nav.scss new file mode 100644 index 0000000000..af38ce0aed --- /dev/null +++ b/src/styles/blocks/nav.scss @@ -0,0 +1,25 @@ +@import '../utils/variables'; + +.nav { + &__list { + display: flex; + margin: 0; + padding: 0; + list-style: none; + } + + &__item { + margin: 0 10px; + } + + &__link { + color: $normal-color; + text-decoration: none; + font-size: 14px; + text-transform: uppercase; + + &:hover { + color: $active-color; + } + } +} diff --git a/src/styles/blocks/stars.scss b/src/styles/blocks/stars.scss new file mode 100644 index 0000000000..ad1d7430e7 --- /dev/null +++ b/src/styles/blocks/stars.scss @@ -0,0 +1,15 @@ +.stars { + display: flex; + + &__star { + width: 16px; + height: 16px; + background-image: url('../images/star.svg'); + background-size: contain; + margin-right: 4px; + + &--filled { + background-image: url('../images/star-filled.svg'); + } + } +} diff --git a/src/styles/blocks/variables.scss b/src/styles/blocks/variables.scss new file mode 100644 index 0000000000..1d335066b4 --- /dev/null +++ b/src/styles/blocks/variables.scss @@ -0,0 +1,2 @@ +$active-color: #ff6b6b; +$normal-color: #333; diff --git a/src/styles/index.scss b/src/styles/index.scss index 293d3b1f13..e5f5a389b3 100644 --- a/src/styles/index.scss +++ b/src/styles/index.scss @@ -1,3 +1,8 @@ +@import 'utils/variables'; +@import 'blocks/nav'; +@import 'blocks/cards'; +@import 'blocks/stars'; + body { margin: 0; } diff --git a/src/styles/utils/variables.scss b/src/styles/utils/variables.scss new file mode 100644 index 0000000000..1d335066b4 --- /dev/null +++ b/src/styles/utils/variables.scss @@ -0,0 +1,2 @@ +$active-color: #ff6b6b; +$normal-color: #333;