mirror of https://github.com/iconify/api.git
feat: support icons list query from API v2
This commit is contained in:
parent
5c53b0a01a
commit
a67e37fec1
|
|
@ -0,0 +1,71 @@
|
|||
import type { IconifyJSON } from '@iconify/types';
|
||||
import type { IconSetAPIv2IconsList, IconSetIconsListIcons } from '../../../types/icon-set/extra';
|
||||
|
||||
type ThemeKey = 'themes' | 'prefixes' | 'suffixes';
|
||||
const themeKeys: ThemeKey[] = ['themes', 'prefixes', 'suffixes'];
|
||||
|
||||
/**
|
||||
* Prepare data for icons list API v2 response
|
||||
*/
|
||||
export function prepareAPIv2IconsList(iconSet: IconifyJSON, iconsList: IconSetIconsListIcons): IconSetAPIv2IconsList {
|
||||
// Prepare data
|
||||
const rendered: IconSetAPIv2IconsList['rendered'] = {
|
||||
prefix: iconSet.prefix,
|
||||
total: iconsList.visible.size,
|
||||
};
|
||||
|
||||
const info = iconSet.info;
|
||||
if (info) {
|
||||
rendered.title = info.name;
|
||||
rendered.info = info;
|
||||
}
|
||||
|
||||
if (iconsList.uncategorised.length) {
|
||||
rendered.uncategorized = iconsList.uncategorised;
|
||||
}
|
||||
|
||||
// Convert categories
|
||||
if (iconsList.tags.length) {
|
||||
const categories = (rendered.categories = Object.create(null) as Record<string, string[]>);
|
||||
for (let i = 0; i < iconsList.tags.length; i++) {
|
||||
const tag = iconsList.tags[i];
|
||||
categories[tag.title] = tag.icons;
|
||||
}
|
||||
}
|
||||
|
||||
// Hidden icons
|
||||
const hidden = Array.from(iconsList.hidden).concat(Object.keys(iconsList.hiddenAliases));
|
||||
if (hidden.length) {
|
||||
rendered.hidden = hidden;
|
||||
}
|
||||
|
||||
// Add aliases
|
||||
const aliases = {
|
||||
...iconsList.visibleAliases,
|
||||
...iconsList.hiddenAliases,
|
||||
};
|
||||
for (const alias in aliases) {
|
||||
rendered.aliases = aliases;
|
||||
break;
|
||||
}
|
||||
|
||||
// Themes
|
||||
for (let i = 0; i < themeKeys.length; i++) {
|
||||
const key = themeKeys[i] as ThemeKey;
|
||||
if (iconSet[key]) {
|
||||
rendered[key as 'themes'] = iconSet[key as 'themes'];
|
||||
}
|
||||
}
|
||||
|
||||
// Result
|
||||
const result: IconSetAPIv2IconsList = {
|
||||
rendered,
|
||||
};
|
||||
|
||||
// Add characters
|
||||
if (iconSet.chars) {
|
||||
result.chars = iconSet.chars;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
@ -0,0 +1,156 @@
|
|||
import type { IconifyAliases, IconifyJSON, IconifyOptional } from '@iconify/types';
|
||||
import { defaultIconProps } from '@iconify/utils/lib/icon/defaults';
|
||||
import type { IconSetIconsListIcons, IconSetIconsListTag } from '../../../types/icon-set/extra';
|
||||
|
||||
const customisableProps = Object.keys(defaultIconProps) as (keyof IconifyOptional)[];
|
||||
|
||||
/**
|
||||
* Generate icons tree
|
||||
*/
|
||||
export function generateIconSetIconsTree(iconSet: IconifyJSON): IconSetIconsListIcons {
|
||||
const iconSetIcons = iconSet.icons;
|
||||
const iconSetAliases = iconSet.aliases || (Object.create(null) as IconifyAliases);
|
||||
|
||||
const checked: Set<string> = new Set();
|
||||
const visible: Set<string> = new Set();
|
||||
const hidden: Set<string> = new Set();
|
||||
const failed: Set<string> = new Set();
|
||||
const visibleAliases = Object.create(null) as Record<string, string>;
|
||||
const hiddenAliases = Object.create(null) as Record<string, string>;
|
||||
|
||||
// Generate list of tags for each icon
|
||||
const tags: IconSetIconsListTag[] = [];
|
||||
const uncategorised: string[] = [];
|
||||
|
||||
const resolvedTags = Object.create(null) as Record<string, Set<IconSetIconsListTag>>;
|
||||
const categories = iconSet.categories;
|
||||
if (categories) {
|
||||
for (const title in categories) {
|
||||
const items = categories[title];
|
||||
if (items instanceof Array) {
|
||||
const tag: IconSetIconsListTag = {
|
||||
title,
|
||||
icons: [],
|
||||
};
|
||||
tags.push(tag);
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
const name = items[i];
|
||||
(resolvedTags[name] || (resolvedTags[name] = new Set())).add(tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Parse all icons
|
||||
for (const name in iconSetIcons) {
|
||||
const isVisible = !iconSetIcons[name].hidden;
|
||||
(isVisible ? visible : hidden).add(name);
|
||||
checked.add(name);
|
||||
|
||||
if (isVisible) {
|
||||
// Check tag
|
||||
const iconTags = resolvedTags[name];
|
||||
if (iconTags) {
|
||||
// Add icon to each tag
|
||||
iconTags.forEach((tag) => {
|
||||
tag.icons.push(name);
|
||||
});
|
||||
} else {
|
||||
// No tags: uncategorised
|
||||
uncategorised.push(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Parse all aliases
|
||||
const resolve = (name: string) => {
|
||||
if (checked.has(name)) {
|
||||
// Already checked
|
||||
return;
|
||||
}
|
||||
checked.add(name);
|
||||
|
||||
// Mark as failed to avoid loop, will be removed later on success
|
||||
failed.add(name);
|
||||
|
||||
const item = iconSetAliases[name];
|
||||
if (!item) {
|
||||
// Failed
|
||||
return;
|
||||
}
|
||||
|
||||
// Get parent
|
||||
const parent = item.parent;
|
||||
if (!checked.has(parent)) {
|
||||
resolve(parent);
|
||||
}
|
||||
|
||||
// Get parent
|
||||
if (failed.has(parent)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if item has transformations
|
||||
let transformed = false;
|
||||
for (let i = 0; i < customisableProps.length; i++) {
|
||||
if (item[customisableProps[i]] !== void 0) {
|
||||
transformed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Success
|
||||
const isVisible =
|
||||
item.hidden === false || (!item.hidden && (visible.has(parent) || visibleAliases[parent] !== void 0));
|
||||
failed.delete(name);
|
||||
|
||||
// Add tags
|
||||
let itemTags: Set<IconSetIconsListTag> | undefined = resolvedTags[name];
|
||||
if (!itemTags) {
|
||||
// Use tags from parent icon
|
||||
itemTags = resolvedTags[name] = resolvedTags[parent];
|
||||
}
|
||||
|
||||
if (isVisible && transformed) {
|
||||
// Icon: add to tags
|
||||
if (itemTags) {
|
||||
itemTags.forEach((tag) => {
|
||||
tag.icons.push(name);
|
||||
});
|
||||
} else {
|
||||
uncategorised.push(name);
|
||||
}
|
||||
}
|
||||
|
||||
// Add icon
|
||||
if (transformed) {
|
||||
// Treat as new icon
|
||||
(isVisible ? visible : hidden).add(name);
|
||||
} else {
|
||||
// Treat as alias
|
||||
const parentName = visibleAliases[parent] || hiddenAliases[parent] || parent;
|
||||
(isVisible ? visibleAliases : hiddenAliases)[name] = parentName;
|
||||
}
|
||||
};
|
||||
|
||||
for (const name in iconSetAliases) {
|
||||
resolve(name);
|
||||
}
|
||||
|
||||
// Sort icons in tags
|
||||
for (let i = 0; i < tags.length; i++) {
|
||||
tags[i].icons.sort((a, b) => a.localeCompare(b));
|
||||
}
|
||||
uncategorised.sort((a, b) => a.localeCompare(b));
|
||||
|
||||
// Return data
|
||||
return {
|
||||
visible,
|
||||
hidden,
|
||||
visibleAliases,
|
||||
hiddenAliases,
|
||||
failed,
|
||||
tags: tags.filter((tag) => tag.icons.length > 0),
|
||||
uncategorised,
|
||||
};
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
import type { IconifyJSON } from '@iconify/types';
|
||||
import type { IconSetIconsListIcons } from '../../../types/icon-set/extra';
|
||||
|
||||
/**
|
||||
* Removes bad aliases
|
||||
*/
|
||||
export function removeBadAliases(data: IconifyJSON, iconsList: IconSetIconsListIcons) {
|
||||
const aliases = data.aliases;
|
||||
if (!aliases) {
|
||||
return;
|
||||
}
|
||||
iconsList.failed.forEach((name) => {
|
||||
delete aliases[name];
|
||||
});
|
||||
}
|
||||
|
|
@ -7,6 +7,9 @@ import type { MemoryStorage, MemoryStorageItem } from '../../../types/storage';
|
|||
import { createSplitRecordsTree, splitRecords } from '../../storage/split';
|
||||
import { createStorage, createStoredItem } from '../../storage/create';
|
||||
import { getIconSetSplitChunksCount, splitIconSetMainData } from './split';
|
||||
import { generateIconSetIconsTree } from '../lists/icons';
|
||||
import { removeBadAliases } from '../lists/validate';
|
||||
import { prepareAPIv2IconsList } from '../lists/icons-v2';
|
||||
|
||||
/**
|
||||
* Storage
|
||||
|
|
@ -28,6 +31,15 @@ export function storeLoadedIconSet(
|
|||
storage: MemoryStorage<IconifyIcons> = iconSetsStorage,
|
||||
config: SplitIconSetConfig = splitIconSetConfig
|
||||
) {
|
||||
// Get icons list and remove bad aliases
|
||||
const icons = generateIconSetIconsTree(iconSet);
|
||||
removeBadAliases(iconSet, icons);
|
||||
|
||||
// Fix icons counter
|
||||
if (iconSet.info) {
|
||||
iconSet.info.total = icons.visible.size;
|
||||
}
|
||||
|
||||
// Get common items
|
||||
const common = splitIconSetMainData(iconSet);
|
||||
|
||||
|
|
@ -66,6 +78,8 @@ export function storeLoadedIconSet(
|
|||
storage,
|
||||
items: storedItems,
|
||||
tree,
|
||||
icons,
|
||||
apiV2IconsCache: prepareAPIv2IconsList(iconSet, icons),
|
||||
};
|
||||
if (iconSet.info) {
|
||||
result.info = iconSet.info;
|
||||
|
|
|
|||
|
|
@ -1,43 +0,0 @@
|
|||
import type { IconifyJSON } from '@iconify/types';
|
||||
|
||||
/**
|
||||
* Removes bad aliases
|
||||
*/
|
||||
export function removeBadAliases(data: IconifyJSON) {
|
||||
const icons = data.icons;
|
||||
const aliases = data.aliases || {};
|
||||
|
||||
const tested: Set<string> = new Set();
|
||||
const failed: Set<string> = new Set();
|
||||
|
||||
function resolve(name: string): boolean {
|
||||
if (icons[name]) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!tested.has(name)) {
|
||||
// Temporary mark as failed if parent alias points to this alias to avoid infinite loop
|
||||
tested.add(name);
|
||||
failed.add(name);
|
||||
|
||||
// Get parent icon name and resolve it
|
||||
const parent = aliases[name]?.parent;
|
||||
if (parent && resolve(parent)) {
|
||||
failed.delete(name);
|
||||
}
|
||||
}
|
||||
|
||||
return !failed.has(name);
|
||||
}
|
||||
|
||||
// Resolve aliases
|
||||
const keys = Object.keys(aliases);
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
resolve(keys[i]);
|
||||
}
|
||||
|
||||
// Remove failed aliases
|
||||
failed.forEach((name) => {
|
||||
delete aliases[name];
|
||||
});
|
||||
}
|
||||
|
|
@ -13,7 +13,7 @@ export function getIconsToRetrieve(
|
|||
copyTo?: IconifyAliases
|
||||
): Set<string> {
|
||||
const icons: Set<string> = new Set();
|
||||
const aliases = iconSetData.aliases || {};
|
||||
const aliases = iconSetData.aliases || (Object.create(null) as IconifyAliases);
|
||||
|
||||
function resolve(name: string) {
|
||||
if (!aliases[name]) {
|
||||
|
|
@ -102,7 +102,7 @@ export function getStoredIconsData(iconSet: StoredIconSet, names: string[], call
|
|||
// Nothing to retrieve
|
||||
callback({
|
||||
...iconSet.common,
|
||||
icons: {},
|
||||
icons: Object.create(null),
|
||||
aliases,
|
||||
not_found: names,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import fastify, { FastifyReply } from 'fastify';
|
|||
import { appConfig } from '../config/app';
|
||||
import { runWhenLoaded } from '../data/loading';
|
||||
import { iconNameRoutePartialRegEx, iconNameRouteRegEx, splitIconName } from '../misc/name';
|
||||
import { generateAPIv2CollectionResponse } from './responses/collection-v2';
|
||||
import { generateCollectionsListResponse } from './responses/collections';
|
||||
import { generateIconsDataResponse } from './responses/icons';
|
||||
import { generateLastModifiedResponse } from './responses/modified';
|
||||
|
|
@ -114,6 +115,13 @@ export async function startHTTPServer() {
|
|||
});
|
||||
});
|
||||
|
||||
// Icons list, API v2
|
||||
server.get('/collection', (req, res) => {
|
||||
runWhenLoaded(() => {
|
||||
generateAPIv2CollectionResponse(req.query, res);
|
||||
});
|
||||
});
|
||||
|
||||
// Update icon sets
|
||||
server.get('/update', (req, res) => {
|
||||
generateUpdateResponse(req.query, res);
|
||||
|
|
@ -148,6 +156,7 @@ export async function startHTTPServer() {
|
|||
// Error handling
|
||||
server.setDefaultRoute((req, res) => {
|
||||
res.statusCode = 301;
|
||||
console.log('404:', req.url);
|
||||
res.setHeader('Location', appConfig.redirectIndex);
|
||||
|
||||
// Need to set custom headers because hooks don't work here
|
||||
|
|
|
|||
|
|
@ -0,0 +1,51 @@
|
|||
import type { FastifyReply, FastifyRequest } from 'fastify';
|
||||
import { generateIconSetIconsTree } from '../../data/icon-set/lists/icons';
|
||||
import { iconSets } from '../../data/icon-sets';
|
||||
import type { APIv2CollectionResponse } from '../../types/server/v2';
|
||||
import { checkJSONPQuery, sendJSONResponse } from '../helpers/json';
|
||||
|
||||
/**
|
||||
* Send API v2 response
|
||||
*
|
||||
* This response ignores the following parameters:
|
||||
* - `aliases` -> always enabled
|
||||
* - `hidden` -> always enabled
|
||||
*
|
||||
* Those parameters are always requested anyway, so does not make sense to re-create data in case they are disabled
|
||||
*/
|
||||
export function generateAPIv2CollectionResponse(query: FastifyRequest['query'], res: FastifyReply) {
|
||||
const q = (query || {}) as Record<string, string>;
|
||||
|
||||
const wrap = checkJSONPQuery(q);
|
||||
if (!wrap) {
|
||||
// Invalid JSONP callback
|
||||
res.send(400);
|
||||
return;
|
||||
}
|
||||
|
||||
// Get icon set
|
||||
const prefix = q.prefix;
|
||||
if (!prefix || !iconSets[prefix]) {
|
||||
res.send(404);
|
||||
return;
|
||||
}
|
||||
|
||||
const iconSet = iconSets[prefix].item;
|
||||
const apiV2IconsCache = iconSet.apiV2IconsCache;
|
||||
|
||||
// Filter prefixes
|
||||
const response: APIv2CollectionResponse = {
|
||||
...apiV2IconsCache.rendered,
|
||||
};
|
||||
|
||||
if (!q.info) {
|
||||
// Delete info
|
||||
delete response.info;
|
||||
}
|
||||
if (q.chars && apiV2IconsCache.chars) {
|
||||
// Add characters map
|
||||
response.chars = apiV2IconsCache.chars;
|
||||
}
|
||||
|
||||
sendJSONResponse(response, q, wrap, res);
|
||||
}
|
||||
|
|
@ -6,6 +6,11 @@ import { filterPrefixesByPrefix } from '../helpers/prefixes';
|
|||
|
||||
/**
|
||||
* Send response
|
||||
*
|
||||
* Request and responses are the same for v2 and v3
|
||||
*
|
||||
* Ignored parameters:
|
||||
* - hidden (always enabled)
|
||||
*/
|
||||
export function generateCollectionsListResponse(query: FastifyRequest['query'], res: FastifyReply) {
|
||||
const q = (query || {}) as Record<string, string>;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,81 @@
|
|||
import type { IconifyInfo, IconifyJSON } from '@iconify/types';
|
||||
|
||||
/**
|
||||
* Tag
|
||||
*/
|
||||
export interface IconSetIconsListTag {
|
||||
// Title
|
||||
title: string;
|
||||
|
||||
// Names of icons
|
||||
icons: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Icons
|
||||
*/
|
||||
export interface IconSetIconsListIcons {
|
||||
// Visible icons
|
||||
visible: Set<string>;
|
||||
|
||||
// Hidden icons
|
||||
hidden: Set<string>;
|
||||
|
||||
// Aliases, pointing to parent icon in either `visible` or `hidden` set
|
||||
visibleAliases: Record<string, string>;
|
||||
hiddenAliases: Record<string, string>;
|
||||
|
||||
// Failed aliases
|
||||
failed: Set<string>;
|
||||
|
||||
// Tags
|
||||
tags: IconSetIconsListTag[];
|
||||
uncategorised: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepared icons list for API v2 response
|
||||
*/
|
||||
export interface IconSetAPIv2IconsList {
|
||||
// Prepared data
|
||||
rendered: {
|
||||
// Icon set prefix
|
||||
prefix: string;
|
||||
|
||||
// Number of icons (duplicate of info?.total)
|
||||
total: number;
|
||||
|
||||
// Icon set title, if available (duplicate of info?.name)
|
||||
title?: string;
|
||||
|
||||
// Icon set info
|
||||
info?: IconifyInfo;
|
||||
|
||||
// List of icons without categories
|
||||
uncategorized?: string[];
|
||||
|
||||
// List of icons, sorted by category
|
||||
categories?: Record<string, string[]>;
|
||||
|
||||
// List of hidden icons
|
||||
hidden?: string[];
|
||||
|
||||
// List of aliases, key = alias, value = parent icon
|
||||
aliases?: Record<string, string>;
|
||||
|
||||
// Themes
|
||||
themes?: IconifyJSON['themes'];
|
||||
prefixes?: IconifyJSON['prefixes'];
|
||||
suffixes?: IconifyJSON['suffixes'];
|
||||
};
|
||||
|
||||
// Characters, key = character, value = icon name
|
||||
chars?: Record<string, string>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extra data generated for each icon set
|
||||
*/
|
||||
export interface IconSetExtraData {
|
||||
v2list: IconSetAPIv2IconsList;
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
import type { IconifyIcons, IconifyInfo, IconifyJSON } from '@iconify/types';
|
||||
import type { SplitDataTree, SplitRecord } from '../split';
|
||||
import type { IconifyIcons, IconifyInfo } from '@iconify/types';
|
||||
import type { SplitDataTree } from '../split';
|
||||
import type { MemoryStorage, MemoryStorageItem } from '../storage';
|
||||
import type { IconSetAPIv2IconsList, IconSetIconsListIcons } from './extra';
|
||||
import type { SplitIconifyJSONMainData } from './split';
|
||||
|
||||
/**
|
||||
|
|
@ -20,6 +21,10 @@ export interface StoredIconSet {
|
|||
items: MemoryStorageItem<IconifyIcons>[];
|
||||
tree: SplitDataTree<MemoryStorageItem<IconifyIcons>>;
|
||||
|
||||
// Icons list
|
||||
icons: IconSetIconsListIcons;
|
||||
apiV2IconsCache: IconSetAPIv2IconsList;
|
||||
|
||||
// TODO: add properties for search data
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,181 @@
|
|||
import { generateIconSetIconsTree } from '../../lib/data/icon-set/lists/icons';
|
||||
|
||||
describe('Icons tree', () => {
|
||||
test('Simple icon set', () => {
|
||||
const tree = generateIconSetIconsTree({
|
||||
prefix: 'foo',
|
||||
icons: {
|
||||
bar: {
|
||||
body: '<g id="bar" />',
|
||||
},
|
||||
baz: {
|
||||
body: '<g id="baz" />',
|
||||
},
|
||||
foo: {
|
||||
body: '<g id="foo" />',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(tree.failed).toEqual(new Set());
|
||||
expect(tree.visible).toEqual(new Set(['bar', 'baz', 'foo']));
|
||||
expect(tree.hidden).toEqual(new Set());
|
||||
expect(tree.visibleAliases).toEqual({});
|
||||
expect(tree.hiddenAliases).toEqual({});
|
||||
|
||||
expect(tree.tags).toEqual([]);
|
||||
expect(tree.uncategorised).toEqual(['bar', 'baz', 'foo']);
|
||||
});
|
||||
|
||||
test('Few aliases', () => {
|
||||
const tree = generateIconSetIconsTree({
|
||||
prefix: 'foo',
|
||||
icons: {
|
||||
bar: {
|
||||
body: '<g />',
|
||||
},
|
||||
bar2: {
|
||||
body: '<g />',
|
||||
},
|
||||
},
|
||||
aliases: {
|
||||
'foo': {
|
||||
parent: 'bar',
|
||||
hFlip: true,
|
||||
},
|
||||
'foo2': {
|
||||
parent: 'foo',
|
||||
},
|
||||
'missing-alias': {
|
||||
parent: 'missing-icon',
|
||||
},
|
||||
},
|
||||
categories: {
|
||||
Bar: ['bar', 'baz'],
|
||||
},
|
||||
});
|
||||
|
||||
expect(tree.failed).toEqual(new Set(['missing-alias', 'missing-icon']));
|
||||
expect(tree.visible).toEqual(new Set(['bar', 'bar2', 'foo']));
|
||||
expect(tree.hidden).toEqual(new Set());
|
||||
expect(tree.visibleAliases).toEqual({ foo2: 'foo' });
|
||||
expect(tree.hiddenAliases).toEqual({});
|
||||
|
||||
expect(tree.tags).toEqual([
|
||||
{
|
||||
title: 'Bar',
|
||||
icons: ['bar', 'foo'],
|
||||
},
|
||||
]);
|
||||
expect(tree.uncategorised).toEqual(['bar2']);
|
||||
});
|
||||
|
||||
test('Many aliases', () => {
|
||||
const tree = generateIconSetIconsTree({
|
||||
prefix: 'foo',
|
||||
icons: {
|
||||
icon1: {
|
||||
body: '<path d="icon1" />',
|
||||
width: 20,
|
||||
height: 20,
|
||||
},
|
||||
icon2: {
|
||||
body: '<path d="icon2" />',
|
||||
width: 24,
|
||||
rotate: 1,
|
||||
hFlip: true,
|
||||
hidden: true,
|
||||
},
|
||||
},
|
||||
aliases: {
|
||||
alias2a: {
|
||||
// Alias before parent
|
||||
parent: 'alias2f',
|
||||
width: 20,
|
||||
height: 20,
|
||||
},
|
||||
alias2f: {
|
||||
parent: 'icon2',
|
||||
width: 22,
|
||||
rotate: 1,
|
||||
hFlip: true,
|
||||
vFlip: true,
|
||||
},
|
||||
alias2z: {
|
||||
// Alias after parent
|
||||
parent: 'alias2f',
|
||||
width: 21,
|
||||
rotate: 3,
|
||||
// Visible, but parent is hidden
|
||||
hidden: false,
|
||||
},
|
||||
alias2z3: {
|
||||
// 3 parents: alias2z, alias2f, icon2
|
||||
parent: 'alias2z',
|
||||
},
|
||||
alias2z4: {
|
||||
// 4 parents: alias2z3, alias2z, alias2f, icon2
|
||||
parent: 'alias2z3',
|
||||
},
|
||||
alias2z5: {
|
||||
// 5 parents: alias2z4, alias2z3, alias2z, alias2f, icon2
|
||||
parent: 'alias2z4',
|
||||
},
|
||||
alias2z6: {
|
||||
// 6 parents: alias2z5, alias2z4, alias2z3, alias2z, alias2f, icon2
|
||||
parent: 'alias2z5',
|
||||
hidden: true,
|
||||
},
|
||||
alias2z7: {
|
||||
// 7 parents: alias2z6, alias2z5, alias2z4, alias2z3, alias2z, alias2f, icon2
|
||||
parent: 'alias2z6',
|
||||
},
|
||||
alias3: {
|
||||
// invalid parent
|
||||
parent: 'icon3',
|
||||
},
|
||||
// Loop
|
||||
loop1: {
|
||||
parent: 'loop3',
|
||||
},
|
||||
loop2: {
|
||||
parent: 'loop1',
|
||||
},
|
||||
loop3: {
|
||||
parent: 'loop1',
|
||||
},
|
||||
},
|
||||
categories: {
|
||||
Loop: ['loop1', 'loop2', 'loop3'],
|
||||
Icon1: ['icon1'],
|
||||
Icon2: ['icon2'],
|
||||
},
|
||||
height: 24,
|
||||
});
|
||||
|
||||
expect(tree.failed).toEqual(new Set(['alias3', 'icon3', 'loop1', 'loop2', 'loop3']));
|
||||
expect(tree.visible).toEqual(new Set(['icon1', 'alias2z']));
|
||||
expect(tree.hidden).toEqual(new Set(['icon2', 'alias2f', 'alias2a']));
|
||||
expect(tree.visibleAliases).toEqual({
|
||||
alias2z3: 'alias2z',
|
||||
alias2z4: 'alias2z',
|
||||
alias2z5: 'alias2z',
|
||||
});
|
||||
expect(tree.hiddenAliases).toEqual({
|
||||
alias2z6: 'alias2z',
|
||||
alias2z7: 'alias2z',
|
||||
});
|
||||
|
||||
expect(tree.tags).toEqual([
|
||||
{
|
||||
title: 'Icon1',
|
||||
icons: ['icon1'],
|
||||
},
|
||||
{
|
||||
title: 'Icon2',
|
||||
icons: ['alias2z'],
|
||||
},
|
||||
]);
|
||||
expect(tree.uncategorised).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
import { generateIconSetIconsTree } from '../../lib/data/icon-set/lists/icons';
|
||||
import { removeBadAliases } from '../../lib/data/icon-set/store/validate';
|
||||
|
||||
describe('Validating icon set', () => {
|
||||
|
|
@ -55,7 +56,7 @@ describe('Validating icon set', () => {
|
|||
},
|
||||
},
|
||||
};
|
||||
removeBadAliases(iconSet);
|
||||
removeBadAliases(iconSet, generateIconSetIconsTree(iconSet));
|
||||
|
||||
// Check aliases
|
||||
expect(Object.keys(iconSet.aliases)).toEqual(['baz', 'baz2', 'baz3', 'baz4', 'baz5', 'baz6', 'bazz5']);
|
||||
|
|
|
|||
Loading…
Reference in New Issue