Merge pull request #2094 from umbraco/v14/bugfix/mntp-min-max-validation
Bugfix: MNTP min/max validation
This commit is contained in:
@@ -99,6 +99,7 @@ export class UmbPickerInputContext<
|
||||
content: 'Are you sure you want to remove this item',
|
||||
confirmLabel: 'Remove',
|
||||
});
|
||||
|
||||
this.#removeItem(unique);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,15 +2,17 @@ import { UmbDocumentPickerContext } from './input-document.context.js';
|
||||
import { classMap, css, customElement, html, property, repeat, state } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { splitStringToArray } from '@umbraco-cms/backoffice/utils';
|
||||
import { UmbChangeEvent } from '@umbraco-cms/backoffice/event';
|
||||
import { UmbFormControlMixin } from '@umbraco-cms/backoffice/validation';
|
||||
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
|
||||
import { UmbSorterController } from '@umbraco-cms/backoffice/sorter';
|
||||
import { UMB_WORKSPACE_MODAL } from '@umbraco-cms/backoffice/modal';
|
||||
import { UmbModalRouteRegistrationController } from '@umbraco-cms/backoffice/router';
|
||||
import type { UmbDocumentItemModel } from '@umbraco-cms/backoffice/document';
|
||||
import type { UmbTreeStartNode } from '@umbraco-cms/backoffice/tree';
|
||||
import { UmbFormControlMixin } from '@umbraco-cms/backoffice/validation';
|
||||
|
||||
@customElement('umb-input-document')
|
||||
const elementName = 'umb-input-document';
|
||||
|
||||
@customElement(elementName)
|
||||
export class UmbInputDocumentElement extends UmbFormControlMixin<string | undefined, typeof UmbLitElement>(
|
||||
UmbLitElement,
|
||||
) {
|
||||
@@ -73,7 +75,7 @@ export class UmbInputDocumentElement extends UmbFormControlMixin<string | undefi
|
||||
* @attr
|
||||
* @default
|
||||
*/
|
||||
@property({ type: String, attribute: 'min-message' })
|
||||
@property({ type: String, attribute: 'max-message' })
|
||||
maxMessage = 'This field exceeds the allowed amount of items';
|
||||
|
||||
public set selection(ids: Array<string>) {
|
||||
@@ -124,28 +126,24 @@ export class UmbInputDocumentElement extends UmbFormControlMixin<string | undefi
|
||||
this.addValidator(
|
||||
'rangeUnderflow',
|
||||
() => this.minMessage,
|
||||
() => !!this.min && this.#pickerContext.getSelection().length < this.min,
|
||||
() => !!this.min && this.selection.length < this.min,
|
||||
);
|
||||
|
||||
this.addValidator(
|
||||
'rangeOverflow',
|
||||
() => this.maxMessage,
|
||||
() => !!this.max && this.#pickerContext.getSelection().length > this.max,
|
||||
() => !!this.max && this.selection.length > this.max,
|
||||
);
|
||||
|
||||
this.observe(this.#pickerContext.selection, (selection) => (this.value = selection.join(',')), '_observeSelection');
|
||||
this.observe(this.#pickerContext.selectedItems, (selectedItems) => (this._items = selectedItems), '_observerItems');
|
||||
}
|
||||
|
||||
protected override getFormElement() {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
#isDraft(item: UmbDocumentItemModel) {
|
||||
return item.variants[0]?.state === 'Draft';
|
||||
}
|
||||
|
||||
#pickableFilter: (item: UmbDocumentItemModel) => boolean = (item) => {
|
||||
#pickableFilter = (item: UmbDocumentItemModel): boolean => {
|
||||
if (this.allowedContentTypeIds && this.allowedContentTypeIds.length > 0) {
|
||||
return this.allowedContentTypeIds.includes(item.documentType.unique);
|
||||
}
|
||||
@@ -160,7 +158,7 @@ export class UmbInputDocumentElement extends UmbFormControlMixin<string | undefi
|
||||
});
|
||||
}
|
||||
|
||||
#removeItem(item: UmbDocumentItemModel) {
|
||||
#onRemove(item: UmbDocumentItemModel) {
|
||||
this.#pickerContext.requestRemoveItem(item.unique);
|
||||
}
|
||||
|
||||
@@ -169,7 +167,7 @@ export class UmbInputDocumentElement extends UmbFormControlMixin<string | undefi
|
||||
}
|
||||
|
||||
#renderAddButton() {
|
||||
if (this.max === 1 && this.selection.length >= this.max) return;
|
||||
if (this.selection.length >= this.max) return;
|
||||
return html`
|
||||
<uui-button
|
||||
id="btn-add"
|
||||
@@ -199,7 +197,7 @@ export class UmbInputDocumentElement extends UmbFormControlMixin<string | undefi
|
||||
${this.#renderIcon(item)} ${this.#renderIsTrashed(item)}
|
||||
<uui-action-bar slot="actions">
|
||||
${this.#renderOpenButton(item)}
|
||||
<uui-button @click=${() => this.#removeItem(item)} label=${this.localize.term('general_remove')}></uui-button>
|
||||
<uui-button @click=${() => this.#onRemove(item)} label=${this.localize.term('general_remove')}></uui-button>
|
||||
</uui-action-bar>
|
||||
</uui-ref-node>
|
||||
`;
|
||||
@@ -229,7 +227,7 @@ export class UmbInputDocumentElement extends UmbFormControlMixin<string | undefi
|
||||
static override styles = [
|
||||
css`
|
||||
#btn-add {
|
||||
width: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
uui-ref-node[drag-placeholder] {
|
||||
@@ -243,8 +241,10 @@ export class UmbInputDocumentElement extends UmbFormControlMixin<string | undefi
|
||||
];
|
||||
}
|
||||
|
||||
export { UmbInputDocumentElement as element };
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'umb-input-document': UmbInputDocumentElement;
|
||||
[elementName]: UmbInputDocumentElement;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ export class UmbInputMediaElement extends UmbFormControlMixin<string | undefined
|
||||
* @attr
|
||||
* @default
|
||||
*/
|
||||
@property({ type: String, attribute: 'min-message' })
|
||||
@property({ type: String, attribute: 'max-message' })
|
||||
maxMessage = 'This field exceeds the allowed amount of items';
|
||||
|
||||
public set selection(ids: Array<string>) {
|
||||
@@ -164,20 +164,16 @@ export class UmbInputMediaElement extends UmbFormControlMixin<string | undefined
|
||||
this.addValidator(
|
||||
'rangeUnderflow',
|
||||
() => this.minMessage,
|
||||
() => !!this.min && this.#pickerContext.getSelection().length < this.min,
|
||||
() => !!this.min && this.selection.length < this.min,
|
||||
);
|
||||
this.addValidator(
|
||||
'rangeOverflow',
|
||||
() => this.maxMessage,
|
||||
() => !!this.max && this.#pickerContext.getSelection().length > this.max,
|
||||
() => !!this.max && this.selection.length > this.max,
|
||||
);
|
||||
}
|
||||
|
||||
protected override getFormElement() {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
#pickableFilter: (item: UmbMediaItemModel) => boolean = (item) => {
|
||||
#pickableFilter = (item: UmbMediaItemModel): boolean => {
|
||||
if (this.allowedContentTypeIds && this.allowedContentTypeIds.length > 0) {
|
||||
return this.allowedContentTypeIds.includes(item.mediaType.unique);
|
||||
}
|
||||
@@ -192,8 +188,9 @@ export class UmbInputMediaElement extends UmbFormControlMixin<string | undefined
|
||||
});
|
||||
}
|
||||
|
||||
#onRemove(item: UmbMediaCardItemModel) {
|
||||
this.#pickerContext.requestRemoveItem(item.unique);
|
||||
async #onRemove(item: UmbMediaCardItemModel) {
|
||||
await this.#pickerContext.requestRemoveItem(item.unique);
|
||||
this._cards = this._cards.filter((x) => x.unique !== item.unique);
|
||||
}
|
||||
|
||||
override render() {
|
||||
|
||||
@@ -9,7 +9,9 @@ import { UmbModalRouteRegistrationController } from '@umbraco-cms/backoffice/rou
|
||||
import { UmbSorterController } from '@umbraco-cms/backoffice/sorter';
|
||||
import { UmbFormControlMixin } from '@umbraco-cms/backoffice/validation';
|
||||
|
||||
@customElement('umb-input-member')
|
||||
const elementName = 'umb-input-member';
|
||||
|
||||
@customElement(elementName)
|
||||
export class UmbInputMemberElement extends UmbFormControlMixin<string | undefined, typeof UmbLitElement>(
|
||||
UmbLitElement,
|
||||
) {
|
||||
@@ -72,7 +74,7 @@ export class UmbInputMemberElement extends UmbFormControlMixin<string | undefine
|
||||
* @attr
|
||||
* @default
|
||||
*/
|
||||
@property({ type: String, attribute: 'min-message' })
|
||||
@property({ type: String, attribute: 'max-message' })
|
||||
maxMessage = 'This field exceeds the allowed amount of items';
|
||||
|
||||
public set selection(ids: Array<string>) {
|
||||
@@ -123,24 +125,20 @@ export class UmbInputMemberElement extends UmbFormControlMixin<string | undefine
|
||||
this.addValidator(
|
||||
'rangeUnderflow',
|
||||
() => this.minMessage,
|
||||
() => !!this.min && this.#pickerContext.getSelection().length < this.min,
|
||||
() => !!this.min && this.selection.length < this.min,
|
||||
);
|
||||
|
||||
this.addValidator(
|
||||
'rangeOverflow',
|
||||
() => this.maxMessage,
|
||||
() => !!this.max && this.#pickerContext.getSelection().length > this.max,
|
||||
() => !!this.max && this.selection.length > this.max,
|
||||
);
|
||||
|
||||
this.observe(this.#pickerContext.selection, (selection) => (this.value = selection.join(',')), '_observeSelection');
|
||||
this.observe(this.#pickerContext.selectedItems, (selectedItems) => (this._items = selectedItems), '_observeItems');
|
||||
}
|
||||
|
||||
protected override getFormElement() {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
#pickableFilter: (item: UmbMemberItemModel) => boolean = (item) => {
|
||||
#pickableFilter = (item: UmbMemberItemModel): boolean => {
|
||||
if (this.allowedContentTypeIds && this.allowedContentTypeIds.length > 0) {
|
||||
return this.allowedContentTypeIds.includes(item.memberType.unique);
|
||||
}
|
||||
@@ -154,7 +152,7 @@ export class UmbInputMemberElement extends UmbFormControlMixin<string | undefine
|
||||
});
|
||||
}
|
||||
|
||||
#removeItem(item: UmbMemberItemModel) {
|
||||
#onRemove(item: UmbMemberItemModel) {
|
||||
this.#pickerContext.requestRemoveItem(item.unique);
|
||||
}
|
||||
|
||||
@@ -176,12 +174,14 @@ export class UmbInputMemberElement extends UmbFormControlMixin<string | undefine
|
||||
}
|
||||
|
||||
#renderAddButton() {
|
||||
if (this.max === 1 && this.selection.length >= this.max) return nothing;
|
||||
return html`<uui-button
|
||||
id="btn-add"
|
||||
look="placeholder"
|
||||
@click=${this.#openPicker}
|
||||
label=${this.localize.term('general_choose')}></uui-button>`;
|
||||
if (this.selection.length >= this.max) return nothing;
|
||||
return html`
|
||||
<uui-button
|
||||
id="btn-add"
|
||||
look="placeholder"
|
||||
@click=${this.#openPicker}
|
||||
label=${this.localize.term('general_choose')}></uui-button>
|
||||
`;
|
||||
}
|
||||
|
||||
#renderItem(item: UmbMemberItemModel) {
|
||||
@@ -190,7 +190,7 @@ export class UmbInputMemberElement extends UmbFormControlMixin<string | undefine
|
||||
<uui-ref-node name=${item.name} id=${item.unique}>
|
||||
<uui-action-bar slot="actions">
|
||||
${this.#renderOpenButton(item)}
|
||||
<uui-button @click=${() => this.#removeItem(item)} label=${this.localize.term('general_remove')}></uui-button>
|
||||
<uui-button @click=${() => this.#onRemove(item)} label=${this.localize.term('general_remove')}></uui-button>
|
||||
</uui-action-bar>
|
||||
</uui-ref-node>
|
||||
`;
|
||||
@@ -210,7 +210,7 @@ export class UmbInputMemberElement extends UmbFormControlMixin<string | undefine
|
||||
static override styles = [
|
||||
css`
|
||||
#btn-add {
|
||||
width: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
uui-ref-node[drag-placeholder] {
|
||||
@@ -220,10 +220,10 @@ export class UmbInputMemberElement extends UmbFormControlMixin<string | undefine
|
||||
];
|
||||
}
|
||||
|
||||
export default UmbInputMemberElement;
|
||||
export { UmbInputMemberElement as element };
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'umb-input-member': UmbInputMemberElement;
|
||||
[elementName]: UmbInputMemberElement;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ export class UmbPropertyEditorUIMemberPickerElement extends UmbLitElement implem
|
||||
}
|
||||
|
||||
override render() {
|
||||
return html` <umb-input-member min="0" max="1" .value=${this.value} @change=${this.#onChange}></umb-input-member> `;
|
||||
return html`<umb-input-member min="0" max="1" .value=${this.value} @change=${this.#onChange}></umb-input-member>`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,66 +2,73 @@ import type { UmbContentPickerSource } from '../../types.js';
|
||||
import { css, html, customElement, property } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UmbChangeEvent } from '@umbraco-cms/backoffice/event';
|
||||
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
|
||||
import type { UmbInputDocumentElement } from '@umbraco-cms/backoffice/document';
|
||||
import type { UmbInputMediaElement } from '@umbraco-cms/backoffice/media';
|
||||
import type { UmbInputMemberElement } from '@umbraco-cms/backoffice/member';
|
||||
import type { UmbReferenceByUniqueAndType } from '@umbraco-cms/backoffice/models';
|
||||
import type { UmbTreeStartNode } from '@umbraco-cms/backoffice/tree';
|
||||
import { splitStringToArray } from '@umbraco-cms/backoffice/utils';
|
||||
import { UmbFormControlMixin } from '@umbraco-cms/backoffice/validation';
|
||||
|
||||
const elementName = 'umb-input-content';
|
||||
|
||||
@customElement(elementName)
|
||||
export class UmbInputContentElement extends UmbFormControlMixin<string | undefined, typeof UmbLitElement>(
|
||||
UmbLitElement,
|
||||
) {
|
||||
protected override getFormElement() {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private _type: UmbContentPickerSource['type'] = 'content';
|
||||
@property({ type: Object, attribute: false })
|
||||
@property()
|
||||
public set type(newType: UmbContentPickerSource['type']) {
|
||||
const oldType = this._type;
|
||||
if (newType?.toLowerCase() !== this._type) {
|
||||
this._type = newType?.toLowerCase() as UmbContentPickerSource['type'];
|
||||
const oldType = this.#type;
|
||||
if (newType?.toLowerCase() !== this.#type) {
|
||||
this.#type = newType?.toLowerCase() as UmbContentPickerSource['type'];
|
||||
this.requestUpdate('type', oldType);
|
||||
}
|
||||
}
|
||||
public get type(): UmbContentPickerSource['type'] {
|
||||
return this._type;
|
||||
return this.#type;
|
||||
}
|
||||
#type: UmbContentPickerSource['type'] = 'content';
|
||||
|
||||
@property({ type: Number })
|
||||
min = 0;
|
||||
|
||||
@property({ type: String, attribute: 'min-message' })
|
||||
minMessage = 'This field need more items';
|
||||
|
||||
@property({ type: Number })
|
||||
max = 0;
|
||||
|
||||
@property({ type: String, attribute: 'max-message' })
|
||||
maxMessage = 'This field exceeds the allowed amount of items';
|
||||
|
||||
@property({ type: Object, attribute: false })
|
||||
startNode?: UmbTreeStartNode;
|
||||
|
||||
private _allowedContentTypeIds: Array<string> = [];
|
||||
@property()
|
||||
public set allowedContentTypeIds(value: string) {
|
||||
this._allowedContentTypeIds = value ? value.split(',') : [];
|
||||
this.#allowedContentTypeIds = value ? value.split(',') : [];
|
||||
}
|
||||
public get allowedContentTypeIds(): string {
|
||||
return this._allowedContentTypeIds.join(',');
|
||||
return this.#allowedContentTypeIds.join(',');
|
||||
}
|
||||
#allowedContentTypeIds: Array<string> = [];
|
||||
|
||||
@property({ type: Boolean })
|
||||
showOpenButton?: boolean;
|
||||
|
||||
#entityTypeLookup = { content: 'document', media: 'media', member: 'member' };
|
||||
@property({ type: Array })
|
||||
public set selection(values: Array<UmbReferenceByUniqueAndType>) {
|
||||
this.#selection = values?.map((item) => item.unique) ?? [];
|
||||
}
|
||||
public get selection(): Array<UmbReferenceByUniqueAndType> {
|
||||
return this.#selection.map((id) => ({ type: this.#entityTypeLookup[this.#type], unique: id }));
|
||||
}
|
||||
|
||||
// TODO: to be consistent with other pickers, this should be named `selection` [NL]
|
||||
/** @deprecated Please use `selection` instead. This property will be removed in Umbraco 15. */
|
||||
@property({ type: Array })
|
||||
public set items(items: Array<UmbReferenceByUniqueAndType>) {
|
||||
this.#selection = items?.map((item) => item.unique) ?? [];
|
||||
this.selection = items;
|
||||
}
|
||||
/** @deprecated Please use `selection` instead. This property will be removed in Umbraco 15. */
|
||||
public get items(): Array<UmbReferenceByUniqueAndType> {
|
||||
return this.#selection.map((id) => ({ type: this.#entityTypeLookup[this._type], unique: id }));
|
||||
return this.selection;
|
||||
}
|
||||
|
||||
@property({ type: String })
|
||||
@@ -72,38 +79,22 @@ export class UmbInputContentElement extends UmbFormControlMixin<string | undefin
|
||||
return this.#selection.length > 0 ? this.#selection.join(',') : undefined;
|
||||
}
|
||||
|
||||
#entityTypeLookup = { content: 'document', media: 'media', member: 'member' };
|
||||
|
||||
#selection: Array<string> = [];
|
||||
|
||||
#onChange(event: CustomEvent) {
|
||||
switch (this._type) {
|
||||
case 'content':
|
||||
{
|
||||
const input = event.target as UmbInputDocumentElement;
|
||||
this.#selection = input.selection;
|
||||
this.value = input.selection.join(',');
|
||||
}
|
||||
break;
|
||||
case 'media': {
|
||||
const input = event.target as UmbInputMediaElement;
|
||||
this.#selection = input.selection;
|
||||
this.value = input.selection.join(',');
|
||||
break;
|
||||
}
|
||||
case 'member': {
|
||||
const input = event.target as UmbInputMemberElement;
|
||||
this.#selection = input.selection;
|
||||
this.value = input.selection.join(',');
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
override firstUpdated() {
|
||||
this.addFormControlElement(this.shadowRoot!.querySelector(`umb-input-${this.#entityTypeLookup[this.#type]}`)!);
|
||||
}
|
||||
|
||||
#onChange(event: CustomEvent & { target: { selection: string[] | undefined } }) {
|
||||
this.#selection = event.target.selection ?? [];
|
||||
this.value = this.#selection.join(',');
|
||||
this.dispatchEvent(new UmbChangeEvent());
|
||||
}
|
||||
|
||||
override render() {
|
||||
switch (this._type) {
|
||||
switch (this.#type) {
|
||||
case 'content':
|
||||
return this.#renderDocumentPicker();
|
||||
case 'media':
|
||||
@@ -116,34 +107,46 @@ export class UmbInputContentElement extends UmbFormControlMixin<string | undefin
|
||||
}
|
||||
|
||||
#renderDocumentPicker() {
|
||||
return html`<umb-input-document
|
||||
.selection=${this.#selection}
|
||||
.startNode=${this.startNode}
|
||||
.allowedContentTypeIds=${this._allowedContentTypeIds}
|
||||
.min=${this.min}
|
||||
.max=${this.max}
|
||||
?showOpenButton=${this.showOpenButton}
|
||||
@change=${this.#onChange}></umb-input-document>`;
|
||||
return html`
|
||||
<umb-input-document
|
||||
.selection=${this.#selection}
|
||||
.startNode=${this.startNode}
|
||||
.allowedContentTypeIds=${this.#allowedContentTypeIds}
|
||||
.min=${this.min}
|
||||
.minMessage=${this.minMessage}
|
||||
.max=${this.max}
|
||||
.maxMessage=${this.maxMessage}
|
||||
?showOpenButton=${this.showOpenButton}
|
||||
@change=${this.#onChange}></umb-input-document>
|
||||
`;
|
||||
}
|
||||
|
||||
#renderMediaPicker() {
|
||||
return html`<umb-input-media
|
||||
.selection=${this.#selection}
|
||||
.allowedContentTypeIds=${this._allowedContentTypeIds}
|
||||
.min=${this.min}
|
||||
.max=${this.max}
|
||||
?showOpenButton=${this.showOpenButton}
|
||||
@change=${this.#onChange}></umb-input-media>`;
|
||||
return html`
|
||||
<umb-input-media
|
||||
.selection=${this.#selection}
|
||||
.allowedContentTypeIds=${this.#allowedContentTypeIds}
|
||||
.min=${this.min}
|
||||
.minMessage=${this.minMessage}
|
||||
.max=${this.max}
|
||||
.maxMessage=${this.maxMessage}
|
||||
?showOpenButton=${this.showOpenButton}
|
||||
@change=${this.#onChange}></umb-input-media>
|
||||
`;
|
||||
}
|
||||
|
||||
#renderMemberPicker() {
|
||||
return html`<umb-input-member
|
||||
.selection=${this.#selection}
|
||||
.allowedContentTypeIds=${this._allowedContentTypeIds}
|
||||
.min=${this.min}
|
||||
.max=${this.max}
|
||||
?showOpenButton=${this.showOpenButton}
|
||||
@change=${this.#onChange}></umb-input-member>`;
|
||||
return html`
|
||||
<umb-input-member
|
||||
.selection=${this.#selection}
|
||||
.allowedContentTypeIds=${this.#allowedContentTypeIds}
|
||||
.min=${this.min}
|
||||
.minMessage=${this.minMessage}
|
||||
.max=${this.max}
|
||||
.maxMessage=${this.maxMessage}
|
||||
?showOpenButton=${this.showOpenButton}
|
||||
@change=${this.#onChange}></umb-input-member>
|
||||
`;
|
||||
}
|
||||
|
||||
static override styles = [
|
||||
@@ -156,7 +159,7 @@ export class UmbInputContentElement extends UmbFormControlMixin<string | undefin
|
||||
];
|
||||
}
|
||||
|
||||
export default UmbInputContentElement;
|
||||
export { UmbInputContentElement as element };
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
import { UmbInputContentElement } from './input-content.element.js';
|
||||
import { expect, fixture, html } from '@open-wc/testing';
|
||||
import { type UmbTestRunnerWindow, defaultA11yConfig } from '@umbraco-cms/internal/test-utils';
|
||||
import { defaultA11yConfig } from '@umbraco-cms/internal/test-utils';
|
||||
import type { UmbTestRunnerWindow } from '@umbraco-cms/internal/test-utils';
|
||||
|
||||
import '@umbraco-cms/backoffice/document';
|
||||
|
||||
describe('UmbInputContentElement', () => {
|
||||
let element: UmbInputContentElement;
|
||||
|
||||
beforeEach(async () => {
|
||||
element = await fixture(html` <umb-input-content></umb-input-content> `);
|
||||
element = await fixture(html`<umb-input-content></umb-input-content>`);
|
||||
});
|
||||
|
||||
it('is defined with its own instance', () => {
|
||||
|
||||
@@ -3,25 +3,39 @@ import type { UmbInputContentElement } from './components/input-content/index.js
|
||||
import type { UmbContentPickerSource, UmbContentPickerSourceType } from './types.js';
|
||||
import { html, customElement, property, state } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
|
||||
import { UmbFormControlMixin } from '@umbraco-cms/backoffice/validation';
|
||||
import { UmbPropertyValueChangeEvent } from '@umbraco-cms/backoffice/property-editor';
|
||||
import { UMB_ENTITY_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace';
|
||||
import type { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor';
|
||||
import type { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry';
|
||||
import { UMB_DOCUMENT_ENTITY_TYPE } from '@umbraco-cms/backoffice/document';
|
||||
import { UMB_ENTITY_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace';
|
||||
import { UMB_MEDIA_ENTITY_TYPE } from '@umbraco-cms/backoffice/media';
|
||||
import { UMB_MEMBER_ENTITY_TYPE } from '@umbraco-cms/backoffice/member';
|
||||
import type { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor';
|
||||
import type { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry';
|
||||
import type { UmbTreeStartNode } from '@umbraco-cms/backoffice/tree';
|
||||
|
||||
// import of local component
|
||||
import './components/input-content/index.js';
|
||||
|
||||
type UmbContentPickerValueType = UmbInputContentElement['selection'];
|
||||
|
||||
const elementName = 'umb-property-editor-ui-content-picker';
|
||||
|
||||
/**
|
||||
* @element umb-property-editor-ui-content-picker
|
||||
*/
|
||||
@customElement('umb-property-editor-ui-content-picker')
|
||||
export class UmbPropertyEditorUIContentPickerElement extends UmbLitElement implements UmbPropertyEditorUiElement {
|
||||
@customElement(elementName)
|
||||
export class UmbPropertyEditorUIContentPickerElement
|
||||
extends UmbFormControlMixin<UmbContentPickerValueType | undefined, typeof UmbLitElement>(UmbLitElement, undefined)
|
||||
implements UmbPropertyEditorUiElement
|
||||
{
|
||||
@property({ type: Array })
|
||||
value: UmbInputContentElement['items'] = [];
|
||||
public override set value(value: UmbContentPickerValueType | undefined) {
|
||||
this.#value = value;
|
||||
}
|
||||
public override get value(): UmbContentPickerValueType | undefined {
|
||||
return this.#value;
|
||||
}
|
||||
#value?: UmbContentPickerValueType = [];
|
||||
|
||||
@state()
|
||||
_type: UmbContentPickerSource['type'] = 'content';
|
||||
@@ -29,9 +43,15 @@ export class UmbPropertyEditorUIContentPickerElement extends UmbLitElement imple
|
||||
@state()
|
||||
_min = 0;
|
||||
|
||||
@state()
|
||||
_minMessage = '';
|
||||
|
||||
@state()
|
||||
_max = Infinity;
|
||||
|
||||
@state()
|
||||
_maxMessage = '';
|
||||
|
||||
@state()
|
||||
_allowedContentTypeUniques?: string | null;
|
||||
|
||||
@@ -64,15 +84,28 @@ export class UmbPropertyEditorUIContentPickerElement extends UmbLitElement imple
|
||||
this.#dynamicRoot = startNode.dynamicRoot;
|
||||
}
|
||||
|
||||
this._min = Number(config.getValueByAlias('minNumber')) || 0;
|
||||
this._max = Number(config.getValueByAlias('maxNumber')) || Infinity;
|
||||
this._min = this.#parseInt(config.getValueByAlias('minNumber'), 0);
|
||||
this._max = this.#parseInt(config.getValueByAlias('maxNumber'), Infinity);
|
||||
|
||||
this._allowedContentTypeUniques = config.getValueByAlias('filter');
|
||||
this._showOpenButton = config.getValueByAlias('showOpenButton');
|
||||
|
||||
this._minMessage = `${this.localize.term('validation_minCount')} ${this._min} ${this.localize.term('validation_items')}`;
|
||||
this._maxMessage = `${this.localize.term('validation_maxCount')} ${this._max} ${this.localize.term('validation_itemsSelected')}`;
|
||||
|
||||
// NOTE: Run validation immediately, to notify if the value is outside of min/max range. [LK]
|
||||
if (this._min > 0 || this._max < Infinity) {
|
||||
this.checkValidity();
|
||||
}
|
||||
}
|
||||
|
||||
override connectedCallback() {
|
||||
super.connectedCallback();
|
||||
#parseInt(value: unknown, fallback: number): number {
|
||||
const num = Number(value);
|
||||
return !isNaN(num) && num > 0 ? num : fallback;
|
||||
}
|
||||
|
||||
override firstUpdated() {
|
||||
this.addFormControlElement(this.shadowRoot!.querySelector('umb-input-content')!);
|
||||
this.#setPickerRootUnique();
|
||||
}
|
||||
|
||||
@@ -96,7 +129,7 @@ export class UmbPropertyEditorUIContentPickerElement extends UmbLitElement imple
|
||||
}
|
||||
|
||||
#onChange(event: CustomEvent & { target: UmbInputContentElement }) {
|
||||
this.value = event.target.items;
|
||||
this.value = event.target.selection;
|
||||
this.dispatchEvent(new UmbPropertyValueChangeEvent());
|
||||
}
|
||||
|
||||
@@ -106,22 +139,26 @@ export class UmbPropertyEditorUIContentPickerElement extends UmbLitElement imple
|
||||
? { unique: this._rootUnique, entityType: this._rootEntityType }
|
||||
: undefined;
|
||||
|
||||
return html`<umb-input-content
|
||||
.items=${this.value}
|
||||
.type=${this._type}
|
||||
.min=${this._min}
|
||||
.max=${this._max}
|
||||
.startNode=${startNode}
|
||||
.allowedContentTypeIds=${this._allowedContentTypeUniques ?? ''}
|
||||
?showOpenButton=${this._showOpenButton}
|
||||
@change=${this.#onChange}></umb-input-content>`;
|
||||
return html`
|
||||
<umb-input-content
|
||||
.selection=${this.value ?? []}
|
||||
.type=${this._type}
|
||||
.min=${this._min}
|
||||
.minMessage=${this._minMessage}
|
||||
.max=${this._max}
|
||||
.maxMessage=${this._maxMessage}
|
||||
.startNode=${startNode}
|
||||
.allowedContentTypeIds=${this._allowedContentTypeUniques ?? ''}
|
||||
?showOpenButton=${this._showOpenButton}
|
||||
@change=${this.#onChange}></umb-input-content>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
export default UmbPropertyEditorUIContentPickerElement;
|
||||
export { UmbPropertyEditorUIContentPickerElement as element };
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'umb-property-editor-ui-content-picker': UmbPropertyEditorUIContentPickerElement;
|
||||
[elementName]: UmbPropertyEditorUIContentPickerElement;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user