TreeView
The TreeView
component is used to display hierarchical data in an expandable/collapsible tree structure. It supports features like selection, status indicators, execution details, and RTL/LTR text direction.
Usage
import { Tree, Folder, File, CollapseButton, type TreeViewElement } from '@harnessio/ui/components'import { ExecutionState } from '@harnessio/ui/views'
//...
return ( <Tree initialSelectedId="1-1" className="h-96 w-full rounded-md border"> <Folder element="Documents" value="folder-1" status={ExecutionState.SUCCESS} level={0} > <File value="1-1" status={ExecutionState.SUCCESS} level={1}> File 1 </File> </Folder> </Tree>)
Basic Tree Structure
Create a simple tree with folders and files. The tree automatically handles expand/collapse interactions.
With Status Indicators
The TreeView component displays status icons based on the ExecutionState
. It supports states like SUCCESS
, RUNNING
, FAILURE
, PENDING
, and more.
With Initial Selection
You can specify an initial selected item using the initialSelectedId
prop. The tree will automatically expand parent folders to show the selected item.
With Initial Expanded Items
Control which folders are initially expanded using the initialExpendedItems
prop.
Non-Selectable Items
You can make specific items non-selectable by setting isSelectable: false
on the element.
With Collapse Button
Add a collapse/expand all button using the CollapseButton
component.
RTL Support
The TreeView component supports right-to-left (RTL) text direction using the dir
prop.
Complex Example - Full-Featured Pipeline Execution Tree
This comprehensive example demonstrates all features of the TreeView component including:
- Multiple nested levels
- Various execution states with status icons
- Duration display for each item
- Initial selection and expansion
- Collapse/expand all functionality
- Mix of selectable and non-selectable items
- Real-world CI/CD pipeline structure
API Reference
TreeViewElement Type
type TreeViewElement = { id: string; // Unique identifier for the tree item name: string; // Display name of the item isSelectable?: boolean; // Whether the item can be selected (default: true) children?: TreeViewElement[]; // Nested child elements status: ExecutionState; // Execution status (see ExecutionState enum) duration?: string; // Formatted duration string (e.g., "2m 30s")};
ExecutionState Enum
enum ExecutionState { PENDING = "pending", RUNNING = "running", SUCCESS = "success", FAILURE = "failure", ERROR = "error", SKIPPED = "skipped", KILLED = "killed", BLOCKED = "blocked", WAITING_ON_DEPENDENCIES = "waiting_on_dependencies", UNKNOWN = "unknown",}
Tree Component Props
Prop | Required | Default | Type |
---|---|---|---|
elements | false | TreeViewElement[] | |
initialSelectedId | false | string | |
initialExpendedItems | false | string[] | |
indicator | false | true | boolean |
dir | false | 'ltr' | 'rtl' | 'ltr' |
className | false | string | |
openIcon | false | React.ReactNode | |
closeIcon | false | React.ReactNode |
Folder Component Props
Prop | Required | Default | Type |
---|---|---|---|
element | true | string | |
value | true | string | |
status | true | ExecutionState | |
duration | false | string | |
level | true | number | |
isSelectable | false | true | boolean |
isSelect | false | boolean | |
expendedItems | false | string[] |
File Component Props
Prop | Required | Default | Type |
---|---|---|---|
value | true | string | |
children | true | React.ReactNode | |
status | true | ExecutionState | |
duration | false | string | |
level | true | number | |
isSelectable | false | true | boolean |
isSelect | false | boolean | |
handleSelect | false | (id: string) => void |
CollapseButton Component Props
Prop | Required | Default | Type |
---|---|---|---|
elements | true | TreeViewElement[] | |
expandAll | false | false | boolean |
children | false | React.ReactNode |
Helper Utility: Rendering Tree Elements
You can create a helper utility to recursively render tree elements from data:
import { ReactNode } from 'react'import { File, Folder, type TreeViewElement } from '@harnessio/ui/components'
interface RenderTreeElementProps { element: TreeViewElement handleClick?: (params: { parentNode: TreeViewElement | null; childNode: TreeViewElement }) => void parentElement?: TreeViewElement | null level?: number}
// Main render functionexport const renderTree = ( elements: TreeViewElement[], handleClick?: (params: { parentNode: TreeViewElement | null; childNode: TreeViewElement }) => void): ReactNode => { if (elements.length === 0) return [] return elements.map(element => ( <div key={element.id}> {renderTreeElement({ element, handleClick, parentElement: null })} </div> ))}
// Render folder with childrenconst renderTreeFolder = ({ element: folderElement, handleClick, level = 0 }: RenderTreeElementProps): ReactNode => { return ( <Folder element={folderElement.name} value={folderElement.id} status={folderElement.status} duration={folderElement.duration} isSelectable={folderElement.isSelectable} level={level} > {folderElement.children?.map(childElement => ( <div key={childElement.id}> {renderTreeElement({ parentElement: folderElement, element: childElement, handleClick, level: level + 1 })} </div> ))} </Folder> )}
// Render file (leaf node)const renderTreeFile = ({ element: fileElement, handleClick, parentElement, level = 0}: RenderTreeElementProps): ReactNode => { return ( <File value={fileElement.id} status={fileElement.status} duration={fileElement.duration} isSelectable={fileElement.isSelectable} handleSelect={() => handleClick?.({ parentNode: parentElement, childNode: fileElement })} level={level} > {fileElement.name} </File> )}
// Decide whether to render folder or fileconst renderTreeElement = ({ element, handleClick, parentElement, level = 0}: RenderTreeElementProps): ReactNode => { if (element.children && element.children.length > 0) { return renderTreeFolder({ element, handleClick, parentElement, level }) } return renderTreeFile({ element, handleClick, parentElement, level })}
// Usage example:const treeData: TreeViewElement[] = [ { id: '1', name: 'Folder', status: 'success', children: [ { id: '1-1', name: 'File 1', status: 'success' } ] }]
<Tree className="h-96 w-full rounded-md border"> {renderTree(treeData, ({ parentNode, childNode }) => { console.log('Selected:', childNode.name) })}</Tree>