Skip to content

Commit

Permalink
MWPW-140452 - Icon authoring in milo using the federal repo and indiv…
Browse files Browse the repository at this point in the history
…idual SVG assets (adobecom#3259)

* updated features/icons to pull from federal and allow the icons set to be sharepoint authorable

* bettter icon-spacing handling

* preload federated.js instead of non used icons.svg on util decorateIcon()

* Updated preload federated as script type not fetch

* Minor clean up based on pr feedback

* remove console

* addressed some issues found w/ icons not rending in tables due to race condition w/ decorateIcon()

* lana instead of console

* no 100% height

* coverage

* full coverage

* minor cleanup

* minor cleanup

* preload first section icons, spread syntax

* no cons

* Performace refactor - loadIcons to hold most related functionallity. Preloaded inView icons and defered others till postSectionLoad.

* async decTooltips and refactor to not chain func calls

* fix: icon alignment in georouting modal's button

* move render blocking code to utils

* fix import of test method

* remove condition that causes error when no icon is in view

* remove redundant link load

* remove extra federated.js import

* small change to push branch

* Fed Icons - Table icon decoration and collapse icon wrapping (adobecom#3202)

* reselect icons after area is decorated

* fix inline margin issue in table collapse titles

---------

Co-authored-by: Saugat Malla <saugat@TBXTOR-2021MBP16-SaugatMalla.local>
Co-authored-by: Saugat Malla <saugat013@gmail.com>
Co-authored-by: Sartxi <sean.archibeque@gmail.com>
  • Loading branch information
4 people authored Dec 9, 2024
1 parent e9f0bf2 commit 81a5770
Show file tree
Hide file tree
Showing 10 changed files with 188 additions and 81 deletions.
1 change: 1 addition & 0 deletions libs/blocks/table/table.css
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,7 @@
width: 15px;
height: 15px;
cursor: pointer;
margin-inline: unset;
}

.table .section-head-title:hover .icon.expand {
Expand Down
7 changes: 1 addition & 6 deletions libs/blocks/text/text.css
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@
position: relative;
}

.text-block .icon-list-item .icon.margin-right:not(.margin-left) { /* target first node only */
.text-block .icon-list-item .icon.node-index-first {
position: absolute;
inset: 0 100% auto auto;
}
Expand All @@ -122,7 +122,6 @@

.text-block .icon-area {
display: flex;
column-gap: var(--spacing-xs);
}

.text-block p.icon-area { /* NOT <a/> tags with icons in them */
Expand Down Expand Up @@ -218,10 +217,6 @@
max-width: unset;
}

.text-block .icon-area.con-button {
column-gap: unset;
}

.text-block .icon-area picture {
line-height: 0em;
height: inherit; /* Safari + FF bug fix */
Expand Down
1 change: 1 addition & 0 deletions libs/features/georoutingv2/georoutingv2.css
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@
}

.dialog-modal.locale-modal-v2 span.icon {
display: inline;
vertical-align: middle;
}

Expand Down
2 changes: 1 addition & 1 deletion libs/features/georoutingv2/georoutingv2.js
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ function buildContent(currentPage, locale, geoData, locales) {
{ once: true },
);
img.src = `${config.miloLibs || config.codeRoot}/img/georouting/${flagFile}`;
const span = createTag('span', { class: 'icon margin-inline-end' }, img);
const span = createTag('span', { class: 'icon node-index-first' }, img);
const mainAction = createTag('a', {
class: 'con-button blue button-l', lang, role: 'button', 'aria-haspopup': !!locales, 'aria-expanded': false, href: '#',
}, span);
Expand Down
2 changes: 1 addition & 1 deletion libs/features/icons/icons.css
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
border-bottom: none;
}

.milo-tooltip::before {
.milo-tooltip::before {
content: attr(data-tooltip);
position: absolute;
top: 50%;
Expand Down
118 changes: 97 additions & 21 deletions libs/features/icons/icons.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import { getFederatedContentRoot } from '../../utils/federated.js';
import { loadLink, loadStyle } from '../../utils/utils.js';

let fetchedIcons;
let fetched = false;
const federalIcons = {};

async function getSVGsfromFile(path) {
/* c8 ignore next */
Expand All @@ -22,6 +26,7 @@ async function getSVGsfromFile(path) {
return miloIcons;
}

// TODO: remove after all consumers have stopped calling this method
// eslint-disable-next-line no-async-promise-executor
export const fetchIcons = (config) => new Promise(async (resolve) => {
/* c8 ignore next */
Expand All @@ -34,41 +39,112 @@ export const fetchIcons = (config) => new Promise(async (resolve) => {
resolve(fetchedIcons);
});

function decorateToolTip(icon) {
async function decorateToolTip(icon) {
const wrapper = icon.closest('em');
wrapper.className = 'tooltip-wrapper';
if (!wrapper) return;
wrapper.className = 'tooltip-wrapper';
const conf = wrapper.textContent.split('|');
// Text is the last part of a tooltip
const content = conf.pop().trim();
if (!content) return;
icon.dataset.tooltip = content;
// Position is the next to last part of a tooltip
const place = conf.pop()?.trim().toLowerCase() || 'right';
icon.className = `icon icon-info milo-tooltip ${place}`;
const defaultIcon = 'info-outline';
icon.className = `icon icon-${defaultIcon} milo-tooltip ${place}`;
icon.dataset.name = defaultIcon;
wrapper.parentElement.replaceChild(icon, wrapper);
}

export default async function loadIcons(icons, config) {
const iconSVGs = await fetchIcons(config);
if (!iconSVGs) return;
export function getIconData(icon) {
const fedRoot = getFederatedContentRoot();
const name = [...icon.classList].find((c) => c.startsWith('icon-'))?.substring(5);
const path = `${fedRoot}/federal/assets/icons/svgs/${name}.svg`;
return { path, name };
}

function preloadInViewIconResources(config) {
const { base } = config;
loadStyle(`${base}/features/icons/icons.css`);
}

const preloadInViewIcons = async (icons = []) => icons.forEach((icon) => {
const { path } = getIconData(icon);
loadLink(path, { rel: 'preload', as: 'fetch', crossorigin: 'anonymous' });
});

function filterDuplicatedIcons(icons) {
if (!icons.length) return [];
const uniqueIconKeys = new Set();
const uniqueIcons = [];
for (const icon of icons) {
const key = [...icon.classList].find((c) => c.startsWith('icon-'))?.substring(5);
if (!uniqueIconKeys.has(key)) {
uniqueIconKeys.add(key);
uniqueIcons.push(icon);
}
}
return uniqueIcons;
}

export async function decorateIcons(area, icons, config) {
if (!icons.length) return;
const uniqueIcons = filterDuplicatedIcons(icons);
if (!uniqueIcons.length) return;
preloadInViewIcons(uniqueIcons);
preloadInViewIconResources(config);
icons.forEach((icon) => {
const iconName = [...icon.classList].find((c) => c.startsWith('icon-'))?.substring(5);
if (!iconName) return;
icon.dataset.name = iconName;
});
}

export default async function loadIcons(icons) {
const fedRoot = getFederatedContentRoot();
const iconRequests = [];
const iconsToFetch = new Map();

icons.forEach(async (icon) => {
const { classList } = icon;
if (classList.contains('icon-tooltip')) decorateToolTip(icon);
const iconName = icon.classList[1].replace('icon-', '');
const existingIcon = icon.querySelector('svg');
if (!iconSVGs[iconName] || existingIcon) return;
const isToolTip = icon.classList.contains('icon-tooltip');
if (isToolTip) decorateToolTip(icon);
const iconName = icon.dataset.name;
if (icon.dataset.svgInjected || !iconName) return;
if (!federalIcons[iconName] && !iconsToFetch.has(iconName)) {
const url = `${fedRoot}/federal/assets/icons/svgs/${iconName}.svg`;
iconsToFetch.set(iconName, fetch(url)
.then(async (res) => {
if (!res.ok) throw new Error(`Failed to fetch SVG for ${iconName}: ${res.statusText}`);
const text = await res.text();
const parser = new DOMParser();
const svgDoc = parser.parseFromString(text, 'image/svg+xml');
const svgElement = svgDoc.querySelector('svg');
if (!svgElement) {
window.lana?.log(`No SVG element found in fetched content for ${iconName}`);
return;
}
const svgClone = svgElement.cloneNode(true);
svgClone.classList.add('icon-milo', `icon-milo-${iconName}`);
federalIcons[iconName] = svgClone;
})
/* c8 ignore next 3 */
.catch((error) => {
window.lana?.log(`Error fetching SVG for ${iconName}:`, error);
}));
}
iconRequests.push(iconsToFetch.get(iconName));
const parent = icon.parentElement;
if (parent.childNodes.length > 1) {
if (parent.lastChild === icon) {
icon.classList.add('margin-inline-start');
} else if (parent.firstChild === icon) {
icon.classList.add('margin-inline-end');
if (parent.parentElement.tagName === 'LI') parent.parentElement.classList.add('icon-list-item');
} else {
icon.classList.add('margin-inline-start', 'margin-inline-end');
}
if (parent && parent.parentElement.tagName === 'LI') parent.parentElement.classList.add('icon-list-item');
});

await Promise.all(iconRequests);

icons.forEach((icon) => {
const iconName = icon.dataset.name;
if (iconName && federalIcons[iconName] && !icon.dataset.svgInjected) {
const svgClone = federalIcons[iconName].cloneNode(true);
icon.appendChild(svgClone);
icon.dataset.svgInjected = 'true';
}
icon.insertAdjacentHTML('afterbegin', iconSVGs[iconName].outerHTML);
});
}
30 changes: 19 additions & 11 deletions libs/styles/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@
--icon-size-s: 32px;
--icon-size-xs: 24px;
--icon-size-xxs: 16px;
--icon-spacing: 8px;

/* z-index */
--above-all: 9000; /* Used for page tools that overlay page content */
Expand Down Expand Up @@ -349,6 +350,7 @@
line-height: 20px;
min-height: 21px;
padding: 7px 18px 8px;
--icon-spacing: 12px;
}

.xl-button .con-button,
Expand All @@ -358,6 +360,7 @@
line-height: 24px;
min-height: 28px;
padding: 10px 24px 8px;
--icon-spacing: 14px;
}

.xxl-button .con-button,
Expand All @@ -367,6 +370,7 @@
line-height: 27px;
min-height: 27px;
padding: 14px 30px 15px;
--icon-spacing: 14px;
}

.con-button.button-justified {
Expand Down Expand Up @@ -559,19 +563,23 @@ div[data-failed="true"]::before {
color: var(--color-gray-300);
}

span.icon.margin-right { margin-right: 8px; }

span.icon.margin-left { margin-left: 8px; }

span.icon.margin-inline-end { margin-inline-end: 8px; }

span.icon.margin-inline-start { margin-inline-start: 8px; }
span.icon {
width: 1em;
display: inline-block;
margin-inline: var(--icon-spacing);
}

.button-l .con-button span.icon.margin-left,
.con-button.button-l span.icon.margin-left { margin-left: 12px; }
span.icon.node-index-first { margin-inline-start: unset; }
span.icon.node-index-middle { margin-inline: var(--icon-spacing); }
span.icon.node-index-last { margin-inline-end: unset; }
span.icon.node-index-only { margin-inline: unset; }

.button-xl .con-button span.icon.margin-left,
.con-button.button-xl span.icon.margin-left { margin-left: 14px; }
span.icon svg {
height: 1em;
position: relative;
top: .1em;
width: auto;
}

/* Con Block Utils */
.con-block.xs-spacing { padding: var(--spacing-xs) 0; }
Expand Down
43 changes: 28 additions & 15 deletions libs/utils/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -789,16 +789,6 @@ function decorateHeader() {
if (promo?.length) header.classList.add('has-promo');
}

async function decorateIcons(area, config) {
const icons = area.querySelectorAll('span.icon');
if (icons.length === 0) return;
const { base } = config;
loadStyle(`${base}/features/icons/icons.css`);
loadLink(`${base}/img/icons/icons.svg`, { rel: 'preload', as: 'fetch', crossorigin: 'anonymous' });
const { default: loadIcons } = await import('../features/icons/icons.js');
await loadIcons(icons, config);
}

export async function customFetch({ resource, withCacheRules }) {
const options = {};
if (withCacheRules) {
Expand Down Expand Up @@ -1275,9 +1265,8 @@ function decorateDocumentExtras() {
decorateHeader();
}

async function documentPostSectionLoading(config) {
async function documentPostSectionLoading(area, config) {
decorateFooterPromo();

const appendage = getMetadata('title-append');
if (appendage) {
import('../features/title-append/title-append.js').then((module) => module.default(appendage));
Expand Down Expand Up @@ -1341,14 +1330,25 @@ async function resolveInlineFrags(section) {
section.preloadLinks = newlyDecoratedSection.preloadLinks;
}

export function setIconsIndexClass(icons) {
[...icons].forEach((icon) => {
const parent = icon.parentNode;
const children = parent.childNodes;
const nodeIndex = [...children].indexOf.call(children, icon);
let indexClass = (nodeIndex === children.length - 1) ? 'last' : 'middle';
if (nodeIndex === 0) indexClass = 'first';
if (children.length === 1) indexClass = 'only';
icon.classList.add(`node-index-${indexClass}`);
});
}

async function processSection(section, config, isDoc) {
await resolveInlineFrags(section);
const firstSection = section.el.dataset.idx === '0';
const stylePromises = firstSection ? preloadBlockResources(section.blocks) : [];
preloadBlockResources(section.preloadLinks);
await Promise.all([
decoratePlaceholders(section.el, config),
decorateIcons(section.el, config),
]);
const loadBlocks = [...stylePromises];
if (section.preloadLinks.length) {
Expand Down Expand Up @@ -1381,6 +1381,11 @@ export async function loadArea(area = document) {
decorateDocumentExtras();
}

const allIcons = area.querySelectorAll('span.icon');
if (allIcons.length) {
setIconsIndexClass(allIcons);
}

const sections = decorateSections(area, isDoc);

const areaBlocks = [];
Expand All @@ -1393,13 +1398,21 @@ export async function loadArea(area = document) {
});
}

if (allIcons.length) {
const { default: loadIcons, decorateIcons } = await import('../features/icons/icons.js');
const areaIcons = area.querySelectorAll('span.icon');
await decorateIcons(area, areaIcons, config);
await loadIcons(areaIcons);
}

const currentHash = window.location.hash;
if (currentHash) {
scrollToHashedElement(currentHash);
}

if (isDoc) await documentPostSectionLoading(config);

if (isDoc) {
await documentPostSectionLoading(area, config);
}
await loadDeferred(area, areaBlocks, config);
}

Expand Down
Loading

0 comments on commit 81a5770

Please sign in to comment.