add types for sections and dashboards and html elements
This commit is contained in:
@@ -1,13 +1,13 @@
|
||||
import { Subscription, map } from 'rxjs';
|
||||
import { UUITextStyles } from '@umbraco-ui/uui-css/lib';
|
||||
import { css, CSSResultGroup, html, LitElement } from 'lit';
|
||||
import { customElement, state } from 'lit/decorators.js';
|
||||
import { when } from 'lit/directives/when.js';
|
||||
import { isPathActive, path } from 'router-slot';
|
||||
import { map, Subscription } from 'rxjs';
|
||||
|
||||
import { getUserSections } from '../core/api/fetcher';
|
||||
import { UmbExtensionRegistry, UmbExtensionManifest, UmbExtensionManifestSection } from '../core/extension';
|
||||
import { UmbContextConsumerMixin } from '../core/context';
|
||||
import { UmbExtensionManifestSection, UmbExtensionRegistry } from '../core/extension';
|
||||
|
||||
@customElement('umb-backoffice-header-sections')
|
||||
export class UmbBackofficeHeaderSections extends UmbContextConsumerMixin(LitElement) {
|
||||
@@ -95,16 +95,11 @@ export class UmbBackofficeHeaderSections extends UmbContextConsumerMixin(LitElem
|
||||
const { data } = await getUserSections({});
|
||||
this._allowedSection = data.sections;
|
||||
|
||||
this._sectionSubscription = this._extensionRegistry?.extensions
|
||||
.pipe(
|
||||
map((extensions: Array<UmbExtensionManifest>) =>
|
||||
extensions
|
||||
.filter((extension) => extension.type === 'section')
|
||||
.sort((a: any, b: any) => b.meta.weight - a.meta.weight)
|
||||
)
|
||||
)
|
||||
.subscribe((sections: Array<any>) => {
|
||||
this._sections = sections.filter((section: any) => this._allowedSection.includes(section.alias));
|
||||
this._sectionSubscription = this._extensionRegistry
|
||||
?.extensionsOfType('section')
|
||||
.pipe(map((extensions) => extensions.sort((a, b) => b.meta.weight - a.meta.weight)))
|
||||
.subscribe((sections) => {
|
||||
this._sections = sections.filter((section) => this._allowedSection.includes(section.alias));
|
||||
this._visibleSections = this._sections;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2,15 +2,10 @@ import { defineElement } from '@umbraco-ui/uui-base/lib/registration';
|
||||
import { UUITextStyles } from '@umbraco-ui/uui-css/lib';
|
||||
import { css, html, LitElement } from 'lit';
|
||||
import { state } from 'lit/decorators.js';
|
||||
import { Subscription, map } from 'rxjs';
|
||||
import { map, Subscription } from 'rxjs';
|
||||
|
||||
import { UmbContextConsumerMixin } from '../core/context';
|
||||
import {
|
||||
UmbExtensionRegistry,
|
||||
createExtensionElement,
|
||||
UmbExtensionManifest,
|
||||
UmbExtensionManifestSection,
|
||||
} from '../core/extension';
|
||||
import { createExtensionElement, UmbExtensionManifestSection, UmbExtensionRegistry } from '../core/extension';
|
||||
|
||||
@defineElement('umb-backoffice-main')
|
||||
export class UmbBackofficeMain extends UmbContextConsumerMixin(LitElement) {
|
||||
@@ -47,14 +42,9 @@ export class UmbBackofficeMain extends UmbContextConsumerMixin(LitElement) {
|
||||
private _useSections() {
|
||||
this._sectionSubscription?.unsubscribe();
|
||||
|
||||
this._sectionSubscription = this._extensionRegistry?.extensions
|
||||
.pipe(
|
||||
map((extensions: Array<UmbExtensionManifest>) =>
|
||||
extensions
|
||||
.filter((extension) => extension.type === 'section')
|
||||
.sort((a: any, b: any) => b.meta.weight - a.meta.weight)
|
||||
)
|
||||
)
|
||||
this._sectionSubscription = this._extensionRegistry
|
||||
?.extensionsOfType('section')
|
||||
.pipe(map((extensions) => extensions.sort((a, b) => b.meta.weight - a.meta.weight)))
|
||||
.subscribe((sections) => {
|
||||
this._routes = [];
|
||||
this._sections = sections as Array<UmbExtensionManifestSection>;
|
||||
|
||||
@@ -1,19 +1,18 @@
|
||||
import '../section/section-sidebar.element';
|
||||
import './backoffice-header.element';
|
||||
import './backoffice-main.element';
|
||||
|
||||
import { defineElement } from '@umbraco-ui/uui-base/lib/registration';
|
||||
import { UUITextStyles } from '@umbraco-ui/uui-css/lib';
|
||||
import { css, html, LitElement } from 'lit';
|
||||
|
||||
import './backoffice-header.element';
|
||||
import '../section/section-sidebar.element';
|
||||
import './backoffice-main.element';
|
||||
|
||||
import { UUIToastNotificationContainerElement } from '@umbraco-ui/uui';
|
||||
import { UmbContextProviderMixin } from '../core/context';
|
||||
import { UmbNodeStore } from '../core/stores/node.store';
|
||||
import { UmbDataTypeStore } from '../core/stores/data-type.store';
|
||||
import { UmbNotificationService } from '../core/service/notifications.store';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { state } from 'lit/decorators.js';
|
||||
import { repeat } from 'lit/directives/repeat.js';
|
||||
import { Subscription } from 'rxjs';
|
||||
|
||||
import { UmbContextProviderMixin } from '../core/context';
|
||||
import { UmbNotificationService } from '../core/service/notifications.store';
|
||||
import { UmbDataTypeStore } from '../core/stores/data-type.store';
|
||||
import { UmbNodeStore } from '../core/stores/node.store';
|
||||
|
||||
@defineElement('umb-backoffice')
|
||||
export default class UmbBackoffice extends UmbContextProviderMixin(LitElement) {
|
||||
@@ -32,11 +31,11 @@ export default class UmbBackoffice extends UmbContextProviderMixin(LitElement) {
|
||||
|
||||
#notifications {
|
||||
position: absolute;
|
||||
top:0;
|
||||
left:0;
|
||||
right:0;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 70px;
|
||||
height:auto;
|
||||
height: auto;
|
||||
padding: var(--uui-size-layout-1);
|
||||
}
|
||||
`,
|
||||
@@ -46,7 +45,7 @@ export default class UmbBackoffice extends UmbContextProviderMixin(LitElement) {
|
||||
private _notificationSubscribtion?: Subscription;
|
||||
|
||||
@state()
|
||||
private _notifications:any[] = [];
|
||||
private _notifications: any[] = [];
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
@@ -58,37 +57,34 @@ export default class UmbBackoffice extends UmbContextProviderMixin(LitElement) {
|
||||
this.provideContext('umbNotificationService', this._notificationService);
|
||||
}
|
||||
|
||||
protected firstUpdated(
|
||||
_changedProperties: Map<string | number | symbol, unknown>
|
||||
): void {
|
||||
protected firstUpdated(_changedProperties: Map<string | number | symbol, unknown>): void {
|
||||
super.firstUpdated(_changedProperties);
|
||||
|
||||
this._notificationSubscribtion = this._notificationService.notifications
|
||||
.subscribe((notifications: Array<any>) => {
|
||||
this._notificationSubscribtion = this._notificationService.notifications.subscribe((notifications: Array<any>) => {
|
||||
this._notifications = notifications;
|
||||
});
|
||||
|
||||
// TODO: listen to close event and remove notification from store.
|
||||
}
|
||||
|
||||
disconnectedCallback(): void {
|
||||
super.disconnectedCallback();
|
||||
|
||||
this._notificationSubscribtion?.unsubscribe();
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<umb-backoffice-header></umb-backoffice-header>
|
||||
<umb-backoffice-main></umb-backoffice-main>
|
||||
<uui-toast-notification-container
|
||||
auto-close="7000"
|
||||
bottom-up
|
||||
id="notifications">
|
||||
${
|
||||
repeat(
|
||||
this._notifications,
|
||||
(notification: any) => notification.key,
|
||||
notification => html`<uui-toast-notification color='positive'>
|
||||
<uui-toast-notification-layout .headline=${notification.headline}>
|
||||
</uui-toast-notification-layout>
|
||||
<uui-toast-notification-container auto-close="7000" bottom-up id="notifications">
|
||||
${repeat(
|
||||
this._notifications,
|
||||
(notification) => notification.key,
|
||||
(notification) => html`<uui-toast-notification color="positive">
|
||||
<uui-toast-notification-layout .headline=${notification.headline}> </uui-toast-notification-layout>
|
||||
</uui-toast-notification>`
|
||||
)
|
||||
}
|
||||
)}
|
||||
</uui-toast-notification-container>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { HTMLElementConstructor } from '../models';
|
||||
import { UmbContextConsumer } from './context-consumer';
|
||||
|
||||
type Constructor<T = HTMLElement> = new (...args: any[]) => T;
|
||||
|
||||
export declare class UmbContextConsumerInterface {
|
||||
consumeContext(alias: string, callback?: (_instance: any) => void): void;
|
||||
whenAvailableOrChanged(contextAliases: string[], callback?: () => void): void;
|
||||
@@ -14,7 +13,7 @@ export declare class UmbContextConsumerInterface {
|
||||
* @param {Object} superClass - superclass to be extended.
|
||||
* @mixin
|
||||
*/
|
||||
export const UmbContextConsumerMixin = <T extends Constructor<HTMLElement>>(superClass: T) => {
|
||||
export const UmbContextConsumerMixin = <T extends HTMLElementConstructor>(superClass: T) => {
|
||||
class UmbContextConsumerClass extends superClass {
|
||||
// all context requesters in the element
|
||||
_consumers: Map<string, UmbContextConsumer> = new Map();
|
||||
@@ -75,7 +74,7 @@ export const UmbContextConsumerMixin = <T extends Constructor<HTMLElement>>(supe
|
||||
}
|
||||
}
|
||||
|
||||
return UmbContextConsumerClass as unknown as Constructor<UmbContextConsumerInterface> & T;
|
||||
return UmbContextConsumerClass as unknown as HTMLElementConstructor<UmbContextConsumerInterface> & T;
|
||||
};
|
||||
|
||||
declare global {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { expect, oneEvent } from '@open-wc/testing';
|
||||
import { UmbContextProvider } from './context-provider';
|
||||
|
||||
import { UmbContextConsumer } from './context-consumer';
|
||||
import { UmbContextProvider } from './context-provider';
|
||||
import { UmbContextRequestEventImplementation, umbContextRequestEventType } from './context-request.event';
|
||||
|
||||
const testContextAlias = 'my-test-context';
|
||||
@@ -38,7 +39,7 @@ describe('UmbContextConsumer', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('works with UmbContextProvider', (done: any) => {
|
||||
it('works with UmbContextProvider', (done) => {
|
||||
const provider = new UmbContextProvider(document.body, testContextAlias, new MyClass());
|
||||
provider.attach();
|
||||
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import { HTMLElementConstructor } from '../models';
|
||||
import { UmbContextProvider } from './context-provider';
|
||||
|
||||
type Constructor<T = HTMLElement> = new (...args: any[]) => T;
|
||||
|
||||
export declare class UmbContextProviderMixinInterface {
|
||||
provideContext(alias: string, instance: unknown): void;
|
||||
}
|
||||
|
||||
export const UmbContextProviderMixin = <T extends Constructor>(superClass: T) => {
|
||||
export const UmbContextProviderMixin = <T extends HTMLElementConstructor>(superClass: T) => {
|
||||
class UmbContextProviderClass extends superClass {
|
||||
_providers: Map<string, UmbContextProvider> = new Map();
|
||||
|
||||
@@ -39,7 +38,7 @@ export const UmbContextProviderMixin = <T extends Constructor>(superClass: T) =>
|
||||
}
|
||||
}
|
||||
|
||||
return UmbContextProviderClass as unknown as Constructor<UmbContextProviderMixinInterface> & T;
|
||||
return UmbContextProviderClass as unknown as HTMLElementConstructor<UmbContextProviderMixinInterface> & T;
|
||||
};
|
||||
|
||||
declare global {
|
||||
|
||||
@@ -1,30 +1,29 @@
|
||||
import { UmbExtensionManifest } from './extension.registry';
|
||||
import { hasDefaultExport } from './has-default-export.function';
|
||||
import { isExtensionType } from './is-extension.function';
|
||||
import { loadExtension } from './load-extension.function';
|
||||
|
||||
export function createExtensionElement(manifest: UmbExtensionManifest): Promise<HTMLElement> | Promise<undefined> {
|
||||
export async function createExtensionElement(manifest: UmbExtensionManifest): Promise<HTMLElement | undefined> {
|
||||
//TODO: Write tests for these extension options:
|
||||
return loadExtension(manifest).then((js) => {
|
||||
if (manifest.elementName) {
|
||||
console.log('-- created by elementName', manifest.elementName);
|
||||
return document.createElement(manifest.elementName as any);
|
||||
const js = await loadExtension(manifest);
|
||||
if (manifest.elementName) {
|
||||
console.log('-- created by elementName', manifest.elementName);
|
||||
return document.createElement(manifest.elementName);
|
||||
}
|
||||
if (js) {
|
||||
if (js instanceof HTMLElement) {
|
||||
console.log('-- created by manifest method providing HTMLElement', js);
|
||||
return js;
|
||||
}
|
||||
|
||||
if (js) {
|
||||
if (js instanceof HTMLElement) {
|
||||
console.log('-- created by manifest method providing HTMLElement', js);
|
||||
return js;
|
||||
}
|
||||
if ((js as any).elementName) {
|
||||
console.log('-- created by export elementName', (js as any).elementName);
|
||||
return document.createElement((js as any).elementName);
|
||||
}
|
||||
if ((js as any).default) {
|
||||
console.log('-- created by default class', (js as any).default);
|
||||
return new (js as any).default() as HTMLElement;
|
||||
}
|
||||
if (isExtensionType(js)) {
|
||||
console.log('-- created by export elementName', js.elementName);
|
||||
return js.elementName ? document.createElement(js.elementName) : Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
console.error('-- Extension did not succeed creating an element');
|
||||
return Promise.resolve(undefined);
|
||||
});
|
||||
if (hasDefaultExport(js)) {
|
||||
console.log('-- created by default class', js.default);
|
||||
return new js.default();
|
||||
}
|
||||
}
|
||||
console.error('-- Extension did not succeed creating an element');
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
import { HTMLElementConstructor } from '../models';
|
||||
|
||||
export function hasDefaultExport(object: unknown): object is { default: HTMLElementConstructor } {
|
||||
return typeof object === 'object' && object !== null && 'default' in object;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { UmbExtensionManifestBase } from './extension.registry';
|
||||
|
||||
export function isExtensionType(manifest: unknown): manifest is UmbExtensionManifestBase {
|
||||
return (
|
||||
typeof manifest === 'object' &&
|
||||
manifest !== null &&
|
||||
(manifest as UmbExtensionManifestBase).elementName !== undefined
|
||||
);
|
||||
}
|
||||
@@ -17,3 +17,6 @@ export type UmbracoPerformInstallDatabaseConfiguration =
|
||||
export type UmbracoInstallerDatabaseModel = components['schemas']['UmbracoInstallerDatabaseModel'];
|
||||
export type UmbracoInstallerUserModel = components['schemas']['UmbracoInstallerUserModel'];
|
||||
export type TelemetryModel = components['schemas']['TelemetryModel'];
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export type HTMLElementConstructor<T = HTMLElement> = new (...args: any[]) => T;
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import { css, html, LitElement, PropertyValueMap } from 'lit';
|
||||
import { UUITextStyles } from '@umbraco-ui/uui-css/lib';
|
||||
import { css, html, LitElement, PropertyValueMap } from 'lit';
|
||||
import { customElement, property, state } from 'lit/decorators.js';
|
||||
import { createExtensionElement, UmbExtensionManifest, UmbExtensionRegistry } from '../../core/extension';
|
||||
import { Subscription, map, switchMap } from 'rxjs';
|
||||
import { EMPTY, of, Subscription } from 'rxjs';
|
||||
import { switchMap } from 'rxjs/operators';
|
||||
|
||||
import { UmbContextConsumerMixin } from '../../core/context';
|
||||
import { DataTypeEntity } from '../../mocks/data/content.data';
|
||||
import { createExtensionElement, UmbExtensionManifest, UmbExtensionRegistry } from '../../core/extension';
|
||||
import { UmbDataTypeStore } from '../../core/stores/data-type.store';
|
||||
import { DataTypeEntity } from '../../mocks/data/content.data';
|
||||
|
||||
@customElement('umb-node-property')
|
||||
class UmbNodeProperty extends UmbContextConsumerMixin(LitElement) {
|
||||
@@ -63,26 +65,27 @@ class UmbNodeProperty extends UmbContextConsumerMixin(LitElement) {
|
||||
private _useDataType() {
|
||||
this._dataTypeSubscription?.unsubscribe();
|
||||
if (this._property.dataTypeKey && this._extensionRegistry && this._dataTypeStore) {
|
||||
|
||||
this._dataTypeSubscription = this._dataTypeStore
|
||||
.getByKey(this._property.dataTypeKey)
|
||||
.pipe(
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
map((dataTypeEntity: DataTypeEntity) => {
|
||||
switchMap((dataTypeEntity) => {
|
||||
if (!dataTypeEntity) {
|
||||
return EMPTY;
|
||||
}
|
||||
this._dataType = dataTypeEntity;
|
||||
return dataTypeEntity.propertyEditorUIAlias;
|
||||
}),
|
||||
switchMap((alias: string) => this._extensionRegistry?.getByAlias(alias) as any)
|
||||
|
||||
return this._extensionRegistry?.getByAlias(dataTypeEntity.propertyEditorUIAlias) ?? of(null);
|
||||
})
|
||||
)
|
||||
.subscribe((propertyEditorUI: any) => {
|
||||
this._gotData(propertyEditorUI);
|
||||
.subscribe((propertyEditorUI) => {
|
||||
if (propertyEditorUI) {
|
||||
this._gotData(propertyEditorUI);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private _gotData(_propertyEditorUI?: UmbExtensionManifest) {
|
||||
|
||||
if (!this._dataType || !_propertyEditorUI) {
|
||||
// TODO: if dataTypeKey didn't exist in store, we should do some nice UI.
|
||||
return;
|
||||
@@ -145,9 +148,7 @@ class UmbNodeProperty extends UmbContextConsumerMixin(LitElement) {
|
||||
<uui-label>${this.property.label}</uui-label>
|
||||
<p>${this.property.description}</p>
|
||||
</div>
|
||||
<div slot="editor">
|
||||
${this._element}
|
||||
</div>
|
||||
<div slot="editor">${this._element}</div>
|
||||
</umb-editor-property-layout>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { css, html, LitElement } from 'lit';
|
||||
import { UUITextStyles } from '@umbraco-ui/uui-css/lib';
|
||||
import { css, html, LitElement } from 'lit';
|
||||
import { customElement, property, state } from 'lit/decorators.js';
|
||||
import { UmbContextConsumerMixin } from '../../../core/context';
|
||||
import { UmbNodeStore } from '../../../core/stores/node.store';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { DocumentNode } from '../../../mocks/data/content.data';
|
||||
|
||||
import { UmbContextConsumerMixin } from '../../../core/context';
|
||||
import { UmbNotificationService } from '../../../core/service/notifications.store';
|
||||
import { UmbNodeStore } from '../../../core/stores/node.store';
|
||||
import { DocumentNode } from '../../../mocks/data/content.data';
|
||||
|
||||
@customElement('umb-content-editor')
|
||||
export class UmbContentEditor extends UmbContextConsumerMixin(LitElement) {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { rest } from 'msw';
|
||||
import { umbContentData } from '../data/content.data';
|
||||
|
||||
import { DocumentNode, umbContentData } from '../data/content.data';
|
||||
|
||||
// TODO: add schema
|
||||
export const handlers = [
|
||||
@@ -13,8 +14,8 @@ export const handlers = [
|
||||
return res(ctx.status(200), ctx.json([document]));
|
||||
}),
|
||||
|
||||
rest.post('/umbraco/backoffice/content/save', (req, res, ctx) => {
|
||||
const data = req.body as any;
|
||||
rest.post<DocumentNode[]>('/umbraco/backoffice/content/save', (req, res, ctx) => {
|
||||
const data = req.body;
|
||||
if (!data) return;
|
||||
|
||||
umbContentData.save(data);
|
||||
|
||||
@@ -5,12 +5,7 @@ import { IRoutingInfo } from 'router-slot';
|
||||
import { map, Subscription } from 'rxjs';
|
||||
|
||||
import { UmbContextConsumerMixin } from '../core/context';
|
||||
import {
|
||||
UmbExtensionManifestDashboard,
|
||||
UmbExtensionManifest,
|
||||
UmbExtensionRegistry,
|
||||
createExtensionElement,
|
||||
} from '../core/extension';
|
||||
import { createExtensionElement, UmbExtensionManifestDashboard, UmbExtensionRegistry } from '../core/extension';
|
||||
|
||||
@customElement('umb-section-dashboards')
|
||||
export class UmbSectionDashboards extends UmbContextConsumerMixin(LitElement) {
|
||||
@@ -60,23 +55,18 @@ export class UmbSectionDashboards extends UmbContextConsumerMixin(LitElement) {
|
||||
private _useDashboards() {
|
||||
this._dashboardsSubscription?.unsubscribe();
|
||||
|
||||
this._dashboardsSubscription = this._extensionRegistry?.extensions
|
||||
.pipe(
|
||||
map((extensions: Array<UmbExtensionManifest>) =>
|
||||
extensions
|
||||
.filter((extension) => extension.type === 'dashboard')
|
||||
.sort((a: any, b: any) => b.meta.weight - a.meta.weight)
|
||||
)
|
||||
)
|
||||
.subscribe((dashboards: Array<UmbExtensionManifest>) => {
|
||||
this._dashboards = dashboards as Array<UmbExtensionManifestDashboard>;
|
||||
this._dashboardsSubscription = this._extensionRegistry
|
||||
?.extensionsOfType('dashboard')
|
||||
.pipe(map((extensions) => extensions.sort((a, b) => b.meta.weight - a.meta.weight)))
|
||||
.subscribe((dashboards) => {
|
||||
this._dashboards = dashboards;
|
||||
this._routes = [];
|
||||
|
||||
this._routes = this._dashboards.map((dashboard) => {
|
||||
return {
|
||||
path: `${dashboard.meta.pathname}`,
|
||||
component: () => createExtensionElement(dashboard),
|
||||
setup: (element: UmbExtensionManifestDashboard, info: IRoutingInfo) => {
|
||||
setup: (_element: UmbExtensionManifestDashboard, info: IRoutingInfo) => {
|
||||
this._current = info.match.route.path;
|
||||
},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user