mirror of
https://github.com/iconify/iconify.git
synced 2025-01-08 15:54:09 +00:00
Add Vue 3 component with API support
This commit is contained in:
parent
5f2e7da715
commit
66b1a26161
@ -15,7 +15,8 @@
|
|||||||
"build": "node build",
|
"build": "node build",
|
||||||
"build:lib": "tsc -b",
|
"build:lib": "tsc -b",
|
||||||
"build:dist": "rollup -c rollup.config.js",
|
"build:dist": "rollup -c rollup.config.js",
|
||||||
"build:api": "api-extractor run --local --verbose --config api-extractor.offline.json",
|
"prebuild:api": "api-extractor run --local --verbose --config api-extractor.offline.json",
|
||||||
|
"build:api": "api-extractor run --local --verbose --config api-extractor.iconify.json",
|
||||||
"pretest": "npm run build",
|
"pretest": "npm run build",
|
||||||
"test": "jest"
|
"test": "jest"
|
||||||
},
|
},
|
||||||
|
@ -2,7 +2,7 @@ import resolve from '@rollup/plugin-node-resolve';
|
|||||||
import commonjs from '@rollup/plugin-commonjs';
|
import commonjs from '@rollup/plugin-commonjs';
|
||||||
import buble from '@rollup/plugin-buble';
|
import buble from '@rollup/plugin-buble';
|
||||||
|
|
||||||
const names = ['offline']; //, 'iconify'];
|
const names = ['offline', 'iconify'];
|
||||||
const component = 'Icon';
|
const component = 'Icon';
|
||||||
|
|
||||||
const config = [];
|
const config = [];
|
||||||
|
412
packages/vue/src/iconify.ts
Normal file
412
packages/vue/src/iconify.ts
Normal file
@ -0,0 +1,412 @@
|
|||||||
|
import { defineComponent } from 'vue';
|
||||||
|
import {
|
||||||
|
DefineComponent,
|
||||||
|
ComponentOptionsMixin,
|
||||||
|
EmitsOptions,
|
||||||
|
VNodeProps,
|
||||||
|
AllowedComponentProps,
|
||||||
|
ComponentCustomProps,
|
||||||
|
} from 'vue';
|
||||||
|
|
||||||
|
import { IconifyJSON } from '@iconify/types';
|
||||||
|
|
||||||
|
// Core
|
||||||
|
import { IconifyIconName } from '@iconify/core/lib/icon/name';
|
||||||
|
import {
|
||||||
|
IconifyIconSize,
|
||||||
|
IconifyHorizontalIconAlignment,
|
||||||
|
IconifyVerticalIconAlignment,
|
||||||
|
} from '@iconify/core/lib/customisations';
|
||||||
|
import {
|
||||||
|
IconifyStorageFunctions,
|
||||||
|
storageFunctions,
|
||||||
|
getIconData,
|
||||||
|
allowSimpleNames,
|
||||||
|
} from '@iconify/core/lib/storage/functions';
|
||||||
|
import {
|
||||||
|
IconifyBuilderFunctions,
|
||||||
|
builderFunctions,
|
||||||
|
} from '@iconify/core/lib/builder/functions';
|
||||||
|
import { fullIcon, IconifyIcon } from '@iconify/core/lib/icon';
|
||||||
|
|
||||||
|
// Modules
|
||||||
|
import { coreModules } from '@iconify/core/lib/modules';
|
||||||
|
|
||||||
|
// API
|
||||||
|
import { API, IconifyAPIInternalStorage } from '@iconify/core/lib/api/';
|
||||||
|
import {
|
||||||
|
IconifyAPIFunctions,
|
||||||
|
IconifyAPIInternalFunctions,
|
||||||
|
APIFunctions,
|
||||||
|
APIInternalFunctions,
|
||||||
|
} from '@iconify/core/lib/api/functions';
|
||||||
|
import {
|
||||||
|
setAPIModule,
|
||||||
|
IconifyAPIModule,
|
||||||
|
IconifyAPISendQuery,
|
||||||
|
IconifyAPIPrepareQuery,
|
||||||
|
GetIconifyAPIModule,
|
||||||
|
} from '@iconify/core/lib/api/modules';
|
||||||
|
import { getAPIModule as getJSONPAPIModule } from '@iconify/core/lib/api/modules/jsonp';
|
||||||
|
import {
|
||||||
|
getAPIModule as getFetchAPIModule,
|
||||||
|
setFetch,
|
||||||
|
} from '@iconify/core/lib/api/modules/fetch';
|
||||||
|
import {
|
||||||
|
setAPIConfig,
|
||||||
|
PartialIconifyAPIConfig,
|
||||||
|
IconifyAPIConfig,
|
||||||
|
getAPIConfig,
|
||||||
|
GetAPIConfig,
|
||||||
|
} from '@iconify/core/lib/api/config';
|
||||||
|
import {
|
||||||
|
IconifyIconLoaderCallback,
|
||||||
|
IconifyIconLoaderAbort,
|
||||||
|
} from '@iconify/core/lib/interfaces/loader';
|
||||||
|
|
||||||
|
// Cache
|
||||||
|
import { storeCache, loadCache } from '@iconify/core/lib/browser-storage';
|
||||||
|
import { toggleBrowserCache } from '@iconify/core/lib/browser-storage/functions';
|
||||||
|
import {
|
||||||
|
IconifyBrowserCacheType,
|
||||||
|
IconifyBrowserCacheFunctions,
|
||||||
|
} from '@iconify/core/lib/browser-storage/functions';
|
||||||
|
|
||||||
|
// Properties
|
||||||
|
import {
|
||||||
|
IconProps,
|
||||||
|
IconifyIconCustomisations,
|
||||||
|
IconifyIconProps,
|
||||||
|
} from './props';
|
||||||
|
|
||||||
|
// Render SVG
|
||||||
|
import { render } from './render';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Export required types
|
||||||
|
*/
|
||||||
|
// Function sets
|
||||||
|
export {
|
||||||
|
IconifyStorageFunctions,
|
||||||
|
IconifyBuilderFunctions,
|
||||||
|
IconifyBrowserCacheFunctions,
|
||||||
|
IconifyAPIFunctions,
|
||||||
|
IconifyAPIInternalFunctions,
|
||||||
|
};
|
||||||
|
|
||||||
|
// JSON stuff
|
||||||
|
export { IconifyIcon, IconifyJSON, IconifyIconName };
|
||||||
|
|
||||||
|
// Customisations
|
||||||
|
export {
|
||||||
|
IconifyIconCustomisations,
|
||||||
|
IconifyIconSize,
|
||||||
|
IconifyHorizontalIconAlignment,
|
||||||
|
IconifyVerticalIconAlignment,
|
||||||
|
IconifyIconProps,
|
||||||
|
IconProps,
|
||||||
|
};
|
||||||
|
|
||||||
|
// API
|
||||||
|
export {
|
||||||
|
IconifyAPIConfig,
|
||||||
|
IconifyIconLoaderCallback,
|
||||||
|
IconifyIconLoaderAbort,
|
||||||
|
IconifyAPIInternalStorage,
|
||||||
|
IconifyAPIModule,
|
||||||
|
GetAPIConfig,
|
||||||
|
IconifyAPIPrepareQuery,
|
||||||
|
IconifyAPISendQuery,
|
||||||
|
PartialIconifyAPIConfig,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Browser cache */
|
||||||
|
export { IconifyBrowserCacheType };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable and disable browser cache
|
||||||
|
*/
|
||||||
|
export const enableCache = (storage: IconifyBrowserCacheType) =>
|
||||||
|
toggleBrowserCache(storage, true);
|
||||||
|
|
||||||
|
export const disableCache = (storage: IconifyBrowserCacheType) =>
|
||||||
|
toggleBrowserCache(storage, false);
|
||||||
|
|
||||||
|
/* Storage functions */
|
||||||
|
/**
|
||||||
|
* Check if icon exists
|
||||||
|
*/
|
||||||
|
export const iconExists = storageFunctions.iconExists;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get icon data
|
||||||
|
*/
|
||||||
|
export const getIcon = storageFunctions.getIcon;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List available icons
|
||||||
|
*/
|
||||||
|
export const listIcons = storageFunctions.listIcons;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add one icon
|
||||||
|
*/
|
||||||
|
export const addIcon = storageFunctions.addIcon;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add icon set
|
||||||
|
*/
|
||||||
|
export const addCollection = storageFunctions.addCollection;
|
||||||
|
|
||||||
|
/* Builder functions */
|
||||||
|
/**
|
||||||
|
* Calculate icon size
|
||||||
|
*/
|
||||||
|
export const calculateSize = builderFunctions.calculateSize;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replace unique ids in content
|
||||||
|
*/
|
||||||
|
export const replaceIDs = builderFunctions.replaceIDs;
|
||||||
|
|
||||||
|
/* API functions */
|
||||||
|
/**
|
||||||
|
* Load icons
|
||||||
|
*/
|
||||||
|
export const loadIcons = APIFunctions.loadIcons;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add API provider
|
||||||
|
*/
|
||||||
|
export const addAPIProvider = APIFunctions.addAPIProvider;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Export internal functions that can be used by third party implementations
|
||||||
|
*/
|
||||||
|
export const _api = APIInternalFunctions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialise stuff
|
||||||
|
*/
|
||||||
|
// Enable short names
|
||||||
|
allowSimpleNames(true);
|
||||||
|
|
||||||
|
// Set API
|
||||||
|
coreModules.api = API;
|
||||||
|
|
||||||
|
// Use Fetch API by default
|
||||||
|
let getAPIModule: GetIconifyAPIModule = getFetchAPIModule;
|
||||||
|
try {
|
||||||
|
if (typeof document !== 'undefined' && typeof window !== 'undefined') {
|
||||||
|
// If window and document exist, attempt to load whatever module is available, otherwise use Fetch API
|
||||||
|
getAPIModule =
|
||||||
|
typeof fetch === 'function' && typeof Promise === 'function'
|
||||||
|
? getFetchAPIModule
|
||||||
|
: getJSONPAPIModule;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
setAPIModule('', getAPIModule(getAPIConfig));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function to enable node-fetch for getting icons on server side
|
||||||
|
*/
|
||||||
|
export function setNodeFetch(nodeFetch: typeof fetch) {
|
||||||
|
setFetch(nodeFetch);
|
||||||
|
if (getAPIModule !== getFetchAPIModule) {
|
||||||
|
getAPIModule = getFetchAPIModule;
|
||||||
|
setAPIModule('', getAPIModule(getAPIConfig));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Browser stuff
|
||||||
|
*/
|
||||||
|
if (typeof document !== 'undefined' && typeof window !== 'undefined') {
|
||||||
|
// Set cache and load existing cache
|
||||||
|
coreModules.cache = storeCache;
|
||||||
|
loadCache();
|
||||||
|
|
||||||
|
const _window = window;
|
||||||
|
|
||||||
|
// Load icons from global "IconifyPreload"
|
||||||
|
interface WindowWithIconifyPreload {
|
||||||
|
IconifyPreload: IconifyJSON[] | IconifyJSON;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
((_window as unknown) as WindowWithIconifyPreload).IconifyPreload !==
|
||||||
|
void 0
|
||||||
|
) {
|
||||||
|
const preload = ((_window as unknown) as WindowWithIconifyPreload)
|
||||||
|
.IconifyPreload;
|
||||||
|
const err = 'Invalid IconifyPreload syntax.';
|
||||||
|
if (typeof preload === 'object' && preload !== null) {
|
||||||
|
(preload instanceof Array ? preload : [preload]).forEach(item => {
|
||||||
|
try {
|
||||||
|
if (
|
||||||
|
// Check if item is an object and not null/array
|
||||||
|
typeof item !== 'object' ||
|
||||||
|
item === null ||
|
||||||
|
item instanceof Array ||
|
||||||
|
// Check for 'icons' and 'prefix'
|
||||||
|
typeof item.icons !== 'object' ||
|
||||||
|
typeof item.prefix !== 'string' ||
|
||||||
|
// Add icon set
|
||||||
|
!addCollection(item)
|
||||||
|
) {
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set API from global "IconifyProviders"
|
||||||
|
interface WindowWithIconifyProviders {
|
||||||
|
IconifyProviders: Record<string, PartialIconifyAPIConfig>;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
((_window as unknown) as WindowWithIconifyProviders)
|
||||||
|
.IconifyProviders !== void 0
|
||||||
|
) {
|
||||||
|
const providers = ((_window as unknown) as WindowWithIconifyProviders)
|
||||||
|
.IconifyProviders;
|
||||||
|
if (typeof providers === 'object' && providers !== null) {
|
||||||
|
for (let key in providers) {
|
||||||
|
const err = 'IconifyProviders[' + key + '] is invalid.';
|
||||||
|
try {
|
||||||
|
const value = providers[key];
|
||||||
|
if (
|
||||||
|
typeof value !== 'object' ||
|
||||||
|
!value ||
|
||||||
|
value.resources === void 0
|
||||||
|
) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!setAPIConfig(key, value)) {
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component
|
||||||
|
*/
|
||||||
|
export const Icon = defineComponent({
|
||||||
|
// Do not inherit other attributes: it is handled by render()
|
||||||
|
inheritAttrs: false,
|
||||||
|
|
||||||
|
// Set initial data
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
// Mounted status
|
||||||
|
mounted: false,
|
||||||
|
|
||||||
|
// Callback counter to trigger re-render
|
||||||
|
counter: 0,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
beforeMount() {
|
||||||
|
// Current icon name
|
||||||
|
this._name = '';
|
||||||
|
|
||||||
|
// Loading
|
||||||
|
this._loadingIcon = null;
|
||||||
|
|
||||||
|
// Mark as mounted
|
||||||
|
this.mounted = true;
|
||||||
|
},
|
||||||
|
|
||||||
|
unmounted() {
|
||||||
|
this.mounted = false;
|
||||||
|
if (this._loadingIcon) {
|
||||||
|
this._loadingIcon.abort();
|
||||||
|
this._loadingIcon = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
abortLoading() {
|
||||||
|
if (this._loadingIcon) {
|
||||||
|
this._loadingIcon.abort();
|
||||||
|
this._loadingIcon = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// Get data for icon to render or null
|
||||||
|
getIcon(icon) {
|
||||||
|
// Icon is an object
|
||||||
|
if (
|
||||||
|
typeof icon === 'object' &&
|
||||||
|
icon !== null &&
|
||||||
|
typeof icon.body === 'string'
|
||||||
|
) {
|
||||||
|
// Stop loading
|
||||||
|
this._name = '';
|
||||||
|
this.abortLoading();
|
||||||
|
return fullIcon(icon);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Invalid icon?
|
||||||
|
if (typeof icon !== 'string') {
|
||||||
|
this.abortLoading();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load icon
|
||||||
|
const data = getIconData(icon);
|
||||||
|
if (data === null) {
|
||||||
|
// Icon needs to be loaded
|
||||||
|
if (!this._loadingIcon || this._loadingIcon.name !== icon) {
|
||||||
|
// New icon to load
|
||||||
|
this.abortLoading();
|
||||||
|
this._name = '';
|
||||||
|
this._loadingIcon = {
|
||||||
|
name: icon,
|
||||||
|
abort: API.loadIcons([icon], () => {
|
||||||
|
this.counter++;
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Icon data is available
|
||||||
|
this._name = icon;
|
||||||
|
this.abortLoading();
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// Render icon
|
||||||
|
render() {
|
||||||
|
if (!this.mounted) {
|
||||||
|
return this.$slots.default ? this.$slots.default() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Re-render when counter changes
|
||||||
|
this.counter;
|
||||||
|
|
||||||
|
// Get icon data
|
||||||
|
const props = this.$attrs;
|
||||||
|
|
||||||
|
// Check icon
|
||||||
|
const icon = this.getIcon(props.icon);
|
||||||
|
|
||||||
|
// Validate icon object
|
||||||
|
if (!icon) {
|
||||||
|
return this.$slots.default ? this.$slots.default() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Valid icon: render it
|
||||||
|
return render(icon, props);
|
||||||
|
},
|
||||||
|
});
|
@ -1,14 +1,22 @@
|
|||||||
import type { VNode } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
import { h } from 'vue';
|
import {
|
||||||
import type { IconifyIcon, IconifyJSON } from '@iconify/types';
|
VNode,
|
||||||
import type {
|
DefineComponent,
|
||||||
|
ComponentOptionsMixin,
|
||||||
|
EmitsOptions,
|
||||||
|
VNodeProps,
|
||||||
|
AllowedComponentProps,
|
||||||
|
ComponentCustomProps,
|
||||||
|
} from 'vue';
|
||||||
|
import { IconifyIcon, IconifyJSON } from '@iconify/types';
|
||||||
|
import {
|
||||||
IconifyHorizontalIconAlignment,
|
IconifyHorizontalIconAlignment,
|
||||||
IconifyVerticalIconAlignment,
|
IconifyVerticalIconAlignment,
|
||||||
IconifyIconSize,
|
IconifyIconSize,
|
||||||
} from '@iconify/core/lib/customisations';
|
} from '@iconify/core/lib/customisations';
|
||||||
import { fullIcon } from '@iconify/core/lib/icon';
|
import { fullIcon } from '@iconify/core/lib/icon';
|
||||||
import { parseIconSet } from '@iconify/core/lib/icon/icon-set';
|
import { parseIconSet } from '@iconify/core/lib/icon/icon-set';
|
||||||
import type {
|
import {
|
||||||
IconifyIconCustomisations,
|
IconifyIconCustomisations,
|
||||||
IconifyIconProps,
|
IconifyIconProps,
|
||||||
IconProps,
|
IconProps,
|
||||||
@ -72,7 +80,32 @@ export function addCollection(
|
|||||||
/**
|
/**
|
||||||
* Component
|
* Component
|
||||||
*/
|
*/
|
||||||
export const Icon = {
|
/*
|
||||||
|
// Currently this cannot be used because in functional component style is inherited, overriding style generated by render()
|
||||||
|
export function Icon(props: Record<string, unknown>, context): VNode {
|
||||||
|
// Check icon
|
||||||
|
const icon =
|
||||||
|
typeof props.icon === 'string'
|
||||||
|
? storage[props.icon]
|
||||||
|
: typeof props.icon === 'object'
|
||||||
|
? fullIcon(props.icon as IconifyIcon)
|
||||||
|
: null;
|
||||||
|
|
||||||
|
// Validate icon object
|
||||||
|
if (
|
||||||
|
icon === null ||
|
||||||
|
typeof icon !== 'object' ||
|
||||||
|
typeof icon.body !== 'string'
|
||||||
|
) {
|
||||||
|
return context.slots.default ? context.slots.default() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Valid icon: render it
|
||||||
|
return render(icon, context.attrs as IconProps);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
export const Icon = defineComponent({
|
||||||
// Do not inherit other attributes: it is handled by render()
|
// Do not inherit other attributes: it is handled by render()
|
||||||
inheritAttrs: false,
|
inheritAttrs: false,
|
||||||
|
|
||||||
@ -100,4 +133,4 @@ export const Icon = {
|
|||||||
// Valid icon: render it
|
// Valid icon: render it
|
||||||
return render(icon, props);
|
return render(icon, props);
|
||||||
},
|
},
|
||||||
};
|
});
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import type { IconifyIcon } from '@iconify/types';
|
import { IconifyIcon } from '@iconify/types';
|
||||||
import type { IconifyIconCustomisations as IconCustomisations } from '@iconify/core/lib/customisations';
|
import { IconifyIconCustomisations as IconCustomisations } from '@iconify/core/lib/customisations';
|
||||||
|
|
||||||
// Allow rotation to be string
|
// Allow rotation to be string
|
||||||
/**
|
/**
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import type { VNode } from 'vue';
|
import { h, VNode } from 'vue';
|
||||||
import { h } from 'vue';
|
import { IconifyIcon } from '@iconify/types';
|
||||||
import type { IconifyIcon } from '@iconify/types';
|
import {
|
||||||
import type { FullIconCustomisations } from '@iconify/core/lib/customisations';
|
FullIconCustomisations,
|
||||||
import { defaults } from '@iconify/core/lib/customisations';
|
defaults,
|
||||||
|
} from '@iconify/core/lib/customisations';
|
||||||
import {
|
import {
|
||||||
flipFromString,
|
flipFromString,
|
||||||
alignmentFromString,
|
alignmentFromString,
|
||||||
@ -11,7 +12,7 @@ import { rotateFromString } from '@iconify/core/lib/customisations/rotate';
|
|||||||
import { iconToSVG } from '@iconify/core/lib/builder';
|
import { iconToSVG } from '@iconify/core/lib/builder';
|
||||||
import { replaceIDs } from '@iconify/core/lib/builder/ids';
|
import { replaceIDs } from '@iconify/core/lib/builder/ids';
|
||||||
import { merge } from '@iconify/core/lib/misc/merge';
|
import { merge } from '@iconify/core/lib/misc/merge';
|
||||||
import type { IconifyIconCustomisations, IconProps } from './props';
|
import { IconifyIconCustomisations, IconProps } from './props';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default SVG attributes
|
* Default SVG attributes
|
||||||
@ -28,8 +29,8 @@ const svgDefaults: Record<string, unknown> = {
|
|||||||
* In Vue 'v-' properties are reserved, so v-align and v-flip must be renamed
|
* In Vue 'v-' properties are reserved, so v-align and v-flip must be renamed
|
||||||
*/
|
*/
|
||||||
let customisationAliases = {};
|
let customisationAliases = {};
|
||||||
['horizontal', 'vertical'].forEach((prefix) => {
|
['horizontal', 'vertical'].forEach(prefix => {
|
||||||
['Align', 'Flip'].forEach((suffix) => {
|
['Align', 'Flip'].forEach(suffix => {
|
||||||
const attr = prefix.slice(0, 1) + suffix;
|
const attr = prefix.slice(0, 1) + suffix;
|
||||||
// vertical-align
|
// vertical-align
|
||||||
customisationAliases[prefix + '-' + suffix.toLowerCase()] = attr;
|
customisationAliases[prefix + '-' + suffix.toLowerCase()] = attr;
|
||||||
@ -44,24 +45,9 @@ let customisationAliases = {};
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface for inline style
|
* Interface for inline style
|
||||||
|
* Support for strings and arrays has been removed.
|
||||||
*/
|
*/
|
||||||
type VStyleObject = Record<string, unknown>;
|
type VStyle = Record<string, unknown>;
|
||||||
interface VStyleAsString {
|
|
||||||
type: 'string';
|
|
||||||
style: string;
|
|
||||||
}
|
|
||||||
interface VStyleAsArray {
|
|
||||||
type: 'array';
|
|
||||||
style: VStyleObject[];
|
|
||||||
}
|
|
||||||
type VStyle = VStyleAsString | VStyleAsArray;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TypeScript guard, never used
|
|
||||||
*/
|
|
||||||
function assertNever(value: never) {
|
|
||||||
// Do nothing
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Render icon
|
* Render icon
|
||||||
@ -81,41 +67,12 @@ export const render = (
|
|||||||
const componentProps = merge(svgDefaults);
|
const componentProps = merge(svgDefaults);
|
||||||
|
|
||||||
// Copy style
|
// Copy style
|
||||||
let style: VStyle;
|
let style: VStyle =
|
||||||
let hasStyle = true;
|
typeof props.style === 'object' && !(props.style instanceof Array)
|
||||||
if (typeof props.style === 'string') {
|
? merge(props.style as VStyle)
|
||||||
// String: copy it
|
: {};
|
||||||
style = {
|
|
||||||
type: 'string',
|
|
||||||
style: props.style,
|
|
||||||
};
|
|
||||||
} else if (
|
|
||||||
typeof props.style === 'object' &&
|
|
||||||
props.style instanceof Array
|
|
||||||
) {
|
|
||||||
// Array of objects
|
|
||||||
style = {
|
|
||||||
type: 'array',
|
|
||||||
style: props.style.slice(0),
|
|
||||||
};
|
|
||||||
} else if (typeof props.style === 'object' && props.style !== null) {
|
|
||||||
// Object
|
|
||||||
style = {
|
|
||||||
type: 'array',
|
|
||||||
style: [props.style as VStyleObject],
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
// No style
|
|
||||||
style = {
|
|
||||||
type: 'string',
|
|
||||||
style: '',
|
|
||||||
};
|
|
||||||
hasStyle = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get element properties
|
// Get element properties
|
||||||
let styleType: typeof style.type;
|
|
||||||
let styleString: string;
|
|
||||||
for (let key in props) {
|
for (let key in props) {
|
||||||
const value = props[key];
|
const value = props[key];
|
||||||
switch (key) {
|
switch (key) {
|
||||||
@ -138,32 +95,9 @@ export const render = (
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Color: append to style
|
// Color: override style
|
||||||
case 'color':
|
case 'color':
|
||||||
styleType = style.type;
|
style.color = value;
|
||||||
switch (styleType) {
|
|
||||||
case 'string':
|
|
||||||
styleString = (style as VStyleAsString).style;
|
|
||||||
(style as VStyleAsString).style =
|
|
||||||
styleString +
|
|
||||||
(styleString.length > 0 && styleString.trim().slice(-1) !== ';'
|
|
||||||
? ';'
|
|
||||||
: '') +
|
|
||||||
'color: ' +
|
|
||||||
value +
|
|
||||||
'; ';
|
|
||||||
hasStyle = true;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'array':
|
|
||||||
(style as VStyleAsArray).style.push({
|
|
||||||
color: value,
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
assertNever(styleType);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Rotation as string
|
// Rotation as string
|
||||||
@ -171,7 +105,7 @@ export const render = (
|
|||||||
if (typeof value === 'string') {
|
if (typeof value === 'string') {
|
||||||
customisations[key] = rotateFromString(value);
|
customisations[key] = rotateFromString(value);
|
||||||
} else if (typeof value === 'number') {
|
} else if (typeof value === 'number') {
|
||||||
componentProps[key] = value;
|
customisations[key] = value;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -203,24 +137,12 @@ export const render = (
|
|||||||
componentProps[key] = item.attributes[key];
|
componentProps[key] = item.attributes[key];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item.inline) {
|
if (
|
||||||
styleType = style.type;
|
item.inline &&
|
||||||
switch (styleType) {
|
style.verticalAlign === void 0 &&
|
||||||
case 'string':
|
style['vertical-align'] === void 0
|
||||||
(style as VStyleAsString).style =
|
) {
|
||||||
'vertical-align: -0.125em; ' + style.style;
|
style.verticalAlign = '-0.125em';
|
||||||
hasStyle = true;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'array':
|
|
||||||
(style as VStyleAsArray).style.unshift({
|
|
||||||
verticalAlign: '-0.125em',
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
assertNever(styleType);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Counter for ids based on "id" property to render icons consistently on server and client
|
// Counter for ids based on "id" property to render icons consistently on server and client
|
||||||
@ -232,8 +154,8 @@ export const render = (
|
|||||||
item.body,
|
item.body,
|
||||||
id ? () => id + '-' + localCounter++ : 'iconify-vue-'
|
id ? () => id + '-' + localCounter++ : 'iconify-vue-'
|
||||||
);
|
);
|
||||||
if (hasStyle) {
|
if (Object.keys(style).length > 0) {
|
||||||
componentProps['style'] = style.style;
|
componentProps['style'] = style;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render icon
|
// Render icon
|
||||||
|
40
packages/vue/tests/api/10-api-mock.test.js
Normal file
40
packages/vue/tests/api/10-api-mock.test.js
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import { loadIcons, iconExists } from '../../dist/iconify';
|
||||||
|
import { mockAPIData } from '@iconify/core/lib/api/modules/mock';
|
||||||
|
import { provider, nextPrefix } from './load';
|
||||||
|
|
||||||
|
describe('Testing fake API', () => {
|
||||||
|
test('using fake API to load icon', done => {
|
||||||
|
const prefix = nextPrefix();
|
||||||
|
const name = 'mock-test';
|
||||||
|
const iconName = `@${provider}:${prefix}:${name}`;
|
||||||
|
mockAPIData({
|
||||||
|
provider,
|
||||||
|
prefix,
|
||||||
|
response: {
|
||||||
|
prefix,
|
||||||
|
icons: {
|
||||||
|
[name]: {
|
||||||
|
body: '<g />',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Check if icon has been loaded
|
||||||
|
expect(iconExists(iconName)).toEqual(false);
|
||||||
|
|
||||||
|
// Load icon
|
||||||
|
loadIcons([iconName], (loaded, missing, pending) => {
|
||||||
|
expect(loaded).toMatchObject([
|
||||||
|
{
|
||||||
|
provider,
|
||||||
|
prefix,
|
||||||
|
name,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
expect(missing).toMatchObject([]);
|
||||||
|
expect(pending).toMatchObject([]);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
158
packages/vue/tests/api/20-rendering-from-api.test.js
Normal file
158
packages/vue/tests/api/20-rendering-from-api.test.js
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
import { mount } from '@vue/test-utils';
|
||||||
|
import { Icon, loadIcons, iconExists } from '../../dist/iconify';
|
||||||
|
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"/>',
|
||||||
|
width: 24,
|
||||||
|
height: 24,
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('Rendering icon', () => {
|
||||||
|
test('rendering icon after loading it', done => {
|
||||||
|
const prefix = nextPrefix();
|
||||||
|
const name = 'render-test';
|
||||||
|
const iconName = `@${provider}:${prefix}:${name}`;
|
||||||
|
mockAPIData({
|
||||||
|
provider,
|
||||||
|
prefix,
|
||||||
|
response: {
|
||||||
|
prefix,
|
||||||
|
icons: {
|
||||||
|
[name]: iconData,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Check if icon has been loaded
|
||||||
|
expect(iconExists(iconName)).toEqual(false);
|
||||||
|
|
||||||
|
// Load icon
|
||||||
|
loadIcons([iconName], (loaded, missing, pending) => {
|
||||||
|
// Make sure icon has been loaded
|
||||||
|
expect(loaded).toMatchObject([
|
||||||
|
{
|
||||||
|
provider,
|
||||||
|
prefix,
|
||||||
|
name,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
expect(missing).toMatchObject([]);
|
||||||
|
expect(pending).toMatchObject([]);
|
||||||
|
expect(iconExists(iconName)).toEqual(true);
|
||||||
|
|
||||||
|
// Render component
|
||||||
|
const Wrapper = {
|
||||||
|
components: { Icon },
|
||||||
|
template: `<Icon icon="${iconName}" />`,
|
||||||
|
};
|
||||||
|
const wrapper = mount(Wrapper, {});
|
||||||
|
const html = wrapper.html().replace(/\s*\n\s*/g, '');
|
||||||
|
|
||||||
|
// Check HTML
|
||||||
|
expect(html).toEqual(
|
||||||
|
'<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>'
|
||||||
|
);
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('rendering icon before loading it', done => {
|
||||||
|
const prefix = nextPrefix();
|
||||||
|
const name = 'mock-test';
|
||||||
|
const iconName = `@${provider}:${prefix}:${name}`;
|
||||||
|
mockAPIData({
|
||||||
|
provider,
|
||||||
|
prefix,
|
||||||
|
response: {
|
||||||
|
prefix,
|
||||||
|
icons: {
|
||||||
|
[name]: iconData,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
delay: next => {
|
||||||
|
// Icon should not have loaded yet
|
||||||
|
expect(iconExists(iconName)).toEqual(false);
|
||||||
|
|
||||||
|
// Send icon data
|
||||||
|
next();
|
||||||
|
|
||||||
|
// Test it again
|
||||||
|
expect(iconExists(iconName)).toEqual(true);
|
||||||
|
|
||||||
|
// Check if state was changed
|
||||||
|
// Wrapped in double setTimeout() because re-render takes 2 ticks
|
||||||
|
setTimeout(() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
// Check HTML
|
||||||
|
expect(wrapper.html().replace(/\s*\n\s*/g, '')).toEqual(
|
||||||
|
'<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>'
|
||||||
|
);
|
||||||
|
|
||||||
|
done();
|
||||||
|
}, 0);
|
||||||
|
}, 0);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Check if icon has been loaded
|
||||||
|
expect(iconExists(iconName)).toEqual(false);
|
||||||
|
|
||||||
|
// Render component
|
||||||
|
const Wrapper = {
|
||||||
|
components: { Icon },
|
||||||
|
template: `<Icon icon="${iconName}" />`,
|
||||||
|
};
|
||||||
|
const wrapper = mount(Wrapper, {});
|
||||||
|
|
||||||
|
// Should render empty icon
|
||||||
|
expect(wrapper.html()).toEqual('<!---->');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('missing icon', done => {
|
||||||
|
const prefix = nextPrefix();
|
||||||
|
const name = 'missing-icon';
|
||||||
|
const iconName = `@${provider}:${prefix}:${name}`;
|
||||||
|
mockAPIData({
|
||||||
|
provider,
|
||||||
|
prefix,
|
||||||
|
response: 404,
|
||||||
|
delay: next => {
|
||||||
|
// Icon should not have loaded yet
|
||||||
|
expect(iconExists(iconName)).toEqual(false);
|
||||||
|
|
||||||
|
// Send icon data
|
||||||
|
next();
|
||||||
|
|
||||||
|
// Test it again
|
||||||
|
expect(iconExists(iconName)).toEqual(false);
|
||||||
|
|
||||||
|
// Check if state was changed
|
||||||
|
// Wrapped in double setTimeout() because re-render takes 2 ticks
|
||||||
|
setTimeout(() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
expect(wrapper.html()).toEqual('<!---->');
|
||||||
|
|
||||||
|
done();
|
||||||
|
}, 0);
|
||||||
|
}, 0);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Check if icon has been loaded
|
||||||
|
expect(iconExists(iconName)).toEqual(false);
|
||||||
|
|
||||||
|
// Render component
|
||||||
|
const Wrapper = {
|
||||||
|
components: { Icon },
|
||||||
|
template: `<Icon icon="${iconName}" />`,
|
||||||
|
};
|
||||||
|
const wrapper = mount(Wrapper, {});
|
||||||
|
|
||||||
|
// Should render empty icon
|
||||||
|
expect(wrapper.html()).toEqual('<!---->');
|
||||||
|
});
|
||||||
|
});
|
264
packages/vue/tests/api/30-changing-props.test.js
Normal file
264
packages/vue/tests/api/30-changing-props.test.js
Normal file
@ -0,0 +1,264 @@
|
|||||||
|
import { mount } from '@vue/test-utils';
|
||||||
|
import { Icon, iconExists } from '../../dist/iconify';
|
||||||
|
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"/>',
|
||||||
|
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"/>',
|
||||||
|
width: 32,
|
||||||
|
height: 32,
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('Rendering icon', () => {
|
||||||
|
test('changing icon property', done => {
|
||||||
|
const prefix = nextPrefix();
|
||||||
|
const name = 'changing-prop';
|
||||||
|
const name2 = 'changing-prop2';
|
||||||
|
const iconName = `@${provider}:${prefix}:${name}`;
|
||||||
|
const iconName2 = `@${provider}:${prefix}:${name2}`;
|
||||||
|
|
||||||
|
mockAPIData({
|
||||||
|
provider,
|
||||||
|
prefix,
|
||||||
|
response: {
|
||||||
|
prefix,
|
||||||
|
icons: {
|
||||||
|
[name]: iconData,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
delay: next => {
|
||||||
|
// Icon should not have loaded yet
|
||||||
|
expect(iconExists(iconName)).toEqual(false);
|
||||||
|
|
||||||
|
// Send icon data
|
||||||
|
next();
|
||||||
|
|
||||||
|
// Test it again
|
||||||
|
expect(iconExists(iconName)).toEqual(true);
|
||||||
|
|
||||||
|
// Check if state was changed
|
||||||
|
// Wrapped in double setTimeout() because re-render takes 2 ticks (one to handle API response, one to re-render)
|
||||||
|
setTimeout(() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
expect(wrapper.html().replace(/\s*\n\s*/g, '')).toEqual(
|
||||||
|
'<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>'
|
||||||
|
);
|
||||||
|
|
||||||
|
wrapper.setProps({
|
||||||
|
icon: iconName2,
|
||||||
|
});
|
||||||
|
}, 0);
|
||||||
|
}, 0);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
mockAPIData({
|
||||||
|
provider,
|
||||||
|
prefix,
|
||||||
|
response: {
|
||||||
|
prefix,
|
||||||
|
icons: {
|
||||||
|
[name2]: iconData2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
delay: next => {
|
||||||
|
// Icon should not have loaded yet
|
||||||
|
expect(iconExists(iconName2)).toEqual(false);
|
||||||
|
|
||||||
|
// Send icon data
|
||||||
|
next();
|
||||||
|
|
||||||
|
// Test it again
|
||||||
|
expect(iconExists(iconName2)).toEqual(true);
|
||||||
|
|
||||||
|
// Check if state was changed
|
||||||
|
// Wrapped in double setTimeout() because re-render takes 2 ticks
|
||||||
|
setTimeout(() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
expect(wrapper.html().replace(/\s*\n\s*/g, '')).toEqual(
|
||||||
|
'<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>'
|
||||||
|
);
|
||||||
|
|
||||||
|
done();
|
||||||
|
}, 0);
|
||||||
|
}, 0);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Check if icon has been loaded
|
||||||
|
expect(iconExists(iconName)).toEqual(false);
|
||||||
|
|
||||||
|
// Render component
|
||||||
|
const Wrapper = {
|
||||||
|
components: { Icon },
|
||||||
|
template: `<Icon icon="${iconName}" />`,
|
||||||
|
};
|
||||||
|
const wrapper = mount(Wrapper, {});
|
||||||
|
|
||||||
|
// Should render placeholder
|
||||||
|
expect(wrapper.html()).toEqual('<!---->');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('changing icon property while loading', done => {
|
||||||
|
const prefix = nextPrefix();
|
||||||
|
const name = 'changing-prop';
|
||||||
|
const name2 = 'changing-prop2';
|
||||||
|
const iconName = `@${provider}:${prefix}:${name}`;
|
||||||
|
const iconName2 = `@${provider}:${prefix}:${name2}`;
|
||||||
|
let isSync = true;
|
||||||
|
|
||||||
|
mockAPIData({
|
||||||
|
provider,
|
||||||
|
prefix,
|
||||||
|
response: {
|
||||||
|
prefix,
|
||||||
|
icons: {
|
||||||
|
[name]: iconData,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
delay: next => {
|
||||||
|
// Should have been called asynchronously, which means icon name has changed
|
||||||
|
expect(isSync).toEqual(false);
|
||||||
|
|
||||||
|
// Send icon data
|
||||||
|
next();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
mockAPIData({
|
||||||
|
provider,
|
||||||
|
prefix,
|
||||||
|
response: {
|
||||||
|
prefix,
|
||||||
|
icons: {
|
||||||
|
[name2]: iconData2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
delay: next => {
|
||||||
|
// Should have been called asynchronously
|
||||||
|
expect(isSync).toEqual(false);
|
||||||
|
|
||||||
|
// Icon should not have loaded yet
|
||||||
|
expect(iconExists(iconName2)).toEqual(false);
|
||||||
|
|
||||||
|
// Send icon data
|
||||||
|
next();
|
||||||
|
|
||||||
|
// Test it again
|
||||||
|
expect(iconExists(iconName2)).toEqual(true);
|
||||||
|
|
||||||
|
// Check if state was changed
|
||||||
|
// Wrapped in double setTimeout() because re-render takes 2 ticks (one to handle API response, one to re-render)
|
||||||
|
setTimeout(() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
expect(wrapper.html().replace(/\s*\n\s*/g, '')).toEqual(
|
||||||
|
'<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>'
|
||||||
|
);
|
||||||
|
|
||||||
|
done();
|
||||||
|
}, 0);
|
||||||
|
}, 0);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Check if icon has been loaded
|
||||||
|
expect(iconExists(iconName)).toEqual(false);
|
||||||
|
|
||||||
|
// Render component
|
||||||
|
// Render component
|
||||||
|
const Wrapper = {
|
||||||
|
components: { Icon },
|
||||||
|
template: `<Icon icon="${iconName}" />`,
|
||||||
|
};
|
||||||
|
const wrapper = mount(Wrapper, {});
|
||||||
|
|
||||||
|
// Should render placeholder
|
||||||
|
expect(wrapper.html()).toEqual('<!---->');
|
||||||
|
|
||||||
|
// Change icon name
|
||||||
|
wrapper.setProps({
|
||||||
|
icon: iconName2,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Async
|
||||||
|
isSync = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
test('changing multiple properties', done => {
|
||||||
|
const prefix = nextPrefix();
|
||||||
|
const name = 'multiple-props';
|
||||||
|
const iconName = `@${provider}:${prefix}:${name}`;
|
||||||
|
|
||||||
|
mockAPIData({
|
||||||
|
provider,
|
||||||
|
prefix,
|
||||||
|
response: {
|
||||||
|
prefix,
|
||||||
|
icons: {
|
||||||
|
[name]: iconData,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
delay: next => {
|
||||||
|
// Icon should not have loaded yet
|
||||||
|
expect(iconExists(iconName)).toEqual(false);
|
||||||
|
|
||||||
|
// Send icon data
|
||||||
|
next();
|
||||||
|
|
||||||
|
// Test it again
|
||||||
|
expect(iconExists(iconName)).toEqual(true);
|
||||||
|
|
||||||
|
// Check if state was changed
|
||||||
|
// Wrapped in double setTimeout() because re-render takes 2 ticks (one to handle API response, one to re-render)
|
||||||
|
setTimeout(() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
expect(wrapper.html().replace(/\s*\n\s*/g, '')).toEqual(
|
||||||
|
'<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>'
|
||||||
|
);
|
||||||
|
|
||||||
|
// Add horizontal flip and style
|
||||||
|
wrapper.setProps({
|
||||||
|
icon: iconName,
|
||||||
|
hFlip: true,
|
||||||
|
style: {
|
||||||
|
color: 'red',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Wait for next tick
|
||||||
|
setTimeout(() => {
|
||||||
|
expect(
|
||||||
|
wrapper.html().replace(/\s*\n\s*/g, '')
|
||||||
|
).toEqual(
|
||||||
|
'<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" style="color: red;"><g transform="translate(24 0) scale(-1 1)"><path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"></path></g></svg>'
|
||||||
|
);
|
||||||
|
|
||||||
|
done();
|
||||||
|
}, 0);
|
||||||
|
}, 0);
|
||||||
|
}, 0);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Check if icon has been loaded
|
||||||
|
expect(iconExists(iconName)).toEqual(false);
|
||||||
|
|
||||||
|
// Render component with placeholder text
|
||||||
|
const Wrapper = {
|
||||||
|
components: { Icon },
|
||||||
|
template: `<Icon icon="${iconName}">loading...</Icon>`,
|
||||||
|
};
|
||||||
|
const wrapper = mount(Wrapper, {});
|
||||||
|
|
||||||
|
// Should render placeholder
|
||||||
|
expect(wrapper.html()).toEqual('loading...');
|
||||||
|
});
|
||||||
|
});
|
15
packages/vue/tests/api/load.js
Normal file
15
packages/vue/tests/api/load.js
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import { _api, addAPIProvider } from '../../dist/iconify';
|
||||||
|
import { mockAPIModule } from '@iconify/core/lib/api/modules/mock';
|
||||||
|
|
||||||
|
// API provider for tests
|
||||||
|
export const provider = 'mock-api';
|
||||||
|
|
||||||
|
// Set API module for provider
|
||||||
|
addAPIProvider(provider, {
|
||||||
|
resources: ['http://localhost'],
|
||||||
|
});
|
||||||
|
_api.setAPIModule(provider, mockAPIModule);
|
||||||
|
|
||||||
|
// Prefix
|
||||||
|
let counter = 0;
|
||||||
|
export const nextPrefix = () => 'mock-' + counter++;
|
40
packages/vue/tests/iconify/10-basic.test.js
Normal file
40
packages/vue/tests/iconify/10-basic.test.js
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
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"/>',
|
||||||
|
width: 24,
|
||||||
|
height: 24,
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('Creating component', () => {
|
||||||
|
test('with wrapper', () => {
|
||||||
|
const Wrapper = {
|
||||||
|
components: { Icon },
|
||||||
|
template: `<Icon :icon='icon' />`,
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
icon: iconData,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const wrapper = mount(Wrapper, {});
|
||||||
|
expect(wrapper.html().replace(/\s*\n\s*/g, '')).toEqual(
|
||||||
|
'<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>'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('without wrapper', () => {
|
||||||
|
const wrapper = mount(Icon, {
|
||||||
|
props: {
|
||||||
|
icon: iconData,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(wrapper.html().replace(/\s*\n\s*/g, '')).toEqual(
|
||||||
|
'<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>'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
46
packages/vue/tests/iconify/10-empty.test.js
Normal file
46
packages/vue/tests/iconify/10-empty.test.js
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import { mount } from '@vue/test-utils';
|
||||||
|
import { Icon } from '../../dist/iconify';
|
||||||
|
|
||||||
|
describe('Empty icon', () => {
|
||||||
|
test('basic test', () => {
|
||||||
|
const wrapper = mount(Icon, {
|
||||||
|
props: {},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(wrapper.html().replace(/\s*\n\s*/g, '')).toEqual('<!---->');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('with child node', () => {
|
||||||
|
const Wrapper = {
|
||||||
|
components: { Icon },
|
||||||
|
template: `<Icon><i class="fa fa-home" /></Icon>`,
|
||||||
|
};
|
||||||
|
|
||||||
|
const wrapper = mount(Wrapper, {});
|
||||||
|
expect(wrapper.html().replace(/\s*\n\s*/g, '')).toEqual(
|
||||||
|
'<i class="fa fa-home"></i>'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('with text child node', () => {
|
||||||
|
const Wrapper = {
|
||||||
|
components: { Icon },
|
||||||
|
template: `<Icon>icon</Icon>`,
|
||||||
|
};
|
||||||
|
|
||||||
|
const wrapper = mount(Wrapper, {});
|
||||||
|
expect(wrapper.html().replace(/\s*\n\s*/g, '')).toEqual('icon');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('with multiple childen', () => {
|
||||||
|
const Wrapper = {
|
||||||
|
components: { Icon },
|
||||||
|
template: `<Icon><i class="fa fa-home" /> Home icon</Icon>`,
|
||||||
|
};
|
||||||
|
|
||||||
|
const wrapper = mount(Wrapper, {});
|
||||||
|
expect(wrapper.html().replace(/\s*\n\s*/g, '')).toEqual(
|
||||||
|
'<i class="fa fa-home"></i> Home icon'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
143
packages/vue/tests/iconify/20-attributes.test.js
Normal file
143
packages/vue/tests/iconify/20-attributes.test.js
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
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"/>',
|
||||||
|
width: 24,
|
||||||
|
height: 24,
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('Passing attributes', () => {
|
||||||
|
test('title', () => {
|
||||||
|
const Wrapper = {
|
||||||
|
components: { Icon },
|
||||||
|
template: `<Icon :icon="icon" title="Icon!" />`,
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
icon: iconData,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const wrapper = mount(Wrapper, {});
|
||||||
|
const html = wrapper.html();
|
||||||
|
expect(html).toContain('role="img" title="Icon!"');
|
||||||
|
|
||||||
|
// Make sure aria-hidden exists (for tests below)
|
||||||
|
expect(html).toContain('aria-hidden="true"');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('aria-hidden', () => {
|
||||||
|
// dashes, string value
|
||||||
|
const Wrapper = {
|
||||||
|
components: { Icon },
|
||||||
|
template: `<Icon :icon="icon" aria-hidden="false" />`,
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
icon: iconData,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const wrapper = mount(Wrapper, {});
|
||||||
|
expect(wrapper.html()).not.toContain('aria-hidden="true"');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('ariaHidden', () => {
|
||||||
|
// camelCase, boolean value
|
||||||
|
const Wrapper = {
|
||||||
|
components: { Icon },
|
||||||
|
template: `<Icon :icon="icon" :ariaHidden="false" />`,
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
icon: iconData,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const wrapper = mount(Wrapper, {});
|
||||||
|
expect(wrapper.html()).not.toContain('aria-hidden="true"');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('style as string', () => {
|
||||||
|
const Wrapper = {
|
||||||
|
components: { Icon },
|
||||||
|
template: `<Icon :icon="icon" style="color: red;" />`,
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
icon: iconData,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const wrapper = mount(Wrapper, {});
|
||||||
|
expect(wrapper.html()).toContain('style="color: red;"');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('style as object', () => {
|
||||||
|
const Wrapper = {
|
||||||
|
components: { Icon },
|
||||||
|
template: `<Icon :icon="icon" :style="style" />`,
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
icon: iconData,
|
||||||
|
style: {
|
||||||
|
color: 'red',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const wrapper = mount(Wrapper, {});
|
||||||
|
expect(wrapper.html()).toContain('style="color: red;"');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('color', () => {
|
||||||
|
const Wrapper = {
|
||||||
|
components: { Icon },
|
||||||
|
template: `<Icon :icon="icon" color="red" />`,
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
icon: iconData,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const wrapper = mount(Wrapper, {});
|
||||||
|
expect(wrapper.html()).toContain('style="color: red;"');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('color with style', () => {
|
||||||
|
// color overrides style
|
||||||
|
const Wrapper = {
|
||||||
|
components: { Icon },
|
||||||
|
template: `<Icon :icon="icon" color="purple" style="color: green;" />`,
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
icon: iconData,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const wrapper = mount(Wrapper, {});
|
||||||
|
expect(wrapper.html()).toContain('style="color: purple;"');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('attributes that cannot change', () => {
|
||||||
|
const Wrapper = {
|
||||||
|
components: { Icon },
|
||||||
|
template: `<Icon :icon="icon" viewBox="0 0 0 0" preserveAspectRatio="none" />`,
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
icon: iconData,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const wrapper = mount(Wrapper, {});
|
||||||
|
const html = wrapper.html();
|
||||||
|
expect(html).not.toContain('viewBox="0 0 0 0"');
|
||||||
|
expect(html).not.toContain('preserveAspectRatio="none"');
|
||||||
|
});
|
||||||
|
});
|
68
packages/vue/tests/iconify/20-dimensions.test.js
Normal file
68
packages/vue/tests/iconify/20-dimensions.test.js
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
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"/>',
|
||||||
|
width: 24,
|
||||||
|
height: 24,
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('Dimensions', () => {
|
||||||
|
test('height', () => {
|
||||||
|
const Wrapper = {
|
||||||
|
components: { Icon },
|
||||||
|
template: `<Icon :icon="icon" height="48" />`,
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
icon: iconData,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const wrapper = mount(Wrapper, {});
|
||||||
|
const html = wrapper.html();
|
||||||
|
expect(html).toContain('height="48"');
|
||||||
|
expect(html).toContain('width="48"');
|
||||||
|
expect(html).not.toContain('height="1em"');
|
||||||
|
expect(html).not.toContain('width="1em"');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('width and height', () => {
|
||||||
|
const Wrapper = {
|
||||||
|
components: { Icon },
|
||||||
|
template: `<Icon :icon="icon" :width="32" height="48" />`,
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
icon: iconData,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const wrapper = mount(Wrapper, {});
|
||||||
|
const html = wrapper.html();
|
||||||
|
expect(html).toContain('height="48"');
|
||||||
|
expect(html).toContain('width="32"');
|
||||||
|
expect(html).not.toContain('height="1em"');
|
||||||
|
expect(html).not.toContain('width="1em"');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('auto', () => {
|
||||||
|
const Wrapper = {
|
||||||
|
components: { Icon },
|
||||||
|
template: `<Icon :icon="icon" height="auto" />`,
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
icon: iconData,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const wrapper = mount(Wrapper, {});
|
||||||
|
const html = wrapper.html();
|
||||||
|
expect(html).toContain('height="24"');
|
||||||
|
expect(html).toContain('width="24"');
|
||||||
|
expect(html).not.toContain('height="1em"');
|
||||||
|
expect(html).not.toContain('width="1em"');
|
||||||
|
});
|
||||||
|
});
|
41
packages/vue/tests/iconify/20-ids.test.js
Normal file
41
packages/vue/tests/iconify/20-ids.test.js
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import { mount } from '@vue/test-utils';
|
||||||
|
import { Icon } from '../../dist/iconify';
|
||||||
|
|
||||||
|
const iconDataWithID = {
|
||||||
|
body:
|
||||||
|
'<defs><path id="ssvg-id-1st-place-medala" d="M.93.01h120.55v58.36H.93z"/><path id="ssvg-id-1st-place-medald" d="M.93.01h120.55v58.36H.93z"/><path id="ssvg-id-1st-place-medalf" d="M.93.01h120.55v58.36H.93z"/><path id="ssvg-id-1st-place-medalh" d="M.93.01h120.55v58.36H.93z"/><path id="ssvg-id-1st-place-medalj" d="M.93.01h120.55v58.36H.93z"/><path id="ssvg-id-1st-place-medalm" d="M.93.01h120.55v58.36H.93z"/><path d="M52.849 78.373v-3.908c3.681-.359 6.25-.958 7.703-1.798c1.454-.84 2.54-2.828 3.257-5.962h4.021v40.385h-5.437V78.373h-9.544z" id="ssvg-id-1st-place-medalp"/><linearGradient x1="49.998%" y1="-13.249%" x2="49.998%" y2="90.002%" id="ssvg-id-1st-place-medalb"><stop stop-color="#1E88E5" offset="13.55%"/><stop stop-color="#1565C0" offset="93.8%"/></linearGradient><linearGradient x1="26.648%" y1="2.735%" x2="77.654%" y2="105.978%" id="ssvg-id-1st-place-medalk"><stop stop-color="#64B5F6" offset="13.55%"/><stop stop-color="#2196F3" offset="94.62%"/></linearGradient><radialGradient cx="22.368%" cy="12.5%" fx="22.368%" fy="12.5%" r="95.496%" id="ssvg-id-1st-place-medalo"><stop stop-color="#FFEB3B" offset="29.72%"/><stop stop-color="#FBC02D" offset="95.44%"/></radialGradient></defs><g fill="none" fill-rule="evenodd"><g transform="translate(3 4)"><mask id="ssvg-id-1st-place-medalc" fill="#fff"><use xlink:href="#ssvg-id-1st-place-medala"/></mask><path fill="url(#ssvg-id-1st-place-medalb)" fill-rule="nonzero" mask="url(#ssvg-id-1st-place-medalc)" d="M45.44 42.18h31.43l30-48.43H75.44z"/></g><g transform="translate(3 4)"><mask id="ssvg-id-1st-place-medale" fill="#fff"><use xlink:href="#ssvg-id-1st-place-medald"/></mask><g opacity=".2" mask="url(#ssvg-id-1st-place-medale)" fill="#424242" fill-rule="nonzero"><path d="M101.23-3L75.2 39H50.85L77.11-3h24.12zm5.64-3H75.44l-30 48h31.42l30.01-48z"/></g></g><g transform="translate(3 4)"><mask id="ssvg-id-1st-place-medalg" fill="#fff"><use xlink:href="#ssvg-id-1st-place-medalf"/></mask><path d="M79 30H43c-4.42 0-8 3.58-8 8v16.04c0 2.17 1.8 3.95 4.02 3.96h.01c2.23-.01 4.97-1.75 4.97-3.96V44c0-1.1.9-2 2-2h30c1.1 0 2 .9 2 2v9.93c0 1.98 2.35 3.68 4.22 4.04c.26.05.52.08.78.08c2.21 0 4-1.79 4-4V38c0-4.42-3.58-8-8-8z" fill="#FDD835" fill-rule="nonzero" mask="url(#ssvg-id-1st-place-medalg)"/></g><g transform="translate(3 4)"><mask id="ssvg-id-1st-place-medali" fill="#fff"><use xlink:href="#ssvg-id-1st-place-medalh"/></mask><g opacity=".2" mask="url(#ssvg-id-1st-place-medali)" fill="#424242" fill-rule="nonzero"><path d="M79 32c3.31 0 6 2.69 6 6v16.04A2.006 2.006 0 0 1 82.59 56c-1.18-.23-2.59-1.35-2.59-2.07V44c0-2.21-1.79-4-4-4H46c-2.21 0-4 1.79-4 4v10.04c0 .88-1.64 1.96-2.97 1.96c-1.12-.01-2.03-.89-2.03-1.96V38c0-3.31 2.69-6 6-6h36zm0-2H43c-4.42 0-8 3.58-8 8v16.04c0 2.17 1.8 3.95 4.02 3.96h.01c2.23-.01 4.97-1.75 4.97-3.96V44c0-1.1.9-2 2-2h30c1.1 0 2 .9 2 2v9.93c0 1.98 2.35 3.68 4.22 4.04c.26.05.52.08.78.08c2.21 0 4-1.79 4-4V38c0-4.42-3.58-8-8-8z"/></g></g><g transform="translate(3 4)"><mask id="ssvg-id-1st-place-medall" fill="#fff"><use xlink:href="#ssvg-id-1st-place-medalj"/></mask><path fill="url(#ssvg-id-1st-place-medalk)" fill-rule="nonzero" mask="url(#ssvg-id-1st-place-medall)" d="M76.87 42.18H45.44l-30-48.43h31.43z"/></g><g transform="translate(3 4)"><mask id="ssvg-id-1st-place-medaln" fill="#fff"><use xlink:href="#ssvg-id-1st-place-medalm"/></mask><g opacity=".2" mask="url(#ssvg-id-1st-place-medaln)" fill="#424242" fill-rule="nonzero"><path d="M45.1-3l26.35 42H47.1L20.86-3H45.1zm1.77-3H15.44l30 48h31.42L46.87-6z"/></g></g><circle fill="url(#ssvg-id-1st-place-medalo)" fill-rule="nonzero" cx="64" cy="86" r="38"/><path d="M64 51c19.3 0 35 15.7 35 35s-15.7 35-35 35s-35-15.7-35-35s15.7-35 35-35zm0-3c-20.99 0-38 17.01-38 38s17.01 38 38 38s38-17.01 38-38s-17.01-38-38-38z" opacity=".2" fill="#424242" fill-rule="nonzero"/><path d="M47.3 63.59h33.4v44.4H47.3z"/><use fill="#000" xlink:href="#ssvg-id-1st-place-medalp"/><use fill="#FFA000" xlink:href="#ssvg-id-1st-place-medalp"/></g>',
|
||||||
|
width: 128,
|
||||||
|
height: 128,
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('Replacing IDs', () => {
|
||||||
|
test('default behavior', () => {
|
||||||
|
const Wrapper = {
|
||||||
|
components: { Icon },
|
||||||
|
template: `<Icon :icon="icon" />`,
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
icon: iconDataWithID,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const wrapper = mount(Wrapper, {});
|
||||||
|
expect(wrapper.html()).not.toContain('ssvg-id-1st-place-medala');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('custom generator', () => {
|
||||||
|
const Wrapper = {
|
||||||
|
components: { Icon },
|
||||||
|
template: `<Icon :icon="icon" id="test" />`,
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
icon: iconDataWithID,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const wrapper = mount(Wrapper, {});
|
||||||
|
expect(wrapper.html()).toContain('id="test-0"');
|
||||||
|
});
|
||||||
|
});
|
132
packages/vue/tests/iconify/20-inline.test.js
Normal file
132
packages/vue/tests/iconify/20-inline.test.js
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
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"/>',
|
||||||
|
width: 24,
|
||||||
|
height: 24,
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('Inline attribute', () => {
|
||||||
|
test('string', () => {
|
||||||
|
const Wrapper = {
|
||||||
|
components: { Icon },
|
||||||
|
template: `<Icon :icon="icon" inline="true" />`,
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
icon: iconData,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const wrapper = mount(Wrapper, {});
|
||||||
|
expect(wrapper.html()).toContain('style="vertical-align: -0.125em;"');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('false string', () => {
|
||||||
|
// "false" = true
|
||||||
|
const Wrapper = {
|
||||||
|
components: { Icon },
|
||||||
|
template: `<Icon :icon="icon" inline="false" />`,
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
icon: iconData,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const wrapper = mount(Wrapper, {});
|
||||||
|
expect(wrapper.html()).toContain('style="vertical-align: -0.125em;"');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('true', () => {
|
||||||
|
const Wrapper = {
|
||||||
|
components: { Icon },
|
||||||
|
template: `<Icon :icon="icon" :inline="true" />`,
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
icon: iconData,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const wrapper = mount(Wrapper, {});
|
||||||
|
expect(wrapper.html()).toContain('style="vertical-align: -0.125em;"');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('false', () => {
|
||||||
|
const Wrapper = {
|
||||||
|
components: { Icon },
|
||||||
|
template: `<Icon :icon="icon" :inline="false" />`,
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
icon: iconData,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const wrapper = mount(Wrapper, {});
|
||||||
|
expect(wrapper.html()).not.toContain(
|
||||||
|
'style="vertical-align: -0.125em;"'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('inline and style string', () => {
|
||||||
|
// Style goes after vertical-align
|
||||||
|
const Wrapper = {
|
||||||
|
components: { Icon },
|
||||||
|
template: `<Icon :icon="icon" :inline="true" style="color: red;" />`,
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
icon: iconData,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const wrapper = mount(Wrapper, {});
|
||||||
|
expect(wrapper.html()).toContain(
|
||||||
|
'color: red; vertical-align: -0.125em;'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('inline and style object', () => {
|
||||||
|
// Style goes after vertical-align
|
||||||
|
const Wrapper = {
|
||||||
|
components: { Icon },
|
||||||
|
template: `<Icon :icon="icon" :inline="true" :style="style" />`,
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
icon: iconData,
|
||||||
|
style: {
|
||||||
|
color: 'red',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const wrapper = mount(Wrapper, {});
|
||||||
|
expect(wrapper.html()).toContain(
|
||||||
|
'color: red; vertical-align: -0.125em;'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('inline and style overriding it', () => {
|
||||||
|
// Style goes after vertical-align
|
||||||
|
const Wrapper = {
|
||||||
|
components: { Icon },
|
||||||
|
template: `<Icon :icon="icon" :inline="true" :style="style" />`,
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
icon: iconData,
|
||||||
|
style: {
|
||||||
|
verticalAlign: 0,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const wrapper = mount(Wrapper, {});
|
||||||
|
expect(wrapper.html()).toContain('style="vertical-align: 0;"');
|
||||||
|
});
|
||||||
|
});
|
208
packages/vue/tests/iconify/20-transformations.test.js
Normal file
208
packages/vue/tests/iconify/20-transformations.test.js
Normal file
@ -0,0 +1,208 @@
|
|||||||
|
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"/>',
|
||||||
|
width: 24,
|
||||||
|
height: 24,
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('Rotation', () => {
|
||||||
|
test('number', () => {
|
||||||
|
const Wrapper = {
|
||||||
|
components: { Icon },
|
||||||
|
template: `<Icon :icon="icon" :rotate="1" />`,
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
icon: iconData,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const wrapper = mount(Wrapper, {});
|
||||||
|
expect(wrapper.html().replace(/\s*\n\s*/g, '')).toEqual(
|
||||||
|
'<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"><g transform="rotate(90 12 12)"><path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"></path></g></svg>'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('string', () => {
|
||||||
|
const Wrapper = {
|
||||||
|
components: { Icon },
|
||||||
|
template: `<Icon :icon="icon" rotate="180deg" />`,
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
icon: iconData,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const wrapper = mount(Wrapper, {});
|
||||||
|
expect(wrapper.html()).toContain('<g transform="rotate(180 12 12)">');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Flip', () => {
|
||||||
|
test('boolean', () => {
|
||||||
|
const Wrapper = {
|
||||||
|
components: { Icon },
|
||||||
|
template: `<Icon :icon="icon" :hFlip="true" />`,
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
icon: iconData,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const wrapper = mount(Wrapper, {});
|
||||||
|
expect(wrapper.html()).toContain(
|
||||||
|
'<g transform="translate(24 0) scale(-1 1)">'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('string', () => {
|
||||||
|
const Wrapper = {
|
||||||
|
components: { Icon },
|
||||||
|
template: `<Icon :icon="icon" flip="vertical" />`,
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
icon: iconData,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const wrapper = mount(Wrapper, {});
|
||||||
|
expect(wrapper.html()).toContain(
|
||||||
|
'<g transform="translate(0 24) scale(1 -1)">'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('string and boolean', () => {
|
||||||
|
const Wrapper = {
|
||||||
|
components: { Icon },
|
||||||
|
template: `<Icon :icon="icon" flip="horizontal" :vFlip="true" />`,
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
icon: iconData,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const wrapper = mount(Wrapper, {});
|
||||||
|
expect(wrapper.html()).toContain('<g transform="rotate(180 12 12)">');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('string for boolean attribute', () => {
|
||||||
|
const Wrapper = {
|
||||||
|
components: { Icon },
|
||||||
|
template: `<Icon :icon="icon" horizontal-flip="true" />`,
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
icon: iconData,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const wrapper = mount(Wrapper, {});
|
||||||
|
expect(wrapper.html()).toContain(
|
||||||
|
'<g transform="translate(24 0) scale(-1 1)">'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('shorthand and boolean', () => {
|
||||||
|
// 'flip' is processed after 'hFlip', overwriting value
|
||||||
|
const Wrapper = {
|
||||||
|
components: { Icon },
|
||||||
|
template: `<Icon :icon="icon" flip="horizontal" :hFlip="false" />`,
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
icon: iconData,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const wrapper = mount(Wrapper, {});
|
||||||
|
expect(wrapper.html()).toContain(
|
||||||
|
'<g transform="translate(24 0) scale(-1 1)">'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('shorthand and boolean as string', () => {
|
||||||
|
const Wrapper = {
|
||||||
|
components: { Icon },
|
||||||
|
template: `<Icon :icon="icon" flip="vertical" :horizontal-flip="true" />`,
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
icon: iconData,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const wrapper = mount(Wrapper, {});
|
||||||
|
expect(wrapper.html()).toContain('<g transform="rotate(180 12 12)">');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('wrong case', () => {
|
||||||
|
const Wrapper = {
|
||||||
|
components: { Icon },
|
||||||
|
template: `<Icon :icon="icon" :vflip="true" />`,
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
icon: iconData,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const wrapper = mount(Wrapper, {});
|
||||||
|
expect(wrapper.html()).not.toContain('<g transform="');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Alignment and slice', () => {
|
||||||
|
test('vAlign and slice', () => {
|
||||||
|
const Wrapper = {
|
||||||
|
components: { Icon },
|
||||||
|
template: `<Icon :icon="icon" vAlign="top" :slice="true" />`,
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
icon: iconData,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const wrapper = mount(Wrapper, {});
|
||||||
|
expect(wrapper.html()).toContain(
|
||||||
|
'preserveAspectRatio="xMidYMin slice"'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('string', () => {
|
||||||
|
const Wrapper = {
|
||||||
|
components: { Icon },
|
||||||
|
template: `<Icon :icon="icon" align="left bottom" />`,
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
icon: iconData,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const wrapper = mount(Wrapper, {});
|
||||||
|
expect(wrapper.html()).toContain('preserveAspectRatio="xMinYMax meet"');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('aliases', () => {
|
||||||
|
const Wrapper = {
|
||||||
|
components: { Icon },
|
||||||
|
template: `<Icon :icon="icon" h-align="left" vertical-align="bottom" />`,
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
icon: iconData,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const wrapper = mount(Wrapper, {});
|
||||||
|
expect(wrapper.html()).toContain('preserveAspectRatio="xMinYMax meet"');
|
||||||
|
});
|
||||||
|
});
|
@ -112,7 +112,7 @@ describe('Passing attributes', () => {
|
|||||||
// color overrides style
|
// color overrides style
|
||||||
const Wrapper = {
|
const Wrapper = {
|
||||||
components: { Icon },
|
components: { Icon },
|
||||||
template: `<Icon :icon="icon" color="red" style="color: green;" />`,
|
template: `<Icon :icon="icon" color="purple" style="color: green;" />`,
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
icon: iconData,
|
icon: iconData,
|
||||||
@ -121,7 +121,7 @@ describe('Passing attributes', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const wrapper = mount(Wrapper, {});
|
const wrapper = mount(Wrapper, {});
|
||||||
expect(wrapper.html()).toContain('style="color: red;"');
|
expect(wrapper.html()).toContain('style="color: purple;"');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('attributes that cannot change', () => {
|
test('attributes that cannot change', () => {
|
||||||
|
@ -86,7 +86,7 @@ describe('Inline attribute', () => {
|
|||||||
|
|
||||||
const wrapper = mount(Wrapper, {});
|
const wrapper = mount(Wrapper, {});
|
||||||
expect(wrapper.html()).toContain(
|
expect(wrapper.html()).toContain(
|
||||||
'vertical-align: -0.125em; color: red;'
|
'color: red; vertical-align: -0.125em;'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -107,7 +107,26 @@ describe('Inline attribute', () => {
|
|||||||
|
|
||||||
const wrapper = mount(Wrapper, {});
|
const wrapper = mount(Wrapper, {});
|
||||||
expect(wrapper.html()).toContain(
|
expect(wrapper.html()).toContain(
|
||||||
'vertical-align: -0.125em; color: red;'
|
'color: red; vertical-align: -0.125em;'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('inline and style overriding it', () => {
|
||||||
|
// Style goes after vertical-align
|
||||||
|
const Wrapper = {
|
||||||
|
components: { Icon },
|
||||||
|
template: `<Icon :icon="icon" :inline="true" :style="style" />`,
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
icon: iconData,
|
||||||
|
style: {
|
||||||
|
verticalAlign: 0,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const wrapper = mount(Wrapper, {});
|
||||||
|
expect(wrapper.html()).toContain('style="vertical-align: 0;"');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -22,7 +22,7 @@ describe('Rotation', () => {
|
|||||||
|
|
||||||
const wrapper = mount(Wrapper, {});
|
const wrapper = mount(Wrapper, {});
|
||||||
expect(wrapper.html().replace(/\s*\n\s*/g, '')).toEqual(
|
expect(wrapper.html().replace(/\s*\n\s*/g, '')).toEqual(
|
||||||
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" rotate="1" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24"><g transform="rotate(90 12 12)"><path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"></path></g></svg>'
|
'<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"><g transform="rotate(90 12 12)"><path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"></path></g></svg>'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -9,7 +9,6 @@
|
|||||||
"strict": false,
|
"strict": false,
|
||||||
"moduleResolution": "node",
|
"moduleResolution": "node",
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
"forceConsistentCasingInFileNames": true,
|
"forceConsistentCasingInFileNames": true
|
||||||
"noLib": false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user