Line Chart for a Monthly Revenue Trend
You are the finance lead, and every month someone sends you the revenue table. Three product lines, twelve months each, thirty-six numbers. The question you actually have is not in any cell: is each line climbing, and when did it dip? A table makes you read your way to that answer. A line chart shows it before you have read anything.
The example below does it with the rows on screen. The chart is bound to the grid, so it draws exactly the rows the grid is showing. Put a product line’s name in the box, or open the filter on the Product line column, and the lines you do not want leave the chart on the next frame. Clear the filter and they come back.
Why a line and not columns
Columns are the obvious choice, and they are right for comparing one month with another. They are the wrong choice for a trend, because the eye has to jump from the top of one column to the top of the next to judge the slope. A line is the slope. With twelve points per series, the summer dip in services and hardware reads as one shape instead of six short columns, and three lines share one plot without the clutter of thirty-six bars. If the figure holds its value between readings, a price list or a plan tier, the step variant draws it as a staircase instead: the same binding with type: 'step'.
The chart is a view of the grid
There is no second data set to keep in step. createChart takes the grid itself, reads its filtered rows, and redraws whenever they change. x names the category column, y the measure, and series splits the measure into one line per product line. Sort, filter or edit the grid and the chart follows, because a chart of rows you cannot see would be describing a different data set.
The code, in plain JavaScript
import { createGrid } from '@toclocoinc/lattice-grid';
import { createChart } from '@toclocoinc/lattice-grid/modules/charts';
import '@toclocoinc/lattice-grid/css';
const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
const revenue = {
Subscriptions: [412, 418, 425, 431, 438, 440, 421, 417, 446, 455, 463, 471],
Services: [236, 241, 258, 262, 255, 248, 189, 176, 251, 266, 272, 281],
Hardware: [158, 149, 163, 171, 169, 162, 118, 104, 176, 214, 262, 298],
};
const rows = months.flatMap((month, m) =>
Object.keys(revenue).map((line, l) => ({
id: m * 3 + l, month, line, revenue: revenue[line][m] * 1000,
}))
);
const grid = createGrid(document.querySelector('#grid'), {
rowKey: 'id',
columns: [
{ field: 'month', title: 'Month' },
{ field: 'line', title: 'Product line', filter: { type: 'set' } },
{ field: 'revenue', title: 'Revenue', type: 'number' },
],
rows,
});
createChart({
grid, container: '#chart', type: 'line',
x: 'month', y: 'revenue', series: 'line',
title: 'Monthly revenue by product line',
});
Give #chart and #grid a height in your page and that is the whole screen.
The same thing in React
The React adapter is a factory: you pass it React and createGrid, and it returns the component. The component forwards a ref whose .grid is the real grid, which is what the chart binds to.
import React, { useEffect, useRef } from 'react';
import { createGrid } from '@toclocoinc/lattice-grid';
import { createChart } from '@toclocoinc/lattice-grid/modules/charts';
import { createLatticeGrid } from '@toclocoinc/lattice-grid/modules/react';
import '@toclocoinc/lattice-grid/css';
const LatticeGrid = createLatticeGrid({ React, createGrid });
const columns = [
{ field: 'month', title: 'Month' },
{ field: 'line', title: 'Product line', filter: { type: 'set' } },
{ field: 'revenue', title: 'Revenue', type: 'number' },
];
export function RevenueTrend({ rows }) {
const gridRef = useRef(null);
const chartEl = useRef(null);
useEffect(() => {
const chart = createChart({
grid: gridRef.current.grid, container: chartEl.current, type: 'line',
x: 'month', y: 'revenue', series: 'line',
title: 'Monthly revenue by product line',
});
return () => chart.destroy();
}, []);
return (
<>
<div ref={chartEl} style={{ height: 280 }} />
<LatticeGrid ref={gridRef} rowKey="id" columns={columns} rows={rows} style={{ height: 300 }} />
</>
);
}
Pass it the same thirty-six rows as the first listing, with month, line and revenue on each.
The options used
| Option | Type | What it does |
|---|---|---|
grid |
Grid | Required. The grid to read. |
container |
Element or string | Required. Where to draw. |
type |
string | 'line' here. 'step' holds each value until the next reading. |
x / y |
string | Category and measure columns. |
series |
string | Splits the measure into one series per distinct value. |
title |
string | Drawn above the plot. |
Go further
The charts page lists every chart type the same grid can feed, and the bar, line and pie family demo shows the neighbours of this one side by side. To see how a chart keeps following the grid when you filter by clicking, read how to draw a chart that follows the grid.