Adjust behaviour of pinned nodes/rect clicks.
Auto-expand and scroll to nodes in main tree when selected from pinned
list or rect.
Fixes: 399470013
Test: npm run test:unit:ci
Change-Id: I29536a10bf5d1c73c8137c5b16deba782beb9856
diff --git a/tools/winscope/src/viewers/components/tree_component.ts b/tools/winscope/src/viewers/components/tree_component.ts
index 1b0950b..9753941 100644
--- a/tools/winscope/src/viewers/components/tree_component.ts
+++ b/tools/winscope/src/viewers/components/tree_component.ts
@@ -15,6 +15,7 @@
*/
import {
ChangeDetectionStrategy,
+ ChangeDetectorRef,
Component,
ElementRef,
EventEmitter,
@@ -88,7 +89,8 @@
(highlightedChange)="propagateNewHighlightedItem($event)"
(pinnedItemChange)="propagateNewPinnedItem($event)"
(hoverStart)="childHover = true"
- (hoverEnd)="childHover = false"></tree-view>
+ (hoverEnd)="childHover = false"
+ (expandParent)="expandTree()"></tree-view>
</div>
`,
styles: [nodeStyles, treeNodeDataViewStyles, nodeInnerItemStyles],
@@ -116,12 +118,13 @@
@Output() readonly pinnedItemChange = new EventEmitter<UiHierarchyTreeNode>();
@Output() readonly hoverStart = new EventEmitter<void>();
@Output() readonly hoverEnd = new EventEmitter<void>();
+ @Output() readonly expandParent = new EventEmitter<void>();
- localExpandedState = true;
childHover = false;
readonly levelOffset = 24;
nodeElement: HTMLElement;
+ private localExpandedState = true;
private storeKeyCollapsedState = '';
childTrackById(
@@ -131,7 +134,10 @@
return child.id;
}
- constructor(@Inject(ElementRef) public elementRef: ElementRef) {
+ constructor(
+ @Inject(ElementRef) public elementRef: ElementRef,
+ @Inject(ChangeDetectorRef) private changeDetectorRef: ChangeDetectorRef,
+ ) {
this.nodeElement = elementRef.nativeElement.querySelector('.node');
this.nodeElement?.addEventListener(
'mousedown',
@@ -237,6 +243,8 @@
expandTree() {
this.setExpandedValue(true);
+ this.changeDetectorRef.detectChanges();
+ this.expandParent.emit();
}
isExpanded() {
diff --git a/tools/winscope/src/viewers/components/tree_component_test.ts b/tools/winscope/src/viewers/components/tree_component_test.ts
index 80728c7..ae12116 100644
--- a/tools/winscope/src/viewers/components/tree_component_test.ts
+++ b/tools/winscope/src/viewers/components/tree_component_test.ts
@@ -15,7 +15,7 @@
*/
import {Clipboard, ClipboardModule} from '@angular/cdk/clipboard';
-import {Component, CUSTOM_ELEMENTS_SCHEMA, ViewChild} from '@angular/core';
+import {Component, ViewChild} from '@angular/core';
import {ComponentFixture, TestBed} from '@angular/core/testing';
import {MatIconModule} from '@angular/material/icon';
import {MatTooltipModule} from '@angular/material/tooltip';
@@ -49,7 +49,6 @@
PropertyTreeNodeDataViewComponent,
],
imports: [MatTooltipModule, MatIconModule, ClipboardModule],
- schemas: [CUSTOM_ELEMENTS_SCHEMA],
}).compileComponents();
fixture = TestBed.createComponent(TestHostComponent);
component = fixture.componentInstance;
@@ -63,17 +62,18 @@
it('shows node', () => {
fixture.detectChanges();
- const treeNode = htmlElement.querySelector('tree-node');
- expect(treeNode).toBeTruthy();
+ expect(htmlElement.querySelector('tree-node')).toBeTruthy();
});
it('can identify if a parent node has a selected child', () => {
fixture.detectChanges();
- const treeComponent = assertDefined(component.treeComponent);
- expect(treeComponent.hasSelectedChild()).toBeFalse();
+ const treeNode = assertDefined(
+ htmlElement.querySelector<HTMLElement>('tree-node'),
+ );
+ expect(treeNode.className.includes('child-selected')).toBeFalse();
component.highlightedItem = '3 Child3';
fixture.detectChanges();
- expect(treeComponent.hasSelectedChild()).toBeTrue();
+ expect(treeNode.className.includes('child-selected')).toBeTrue();
});
it('highlights node and inner node upon click', () => {
@@ -97,29 +97,23 @@
it('toggles tree upon node double click', () => {
fixture.detectChanges();
- const treeComponent = assertDefined(component.treeComponent);
- const treeNode = assertDefined(
- htmlElement.querySelector<HTMLElement>('tree-node'),
+ const toggleButton = assertDefined(
+ htmlElement.querySelector('.toggle-tree-btn'),
);
- const currLocalExpandedState = treeComponent.localExpandedState;
- treeNode.dispatchEvent(new MouseEvent('click', {detail: 2}));
- fixture.detectChanges();
- expect(!currLocalExpandedState).toEqual(treeComponent.localExpandedState);
+ expect(toggleButton.textContent?.trim()).toEqual('arrow_drop_down');
+ checkIsExpanded(true);
+
+ doubleClickFirstNode();
+ expect(toggleButton.textContent?.trim()).toEqual('chevron_right');
+ checkIsExpanded(false);
});
it('does not toggle tree in flat mode on double click', () => {
fixture.detectChanges();
- const treeComponent = assertDefined(component.treeComponent);
component.isFlattened = true;
fixture.detectChanges();
- const treeNode = assertDefined(
- htmlElement.querySelector<HTMLElement>('tree-node'),
- );
-
- const currLocalExpandedState = treeComponent.localExpandedState;
- treeNode.dispatchEvent(new MouseEvent('click', {detail: 2}));
- fixture.detectChanges();
- expect(currLocalExpandedState).toEqual(treeComponent.localExpandedState);
+ doubleClickFirstNode();
+ checkIsExpanded(true);
});
it('pins node on click', () => {
@@ -138,66 +132,73 @@
it('expands tree on expand tree button click', () => {
fixture.detectChanges();
- const treeNode = assertDefined(
- htmlElement.querySelector<HTMLElement>('tree-node'),
- );
- treeNode.dispatchEvent(new MouseEvent('click', {detail: 2}));
- fixture.detectChanges();
- expect(component.treeComponent?.localExpandedState).toEqual(false);
+ doubleClickFirstNode();
+ checkIsExpanded(false);
+
assertDefined(
htmlElement.querySelector<HTMLElement>('.expand-tree-btn'),
).click();
fixture.detectChanges();
- expect(component.treeComponent?.localExpandedState).toEqual(true);
+ checkIsExpanded(true);
+ });
+
+ it('expands tree recursively on node selection', () => {
+ fixture.detectChanges();
+ doubleClickFirstNode();
+ checkIsExpanded(false);
+ component.highlightedItem = '79 Child79';
+ fixture.detectChanges();
+ checkIsExpanded(true);
});
it('scrolls selected node only if not in view', () => {
fixture.detectChanges();
- const treeComponent = assertDefined(component.treeComponent);
- const treeNode = assertDefined(
- treeComponent.elementRef.nativeElement.querySelector(`#nodeChild79`),
- );
+ checkNodeScrolling();
+ });
- component.highlightedItem = 'Root node';
+ it('scrolls selected node if not in view even if pinned', () => {
+ component.pinnedItems = [
+ assertDefined(component.tree.getChildByName('Child78')),
+ assertDefined(component.tree.getChildByName('Child79')),
+ ];
fixture.detectChanges();
-
- const spy = spyOn(treeNode, 'scrollIntoView').and.callThrough();
- component.highlightedItem = '79 Child79';
- fixture.detectChanges();
- expect(spy).toHaveBeenCalledTimes(1);
-
- component.highlightedItem = '78 Child78';
- fixture.detectChanges();
- expect(spy).toHaveBeenCalledTimes(1);
+ checkNodeScrolling();
});
it('sets initial expanded state to true by default for leaf', () => {
fixture.detectChanges();
- expect(assertDefined(component.treeComponent).isExpanded()).toBeTrue();
+ checkIsExpanded(true);
});
it('sets initial expanded state to true by default for non root', () => {
- component.tree = component.tree.getAllChildren()[0];
+ const child = component.tree.getAllChildren()[0] as UiHierarchyTreeNode;
+ const innerChild = UiHierarchyTreeNode.from(
+ new HierarchyTreeBuilder()
+ .setId('InnerChild')
+ .setName('child')
+ .setChildren([])
+ .build(),
+ );
+ child.addOrReplaceChild(innerChild);
+ component.tree = child;
fixture.detectChanges();
- expect(assertDefined(component.treeComponent).isExpanded()).toBeTrue();
+ checkIsExpanded(true);
});
it('sets initial expanded state to false if collapse state exists in store', () => {
component.useStoredExpandedState = true;
fixture.detectChanges();
- const treeComponent = assertDefined(component.treeComponent);
// tree expanded by default
- expect(treeComponent.isExpanded()).toBeTrue();
+ checkIsExpanded(true);
// tree collapsed
- treeComponent.toggleTree();
- fixture.detectChanges();
- expect(treeComponent.isExpanded()).toBeFalse();
+ doubleClickFirstNode();
+ checkIsExpanded(false);
// tree collapsed state retained
component.tree = makeTree();
fixture.detectChanges();
- expect(treeComponent.isExpanded()).toBeFalse();
+ checkIsExpanded(false);
});
it('renders show state button if applicable', () => {
@@ -313,6 +314,35 @@
);
}
+ function doubleClickFirstNode() {
+ assertDefined(
+ htmlElement.querySelector<HTMLElement>('tree-node'),
+ ).dispatchEvent(new MouseEvent('click', {detail: 2}));
+ fixture.detectChanges();
+ }
+
+ function checkIsExpanded(isExpanded: boolean) {
+ expect(htmlElement.querySelector<HTMLElement>('.children')?.hidden).toEqual(
+ !isExpanded,
+ );
+ }
+
+ function checkNodeScrolling() {
+ const treeNode = assertDefined(htmlElement.querySelector(`#nodeChild79`));
+ const spy = spyOn(treeNode, 'scrollIntoView').and.callThrough();
+
+ component.highlightedItem = 'Root node';
+ fixture.detectChanges();
+
+ component.highlightedItem = '79 Child79';
+ fixture.detectChanges();
+ expect(spy).toHaveBeenCalledTimes(1);
+
+ component.highlightedItem = '78 Child78';
+ fixture.detectChanges();
+ expect(spy).toHaveBeenCalledTimes(1);
+ }
+
@Component({
selector: 'host-component',
template: `
@@ -320,7 +350,7 @@
<tree-view
[node]="tree"
[isFlattened]="isFlattened"
- [isPinned]="false"
+ [pinnedItems]="pinnedItems"
[highlightedItem]="highlightedItem"
[useStoredExpandedState]="useStoredExpandedState"
[itemsClickable]="true"
@@ -342,6 +372,7 @@
isFlattened = false;
useStoredExpandedState = false;
rectIdToShowState: Map<string, RectShowState> | undefined;
+ pinnedItems: Array<UiHierarchyTreeNode | UiPropertyTreeNode> = [];
constructor() {
this.tree = makeTree();
diff --git a/tools/winscope/src/viewers/components/tree_node_component.ts b/tools/winscope/src/viewers/components/tree_node_component.ts
index 9de6728..820f4ac 100644
--- a/tools/winscope/src/viewers/components/tree_node_component.ts
+++ b/tools/winscope/src/viewers/components/tree_node_component.ts
@@ -106,7 +106,7 @@
@Output() readonly toggleTreeChange = new EventEmitter<void>();
@Output() readonly rectShowStateChange = new EventEmitter<void>();
- @Output() readonly expandTreeChange = new EventEmitter<boolean>();
+ @Output() readonly expandTreeChange = new EventEmitter<void>();
@Output() readonly pinNodeChange = new EventEmitter<UiHierarchyTreeNode>();
collapseDiffClass = '';
@@ -123,8 +123,11 @@
}
ngOnChanges() {
+ if (!this.isInPinnedSection && this.isSelected) {
+ this.expandTreeChange.emit();
+ }
this.collapseDiffClass = this.updateCollapseDiffClass();
- if (!this.isPinned && this.isSelected && !this.isNodeInView()) {
+ if (!this.isInPinnedSection && this.isSelected && !this.isNodeInView()) {
this.el.scrollIntoView({block: 'center', inline: 'nearest'});
}
}
@@ -184,7 +187,7 @@
this.pinNodeChange.emit(assertDefined(this.node) as UiHierarchyTreeNode);
}
- updateCollapseDiffClass() {
+ updateCollapseDiffClass(): string {
if (this.isExpanded) {
return '';
}
@@ -199,7 +202,7 @@
return '';
}
if (childrenDiffClasses.size === 1) {
- const diffType = childrenDiffClasses.values().next().value;
+ const diffType = assertDefined(childrenDiffClasses.values().next().value);
return diffType;
}
return DiffType.MODIFIED;
diff --git a/tools/winscope/src/viewers/components/tree_node_component_test.ts b/tools/winscope/src/viewers/components/tree_node_component_test.ts
index b5cf8ae..9df330b 100644
--- a/tools/winscope/src/viewers/components/tree_node_component_test.ts
+++ b/tools/winscope/src/viewers/components/tree_node_component_test.ts
@@ -117,6 +117,24 @@
expect(spy).toHaveBeenCalled();
});
+ it('can trigger tree expansion if node is selected and not in pinned section', () => {
+ const spy = spyOn(
+ assertDefined(component.treeNodeComponent).expandTreeChange,
+ 'emit',
+ );
+ component.isInPinnedSection = true;
+ component.isSelected = true;
+ fixture.detectChanges();
+ expect(spy).not.toHaveBeenCalled();
+
+ component.isSelected = false;
+ component.isInPinnedSection = false;
+ fixture.detectChanges();
+ component.isSelected = true;
+ fixture.detectChanges();
+ expect(spy).toHaveBeenCalledTimes(1);
+ });
+
it('assigns diff css classes to expand tree button', () => {
const expandButton = assertDefined(
htmlElement.querySelector<HTMLElement>('.expand-tree-btn'),
@@ -221,7 +239,7 @@
[node]="node"
[isExpanded]="isExpanded"
[isPinned]="false"
- [isInPinnedSection]="false"
+ [isInPinnedSection]="isInPinnedSection"
[isSelected]="isSelected"
[isLeaf]="isLeaf"></tree-node>
`,
@@ -238,6 +256,7 @@
isSelected = false;
isLeaf = false;
isExpanded = false;
+ isInPinnedSection = false;
@ViewChild(TreeNodeComponent)
treeNodeComponent: TreeNodeComponent | undefined;