2
0
mirror of https://github.com/iconify/iconify.git synced 2024-12-13 14:13:06 +00:00

Add loadIcon function

This commit is contained in:
Vjacheslav Trushkin 2022-01-13 22:19:49 +02:00
parent 035d2fe305
commit bf8d17f97a
11 changed files with 222 additions and 16 deletions

View File

@ -14,6 +14,7 @@ import type {
IconifyAPICustomQueryParams,
} from './modules';
import type { MergeParams, IconifyAPIMergeQueryParams } from './params';
import type { IconifyIcon } from '@iconify/types';
/**
* Iconify API functions
@ -27,6 +28,13 @@ export interface IconifyAPIFunctions {
callback?: IconifyIconLoaderCallback
) => IconifyIconLoaderAbort;
/**
* Load one icon, using Promise syntax
*/
loadIcon: (
icon: IconifyIconName | string
) => Promise<Required<IconifyIcon>>;
/**
* Add API provider
*/

View File

@ -1,13 +1,14 @@
import type { IconifyJSON } from '@iconify/types';
import type {
import type { IconifyIcon, IconifyJSON } from '@iconify/types';
import {
IconifyIconName,
IconifyIconSource,
stringToIcon,
} from '@iconify/utils/lib/icon/name';
import type { SortedIcons } from '../icon/sort';
import { sortIcons } from '../icon/sort';
import { storeCallback, updateCallbacks } from './callbacks';
import { getAPIModule } from './modules';
import { getStorage, addIconSet } from '../storage/storage';
import { getStorage, addIconSet, getIconFromStorage } from '../storage/storage';
import { listToIcons } from '../icon/list';
import { allowSimpleNames } from '../storage/functions';
import { sendAPIQuery } from './query';
@ -338,3 +339,36 @@ export const loadIcons: IconifyLoadIcons = (
? storeCallback(callback, sortedIcons, sources)
: emptyCallback;
};
/**
* Cache for loadIcon promises
*/
type LoadIconResult = Promise<Required<IconifyIcon>>;
const iconsQueue: Record<string, LoadIconResult> = Object.create(null);
export const loadIcon = (icon: IconifyIconName | string): LoadIconResult => {
if (typeof icon === 'string' && iconsQueue[icon]) {
return iconsQueue[icon];
}
const result: LoadIconResult = new Promise((fulfill, reject) => {
const iconObj = typeof icon === 'string' ? stringToIcon(icon) : icon;
loadIcons([iconObj || icon], (loaded) => {
if (loaded.length && iconObj) {
const storage = getStorage(iconObj.provider, iconObj.prefix);
const data = getIconFromStorage(storage, iconObj.name);
if (data) {
fulfill(data);
return;
}
}
reject(icon);
});
});
if (typeof icon === 'string') {
iconsQueue[icon] = result;
}
return result;
};

View File

@ -5,7 +5,7 @@ import type {
IconifyAPIQueryParams,
} from '../../lib/api/modules';
import { setAPIModule } from '../../lib/api/modules';
import { loadIcons, isPending } from '../../lib/api/icons';
import { loadIcons, loadIcon, isPending } from '../../lib/api/icons';
describe('Testing API loadIcons', () => {
let prefixCounter = 0;
@ -154,6 +154,167 @@ describe('Testing API loadIcons', () => {
asyncCounter++;
});
it('Loading one icon with Promise', async () => {
const provider = nextPrefix();
const prefix = nextPrefix();
// Set config
addAPIProvider(provider, {
resources: ['https://api1.local', 'https://api2.local'],
});
// Icon loader
const prepareQuery = (
provider: string,
prefix: string,
icons: string[]
): IconifyAPIIconsQueryParams[] => {
const item: IconifyAPIIconsQueryParams = {
type: 'icons',
provider,
prefix,
icons,
};
// Test input and return as one item
const expected: IconifyAPIIconsQueryParams = {
type: 'icons',
provider,
prefix,
icons: ['icon1'],
};
expect(item).toEqual(expected);
return [item];
};
const sendQuery = (
host: string,
params: IconifyAPIQueryParams,
item: PendingQueryItem
): void => {
expect(params.type).toBe('icons');
// Test input
expect(host).toBe('https://api1.local');
const expected: IconifyAPIQueryParams = {
type: 'icons',
provider,
prefix,
icons: ['icon1'],
};
expect(params).toEqual(expected);
// Send data
item.done({
prefix,
icons: {
icon1: {
body: '<path d="" />',
},
},
});
};
setAPIModule(provider, {
prepare: prepareQuery,
send: sendQuery,
});
// Load icon
await loadIcon(provider + ':' + prefix + ':icon1');
// Test isPending
expect(isPending({ provider, prefix, name: 'icon1' })).toBe(false);
});
it('Loading one icon twice with Promise', (done) => {
const provider = nextPrefix();
const prefix = nextPrefix();
// Set config
addAPIProvider(provider, {
resources: ['https://api1.local', 'https://api2.local'],
});
// Icon loader
const prepareQuery = (
provider: string,
prefix: string,
icons: string[]
): IconifyAPIIconsQueryParams[] => {
const item: IconifyAPIIconsQueryParams = {
type: 'icons',
provider,
prefix,
icons,
};
// Test input and return as one item
const expected: IconifyAPIIconsQueryParams = {
type: 'icons',
provider,
prefix,
icons: ['icon1'],
};
expect(item).toEqual(expected);
return [item];
};
const sendQuery = (
host: string,
params: IconifyAPIQueryParams,
item: PendingQueryItem
): void => {
expect(params.type).toBe('icons');
// Test input
expect(host).toBe('https://api1.local');
const expected: IconifyAPIQueryParams = {
type: 'icons',
provider,
prefix,
icons: ['icon1'],
};
expect(params).toEqual(expected);
// Send data
item.done({
prefix,
icons: {
icon1: {
body: '<path d="" />',
},
},
});
};
setAPIModule(provider, {
prepare: prepareQuery,
send: sendQuery,
});
// Load icon, twice
const p1 = loadIcon(provider + ':' + prefix + ':icon1');
const p2 = loadIcon(provider + ':' + prefix + ':icon1');
// Promise instances should be the same because parameter is a string that is cached
expect(p1).toEqual(p2);
// Test isPending
expect(isPending({ provider, prefix, name: 'icon1' })).toBe(true);
// Wait for Promise
p1.then((data) => {
expect(data.body).toEqual('<path d="" />');
done();
}).catch((err) => {
console.error(err);
done('Failed to load icon');
});
});
it('Split results', (done) => {
const provider = nextPrefix();
const prefix = nextPrefix();

View File

@ -51,7 +51,7 @@ import type {
IconifyIconLoaderCallback,
IconifyIconLoaderAbort,
} from '@iconify/core/lib/api/icons';
import { loadIcons } from '@iconify/core/lib/api/icons';
import { loadIcons, loadIcon } from '@iconify/core/lib/api/icons';
import { sendAPIQuery } from '@iconify/core/lib/api/query';
import { mergeParams } from '@iconify/core/lib/api/params';
@ -230,7 +230,7 @@ const _api: IconifyAPIInternalFunctions = {
export { _api };
// IconifyAPIFunctions
export { addAPIProvider, loadIcons };
export { addAPIProvider, loadIcons, loadIcon };
// IconifyStorageFunctions
export { iconExists, getIcon, listIcons, addIcon, addCollection, shareStorage };

View File

@ -64,7 +64,7 @@ import type {
IconifyIconLoaderCallback,
IconifyIconLoaderAbort,
} from '@iconify/core/lib/api/icons';
import { loadIcons } from '@iconify/core/lib/api/icons';
import { loadIcons, loadIcon } from '@iconify/core/lib/api/icons';
import { sendAPIQuery } from '@iconify/core/lib/api/query';
import { mergeParams } from '@iconify/core/lib/api/params';
@ -223,6 +223,7 @@ const Iconify: IconifyGlobal = {
// IconifyAPIFunctions
addAPIProvider,
loadIcons,
loadIcon,
// IconifyStorageFunctions
iconExists,

View File

@ -55,7 +55,7 @@ import type {
IconifyIconLoaderCallback,
IconifyIconLoaderAbort,
} from '@iconify/core/lib/api/icons';
import { loadIcons } from '@iconify/core/lib/api/icons';
import { loadIcons, loadIcon } from '@iconify/core/lib/api/icons';
import { sendAPIQuery } from '@iconify/core/lib/api/query';
import { mergeParams } from '@iconify/core/lib/api/params';
@ -461,7 +461,7 @@ const _api: IconifyAPIInternalFunctions = {
export { _api };
// IconifyAPIFunctions
export { addAPIProvider, loadIcons };
export { addAPIProvider, loadIcons, loadIcon };
// IconifyStorageFunctions
export { iconExists, getIcon, listIcons, addIcon, addCollection, shareStorage };

View File

@ -15,6 +15,7 @@ import {
replaceIDs,
buildIcon,
loadIcons,
loadIcon,
addAPIProvider,
_api
} from './functions';
@ -32,6 +33,7 @@ export {
replaceIDs,
buildIcon,
loadIcons,
loadIcon,
addAPIProvider,
_api
}

View File

@ -54,7 +54,7 @@ import type {
IconifyIconLoaderCallback,
IconifyIconLoaderAbort,
} from '@iconify/core/lib/api/icons';
import { loadIcons } from '@iconify/core/lib/api/icons';
import { loadIcons, loadIcon } from '@iconify/core/lib/api/icons';
import { sendAPIQuery } from '@iconify/core/lib/api/query';
import { mergeParams } from '@iconify/core/lib/api/params';
@ -357,7 +357,7 @@ const _api: IconifyAPIInternalFunctions = {
export { _api };
// IconifyAPIFunctions
export { addAPIProvider, loadIcons };
export { addAPIProvider, loadIcons, loadIcon };
// IconifyStorageFunctions
export { iconExists, getIcon, listIcons, addIcon, addCollection, shareStorage };

View File

@ -62,4 +62,4 @@ export {
export { calculateSize, replaceIDs, buildIcon } from './functions';
export { loadIcons, addAPIProvider, _api } from './functions';
export { loadIcons, loadIcon, addAPIProvider, _api } from './functions';

View File

@ -64,7 +64,7 @@ import type {
IconifyIconLoaderCallback,
IconifyIconLoaderAbort,
} from '@iconify/core/lib/api/icons';
import { loadIcons } from '@iconify/core/lib/api/icons';
import { loadIcons, loadIcon } from '@iconify/core/lib/api/icons';
import { sendAPIQuery } from '@iconify/core/lib/api/query';
import { mergeParams } from '@iconify/core/lib/api/params';
@ -397,7 +397,7 @@ const _api: IconifyAPIInternalFunctions = {
export { _api };
// IconifyAPIFunctions
export { addAPIProvider, loadIcons };
export { addAPIProvider, loadIcons, loadIcon };
// IconifyStorageFunctions
export { iconExists, getIcon, listIcons, addIcon, addCollection, shareStorage };

View File

@ -57,7 +57,7 @@ import type {
IconifyIconLoaderCallback,
IconifyIconLoaderAbort,
} from '@iconify/core/lib/api/icons';
import { loadIcons } from '@iconify/core/lib/api/icons';
import { loadIcons, loadIcon } from '@iconify/core/lib/api/icons';
import { sendAPIQuery } from '@iconify/core/lib/api/query';
import { mergeParams } from '@iconify/core/lib/api/params';
@ -399,7 +399,7 @@ const _api: IconifyAPIInternalFunctions = {
export { _api };
// IconifyAPIFunctions
export { addAPIProvider, loadIcons };
export { addAPIProvider, loadIcons, loadIcon };
// IconifyStorageFunctions
export { iconExists, getIcon, listIcons, addIcon, addCollection, shareStorage };