diff --git a/libs/features/icons/icons.js b/libs/features/icons/icons.js index a39ff61e06..297e044728 100644 --- a/libs/features/icons/icons.js +++ b/libs/features/icons/icons.js @@ -39,23 +39,6 @@ export const fetchIcons = (config) => new Promise(async (resolve) => { resolve(fetchedIcons); }); -async function decorateToolTip(icon) { - const wrapper = icon.closest('em'); - 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'; - const defaultIcon = 'info-outline'; - icon.className = `icon icon-${defaultIcon} milo-tooltip ${place}`; - icon.dataset.name = defaultIcon; - wrapper.parentElement.replaceChild(icon, wrapper); -} - export function getIconData(icon) { const fedRoot = getFederatedContentRoot(); const name = [...icon.classList].find((c) => c.startsWith('icon-'))?.substring(5); @@ -87,8 +70,15 @@ function filterDuplicatedIcons(icons) { return uniqueIcons; } -export async function decorateIcons(area, icons, config) { +export function handleLegacyToolTip(icons, iconClass = 'icon-info-outline') { + const tooltips = [...icons].filter((icon) => icon.classList.contains('icon-tooltip')); + if (!tooltips.length) return; + tooltips.forEach((icon) => icon.classList.replace('icon-tooltip', iconClass)); +} + +export async function decorateIcons(icons, config) { if (!icons.length) return; + handleLegacyToolTip(icons); const uniqueIcons = filterDuplicatedIcons(icons); if (!uniqueIcons.length) return; preloadInViewIcons(uniqueIcons); @@ -106,8 +96,9 @@ export default async function loadIcons(icons) { const iconsToFetch = new Map(); icons.forEach(async (icon) => { - const isToolTip = icon.classList.contains('icon-tooltip'); - if (isToolTip) decorateToolTip(icon); + if (icon.dataset.tooltip) { + icon.classList.add('milo-tooltip', icon.dataset.tooltipdir); + } const iconName = icon.dataset.name; if (icon.dataset.svgInjected || !iconName) return; if (!federalIcons[iconName] && !iconsToFetch.has(iconName)) { diff --git a/libs/utils/utils.js b/libs/utils/utils.js index c29960c7de..5e3ff48381 100644 --- a/libs/utils/utils.js +++ b/libs/utils/utils.js @@ -793,9 +793,11 @@ const findReplaceableNodes = (area) => { let matchFound = false; if (node.nodeType === Node.TEXT_NODE) { matchFound = regex.test(node.nodeValue); - } else if (node.nodeType === Node.ELEMENT_NODE && node.hasAttribute('href')) { - const hrefValue = node.getAttribute('href'); - matchFound = regex.test(hrefValue); + } else if (node.nodeType === Node.ELEMENT_NODE) { + const attr = node.getAttribute('href') || node.getAttribute('data-tooltip'); + if (attr) { + matchFound = regex.test(attr); + } } if (matchFound) { nodes.push(node); @@ -1250,8 +1252,15 @@ async function resolveInlineFrags(section) { section.preloadLinks = newlyDecoratedSection.preloadLinks; } -export function setIconsIndexClass(icons) { +export function setIconAttrs(icons) { [...icons].forEach((icon) => { + const em = icon.closest('em'); + const conf = em?.textContent.split('|'); + if (em && conf.length) { + icon.dataset.tooltip = conf?.pop()?.trim(); + icon.dataset.tooltipdir = conf?.pop()?.trim().toLowerCase() || 'right'; + em.parentElement.replaceChild(icon, em); + } const parent = icon.parentNode; const children = parent.childNodes; const nodeIndex = [...children].indexOf.call(children, icon); @@ -1303,7 +1312,7 @@ export async function loadArea(area = document) { const allIcons = area.querySelectorAll('span.icon'); if (allIcons.length) { - setIconsIndexClass(allIcons); + setIconAttrs(allIcons); } const sections = decorateSections(area, isDoc); @@ -1320,7 +1329,7 @@ export async function loadArea(area = document) { if (allIcons.length) { const { default: loadIcons, decorateIcons } = await import('../features/icons/icons.js'); - await decorateIcons(area, allIcons, config); + await decorateIcons(allIcons, config); await loadIcons(allIcons); } diff --git a/test/features/icons/icons.test.js b/test/features/icons/icons.test.js index cb3cb994db..0687086d1d 100644 --- a/test/features/icons/icons.test.js +++ b/test/features/icons/icons.test.js @@ -1,10 +1,10 @@ import { readFile } from '@web/test-runner-commands'; import { expect } from '@esm-bundle/chai'; -import sinon, { stub } from 'sinon'; +import { stub } from 'sinon'; import { waitForElement } from '../../helpers/waitfor.js'; -const { default: loadIcons, getIconData } = await import('../../../libs/features/icons/icons.js'); -const { setIconsIndexClass } = await import('../../../libs/utils/utils.js'); +const { default: loadIcons, getIconData, handleLegacyToolTip } = await import('../../../libs/features/icons/icons.js'); +const { setIconAttrs } = await import('../../../libs/utils/utils.js'); const mockRes = ({ payload, status = 200, ok = true } = {}) => new Promise((resolve) => { resolve({ status, @@ -22,33 +22,32 @@ const svgEx = ` `; -describe('Icon Suppprt', () => { - beforeEach(() => { - stub(window, 'fetch').callsFake(() => mockRes({})); - }); - - afterEach(() => { - sinon.restore(); - }); - - it('Replaces span.icon', async () => { +describe('Icon Support', () => { + let fetchStub; + before(async () => { const payload = svgEx; - window.fetch.returns(mockRes({ payload })); - + fetchStub = stub(window, 'fetch').callsFake(() => mockRes({ payload })); icons = document.querySelectorAll('span.icon'); + setIconAttrs(icons); + handleLegacyToolTip(icons, 'icon-play'); icons.forEach((icon) => { const { name } = getIconData(icon); icon.dataset.name = name; }); await loadIcons(icons); + }); + after(() => { + fetchStub.restore(); + }); + + it('Replaces span.icon', async () => { const selector = await waitForElement('span.icon svg'); expect(selector).to.exist; }); it('Sets icon index class', async () => { icons = document.querySelectorAll('span.icon'); - setIconsIndexClass(icons); const secondIconHasIndexClass = icons[2].classList.contains('node-index-last'); expect(secondIconHasIndexClass).to.be.true; }); diff --git a/test/features/icons/mocks/body.html b/test/features/icons/mocks/body.html index 2d908a81ca..5f1413c840 100644 --- a/test/features/icons/mocks/body.html +++ b/test/features/icons/mocks/body.html @@ -12,6 +12,7 @@

This is text w/ an in the middle of the paragraph.

| This is my tooltip text.

| top | This is my tooltip text.

+

| left | This is my tooltip text.