Skip to content

Commit

Permalink
Merge pull request #2200 from umbraco/v14/feature/document-validation…
Browse files Browse the repository at this point in the history
…-take1

Document Validation
  • Loading branch information
nielslyngsoe authored Aug 19, 2024
2 parents c684fb0 + 5f98e1b commit 3cefb9c
Show file tree
Hide file tree
Showing 81 changed files with 1,686 additions and 460 deletions.
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"umbraco",
"Uncategorized",
"uninitialize",
"unprovide",
"variantable"
],
"exportall.config.folderListener": [],
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
import { css, html, customElement, LitElement } from '@umbraco-cms/backoffice/external/lit';
import { UmbElementMixin } from '@umbraco-cms/backoffice/element-api';
import { UmbPropertyValueData, type UmbPropertyDatasetElement } from '@umbraco-cms/backoffice/property';
import { type UmbPropertyValueData, type UmbPropertyDatasetElement } from '@umbraco-cms/backoffice/property';

@customElement('example-dataset-dashboard')
export class ExampleDatasetDashboard extends UmbElementMixin(LitElement) {
Expand Down
2 changes: 1 addition & 1 deletion examples/sorter-with-nested-containers/sorter-group.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
import { css, html, customElement, LitElement, repeat, property } from '@umbraco-cms/backoffice/external/lit';
import { UmbElementMixin } from '@umbraco-cms/backoffice/element-api';
import { UmbSorterConfig, UmbSorterController } from '@umbraco-cms/backoffice/sorter';
import { UmbSorterController } from '@umbraco-cms/backoffice/sorter';

import './sorter-item.js';
import ExampleSorterItem from './sorter-item.js';
Expand Down
2 changes: 1 addition & 1 deletion src/external/lit/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
export * from 'lit';
export * from 'lit/decorators.js';
export { directive, AsyncDirective } from 'lit/async-directive.js';
export { directive, AsyncDirective, type PartInfo } from 'lit/async-directive.js';
export * from 'lit/directives/class-map.js';
export * from 'lit/directives/if-defined.js';
export * from 'lit/directives/map.js';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { UmbBlockGridEntriesContext } from '../../context/block-grid-entries.context.js';
import type { UmbBlockGridEntryElement } from '../block-grid-entry/index.js';
import {
getAccumulatedValueOfIndex,
getInterpolatedIndexOfPositionInWeightMap,
Expand All @@ -14,13 +16,12 @@ import {
type UmbFormControlValidatorConfig,
} from '@umbraco-cms/backoffice/validation';
import type { UmbNumberRangeValueType } from '@umbraco-cms/backoffice/models';
import { UmbBlockGridEntriesContext } from '../../context/block-grid-entries.context.js';
import type { UmbBlockGridEntryElement } from '../block-grid-entry/index.js';
import type { UmbBlockGridLayoutModel } from '@umbraco-cms/backoffice/block-grid';

/**
* Notice this utility method is not really shareable with others as it also takes areas into account. [NL]
* @param args
* @returns { null | true }
*/
function resolvePlacementAsGrid(args: resolvePlacementArgs<UmbBlockGridLayoutModel, UmbBlockGridEntryElement>) {
// If this has areas, we do not want to move, unless we are at the edge
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,20 @@ export class UmbBlockGridEntriesContext
return this.#layoutColumns.getValue();
}

getMinAllowed() {
if (this.#areaKey) {
return this.#areaType?.minAllowed ?? 0;
}
return this._manager?.getMinAllowed() ?? 0;
}

getMaxAllowed() {
if (this.#areaKey) {
return this.#areaType?.maxAllowed ?? Infinity;
}
return this._manager?.getMaxAllowed() ?? Infinity;
}

getLayoutContainerElement() {
return this.getHostElement().shadowRoot?.querySelector('.umb-block-grid__layout-container') as
| HTMLElement
Expand Down Expand Up @@ -131,7 +145,7 @@ export class UmbBlockGridEntriesContext
data: {
entityType: 'block',
preset: {},
originData: { areaKey: this.#areaKey, parentUnique: this.#parentUnique },
originData: { areaKey: this.#areaKey, parentUnique: this.#parentUnique, baseDataPath: this._dataPath },
},
modal: { size: 'medium' },
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { UMB_APP_CONTEXT } from '@umbraco-cms/backoffice/app';
import type { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor';
import { type UmbBlockDataType, UmbBlockManagerContext } from '@umbraco-cms/backoffice/block';
import type { UmbBlockTypeGroup } from '@umbraco-cms/backoffice/block-type';
import type { UmbNumberRangeValueType } from '@umbraco-cms/backoffice/models';

export const UMB_BLOCK_GRID_DEFAULT_LAYOUT_STYLESHEET = '/umbraco/backoffice/css/umbraco-blockgridlayout.css';

Expand Down Expand Up @@ -38,6 +39,16 @@ export class UmbBlockGridManagerContext<
return parseInt(value && value !== '' ? value : '12');
});

getMinAllowed() {
return this._editorConfiguration.getValue()?.getValueByAlias<UmbNumberRangeValueType>('validationLimit')?.min ?? 0;
}

getMaxAllowed() {
return (
this._editorConfiguration.getValue()?.getValueByAlias<UmbNumberRangeValueType>('validationLimit')?.max ?? Infinity
);
}

override setEditorConfiguration(configs: UmbPropertyEditorConfigCollection) {
this.#initAppUrl.then(() => {
// we await initAppUrl, So the appUrl begin here is available when retrieving the layoutStylesheet.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,10 +127,7 @@ export class UmbPropertyEditorUIBlockGridAreasConfigElement
.key=${area.key}></umb-block-area-config-entry>`,
)}
</div>
<uui-button
look="placeholder"
label=${'Add area'}
href=${this._workspacePath + 'create'}></uui-button>`
<uui-button look="placeholder" label=${'Add area'} href=${this._workspacePath + 'create'}></uui-button>`
: '';
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,12 @@ export const UMB_BLOCK_GRID_WORKSPACE_MODAL = new UmbModalToken<UmbBlockGridWork
type: 'sidebar',
size: 'medium',
},
data: { entityType: 'block', preset: {}, originData: { index: -1, parentUnique: null } },
data: {
entityType: 'block',
preset: {},
originData: { index: -1, parentUnique: null },
baseDataPath: undefined as unknown as string,
},
},
// Recast the type, so the entityType data prop is not required:
) as UmbModalToken<Omit<UmbWorkspaceModalData, 'entityType'>, UmbWorkspaceModalValue>;
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import '../inline-list-block/index.js';
import { stringOrStringArrayContains } from '@umbraco-cms/backoffice/utils';
import { UmbBlockListEntryContext } from '../../context/block-list-entry.context.js';
import { UMB_BLOCK_LIST, type UmbBlockListLayoutModel } from '../../types.js';
import { UmbObserveValidationStateController } from '@umbraco-cms/backoffice/validation';
import { UmbDataPathBlockElementDataQuery } from '@umbraco-cms/backoffice/block';

/**
* @element umb-block-list-entry
Expand All @@ -33,6 +35,16 @@ export class UmbBlockListEntryElement extends UmbLitElement implements UmbProper
if (!value) return;
this._contentUdi = value;
this.#context.setContentUdi(value);

new UmbObserveValidationStateController(
this,
`$.contentData[${UmbDataPathBlockElementDataQuery({ udi: value })}]`,
(hasMessages) => {
this._contentInvalid = hasMessages;
this._blockViewProps.contentInvalid = hasMessages;
},
'observeMessagesForContent',
);
}
private _contentUdi?: string | undefined;

Expand Down Expand Up @@ -61,6 +73,14 @@ export class UmbBlockListEntryElement extends UmbLitElement implements UmbProper
@state()
_inlineEditingMode?: boolean;

// 'content-invalid' attribute is used for styling purpose.
@property({ type: Boolean, attribute: 'content-invalid', reflect: true })
_contentInvalid?: boolean;

// 'settings-invalid' attribute is used for styling purpose.
@property({ type: Boolean, attribute: 'settings-invalid', reflect: true })
_settingsInvalid?: boolean;

@state()
_blockViewProps: UmbBlockEditorCustomViewProperties<UmbBlockListLayoutModel> = {
contentUdi: undefined!,
Expand Down Expand Up @@ -141,6 +161,20 @@ export class UmbBlockListEntryElement extends UmbLitElement implements UmbProper
this.#context.settings,
(settings) => {
this.#updateBlockViewProps({ settings });

this.removeUmbControllerByAlias('observeMessagesForSettings');
if (settings) {
// Observe settings validation state:
new UmbObserveValidationStateController(
this,
`$.settingsData[${UmbDataPathBlockElementDataQuery(settings)}]`,
(hasMessages) => {
this._settingsInvalid = hasMessages;
this._blockViewProps.settingsInvalid = hasMessages;
},
'observeMessagesForSettings',
);
}
},
null,
);
Expand Down Expand Up @@ -218,16 +252,30 @@ export class UmbBlockListEntryElement extends UmbLitElement implements UmbProper
>
<uui-action-bar>
${this._showContentEdit && this._workspaceEditContentPath
? html`<uui-button label="edit" compact href=${this._workspaceEditContentPath}>
? html`<uui-button
label="edit"
look="secondary"
color=${this._contentInvalid ? 'danger' : ''}
href=${this._workspaceEditContentPath}>
<uui-icon name="icon-edit"></uui-icon>
${this._contentInvalid
? html`<uui-badge attention color="danger" label="Invalid settings">!</uui-badge>`
: ''}
</uui-button>`
: ''}
${this._hasSettings && this._workspaceEditSettingsPath
? html`<uui-button label="Edit settings" compact href=${this._workspaceEditSettingsPath}>
? html`<uui-button
label="Edit settings"
look="secondary"
color=${this._settingsInvalid ? 'danger' : ''}
href=${this._workspaceEditSettingsPath}>
<uui-icon name="icon-settings"></uui-icon>
${this._settingsInvalid
? html`<uui-badge attention color="danger" label="Invalid settings">!</uui-badge>`
: ''}
</uui-button>`
: ''}
<uui-button label="delete" compact @click=${() => this.#context.requestDelete()}>
<uui-button label="delete" look="secondary" @click=${() => this.#context.requestDelete()}>
<uui-icon name="icon-remove"></uui-icon>
</uui-button>
</uui-action-bar>
Expand All @@ -243,15 +291,41 @@ export class UmbBlockListEntryElement extends UmbLitElement implements UmbProper
:host {
position: relative;
display: block;
--umb-block-list-entry-actions-opacity: 0;
}
:host([settings-invalid]),
:host([content-invalid]),
:host(:hover),
:host(:focus-within) {
--umb-block-list-entry-actions-opacity: 1;
}
uui-action-bar {
position: absolute;
top: var(--uui-size-2);
right: var(--uui-size-2);
opacity: var(--umb-block-list-entry-actions-opacity, 0);
transition: opacity 120ms;
}
:host([drag-placeholder]) {
opacity: 0.2;
--umb-block-list-entry-actions-opacity: 0;
}
:host([settings-invalid])::after,
:host([content-invalid])::after {
content: '';
position: absolute;
inset: 0;
pointer-events: none;
border: 1px solid var(--uui-color-danger);
border-radius: var(--uui-border-radius);
}
uui-badge {
z-index: 2;
}
`,
];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export class UmbRefListBlockElement extends UmbLitElement {
@state()
_content?: UmbBlockDataType;

@state()
@property()
_workspaceEditPath?: string;

constructor() {
Expand All @@ -44,6 +44,7 @@ export class UmbRefListBlockElement extends UmbLitElement {
}

override render() {
// TODO: apply `slot="name"` to the `umb-ufm-render` element, when UUI supports it. [NL]
return html`
<uui-ref-node standalone href=${this._workspaceEditPath ?? '#'}>
<umb-ufm-render inline .markdown=${this.label} .value=${this._content}></umb-ufm-render>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export class UmbBlockListEntriesContext extends UmbBlockEntriesContext<
.addUniquePaths(['propertyAlias', 'variantId'])
.addAdditionalPath('block')
.onSetup(() => {
return { data: { entityType: 'block', preset: {} }, modal: { size: 'medium' } };
return { data: { entityType: 'block', preset: {}, baseDataPath: this._dataPath }, modal: { size: 'medium' } };
})
.observeRouteBuilder((routeBuilder) => {
const newPath = routeBuilder({});
Expand Down
Loading

0 comments on commit 3cefb9c

Please sign in to comment.