-
Notifications
You must be signed in to change notification settings - Fork 169
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Container PR for initial TWP features #2407
Changes from all commits
39a27ea
0b4e397
4491118
dc7e023
ed261cd
8b2b97a
2971caa
bf04e5d
cf77779
5e54633
3d11396
b2f8be9
b5a3c8f
2058351
c58efb0
ce11dd2
e6afe56
f0891c4
04ead20
16cb1bb
9d18c9a
56b3a43
7f691fe
cc28d9e
16ab911
cd59186
52c214b
c6460b0
3285bd0
91c148e
e199f2d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,7 +7,24 @@ import '../../deps/merch-card.js'; | |
|
||
const TAG_PATTERN = /^[a-zA-Z0-9_-]+:[a-zA-Z0-9_-]+\/[a-zA-Z0-9_-].*$/; | ||
|
||
const CARD_TYPES = ['segment', 'special-offers', 'plans', 'catalog', 'product', 'inline-heading', 'image', 'mini-compare-chart']; | ||
const SEGMENT = 'segment'; | ||
const SPECIAL_OFFERS = 'special-offers'; | ||
const PLANS = 'plans'; | ||
const CATALOG = 'catalog'; | ||
const PRODUCT = 'product'; | ||
const MINI_COMPARE_CHART = 'mini-compare-chart'; | ||
const TWP = 'twp'; | ||
const CARD_TYPES = [ | ||
SEGMENT, | ||
SPECIAL_OFFERS, | ||
PLANS, | ||
CATALOG, | ||
PRODUCT, | ||
'inline-heading', | ||
'image', | ||
MINI_COMPARE_CHART, | ||
TWP, | ||
]; | ||
|
||
const CARD_SIZES = ['wide', 'super-wide']; | ||
|
||
|
@@ -25,13 +42,9 @@ const HEADING_MAP = { | |
}, | ||
}; | ||
|
||
const MINI_COMPARE_CHART = 'mini-compare-chart'; | ||
const PLANS = 'plans'; | ||
const SEGMENT = 'segment'; | ||
|
||
const INNER_ELEMENTS_SELECTOR = 'h2, h3, h4, h5, p, ul, em'; | ||
|
||
const MULTI_OFFER_CARDS = ['plans', 'product', MINI_COMPARE_CHART]; | ||
const MULTI_OFFER_CARDS = [PLANS, PRODUCT, MINI_COMPARE_CHART, TWP]; | ||
// Force cards to refresh once they become visible so that the footer rows are properly aligned. | ||
const intersectionObserver = new IntersectionObserver((entries) => { | ||
entries.forEach((entry) => { | ||
|
@@ -46,10 +59,10 @@ const getPodType = (styles) => styles?.find((style) => CARD_TYPES.includes(style | |
const isHeadingTag = (tagName) => /^H[2-5]$/.test(tagName); | ||
const isParagraphTag = (tagName) => tagName === 'P'; | ||
|
||
const appendSlot = (slotEls, slotName, merchCard) => { | ||
const appendSlot = (slotEls, slotName, merchCard, nodeName = 'p') => { | ||
if (slotEls.length === 0 || merchCard.variant !== MINI_COMPARE_CHART) return; | ||
const newEl = createTag( | ||
'p', | ||
nodeName, | ||
{ slot: slotName, class: slotName }, | ||
); | ||
slotEls.forEach((e) => { | ||
|
@@ -72,6 +85,68 @@ export async function loadMnemonicList(foreground) { | |
} | ||
} | ||
|
||
function extractQuantitySelect(el) { | ||
const quantitySelectConfig = [...el.querySelectorAll('ul')] | ||
.find((ul) => ul.querySelector('li')?.innerText?.includes('Quantity')); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This does sound a bit error-prone and might lead to confusion, is there any other mechanism to stop this from occurring? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I am not sure we should support the lowercase option. In general, it's a bad idea to depend on the text since it could get translated.. and break down in non-english geos. This will be addressed in the separate PR if you are ok with it. Need to design and agree with GWP on how it will work in future. |
||
const configMarkup = quantitySelectConfig?.querySelector('ul'); | ||
if (!configMarkup) return null; | ||
const config = configMarkup.children; | ||
if (config.length !== 2) return null; | ||
const attributes = {}; | ||
attributes.title = config[0].textContent.trim(); | ||
const values = config[1].textContent.split(',') | ||
.map((value) => value.trim()) | ||
.filter((value) => /^\d*$/.test(value)) | ||
.map((value) => (value === '' ? undefined : Number(value))); | ||
yesil marked this conversation as resolved.
Show resolved
Hide resolved
|
||
quantitySelectConfig.remove(); | ||
if (![3, 4, 5].includes(values.length)) return null; | ||
import('../../deps/merch-quantity-select.js'); | ||
[attributes.min, attributes.max, attributes.step, attributes['default-value'], attributes['max-input']] = values; | ||
const quantitySelect = createTag('merch-quantity-select', attributes); | ||
return quantitySelect; | ||
} | ||
|
||
const parseTwpContent = async (el, merchCard) => { | ||
const quantitySelect = extractQuantitySelect(el); | ||
if (quantitySelect) { | ||
merchCard.append(quantitySelect); | ||
} | ||
let allElements = el?.children[0]?.children[0]?.children; | ||
if (!allElements?.length) return; | ||
allElements = [...allElements]; | ||
const contentGroups = allElements.reduce((acc, curr) => { | ||
if (curr.tagName.toLowerCase() === 'p' && curr.textContent.trim() === '--') { | ||
acc.push([]); | ||
} else { | ||
acc[acc.length - 1].push(curr); | ||
} | ||
return acc; | ||
}, [[]]); | ||
|
||
contentGroups.forEach((group, index) => { | ||
if (index === 0) { // Top section | ||
const headings = group.filter((e) => e.tagName.toLowerCase() === 'h3'); | ||
const topBody = group.filter((e) => e.tagName.toLowerCase() === 'p'); | ||
appendSlot(headings, 'heading-xs', merchCard); | ||
appendSlot(topBody, 'body-xs-top', merchCard); | ||
} else if (index === 1) { // Body section | ||
const content = group.filter((e) => e.tagName.toLowerCase() === 'p' || e.tagName.toLowerCase() === 'ul'); | ||
const bodySlot = createTag('div', { slot: 'body-xs' }, content); | ||
merchCard.append(bodySlot); | ||
} else if (index === 2) { // Footer section | ||
const footerContent = group.filter((e) => ['h5', 'p'].includes(e.tagName.toLowerCase())); | ||
const footer = createTag('div', { slot: 'footer' }, footerContent); | ||
merchCard.append(footer); | ||
} | ||
}); | ||
|
||
const offerSelection = el.querySelector('ul'); | ||
if (offerSelection) { | ||
const { initOfferSelection } = await import('./merch-offer-select.js'); | ||
await initOfferSelection(merchCard, offerSelection); | ||
} | ||
}; | ||
|
||
const parseContent = async (el, merchCard) => { | ||
let bodySlotName = `body-${merchCard.variant !== MINI_COMPARE_CHART ? 'xs' : 'm'}`; | ||
let headingMCount = 0; | ||
|
@@ -201,7 +276,7 @@ const decorateMerchCardLinkAnalytics = (el) => { | |
}; | ||
|
||
const addStock = (merchCard, styles) => { | ||
if (styles.includes('add-stock')) { | ||
if (styles.includes('add-stock') && merchCard.variant !== TWP) { | ||
let stock; | ||
const selector = styles.includes('edu') ? '.merch-offers.stock.edu > *' : '.merch-offers.stock > *'; | ||
const [label, ...rest] = [...document.querySelectorAll(selector)]; | ||
|
@@ -225,27 +300,6 @@ const simplifyHrs = (el) => { | |
}); | ||
}; | ||
|
||
async function extractQuantitySelect(el) { | ||
const quantitySelectConfig = el.querySelector('ul'); | ||
if (!quantitySelectConfig) return null; | ||
const configMarkup = quantitySelectConfig.querySelector('li'); | ||
if (!configMarkup || !configMarkup.textContent.includes('Quantity')) return null; | ||
const config = configMarkup.querySelector('ul').querySelectorAll('li'); | ||
if (config.length !== 2) return null; | ||
const attributes = {}; | ||
attributes.title = config[0].textContent.trim(); | ||
const values = config[1].textContent.split(',') | ||
.map((value) => value.trim()) | ||
.filter((value) => /^\d*$/.test(value)) | ||
.map((value) => (value === '' ? undefined : Number(value))); | ||
if (![3, 4, 5].includes(values.length)) return null; | ||
await import('../../deps/merch-quantity-select.js'); | ||
[attributes.min, attributes.max, attributes.step, attributes['default-value'], attributes['max-input']] = values; | ||
const quantitySelect = createTag('merch-quantity-select', attributes); | ||
quantitySelectConfig.remove(); | ||
return quantitySelect; | ||
} | ||
|
||
const getMiniCompareChartFooterRows = (el) => { | ||
let footerRows = Array.from(el.children).slice(1); | ||
footerRows = footerRows.filter((row) => !row.querySelector('.footer-row-cell')); | ||
|
@@ -289,7 +343,7 @@ const setMiniCompareOfferSlot = (merchCard, offers) => { | |
export default async function init(el) { | ||
if (!el.querySelector(INNER_ELEMENTS_SELECTOR)) return el; | ||
const styles = [...el.classList]; | ||
const cardType = getPodType(styles) || 'product'; | ||
const cardType = getPodType(styles) || PRODUCT; | ||
if (!styles.includes(cardType)) { | ||
styles.push(cardType); | ||
} | ||
|
@@ -344,7 +398,6 @@ export default async function init(el) { | |
); | ||
merchCard.setAttribute('badge-color', badge.badgeColor); | ||
merchCard.setAttribute('badge-text', badge.badgeText); | ||
if (document.querySelector('html').dir === 'rtl') merchCard.setAttribute('is-rtl', 'true'); | ||
merchCard.classList.add('badge-card'); | ||
} | ||
} | ||
|
@@ -372,7 +425,7 @@ export default async function init(el) { | |
} | ||
} | ||
}); | ||
const actionMenuContent = cardType === 'catalog' | ||
const actionMenuContent = cardType === CATALOG | ||
? getActionMenuContent(el) | ||
: null; | ||
if (actionMenuContent) { | ||
|
@@ -397,7 +450,6 @@ export default async function init(el) { | |
imageSlot.appendChild(image); | ||
merchCard.appendChild(imageSlot); | ||
} | ||
parseContent(el, merchCard); | ||
if (!icons || icons.length > 0) { | ||
const iconImgs = Array.from(icons).map((icon) => { | ||
const img = { | ||
|
@@ -421,46 +473,45 @@ export default async function init(el) { | |
merchCard.setAttribute('filters', categories.join(',')); | ||
merchCard.setAttribute('types', types.join(',')); | ||
|
||
const footer = createTag('div', { slot: 'footer' }); | ||
if (ctas) { | ||
if (merchCard.variant === 'mini-compare-chart') { | ||
decorateButtons(ctas, 'button-l'); | ||
} else { | ||
decorateButtons(ctas); | ||
} | ||
const links = ctas.querySelectorAll('a'); | ||
ctas.remove(); | ||
footer.append(...links); | ||
} | ||
merchCard.appendChild(footer); | ||
if (merchCard.variant !== TWP) { | ||
parseContent(el, merchCard); | ||
|
||
if (MULTI_OFFER_CARDS.includes(cardType)) { | ||
if (merchCard.variant === MINI_COMPARE_CHART) { | ||
const miniCompareOffers = createTag('div', { slot: 'offers' }); | ||
merchCard.append(miniCompareOffers); | ||
} | ||
const quantitySelect = await extractQuantitySelect(el, cardType); | ||
const offerSelection = el.querySelector('ul'); | ||
if (offerSelection) { | ||
const { initOfferSelection } = await import('./merch-offer-select.js'); | ||
setMiniCompareOfferSlot(merchCard, undefined); | ||
initOfferSelection(merchCard, offerSelection, quantitySelect); | ||
const footer = createTag('div', { slot: 'footer' }); | ||
if (ctas) { | ||
decorateButtons(ctas, (merchCard.variant === MINI_COMPARE_CHART) ? 'button-l' : undefined); | ||
footer.append(ctas); | ||
} | ||
if (quantitySelect) { | ||
merchCard.appendChild(footer); | ||
|
||
if (MULTI_OFFER_CARDS.includes(cardType)) { | ||
const quantitySelect = extractQuantitySelect(el); | ||
const offerSelection = el.querySelector('ul'); | ||
if (merchCard.variant === MINI_COMPARE_CHART) { | ||
setMiniCompareOfferSlot(merchCard, quantitySelect); | ||
} else { | ||
const bodySlot = merchCard.querySelector('div[slot="body-xs"]'); | ||
bodySlot.append(quantitySelect); | ||
const miniCompareOffers = createTag('div', { slot: 'offers' }); | ||
merchCard.append(miniCompareOffers); | ||
} | ||
if (offerSelection) { | ||
const { initOfferSelection } = await import('./merch-offer-select.js'); | ||
setMiniCompareOfferSlot(merchCard, undefined); | ||
initOfferSelection(merchCard, offerSelection, quantitySelect); | ||
} | ||
if (quantitySelect) { | ||
if (merchCard.variant === MINI_COMPARE_CHART) { | ||
setMiniCompareOfferSlot(merchCard, quantitySelect); | ||
} else { | ||
const bodySlot = merchCard.querySelector('div[slot="body-xs"]'); | ||
bodySlot.append(quantitySelect); | ||
} | ||
} | ||
} | ||
|
||
decorateBlockHrs(merchCard); | ||
simplifyHrs(merchCard); | ||
if (merchCard.classList.contains('has-divider')) merchCard.setAttribute('custom-hr', true); | ||
decorateFooterRows(merchCard, footerRows); | ||
} else { | ||
parseTwpContent(el, merchCard); | ||
} | ||
decorateBlockHrs(merchCard); | ||
simplifyHrs(merchCard); | ||
if (merchCard.classList.contains('has-divider')) { | ||
merchCard.setAttribute('custom-hr', true); | ||
} | ||
decorateFooterRows(merchCard, footerRows); | ||
el.replaceWith(merchCard); | ||
decorateMerchCardLinkAnalytics(merchCard); | ||
return merchCard; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Personal preference, but 3+ parameters become difficult to manage, especially if one needs to be omitted at some point. A config object might be better for maintenance.