Skip to content

Tree Specification

Viktor Slavov edited this page Feb 9, 2021 · 37 revisions

IgxTree Specification

Contents

  1. Overview

  2. User and Developer Stories

  3. Functionality

    3.1. Keyboard Navigation

    3.2. API

  4. Test scenarios

    4.1. Automation

    4.2. Manual tests

  5. ARIA support

  6. Assumptions and Limitations

  7. References

Revision History

Version User Date Notes
0.1 Viktor Slavov Jan 27, 2021 Initial Draft
0.2 Viktor Slavov Feb 8, 2021 Update Initial Draft
0.3 Viktor Slavov Feb 9, 2021 Add Test Scenario Outline

Objectives

The IgxTreeComponent is a component with which users can easily represent hierarchical data in an easy-to-navigate way. The igxTreeComponent should primarily be used as a navigational component when visualizing a nested data structure.

User Stories

As an end-user, I want to:

  • have hierarchical data visualized in a pleasing and easy-to-navigate way
  • be able to expand and collapse tree nodes so I can always have only the most viable information visible on my screen
    • have a visually pleasing transition between opened and closed node states
  • have a node highlighted to better keep track of the information I'm looking at (active node)
  • be able to quickly navigate through and expand/collapse nodes of the tree using a mouse
  • be able to quickly navigate through and expand/collapse nodes of the tree using a keyboard
  • mark a single node as selected, using my mouse and a specific UI part
  • mark a single node as selected, using my keyboard
  • mark a single node as selected, also marking all of its descendants
  • have a visual indicator if:
    • a node is selected
    • if all of a node's descendants are marked as selected
    • if some of a node's descendants are marked as selected
    • if none of a node's descendants are marked as selected
  • be able to rearrange data in via drag and drop, considering that I can achieve the following:
    • move a node to a different parent
    • rearrange nodes under the same parent
    • have a visual indicator of the expected result of my drag-and-drop interaction
  • be able to discern when a node's information is being loaded (load-on-demand scenario)

Developer Stories

As a developer, I want to:

  • be able to quickly create a tree view declaring the node hierarchy
  • have full control over a node's content, as well as it's child nodes (declarative)
  • have an easy way to find a node inside of the control with OOB API
  • expand/collapse a single or multiple nodes programmatically
  • enable/disable selection for nodes and differentiate between selection scenarios:
    • selecting a node marks only the focused node for as selection
    • selecting a node marks the focused node and all child nodes as selected
  • set a node's selection state programmatically
  • be able to perform custom handling on user interaction - node selection, toggling
  • be able to control the opening / closing animation of nodes
  • be able to manipulate the visualized tree by manipulating the my data source
  • be able to load data on demand to expediate initial load times
  • be able to ensure proper aria roles in the different cases (e.g. node has a link)

Examples

Simple tree iterating data:

<!-- Standard example -->
<igx-tree>
	<igx-tree-node *ngFor="let nodes of data" [data]="node" [expanded]="isNodeExpaded(node)" [selected]="isNodeSelected(node)">
		{{ node.text }}
		<img [src]="node.image" alt="node.imageAlt" />
		<igx-tree-node *ngFor="let child of node.children" [data]="child" [expanded]="isNodeExpaded(child)" [selected]="isNodeSelected(child)">
      {{ child.text }}
		</igx-tree-node>
	</igx-tree-node>
</igx-tree>

Simple tree w/ links

When a node should render a link, add igxTreeNodeLink to the a tag. This will make sure the proper aria role is assigned to the node's DOM elements.

<igx-tree>
	<igx-tree-node *ngFor="let nodes of data" [data]="node" [expanded]="isNodeExpaded(node)" [selected]="isNodeSelected(node)">
		{{ node.text }}
		<img [src]="node.image" alt="node.imageAlt" />
		<igx-tree-node *ngFor="let child of node.children" [data]="child">
				<a igxTreeNodeLink href="child.url" target="_blank">{{ child.text }}</a>
		</igx-tree-node>
	</igx-tree-node>
</igx-tree>

Tree w/ hardcoded nodes

