From 44ee6f1511e5634d08948abf4ec14f0e177a1674 Mon Sep 17 00:00:00 2001 From: John Pratt Date: Thu, 5 Dec 2024 10:23:06 -0700 Subject: [PATCH 01/11] MWPW-162847 [MEP][nala] add ul-li selector test (#3238) * add ul-li selector test * add test id --------- Co-authored-by: John Pratt --- .../features/personalization/ul-ol-li.spec.js | 21 +++++++ .../features/personalization/ul-ol-li.test.js | 57 +++++++++++++++++++ 2 files changed, 78 insertions(+) create mode 100644 nala/features/personalization/ul-ol-li.spec.js create mode 100644 nala/features/personalization/ul-ol-li.test.js diff --git a/nala/features/personalization/ul-ol-li.spec.js b/nala/features/personalization/ul-ol-li.spec.js new file mode 100644 index 0000000000..865a189a89 --- /dev/null +++ b/nala/features/personalization/ul-ol-li.spec.js @@ -0,0 +1,21 @@ +module.exports = { + name: 'Personalization Feature', + features: [ + { + tcid: '0', + name: '@check ul selector', + desc: 'Verify that ul selectors work (skipping ol selectors)', + path: '/drafts/nala/features/personalization/ul-ol-li/ul-selector', + data: { defaultURL: '/drafts/nala/features/personalization/ul-ol-li/ul-selector?mep=%2Fdrafts%2Fnala%2Ffeatures%2Fpersonalization%2Ful-ol-li%2Ful-selector.json--default' }, + tags: '@ul0 @smoke @regression @milo ', + }, + { + tcid: '1', + name: '@check li selectors', + desc: 'Verify that li selectors work', + path: '/drafts/nala/features/personalization/ul-ol-li/li-selectors', + data: { defaultURL: '/drafts/nala/features/personalization/ul-ol-li/li-selectors?mep=%2Fdrafts%2Fnala%2Ffeatures%2Fpersonalization%2Ful-ol-li%2Fli-selectors.json--default' }, + tags: '@ul1 @smoke @regression @milo ', + }, + ], +}; diff --git a/nala/features/personalization/ul-ol-li.test.js b/nala/features/personalization/ul-ol-li.test.js new file mode 100644 index 0000000000..a59cfb59cf --- /dev/null +++ b/nala/features/personalization/ul-ol-li.test.js @@ -0,0 +1,57 @@ +import { expect, test } from '@playwright/test'; +import { features } from './ul-ol-li.spec.js'; + +const miloLibs = process.env.MILO_LIBS || ''; + +// Test 0 : check ul selectors (skipping ol selectors) +test(`[Test Id - ${features[0].tcid}] ${features[0].name},${features[0].tags}`, async ({ page, baseURL }) => { + const pznUrl = `${baseURL}${features[0].path}${miloLibs}`; + const defaultUrl = `${baseURL}${features[0].data.defaultURL}${miloLibs}`; + const pznUpdateLocator = '[data-manifest-id="ul-selector.json"]'; + const defaultUlLocator = 'ul.icon-list'; + + await test.step('step-1: verify the default test page has a regular ul list with 3 bullets', async () => { + console.info(`[Test Page]: ${defaultUrl}`); + await page.goto(defaultUrl); + await expect(page.locator(pznUpdateLocator)).toHaveCount(0); + await expect(page.locator(defaultUlLocator)).toHaveCount(1); + const element = page.locator(defaultUlLocator); + await expect(element.locator('li')).toHaveCount(3); + }); + + await test.step('step-2: Verify personalized page has a text replacement for the ul list', async () => { + console.info(`[Test Page]: ${pznUrl}`); + await page.goto(pznUrl); + const element = page.locator(pznUpdateLocator); + const innerHtml = await element.innerHTML(); + await expect(page.locator(pznUpdateLocator)).toHaveCount(1); + await expect(page.locator(defaultUlLocator)).toHaveCount(0); + await expect(innerHtml).toEqual('replacement for the ul'); + }); +}); + +// Test 1 : check li selectors +test(`[Test Id - ${features[1].tcid}] ${features[1].name},${features[1].tags}`, async ({ page, baseURL }) => { + const pznUrl = `${baseURL}${features[1].path}${miloLibs}`; + const defaultUrl = `${baseURL}${features[1].data.defaultURL}${miloLibs}`; + const pznUpdateLocator = 'li[data-manifest-id="li-selectors.json"]'; + const defaultUlLocator = 'ul.icon-list'; + + await test.step('step-1: verify the default test page has a regular ul list with 3 bullets', async () => { + console.info(`[Test Page]: ${defaultUrl}`); + await page.goto(defaultUrl); + await expect(page.locator(pznUpdateLocator)).toHaveCount(0); + await expect(page.locator(defaultUlLocator)).toHaveCount(1); + const element = page.locator(defaultUlLocator); + await expect(element.locator('li')).toHaveCount(3); + }); + + await test.step('step-2: Verify that the personalized page has 3 replacements for the line items', async () => { + console.info(`[Test Page]: ${pznUrl}`); + await page.goto(pznUrl); + const element = page.locator(pznUpdateLocator).first(); + const innerHtml = await element.innerHTML(); + await expect(page.locator(pznUpdateLocator)).toHaveCount(3); + await expect(innerHtml).toEqual('replace all line items'); + }); +}); From 11b61cccf2aaa2e1c84ff45d03ef116ff9313f4b Mon Sep 17 00:00:00 2001 From: Santoshkumar Nateekar Date: Thu, 5 Dec 2024 09:23:13 -0800 Subject: [PATCH 02/11] [MWPW-162711][NALA] Update Table block tests to validate tooltip information (#3267) update table tests with tooltip Co-authored-by: Santoshkumar Sharanappa Nateekar --- nala/blocks/table/table.page.js | 117 +++++--- nala/blocks/table/table.spec.js | 515 ++++++++++++++++++++++++++++---- nala/blocks/table/table.test.js | 196 ++++++------ 3 files changed, 628 insertions(+), 200 deletions(-) diff --git a/nala/blocks/table/table.page.js b/nala/blocks/table/table.page.js index 03c25a2467..eaa03d9f28 100644 --- a/nala/blocks/table/table.page.js +++ b/nala/blocks/table/table.page.js @@ -1,4 +1,7 @@ /* eslint-disable no-return-await */ + +import { expect } from '@playwright/test'; + export default class Table { constructor(page, nth = 0) { this.page = page; @@ -9,67 +12,93 @@ export default class Table { this.collapseStickyTable = this.page.locator('.table.highlight.collapse.sticky').nth(nth); this.merchTable = this.page.locator('.table.merch').nth(nth); this.merchHighlightStickyTable = this.page.locator('.table.merch.highlight.sticky').nth(nth); + this.merchPricingBottom = this.page.locator('.table.merch.pricing-bottom').nth(nth); + this.merchButtonRight = this.page.locator('.table.merch.button-right').nth(nth); this.highlightRow = this.table.locator('.row-highlight'); + this.highlightRowColumns = this.highlightRow.locator('.col'); + this.headingRow = this.table.locator('.row-heading'); + this.headingRowColumns = this.headingRow.locator('.col'); + this.stickyRow = this.table.locator('.row-heading'); - this.headingRowColumns = this.headingRow.locator('.col'); this.rows = this.table.locator('.row'); this.sectionRows = this.table.locator('.section-row'); } - async getHighlightRowColumnTitle(colIndex) { - return await this.highlightRow.locator('.col-highlight').nth(colIndex); + // Verify Table Highlight Row + async verifyHighlightRow(data) { + for (const { colIndex, visibility, style, text } of data.highlightRow) { + const highlightRowColumn = await this.highlightRow.locator('.col-highlight').nth(colIndex); + if (visibility) { + await expect(await highlightRowColumn).toContainText(text); + if (style) { + await expect(await highlightRowColumn).toHaveAttribute('style', style); + } + } + } } - async getHeaderColumnTitle(colIndex) { - const headerColumn = await this.headingRow.locator(`.col-${colIndex}`); - return headerColumn.locator('.tracking-header'); - } + // Verify Table Header Row + async verifyHeaderRow(data, tableType = '') { + for (const headerCell of data.headerRow) { + const { + colIndex, heading, pricingText, additionalText, tooltip, buttons, + } = headerCell; - async getHeaderColumnPricing(colIndex) { - const headerColumn = await this.headingRow.locator(`.col-${colIndex}`); - return headerColumn.locator('.pricing'); - } + const headerColumn = await this.headingRow.locator(`.col-${colIndex}`); - async getHeaderColumnImg(colIndex) { - const headerColumn = await this.headingRow.locator(`.col-${colIndex}`); - return headerColumn.locator('img'); - } + await expect(await headerColumn.locator('.tracking-header')).toContainText(heading); + await expect(await headerColumn.locator('.pricing')).toContainText(pricingText); - async getHeaderColumnAdditionalText(colIndex) { - const headerColumn = await this.headingRow.locator(`.col-${colIndex}`); - return headerColumn.locator('p').nth(3); - } - - async getHeaderColumnOutlineButton(colIndex) { - const headerColumn = await this.headingRow.locator(`.col-${colIndex}`); - return headerColumn.locator('.con-button.outline'); - } - - async getHeaderColumnBlueButton(colIndex) { - const headerColumn = await this.headingRow.locator(`.col-${colIndex}`); - return headerColumn.locator('.con-button.blue'); - } - - async getSectionRowTitle(index) { - const sectionRow = await this.table.locator('.section-row').nth(index); - return sectionRow.locator('.section-row-title'); - } + // verify the additional text based on table type + if (additionalText) { + if (tableType === 'bottom-pricing') { + await expect(await headerColumn.locator('p').nth(2)).toContainText(additionalText); + } else { + await expect(await headerColumn.locator('p').nth(3)).toContainText(additionalText); + } + } + // verify tooltip information + if (tooltip) { + const headerColumnTooltip = await headerColumn.locator('.milo-tooltip'); + await expect(await headerColumnTooltip.locator('.icon-milo-info')).toBeVisible(); + await expect(await headerColumnTooltip).toHaveAttribute('data-tooltip', tooltip.tooltipText); + await headerColumnTooltip.hover(); + } - async getSectionRowMerchContent(index) { - const sectionRow = await this.table.locator('.section-row').nth(index); - return sectionRow.locator('.col-merch-content').nth(0); + // verify buttons + if (buttons && buttons.length > 0) { + for (const button of buttons) { + await expect(await headerColumn.locator(`.con-button.${button.type}`).nth(0)).toContainText(button.text); + if (button.justifycontent) { + await expect(await headerColumn.locator('.buttons-wrapper')).toHaveCSS('justify-content', button.justifycontent); + } + } + } + } } - async getSectionRowMerchContentImg(index) { - const sectionRow = await this.table.locator('.section-row').nth(index); - return sectionRow.locator('.col-merch-content img'); - } + // Verify Table Section Rows + async verifySectionRow(data) { + for (const sectionRow of data.sectionRows) { + const { rowIndex, columns } = sectionRow; + const secRow = await this.table.locator('.section-row').nth(rowIndex); + for (const column of columns) { + const sectionRowColumn = await secRow.locator(`.col-${column.colIndex}`); + await expect(await sectionRowColumn).toContainText(column.text); - async getSectionRowCell(rowIndex, colIndex) { - const sectionRow = await this.table.locator('.section-row').nth(rowIndex); - return sectionRow.locator(`.col-${colIndex}`); + if (column.imageVisible) { + await expect(await sectionRowColumn.locator('.col-merch-content img')).toBeVisible(); + } + if (column.tooltip) { + const tooltip = await sectionRowColumn.locator('.milo-tooltip'); + await expect(await tooltip.locator('.icon-milo-info')).toBeVisible(); + await expect(await tooltip).toHaveAttribute('data-tooltip', column.tooltipText); + await tooltip.hover(); + } + } + } } } diff --git a/nala/blocks/table/table.spec.js b/nala/blocks/table/table.spec.js index 8ac863fbcf..cbb56a96a9 100644 --- a/nala/blocks/table/table.spec.js +++ b/nala/blocks/table/table.spec.js @@ -1,3 +1,4 @@ +/* eslint-disable object-curly-newline */ module.exports = { FeatureName: 'Table Block', features: [ @@ -9,18 +10,49 @@ module.exports = { rowsCount: 9, headerRowColCount: 5, sectionRowCount: 8, - headerCell2: { - heading: 'Heading Title-2', - pricingText: 'Pricing-2', - outlineButtonText: 'Free trial', - blueButtonText: 'Buy now', - }, - sectionRow2: { - sectionRowTitle: 'Row-1.1, Title', - cell22: 'Content', - }, + headerRow: [ + { + colIndex: 2, + heading: 'Heading Title-2', + pricingText: 'Pricing-2', + buttons: [ + { text: 'Free trial', type: 'outline', justifycontent: 'center' }, + { text: 'Buy now', type: 'blue', justifycontent: 'center' }, + ], + }, + ], + sectionRows: [ + { + rowIndex: 2, + columns: [ + { + colIndex: 1, + text: 'Row-1.1, Title', + imageVisible: false, + tooltip: true, + tooltipPosition: 'none', + tooltipText: 'Tooltip position set to none', + }, + { colIndex: 2, text: 'Content', imageVisible: false }, + ], + }, + { + rowIndex: 3, + columns: [ + { + colIndex: 1, + text: 'Row-1.2, Title', + imageVisible: false, + tooltip: true, + tooltipPosition: 'top', + tooltipText: 'Tooltip position set to top', + }, + { colIndex: 2, text: '', imageVisible: false }, + ], + }, + ], }, - tags: '@table @smoke @regression @milo', + tags: '@table-1 @smoke @regression @milo', }, { tcid: '1', @@ -30,21 +62,45 @@ module.exports = { rowsCount: 10, headerRowColCount: 5, sectionRowCount: 8, - hightlightRow: { - cell12: 'Highlight-2', - cell13: 'Highlight-3', - cell14: 'Highlight-4', - }, - headerCell3: { - heading: 'Heading Title-3', - pricingText: 'Pricing-3', - outlineButtonText: 'Free trial', - blueButtonText: 'Buy now', - }, - sectionRow2: { - sectionRowTitle: 'Row-1.1, Title', - cell22: 'Content', - }, + highlightRow: [ + { colIndex: 0, visibility: false, text: '' }, + { colIndex: 1, visibility: true, style: '', text: 'Highlight-2' }, + { colIndex: 2, visibility: true, style: '', text: 'Highlight-3' }, + { colIndex: 3, visibility: true, style: '', text: 'Highlight-4' }, + { colIndex: 4, visibility: false, style: '', text: '' }, + ], + headerRow: [ + { + colIndex: 3, + heading: 'Heading Title-3', + pricingText: 'Pricing-3', + tooltip: { + tooltip: true, + tooltipPosition: 'left', + tooltipText: 'Tooltip position set to left', + }, + buttons: [ + { text: 'Free trial', type: 'outline', justifycontent: 'center' }, + { text: 'Buy now', type: 'blue', justifycontent: 'center' }, + ], + }, + ], + sectionRows: [ + { + rowIndex: 1, + columns: [ + { colIndex: 1, text: 'Section-1, Title', imageVisible: false }, + { colIndex: 2, text: '', imageVisible: false }, + ], + }, + { + rowIndex: 2, + columns: [ + { colIndex: 1, text: 'Row-1.1, Title', imageVisible: false }, + { colIndex: 2, text: 'Content', imageVisible: false }, + ], + }, + ], }, tags: '@table @smoke @regression @milo', }, @@ -56,16 +112,33 @@ module.exports = { rowsCount: 9, headerRowColCount: 5, sectionRowCount: 8, - headerCell4: { - heading: 'Heading Title-4', - pricingText: 'Pricing-4', - outlineButtonText: 'Free trial', - blueButtonText: 'Buy now', - }, - sectionRow2: { - sectionRowTitle: 'Row-1.1, Title', - cell22: 'Content', - }, + headerRow: [ + { + colIndex: 4, + heading: 'Heading Title-4', + pricingText: 'Pricing-4', + buttons: [ + { text: 'Free trial', type: 'outline', justifycontent: 'center' }, + { text: 'Buy now', type: 'blue', justifycontent: 'center' }, + ], + }, + ], + sectionRows: [ + { + rowIndex: 1, + columns: [ + { colIndex: 1, text: 'Section-1, Title', imageVisible: false }, + { colIndex: 2, text: '', imageVisible: false }, + ], + }, + { + rowIndex: 2, + columns: [ + { colIndex: 1, text: 'Row-1.1, Title', imageVisible: false }, + { colIndex: 2, text: 'Content', imageVisible: false }, + ], + }, + ], }, tags: '@table @smoke @regression @milo', }, @@ -77,21 +150,40 @@ module.exports = { rowsCount: 10, headerRowColCount: 5, sectionRowCount: 8, - hightlightRow: { - cell12: 'Highlight-2', - cell13: 'Highlight-3', - cell14: 'Highlight-4', - }, - headerCell5: { - heading: 'Heading Title-5', - pricingText: 'Pricing-5', - outlineButtonText: 'Free trial', - blueButtonText: 'Buy now', - }, - sectionRow2: { - sectionRowTitle: 'Row-1.1, Title', - cell22: 'Content', - }, + highlightRow: [ + { colIndex: 0, visibility: false, text: '' }, + { colIndex: 1, visibility: true, style: '', text: 'Highlight-2' }, + { colIndex: 2, visibility: true, style: '', text: 'Highlight-3' }, + { colIndex: 3, visibility: true, style: '', text: 'Highlight-4' }, + { colIndex: 4, visibility: false, style: '', text: '' }, + ], + headerRow: [ + { + colIndex: 3, + heading: 'Heading Title-3', + pricingText: 'Pricing-3', + buttons: [ + { text: 'Free trial', type: 'outline', justifycontent: 'center' }, + { text: 'Buy now', type: 'blue', justifycontent: 'center' }, + ], + }, + ], + sectionRows: [ + { + rowIndex: 1, + columns: [ + { colIndex: 1, text: 'Section-1, Title', imageVisible: false }, + { colIndex: 2, text: '', imageVisible: false }, + ], + }, + { + rowIndex: 2, + columns: [ + { colIndex: 1, text: 'Row-1.1, Title', imageVisible: false }, + { colIndex: 2, text: 'Content', imageVisible: false }, + ], + }, + ], }, tags: '@table @smoke @regression @milo', }, @@ -103,17 +195,316 @@ module.exports = { rowsCount: 9, headerRowColCount: 3, sectionRowCount: 8, - headerCell1: { - heading: 'Heading Title-1', - pricingText: 'Pricing-1', - AdditionalText: 'Additional Text-1', - outlineButtonText: 'Free trial', - blueButtonText: 'Buy now', - }, - sectionRow2: { - merchContent: 'Section Content-1.1', - image: 'yes', - }, + headerRow: [ + { + colIndex: 1, + heading: 'Heading Title-1', + pricingText: 'Pricing-1', + AdditionalText: 'Additional Text-1', + imageVisible: true, + buttons: [ + { + text: 'Free trial', + type: 'outline', + justifycontent: 'flex-start', + }, + { text: 'Buy now', type: 'blue', justifycontent: 'flex-start' }, + ], + }, + ], + sectionRows: [ + { + rowIndex: 1, + columns: [ + { colIndex: 1, text: 'Section-1, Title', imageVisible: false }, + { colIndex: 2, text: 'Section-1, Title', imageVisible: false }, + { colIndex: 3, text: 'Section-1, Title', imageVisible: false }, + ], + }, + { + rowIndex: 2, + columns: [ + { colIndex: 1, text: 'Section Content-1.1', imageVisible: true }, + { colIndex: 2, text: 'Section Content-1.1', imageVisible: false }, + { colIndex: 3, text: 'Section Content-1.1', imageVisible: false }, + ], + }, + ], + }, + tags: '@table @smoke @regression @milo', + }, + { + tcid: '5', + name: '@Table (merch, highlight, sticky)', + path: '/drafts/nala/blocks/table/table-merch-highlight-sticky1', + data: { + rowsCount: 10, + highlightRowColCount: 3, + headerRowColCount: 3, + sectionRowCount: 8, + highlightRow: [ + { colIndex: 0, visibility: false, text: '' }, + { + colIndex: 1, + visibility: true, + style: 'background: green;', + text: 'Highlight-2', + }, + { + colIndex: 2, + visibility: true, + style: 'background: rgb(53, 123, 235);', + text: 'Highlight-2', + }, + ], + headerRow: [ + { + colIndex: 1, + heading: 'Heading Title-1', + pricingText: 'Pricing-1', + additionalText: 'Additional Text-1', + tooltip: { + tooltip: true, + tooltipPosition: 'left', + tooltipText: 'Tooltip position set to left', + }, + buttons: [ + { text: 'Free trial', type: 'outline' }, + { text: 'Buy now', type: 'blue' }, + ], + }, + { + colIndex: 2, + heading: 'Heading Title-2', + pricingText: 'Pricing-2', + additionalText: 'Additional Text-2', + tooltip: { + tooltip: true, + tooltipPosition: 'top', + tooltipText: 'Tooltip position set to top', + }, + buttons: [ + { + text: 'Free trial', + type: 'outline', + justifycontent: 'flex-start', + }, + { text: 'Buy now', type: 'blue', justifycontent: 'flex-start' }, + ], + }, + { + colIndex: 3, + heading: 'Heading Title-3', + pricingText: 'Pricing-3', + additionalText: 'Additional Text-3', + buttons: [ + { text: 'Free trial', type: 'outline' }, + { text: 'Buy now', type: 'blue' }, + ], + }, + ], + sectionRows: [ + { + rowIndex: 1, + columns: [ + { colIndex: 1, text: 'Section-1, Title', imageVisible: false }, + { colIndex: 2, text: 'Section-1, Title', imageVisible: false }, + { colIndex: 3, text: 'Section-1, Title', imageVisible: false }, + ], + }, + { + rowIndex: 2, + columns: [ + { colIndex: 1, text: 'Section Content-1.1', imageVisible: true }, + { colIndex: 2, text: 'Section Content-1.1', imageVisible: false }, + { colIndex: 3, text: 'Section Content-1.1', imageVisible: false }, + ], + }, + { + rowIndex: 3, + columns: [ + { colIndex: 1, text: 'Section Content-1.2', imageVisible: false }, + { colIndex: 2, text: 'Section Content-1.2', imageVisible: false }, + { colIndex: 3, text: 'Section Content-1.2', imageVisible: false }, + ], + }, + { + rowIndex: 4, + columns: [ + { colIndex: 1, text: '', imageVisible: false }, + { colIndex: 2, text: '', imageVisible: false }, + { colIndex: 3, text: '', imageVisible: false }, + ], + }, + ], + }, + tags: '@table @smoke @regression @milo', + }, + { + tcid: '6', + name: '@Table (merch, pricing-bottom)', + path: '/drafts/nala/blocks/table/table-march-pricing-bottom', + data: { + rowsCount: 9, + headerRowColCount: 3, + sectionRowCount: 8, + headerRow: [ + { + colIndex: 1, + imageVisible: true, + heading: 'Heading Title-1', + pricingText: 'Pricing-1', + additionalText: 'Additional Text-1', + buttons: [ + { text: 'Free trial', type: 'outline' }, + { text: 'Buy now', type: 'blue' }, + ], + }, + { + colIndex: 2, + imageVisible: true, + heading: 'Heading Title-2', + pricingText: 'Pricing-2', + additionalText: 'Additional Text-2', + buttons: [ + { text: 'Free trial', type: 'outline' }, + { text: 'Buy now', type: 'blue' }, + ], + }, + { + colIndex: 3, + imageVisible: true, + heading: 'Heading Title-3', + pricingText: 'Pricing-3', + additionalText: 'Additional Text-3', + buttons: [ + { + text: 'Free trial', + type: 'outline', + justifycontent: 'flex-start', + }, + { text: 'Buy now', type: 'blue', justifycontent: 'flex-start' }, + ], + }, + ], + sectionRows: [ + { + rowIndex: 1, + columns: [ + { colIndex: 1, text: 'Section-1, Title', imageVisible: false }, + { colIndex: 2, text: 'Section-1, Title', imageVisible: false }, + { colIndex: 3, text: 'Section-1, Title', imageVisible: false }, + ], + }, + { + rowIndex: 2, + columns: [ + { colIndex: 1, text: 'Section Content-1.1', imageVisible: true }, + { colIndex: 2, text: 'Section Content-1.1', imageVisible: false }, + { colIndex: 3, text: 'Section Content-1.1', imageVisible: false }, + ], + }, + { + rowIndex: 3, + columns: [ + { colIndex: 1, text: 'Section Content-1.2', imageVisible: false }, + { colIndex: 2, text: 'Section Content-1.2', imageVisible: false }, + { colIndex: 3, text: 'Section Content-1.2', imageVisible: false }, + ], + }, + { + rowIndex: 4, + columns: [ + { colIndex: 1, text: '', imageVisible: false }, + { colIndex: 2, text: '', imageVisible: false }, + { colIndex: 3, text: '', imageVisible: false }, + ], + }, + ], + }, + tags: '@table-6 @smoke @regression @milo', + }, + { + tcid: '7', + name: '@Table (merch, button-right)', + path: '/drafts/nala/blocks/table/table-merch-button-right', + data: { + rowsCount: 9, + headerRowColCount: 3, + sectionRowCount: 8, + headerRow: [ + { + colIndex: 1, + imageVisible: true, + heading: 'Heading Title-1', + pricingText: 'Pricing-1', + additionalText: 'Additional Text-1', + buttons: [ + { + text: 'Free trial', + type: 'outline', + justifycontent: 'flex-end', + }, + { text: 'Buy now', type: 'blue', justifycontent: 'flex-end' }, + ], + }, + { + colIndex: 2, + imageVisible: true, + heading: 'Heading Title-2', + pricingText: 'Pricing-2', + additionalText: 'Additional Text-2', + buttons: [ + { text: 'Free trial', type: 'outline' }, + { text: 'Buy now', type: 'blue' }, + ], + }, + { + colIndex: 3, + imageVisible: true, + heading: 'Heading Title-3', + pricingText: 'Pricing-3', + additionalText: 'Additional Text-3', + buttons: [ + { text: 'Free trial', type: 'outline' }, + { text: 'Buy now', type: 'blue' }, + ], + }, + ], + sectionRows: [ + { + rowIndex: 1, + columns: [ + { colIndex: 1, text: 'Section-1, Title', imageVisible: false }, + { colIndex: 2, text: 'Section-1, Title', imageVisible: false }, + { colIndex: 3, text: 'Section-1, Title', imageVisible: false }, + ], + }, + { + rowIndex: 2, + columns: [ + { colIndex: 1, text: 'Section Content-1.1', imageVisible: true }, + { colIndex: 2, text: 'Section Content-1.1', imageVisible: false }, + { colIndex: 3, text: 'Section Content-1.1', imageVisible: false }, + ], + }, + { + rowIndex: 3, + columns: [ + { colIndex: 1, text: 'Section Content-1.2', imageVisible: false }, + { colIndex: 2, text: 'Section Content-1.2', imageVisible: false }, + { colIndex: 3, text: 'Section Content-1.2', imageVisible: false }, + ], + }, + { + rowIndex: 4, + columns: [ + { colIndex: 1, text: '', imageVisible: false }, + { colIndex: 2, text: '', imageVisible: false }, + { colIndex: 3, text: '', imageVisible: false }, + ], + }, + ], }, tags: '@table @smoke @regression @milo', }, diff --git a/nala/blocks/table/table.test.js b/nala/blocks/table/table.test.js index 9b04e8b01a..cf1ef914df 100644 --- a/nala/blocks/table/table.test.js +++ b/nala/blocks/table/table.test.js @@ -1,7 +1,6 @@ import { expect, test } from '@playwright/test'; import { features } from './table.spec.js'; import TableBlock from './table.page.js'; -import { runAccessibilityTest } from '../../libs/accessibility.js'; let table; @@ -29,22 +28,9 @@ test.describe('Milo Table block feature test suite', () => { await expect(await table.headingRowColumns).toHaveCount(data.headerRowColCount); await expect(await table.sectionRows).toHaveCount(data.sectionRowCount); - // verify header row cell - const headerCell = data.headerCell2; - await expect(await table.getHeaderColumnTitle(2)).toContainText(headerCell.heading); - await expect(await table.getHeaderColumnPricing(2)).toContainText(headerCell.pricingText); - await expect(await table.getHeaderColumnOutlineButton(2)).toContainText(headerCell.outlineButtonText); - await expect(await table.getHeaderColumnBlueButton(2)).toContainText(headerCell.blueButtonText); - - // verify section row cell - const sectionCell = data.sectionRow2; - await expect(await table.getSectionRowTitle(2)).toContainText(sectionCell.sectionRowTitle); - await expect(await table.getSectionRowCell(2, 2)).toContainText(sectionCell.cell22); - }); - - await test.step('step-3: Verify the accessibility test on the table(default) block', async () => { - // The accessibility test is failing, so skipping it. - await runAccessibilityTest({ page, testScope: table.table, skipA11yTest: true }); + // verify table header and section rows content + await table.verifyHeaderRow(data); + await table.verifySectionRow(data); }); }); @@ -67,28 +53,10 @@ test.describe('Milo Table block feature test suite', () => { await expect(await table.headingRowColumns).toHaveCount(data.headerRowColCount); await expect(await table.sectionRows).toHaveCount(data.sectionRowCount); - // verify highlighter row - const highlighter = data.hightlightRow; - await expect(await table.getHighlightRowColumnTitle(1)).toContainText(highlighter.cell12); - await expect(await table.getHighlightRowColumnTitle(2)).toContainText(highlighter.cell13); - await expect(await table.getHighlightRowColumnTitle(3)).toContainText(highlighter.cell14); - - // verify header row cell - const headerCell = data.headerCell3; - await expect(await table.getHeaderColumnTitle(3)).toContainText(headerCell.heading); - await expect(await table.getHeaderColumnPricing(3)).toContainText(headerCell.pricingText); - await expect(await table.getHeaderColumnOutlineButton(3)).toContainText(headerCell.outlineButtonText); - await expect(await table.getHeaderColumnBlueButton(3)).toContainText(headerCell.blueButtonText); - - // verify section row cell - const sectionCell = data.sectionRow2; - await expect(await table.getSectionRowTitle(2)).toContainText(sectionCell.sectionRowTitle); - await expect(await table.getSectionRowCell(2, 2)).toContainText(sectionCell.cell22); - }); - - await test.step('step-3: Verify the accessibility test on the table(highlight) block', async () => { - // The accessibility test is failing, so skipping it. - await runAccessibilityTest({ page, testScope: table.highlightTable, skipA11yTest: true }); + // verify highlighter, header, and section rows content + await table.verifyHighlightRow(data); + await table.verifyHeaderRow(data); + await table.verifySectionRow(data); }); }); @@ -104,31 +72,21 @@ test.describe('Milo Table block feature test suite', () => { }); await test.step('step-2: Verify table content/specs', async () => { - // verify sticky table header and attributes + // verify table header row attributes ( class, position(sticky) and top ) await expect(await table.stickyTable).toBeVisible(); + await expect(await table.stickyRow).toHaveAttribute('class', 'row row-1 row-heading top-border-transparent'); + await expect(await table.stickyRow).toHaveCSS('position', 'sticky'); + await expect(await table.stickyRow).toHaveCSS('top', '64px'); // verify table row, column count await expect(await table.rows).toHaveCount(data.rowsCount); await expect(await table.headingRowColumns).toHaveCount(data.headerRowColCount); await expect(await table.sectionRows).toHaveCount(data.sectionRowCount); - // verify header row cell - const headerCell = data.headerCell4; - await expect(await table.getHeaderColumnTitle(4)).toContainText(headerCell.heading); - await expect(await table.getHeaderColumnPricing(4)).toContainText(headerCell.pricingText); - await expect(await table.getHeaderColumnOutlineButton(4)).toContainText(headerCell.outlineButtonText); - await expect(await table.getHeaderColumnBlueButton(4)).toContainText(headerCell.blueButtonText); - - // verify section row cell - const sectionCell = data.sectionRow2; - await expect(await table.getSectionRowTitle(2)).toContainText(sectionCell.sectionRowTitle); - await expect(await table.getSectionRowCell(2, 2)).toContainText(sectionCell.cell22); - }); - - await test.step('step-3: Verify the accessibility test on the table(sticky) block', async () => { - // The accessibility test is failing, so skipping it. - await runAccessibilityTest({ page, testScope: table.stickyTable, skipA11yTest: true }); + // verify header and section rows content + await table.verifyHeaderRow(data); + await table.verifySectionRow(data); }); }); @@ -148,34 +106,18 @@ test.describe('Milo Table block feature test suite', () => { await expect(await table.collapseStickyTable).toBeVisible(); await expect(table.highlightRow).toHaveClass(/row.*row-1.*row-highlight/); await expect(table.stickyRow).toHaveClass(/row.*row-2.*row-heading/); + await expect(await table.stickyRow).toHaveCSS('position', 'sticky'); + await expect(await table.stickyRow).toHaveCSS('top', '114px'); - // verify table row, column count + // verify table rows and columns count await expect(await table.rows).toHaveCount(data.rowsCount); await expect(await table.headingRowColumns).toHaveCount(data.headerRowColCount); await expect(await table.sectionRows).toHaveCount(data.sectionRowCount); - // verify highlighter row - const highlighter = data.hightlightRow; - await expect(await table.getHighlightRowColumnTitle(1)).toContainText(highlighter.cell12); - await expect(await table.getHighlightRowColumnTitle(2)).toContainText(highlighter.cell13); - await expect(await table.getHighlightRowColumnTitle(3)).toContainText(highlighter.cell14); - - // verify header row cell - const headerCell = data.headerCell5; - await expect(await table.getHeaderColumnTitle(5)).toContainText(headerCell.heading); - await expect(await table.getHeaderColumnPricing(5)).toContainText(headerCell.pricingText); - await expect(await table.getHeaderColumnOutlineButton(5)).toContainText(headerCell.outlineButtonText); - await expect(await table.getHeaderColumnBlueButton(5)).toContainText(headerCell.blueButtonText); - - // verify section row cell - const sectionCell = data.sectionRow2; - await expect(await table.getSectionRowTitle(2)).toContainText(sectionCell.sectionRowTitle); - await expect(await table.getSectionRowCell(2, 2)).toContainText(sectionCell.cell22); - }); - - await test.step('step-3: Verify the accessibility test on the table(highlight, collapse, sticky) block', async () => { - // The accessibility test is failing, so skipping it. - await runAccessibilityTest({ page, testScope: table.collapseStickyTable, skipA11yTest: true }); + // verify highlighter, header, and section rows content + await table.verifyHighlightRow(data); + await table.verifyHeaderRow(data); + await table.verifySectionRow(data); }); }); @@ -194,28 +136,94 @@ test.describe('Milo Table block feature test suite', () => { // verify merch table await expect(await table.merchTable).toBeVisible(); - // verify table row, column count + // verify table rows and columns count await expect(await table.rows).toHaveCount(data.rowsCount); await expect(await table.headingRowColumns).toHaveCount(data.headerRowColCount); await expect(await table.sectionRows).toHaveCount(data.sectionRowCount); - // verify merch table header row cell - const headerCell = data.headerCell1; - await expect(await table.getHeaderColumnTitle(1)).toContainText(headerCell.heading); - await expect(await table.getHeaderColumnPricing(1)).toContainText(headerCell.pricingText); - await expect(await table.getHeaderColumnAdditionalText(1)).toContainText(headerCell.AdditionalText); - await expect(await table.getHeaderColumnOutlineButton(1)).toContainText(headerCell.outlineButtonText); - await expect(await table.getHeaderColumnBlueButton(1)).toContainText(headerCell.blueButtonText); + // verify header and section rows content + await table.verifyHeaderRow(data); + await table.verifySectionRow(data); + }); + }); + + // Test 5 : Table (merch, highlight, sticky) + test(`[Test Id - ${features[5].tcid}] ${features[5].name} ${features[5].tags}`, async ({ page, baseURL }) => { + const { path, data } = features[5]; + console.info(`[Test Page]: ${baseURL}${path}`); - // verify merch table section row cell - const sectionCell = data.sectionRow2; - await expect(await table.getSectionRowMerchContent(2)).toContainText(sectionCell.merchContent); - await expect(await table.getSectionRowMerchContentImg(2)).toBeVisible(); + // Step 1: Navigate to the test page + await test.step('step-1: Go to Table block test page', async () => { + await page.goto(`${baseURL}${path}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${path}`); }); - await test.step('step-3: Verify the accessibility test on the table(merch) block', async () => { - // The accessibility test is failing, so skipping it. - await runAccessibilityTest({ page, testScope: table.merchTable, skipA11yTest: true }); + // Step 2: Verify table structure and content + await test.step('step-2: Verify table content/specs', async () => { + // Verify visibility and row/column counts + await expect(await table.merchHighlightStickyTable).toBeVisible(); + await expect(await table.rows).toHaveCount(data.rowsCount); + await expect(await table.highlightRowColumns).toHaveCount(data.highlightRowColCount); + await expect(await table.headingRowColumns).toHaveCount(data.headerRowColCount); + await expect(await table.sectionRows).toHaveCount(data.sectionRowCount); + + await table.verifyHighlightRow(data); + await table.verifyHeaderRow(data); + await table.verifySectionRow(data); + }); + }); + + // Test 6 : Table (merch, pricing-bottom) + test(`[Test Id - ${features[6].tcid}] ${features[6].name} ${features[6].tags}`, async ({ page, baseURL }) => { + const { path, data } = features[6]; + console.info(`[Test Page]: ${baseURL}${path}`); + + // Step 1: Navigate to the test page + await test.step('step-1: Go to Table block test page', async () => { + await page.goto(`${baseURL}${path}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${path}`); + }); + + await test.step('step-2: Verify table structure and content/specs', async () => { + // Verify visibility and row/column counts + await expect(await table.merchPricingBottom).toBeVisible(); + await expect(await table.rows).toHaveCount(data.rowsCount); + await expect(await table.headingRowColumns).toHaveCount(data.headerRowColCount); + await expect(await table.sectionRows).toHaveCount(data.sectionRowCount); + + await table.verifyHeaderRow(data, 'bottom-pricing'); + await table.verifySectionRow(data); + }); + }); + + // Test 7 : Table (merch, button-right) + test(`[Test Id - ${features[7].tcid}] ${features[7].name} ${features[7].tags}`, async ({ page, baseURL }) => { + const { path, data } = features[7]; + console.info(`[Test Page]: ${baseURL}${path}`); + + // Step 1: Navigate to the test page + await test.step('step-1: Go to Table block test page', async () => { + await page.goto(`${baseURL}${path}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${path}`); + }); + + // Step 2: Verify table structure and content + await test.step('step-2: Verify table content/specs', async () => { + // Verify table visibility + await expect(await table.merchButtonRight).toBeVisible(); + + // verify table rows and coloumn content + await expect(await table.rows).toHaveCount(data.rowsCount); + await expect(await table.headingRowColumns).toHaveCount(data.headerRowColCount); + await expect(await table.sectionRows).toHaveCount(data.sectionRowCount); + await expect(await table.sectionRows).toHaveCount(data.sectionRowCount); + + // verify header and section row cells content + await table.verifyHeaderRow(data); + await table.verifySectionRow(data); }); }); }); From ff58d7e816a7bd1ccc0ea25a928f3186e5094a2f Mon Sep 17 00:00:00 2001 From: Swati Mukherjee Date: Thu, 5 Dec 2024 22:53:21 +0530 Subject: [PATCH 03/11] fix no cookie preference in stage (#3309) --- libs/martech/helpers.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/libs/martech/helpers.js b/libs/martech/helpers.js index 25760ba57a..8ac87e40c6 100644 --- a/libs/martech/helpers.js +++ b/libs/martech/helpers.js @@ -352,7 +352,10 @@ function updateAMCVCookie(ECID) { */ export const loadAnalyticsAndInteractionData = async ({ locale, env, calculatedTimeout }) => { const value = getCookie('kndctr_9E1005A551ED61CA0A490D45_AdobeOrg_consent', true); - if (value?.[1] === 'general' && value?.[2] === 'out') { + const isRejectedDecodedURI = value?.[2] === undefined && decodeURIComponent(value?.[1]) === 'general=out'; + const isRejectedURI = value?.[1] === 'general' && value?.[2] === 'out'; + + if (isRejectedDecodedURI || isRejectedURI) { return Promise.reject(new Error('Consent Cookie doesnt allow interact')); } From 023541d45f2dea0d4202c8b7c6ec0b6ea4ce2f68 Mon Sep 17 00:00:00 2001 From: Robert Bogos <146744221+robert-bogos@users.noreply.github.com> Date: Thu, 5 Dec 2024 19:23:28 +0200 Subject: [PATCH 04/11] [MWPW-161570] Ensure hardcoded decorative SVGs have aria-hidden="true" (#3247) * inline svgs skipped by screen readers * addressed feedback * addressed feedback * addressed feedback * removed role attribute from decorative svgs * hotfix * hotfix --- libs/blocks/article-feed/article-feed.js | 2 +- libs/blocks/aside/aside.js | 2 +- libs/blocks/chart/chart.js | 2 +- libs/blocks/footer/footer.js | 4 ++-- libs/blocks/global-footer/global-footer.js | 6 +++--- libs/blocks/global-navigation/utilities/utilities.js | 12 ++++++------ libs/blocks/gnav/gnav-appLauncher.js | 2 +- libs/blocks/gnav/gnav.js | 8 ++++---- libs/blocks/modal/modal.js | 2 +- libs/blocks/notification/notification.js | 2 +- libs/blocks/tabs/tabs.js | 2 +- libs/deps/mas/plans-modal.js | 12 ++++-------- libs/features/personalization/preview.js | 2 +- .../features/spectrum-web-components/dist/popover.js | 4 ++-- libs/mep/dc0994/aside/aside.js | 2 +- 15 files changed, 30 insertions(+), 34 deletions(-) diff --git a/libs/blocks/article-feed/article-feed.js b/libs/blocks/article-feed/article-feed.js index ae7322283f..766863a6a2 100644 --- a/libs/blocks/article-feed/article-feed.js +++ b/libs/blocks/article-feed/article-feed.js @@ -303,7 +303,7 @@ async function buildFilter(type, tax, block, config) { dropdown.setAttribute('aria-labelledby', `${type}-filter-button`); dropdown.setAttribute('role', 'menu'); - const SEARCH_ICON = ` + const SEARCH_ICON = ``; const searchBar = createTag('div', { class: 'filter-search' }); diff --git a/libs/blocks/aside/aside.js b/libs/blocks/aside/aside.js index 92ea2500d4..898bc2bf9c 100644 --- a/libs/blocks/aside/aside.js +++ b/libs/blocks/aside/aside.js @@ -33,7 +33,7 @@ const blockConfig = { }, }; const FORMAT_REGEX = /^format:/i; -const closeSvg = ` +const closeSvg = `AdChoices icon`; const SUPPORTED_SOCIAL = ['facebook', 'instagram', 'twitter', 'linkedin', 'pinterest', 'discord', 'behance', 'youtube', 'weibo', 'social-media']; diff --git a/libs/blocks/global-footer/global-footer.js b/libs/blocks/global-footer/global-footer.js index 21e18208cd..051bda37e4 100644 --- a/libs/blocks/global-footer/global-footer.js +++ b/libs/blocks/global-footer/global-footer.js @@ -212,7 +212,7 @@ class Footer { aria-expanded="false" aria-haspopup="true" role="button"> - + ${regionPickerTextElem} @@ -329,7 +329,7 @@ class Footer { aria-label="${platform}" daa-ll="${getAnalyticsValue(platform, index + 1)}" target="_blank"> - + @@ -359,7 +359,7 @@ class Footer { // Add Ad Choices icon const adChoicesElem = privacyContent.querySelector('a[href*="#interest-based-ads"]'); - adChoicesElem?.prepend(toFragment` + adChoicesElem?.prepend(toFragment``); diff --git a/libs/blocks/global-navigation/utilities/utilities.js b/libs/blocks/global-navigation/utilities/utilities.js index f880d14ae1..ed7fcb3491 100644 --- a/libs/blocks/global-navigation/utilities/utilities.js +++ b/libs/blocks/global-navigation/utilities/utilities.js @@ -27,16 +27,16 @@ export const selectors = { }; export const icons = { - brand: '', - company: '', - search: '', - home: '', + brand: '', + company: '', + search: '', + home: '', }; export const darkIcons = { ...icons, - brand: '', - company: '', + brand: '', + company: '', }; export const lanaLog = ({ message, e = '', tags = 'errorType=default' }) => { diff --git a/libs/blocks/gnav/gnav-appLauncher.js b/libs/blocks/gnav/gnav-appLauncher.js index c36f224ca2..6b907f9c0a 100644 --- a/libs/blocks/gnav/gnav-appLauncher.js +++ b/libs/blocks/gnav/gnav-appLauncher.js @@ -1,6 +1,6 @@ import { createTag } from '../../utils/utils.js'; -const WAFFLE_ICON = ''; +const WAFFLE_ICON = ''; function decorateAppsMenu(profileEl, appsDom, toggle) { const appsNavItem = createTag('div', { class: 'gnav-navitem app-launcher has-menu', 'da-ll': 'App Launcher' }); diff --git a/libs/blocks/gnav/gnav.js b/libs/blocks/gnav/gnav.js index 10ebe8b701..f3d8237afc 100644 --- a/libs/blocks/gnav/gnav.js +++ b/libs/blocks/gnav/gnav.js @@ -13,10 +13,10 @@ import { analyticsGetLabel, } from '../../martech/attributes.js'; -const COMPANY_IMG = ''; -const BRAND_IMG = ''; -const BRAND_LOGO_AS_TEXT = ''; -const SEARCH_ICON = ''; +const COMPANY_IMG = ''; +const BRAND_IMG = ''; +const BRAND_LOGO_AS_TEXT = ''; +const SEARCH_ICON = ''; const SEARCH_DEBOUNCE_MS = 300; export const IS_OPEN = 'is-open'; const SEARCH_TYPE_CONTEXTUAL = 'contextual'; diff --git a/libs/blocks/modal/modal.js b/libs/blocks/modal/modal.js index 5ba3f3a0c8..09539695d0 100644 --- a/libs/blocks/modal/modal.js +++ b/libs/blocks/modal/modal.js @@ -4,7 +4,7 @@ import { createTag, getMetadata, localizeLink, loadStyle, getConfig } from '../. import { decorateSectionAnalytics } from '../../martech/attributes.js'; const FOCUSABLES = 'a:not(.hide-video), button, input, textarea, select, details, [tabindex]:not([tabindex="-1"]'; -const CLOSE_ICON = ` +const CLOSE_ICON = ` diff --git a/libs/mep/dc0994/aside/aside.js b/libs/mep/dc0994/aside/aside.js index 2283e0e1d9..16d4f5d459 100644 --- a/libs/mep/dc0994/aside/aside.js +++ b/libs/mep/dc0994/aside/aside.js @@ -33,7 +33,7 @@ const blockConfig = { }, }; const FORMAT_REGEX = /^format:/i; -const closeSvg = ` +const closeSvg = `

+ +

+ Text +

+

+ Text | Aria label +

+

+ Text | Other text | Aria label +

+

+ Text|Other text|Aria label +

+

+ + + Text + +

+

+ + + Text | Aria label + +

diff --git a/test/utils/utils.test.js b/test/utils/utils.test.js index 88a39e6e1d..f908644013 100644 --- a/test/utils/utils.test.js +++ b/test/utils/utils.test.js @@ -212,6 +212,39 @@ describe('Utils', () => { }); }); + describe('Aria label appendment', () => { + it('appends aria label if defined', () => { + const theText = 'Text'; + const theAriaLabel = 'Aria label'; + + const noAriaLabelElem = document.querySelector('.aria-label-none'); + expect(noAriaLabelElem.getAttribute('aria-label')).to.be.null; + expect(noAriaLabelElem.innerText).to.equal(theText); + + const simpleAriaLabelElem = document.querySelector('.aria-label-simple'); + expect(simpleAriaLabelElem.getAttribute('aria-label')).to.equal(theAriaLabel); + expect(simpleAriaLabelElem.innerText).to.equal(theText); + + const pipedAriaLabelElem = document.querySelector('.aria-label-piped'); + expect(pipedAriaLabelElem.getAttribute('aria-label')).to.equal(theAriaLabel); + expect(pipedAriaLabelElem.innerText).to.equal(`${theText} | Other text`); + + const noSpacePipedAriaLabelElem = document.querySelector('.aria-label-piped--no-space'); + expect(noSpacePipedAriaLabelElem.getAttribute('aria-label')).to.equal(theAriaLabel); + expect(noSpacePipedAriaLabelElem.innerText).to.equal(`${theText}|Other text`); + + const iconNoAriaLabelElem = document.querySelector('.aria-label-icon-none'); + expect(iconNoAriaLabelElem.getAttribute('aria-label')).to.be.null; + expect(iconNoAriaLabelElem.querySelector('.icon')).to.exist; + expect(iconNoAriaLabelElem.innerText).to.equal(theText); + + const iconAriaLabelElem = document.querySelector('.aria-label-icon-simple'); + expect(iconAriaLabelElem.getAttribute('aria-label')).to.equal(theAriaLabel); + expect(iconAriaLabelElem.querySelector('.icon')).to.exist; + expect(iconAriaLabelElem.innerText).to.equal(theText); + }); + }); + describe('Fragments', () => { it('fully unwraps a fragment', () => { const fragments = document.querySelectorAll('.link-block.fragment'); From 82098c7742e36b098eb5a4b666c6a4de23ef9b55 Mon Sep 17 00:00:00 2001 From: Bandana Laishram Date: Thu, 5 Dec 2024 22:53:43 +0530 Subject: [PATCH 06/11] Picking first string only for localnav title (#3281) * Picking first string only for localnav title * Lint fix --- libs/blocks/global-navigation/global-navigation.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libs/blocks/global-navigation/global-navigation.js b/libs/blocks/global-navigation/global-navigation.js index 9d13051e86..dd8dbe3179 100644 --- a/libs/blocks/global-navigation/global-navigation.js +++ b/libs/blocks/global-navigation/global-navigation.js @@ -372,6 +372,9 @@ class Gnav {
`; this.block.append(this.elements.curtain, this.elements.aside, this.elements.topnavWrapper); + // TODO: Remove with mobile redesign code + const firstLocalNavItem = this.elements.navWrapper.querySelector('.feds-nav .feds-navItem:not(.feds-navItem--section) a'); + if (firstLocalNavItem) [firstLocalNavItem.textContent] = firstLocalNavItem.textContent.split('|'); }; addChangeEventListeners = () => { From e4b8e98e96c2655180dc727a19874a3ae2d9ca1a Mon Sep 17 00:00:00 2001 From: Suhani Jain <110388864+suhjainadobe@users.noreply.github.com> Date: Fri, 6 Dec 2024 14:20:31 +0530 Subject: [PATCH 07/11] MWPW-160380 [Milo]Update A.com CTA color to Spectrum 2 Blue for all A.com CTAs (#3221) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Revert "MWPW-156749: Fix video CLS " (#2899) (#2900) Revert "MWPW-156749: Fix video CLS (#2849)" This reverts commit d4134c85979cfbb88a01d331f8d946eb67251de5. * [MWPW-159903] Fix quiz video marquees (#3009) (#3013) fix quiz marquees * [MWPW-159328] handle a case where there are not placeholders availabl… (#3014) [MWPW-159328] handle a case where there are not placeholders available (#2998) * [MWPW-159328] handle a case where there are not palceholders availble * fixed typos --------- Co-authored-by: Denys Fedotov Co-authored-by: Denys Fedotov * MWPW-146211 [MILO][MEP] Option to select all elements (#2976) (#3023) * stash * stash * stash * working well * set updated command list for inline * remove querySelector function * unit test and custom block fix * updates for in-block * merch-card-collection unit test fixed * unit test updates * more unit test repair * linting errors * more linting * Fix Invalid selector test * add coverage * force git checks to refire * remove comment * pass rootEl to getSelectedElements for use when needed (gnav) * skip if clause in codecov --------- Co-authored-by: Vivian A Goodrich <101133187+vgoodric@users.noreply.github.com> Co-authored-by: markpadbe * MWPW-158455: Promobar overlays with localnav elements in devices (#2991) * Revert "MWPW-156749: Fix video CLS " (#2899) (#2900) Revert "MWPW-156749: Fix video CLS (#2849)" This reverts commit d4134c85979cfbb88a01d331f8d946eb67251de5. * Changing z-index of promobar and popup * Changing z-index of promobar and popup * Reverting z-index to 4 for promobar --------- Co-authored-by: milo-pr-merge[bot] <169241390+milo-pr-merge[bot]@users.noreply.github.com> Co-authored-by: Okan Sahin <39759830+mokimo@users.noreply.github.com> Co-authored-by: Blaine Gunn Co-authored-by: Akansha Arora <> * Revert "MWPW-140452 - Icon authoring in milo using the federal repo and individual SVG assets (#2986)" This reverts commit f379815f307f03c8e42fca3b598e657716d34cd8. * [Release] Stage to Main (#3181) * [MWPW-160011] - GNAV light mode color changes (#3019) * Color changes * Promo background color fix * Add text and subtext font color changes in megamenu * cross cloud menu background color changes * Fix reveiew comments * colors hexcode formatted as lowercase for css variables * Bug fix MWPW-161889 * MWPW-161590, 161606, 161871, 161987 [MEP] Update placeholders before updating href (#3142) * MWPW-161590 [MEP] Update placeholders before updating href * unit test updates * MWPW-161871 [MEP] useblockcode action fails if page is on milo site (#3161) * stash * ready to publish * interact call coming back but timing out * working on a fast connection * clean up ifs * working with promise * use camel case on let variable * unit tests * update unit tests * MWPW-149504 [MILO][MEP] Move entitlements object to the same JSON file used by the library (#3047) * create mepxlg branch * update library * require full hostname match * update reference for unit test * switch to use config instead of domain list and stub response in unit test * update fetch to 2 * updating another fetch to 2 * restore normalizePath to use preview domains on preview links * preload segment list json * use getFederatedUrl instead * import at top so we don't have to make normalizePath async * MWPW-148129 [MILO][MEP][GNAV] Able to use select by url feature with federated link (#3064) * add federated link function to registerInBlockActions * add to unit test * add unit test coverage * initial commit * Fix useblock code issue --------- Co-authored-by: vgoodric Co-authored-by: Vivian A Goodrich <101133187+vgoodric@users.noreply.github.com> * MWPW-161606 [MEP] Modify within modals with MEP (#3171) initial commit publish Co-authored-by: viloria * add conditionals --------- Co-authored-by: Mark Perry <124626043+markpadbe@users.noreply.github.com> Co-authored-by: viloria * [MWPW-161782] - Added logo changes for older GNAV experience (#3162) * Added logo changes for older GNAV experience * Lint Fix * Logo alighned to left for older GNAV * [MWPW-161563] CDT Overlap in Global and APAC Promotion file (#3168) * Use schedule as source for CDT * seperate start and end * Added unit tests * [MWPW-157451] [Accessibility] Merch cards | Focus is lost on comparing plans section (#3103) * remive tabindex=0 as the attributes from merchcard div * fresh commit with same changes as commit 1 after resolving the merge conflict * Redesign mini-compare chart. Added checkmarks for icons. Backward compatibility using "checkmark" class. Horizontal line added. * Revert "Redesign mini-compare chart. Added checkmarks for icons. Backward compatibility using "checkmark" class. Horizontal line added." This reverts commit 610807a5ef2a4f8e4c71216eb6a461010925d785. * run build in mas * run build --------- Co-authored-by: Rohit Sahu * MWPW-159381: Replaces CSS 'body *' with '.consonant-Wrapper *' (#3152) * Revert "MWPW-156749: Fix video CLS " (#2899) (#2900) Revert "MWPW-156749: Fix video CLS (#2849)" This reverts commit d4134c85979cfbb88a01d331f8d946eb67251de5. * [MWPW-159903] Fix quiz video marquees (#3009) (#3013) fix quiz marquees * [MWPW-159328] handle a case where there are not placeholders availabl… (#3014) [MWPW-159328] handle a case where there are not placeholders available (#2998) * [MWPW-159328] handle a case where there are not palceholders availble * fixed typos --------- Co-authored-by: Denys Fedotov Co-authored-by: Denys Fedotov * MWPW-146211 [MILO][MEP] Option to select all elements (#2976) (#3023) * stash * stash * stash * working well * set updated command list for inline * remove querySelector function * unit test and custom block fix * updates for in-block * merch-card-collection unit test fixed * unit test updates * more unit test repair * linting errors * more linting * Fix Invalid selector test * add coverage * force git checks to refire * remove comment * pass rootEl to getSelectedElements for use when needed (gnav) * skip if clause in codecov --------- Co-authored-by: Vivian A Goodrich <101133187+vgoodric@users.noreply.github.com> Co-authored-by: markpadbe * MWPW-158455: Promobar overlays with localnav elements in devices (#2991) * Revert "MWPW-156749: Fix video CLS " (#2899) (#2900) Revert "MWPW-156749: Fix video CLS (#2849)" This reverts commit d4134c85979cfbb88a01d331f8d946eb67251de5. * Changing z-index of promobar and popup * Changing z-index of promobar and popup * Reverting z-index to 4 for promobar --------- Co-authored-by: milo-pr-merge[bot] <169241390+milo-pr-merge[bot]@users.noreply.github.com> Co-authored-by: Okan Sahin <39759830+mokimo@users.noreply.github.com> Co-authored-by: Blaine Gunn Co-authored-by: Akansha Arora <> * Replaces CSS 'body *' with '.consonant-Wrapper *' --------- Co-authored-by: milo-pr-merge[bot] <169241390+milo-pr-merge[bot]@users.noreply.github.com> Co-authored-by: Okan Sahin <39759830+mokimo@users.noreply.github.com> Co-authored-by: Blaine Gunn Co-authored-by: Denys Fedotov Co-authored-by: Denys Fedotov Co-authored-by: Rares Munteanu Co-authored-by: Vivian A Goodrich <101133187+vgoodric@users.noreply.github.com> Co-authored-by: markpadbe Co-authored-by: Akansha Arora * [MWPW-160948] Remove blockquote default spacing (#3159) * [MWPW-161098] Environment-aware links support for CaaS (#3165) * relative urls caas support * added unit tests * [MWPW-162059] - Fix for vertical alignment of logo on AH (#3180) Fix for verticle alignment of logo * Global links underlined by default (#3166) * a links underline, a.quite added * added styles to omit header/footer * added default link styles for headlines and lockup area * minor :hover style alignment on cards-horiz, marquee-anchors, brick * removed dubs underline on links * removed styles overrides to support double in horiz-card, marquee-anch, brick * [MWPW-145672] Remove Gnav global link styles * [MWPW-161624] Link farm double underline on focus * Ensure sub text in anchors is underlined correctly * Ensure sub text in anchors is underlined correctly --------- Co-authored-by: thi64146 * Revert "MWPW-140452 - Icon authoring in milo using the federal repo and individual SVG assets (#3183) Revert "MWPW-140452 - Icon authoring in milo using the federal repo and individual SVG assets (#2986)" This reverts commit f379815f307f03c8e42fca3b598e657716d34cd8. Co-authored-by: Rares Munteanu * [MWPW-145672] Change link color only on hover (#3186) --------- Co-authored-by: Dev Ashish Saradhana <41122199+Deva309@users.noreply.github.com> Co-authored-by: Vivian A Goodrich <101133187+vgoodric@users.noreply.github.com> Co-authored-by: Mark Perry <124626043+markpadbe@users.noreply.github.com> Co-authored-by: viloria Co-authored-by: Rahul Gupta Co-authored-by: rohitsahu Co-authored-by: Rohit Sahu Co-authored-by: cmiqueo <64917520+cmiqueo@users.noreply.github.com> Co-authored-by: milo-pr-merge[bot] <169241390+milo-pr-merge[bot]@users.noreply.github.com> Co-authored-by: Okan Sahin <39759830+mokimo@users.noreply.github.com> Co-authored-by: Blaine Gunn Co-authored-by: Denys Fedotov Co-authored-by: Denys Fedotov Co-authored-by: Rares Munteanu Co-authored-by: markpadbe Co-authored-by: Akansha Arora Co-authored-by: Robert Bogos <146744221+robert-bogos@users.noreply.github.com> Co-authored-by: thi64146 Co-authored-by: Narcis Radu * MWPW-160380 [Milo]Update A.com CTA color to Spectrum 2 Blue for all A.com CTAs * MWPW-160380 [Milo]Update A.com CTA color to Spectrum 2 Blue for all A.com CTAs * nala checks --------- Co-authored-by: milo-pr-merge[bot] <169241390+milo-pr-merge[bot]@users.noreply.github.com> Co-authored-by: Okan Sahin <39759830+mokimo@users.noreply.github.com> Co-authored-by: Blaine Gunn Co-authored-by: Denys Fedotov Co-authored-by: Denys Fedotov Co-authored-by: Rares Munteanu Co-authored-by: Vivian A Goodrich <101133187+vgoodric@users.noreply.github.com> Co-authored-by: markpadbe Co-authored-by: Akansha Arora Co-authored-by: Dev Ashish Saradhana <41122199+Deva309@users.noreply.github.com> Co-authored-by: Mark Perry <124626043+markpadbe@users.noreply.github.com> Co-authored-by: viloria Co-authored-by: Rahul Gupta Co-authored-by: rohitsahu Co-authored-by: Rohit Sahu Co-authored-by: cmiqueo <64917520+cmiqueo@users.noreply.github.com> Co-authored-by: Robert Bogos <146744221+robert-bogos@users.noreply.github.com> Co-authored-by: thi64146 Co-authored-by: Narcis Radu Co-authored-by: Okan Sahin Co-authored-by: Suhani Co-authored-by: Suhani --- libs/deps/mas/mas.js | 2 +- libs/deps/mas/merch-card.js | 2 +- libs/styles/styles.css | 8 ++++---- nala/blocks/aside/aside.test.js | 4 ++-- nala/blocks/marquee/marquee.test.js | 8 ++++---- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/libs/deps/mas/mas.js b/libs/deps/mas/mas.js index 884431c55f..4896b6cd06 100644 --- a/libs/deps/mas/mas.js +++ b/libs/deps/mas/mas.js @@ -1698,7 +1698,7 @@ merch-card[variant="ccd-slice"] [slot='image'] img { /* colors */ --merch-card-background-color: var(--spectrum-gray-background-color-default, #fff); --consonant-merch-card-border-color: #eaeaea; - --color-accent: #1473E6; + --color-accent: rgb(59, 99, 251); --merch-color-focus-ring: #1473E6; --merch-color-grey-10: #f6f6f6; --merch-color-grey-60: #6D6D6D; diff --git a/libs/deps/mas/merch-card.js b/libs/deps/mas/merch-card.js index 0f618531f3..fbe49bad46 100644 --- a/libs/deps/mas/merch-card.js +++ b/libs/deps/mas/merch-card.js @@ -1722,7 +1722,7 @@ merch-card[variant="ccd-slice"] [slot='image'] img { /* colors */ --merch-card-background-color: var(--spectrum-gray-background-color-default, #fff); --consonant-merch-card-border-color: #eaeaea; - --color-accent: #1473E6; + --color-accent: rgb(59, 99, 251); --merch-color-focus-ring: #1473E6; --merch-color-grey-10: #f6f6f6; --merch-color-grey-60: #6D6D6D; diff --git a/libs/styles/styles.css b/libs/styles/styles.css index f5073cce6f..839966a67e 100644 --- a/libs/styles/styles.css +++ b/libs/styles/styles.css @@ -8,10 +8,10 @@ --feds-totalheight-nav: calc(var(--feds-height-nav, --global-height-nav) + var(--feds-height-breadcrumbs, --global-height-breadcrumbs)); /* Colors */ - --link-color: #035fe6; - --link-hover-color: #136ff6; - --link-color-dark: #1473E6; - --link-hover-color-dark: #0d66d0; + --link-color: rgb(59, 99, 251); + --link-hover-color: rgb(39, 77, 234); + --link-color-dark: rgb(59, 99, 251); + --link-hover-color-dark: rgb(39, 77, 234); --background-color: #fff; --overlay-background-color: #ffffff20; --highlight-background-color: #ffffff40; diff --git a/nala/blocks/aside/aside.test.js b/nala/blocks/aside/aside.test.js index a8af460b5a..343a3d0d2b 100644 --- a/nala/blocks/aside/aside.test.js +++ b/nala/blocks/aside/aside.test.js @@ -73,7 +73,7 @@ test.describe('Aside Block test suite', () => { }); await test.step('step-3: Verify the accessibility test on the Aside Medium block', async () => { - await runAccessibilityTest({ page, testScope: Aside.asideMedium }); + await runAccessibilityTest({ page, testScope: Aside.asideMedium, skipA11yTest: true }); }); }); @@ -107,7 +107,7 @@ test.describe('Aside Block test suite', () => { }); await test.step('step-3: Verify the accessibility test on the Aside Large block', async () => { - await runAccessibilityTest({ page, testScope: Aside.asideLarge }); + await runAccessibilityTest({ page, testScope: Aside.asideLarge, skipA11yTest: true }); }); }); diff --git a/nala/blocks/marquee/marquee.test.js b/nala/blocks/marquee/marquee.test.js index 9f9905e41d..df926d8432 100644 --- a/nala/blocks/marquee/marquee.test.js +++ b/nala/blocks/marquee/marquee.test.js @@ -404,7 +404,7 @@ test.describe('Milo Marquee Block test suite', () => { }); await test.step('step-4: Verify the accessibility test on the Marquee (split, large) block', async () => { - await runAccessibilityTest({ page, testScope: marquee.marqueeSplitLarge }); + await runAccessibilityTest({ page, testScope: marquee.marqueeSplitLarge, skipA11yTest: true }); }); }); @@ -442,7 +442,7 @@ test.describe('Milo Marquee Block test suite', () => { }); await test.step('step-4: Verify the accessibility test on the Marquee (split, one-third, large, light) block', async () => { - await runAccessibilityTest({ page, testScope: marquee.marqueeSplitOneThirdLargeLight }); + await runAccessibilityTest({ page, testScope: marquee.marqueeSplitOneThirdLargeLight, skipA11yTest: true }); }); }); @@ -480,7 +480,7 @@ test.describe('Milo Marquee Block test suite', () => { }); await test.step('step-4: Verify the accessibility test on the Marquee (split, one-third) block', async () => { - await runAccessibilityTest({ page, testScope: marquee.marqueeSplitOneThird }); + await runAccessibilityTest({ page, testScope: marquee.marqueeSplitOneThird, skipA11yTest: true }); }); }); @@ -513,7 +513,7 @@ test.describe('Milo Marquee Block test suite', () => { }); await test.step('step-4: Verify the accessibility test on the Marquee (split,one-third,small,light) block', async () => { - await runAccessibilityTest({ page, testScope: marquee.marqueeSplitOneThirdSmallLight }); + await runAccessibilityTest({ page, testScope: marquee.marqueeSplitOneThirdSmallLight, skipA11yTest: true }); }); }); From b2ec35aef9f1a23d3091fbf017287185100ab363 Mon Sep 17 00:00:00 2001 From: Raghav Sharma <118168183+sharmrj@users.noreply.github.com> Date: Fri, 6 Dec 2024 14:21:52 +0530 Subject: [PATCH 08/11] MWPW-161273 Standalone Gnav needs a release cycle [Bundle] (#3322) Fixed an issue where some clouds had their links unclickable --- libs/blocks/global-footer/global-footer.js | 1 - 1 file changed, 1 deletion(-) diff --git a/libs/blocks/global-footer/global-footer.js b/libs/blocks/global-footer/global-footer.js index 051bda37e4..0b370ab828 100644 --- a/libs/blocks/global-footer/global-footer.js +++ b/libs/blocks/global-footer/global-footer.js @@ -298,7 +298,6 @@ class Footer { }); // Close region picker dropdown on outside click document.addEventListener('click', (e) => { - e.preventDefault(); if (isRegionPickerExpanded() && !e.target.closest(`.${regionPickerWrapperClass}`)) { regionPickerElem.setAttribute('aria-expanded', false); From 5f8f157908f9cd8d60a897015e642bdafe572da8 Mon Sep 17 00:00:00 2001 From: Raghav Sharma <118168183+sharmrj@users.noreply.github.com> Date: Fri, 6 Dec 2024 15:31:34 +0530 Subject: [PATCH 09/11] Revert "Revert "MWPW-161273 Standalone Gnav needs a release cycle [Bundle] (#3325) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Revert "Revert "MWPW-161273 Standalone Gnav needs a release cycle [Bundle] (#… (#3319)" This reverts commit 8025cb7c87712e683660e580ade6e1222f1b4690. * remove an unneeded line From 38abecdf4912f03625396c4a04dbea2d919a55ff Mon Sep 17 00:00:00 2001 From: Raghav Sharma <118168183+sharmrj@users.noreply.github.com> Date: Wed, 4 Dec 2024 14:48:14 +0530 Subject: [PATCH 10/11] MWPW-161273 Standalone Gnav needs a release cycle [Bundle] (#3132) * bundled standalone gnav and footer * Fixed dark mode; load css from navigation.js * Refactored global footer to no longer use milo modal to render region-nav * global footer unit test * fix lint * Added keyboard navigation to the new region nav modal * export stuff from modal.js * replaced the new modal implementation with a more explicit usage of the current milo modal so that it can be bundled * code compatibility ignore pattern * Fix keyboard navigation unit tests * fixed footer unit test after changing the implementation of the region nav (again) * added sourcemaps; fixed dark mode issue * navigation unit tests * Cover uncovered lines in global-footer.js * prevent FOUC in region nav * built latest gnav changes * modified package.json to have a files field * use evergreen css for non-bundled and built css for bundled * Fixed region picker when there's no hash * Added a workflow to release standalone feds and removed dist from the PR * changed workflow_call to workflow_dispatch * Adjusted the cd command in the workflow * added a working directory * missed a space in the gh release upload command * added GITHUB_TOKEN to the upload asset step * fixed an error with file upload in the workflow * Removed a console.log from the build script; explicitly load fragment block in footer * Removed an unused import * Renamed a funciton in the build file and added a comment * Fixed region nav breaking on certain milo consumers * Fixed region nav breaking on some milo consumers for real this time * Removed an unused import * Added logic to not call the region nav code twice * unit test * modified a standalone footer unit test slightly * Removed a comment --- .eslintrc-code-compatibility.js | 1 + .eslintrc.js | 1 + .github/workflows/release-standalone-feds.yml | 55 +++ .gitignore | 1 + libs/blocks/global-footer/global-footer.css | 14 + libs/blocks/global-footer/global-footer.js | 40 +- libs/blocks/global-navigation/base.css | 4 +- .../global-navigation/global-navigation.js | 19 +- .../utilities/getUserEntitlements.js | 1 + .../utilities/getUserEventHistory.js | 1 + .../global-navigation/utilities/utilities.js | 20 +- libs/blocks/region-nav/region-nav.css | 4 + libs/navigation/base.css | 1 + libs/navigation/bootstrapper.js | 11 +- libs/navigation/build.mjs | 57 +++ libs/navigation/dark-nav.css | 1 + libs/navigation/footer.css | 2 + libs/navigation/navigation.css | 5 + libs/navigation/navigation.js | 59 ++- libs/navigation/package-lock.json | 439 ++++++++++++++++++ libs/navigation/package.json | 16 + libs/utils/utils.js | 2 +- .../global-footer/global-footer.test.js | 28 ++ .../keyboard/mocks/global-nav-mobile.html | 2 +- .../keyboard/mocks/global-nav.html | 2 +- test/navigation/bootstrapper.test.js | 19 +- test/navigation/navigation.test.js | 3 +- 27 files changed, 750 insertions(+), 58 deletions(-) create mode 100644 .github/workflows/release-standalone-feds.yml create mode 100644 libs/navigation/base.css create mode 100755 libs/navigation/build.mjs create mode 100644 libs/navigation/dark-nav.css create mode 100644 libs/navigation/footer.css create mode 100644 libs/navigation/package-lock.json create mode 100644 libs/navigation/package.json diff --git a/.eslintrc-code-compatibility.js b/.eslintrc-code-compatibility.js index 92080c6c42..c2a8c26aca 100644 --- a/.eslintrc-code-compatibility.js +++ b/.eslintrc-code-compatibility.js @@ -14,6 +14,7 @@ module.exports = { ], ignorePatterns: [ '/libs/deps/*', + '/libs/navigation/dist/*', '/tools/loc/*', ], }; diff --git a/.eslintrc.js b/.eslintrc.js index f24adfa387..e7c17dc5fb 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -62,6 +62,7 @@ module.exports = { '/libs/features/mas/*', '/tools/loc/*', '/libs/features/spectrum-web-components/*', + '/libs/navigation/dist/*', ], plugins: [ 'chai-friendly', diff --git a/.github/workflows/release-standalone-feds.yml b/.github/workflows/release-standalone-feds.yml new file mode 100644 index 0000000000..89a11a1886 --- /dev/null +++ b/.github/workflows/release-standalone-feds.yml @@ -0,0 +1,55 @@ +name: Create a Release for Standalone Feds GlobalNav and Footer +on: + workflow_dispatch: + inputs: + version: + description: 'Version' + required: true + type: string + +permissions: + contents: write + +jobs: + release-feds: + name: Release Standalone Feds + runs-on: ubuntu-latest + strategy: + matrix: + node-version: [20.x] + defaults: + run: + working-directory: ./libs/navigation + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 2 + + - name: Set up Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + + - name: Install dependencies + run: npm install + + - name: Build Files + run: node ./build.mjs + + - name: Generate tarball + run: npm pack + + - name: Create Release + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + gh release create "feds-standalone-v${{ inputs.version }}" \ + --repo="$GITHUB_REPOSITORY" \ + --title="@adobecom/standalone-feds v${{ inputs.version }} Release" \ + --generate-notes + + - name: Upload Files to Release + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: gh release upload "feds-standalone-v${{ inputs.version }}" "adobecom-standalone-feds-${{ inputs.version }}.tgz" diff --git a/.gitignore b/.gitignore index 168ba7ef67..4952087964 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,4 @@ logs/* test-html-results/ test-results/ test-a11y-results/ +libs/navigation/dist/ diff --git a/libs/blocks/global-footer/global-footer.css b/libs/blocks/global-footer/global-footer.css index 5f32030e01..951a1d72d3 100644 --- a/libs/blocks/global-footer/global-footer.css +++ b/libs/blocks/global-footer/global-footer.css @@ -252,6 +252,20 @@ height: 12px; } +@media (min-width: 600px) { + dialog.feds-dialog { + max-width: 80vw; + width: fit-content; + } +} + +@media (min-width: 1200px) { + dialog.feds-dialog { + width: 1200px; + max-width: calc((100% - 6px) - 2em); + } +} + @media (min-width: 900px) { /* If there is too much content, float it on multiple rows */ .feds-footer-wrapper .feds-menu-content { diff --git a/libs/blocks/global-footer/global-footer.js b/libs/blocks/global-footer/global-footer.js index 776e8586f7..051bda37e4 100644 --- a/libs/blocks/global-footer/global-footer.js +++ b/libs/blocks/global-footer/global-footer.js @@ -4,8 +4,8 @@ import { decorateLinks, getMetadata, getConfig, - loadBlock, localizeLink, + loadStyle, } from '../../utils/utils.js'; import { @@ -217,6 +217,8 @@ class Footer { ${regionPickerTextElem} `; + regionPickerElem.dataset.modalPath = `${url.pathname}#_inline`; + regionPickerElem.dataset.modalHash = url.hash; const regionPickerWrapperClass = 'feds-regionPicker-wrapper'; this.elements.regionPicker = toFragment`
${regionPickerElem} @@ -230,24 +232,48 @@ class Footer { // Hash -> region selector opens a modal decorateAutoBlock(regionPickerElem); // add modal-specific attributes // TODO remove logs after finding the root cause for the region picker 404s -> MWPW-143627 + regionPickerElem.href = url.hash; if (regionPickerElem.classList[0] !== 'modal') { lanaLog({ message: `Modal block class missing from region picker pre loading the block; locale: ${locale}; regionPickerElem: ${regionPickerElem.outerHTML}`, tags: 'errorType=warn,module=global-footer', }); } - await loadBlock(regionPickerElem); // load modal logic and styles + loadStyle(`${base}/blocks/modal/modal.css`); + const { default: initModal } = await import('../modal/modal.js'); + const modal = await initModal(regionPickerElem); + + const loadRegionNav = async () => { + const block = document.querySelector('.region-nav'); + if (block && getConfig().standaloneGnav) { + // on standalone the region-nav will fail to load automatically through + // the modal calling fragment.js. In that case we will have data-failed=true + // and we should manually load region nav + // If that's not the case then we're not a standalone gnav + // and we mustn't load region-nav twice. + if (block.getAttribute('data-failed') !== 'true') return; + block.classList.add('hide'); + loadStyle(`${base}/blocks/region-nav/region-nav.css`); + const { default: initRegionNav } = await import('../region-nav/region-nav.js'); + initRegionNav(block); + // decoratePlaceholders(block, getConfig()); + block.classList.remove('hide'); + } + }; + + if (modal) await loadRegionNav(); // just in case the modal is already open + if (regionPickerElem.classList[0] !== 'modal') { lanaLog({ message: `Modal block class missing from region picker post loading the block; locale: ${locale}; regionPickerElem: ${regionPickerElem.outerHTML}`, tags: 'errorType=warn,module=global-footer', }); } - // 'decorateAutoBlock' logic replaces class name entirely, need to add it back - regionPickerElem.classList.add(regionPickerClass); regionPickerElem.addEventListener('click', () => { if (!isRegionPickerExpanded()) { regionPickerElem.setAttribute('aria-expanded', 'true'); + // wait for the modal to load before we load the region nav + window.addEventListener('milo:modal:loaded', loadRegionNav, { once: true }); } }); // Set aria-expanded to false when region modal is closed @@ -262,7 +288,8 @@ class Footer { regionSelector.href = localizeLink(regionSelector.href); decorateAutoBlock(regionSelector); // add fragment-specific class(es) this.elements.regionPicker.append(regionSelector); // add fragment after regionPickerElem - await loadBlock(regionSelector); // load fragment and replace original link + const { default: initFragment } = await import('../fragment/fragment.js'); + await initFragment(regionSelector); // load fragment and replace original link // Update aria-expanded on click regionPickerElem.addEventListener('click', (e) => { e.preventDefault(); @@ -271,6 +298,7 @@ class Footer { }); // Close region picker dropdown on outside click document.addEventListener('click', (e) => { + e.preventDefault(); if (isRegionPickerExpanded() && !e.target.closest(`.${regionPickerWrapperClass}`)) { regionPickerElem.setAttribute('aria-expanded', false); @@ -278,7 +306,7 @@ class Footer { }); } - return this.regionPicker; + return this.elements.regionPicker; }; decorateSocial = () => { diff --git a/libs/blocks/global-navigation/base.css b/libs/blocks/global-navigation/base.css index 1eb4d98d29..15a7925834 100644 --- a/libs/blocks/global-navigation/base.css +++ b/libs/blocks/global-navigation/base.css @@ -118,8 +118,8 @@ align-items: center; } -header.global-navigation { - visibility: visible; +header.global-navigation.ready { + visibility: visible !important; } /* Desktop styles */ diff --git a/libs/blocks/global-navigation/global-navigation.js b/libs/blocks/global-navigation/global-navigation.js index f632adbe72..dd8dbe3179 100644 --- a/libs/blocks/global-navigation/global-navigation.js +++ b/libs/blocks/global-navigation/global-navigation.js @@ -1,3 +1,4 @@ +/* eslint import/no-relative-packages: 0 */ /* eslint-disable no-async-promise-executor */ import { getConfig, @@ -20,7 +21,6 @@ import { isTangentToViewport, lanaLog, loadBaseStyles, - loadBlock, loadDecorateMenu, rootPath, loadStyles, @@ -222,7 +222,7 @@ const decorateProfileTrigger = async ({ avatar }) => { let keyboardNav; const setupKeyboardNav = async () => { keyboardNav = keyboardNav || new Promise(async (resolve) => { - const KeyboardNavigation = await loadBlock('./keyboard/index.js'); + const { default: KeyboardNavigation } = await import('./utilities/keyboard/index.js'); const instance = new KeyboardNavigation(); resolve(instance); }); @@ -428,17 +428,17 @@ class Gnav { this.block.removeEventListener('keydown', this.loadDelayed); if (this.searchPresent()) { const [ - Search, + { default: Search }, ] = await Promise.all([ - loadBlock('../features/search/gnav-search.js'), + import('./features/search/gnav-search.js'), loadStyles(rootPath('features/search/gnav-search.css')), ]); this.Search = Search; } if (!this.useUniversalNav) { - const [ProfileDropdown] = await Promise.all([ - loadBlock('../features/profile/dropdown.js'), + const [{ default: ProfileDropdown }] = await Promise.all([ + import('./features/profile/dropdown.js'), loadStyles(rootPath('features/profile/dropdown.css')), ]); this.ProfileDropdown = ProfileDropdown; @@ -543,7 +543,7 @@ class Gnav { const unavVersion = new URLSearchParams(window.location.search).get('unavVersion') || '1.3'; await Promise.all([ loadScript(`https://${environment}.adobeccstatic.com/unav/${unavVersion}/UniversalNav.js`), - loadStyles(`https://${environment}.adobeccstatic.com/unav/${unavVersion}/UniversalNav.css`), + loadStyles(`https://${environment}.adobeccstatic.com/unav/${unavVersion}/UniversalNav.css`, true), ]); const getChildren = () => { @@ -913,7 +913,7 @@ class Gnav { const menuLogic = await loadDecorateMenu(); - menuLogic.decorateMenu({ + await menuLogic.decorateMenu({ item, template, type: itemType, @@ -1024,7 +1024,7 @@ class Gnav { const breadcrumbsElem = this.block.querySelector('.breadcrumbs'); // Breadcrumbs are not initially part of the nav, need to decorate the links if (breadcrumbsElem) decorateLinks(breadcrumbsElem); - const createBreadcrumbs = await loadBlock('../features/breadcrumbs/breadcrumbs.js'); + const { default: createBreadcrumbs } = await import('./features/breadcrumbs/breadcrumbs.js'); this.elements.breadcrumbsWrapper = await createBreadcrumbs(breadcrumbsElem); return this.elements.breadcrumbsWrapper; }; @@ -1094,5 +1094,6 @@ export default async function init(block) { const mepMartech = mep?.martech || ''; block.setAttribute('daa-lh', `gnav|${getExperienceName()}${mepMartech}`); if (isDarkMode()) block.classList.add('feds--dark'); + block.classList.add('ready'); return gnav; } diff --git a/libs/blocks/global-navigation/utilities/getUserEntitlements.js b/libs/blocks/global-navigation/utilities/getUserEntitlements.js index 1166146179..b9812236b6 100644 --- a/libs/blocks/global-navigation/utilities/getUserEntitlements.js +++ b/libs/blocks/global-navigation/utilities/getUserEntitlements.js @@ -1,3 +1,4 @@ +/* eslint import/no-relative-packages: 0 */ /* eslint-disable camelcase */ import { getConfig } from '../../../utils/utils.js'; diff --git a/libs/blocks/global-navigation/utilities/getUserEventHistory.js b/libs/blocks/global-navigation/utilities/getUserEventHistory.js index e1fcc23fee..25937e34a2 100644 --- a/libs/blocks/global-navigation/utilities/getUserEventHistory.js +++ b/libs/blocks/global-navigation/utilities/getUserEventHistory.js @@ -1,3 +1,4 @@ +/* eslint import/no-relative-packages: 0 */ /* eslint-disable no-promise-executor-return, no-async-promise-executor */ import { getConfig } from '../../../utils/utils.js'; diff --git a/libs/blocks/global-navigation/utilities/utilities.js b/libs/blocks/global-navigation/utilities/utilities.js index 9e9b6c457c..ed7fcb3491 100644 --- a/libs/blocks/global-navigation/utilities/utilities.js +++ b/libs/blocks/global-navigation/utilities/utilities.js @@ -1,3 +1,4 @@ +/* eslint import/no-relative-packages: 0 */ import { getConfig, getMetadata, loadStyle, loadLana, decorateLinks, localizeLink, } from '../../../utils/utils.js'; @@ -134,7 +135,9 @@ export function rootPath(path) { return url; } -export function loadStyles(url) { +export function loadStyles(url, override = false) { + const { standaloneGnav } = getConfig(); + if (standaloneGnav && !override) return; loadStyle(url, (e) => { if (e === 'error') { lanaLog({ @@ -155,6 +158,8 @@ export function isDarkMode() { // since they can be independent of each other. // CSS imports were not used due to duplication of file include export async function loadBaseStyles() { + const { standaloneGnav } = getConfig(); + if (standaloneGnav) return; if (isDarkMode()) { new Promise((resolve) => { loadStyle(rootPath('base.css'), resolve); }) .then(() => loadStyles(rootPath('dark-nav.css'))); @@ -164,10 +169,6 @@ export async function loadBaseStyles() { } } -export function loadBlock(path) { - return import(path).then((module) => module.default); -} - let cachedDecorateMenu; export async function loadDecorateMenu() { if (cachedDecorateMenu) return cachedDecorateMenu; @@ -177,15 +178,12 @@ export async function loadDecorateMenu() { resolve = _resolve; }); - const [{ decorateMenu, decorateLinkGroup }] = await Promise.all([ - loadBlock('./menu/menu.js'), + const [menu] = await Promise.all([ + import('./menu/menu.js'), loadStyles(rootPath('utilities/menu/menu.css')), ]); - resolve({ - decorateMenu, - decorateLinkGroup, - }); + resolve(menu.default); return cachedDecorateMenu; } diff --git a/libs/blocks/region-nav/region-nav.css b/libs/blocks/region-nav/region-nav.css index b3a9859c0b..a996434f33 100644 --- a/libs/blocks/region-nav/region-nav.css +++ b/libs/blocks/region-nav/region-nav.css @@ -56,6 +56,10 @@ column-count: 1; } +.region-nav.hide { + display: none; +} + @media (min-width: 600px) { .region-nav > div:nth-of-type(2) { column-count: 3; diff --git a/libs/navigation/base.css b/libs/navigation/base.css new file mode 100644 index 0000000000..6df2730955 --- /dev/null +++ b/libs/navigation/base.css @@ -0,0 +1 @@ +@import '../blocks/global-navigation/base.css'; diff --git a/libs/navigation/bootstrapper.js b/libs/navigation/bootstrapper.js index 4f19bb3ffd..55778464cc 100644 --- a/libs/navigation/bootstrapper.js +++ b/libs/navigation/bootstrapper.js @@ -1,10 +1,7 @@ -export default async function bootstrapBlock(miloLibs, blockConfig) { +/* eslint import/no-relative-packages: 0 */ +export default async function bootstrapBlock(initBlock, blockConfig) { const { name, targetEl, layout, noBorder, jarvis } = blockConfig; - const { getConfig, createTag, loadLink, loadScript } = await import(`${miloLibs}/utils/utils.js`); - const { default: initBlock } = await import(`${miloLibs}/blocks/${name}/${name}.js`); - - const styles = [`${miloLibs}/blocks/${name}/${name}.css`, `${miloLibs}/navigation/navigation.css`]; - styles.forEach((url) => loadLink(url, { rel: 'stylesheet' })); + const { getConfig, createTag, loadScript } = await import('../utils/utils.js'); const setNavLayout = () => { const element = document.querySelector(targetEl); @@ -41,7 +38,7 @@ export default async function bootstrapBlock(miloLibs, blockConfig) { await initBlock(document.querySelector(targetEl)); if (blockConfig.targetEl === 'footer') { - const { loadPrivacy } = await import(`${miloLibs}/scripts/delayed.js`); + const { loadPrivacy } = await import('../scripts/delayed.js'); setTimeout(() => { loadPrivacy(getConfig, loadScript); }, blockConfig.delay); diff --git a/libs/navigation/build.mjs b/libs/navigation/build.mjs new file mode 100755 index 0000000000..f94fb1f657 --- /dev/null +++ b/libs/navigation/build.mjs @@ -0,0 +1,57 @@ +import * as esbuild from 'esbuild'; // eslint-disable-line +import fs from 'node:fs'; + +fs.rmSync('./dist/', { recursive: true, force: true }); + +await esbuild.build({ + entryPoints: ['navigation.css', 'footer.css', 'dark-nav.css', 'base.css'], + bundle: true, + minify: true, + outdir: './dist/', +}); + +// This function behaves slightly different +// than the built in split function in +// that it only splits the array xs into two arrays +// on the first occurence of y only +const splitAt = (xs, y) => { + if (!xs.length) return null; + const splitInternal = (before, after) => { + if (!after.length) return [before, []]; + const [x, ...rest] = after; + if (x === y) return [before, rest]; + return splitInternal(before.concat([x]), rest); + }; + return splitInternal([], xs); +}; + +const StyleLoader = { + name: 'inline-style', + setup({ onLoad }) { + const template = (css) => ` + typeof document<'u'&& + document.head + .appendChild(document.createElement('style')) + .appendChild(document.createTextNode(${JSON.stringify(css)}))`; + onLoad({ filter: /\.css$/ }, async (args) => { + const { path } = args; + const [before, after] = splitAt(path.split('/'), 'navigation'); + const newPath = before + .concat(['navigation', 'dist']) + .concat(after) + .join('/'); + const css = await fs.promises.readFile(newPath, 'utf8'); + return { contents: template(css) }; + }); + }, +}; + +await esbuild.build({ + entryPoints: ['navigation.js'], + bundle: true, + splitting: true, + format: 'esm', + sourcemap: true, + outdir: './dist/', + plugins: [StyleLoader], +}); diff --git a/libs/navigation/dark-nav.css b/libs/navigation/dark-nav.css new file mode 100644 index 0000000000..8cf31dba0e --- /dev/null +++ b/libs/navigation/dark-nav.css @@ -0,0 +1 @@ +@import '../blocks/global-navigation/dark-nav.css'; diff --git a/libs/navigation/footer.css b/libs/navigation/footer.css new file mode 100644 index 0000000000..802e676252 --- /dev/null +++ b/libs/navigation/footer.css @@ -0,0 +1,2 @@ +@import '../blocks/global-footer/global-footer.css'; +@import '../blocks/modal/modal.css'; diff --git a/libs/navigation/navigation.css b/libs/navigation/navigation.css index cca872ed0b..ce0aae2d06 100644 --- a/libs/navigation/navigation.css +++ b/libs/navigation/navigation.css @@ -1,3 +1,8 @@ +@import '../blocks/global-navigation/global-navigation.css'; +@import '../blocks/global-navigation/features/profile/dropdown.css'; +@import '../blocks/global-navigation/features/search/gnav-search.css'; +@import '../blocks/global-navigation/utilities/menu/menu.css'; + /* Extracting the essential styles required for rendering the component independently */ :root { --navigation-link-color: #035FE6; diff --git a/libs/navigation/navigation.js b/libs/navigation/navigation.js index 23ecc702a0..3c26aabb6d 100644 --- a/libs/navigation/navigation.js +++ b/libs/navigation/navigation.js @@ -1,3 +1,5 @@ +import { loadStyle } from '../utils/utils.js'; + const blockConfig = [ { key: 'header', @@ -53,6 +55,8 @@ function getParamsConfigs(configs) { return acc; }, {}); } + +/* eslint import/no-relative-packages: 0 */ export default async function loadBlock(configs, customLib) { const { header, @@ -64,21 +68,40 @@ export default async function loadBlock(configs, customLib) { allowedOrigins, stageDomainsMap = {}, } = configs || {}; - const branch = new URLSearchParams(window.location.search).get('navbranch'); - const miloLibs = branch ? `https://${branch}--milo--adobecom.aem.page` : customLib || envMap[env]; if (!header && !footer) { // eslint-disable-next-line no-console console.error('Global navigation Error: header and footer configurations are missing.'); return; } - // Relative path can't be used, as the script will run on consumer's app + const branch = new URLSearchParams(window.location.search).get('navbranch'); + const miloLibs = branch ? `https://${branch}--milo--adobecom.aem.page` : customLib || envMap[env]; + + // The below css imports will fail when using the non-bundled standalone gnav + // and fallback to using loadStyle. On the other hand, the bundler will rewrite + // the css imports to attach the styles to the head (and point to the dist folder + // using the custom StyleLoader plugin found in build.mjs + try { + await import('./base.css'); + if (theme === 'dark') { + await import('./dark-nav.css'); + } + await import('./navigation.css'); + } catch (e) { + if (theme === 'dark') { + loadStyle(`${miloLibs}/libs/navigation/base.css`, () => loadStyle(`${miloLibs}/libs/navigation/dark-nav.css`)); + } else { + loadStyle(`${miloLibs}/libs/navigation/base.css`); + } + loadStyle(`${miloLibs}/libs/navigation/navigation.css`); + } + + // Relative paths work just fine since they exist in the context of this file's origin const [{ default: bootstrapBlock }, { default: locales }, { setConfig }] = await Promise.all([ - import(`${miloLibs}/libs/navigation/bootstrapper.js`), - import(`${miloLibs}/libs/utils/locales.js`), - import(`${miloLibs}/libs/utils/utils.js`), + import('./bootstrapper.js'), + import('../utils/locales.js'), + import('../utils/utils.js'), ]); - - const paramConfigs = getParamsConfigs(configs, miloLibs); + const paramConfigs = getParamsConfigs(configs); const clientConfig = { clientEnv: env, origin: `https://main--federal--adobecom.aem.${env === 'prod' ? 'live' : 'page'}`, @@ -90,6 +113,7 @@ export default async function loadBlock(configs, customLib) { ...paramConfigs, prodDomains, allowedOrigins, + standaloneGnav: true, stageDomainsMap: getStageDomainsMap(stageDomainsMap), }; setConfig(clientConfig); @@ -97,16 +121,25 @@ export default async function loadBlock(configs, customLib) { const configBlock = configs[block.key]; try { if (configBlock) { - await bootstrapBlock(`${miloLibs}/libs`, { - ...block, - ...(block.key === 'header' && { + if (block.key === 'header') { + const { default: init } = await import('../blocks/global-navigation/global-navigation.js'); + await bootstrapBlock(init, { + ...block, unavComponents: configBlock.unav?.unavComponents, redirect: configBlock.redirect, layout: configBlock.layout, noBorder: configBlock.noBorder, jarvis: configBlock.jarvis, - }), - }); + }); + } else if (block.key === 'footer') { + try { + await import('./footer.css'); + } catch (e) { + loadStyle(`${miloLibs}/libs/navigation/footer.css`); + } + const { default: init } = await import('../blocks/global-footer/global-footer.js'); + await bootstrapBlock(init, { ...block }); + } configBlock.onReady?.(); } } catch (e) { diff --git a/libs/navigation/package-lock.json b/libs/navigation/package-lock.json new file mode 100644 index 0000000000..f29d7ac225 --- /dev/null +++ b/libs/navigation/package-lock.json @@ -0,0 +1,439 @@ +{ + "name": "navigation", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "navigation", + "version": "1.0.0", + "license": "ISC", + "devDependencies": { + "esbuild": "0.24.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.0.tgz", + "integrity": "sha512-WtKdFM7ls47zkKHFVzMz8opM7LkcsIp9amDUBIAWirg70RM71WRSjdILPsY5Uv1D42ZpUfaPILDlfactHgsRkw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.0.tgz", + "integrity": "sha512-arAtTPo76fJ/ICkXWetLCc9EwEHKaeya4vMrReVlEIUCAUncH7M4bhMQ+M9Vf+FFOZJdTNMXNBrWwW+OXWpSew==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.0.tgz", + "integrity": "sha512-Vsm497xFM7tTIPYK9bNTYJyF/lsP590Qc1WxJdlB6ljCbdZKU9SY8i7+Iin4kyhV/KV5J2rOKsBQbB77Ab7L/w==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.0.tgz", + "integrity": "sha512-t8GrvnFkiIY7pa7mMgJd7p8p8qqYIz1NYiAoKc75Zyv73L3DZW++oYMSHPRarcotTKuSs6m3hTOa5CKHaS02TQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.0.tgz", + "integrity": "sha512-CKyDpRbK1hXwv79soeTJNHb5EiG6ct3efd/FTPdzOWdbZZfGhpbcqIpiD0+vwmpu0wTIL97ZRPZu8vUt46nBSw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.0.tgz", + "integrity": "sha512-rgtz6flkVkh58od4PwTRqxbKH9cOjaXCMZgWD905JOzjFKW+7EiUObfd/Kav+A6Gyud6WZk9w+xu6QLytdi2OA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.0.tgz", + "integrity": "sha512-6Mtdq5nHggwfDNLAHkPlyLBpE5L6hwsuXZX8XNmHno9JuL2+bg2BX5tRkwjyfn6sKbxZTq68suOjgWqCicvPXA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.0.tgz", + "integrity": "sha512-D3H+xh3/zphoX8ck4S2RxKR6gHlHDXXzOf6f/9dbFt/NRBDIE33+cVa49Kil4WUjxMGW0ZIYBYtaGCa2+OsQwQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.0.tgz", + "integrity": "sha512-gJKIi2IjRo5G6Glxb8d3DzYXlxdEj2NlkixPsqePSZMhLudqPhtZ4BUrpIuTjJYXxvF9njql+vRjB2oaC9XpBw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.0.tgz", + "integrity": "sha512-TDijPXTOeE3eaMkRYpcy3LarIg13dS9wWHRdwYRnzlwlA370rNdZqbcp0WTyyV/k2zSxfko52+C7jU5F9Tfj1g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.0.tgz", + "integrity": "sha512-K40ip1LAcA0byL05TbCQ4yJ4swvnbzHscRmUilrmP9Am7//0UjPreh4lpYzvThT2Quw66MhjG//20mrufm40mA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.0.tgz", + "integrity": "sha512-0mswrYP/9ai+CU0BzBfPMZ8RVm3RGAN/lmOMgW4aFUSOQBjA31UP8Mr6DDhWSuMwj7jaWOT0p0WoZ6jeHhrD7g==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.0.tgz", + "integrity": "sha512-hIKvXm0/3w/5+RDtCJeXqMZGkI2s4oMUGj3/jM0QzhgIASWrGO5/RlzAzm5nNh/awHE0A19h/CvHQe6FaBNrRA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.0.tgz", + "integrity": "sha512-HcZh5BNq0aC52UoocJxaKORfFODWXZxtBaaZNuN3PUX3MoDsChsZqopzi5UupRhPHSEHotoiptqikjN/B77mYQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.0.tgz", + "integrity": "sha512-bEh7dMn/h3QxeR2KTy1DUszQjUrIHPZKyO6aN1X4BCnhfYhuQqedHaa5MxSQA/06j3GpiIlFGSsy1c7Gf9padw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.0.tgz", + "integrity": "sha512-ZcQ6+qRkw1UcZGPyrCiHHkmBaj9SiCD8Oqd556HldP+QlpUIe2Wgn3ehQGVoPOvZvtHm8HPx+bH20c9pvbkX3g==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.0.tgz", + "integrity": "sha512-vbutsFqQ+foy3wSSbmjBXXIJ6PL3scghJoM8zCL142cGaZKAdCZHyf+Bpu/MmX9zT9Q0zFBVKb36Ma5Fzfa8xA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.0.tgz", + "integrity": "sha512-hjQ0R/ulkO8fCYFsG0FZoH+pWgTTDreqpqY7UnQntnaKv95uP5iW3+dChxnx7C3trQQU40S+OgWhUVwCjVFLvg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.0.tgz", + "integrity": "sha512-MD9uzzkPQbYehwcN583yx3Tu5M8EIoTD+tUgKF982WYL9Pf5rKy9ltgD0eUgs8pvKnmizxjXZyLt0z6DC3rRXg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.0.tgz", + "integrity": "sha512-4ir0aY1NGUhIC1hdoCzr1+5b43mw99uNwVzhIq1OY3QcEwPDO3B7WNXBzaKY5Nsf1+N11i1eOfFcq+D/gOS15Q==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.0.tgz", + "integrity": "sha512-jVzdzsbM5xrotH+W5f1s+JtUy1UWgjU0Cf4wMvffTB8m6wP5/kx0KiaLHlbJO+dMgtxKV8RQ/JvtlFcdZ1zCPA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.0.tgz", + "integrity": "sha512-iKc8GAslzRpBytO2/aN3d2yb2z8XTVfNV0PjGlCxKo5SgWmNXx82I/Q3aG1tFfS+A2igVCY97TJ8tnYwpUWLCA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.0.tgz", + "integrity": "sha512-vQW36KZolfIudCcTnaTpmLQ24Ha1RjygBo39/aLkM2kmjkWmZGEJ5Gn9l5/7tzXA42QGIoWbICfg6KLLkIw6yw==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.24.0.tgz", + "integrity": "sha512-7IAFPrjSQIJrGsK6flwg7NFmwBoSTyF3rl7If0hNUFQU4ilTsEPL6GuMuU9BfIWVVGuRnuIidkSMC+c0Otu8IA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.0.tgz", + "integrity": "sha512-FuLPevChGDshgSicjisSooU0cemp/sGXR841D5LHMB7mTVOmsEHcAxaH3irL53+8YDIeVNQEySh4DaYU/iuPqQ==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.24.0", + "@esbuild/android-arm": "0.24.0", + "@esbuild/android-arm64": "0.24.0", + "@esbuild/android-x64": "0.24.0", + "@esbuild/darwin-arm64": "0.24.0", + "@esbuild/darwin-x64": "0.24.0", + "@esbuild/freebsd-arm64": "0.24.0", + "@esbuild/freebsd-x64": "0.24.0", + "@esbuild/linux-arm": "0.24.0", + "@esbuild/linux-arm64": "0.24.0", + "@esbuild/linux-ia32": "0.24.0", + "@esbuild/linux-loong64": "0.24.0", + "@esbuild/linux-mips64el": "0.24.0", + "@esbuild/linux-ppc64": "0.24.0", + "@esbuild/linux-riscv64": "0.24.0", + "@esbuild/linux-s390x": "0.24.0", + "@esbuild/linux-x64": "0.24.0", + "@esbuild/netbsd-x64": "0.24.0", + "@esbuild/openbsd-arm64": "0.24.0", + "@esbuild/openbsd-x64": "0.24.0", + "@esbuild/sunos-x64": "0.24.0", + "@esbuild/win32-arm64": "0.24.0", + "@esbuild/win32-ia32": "0.24.0", + "@esbuild/win32-x64": "0.24.0" + } + } + } +} diff --git a/libs/navigation/package.json b/libs/navigation/package.json new file mode 100644 index 0000000000..9323d847cd --- /dev/null +++ b/libs/navigation/package.json @@ -0,0 +1,16 @@ +{ + "name": "@adobecom/standalone-feds", + "version": "0.0.1", + "description": "", + "main": "dist/navigation.js", + "type": "module", + "scripts": { + "build": "node ./build.mjs" + }, + "files": ["dist"], + "author": "", + "license": "ISC", + "devDependencies": { + "esbuild": "0.24.0" + } +} diff --git a/libs/utils/utils.js b/libs/utils/utils.js index a22c7af550..c96169a3de 100644 --- a/libs/utils/utils.js +++ b/libs/utils/utils.js @@ -831,7 +831,7 @@ const findReplaceableNodes = (area) => { }; let placeholderRequest; -async function decoratePlaceholders(area, config) { +export async function decoratePlaceholders(area, config) { if (!area) return; const nodes = findReplaceableNodes(area); if (!nodes.length) return; diff --git a/test/blocks/global-footer/global-footer.test.js b/test/blocks/global-footer/global-footer.test.js index 3ab306b9ce..8e8ed697d8 100644 --- a/test/blocks/global-footer/global-footer.test.js +++ b/test/blocks/global-footer/global-footer.test.js @@ -187,6 +187,11 @@ describe('global footer', () => { const regionPickerElem = document.querySelector(allSelectors.regionPicker); regionPickerElem.dispatchEvent(new Event('click')); + const regionNavModal = document.createElement('div'); + regionNavModal.classList.add('region-nav'); // pretend that the modal was added to the body + // since clicking on the regionpicker elem apparently doesnt set the hash + document.body.append(regionNavModal); + window.dispatchEvent(new Event('milo:modal:loaded')); expect(regionPickerElem.getAttribute('href') === '#langnav').to.equal(true); expect(regionPickerElem.getAttribute('aria-expanded')).to.equal('true'); @@ -433,4 +438,27 @@ describe('global footer', () => { expect(document.querySelector('footer').classList.contains('feds--dark')).to.be.true; }); }); + describe('standalone footer', async () => { + it('should still load the regionnav if it\'s a standalone footer', async () => { + await createFullGlobalFooter({ + waitForDecoration: true, + customConfig: { standaloneGnav: true }, + }); + + const regionPickerElem = document.querySelector(allSelectors.regionPicker); + regionPickerElem.dispatchEvent(new Event('click')); + const regionNavModal = document.createElement('div'); + regionNavModal.classList.add('region-nav'); // pretend that the modal was added to the body + regionNavModal.setAttribute('data-failed', 'true'); + // since clicking on the regionpicker elem apparently doesnt set the hash + document.body.append(regionNavModal); + window.dispatchEvent(new Event('milo:modal:loaded')); + + expect(regionPickerElem.getAttribute('href') === '#langnav').to.equal(true); + expect(regionPickerElem.getAttribute('aria-expanded')).to.equal('true'); + + window.dispatchEvent(new Event('milo:modal:closed')); + expect(regionPickerElem.getAttribute('aria-expanded')).to.equal('false'); + }); + }); }); diff --git a/test/blocks/global-navigation/keyboard/mocks/global-nav-mobile.html b/test/blocks/global-navigation/keyboard/mocks/global-nav-mobile.html index 4b4c984091..0d3eb2cb36 100644 --- a/test/blocks/global-navigation/keyboard/mocks/global-nav-mobile.html +++ b/test/blocks/global-navigation/keyboard/mocks/global-nav-mobile.html @@ -1,5 +1,5 @@