Skip to content

Commit 700a6fb

Browse files
authored
Feat: Single mode for Extension Slot (#2236)
feature: Only render a single Custom view + example with settings
2 parents fc1f80e + 1d630d6 commit 700a6fb

File tree

11 files changed

+158
-19
lines changed

11 files changed

+158
-19
lines changed

examples/block-custom-view/block-custom-view.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,15 @@ export class ExampleBlockCustomView extends UmbElementMixin(LitElement) implemen
1111
@property({ attribute: false })
1212
content?: UmbBlockDataType;
1313

14+
@property({ attribute: false })
15+
settings?: UmbBlockDataType;
16+
1417
override render() {
1518
return html`
16-
<div class="uui-text">
19+
<div class="uui-text ${this.settings?.blockAlignment ? 'align-' + this.settings?.blockAlignment : undefined}">
1720
<h5 class="uui-text">My Custom View</h5>
1821
<p>Headline: ${this.content?.headline}</p>
22+
<p>Alignment: ${this.settings?.blockAlignment}</p>
1923
</div>
2024
`;
2125
}
@@ -31,6 +35,13 @@ export class ExampleBlockCustomView extends UmbElementMixin(LitElement) implemen
3135
border-radius: 9px;
3236
padding: 12px;
3337
}
38+
39+
.align-center {
40+
text-align: center;
41+
}
42+
.align-right {
43+
text-align: right;
44+
}
3445
`,
3546
];
3647
}

examples/block-custom-view/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,6 @@ export const manifests: Array<ManifestBlockEditorCustomView> = [
77
name: 'Block Editor Custom View Test',
88
element: () => import('./block-custom-view.js'),
99
forContentTypeAlias: 'headlineUmbracoDemoBlock',
10-
forBlockEditor: 'block-grid',
10+
forBlockEditor: 'block-list',
1111
},
1212
];

src/libs/extension-api/controller/base-extensions-initializer.controller.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,8 +111,6 @@ export abstract class UmbBaseExtensionsInitializer<
111111
manifests.forEach((manifest) => {
112112
const existing = this._extensions.find((x) => x.alias === manifest.alias);
113113
if (!existing) {
114-
// Idea: could be abstracted into a createController method, so we can override it in a subclass.
115-
// (This should be enough to be able to create a element extension controller instead.)
116114
this._extensions.push(this._createController(manifest));
117115
}
118116
});

src/mocks/data/data-type/data-type.data.ts

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,27 @@ export const data: Array<UmbMockDataTypeModel> = [
406406
},
407407
],
408408
},
409+
{
410+
name: 'Dropdown Alignment Options',
411+
id: 'dt-dropdown-align',
412+
parent: null,
413+
editorAlias: 'Umbraco.DropDown.Flexible',
414+
editorUiAlias: 'Umb.PropertyEditorUi.Dropdown',
415+
hasChildren: false,
416+
isFolder: false,
417+
isDeletable: true,
418+
canIgnoreStartNodes: false,
419+
values: [
420+
{
421+
alias: 'multiple',
422+
value: false,
423+
},
424+
{
425+
alias: 'items',
426+
value: ['left', 'center', 'right'],
427+
},
428+
],
429+
},
409430
{
410431
name: 'Slider',
411432
id: 'dt-slider',
@@ -587,6 +608,7 @@ export const data: Array<UmbMockDataTypeModel> = [
587608
{
588609
label: 'Headline',
589610
contentElementTypeKey: 'headline-umbraco-demo-block-id',
611+
settingsElementTypeKey: 'headline-settings-demo-block-id',
590612
backgroundColor: 'gold',
591613
editorSize: 'medium',
592614
icon: 'icon-edit',
@@ -613,7 +635,7 @@ export const data: Array<UmbMockDataTypeModel> = [
613635
},
614636
{
615637
alias: 'useInlineEditingAsDefault',
616-
value: true,
638+
value: false,
617639
},
618640
{
619641
alias: 'useLiveEditing',

src/mocks/data/document-type/document-type.data.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1507,6 +1507,60 @@ export const data: Array<UmbMockDocumentTypeModel> = [
15071507
},
15081508
],
15091509
},
1510+
{
1511+
allowedTemplates: [],
1512+
defaultTemplate: null,
1513+
id: 'headline-settings-demo-block-id',
1514+
alias: 'headlineSettingsUmbracoDemoBlock',
1515+
name: 'Headline',
1516+
description: null,
1517+
icon: 'icon-edit',
1518+
allowedAsRoot: true,
1519+
variesByCulture: false,
1520+
variesBySegment: false,
1521+
isElement: true,
1522+
hasChildren: false,
1523+
parent: { id: 'folder-umbraco-demo-blocks-id' },
1524+
isFolder: false,
1525+
allowedDocumentTypes: [],
1526+
compositions: [],
1527+
cleanup: {
1528+
preventCleanup: false,
1529+
keepAllVersionsNewerThanDays: null,
1530+
keepLatestVersionPerDayForDays: null,
1531+
},
1532+
properties: [
1533+
{
1534+
id: 'block-alignment-id',
1535+
container: { id: 'settings-group-key' },
1536+
alias: 'blockAlignment',
1537+
name: 'Block Alignment',
1538+
description: '',
1539+
dataType: { id: 'dt-dropdown-align' },
1540+
variesByCulture: false,
1541+
variesBySegment: false,
1542+
sortOrder: 0,
1543+
validation: {
1544+
mandatory: false,
1545+
mandatoryMessage: null,
1546+
regEx: null,
1547+
regExMessage: null,
1548+
},
1549+
appearance: {
1550+
labelOnTop: false,
1551+
},
1552+
},
1553+
],
1554+
containers: [
1555+
{
1556+
id: 'settings-group-key',
1557+
parent: null,
1558+
name: 'Settings',
1559+
type: 'Group',
1560+
sortOrder: 0,
1561+
},
1562+
],
1563+
},
15101564
{
15111565
allowedTemplates: [],
15121566
defaultTemplate: null,

src/packages/block/block-grid/components/block-grid-entry/block-grid-entry.element.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,7 @@ export class UmbBlockGridEntryElement extends UmbLitElement implements UmbProper
343343
default-element="umb-block-grid-block"
344344
.props=${this._blockViewProps}
345345
.filter=${this.#extensionSlotFilterMethod}
346+
single
346347
>${this._inlineEditingMode ? this.#renderInlineEditBlock() : this.#renderRefBlock()}</umb-extension-slot
347348
>
348349
<uui-action-bar>

src/packages/block/block-list/components/block-list-entry/block-list-entry.element.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,7 @@ export class UmbBlockListEntryElement extends UmbLitElement implements UmbProper
248248
default-element=${this._inlineEditingMode ? 'umb-inline-list-block' : 'umb-ref-list-block'}
249249
.props=${this._blockViewProps}
250250
.filter=${this.#extensionSlotFilterMethod}
251+
single
251252
>${this._inlineEditingMode ? this.#renderInlineBlock() : this.#renderRefBlock()}</umb-extension-slot
252253
>
253254
<uui-action-bar>

src/packages/block/block-rte/components/block-rte-entry/block-rte-entry.element.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ export class UmbBlockRteEntryElement extends UmbLitElement implements UmbPropert
151151
type="blockEditorCustomView"
152152
default-element=${'umb-ref-rte-block'}
153153
.props=${this._blockViewProps}
154+
single
154155
>${this.#renderRefBlock()}</umb-extension-slot
155156
>
156157
<uui-action-bar>

src/packages/core/extension-registry/components/extension-slot/extension-slot.element.ts

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,20 @@ import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
1515
* @augments {UmbLitElement}
1616
*/
1717

18+
// TODO: Refactor extension-slot and extension-with-api slot.
1819
// TODO: Fire change event.
1920
// TODO: Make property that reveals the amount of displayed/permitted extensions.
2021
@customElement('umb-extension-slot')
2122
export class UmbExtensionSlotElement extends UmbLitElement {
2223
#attached = false;
23-
#extensionsController?: UmbExtensionsElementInitializer;
24+
#extensionsController?: UmbExtensionsElementInitializer | UmbExtensionElementInitializer;
2425

2526
@state()
2627
private _permitted?: Array<UmbExtensionElementInitializer>;
2728

29+
@property({ type: Boolean })
30+
single?: boolean;
31+
2832
/**
2933
* The type or types of extensions to render.
3034
* @type {string | string[]}
@@ -77,7 +81,6 @@ export class UmbExtensionSlotElement extends UmbLitElement {
7781
return this.#props;
7882
}
7983
set props(newVal: Record<string, unknown> | undefined) {
80-
// TODO, compare changes since last time. only reset the ones that changed. This might be better done by the controller is self:
8184
this.#props = newVal;
8285
if (this.#extensionsController) {
8386
this.#extensionsController.properties = newVal;
@@ -88,7 +91,7 @@ export class UmbExtensionSlotElement extends UmbLitElement {
8891
@property({ type: String, attribute: 'default-element' })
8992
public defaultElement?: string;
9093

91-
@property()
94+
@property({ attribute: false })
9295
public renderMethod?: (
9396
extension: UmbExtensionElementInitializer,
9497
index: number,
@@ -128,15 +131,17 @@ export class UmbExtensionSlotElement extends UmbLitElement {
128131
override render() {
129132
return this._permitted
130133
? this._permitted.length > 0
131-
? repeat(
132-
this._permitted,
133-
(ext) => ext.alias,
134-
(ext, i) => (this.renderMethod ? this.renderMethod(ext, i) : ext.component),
135-
)
134+
? this.single
135+
? this.#renderExtension(this._permitted[0], 0)
136+
: repeat(this._permitted, (ext) => ext.alias, this.#renderExtension)
136137
: html`<slot></slot>`
137138
: '';
138139
}
139140

141+
#renderExtension = (ext: UmbExtensionElementInitializer, i: number) => {
142+
return this.renderMethod ? this.renderMethod(ext, i) : ext.component;
143+
};
144+
140145
static override styles = css`
141146
:host {
142147
display: contents;

src/packages/core/extension-registry/components/extension-slot/extension-slot.test.ts

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import type { UmbExtensionElementInitializer } from '@umbraco-cms/backoffice/ext
77

88
@customElement('umb-test-extension-slot-manifest-element')
99
class UmbTestExtensionSlotManifestElement extends HTMLElement {}
10+
@customElement('umb-test-extension-slot-manifest-element-2')
11+
class UmbTestExtensionSlotManifestElement2 extends HTMLElement {}
1012

1113
function sleep(timeMs: number) {
1214
return new Promise((resolve) => {
@@ -44,9 +46,21 @@ describe('UmbExtensionSlotElement', () => {
4446
expect(element).to.have.property('filter');
4547
});
4648

49+
it('has a props property', () => {
50+
expect(element).to.have.property('props');
51+
});
52+
4753
it('has a defaultElement property', () => {
4854
expect(element).to.have.property('defaultElement');
4955
});
56+
57+
it('has a renderMethod property', () => {
58+
expect(element).to.have.property('renderMethod');
59+
});
60+
61+
it('has a single property', () => {
62+
expect(element).to.have.property('single');
63+
});
5064
});
5165
});
5266

