Integrated Charts can be customised via the AG Charts Theme API.
Provided Themes Copy Link
The following themes are provided to Integrated Charts by default.
['ag-default', 'ag-material', 'ag-sheets', 'ag-polychroma', 'ag-vivid']These themes correspond to AG Charts Base Themes.
When using a dark colour scheme for the grid, the application must provide the dark equivalents of the chart themes. If a default AG Chart theme is used, the dark themes are named with a -dark suffix, e.g. 'ag-vivid-dark'.
The selected theme can be changed by the user via the Chart Tool Panel or by changing the order of the provided themes using the chartThemes grid option as shown below:
const gridOptions = {
chartThemes: ['ag-vivid', 'ag-polychroma', 'ag-material', 'ag-sheets', 'ag-default'],
// other grid options ...
} Overriding Themes Copy Link
Integrated Charts uses a theme based configuration which 'overrides' the theme defaults.
To override a charts theme, use the chartsThemeOverrides grid option.
const gridOptions = {
chartThemeOverrides: {
common: {
title: {
fontSize: 22,
fontFamily: 'Arial, sans-serif'
}
}
},
// other grid options ...
}Note that the chartThemeOverrides grid option maps to AG Charts Theme Overrides.
Common Overrides Copy Link
These overrides can be used with any series type. For full list of overrides see Common Overrides in the AG Charts documentation.
import { AgChartsEnterpriseModule } from "ag-charts-enterprise";
import {
ClientSideRowModelModule,
FirstDataRenderedEvent,
GridApi,
GridOptions,
GridReadyEvent,
ModuleRegistry,
createGrid,
enableDevValidations,
} from "ag-grid-community";
import {
ColumnMenuModule,
ContextMenuModule,
IntegratedChartsModule,
RowGroupingModule,
} from "ag-grid-enterprise";
import { getData } from "./data";
// Enable extended validations only for development
if (process.env.NODE_ENV !== "production") {
enableDevValidations();
}
ModuleRegistry.registerModules([
ClientSideRowModelModule,
IntegratedChartsModule.with(AgChartsEnterpriseModule),
ColumnMenuModule,
ContextMenuModule,
RowGroupingModule,
]);
let gridApi: GridApi;
const gridOptions: GridOptions = {
columnDefs: [
{ field: "country", width: 150, chartDataType: "category" },
{ field: "gold", chartDataType: "series" },
{ field: "silver", chartDataType: "series" },
{ field: "bronze", chartDataType: "series" },
],
defaultColDef: {
flex: 1,
minWidth: 100,
},
popupParent: document.body,
cellSelection: true,
enableCharts: true,
chartThemeOverrides: {
common: {
title: {
enabled: true,
text: "Precious Metals Production",
},
subtitle: {
enabled: true,
text: "by country",
fontSize: 14,
fontFamily: "Monaco, monospace",
color: "#aaa",
spacing: 10,
},
padding: {
left: 80,
right: 80,
},
legend: {
spacing: 30,
item: {
label: {
fontStyle: "italic",
fontWeight: "bold",
fontSize: 18,
fontFamily: "Palatino, serif",
color: "#aaa",
},
marker: {
shape: "circle",
size: 10,
padding: 10,
strokeWidth: 2,
},
padding: {
left: 15,
right: 15,
},
},
},
},
},
onGridReady: (params: GridReadyEvent) => {
getData().then((rowData) => params.api.setGridOption("rowData", rowData));
},
onFirstDataRendered,
};
function onFirstDataRendered(params: FirstDataRenderedEvent) {
params.api.createRangeChart({
cellRange: {
rowStartIndex: 0,
rowEndIndex: 3,
columns: ["country", "gold", "silver", "bronze"],
},
chartType: "groupedColumn",
});
}
const gridDiv = document.querySelector<HTMLElement>("#myGrid")!;
gridApi = createGrid(gridDiv, gridOptions);
.my-tooltip-class {
border: 4px solid black;
}
export async function getData(delay: number = 100): Promise<any[]> {
return new Promise((resolve) => setTimeout(() => resolve(generateData()), delay));
}
function generateData(): any[] {
const countries = [
'Ireland',
'Spain',
'United Kingdom',
'France',
'Germany',
'Luxembourg',
'Sweden',
'Norway',
'Italy',
'Greece',
'Iceland',
'Portugal',
'Malta',
'Brazil',
'Argentina',
'Colombia',
'Peru',
'Venezuela',
'Uruguay',
'Belgium',
];
return countries.map((country, index) => ({
country,
gold: Math.floor(((index + 1 / 7) * 333) % 100),
silver: Math.floor(((index + 1 / 3) * 555) % 100),
bronze: Math.floor(((index + 1 / 7.3) * 777) % 100),
}));
}
<div id="myGrid" style="height: 100%"></div>
Chart-specific Overrides Copy Link
The following documentation links describe different types of overrides specific to individual AG Charts series types.
- Line Overrides
- Bar Overrides
- Area Overrides
- Scatter Overrides
- Pie Overrides
- Radar Line Overrides
- Radar Area Overrides
- Nightingale Overrides
- Radial Column Overrides
- Radial Bar Overrides
- Range Bar Overrides
- Range Area Overrides
- Box Plot Overrides
- Waterfall Overrides
- Heatmap Overrides
- Treemap Overrides
- Sunburst Overrides
Custom Chart Themes Copy Link
Custom AG Charts Themes can also be supplied to the grid via the customChartThemes grid option.
const gridOptions = {
customChartThemes: {
myCustomTheme: {
palette: {
fills: ['#42a5f5', '#ffa726', '#81c784'],
strokes: ['#000000', '#424242'],
},
overrides: {
common: {
background: {
fill: '#f4f4f4',
},
legend: {
item: {
label: {
color: '#333333',
},
},
},
},
},
},
chartThemes: ['myCustomTheme', 'ag-vivid'],
},
// other grid options ...
}The example below shows a custom chart theme being used with the grid. Note that other provided themes can be used alongside a custom theme, and are unaffected by the settings in the custom theme.
import {
AgChartsEnterpriseModule,
AgThemeOverrides,
} from "ag-charts-enterprise";
import {
ClientSideRowModelModule,
FirstDataRenderedEvent,
GridApi,
GridOptions,
GridReadyEvent,
ModuleRegistry,
createGrid,
enableDevValidations,
} from "ag-grid-community";
import {
ColumnMenuModule,
ContextMenuModule,
IntegratedChartsModule,
RowGroupingModule,
} from "ag-grid-enterprise";
import { deepMerge, getData } from "./data";
// Enable extended validations only for development
if (process.env.NODE_ENV !== "production") {
enableDevValidations();
}
ModuleRegistry.registerModules([
ClientSideRowModelModule,
IntegratedChartsModule.with(AgChartsEnterpriseModule),
ColumnMenuModule,
ContextMenuModule,
RowGroupingModule,
]);
let gridApi: GridApi;
const commonThemeProperties: { overrides: AgThemeOverrides } = {
overrides: {
common: {
legend: {
position: "top",
spacing: 25,
item: {
label: {
fontStyle: "italic",
fontWeight: "bold",
fontSize: 18,
fontFamily: "Palatino, serif",
},
marker: {
shape: "circle",
size: 14,
padding: 8,
strokeWidth: 2,
},
},
},
},
bar: {
axes: {
number: {
line: {
width: 4,
},
},
category: {
line: {
width: 2,
},
label: {
rotation: 0,
},
},
},
},
},
};
const myCustomOverridesLight: { overrides: AgThemeOverrides } = {
overrides: {
common: {
background: {
fill: "#f4f4f4",
},
legend: {
item: {
label: {
color: "#333333",
},
},
},
},
bar: {
axes: {
number: {
bottom: {
line: {
stroke: "#424242",
},
label: {
color: "#555555",
fontStyle: "italic",
fontWeight: "bold",
fontSize: 12,
spacing: 5,
},
},
},
category: {
left: {
line: {
stroke: "#424242",
},
label: {
color: "#555555",
fontStyle: "italic",
fontWeight: "bold",
fontSize: 14,
spacing: 8,
},
},
},
},
},
},
};
const myCustomThemeLight = deepMerge(commonThemeProperties, {
palette: {
fills: ["#42a5f5", "#ffa726", "#81c784"],
strokes: ["#000000", "#424242"],
},
...myCustomOverridesLight,
});
const myCustomOverridesDark: { overrides: AgThemeOverrides } = {
overrides: {
common: {
background: {
fill: "#15181c",
},
legend: {
item: {
label: {
color: "#ECEFF1",
},
},
},
},
bar: {
axes: {
number: {
bottom: {
line: {
stroke: "#757575",
},
label: {
color: "#B0BEC5",
fontStyle: "italic",
fontWeight: "bold",
fontSize: 12,
spacing: 5,
},
},
},
category: {
left: {
line: {
stroke: "#757575",
},
label: {
color: "#B0BEC5",
fontStyle: "italic",
fontWeight: "bold",
fontSize: 14,
spacing: 8,
},
},
},
},
},
},
};
const myCustomThemeDark = deepMerge(commonThemeProperties, {
palette: {
fills: ["#42a5f5", "#ffa726", "#81c784"],
strokes: ["#ffffff", "#B0BEC5"],
},
...myCustomOverridesDark,
});
const gridOptions: GridOptions = {
columnDefs: [
{ field: "country", width: 150, chartDataType: "category" },
{ field: "gold", chartDataType: "series" },
{ field: "silver", chartDataType: "series" },
{ field: "bronze", chartDataType: "series" },
],
defaultColDef: {
flex: 1,
minWidth: 100,
},
popupParent: document.body,
cellSelection: true,
enableCharts: true,
chartThemes: ["my-custom-theme-light", "my-custom-theme-dark"],
customChartThemes: {
"my-custom-theme-light": myCustomThemeLight,
"my-custom-theme-dark": myCustomThemeDark,
},
onGridReady: (params: GridReadyEvent) => {
getData().then((rowData) => params.api.setGridOption("rowData", rowData));
},
onFirstDataRendered,
};
function onFirstDataRendered(params: FirstDataRenderedEvent) {
params.api.createRangeChart({
cellRange: {
rowStartIndex: 0,
rowEndIndex: 4,
columns: ["country", "gold", "silver", "bronze"],
},
chartType: "groupedBar",
});
}
const gridDiv = document.querySelector<HTMLElement>("#myGrid")!;
gridApi = createGrid(gridDiv, gridOptions);
export async function getData(delay: number = 100): Promise<any[]> {
return new Promise((resolve) => setTimeout(() => resolve(generateData()), delay));
}
export function deepMerge(obj1: any, obj2: any): any {
const output = { ...obj1 };
for (const key in obj2) {
if (obj2.hasOwnProperty(key)) {
if (typeof obj2[key] === 'object' && obj2[key] !== null && !Array.isArray(obj2[key])) {
output[key] = deepMerge(obj1[key] || {}, obj2[key]);
} else {
output[key] = obj2[key];
}
}
}
return output;
}
function generateData(): any[] {
const countries = [
'Ireland',
'Spain',
'United Kingdom',
'France',
'Germany',
'Luxembourg',
'Sweden',
'Norway',
'Italy',
'Greece',
'Iceland',
'Portugal',
'Malta',
'Brazil',
'Argentina',
'Colombia',
'Peru',
'Venezuela',
'Uruguay',
'Belgium',
];
return countries.map((country, index) => ({
country,
gold: Math.floor(((index + 1 / 7) * 333) % 100),
silver: Math.floor(((index + 1 / 3) * 555) % 100),
bronze: Math.floor(((index + 1 / 7.3) * 777) % 100),
}));
}
<div id="myGrid" style="height: 100%"></div>
Formatting Copy Link
Chart values can be formatted in several different ways.
Label Formatting Copy Link
The valueFormatter is not automatically applied to the chart axes. If you wish to use the grid's value formatters, you must apply them manually.
Custom label formatting can be applied to the chart axes by providing suitable formatters.
const gridOptions = {
chartThemeOverrides: {
common: {
axes: {
number: {
label: {
formatter: function(params) {
// prefix with dollar sign
return '$' + params.value;
},
},
},
},
},
},
// other grid options ...
}The example below shows:
- A custom label formatter being used with the vertical axis to display SI units for the data. Additionally, this example demonstrates the use of the
domainproperty passed through to the formatter to provide a consistent scale across the value range. - A custom title formatter using
boundSeriesproperty passed through to the formatter for the vertical axis to display which series the axes are representing.
import {
AgAxisCaptionFormatterParams,
AgAxisLabelFormatterParams,
AgChartsEnterpriseModule,
} from "ag-charts-enterprise";
import {
ClientSideRowModelModule,
FirstDataRenderedEvent,
GridApi,
GridOptions,
GridReadyEvent,
ModuleRegistry,
createGrid,
enableDevValidations,
} from "ag-grid-community";
import {
ColumnMenuModule,
ContextMenuModule,
IntegratedChartsModule,
RowGroupingModule,
} from "ag-grid-enterprise";
import { data } from "./data";
// Enable extended validations only for development
if (process.env.NODE_ENV !== "production") {
enableDevValidations();
}
ModuleRegistry.registerModules([
ClientSideRowModelModule,
IntegratedChartsModule.with(AgChartsEnterpriseModule),
ColumnMenuModule,
ContextMenuModule,
RowGroupingModule,
]);
const titleFormatter = (params: AgAxisCaptionFormatterParams) =>
`Power (${params.boundSeries.map((s) => s.name).join(", ")})`;
function createSIFormatter(units = "", precision = 0) {
const SI_UNITS = ["", "K", "M", "G"];
let tier: number | undefined;
function calculateSITier(domain: number[]): number {
const [min, max] = domain;
const value = Math.max(Math.abs(min), Math.abs(max));
return Math.floor(Math.log10(Math.abs(value)) / 3);
}
function formatSI(value: number, tier: number, precision: number) {
if (value === 0) {
return "0";
}
const suffix = SI_UNITS[tier] || "";
const scaled = value / 10 ** (tier * 3);
return `${scaled.toFixed(precision)}${suffix}${units}`;
}
return (params: AgAxisLabelFormatterParams) => {
tier ??= calculateSITier(params.domain);
return formatSI(params.value as number, tier, precision);
};
}
const siFormatter = createSIFormatter("W", 2);
const isEfficiencySeries = (params: any) =>
params.boundSeries.find((s: any) => s.key === "efficiency");
let gridApi: GridApi;
const gridOptions: GridOptions = {
columnDefs: [
{ field: "year", width: 150, chartDataType: "category" },
{ field: "generated", chartDataType: "series", cellDataType: "number" },
{ field: "consumed", chartDataType: "series", cellDataType: "number" },
{ field: "surplus", chartDataType: "series", cellDataType: "number" },
{ field: "efficiency", chartDataType: "series", cellDataType: "number" },
],
defaultColDef: {
flex: 1,
minWidth: 100,
},
popupParent: document.body,
cellSelection: true,
enableCharts: true,
chartThemeOverrides: {
common: {
axes: {
number: {
title: {
enabled: true,
formatter: (params) => {
return isEfficiencySeries(params)
? "Efficiency (%)"
: titleFormatter(params);
},
},
label: {
formatter: (params) => {
return isEfficiencySeries(params)
? `${params.value}%`
: siFormatter(params);
},
},
},
},
},
},
onGridReady: (params: GridReadyEvent) => {
params.api.setGridOption("rowData", data);
},
onFirstDataRendered,
};
function onFirstDataRendered(params: FirstDataRenderedEvent) {
params.api.createRangeChart({
cellRange: {
rowStartIndex: 0,
rowEndIndex: 4,
columns: ["year", "generated", "consumed", "surplus", "efficiency"],
},
seriesChartTypes: [
{ colId: "generated", chartType: "groupedColumn", secondaryAxis: false },
{ colId: "consumed", chartType: "groupedColumn", secondaryAxis: false },
{ colId: "surplus", chartType: "groupedColumn", secondaryAxis: false },
{ colId: "efficiency", chartType: "line", secondaryAxis: true },
],
chartType: "columnLineCombo",
chartContainer: document.querySelector("#myChart") as any,
aggFunc: "sum",
});
}
const gridDiv = document.querySelector<HTMLElement>("#myGrid")!;
gridApi = createGrid(gridDiv, gridOptions);
#wrapper {
display: grid;
grid-template-columns: 100%;
grid-template-rows: 33% calc(66% - 10px);
width: 100%;
height: 100%;
gap: 10px;
}
export const data = [
{
year: 2016,
generated: 318157080,
consumed: 309000000,
surplus: 9157080,
efficiency: 97.1,
},
{
year: 2017,
generated: 120000000,
consumed: 10000000,
surplus: 10000000,
efficiency: 8.33,
},
{
year: 2018,
generated: 900000000,
consumed: 815000000,
surplus: 85000000,
efficiency: 68.33,
},
{
year: 2019,
generated: 330000000,
consumed: 320000000,
surplus: 10000000,
efficiency: 96.97,
},
{
year: 2020,
generated: 340000000,
consumed: 330000000,
surplus: 10000000,
efficiency: 97.06,
},
{
year: 2021,
generated: 350000000,
consumed: 340000000,
surplus: 10000000,
efficiency: 97.14,
},
{
year: 2022,
generated: 360000000,
consumed: 350000000,
surplus: 10000000,
efficiency: 97.22,
},
{
year: 2023,
generated: 370000000,
consumed: 360000000,
surplus: 10000000,
efficiency: 97.3,
},
{
year: 2024,
generated: 380000000,
consumed: 370000000,
surplus: 10000000,
efficiency: 97.37,
},
];
<div id="wrapper">
<div id="myGrid"></div>
<div id="myChart"></div>
</div>
For more information on the formatter property for axes labels, see AG Charts Axis Labels Label Text Formatting.
Global Formatter Copy Link
A single, topālevel formatter can be used to control the text for every labelābearing element, e.g. axes, series labels, legend items, callouts, etc. The formatter will run for each element, unless that element defines its own formatter.
The callback receives the similar params object provided to elementālevel formatters, providing access to properties such as value, type, and elementId. See the AG Charts API Reference for more information.
const gridOptions = {
chartThemeOverrides: {
common: {
formatter: (params) => {
if (params.type === 'number') {
return `Ā£${params.value}`;
// prefix with `Ā£` sign
}
const gridApi = params.context.api;
// do something with the grid API
}
}
},
// other grid options ...
}The example below shows a global formatter that prefixes all number values with a £ symbol.
import {
AgChartsEnterpriseModule,
FormatterParams,
} from "ag-charts-enterprise";
import {
ChartRef,
ChartType,
ClientSideRowModelModule,
FirstDataRenderedEvent,
GridApi,
GridOptions,
GridReadyEvent,
ModuleRegistry,
createGrid,
enableDevValidations,
} from "ag-grid-community";
import {
ColumnMenuModule,
ContextMenuModule,
IntegratedChartsModule,
RowGroupingModule,
} from "ag-grid-enterprise";
import { getData } from "./data";
// Enable extended validations only for development
if (process.env.NODE_ENV !== "production") {
enableDevValidations();
}
ModuleRegistry.registerModules([
ClientSideRowModelModule,
IntegratedChartsModule.with(AgChartsEnterpriseModule),
ColumnMenuModule,
ContextMenuModule,
RowGroupingModule,
]);
let gridApi: GridApi;
let chartRef: ChartRef;
const gridOptions: GridOptions = {
columnDefs: [
{
field: "period",
chartDataType: "category",
headerName: "Financial Period",
width: 150,
},
{
field: "recurring",
chartDataType: "series",
headerName: "Recurring Revenue",
},
{
field: "individual",
chartDataType: "series",
headerName: "Individual Sales",
},
],
defaultColDef: {
flex: 1,
minWidth: 100,
},
popupParent: document.body,
cellSelection: true,
enableCharts: true,
chartToolPanelsDef: {
defaultToolPanel: "settings",
},
onGridReady: (params: GridReadyEvent) => {
getData().then((rowData) => params.api.setGridOption("rowData", rowData));
},
onFirstDataRendered,
chartThemeOverrides: {
common: {
formatter: (params: FormatterParams) => {
if (params.type === "number") {
return `Ā£${params.value}`;
}
},
},
},
};
function onFirstDataRendered(params: FirstDataRenderedEvent) {
chartRef = params.api.createRangeChart({
chartContainer: document.querySelector("#myChart") as HTMLElement,
cellRange: {
columns: ["period", "recurring", "individual"],
},
chartType: "groupedColumn",
})!;
}
function updateChart(chartType: ChartType) {
gridApi.updateChart({
type: "rangeChartUpdate",
chartId: `${chartRef.chartId}`,
chartType: chartType,
});
}
const gridDiv = document.querySelector<HTMLElement>("#myGrid")!;
gridApi = createGrid(gridDiv, gridOptions);
if (typeof window !== "undefined") {
// Attach external event handlers to window so they can be called from index.html
(<any>window).updateChart = updateChart;
}
.wrapper {
display: flex;
flex-direction: column;
height: 100%;
}
.button-container {
flex-wrap: wrap;
}
#myGrid {
flex: 1;
}
#myChart {
flex: 2;
min-height: 530px;
}
export async function getData(delay: number = 100): Promise<any[]> {
return new Promise((resolve) => setTimeout(() => resolve(generateData()), delay));
}
function generateData(): any[] {
return [
{ period: 'Q1 2021', recurring: 485829, individual: 237438 },
{ period: 'Q2 2021', recurring: 512743, individual: 245672 },
{ period: 'Q3 2021', recurring: 521938, individual: 259371 },
{ period: 'Q4 2021', recurring: 535421, individual: 271839 },
{ period: 'Q1 2022', recurring: 558329, individual: 284738 },
{ period: 'Q2 2022', recurring: 572843, individual: 298472 },
{ period: 'Q3 2022', recurring: 589372, individual: 312849 },
{ period: 'Q4 2022', recurring: 601234, individual: 327195 },
{ period: 'Q1 2023', recurring: 615928, individual: 342839 },
{ period: 'Q2 2023', recurring: 628472, individual: 358293 },
{ period: 'Q3 2023', recurring: 642839, individual: 374829 },
{ period: 'Q4 2023', recurring: 657382, individual: 391829 },
];
}
<div class="wrapper">
<div class="button-container">
<button onclick="updateChart('groupedColumn')">Grouped Column</button>
<button onclick="updateChart('line')">Line</button>
<button onclick="updateChart('donut')">Donut</button>
</div>
<div id="myGrid"></div>
<div id="myChart"></div>
</div>
The order of formatting is as follows:
- Elementāspecific
label.formatter(axis, series, legend, callout, etc.) - Global
formatter - Default AGĀ Charts formatting rules
Accessing Grid Context in Formatters Copy Link
The grid will pass the grid API and context into the context property of the formatter parameters.
Properties available on the GridChartContext<TData = any, TContext = any> interface.
The grid api. |
Application context as set on gridOptions.context. |
For formatters that are directly linked to row data, the row node will be passed in the datum.node property (note that datum may be undefined).
For formatters related to columns, the key property will usually contain the column ID. In some instances this will be in the key property of the corresponding series in the boundSeries property.
In the example below, the global formatter is used to access the valueFormatter in each of the columns:
- The x axis uses the Financial Period column value formatter.
- The y axis does not use a formatter as it belongs to multiple columns.
- The tooltip (when mousing over the series) uses the value formatters from each of the three columns.
import {
AgChartsEnterpriseModule,
FormatterParams,
} from "ag-charts-enterprise";
import {
ChartRef,
ChartType,
ClientSideRowModelModule,
ColumnApiModule,
FirstDataRenderedEvent,
GridApi,
GridChartContext,
GridOptions,
GridReadyEvent,
IRowNode,
ModuleRegistry,
createGrid,
enableDevValidations,
} from "ag-grid-community";
import {
ColumnMenuModule,
ContextMenuModule,
IntegratedChartsModule,
RowGroupingModule,
} from "ag-grid-enterprise";
import { getData } from "./data";
// Enable extended validations only for development
if (process.env.NODE_ENV !== "production") {
enableDevValidations();
}
ModuleRegistry.registerModules([
ClientSideRowModelModule,
IntegratedChartsModule.with(AgChartsEnterpriseModule),
ColumnMenuModule,
ContextMenuModule,
RowGroupingModule,
ColumnApiModule,
]);
let gridApi: GridApi;
let chartRef: ChartRef;
const gridOptions: GridOptions = {
columnDefs: [
{
field: "period",
chartDataType: "category",
headerName: "Financial Period",
width: 150,
valueFormatter: (params) => {
const parts = params.value?.split(" ");
return parts ? `${parts[1]} - ${parts[0]}` : "";
},
},
{
field: "recurring",
chartDataType: "series",
headerName: "Recurring Revenue",
valueFormatter: (params) => {
return `Ā£${params.value}`;
},
},
{
field: "individual",
chartDataType: "series",
headerName: "Individual Sales",
valueFormatter: (params) => {
return `$${params.value}`;
},
},
],
defaultColDef: {
flex: 1,
minWidth: 100,
},
popupParent: document.body,
cellSelection: true,
enableCharts: true,
chartToolPanelsDef: {
defaultToolPanel: "settings",
},
onGridReady: (params: GridReadyEvent) => {
getData().then((rowData) => params.api.setGridOption("rowData", rowData));
},
onFirstDataRendered,
chartThemeOverrides: {
common: {
formatter: (params: FormatterParams) => {
const { type, key, datum, value, context, boundSeries } = params;
if (type === "number") {
return formatValue(
key,
datum?.node,
context as GridChartContext,
value,
);
}
if (type === "category") {
return formatValue(
boundSeries?.[0]?.key,
datum?.node,
context as GridChartContext,
value?.toString(),
);
}
// fall back to default
return undefined;
},
},
},
};
function formatValue(
colId: string | undefined,
node: IRowNode | undefined,
chartContext: GridChartContext,
value: any,
) {
const column = colId ? chartContext.api.getColumn(colId) : null;
if (column) {
const colDef = column.getColDef();
const valueFormatter = colDef.valueFormatter;
if (typeof valueFormatter === "function") {
const formattedValue = valueFormatter({
...chartContext,
column,
colDef,
node: node ?? null,
data: node?.data,
value,
});
return formattedValue;
}
}
return undefined;
}
function onFirstDataRendered(params: FirstDataRenderedEvent) {
chartRef = params.api.createRangeChart({
chartContainer: document.querySelector("#myChart") as HTMLElement,
cellRange: {
columns: ["period", "recurring", "individual"],
},
chartType: "groupedColumn",
})!;
}
function updateChart(chartType: ChartType) {
gridApi.updateChart({
type: "rangeChartUpdate",
chartId: `${chartRef.chartId}`,
chartType: chartType,
});
}
const gridDiv = document.querySelector<HTMLElement>("#myGrid")!;
gridApi = createGrid(gridDiv, gridOptions);
if (typeof window !== "undefined") {
// Attach external event handlers to window so they can be called from index.html
(<any>window).updateChart = updateChart;
}
.wrapper {
display: flex;
flex-direction: column;
height: 100%;
}
.button-container {
flex-wrap: wrap;
}
#myGrid {
flex: 1;
}
#myChart {
flex: 2;
min-height: 530px;
}
export async function getData(delay: number = 100): Promise<any[]> {
return new Promise((resolve) => setTimeout(() => resolve(generateData()), delay));
}
function generateData(): any[] {
return [
{ period: 'Q1 2021', recurring: 485829, individual: 237438 },
{ period: 'Q2 2021', recurring: 512743, individual: 245672 },
{ period: 'Q3 2021', recurring: 521938, individual: 259371 },
{ period: 'Q4 2021', recurring: 535421, individual: 271839 },
{ period: 'Q1 2022', recurring: 558329, individual: 284738 },
{ period: 'Q2 2022', recurring: 572843, individual: 298472 },
{ period: 'Q3 2022', recurring: 589372, individual: 312849 },
{ period: 'Q4 2022', recurring: 601234, individual: 327195 },
{ period: 'Q1 2023', recurring: 615928, individual: 342839 },
{ period: 'Q2 2023', recurring: 628472, individual: 358293 },
{ period: 'Q3 2023', recurring: 642839, individual: 374829 },
{ period: 'Q4 2023', recurring: 657382, individual: 391829 },
];
}
<div class="wrapper">
<div class="button-container">
<button onclick="updateChart('groupedColumn')">Grouped Column</button>
<button onclick="updateChart('line')">Line</button>
<button onclick="updateChart('donut')">Donut</button>
</div>
<div id="myGrid"></div>
<div id="myChart"></div>
</div>