Merge pull request #2094 from umbraco/v14/bugfix/mntp-min-max-validation

Bugfix: MNTP min/max validation
This commit is contained in:
Lee Kelleher
2024-07-08 10:18:07 +01:00
committed by GitHub
8 changed files with 180 additions and 138 deletions

View File

@@ -99,6 +99,7 @@ export class UmbPickerInputContext<
content: 'Are you sure you want to remove this item',
confirmLabel: 'Remove',
});
this.#removeItem(unique);
}

View File

@@ -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;
}
}

View File

@@ -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() {

View File

@@ -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;
}
}

View File

@@ -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>`;
}
}

View File

@@ -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 {

View File

@@ -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', () => {

View File

@@ -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;
}
}