Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[charts] Allow the creation of custom HTML components using charts data #15511

Merged
merged 29 commits into from
Nov 27, 2024

Conversation

JCQuintas
Copy link
Member

@JCQuintas JCQuintas commented Nov 20, 2024

Resolves #13852

Changelog

  • [Breaking] Charts don't have a div wrapping them anymore. All props are now passed to the root svg instead of the div.
  • ChartDataProvider and ChartsSurface are now fully divided — Learn more.
  • Users can create their own HTML components using chart data — Learn more.

@JCQuintas JCQuintas added enhancement This is not a bug, nor a new feature component: charts This is the name of the generic UI component, not the React module! labels Nov 20, 2024
@JCQuintas JCQuintas self-assigned this Nov 20, 2024
@mui-bot
Copy link

mui-bot commented Nov 20, 2024

Copy link

codspeed-hq bot commented Nov 20, 2024

CodSpeed Performance Report

Merging #15511 will not alter performance

Comparing JCQuintas:remove-wrapper-div (e55b66e) with master (6801300)

Summary

✅ 6 untouched benchmarks

@JCQuintas
Copy link
Member Author

JCQuintas commented Nov 20, 2024

@alexfauquette check this out. It seems to be possible to fully remove the wrapper div around the charts. 🤔
There are only minimal changes to the argos visuals

Still need to fix the tooltips though 😆

@alexfauquette
Copy link
Member

It seems to be possible to fully remove the wrapper div around the charts. 🤔

Why are you trying to remove it? I'm looking at the Recharts DOM structure and it seems quite similar our current structure

<div class="recharts-responsive-container"> // Takes full parent size
  <div class="recharts-wrapper"> // Full size and position relative
    <svg class="recharts-surface">...</svg> // Full size
    <div class="recharts-legend-wrapper">...</div> // Position absolute
  </div>
</div>

Do you have a particular use case in mind where it's needed?

@JCQuintas
Copy link
Member Author

JCQuintas commented Nov 20, 2024

It seems to be possible to fully remove the wrapper div around the charts. 🤔

Why are you trying to remove it? I'm looking at the Recharts DOM structure and it seems quite similar our current structure

<div class="recharts-responsive-container"> // Takes full parent size
  <div class="recharts-wrapper"> // Full size and position relative
    <svg class="recharts-surface">...</svg> // Full size
    <div class="recharts-legend-wrapper">...</div> // Position absolute
  </div>
</div>

Do you have a particular use case in mind where it's needed?

Not exactly needed, but feels useless.

My goal is that the user will be able to do:

function MyComponent() {
  const a = useBarSeries();

  return (
    <div>
      {a?.seriesOrder.map((id) => {
        const series = a.series[id];
        return <div key={id}>{series.data.join('')}</div>;
      })}
    </div>
  );
}

const MyChart = () => {
  return (
    <div style={{ display: 'flex', flexDirection: 'column' }}>
      <ChartDataProvider ... >
        <ChartsSurface ...>{...}</ChartsSurface>
        <MyComponent />
      </ChartDataProvider>
    </div>
  );
});

And this will result in a very simple initial structure.

<div>     // provided by user
  <svg /> // chart
  <div /> // provided by user
</div>

// current

<div>       // provided by user
  <div>     // responsive container
    <svg /> // chart
  </div>    // provided by user
  <div />
</div>

There is only one layer the user needs to think about for classes and props.
And if they want to put stuff outside of the chart that has to take the same size, they can handle it themselves.

@alexfauquette
Copy link
Member

I was surprised that this work

<ChartDataProvider ... >
    <ChartsSurface ...>{...}</ChartsSurface>
    <MyComponent />
</ChartDataProvider>

But it's because I thaugh we measure the parent size but we are measuring the element itself

const win = ownerWindow(mainEl);
const computedStyle = win.getComputedStyle(mainEl);

I'm ok with the idea. I would be interested to see a component. For example the BarChart with an HTML legend

@JCQuintas
Copy link
Member Author

I would be interested to see a component. For example the BarChart with an HTML legend

Would something like this be good? https://codesandbox.io/p/sandbox/mui-mui-x-x-data-grid-forked-qvc5q3?file=%2Fsrc%2Fdemo.tsx%3A33%2C31

