From c2fab69600a7424a86a7f56c494aaefaffd63d3b Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Wed, 15 Feb 2023 16:42:50 +0000 Subject: [PATCH] Adds way to open debug info in a dialog * UI needs tidying up - spacing * Consider dropping the other UI approach and use dialog only * Need to ensure all typed * Neaten/refactor code if needed * Lint code --- .../modal-views/fields-viewer.element.ts | 6 +- .../dashboard-published-status.element.ts | 2 +- .../shared/components/debug/debug.element.ts | 115 ++++++++---- .../components/debug/debug.modal.element.ts | 163 ++++++++++++++++++ .../src/backoffice/shared/components/index.ts | 3 +- 5 files changed, 248 insertions(+), 41 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/backoffice/shared/components/debug/debug.modal.element.ts diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/settings/dashboards/examine-management/views/modal-views/fields-viewer.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/settings/dashboards/examine-management/views/modal-views/fields-viewer.element.ts index 6e81567c79..e55b6b4b22 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/settings/dashboards/examine-management/views/modal-views/fields-viewer.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/settings/dashboards/examine-management/views/modal-views/fields-viewer.element.ts @@ -16,11 +16,7 @@ export class UmbModalLayoutFieldsViewerElement extends UmbModalLayoutElement +

${this._publishedStatusText}

{ + this._modalService = modalService; + }); + } + connectedCallback(): void { super.connectedCallback(); // Dispatch it this.dispatchEvent( - new UmbContextDebugRequest((instances: Map) => { - console.log('I have contexts now', instances); + new UmbContextDebugRequest((contexts: Map) => { - this.contextAliases = instances; + // The Contexts are collected + // When travelling up through the DOM from this element + // to the root of which then uses the callback prop + // of the this event tha has been raised to assign the contexts + // back to this property of the WebComponent + this.contexts = contexts; }) ); } render() { - if (this.enabled) { - return html` -
- - - Debug - + if (this.enabled) { + return this.useDialog ? this._renderDialog() : this._renderPanel(); + } else { + return nothing; + } + + } -
-
-

Context Aliases to consume

-
    - ${this._renderContextAliases()} -
