Skip to content
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

MWPW-140452 - Icon authoring in milo using the federal repo and individual SVG assets #3259

Merged
merged 25 commits into from
Dec 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
cdb7351
updated features/icons to pull from federal and allow the icons set t…
ryanmparrish Sep 30, 2024
38bdec5
bettter icon-spacing handling
ryanmparrish Sep 30, 2024
f70f5f3
preload federated.js instead of non used icons.svg on util decorateIc…
ryanmparrish Oct 1, 2024
50b35d4
Updated preload federated as script type not fetch
ryanmparrish Oct 2, 2024
df629ee
Minor clean up based on pr feedback
ryanmparrish Oct 2, 2024
4b6fde8
remove console
ryanmparrish Oct 2, 2024
c696c91
addressed some issues found w/ icons not rending in tables due to rac…
ryanmparrish Oct 3, 2024
bab3770
lana instead of console
ryanmparrish Oct 3, 2024
bac6190
no 100% height
ryanmparrish Oct 3, 2024
ae5f989
coverage
ryanmparrish Oct 4, 2024
6340843
full coverage
ryanmparrish Oct 4, 2024
c2ea4b5
minor cleanup
ryanmparrish Oct 8, 2024
9a392e9
minor cleanup
ryanmparrish Oct 8, 2024
ccfabec
preload first section icons, spread syntax
ryanmparrish Oct 9, 2024
6eebd45
no cons
ryanmparrish Oct 9, 2024
cc8d0a5
Performace refactor - loadIcons to hold most related functionallity. …
ryanmparrish Oct 9, 2024
68a649e
async decTooltips and refactor to not chain func calls
ryanmparrish Oct 10, 2024
fe33191
fix: icon alignment in georouting modal's button
Oct 23, 2024
deb5af5
move render blocking code to utils
saugatmalla Oct 30, 2024
727d649
fix import of test method
saugatmalla Oct 31, 2024
cc1dce7
remove condition that causes error when no icon is in view
saugatmalla Nov 6, 2024
807070d
remove redundant link load
saugatmalla Nov 6, 2024
0d03fb5
remove extra federated.js import
saugatmalla Nov 6, 2024
0d16d86
small change to push branch
Sartxi Nov 14, 2024
d31368a
Fed Icons - Table icon decoration and collapse icon wrapping (#3202)
Sartxi Nov 25, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions libs/blocks/table/table.css
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,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
Loading
Loading