diff --git a/src/Umbraco.Web.UI.Client/schemas/api/api.yml b/src/Umbraco.Web.UI.Client/schemas/api/api.yml
index 1e8a770699..532bec33a9 100644
--- a/src/Umbraco.Web.UI.Client/schemas/api/api.yml
+++ b/src/Umbraco.Web.UI.Client/schemas/api/api.yml
@@ -566,13 +566,9 @@ components:
type: string
pathname:
type: string
- weight:
- type: number
- format: float
required:
- label
- pathname
- - weight
IManifestSection:
type: object
properties:
@@ -590,6 +586,9 @@ components:
type: string
name:
type: string
+ weight:
+ type: number
+ format: float
required:
- type
- meta
@@ -634,6 +633,9 @@ components:
type: string
name:
type: string
+ weight:
+ type: number
+ format: float
required:
- type
- meta
@@ -642,15 +644,11 @@ components:
MetaTree:
type: object
properties:
- weight:
- type: number
- format: float
sections:
type: array
items:
type: string
required:
- - weight
- sections
IManifestTree:
type: object
@@ -669,6 +667,9 @@ components:
type: string
name:
type: string
+ weight:
+ type: number
+ format: float
required:
- type
- meta
@@ -698,6 +699,9 @@ components:
type: string
name:
type: string
+ weight:
+ type: number
+ format: float
required:
- type
- meta
@@ -729,6 +733,9 @@ components:
type: string
name:
type: string
+ weight:
+ type: number
+ format: float
required:
- type
- meta
@@ -743,9 +750,6 @@ components:
type: string
pathname:
type: string
- weight:
- type: number
- format: float
label:
type: string
icon:
@@ -753,7 +757,6 @@ components:
required:
- editors
- pathname
- - weight
- label
- icon
IManifestEditorView:
@@ -773,6 +776,9 @@ components:
type: string
name:
type: string
+ weight:
+ type: number
+ format: float
required:
- type
- meta
@@ -789,14 +795,10 @@ components:
type: string
icon:
type: string
- weight:
- type: number
- format: float
required:
- trees
- label
- icon
- - weight
IManifestTreeItemAction:
type: object
properties:
@@ -814,6 +816,9 @@ components:
type: string
name:
type: string
+ weight:
+ type: number
+ format: float
required:
- type
- meta
@@ -911,6 +916,9 @@ components:
type: string
name:
type: string
+ weight:
+ type: number
+ format: float
required:
- type
- meta
@@ -925,15 +933,11 @@ components:
type: string
pathname:
type: string
- weight:
- type: number
- format: float
label:
type: string
required:
- sections
- pathname
- - weight
IManifestDashboard:
type: object
properties:
@@ -951,6 +955,9 @@ components:
type: string
name:
type: string
+ weight:
+ type: number
+ format: float
required:
- type
- meta
@@ -982,6 +989,9 @@ components:
type: string
name:
type: string
+ weight:
+ type: number
+ format: float
required:
- type
- meta
@@ -1011,6 +1021,9 @@ components:
type: string
name:
type: string
+ weight:
+ type: number
+ format: float
required:
- type
- meta
@@ -1029,6 +1042,9 @@ components:
type: string
name:
type: string
+ weight:
+ type: number
+ format: float
required:
- type
- js
@@ -1047,6 +1063,9 @@ components:
type: string
name:
type: string
+ weight:
+ type: number
+ format: float
required:
- type
- alias
diff --git a/src/Umbraco.Web.UI.Client/schemas/generated-schema.ts b/src/Umbraco.Web.UI.Client/schemas/generated-schema.ts
index 5ca9c6fb50..5abca1cf7e 100644
--- a/src/Umbraco.Web.UI.Client/schemas/generated-schema.ts
+++ b/src/Umbraco.Web.UI.Client/schemas/generated-schema.ts
@@ -142,8 +142,6 @@ export interface components {
MetaSection: {
label: string;
pathname: string;
- /** Format: float */
- weight: number;
};
IManifestSection: {
/** @enum {string} */
@@ -153,6 +151,8 @@ export interface components {
elementName?: string;
alias: string;
name: string;
+ /** Format: float */
+ weight?: number;
};
MetaSectionView: {
sections: string[];
@@ -170,10 +170,10 @@ export interface components {
elementName?: string;
alias: string;
name: string;
+ /** Format: float */
+ weight?: number;
};
MetaTree: {
- /** Format: float */
- weight: number;
sections: string[];
};
IManifestTree: {
@@ -184,6 +184,8 @@ export interface components {
elementName?: string;
alias: string;
name: string;
+ /** Format: float */
+ weight?: number;
};
MetaEditor: {
entityType: string;
@@ -196,6 +198,8 @@ export interface components {
elementName?: string;
alias: string;
name: string;
+ /** Format: float */
+ weight?: number;
};
MetaEditorAction: {
editors: string[];
@@ -208,12 +212,12 @@ export interface components {
elementName?: string;
alias: string;
name: string;
+ /** Format: float */
+ weight?: number;
};
MetaEditorView: {
editors: string[];
pathname: string;
- /** Format: float */
- weight: number;
label: string;
icon: string;
};
@@ -225,13 +229,13 @@ export interface components {
elementName?: string;
alias: string;
name: string;
+ /** Format: float */
+ weight?: number;
};
MetaTreeItemAction: {
trees: string[];
label: string;
icon: string;
- /** Format: float */
- weight: number;
};
IManifestTreeItemAction: {
/** @enum {string} */
@@ -241,6 +245,8 @@ export interface components {
elementName?: string;
alias: string;
name: string;
+ /** Format: float */
+ weight?: number;
};
PropertyEditorConfigProperty: {
label: string;
@@ -282,12 +288,12 @@ export interface components {
elementName?: string;
alias: string;
name: string;
+ /** Format: float */
+ weight?: number;
};
MetaDashboard: {
sections: string[];
pathname: string;
- /** Format: float */
- weight: number;
label?: string;
};
IManifestDashboard: {
@@ -298,6 +304,8 @@ export interface components {
elementName?: string;
alias: string;
name: string;
+ /** Format: float */
+ weight?: number;
};
MetaPropertyAction: {
propertyEditors: string[];
@@ -310,6 +318,8 @@ export interface components {
elementName?: string;
alias: string;
name: string;
+ /** Format: float */
+ weight?: number;
};
MetaPackageView: {
packageAlias: string;
@@ -322,6 +332,8 @@ export interface components {
elementName?: string;
alias: string;
name: string;
+ /** Format: float */
+ weight?: number;
};
IManifestEntrypoint: {
/** @enum {string} */
@@ -329,6 +341,8 @@ export interface components {
js: string;
alias: string;
name: string;
+ /** Format: float */
+ weight?: number;
};
IManifestCustom: {
/** @enum {string} */
@@ -336,6 +350,8 @@ export interface components {
meta?: { [key: string]: unknown };
alias: string;
name: string;
+ /** Format: float */
+ weight?: number;
};
Manifest:
| components["schemas"]["IManifestSection"]
diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/editors/extensions/editor-extensions.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/editors/extensions/editor-extensions.element.ts
index 0f2d510ec7..1910189a0d 100644
--- a/src/Umbraco.Web.UI.Client/src/backoffice/editors/extensions/editor-extensions.element.ts
+++ b/src/Umbraco.Web.UI.Client/src/backoffice/editors/extensions/editor-extensions.element.ts
@@ -37,11 +37,6 @@ export class UmbEditorExtensionsElement extends UmbContextConsumerMixin(LitEleme
this._extensionsSubscription = this._extensionRegistry.extensions.subscribe((extensions) => {
this._extensions = [...extensions]; // TODO: Though, this is a shallow clone, wouldn't we either do a deep clone or no clone at all?
});
-
- this._extensionsSubscription = this._extensionRegistry.extensionsOfType('section').subscribe((sections) => {
- // In this callback sections are typed. Example meta.weight...
- console.log(sections[0].meta.weight);
- });
});
}
diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/editors/shared/editor-entity-layout/editor-entity-layout.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/editors/shared/editor-entity-layout/editor-entity-layout.element.ts
index d3cdfb25ab..c3cf0e55ac 100644
--- a/src/Umbraco.Web.UI.Client/src/backoffice/editors/shared/editor-entity-layout/editor-entity-layout.element.ts
+++ b/src/Umbraco.Web.UI.Client/src/backoffice/editors/shared/editor-entity-layout/editor-entity-layout.element.ts
@@ -122,13 +122,7 @@ export class UmbEditorEntityLayout extends UmbContextConsumerMixin(LitElement) {
this._editorViewsSubscription = this._extensionRegistry
?.extensionsOfType('editorView')
- .pipe(
- map((extensions) =>
- extensions
- .filter((extension) => extension.meta.editors.includes(this.alias))
- .sort((a, b) => b.meta.weight - a.meta.weight)
- )
- )
+ .pipe(map((extensions) => extensions.filter((extension) => extension.meta.editors.includes(this.alias))))
.subscribe((editorViews) => {
this._editorViews = editorViews;
this._createRoutes();
diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/sections/section.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/sections/section.context.ts
index 9828b1a9e0..d7eaef5b2e 100644
--- a/src/Umbraco.Web.UI.Client/src/backoffice/sections/section.context.ts
+++ b/src/Umbraco.Web.UI.Client/src/backoffice/sections/section.context.ts
@@ -11,10 +11,10 @@ export class UmbSectionContext {
name: '',
js: '',
elementName: '',
+ weight: 0,
meta: {
label: '',
pathname: '',
- weight: 0,
},
});
public readonly data = this._data.asObservable();
diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/sections/shared/section-dashboards/section-dashboards.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/sections/shared/section-dashboards/section-dashboards.element.ts
index 2ca731fcff..256ee9275b 100644
--- a/src/Umbraco.Web.UI.Client/src/backoffice/sections/shared/section-dashboards/section-dashboards.element.ts
+++ b/src/Umbraco.Web.UI.Client/src/backoffice/sections/shared/section-dashboards/section-dashboards.element.ts
@@ -94,9 +94,7 @@ export class UmbSectionDashboardsElement extends UmbContextConsumerMixin(LitElem
?.extensionsOfType('dashboard')
.pipe(
map((extensions) =>
- extensions
- .filter((extension) => extension.meta.sections.includes(this._currentSectionAlias))
- .sort((a, b) => b.meta.weight - a.meta.weight)
+ extensions.filter((extension) => extension.meta.sections.includes(this._currentSectionAlias))
)
)
.subscribe((dashboards) => {
diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/sections/shared/section-trees/section-trees.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/sections/shared/section-trees/section-trees.element.ts
index dbe3f1c3b6..31f56245f0 100644
--- a/src/Umbraco.Web.UI.Client/src/backoffice/sections/shared/section-trees/section-trees.element.ts
+++ b/src/Umbraco.Web.UI.Client/src/backoffice/sections/shared/section-trees/section-trees.element.ts
@@ -48,14 +48,13 @@ export class UmbSectionTreesElement extends UmbContextConsumerMixin(LitElement)
if (!section) return EMPTY;
return (
- this._extensionStore?.extensionsOfType('tree').pipe(
- map((trees) =>
- trees
- .filter((tree) => tree.meta.sections.includes(section.alias))
- .sort((a, b) => b.meta.weight - a.meta.weight)
- .map((tree) => tree.alias)
- )
- ) ?? of([])
+ this._extensionStore
+ ?.extensionsOfType('tree')
+ .pipe(
+ map((trees) =>
+ trees.filter((tree) => tree.meta.sections.includes(section.alias)).map((tree) => tree.alias)
+ )
+ ) ?? of([])
);
})
)
diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/sections/shared/section.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/sections/shared/section.element.ts
index 1ee44a95be..b0472d1207 100644
--- a/src/Umbraco.Web.UI.Client/src/backoffice/sections/shared/section.element.ts
+++ b/src/Umbraco.Web.UI.Client/src/backoffice/sections/shared/section.element.ts
@@ -95,13 +95,7 @@ export class UmbSectionElement extends UmbContextConsumerMixin(LitElement) {
return (
this._extensionRegistry
?.extensionsOfType('tree')
- .pipe(
- map((trees) =>
- trees
- .filter((tree) => tree.meta.sections.includes(section.alias))
- .sort((a, b) => b.meta.weight - a.meta.weight)
- )
- ) ?? of([])
+ .pipe(map((trees) => trees.filter((tree) => tree.meta.sections.includes(section.alias)))) ?? of([])
);
})
)
diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/trees/shared/context-menu/tree-context-menu-page-action-list.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/trees/shared/context-menu/tree-context-menu-page-action-list.element.ts
index c20e75dd96..bb1df03f59 100644
--- a/src/Umbraco.Web.UI.Client/src/backoffice/trees/shared/context-menu/tree-context-menu-page-action-list.element.ts
+++ b/src/Umbraco.Web.UI.Client/src/backoffice/trees/shared/context-menu/tree-context-menu-page-action-list.element.ts
@@ -90,11 +90,9 @@ export class UmbTreeContextMenuPageActionListElement extends UmbContextConsumerM
}
private _renderActions() {
- return this._actions
- .sort((a, b) => a.meta.weight - b.meta.weight)
- .map((action) => {
- return html` `;
- });
+ return this._actions.map((action) => {
+ return html` `;
+ });
}
disconnectedCallback(): void {
diff --git a/src/Umbraco.Web.UI.Client/src/core/extension/index.ts b/src/Umbraco.Web.UI.Client/src/core/extension/index.ts
index 2f96a3abcd..2c6c0f8315 100644
--- a/src/Umbraco.Web.UI.Client/src/core/extension/index.ts
+++ b/src/Umbraco.Web.UI.Client/src/core/extension/index.ts
@@ -1,3 +1,3 @@
-export * from './extension.registry';
+export * from './registry/extension.registry';
export * from './create-extension-element.function';
export * from './load-extension.function';
diff --git a/src/Umbraco.Web.UI.Client/src/core/extension/registry/extension.registry.test.ts b/src/Umbraco.Web.UI.Client/src/core/extension/registry/extension.registry.test.ts
new file mode 100644
index 0000000000..ae6f221a46
--- /dev/null
+++ b/src/Umbraco.Web.UI.Client/src/core/extension/registry/extension.registry.test.ts
@@ -0,0 +1,90 @@
+import { expect } from '@open-wc/testing';
+import type { ManifestTypes } from '../../models';
+import { UmbExtensionRegistry } from './extension.registry';
+
+describe('UmbContextRequestEvent', () => {
+ let extensionRegistry: UmbExtensionRegistry;
+ let manifests: Array;
+
+ beforeEach(() => {
+ extensionRegistry = new UmbExtensionRegistry();
+ manifests = [
+ {
+ type: 'section',
+ name: 'test-section-1',
+ alias: 'Umb.Test.Section.1',
+ weight: 1,
+ meta: {
+ label: 'Test Section 1',
+ pathname: 'test-section-1',
+ },
+ },
+ {
+ type: 'section',
+ name: 'test-section-2',
+ alias: 'Umb.Test.Section.2',
+ weight: 200,
+ meta: {
+ label: 'Test Section 2',
+ pathname: 'test-section-2',
+ },
+ },
+ {
+ type: 'section',
+ name: 'test-section-3',
+ alias: 'Umb.Test.Section.3',
+ weight: 25,
+ meta: {
+ label: 'Test Section 3',
+ pathname: 'test-section-3',
+ },
+ },
+ {
+ type: 'editor',
+ name: 'test-editor-1',
+ alias: 'Umb.Test.Editor.1',
+ meta: {
+ entityType: 'testEntity',
+ },
+ },
+ ];
+
+ manifests.forEach((manifest) => extensionRegistry.register(manifest));
+ });
+
+ it('should register an extension', () => {
+ const registeredExtensions = extensionRegistry['_extensions'].getValue();
+ expect(registeredExtensions).to.have.lengthOf(4);
+ expect(registeredExtensions?.[0]?.name).to.eq('test-section-1');
+ });
+
+ it('should get an extension by alias', (done) => {
+ const alias = 'Umb.Test.Section.1';
+ extensionRegistry.getByAlias(alias).subscribe((extension) => {
+ expect(extension?.alias).to.eq(alias);
+ done();
+ });
+ });
+
+ describe('getByType', () => {
+ const type = 'section';
+
+ it('should get all extensions by type', (done) => {
+ extensionRegistry.extensionsOfType(type).subscribe((extensions) => {
+ expect(extensions).to.have.lengthOf(3);
+ expect(extensions?.[0]?.type).to.eq(type);
+ expect(extensions?.[1]?.type).to.eq(type);
+ done();
+ });
+ });
+
+ it('should return extensions ordered by weight', (done) => {
+ extensionRegistry.extensionsOfType(type).subscribe((extensions) => {
+ expect(extensions?.[0]?.weight).to.eq(200);
+ expect(extensions?.[1]?.weight).to.eq(25);
+ expect(extensions?.[2]?.weight).to.eq(1);
+ done();
+ });
+ });
+ });
+});
diff --git a/src/Umbraco.Web.UI.Client/src/core/extension/extension.registry.ts b/src/Umbraco.Web.UI.Client/src/core/extension/registry/extension.registry.ts
similarity index 90%
rename from src/Umbraco.Web.UI.Client/src/core/extension/extension.registry.ts
rename to src/Umbraco.Web.UI.Client/src/core/extension/registry/extension.registry.ts
index 0d2956f597..d5692e9cbc 100644
--- a/src/Umbraco.Web.UI.Client/src/core/extension/extension.registry.ts
+++ b/src/Umbraco.Web.UI.Client/src/core/extension/registry/extension.registry.ts
@@ -14,8 +14,8 @@ import type {
ManifestEditorAction,
ManifestCustom,
ManifestPackageView,
-} from '../models';
-import { createExtensionElement } from './create-extension-element.function';
+} from '../../models';
+import { createExtensionElement } from '../create-extension-element.function';
export type UmbExtensionManifestJSModel = {
elementName?: string;
@@ -74,6 +74,14 @@ export class UmbExtensionRegistry {
extensionsOfType(type: 'custom'): Observable>;
extensionsOfType(type: string): Observable>;
extensionsOfType(type: string): Observable> {
- return this.extensions.pipe(map((exts) => exts.filter((ext) => ext.type === type)));
+ return this.extensions.pipe(
+ map((exts) =>
+ exts
+ .filter((ext) => ext.type === type)
+ .sort((a, b) => {
+ return (b.weight || 0) - (a.weight || 0);
+ })
+ )
+ );
}
}
diff --git a/src/Umbraco.Web.UI.Client/src/core/stores/section.store.ts b/src/Umbraco.Web.UI.Client/src/core/stores/section.store.ts
index 34a465a5e9..034cb4c686 100644
--- a/src/Umbraco.Web.UI.Client/src/core/stores/section.store.ts
+++ b/src/Umbraco.Web.UI.Client/src/core/stores/section.store.ts
@@ -20,9 +20,7 @@ export class UmbSectionStore {
this._allowedSection = data.sections;
*/
- return this._extensionRegistry
- ?.extensionsOfType('section')
- .pipe(map((extensions) => extensions.sort((a, b) => b.meta.weight - a.meta.weight)));
+ return this._extensionRegistry?.extensionsOfType('section');
}
public setCurrent(alias: string) {
diff --git a/src/Umbraco.Web.UI.Client/src/mocks/domains/manifests.handlers.ts b/src/Umbraco.Web.UI.Client/src/mocks/domains/manifests.handlers.ts
index 13bb1d17d1..53025cb2a3 100644
--- a/src/Umbraco.Web.UI.Client/src/mocks/domains/manifests.handlers.ts
+++ b/src/Umbraco.Web.UI.Client/src/mocks/domains/manifests.handlers.ts
@@ -16,10 +16,10 @@ export const manifestDevelopmentHandler = rest.get(umbracoPath('/manifests'), (_
name: 'Custom Section',
js: '/src/mocks/App_Plugins/section.js',
elementName: 'my-section-custom',
+ weight: 1,
meta: {
label: 'Custom',
pathname: 'my-custom',
- weight: 1,
},
},
{
diff --git a/src/Umbraco.Web.UI.Client/src/temp-internal-manifests/index.ts b/src/Umbraco.Web.UI.Client/src/temp-internal-manifests/index.ts
index c1597b3673..ce97628fbe 100644
--- a/src/Umbraco.Web.UI.Client/src/temp-internal-manifests/index.ts
+++ b/src/Umbraco.Web.UI.Client/src/temp-internal-manifests/index.ts
@@ -1,7 +1,6 @@
import type { ManifestTypes } from '../core/models';
import { manifests as propertyEditorUIManifests } from './property-editor-ui';
-// TODO: consider moving weight from meta to the main part of the manifest. We need it for every extension.
export const internalManifests: Array Promise