Skip to content

Tree Specification

Stefan Ivanov edited this page Feb 19, 2021 · 37 revisions

IgxTree Specification

Contents

  1. Overview

  2. User Stories

  3. Functionality

    3.1 End-user Expereince

    3.2 Developer Experience

    3.3 Globalization/Localization

    3.4 Keyboard Navigation

    3.5 API

  4. Test scenarios

    4.1. Automation

    4.2. Manual tests

  5. Accessibility

  6. Assumptions and Limitations

  7. References

Owned by

Team Name

Developer Name

Stefan Ivanov

Requires approval from

  • Peer Developer Name | Date:
  • Stefan Ivanov | Date: 15 Feb 2021

Signed off by

  • Product Owner Name | Date:
  • Platform Architect Name | Date:

Revision History

Version User Date Notes
1 Viktor Slavov, Stefan Ivanov Feb 15, 2021 Initial Draft
2 Viktor Slavov Feb 18, 2021 Fix Link Formatting

Objectives

The IgxTreeComponent is a component allowing a developer user to show a set of nodes in a hierarchical fashion allowing his end-users to use them for easily navigating structured information e.g. a site map of a website or documentation topics. The igxTreeComponent should primarily be used as a navigational component when visualizing a nested data structure.

Acceptance criteria

Must-have before we can consider the feature a sprint candidate

...

End-user Stories

The tree should allow me, as an end-user, to understand the relationships between the various nodes and use it to navigate the hierarchy of content.

End-user stories:

  • Story 1: As an end-user, I want to have the nodes organized in a clear hierarchy, so that I can understand their relationship with one another.
  • Story 2: As an end-user, I want to have the nodes organized in a clear hierarchy, so that I can easily expand/collapse them and select the node I am looking for.
  • Story 3: As an end-user, I want to be able to expand/collapse nodes, so that I can create a state with the most viable information for me on my screen.
  • Story 4: As an end-user, I want to have animations when nodes are expanded/collapsed, so that their relationship with one another is more obvious.
  • Story 5: As an end-user, I want to have a clear indication of the selected (active) node, so that I have a better understanding of the information I'm looking at on the rest of my screen.

  • Story 6: As an end-user, I want to have a clear indication if some/all/none of the child nodes of a given node are selected, so that I have a clear understanding of the current selection of nodes.
  • Story 7: As an end-user, I want to select a single node, so that I navigate to the content it offers.
  • Story 8: As an end-user, I want to select multiple nodes, so that I can perform an operation on all of them at once.

  • Story 9: As an end-user, I want to have a cascading selection of a parent node implicitly selecting all of its children, grandchildren etc., so that I can perform an operation on all of them at once.

  • Story 10: As an end-user, I want to be able to quickly expand/collapse/select a node with mouse and keyboard, so that I have a variety of convenient ways to interact with the hierarchy.
  • Story 11: As an end-user, I want to change a node's parent by dragging it to another one, so that I can reorganize the hierarchy.

  • Story 12: As an end-user, I want to move a node by dragging it above/below a sibling one, so that I can reorder nodes at the same level of the hierarchy.

  • Story 13: As an end-user, I want to have a clear visual indication when dragging a node, so that I can have the right expectancy of where a node would be placed upon dropping it.

  • Story 14: As an end-user, I want to have a clear visual indication when a node is loading its information/children, so that I have expectancy in scenarios with remote data/load on demand.

Developer Stories

The tree should allow me, as a developer, to visually represent the relationship between nodes in my data and use it to provide means for my users to navigate and edit it.

Developer Stories

  • Story 1: As a developer, I want to be able to easily create a tree view declaring the node hierarchy.
  • Story 2: As a developer, I want to have full control over a node's content, as well as its child nodes (declarative approach).
  • Story 3: As a developer, I want to have an easy way to find a node inside of the control with OOB API.
  • Story 4: As a developer, I want to expand/collapse one or more nodes programmatically.
  • Story 5: As a developer, I want to enable/disable the selection of a single node.
  • Story 6: As a developer, I want to enable/disable the selection of multiple nodes.
  • Story 7: As a developer, I want to enable/disable cascading selection of nodes so that selecting a node, selects all nodes in the tree below it as well and the state of each node reflects the cumulative one of its children.
  • Story 8: As a developer, I want to set a node's selection state programmatically.
  • Story 9: As a developer, I want to be able to perform custom handling on user interaction - node selection, expansion/collapsing.
  • Story 10: As a developer, I want to be able to control the animation when nodes are expanded/collapsed.
  • Story 11: As a developer, I expect that manipulating the data source would update the visualized tree accordingly.
  • Story 12: As a developer, I want to be able to load data on demand to expedite initial load times.
  • Story 13: As a developer, I want to be able to ensure accessibility e.g. via proper aria roles in different cases such as node having a link.

Describe behavior, design, look and feel of the implemented feature. Always include visual mock-up

3.1. End-User Experience

The Tree component should support the three display densities available for other components comfortable, cosy and compact. They come with distinct node heights and padding.

** All use cases and variants above are also available for hand-off

3.2. Developer Experience

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);
  }
}

3.3. Globalization/Localization

Describe any special localization requirements such as the number of localizable strings, regional formats

3.4. Keyboard Navigation

The 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

Keys. Description
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 moves to the next visible node. If on the LAST node, moves to the next element w/ tabIndex outside of the tree.
SHIFT + TAB moves to the previous visible node. If on the LAST node, moves to the next 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. If on a leaf node, moves to the next visible node. If on a collapsed parent node, expands it.
ARROW LEFT on an expanded parent node, collapses it. If on a child node, moves to its parent node.
SPACE toggles selection of the current node.
SHIFT + CLICK when multiple selection is enabled, toggles selection of all nodes between the active one and the one clicked while holding SHIFT.
CTRL + CLICK when multiple selection is enabled, add the clicked node to the selected nodes collection or remove it if already selected.

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 Returns
findNodes Returns an array of nodes which match the specified data. [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). Returns null if no nodes match data: T| comparer?: (data: T, node: IgxTreeNodeComponent<T>) => boolean IgxTreeNodeComponent<T>[] | null
selectAll Sets the specified nodes as selected, clearing the previous selection. If no nodes passed, selects all nodes nodes?: IgxTreeNodeComponent[] void
expandAll Sets the specified nodes as expanded. If no nodes passed, expands all parent nodes nodes?: IgxTreeNodeComponent[] void

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