mirror of
https://github.com/iconify/iconify.git
synced 2025-01-22 14:48:24 +00:00
svg framework: add bg and mask modes, test changing modes
This commit is contained in:
parent
b08d1221e3
commit
40dd0aba48
@ -47,7 +47,8 @@ for (const prefix in propsToAddTo) {
|
|||||||
export function renderBackground(
|
export function renderBackground(
|
||||||
element: IconifyElement,
|
element: IconifyElement,
|
||||||
props: IconifyElementProps,
|
props: IconifyElementProps,
|
||||||
iconData: FullIconifyIcon
|
iconData: FullIconifyIcon,
|
||||||
|
useMask: boolean
|
||||||
): IconifyElement {
|
): IconifyElement {
|
||||||
// Generate data to render
|
// Generate data to render
|
||||||
const renderData = iconToSVG(iconData, {
|
const renderData = iconToSVG(iconData, {
|
||||||
@ -74,14 +75,13 @@ export function renderBackground(
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Update style
|
// Update style
|
||||||
const isMonotone = renderData.body.indexOf('currentColor') !== -1;
|
|
||||||
const url = svgToURL(html);
|
const url = svgToURL(html);
|
||||||
const newStyles: Record<string, string> = {
|
const newStyles: Record<string, string> = {
|
||||||
'--svg': url,
|
'--svg': url,
|
||||||
'width': renderAttribs.width,
|
'width': renderAttribs.width,
|
||||||
'height': renderAttribs.height,
|
'height': renderAttribs.height,
|
||||||
...commonProps,
|
...commonProps,
|
||||||
...(isMonotone ? monotoneProps : coloredProps),
|
...(useMask ? monotoneProps : coloredProps),
|
||||||
};
|
};
|
||||||
if (renderData.inline) {
|
if (renderData.inline) {
|
||||||
newStyles['vertical-align'] = '-0.125em';
|
newStyles['vertical-align'] = '-0.125em';
|
||||||
|
@ -8,7 +8,7 @@ export function propsChanged(
|
|||||||
props1: IconifyElementProps,
|
props1: IconifyElementProps,
|
||||||
props2: IconifyElementProps
|
props2: IconifyElementProps
|
||||||
): boolean {
|
): boolean {
|
||||||
if (props1.name !== props2.name) {
|
if (props1.name !== props2.name || props1.mode !== props2.mode) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,8 +9,13 @@ export const inlineClass = 'iconify-inline';
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Icon render mode
|
* Icon render mode
|
||||||
|
*
|
||||||
|
* 'style' = 'bg' or 'mask', depending on icon content
|
||||||
|
* 'bg' = inline style using `background`
|
||||||
|
* 'mask' = inline style using `mask`
|
||||||
|
* 'inline' = inline SVG.
|
||||||
*/
|
*/
|
||||||
export type IconRenderMode = 'style' | 'inline';
|
export type IconRenderMode = 'style' | 'bg' | 'mask' | 'inline';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Data used to verify if icon is the same
|
* Data used to verify if icon is the same
|
||||||
|
@ -123,16 +123,26 @@ export function scanDOM(rootNode?: ObservedNode, addTempNode = false): void {
|
|||||||
pauseObservingNode(observedNode);
|
pauseObservingNode(observedNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (element.tagName.toUpperCase() !== 'SVG') {
|
||||||
element.tagName.toUpperCase() === 'SVG' ||
|
// Check for one of style modes
|
||||||
props.mode !== 'style'
|
const mode = props.mode;
|
||||||
) {
|
const isMask: boolean | null =
|
||||||
renderInlineSVG(element, props, iconData);
|
mode === 'mask' ||
|
||||||
} else {
|
(mode === 'bg'
|
||||||
renderBackground(element, props, iconData);
|
? false
|
||||||
|
: mode === 'style'
|
||||||
|
? iconData.body.indexOf('currentColor') !== -1
|
||||||
|
: null);
|
||||||
|
|
||||||
|
if (typeof isMask === 'boolean') {
|
||||||
|
renderBackground(element, props, iconData, isMask);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderInlineSVG(element, props, iconData);
|
||||||
|
}
|
||||||
|
|
||||||
// Find all elements
|
// Find all elements
|
||||||
scanRootNode(root).forEach(({ node, props }) => {
|
scanRootNode(root).forEach(({ node, props }) => {
|
||||||
// Check if item already has props
|
// Check if item already has props
|
||||||
|
117
packages/iconify/tests/changing-modes-test.ts
Normal file
117
packages/iconify/tests/changing-modes-test.ts
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
import { iconExists } from '@iconify/core/lib/storage/functions';
|
||||||
|
import {
|
||||||
|
fakeAPI,
|
||||||
|
nextPrefix,
|
||||||
|
setupDOM,
|
||||||
|
waitDOMReady,
|
||||||
|
resetState,
|
||||||
|
mockAPIData,
|
||||||
|
awaitUntil,
|
||||||
|
nextTick,
|
||||||
|
} from './helpers';
|
||||||
|
import { addBodyNode } from '../src/observer/root';
|
||||||
|
import { scanDOM } from '../src/scanner/index';
|
||||||
|
import { elementDataProperty } from '../src/scanner/config';
|
||||||
|
import { initObserver } from '../src/observer';
|
||||||
|
|
||||||
|
describe('Changing render modes', () => {
|
||||||
|
const provider = nextPrefix();
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
fakeAPI(provider);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(resetState);
|
||||||
|
|
||||||
|
it('Various background modes', async () => {
|
||||||
|
const prefix = nextPrefix();
|
||||||
|
const iconName = `@${provider}:${prefix}:home`;
|
||||||
|
|
||||||
|
// Add icon with API
|
||||||
|
expect(iconExists(iconName)).toBe(false);
|
||||||
|
mockAPIData({
|
||||||
|
type: 'icons',
|
||||||
|
provider,
|
||||||
|
prefix,
|
||||||
|
response: {
|
||||||
|
prefix,
|
||||||
|
icons: {
|
||||||
|
home: {
|
||||||
|
body: '<g />',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Setup DOM and wait for it to be ready
|
||||||
|
setupDOM(
|
||||||
|
`<span class="iconify" data-icon="${iconName}" data-mode="style"></span>`
|
||||||
|
);
|
||||||
|
await waitDOMReady();
|
||||||
|
|
||||||
|
// Observe body
|
||||||
|
addBodyNode();
|
||||||
|
initObserver(scanDOM);
|
||||||
|
|
||||||
|
// Check HTML and data
|
||||||
|
expect(document.body.innerHTML).toBe(
|
||||||
|
`<span class="iconify" data-icon="${iconName}" data-mode="style"></span>`
|
||||||
|
);
|
||||||
|
|
||||||
|
// Wait for re-render
|
||||||
|
const placeholder = document.body.childNodes[0] as HTMLSpanElement;
|
||||||
|
const style = placeholder.style;
|
||||||
|
|
||||||
|
await awaitUntil(() => !!style.getPropertyValue('--svg'));
|
||||||
|
|
||||||
|
// Check HTML
|
||||||
|
expect(document.body.innerHTML).toBe(
|
||||||
|
`<span class="iconify iconify--${provider} iconify--${prefix}" data-icon="${iconName}" data-mode="style" style="--svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' preserveAspectRatio='xMidYMid meet' viewBox='0 0 16 16'%3E%3Cg /%3E%3C/svg%3E"); width: 1em; height: 1em; display: inline-block; background-color: transparent; background-repeat: no-repeat; background-size: 100% 100%;"></span>`
|
||||||
|
);
|
||||||
|
|
||||||
|
let data = placeholder[elementDataProperty];
|
||||||
|
expect(data.addedStyles.indexOf('background-image') !== -1).toBe(true);
|
||||||
|
expect(data.addedStyles.indexOf('mask-image') !== -1).toBe(false);
|
||||||
|
expect(style.getPropertyValue('mask-image')).toBe('');
|
||||||
|
|
||||||
|
// Render as mask
|
||||||
|
placeholder.setAttribute('data-mode', 'mask');
|
||||||
|
await awaitUntil(() => !!style.getPropertyValue('mask-image'));
|
||||||
|
expect(document.body.innerHTML).toBe(
|
||||||
|
`<span class="iconify iconify--${provider} iconify--${prefix}" data-icon="${iconName}" data-mode="mask" style="--svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' preserveAspectRatio='xMidYMid meet' viewBox='0 0 16 16'%3E%3Cg /%3E%3C/svg%3E"); width: 1em; height: 1em; display: inline-block; background-color: currentColor; mask-image: var(--svg); mask-repeat: no-repeat; mask-size: 100% 100%;"></span>`
|
||||||
|
);
|
||||||
|
|
||||||
|
data = placeholder[elementDataProperty];
|
||||||
|
expect(data.addedStyles.indexOf('background-image') !== -1).toBe(false);
|
||||||
|
expect(data.addedStyles.indexOf('mask-image') !== -1).toBe(true);
|
||||||
|
expect(style.getPropertyValue('background-image')).toBe('');
|
||||||
|
|
||||||
|
// Re-render as background
|
||||||
|
placeholder.setAttribute('data-mode', 'bg');
|
||||||
|
await awaitUntil(() => !style.getPropertyValue('mask-image'));
|
||||||
|
expect(document.body.innerHTML).toBe(
|
||||||
|
`<span class="iconify iconify--${provider} iconify--${prefix}" data-icon="${iconName}" data-mode="bg" style="--svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' preserveAspectRatio='xMidYMid meet' viewBox='0 0 16 16'%3E%3Cg /%3E%3C/svg%3E"); width: 1em; height: 1em; display: inline-block; background-color: transparent; background-repeat: no-repeat; background-size: 100% 100%;"></span>`
|
||||||
|
);
|
||||||
|
|
||||||
|
data = placeholder[elementDataProperty];
|
||||||
|
expect(data.addedStyles.indexOf('background-image') !== -1).toBe(true);
|
||||||
|
expect(data.addedStyles.indexOf('mask-image') !== -1).toBe(false);
|
||||||
|
expect(style.getPropertyValue('mask-image')).toBe('');
|
||||||
|
|
||||||
|
// Re-render as SVG
|
||||||
|
placeholder.setAttribute('data-mode', 'inline');
|
||||||
|
await awaitUntil(() => document.body.childNodes[0] !== placeholder);
|
||||||
|
expect(document.body.innerHTML).toBe(
|
||||||
|
`<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 16 16" data-icon="${iconName}" data-mode="inline" style="" class="iconify iconify--${provider} iconify--${prefix}"><g></g></svg>`
|
||||||
|
);
|
||||||
|
const svgData = document.body.childNodes[0][elementDataProperty];
|
||||||
|
expect(svgData.mode).toBe('inline');
|
||||||
|
|
||||||
|
// Change to style (should not work!)
|
||||||
|
placeholder.setAttribute('data-mode', 'bg');
|
||||||
|
await awaitUntil(() => svgData.mode !== 'bg');
|
||||||
|
expect(document.body.innerHTML).toBe(
|
||||||
|
`<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 16 16" data-icon="${iconName}" data-mode="inline" style="" class="iconify iconify--${provider} iconify--${prefix}"><g></g></svg>`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
@ -36,10 +36,15 @@ describe('Testing rendering nodes as background', () => {
|
|||||||
|
|
||||||
// Get node and render it
|
// Get node and render it
|
||||||
const { node, props } = items[0];
|
const { node, props } = items[0];
|
||||||
const result = renderBackground(node, props, {
|
const result = renderBackground(
|
||||||
|
node,
|
||||||
|
props,
|
||||||
|
{
|
||||||
...iconDefaults,
|
...iconDefaults,
|
||||||
...data,
|
...data,
|
||||||
});
|
},
|
||||||
|
data.body.indexOf('currentColor') !== -1
|
||||||
|
);
|
||||||
|
|
||||||
// Make sure node did not change
|
// Make sure node did not change
|
||||||
expect(result).toBe(node);
|
expect(result).toBe(node);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user