Merge branch 'main' into bugfix/workspace-create-routes

This commit is contained in:
Jacob Overgaard
2024-02-28 09:42:12 +01:00
committed by GitHub
11 changed files with 336 additions and 32 deletions

View File

@@ -0,0 +1,216 @@
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,
state,
repeat,
ifDefined,
nothing,
query,
} from '@umbraco-cms/backoffice/external/lit';
import type { UUIComboboxEvent, UUIComboboxElement } from '@umbraco-cms/backoffice/external/uui';
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
import { UmbMediaTypeDetailRepository } from '@umbraco-cms/backoffice/media-type';
import {
UMB_DOCUMENT_TYPE_PICKER_MODAL,
UMB_MEDIA_TYPE_PICKER_MODAL,
UMB_MODAL_MANAGER_CONTEXT,
type UmbModalManagerContext,
} from '@umbraco-cms/backoffice/modal';
interface FieldPickerValue {
alias: string;
label: string;
}
enum FieldType {
MEDIA_TYPE = 'media-type',
DOCUMENT_TYPE = 'document-type',
SYSTEM = 'system',
}
@customElement('umb-field-dropdown-list')
export class UmbFieldDropdownListElement extends UmbLitElement {
@property({ type: Boolean, attribute: 'exclude-media-type', reflect: true })
public excludeMediaType = false;
private _value: FieldPickerValue | undefined;
@property({ type: Object })
public get value(): FieldPickerValue | undefined {
return this._value;
}
public set value(val: FieldPickerValue | undefined) {
const oldVal = this._value;
this._value = val;
this.requestUpdate('value', oldVal);
this.dispatchEvent(new UmbChangeEvent());
}
@state()
private _type?: FieldType;
@state()
private _uniqueName?: string;
@state()
private _unique?: string;
@query('#value')
private _valueElement?: UUIComboboxElement;
#documentTypeDetailRepository = new UmbDocumentTypeDetailRepository(this);
#mediaTypeDetailRepository = new UmbMediaTypeDetailRepository(this);
#modalManager?: UmbModalManagerContext;
@state()
private _customFields: Array<Partial<UmbPropertyTypeModel>> = [];
private _systemFields: Array<Partial<UmbPropertyTypeModel>> = [
{ alias: 'sortOrder', name: this.localize.term('general_sort') },
{ alias: 'updateDate', name: this.localize.term('content_updateDate') },
{ alias: 'updater', name: this.localize.term('content_updatedBy') },
{ alias: 'createDate', name: this.localize.term('content_createDate') },
{ alias: 'owner', name: this.localize.term('content_createBy') },
{ alias: 'published', name: this.localize.term('content_isPublished') },
{ alias: 'contentTypeAlias', name: this.localize.term('content_documentType') },
];
constructor() {
super();
this.consumeContext(UMB_MODAL_MANAGER_CONTEXT, (modalManager) => {
this.#modalManager = modalManager;
});
}
async #getDocumentTypeFields() {
if (!this.#modalManager) return;
const modalContext = this.#modalManager.open(UMB_DOCUMENT_TYPE_PICKER_MODAL, {
data: {
hideTreeRoot: true,
multiple: false,
},
});
const modalValue = await modalContext.onSubmit();
const unique = modalValue.selection[0] ?? '';
const { data } = await this.#documentTypeDetailRepository.requestByUnique(unique);
if (!data) return;
this._unique = data.unique;
this._uniqueName = data.name;
this._customFields = data.properties;
}
async #getMediaTypeFields() {
if (!this.#modalManager) return;
const modalContext = this.#modalManager.open(UMB_MEDIA_TYPE_PICKER_MODAL, {
data: {
hideTreeRoot: true,
multiple: false,
},
});
const modalValue = await modalContext.onSubmit();
const unique = modalValue.selection[0] ?? '';
const { data } = await this.#mediaTypeDetailRepository.requestByUnique(unique);
if (!data) return;
this._unique = data.unique;
this._uniqueName = data.name;
this._customFields = data.properties;
}
#onChange(e: UUIComboboxEvent) {
this._type = (e.composedPath()[0] as UUIComboboxElement).value as FieldType;
this.value = undefined;
if (this._valueElement) this._valueElement.value = '';
switch (this._type) {
case FieldType.DOCUMENT_TYPE:
this.#getDocumentTypeFields();
break;
case FieldType.MEDIA_TYPE:
this.#getMediaTypeFields();
break;
default:
this._uniqueName = '';
this._unique = '';
this._customFields = this._systemFields;
break;
}
}
#onChangeValue(e: UUIComboboxEvent) {
e.stopPropagation();
const alias = (e.composedPath()[0] as UUIComboboxElement).value as FieldType;
this.value = this._customFields.find((field) => field.alias === alias) as FieldPickerValue;
}
render() {
return html`
<uui-combobox id="preview">
<uui-combobox-list @change=${this.#onChange}>
<uui-combobox-list-option value="system">
<strong>${this.localize.term('formSettings_systemFields')}</strong>
</uui-combobox-list-option>
<uui-combobox-list-option value="document-type" display-value=${this.localize.term('content_documentType')}>
<strong> ${this.localize.term('content_documentType')} </strong>
${this.localize.term('defaultdialogs_treepicker')}
</uui-combobox-list-option>
${!this.excludeMediaType
? html`<uui-combobox-list-option
value="media-type"
display-value=${this.localize.term('content_mediatype')}>
<strong> ${this.localize.term('content_mediatype')} </strong>
${this.localize.term('defaultdialogs_treepicker')}
</uui-combobox-list-option>`
: nothing}
</uui-combobox-list>
</uui-combobox>
${this.#renderAliasDropdown()}
`;
}
#renderAliasDropdown() {
if (this._type !== FieldType.SYSTEM && !this._unique) return;
return html`<strong>${this._uniqueName}</strong>
<uui-combobox id="value" value=${ifDefined(this.value?.alias)}>
<uui-combobox-list @change=${this.#onChangeValue}>
${repeat(
this._customFields,
(field) => field.alias,
(field) =>
html`<uui-combobox-list-option value=${ifDefined(field.alias)}>${field.alias}</uui-combobox-list-option>`,
)}
</uui-combobox-list>
</uui-combobox>`;
}
static styles = [
css`
uui-combobox {
width: 100%;
}
strong {
display: block;
}
uui-combobox-list-option {
padding: calc(var(--uui-size-2, 6px) + 1px);
}
`,
];
}
export default UmbFieldDropdownListElement;
declare global {
interface HTMLElementTagNameMap {
'umb-field-dropdown-list': UmbFieldDropdownListElement;
}
}

