-
Notifications
You must be signed in to change notification settings - Fork 160
Tree Specification
Team Name
Developer Name
Stefan Ivanov
- Peer Developer Name | Date:
- Stefan Ivanov | Date: 15 Feb 2021
- Product Owner Name | Date:
- Platform Architect Name | Date:
Version | User | Date | Notes |
---|---|---|---|
1 | Viktor Slavov, Stefan Ivanov | Feb 15, 2021 | Initial Draft |
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.
Must-have before we can consider the feature a sprint candidate
...
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.
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.
<!-- 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>
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>
<!-- 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>
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);
}
}
- TBD
- TBD
- TBD
- TBD
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.
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.
When selection is enabled, node can be selected via keyboard interaction.
- SPACE toggles a node's selection state (selected/deselected).
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 |
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 |
Get
Name | Description | Type |
---|---|---|
id | The unique id of tree (not bound to the id html attribute ) |
string |
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 |
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 visible (expanded parents and leaf) nodes | nodes?: IgxTreeNodeComponent[] |
void |
expandAll | Sets the specified nodes as expanded. If no nodes passed, expands all visible parent nodes | nodes?: IgxTreeNodeComponent[] |
void |
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 } |
- 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
- Should collapse nodes
- Should expand nodes
- Should collapse all child nodes when collapsing a node
- Should collapse sibling nodes when
singleBranchExpand === true
- Should be able to change selection type to all 3 options ('None' (default), 'BiState', 'Cascading')
- Should select nodes
- Should deselect nodes
- Should be able to set the node selection and clear the previous selection (
setSelection
)
- 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
See navigation
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
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