mirror of
https://github.com/iconify/iconify.git
synced 2024-11-09 23:00:56 +00:00
Change Vue 2 component from functional to class in preparation for API support
This commit is contained in:
parent
a4422ec145
commit
56df8fa1e8
@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
|
||||
"mainEntryPointFilePath": "lib/IconifyIcon.d.ts",
|
||||
"mainEntryPointFilePath": "lib/iconify.d.ts",
|
||||
"bundledPackages": [
|
||||
"@iconify/types",
|
||||
"@iconify/core",
|
||||
@ -15,7 +15,7 @@
|
||||
},
|
||||
"dtsRollup": {
|
||||
"enabled": true,
|
||||
"untrimmedFilePath": "<projectFolder>/dist/IconifyIcon.d.ts"
|
||||
"untrimmedFilePath": "<projectFolder>/dist/iconify.d.ts"
|
||||
},
|
||||
"tsdocMetadata": {
|
||||
"enabled": false
|
43
packages/vue2/api-extractor.offline.json
Normal file
43
packages/vue2/api-extractor.offline.json
Normal file
@ -0,0 +1,43 @@
|
||||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
|
||||
"mainEntryPointFilePath": "lib/offline.d.ts",
|
||||
"bundledPackages": [
|
||||
"@iconify/types",
|
||||
"@iconify/core",
|
||||
"@cyberalien/redundancy"
|
||||
],
|
||||
"compiler": {},
|
||||
"apiReport": {
|
||||
"enabled": false
|
||||
},
|
||||
"docModel": {
|
||||
"enabled": false
|
||||
},
|
||||
"dtsRollup": {
|
||||
"enabled": true,
|
||||
"untrimmedFilePath": "<projectFolder>/dist/offline.d.ts"
|
||||
},
|
||||
"tsdocMetadata": {
|
||||
"enabled": false
|
||||
},
|
||||
"messages": {
|
||||
"compilerMessageReporting": {
|
||||
"default": {
|
||||
"logLevel": "warning"
|
||||
}
|
||||
},
|
||||
"extractorMessageReporting": {
|
||||
"default": {
|
||||
"logLevel": "warning"
|
||||
},
|
||||
"ae-missing-release-tag": {
|
||||
"logLevel": "none"
|
||||
}
|
||||
},
|
||||
"tsdocMessageReporting": {
|
||||
"default": {
|
||||
"logLevel": "warning"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -2,21 +2,18 @@ const fs = require('fs');
|
||||
const path = require('path');
|
||||
const child_process = require('child_process');
|
||||
|
||||
// const packagesDir = path.dirname(path.dirname(__dirname)) + '/packages';
|
||||
const packagesDir = path.dirname(__dirname);
|
||||
|
||||
// List of commands to run
|
||||
const commands = [];
|
||||
|
||||
// Parse command line
|
||||
const compile = {
|
||||
// core: false,
|
||||
core: false,
|
||||
lib: true,
|
||||
dist: true,
|
||||
api: true,
|
||||
};
|
||||
|
||||
// Disable compiling dependencies because project was moved to archive
|
||||
/*
|
||||
process.argv.slice(2).forEach((cmd) => {
|
||||
if (cmd.slice(0, 2) !== '--') {
|
||||
return;
|
||||
@ -79,7 +76,6 @@ if (compile.core) {
|
||||
cwd: packagesDir + '/core',
|
||||
});
|
||||
}
|
||||
*/
|
||||
|
||||
// Compile other packages
|
||||
Object.keys(compile).forEach((key) => {
|
||||
|
@ -6,4 +6,5 @@ module.exports = {
|
||||
'^.+\\.js$': '<rootDir>/node_modules/babel-jest',
|
||||
},
|
||||
collectCoverage: false,
|
||||
testMatch: ['**/tests/**/*.test.js'],
|
||||
};
|
||||
|
@ -15,13 +15,14 @@
|
||||
"build": "node build",
|
||||
"build:lib": "tsc -b",
|
||||
"build:dist": "rollup -c rollup.config.js",
|
||||
"build:api": "api-extractor run --local --verbose",
|
||||
"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",
|
||||
"test": "jest"
|
||||
},
|
||||
"main": "dist/IconifyIcon.umd.js",
|
||||
"module": "dist/IconifyIcon.esm.js",
|
||||
"types": "dist/IconifyIcon.d.ts",
|
||||
"main": "dist/iconify.js",
|
||||
"module": "dist/iconify.mjs",
|
||||
"types": "dist/iconify.d.ts",
|
||||
"devDependencies": {
|
||||
"@iconify/core": "^1.0.0-rc.3",
|
||||
"@microsoft/api-extractor": "^7.15.1",
|
||||
|
@ -1,78 +1,45 @@
|
||||
import resolve from '@rollup/plugin-node-resolve';
|
||||
import commonjs from '@rollup/plugin-commonjs';
|
||||
import buble from '@rollup/plugin-buble';
|
||||
// import { terser } from 'rollup-plugin-terser';
|
||||
|
||||
const name = 'IconifyIcon';
|
||||
const names = ['offline', 'iconify'];
|
||||
const component = 'Icon';
|
||||
|
||||
// Module footer
|
||||
const footer = `
|
||||
// Export as ES module
|
||||
if (typeof exports === 'object') {
|
||||
try {
|
||||
exports.__esModule = true;
|
||||
exports.default = ${name};
|
||||
} catch (err) {
|
||||
}
|
||||
}`;
|
||||
const config = [];
|
||||
|
||||
// Export configuration
|
||||
const config = [
|
||||
// ES Module
|
||||
{
|
||||
// Write all packages
|
||||
names.forEach((name) => {
|
||||
// ES module
|
||||
config.push({
|
||||
input: `lib/${name}.js`,
|
||||
output: [
|
||||
{
|
||||
file: `dist/${name}.esm.js`,
|
||||
file: `dist/${name}.mjs`,
|
||||
format: 'esm',
|
||||
exports: 'named',
|
||||
},
|
||||
],
|
||||
external: ['vue'],
|
||||
plugins: [resolve(), commonjs(), buble()],
|
||||
},
|
||||
// UMD Module
|
||||
{
|
||||
input: `lib/${name}.js`,
|
||||
output: [
|
||||
{
|
||||
file: `dist/${name}.umd.js`,
|
||||
format: 'umd',
|
||||
name,
|
||||
exports: 'named',
|
||||
},
|
||||
],
|
||||
plugins: [resolve(), commonjs(), buble()],
|
||||
},
|
||||
/*
|
||||
// Web and module
|
||||
// "unpkg": "dist/IconifyIcon.min.js",
|
||||
});
|
||||
|
||||
{
|
||||
// UMD module
|
||||
config.push({
|
||||
input: `lib/${name}.js`,
|
||||
output: [
|
||||
{
|
||||
file: `dist/${name}.js`,
|
||||
name,
|
||||
format: 'iife',
|
||||
footer,
|
||||
},
|
||||
],
|
||||
plugins: [resolve(), commonjs(), buble()],
|
||||
},
|
||||
// Web
|
||||
{
|
||||
input: `lib/${name}.js`,
|
||||
output: [
|
||||
{
|
||||
file: `dist/${name}.min.js`,
|
||||
name,
|
||||
format: 'umd',
|
||||
name: component,
|
||||
exports: 'named',
|
||||
format: 'iife',
|
||||
globals: {
|
||||
vue: 'Vue',
|
||||
},
|
||||
},
|
||||
],
|
||||
plugins: [resolve(), commonjs(), buble(), terser()],
|
||||
},
|
||||
*/
|
||||
];
|
||||
external: ['vue'],
|
||||
plugins: [resolve(), commonjs(), buble()],
|
||||
});
|
||||
});
|
||||
|
||||
export default config;
|
||||
|
@ -1,339 +0,0 @@
|
||||
import _Vue, { PluginFunction, VueConstructor, VNode, VNodeData } from 'vue';
|
||||
import { FunctionalRenderContext } from 'vue/src/core';
|
||||
|
||||
import { IconifyIcon as IconifyIconData, IconifyJSON } from '@iconify/types';
|
||||
import {
|
||||
IconifyIconCustomisations as IconCustomisations,
|
||||
FullIconCustomisations,
|
||||
defaults,
|
||||
IconifyHorizontalIconAlignment,
|
||||
IconifyVerticalIconAlignment,
|
||||
IconifyIconSize,
|
||||
} from '@iconify/core/lib/customisations';
|
||||
import {
|
||||
flipFromString,
|
||||
alignmentFromString,
|
||||
} from '@iconify/core/lib/customisations/shorthand';
|
||||
import { rotateFromString } from '@iconify/core/lib/customisations/rotate';
|
||||
import { fullIcon } from '@iconify/core/lib/icon';
|
||||
import { iconToSVG } from '@iconify/core/lib/builder';
|
||||
import { replaceIDs } from '@iconify/core/lib/builder/ids';
|
||||
import { merge } from '@iconify/core/lib/misc/merge';
|
||||
import { parseIconSet } from '@iconify/core/lib/icon/icon-set';
|
||||
|
||||
/**
|
||||
* Export types that could be used in component
|
||||
*/
|
||||
export {
|
||||
IconifyIconData,
|
||||
IconifyJSON,
|
||||
IconifyHorizontalIconAlignment,
|
||||
IconifyVerticalIconAlignment,
|
||||
IconifyIconSize,
|
||||
};
|
||||
|
||||
// Allow rotation to be string
|
||||
/**
|
||||
* Icon customisations
|
||||
*/
|
||||
export type IconifyIconCustomisations = IconCustomisations & {
|
||||
rotate?: string | number;
|
||||
};
|
||||
|
||||
/**
|
||||
* Icon properties
|
||||
*/
|
||||
export interface IconifyIconProps extends IconifyIconCustomisations {
|
||||
icon: IconifyIconData;
|
||||
|
||||
// Style
|
||||
color?: string;
|
||||
|
||||
// Shorthand properties
|
||||
flip?: string;
|
||||
align?: string;
|
||||
|
||||
// Aliases for alignment because "v-align" is treated like directive
|
||||
horizontalAlign?: IconifyHorizontalIconAlignment;
|
||||
verticalAlign?: IconifyVerticalIconAlignment;
|
||||
|
||||
// Aliases for flip because "v-flip" is treated like directive
|
||||
horizontalFlip?: boolean;
|
||||
verticalFlip?: boolean;
|
||||
}
|
||||
|
||||
// Interface for functional component context that is missing in Vue types.
|
||||
// Missing some unused stuff: children, slots, scopedSlots, injections
|
||||
// interface FunctionalRenderContext {
|
||||
// props: { [key: string]: unknown };
|
||||
// data?: VNodeData;
|
||||
// parent?: VNode;
|
||||
// listeners?: object; // alias of data.on
|
||||
// }
|
||||
|
||||
/**
|
||||
* Default SVG attributes
|
||||
*/
|
||||
const svgDefaults = {
|
||||
'xmlns': 'http://www.w3.org/2000/svg',
|
||||
'xmlns:xlink': 'http://www.w3.org/1999/xlink',
|
||||
'aria-hidden': true,
|
||||
'role': 'img',
|
||||
};
|
||||
|
||||
/**
|
||||
* Aliases for customisations.
|
||||
* In Vue 'v-' properties are reserved, so v-align and v-flip must be renamed
|
||||
*/
|
||||
const customisationAliases = {
|
||||
horizontalAlign: 'hAlign',
|
||||
verticalAlign: 'vAlign',
|
||||
horizontalFlip: 'hFlip',
|
||||
verticalFlip: 'vFlip',
|
||||
};
|
||||
|
||||
/**
|
||||
* Storage for icons referred by name
|
||||
*/
|
||||
const storage: Record<string, Required<IconifyIconData>> = Object.create(null);
|
||||
|
||||
/**
|
||||
* Interface for style variable
|
||||
*/
|
||||
type VNodeStyle = (string | Record<string, unknown>)[];
|
||||
|
||||
/**
|
||||
* IconifyIcon component
|
||||
*/
|
||||
const IconifyIcon = {
|
||||
name: 'IconifyIcon',
|
||||
functional: true,
|
||||
|
||||
/**
|
||||
* Render icon
|
||||
*
|
||||
* @param createElement
|
||||
* @param context
|
||||
*/
|
||||
render(
|
||||
createElement: typeof _Vue.prototype.$createElement,
|
||||
context: FunctionalRenderContext
|
||||
): VNode {
|
||||
const props = context.props;
|
||||
|
||||
// Split properties
|
||||
const icon =
|
||||
typeof props.icon === 'string'
|
||||
? storage[props.icon]
|
||||
: fullIcon(props.icon);
|
||||
if (!icon) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const customisations = merge(
|
||||
defaults,
|
||||
props as IconifyIconCustomisations
|
||||
) as FullIconCustomisations;
|
||||
const componentProps = merge(svgDefaults);
|
||||
|
||||
// Copy style
|
||||
let stylesList: VNodeStyle;
|
||||
let styleString: string;
|
||||
let isStyleString = false;
|
||||
let hasStyle = true;
|
||||
|
||||
function setStyle(value: unknown): boolean {
|
||||
if (typeof value === 'string') {
|
||||
// Style as string
|
||||
styleString = value;
|
||||
isStyleString = true;
|
||||
return true;
|
||||
}
|
||||
if (typeof value !== 'object') {
|
||||
// Unknown type ???
|
||||
return false;
|
||||
}
|
||||
|
||||
stylesList = value instanceof Array ? value.slice(0) : [value];
|
||||
return true;
|
||||
}
|
||||
|
||||
const contextData = context.data;
|
||||
if (
|
||||
!contextData ||
|
||||
(!setStyle(contextData.staticStyle) && !setStyle(contextData.style))
|
||||
) {
|
||||
stylesList = [];
|
||||
hasStyle = false;
|
||||
}
|
||||
|
||||
// Get element properties
|
||||
for (let key in props) {
|
||||
const value = props[key];
|
||||
switch (key) {
|
||||
// Properties to ignore
|
||||
case 'icon':
|
||||
case 'style':
|
||||
break;
|
||||
|
||||
// Flip as string: 'horizontal,vertical'
|
||||
case 'flip':
|
||||
flipFromString(customisations, value);
|
||||
break;
|
||||
|
||||
// Alignment as string
|
||||
case 'align':
|
||||
alignmentFromString(customisations, value);
|
||||
break;
|
||||
|
||||
// Color: copy to style
|
||||
case 'color':
|
||||
if (isStyleString) {
|
||||
styleString = 'color: ' + value + '; ' + styleString;
|
||||
} else {
|
||||
stylesList.unshift({
|
||||
color: value,
|
||||
});
|
||||
}
|
||||
hasStyle = true;
|
||||
break;
|
||||
|
||||
// Rotation as string
|
||||
case 'rotate':
|
||||
if (typeof value !== 'number') {
|
||||
customisations[key] = rotateFromString(value);
|
||||
} else {
|
||||
componentProps[key] = value;
|
||||
}
|
||||
break;
|
||||
|
||||
// Remove aria-hidden
|
||||
case 'ariaHidden':
|
||||
case 'aria-hidden':
|
||||
// Vue transforms 'aria-hidden' property to 'ariaHidden'
|
||||
if (value !== true && value !== 'true') {
|
||||
delete componentProps['aria-hidden'];
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if (customisationAliases[key] !== void 0) {
|
||||
// Aliases for customisations
|
||||
customisations[customisationAliases[key]] = value;
|
||||
} else if (defaults[key] === void 0) {
|
||||
// Copy missing property if it does not exist in customisations
|
||||
componentProps[key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Generate icon
|
||||
const item = iconToSVG(icon, customisations);
|
||||
|
||||
// Add icon stuff
|
||||
for (let key in item.attributes) {
|
||||
componentProps[key] = item.attributes[key];
|
||||
}
|
||||
|
||||
if (item.inline) {
|
||||
if (isStyleString) {
|
||||
styleString = 'vertical-align: -0.125em; ' + styleString;
|
||||
} else {
|
||||
stylesList.unshift({
|
||||
verticalAlign: '-0.125em',
|
||||
});
|
||||
}
|
||||
hasStyle = true;
|
||||
}
|
||||
|
||||
// Generate node data
|
||||
const data: VNodeData = {
|
||||
attrs: componentProps,
|
||||
domProps: {
|
||||
innerHTML: replaceIDs(item.body),
|
||||
},
|
||||
};
|
||||
if (hasStyle) {
|
||||
data.style = isStyleString ? styleString : stylesList;
|
||||
}
|
||||
|
||||
if (contextData) {
|
||||
['on', 'ref'].forEach((attr) => {
|
||||
if (contextData[attr] !== void 0) {
|
||||
data[attr] = contextData[attr];
|
||||
}
|
||||
});
|
||||
['staticClass', 'class'].forEach((attr) => {
|
||||
if (contextData[attr] !== void 0) {
|
||||
data.class = contextData[attr];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return createElement('svg', data);
|
||||
},
|
||||
|
||||
/**
|
||||
* Add icon to storage that can later be used by name, for example: <iconify-icon icon="home" />
|
||||
*/
|
||||
addIcon: (name: string, data: IconifyIconData) => {
|
||||
storage[name] = fullIcon(data);
|
||||
},
|
||||
|
||||
/**
|
||||
* Add collection to storage, allowing to call icons by name
|
||||
*
|
||||
* @param data Icon set
|
||||
* @param prefix Optional prefix to add to icon names, true if prefix from icon set should be used.
|
||||
*/
|
||||
addCollection: (data: IconifyJSON, prefix?: string | boolean) => {
|
||||
const iconPrefix: string =
|
||||
typeof prefix === 'string'
|
||||
? prefix
|
||||
: prefix !== false && typeof data.prefix === 'string'
|
||||
? data.prefix + ':'
|
||||
: '';
|
||||
parseIconSet(data, (name, icon) => {
|
||||
if (icon !== null) {
|
||||
storage[iconPrefix + name] = icon;
|
||||
}
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
// Install function
|
||||
interface InstallFunction extends PluginFunction<unknown> {
|
||||
installed?: boolean;
|
||||
}
|
||||
interface InstallableComponent extends VueConstructor<_Vue> {
|
||||
install: InstallFunction;
|
||||
}
|
||||
|
||||
const install: InstallFunction = function installIconifyIcon(Vue: typeof _Vue) {
|
||||
if (install.installed) return;
|
||||
install.installed = true;
|
||||
Vue.component('IconifyIcon', IconifyIcon);
|
||||
};
|
||||
|
||||
// Create module definition for Vue.use()
|
||||
const plugin = {
|
||||
install,
|
||||
};
|
||||
|
||||
// Auto-install when vue is found (eg. in browser via <script> tag)
|
||||
let GlobalVue = null;
|
||||
if (typeof window !== 'undefined') {
|
||||
GlobalVue = window.Vue;
|
||||
} else if (typeof global !== 'undefined') {
|
||||
GlobalVue = ((global as unknown) as Record<string, unknown>).Vue;
|
||||
}
|
||||
if (GlobalVue) {
|
||||
GlobalVue.use(plugin);
|
||||
}
|
||||
|
||||
// Inject install function into component - allows component
|
||||
// to be registered via Vue.use() as well as Vue.component()
|
||||
((IconifyIcon as unknown) as InstallableComponent).install = install;
|
||||
|
||||
// Export component
|
||||
export default IconifyIcon;
|
114
packages/vue2/src/iconify.ts
Normal file
114
packages/vue2/src/iconify.ts
Normal file
@ -0,0 +1,114 @@
|
||||
import Vue, { CreateElement, VNode } from 'vue';
|
||||
import { ExtendedVue } from 'vue/types/vue';
|
||||
import { IconifyIcon, IconifyJSON } from '@iconify/types';
|
||||
import {
|
||||
IconifyHorizontalIconAlignment,
|
||||
IconifyVerticalIconAlignment,
|
||||
IconifyIconSize,
|
||||
} from '@iconify/core/lib/customisations';
|
||||
import { fullIcon } from '@iconify/core/lib/icon';
|
||||
import { parseIconSet } from '@iconify/core/lib/icon/icon-set';
|
||||
import {
|
||||
IconifyIconCustomisations,
|
||||
IconifyIconProps,
|
||||
IconProps,
|
||||
} from './props';
|
||||
import { render } from './render';
|
||||
|
||||
/**
|
||||
* Export stuff from props.ts
|
||||
*/
|
||||
export { IconifyIconCustomisations, IconifyIconProps, IconProps };
|
||||
|
||||
/**
|
||||
* Export types that could be used in component
|
||||
*/
|
||||
export {
|
||||
IconifyIcon,
|
||||
IconifyJSON,
|
||||
IconifyHorizontalIconAlignment,
|
||||
IconifyVerticalIconAlignment,
|
||||
IconifyIconSize,
|
||||
};
|
||||
|
||||
/**
|
||||
* Storage for icons referred by name
|
||||
*/
|
||||
const storage: Record<string, Required<IconifyIcon>> = Object.create(null);
|
||||
|
||||
/**
|
||||
* Add icon to storage, allowing to call it by name
|
||||
*
|
||||
* @param name
|
||||
* @param data
|
||||
*/
|
||||
export function addIcon(name: string, data: IconifyIcon): void {
|
||||
storage[name] = fullIcon(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add collection to storage, allowing to call icons by name
|
||||
*
|
||||
* @param data Icon set
|
||||
* @param prefix Optional prefix to add to icon names, true (default) if prefix from icon set should be used.
|
||||
*/
|
||||
export function addCollection(
|
||||
data: IconifyJSON,
|
||||
prefix?: string | boolean
|
||||
): void {
|
||||
const iconPrefix: string =
|
||||
typeof prefix === 'string'
|
||||
? prefix
|
||||
: prefix !== false && typeof data.prefix === 'string'
|
||||
? data.prefix + ':'
|
||||
: '';
|
||||
parseIconSet(data, (name, icon) => {
|
||||
if (icon !== null) {
|
||||
storage[iconPrefix + name] = icon;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Component
|
||||
*/
|
||||
export const Icon = Vue.extend({
|
||||
// Do not inherit other attributes: it is handled by render()
|
||||
// In Vue 2 style is still passed!
|
||||
inheritAttrs: false,
|
||||
|
||||
// Render icon
|
||||
render(createElement: CreateElement): VNode {
|
||||
const props = this.$attrs;
|
||||
|
||||
// Check icon
|
||||
const icon =
|
||||
typeof props.icon === 'string'
|
||||
? storage[props.icon]
|
||||
: typeof props.icon === 'object'
|
||||
? fullIcon(props.icon)
|
||||
: null;
|
||||
|
||||
// Validate icon object
|
||||
if (
|
||||
icon === null ||
|
||||
typeof icon !== 'object' ||
|
||||
typeof icon.body !== 'string'
|
||||
) {
|
||||
// Render child nodes
|
||||
if (this.$slots.default) {
|
||||
const result = this.$slots.default;
|
||||
if (result instanceof Array && result.length > 0) {
|
||||
// If there are multiple child nodes, they must be wrapped in Vue 2
|
||||
return result.length === 1
|
||||
? result[0]
|
||||
: createElement('span', result);
|
||||
}
|
||||
}
|
||||
return (null as unknown) as VNode;
|
||||
}
|
||||
|
||||
// Valid icon: render it
|
||||
return render(createElement, props, this.$data, icon);
|
||||
},
|
||||
});
|
114
packages/vue2/src/offline.ts
Normal file
114
packages/vue2/src/offline.ts
Normal file
@ -0,0 +1,114 @@
|
||||
import Vue, { CreateElement, VNode } from 'vue';
|
||||
import { ExtendedVue } from 'vue/types/vue';
|
||||
import { IconifyIcon, IconifyJSON } from '@iconify/types';
|
||||
import {
|
||||
IconifyHorizontalIconAlignment,
|
||||
IconifyVerticalIconAlignment,
|
||||
IconifyIconSize,
|
||||
} from '@iconify/core/lib/customisations';
|
||||
import { fullIcon } from '@iconify/core/lib/icon';
|
||||
import { parseIconSet } from '@iconify/core/lib/icon/icon-set';
|
||||
import {
|
||||
IconifyIconCustomisations,
|
||||
IconifyIconProps,
|
||||
IconProps,
|
||||
} from './props';
|
||||
import { render } from './render';
|
||||
|
||||
/**
|
||||
* Export stuff from props.ts
|
||||
*/
|
||||
export { IconifyIconCustomisations, IconifyIconProps, IconProps };
|
||||
|
||||
/**
|
||||
* Export types that could be used in component
|
||||
*/
|
||||
export {
|
||||
IconifyIcon,
|
||||
IconifyJSON,
|
||||
IconifyHorizontalIconAlignment,
|
||||
IconifyVerticalIconAlignment,
|
||||
IconifyIconSize,
|
||||
};
|
||||
|
||||
/**
|
||||
* Storage for icons referred by name
|
||||
*/
|
||||
const storage: Record<string, Required<IconifyIcon>> = Object.create(null);
|
||||
|
||||
/**
|
||||
* Add icon to storage, allowing to call it by name
|
||||
*
|
||||
* @param name
|
||||
* @param data
|
||||
*/
|
||||
export function addIcon(name: string, data: IconifyIcon): void {
|
||||
storage[name] = fullIcon(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add collection to storage, allowing to call icons by name
|
||||
*
|
||||
* @param data Icon set
|
||||
* @param prefix Optional prefix to add to icon names, true (default) if prefix from icon set should be used.
|
||||
*/
|
||||
export function addCollection(
|
||||
data: IconifyJSON,
|
||||
prefix?: string | boolean
|
||||
): void {
|
||||
const iconPrefix: string =
|
||||
typeof prefix === 'string'
|
||||
? prefix
|
||||
: prefix !== false && typeof data.prefix === 'string'
|
||||
? data.prefix + ':'
|
||||
: '';
|
||||
parseIconSet(data, (name, icon) => {
|
||||
if (icon !== null) {
|
||||
storage[iconPrefix + name] = icon;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Component
|
||||
*/
|
||||
export const Icon = Vue.extend({
|
||||
// Do not inherit other attributes: it is handled by render()
|
||||
// In Vue 2 style is still passed!
|
||||
inheritAttrs: false,
|
||||
|
||||
// Render icon
|
||||
render(createElement: CreateElement): VNode {
|
||||
const props = this.$attrs;
|
||||
|
||||
// Check icon
|
||||
const icon =
|
||||
typeof props.icon === 'string'
|
||||
? storage[props.icon]
|
||||
: typeof props.icon === 'object'
|
||||
? fullIcon(props.icon)
|
||||
: null;
|
||||
|
||||
// Validate icon object
|
||||
if (
|
||||
icon === null ||
|
||||
typeof icon !== 'object' ||
|
||||
typeof icon.body !== 'string'
|
||||
) {
|
||||
// Render child nodes
|
||||
if (this.$slots.default) {
|
||||
const result = this.$slots.default;
|
||||
if (result instanceof Array && result.length > 0) {
|
||||
// If there are multiple child nodes, they must be wrapped in Vue 2
|
||||
return result.length === 1
|
||||
? result[0]
|
||||
: createElement('span', result);
|
||||
}
|
||||
}
|
||||
return (null as unknown) as VNode;
|
||||
}
|
||||
|
||||
// Valid icon: render it
|
||||
return render(createElement, props, this.$data, icon);
|
||||
},
|
||||
});
|
41
packages/vue2/src/props.ts
Normal file
41
packages/vue2/src/props.ts
Normal file
@ -0,0 +1,41 @@
|
||||
import { IconifyIcon } from '@iconify/types';
|
||||
import { IconifyIconCustomisations as IconCustomisations } from '@iconify/core/lib/customisations';
|
||||
|
||||
// Allow rotation to be string
|
||||
/**
|
||||
* Icon customisations
|
||||
*/
|
||||
export type IconifyIconCustomisations = IconCustomisations & {
|
||||
rotate?: string | number;
|
||||
};
|
||||
|
||||
/**
|
||||
* Icon properties
|
||||
*/
|
||||
export interface IconifyIconProps extends IconifyIconCustomisations {
|
||||
// Icon object
|
||||
icon: IconifyIcon | string;
|
||||
|
||||
// Style
|
||||
color?: string;
|
||||
|
||||
// Shorthand properties
|
||||
flip?: string;
|
||||
align?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Properties for element that are mentioned in render.ts
|
||||
*/
|
||||
interface IconifyElementProps {
|
||||
// Unique id, used as base for ids for shapes. Use it to get consistent ids for server side rendering
|
||||
id?: string;
|
||||
|
||||
// Style
|
||||
style?: unknown;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mix of icon properties and HTMLElement properties
|
||||
*/
|
||||
export type IconProps = IconifyElementProps & IconifyIconProps;
|
178
packages/vue2/src/render.ts
Normal file
178
packages/vue2/src/render.ts
Normal file
@ -0,0 +1,178 @@
|
||||
import _Vue, { VNode, VNodeData, RenderContext } from 'vue';
|
||||
import { IconifyIcon } from '@iconify/types';
|
||||
import {
|
||||
FullIconCustomisations,
|
||||
defaults,
|
||||
} from '@iconify/core/lib/customisations';
|
||||
import {
|
||||
flipFromString,
|
||||
alignmentFromString,
|
||||
} from '@iconify/core/lib/customisations/shorthand';
|
||||
import { rotateFromString } from '@iconify/core/lib/customisations/rotate';
|
||||
import { iconToSVG } from '@iconify/core/lib/builder';
|
||||
import { replaceIDs } from '@iconify/core/lib/builder/ids';
|
||||
import { merge } from '@iconify/core/lib/misc/merge';
|
||||
import { IconifyIconCustomisations, IconProps } from './props';
|
||||
|
||||
/**
|
||||
* Default SVG attributes
|
||||
*/
|
||||
const svgDefaults: Record<string, unknown> = {
|
||||
'xmlns': 'http://www.w3.org/2000/svg',
|
||||
'xmlns:xlink': 'http://www.w3.org/1999/xlink',
|
||||
'aria-hidden': true,
|
||||
'role': 'img',
|
||||
};
|
||||
|
||||
/**
|
||||
* Aliases for customisations.
|
||||
* In Vue 'v-' properties are reserved, so v-align and v-flip must be renamed
|
||||
*/
|
||||
let customisationAliases = {};
|
||||
['horizontal', 'vertical'].forEach((prefix) => {
|
||||
['Align', 'Flip'].forEach((suffix) => {
|
||||
const attr = prefix.slice(0, 1) + suffix;
|
||||
// vertical-align
|
||||
customisationAliases[prefix + '-' + suffix.toLowerCase()] = attr;
|
||||
// v-align
|
||||
customisationAliases[
|
||||
prefix.slice(0, 1) + '-' + suffix.toLowerCase()
|
||||
] = attr;
|
||||
// verticalAlign
|
||||
customisationAliases[prefix + suffix] = attr;
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Interface for inline style
|
||||
*/
|
||||
type VNodeStyle = (string | Record<string, unknown>)[];
|
||||
|
||||
/**
|
||||
* Render icon
|
||||
*/
|
||||
export const render = (
|
||||
createElement: typeof _Vue.prototype.$createElement,
|
||||
|
||||
// context.props
|
||||
props: IconProps,
|
||||
|
||||
// context.data
|
||||
contextData: VNodeData,
|
||||
|
||||
// Icon must be validated before calling this function
|
||||
icon: Required<IconifyIcon>
|
||||
): VNode => {
|
||||
// Split properties
|
||||
const customisations = merge(
|
||||
defaults,
|
||||
props as IconifyIconCustomisations
|
||||
) as FullIconCustomisations;
|
||||
const componentProps = merge(svgDefaults);
|
||||
|
||||
// Style in Vue 2 components is always passed to rendered component, so no point in parsing it
|
||||
const style: Record<string, unknown> = {};
|
||||
|
||||
// Get element properties
|
||||
for (let key in props) {
|
||||
const value = props[key];
|
||||
switch (key) {
|
||||
// Properties to ignore
|
||||
case 'icon':
|
||||
case 'style':
|
||||
break;
|
||||
|
||||
// Flip as string: 'horizontal,vertical'
|
||||
case 'flip':
|
||||
if (typeof value === 'string') {
|
||||
flipFromString(customisations, value);
|
||||
}
|
||||
break;
|
||||
|
||||
// Alignment as string
|
||||
case 'align':
|
||||
if (typeof value === 'string') {
|
||||
alignmentFromString(customisations, value);
|
||||
}
|
||||
break;
|
||||
|
||||
// Color: override style
|
||||
case 'color':
|
||||
style.color = value;
|
||||
break;
|
||||
|
||||
// Rotation as string
|
||||
case 'rotate':
|
||||
if (typeof value === 'string') {
|
||||
customisations[key] = rotateFromString(value);
|
||||
} else if (typeof value === 'number') {
|
||||
customisations[key] = value;
|
||||
}
|
||||
break;
|
||||
|
||||
// Remove aria-hidden
|
||||
case 'ariaHidden':
|
||||
case 'aria-hidden':
|
||||
// Vue transforms 'aria-hidden' property to 'ariaHidden'
|
||||
if (value !== true && value !== 'true') {
|
||||
delete componentProps['aria-hidden'];
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if (customisationAliases[key] !== void 0) {
|
||||
// Aliases for customisations
|
||||
customisations[customisationAliases[key]] = value;
|
||||
} else if (defaults[key] === void 0) {
|
||||
// Copy missing property if it does not exist in customisations
|
||||
componentProps[key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Generate icon
|
||||
const item = iconToSVG(icon, customisations);
|
||||
|
||||
// Add icon stuff
|
||||
for (let key in item.attributes) {
|
||||
componentProps[key] = item.attributes[key];
|
||||
}
|
||||
|
||||
if (item.inline) {
|
||||
style.verticalAlign = '-0.125em';
|
||||
}
|
||||
|
||||
// Counter for ids based on "id" property to render icons consistently on server and client
|
||||
let localCounter = 0;
|
||||
const id = props.id;
|
||||
|
||||
// Generate node data
|
||||
const data: VNodeData = {
|
||||
attrs: componentProps,
|
||||
domProps: {
|
||||
innerHTML: replaceIDs(
|
||||
item.body,
|
||||
id ? () => id + '-' + localCounter++ : 'iconify-vue-'
|
||||
),
|
||||
},
|
||||
};
|
||||
if (Object.keys(style).length > 0) {
|
||||
data.style = style;
|
||||
}
|
||||
|
||||
if (contextData) {
|
||||
['on', 'ref'].forEach((attr) => {
|
||||
if (contextData[attr] !== void 0) {
|
||||
data[attr] = contextData[attr];
|
||||
}
|
||||
});
|
||||
['staticClass', 'class'].forEach((attr) => {
|
||||
if (contextData[attr] !== void 0) {
|
||||
data.class = contextData[attr];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Render icon
|
||||
return createElement('svg', data);
|
||||
};
|
@ -1,570 +0,0 @@
|
||||
import { mount } from '@vue/test-utils';
|
||||
import IconifyIcon from '../';
|
||||
|
||||
const iconData = {
|
||||
body:
|
||||
'<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
|
||||
width: 24,
|
||||
height: 24,
|
||||
};
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
// Spacing for HTML matches
|
||||
const spacing = (count) => {
|
||||
return '\n' + ' '.repeat(count);
|
||||
};
|
||||
|
||||
describe('Mounting component', () => {
|
||||
test('with wrapper', () => {
|
||||
const Wrapper = {
|
||||
components: { IconifyIcon },
|
||||
template: `<iconify-icon />`,
|
||||
};
|
||||
|
||||
const wrapper = mount(Wrapper, {});
|
||||
const item = wrapper.findComponent(IconifyIcon);
|
||||
expect(item.exists()).toBe(true);
|
||||
});
|
||||
|
||||
test('without wrapper', () => {
|
||||
const wrapper = mount(IconifyIcon, {});
|
||||
const item = wrapper.findComponent(IconifyIcon);
|
||||
expect(item.exists()).toBe(true);
|
||||
expect(item.html()).toStrictEqual(
|
||||
'<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 16 16"></svg>'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Rendering icon', () => {
|
||||
test('as object', () => {
|
||||
const Wrapper = {
|
||||
components: { IconifyIcon },
|
||||
template: `<iconify-icon :icon='icon' />`,
|
||||
data() {
|
||||
return {
|
||||
icon: iconData,
|
||||
};
|
||||
},
|
||||
};
|
||||
const wrapper = mount(Wrapper, {});
|
||||
|
||||
const item = wrapper.findComponent(IconifyIcon);
|
||||
expect(item.exists()).toBe(true);
|
||||
expect(item.html()).toStrictEqual(
|
||||
'<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">' +
|
||||
spacing(1) +
|
||||
'<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"></path>' +
|
||||
spacing(0) +
|
||||
'</svg>'
|
||||
);
|
||||
});
|
||||
|
||||
test('as string', () => {
|
||||
const iconName = 'test-string';
|
||||
IconifyIcon.addIcon(iconName, iconData);
|
||||
|
||||
const Wrapper = {
|
||||
components: { IconifyIcon },
|
||||
template: `<iconify-icon :icon='icon' />`,
|
||||
data() {
|
||||
return {
|
||||
icon: iconName,
|
||||
};
|
||||
},
|
||||
};
|
||||
const wrapper = mount(Wrapper, {});
|
||||
|
||||
const item = wrapper.findComponent(IconifyIcon);
|
||||
expect(item.exists()).toBe(true);
|
||||
expect(item.html()).toStrictEqual(
|
||||
'<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">' +
|
||||
spacing(1) +
|
||||
'<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"></path>' +
|
||||
spacing(0) +
|
||||
'</svg>'
|
||||
);
|
||||
});
|
||||
|
||||
test('as string with icon set', () => {
|
||||
const iconSet = {
|
||||
prefix: 'mdi-light',
|
||||
icons: {
|
||||
account: {
|
||||
body:
|
||||
'<path d="M11.5 14c4.142 0 7.5 1.567 7.5 3.5V20H4v-2.5c0-1.933 3.358-3.5 7.5-3.5zm6.5 3.5c0-1.38-2.91-2.5-6.5-2.5S5 16.12 5 17.5V19h13v-1.5zM11.5 5a3.5 3.5 0 1 1 0 7a3.5 3.5 0 0 1 0-7zm0 1a2.5 2.5 0 1 0 0 5a2.5 2.5 0 0 0 0-5z" fill="currentColor"/>',
|
||||
},
|
||||
home: {
|
||||
body:
|
||||
'<path d="M16 8.414l-4.5-4.5L4.414 11H6v8h3v-6h5v6h3v-8h1.586L17 9.414V6h-1v2.414zM2 12l9.5-9.5L15 6V5h3v4l3 3h-3v7.998h-5v-6h-3v6H5V12H2z" fill="currentColor"/>',
|
||||
},
|
||||
},
|
||||
width: 24,
|
||||
height: 24,
|
||||
};
|
||||
|
||||
IconifyIcon.addCollection(iconSet);
|
||||
|
||||
const Wrapper = {
|
||||
components: { IconifyIcon },
|
||||
template: `<iconify-icon icon='mdi-light:home' />`,
|
||||
};
|
||||
const wrapper = mount(Wrapper, {});
|
||||
|
||||
const item = wrapper.findComponent(IconifyIcon);
|
||||
expect(item.exists()).toBe(true);
|
||||
expect(item.html()).toStrictEqual(
|
||||
'<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">' +
|
||||
spacing(1) +
|
||||
'<path d="M16 8.414l-4.5-4.5L4.414 11H6v8h3v-6h5v6h3v-8h1.586L17 9.414V6h-1v2.414zM2 12l9.5-9.5L15 6V5h3v4l3 3h-3v7.998h-5v-6h-3v6H5V12H2z" fill="currentColor"></path>' +
|
||||
spacing(0) +
|
||||
'</svg>'
|
||||
);
|
||||
});
|
||||
|
||||
test('as string with icon set with custom prefix', () => {
|
||||
const iconSet = {
|
||||
prefix: 'mdi-light',
|
||||
icons: {
|
||||
'account-alert': {
|
||||
body:
|
||||
'<path d="M10.5 14c4.142 0 7.5 1.567 7.5 3.5V20H3v-2.5c0-1.933 3.358-3.5 7.5-3.5zm6.5 3.5c0-1.38-2.91-2.5-6.5-2.5S4 16.12 4 17.5V19h13v-1.5zM10.5 5a3.5 3.5 0 1 1 0 7a3.5 3.5 0 0 1 0-7zm0 1a2.5 2.5 0 1 0 0 5a2.5 2.5 0 0 0 0-5zM20 16v-1h1v1h-1zm0-3V7h1v6h-1z" fill="currentColor"/>',
|
||||
},
|
||||
'link': {
|
||||
body:
|
||||
'<path d="M8 13v-1h7v1H8zm7.5-6a5.5 5.5 0 1 1 0 11H13v-1h2.5a4.5 4.5 0 1 0 0-9H13V7h2.5zm-8 11a5.5 5.5 0 1 1 0-11H10v1H7.5a4.5 4.5 0 1 0 0 9H10v1H7.5z" fill="currentColor"/>',
|
||||
},
|
||||
},
|
||||
width: 24,
|
||||
height: 24,
|
||||
};
|
||||
|
||||
IconifyIcon.addCollection(iconSet, 'custom-');
|
||||
|
||||
const Wrapper = {
|
||||
components: { IconifyIcon },
|
||||
template: `<iconify-icon icon='custom-link' />`,
|
||||
};
|
||||
const wrapper = mount(Wrapper, {});
|
||||
|
||||
const item = wrapper.findComponent(IconifyIcon);
|
||||
expect(item.exists()).toBe(true);
|
||||
expect(item.html()).toStrictEqual(
|
||||
'<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">' +
|
||||
spacing(1) +
|
||||
'<path d="M8 13v-1h7v1H8zm7.5-6a5.5 5.5 0 1 1 0 11H13v-1h2.5a4.5 4.5 0 1 0 0-9H13V7h2.5zm-8 11a5.5 5.5 0 1 1 0-11H10v1H7.5a4.5 4.5 0 1 0 0 9H10v1H7.5z" fill="currentColor"></path>' +
|
||||
spacing(0) +
|
||||
'</svg>'
|
||||
);
|
||||
});
|
||||
|
||||
test('replacing id', () => {
|
||||
const Wrapper = {
|
||||
components: { IconifyIcon },
|
||||
template: `<iconify-icon :icon='icon' />`,
|
||||
data() {
|
||||
return {
|
||||
icon: iconDataWithID,
|
||||
};
|
||||
},
|
||||
};
|
||||
const wrapper = mount(Wrapper, {});
|
||||
|
||||
const item = wrapper.findComponent(IconifyIcon);
|
||||
expect(item.exists()).toBe(true);
|
||||
expect(item.html()).not.toMatch('id="ssvg-id-1st-place-medala"');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Passing attributes', () => {
|
||||
test('title', () => {
|
||||
const Wrapper = {
|
||||
components: { IconifyIcon },
|
||||
template: `<iconify-icon :icon='icon' title='Icon!' />`,
|
||||
data() {
|
||||
return {
|
||||
icon: iconData,
|
||||
};
|
||||
},
|
||||
};
|
||||
const wrapper = mount(Wrapper, {});
|
||||
|
||||
const item = wrapper.findComponent(IconifyIcon);
|
||||
expect(item.exists()).toBe(true);
|
||||
expect(item.html()).toStrictEqual(
|
||||
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" title="Icon!" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24">' +
|
||||
spacing(1) +
|
||||
'<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"></path>' +
|
||||
spacing(0) +
|
||||
'</svg>'
|
||||
);
|
||||
});
|
||||
|
||||
test('aria-hidden', () => {
|
||||
const Wrapper = {
|
||||
components: { IconifyIcon },
|
||||
template: `<iconify-icon :icon='icon' :aria-hidden='false' />`,
|
||||
data() {
|
||||
return {
|
||||
icon: iconData,
|
||||
};
|
||||
},
|
||||
};
|
||||
const wrapper = mount(Wrapper, {});
|
||||
|
||||
const item = wrapper.findComponent(IconifyIcon);
|
||||
expect(item.exists()).toBe(true);
|
||||
expect(item.html()).toStrictEqual(
|
||||
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24">' +
|
||||
spacing(1) +
|
||||
'<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"></path>' +
|
||||
spacing(0) +
|
||||
'</svg>'
|
||||
);
|
||||
});
|
||||
|
||||
test('ariaHidden', () => {
|
||||
const Wrapper = {
|
||||
components: { IconifyIcon },
|
||||
template: `<iconify-icon :icon='icon' :ariaHidden='false' />`,
|
||||
data() {
|
||||
return {
|
||||
icon: iconData,
|
||||
};
|
||||
},
|
||||
};
|
||||
const wrapper = mount(Wrapper, {});
|
||||
|
||||
const item = wrapper.findComponent(IconifyIcon);
|
||||
expect(item.exists()).toBe(true);
|
||||
expect(item.html()).toStrictEqual(
|
||||
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24">' +
|
||||
spacing(1) +
|
||||
'<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"></path>' +
|
||||
spacing(0) +
|
||||
'</svg>'
|
||||
);
|
||||
});
|
||||
|
||||
test('attributes that cannot change', () => {
|
||||
const Wrapper = {
|
||||
components: { IconifyIcon },
|
||||
template: `<iconify-icon :icon='icon' viewBox='0 0 0 0' preserveAspectRatio='none' />`,
|
||||
data() {
|
||||
return {
|
||||
icon: iconData,
|
||||
};
|
||||
},
|
||||
};
|
||||
const wrapper = mount(Wrapper, {});
|
||||
|
||||
const item = wrapper.findComponent(IconifyIcon);
|
||||
expect(item.exists()).toBe(true);
|
||||
expect(item.html()).toStrictEqual(
|
||||
// same values, but different order
|
||||
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" viewBox="0 0 24 24" preserveAspectRatio="xMidYMid meet" width="1em" height="1em">' +
|
||||
spacing(1) +
|
||||
'<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"></path>' +
|
||||
spacing(0) +
|
||||
'</svg>'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Dimensions', () => {
|
||||
test('height', () => {
|
||||
const Wrapper = {
|
||||
components: { IconifyIcon },
|
||||
template: `<iconify-icon :icon='icon' :height='height' />`,
|
||||
data() {
|
||||
return {
|
||||
icon: iconData,
|
||||
height: 24,
|
||||
};
|
||||
},
|
||||
};
|
||||
const wrapper = mount(Wrapper, {});
|
||||
|
||||
const item = wrapper.findComponent(IconifyIcon);
|
||||
expect(item.exists()).toBe(true);
|
||||
expect(item.html()).toStrictEqual(
|
||||
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="24" height="24" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24">' +
|
||||
spacing(1) +
|
||||
'<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"></path>' +
|
||||
spacing(0) +
|
||||
'</svg>'
|
||||
);
|
||||
});
|
||||
|
||||
test('width and height', () => {
|
||||
const Wrapper = {
|
||||
components: { IconifyIcon },
|
||||
template: `<iconify-icon :icon='icon' :width='width' :height='height' />`,
|
||||
data() {
|
||||
return {
|
||||
icon: iconData,
|
||||
width: 32, // as number
|
||||
height: '48', // as string
|
||||
};
|
||||
},
|
||||
};
|
||||
const wrapper = mount(Wrapper, {});
|
||||
|
||||
const item = wrapper.findComponent(IconifyIcon);
|
||||
expect(item.exists()).toBe(true);
|
||||
expect(item.html()).toStrictEqual(
|
||||
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="32" height="48" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24">' +
|
||||
spacing(1) +
|
||||
'<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"></path>' +
|
||||
spacing(0) +
|
||||
'</svg>'
|
||||
);
|
||||
});
|
||||
|
||||
test('auto', () => {
|
||||
const Wrapper = {
|
||||
components: { IconifyIcon },
|
||||
template: `<iconify-icon :icon='icon' :height='height' />`,
|
||||
data() {
|
||||
return {
|
||||
icon: iconData,
|
||||
height: 'auto',
|
||||
};
|
||||
},
|
||||
};
|
||||
const wrapper = mount(Wrapper, {});
|
||||
|
||||
const item = wrapper.findComponent(IconifyIcon);
|
||||
expect(item.exists()).toBe(true);
|
||||
expect(item.html()).toStrictEqual(
|
||||
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="24" height="24" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24">' +
|
||||
spacing(1) +
|
||||
'<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"></path>' +
|
||||
spacing(0) +
|
||||
'</svg>'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Rotation', () => {
|
||||
test('number', () => {
|
||||
const Wrapper = {
|
||||
components: { IconifyIcon },
|
||||
template: `<iconify-icon :icon='icon' :rotate='rotate' />`,
|
||||
data() {
|
||||
return {
|
||||
icon: iconData,
|
||||
rotate: 1,
|
||||
};
|
||||
},
|
||||
};
|
||||
const wrapper = mount(Wrapper, {});
|
||||
|
||||
const item = wrapper.findComponent(IconifyIcon);
|
||||
expect(item.exists()).toBe(true);
|
||||
expect(item.html()).toMatch('rotate(90 ');
|
||||
});
|
||||
|
||||
test('string', () => {
|
||||
const Wrapper = {
|
||||
components: { IconifyIcon },
|
||||
template: `<iconify-icon :icon='icon' rotate='270deg' />`,
|
||||
data() {
|
||||
return {
|
||||
icon: iconData,
|
||||
};
|
||||
},
|
||||
};
|
||||
const wrapper = mount(Wrapper, {});
|
||||
|
||||
const item = wrapper.findComponent(IconifyIcon);
|
||||
expect(item.exists()).toBe(true);
|
||||
expect(item.html()).toMatch('rotate(-90 ');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Flip', () => {
|
||||
test('boolean', () => {
|
||||
const Wrapper = {
|
||||
components: { IconifyIcon },
|
||||
template: `<iconify-icon :icon='icon' :horizontalFlip='horizontalFlip' />`,
|
||||
data() {
|
||||
return {
|
||||
icon: iconData,
|
||||
horizontalFlip: true,
|
||||
};
|
||||
},
|
||||
};
|
||||
const wrapper = mount(Wrapper, {});
|
||||
|
||||
const item = wrapper.findComponent(IconifyIcon);
|
||||
expect(item.exists()).toBe(true);
|
||||
expect(item.html()).toMatch('scale(-1 1)');
|
||||
});
|
||||
|
||||
test('string', () => {
|
||||
const Wrapper = {
|
||||
components: { IconifyIcon },
|
||||
template: `<iconify-icon :icon='icon' flip='vertical' />`,
|
||||
data() {
|
||||
return {
|
||||
icon: iconData,
|
||||
};
|
||||
},
|
||||
};
|
||||
const wrapper = mount(Wrapper, {});
|
||||
|
||||
const item = wrapper.findComponent(IconifyIcon);
|
||||
expect(item.exists()).toBe(true);
|
||||
expect(item.html()).toMatch('scale(1 -1)');
|
||||
});
|
||||
|
||||
test('string and boolean', () => {
|
||||
const Wrapper = {
|
||||
components: { IconifyIcon },
|
||||
template: `<iconify-icon :icon='icon' flip='horizontal' :verticalFlip='true' />`,
|
||||
data() {
|
||||
return {
|
||||
icon: iconData,
|
||||
};
|
||||
},
|
||||
};
|
||||
const wrapper = mount(Wrapper, {});
|
||||
|
||||
const item = wrapper.findComponent(IconifyIcon);
|
||||
expect(item.exists()).toBe(true);
|
||||
// horizontal + vertical = 180deg rotation
|
||||
expect(item.html()).toMatch('rotate(180 ');
|
||||
});
|
||||
|
||||
test('string for boolean attribute', () => {
|
||||
const Wrapper = {
|
||||
components: { IconifyIcon },
|
||||
template: `<iconify-icon :icon='icon' horizontalFlip='true' />`,
|
||||
data() {
|
||||
return {
|
||||
icon: iconData,
|
||||
};
|
||||
},
|
||||
};
|
||||
const wrapper = mount(Wrapper, {});
|
||||
|
||||
const item = wrapper.findComponent(IconifyIcon);
|
||||
expect(item.exists()).toBe(true);
|
||||
expect(item.html()).toMatch('scale(-1 1)');
|
||||
});
|
||||
|
||||
test('shorthand and boolean', () => {
|
||||
// 'flip' is processed after 'hFlip', overwriting value
|
||||
const Wrapper = {
|
||||
components: { IconifyIcon },
|
||||
template: `<iconify-icon :icon='icon' flip='horizontal' :hFlip='false' />`,
|
||||
data() {
|
||||
return {
|
||||
icon: iconData,
|
||||
};
|
||||
},
|
||||
};
|
||||
const wrapper = mount(Wrapper, {});
|
||||
|
||||
const item = wrapper.findComponent(IconifyIcon);
|
||||
expect(item.exists()).toBe(true);
|
||||
expect(item.html()).toMatch('scale(-1 1)');
|
||||
});
|
||||
|
||||
test('shorthand and boolean as string', () => {
|
||||
const Wrapper = {
|
||||
components: { IconifyIcon },
|
||||
template: `<iconify-icon :icon='icon' flip='vertical' horizontalFlip='true' />`,
|
||||
data() {
|
||||
return {
|
||||
icon: iconData,
|
||||
};
|
||||
},
|
||||
};
|
||||
const wrapper = mount(Wrapper, {});
|
||||
|
||||
const item = wrapper.findComponent(IconifyIcon);
|
||||
expect(item.exists()).toBe(true);
|
||||
// horizontal + vertical = 180deg rotation
|
||||
expect(item.html()).toMatch('rotate(180 ');
|
||||
});
|
||||
|
||||
test('wrong case', () => {
|
||||
const Wrapper = {
|
||||
components: { IconifyIcon },
|
||||
template: `<iconify-icon :icon='icon' :verticalflip='verticalflip' :horizontalflip='true' />`,
|
||||
data() {
|
||||
return {
|
||||
icon: iconData,
|
||||
verticalflip: true,
|
||||
};
|
||||
},
|
||||
};
|
||||
const wrapper = mount(Wrapper, {});
|
||||
|
||||
const item = wrapper.findComponent(IconifyIcon);
|
||||
expect(item.exists()).toBe(true);
|
||||
expect(item.html()).not.toMatch('scale(');
|
||||
expect(item.html()).not.toMatch('rotate(');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Alignment and slice', () => {
|
||||
test('vAlign and slice', () => {
|
||||
const Wrapper = {
|
||||
components: { IconifyIcon },
|
||||
template: `<iconify-icon :icon='icon' vAlign='top' :slice='true' />`,
|
||||
data() {
|
||||
return {
|
||||
icon: iconData,
|
||||
};
|
||||
},
|
||||
};
|
||||
const wrapper = mount(Wrapper, {});
|
||||
|
||||
const item = wrapper.findComponent(IconifyIcon);
|
||||
expect(item.exists()).toBe(true);
|
||||
expect(item.html()).toMatch('preserveAspectRatio="xMidYMin slice"');
|
||||
});
|
||||
|
||||
test('string', () => {
|
||||
const Wrapper = {
|
||||
components: { IconifyIcon },
|
||||
template: `<iconify-icon :icon='icon' align='left bottom' />`,
|
||||
data() {
|
||||
return {
|
||||
icon: iconData,
|
||||
};
|
||||
},
|
||||
};
|
||||
const wrapper = mount(Wrapper, {});
|
||||
|
||||
const item = wrapper.findComponent(IconifyIcon);
|
||||
expect(item.exists()).toBe(true);
|
||||
expect(item.html()).toMatch('preserveAspectRatio="xMinYMax meet"');
|
||||
});
|
||||
|
||||
test('Alignment aliases', () => {
|
||||
const Wrapper = {
|
||||
components: { IconifyIcon },
|
||||
template: `<iconify-icon :icon='icon' verticalAlign='top' horizontalAlign='right' />`,
|
||||
data() {
|
||||
return {
|
||||
icon: iconData,
|
||||
};
|
||||
},
|
||||
};
|
||||
const wrapper = mount(Wrapper, {});
|
||||
|
||||
const item = wrapper.findComponent(IconifyIcon);
|
||||
expect(item.exists()).toBe(true);
|
||||
expect(item.html()).toMatch('preserveAspectRatio="xMaxYMin meet"');
|
||||
});
|
||||
});
|
40
packages/vue2/tests/iconify/10-basic.test.js
Normal file
40
packages/vue2/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, {
|
||||
propsData: {
|
||||
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/vue2/tests/iconify/10-empty.test.js
Normal file
46
packages/vue2/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, {
|
||||
propsData: {},
|
||||
});
|
||||
|
||||
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.text()).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(
|
||||
'<span><i class="fa fa-home"></i> Home icon</span>'
|
||||
);
|
||||
});
|
||||
});
|
144
packages/vue2/tests/iconify/20-attributes.test.js
Normal file
144
packages/vue2/tests/iconify/20-attributes.test.js
Normal file
@ -0,0 +1,144 @@
|
||||
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', () => {
|
||||
// In Vue 2 style overrides color in class components
|
||||
// Can be fixed by using functional component, but functional component is not possible with API
|
||||
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: green;"');
|
||||
});
|
||||
|
||||
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/vue2/tests/iconify/20-dimensions.test.js
Normal file
68
packages/vue2/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/vue2/tests/iconify/20-ids.test.js
Normal file
41
packages/vue2/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"');
|
||||
});
|
||||
});
|
135
packages/vue2/tests/iconify/20-inline.test.js
Normal file
135
packages/vue2/tests/iconify/20-inline.test.js
Normal file
@ -0,0 +1,135 @@
|
||||
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(
|
||||
'vertical-align: -0.125em; color: red;'
|
||||
);
|
||||
});
|
||||
|
||||
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(
|
||||
'vertical-align: -0.125em; color: red;'
|
||||
);
|
||||
});
|
||||
|
||||
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(
|
||||
'vertical-align: -0.125em; color: red;'
|
||||
);
|
||||
});
|
||||
|
||||
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, {});
|
||||
// For some reason style is duplicated
|
||||
expect(wrapper.html()).toContain(
|
||||
'style="vertical-align: 0; vertical-align: 0;"'
|
||||
);
|
||||
});
|
||||
});
|
208
packages/vue2/tests/iconify/20-transformations.test.js
Normal file
208
packages/vue2/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"');
|
||||
});
|
||||
});
|
40
packages/vue2/tests/offline/10-basic.test.js
Normal file
40
packages/vue2/tests/offline/10-basic.test.js
Normal file
@ -0,0 +1,40 @@
|
||||
import { mount } from '@vue/test-utils';
|
||||
import { Icon } from '../../dist/offline';
|
||||
|
||||
const iconData = {
|
||||
body:
|
||||
'<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
|
||||
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, {
|
||||
propsData: {
|
||||
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/vue2/tests/offline/10-empty.test.js
Normal file
46
packages/vue2/tests/offline/10-empty.test.js
Normal file
@ -0,0 +1,46 @@
|
||||
import { mount } from '@vue/test-utils';
|
||||
import { Icon } from '../../dist/offline';
|
||||
|
||||
describe('Empty icon', () => {
|
||||
test('basic test', () => {
|
||||
const wrapper = mount(Icon, {
|
||||
propsData: {},
|
||||
});
|
||||
|
||||
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.text()).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(
|
||||
'<span><i class="fa fa-home"></i> Home icon</span>'
|
||||
);
|
||||
});
|
||||
});
|
144
packages/vue2/tests/offline/20-attributes.test.js
Normal file
144
packages/vue2/tests/offline/20-attributes.test.js
Normal file
@ -0,0 +1,144 @@
|
||||
import { mount } from '@vue/test-utils';
|
||||
import { Icon } from '../../dist/offline';
|
||||
|
||||
const iconData = {
|
||||
body:
|
||||
'<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
|
||||
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', () => {
|
||||
// In Vue 2 style overrides color in class components
|
||||
// Can be fixed by using functional component, but functional component is not possible with API
|
||||
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: green;"');
|
||||
});
|
||||
|
||||
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/vue2/tests/offline/20-dimensions.test.js
Normal file
68
packages/vue2/tests/offline/20-dimensions.test.js
Normal file
@ -0,0 +1,68 @@
|
||||
import { mount } from '@vue/test-utils';
|
||||
import { Icon } from '../../dist/offline';
|
||||
|
||||
const iconData = {
|
||||
body:
|
||||
'<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
|
||||
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/vue2/tests/offline/20-ids.test.js
Normal file
41
packages/vue2/tests/offline/20-ids.test.js
Normal file
@ -0,0 +1,41 @@
|
||||
import { mount } from '@vue/test-utils';
|
||||
import { Icon } from '../../dist/offline';
|
||||
|
||||
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"');
|
||||
});
|
||||
});
|
135
packages/vue2/tests/offline/20-inline.test.js
Normal file
135
packages/vue2/tests/offline/20-inline.test.js
Normal file
@ -0,0 +1,135 @@
|
||||
import { mount } from '@vue/test-utils';
|
||||
import { Icon } from '../../dist/offline';
|
||||
|
||||
const iconData = {
|
||||
body:
|
||||
'<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
|
||||
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(
|
||||
'vertical-align: -0.125em; color: red;'
|
||||
);
|
||||
});
|
||||
|
||||
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(
|
||||
'vertical-align: -0.125em; color: red;'
|
||||
);
|
||||
});
|
||||
|
||||
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, {});
|
||||
// For some reason style is duplicated
|
||||
expect(wrapper.html()).toContain(
|
||||
'style="vertical-align: 0; vertical-align: 0;"'
|
||||
);
|
||||
});
|
||||
});
|
100
packages/vue2/tests/offline/20-storage.test.js
Normal file
100
packages/vue2/tests/offline/20-storage.test.js
Normal file
@ -0,0 +1,100 @@
|
||||
import { mount } from '@vue/test-utils';
|
||||
import { Icon, addIcon, addCollection } from '../../dist/offline';
|
||||
|
||||
const iconData = {
|
||||
body:
|
||||
'<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
|
||||
width: 24,
|
||||
height: 24,
|
||||
};
|
||||
|
||||
describe('Using storage', () => {
|
||||
test('using storage', () => {
|
||||
addIcon('test-icon', iconData);
|
||||
|
||||
const Wrapper = {
|
||||
components: { Icon },
|
||||
template: `<Icon icon="test-icon" />`,
|
||||
};
|
||||
|
||||
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('using storage with icon set', () => {
|
||||
const iconSet = {
|
||||
prefix: 'mdi-light',
|
||||
icons: {
|
||||
account: {
|
||||
body:
|
||||
'<path d="M11.5 14c4.142 0 7.5 1.567 7.5 3.5V20H4v-2.5c0-1.933 3.358-3.5 7.5-3.5zm6.5 3.5c0-1.38-2.91-2.5-6.5-2.5S5 16.12 5 17.5V19h13v-1.5zM11.5 5a3.5 3.5 0 1 1 0 7a3.5 3.5 0 0 1 0-7zm0 1a2.5 2.5 0 1 0 0 5a2.5 2.5 0 0 0 0-5z" fill="currentColor"/>',
|
||||
},
|
||||
home: {
|
||||
body:
|
||||
'<path d="M16 8.414l-4.5-4.5L4.414 11H6v8h3v-6h5v6h3v-8h1.586L17 9.414V6h-1v2.414zM2 12l9.5-9.5L15 6V5h3v4l3 3h-3v7.998h-5v-6h-3v6H5V12H2z" fill="currentColor"/>',
|
||||
},
|
||||
},
|
||||
width: 24,
|
||||
height: 24,
|
||||
};
|
||||
|
||||
addCollection(iconSet);
|
||||
|
||||
const Wrapper = {
|
||||
components: { Icon },
|
||||
template: `<Icon :icon="icon" />`,
|
||||
data() {
|
||||
return {
|
||||
icon: 'mdi-light:account',
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
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="M11.5 14c4.142 0 7.5 1.567 7.5 3.5V20H4v-2.5c0-1.933 3.358-3.5 7.5-3.5zm6.5 3.5c0-1.38-2.91-2.5-6.5-2.5S5 16.12 5 17.5V19h13v-1.5zM11.5 5a3.5 3.5 0 1 1 0 7a3.5 3.5 0 0 1 0-7zm0 1a2.5 2.5 0 1 0 0 5a2.5 2.5 0 0 0 0-5z" fill="currentColor"></path></svg>'
|
||||
);
|
||||
});
|
||||
|
||||
test('using storage with icon set with custom prefix', () => {
|
||||
const iconSet = {
|
||||
prefix: 'mdi-light',
|
||||
icons: {
|
||||
'account-alert': {
|
||||
body:
|
||||
'<path d="M10.5 14c4.142 0 7.5 1.567 7.5 3.5V20H3v-2.5c0-1.933 3.358-3.5 7.5-3.5zm6.5 3.5c0-1.38-2.91-2.5-6.5-2.5S4 16.12 4 17.5V19h13v-1.5zM10.5 5a3.5 3.5 0 1 1 0 7a3.5 3.5 0 0 1 0-7zm0 1a2.5 2.5 0 1 0 0 5a2.5 2.5 0 0 0 0-5zM20 16v-1h1v1h-1zm0-3V7h1v6h-1z" fill="currentColor"/>',
|
||||
},
|
||||
'link': {
|
||||
body:
|
||||
'<path d="M8 13v-1h7v1H8zm7.5-6a5.5 5.5 0 1 1 0 11H13v-1h2.5a4.5 4.5 0 1 0 0-9H13V7h2.5zm-8 11a5.5 5.5 0 1 1 0-11H10v1H7.5a4.5 4.5 0 1 0 0 9H10v1H7.5z" fill="currentColor"/>',
|
||||
},
|
||||
},
|
||||
width: 24,
|
||||
height: 24,
|
||||
};
|
||||
|
||||
addCollection(iconSet, 'custom-');
|
||||
|
||||
const Wrapper = {
|
||||
components: { Icon },
|
||||
template: `<Icon icon="custom-link" />`,
|
||||
};
|
||||
|
||||
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="M8 13v-1h7v1H8zm7.5-6a5.5 5.5 0 1 1 0 11H13v-1h2.5a4.5 4.5 0 1 0 0-9H13V7h2.5zm-8 11a5.5 5.5 0 1 1 0-11H10v1H7.5a4.5 4.5 0 1 0 0 9H10v1H7.5z" fill="currentColor"></path></svg>'
|
||||
);
|
||||
});
|
||||
|
||||
test('missing icon from storage', () => {
|
||||
const Wrapper = {
|
||||
components: { Icon },
|
||||
template: `<Icon icon="missing-icon" />`,
|
||||
};
|
||||
|
||||
const wrapper = mount(Wrapper, {});
|
||||
expect(wrapper.html().replace(/\s*\n\s*/g, '')).toEqual('');
|
||||
});
|
||||
});
|
208
packages/vue2/tests/offline/20-transformations.test.js
Normal file
208
packages/vue2/tests/offline/20-transformations.test.js
Normal file
@ -0,0 +1,208 @@
|
||||
import { mount } from '@vue/test-utils';
|
||||
import { Icon } from '../../dist/offline';
|
||||
|
||||
const iconData = {
|
||||
body:
|
||||
'<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
|
||||
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"');
|
||||
});
|
||||
});
|
@ -2,7 +2,7 @@
|
||||
"compilerOptions": {
|
||||
"rootDir": "./src",
|
||||
"outDir": "./lib",
|
||||
"target": "ES2019",
|
||||
"target": "ESNext",
|
||||
"module": "ESNext",
|
||||
"declaration": true,
|
||||
"sourceMap": false,
|
||||
|
Loading…
Reference in New Issue
Block a user