Skip to content

Waterfall ​

A cumulative bridge: each yField value is a change, and bars grow from the running total. totals marks the indices of total rows (a bar from zero to the running total).

ts
import { getData } from './data';
import type { ChartOptions } from 'grafit-charts';

export function createOptions(): ChartOptions {
  return {
    data: getData(),
    title: { text: 'P&L bridge' },
    subtitle: { text: '₽M' },
    series: [
      {
        type: 'waterfall',
        xField: 'step',
        yField: 'value',
        name: 'Change',
        // indices 3 and 6 are the subtotal and the final total
        totals: [3, 6],
      },
    ],
    legend: { enabled: false },
  };
}
ts
export function getData() {
  return [
    { step: 'Revenue', value: 420 },
    { step: 'COGS', value: -180 },
    { step: 'Marketing', value: -65 },
    { step: 'Gross', value: 0 },
    { step: 'Other income', value: 38 },
    { step: 'Taxes', value: -52 },
    { step: 'Net', value: 0 },
  ];
}

Subtotals and styles ​

totals — indices of total rows (a bar from zero), colors by sign via item; labels — label.formatter({ value, isTotal, datum }) (placements are the same as for bar):

ts
import { getData } from './data';
import type { ChartOptions } from 'grafit-charts';

export function createOptions(): ChartOptions {
  return {
    data: getData(),
    title: { text: 'P&L with subtotals' },
    series: [
      {
        type: 'waterfall',
        xField: 'step',
        yField: 'value',
        totals: [2, 6],
        cornerRadius: 4,
        item: {
          positive: { fill: '#21a06c' },
          negative: { fill: '#e5484d' },
          total: { fill: '#33404f' },
        },
        line: { enabled: true },
        label: {
          enabled: true,
          formatter: ({ value, isTotal }) => (isTotal ? String(value) : `${value > 0 ? '+' : ''}${value}`),
        },
      },
    ],
    legend: { enabled: false },
  };
}
ts
export function getData() {
  return [
    { step: 'Revenue', value: 420 },
    { step: 'COGS', value: -180 },
    { step: 'Gross profit', value: 0 },
    { step: 'Marketing', value: -60 },
    { step: 'R&D', value: -75 },
    { step: 'Other', value: 18 },
    { step: 'Operating profit', value: 0 },
  ];
}
OptionTypeDefaultDescription
xFieldstring—category and change
yFieldstring—category and change
totalsnumber[]—indices of total rows
cornerRadiusPixels2bar corner rounding
label.enabledbooleanfalseshow value labels
label.placementouter/center/inner-* (17 placements)'top'label placement
label.formatter({ value, isTotal, datum }) => stringvaluelabel content
label.fontSizePixels11label font size
label.fontWeightstring | numbernormalfont weight
label.fontFamilystringtheme fontfont family
label.colorColorValueforeground; inside — auto contrasttext color
item.positive.fillColorValueseries colorfill of positive steps
item.negative.fillColorValuetheme redfill of negative steps
item.total.fillColorValuetheme mutedfill of total bars
itemStyler(params) => style—style of one step over its kind's fill; params.kind is positive/negative/total
line.enabledbooleantrueconnector lines
line.strokeColorValuetheme mutedconnector line color

Options ​

Options common to all series (name, showInLegend, tooltip.renderer, …) are covered in Common series options.

Its tooltip is handed more than a value: a step is a difference and a running total at once, so tooltip.renderer receives WaterfallTooltipRendererParams — { datum, xValue, delta, total, isTotal, seriesName, color }.

js
tooltip: { renderer: ({ xValue, delta, total }) => `${String(xValue)}: ${delta > 0 ? '+' : ''}${delta} → ${total}` },

Without a renderer the rows are named Total and Cumulative; both are locale keys.