Hiya
We are currently trying to update our custom block previews (around 40 or so) that allows the content editors to click on the preview to open the edit block dialog, instead of having specifically clicking the edit pencil icon to open the dialog.
We are able to do this with code like so by using the UMB_BLOCK_ENTRY_CONTEXT
Simple Example
Example Block Preview
import { UMB_BLOCK_ENTRY_CONTEXT, UmbBlockDataType } from "@umbraco-cms/backoffice/block";
import { UmbBlockEditorCustomViewElement } from "@umbraco-cms/backoffice/block-custom-view";
import { UmbElementMixin } from "@umbraco-cms/backoffice/element-api";
import { css, customElement, html, LitElement, property, unsafeHTML } from "@umbraco-cms/backoffice/external/lit";
import { BackofficeCssStyles } from "../../common/styles/backoffice-css-styles";
import { RTEData } from "../../common/types/rte";
interface ParagraphBlockContent extends UmbBlockDataType {
richText: RTEData;
}
@customElement('paragraph-block-view')
export class ParagraphBlockViewElement extends UmbElementMixin(LitElement) implements UmbBlockEditorCustomViewElement {
#blockEntryCtx?: typeof UMB_BLOCK_ENTRY_CONTEXT.TYPE;
constructor() {
super();
this.consumeContext(UMB_BLOCK_ENTRY_CONTEXT,(blockEntryCtx) => {
this.#blockEntryCtx = blockEntryCtx;
});
}
// Ideally dont want to keep copying this into every block preview
// Along with the consumeContext above in the ctor
editBlock() {
this.#blockEntryCtx?.edit();
}
@property({attribute: false})
label?: string;
@property({attribute: false})
content?: ParagraphBlockContent;
@property({attribute: false})
settings?: UmbBlockDataType;
render() {
return html`
<fieldset>
<legend>${this.label ?? 'Paragraph'}</legend>
<div @click=${this.editBlock}>
${unsafeHTML(this.content?.richText.markup)}
</div>
</fieldset>
`;
}
static styles = [
BackofficeCssStyles,
css ``
]
}
export default ParagraphBlockViewElement;
declare global {
interface HTMLElementTagNameMap {
'paragraph-block-view': ParagraphBlockViewElement;
}
}
Rather than doing this lots of times over, we are hoping to write this once and the 40 or so Block Preview elements can then have access to this method to launch the edit dialog. The current thought process is to use a mixin approach.
Mixin approach
Mixin code
import { UMB_BLOCK_ENTRY_CONTEXT } from "@umbraco-cms/backoffice/block";
import { UmbElement } from "@umbraco-cms/backoffice/element-api";
import { BlockPreviewService } from "../../api";
type Constructor<T = {}> = new (...args: any[]) => T;
export const BlockPreviewMixin = <TBase extends Constructor<UmbElement>>(Base: TBase) =>
class extends Base {
protected _blockEntryCtx?: typeof UMB_BLOCK_ENTRY_CONTEXT.TYPE;
connectedCallback() {
super.connectedCallback();
this.consumeContext(UMB_BLOCK_ENTRY_CONTEXT, (blockEntryCtx) => {
console.log("BlockPreviewMixin: Consumed UMB_BLOCK_ENTRY_CONTEXT", blockEntryCtx);
this._blockEntryCtx = blockEntryCtx;
});
}
editBlock() {
console.log("BlockPreviewMixin: editBlock called");
this._blockEntryCtx?.edit();
}
// *****************************************************
// TODO: HELP Why do I need this dummy async function
// In order for the block preview to be rendered ?
// *****************************************************
async dummyAsyncFunction() {
await BlockPreviewService.getAllCouncilConfigurations();
}
};
Updated Block Preview
import { UmbBlockDataType } from "@umbraco-cms/backoffice/block";
import { UmbBlockEditorCustomViewElement } from "@umbraco-cms/backoffice/block-custom-view";
import { UmbElementMixin } from "@umbraco-cms/backoffice/element-api";
import { css, customElement, html, LitElement, property, unsafeHTML } from "@umbraco-cms/backoffice/external/lit";
import { BackofficeCssStyles } from "../../common/styles/backoffice-css-styles";
import { RTEData } from "../../common/types/rte";
import { BlockPreviewMixin } from "./BlockPreviewMixin";
interface ParagraphBlockContent extends UmbBlockDataType {
richText: RTEData;
}
// Our NEW BlockPreviewMixin wraps UmbElementMixin(LitElement)
@customElement('paragraph-block-view')
export class ParagraphBlockViewElement extends BlockPreviewMixin(UmbElementMixin(LitElement)) implements UmbBlockEditorCustomViewElement {
constructor() {
super();
}
@property({attribute: false})
label?: string;
@property({attribute: false})
content?: ParagraphBlockContent;
@property({attribute: false})
settings?: UmbBlockDataType;
render() {
return html`
<fieldset>
<legend>${this.label ?? 'Paragraph'}</legend>
<div @click=${this.editBlock}>
${unsafeHTML(this.content?.richText.markup)}
</div>
</fieldset>
`;
}
static styles = [
BackofficeCssStyles,
css ``
]
}
export default ParagraphBlockViewElement;
declare global {
interface HTMLElementTagNameMap {
'paragraph-block-view': ParagraphBlockViewElement;
}
}
Problem I need help with
The mixin approach only works if I have a method that is async and awaits a function. Even if I don’t use or consume it in my block preview. Without the async method then the preview for the block element does not appear at all.
-
Does anyone have any smart ideas or reasons as to why this is happening?
-
Alternatively perhaps you have a nicer way to have this code in one place to share across all block previews ?
Thanks,
Warren