mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
fix(devtools): make sure we update the property explorer on state change (rangle/angular-devtools#821)
Fix rangle/angular-devtools#786 With this PR now we consider not only the property name but also the value of properties in the differ comparison. The change also contains e2e and unit tests.
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
describe('change of the state should reflect in property update', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/');
|
||||
});
|
||||
|
||||
it('should update the property value', () => {
|
||||
// Complete the todo
|
||||
cy.enter('#sample-app').then((getBody) => {
|
||||
getBody().find('input[type="checkbox"].toggle').first().click();
|
||||
});
|
||||
|
||||
// Select the todo item
|
||||
cy.get('.tree-wrapper').find('.tree-node:contains("app-todo[TooltipDirective]")').first().click({ force: true });
|
||||
|
||||
// Expand the todo in the property explorer
|
||||
cy.get('.explorer-panel:contains("app-todo")').find('ng-property-view mat-tree-node:contains("todo")').click();
|
||||
|
||||
// Verify its value is now completed
|
||||
cy.get('.explorer-panel:contains("app-todo")')
|
||||
.find('ng-property-view mat-tree-node:contains("completed")')
|
||||
.find('ng-property-editor .editor')
|
||||
.should((el) => {
|
||||
expect(el.text().trim()).equal('true');
|
||||
});
|
||||
});
|
||||
});
|
||||
+3
-38
@@ -1,28 +1,16 @@
|
||||
import { Descriptor, PropType, MessageBus, Events, Properties, DirectivePosition, NestedProp } from 'protocol';
|
||||
import { MatTreeFlattener } from '@angular/material/tree';
|
||||
import { Descriptor, MessageBus, Events, Properties, DirectivePosition, NestedProp } from 'protocol';
|
||||
import { PropertyDataSource } from './property-data-source';
|
||||
import { FlatTreeControl } from '@angular/cdk/tree';
|
||||
import { getExpandedDirectiveProperties } from './property-expanded-directive-properties';
|
||||
import { Observable } from 'rxjs';
|
||||
import { Property, FlatNode } from './element-property-resolver';
|
||||
import { ViewEncapsulation } from '@angular/core';
|
||||
import { arrayifyProps } from './arrayify-props';
|
||||
import { getTreeFlattener } from './flatten';
|
||||
|
||||
export interface DirectiveTreeData {
|
||||
dataSource: PropertyDataSource;
|
||||
treeControl: FlatTreeControl<FlatNode>;
|
||||
}
|
||||
|
||||
const expandable = (prop: Descriptor) => {
|
||||
if (!prop) {
|
||||
return false;
|
||||
}
|
||||
if (!prop.expandable) {
|
||||
return false;
|
||||
}
|
||||
return !(prop.type !== PropType.Object && prop.type !== PropType.Array);
|
||||
};
|
||||
|
||||
const getDirectiveControls = (
|
||||
dataSource: PropertyDataSource
|
||||
): { dataSource: PropertyDataSource; treeControl: FlatTreeControl<FlatNode> } => {
|
||||
@@ -43,18 +31,7 @@ export const constructPathOfKeysToPropertyValue = (nodePropToGetKeysFor: Propert
|
||||
};
|
||||
|
||||
export class DirectivePropertyResolver {
|
||||
private _treeFlattener = new MatTreeFlattener(
|
||||
(node: Property, level: number): FlatNode => {
|
||||
return {
|
||||
expandable: expandable(node.descriptor),
|
||||
prop: node,
|
||||
level,
|
||||
};
|
||||
},
|
||||
(node) => node.level,
|
||||
(node) => node.expandable,
|
||||
(node) => this._getChildren(node)
|
||||
);
|
||||
private _treeFlattener = getTreeFlattener();
|
||||
|
||||
private _treeControl = new FlatTreeControl<FlatNode>(
|
||||
(node) => node.level,
|
||||
@@ -125,18 +102,6 @@ export class DirectivePropertyResolver {
|
||||
node.prop.descriptor.value = newValue;
|
||||
}
|
||||
|
||||
private _getChildren(prop: Property): Property[] | undefined {
|
||||
const descriptor = prop.descriptor;
|
||||
if (
|
||||
(descriptor.type === PropType.Object || descriptor.type === PropType.Array) &&
|
||||
!(descriptor.value instanceof Observable)
|
||||
) {
|
||||
return arrayifyProps(descriptor.value || {}, prop);
|
||||
} else {
|
||||
console.error('Unexpected data type', descriptor, 'in property', prop);
|
||||
}
|
||||
}
|
||||
|
||||
private _initDataSources(): void {
|
||||
const { inputProps, outputProps, stateProps } = this._classifyProperties();
|
||||
|
||||
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
import { MatTreeFlattener } from '@angular/material/tree';
|
||||
import { Descriptor, PropType } from 'protocol';
|
||||
import { Observable } from 'rxjs';
|
||||
import { arrayifyProps } from './arrayify-props';
|
||||
import { FlatNode, Property } from './element-property-resolver';
|
||||
|
||||
export const getTreeFlattener = () =>
|
||||
new MatTreeFlattener(
|
||||
(node: Property, level: number): FlatNode => {
|
||||
return {
|
||||
expandable: expandable(node.descriptor),
|
||||
prop: node,
|
||||
level,
|
||||
};
|
||||
},
|
||||
(node) => node.level,
|
||||
(node) => node.expandable,
|
||||
(node) => getChildren(node)
|
||||
);
|
||||
|
||||
export const expandable = (prop: Descriptor) => {
|
||||
if (!prop) {
|
||||
return false;
|
||||
}
|
||||
if (!prop.expandable) {
|
||||
return false;
|
||||
}
|
||||
return !(prop.type !== PropType.Object && prop.type !== PropType.Array);
|
||||
};
|
||||
|
||||
const getChildren = (prop: Property): Property[] | undefined => {
|
||||
const descriptor = prop.descriptor;
|
||||
if (
|
||||
(descriptor.type === PropType.Object || descriptor.type === PropType.Array) &&
|
||||
!(descriptor.value instanceof Observable)
|
||||
) {
|
||||
return arrayifyProps(descriptor.value || {}, prop);
|
||||
} else {
|
||||
console.error('Unexpected data type', descriptor, 'in property', prop);
|
||||
}
|
||||
};
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
import { FlatTreeControl } from '@angular/cdk/tree';
|
||||
import { PropType } from 'protocol';
|
||||
import { FlatNode } from './element-property-resolver';
|
||||
import { getTreeFlattener } from './flatten';
|
||||
import { PropertyDataSource } from './property-data-source';
|
||||
|
||||
const flatTreeControl = new FlatTreeControl<FlatNode>(
|
||||
(node) => node.level,
|
||||
(node) => node.expandable
|
||||
);
|
||||
|
||||
describe('PropertyDataSource', () => {
|
||||
it('should detect changes in the collection', () => {
|
||||
const source = new PropertyDataSource(
|
||||
{
|
||||
foo: {
|
||||
editable: true,
|
||||
expandable: false,
|
||||
preview: '42',
|
||||
type: PropType.Number,
|
||||
value: 42,
|
||||
},
|
||||
},
|
||||
getTreeFlattener(),
|
||||
flatTreeControl,
|
||||
{ element: [1, 2, 3] },
|
||||
null as any
|
||||
);
|
||||
|
||||
source.update({
|
||||
foo: {
|
||||
editable: true,
|
||||
expandable: false,
|
||||
preview: '43',
|
||||
type: PropType.Number,
|
||||
value: 43,
|
||||
},
|
||||
});
|
||||
|
||||
expect(source.data).toEqual([
|
||||
{
|
||||
expandable: false,
|
||||
level: 0,
|
||||
prop: {
|
||||
descriptor: {
|
||||
editable: true,
|
||||
expandable: false,
|
||||
preview: '43',
|
||||
type: PropType.Number,
|
||||
value: 43,
|
||||
},
|
||||
name: 'foo',
|
||||
parent: null,
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
+1
-3
@@ -9,9 +9,7 @@ import { diff } from '../../diffing';
|
||||
import { FlatNode, Property } from './element-property-resolver';
|
||||
import { arrayifyProps } from './arrayify-props';
|
||||
|
||||
const trackBy = (_: number, item: FlatNode) => {
|
||||
return `#${item.prop.name}#${item.level}`;
|
||||
};
|
||||
const trackBy = (_: number, item: FlatNode) => `#${item.prop.name}#${item.prop.descriptor.preview}#${item.level}`;
|
||||
|
||||
export class PropertyDataSource extends DataSource<FlatNode> {
|
||||
private _data = new BehaviorSubject<FlatNode[]>([]);
|
||||
|
||||
Reference in New Issue
Block a user