tremorlabs / tremor

React components to build charts and dashboards
https://tremor.so
Apache License 2.0
15.39k stars 446 forks source link

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

Open BenJenkinson opened 1 month ago

BenJenkinson commented 1 month ago

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:

Which could be represented by a single object:

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)

// 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;
}
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>
  );
}
BenJenkinson commented 1 month ago

Related issues:

Erasmus001 commented 1 month ago

great

orinamio commented 3 weeks ago

Hi! I would like to work on this. Can I be assigned to the issue?