Hierarchy Plugin
The Hierarchy plugin builds one hierarchical row model from every plugin that adds structure to the rows. Tree adds each row’s children and Row Grouping groups lists of sibling rows. The two plugins never talk to each other, so they combine: with both enabled, the children of every tree node can be grouped.
You don’t add this plugin yourself. Tree and Row Grouping depend on it, and the grid attaches it automatically. It has no configuration.
Installation
Section titled “Installation”Nothing to install. Importing Tree or Row Grouping brings the Hierarchy plugin along:
import '@toolbox-web/grid/features/tree';import '@toolbox-web/grid/features/grouping-rows';Basic Usage
Section titled “Basic Usage”Group the children of each tree node by a property, and leave the root rows ungrouped:
<tbw-grid style="height: 450px;"></tbw-grid>import '@toolbox-web/grid';import { queryGrid } from '@toolbox-web/grid';import '@toolbox-web/grid/features/grouping-rows';import '@toolbox-web/grid/features/tree';
type PlanRow = { name: string; region?: string; kind?: string };
const deliveryPlans = [ { id: 'plan-1', name: 'Rotterdam Q3', region: 'Europe', children: [ { id: 'p1-a', name: 'Brent cargo 0712', kind: 'Cargo', counterparty: 'Northsea Oil', volume: 600000 }, { id: 'p1-b', name: 'Fixed-price swap', kind: 'Deal', counterparty: 'Fjord Trading', volume: 250000 }, { id: 'p1-c', name: 'Forties cargo 0719', kind: 'Cargo', counterparty: 'Baltic Energy', volume: 450000 }, ], }, { id: 'plan-2', name: 'Houston Q3', region: 'Americas', children: [ { id: 'p2-a', name: 'WTI forward', kind: 'Deal', counterparty: 'Gulf Partners', volume: 300000 }, { id: 'p2-b', name: 'LNG cargo 0802', kind: 'Cargo', counterparty: 'Coastal Gas', volume: 150000 }, ], }, { id: 'plan-3', name: 'Hamburg Q3', region: 'Europe', children: [ { id: 'p3-a', name: 'Diesel option', kind: 'Deal', counterparty: 'Elbe Fuels', volume: 80000 }, { id: 'p3-b', name: 'Diesel cargo 0725', kind: 'Cargo', counterparty: 'Elbe Fuels', volume: 120000 }, ], },];
const groupOnFor = (levels: string) => { if (levels === 'every level') { return (row: PlanRow, { depth }: { depth: number }) => (depth === 0 ? row.region : row.kind); } if (levels === 'children') return (row: PlanRow, { depth }: { depth: number }) => (depth > 0 ? row.kind : null); return () => null;};
const grid = queryGrid('tbw-grid');grid.gridConfig = { columns: [ { field: 'name', header: 'Name' }, { field: 'kind', header: 'Kind', width: 90 }, { field: 'counterparty', header: 'Counterparty' }, { field: 'volume', header: 'Volume (bbl)', type: 'number', align: 'right' }, ], features: { tree: { childrenField: 'children', defaultExpanded: true }, groupingRows: { groupOn: groupOnFor('children'), defaultExpanded: true, showRowCount: true }, },};grid.rows = deliveryPlans;Switch Group to every level to group the plans by region as well, or to off to see the plain tree.
import { queryGrid } from '@toolbox-web/grid';import '@toolbox-web/grid/features/tree';import '@toolbox-web/grid/features/grouping-rows';
const grid = queryGrid('tbw-grid');grid.gridConfig = { columns: [{ field: 'name', header: 'Name' }], features: { tree: { childrenField: 'children', defaultExpanded: true }, groupingRows: { groupOn: (row, { depth }) => (depth > 0 ? row.kind : null), defaultExpanded: true, }, },};grid.rows = deliveryPlans;import '@toolbox-web/grid-react/features/tree';import '@toolbox-web/grid-react/features/grouping-rows';import { DataGrid } from '@toolbox-web/grid-react';import type { GridConfig } from '@toolbox-web/grid-react';
const gridConfig: GridConfig = { columns: [{ field: 'name', header: 'Name' }], features: { tree: { childrenField: 'children', defaultExpanded: true }, groupingRows: { groupOn: (row, { depth }) => (depth > 0 ? row.kind : null), defaultExpanded: true }, },};
function PlanGrid({ plans }) { return <DataGrid rows={plans} gridConfig={gridConfig} style={{ height: '400px' }} />;}<script setup lang="ts">import '@toolbox-web/grid-vue/features/tree';import '@toolbox-web/grid-vue/features/grouping-rows';import { TbwGrid } from '@toolbox-web/grid-vue';import type { GridConfig } from '@toolbox-web/grid-vue';
defineProps<{ plans: unknown[] }>();
const gridConfig: GridConfig = { columns: [{ field: 'name', header: 'Name' }], features: { tree: { childrenField: 'children', defaultExpanded: true }, groupingRows: { groupOn: (row, { depth }) => (depth > 0 ? row.kind : null), defaultExpanded: true }, },};</script>
<template> <TbwGrid :rows="plans" :grid-config="gridConfig" style="height: 400px" /></template>import '@toolbox-web/grid-angular/features/tree';import '@toolbox-web/grid-angular/features/grouping-rows';import { Component, input } from '@angular/core';import { Grid } from '@toolbox-web/grid-angular';import type { GridConfig } from '@toolbox-web/grid-angular';
@Component({ selector: 'app-plan-grid', imports: [Grid], template: `<tbw-grid [rows]="plans()" [gridConfig]="gridConfig" style="height: 400px; display: block;" />`,})export class PlanGridComponent { plans = input<unknown[]>([]);
gridConfig: GridConfig = { columns: [{ field: 'name', header: 'Name' }], features: { tree: { childrenField: 'children', defaultExpanded: true }, groupingRows: { groupOn: (row: any, { depth }) => (depth > 0 ? row.kind : null), defaultExpanded: true }, }, };}See Grouping Tree Children for how groupOn receives the tree level and how nested group keys are formed.
How It Works
Section titled “How It Works”On every row-model rebuild the Hierarchy plugin walks the rows one sibling list at a time — the root rows first, then the children of each expanded node:
- Structure plugins run first. Tree sorts the list and attaches the children of expanded nodes.
- Transform plugins run next. Row Grouping replaces the list with group headers that contain the rows.
- The result is flattened in display order.
Each plugin keeps ownership of its own expand/collapse state, events and API. grid.getPluginByName('tree').toggle(key) and grid.getPluginByName('groupingRows').toggle(key) keep working as before.
Read a row’s position in the combined hierarchy with getRowMeta:
const meta = grid.getPluginByName('hierarchy')?.getRowMeta(grid.rows[0]);// { level: 1, setSize: 2, posInSet: 1 }Accessibility
Section titled “Accessibility”WCAG 2.2 SC 1.3.1 Info and Relationships — nesting is conveyed to assistive technology, not only visually. While Tree or Row Grouping is active, the rows container uses role="treegrid", and every rendered row — tree rows, group headers and grouped rows — carries aria-level, aria-setsize and aria-posinset for its position in the combined hierarchy. Rows that scroll into view get the same attributes. This is automatic; there is nothing to configure.
CDN (UMD) Builds
Section titled “CDN (UMD) Builds”tree.umd.js and grouping-rows.umd.js each bundle a copy of this plugin, so loading either one is enough.
See Also
Section titled “See Also”- Tree — Hierarchical tree data
- Row Grouping — Group rows by field values
- Custom Plugins → Provided dependencies — How a plugin declares a dependency the grid attaches automatically