Skip to content

Commit

Permalink
MWPW-146211 [MILO][MEP] Option to select all elements (#2976)
Browse files Browse the repository at this point in the history
* 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: markpadbe <[email protected]>
  • Loading branch information
vgoodric and markpadbe authored Oct 3, 2024
1 parent 008a327 commit 7543e27
Show file tree
Hide file tree
Showing 10 changed files with 138 additions and 99 deletions.
18 changes: 14 additions & 4 deletions libs/blocks/fragment/fragment.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,18 @@ const updateFragMap = (fragment, a, href) => {
}
};

const insertInlineFrag = (sections, a, relHref) => {
const insertInlineFrag = (sections, a, relHref, mep, handleMepCommands) => {
// Inline fragments only support one section, other sections are ignored
const fragChildren = [...sections[0].children];
fragChildren.forEach((child) => child.setAttribute('data-path', relHref));
if (a.parentElement.nodeName === 'DIV' && !a.parentElement.attributes.length) {
a.parentElement.replaceWith(...fragChildren);
} else {
a.replaceWith(...fragChildren);
}
fragChildren.forEach((child) => {
child.setAttribute('data-path', relHref);
if (handleMepCommands) mep.commands = handleMepCommands(mep.commands, child);
});
};

function replaceDotMedia(path, doc) {
Expand Down Expand Up @@ -74,7 +77,8 @@ export default async function init(a) {

const path = new URL(a.href).pathname;
if (mep?.fragments?.[path]) {
relHref = mep.handleFragmentCommand(mep?.fragments[path], a);
const { handleFragmentCommand } = await import('../../features/personalization/personalization.js');
relHref = handleFragmentCommand(mep?.fragments[path], a);
if (!relHref) return;
}

Expand Down Expand Up @@ -119,10 +123,16 @@ export default async function init(a) {
const { updateFragDataProps } = await import('../../features/personalization/personalization.js');
updateFragDataProps(a, inline, sections, fragment);
}
let handleMepCommands = false;
if (mep?.commands?.length) {
const { handleCommands } = await import('../../features/personalization/personalization.js');
handleMepCommands = handleCommands;
}
if (inline) {
insertInlineFrag(sections, a, relHref);
insertInlineFrag(sections, a, relHref, mep, handleMepCommands);
} else {
a.parentElement.replaceChild(fragment, a);
if (handleMepCommands) handleMepCommands(mep?.commands, fragment);
await loadArea(fragment);
}
}
Expand Down
5 changes: 3 additions & 2 deletions libs/blocks/global-navigation/utilities/utilities.js
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,7 @@ export async function fetchAndProcessPlainHtml({ url, shouldDecorateLinks = true
const mepGnav = getConfig()?.mep?.inBlock?.['global-navigation'];
const mepFragment = mepGnav?.fragments?.[path];
if (mepFragment && mepFragment.action === 'replace') {
path = mepFragment.target;
path = mepFragment.content;
}
const res = await fetch(path.replace(/(\.html$|$)/, '.plain.html'));
if (res.status !== 200) {
Expand All @@ -338,8 +338,9 @@ export async function fetchAndProcessPlainHtml({ url, shouldDecorateLinks = true
if (mepFragment?.targetManifestId) body.dataset.adobeTargetTestid = mepFragment.targetManifestId;
const commands = mepGnav?.commands;
if (commands?.length) {
/* c8 ignore next 4 */
const { handleCommands, deleteMarkedEls } = await import('../../../features/personalization/personalization.js');
handleCommands(commands, body, true);
handleCommands(commands, body, true, true);
deleteMarkedEls(body);
}
const inlineFrags = [...body.querySelectorAll('a[href*="#_inline"]')];
Expand Down
4 changes: 2 additions & 2 deletions libs/blocks/merch-card-collection/merch-card-collection.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,10 @@ async function getCardsRoot(config, html) {
}

const fetchOverrideCard = (action, config) => new Promise((resolve, reject) => {
fetch(`${localizeLink(overrideUrlOrigin(action?.target))}.plain.html`).then((res) => {
fetch(`${localizeLink(overrideUrlOrigin(action?.content))}.plain.html`).then((res) => {
if (res.ok) {
res.text().then((cardContent) => {
const response = { path: action.target, cardContent: /^<div>(.*)<\/div>$/.exec(cardContent.replaceAll('\n', ''))[1] };
const response = { path: action.content, cardContent: /^<div>(.*)<\/div>$/.exec(cardContent.replaceAll('\n', ''))[1] };
if (config?.mep?.preview) response.manifestId = action.manifestId;
resolve(response);
});
Expand Down
103 changes: 58 additions & 45 deletions libs/features/personalization/personalization.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ const COLUMN_NOT_OPERATOR = 'not ';
const TARGET_EXP_PREFIX = 'target-';
const INLINE_HASH = '_inline';
const PAGE_URL = new URL(window.location.href);
const FLAGS = {
all: 'all',
includeFragments: 'include-fragments',
};

export const TRACKED_MANIFEST_TYPE = 'personalization';

Expand Down Expand Up @@ -159,7 +163,7 @@ export function replacePlaceholders(value, placeholders) {
return val;
}

export const createContent = (el, content, manifestId, targetManifestId, action, modifiers) => {
export const createContent = (el, { content, manifestId, targetManifestId, action, modifiers }) => {
if (action === 'replace') {
addIds(el, manifestId, targetManifestId);
}
Expand Down Expand Up @@ -190,19 +194,19 @@ export const createContent = (el, content, manifestId, targetManifestId, action,
};

const COMMANDS = {
[COMMANDS_KEYS.remove]: ({ el, target, manifestId }) => {
if (target === 'false') return;
[COMMANDS_KEYS.remove]: (el, { content, manifestId }) => {
if (content === 'false') return;
if (manifestId) {
el.dataset.removedManifestId = manifestId;
return;
}
el.classList.add(CLASS_EL_DELETE);
},
[COMMANDS_KEYS.replace]: ({ el, target, modifiers, manifestId, targetManifestId }) => {
[COMMANDS_KEYS.replace]: (el, cmd) => {
if (!el || el.classList.contains(CLASS_EL_REPLACE)) return;
el.insertAdjacentElement(
'beforebegin',
createContent(el, target, manifestId, targetManifestId, 'replace', modifiers),
createContent(el, cmd),
);
},
};
Expand Down Expand Up @@ -319,27 +323,17 @@ function normalizeKeys(obj) {
}, {});
}

const querySelector = (el, selector, all = false) => {
try {
return all ? el.querySelectorAll(selector) : el.querySelector(selector);
} catch (e) {
/* eslint-disable-next-line no-console */
log('Invalid selector: ', selector);
return null;
}
};
function registerInBlockActions(cmd, manifestId, targetManifestId) {
const { action, target, selector } = cmd;
const command = { action, target, manifestId, targetManifestId };

const blockAndSelector = selector.substring(IN_BLOCK_SELECTOR_PREFIX.length).trim().split(/\s+/);
function registerInBlockActions(command) {
const blockAndSelector = command.selector.substring(IN_BLOCK_SELECTOR_PREFIX.length)
.trim().split(/\s+/);
const [blockName] = blockAndSelector;

const config = getConfig();
config.mep.inBlock ??= {};
config.mep.inBlock[blockName] ??= {};

let blockSelector;
if (blockAndSelector.length === 1) delete command.selector;
if (blockAndSelector.length > 1) {
blockSelector = blockAndSelector.slice(1).join(' ');
command.selector = blockSelector;
Expand All @@ -351,14 +345,14 @@ function registerInBlockActions(cmd, manifestId, targetManifestId) {

// eslint-disable-next-line no-restricted-syntax
for (const key in fragments) {
if (fragments[key].target === blockSelector) fragments[key] = command;
if (fragments[key].content === blockSelector) fragments[key] = command;
}
fragments[blockSelector] = command;

blockSelector = normalizePath(blockSelector);
// eslint-disable-next-line no-restricted-syntax
for (const key in fragments) {
if (fragments[key].target === blockSelector) fragments[key] = command;
if (fragments[key].content === blockSelector) fragments[key] = command;
}
fragments[blockSelector] = command;
return;
Expand Down Expand Up @@ -439,24 +433,34 @@ export function modifyNonFragmentSelector(selector) {
};
}

function getSelectedElement({ selector: sel, rootEl }) {
function getSelectedElements(sel, rootEl, forceRootEl) {
const root = forceRootEl ? rootEl : document;
const selector = sel.trim();
if (!selector) return {};

if (getSelectorType(selector) === 'fragment') {
try {
const fragment = document.querySelector(
const fragments = root.querySelectorAll(
`a[href*="${normalizePath(selector, false)}"], a[href*="${normalizePath(selector, true)}"]`,
);
if (fragment) return { el: fragment.parentNode };
return {};
return { els: fragments, modifiers: [FLAGS.all, FLAGS.includeFragments] };
} catch (e) {
/* c8 ignore next */
return {};
return { els: [], modifiers: [] };
}
}
const { modifiedSelector, modifiers } = modifyNonFragmentSelector(selector);
return { el: querySelector(rootEl || document, modifiedSelector), modifiers };
let els;
try {
els = root.querySelectorAll(modifiedSelector);
} catch (e) {
/* eslint-disable-next-line no-console */
log('Invalid selector: ', selector);
return null;
}
if (modifiers.includes(FLAGS.all) || !els.length) return { els, modifiers };
els = [els[0]];
return { els, modifiers };
}
const addHash = (url, newHash) => {
if (!newHash) return url;
Expand Down Expand Up @@ -484,29 +488,39 @@ export const updateFragDataProps = (a, inline, sections, fragment) => {
}
};

export function handleCommands(commands, rootEl = document, forceInline = false) {
export function handleCommands(commands, rootEl, forceInline = false, forceRootEl = false) {
commands.forEach((cmd) => {
const { manifestId, targetManifestId, action, selector, target: trgt } = cmd;
const target = forceInline ? addHash(trgt, INLINE_HASH) : trgt;
const { action, content, selector } = cmd;
cmd.content = forceInline ? addHash(content, INLINE_HASH) : content;
if (selector.startsWith(IN_BLOCK_SELECTOR_PREFIX)) {
registerInBlockActions(cmd, manifestId, targetManifestId);
registerInBlockActions(cmd);
cmd.selectorType = IN_BLOCK_SELECTOR_PREFIX;
return;
}
const { el, modifiers } = getSelectedElement({ selector, rootEl });
const { els, modifiers } = getSelectedElements(selector, rootEl, forceRootEl);
cmd.modifiers = modifiers;

if (!el || (!(action in COMMANDS) && !(action in CREATE_CMDS))) return;
els?.forEach((el) => {
if (!el || (!(action in COMMANDS) && !(action in CREATE_CMDS))
|| (rootEl && !rootEl.contains(el))) return;

if (action in COMMANDS) {
COMMANDS[action]({
el, target, manifestId, targetManifestId, action, modifiers,
});
return;
if (action in COMMANDS) {
COMMANDS[action](el, cmd);
return;
}
const insertAnchor = getSelectorType(selector) === 'fragment' ? el.parentElement : el;
insertAnchor?.insertAdjacentElement(
CREATE_CMDS[action],
createContent(el, cmd),
);
});
if ((els.length && !cmd.modifiers.includes(FLAGS.all))
|| !cmd.modifiers.includes(FLAGS.includeFragments)) {
cmd.completed = true;
}
el?.insertAdjacentElement(
CREATE_CMDS[action],
createContent(el, target, manifestId, targetManifestId, action, modifiers),
);
});
return commands.filter((cmd) => !cmd.completed
&& cmd.selectorType !== IN_BLOCK_SELECTOR_PREFIX);
}

const getVariantInfo = (line, variantNames, variants, manifestPath, fTargetId) => {
Expand Down Expand Up @@ -537,7 +551,7 @@ const getVariantInfo = (line, variantNames, variants, manifestPath, fTargetId) =
action,
selector,
pageFilter,
target: line[vn],
content: line[vn],
selectorType: getSelectorType(selector),
manifestId,
targetManifestId,
Expand Down Expand Up @@ -986,7 +1000,7 @@ export async function applyPers(manifests, postLCP = false) {
addIds(main, manifestId, targetManifestId);
}

if (!postLCP) handleCommands(config.mep.commands);
if (!postLCP) config.mep.commands = handleCommands(config.mep.commands);
deleteMarkedEls();

const pznList = results.filter((r) => (r.experiment?.manifestType === TRACKED_MANIFEST_TYPE));
Expand Down Expand Up @@ -1048,7 +1062,6 @@ export async function init(enablements = {}) {
const config = getConfig();
if (!postLCP) {
config.mep = {
handleFragmentCommand,
updateFragDataProps,
preview: (mepButton !== 'off'
&& (config.env?.name !== 'prod' || mepParam || mepParam === '' || mepButton)),
Expand Down
14 changes: 14 additions & 0 deletions test/blocks/fragment/fragment.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,20 @@ const config = {
contentRoot: `${window.location.origin}${getLocale(locales).prefix}`,
decorateArea,
locales,
mep: {
commands: [
{
action: 'remove',
selector: 'aside.large p:nth-child(1):has(picture) #_include-fragments',
pageFilter: '',
content: 'true',
selectorType: 'other',
manifestId: 'manifest.json',
targetManifestId: false,
modifiers: ['include-fragments'],
},
],
},
};
setConfig(config);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -171,12 +171,12 @@ describe('Merch Cards', async () => {
{
action: 'replace',
manifestId: 'promo1.json',
target: '/override-photoshop',
content: '/override-photoshop',
},
{
action: 'replace',
manifestId: 'promo2.json',
target: '/override-express',
content: '/override-express',
},
],
},
Expand Down
Loading

0 comments on commit 7543e27

Please sign in to comment.