View File

@@ -0,0 +1,17 @@
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: {
excludeMediaType: false,
},
};

View File

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

View File

@@ -0,0 +1 @@
export * from './field-dropdown-list.element.js';

View File

@@ -7,6 +7,7 @@ export * from './code-block/index.js';
export * from './dropdown/index.js';
export * from './entity-actions-bundle/index.js';
export * from './extension-slot/index.js';
export * from './field-dropdown-list/index.js';
export * from './footer-layout/index.js';
export * from './header-app/index.js';
export * from './history/index.js';

View File

@@ -1,17 +1,17 @@
import { umbPickDocumentVariantModal } from '../modals/pick-document-variant-modal.controller.js';
import { type UmbDocumentDetailRepository, UmbDocumentPublishingRepository } from '../repository/index.js';
import { UmbDocumentDetailRepository, UmbDocumentPublishingRepository } from '../repository/index.js';
import { UmbDocumentVariantState } from '../types.js';
import { UmbLanguageCollectionRepository } from '@umbraco-cms/backoffice/language';
import { UmbEntityActionBase } from '@umbraco-cms/backoffice/entity-action';
import { UmbVariantId } from '@umbraco-cms/backoffice/variant';
export class UmbPublishDocumentEntityAction extends UmbEntityActionBase<UmbDocumentDetailRepository> {
export class UmbPublishDocumentEntityAction extends UmbEntityActionBase<unknown> {
async execute() {
if (!this.repository) throw new Error('Document repository not set');
const languageRepository = new UmbLanguageCollectionRepository(this._host);
const { data: languageData } = await languageRepository.requestCollection({});
const { data: documentData } = await this.repository.requestByUnique(this.unique);
const documentRepository = new UmbDocumentDetailRepository(this._host);
const { data: documentData } = await documentRepository.requestByUnique(this.unique);
if (!documentData) throw new Error('The document was not found');

View File

@@ -1,17 +1,17 @@
import { umbPickDocumentVariantModal } from '../modals/pick-document-variant-modal.controller.js';
import { type UmbDocumentDetailRepository, UmbDocumentPublishingRepository } from '../repository/index.js';
import { UmbDocumentDetailRepository, UmbDocumentPublishingRepository } from '../repository/index.js';
import { UmbDocumentVariantState } from '../types.js';
import { UmbLanguageCollectionRepository } from '@umbraco-cms/backoffice/language';
import { UmbEntityActionBase } from '@umbraco-cms/backoffice/entity-action';
import { UmbVariantId } from '@umbraco-cms/backoffice/variant';
export class UmbUnpublishDocumentEntityAction extends UmbEntityActionBase<UmbDocumentDetailRepository> {
export class UmbUnpublishDocumentEntityAction extends UmbEntityActionBase<unknown> {
async execute() {
if (!this.repository) throw new Error('Document repository not set');
const languageRepository = new UmbLanguageCollectionRepository(this._host);
const { data: languageData } = await languageRepository.requestCollection({});
const { data: documentData } = await this.repository.requestByUnique(this.unique);
const documentRepository = new UmbDocumentDetailRepository(this._host);
const { data: documentData } = await documentRepository.requestByUnique(this.unique);
if (!documentData) throw new Error('The document was not found');

View File

@@ -1,5 +1,5 @@
import type { UmbCollectionBulkActionPermissions } from '../../../core/collection/types.js';
import { UMB_DOCUMENT_DETAIL_REPOSITORY_ALIAS } from '../repository/index.js';
import { UMB_DOCUMENT_DETAIL_REPOSITORY_ALIAS, UMB_DOCUMENT_PUBLISHING_REPOSITORY_ALIAS } from '../repository/index.js';
import { UMB_DOCUMENT_COLLECTION_ALIAS } from '../collection/index.js';
import { UmbDocumentCopyEntityBulkAction } from './copy/copy.action.js';
import { UmbDocumentDeleteEntityBulkAction } from './delete/delete.action.js';
@@ -21,7 +21,7 @@ export const manifests: Array<ManifestEntityBulkAction> = [
api: UmbDocumentPublishEntityBulkAction,
meta: {
label: 'Publish',
repositoryAlias: UMB_DOCUMENT_DETAIL_REPOSITORY_ALIAS,
repositoryAlias: UMB_DOCUMENT_PUBLISHING_REPOSITORY_ALIAS,
},
conditions: [
{
@@ -42,7 +42,7 @@ export const manifests: Array<ManifestEntityBulkAction> = [
api: UmbDocumentUnpublishEntityBulkAction,
meta: {
label: 'Unpublish',
repositoryAlias: UMB_DOCUMENT_DETAIL_REPOSITORY_ALIAS,
repositoryAlias: UMB_DOCUMENT_PUBLISHING_REPOSITORY_ALIAS,
},
conditions: [
{

View File

@@ -1,14 +1,36 @@
import type { UmbDocumentDetailRepository } from '../../repository/index.js';
import type { UmbDocumentPublishingRepository } from '../../repository/index.js';
import { UmbPublishDocumentEntityAction } from '../../entity-actions/publish.action.js';
import type { UmbDocumentVariantOptionModel } from '../../types.js';
import { umbPickDocumentVariantModal } from '../../modals/index.js';
import { UmbEntityBulkActionBase } from '@umbraco-cms/backoffice/entity-bulk-action';
import type { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api';
export class UmbDocumentPublishEntityBulkAction extends UmbEntityBulkActionBase<UmbDocumentDetailRepository> {
constructor(host: UmbControllerHostElement, repositoryAlias: string, selection: Array<string>) {
super(host, repositoryAlias, selection);
}
import { UmbLanguageCollectionRepository } from '@umbraco-cms/backoffice/language';
import { UmbVariantId } from '@umbraco-cms/backoffice/variant';
export class UmbDocumentPublishEntityBulkAction extends UmbEntityBulkActionBase<UmbDocumentPublishingRepository> {
async execute() {
console.log(`execute publish for: ${this.selection}`);
//await this.repository?.publish();
// If there is only one selection, we can refer to the regular publish entity action:
if (this.selection.length === 1) {
const action = new UmbPublishDocumentEntityAction(this._host, '', this.selection[0], '');
await action.execute();
return;
}
if (!this.repository) throw new Error('Document publishing repository not set');
const languageRepository = new UmbLanguageCollectionRepository(this._host);
const { data: languageData } = await languageRepository.requestCollection({});
const options: UmbDocumentVariantOptionModel[] = (languageData?.items ?? []).map((language) => ({
language,
unique: new UmbVariantId(language.unique, null).toString(),
}));
const selectedVariants = await umbPickDocumentVariantModal(this, { type: 'publish', options });
if (selectedVariants.length) {
for (const unique of this.selection) {
await this.repository.publish(unique, selectedVariants);
}
}
}
}

View File

@@ -1,14 +1,36 @@
import type { UmbDocumentDetailRepository } from '../../repository/index.js';
import { UmbUnpublishDocumentEntityAction } from '../../entity-actions/unpublish.action.js';
import { umbPickDocumentVariantModal } from '../../modals/index.js';
import type { UmbDocumentPublishingRepository } from '../../repository/index.js';
import type { UmbDocumentVariantOptionModel } from '../../types.js';
import { UmbEntityBulkActionBase } from '@umbraco-cms/backoffice/entity-bulk-action';
import type { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api';
export class UmbDocumentUnpublishEntityBulkAction extends UmbEntityBulkActionBase<UmbDocumentDetailRepository> {
constructor(host: UmbControllerHostElement, repositoryAlias: string, selection: Array<string>) {
super(host, repositoryAlias, selection);
}
import { UmbLanguageCollectionRepository } from '@umbraco-cms/backoffice/language';
import { UmbVariantId } from '@umbraco-cms/backoffice/variant';
export class UmbDocumentUnpublishEntityBulkAction extends UmbEntityBulkActionBase<UmbDocumentPublishingRepository> {
async execute() {
console.log(`execute unpublish for: ${this.selection}`);
//await this.repository?.unpublish();
// If there is only one selection, we can refer to the regular publish entity action:
if (this.selection.length === 1) {
const action = new UmbUnpublishDocumentEntityAction(this._host, '', this.selection[0], '');
await action.execute();
return;
}
if (!this.repository) throw new Error('Document publishing repository not set');
const languageRepository = new UmbLanguageCollectionRepository(this._host);
const { data: languageData } = await languageRepository.requestCollection({});
const options: UmbDocumentVariantOptionModel[] = (languageData?.items ?? []).map((language) => ({
language,
unique: new UmbVariantId(language.unique, null).toString(),
}));
const selectedVariants = await umbPickDocumentVariantModal(this, { type: 'unpublish', options });
if (selectedVariants.length) {
for (const unique of this.selection) {
await this.repository.unpublish(unique, selectedVariants);
}
}
}
}

View File

@@ -6,6 +6,7 @@ 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 type { UmbFieldDropdownListElement } from '@umbraco-cms/backoffice/components';
@customElement('umb-templating-page-field-builder-modal')
export class UmbTemplatingPageFieldBuilderModalElement extends UmbModalBaseElement<
@@ -36,6 +37,10 @@ export class UmbTemplatingPageFieldBuilderModalElement extends UmbModalBaseEleme
/** TODO: Implement "Choose field" */
#onChangeFieldValue(e: Event) {
this._field = (e.target as UmbFieldDropdownListElement).value?.alias;
}
render() {
return html`
<umb-body-layout headline=${this.localize.term('template_insert')}>
@@ -44,7 +49,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} exclude-media-type></umb-field-dropdown-list>
<uui-label for="page-field-default-value">
<umb-localize key="templateEditor_defaultValue">Default value</umb-localize>