Merge branch 'main' into feature/property-editor-tags

This commit is contained in:
Lone Iversen
2023-05-09 12:02:51 +02:00
committed by GitHub
17 changed files with 225 additions and 255 deletions

View File

@@ -3,12 +3,12 @@ import { UmbModalToken } from '@umbraco-cms/backoffice/modal';
export interface UmbLanguagePickerModalData {
multiple?: boolean;
selection?: Array<string>;
selection?: Array<string | null>;
filter?: (language: LanguageResponseModel) => boolean;
}
export interface UmbLanguagePickerModalResult {
selection: Array<string>;
selection: Array<string | null>;
}
export const UMB_LANGUAGE_PICKER_MODAL = new UmbModalToken<UmbLanguagePickerModalData, UmbLanguagePickerModalResult>(

View File

@@ -2,10 +2,17 @@ import { UmbModalToken } from '@umbraco-cms/backoffice/modal';
export interface UmbSectionPickerModalData {
multiple: boolean;
selection: string[];
selection: Array<string | null>;
}
export const UMB_SECTION_PICKER_MODAL = new UmbModalToken<UmbSectionPickerModalData>('Umb.Modal.SectionPicker', {
type: 'sidebar',
size: 'small',
});
export interface UmbSectionPickerModalResult {
selection: Array<string | null>;
}
export const UMB_SECTION_PICKER_MODAL = new UmbModalToken<UmbSectionPickerModalData, UmbSectionPickerModalResult>(
'Umb.Modal.SectionPicker',
{
type: 'sidebar',
size: 'small',
}
);

View File

@@ -4,7 +4,7 @@ import { UserResponseModel } from '@umbraco-cms/backoffice/backend-api';
export type UmbUserPickerModalData = UmbPickerModalData<UserResponseModel>;
export interface UmbUserPickerModalResult {
selection: Array<string>;
selection: Array<string | null>;
}
export const UMB_USER_PICKER_MODAL = new UmbModalToken<UmbUserPickerModalData, UmbUserPickerModalResult>(

View File

@@ -1,19 +1,23 @@
import { Observable, map } from 'rxjs';
import { UmbPagedData, UmbTreeRepository } from '@umbraco-cms/backoffice/repository';
import type { ManifestTree } from '@umbraco-cms/backoffice/extensions-registry';
import { UmbBooleanState, UmbArrayState, UmbObserverController } from '@umbraco-cms/backoffice/observable-api';
import { UmbBooleanState, UmbObserverController } from '@umbraco-cms/backoffice/observable-api';
import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller';
import { createExtensionClass, umbExtensionsRegistry } from '@umbraco-cms/backoffice/extensions-api';
import { ProblemDetailsModel, TreeItemPresentationModel } from '@umbraco-cms/backoffice/backend-api';
import { UmbContextProviderController } from '@umbraco-cms/backoffice/context-api';
import { UmbSelectionManagerBase } from '@umbraco-cms/backoffice/utils';
// TODO: update interface
export interface UmbTreeContext<TreeItemType extends TreeItemPresentationModel> {
readonly selectable: Observable<boolean>;
readonly selection: Observable<Array<string | null>>;
setSelectable(value: boolean): void;
getSelectable(): boolean;
setMultiple(value: boolean): void;
getMultiple(): boolean;
setSelection(value: Array<string | null>): void;
getSelection(): Array<string | null>;
select(unique: string | null): void;
deselect(unique: string | null): void;
requestChildrenOf: (parentUnique: string | null) => Promise<{
@@ -28,18 +32,16 @@ export class UmbTreeContextBase<TreeItemType extends TreeItemPresentationModel>
{
public host: UmbControllerHostElement;
#selectionManager = new UmbSelectionManagerBase();
#selectable = new UmbBooleanState(false);
public readonly selectable = this.#selectable.asObservable();
#multiple = new UmbBooleanState(false);
public readonly multiple = this.#multiple.asObservable();
#selection = new UmbArrayState(<Array<string | null>>[]);
public readonly selection = this.#selection.asObservable();
public readonly multiple = this.#selectionManager.multiple;
public readonly selection = this.#selectionManager.selection;
#treeAlias?: string;
repository?: UmbTreeRepository<TreeItemType>;
#treeManifestObserver?: UmbObserverController<any>;
#initResolver?: () => void;
@@ -84,32 +86,29 @@ export class UmbTreeContextBase<TreeItemType extends TreeItemPresentationModel>
}
public setMultiple(value: boolean) {
this.#multiple.next(value);
this.#selectionManager.setMultiple(value);
}
public getMultiple() {
return this.#multiple.getValue();
return this.#selectionManager.getMultiple();
}
public setSelection(value: Array<string | null>) {
if (!value) return;
this.#selection.next(value);
this.#selectionManager.setSelection(value);
}
public getSelection() {
return this.#selection.getValue();
return this.#selectionManager.getSelection();
}
public select(unique: string | null) {
if (!this.getSelectable()) return;
const newSelection = this.getMultiple() ? [...this.getSelection(), unique] : [unique];
this.#selection.next(newSelection);
this.#selectionManager.select(unique);
this.host.dispatchEvent(new CustomEvent('selected'));
}
public deselect(unique: string | null) {
const newSelection = this.getSelection().filter((x) => x !== unique);
this.#selection.next(newSelection);
this.#selectionManager.deselect(unique);
this.host.dispatchEvent(new CustomEvent('selected'));
}

View File

@@ -1,2 +1,3 @@
export * from './umbraco-path';
export * from './udi-service';
export * from './selection-manager';

View File

@@ -0,0 +1,61 @@
import { Observable } from 'rxjs';
import { UmbArrayState, UmbBooleanState } from '../observable-api';
export interface UmbSelectionManager {
selection: Observable<Array<string | null>>;
multiple: Observable<boolean>;
getSelection(): Array<string | null>;
setSelection(value: Array<string | null>): void;
getMultiple(): boolean;
setMultiple(value: boolean): void;
toggleSelect(unique: string | null): void;
select(unique: string | null): void;
deselect(unique: string | null): void;
isSelected(unique: string | null): boolean;
}
export class UmbSelectionManagerBase implements UmbSelectionManager {
#selection = new UmbArrayState(<Array<string | null>>[]);
public readonly selection = this.#selection.asObservable();
#multiple = new UmbBooleanState(false);
public readonly multiple = this.#multiple.asObservable();
public getSelection() {
return this.#selection.getValue();
}
public setSelection(value: Array<string | null>) {
if (value === undefined) throw new Error('Value cannot be undefined');
this.#selection.next(value);
}
public getMultiple() {
return this.#multiple.getValue();
}
public setMultiple(value: boolean) {
this.#multiple.next(value);
}
public toggleSelect(unique: string | null) {
this.isSelected(unique) ? this.deselect(unique) : this.select(unique);
}
public select(unique: string | null) {
const newSelection = this.getMultiple() ? [...this.getSelection(), unique] : [unique];
this.#selection.next(newSelection);
}
public deselect(unique: string | null) {
const newSelection = this.getSelection().filter((x) => x !== unique);
this.#selection.next(newSelection);
}
public isSelected(unique: string | null) {
return this.getSelection().includes(unique);
}
}

View File

@@ -5,6 +5,7 @@ import 'element-internals-polyfill';
import './core/router/router-slot.element';
import './core/router/variant-router-slot.element';
import './core/context-provider/context-provider.element';
import { UUIIconRegistryEssential } from '@umbraco-ui/uui';
import { css, html } from 'lit';

View File

@@ -34,6 +34,7 @@ export class UmbTreeElement extends UmbLitElement {
return this.#treeContext.getSelection();
}
set selection(newVal) {
if (!Array.isArray(newVal)) return;
this.#treeContext?.setSelection(newVal);
}

View File

@@ -1,96 +1,69 @@
import { UUITextStyles } from '@umbraco-ui/uui-css';
import { css, html } from 'lit';
import { customElement, state } from 'lit/decorators.js';
import { UmbModalElementPickerBase } from '@umbraco-cms/internal/modal';
import { UmbSelectionManagerBase } from '@umbraco-cms/backoffice/utils';
import { UmbModalBaseElement } from '@umbraco-cms/internal/modal';
import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extensions-api';
import type { ManifestSection } from '@umbraco-cms/backoffice/extensions-registry';
import { UmbSectionPickerModalData, UmbSectionPickerModalResult } from '@umbraco-cms/backoffice/modal';
@customElement('umb-section-picker-modal')
export class UmbSectionPickerModalElement extends UmbModalElementPickerBase<ManifestSection> {
export class UmbSectionPickerModalElement extends UmbModalBaseElement<
UmbSectionPickerModalData,
UmbSectionPickerModalResult
> {
@state()
private _sections: Array<ManifestSection> = [];
#selectionManager = new UmbSelectionManagerBase();
#submit() {
this.modalHandler?.submit({
selection: this.#selectionManager.getSelection(),
});
}
#close() {
this.modalHandler?.reject();
}
connectedCallback(): void {
super.connectedCallback();
this.observe(umbExtensionsRegistry.extensionsOfType('section'), (sections: Array<ManifestSection>) => {
this._sections = sections;
});
// TODO: in theory this config could change during the lifetime of the modal, so we could observe it
this.#selectionManager.setMultiple(this.data?.multiple ?? false);
this.#selectionManager.setSelection(this.data?.selection ?? []);
this.observe(
umbExtensionsRegistry.extensionsOfType('section'),
(sections: Array<ManifestSection>) => (this._sections = sections)
);
}
render() {
return html`
<umb-workspace-editor headline="Select sections">
<uui-box>
<uui-input label="search"></uui-input>
<hr />
<div id="item-list">
${this._sections.map(
(item) => html`
<div
@click=${() => this.handleSelection(item.alias)}
@keydown=${(e: KeyboardEvent) => this._handleKeydown(e, item.alias)}
class=${this.isSelected(item.alias) ? 'item selected' : 'item'}>
<span>${item.meta.label}</span>
</div>
`
)}
</div>
${this._sections.map(
(item) => html`
<uui-menu-item
label=${item.meta.label}
selectable
?selected=${this.#selectionManager.isSelected(item.alias)}
@selected=${() => this.#selectionManager.select(item.alias)}
@unselected=${() => this.#selectionManager.deselect(item.alias)}></uui-menu-item>
`
)}
</uui-box>
<div slot="actions">
<uui-button label="Close" @click=${this.close}></uui-button>
<uui-button label="Submit" look="primary" color="positive" @click=${this.submit}></uui-button>
<uui-button label="Close" @click=${this.#close}></uui-button>
<uui-button label="Submit" look="primary" color="positive" @click=${this.#submit}></uui-button>
</div>
</umb-workspace-editor>
`;
}
static styles = [
UUITextStyles,
css`
uui-input {
width: 100%;
}
hr {
border: none;
border-bottom: 1px solid var(--uui-color-divider);
margin: 16px 0;
}
#item-list {
display: flex;
flex-direction: column;
gap: var(--uui-size-1);
}
.item {
color: var(--uui-color-interactive);
display: grid;
grid-template-columns: var(--uui-size-8) 1fr;
padding: var(--uui-size-4) var(--uui-size-2);
gap: var(--uui-size-space-5);
align-items: center;
border-radius: var(--uui-border-radius);
cursor: pointer;
}
.item.selected {
background-color: var(--uui-color-selected);
color: var(--uui-color-selected-contrast);
}
.item:not(.selected):hover {
background-color: var(--uui-color-surface-emphasis);
color: var(--uui-color-interactive-emphasis);
}
.item.selected:hover {
background-color: var(--uui-color-selected-emphasis);
}
.item uui-icon {
width: 100%;
box-sizing: border-box;
display: flex;
height: fit-content;
}
`,
];
static styles = [UUITextStyles, css``];
}
export default UmbSectionPickerModalElement;

View File

@@ -2,32 +2,32 @@ import { UUITextStyles } from '@umbraco-ui/uui-css';
import { css, html } from 'lit';
import { customElement, state } from 'lit/decorators.js';
import { repeat } from 'lit/directives/repeat.js';
import { UUIMenuItemElement, UUIMenuItemEvent } from '@umbraco-ui/uui';
import { ifDefined } from 'lit/directives/if-defined.js';
import { UmbLanguageRepository } from '../../repository/language.repository';
import { UmbModalElementPickerBase } from '@umbraco-cms/internal/modal';
import { UmbModalBaseElement } from '@umbraco-cms/internal/modal';
import { LanguageResponseModel } from '@umbraco-cms/backoffice/backend-api';
import { UmbSelectionManagerBase } from '@umbraco-cms/backoffice/utils';
import { UmbLanguagePickerModalResult, UmbLanguagePickerModalData } from '@umbraco-cms/backoffice/modal';
@customElement('umb-language-picker-modal')
export class UmbLanguagePickerModalElement extends UmbModalElementPickerBase<LanguageResponseModel> {
export class UmbLanguagePickerModalElement extends UmbModalBaseElement<
UmbLanguagePickerModalData,
UmbLanguagePickerModalResult
> {
@state()
private _languages: Array<LanguageResponseModel> = [];
private _languageRepository = new UmbLanguageRepository(this);
#languageRepository = new UmbLanguageRepository(this);
#selectionManager = new UmbSelectionManagerBase();
async firstUpdated() {
const { data } = await this._languageRepository.requestLanguages();
this._languages = data?.items ?? [];
connectedCallback(): void {
super.connectedCallback();
this.#selectionManager.setMultiple(this.data?.multiple ?? false);
this.#selectionManager.setSelection(this.data?.selection ?? []);
}
#onSelection(event: UUIMenuItemEvent) {
event?.stopPropagation();
const language = event?.target as UUIMenuItemElement;
const isoCode = language.dataset.isoCode;
if (!isoCode) return;
this.handleSelection(isoCode);
async firstUpdated() {
const { data } = await this.#languageRepository.requestLanguages();
this._languages = data?.items ?? [];
}
get #filteredLanguages() {
@@ -38,6 +38,14 @@ export class UmbLanguagePickerModalElement extends UmbModalElementPickerBase<Lan
}
}
#submit() {
this.modalHandler?.submit({ selection: this.#selectionManager.getSelection() });
}
#close() {
this.modalHandler?.reject();
}
render() {
return html`<umb-body-layout headline="Select languages">
<uui-box>
@@ -47,23 +55,22 @@ export class UmbLanguagePickerModalElement extends UmbModalElementPickerBase<Lan
(item) => html`
<uui-menu-item
label=${item.name ?? ''}
selectable="true"
@selected=${this.#onSelection}
@unselected=${this.#onSelection}
?selected=${this.isSelected(item.isoCode!)}
data-iso-code="${ifDefined(item.isoCode)}">
selectable
@selected=${() => this.#selectionManager.select(item.isoCode!)}
@unselected=${() => this.#selectionManager.deselect(item.isoCode!)}
?selected=${this.#selectionManager.isSelected(item.isoCode!)}>
<uui-icon slot="icon" name="umb:globe"></uui-icon>
</uui-menu-item>
`
)}
</uui-box>
<div slot="actions">
<uui-button label="Close" @click=${this.close}></uui-button>
<uui-button label="Submit" look="primary" color="positive" @click=${this.submit}></uui-button>
<uui-button label="Close" @click=${this.#close}></uui-button>
<uui-button label="Submit" look="primary" color="positive" @click=${this.#submit}></uui-button>
</div>
</umb-body-layout> `;
}
static styles = [UUITextStyles, css``];
}

View File

@@ -3,17 +3,24 @@ import { css, html } from 'lit';
import { customElement, state } from 'lit/decorators.js';
import { UmbUserGroupStore, UMB_USER_GROUP_STORE_CONTEXT_TOKEN } from '../../repository/user-group.store';
import type { UserGroupDetails } from '../../types';
import { UmbModalElementPickerBase } from '@umbraco-cms/internal/modal';
import { UmbSelectionManagerBase } from '@umbraco-cms/backoffice/utils';
import { UmbModalBaseElement } from '@umbraco-cms/internal/modal';
@customElement('umb-user-group-picker-modal')
export class UmbUserGroupPickerModalElement extends UmbModalElementPickerBase<UserGroupDetails> {
export class UmbUserGroupPickerModalElement extends UmbModalBaseElement<any, any> {
@state()
private _userGroups: Array<UserGroupDetails> = [];
private _userGroupStore?: UmbUserGroupStore;
#selectionManager = new UmbSelectionManagerBase();
connectedCallback(): void {
super.connectedCallback();
// TODO: in theory this config could change during the lifetime of the modal, so we could observe it
this.#selectionManager.setMultiple(this.data?.multiple ?? false);
this.#selectionManager.setSelection(this.data?.selection ?? []);
this.consumeContext(UMB_USER_GROUP_STORE_CONTEXT_TOKEN, (userGroupStore) => {
this._userGroupStore = userGroupStore;
this._observeUserGroups();
@@ -25,79 +32,42 @@ export class UmbUserGroupPickerModalElement extends UmbModalElementPickerBase<Us
this.observe(this._userGroupStore.getAll(), (userGroups) => (this._userGroups = userGroups));
}
#submit() {
this.modalHandler?.submit({
selection: this.#selectionManager.getSelection(),
});
}
#close() {
this.modalHandler?.reject();
}
render() {
return html`
<umb-workspace-editor headline="Select user groups">
<uui-box>
<uui-input label="search"></uui-input>
<hr />
<div id="item-list">
${this._userGroups.map(
(item) => html`
<div
@click=${() => this.handleSelection(item.id)}
@keydown=${(e: KeyboardEvent) => this._handleKeydown(e, item.id)}
class=${this.isSelected(item.id) ? 'item selected' : 'item'}>
<uui-icon .name=${item.icon}></uui-icon>
<span>${item.name}</span>
</div>
`
)}
</div>
${this._userGroups.map(
(item) => html`
<uui-menu-item
label=${item.name}
selectable
@selected=${() => this.#selectionManager.select(item.id!)}
@unselected=${() => this.#selectionManager.deselect(item.id!)}
?selected=${this.#selectionManager.isSelected(item.id!)}>
<uui-icon .name=${item.icon} slot="icon"></uui-icon>
</uui-menu-item>
`
)}
</uui-box>
<div slot="actions">
<uui-button label="Close" @click=${this.close}></uui-button>
<uui-button label="Submit" look="primary" color="positive" @click=${this.submit}></uui-button>
<uui-button label="Close" @click=${this.#close}></uui-button>
<uui-button label="Submit" look="primary" color="positive" @click=${this.#submit}></uui-button>
</div>
</umb-workspace-editor>
`;
}
static styles = [
UUITextStyles,
css`
uui-input {
width: 100%;
}
hr {
border: none;
border-bottom: 1px solid var(--uui-color-divider);
margin: 16px 0;
}
#item-list {
display: flex;
flex-direction: column;
gap: var(--uui-size-1);
}
.item {
color: var(--uui-color-interactive);
display: grid;
grid-template-columns: var(--uui-size-8) 1fr;
padding: var(--uui-size-4) var(--uui-size-2);
gap: var(--uui-size-space-5);
align-items: center;
border-radius: var(--uui-border-radius);
cursor: pointer;
}
.item.selected {
background-color: var(--uui-color-selected);
color: var(--uui-color-selected-contrast);
}
.item:not(.selected):hover {
background-color: var(--uui-color-surface-emphasis);
color: var(--uui-color-interactive-emphasis);
}
.item.selected:hover {
background-color: var(--uui-color-selected-emphasis);
}
.item uui-icon {
width: 100%;
box-sizing: border-box;
display: flex;
height: fit-content;
}
`,
];
static styles = [UUITextStyles, css``];
}
export default UmbUserGroupPickerModalElement;

View File

@@ -1,30 +1,20 @@
import { UUITextStyles } from '@umbraco-ui/uui-css';
import { css, html } from 'lit';
import { customElement, property, state } from 'lit/decorators.js';
import { customElement, state } from 'lit/decorators.js';
import { UmbUserRepository } from '../../repository/user.repository';
import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
import { UmbModalHandler, UmbUserPickerModalData, UmbUserPickerModalResult } from '@umbraco-cms/backoffice/modal';
import { UmbUserPickerModalData, UmbUserPickerModalResult } from '@umbraco-cms/backoffice/modal';
import { createExtensionClass, umbExtensionsRegistry } from '@umbraco-cms/backoffice/extensions-api';
import { UmbObserverController } from '@umbraco-cms/backoffice/observable-api';
import { UserResponseModel } from '@umbraco-cms/backoffice/backend-api';
import { UmbModalBaseElement } from '@umbraco-cms/internal/modal';
import { UmbSelectionManagerBase } from '@umbraco-cms/backoffice/utils';
@customElement('umb-user-picker-modal')
export class UmbUserPickerModalElement extends UmbLitElement {
@property({ attribute: false })
modalHandler?: UmbModalHandler<UmbUserPickerModalData, UmbUserPickerModalResult>;
@property({ type: Object, attribute: false })
data?: UmbUserPickerModalData;
@state()
_selection: Array<string> = [];
@state()
_multiple = false;
export class UmbUserPickerModalElement extends UmbModalBaseElement<UmbUserPickerModalData, UmbUserPickerModalResult> {
@state()
private _users: Array<UserResponseModel> = [];
#selectionManager = new UmbSelectionManagerBase();
#userRepository?: UmbUserRepository;
constructor() {
@@ -59,7 +49,7 @@ export class UmbUserPickerModalElement extends UmbLitElement {
}
#submit() {
this.modalHandler?.submit({ selection: this._selection });
this.modalHandler?.submit({ selection: this.#selectionManager.getSelection() });
}
#close() {
@@ -72,10 +62,14 @@ export class UmbUserPickerModalElement extends UmbLitElement {
<uui-box>
${this._users.map(
(user) => html`
<uui-menu-item label=${user.name} selectable>
<uui-menu-item
label=${user.name}
selectable
@selected=${() => this.#selectionManager.select(user.id!)}
@unselected=${() => this.#selectionManager.deselect(user.id!)}
?selected=${this.#selectionManager.isSelected(user.id!)}>
<uui-avatar slot="icon" name=${user.name}></uui-avatar>
Hello</uui-menu-item
>
</uui-menu-item>
`
)}
</uui-box>

View File

@@ -2,12 +2,21 @@ import { html } from 'lit';
import { customElement, property } from 'lit/decorators.js';
import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
import type { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller';
import type { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
/**
* Provides a value to the context down the DOM tree.
*
* @remarks This element is a wrapper around the `provideContext` function.
* @slot - The context will be available to all descendants given in the default slot.
* @throws {Error} If the key property is not set.
* @throws {Error} If the value property is not set.
*/
@customElement('umb-context-provider')
export class UmbContextProviderElement extends UmbLitElement {
/**
* The value to provide to the context.
* @required
* @optional
*/
@property({ type: Object, attribute: false })
create?: (host: UmbControllerHostElement) => unknown;
@@ -24,7 +33,7 @@ export class UmbContextProviderElement extends UmbLitElement {
* @required
*/
@property({ type: String })
key!: string;
key!: string | UmbContextToken;
connectedCallback() {
super.connectedCallback();

View File

@@ -1,2 +1 @@
export * from './modal-element-picker-base';
export * from './modal-element.element';

View File

@@ -1,51 +0,0 @@
import { property } from 'lit/decorators.js';
import { UmbModalBaseElement } from './modal-element.element';
import { UmbPickerModalData, UmbPickerModalResult } from '@umbraco-cms/backoffice/modal';
// TODO: we should consider moving this into a class/context instead of an element.
// So we don't have to extend an element to get basic picker/selection logic
export class UmbModalElementPickerBase<T> extends UmbModalBaseElement<UmbPickerModalData<T>, UmbPickerModalResult> {
@property()
selection: Array<string | null> = [];
connectedCallback(): void {
super.connectedCallback();
this.selection = this.data?.selection || [];
}
submit() {
this.modalHandler?.submit({ selection: this.selection });
}
close() {
this.modalHandler?.reject();
}
protected _handleKeydown(e: KeyboardEvent, id?: string | null) {
if (e.key === 'Enter') {
this.handleSelection(id);
}
}
/* TODO: Write test for this select/deselect method. */
handleSelection(id?: string | null) {
if (id === undefined) throw new Error('No key provided');
if (this.data?.multiple) {
if (this.isSelected(id)) {
this.selection = this.selection.filter((selectedKey) => selectedKey !== id);
} else {
this.selection.push(id);
}
} else {
this.selection = [id];
}
this.requestUpdate('_selection');
}
isSelected(id?: string | null): boolean {
if (id === undefined) throw new Error('No Id provided');
return this.selection.includes(id);
}
}

View File

@@ -1,6 +1,5 @@
import { html } from 'lit';
import { UmbInstallerContext } from '../installer.context';
import '../../../storybook/utils/context-provider/context-provider.element';
export const installerContextProvider = (story: any, installerContext = new UmbInstallerContext()) => html`
<umb-context-provider