<!-- Simple example with hardcoded nodes -->
<igx-tree>
	<igx-tree-node [expanded]="true" [selected]="false">
		I am a parent node 1
		<img [src]="hard_coded_src.webb" alt="Alt Text" />
		
		<igx-tree-node [expanded]="true" [selected]="false">
			I am a child node 1
			<igx-tree-url-node [url]="https://google.com">
				I am a child node of the child
			</igx-tree-url-node>
		</igx-tree-node>
	</igx-tree-node>
	
	<igx-tree-node [expanded]="false" [selected]="false">
		I am a parent node 2
		<img [src]="hard_coded_src.webb" alt="Alt Text" />
		
		<igx-tree-node [expanded]="false" [selected]="false">
			I am a child node 1
		</igx-tree-node>
	</igx-tree-node>

	<igx-tree-node [expanded]="false" [selected]="false">
		I am a parent node 3
		<img [src]="hard_coded_src.webb" alt="Alt Text" />
		
		<igx-tree-node [expanded]="false" [selected]="false">
			I am a child node 1
		</igx-tree-node>
	</igx-tree-node>
</igx-tree>

Finding nodes

When finding nodes, you can pass a custom comparer function in order to find the data

<igx-tree>
	<igx-tree-node *ngFor="let nodes of data" [data]="node" [expanded]="isNodeExpaded(node)" [selected]="isNodeSelected(node)">
		{{ node.text }}
		<img [src]="node.image" alt="node.imageAlt" />
		<igx-tree-node *ngFor="let child of node.children" [data]="child" [expanded]="isNodeExpaded(child)" [selected]="isNodeSelected(child)">
      {{ child.text }}
		</igx-tree-node>
	</igx-tree-node>
</igx-tree>
export class MyTreeViewComponent {
  public data: { [key: string]: any, valueKey: string } = MY_DATA;
  @ViewChild(IgxTreeComponent, { read: IgxTreeComponent })
  public tree;

  findNode(valueKey: string) {
    const comparer: (nodeData: any, node: IgxTreeNodeComponent) => boolean =
      (data: any, node: IgxTreeNodeComponent) => nodeData.valueKey === data;
    this.tree.findData(valueKey, comparer);
  }
}

End User Experience

Scenarios:

  • TBD
  • TBD
  • TBD
  • TBD

Node navigation

Keyboard can be used to navigate through all nodes in the tree. FIRST and LAST node refers to the respective visible node WITHOUT expanding/collapsing any existing node

  • ARROW DOWN moves to the next visible node. Does nothing if on the LAST node.
  • ARROW UP moves to the previous visible node. Does nothing if one the FIRST node.
  • TAB performs the same as ARROW DOWN. If on the LAST node, moves to the next element w/ tabIndex outside of the tree.
  • SHIFT + TAB performs the same as ARROW UP. If on the FIRST node, moves to the previous element w/ tabIndex outside of the tree.
  • HOME navigates to the FIRST node.
  • END navigates to the LAST node.
  • ARROW RIGHT on an expanded parent node navigates to the first child of the node.
  • ARROW LEFT on a child node navigates to the parent node.

Toggling nodes

Node expanded state can be toggled via keyboard interaction w/ ARROW KEYS.

  • ARROW LEFT collapses and expanded node. If the node is already collapsed and is not a child node itself - does nothing.
  • ARROW RIGHT expands a collapsed node. If the node is already expanded - moves to the first child node.

Selection

When selection is enabled, node can be selected via keyboard interaction.

  • SPACE toggles a node's selection state (selected/deselected).

IgxTreeNodeComponent

Accessors

Get

Name Description Type
id The unique id of the tree node. Used for state management and selection. string
parentNode The parent node of the current node (if any) IgxTreeNodeComponent
level The "depth" of the node. If root node - 0, if a child of parent - parent.level + 1 number
tree A reference to the tree the node is a part of IgxTree
children A collection of child nodes. null if node does not have children any[] | null
role The role attribute assigned to the rendered igx-tree-node DOM element string

Properties

Name Description Type
expanded If the node is expanded boolean | null
selected If the node is selected boolean
data The data entry that the node is visualizing. Required for searching through nodes any

IgxTreeComponent

Accessors

Get

Name Description Type
id The unique id of tree (not bound to the id html attribute ) string

Properties

