From 8ea84d577642bea4f379cd2d2cff6fe0f207681c Mon Sep 17 00:00:00 2001 From: AleksanderBodurri Date: Fri, 20 Mar 2020 13:24:22 -0400 Subject: [PATCH] feat(devtools): implement input/output/state preview functionality with the reworked property explorer --- cypress/integration/node-search.e2e.js | 2 +- .../directive-explorer.component.ts | 3 +- .../directive-property-resolver.ts | 29 ++++++---- .../property-resolver/property-data-source.ts | 55 ++++++++++++------- .../property-view-body.component.ts | 13 ++++- .../property-view-tree.component.css | 50 +++++++++++++++++ .../property-view-tree.component.html | 32 +++++++++++ .../property-view-tree.component.ts | 49 +++++++++++++++++ .../property-view-header.component.css | 6 +- .../property-view-header.component.html | 13 ++++- .../property-view-header.component.ts | 9 +++ .../property-view.component.html | 7 ++- .../property-view/property-view.component.ts | 54 ++++++++++++++++-- .../property-view/property-view.module.ts | 12 +++- .../property-tab/property-tab.component.ts | 3 +- 15 files changed, 289 insertions(+), 48 deletions(-) create mode 100644 projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-tab-body/property-view/property-view-body/property-view-tree/property-view-tree.component.css create mode 100644 projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-tab-body/property-view/property-view-body/property-view-tree/property-view-tree.component.html create mode 100644 projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-tab-body/property-view/property-view-body/property-view-tree/property-view-tree.component.ts diff --git a/cypress/integration/node-search.e2e.js b/cypress/integration/node-search.e2e.js index b3fa0ed4dec..145577ffbf6 100644 --- a/cypress/integration/node-search.e2e.js +++ b/cypress/integration/node-search.e2e.js @@ -86,7 +86,7 @@ describe('Search items in component tree', () => { checkComponentName('app-todos'); // should display correct title for properties panel - cy.get('header').should('have.text', ' Properties of app-todos '); + cy.get('header span').should('have.text', ' Properties of app-todos '); // should show correct component properties cy.get('ng-property-view').find('mat-tree-node'); diff --git a/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-explorer.component.ts b/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-explorer.component.ts index 4fbe4303221..cc7dd51b660 100644 --- a/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-explorer.component.ts +++ b/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-explorer.component.ts @@ -46,7 +46,6 @@ const sameDirectives = (a: IndexedNode, b: IndexedNode) => { ], }) export class DirectiveExplorerComponent implements OnInit { - directivesData: DirectivesProperties | null = null; currentSelectedElement: IndexedNode | null = null; forest: DevToolsNode[]; highlightIDinTreeFromElement: ElementPosition | null = null; @@ -189,7 +188,7 @@ export class DirectiveExplorerComponent implements OnInit { let data = {}; const controller = this._propResolver.getDirectiveController(directive); if (controller) { - data = controller.getDirectiveProperties(); + data = controller.directiveProperties; } if (!e.clipboardData) { return; diff --git a/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-resolver/directive-property-resolver.ts b/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-resolver/directive-property-resolver.ts index 293bfda54cc..6e7c7b10171 100644 --- a/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-resolver/directive-property-resolver.ts +++ b/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-resolver/directive-property-resolver.ts @@ -6,6 +6,12 @@ import { getExpandedDirectiveProperties } from './property-expanded-directive-pr import { Observable } from 'rxjs'; import { Property, FlatNode } from './element-property-resolver'; +export enum PropertyViewFilterOptions { + INPUTS, + OUTPUTS, + STATE, +} + const expandable = (prop: Descriptor) => { if (!prop) { return false; @@ -60,10 +66,18 @@ export class DirectivePropertyResolver { }; } - getDirectiveProperties(): { [name: string]: Descriptor } { + get directiveProperties(): { [name: string]: Descriptor } { return this._props.props; } + get directiveInputs(): string[] { + return Object.keys(this._props.inputs || {}); + } + + get directiveOutputs(): string[] { + return Object.keys(this._props.outputs || {}); + } + getExpandedProperties(): NestedProp[] { return getExpandedDirectiveProperties(this._dataSource.data); } @@ -91,7 +105,10 @@ export class DirectivePropertyResolver { private _getChildren(prop: Property): Property[] | undefined { const descriptor = prop.descriptor; - if (descriptor.type === PropType.Object && !(descriptor.value instanceof Observable)) { + if ( + (descriptor.type === PropType.Object || descriptor.type === PropType.Array) && + !(descriptor.value instanceof Observable) + ) { return Object.keys(descriptor.value || {}).map(name => { return { name, @@ -99,14 +116,6 @@ export class DirectivePropertyResolver { parent: prop, }; }); - } else if (descriptor.type === PropType.Array && !(descriptor.value instanceof Observable)) { - return (descriptor.value || []).map((el: Descriptor, idx: number) => { - return { - name: idx.toString(), - descriptor: el, - parent: prop, - }; - }); } else { console.error('Unexpected data type', descriptor, 'in property', prop); } diff --git a/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-resolver/property-data-source.ts b/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-resolver/property-data-source.ts index f22a16c9519..8360062c073 100644 --- a/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-resolver/property-data-source.ts +++ b/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-resolver/property-data-source.ts @@ -7,6 +7,20 @@ import { map } from 'rxjs/operators'; import { DefaultIterableDiffer } from '@angular/core'; import { diff } from '../../diffing'; import { FlatNode, Property } from './element-property-resolver'; +import { PropertyViewFilterOptions } from './directive-property-resolver'; + +const expandable = (prop: Descriptor, messageBus?: MessageBus) => { + if (!prop) { + return false; + } + if (!prop.value && !messageBus) { + return false; + } + if (!prop.expandable) { + return false; + } + return !(prop.type !== PropType.Object && prop.type !== PropType.Array); +}; const trackBy = (_: number, item: FlatNode) => { return `#${item.prop.name}#${item.level}`; @@ -18,6 +32,8 @@ export class PropertyDataSource extends DataSource { private _expandedData = new BehaviorSubject([]); private _differ = new DefaultIterableDiffer(trackBy); + private readonly _originalData: FlatNode[]; + constructor( props: { [prop: string]: Descriptor }, private _treeFlattener: MatTreeFlattener, @@ -28,7 +44,8 @@ export class PropertyDataSource extends DataSource { private _onReceivedNestedProperties: () => void ) { super(); - this._data.next(this._treeFlattener.flattenNodes(this._arrayify(props))); + this._originalData = this._treeFlattener.flattenNodes(this._arrayify(props)); + this._data.next(this._originalData); } get data(): FlatNode[] { @@ -110,25 +127,23 @@ export class PropertyDataSource extends DataSource { }); } - private _getChildren(prop: Property): Property[] { - const descriptor = prop.descriptor; - if (descriptor.type === PropType.Object && !(descriptor.value instanceof Observable)) { - return Object.keys(descriptor.value || {}).map(name => { - return { - name, - descriptor: descriptor.value ? descriptor.value[name] : null, - parent: prop, - }; - }); - } else if (descriptor.type === PropType.Array && !(descriptor.value instanceof Observable)) { - return (descriptor.value || []).map((el: Descriptor, idx: number) => { - return { - name: idx.toString(), - descriptor: el, - parent: prop, - }; - }); + filterDataSource(filter: string[] | null): void { + if (filter === null) { + return; } - throw new Error('Unexpected data type'); + let pushFlag = false; + const filteredData: FlatNode[] = []; + + // tslint:disable-next-line:prefer-for-of + for (let i = 0; i < this._originalData.length; i++) { + const node = this._originalData[i]; + if (node.level === 0) { + pushFlag = filter.includes(node.prop.name); + } + if (pushFlag) { + filteredData.push(node); + } + } + this._data.next(filteredData); } } diff --git a/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-tab-body/property-view/property-view-body/property-view-body.component.ts b/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-tab-body/property-view/property-view-body/property-view-body.component.ts index 032a6cc0455..040d3a7a460 100644 --- a/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-tab-body/property-view/property-view-body/property-view-body.component.ts +++ b/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-tab-body/property-view/property-view-body/property-view-body.component.ts @@ -1,4 +1,4 @@ -import { Component, Input } from '@angular/core'; +import { Component, Input, OnChanges, OnInit } from '@angular/core'; import { FlatTreeControl } from '@angular/cdk/tree'; import { PropertyDataSource } from '../../../../property-resolver/property-data-source'; import { FlatNode } from '../../../../property-resolver/element-property-resolver'; @@ -9,13 +9,22 @@ import { DirectivePropertyResolver } from '../../../../property-resolver/directi templateUrl: './property-view-body.component.html', styleUrls: ['./property-view-body.component.css'], }) -export class PropertyViewBodyComponent { +export class PropertyViewBodyComponent implements OnChanges { @Input() dataSource: PropertyDataSource; @Input() treeControl: FlatTreeControl; @Input() controller: DirectivePropertyResolver; + @Input() filterList: string[] | null = null; hasChild = (_: number, node: FlatNode): boolean => node.expandable; + ngOnChanges(): void { + this.filterTreeNodes(); + } + + filterTreeNodes(): void { + this.dataSource.filterDataSource(this.filterList); + } + toggle(node: FlatNode): void { if (this.treeControl.isExpanded(node)) { this.treeControl.collapse(node); diff --git a/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-tab-body/property-view/property-view-body/property-view-tree/property-view-tree.component.css b/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-tab-body/property-view/property-view-body/property-view-tree/property-view-tree.component.css new file mode 100644 index 00000000000..cf79f806120 --- /dev/null +++ b/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-tab-body/property-view/property-view-body/property-view-tree/property-view-tree.component.css @@ -0,0 +1,50 @@ +:host { + width: 325px; + display: block; + overflow: auto; + height: calc(100% - 24px); +} +:host mat-tree { + display: table; +} +.name { + margin-left: -9px; +} +.non-expandable { + margin-left: 13px; +} +.property-list { + margin: 5px; + margin-left: 15px; +} +.property-list mat-tree-node .name { + color: #b82519; +} +.property-list mat-tree-node.disabled .name, +.disabled { + color: #333; +} + +.arrow { + font-family: monospace; + font-size: 7px; + color: #6e6e6e; +} + +:host /deep/ .mat-tree-node .mat-icon { + font-size: 12px; + width: 16px; + height: 16px; +} + +:host /deep/ .mat-tree-node { + min-height: 20px !important; + cursor: default; + font-family: Menlo, monospace; + border-radius: 5px; +} + +:host /deep/ .mat-tree-node, +.mat-nested-tree-node { + font-size: 11px; +} diff --git a/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-tab-body/property-view/property-view-body/property-view-tree/property-view-tree.component.html b/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-tab-body/property-view/property-view-body/property-view-tree/property-view-tree.component.html new file mode 100644 index 00000000000..a530ef1ac51 --- /dev/null +++ b/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-tab-body/property-view/property-view-body/property-view-tree/property-view-tree.component.html @@ -0,0 +1,32 @@ + + + {{ node.prop.name }} :  + + + {{ node.prop.descriptor.preview }} + + + + + + + + + + LOL +
+ + {{ _treeControl.isExpanded(node) ? '▼' : '►' }} + +   + {{ node.prop.name }} :  + + {{ _treeControl.isExpanded(node) ? '' : node.prop.descriptor.preview }} + +
+
+
diff --git a/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-tab-body/property-view/property-view-body/property-view-tree/property-view-tree.component.ts b/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-tab-body/property-view/property-view-body/property-view-tree/property-view-tree.component.ts new file mode 100644 index 00000000000..7135b5b754b --- /dev/null +++ b/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-tab-body/property-view/property-view-body/property-view-tree/property-view-tree.component.ts @@ -0,0 +1,49 @@ +import { Component, EventEmitter, Input, Output } from '@angular/core'; +import { PropertyDataSource } from '../../../../../property-resolver/property-data-source'; +import { FlatNode } from '../../../../../property-resolver/element-property-resolver'; +import { UpdateEvent } from '../property-view-body.component'; +import { FlatTreeControl } from '@angular/cdk/tree'; + +@Component({ + selector: 'ng-property-view-tree', + templateUrl: './property-view-tree.component.html', + styleUrls: ['./property-view-tree.component.css'], +}) +export class PropertyViewTreeComponent { + @Input() set dataSource(dataSource: PropertyDataSource) { + this._dataSource = dataSource; + this._treeControl = dataSource.getTreeControl(); + } + @Output() updateValue = new EventEmitter(); + + _dataSource: PropertyDataSource; + _treeControl: FlatTreeControl; + + hasChild = (_: number, node: FlatNode): boolean => { + debugger; + return node.expandable; + }; + + toggle(node: FlatNode): void { + if (this._treeControl.isExpanded(node)) { + this._treeControl.collapse(node); + return; + } + this.expand(node); + } + + expand(node: FlatNode): void { + const { prop } = node; + if (!prop.descriptor.expandable) { + return; + } + this._treeControl.expand(node); + } + + handleUpdate(node: FlatNode, updatedValue: any): void { + this.updateValue.emit({ + node, + updatedValue, + }); + } +} diff --git a/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-tab-body/property-view/property-view-header/property-view-header.component.css b/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-tab-body/property-view/property-view-header/property-view-header.component.css index 0ca72dc5263..4434ea78467 100644 --- a/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-tab-body/property-view/property-view-header/property-view-header.component.css +++ b/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-tab-body/property-view/property-view-header/property-view-header.component.css @@ -1,4 +1,4 @@ -.explorer-panel header { +header { background-color: #eee; padding-left: 9px; border-top: 1px solid #ccc; @@ -22,3 +22,7 @@ line-height: 15px; margin-right: 7px; } + +:host /deep/ .mat-button-toggle-appearance-standard .mat-button-toggle-label-content { + line-height: 25px; +} diff --git a/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-tab-body/property-view/property-view-header/property-view-header.component.html b/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-tab-body/property-view/property-view-header/property-view-header.component.html index 83e2ef22f40..01e6d991f0b 100644 --- a/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-tab-body/property-view/property-view-header/property-view-header.component.html +++ b/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-tab/property-tab-body/property-view/property-view-header/property-view-header.component.html @@ -1,5 +1,16 @@
- Properties of {{ directive }} + Properties of {{ directive }} + + + Inputs + + + Outputs + + + State + +