2
0
mirror of https://github.com/iconify/iconify.git synced 2025-01-06 07:20:40 +00:00

Add onLoad callback to components, export buildIcon, add onLoad event to components, fix customisations, publish new versions

This commit is contained in:
Vjacheslav Trushkin 2021-05-11 23:27:13 +03:00
parent 46ae993b95
commit 144604ce2b
54 changed files with 677 additions and 219 deletions

View File

@ -1,12 +1,12 @@
{
"name": "@iconify/core",
"version": "1.0.0",
"version": "1.0.1",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@iconify/core",
"version": "1.0.0",
"version": "1.0.1",
"license": "(Apache-2.0 OR GPL-2.0)",
"dependencies": {
"@cyberalien/redundancy": "^1.1.0",

View File

@ -2,7 +2,7 @@
"name": "@iconify/core",
"description": "Reusable files used by multiple Iconify packages",
"author": "Vjacheslav Trushkin <cyberalien@gmail.com> (https://iconify.design)",
"version": "1.0.0",
"version": "1.0.1",
"license": "(Apache-2.0 OR GPL-2.0)",
"bugs": "https://github.com/iconify/iconify/issues",
"homepage": "https://iconify.design/",

View File

@ -21,13 +21,13 @@ Iconify SVG framework is designed to be as easy to use as possible.
Add this line to your page to load Iconify SVG framework (you can add it to `<head>` section of the page or before `</body>`):
```html
<script src="https://code.iconify.design/2/2.0.0/iconify.min.js"></script>
<script src="https://code.iconify.design/2/2.0.1/iconify.min.js"></script>
```
or
```html
<script src="https://cdn.jsdelivr.net/npm/@iconify/iconify@2.0.0/dist/iconify.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@iconify/iconify@2.0.1/dist/iconify.min.js"></script>
```
or, if you are building a project with something like WebPack or Rollup, you can include the script by installing `@iconify/iconify` as a dependency and importing it in your project:

View File

@ -1,18 +1,18 @@
{
"name": "@iconify/iconify",
"version": "2.0.0",
"version": "2.0.1",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@iconify/iconify",
"version": "2.0.0",
"version": "2.0.1",
"license": "(Apache-2.0 OR GPL-2.0)",
"dependencies": {
"cross-fetch": "^3.0.6"
},
"devDependencies": {
"@iconify/core": "^1.0.0",
"@iconify/core": "^1.0.1",
"@iconify/types": "^1.0.6",
"@microsoft/api-extractor": "^7.12.0",
"@rollup/plugin-buble": "^0.21.3",
@ -62,9 +62,9 @@
"dev": true
},
"node_modules/@iconify/core": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@iconify/core/-/core-1.0.0.tgz",
"integrity": "sha512-jzGZQMOqoPpKFZ4K4dQ6gNcDqALoJE02FMExm+kcN4vp2GJ5JKCccYxJLBWTmR23vVeZzpxlCtL3KSKjUfe2Kw==",
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@iconify/core/-/core-1.0.1.tgz",
"integrity": "sha512-gbPClcrRJ7sIKgwcEPLUaT1u8PzpOGdsCM3O63wJa5FYosC3ZZBymqR1LFT6MSiPWGlF2XowabzoHS8HaICEpg==",
"dev": true,
"dependencies": {
"@cyberalien/redundancy": "^1.1.0",
@ -2180,9 +2180,9 @@
"dev": true
},
"@iconify/core": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@iconify/core/-/core-1.0.0.tgz",
"integrity": "sha512-jzGZQMOqoPpKFZ4K4dQ6gNcDqALoJE02FMExm+kcN4vp2GJ5JKCccYxJLBWTmR23vVeZzpxlCtL3KSKjUfe2Kw==",
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@iconify/core/-/core-1.0.1.tgz",
"integrity": "sha512-gbPClcrRJ7sIKgwcEPLUaT1u8PzpOGdsCM3O63wJa5FYosC3ZZBymqR1LFT6MSiPWGlF2XowabzoHS8HaICEpg==",
"dev": true,
"requires": {
"@cyberalien/redundancy": "^1.1.0",

View File

@ -2,7 +2,7 @@
"name": "@iconify/iconify",
"description": "Unified SVG framework with over 70,000 icons to choose from",
"author": "Vjacheslav Trushkin <cyberalien@gmail.com> (https://iconify.design)",
"version": "2.0.0",
"version": "2.0.1",
"license": "(Apache-2.0 OR GPL-2.0)",
"main": "./dist/iconify.min.js",
"types": "./dist/iconify.d.ts",
@ -29,7 +29,7 @@
"cross-fetch": "^3.0.6"
},
"devDependencies": {
"@iconify/core": "^1.0.0",
"@iconify/core": "^1.0.1",
"@iconify/types": "^1.0.6",
"@microsoft/api-extractor": "^7.12.0",
"@rollup/plugin-buble": "^0.21.3",

View File

@ -2,7 +2,8 @@ import { IconifyJSON } from '@iconify/types';
import { stringToIcon } from '@iconify/core/lib/icon/name';
import {
IconifyIconCustomisations,
fullCustomisations,
defaults,
mergeCustomisations,
} from '@iconify/core/lib/customisations';
import {
storageFunctions,
@ -39,7 +40,7 @@ function buildIcon(
}
// Clean up customisations
const changes = fullCustomisations(customisations);
const changes = mergeCustomisations(defaults, customisations);
// Get data
return iconToSVG(iconData, changes);
@ -63,17 +64,17 @@ function generateIcon(
const iconName = stringToIcon(name);
// Clean up customisations
const changes = fullCustomisations(customisations);
const changes = mergeCustomisations(defaults, customisations);
// Get data
return (renderIcon(
return renderIcon(
{
name: iconName,
},
changes,
iconData,
returnString
) as unknown) as SVGElement | string | null;
) as unknown as SVGElement | string | null;
}
/**
@ -217,10 +218,10 @@ if (typeof document !== 'undefined' && typeof window !== 'undefined') {
IconifyPreload: IconifyJSON[] | IconifyJSON;
}
if (
((_window as unknown) as WindowWithIconifyPreload).IconifyPreload !==
(_window as unknown as WindowWithIconifyPreload).IconifyPreload !==
void 0
) {
const preload = ((_window as unknown) as WindowWithIconifyPreload)
const preload = (_window as unknown as WindowWithIconifyPreload)
.IconifyPreload;
const err = 'Invalid IconifyPreload syntax.';
if (typeof preload === 'object' && preload !== null) {

View File

@ -1,7 +1,8 @@
import { FullIconifyIcon } from '@iconify/core/lib/icon';
import {
IconifyIconCustomisations,
fullCustomisations,
mergeCustomisations,
defaults,
} from '@iconify/core/lib/customisations';
import { iconToSVG } from '@iconify/core/lib/builder';
import { replaceIDs } from '@iconify/core/lib/builder/ids';
@ -30,7 +31,10 @@ export function renderIcon(
return returnString ? '' : null;
}
const data = iconToSVG(iconData, fullCustomisations(customisations));
const data = iconToSVG(
iconData,
mergeCustomisations(defaults, customisations)
);
// Placeholder properties
const placeholderElement = placeholder.element;

View File

@ -1,17 +1,17 @@
{
"name": "@iconify/react",
"version": "3.0.0-alpha.0",
"version": "3.0.0-alpha.1",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@iconify/react",
"version": "3.0.0-alpha.0",
"version": "3.0.0-alpha.1",
"license": "MIT",
"devDependencies": {
"@babel/preset-env": "^7.13.15",
"@babel/preset-react": "^7.13.13",
"@iconify/core": "^1.0.0",
"@iconify/core": "^1.0.1",
"@iconify/types": "^1.0.6",
"@microsoft/api-extractor": "^7.13.5",
"@rollup/plugin-buble": "^0.21.3",
@ -1381,9 +1381,9 @@
"dev": true
},
"node_modules/@iconify/core": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@iconify/core/-/core-1.0.0.tgz",
"integrity": "sha512-jzGZQMOqoPpKFZ4K4dQ6gNcDqALoJE02FMExm+kcN4vp2GJ5JKCccYxJLBWTmR23vVeZzpxlCtL3KSKjUfe2Kw==",
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@iconify/core/-/core-1.0.1.tgz",
"integrity": "sha512-gbPClcrRJ7sIKgwcEPLUaT1u8PzpOGdsCM3O63wJa5FYosC3ZZBymqR1LFT6MSiPWGlF2XowabzoHS8HaICEpg==",
"dev": true,
"dependencies": {
"@cyberalien/redundancy": "^1.1.0",
@ -10661,9 +10661,9 @@
"dev": true
},
"@iconify/core": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@iconify/core/-/core-1.0.0.tgz",
"integrity": "sha512-jzGZQMOqoPpKFZ4K4dQ6gNcDqALoJE02FMExm+kcN4vp2GJ5JKCccYxJLBWTmR23vVeZzpxlCtL3KSKjUfe2Kw==",
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@iconify/core/-/core-1.0.1.tgz",
"integrity": "sha512-gbPClcrRJ7sIKgwcEPLUaT1u8PzpOGdsCM3O63wJa5FYosC3ZZBymqR1LFT6MSiPWGlF2XowabzoHS8HaICEpg==",
"dev": true,
"requires": {
"@cyberalien/redundancy": "^1.1.0",

View File

@ -2,7 +2,7 @@
"name": "@iconify/react",
"description": "Iconify icon component for React.",
"author": "Vjacheslav Trushkin",
"version": "3.0.0-alpha.0",
"version": "3.0.0-alpha.1",
"license": "MIT",
"bugs": "https://github.com/iconify/iconify/issues",
"homepage": "https://iconify.design/",
@ -26,7 +26,7 @@
"devDependencies": {
"@babel/preset-env": "^7.13.15",
"@babel/preset-react": "^7.13.13",
"@iconify/core": "^1.0.0",
"@iconify/core": "^1.0.1",
"@iconify/types": "^1.0.6",
"@microsoft/api-extractor": "^7.13.5",
"@rollup/plugin-buble": "^0.21.3",

View File

@ -18,6 +18,7 @@ import {
IconifyBuilderFunctions,
builderFunctions,
} from '@iconify/core/lib/builder/functions';
import type { IconifyIconBuildResult } from '@iconify/core/lib/builder';
import { fullIcon, IconifyIcon } from '@iconify/core/lib/icon';
// Modules
@ -65,6 +66,8 @@ import type {
// Properties
import type {
RawIconCustomisations,
IconifyIconOnLoad,
IconifyIconCustomisations,
IconifyIconProps,
IconProps,
@ -90,7 +93,7 @@ export {
// JSON stuff
export { IconifyIcon, IconifyJSON, IconifyIconName };
// Customisations
// Customisations and icon props
export {
IconifyIconCustomisations,
IconifyIconSize,
@ -98,6 +101,7 @@ export {
IconifyVerticalIconAlignment,
IconifyIconProps,
IconProps,
IconifyIconOnLoad,
};
// API
@ -113,6 +117,9 @@ export {
PartialIconifyAPIConfig,
};
// Builder functions
export { RawIconCustomisations, IconifyIconBuildResult };
/* Browser cache */
export { IconifyBrowserCacheType };
@ -162,6 +169,11 @@ export const calculateSize = builderFunctions.calculateSize;
*/
export const replaceIDs = builderFunctions.replaceIDs;
/**
* Build SVG
*/
export const buildIcon = builderFunctions.buildIcon;
/* API functions */
/**
* Load icons
@ -228,10 +240,10 @@ if (typeof document !== 'undefined' && typeof window !== 'undefined') {
IconifyPreload: IconifyJSON[] | IconifyJSON;
}
if (
((_window as unknown) as WindowWithIconifyPreload).IconifyPreload !==
(_window as unknown as WindowWithIconifyPreload).IconifyPreload !==
void 0
) {
const preload = ((_window as unknown) as WindowWithIconifyPreload)
const preload = (_window as unknown as WindowWithIconifyPreload)
.IconifyPreload;
const err = 'Invalid IconifyPreload syntax.';
if (typeof preload === 'object' && preload !== null) {
@ -262,10 +274,10 @@ if (typeof document !== 'undefined' && typeof window !== 'undefined') {
IconifyProviders: Record<string, PartialIconifyAPIConfig>;
}
if (
((_window as unknown) as WindowWithIconifyProviders)
.IconifyProviders !== void 0
(_window as unknown as WindowWithIconifyProviders).IconifyProviders !==
void 0
) {
const providers = ((_window as unknown) as WindowWithIconifyProviders)
const providers = (_window as unknown as WindowWithIconifyProviders)
.IconifyProviders;
if (typeof providers === 'object' && providers !== null) {
for (let key in providers) {
@ -402,6 +414,9 @@ class IconComponent extends React.Component<
this._abortLoading();
this._icon = icon;
this._setData(data);
if (this.props.onLoad) {
this.props.onLoad(icon);
}
}
}

View File

@ -1,15 +1,22 @@
import type { HTMLProps, RefObject } from 'react';
import type { IconifyIcon } from '@iconify/types';
import type { IconifyIconCustomisations as IconCustomisations } from '@iconify/core/lib/customisations';
import type { IconifyIconCustomisations as RawIconCustomisations } from '@iconify/core/lib/customisations';
export { RawIconCustomisations };
// Allow rotation to be string
/**
* Icon customisations
*/
export type IconifyIconCustomisations = IconCustomisations & {
export type IconifyIconCustomisations = RawIconCustomisations & {
rotate?: string | number;
};
/**
* Callback for when icon has been loaded (only triggered for icons loaded from API)
*/
export type IconifyIconOnLoad = (name: string) => void;
/**
* Icon properties
*/
@ -26,6 +33,9 @@ export interface IconifyIconProps extends IconifyIconCustomisations {
// Unique id, used as base for ids for shapes. Use it to get consistent ids for server side rendering
id?: string;
// Callback to call when icon data has been loaded. Used only for icons loaded from API
onLoad?: IconifyIconOnLoad;
}
/**

View File

@ -2,7 +2,10 @@ import type { SVGProps } from 'react';
import React from 'react';
import type { IconifyIcon } from '@iconify/types';
import type { FullIconCustomisations } from '@iconify/core/lib/customisations';
import { defaults } from '@iconify/core/lib/customisations';
import {
defaults,
mergeCustomisations,
} from '@iconify/core/lib/customisations';
import {
flipFromString,
alignmentFromString,
@ -51,7 +54,7 @@ export const render = (
const defaultProps = inline ? inlineDefaults : defaults;
// Get all customisations
const customisations = merge(
const customisations = mergeCustomisations(
defaultProps,
props as IconifyIconCustomisations
) as FullIconCustomisations;
@ -71,15 +74,27 @@ export const render = (
// Get element properties
for (let key in props) {
const value = props[key];
if (value === void 0) {
continue;
}
switch (key) {
// Properties to ignore
case 'icon':
case 'style':
case 'children':
case 'onLoad':
case '_ref':
case '_inline':
break;
// Boolean attributes
case 'inline':
case 'hFlip':
case 'vFlip':
customisations[key] =
value === true || value === 'true' || value === 1;
break;
// Flip as string: 'horizontal,vertical'
case 'flip':
if (typeof value === 'string') {

View File

@ -5,8 +5,7 @@ import { mockAPIData } from '@iconify/core/lib/api/modules/mock';
import { provider, nextPrefix } from './load';
const iconData = {
body:
'<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
body: '<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
width: 24,
height: 24,
};
@ -16,6 +15,8 @@ describe('Rendering icon', () => {
const prefix = nextPrefix();
const name = 'render-test';
const iconName = `@${provider}:${prefix}:${name}`;
let onLoadCalled = false;
mockAPIData({
provider,
prefix,
@ -45,7 +46,16 @@ describe('Rendering icon', () => {
expect(iconExists(iconName)).toEqual(true);
// Render component
const component = renderer.create(<Icon icon={iconName} />);
const component = renderer.create(
<Icon
icon={iconName}
onLoad={(name) => {
expect(name).toEqual(iconName);
expect(onLoadCalled).toEqual(false);
onLoadCalled = true;
}}
/>
);
const tree = component.toJSON();
expect(tree).toMatchObject({
@ -67,6 +77,9 @@ describe('Rendering icon', () => {
children: null,
});
// Make sure onLoad has been called
expect(onLoadCalled).toEqual(true);
done();
});
});
@ -75,6 +88,8 @@ describe('Rendering icon', () => {
const prefix = nextPrefix();
const name = 'mock-test';
const iconName = `@${provider}:${prefix}:${name}`;
let onLoadCalled = false;
mockAPIData({
provider,
prefix,
@ -88,6 +103,9 @@ describe('Rendering icon', () => {
// Icon should not have loaded yet
expect(iconExists(iconName)).toEqual(false);
// onLoad should not have been called yet
expect(onLoadCalled).toEqual(false);
// Send icon data
next();
@ -123,6 +141,9 @@ describe('Rendering icon', () => {
children: null,
});
// onLoad should have been called
expect(onLoadCalled).toEqual(true);
done();
}, 0);
}, 0);
@ -133,7 +154,16 @@ describe('Rendering icon', () => {
expect(iconExists(iconName)).toEqual(false);
// Render component
const component = renderer.create(<Icon icon={iconName} />);
const component = renderer.create(
<Icon
icon={iconName}
onLoad={(name) => {
expect(name).toEqual(iconName);
expect(onLoadCalled).toEqual(false);
onLoadCalled = true;
}}
/>
);
const tree = component.toJSON();
// Should render placeholder
@ -142,6 +172,9 @@ describe('Rendering icon', () => {
props: {},
children: null,
});
// onLoad should not have been called yet
expect(onLoadCalled).toEqual(false);
});
test('missing icon', (done) => {
@ -184,7 +217,14 @@ describe('Rendering icon', () => {
expect(iconExists(iconName)).toEqual(false);
// Render component
const component = renderer.create(<Icon icon={iconName}></Icon>);
const component = renderer.create(
<Icon
icon={iconName}
onLoad={() => {
throw new Error('onLoad called for empty icon!');
}}
></Icon>
);
const tree = component.toJSON();
// Should render placeholder

View File

@ -5,15 +5,13 @@ import { mockAPIData } from '@iconify/core/lib/api/modules/mock';
import { provider, nextPrefix } from './load';
const iconData = {
body:
'<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
body: '<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
width: 24,
height: 24,
};
const iconData2 = {
body:
'<path d="M19.031 4.281l-11 11l-.687.719l.687.719l11 11l1.438-1.438L10.187 16L20.47 5.719z" fill="currentColor"/>',
body: '<path d="M19.031 4.281l-11 11l-.687.719l.687.719l11 11l1.438-1.438L10.187 16L20.47 5.719z" fill="currentColor"/>',
width: 32,
height: 32,
};
@ -25,6 +23,26 @@ describe('Rendering icon', () => {
const name2 = 'changing-prop2';
const iconName = `@${provider}:${prefix}:${name}`;
const iconName2 = `@${provider}:${prefix}:${name2}`;
let onLoadCalled = ''; // Name of icon from last onLoad call
const onLoad = (name) => {
// onLoad should be called only once per icon
switch (name) {
// First onLoad call
case iconName:
expect(onLoadCalled).toEqual('');
break;
// Second onLoad call
case iconName2:
expect(onLoadCalled).toEqual(iconName);
break;
default:
throw new Error(`Unexpected onLoad('${name}') call`);
}
onLoadCalled = name;
};
mockAPIData({
provider,
@ -39,6 +57,9 @@ describe('Rendering icon', () => {
// Icon should not have loaded yet
expect(iconExists(iconName)).toEqual(false);
// onLoad should not have been called yet
expect(onLoadCalled).toEqual('');
// Send icon data
next();
@ -74,7 +95,13 @@ describe('Rendering icon', () => {
children: null,
});
component.update(<Icon icon={iconName2} />);
// onLoad should have been called
expect(onLoadCalled).toEqual(iconName);
// Change property
component.update(
<Icon icon={iconName2} onLoad={onLoad} />
);
}, 0);
}, 0);
},
@ -128,6 +155,9 @@ describe('Rendering icon', () => {
children: null,
});
// onLoad should have been called for second icon
expect(onLoadCalled).toEqual(iconName2);
done();
}, 0);
}, 0);
@ -138,7 +168,9 @@ describe('Rendering icon', () => {
expect(iconExists(iconName)).toEqual(false);
// Render component
const component = renderer.create(<Icon icon={iconName} />);
const component = renderer.create(
<Icon icon={iconName} onLoad={onLoad} />
);
const tree = component.toJSON();
// Should render placeholder
@ -147,6 +179,9 @@ describe('Rendering icon', () => {
props: {},
children: null,
});
// onLoad should not have been called yet
expect(onLoadCalled).toEqual('');
});
test('changing icon property while loading', (done) => {

View File

@ -3,15 +3,22 @@ import { Icon, InlineIcon } from '../../dist/iconify';
import renderer from 'react-test-renderer';
const iconData = {
body:
'<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
body: '<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
width: 24,
height: 24,
};
describe('Creating component using object', () => {
test('basic icon', () => {
const component = renderer.create(<Icon icon={iconData} />);
const component = renderer.create(
<Icon
icon={iconData}
onLoad={() => {
// Should be called only for icons loaded from API
throw new Error('onLoad called for object!');
}}
/>
);
const tree = component.toJSON();
expect(tree).toMatchObject({

View File

@ -3,8 +3,7 @@ import { Icon } from '../../dist/iconify';
import renderer from 'react-test-renderer';
const iconData = {
body:
'<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
body: '<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
width: 24,
height: 24,
};
@ -20,12 +19,12 @@ describe('Inline attribute', () => {
});
test('false string', () => {
// "false" = true
// "false" should be ignored
const component = renderer.create(
<Icon icon={iconData} inline="false" />
);
const tree = component.toJSON();
expect(tree.props.style.verticalAlign).toStrictEqual('-0.125em');
expect(tree.props.style.verticalAlign).toEqual(void 0);
});
});

View File

@ -3,8 +3,7 @@ import { InlineIcon } from '../../dist/iconify';
import renderer from 'react-test-renderer';
const iconData = {
body:
'<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
body: '<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
width: 24,
height: 24,
};
@ -80,9 +79,9 @@ describe('Flip', () => {
});
test('shorthand and boolean', () => {
// 'flip' is processed after 'hFlip', overwriting value
// 'flip' is processed after 'hFlip' because of order of elements in object, overwriting value
const component = renderer.create(
<InlineIcon icon={iconData} flip="horizontal" hFlip={false} />
<InlineIcon icon={iconData} hFlip={false} flip="horizontal" />
);
const tree = component.toJSON();
const body = tree.props.dangerouslySetInnerHTML.__html;

View File

@ -3,13 +3,21 @@ import { Icon } from '../../dist/offline';
import renderer from 'react-test-renderer';
const iconData = {
body:
'<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
body: '<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
width: 24,
height: 24,
};
describe('Inline attribute', () => {
test('boolean true', () => {
const component = renderer.create(
<Icon icon={iconData} inline={true} />
);
const tree = component.toJSON();
expect(tree.props.style.verticalAlign).toStrictEqual('-0.125em');
});
test('string', () => {
const component = renderer.create(
<Icon icon={iconData} inline="true" />
@ -19,13 +27,22 @@ describe('Inline attribute', () => {
expect(tree.props.style.verticalAlign).toStrictEqual('-0.125em');
});
test('false', () => {
const component = renderer.create(
<Icon icon={iconData} inline={false} />
);
const tree = component.toJSON();
expect(tree.props.style.verticalAlign).toEqual(void 0);
});
test('false string', () => {
// "false" = true
// "false" should be ignored
const component = renderer.create(
<Icon icon={iconData} inline="false" />
);
const tree = component.toJSON();
expect(tree.props.style.verticalAlign).toStrictEqual('-0.125em');
expect(tree.props.style.verticalAlign).toEqual(void 0);
});
});

View File

@ -3,8 +3,7 @@ import { InlineIcon } from '../../dist/offline';
import renderer from 'react-test-renderer';
const iconData = {
body:
'<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
body: '<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
width: 24,
height: 32,
};
@ -80,9 +79,9 @@ describe('Flip', () => {
});
test('shorthand and boolean', () => {
// 'flip' is processed after 'hFlip', overwriting value
// 'flip' is processed after 'hFlip' because of order of elements in object, overwriting value
const component = renderer.create(
<InlineIcon icon={iconData} flip="horizontal" hFlip={false} />
<InlineIcon icon={iconData} hFlip={false} flip="horizontal" />
);
const tree = component.toJSON();
const body = tree.props.dangerouslySetInnerHTML.__html;

View File

@ -1,7 +1,9 @@
.DS_Store
rollup.config.js
tsconfig.json
api-extractor*.json
build.js
copy.js
node_modules
build
src

View File

@ -1,16 +1,16 @@
{
"name": "@iconify/svelte",
"version": "2.0.0-alpha.4",
"version": "2.0.0-alpha.6",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@iconify/svelte",
"version": "2.0.0-alpha.4",
"version": "2.0.0-alpha.6",
"license": "MIT",
"devDependencies": {
"@babel/preset-env": "^7.14.0",
"@iconify/core": "^1.0.0-rc.3",
"@iconify/core": "^1.0.1",
"@iconify/types": "^1.0.6",
"@microsoft/api-extractor": "^7.15.0",
"@rollup/plugin-commonjs": "^16.0.0",
@ -1484,9 +1484,9 @@
"dev": true
},
"node_modules/@iconify/core": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@iconify/core/-/core-1.0.0.tgz",
"integrity": "sha512-jzGZQMOqoPpKFZ4K4dQ6gNcDqALoJE02FMExm+kcN4vp2GJ5JKCccYxJLBWTmR23vVeZzpxlCtL3KSKjUfe2Kw==",
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@iconify/core/-/core-1.0.1.tgz",
"integrity": "sha512-gbPClcrRJ7sIKgwcEPLUaT1u8PzpOGdsCM3O63wJa5FYosC3ZZBymqR1LFT6MSiPWGlF2XowabzoHS8HaICEpg==",
"dev": true,
"dependencies": {
"@cyberalien/redundancy": "^1.1.0",
@ -9800,9 +9800,9 @@
"dev": true
},
"@iconify/core": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@iconify/core/-/core-1.0.0.tgz",
"integrity": "sha512-jzGZQMOqoPpKFZ4K4dQ6gNcDqALoJE02FMExm+kcN4vp2GJ5JKCccYxJLBWTmR23vVeZzpxlCtL3KSKjUfe2Kw==",
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@iconify/core/-/core-1.0.1.tgz",
"integrity": "sha512-gbPClcrRJ7sIKgwcEPLUaT1u8PzpOGdsCM3O63wJa5FYosC3ZZBymqR1LFT6MSiPWGlF2XowabzoHS8HaICEpg==",
"dev": true,
"requires": {
"@cyberalien/redundancy": "^1.1.0",

View File

@ -2,7 +2,7 @@
"name": "@iconify/svelte",
"description": "Iconify icon component for Svelte.",
"author": "Vjacheslav Trushkin",
"version": "2.0.0-alpha.4",
"version": "2.0.0-alpha.6",
"license": "MIT",
"bugs": "https://github.com/iconify/iconify/issues",
"homepage": "https://github.com/iconify/iconify",
@ -27,7 +27,7 @@
},
"devDependencies": {
"@babel/preset-env": "^7.14.0",
"@iconify/core": "^1.0.0-rc.3",
"@iconify/core": "^1.0.1",
"@iconify/types": "^1.0.6",
"@microsoft/api-extractor": "^7.15.0",
"@rollup/plugin-commonjs": "^16.0.0",

View File

@ -236,10 +236,10 @@ if (typeof document !== 'undefined' && typeof window !== 'undefined') {
IconifyPreload: IconifyJSON[] | IconifyJSON;
}
if (
((_window as unknown) as WindowWithIconifyPreload).IconifyPreload !==
(_window as unknown as WindowWithIconifyPreload).IconifyPreload !==
void 0
) {
const preload = ((_window as unknown) as WindowWithIconifyPreload)
const preload = (_window as unknown as WindowWithIconifyPreload)
.IconifyPreload;
const err = 'Invalid IconifyPreload syntax.';
if (typeof preload === 'object' && preload !== null) {
@ -270,10 +270,10 @@ if (typeof document !== 'undefined' && typeof window !== 'undefined') {
IconifyProviders: Record<string, PartialIconifyAPIConfig>;
}
if (
((_window as unknown) as WindowWithIconifyProviders)
.IconifyProviders !== void 0
(_window as unknown as WindowWithIconifyProviders).IconifyProviders !==
void 0
) {
const providers = ((_window as unknown) as WindowWithIconifyProviders)
const providers = (_window as unknown as WindowWithIconifyProviders)
.IconifyProviders;
if (typeof providers === 'object' && providers !== null) {
for (let key in providers) {
@ -378,13 +378,13 @@ export function checkIconState(
}
// Icon data is available
abortLoading();
if (state.name !== icon) {
state.name = icon;
if (onload && !state.destroyed) {
onload(icon);
}
}
abortLoading();
return data;
}

View File

@ -1,17 +1,50 @@
// Types
export type { IconifyJSON } from '@iconify/types';
export type { IconifyIcon } from '@iconify/core/lib/icon';
export type {
/**
* Export required types
*/
// Function sets
export {
IconifyStorageFunctions,
IconifyBuilderFunctions,
IconifyBrowserCacheFunctions,
IconifyAPIFunctions,
IconifyAPIInternalFunctions,
} from './functions';
// JSON stuff
export { IconifyIcon, IconifyJSON, IconifyIconName } from './functions';
// Customisations
export {
IconifyIconCustomisations,
IconifyIconSize,
IconifyHorizontalIconAlignment,
IconifyVerticalIconAlignment,
} from '@iconify/core/lib/customisations';
IconifyIconProps,
IconProps,
} from './functions';
// Types from props.ts
export type { IconifyIconCustomisations, IconProps } from './props';
// API
export {
IconifyAPIConfig,
IconifyIconLoaderCallback,
IconifyIconLoaderAbort,
IconifyAPIInternalStorage,
IconifyAPIModule,
GetAPIConfig,
IconifyAPIPrepareQuery,
IconifyAPISendQuery,
PartialIconifyAPIConfig,
} from './functions';
// Component
// Builder functions
export { RawIconCustomisations, IconifyIconBuildResult } from './functions';
// Browser cache
export { IconifyBrowserCacheType } from './functions';
// Component and params
export { default as Icon } from './Icon.svelte';
export { IconifyIconOnLoad } from './functions';
// Functions
export { enableCache, disableCache } from './functions';

View File

@ -1,5 +1,8 @@
import type { IconifyIcon } from '@iconify/types';
import { defaults } from '@iconify/core/lib/customisations';
import {
defaults,
mergeCustomisations,
} from '@iconify/core/lib/customisations';
import {
flipFromString,
alignmentFromString,
@ -37,7 +40,10 @@ export function render(
// Properties
props: IconProps
): RenderResult {
const customisations = merge(defaults, props as typeof defaults);
const customisations = mergeCustomisations(
defaults,
props as typeof defaults
);
const componentProps = merge(svgDefaults) as Record<string, unknown>;
// Create style if missing
@ -46,6 +52,9 @@ export function render(
// Get element properties
for (let key in props) {
const value = props[key as keyof typeof props] as unknown;
if (value === void 0) {
continue;
}
switch (key) {
// Properties to ignore
case 'icon':
@ -53,6 +62,14 @@ export function render(
case 'onLoad':
break;
// Boolean attributes
case 'inline':
case 'hFlip':
case 'vFlip':
customisations[key] =
value === true || value === 'true' || value === 1;
break;
// Flip as string: 'horizontal,vertical'
case 'flip':
if (typeof value === 'string') {

View File

@ -37,4 +37,15 @@ describe('Dimensions', () => {
expect(node.getAttribute('height')).toEqual('24');
expect(node.getAttribute('width')).toEqual('24');
});
test('invalid values', () => {
const component = render(Icon, {
icon: iconData,
height: null,
width: void 0,
});
const node = component.container.querySelector('svg');
expect(node.getAttribute('height')).toEqual('1em');
expect(node.getAttribute('width')).toEqual('1em');
});
});

View File

@ -2,8 +2,7 @@ import { render } from '@testing-library/svelte';
import { Icon } from '../../dist/iconify';
const iconData = {
body:
'<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
body: '<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
width: 24,
height: 24,
};
@ -34,11 +33,11 @@ describe('Inline attribute', () => {
});
test('false string', () => {
// "false" = true
// "false" should be ignored
const component = render(Icon, { icon: iconData, inline: 'false' });
const node = component.container.querySelector('svg');
const style = node.style;
expect(style.verticalAlign).toEqual('-0.125em');
expect(style.verticalAlign).toEqual('');
});
});

View File

@ -2,8 +2,7 @@ import { render } from '@testing-library/svelte';
import { Icon } from '../../dist/iconify';
const iconData = {
body:
'<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
body: '<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
width: 24,
height: 32,
};
@ -86,11 +85,11 @@ describe('Flip', () => {
});
test('shorthand and boolean', () => {
// 'flip' is processed after 'hFlip', overwriting value
// 'flip' is processed after 'hFlip' because of order of elements in object, overwriting value
const component = render(Icon, {
icon: iconData,
flip: 'horizontal',
hFlip: false,
flip: 'horizontal',
});
const node = component.container.querySelector('svg');
@ -103,7 +102,6 @@ describe('Flip', () => {
});
test('shorthand and boolean as string', () => {
// 'flip' is processed after 'hFlip', overwriting value
const component = render(Icon, {
icon: iconData,
flip: 'vertical',
@ -118,7 +116,6 @@ describe('Flip', () => {
});
test('wrong case', () => {
// 'flip' is processed after 'hFlip', overwriting value
const component = render(Icon, {
icon: iconData,
vflip: true,

View File

@ -2,8 +2,7 @@ import { render } from '@testing-library/svelte';
import { Icon } from '../../dist/offline';
const iconData = {
body:
'<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
body: '<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
width: 24,
height: 24,
};
@ -34,11 +33,11 @@ describe('Inline attribute', () => {
});
test('false string', () => {
// "false" = true
// "false" should be ignored
const component = render(Icon, { icon: iconData, inline: 'false' });
const node = component.container.querySelector('svg');
const style = node.style;
expect(style.verticalAlign).toEqual('-0.125em');
expect(style.verticalAlign).toEqual('');
});
});

View File

@ -2,8 +2,7 @@ import { render } from '@testing-library/svelte';
import { Icon } from '../../dist/offline';
const iconData = {
body:
'<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
body: '<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
width: 24,
height: 32,
};
@ -86,11 +85,11 @@ describe('Flip', () => {
});
test('shorthand and boolean', () => {
// 'flip' is processed after 'hFlip', overwriting value
// 'flip' is processed after 'hFlip' because of order of elements in object, overwriting value
const component = render(Icon, {
icon: iconData,
flip: 'horizontal',
hFlip: false,
flip: 'horizontal',
});
const node = component.container.querySelector('svg');
@ -103,7 +102,6 @@ describe('Flip', () => {
});
test('shorthand and boolean as string', () => {
// 'flip' is processed after 'hFlip', overwriting value
const component = render(Icon, {
icon: iconData,
flip: 'vertical',
@ -118,7 +116,6 @@ describe('Flip', () => {
});
test('wrong case', () => {
// 'flip' is processed after 'hFlip', overwriting value
const component = render(Icon, {
icon: iconData,
vflip: true,

View File

@ -1,15 +1,15 @@
{
"name": "@iconify/vue",
"version": "3.0.0-dev",
"version": "3.0.0-alpha.1",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@iconify/vue",
"version": "3.0.0-dev",
"version": "3.0.0-alpha.1",
"license": "MIT",
"devDependencies": {
"@iconify/core": "^1.0.0",
"@iconify/core": "^1.0.1",
"@iconify/types": "^1.0.6",
"@microsoft/api-extractor": "^7.12.0",
"@rollup/plugin-buble": "^0.21.3",
@ -547,9 +547,9 @@
}
},
"node_modules/@iconify/core": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@iconify/core/-/core-1.0.0.tgz",
"integrity": "sha512-jzGZQMOqoPpKFZ4K4dQ6gNcDqALoJE02FMExm+kcN4vp2GJ5JKCccYxJLBWTmR23vVeZzpxlCtL3KSKjUfe2Kw==",
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@iconify/core/-/core-1.0.1.tgz",
"integrity": "sha512-gbPClcrRJ7sIKgwcEPLUaT1u8PzpOGdsCM3O63wJa5FYosC3ZZBymqR1LFT6MSiPWGlF2XowabzoHS8HaICEpg==",
"dev": true,
"dependencies": {
"@cyberalien/redundancy": "^1.1.0",
@ -24398,9 +24398,9 @@
}
},
"@iconify/core": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@iconify/core/-/core-1.0.0.tgz",
"integrity": "sha512-jzGZQMOqoPpKFZ4K4dQ6gNcDqALoJE02FMExm+kcN4vp2GJ5JKCccYxJLBWTmR23vVeZzpxlCtL3KSKjUfe2Kw==",
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@iconify/core/-/core-1.0.1.tgz",
"integrity": "sha512-gbPClcrRJ7sIKgwcEPLUaT1u8PzpOGdsCM3O63wJa5FYosC3ZZBymqR1LFT6MSiPWGlF2XowabzoHS8HaICEpg==",
"dev": true,
"requires": {
"@cyberalien/redundancy": "^1.1.0",

View File

@ -2,7 +2,7 @@
"name": "@iconify/vue",
"description": "Iconify icon component for Vue 3.",
"author": "Vjacheslav Trushkin",
"version": "3.0.0-alpha.0",
"version": "3.0.0-alpha.1",
"license": "MIT",
"bugs": "https://github.com/iconify/iconify/issues",
"homepage": "https://iconify.design/",
@ -24,7 +24,7 @@
"module": "dist/iconify.mjs",
"types": "dist/iconify.d.ts",
"devDependencies": {
"@iconify/core": "^1.0.0",
"@iconify/core": "^1.0.1",
"@iconify/types": "^1.0.6",
"@microsoft/api-extractor": "^7.12.0",
"@rollup/plugin-buble": "^0.21.3",

View File

@ -27,6 +27,7 @@ import {
IconifyBuilderFunctions,
builderFunctions,
} from '@iconify/core/lib/builder/functions';
import type { IconifyIconBuildResult } from '@iconify/core/lib/builder';
import { fullIcon, IconifyIcon } from '@iconify/core/lib/icon';
// Modules
@ -74,6 +75,8 @@ import {
// Properties
import {
RawIconCustomisations,
IconifyIconOnLoad,
IconProps,
IconifyIconCustomisations,
IconifyIconProps,
@ -97,7 +100,7 @@ export {
// JSON stuff
export { IconifyIcon, IconifyJSON, IconifyIconName };
// Customisations
// Customisations and icon props
export {
IconifyIconCustomisations,
IconifyIconSize,
@ -105,6 +108,7 @@ export {
IconifyVerticalIconAlignment,
IconifyIconProps,
IconProps,
IconifyIconOnLoad,
};
// API
@ -120,6 +124,9 @@ export {
PartialIconifyAPIConfig,
};
// Builder functions
export { RawIconCustomisations, IconifyIconBuildResult };
/* Browser cache */
export { IconifyBrowserCacheType };
@ -169,6 +176,11 @@ export const calculateSize = builderFunctions.calculateSize;
*/
export const replaceIDs = builderFunctions.replaceIDs;
/**
* Build SVG
*/
export const buildIcon = builderFunctions.buildIcon;
/* API functions */
/**
* Load icons
@ -338,7 +350,7 @@ export const Icon = defineComponent({
}
},
// Get data for icon to render or null
getIcon(icon) {
getIcon(icon: IconifyIcon | string, onload?: IconifyIconOnLoad) {
// Icon is an object
if (
typeof icon === 'object' &&
@ -376,8 +388,13 @@ export const Icon = defineComponent({
}
// Icon data is available
this._name = icon;
this.abortLoading();
if (this._name !== icon) {
this._name = icon;
if (onload) {
onload(icon);
}
}
return data;
},
},
@ -393,7 +410,7 @@ export const Icon = defineComponent({
// Get icon data
const props = this.$attrs;
const icon = this.getIcon(props.icon);
const icon = this.getIcon(props.icon, props.onLoad);
// Validate icon object
if (!icon) {

View File

@ -1,14 +1,21 @@
import { IconifyIcon } from '@iconify/types';
import { IconifyIconCustomisations as IconCustomisations } from '@iconify/core/lib/customisations';
import { IconifyIconCustomisations as RawIconCustomisations } from '@iconify/core/lib/customisations';
export { RawIconCustomisations };
// Allow rotation to be string
/**
* Icon customisations
*/
export type IconifyIconCustomisations = IconCustomisations & {
export type IconifyIconCustomisations = RawIconCustomisations & {
rotate?: string | number;
};
/**
* Callback for when icon has been loaded (only triggered for icons loaded from API)
*/
export type IconifyIconOnLoad = (name: string) => void;
/**
* Icon properties
*/
@ -33,6 +40,9 @@ interface IconifyElementProps {
// Style
style?: unknown;
// Callback to call when icon data has been loaded. Used only for icons loaded from API
onLoad?: IconifyIconOnLoad;
}
/**

View File

@ -3,6 +3,7 @@ import { IconifyIcon } from '@iconify/types';
import {
FullIconCustomisations,
defaults,
mergeCustomisations,
} from '@iconify/core/lib/customisations';
import {
flipFromString,
@ -32,14 +33,19 @@ let customisationAliases = {};
['horizontal', 'vertical'].forEach(prefix => {
['Align', 'Flip'].forEach(suffix => {
const attr = prefix.slice(0, 1) + suffix;
const value = {
attr,
boolean: suffix === 'Flip',
};
// vertical-align
customisationAliases[prefix + '-' + suffix.toLowerCase()] = attr;
customisationAliases[prefix + '-' + suffix.toLowerCase()] = value;
// v-align
customisationAliases[
prefix.slice(0, 1) + '-' + suffix.toLowerCase()
] = attr;
] = value;
// verticalAlign
customisationAliases[prefix + suffix] = attr;
customisationAliases[prefix + suffix] = value;
});
});
@ -60,7 +66,7 @@ export const render = (
props: IconProps
): VNode => {
// Split properties
const customisations = merge(
const customisations = mergeCustomisations(
defaults,
props as IconifyIconCustomisations
) as FullIconCustomisations;
@ -75,10 +81,22 @@ export const render = (
// Get element properties
for (let key in props) {
const value = props[key];
if (value === void 0) {
continue;
}
switch (key) {
// Properties to ignore
case 'icon':
case 'style':
case 'onLoad':
break;
// Boolean attributes
case 'inline':
case 'hFlip':
case 'vFlip':
customisations[key] =
value === true || value === 'true' || value === 1;
break;
// Flip as string: 'horizontal,vertical'
@ -121,7 +139,20 @@ export const render = (
default:
if (customisationAliases[key] !== void 0) {
// Aliases for customisations
customisations[customisationAliases[key]] = value;
if (
customisationAliases[key].boolean &&
(value === true || value === 'true' || value === 1)
) {
// Check for boolean
customisations[customisationAliases[key].attr] = true;
} else if (
!customisationAliases[key].boolean &&
typeof value === 'string' &&
value !== ''
) {
// String
customisations[customisationAliases[key].attr] = value;
}
} else if (defaults[key] === void 0) {
// Copy missing property if it does not exist in customisations
componentProps[key] = value;

View File

@ -15,6 +15,8 @@ describe('Rendering icon', () => {
const prefix = nextPrefix();
const name = 'render-test';
const iconName = `@${provider}:${prefix}:${name}`;
let onLoadCalled = false;
mockAPIData({
provider,
prefix,
@ -46,7 +48,14 @@ describe('Rendering icon', () => {
// Render component
const Wrapper = {
components: { Icon },
template: `<Icon icon="${iconName}" />`,
template: `<Icon icon="${iconName}" :onLoad='onLoad' />`,
methods: {
onLoad(name) {
expect(name).toEqual(iconName);
expect(onLoadCalled).toEqual(false);
onLoadCalled = true;
},
},
};
const wrapper = mount(Wrapper, {});
const html = wrapper.html().replace(/\s*\n\s*/g, '');
@ -56,6 +65,9 @@ describe('Rendering icon', () => {
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24"><path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"></path></svg>'
);
// Make sure onLoad has been called
expect(onLoadCalled).toEqual(true);
done();
});
});
@ -64,6 +76,8 @@ describe('Rendering icon', () => {
const prefix = nextPrefix();
const name = 'mock-test';
const iconName = `@${provider}:${prefix}:${name}`;
let onLoadCalled = false;
mockAPIData({
provider,
prefix,
@ -77,6 +91,9 @@ describe('Rendering icon', () => {
// Icon should not have loaded yet
expect(iconExists(iconName)).toEqual(false);
// onLoad should not have been called yet
expect(onLoadCalled).toEqual(false);
// Send icon data
next();
@ -92,6 +109,9 @@ describe('Rendering icon', () => {
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24"><path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"></path></svg>'
);
// onLoad should have been called
expect(onLoadCalled).toEqual(true);
done();
}, 0);
}, 0);
@ -104,12 +124,22 @@ describe('Rendering icon', () => {
// Render component
const Wrapper = {
components: { Icon },
template: `<Icon icon="${iconName}" />`,
template: `<Icon icon="${iconName}" :onLoad='onLoad' />`,
methods: {
onLoad(name) {
expect(name).toEqual(iconName);
expect(onLoadCalled).toEqual(false);
onLoadCalled = true;
},
},
};
const wrapper = mount(Wrapper, {});
// Should render empty icon
expect(wrapper.html()).toEqual('<!---->');
// onLoad should not have been called yet
expect(onLoadCalled).toEqual(false);
});
test('missing icon', done => {
@ -148,7 +178,12 @@ describe('Rendering icon', () => {
// Render component
const Wrapper = {
components: { Icon },
template: `<Icon icon="${iconName}" />`,
template: `<Icon icon="${iconName}" :onLoad='onLoad' />`,
methods: {
onLoad() {
throw new Error('onLoad called for empty icon!');
},
},
};
const wrapper = mount(Wrapper, {});

View File

@ -24,6 +24,26 @@ describe('Rendering icon', () => {
const name2 = 'changing-prop2';
const iconName = `@${provider}:${prefix}:${name}`;
const iconName2 = `@${provider}:${prefix}:${name2}`;
let onLoadCalled = ''; // Name of icon from last onLoad call
const onLoad = name => {
// onLoad should be called only once per icon
switch (name) {
// First onLoad call
case iconName:
expect(onLoadCalled).toEqual('');
break;
// Second onLoad call
case iconName2:
expect(onLoadCalled).toEqual(iconName);
break;
default:
throw new Error(`Unexpected onLoad('${name}') call`);
}
onLoadCalled = name;
};
mockAPIData({
provider,
@ -38,6 +58,9 @@ describe('Rendering icon', () => {
// Icon should not have loaded yet
expect(iconExists(iconName)).toEqual(false);
// onLoad should not have been called yet
expect(onLoadCalled).toEqual('');
// Send icon data
next();
@ -52,6 +75,9 @@ describe('Rendering icon', () => {
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24"><path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"></path></svg>'
);
// onLoad should have been called
expect(onLoadCalled).toEqual(iconName);
wrapper.setProps({
icon: iconName2,
});
@ -73,6 +99,9 @@ describe('Rendering icon', () => {
// Icon should not have loaded yet
expect(iconExists(iconName2)).toEqual(false);
// onLoad should have been called only once for previous icon
expect(onLoadCalled).toEqual(iconName);
// Send icon data
next();
@ -87,6 +116,9 @@ describe('Rendering icon', () => {
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path d="M19.031 4.281l-11 11l-.687.719l.687.719l11 11l1.438-1.438L10.187 16L20.47 5.719z" fill="currentColor"></path></svg>'
);
// onLoad should have been called for second icon
expect(onLoadCalled).toEqual(iconName2);
done();
}, 0);
}, 0);
@ -99,12 +131,18 @@ describe('Rendering icon', () => {
// Render component
const Wrapper = {
components: { Icon },
template: `<Icon icon="${iconName}" />`,
template: `<Icon icon="${iconName}" :onLoad="onLoad" />`,
methods: {
onLoad,
},
};
const wrapper = mount(Wrapper, {});
// Should render placeholder
expect(wrapper.html()).toEqual('<!---->');
// onLoad should not have been called yet
expect(onLoadCalled).toEqual('');
});
test('changing icon property while loading', done => {

View File

@ -30,6 +30,10 @@ describe('Creating component', () => {
const wrapper = mount(Icon, {
props: {
icon: iconData,
onLoad: () => {
// Should be called only for icons loaded from API
throw new Error('onLoad called for object!');
},
},
});

View File

@ -25,7 +25,7 @@ describe('Inline attribute', () => {
});
test('false string', () => {
// "false" = true
// "false" should be ignored
const Wrapper = {
components: { Icon },
template: `<Icon :icon="icon" inline="false" />`,
@ -37,7 +37,9 @@ describe('Inline attribute', () => {
};
const wrapper = mount(Wrapper, {});
expect(wrapper.html()).toContain('style="vertical-align: -0.125em;"');
expect(wrapper.html()).not.toContain(
'style="vertical-align: -0.125em;"'
);
});
test('true', () => {

View File

@ -110,10 +110,10 @@ describe('Flip', () => {
});
test('shorthand and boolean', () => {
// 'flip' is processed after 'hFlip', overwriting value
// 'flip' is processed after 'hFlip' because of order of elements in object, overwriting value
const Wrapper = {
components: { Icon },
template: `<Icon :icon="icon" flip="horizontal" :hFlip="false" />`,
template: `<Icon :icon="icon" :hFlip="false" flip="horizontal" />`,
data() {
return {
icon: iconData,

View File

@ -25,7 +25,7 @@ describe('Inline attribute', () => {
});
test('false string', () => {
// "false" = true
// "false" should be ignored
const Wrapper = {
components: { Icon },
template: `<Icon :icon="icon" inline="false" />`,
@ -37,7 +37,9 @@ describe('Inline attribute', () => {
};
const wrapper = mount(Wrapper, {});
expect(wrapper.html()).toContain('style="vertical-align: -0.125em;"');
expect(wrapper.html()).not.toContain(
'style="vertical-align: -0.125em;"'
);
});
test('true', () => {

View File

@ -110,10 +110,10 @@ describe('Flip', () => {
});
test('shorthand and boolean', () => {
// 'flip' is processed after 'hFlip', overwriting value
// 'flip' is processed after 'hFlip' because of order of elements in object, overwriting value
const Wrapper = {
components: { Icon },
template: `<Icon :icon="icon" flip="horizontal" :hFlip="false" />`,
template: `<Icon :icon="icon" :hFlip="false" flip="horizontal" />`,
data() {
return {
icon: iconData,

View File

@ -1,15 +1,15 @@
{
"name": "@iconify/vue2",
"version": "1.0.0-alpha.0",
"version": "1.0.0-alpha.1",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@iconify/vue2",
"version": "1.0.0-alpha.0",
"version": "1.0.0-alpha.1",
"license": "MIT",
"devDependencies": {
"@iconify/core": "^1.0.0",
"@iconify/core": "^1.0.1",
"@iconify/types": "^1.0.6",
"@microsoft/api-extractor": "^7.15.1",
"@rollup/plugin-buble": "^0.21.3",
@ -707,9 +707,9 @@
"dev": true
},
"node_modules/@iconify/core": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@iconify/core/-/core-1.0.0.tgz",
"integrity": "sha512-jzGZQMOqoPpKFZ4K4dQ6gNcDqALoJE02FMExm+kcN4vp2GJ5JKCccYxJLBWTmR23vVeZzpxlCtL3KSKjUfe2Kw==",
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@iconify/core/-/core-1.0.1.tgz",
"integrity": "sha512-gbPClcrRJ7sIKgwcEPLUaT1u8PzpOGdsCM3O63wJa5FYosC3ZZBymqR1LFT6MSiPWGlF2XowabzoHS8HaICEpg==",
"dev": true,
"dependencies": {
"@cyberalien/redundancy": "^1.1.0",
@ -10091,9 +10091,9 @@
"dev": true
},
"@iconify/core": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@iconify/core/-/core-1.0.0.tgz",
"integrity": "sha512-jzGZQMOqoPpKFZ4K4dQ6gNcDqALoJE02FMExm+kcN4vp2GJ5JKCccYxJLBWTmR23vVeZzpxlCtL3KSKjUfe2Kw==",
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@iconify/core/-/core-1.0.1.tgz",
"integrity": "sha512-gbPClcrRJ7sIKgwcEPLUaT1u8PzpOGdsCM3O63wJa5FYosC3ZZBymqR1LFT6MSiPWGlF2XowabzoHS8HaICEpg==",
"dev": true,
"requires": {
"@cyberalien/redundancy": "^1.1.0",

View File

@ -2,7 +2,7 @@
"name": "@iconify/vue2",
"description": "Iconify icon component for Vue 2.",
"author": "Vjacheslav Trushkin",
"version": "1.0.0-alpha.0",
"version": "1.0.0-alpha.1",
"license": "MIT",
"bugs": "https://github.com/iconify/iconify/issues",
"homepage": "https://iconify.design/",
@ -24,7 +24,7 @@
"module": "dist/iconify.mjs",
"types": "dist/iconify.d.ts",
"devDependencies": {
"@iconify/core": "^1.0.0",
"@iconify/core": "^1.0.1",
"@iconify/types": "^1.0.6",
"@microsoft/api-extractor": "^7.15.1",
"@rollup/plugin-buble": "^0.21.3",

View File

@ -19,6 +19,7 @@ import {
IconifyBuilderFunctions,
builderFunctions,
} from '@iconify/core/lib/builder/functions';
import type { IconifyIconBuildResult } from '@iconify/core/lib/builder';
import { fullIcon, IconifyIcon } from '@iconify/core/lib/icon';
// Modules
@ -66,6 +67,8 @@ import {
// Properties
import {
RawIconCustomisations,
IconifyIconOnLoad,
IconProps,
IconifyIconCustomisations,
IconifyIconProps,
@ -89,7 +92,7 @@ export {
// JSON stuff
export { IconifyIcon, IconifyJSON, IconifyIconName };
// Customisations
// Customisations and icon props
export {
IconifyIconCustomisations,
IconifyIconSize,
@ -97,6 +100,7 @@ export {
IconifyVerticalIconAlignment,
IconifyIconProps,
IconProps,
IconifyIconOnLoad,
};
// API
@ -112,6 +116,9 @@ export {
PartialIconifyAPIConfig,
};
// Builder functions
export { RawIconCustomisations, IconifyIconBuildResult };
/* Browser cache */
export { IconifyBrowserCacheType };
@ -161,6 +168,11 @@ export const calculateSize = builderFunctions.calculateSize;
*/
export const replaceIDs = builderFunctions.replaceIDs;
/**
* Build SVG
*/
export const buildIcon = builderFunctions.buildIcon;
/* API functions */
/**
* Load icons
@ -227,10 +239,10 @@ if (typeof document !== 'undefined' && typeof window !== 'undefined') {
IconifyPreload: IconifyJSON[] | IconifyJSON;
}
if (
((_window as unknown) as WindowWithIconifyPreload).IconifyPreload !==
(_window as unknown as WindowWithIconifyPreload).IconifyPreload !==
void 0
) {
const preload = ((_window as unknown) as WindowWithIconifyPreload)
const preload = (_window as unknown as WindowWithIconifyPreload)
.IconifyPreload;
const err = 'Invalid IconifyPreload syntax.';
if (typeof preload === 'object' && preload !== null) {
@ -261,10 +273,10 @@ if (typeof document !== 'undefined' && typeof window !== 'undefined') {
IconifyProviders: Record<string, PartialIconifyAPIConfig>;
}
if (
((_window as unknown) as WindowWithIconifyProviders)
.IconifyProviders !== void 0
(_window as unknown as WindowWithIconifyProviders).IconifyProviders !==
void 0
) {
const providers = ((_window as unknown) as WindowWithIconifyProviders)
const providers = (_window as unknown as WindowWithIconifyProviders)
.IconifyProviders;
if (typeof providers === 'object' && providers !== null) {
for (let key in providers) {
@ -328,7 +340,7 @@ export const Icon = Vue.extend({
}
},
// Get data for icon to render or null
getIcon(icon) {
getIcon(icon: IconifyIcon | string, onload?: IconifyIconOnLoad) {
// Icon is an object
if (
typeof icon === 'object' &&
@ -366,8 +378,13 @@ export const Icon = Vue.extend({
}
// Icon data is available
this._name = icon;
this.abortLoading();
if (this._name !== icon) {
this._name = icon;
if (onload) {
onload(icon);
}
}
return data;
},
},
@ -385,7 +402,7 @@ export const Icon = Vue.extend({
: createElement('span', result);
}
}
return (null as unknown) as VNode;
return null as unknown as VNode;
}
if (!this.mounted) {
return placeholder(this.$slots);
@ -393,7 +410,7 @@ export const Icon = Vue.extend({
// Get icon data
const props = this.$attrs;
const icon = this.getIcon(props.icon);
const icon = this.getIcon(props.icon, props.onLoad);
// Validate icon object
if (!icon) {

View File

@ -1,14 +1,21 @@
import { IconifyIcon } from '@iconify/types';
import { IconifyIconCustomisations as IconCustomisations } from '@iconify/core/lib/customisations';
import { IconifyIconCustomisations as RawIconCustomisations } from '@iconify/core/lib/customisations';
export { RawIconCustomisations };
// Allow rotation to be string
/**
* Icon customisations
*/
export type IconifyIconCustomisations = IconCustomisations & {
export type IconifyIconCustomisations = RawIconCustomisations & {
rotate?: string | number;
};
/**
* Callback for when icon has been loaded (only triggered for icons loaded from API)
*/
export type IconifyIconOnLoad = (name: string) => void;
/**
* Icon properties
*/
@ -33,6 +40,9 @@ interface IconifyElementProps {
// Style
style?: unknown;
// Callback to call when icon data has been loaded. Used only for icons loaded from API
onLoad?: IconifyIconOnLoad;
}
/**

View File

@ -3,6 +3,7 @@ import { IconifyIcon } from '@iconify/types';
import {
FullIconCustomisations,
defaults,
mergeCustomisations,
} from '@iconify/core/lib/customisations';
import {
flipFromString,
@ -32,22 +33,21 @@ let customisationAliases = {};
['horizontal', 'vertical'].forEach((prefix) => {
['Align', 'Flip'].forEach((suffix) => {
const attr = prefix.slice(0, 1) + suffix;
const value = {
attr,
boolean: suffix === 'Flip',
};
// vertical-align
customisationAliases[prefix + '-' + suffix.toLowerCase()] = attr;
customisationAliases[prefix + '-' + suffix.toLowerCase()] = value;
// v-align
customisationAliases[
prefix.slice(0, 1) + '-' + suffix.toLowerCase()
] = attr;
customisationAliases[prefix.slice(0, 1) + '-' + suffix.toLowerCase()] =
value;
// verticalAlign
customisationAliases[prefix + suffix] = attr;
customisationAliases[prefix + suffix] = value;
});
});
/**
* Interface for inline style
*/
type VNodeStyle = (string | Record<string, unknown>)[];
/**
* Render icon
*/
@ -64,7 +64,7 @@ export const render = (
icon: Required<IconifyIcon>
): VNode => {
// Split properties
const customisations = merge(
const customisations = mergeCustomisations(
defaults,
props as IconifyIconCustomisations
) as FullIconCustomisations;
@ -76,10 +76,22 @@ export const render = (
// Get element properties
for (let key in props) {
const value = props[key];
if (value === void 0) {
continue;
}
switch (key) {
// Properties to ignore
case 'icon':
case 'style':
case 'onLoad':
break;
// Boolean attributes
case 'inline':
case 'hFlip':
case 'vFlip':
customisations[key] =
value === true || value === 'true' || value === 1;
break;
// Flip as string: 'horizontal,vertical'
@ -122,7 +134,20 @@ export const render = (
default:
if (customisationAliases[key] !== void 0) {
// Aliases for customisations
customisations[customisationAliases[key]] = value;
if (
customisationAliases[key].boolean &&
(value === true || value === 'true' || value === 1)
) {
// Check for boolean
customisations[customisationAliases[key].attr] = true;
} else if (
!customisationAliases[key].boolean &&
typeof value === 'string' &&
value !== ''
) {
// String
customisations[customisationAliases[key].attr] = value;
}
} else if (defaults[key] === void 0) {
// Copy missing property if it does not exist in customisations
componentProps[key] = value;

View File

@ -4,8 +4,7 @@ import { mockAPIData } from '@iconify/core/lib/api/modules/mock';
import { provider, nextPrefix } from './load';
const iconData = {
body:
'<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
body: '<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
width: 24,
height: 24,
};
@ -15,6 +14,8 @@ describe('Rendering icon', () => {
const prefix = nextPrefix();
const name = 'render-test';
const iconName = `@${provider}:${prefix}:${name}`;
let onLoadCalled = false;
mockAPIData({
provider,
prefix,
@ -46,7 +47,14 @@ describe('Rendering icon', () => {
// Render component
const Wrapper = {
components: { Icon },
template: `<Icon icon="${iconName}" />`,
template: `<Icon icon="${iconName}" :onLoad='onLoad' />`,
methods: {
onLoad(name) {
expect(name).toEqual(iconName);
expect(onLoadCalled).toEqual(false);
onLoadCalled = true;
},
},
};
const wrapper = mount(Wrapper, {});
const html = wrapper.html().replace(/\s*\n\s*/g, '');
@ -56,6 +64,9 @@ describe('Rendering icon', () => {
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24"><path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"></path></svg>'
);
// Make sure onLoad has been called
expect(onLoadCalled).toEqual(true);
done();
});
});
@ -64,6 +75,8 @@ describe('Rendering icon', () => {
const prefix = nextPrefix();
const name = 'mock-test';
const iconName = `@${provider}:${prefix}:${name}`;
let onLoadCalled = false;
mockAPIData({
provider,
prefix,
@ -77,6 +90,9 @@ describe('Rendering icon', () => {
// Icon should not have loaded yet
expect(iconExists(iconName)).toEqual(false);
// onLoad should not have been called yet
expect(onLoadCalled).toEqual(false);
// Send icon data
next();
@ -92,6 +108,9 @@ describe('Rendering icon', () => {
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24"><path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"></path></svg>'
);
// onLoad should have been called
expect(onLoadCalled).toEqual(true);
done();
}, 0);
}, 0);
@ -104,12 +123,22 @@ describe('Rendering icon', () => {
// Render component
const Wrapper = {
components: { Icon },
template: `<Icon icon="${iconName}" />`,
template: `<Icon icon="${iconName}" :onLoad='onLoad' />`,
methods: {
onLoad(name) {
expect(name).toEqual(iconName);
expect(onLoadCalled).toEqual(false);
onLoadCalled = true;
},
},
};
const wrapper = mount(Wrapper, {});
// Should render empty icon
expect(wrapper.html()).toEqual('');
// onLoad should not have been called yet
expect(onLoadCalled).toEqual(false);
});
test('missing icon', (done) => {
@ -148,7 +177,12 @@ describe('Rendering icon', () => {
// Render component
const Wrapper = {
components: { Icon },
template: `<Icon icon="${iconName}" />`,
template: `<Icon icon="${iconName}" :onLoad='onLoad' />`,
methods: {
onLoad() {
throw new Error('onLoad called for empty icon!');
},
},
};
const wrapper = mount(Wrapper, {});

View File

@ -4,15 +4,13 @@ import { mockAPIData } from '@iconify/core/lib/api/modules/mock';
import { provider, nextPrefix } from './load';
const iconData = {
body:
'<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
body: '<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
width: 24,
height: 24,
};
const iconData2 = {
body:
'<path d="M19.031 4.281l-11 11l-.687.719l.687.719l11 11l1.438-1.438L10.187 16L20.47 5.719z" fill="currentColor"/>',
body: '<path d="M19.031 4.281l-11 11l-.687.719l.687.719l11 11l1.438-1.438L10.187 16L20.47 5.719z" fill="currentColor"/>',
width: 32,
height: 32,
};
@ -24,6 +22,26 @@ describe('Rendering icon', () => {
const name2 = 'changing-prop2';
const iconName = `@${provider}:${prefix}:${name}`;
const iconName2 = `@${provider}:${prefix}:${name2}`;
let onLoadCalled = ''; // Name of icon from last onLoad call
const onLoad = (name) => {
// onLoad should be called only once per icon
switch (name) {
// First onLoad call
case iconName:
expect(onLoadCalled).toEqual('');
break;
// Second onLoad call
case iconName2:
expect(onLoadCalled).toEqual(iconName);
break;
default:
throw new Error(`Unexpected onLoad('${name}') call`);
}
onLoadCalled = name;
};
mockAPIData({
provider,
@ -38,6 +56,9 @@ describe('Rendering icon', () => {
// Icon should not have loaded yet
expect(iconExists(iconName)).toEqual(false);
// onLoad should not have been called yet
expect(onLoadCalled).toEqual('');
// Send icon data
next();
@ -52,6 +73,9 @@ describe('Rendering icon', () => {
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24"><path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"></path></svg>'
);
// onLoad should have been called
expect(onLoadCalled).toEqual(iconName);
wrapper.setProps({
icon: iconName2,
});
@ -73,6 +97,9 @@ describe('Rendering icon', () => {
// Icon should not have loaded yet
expect(iconExists(iconName2)).toEqual(false);
// onLoad should have been called only once for previous icon
expect(onLoadCalled).toEqual(iconName);
// Send icon data
next();
@ -87,6 +114,9 @@ describe('Rendering icon', () => {
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path d="M19.031 4.281l-11 11l-.687.719l.687.719l11 11l1.438-1.438L10.187 16L20.47 5.719z" fill="currentColor"></path></svg>'
);
// onLoad should have been called for second icon
expect(onLoadCalled).toEqual(iconName2);
done();
}, 0);
}, 0);
@ -100,11 +130,15 @@ describe('Rendering icon', () => {
const wrapper = mount(Icon, {
propsData: {
icon: iconName,
onLoad,
},
});
// Should render placeholder
expect(wrapper.html()).toEqual('');
// onLoad should not have been called yet
expect(onLoadCalled).toEqual('');
});
test('changing icon property while loading', (done) => {

View File

@ -2,8 +2,7 @@ import { mount } from '@vue/test-utils';
import { Icon } from '../../dist/iconify';
const iconData = {
body:
'<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
body: '<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
width: 24,
height: 24,
};
@ -30,6 +29,10 @@ describe('Creating component', () => {
const wrapper = mount(Icon, {
propsData: {
icon: iconData,
onLoad: () => {
// Should be called only for icons loaded from API
throw new Error('onLoad called for object!');
},
},
});

View File

@ -2,8 +2,7 @@ import { mount } from '@vue/test-utils';
import { Icon } from '../../dist/iconify';
const iconData = {
body:
'<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
body: '<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
width: 24,
height: 24,
};
@ -25,7 +24,7 @@ describe('Inline attribute', () => {
});
test('false string', () => {
// "false" = true
// "false" should be ignored
const Wrapper = {
components: { Icon },
template: `<Icon :icon="icon" inline="false" />`,
@ -37,7 +36,9 @@ describe('Inline attribute', () => {
};
const wrapper = mount(Wrapper, {});
expect(wrapper.html()).toContain('style="vertical-align: -0.125em;"');
expect(wrapper.html()).not.toContain(
'style="vertical-align: -0.125em;"'
);
});
test('true', () => {

View File

@ -2,8 +2,7 @@ import { mount } from '@vue/test-utils';
import { Icon } from '../../dist/iconify';
const iconData = {
body:
'<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
body: '<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
width: 24,
height: 24,
};
@ -110,10 +109,10 @@ describe('Flip', () => {
});
test('shorthand and boolean', () => {
// 'flip' is processed after 'hFlip', overwriting value
// 'flip' is processed after 'hFlip' because of order of elements in object, overwriting value
const Wrapper = {
components: { Icon },
template: `<Icon :icon="icon" flip="horizontal" :hFlip="false" />`,
template: `<Icon :icon="icon" :hFlip="false" flip="horizontal" />`,
data() {
return {
icon: iconData,

View File

@ -2,8 +2,7 @@ import { mount } from '@vue/test-utils';
import { Icon } from '../../dist/offline';
const iconData = {
body:
'<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
body: '<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
width: 24,
height: 24,
};
@ -25,7 +24,7 @@ describe('Inline attribute', () => {
});
test('false string', () => {
// "false" = true
// "false" should be ignored
const Wrapper = {
components: { Icon },
template: `<Icon :icon="icon" inline="false" />`,
@ -37,7 +36,9 @@ describe('Inline attribute', () => {
};
const wrapper = mount(Wrapper, {});
expect(wrapper.html()).toContain('style="vertical-align: -0.125em;"');
expect(wrapper.html()).not.toContain(
'style="vertical-align: -0.125em;"'
);
});
test('true', () => {

View File

@ -2,8 +2,7 @@ import { mount } from '@vue/test-utils';
import { Icon } from '../../dist/offline';
const iconData = {
body:
'<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
body: '<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
width: 24,
height: 24,
};
@ -110,10 +109,10 @@ describe('Flip', () => {
});
test('shorthand and boolean', () => {
// 'flip' is processed after 'hFlip', overwriting value
// 'flip' is processed after 'hFlip' because of order of elements in object, overwriting value
const Wrapper = {
components: { Icon },
template: `<Icon :icon="icon" flip="horizontal" :hFlip="false" />`,
template: `<Icon :icon="icon" :hFlip="false" flip="horizontal" />`,
data() {
return {
icon: iconData,