Name Description Type
selection The selection state of the tree "None"
animationSettings The setting for the animation when opening / closing a node { openAnimation: AnimationMetadata, closeAnimation: AnimationMetadata }
singleBranchExpand Whether a single or multiple of a parent's child nodes can be expanded. Default is false boolean

Methods

Name Description Parameters
findNode Returns a reference to a node with specified [data] as an object reference. [data] input should be specified in order to find nodes. A custom comparer function can be specified for custom search (e.g. by a specific value key) data: any | comparer?: (data: any, node: IgxTreeNodeComponent) => boolean
toggle Toggles a specified node node: IgxTreeNodeComponent
expand Expands a specified node node: IgxTreeNodeComponent
select Marks the specified node as selected. Behavior depends on specified selection mode. node: IgxTreeNodeComponent
deselect Deselects the specified node as selected. Behavior depends on specified selection mode. node: IgxTreeNodeComponent
setSelection Sets the specified nodes as selected, clearing the previous selection node: IgxTreeNodeComponent[]

Events

Name Description Cancelable Parameters
selection Emitted when item selection is changing, before the selection completes true owner: IgxTreeComponent, selectedNodes: IgxTreeComponent[]
nodeOpening Emitted before the a node is opened. true node: IgxTreeNodeComponent, owner: IgxTreeComponent, cancel: boolean
nodeOpened Emitted after the a node is opened. false node: IgxTreeNodeComponent, owner: IgxTreeComponent
nodeClosing Emitted before the a node is closed. true node: IgxTreeNodeComponent, owner: IgxTreeComponent, cancel: boolean
nodeClosed Emitted after the a node is closed. false node: IgxTreeNodeComponent, owner: IgxTreeComponent
nodeDragStart Emitted when a node drag is stared true IDragStartEventArgs & { node: IgxTreeNodeComponent }
nodeDragMove Emitted when the cursor moves while a node dragged true IDragMoveEventArgs & { node: IgxTreeNodeComponent }
nodeDrop Emitted when a node is dropped false IDragDropEventArgs & { node: IgxTreeNodeComponent }

Basic

  • Should render tree w/ nodes
  • Should only render nodes inside of tree (no other elements)
  • Should support multiple levels of nesting (igx-tree-node under igx-tree-node)
  • Should not render collapsed node's children
  • Should not render expand indicator if node has no children
  • Should not render default select indicator if selection mode is 'None'
  • Should render default indicator for expansion properly depending on node state
  • Should render default select marker properly depending on node state
  • Should accept template for expansion indicator
  • Should accept template for select marker

Expand/Collapse

  • Should collapse nodes
  • Should expand nodes
  • Should collapse all child nodes when collapsing a node
  • Should collapse sibling nodes when singleBranchExpand === true

Selection

  • Should be able to change selection type to all 3 options ('None' (default), 'BiState', 'Cascading')

BiState

  • Should select nodes
  • Should deselect nodes
  • Should be able to set the node selection and clear the previous selection (setSelection)

Cascading

  • Partially selected parents should have the default indicator render as indeterminate
  • Selecting a node should select its children
  • Selecting a child should mark its parent node as partially selected
  • Selecting the last non-selected child should mark the parent as selected
  • Deselecting a selected child under a selected parent should mark the parent as partially selected

Keyboard Navigation

See navigation

Aria

See aria-support

  • Rendered tree and nodes have expected aria attributes
  • Passing a link child in a node w/ igxTreeNodeLink directive should change aria roles of the the node

Tree Aria example

An igx-tree will have role="tree". aria-labelledby should be manually added if there is a label/ heading associated w/ the tree.

An igx-tree-node's child will be held in a container w/ role="group".

An igx-tree-node will have role="treeitem" if there is no link w/ igxTreeNodeLink directive specified in it. If there is such a link (a tag), the role="treeitem" will go on the link (as it is the interactable component of the node).

A node's expanded state will be properly reflected in the node's aria-expanded attribute.

The igx-tree does not support recursively creating the igx-tree-nodes via template. This is a limitation in place because of a bug in the Angular framework. All of the nodes should be declared manually, meaning if you intend to visualize a very deep hierarchy, this would impact the size of your template file. The tree is intended to be primarily used as a layout / navigational component. If a hierarchical data source with numerous levels of depth and homogenous data needs to be visualized, you could use the igx-tree-grid

IgxTree Issue

Clone this wiki locally