@@ -57,6 +71,17 @@ describe('UmbExtensionSlotElement', () => {
5771
alias: 'unit-test-ext-slot-element-manifest',
5872
name: 'unit-test-extension',
5973
elementName: 'umb-test-extension-slot-manifest-element',
74+
weight: 200, // first is the heaviest and is therefor rendered first.
75+
meta: {
76+
pathname: 'test/test',
77+
},
78+
});
79+
umbExtensionsRegistry.register({
80+
type: 'dashboard',
81+
alias: 'unit-test-ext-slot-element-manifest-2',
82+
name: 'unit-test-extension-2',
83+
elementName: 'umb-test-extension-slot-manifest-element-2',
84+
weight: 100,
6085
meta: {
6186
pathname: 'test/test',
6287
},
@@ -65,6 +90,7 @@ describe('UmbExtensionSlotElement', () => {
6590

6691
afterEach(async () => {
6792
umbExtensionsRegistry.unregister('unit-test-ext-slot-element-manifest');
93+
umbExtensionsRegistry.unregister('unit-test-ext-slot-element-manifest-2');
6894
});
6995

7096
it('renders a manifest element', async () => {
@@ -73,18 +99,30 @@ describe('UmbExtensionSlotElement', () => {
7399
await sleep(20);
74100

75101
expect(element.shadowRoot!.firstElementChild).to.be.instanceOf(UmbTestExtensionSlotManifestElement);
102+
expect(element.shadowRoot!.childElementCount).to.be.equal(2);
76103
});
77104

78105
it('works with the filtering method', async () => {
79106
element = await fixture(
80107
html`<umb-extension-slot
81108
type="dashboard"
82-
.filter=${(x: ManifestDashboard) => x.alias === 'unit-test-ext-slot-element-manifest'}></umb-extension-slot>`,
109+
.filter=${(x: ManifestDashboard) =>
110+
x.alias === 'unit-test-ext-slot-element-manifest-2'}></umb-extension-slot>`,
83111
);
84112

85113
await sleep(20);
86114

115+
expect(element.shadowRoot!.firstElementChild).to.be.instanceOf(UmbTestExtensionSlotManifestElement2);
116+
expect(element.shadowRoot!.childElementCount).to.be.equal(1);
117+
});
118+
119+
it('works with the single mode', async () => {
120+
element = await fixture(html`<umb-extension-slot type="dashboard" single></umb-extension-slot>`);
121+
122+
await sleep(20);
123+
87124
expect(element.shadowRoot!.firstElementChild).to.be.instanceOf(UmbTestExtensionSlotManifestElement);
125+
expect(element.shadowRoot!.childElementCount).to.be.equal(1);
88126
});
89127

90128
it('use the render method', async () => {
@@ -102,6 +140,7 @@ describe('UmbExtensionSlotElement', () => {
102140
expect(element.shadowRoot!.firstElementChild?.firstElementChild).to.be.instanceOf(
103141
UmbTestExtensionSlotManifestElement,
104142
);
143+
expect(element.shadowRoot!.childElementCount).to.be.equal(1);
105144
});
106145

107146
it('parses the props', async () => {
@@ -117,6 +156,7 @@ describe('UmbExtensionSlotElement', () => {
117156

118157
expect((element.shadowRoot!.firstElementChild as any).testProp).to.be.equal('fooBar');
119158
expect(element.shadowRoot!.firstElementChild).to.be.instanceOf(UmbTestExtensionSlotManifestElement);
159+
expect(element.shadowRoot!.childElementCount).to.be.equal(1);
120160
});
121161
});
122162
});

0 commit comments

Comments
 (0)