Tree Specification - IgniteUI/igniteui-webcomponents Wiki

Tree 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

  5. Accessibility

  6. Assumptions and Limitations

  7. References

Owned by

Team Name: Astrea

Developer Name: Martin Evtimov, Monika Kirkova

Designer Name: Marin Popov

Requires approval from

  • Peer Developer Name | Date:
  • Design Manager Stefan Ivanov| Date: 19 Jan 2022

Signed off by

  • Radoslav Mirchev| Date:2022-05-20
  • Radoslav Karaivanov | Date: 2022-05-19

Revision History

Version Users Date Notes
1

1. Overview

The <igc-tree> allows users to represent hierarchical data in a tree-view structure, maintaining parent-child relationships, as well as to define static tree-view structure without a corresponding data model.

<igc-tree>
    ${data.map((x) => html`
        <igc-tree-item .value=${x} .expanded=${x.expanded} .selected=${x.selected} .label=${x.value}>
              ${x.children.map((y) => html`
                  <igc-tree-item .value=${y} .expanded=${y.expanded} .selected=${y.selected}>
                      <img slot="label" src=${y.image} alt=${y.imageAlt}>
                  </igc-tree-item>
              `
        </igc-tree-item>
    `
</igc-tree>

Objectives

The <igc-tree> should primarily be used as a navigational component when visualizing a nested data structure. The control is not data-bound and takes a declarative approach, giving users more control over what is being rendered.

Acceptance criteria

The tree should have the following features out-of-the-box

  1. Rendering nested items in a hierarchical fashion, maintaining a parent-child relationship
  2. Expanding and collapsing items, so all crucial information can be clearly visible at all times
  3. Disabled items which do not support any user interaction (e.g. permission-based tree view)
  4. Active items - a node marked as "active" is highlighted w/ a specific style (e.g. the current location on a site map)
  5. Selection
    • 5.1. None - no selection through user input. API selection manipulation is still possible
    • 5.2. Multiple - each item has a checkbox through which it can be selected via user input. Each item has two states - selected or not. Supports multiple selection.
    • 5.3. Cascade - selecting an item selects its child items. Partially selected collections mark the resp. items as indeterminate.
  6. Keyboard navigation, supporting all of the previously mention features
  7. Accessibility features - all items should have proper aria roles and be fully readable and traversable via screen readers

2. User Stories

Developer stories:

  • Story 1: As a developer, I want to be able to easily create a tree view declaring the item hierarchy.
  • Story 2: As a developer, I want to have full control over an item's content, as well as its child items (declarative approach).
  • Story 3: As a developer, I want to have an easy way to find an item inside of the control with out-of-the-box API.
  • Story 4: As a developer, I want to expand/collapse one or more items programmatically.
  • Story 5: As a developer, I want to enable/disable the selection of a single item.
  • Story 6: As a developer, I want to enable/disable the selection of multiple items.
  • Story 7: As a developer, I want to enable/disable cascade selection of items so that selecting an item, selects all items in the tree below it as well and the state of each item reflects the cumulative one of its children.
  • Story 8: As a developer, I want to set an item's selection state programmatically.
  • Story 9: As a developer, I want to set an item's selection state programmatically, even when selection through user interaction is disabled.
  • Story 10: As a developer, I want to be able to perform custom handling on user interaction - item selection, expansion/collapsing.
  • Story 11: As a developer, I want to be able to ensure accessibility e.g. via proper aria roles in different cases.
  • Story 12: As a developer, I want to be able to mark items as disabled, so they are ignored in user interaction.
  • Story 13: As a developer, I want to mark an item as the tree's active item, styling it in a pre-defined way and marking it as a point of interest to the users.
  • Story 14: As a developer, I want to be able to distinguish when the active item changes through user interaction.
  • Story 15: As a developer, I want to be able to bind an item's expansion state to a data model and ensure consistency between the two.
  • Story 16: As a developer, I want to be able to bind an item's selection state to a data model and ensure consistency between the two.
  • Story 17: As a developer, I want to be able to template the expand/collapse indicators for the tree.
  • Story 18: As a developer, I want to be able to specify how many expanded sibling items there can be (singleBranchExpand input).

End-user stories:

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

  • Story 1: As an end-user, I want to have the items 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 items organized in a clear hierarchy, so that I can easily expand/collapse them to get to the items I am looking for.
  • Story 3: As an end-user, I want to be able to expand/collapse items, 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 a clear indication of the active item, so that I have a better understanding of the information I'm looking at on the rest of my screen.

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

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

  • Story 9: As an end-user, I want to be able to quickly expand/collapse/select an item with a mouse and keyboard, so that I have a variety of convenient ways to interact with the hierarchy.

3. Functionality

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

3.1. End-User Experience

The IgcTreeComponent should support the three display sizes available for other components - 'small', 'medium' and 'large'(default). They come with distinct item heights and padding.

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

3.2. Developer Experience

Simple tree iterating data:

Developers are able to declare the tree and its items by specifying the item hierarchy and iterating through a data set:

<!-- Standard example -->
<igc-tree>
    ${data.map((x) => html`
        <igc-tree-item .value=${x} .expanded=${x.expanded} .selected=${x.selected} .label=${x.value}>
              ${x.children.map((y) => html`
                  <igc-tree-item .value=${y} .expanded=${y.expanded} .selected=${y.selected}>
                      <img slot="label" src=${y.image} alt=${y.imageAlt}>
                  </igc-tree-item>
              `
        </igc-tree-item>
    `
</igc-tree>

Simple tree w/ links

When a node should render a link, button or some tabbable element, enable the tree hasFocusableContent prop. This will make sure the proper aria role is assigned to the node's DOM elements.

<igc-tree hasFocusableContent>
    <igc-tree-item>
        <p slot="label">
            <a href="https://www.infragistics.com/" target="_blank">Infragistics</a>
        </p>
    </igc-tree-item>
</igc-tree>

Tree w/ hardcoded items

To render a tree you do not necessarily need a data set - developers can create individual items w/o binding them to data:

<!-- Simple example with hardcoded items -->
<igc-tree .selection="multiple">
    <igc-tree-item expanded>
        <div slot="label">
            I am a parent item 1
	    <img src="hard_coded_src.webb" alt="Alt Text">
        </div>
	<igc-tree-item expanded .label="I am a child item 1">
	</igc-tree-item>
    </igc-tree-item>

    <igc-tree-item selected>
        <div slot="label">
            I am a parent item 2
	    <img src="hard_coded_src.webb" alt="Alt Text">
        </div>
	<igc-tree-item .label="I am a child item 1">
	</igc-tree-item>
    </igc-tree-item>
</igc-tree>

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 items in the tree. The control distinguishes two states - focused and active. The focused item is where all events are fired and from where navigation will begin/continue. Focused items are marked with a distinct style. The active item, in most cases, is the last item on which user interaction took place. Active items also have a distinct style. Active items can be used to better accent an item in that tree that indicates the app's current state (e.g. a current route in the app when using a tree as a navigation component).

In most cases, moving the focused item also moves the active item.

When navigating to items that are outside of view, if the tree (igc-tree tag) has a scrollbar, scrolls the focused item into view. If the target item is outside of view AND if the tree (igc-tree tag) has a scrollbar, scrolls the focused item into view. When initializing the tree and an item is marked as active, if that item is outside of view AND if the tree (igc-tree tag) has a scrollbar, scrolls the activated item into view.

FIRST and LAST item refers to the respective visible item WITHOUT expanding/collapsing any existing item.

Disabled items are not counted as visible items for the purpose of keyboard navigation.

Keys Description Activates Item
ARROW DOWN Moves to the next visible item. Does nothing if on the LAST tree item. true
CTRL + ARROW DOWN Performs the same as ARROW DOWN. false
ARROW UP Moves to the previous visible item. Does nothing if on the FIRST tree item. true
CTRL + ARROW UP Performs the same as ARROW UP. false
TAB Navigate to the next focusable element on the page* false
SHIFT + TAB Navigate to the previous focusable element on the page* false
HOME Navigates to the FIRST tree item. true
END Navigates to the LAST tree item. true
ARROW RIGHT On an expanded parent item, navigate to the first child of the item. If on a collapsed parent item, expand it. true
ARROW LEFT On an expanded parent item, collapses it. If on a child item, moves to its parent item. true
SPACE Toggles selection of the current tree item. Marks the tree item as active. true
* Expand the tree item and all sibling items on the same level w/ children true
CLICK Focuses the tree item true

When selection is enabled, end-user selection of tree items is only allowed through the displayed checkbox. Since both selection types allow multiple selection, the following mouse + keyboard interaction is available:

Combination Description Activates Item
SHIFT + CLICK / SPACE when multiple selection is enabled, toggles selection of all tree items between the active one and the one clicked while holding SHIFT. true

3.5 API

IgcTreeComponent

The tree allows users to represent hierarchical data in a tree-view structure, maintaining parent-child relationships, as well as to define static tree-view structure without a corresponding data model.

Mixins: SizableMixin, EventEmitterMixin

Properties

Property Attribute Modifiers Type Default Description
dir dir Direction "auto" The direction attribute of the control.
items readonly IgcTreeItemComponent[] Returns all of the tree's items.
selection selection "multiple" | "none" | "cascade" "none" The selection state of the tree.
singleBranchExpand single-branch-expand boolean false Whether a single or multiple of a parent's child items can be expanded.
size size "small" | "medium" | "large" "large" Determines the size of the component.

Methods

Method Type Description
collapse (items?: IgcTreeItemComponent[] | undefined): void Collapses all of the passed items.
If no items are passed, collapses ALL items.
deselect (items?: IgcTreeItemComponent[] | undefined): void Deselect all items if the items collection is empty. Otherwise, deselect the items in the items collection.
expand (items?: IgcTreeItemComponent[] | undefined): void Expands all of the passed items.
If no items are passed, expands ALL items.
select (items?: IgcTreeItemComponent[] | undefined): void Select all items if the items collection is empty. Otherwise, select the items in the items collection.

Events

Event Description
igcItemActivated Emitted when the tree's active item changes.
igcItemCollapsed Emitted when tree item is collapsed.
igcItemCollapsing Emitted when tree item is about to collapse.
igcItemExpanded Emitted when tree item is expanded.
igcItemExpanding Emitted when tree item is about to expand.
igcSelection Emitted when item selection is changing, before the selection completes.

Slots

Name Description
Renders the tree items inside default slot.

IgcTreeItemComponent

The tree-item component represents a child item of the tree component or another tree item.

Properties

Property Attribute Modifiers Type Default Description
active active boolean false Marks the item as the tree's active item.
disabled disabled boolean false Get/Set whether the tree item is disabled. Disabled items are ignored for user interactions.
expanded expanded boolean false The tree item expansion state.
label label string "" The tree item label.
level number 0 The depth of the item, relative to the root.
loading loading boolean false To be used for load-on-demand scenarios in order to specify whether the item is loading data.
parent IgcTreeItemComponent | null null The parent item of the current tree item (if any)
path readonly IgcTreeItemComponent[] The full path to the tree item, starting from the top-most ancestor.
selected selected boolean false The tree item selection state.
tree IgcTreeComponent | undefined A reference to the tree the item is a part of.
value value "undefined" The value entry that the tree item is visualizing. Required for searching through items.

Methods

Method Type Description
collapse (): void Collapses the tree item.
expand (): void Expands the tree item.
getChildren (options?: { flatten: boolean; }): IgcTreeItemComponent[] Returns a collection of child items.
If the parameter value is true returns all tree item's direct children,
otherwise - only the direct children.
toggle (): void Toggles tree item expansion state.

Slots

Name Description
Renders nested tree-item component.
indentation Renders the container (by default the space) before the tree item.
indicator Renders the expand indicator container.
label Renders the tree item container.
loading Renders the tree item loading indicator container.

CSS Shadow Parts

Part Description
active Indicates an active state. Applies to wrapper.
focused Indicates focused state. Applies to wrapper.
indicator The expand indicator of the tree item.
label The tree item content.
select The checkbox of the tree item when selection is enabled.
selected Indicates selected state. Applies to wrapper.
text The tree item displayed text.
wrapper The wrapper for the tree item.

4. Test Scenarios

Automation

Basic

  • Should render tree w/ items
  • Verify tree item slots are rendered successfully.
  • Verify the elements defined in the slots are displayed.
  • Should support multiple levels of nesting (igc-tree-item under igc-tree-item)
  • Should calculate items' path and level correctly, depending on data hierarchy
  • Should not render collapsed item's children
  • Should not render expand indicator if an item has no children
  • Should render default indicator for expansion properly depending on item state
  • Should render default select marker properly depending on item state
  • Should accept custom slot for the tree item expansion indicator
  • Should accept custom slot for the tree item indentation area
  • Should accept custom slot for the tree item label
  • Should accept custom slot for the tree item loading indicator
  • Should emit igcActiveItem event when the active item changes through UI
  • Should activate the last tree item set as active if there are multiple
  • Should scroll to active item (when set through API) if the tree has a scrollbar and the item is out of view
  • Should render items correctly depending on size settings
  • tree.items should return all tree items
  • Should be able to set tree item's value and label properties successfully
  • item.getChildren({flatten: true}) should return all item's children
  • item.getChildren({flatten: false}) should return only the direct children of item
  • When an item is deleted/added the visible tree items collection should be calculated properly

Expand/Collapse

  • Should expand all collapsed (including the disabled) items w/ tree.expand()
  • Should expand only specified items w/ tree.expand(item: IgcTreeItemComponent[])
  • Should collapse all expanded (including the disabled) items w/ tree.collapse()
  • Should collapse only specified items w/ tree.collapse(item: IgcTreeItemComponent[])
  • Should collapse items when user interacts w/ indicator and item.expanded === false
  • Should expand items when user interacts w/ indicator and item.expanded === true
  • Should emit ing and ed events when item state is toggled through UI
  • Should collapse items when item.expanded is set to false
  • Should expand items when item.expanded is set to true
  • Should expand items when item.expand() is called
  • Should collapse items when item.collapse() is called
  • Should toggle item state when item.toggle() is called
  • Should be able to prevent the expansion through the ing events.

singleBranchExpand

  • If singleBranchExpand === true, should support only one expanded tree item per level.
  • If singleBranchExpand === true and item.active is set to true, should expand all item to the active one and preserve the state of the other branches.
  • If singleBranchExpand === true and the item is expanded through API, should not collapse the currently expanded items.
  • Should collapse all items when setting singleBranchExpand to true and there is no active tree item.
  • When enabling singleBranchExpand and there is an active item, should collapse all tree items except the active item's ancestors.

Selection

  • Should be able to change selection type to all 3 options ('None' (default), 'Multiple', 'Cascade')
  • Should deselect all selected items w/ tree.deselect()
  • Should deselect only specified items w/ tree.deselect(item: IgcTreeItemComponent[])
  • Should select all deselected items w/ tree.select()
  • Should select only specified items w/ tree.select(item: IgcTreeItemComponent[])
  • Deleting an item should keep its selection state.

None

  • Should allow setting items as selected through API when tree.selection === 'None'
  • Should not render indicators when tree.selection === 'None'

Multiple

  • Should select/deselect an item by clicking on the checkbox
  • Should emit igcSelection event w/ correct args when an item is selected / deselected
  • Should support multiple selection (e.g. newly selected items do not empty selected collection)
  • Should be able to set item.selected correctly
  • Should be able to prevent the igcSelection event.
  • When selecting a range of records using Shift + click key selection of parents should NOT select their children if they are not in the selected range.

Cascade

  • Selecting an item should select its not selected children
  • Deselecting selected item should deselect its children
  • Should be able to set item.selected correctly. All direct and non-direct parents and children should be affected correctly.
  • Selecting all children of a parent should mark the parent as selected. All direct and non-direct parents should be affected correctly.
  • Deselecting all children of a parent (through API) should mark the parent as deselected. All direct and non-direct parents should be affected correctly.
  • Selecting a single child should mark the parent as indeterminate. All direct and non-direct parents should be affected correctly.
  • Deselecting a single child should mark the parent as indeterminate. All direct and non-direct parents should be affected correctly.
  • Selecting the last non-selected child should mark the parent as selected and NOT indeterminate. All direct and non-direct parents should be affected correctly.
  • Deselecting the last selected child should mark the parent as deselected and NOT indeterminate. All direct and non-direct parents should be affected correctly.
  • Set nested child, that has its own children, as initially selected. Verify that direct and indirect parents have correct states.
  • If a parent is initially selected, all of its children should be selected even if they are initially marked as deselected.
  • Partially selected parents should have the default indicator rendered as indeterminate
  • Deleting a single selected child should mark the parent as deselected. All direct and non-direct parents should be affected correctly.
  • Deleting a single deselected child should mark the parent as selected. All direct and non-direct parents should be affected correctly.
  • Deleting a child should from a selected/deselected parent should not affect its selection state.
  • Adding a selected child to deselected parent should mark it as indeterminate. All direct and non-direct parents should be affected correctly.
  • Adding a deselected child to selected parent should mark it as indeterminate. All direct and non-direct parents should be affected correctly.
  • Adding a (de)selected child to (de)selected parent should not affect the parent selection state.
  • Should be able to prevent the igcSelection event.
  • When selecting a range of records using Shift + click key selection of parents should select all their children even if they are not in the selected range.

Disabled item

  • Should not be able to interact (select/activate/expand) with disabled item through UI
  • Should be able to select/activate/expand disabled item through API
  • Should render disabled items as not reachable for tab navigation (tabIndex should -1)
  • Should not count disabled items as traversable items for keyboard navigation purposes
  • If a tree item is expanded and all its children are disabled the focus and activation should not be moved from the item on Arrow Right key press
  • If a tree item is expanded and has enabled children the focus and activation should be moved to the first enabled child on Arrow Right key press
  • Pressing Arrow Up/Down should move the focus and activation to the previous/next enabled and visible item (if there is any) (skipping disabled)
  • Pressing Asterisk on a focused item should expand only the enabled and expandable items in the same group

Keyboard Navigation

Basic

  • Should focus and activate the first tree item on Home key press
  • Should focus and activate the last visible tree item on End key press
  • Should not navigate when a tree item has no parent and item is collapsed on Arrow Left key press
  • Should navigate to the parent item of a tree item w/ expanded === true on Arrow Left key press, moving focus and active
  • Should collapse expanded tree items on Arrow Left key press
  • Should not navigate when a tree item has no children on Arrow Right key press
  • Should navigate to the first child of an expanded on Arrow Right key press, moving focus and active
  • Should expand collapsed tree item w/ children on Arrow Right key press
  • Should focus and activate the next visible tree item on Arrow Down key press
  • Should only focus the next visible tree item on Arrow Down + Ctrl key press
  • Should focus and activate the previous visible tree item on Arrow Up key press
  • Should only focus the previous visible tree item on Arrow Up + Ctrl key press
  • Should expand all sibling tree items of the focused item on asterisk (*) key press
  • Should active the focused tree item on Enter key press
  • Should not prevent event's default behavior on Enter key press
  • Should only activate the tree item when tree.selection === 'None' on Space key press
  • Should select and activate item when tree.selection !== 'None' on Space key press
  • Should select item range when tree.selection !== 'None' on Space + Shift keys press, moving active
  • Should emit the correct activation, selection and expansion events

Aria

  • Should render proper role and attributes for the tree
  • Should render proper aria attributes for each tree-item

5. Accessibility

ARIA Support Tree Aria example

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

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

An igc-tree-item will have role="treeitem" if its hasFocusableContent property is not enabled. If it is enabled, the role="treeitem" will go on the child element.

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

RTL Support

6. Assumptions and Limitations

Assumptions Limitation Notes

7. References

⚠️ **GitHub.com Fallback** ⚠️