-
+ private _toggleDebugPane() { + this._debugPaneOpen = !this._debugPaneOpen; + } + + private _openDialog() { + const modalHandler = this._modalService?.open('umb-debug-modal-layout', { size: 'medium', type: 'sidebar', data:{ contexts: this.contexts }}); + + modalHandler?.onClose().then((data) => { + // if any data is supplied on close, it will be available here. + console.log('modal closed data', data); + }); + } + + + + private _renderDialog() { + return html` +
+ + Debug + +
`; + } + + private _renderPanel(){ + return html` +
+ + + Debug + + +
+
+

Context Aliases to consume

+
    + ${this._renderContextAliases()} +
- `; - } - - return nothing; +
`; } private _renderContextAliases() { const aliases = []; - for (const [alias, instance] of this.contextAliases) { + for (const [alias, instance] of this.contexts) { aliases.push( html`
  • Context: ${alias} @@ -124,10 +175,6 @@ export class UmbDebug extends LitElement { if (key.startsWith('_')) { continue; } - // Goes KABOOM - if try to loop over the class/object - // instanceKeys.push(html`
  • ${key} = ${instance[key]}
  • `); - - // console.log(`key: ${key} = ${value} TYPEOF: ${typeof value}`); const value = instance[key]; if (typeof value === 'string') { diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/debug/debug.modal.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/debug/debug.modal.element.ts new file mode 100644 index 0000000000..56f898c6d5 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/debug/debug.modal.element.ts @@ -0,0 +1,163 @@ +import { css, html, nothing, TemplateResult } from 'lit'; +import { customElement, property } from 'lit/decorators.js'; +import { UUITextStyles } from '@umbraco-ui/uui-css'; +import { UmbModalHandler, UmbModalLayoutElement } from '@umbraco-cms/modal'; + +@customElement('umb-debug-modal-layout') +export class UmbDebugModalLayout extends UmbModalLayoutElement { + static styles = [ + UUITextStyles, + css` + uui-dialog-layout { + display: flex; + flex-direction: column; + height: 100%; + + padding: var(--uui-size-space-5); + box-sizing: border-box; + } + + uui-scroll-container { + overflow-y: scroll; + max-height: 100%; + min-height: 0; + flex: 1; + } + + uui-icon { + vertical-align: text-top; + color: var(--uui-color-danger); + } + + .context { + padding:15px 0; + border-bottom:1px solid var(--uui-color-danger-emphasis); + } + + h3 { + margin-top: 0; + margin-bottom: 0; + } + + h3 > span { + border-radius: var(--uui-size-4); + background-color: var(--uui-color-danger); + color: var(--uui-color-danger-contrast); + padding: 8px; + font-size: 12px; + } + + + ul { + margin-top: 0; + } + `, + ]; + + + // the modal handler will be injected into the element when the modal is opened. + @property({ attribute: false }) + modalHandler?: UmbModalHandler; + + private _handleClose() { + /* Optional data of any type can be applied to the close method to pass it + to the modal parent through the onClose promise. */ + //this.modalHandler?.close('MY DATA'); + this.modalHandler?.close(); + } + + render() { + return html` + + + Debug: Contexts + + + ${this._renderContextAliases()} + + Close + + `; + } + + private _renderContextAliases() { + if(!this.data) { + return nothing; + } + + const aliases = []; + for (const [alias, instance] of this.data.contexts) { + aliases.push( + html` +
    +

    ${alias} ${typeof instance}

    + ${this._renderInstance(instance)} +
    ` + ); + } + + return aliases; + } + + private _renderInstance(instance: any) { + const instanceKeys: TemplateResult[] = []; + + if (typeof instance === 'function') { + return instanceKeys.push(html`
  • Callable Function
  • `); + } else if (typeof instance === 'object') { + const methodNames = this.getClassMethodNames(instance); + if (methodNames.length) { + instanceKeys.push( + html` +

    Methods

    +
      + ${methodNames.map((methodName) => html`
    • ${methodName}
    • `)} +
    + `); + } + + instanceKeys.push(html`

    Properties

    `); + + for (const key in instance) { + if (key.startsWith('_')) { + continue; + } + + const value = instance[key]; + if (typeof value === 'string') { + instanceKeys.push(html`
  • ${key} = ${value}
  • `); + } else { + instanceKeys.push(html`
  • ${key} Type (${typeof value})
  • `); + } + } + } else { + instanceKeys.push(html`
  • Context is a primitive with value: ${instance}
  • `); + } + + return instanceKeys; + } + + + private getClassMethodNames(klass: any) { + const isGetter = (x: any, name: string): boolean => !!(Object.getOwnPropertyDescriptor(x, name) || {}).get; + const isFunction = (x: any, name: string): boolean => typeof x[name] === 'function'; + const deepFunctions = (x: any): any => + x !== Object.prototype && + Object.getOwnPropertyNames(x) + .filter((name) => isGetter(x, name) || isFunction(x, name)) + .concat(deepFunctions(Object.getPrototypeOf(x)) || []); + const distinctDeepFunctions = (klass: any) => Array.from(new Set(deepFunctions(klass))); + + const allMethods = + typeof klass.prototype === 'undefined' + ? distinctDeepFunctions(klass) + : Object.getOwnPropertyNames(klass.prototype); + return allMethods.filter((name: any) => name !== 'constructor' && !name.startsWith('_')); + } +} + +declare global { + interface HTMLElementTagNameMap { + 'umb-debug-modal-layout': UmbDebugModalLayout; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/index.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/index.ts index c9602d9759..b09a13320e 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/index.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/index.ts @@ -31,4 +31,5 @@ import './input-document-picker/input-document-picker.element'; import './empty-state/empty-state.element'; import './color-picker/color-picker.element'; -import './debug/debug.element'; \ No newline at end of file +import './debug/debug.element'; +import './debug/debug.modal.element'; \ No newline at end of file