[Feature]: Allow active bar/category/legend to be controlled via props, not only as uncontrolled internal state

Author: BenJenkinsonCreated Mar 23, 2024Updated Mar 4, 2025
LabelsStatus: Help WantedType: Feature

What problem does this feature solve?

Many of the charts provide an onValueChanged callback which is called when different pieces of the chart are clicked on, usually "selecting" that piece of data and storing the selection in state.

The chart components do not provide a way to control this "selected" data from outside the component, via props.

Example Scenario 1

I have multiple charts on a page. All charts have the same categories of data.

When I click on the legend for the first chart "selecting" a category of data I would want to set all charts on the page to reflect the same category.

Example Scenario 2

I want to let users link to a page that displays a chart, including their current selection of data.

I want to derive the currently selected category from URL search params, and then pass that category to the charts.

What does the proposed API look like?

Using the BarChart as an example, there are four possible selection states:

  • All bars for a single category (clicking the legend)
  • A single bar, of a single category (clicking the bar)
  • All bars, of a single group/index (clicking the label on the x-axis)
  • Nothing

Which could be represented by a single object:

typescript
type ChartSelection = {
    type: "category";
    category: string;
    index: never;
} | {
    type: "index";
    category: never;
    index: string;
} | {
    type: "bar"; // To make this work for non-bar charts (where you use "bubble" and others), you could use "datum"
    category: string;
    index: string;
}

I'm not sure whether this would be more useful as a single prop which expected a matching object (e.g. selected), or with one prop for each aspect (e.g. selectedCategory/selectedIndex)

typescript
// With a single prop
type Props = {
  // ...
  selected: { category?: string; index?: string } | null | undefined;
}

// With one prop per aspect
type Props = {
  // ...
  selectedCategory?: string | null | undefined;
  selectedIndex?: string | null | undefined;
}
typescript
function Example() {
  const [selectedCategory, setSelectedCategory] = useState<string>();
  const categories = ["2022", "2023"];

  return (
    <div>
      <BarChart
        data={firstChartData}
        categories={categories}
        onValueChange={(value) => {
          setSelectedCategory(value?.categoryClicked);
        }}
        selectedCategory={selectedCategory}
      />
      <BarChart
        data={secondChartData}
        categories={categories}
        onValueChange={(value) => {
          setSelectedCategory(value?.categoryClicked);
        }}
        selectedCategory={selectedCategory}
      />
    </div>
  );
}