diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/components/field-list-input/field-dropdown-list.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/components/field-list-input/field-dropdown-list.element.ts new file mode 100644 index 0000000000..627eb7e158 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/components/field-list-input/field-dropdown-list.element.ts @@ -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(''); + 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 = []; + + @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` + + + + + ${this.documentTypeUnique + ? repeat( + this._customFields, + (item) => item.id, + (item) => + html`${item.alias}`, + ) + : repeat( + this._systemFields, + (item) => item.value, + (item) => + html`${item.name}`, + )} + + + + `; + } + + 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; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/components/field-list-input/field-dropdown-list.stories.ts b/src/Umbraco.Web.UI.Client/src/packages/core/components/field-list-input/field-dropdown-list.stories.ts new file mode 100644 index 0000000000..3f689947cd --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/components/field-list-input/field-dropdown-list.stories.ts @@ -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 = { + title: 'Components/Inputs/Field Dropdown List', + component: 'umb-field-dropdown-list', +}; + +export default meta; +type Story = StoryObj; + +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', + }, + ], + }, +}; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/components/field-list-input/field-dropdown-list.test.ts b/src/Umbraco.Web.UI.Client/src/packages/core/components/field-list-input/field-dropdown-list.test.ts new file mode 100644 index 0000000000..2f741562a7 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/components/field-list-input/field-dropdown-list.test.ts @@ -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` `); + }); + + 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); + }); + } +}); diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/components/field-list-input/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/components/field-list-input/index.ts new file mode 100644 index 0000000000..2864ccf8fc --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/components/field-list-input/index.ts @@ -0,0 +1 @@ +export * from './field-dropdown-list.element.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/components/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/components/index.ts index 50727337dc..27c0026b8e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/components/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/components/index.ts @@ -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'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/modals/templating-page-field-builder/templating-page-field-builder-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/modals/templating-page-field-builder/templating-page-field-builder-modal.element.ts index 37afe148ca..de2866283f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/modals/templating-page-field-builder/templating-page-field-builder-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/modals/templating-page-field-builder/templating-page-field-builder-modal.element.ts @@ -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` @@ -44,7 +50,7 @@ export class UmbTemplatingPageFieldBuilderModalElement extends UmbModalBaseEleme Choose field - (Not implemented yet) + Default value diff --git a/src/Umbraco.Web.UI.Client/src/packages/umbraco-news/umbraco-news-dashboard.element.ts b/src/Umbraco.Web.UI.Client/src/packages/umbraco-news/umbraco-news-dashboard.element.ts index e22db293ed..4d03dfacd4 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/umbraco-news/umbraco-news-dashboard.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/umbraco-news/umbraco-news-dashboard.element.ts @@ -28,6 +28,7 @@ export class UmbUmbracoNewsDashboardElement extends UmbLitElement { render() { return html` +

Welcome, ${this.name}

This is a preview version of Umbraco, where you can have a first-hand look at the new Backoffice.