diff --git a/packages/core/package-lock.json b/packages/core/package-lock.json index f07629e..97068c1 100644 --- a/packages/core/package-lock.json +++ b/packages/core/package-lock.json @@ -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", diff --git a/packages/core/package.json b/packages/core/package.json index d012111..4569d4b 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -2,7 +2,7 @@ "name": "@iconify/core", "description": "Reusable files used by multiple Iconify packages", "author": "Vjacheslav Trushkin (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/", diff --git a/packages/iconify/README.md b/packages/iconify/README.md index 430f79e..95afeb8 100644 --- a/packages/iconify/README.md +++ b/packages/iconify/README.md @@ -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 `` section of the page or before ``): ```html - + ``` or ```html - + ``` 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: diff --git a/packages/iconify/package-lock.json b/packages/iconify/package-lock.json index 1e34015..9c541b0 100644 --- a/packages/iconify/package-lock.json +++ b/packages/iconify/package-lock.json @@ -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", diff --git a/packages/iconify/package.json b/packages/iconify/package.json index ae6a781..c7f2985 100644 --- a/packages/iconify/package.json +++ b/packages/iconify/package.json @@ -2,7 +2,7 @@ "name": "@iconify/iconify", "description": "Unified SVG framework with over 70,000 icons to choose from", "author": "Vjacheslav Trushkin (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", diff --git a/packages/iconify/src/common.ts b/packages/iconify/src/common.ts index a53a5bc..612f7b0 100644 --- a/packages/iconify/src/common.ts +++ b/packages/iconify/src/common.ts @@ -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) { diff --git a/packages/iconify/src/modules/render.ts b/packages/iconify/src/modules/render.ts index c3860de..848148e 100644 --- a/packages/iconify/src/modules/render.ts +++ b/packages/iconify/src/modules/render.ts @@ -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; diff --git a/packages/react/package-lock.json b/packages/react/package-lock.json index 20bc511..b730e5c 100644 --- a/packages/react/package-lock.json +++ b/packages/react/package-lock.json @@ -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", diff --git a/packages/react/package.json b/packages/react/package.json index 89dbc59..63396fd 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -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", diff --git a/packages/react/src/iconify.ts b/packages/react/src/iconify.ts index efdc841..81f7246 100644 --- a/packages/react/src/iconify.ts +++ b/packages/react/src/iconify.ts @@ -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; } 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); + } } } diff --git a/packages/react/src/props.ts b/packages/react/src/props.ts index 80273b6..a10986d 100644 --- a/packages/react/src/props.ts +++ b/packages/react/src/props.ts @@ -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; } /** diff --git a/packages/react/src/render.ts b/packages/react/src/render.ts index 829af74..3175f8a 100644 --- a/packages/react/src/render.ts +++ b/packages/react/src/render.ts @@ -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') { diff --git a/packages/react/tests/api/20-rendering-from-api.test.js b/packages/react/tests/api/20-rendering-from-api.test.js index c002c51..7daba26 100644 --- a/packages/react/tests/api/20-rendering-from-api.test.js +++ b/packages/react/tests/api/20-rendering-from-api.test.js @@ -5,8 +5,7 @@ import { mockAPIData } from '@iconify/core/lib/api/modules/mock'; import { provider, nextPrefix } from './load'; const iconData = { - body: - '', + body: '', 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(); + const component = renderer.create( + { + 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(); + const component = renderer.create( + { + 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(); + const component = renderer.create( + { + throw new Error('onLoad called for empty icon!'); + }} + > + ); const tree = component.toJSON(); // Should render placeholder diff --git a/packages/react/tests/api/30-changing-props.test.js b/packages/react/tests/api/30-changing-props.test.js index 8130ffd..f742b48 100644 --- a/packages/react/tests/api/30-changing-props.test.js +++ b/packages/react/tests/api/30-changing-props.test.js @@ -5,15 +5,13 @@ import { mockAPIData } from '@iconify/core/lib/api/modules/mock'; import { provider, nextPrefix } from './load'; const iconData = { - body: - '', + body: '', width: 24, height: 24, }; const iconData2 = { - body: - '', + body: '', 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(); + // onLoad should have been called + expect(onLoadCalled).toEqual(iconName); + + // Change property + component.update( + + ); }, 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(); + const component = renderer.create( + + ); 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) => { diff --git a/packages/react/tests/iconify/10-basic.test.js b/packages/react/tests/iconify/10-basic.test.js index beaec32..d439a30 100644 --- a/packages/react/tests/iconify/10-basic.test.js +++ b/packages/react/tests/iconify/10-basic.test.js @@ -3,15 +3,22 @@ import { Icon, InlineIcon } from '../../dist/iconify'; import renderer from 'react-test-renderer'; const iconData = { - body: - '', + body: '', width: 24, height: 24, }; describe('Creating component using object', () => { test('basic icon', () => { - const component = renderer.create(); + const component = renderer.create( + { + // Should be called only for icons loaded from API + throw new Error('onLoad called for object!'); + }} + /> + ); const tree = component.toJSON(); expect(tree).toMatchObject({ diff --git a/packages/react/tests/iconify/20-inline.test.js b/packages/react/tests/iconify/20-inline.test.js index 9d62fd9..b8a39c0 100644 --- a/packages/react/tests/iconify/20-inline.test.js +++ b/packages/react/tests/iconify/20-inline.test.js @@ -3,8 +3,7 @@ import { Icon } from '../../dist/iconify'; import renderer from 'react-test-renderer'; const iconData = { - body: - '', + body: '', width: 24, height: 24, }; @@ -20,12 +19,12 @@ describe('Inline attribute', () => { }); test('false string', () => { - // "false" = true + // "false" should be ignored const component = renderer.create( ); const tree = component.toJSON(); - expect(tree.props.style.verticalAlign).toStrictEqual('-0.125em'); + expect(tree.props.style.verticalAlign).toEqual(void 0); }); }); diff --git a/packages/react/tests/iconify/20-transformations.test.js b/packages/react/tests/iconify/20-transformations.test.js index 020d924..6af727c 100644 --- a/packages/react/tests/iconify/20-transformations.test.js +++ b/packages/react/tests/iconify/20-transformations.test.js @@ -3,8 +3,7 @@ import { InlineIcon } from '../../dist/iconify'; import renderer from 'react-test-renderer'; const iconData = { - body: - '', + body: '', 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( - + ); const tree = component.toJSON(); const body = tree.props.dangerouslySetInnerHTML.__html; diff --git a/packages/react/tests/offline/20-inline.test.js b/packages/react/tests/offline/20-inline.test.js index a476fa3..006927c 100644 --- a/packages/react/tests/offline/20-inline.test.js +++ b/packages/react/tests/offline/20-inline.test.js @@ -3,13 +3,21 @@ import { Icon } from '../../dist/offline'; import renderer from 'react-test-renderer'; const iconData = { - body: - '', + body: '', width: 24, height: 24, }; describe('Inline attribute', () => { + test('boolean true', () => { + const component = renderer.create( + + ); + const tree = component.toJSON(); + + expect(tree.props.style.verticalAlign).toStrictEqual('-0.125em'); + }); + test('string', () => { const component = renderer.create( @@ -19,13 +27,22 @@ describe('Inline attribute', () => { expect(tree.props.style.verticalAlign).toStrictEqual('-0.125em'); }); + test('false', () => { + const component = renderer.create( + + ); + 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( ); const tree = component.toJSON(); - expect(tree.props.style.verticalAlign).toStrictEqual('-0.125em'); + expect(tree.props.style.verticalAlign).toEqual(void 0); }); }); diff --git a/packages/react/tests/offline/20-transformations.test.js b/packages/react/tests/offline/20-transformations.test.js index c5615d6..6981462 100644 --- a/packages/react/tests/offline/20-transformations.test.js +++ b/packages/react/tests/offline/20-transformations.test.js @@ -3,8 +3,7 @@ import { InlineIcon } from '../../dist/offline'; import renderer from 'react-test-renderer'; const iconData = { - body: - '', + body: '', 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( - + ); const tree = component.toJSON(); const body = tree.props.dangerouslySetInnerHTML.__html; diff --git a/packages/svelte/.npmignore b/packages/svelte/.npmignore index dbdc3c4..90bfa23 100644 --- a/packages/svelte/.npmignore +++ b/packages/svelte/.npmignore @@ -1,7 +1,9 @@ .DS_Store rollup.config.js tsconfig.json +api-extractor*.json build.js +copy.js node_modules build src diff --git a/packages/svelte/package-lock.json b/packages/svelte/package-lock.json index 3b7930a..d160184 100644 --- a/packages/svelte/package-lock.json +++ b/packages/svelte/package-lock.json @@ -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", diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 2321dbf..b2486f3 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -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", diff --git a/packages/svelte/src/functions.ts b/packages/svelte/src/functions.ts index 7ec8649..007f27a 100644 --- a/packages/svelte/src/functions.ts +++ b/packages/svelte/src/functions.ts @@ -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; } 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; } diff --git a/packages/svelte/src/iconify.ts b/packages/svelte/src/iconify.ts index 06c743b..5194fb4 100644 --- a/packages/svelte/src/iconify.ts +++ b/packages/svelte/src/iconify.ts @@ -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'; diff --git a/packages/svelte/src/render.ts b/packages/svelte/src/render.ts index 09e05c8..3b2dc8b 100644 --- a/packages/svelte/src/render.ts +++ b/packages/svelte/src/render.ts @@ -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; // 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') { diff --git a/packages/svelte/tests/iconify/20-dimensions.test.js b/packages/svelte/tests/iconify/20-dimensions.test.js index 8d0b555..2ee0d2b 100644 --- a/packages/svelte/tests/iconify/20-dimensions.test.js +++ b/packages/svelte/tests/iconify/20-dimensions.test.js @@ -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'); + }); }); diff --git a/packages/svelte/tests/iconify/20-inline.test.js b/packages/svelte/tests/iconify/20-inline.test.js index bb740c6..94ba5a5 100644 --- a/packages/svelte/tests/iconify/20-inline.test.js +++ b/packages/svelte/tests/iconify/20-inline.test.js @@ -2,8 +2,7 @@ import { render } from '@testing-library/svelte'; import { Icon } from '../../dist/iconify'; const iconData = { - body: - '', + body: '', 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(''); }); }); diff --git a/packages/svelte/tests/iconify/20-transformations.test.js b/packages/svelte/tests/iconify/20-transformations.test.js index 0b7d503..2210bca 100644 --- a/packages/svelte/tests/iconify/20-transformations.test.js +++ b/packages/svelte/tests/iconify/20-transformations.test.js @@ -2,8 +2,7 @@ import { render } from '@testing-library/svelte'; import { Icon } from '../../dist/iconify'; const iconData = { - body: - '', + body: '', 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, diff --git a/packages/svelte/tests/offline/20-inline.test.js b/packages/svelte/tests/offline/20-inline.test.js index af21d04..ca0e736 100644 --- a/packages/svelte/tests/offline/20-inline.test.js +++ b/packages/svelte/tests/offline/20-inline.test.js @@ -2,8 +2,7 @@ import { render } from '@testing-library/svelte'; import { Icon } from '../../dist/offline'; const iconData = { - body: - '', + body: '', 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(''); }); }); diff --git a/packages/svelte/tests/offline/20-transformations.test.js b/packages/svelte/tests/offline/20-transformations.test.js index 0001759..c44cafa 100644 --- a/packages/svelte/tests/offline/20-transformations.test.js +++ b/packages/svelte/tests/offline/20-transformations.test.js @@ -2,8 +2,7 @@ import { render } from '@testing-library/svelte'; import { Icon } from '../../dist/offline'; const iconData = { - body: - '', + body: '', 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, diff --git a/packages/vue/package-lock.json b/packages/vue/package-lock.json index 3ae01c4..ae8ba61 100644 --- a/packages/vue/package-lock.json +++ b/packages/vue/package-lock.json @@ -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", diff --git a/packages/vue/package.json b/packages/vue/package.json index 97c29ce..449c8ed 100644 --- a/packages/vue/package.json +++ b/packages/vue/package.json @@ -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", diff --git a/packages/vue/src/iconify.ts b/packages/vue/src/iconify.ts index 915b738..0c9a9ca 100644 --- a/packages/vue/src/iconify.ts +++ b/packages/vue/src/iconify.ts @@ -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) { diff --git a/packages/vue/src/props.ts b/packages/vue/src/props.ts index e6154ae..1912633 100644 --- a/packages/vue/src/props.ts +++ b/packages/vue/src/props.ts @@ -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; } /** diff --git a/packages/vue/src/render.ts b/packages/vue/src/render.ts index 7e7f080..278670d 100644 --- a/packages/vue/src/render.ts +++ b/packages/vue/src/render.ts @@ -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; diff --git a/packages/vue/tests/api/20-rendering-from-api.test.js b/packages/vue/tests/api/20-rendering-from-api.test.js index c23c1b7..0639a52 100644 --- a/packages/vue/tests/api/20-rendering-from-api.test.js +++ b/packages/vue/tests/api/20-rendering-from-api.test.js @@ -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: ``, + template: ``, + 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', () => { '' ); + // 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', () => { '' ); + // 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: ``, + template: ``, + 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: ``, + template: ``, + methods: { + onLoad() { + throw new Error('onLoad called for empty icon!'); + }, + }, }; const wrapper = mount(Wrapper, {}); diff --git a/packages/vue/tests/api/30-changing-props.test.js b/packages/vue/tests/api/30-changing-props.test.js index f82f30d..061310d 100644 --- a/packages/vue/tests/api/30-changing-props.test.js +++ b/packages/vue/tests/api/30-changing-props.test.js @@ -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', () => { '' ); + // 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', () => { '' ); + // 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: ``, + template: ``, + 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 => { diff --git a/packages/vue/tests/iconify/10-basic.test.js b/packages/vue/tests/iconify/10-basic.test.js index bd04654..acc290c 100644 --- a/packages/vue/tests/iconify/10-basic.test.js +++ b/packages/vue/tests/iconify/10-basic.test.js @@ -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!'); + }, }, }); diff --git a/packages/vue/tests/iconify/20-inline.test.js b/packages/vue/tests/iconify/20-inline.test.js index d51fd4c..b36ffe8 100644 --- a/packages/vue/tests/iconify/20-inline.test.js +++ b/packages/vue/tests/iconify/20-inline.test.js @@ -25,7 +25,7 @@ describe('Inline attribute', () => { }); test('false string', () => { - // "false" = true + // "false" should be ignored const Wrapper = { components: { Icon }, template: ``, @@ -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', () => { diff --git a/packages/vue/tests/iconify/20-transformations.test.js b/packages/vue/tests/iconify/20-transformations.test.js index 020b3c9..f50c3e0 100644 --- a/packages/vue/tests/iconify/20-transformations.test.js +++ b/packages/vue/tests/iconify/20-transformations.test.js @@ -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: ``, + template: ``, data() { return { icon: iconData, diff --git a/packages/vue/tests/offline/20-inline.test.js b/packages/vue/tests/offline/20-inline.test.js index 87abf67..2ddd214 100644 --- a/packages/vue/tests/offline/20-inline.test.js +++ b/packages/vue/tests/offline/20-inline.test.js @@ -25,7 +25,7 @@ describe('Inline attribute', () => { }); test('false string', () => { - // "false" = true + // "false" should be ignored const Wrapper = { components: { Icon }, template: ``, @@ -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', () => { diff --git a/packages/vue/tests/offline/20-transformations.test.js b/packages/vue/tests/offline/20-transformations.test.js index aa009f1..824287d 100644 --- a/packages/vue/tests/offline/20-transformations.test.js +++ b/packages/vue/tests/offline/20-transformations.test.js @@ -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: ``, + template: ``, data() { return { icon: iconData, diff --git a/packages/vue2/package-lock.json b/packages/vue2/package-lock.json index 6e5f1c5..fc1aa13 100644 --- a/packages/vue2/package-lock.json +++ b/packages/vue2/package-lock.json @@ -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", diff --git a/packages/vue2/package.json b/packages/vue2/package.json index efa904a..0963d35 100644 --- a/packages/vue2/package.json +++ b/packages/vue2/package.json @@ -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", diff --git a/packages/vue2/src/iconify.ts b/packages/vue2/src/iconify.ts index cfea946..5f0b968 100644 --- a/packages/vue2/src/iconify.ts +++ b/packages/vue2/src/iconify.ts @@ -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; } 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) { diff --git a/packages/vue2/src/props.ts b/packages/vue2/src/props.ts index e6154ae..1912633 100644 --- a/packages/vue2/src/props.ts +++ b/packages/vue2/src/props.ts @@ -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; } /** diff --git a/packages/vue2/src/render.ts b/packages/vue2/src/render.ts index 6235d02..90a6ae4 100644 --- a/packages/vue2/src/render.ts +++ b/packages/vue2/src/render.ts @@ -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)[]; - /** * Render icon */ @@ -64,7 +64,7 @@ export const render = ( icon: Required ): 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; diff --git a/packages/vue2/tests/api/20-rendering-from-api.test.js b/packages/vue2/tests/api/20-rendering-from-api.test.js index b023bf0..08287fe 100644 --- a/packages/vue2/tests/api/20-rendering-from-api.test.js +++ b/packages/vue2/tests/api/20-rendering-from-api.test.js @@ -4,8 +4,7 @@ import { mockAPIData } from '@iconify/core/lib/api/modules/mock'; import { provider, nextPrefix } from './load'; const iconData = { - body: - '', + body: '', 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: ``, + template: ``, + 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', () => { '' ); + // 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', () => { '' ); + // 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: ``, + template: ``, + 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: ``, + template: ``, + methods: { + onLoad() { + throw new Error('onLoad called for empty icon!'); + }, + }, }; const wrapper = mount(Wrapper, {}); diff --git a/packages/vue2/tests/api/30-changing-props.test.js b/packages/vue2/tests/api/30-changing-props.test.js index 4de1020..8b6b8e8 100644 --- a/packages/vue2/tests/api/30-changing-props.test.js +++ b/packages/vue2/tests/api/30-changing-props.test.js @@ -4,15 +4,13 @@ import { mockAPIData } from '@iconify/core/lib/api/modules/mock'; import { provider, nextPrefix } from './load'; const iconData = { - body: - '', + body: '', width: 24, height: 24, }; const iconData2 = { - body: - '', + body: '', 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', () => { '' ); + // 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', () => { '' ); + // 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) => { diff --git a/packages/vue2/tests/iconify/10-basic.test.js b/packages/vue2/tests/iconify/10-basic.test.js index 6acc1b7..7cbcf06 100644 --- a/packages/vue2/tests/iconify/10-basic.test.js +++ b/packages/vue2/tests/iconify/10-basic.test.js @@ -2,8 +2,7 @@ import { mount } from '@vue/test-utils'; import { Icon } from '../../dist/iconify'; const iconData = { - body: - '', + body: '', 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!'); + }, }, }); diff --git a/packages/vue2/tests/iconify/20-inline.test.js b/packages/vue2/tests/iconify/20-inline.test.js index 15b11c6..8469f13 100644 --- a/packages/vue2/tests/iconify/20-inline.test.js +++ b/packages/vue2/tests/iconify/20-inline.test.js @@ -2,8 +2,7 @@ import { mount } from '@vue/test-utils'; import { Icon } from '../../dist/iconify'; const iconData = { - body: - '', + body: '', 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: ``, @@ -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', () => { diff --git a/packages/vue2/tests/iconify/20-transformations.test.js b/packages/vue2/tests/iconify/20-transformations.test.js index 020b3c9..5df5221 100644 --- a/packages/vue2/tests/iconify/20-transformations.test.js +++ b/packages/vue2/tests/iconify/20-transformations.test.js @@ -2,8 +2,7 @@ import { mount } from '@vue/test-utils'; import { Icon } from '../../dist/iconify'; const iconData = { - body: - '', + body: '', 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: ``, + template: ``, data() { return { icon: iconData, diff --git a/packages/vue2/tests/offline/20-inline.test.js b/packages/vue2/tests/offline/20-inline.test.js index 3c235b7..751658c 100644 --- a/packages/vue2/tests/offline/20-inline.test.js +++ b/packages/vue2/tests/offline/20-inline.test.js @@ -2,8 +2,7 @@ import { mount } from '@vue/test-utils'; import { Icon } from '../../dist/offline'; const iconData = { - body: - '', + body: '', 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: ``, @@ -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', () => { diff --git a/packages/vue2/tests/offline/20-transformations.test.js b/packages/vue2/tests/offline/20-transformations.test.js index aa009f1..5ab3f4a 100644 --- a/packages/vue2/tests/offline/20-transformations.test.js +++ b/packages/vue2/tests/offline/20-transformations.test.js @@ -2,8 +2,7 @@ import { mount } from '@vue/test-utils'; import { Icon } from '../../dist/offline'; const iconData = { - body: - '', + body: '', 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: ``, + template: ``, data() { return { icon: iconData,