A single cell can be used to represent multiple contiguous leaf rows with equal values.
"use client";
import React, {
useCallback,
useMemo,
useRef,
useState,
StrictMode,
} from "react";
import { createRoot } from "react-dom/client";
import { AgGridReact, AgGridProvider } from "ag-grid-react";
import {
CellSpanModule,
ClientSideRowModelModule,
ColDef,
ColGroupDef,
ColumnApiModule,
GridApi,
GridOptions,
ModuleRegistry,
enableDevValidations,
} from "ag-grid-community";
import { IOlympicData } from "./interfaces";
import { useFetchJson } from "./useFetchJson";
if (process.env.NODE_ENV !== "production") {
enableDevValidations();
}
const modules = [CellSpanModule, ClientSideRowModelModule, ColumnApiModule];
const GridExample = () => {
const containerStyle = useMemo(() => ({ width: "100%", height: "100%" }), []);
const gridStyle = useMemo(() => ({ height: "100%", width: "100%" }), []);
const [columnDefs, setColumnDefs] = useState<ColDef[]>([
{ field: "country", spanRows: true, sort: "asc" },
{ field: "year", spanRows: true, sort: "asc" },
{ field: "sport", spanRows: true, sort: "asc" },
{ field: "athlete" },
{ field: "age" },
{ field: "total" },
]);
const defaultColDef = useMemo<ColDef>(() => {
return {
flex: 1,
};
}, []);
const { data, loading } = useFetchJson<IOlympicData>(
"https://www.ag-grid.com/example-assets/olympic-winners.json",
);
return (
<AgGridProvider modules={modules}>
<div style={containerStyle}>
<div style={gridStyle}>
<AgGridReact<IOlympicData>
rowData={data}
loading={loading}
columnDefs={columnDefs}
defaultColDef={defaultColDef}
enableCellSpan={true}
/>
</div>
</div>
</AgGridProvider>
);
};
const root = createRoot(document.getElementById("root")!);
root.render(
<StrictMode>
<GridExample />
</StrictMode>,
);
export interface IOlympicData {
athlete: string,
age: number,
country: string,
year: number,
date: string,
sport: string,
gold: number,
silver: number,
bronze: number,
total: number
} import { useState, useEffect } from 'react';
/**
* Fetch example Json data
* Not recommended for production use!
*/
export const useFetchJson = <T,>(url:string, limit?: number) => {
const [data, setData] = useState<T[]>();
const [loading, setLoading] = useState(true);
useEffect(() => {
const fetchData = async () => {
setLoading(true);
// Note error handling is omitted here for brevity
const response = await fetch(url);
const json = await response.json();
const data = limit ? json.slice(0, limit) : json;
setData(data);
setLoading(false);
};
fetchData();
}, [url, limit]);
return { data, loading };
}; Enabling Row Spanning Copy Link
The example above demonstrates merging cells with equal values into a single cell that spans multiple rows.
Row spanning requires the CellSpanModule to be registered. The enableCellSpan grid option is an initial property and cannot be changed after the grid is created.
The following snippet demonstrates enabling row spanning by setting gridOptions.enableCellSpan to true. The country, year, and sport columns then configure row span by setting colDef.spanRows to true.
const [columnDefs, setColumnDefs] = useState([
{
field: 'country',
spanRows: true,
},
{
field: 'year',
spanRows: true,
},
{
field: 'sport',
spanRows: true,
},
// other column definitions ...
]);
const enableCellSpan = true;
<AgGridReact
columnDefs={columnDefs}
enableCellSpan={enableCellSpan}
/> Custom Row Spanning Copy Link
Row spanning can be customised by providing a callback function to colDef.spanRows. The callback returns true if the two adjacent rows should be spanned together.
The example below demonstrates custom row spanning which prevents any country cells with the value "Algeria" from being spanned.
"use client";
import React, {
useCallback,
useMemo,
useRef,
useState,
StrictMode,
} from "react";
import { createRoot } from "react-dom/client";
import { AgGridReact, AgGridProvider } from "ag-grid-react";
import {
CellSpanModule,
ClientSideRowModelModule,
ColDef,
ColGroupDef,
GridApi,
GridOptions,
ModuleRegistry,
SpanRowsParams,
enableDevValidations,
} from "ag-grid-community";
import { IOlympicData } from "./interfaces";
import { useFetchJson } from "./useFetchJson";
if (process.env.NODE_ENV !== "production") {
enableDevValidations();
}
const modules = [CellSpanModule, ClientSideRowModelModule];
const customSpanFunc = ({ valueA, valueB }: SpanRowsParams) => {
return valueA != "Algeria" && valueA === valueB;
};
const GridExample = () => {
const containerStyle = useMemo(() => ({ width: "100%", height: "100%" }), []);
const gridStyle = useMemo(() => ({ height: "100%", width: "100%" }), []);
const [columnDefs, setColumnDefs] = useState<ColDef[]>([
{ field: "country", spanRows: customSpanFunc, sort: "asc" },
{ field: "year", spanRows: true, sort: "asc" },
{ field: "sport", spanRows: true, sort: "asc" },
{ field: "athlete" },
{ field: "age" },
{ field: "total" },
]);
const defaultColDef = useMemo<ColDef>(() => {
return {
flex: 1,
};
}, []);
const { data, loading } = useFetchJson<IOlympicData>(
"https://www.ag-grid.com/example-assets/olympic-winners.json",
);
return (
<AgGridProvider modules={modules}>
<div style={containerStyle}>
<div style={gridStyle}>
<AgGridReact<IOlympicData>
rowData={data}
loading={loading}
columnDefs={columnDefs}
defaultColDef={defaultColDef}
enableCellSpan={true}
/>
</div>
</div>
</AgGridProvider>
);
};
const root = createRoot(document.getElementById("root")!);
root.render(
<StrictMode>
<GridExample />
</StrictMode>,
);
export interface IOlympicData {
athlete: string,
age: number,
country: string,
year: number,
date: string,
sport: string,
gold: number,
silver: number,
bronze: number,
total: number
} import { useState, useEffect } from 'react';
/**
* Fetch example Json data
* Not recommended for production use!
*/
export const useFetchJson = <T,>(url:string, limit?: number) => {
const [data, setData] = useState<T[]>();
const [loading, setLoading] = useState(true);
useEffect(() => {
const fetchData = async () => {
setLoading(true);
// Note error handling is omitted here for brevity
const response = await fetch(url);
const json = await response.json();
const data = limit ? json.slice(0, limit) : json;
setData(data);
setLoading(false);
};
fetchData();
}, [url, limit]);
return { data, loading };
}; The following snippet demonstrates how to configure custom row spanning on the country column:
const [columnDefs, setColumnDefs] = useState([
{
field: 'country',
spanRows: ({ valueA, valueB }) => valueA != 'Algeria' && valueA === valueB,
},
// other column definitions ...
]);
const enableCellSpan = true;
<AgGridReact
columnDefs={columnDefs}
enableCellSpan={enableCellSpan}
/> Auto Height and Row Spanning Copy Link
Row spanning can be configured alongside auto height. Note when doing so, if the cell is taller than the combined height of the rows, the last row in the span gains any additional required height.
"use client";
import React, {
useCallback,
useMemo,
useRef,
useState,
StrictMode,
} from "react";
import { createRoot } from "react-dom/client";
import { AgGridReact, AgGridProvider } from "ag-grid-react";
import {
CellSpanModule,
ClientSideRowModelModule,
ColDef,
ColGroupDef,
GridApi,
GridOptions,
GridReadyEvent,
ModuleRegistry,
RowAutoHeightModule,
enableDevValidations,
} from "ag-grid-community";
import { IOlympicData } from "./interfaces";
if (process.env.NODE_ENV !== "production") {
enableDevValidations();
}
const modules = [CellSpanModule, ClientSideRowModelModule, RowAutoHeightModule];
const lorem = `Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.`;
const GridExample = () => {
const containerStyle = useMemo(() => ({ width: "100%", height: "100%" }), []);
const gridStyle = useMemo(() => ({ height: "100%", width: "100%" }), []);
const [rowData, setRowData] = useState<IOlympicData[]>();
const [columnDefs, setColumnDefs] = useState<ColDef[]>([
{
field: "lorem",
spanRows: true,
wrapText: true,
autoHeight: true,
minWidth: 300,
},
{ field: "athlete" },
{ field: "age" },
{ field: "total" },
]);
const defaultColDef = useMemo<ColDef>(() => {
return {
flex: 1,
};
}, []);
const onGridReady = useCallback((params: GridReadyEvent) => {
fetch("https://www.ag-grid.com/example-assets/olympic-winners.json")
.then((resp) => resp.json())
.then((data: IOlympicData[]) => {
data.forEach((row, i) => {
if (i % 3 === 0) {
return;
}
row.lorem = lorem;
});
setRowData(data);
});
}, []);
return (
<AgGridProvider modules={modules}>
<div style={containerStyle}>
<div style={gridStyle}>
<AgGridReact<IOlympicData>
rowData={rowData}
columnDefs={columnDefs}
defaultColDef={defaultColDef}
enableCellSpan={true}
onGridReady={onGridReady}
/>
</div>
</div>
</AgGridProvider>
);
};
const root = createRoot(document.getElementById("root")!);
root.render(
<StrictMode>
<GridExample />
</StrictMode>,
);
export interface IOlympicData {
athlete: string,
age: number,
country: string,
year: number,
date: string,
sport: string,
gold: number,
silver: number,
bronze: number,
total: number
} The following snippet demonstrates how to configure auto height and row spanning:
const [columnDefs, setColumnDefs] = useState([
{
field: 'lorem',
spanRows: true,
autoHeight: true,
wrapText: true,
},
// other column definitions ...
]);
const enableCellSpan = true;
<AgGridReact
columnDefs={columnDefs}
enableCellSpan={enableCellSpan}
/>