Is there anything specific you would like to see? 😆

@alexfauquette
Copy link
Member

Would something like this be good?

Yes, it looks nice. I hope people will not try too crazy stuff with the div that wrapp the SVG and the legend together.

@JCQuintas
Copy link
Member Author

Would something like this be good?

Yes, it looks nice. I hope people will not try too crazy stuff with the div that wrapp the SVG and the legend together.

What do you mean by "too crazy"?

@JCQuintas
Copy link
Member Author

Arbitrary absolute positioning also works: https://codesandbox.io/p/sandbox/mui-mui-x-x-data-grid-forked-xy38kf?file=%2Fsrc%2Fdemo.tsx%3A22%2C44&workspaceId=836800c1-9711-491c-a89b-3ac24cbd8cd8

@alexfauquette
Copy link
Member

What do you mean by "too crazy"?

It's just that devs will probably use that with all the display options possible. But that's not a real issue. With the two examples you made it should be ok 👍

@JCQuintas
Copy link
Member Author

Regarding docs any idea where it should stand? I suppose Composition, but should we divide the docs into two pages?

ChartContainer composition vs ChartDataProvider composition?

Or inside composition we have a section for ## Accessing data inside HTML? 🤔

@JCQuintas
Copy link
Member Author

JCQuintas commented Nov 21, 2024

Btw should we remove the unstable_ from the useSeries hooks?

created the PR: #15545

@alexfauquette
Copy link
Member

Regarding docs any idea where it should stand? I suppose Composition, but should we divide the docs into two pages?

What about a third option that would be to start with a general introduction about ChartDataProvider, ChartSurface, ChartBarPlot

And then a subsection explaining that we provide a helpêr ChartCOntainer that group ChartDataProvider and ChartSurface which is adapted to most usecases

@github-actions github-actions bot added the PR: out-of-date The pull request has merge conflicts and can't be merged label Nov 26, 2024
Copy link

This pull request has conflicts, please resolve those before we can evaluate the pull request.

@github-actions github-actions bot removed the PR: out-of-date The pull request has merge conflicts and can't be merged label Nov 26, 2024
docs/data/charts/components/components.md Outdated Show resolved Hide resolved
docs/data/charts/components/components.md Outdated Show resolved Hide resolved
docs/data/charts/components/components.md Outdated Show resolved Hide resolved
docs/data/charts/composition/composition.md Outdated Show resolved Hide resolved
Comment on lines 117 to 121
const element = svgRef.current;
if (element === null) {
return 'no-point-found';
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that's why the check for element === null was initially at the beginning. If the element is null, we will not attach events to it so we can do early return.

If your concern is about the risk of mutation, notice it's the SVG ref. The moment it's null, it means we have no chart to display, so we have bigger issue in that case :)

Co-authored-by: Alexandre Fauquette <[email protected]>
Signed-off-by: Jose C Quintas Jr <[email protected]>
Signed-off-by: Jose C Quintas Jr <[email protected]>
@JCQuintas JCQuintas changed the title [charts] Remove charts wrapper div [charts] Allow the creation of custom HTML components using charts data Nov 27, 2024
@JCQuintas JCQuintas added the v8.x label Nov 27, 2024
Comment on lines 109 to 113
React.useEffect(() => {
if (svgRef.current === null) {
return undefined;
}
const element = svgRef.current;
if (element === null) {
return () => {};
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you don't do this revert, You should be able to remove the ! of element. I don't know why TS does not take into account the early return, because event mutation is not an explanation

const x = { current: document };
const a = x.current;

x.current = null;

console.log(x) // { current: null }
console.log(a) // document

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah it is odd 🤔

@JCQuintas JCQuintas enabled auto-merge (squash) November 27, 2024 13:39
@JCQuintas JCQuintas merged commit 1992f8d into mui:master Nov 27, 2024
19 checks passed
@JCQuintas JCQuintas deleted the remove-wrapper-div branch November 27, 2024 13:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
breaking change component: charts This is the name of the generic UI component, not the React module! enhancement This is not a bug, nor a new feature v8.x
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[charts] Get access to the data outside of the SVG
3 participants