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
This commit is contained in:
Warren Buckley
2023-02-15 16:42:50 +00:00
parent c48b913a72
commit c2fab69600
5 changed files with 248 additions and 41 deletions

View File

@@ -16,11 +16,7 @@ export class UmbModalLayoutFieldsViewerElement extends UmbModalLayoutElement<Sea
display: flex;
flex-direction: column;
height: 100%;
background-color: var(--uui-color-surface);
box-shadow: var(--uui-shadow-depth-1, 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24));
border-radius: var(--uui-border-radius);
padding: var(--uui-size-space-5);
box-sizing: border-box;
}
span {

View File

@@ -133,7 +133,7 @@ export class UmbDashboardPublishedStatusElement extends UmbLitElement {
render() {
return html`
<umb-debug enabled></umb-debug>
<umb-debug enabled useDialog></umb-debug>
<uui-box headline="Published Cache Status">
<p>${this._publishedStatusText}</p>
<uui-button

View File

@@ -1,28 +1,39 @@
import { UUITextStyles } from '@umbraco-ui/uui-css/lib';
import { css, html, LitElement, nothing, TemplateResult } from 'lit';
import { css, html, nothing, TemplateResult } from 'lit';
import { customElement, property, state } from 'lit/decorators.js';
import { UmbContextDebugRequest } from '@umbraco-cms/context-api';
import { UmbLitElement } from '@umbraco-cms/element';
import { UmbModalService, UMB_MODAL_SERVICE_CONTEXT_TOKEN } from '@umbraco-cms/modal';
@customElement('umb-debug')
export class UmbDebug extends LitElement {
export class UmbDebug extends UmbLitElement {
static styles = [
UUITextStyles,
css`
css`
#container {
display: block;
font-family: monospace;
z-index: 10000;
position:relative;
width: 100%;
padding: 10px 0;
}
uui-badge {
cursor: pointer;
}
uui-icon {
font-size: 15px;
}
.events {
background-color: var(--uui-color-danger);
color: var(--uui-color-selected-contrast);
max-height: 0;
transition: max-height 0.15s ease-out;
transition: max-height 0.25s ease-out;
overflow: hidden;
}
@@ -44,57 +55,97 @@ export class UmbDebug extends LitElement {
@property({ reflect: true, type: Boolean })
enabled = false;
@property({ reflect: true, type: Boolean })
useDialog = false;
@property()
contextAliases = new Map();
contexts = new Map();
@state()
private _debugPaneOpen = false;
private _toggleDebugPane() {
this._debugPaneOpen = !this._debugPaneOpen;
}
private _modalService?: UmbModalService;
constructor() {
super();
this.consumeContext(UMB_MODAL_SERVICE_CONTEXT_TOKEN, (modalService) => {
this._modalService = modalService;
});
}
connectedCallback(): void {
super.connectedCallback();
// Dispatch it
this.dispatchEvent(
new UmbContextDebugRequest((instances: Map<any, any>) => {
console.log('I have contexts now', instances);
new UmbContextDebugRequest((contexts: Map<any, any>) => {
this.contextAliases = instances;
// The Contexts are collected
// When travelling up through the DOM from this element
// to the root of <umb-app> 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`
<div id="container">
<uui-button color="danger" look="primary" @click="${this._toggleDebugPane}">
<uui-icon name="umb:bug"></uui-icon>
Debug
</uui-button>
if (this.enabled) {
return this.useDialog ? this._renderDialog() : this._renderPanel();
} else {
return nothing;
}
}
<div class="events ${this._debugPaneOpen ? 'open' : ''}">
<div>
<h4>Context Aliases to consume</h4>
<ul>
${this._renderContextAliases()}
</ul>
</div>
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`
<div id="container">
<uui-badge color="danger" look="primary" attention @click="${this._openDialog}">
<uui-icon name="umb:bug"></uui-icon> Debug
</uui-badge>
</div>`;
}
private _renderPanel(){
return html`
<div id="container">
<uui-button color="danger" look="primary" @click="${this._toggleDebugPane}">
<uui-icon name="umb:bug"></uui-icon>
Debug
</uui-button>
<div class="events ${this._debugPaneOpen ? 'open' : ''}">
<div>
<h4>Context Aliases to consume</h4>
<ul>
${this._renderContextAliases()}
</ul>
</div>
</div>
`;
}
return nothing;
</div>`;
}
private _renderContextAliases() {
const aliases = [];
for (const [alias, instance] of this.contextAliases) {
for (const [alias, instance] of this.contexts) {
aliases.push(
html` <li>
Context: <strong>${alias}</strong>
@@ -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`<li>${key} = ${instance[key]}</li>`);
// console.log(`key: ${key} = ${value} TYPEOF: ${typeof value}`);
const value = instance[key];
if (typeof value === 'string') {

View File

@@ -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`
<uui-dialog-layout>
<span slot="headline">
<uui-icon name="umb:bug"></uui-icon> Debug: Contexts
</span>
<uui-scroll-container id="field-settings">
${this._renderContextAliases()}
</uui-scroll-container>
<uui-button slot="actions" look="primary" label="Close sidebar" @click="${this._handleClose}">Close</uui-button>
</uui-dialog-layout>
`;
}
private _renderContextAliases() {
if(!this.data) {
return nothing;
}
const aliases = [];
for (const [alias, instance] of this.data.contexts) {
aliases.push(
html`
<div class="context">
<h3>${alias} <span>${typeof instance}</span></h3>
${this._renderInstance(instance)}
</div>`
);
}
return aliases;
}
private _renderInstance(instance: any) {
const instanceKeys: TemplateResult[] = [];
if (typeof instance === 'function') {
return instanceKeys.push(html`<li>Callable Function</li>`);
} else if (typeof instance === 'object') {
const methodNames = this.getClassMethodNames(instance);
if (methodNames.length) {
instanceKeys.push(
html`
<h4>Methods</h4>
<ul>
${methodNames.map((methodName) => html`<li>${methodName}</li>`)}
</ul>
`);
}
instanceKeys.push(html`<h4>Properties</h4>`);
for (const key in instance) {
if (key.startsWith('_')) {
continue;
}
const value = instance[key];
if (typeof value === 'string') {
instanceKeys.push(html`<li>${key} = ${value}</li>`);
} else {
instanceKeys.push(html`<li>${key} Type (${typeof value})</li>`);
}
}
} else {
instanceKeys.push(html`<li>Context is a primitive with value: ${instance}</li>`);
}
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;
}
}

View File

@@ -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';
import './debug/debug.element';
import './debug/debug.modal.element';