Master Detail

The Master Detail plugin provides expandable detail rows beneath master rows. When a master row is expanded, a detail row is inserted below it with configurable height. The plugin supports async data fetching, detail row caching, and full display integration with the virtual scroll system. This is an enterprise plugin that requires a license for production use.

Installation

npm install @gridstorm/plugin-master-detail

Setup

import { createGrid } from '@gridstorm/core';
import { MasterDetailPlugin } from '@gridstorm/plugin-master-detail';

const grid = createGrid({
  columns: [
    { colId: 'orderId', field: 'orderId', headerName: 'Order ID' },
    { colId: 'customer', field: 'customer', headerName: 'Customer' },
    { colId: 'total', field: 'total', headerName: 'Total' },
  ],
  rowData: [],
  plugins: [
    MasterDetailPlugin({
      getDetailRowData: ({ node, data, successCallback }) => {
        // Return data synchronously
        return data.lineItems;
      },
      detailRowHeight: 200,
      keepDetailRows: false,
      embedFullWidthRows: true,
    }),
  ],
});

:::example{title="Master Detail Demo" href="/cookbook/#master-detail"} Expand rows to reveal detail grids with async data fetching, configurable row heights, and optional caching of detail data. :::

Plugin Options

Name Type Default Description
getDetailRowData (params: DetailDataParams) => any[] | Promise<any[]> required Fetches the detail data for a master row. Can return data synchronously, via the successCallback, or as a Promise.
detailGridOptions (params: DetailGridParams) => any undefined Factory returning grid configuration for the detail grid (columns, plugins, etc.).
detailRowHeight number | (params: DetailHeightParams) => number 200 Height of the detail row in pixels. Use a function for per-row dynamic heights.
keepDetailRows boolean false When true, detail row data and DOM nodes are cached when collapsed. When false, they are destroyed on collapse.
embedFullWidthRows boolean true When true, detail rows span the full width of the grid.

Data Fetching

The getDetailRowData callback supports three patterns:

// 1. Synchronous return
getDetailRowData: ({ data }) => data.lineItems;

// 2. Promise return
getDetailRowData: async ({ data }) => {
  const response = await fetch(`/api/orders/${data.orderId}/items`);
  return response.json();
};

// 3. Callback pattern
getDetailRowData: ({ node, successCallback }) => {
  fetch(`/api/orders/${node.id}/items`)
    .then((res) => res.json())
    .then((items) => successCallback(items));
};

Fetched data is cached by master row ID. Subsequent expansions of the same row use the cache unless the detail was destroyed on collapse (keepDetailRows: false).

Usage Examples

Expand and Collapse Master Rows

// Expand a master row
grid.commandBus.dispatch('detail:expand', { nodeId: 'order-1' });

// Collapse a master row
grid.commandBus.dispatch('detail:collapse', { nodeId: 'order-1' });

// Toggle (expand if collapsed, collapse if expanded)
grid.commandBus.dispatch('detail:toggle', { nodeId: 'order-1' });

Expand and Collapse All

// Expand all master rows (fetches data for each asynchronously)
grid.commandBus.dispatch('detail:expandAll', {});

// Collapse all master rows
grid.commandBus.dispatch('detail:collapseAll', {});

Refresh Detail Data

Force a re-fetch of detail data for a specific master row by clearing the cache.

grid.commandBus.dispatch('detail:refreshDetail', { nodeId: 'order-1' });

Commands

Name Payload Description
detail:expand { nodeId: string } Expand a master row. Inserts a detail row and fetches data asynchronously.
detail:collapse { nodeId: string } Collapse a master row. Removes the detail row (and cache unless keepDetailRows is true).
detail:toggle { nodeId: string } Toggle a master row between expanded and collapsed.
detail:expandAll {} Expand all displayed master rows. Fetches data for each in parallel.
detail:collapseAll {} Collapse all expanded master rows.
detail:refreshDetail { nodeId: string } Clear cached data for a master row and re-fetch if currently expanded.

Events

Name Payload Description
detail:opened { nodeId: string; node: RowNode } Emitted when a master row is expanded.
detail:closed { nodeId: string; node: RowNode } Emitted when a master row is collapsed.

React Integration

import { GridStorm, useGridApi } from '@gridstorm/react';
import { MasterDetailPlugin } from '@gridstorm/plugin-master-detail';

function MasterDetailGrid({ rowData, columns }) {
  const apiRef = useGridApi();

  const expandAll = () => apiRef.current?.commandBus.dispatch('detail:expandAll', {});
  const collapseAll = () => apiRef.current?.commandBus.dispatch('detail:collapseAll', {});

  return (
    <>
      <button onClick={expandAll}>Expand All</button>
      <button onClick={collapseAll}>Collapse All</button>
      <GridStorm
        rowData={rowData}
        columns={columns}
        plugins={[
          MasterDetailPlugin({
            getDetailRowData: async ({ data }) => {
              const res = await fetch(`/api/orders/${data.id}/items`);
              return res.json();
            },
            detailRowHeight: 250,
            keepDetailRows: true,
          }),
        ]}
      />
    </>
  );
}

Next Steps