Skip to content

Commit a1deb87

Browse files
hawkgsmmalerba
authored andcommitted
refactor(devtools): improve directive explorer filtering (angular#60672)
- Highlight only the matched part of the text - Select the first match by default - Show the matches count along with the currently selected one - Minor UI fixes and performance optimizations PR Close angular#60672
1 parent 6845d2a commit a1deb87

19 files changed

+1036
-415
lines changed

devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-explorer.component.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,7 @@ export class DirectiveExplorerComponent {
291291
}
292292

293293
handleSelect(node: FlatNode): void {
294-
this.directiveForest()?.handleSelect(node);
294+
this.directiveForest()?.selectAndEnsureVisible(node);
295295
}
296296

297297
handleSetParents(parents: FlatNode[] | null): void {
Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,60 @@
11
load("@io_bazel_rules_sass//:defs.bzl", "sass_binary")
2+
load("//devtools/tools:defaults.bzl", "karma_web_test_suite")
23
load("//devtools/tools:ng_module.bzl", "ng_module")
4+
load("//devtools/tools:typescript.bzl", "ts_library", "ts_test_library")
35

46
package(default_visibility = ["//visibility:public"])
57

68
sass_binary(
79
name = "directive_forest_component_styles",
810
src = "directive-forest.component.scss",
9-
deps = [
10-
"//devtools/projects/ng-devtools/src/styles:typography",
11-
],
1211
)
1312

1413
ng_module(
1514
name = "directive-forest",
1615
srcs = [
1716
"directive-forest.component.ts",
18-
"directive-forest-utils.ts",
1917
],
2018
angular_assets = [
2119
"directive-forest.component.html",
2220
":directive_forest_component_styles",
2321
],
2422
deps = [
23+
":directive_forest_utils",
2524
"//devtools/projects/ng-devtools/src/lib/devtools-tabs/diffing",
2625
"//devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-forest/breadcrumbs",
2726
"//devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-forest/component-data-source",
2827
"//devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-forest/filter",
2928
"//devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-forest/index-forest",
29+
"//devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-forest/tree-node",
3030
"//devtools/projects/ng-devtools/src/lib/devtools-tabs/tab-update",
3131
"//devtools/projects/protocol",
32-
"//packages/common",
3332
"//packages/core",
34-
"//packages/core/rxjs-interop",
3533
"@npm//@angular/cdk",
36-
"@npm//@angular/material",
37-
"@npm//@types",
38-
"@npm//rxjs",
34+
],
35+
)
36+
37+
ts_library(
38+
name = "directive_forest_utils",
39+
srcs = ["directive-forest-utils.ts"],
40+
deps = [
41+
"//devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-forest/component-data-source",
42+
"@npm//@angular/cdk",
43+
],
44+
)
45+
46+
ts_test_library(
47+
name = "directive_forest_utils_test",
48+
srcs = ["directive-forest-utils.spec.ts"],
49+
deps = [
50+
":directive_forest_utils",
51+
"//devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-forest/component-data-source",
52+
],
53+
)
54+
55+
karma_web_test_suite(
56+
name = "test",
57+
deps = [
58+
":directive_forest_utils_test",
3959
],
4060
)

devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-forest/component-data-source/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export interface FlatNode {
2222
id: string;
2323
expandable: boolean;
2424
name: string;
25-
directives: string;
25+
directives: string[];
2626
position: number[];
2727
level: number;
2828
original: IndexedNode;
@@ -91,7 +91,7 @@ export class ComponentDataSource extends DataSource<FlatNode> {
9191
// and the reference is preserved after transformation.
9292
position: node.position,
9393
name: node.component ? node.component.name : node.element,
94-
directives: node.directives.map((d) => d.name).join(', '),
94+
directives: node.directives.map((d) => d.name),
9595
original: node,
9696
level,
9797
hydration: node.hydration,
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.dev/license
7+
*/
8+
9+
import {FlatNode} from './component-data-source';
10+
import {getDirectivesArrayString, getFullNodeNameString} from './directive-forest-utils';
11+
12+
describe('directive-forest-utils', () => {
13+
describe('getDirectivesArrayString', () => {
14+
it('should return an empty string, if no directives', () => {
15+
const output = getDirectivesArrayString({} as FlatNode);
16+
17+
expect(output).toEqual('');
18+
});
19+
20+
it('should return a single directive string', () => {
21+
const output = getDirectivesArrayString({
22+
directives: ['Foo'],
23+
} as FlatNode);
24+
25+
expect(output).toEqual('[Foo]');
26+
});
27+
28+
it('should return multiple directives string', () => {
29+
const output = getDirectivesArrayString({
30+
directives: ['Foo', 'Bar'],
31+
} as FlatNode);
32+
33+
expect(output).toEqual('[Foo][Bar]');
34+
});
35+
});
36+
37+
describe('getFullNodeNameString', () => {
38+
it('should return a simple component name string', () => {
39+
const output = getFullNodeNameString({
40+
name: 'app-test',
41+
original: {component: {}},
42+
} as FlatNode);
43+
44+
expect(output).toEqual('app-test');
45+
});
46+
47+
it('should enclose name in a tag, if an element', () => {
48+
const output = getFullNodeNameString({
49+
name: 'app-element',
50+
original: {
51+
component: {
52+
isElement: true,
53+
},
54+
},
55+
} as FlatNode);
56+
57+
expect(output).toEqual('<app-element/>');
58+
});
59+
60+
it('should return component name with directives string, if any', () => {
61+
const output = getFullNodeNameString({
62+
name: 'app-test',
63+
directives: ['Foo', 'Bar'],
64+
original: {component: {}},
65+
} as FlatNode);
66+
67+
expect(output).toEqual('app-test[Foo][Bar]');
68+
});
69+
});
70+
});

devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-forest/directive-forest-utils.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,16 @@ export const parentCollapsed = (
3535
}
3636
return false;
3737
};
38+
39+
/** Returns the `FlatNode`'s directive array string. */
40+
export const getDirectivesArrayString = (node: FlatNode): string =>
41+
node.directives ? node.directives.map((dir) => `[${dir}]`).join('') : '';
42+
43+
/** Returns the full node name string as rendered by the tree-node component. */
44+
export const getFullNodeNameString = (node: FlatNode): string => {
45+
const cmp = node.original.component;
46+
if (cmp && cmp.isElement) {
47+
return `<${node.name}/>`;
48+
}
49+
return node.name + getDirectivesArrayString(node);
50+
};
Lines changed: 17 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -1,89 +1,21 @@
11
<ng-filter
2-
[hasMatched]="hasMatched"
32
(filter)="handleFilter($event)"
4-
(nextMatched)="nextMatched()"
5-
(prevMatched)="prevMatched()"
6-
>
7-
</ng-filter>
3+
(nextMatched)="navigateMatchedNode('next')"
4+
(prevMatched)="navigateMatchedNode('prev')"
5+
[matchesCount]="matchesCount()"
6+
[currentMatch]="currentlyMatchedIndex() + 1"
7+
/>
88
<cdk-virtual-scroll-viewport class="tree-wrapper" [itemSize]="itemHeight">
9-
<ng-container *cdkVirtualFor="let node of dataSource; let idx = index">
10-
@let hydration = node.hydration;
11-
12-
<div
13-
[class]="{
14-
matched: isMatched(node),
15-
selected: isSelected(node),
16-
highlighted: isHighlighted(node),
17-
'new-node': node.newItem
18-
}"
19-
class="tree-node"
20-
(click)="selectAndEnsureVisible(node)"
21-
(dblclick)="handleSelectDomElement(node)"
22-
(mouseenter)="highlightNode(node.position)"
23-
(mouseleave)="removeHighlight()"
24-
[style.padding-left]="15 + 15 * node.level + 'px'"
25-
>
26-
<div class="tree-node-info">
27-
@if (node.expandable) {
28-
<!-- We stop propagration on dblClick to prevent to show the element panel -->
29-
<button
30-
[style.left]="15 * node.level + 'px'"
31-
(click)="treeControl.toggle(node)"
32-
(dblclick)="stopPropagation($event)"
33-
[attr.aria-label]="'toggle ' + node.name"
34-
>
35-
<mat-icon class="mat-icon-rtl-mirror">
36-
{{ treeControl.isExpanded(node) ? 'expand_more' : 'chevron_right' }}
37-
</mat-icon>
38-
</button>
39-
}
40-
<span class="element-name" [class.angular-element]="isElement(node)">{{ node.name }}</span>
41-
42-
@if (node.directives) {
43-
<span class="dir-names">[{{ node.directives }}]</span>
44-
}
45-
@if (isSelected(node)) {
46-
<span class="console-reference"> == $ng0 </span>
47-
}
48-
49-
@if (node.onPush) {
50-
<span class="on-push">OnPush</span>
51-
}
52-
53-
@switch (hydration?.status) {
54-
@case ('hydrated') {
55-
<mat-icon matTooltip="Hydrated" class="hydration">water_drop</mat-icon>
56-
}
57-
@case ('skipped') {
58-
<mat-icon matTooltip="Hydration skipped" class="hydration skipped"
59-
>invert_colors_off</mat-icon
60-
>
61-
}
62-
@case ('mismatched') {
63-
<mat-icon matTooltip="Hydration mismatch" class="hydration mismatched"
64-
>error_outline</mat-icon
65-
>
66-
}
67-
}
68-
</div>
69-
70-
@if (
71-
treeControl.isExpanded(node) &&
72-
hydration &&
73-
hydration.status === 'mismatched' &&
74-
(hydration.expectedNodeDetails || hydration.actualNodeDetails)
75-
) {
76-
<div class="hydration-error">
77-
@if (hydration.expectedNodeDetails) {
78-
<div>Expected Dom:</div>
79-
<pre>{{hydration.expectedNodeDetails}}</pre>
80-
}
81-
@if (hydration.actualNodeDetails) {
82-
<div>Actual Dom:</div>
83-
<pre>{{hydration.actualNodeDetails}}</pre>
84-
}
85-
</div>
86-
}
87-
</div>
88-
</ng-container>
9+
<ng-tree-node
10+
*cdkVirtualFor="let node of dataSource; let idx = index"
11+
[node]="node"
12+
[selectedNode]="selectedNode()"
13+
[highlightedId]="highlightIdInTreeFromElement()"
14+
[treeControl]="treeControl"
15+
[textMatch]="matchedNodes().get(idx)"
16+
(selectNode)="selectAndEnsureVisible($event)"
17+
(selectDomElement)="handleSelectDomElement($event)"
18+
(highlightNode)="highlightNode($event)"
19+
(removeHighlight)="removeHighlight()"
20+
/>
8921
</cdk-virtual-scroll-viewport>

0 commit comments

Comments
 (0)