diff --git a/src/Umbraco.Web.UI.Client/.storybook/main.js b/src/Umbraco.Web.UI.Client/.storybook/main.js index 97b5f36ad8..73a7c5ea34 100644 --- a/src/Umbraco.Web.UI.Client/.storybook/main.js +++ b/src/Umbraco.Web.UI.Client/.storybook/main.js @@ -4,9 +4,10 @@ module.exports = { framework: '@storybook/web-components', features: { previewMdx2: true, - storyStoreV7: true + storyStoreV7: true, }, core: { builder: '@storybook/builder-vite', }, + staticDirs: ['../public-assets'], }; diff --git a/src/Umbraco.Web.UI.Client/.storybook/preview.js b/src/Umbraco.Web.UI.Client/.storybook/preview.js index fe7bb84c12..87c14d54d0 100644 --- a/src/Umbraco.Web.UI.Client/.storybook/preview.js +++ b/src/Umbraco.Web.UI.Client/.storybook/preview.js @@ -11,13 +11,32 @@ import { UmbDocumentTypeStore } from '../src/core/stores/document-type.store'; import { UmbNodeStore } from '../src/core/stores/node.store'; import { UmbPropertyEditorStore } from '../src/core/stores/property-editor/property-editor.store'; import { UmbPropertyEditorConfigStore } from '../src/core/stores/property-editor-config/property-editor-config.store'; +import { UmbIconStore } from '../src/core/stores/icon/icon.store'; import { onUnhandledRequest } from '../src/mocks/browser'; import { handlers } from '../src/mocks/browser-handlers'; import { internalManifests } from '../src/temp-internal-manifests'; +import { LitElement } from 'lit'; const extensionRegistry = new UmbExtensionRegistry(); internalManifests.forEach((manifest) => extensionRegistry.register(manifest)); +class UmbStoryBookElement extends LitElement { + _umbIconStore = new UmbIconStore(); + + constructor() { + super(); + this._umbIconStore.attach(this); + } + + render() { + return html``; + } +} + +customElements.define('umb-storybook', UmbStoryBookElement); + +const storybookProvider = (story) => html` ${story()} `; + const extensionRegistryProvider = (story) => html` ${story()} `; @@ -54,6 +73,7 @@ initialize({ onUnhandledRequest }); // Provide the MSW addon decorator globally export const decorators = [ mswDecorator, + storybookProvider, extensionRegistryProvider, nodeStoreProvider, dataTypeStoreProvider, diff --git a/src/Umbraco.Web.UI.Client/scripts/generate-icons.mjs b/src/Umbraco.Web.UI.Client/scripts/generate-icons.mjs index 566e4fe3e5..432b5e63ef 100644 --- a/src/Umbraco.Web.UI.Client/scripts/generate-icons.mjs +++ b/src/Umbraco.Web.UI.Client/scripts/generate-icons.mjs @@ -13,7 +13,7 @@ const iconsOutputDirectory = `public-assets/icons/`; const run = async () => { const icons = await collectIcons(); outputIcons(icons); - generateManifest(icons); + generateJSON(icons); }; const collectIcons = async () => { @@ -57,19 +57,19 @@ const outputIcons = (icons) => { }); }; -const generateManifest = (icons) => { - const manifestsPath = `${iconsOutputDirectory}icon.manifests.json`; +const generateJSON = (icons) => { + const JSONPath = `${iconsOutputDirectory}icons.json`; - const iconManifests = icons.map((icon) => { + const iconDescriptors = icons.map((icon) => { return { name: `umb:${icon.name}`, path: `/icons/${icon.iconFileName}.js`, }; }); - const content = `${JSON.stringify(iconManifests)}`; + const content = `${JSON.stringify(iconDescriptors)}`; - writeFileWithDir(manifestsPath, content, (err) => { + writeFileWithDir(JSONPath, content, (err) => { if (err) { // eslint-disable-next-line no-undef console.log(err); diff --git a/src/Umbraco.Web.UI.Client/src/core/stores/data-type/data-type.store.ts b/src/Umbraco.Web.UI.Client/src/core/stores/data-type/data-type.store.ts index 0950ed46c4..1ac1167214 100644 --- a/src/Umbraco.Web.UI.Client/src/core/stores/data-type/data-type.store.ts +++ b/src/Umbraco.Web.UI.Client/src/core/stores/data-type/data-type.store.ts @@ -18,7 +18,7 @@ export class UmbDataTypeStore extends UmbDataStoreBase { } /** - * @description - Request a Data Type by key. The Data Type is returned as an Observable. + * @description - Request a Data Type by key. The Data Type is added to the store and is returned as an Observable. * @param {string} key * @return {*} {(Observable)} * @memberof UmbDataTypeStore diff --git a/src/Umbraco.Web.UI.Client/src/core/stores/icon/README.md b/src/Umbraco.Web.UI.Client/src/core/stores/icon/README.md deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/Umbraco.Web.UI.Client/src/core/stores/icon/icon.store.ts b/src/Umbraco.Web.UI.Client/src/core/stores/icon/icon.store.ts index b1c0f74398..5e75c4c305 100644 --- a/src/Umbraco.Web.UI.Client/src/core/stores/icon/icon.store.ts +++ b/src/Umbraco.Web.UI.Client/src/core/stores/icon/icon.store.ts @@ -1,9 +1,25 @@ import { UUIIconRegistry } from '@umbraco-ui/uui'; -import iconManifests from '../../../../public-assets/icons/icon.manifests.json'; +import icons from '../../../../public-assets/icons/icons.json'; +interface UmbIconDescriptor { + name: string; + path: string; +} + +/** + * @export + * @class UmbIconStore + * @extends {UUIIconRegistry} + * @description - Icon Store. Provides icons from the icon manifest. Icons are loaded on demand. All icons are prefixed with 'umb:' + */ export class UmbIconStore extends UUIIconRegistry { - acceptIcon(iconName: string) { - const iconManifest = iconManifests.find((i: any) => i.name === iconName); + /** + * @param {string} iconName + * @return {*} {boolean} + * @memberof UmbIconStore + */ + acceptIcon(iconName: string): boolean { + const iconManifest = icons.find((i: UmbIconDescriptor) => i.name === iconName); if (!iconManifest) return false; const icon = this.provideIcon(iconName); diff --git a/src/Umbraco.Web.UI.Client/src/core/stores/icon/icon.stories.ts b/src/Umbraco.Web.UI.Client/src/core/stores/icon/icon.stories.ts new file mode 100644 index 0000000000..618d96ff8a --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/core/stores/icon/icon.stories.ts @@ -0,0 +1,41 @@ +import { Meta, Story } from '@storybook/web-components'; +import { html } from 'lit-html'; +import { repeat } from 'lit/directives/repeat.js'; +import icons from '../../../../public-assets/icons/icons.json'; + +export default { + title: 'API/Icons', + id: 'umb-icons', +} as Meta; + +const Template: Story = () => { + return html` +
+ ${repeat( + icons, + (icon) => icon.name, + (icon) => + html`
+ ${icon.name} +
` + )} +
+ `; +}; + +export const Overview = Template.bind({}); diff --git a/src/Umbraco.Web.UI.Client/src/core/stores/property-editor-config/property-editor-config.store.ts b/src/Umbraco.Web.UI.Client/src/core/stores/property-editor-config/property-editor-config.store.ts index 7bec2f5dd1..5556369b3b 100644 --- a/src/Umbraco.Web.UI.Client/src/core/stores/property-editor-config/property-editor-config.store.ts +++ b/src/Umbraco.Web.UI.Client/src/core/stores/property-editor-config/property-editor-config.store.ts @@ -8,7 +8,20 @@ export interface PropertyEditorConfigRef { config: PropertyEditorConfig; } +/** + * @export + * @class UmbPropertyEditorConfigStore + * @extends {UmbDataStoreBase} + * @description - Data Store for Property Editor Configs + */ export class UmbPropertyEditorConfigStore extends UmbDataStoreBase { + /** + * + * @description - Request a Property Editor Config by alias. The Property Editor Config is added to the store and is returned as an Observable. + * @param {string} alias + * @return {*} {(Observable)} + * @memberof UmbPropertyEditorConfigStore + */ getByAlias(alias: string): Observable { // TODO: only fetch if the data type is not in the store? getPropertyEditorConfig({ propertyEditorAlias: alias }) diff --git a/src/Umbraco.Web.UI.Client/src/core/stores/property-editor/property-editor.store.ts b/src/Umbraco.Web.UI.Client/src/core/stores/property-editor/property-editor.store.ts index bc3942b0b2..678ff1f3f3 100644 --- a/src/Umbraco.Web.UI.Client/src/core/stores/property-editor/property-editor.store.ts +++ b/src/Umbraco.Web.UI.Client/src/core/stores/property-editor/property-editor.store.ts @@ -3,7 +3,18 @@ import type { PropertyEditor } from '../../models'; import { getPropertyEditorsList, getPropertyEditor } from '../../api/fetcher'; import { UmbDataStoreBase } from '../store'; +/** + * @export + * @class UmbPropertyEditorStore + * @extends {UmbDataStoreBase} + * @description - Data Store for Property Editors + */ export class UmbPropertyEditorStore extends UmbDataStoreBase { + /** + * @description - Request all Property Editors. The Property Editors are added to the store and are returned as an Observable. + * @return {*} {(Observable | []>)} + * @memberof UmbPropertyEditorStore + */ getAll(): Observable | []> { // TODO: only fetch if the data type is not in the store? getPropertyEditorsList({}) @@ -17,6 +28,12 @@ export class UmbPropertyEditorStore extends UmbDataStoreBase { return this.items; } + /** + * Request a Property Editor by alias. The Property Editor is added to the store and is returned as an Observable. + * @param {string} alias + * @return {*} {(Observable)} + * @memberof UmbPropertyEditorStore + */ getByAlias(alias: string): Observable { // TODO: only fetch if the data type is not in the store? getPropertyEditor({ propertyEditorAlias: alias }) diff --git a/src/Umbraco.Web.UI.Client/src/core/stores/store.ts b/src/Umbraco.Web.UI.Client/src/core/stores/store.ts index bc20672373..eecf943a10 100644 --- a/src/Umbraco.Web.UI.Client/src/core/stores/store.ts +++ b/src/Umbraco.Web.UI.Client/src/core/stores/store.ts @@ -10,10 +10,23 @@ export interface UmbDataStore { update(items: Array): void; } +/** + * @export + * @class UmbDataStoreBase + * @implements {UmbDataStore} + * @template T + * @description - Base class for Data Stores + */ export class UmbDataStoreBase implements UmbDataStore { private _items: BehaviorSubject> = new BehaviorSubject(>[]); public readonly items: Observable> = this._items.asObservable(); + /** + * @description - Update the store with new items. Existing items are updated, new items are added. Existing items are matched by the compareKey. + * @param {Array} updatedItems + * @param {keyof T} [compareKey='key'] + * @memberof UmbDataStoreBase + */ public update(updatedItems: Array, compareKey: keyof T = 'key'): void { const storedItems = this._items.getValue(); const updated: T[] = [...storedItems];