Feature: System and DT fields component
This commit is contained in:
@@ -0,0 +1,144 @@
|
||||
import type { UmbPropertyTypeModel } from '@umbraco-cms/backoffice/content-type';
|
||||
import { UmbDocumentTypeDetailRepository } from '@umbraco-cms/backoffice/document-type';
|
||||
import { UmbChangeEvent } from '@umbraco-cms/backoffice/event';
|
||||
import { css, html, customElement, property, repeat, state, query } from '@umbraco-cms/backoffice/external/lit';
|
||||
import type { UUIComboboxElement, UUIComboboxEvent } from '@umbraco-cms/backoffice/external/uui';
|
||||
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
|
||||
import { UMB_DOCUMENT_TYPE_PICKER_MODAL, UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal';
|
||||
import { UmbStringState } from '@umbraco-cms/backoffice/observable-api';
|
||||
|
||||
@customElement('umb-field-dropdown-list')
|
||||
export class UmbFieldDropdownListElement extends UmbLitElement {
|
||||
@property({ type: String })
|
||||
public value = '';
|
||||
|
||||
#documentTypeDetailRepository = new UmbDocumentTypeDetailRepository(this);
|
||||
|
||||
#unique = new UmbStringState<string>('');
|
||||
readonly unique = this.#unique.asObservable();
|
||||
|
||||
@property({ type: String })
|
||||
set documentTypeUnique(value: string) {
|
||||
this.#unique.setValue(value);
|
||||
}
|
||||
get documentTypeUnique(): string | null | undefined {
|
||||
return this.#unique.getValue();
|
||||
}
|
||||
|
||||
@state()
|
||||
private _documentTypeName?: string;
|
||||
|
||||
@state()
|
||||
private _documentTypeIcon?: string;
|
||||
|
||||
@state()
|
||||
private _customFields: Array<UmbPropertyTypeModel> = [];
|
||||
|
||||
@query('uui-combobox')
|
||||
private _combobox!: UUIComboboxElement;
|
||||
|
||||
private _systemFields = [
|
||||
{ value: 'sortOrder', name: this.localize.term('general_sort'), group: 'System Fields' },
|
||||
{ value: 'updateDate', name: this.localize.term('content_updateDate'), group: 'System Fields' },
|
||||
{ value: 'updater', name: this.localize.term('content_updatedBy'), group: 'System Fields' },
|
||||
{ value: 'createDate', name: this.localize.term('content_createDate'), group: 'System Fields' },
|
||||
{ value: 'owner', name: this.localize.term('content_createBy'), group: 'System Fields' },
|
||||
{ value: 'published', name: this.localize.term('content_isPublished'), group: 'System Fields' },
|
||||
{ value: 'contentTypeAlias', name: this.localize.term('content_documentType'), group: 'System Fields' },
|
||||
{ value: 'email', name: this.localize.term('general_email'), group: 'System Fields' },
|
||||
{ value: 'username', name: this.localize.term('general_username'), group: 'System Fields' },
|
||||
];
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.observe(this.unique, async (unique) => {
|
||||
if (unique) {
|
||||
const { data } = await this.#documentTypeDetailRepository.requestByUnique(unique);
|
||||
this._customFields = data?.properties ?? [];
|
||||
this._documentTypeName = data?.name;
|
||||
this._documentTypeIcon = data?.icon;
|
||||
} else {
|
||||
this._customFields = [];
|
||||
this._documentTypeIcon = undefined;
|
||||
this._documentTypeName = undefined;
|
||||
}
|
||||
this.value = '';
|
||||
this.dispatchEvent(new UmbChangeEvent());
|
||||
});
|
||||
}
|
||||
|
||||
#changeFieldType() {
|
||||
this.consumeContext(UMB_MODAL_MANAGER_CONTEXT, async (modalManager) => {
|
||||
if (modalManager) {
|
||||
// TODO: Make as mode for the Picker Modal, so the click to select immediately submits the modal(And in that mode we do not want to see a Submit button).
|
||||
const modalContext = modalManager.open(UMB_DOCUMENT_TYPE_PICKER_MODAL, {
|
||||
data: {
|
||||
hideTreeRoot: true,
|
||||
multiple: false,
|
||||
},
|
||||
value: {
|
||||
selection: this.documentTypeUnique ? [this.documentTypeUnique] : [],
|
||||
},
|
||||
});
|
||||
|
||||
const modalValue = await modalContext?.onSubmit();
|
||||
this.documentTypeUnique = modalValue.selection[0] ?? '';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#onChange(e: UUIComboboxEvent) {
|
||||
e.stopPropagation();
|
||||
const alias = (e.composedPath()[0] as UUIComboboxElement).value as string;
|
||||
this.value = alias;
|
||||
this.dispatchEvent(new UmbChangeEvent());
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<uui-ref-node
|
||||
@open=${this.#changeFieldType}
|
||||
.name=${this._documentTypeName ?? 'System Field'}
|
||||
@change=${this.#onChange}>
|
||||
<uui-icon name=${this._documentTypeIcon ?? 'icon-database'} slot="icon"></uui-icon>
|
||||
<uui-combobox slot="tag" .value=${this.value}>
|
||||
<uui-combobox-list>
|
||||
${this.documentTypeUnique
|
||||
? repeat(
|
||||
this._customFields,
|
||||
(item) => item.id,
|
||||
(item) =>
|
||||
html`<uui-combobox-list-option .value=${item.alias}>${item.alias}</uui-combobox-list-option>`,
|
||||
)
|
||||
: repeat(
|
||||
this._systemFields,
|
||||
(item) => item.value,
|
||||
(item) =>
|
||||
html`<uui-combobox-list-option .value=${item.value}>${item.name}</uui-combobox-list-option>`,
|
||||
)}
|
||||
</uui-combobox-list>
|
||||
</uui-combobox>
|
||||
</uui-ref-node>
|
||||
`;
|
||||
}
|
||||
|
||||
static styles = [
|
||||
css`
|
||||
uui-ref-node {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
uui-combobox-list-option {
|
||||
padding: calc(var(--uui-size-2, 6px) + 1px);
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
export default UmbFieldDropdownListElement;
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'umb-field-dropdown-list': UmbFieldDropdownListElement;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
import type { Meta, StoryObj } from '@storybook/web-components';
|
||||
import './field-dropdown-list.element.js';
|
||||
import type { UmbFieldDropdownListElement } from './field-dropdown-list.element.js';
|
||||
|
||||
const meta: Meta<UmbFieldDropdownListElement> = {
|
||||
title: 'Components/Inputs/Field Dropdown List',
|
||||
component: 'umb-field-dropdown-list',
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<UmbFieldDropdownListElement>;
|
||||
|
||||
export const Overview: Story = {
|
||||
args: {
|
||||
options: [
|
||||
{
|
||||
name: 'One',
|
||||
value: 'One',
|
||||
},
|
||||
{
|
||||
name: 'Two',
|
||||
value: 'Two',
|
||||
},
|
||||
{
|
||||
name: 'Three',
|
||||
value: 'Three',
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
export const WithSelectedValue: Story = {
|
||||
args: {
|
||||
options: [
|
||||
{
|
||||
name: 'One',
|
||||
value: 'One',
|
||||
},
|
||||
{
|
||||
name: 'Two',
|
||||
value: 'Two',
|
||||
selected: true,
|
||||
},
|
||||
{
|
||||
name: 'Three',
|
||||
value: 'Three',
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,20 @@
|
||||
import { expect, fixture, html } from '@open-wc/testing';
|
||||
import { UmbFieldDropdownListElement } from './field-dropdown-list.element.js';
|
||||
import { type UmbTestRunnerWindow, defaultA11yConfig } from '@umbraco-cms/internal/test-utils';
|
||||
describe('UmbInputDateElement', () => {
|
||||
let element: UmbFieldDropdownListElement;
|
||||
|
||||
beforeEach(async () => {
|
||||
element = await fixture(html` <umb-field-dropdown-list></umb-field-dropdown-list> `);
|
||||
});
|
||||
|
||||
it('is defined with its own instance', () => {
|
||||
expect(element).to.be.instanceOf(UmbFieldDropdownListElement);
|
||||
});
|
||||
|
||||
if ((window as UmbTestRunnerWindow).__UMBRACO_TEST_RUN_A11Y_TEST) {
|
||||
it('passes the a11y audit', async () => {
|
||||
await expect(element).shadowDom.to.be.accessible(defaultA11yConfig);
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1 @@
|
||||
export * from './field-dropdown-list.element.js';
|
||||
@@ -8,6 +8,7 @@ export * from './dropdown/index.js';
|
||||
export * from './empty-state/index.js';
|
||||
export * from './entity-actions-bundle/index.js';
|
||||
export * from './extension-slot/index.js';
|
||||
export * from './field-list-input/index.js';
|
||||
export * from './footer-layout/index.js';
|
||||
export * from './header-app/index.js';
|
||||
export * from './history/index.js';
|
||||
|
||||
@@ -6,6 +6,8 @@ import type {
|
||||
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
|
||||
import { css, html, customElement, state } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UmbModalBaseElement } from '@umbraco-cms/backoffice/modal';
|
||||
import { UmbChangeEvent } from '@umbraco-cms/backoffice/event';
|
||||
import { UmbFieldDropdownListElement } from '@umbraco-cms/backoffice/components';
|
||||
|
||||
@customElement('umb-templating-page-field-builder-modal')
|
||||
export class UmbTemplatingPageFieldBuilderModalElement extends UmbModalBaseElement<
|
||||
@@ -36,6 +38,10 @@ export class UmbTemplatingPageFieldBuilderModalElement extends UmbModalBaseEleme
|
||||
|
||||
/** TODO: Implement "Choose field" */
|
||||
|
||||
#onChangeFieldValue(e: Event) {
|
||||
this._field = (e.target as UmbFieldDropdownListElement).value;
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<umb-body-layout headline=${this.localize.term('template_insert')}>
|
||||
@@ -44,7 +50,7 @@ export class UmbTemplatingPageFieldBuilderModalElement extends UmbModalBaseEleme
|
||||
<uui-label for="page-field-value">
|
||||
<umb-localize key="templateEditor_chooseField">Choose field</umb-localize>
|
||||
</uui-label>
|
||||
(Not implemented yet)
|
||||
<umb-field-dropdown-list @change=${this.#onChangeFieldValue}></umb-field-dropdown-list>
|
||||
|
||||
<uui-label for="page-field-default-value">
|
||||
<umb-localize key="templateEditor_defaultValue">Default value</umb-localize>
|
||||
|
||||
@@ -28,6 +28,7 @@ export class UmbUmbracoNewsDashboardElement extends UmbLitElement {
|
||||
render() {
|
||||
return html`
|
||||
<uui-box class="uui-text">
|
||||
<umb-field-dropdown-list></umb-field-dropdown-list>
|
||||
<h1 class="uui-h2" style="margin-top: var(--uui-size-layout-1);">Welcome, ${this.name}</h1>
|
||||
<p class="uui-lead">
|
||||
This is a preview version of Umbraco, where you can have a first-hand look at the new Backoffice.
|
||||
|
||||
Reference in New Issue
Block a user