Skip to content

[charts-pro] Add a borderRadius property to FunnelChart #17660

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

Merged
merged 24 commits into from
May 6, 2025

Conversation

JCQuintas
Copy link
Member

@JCQuintas JCQuintas commented May 2, 2025

  • Adds a new borderRadius property to FunnelChart
  • Rewrite some curve implementations to gather all the points before drawing anything, as it makes calculating the border radius simpler.
  • Rename the FunnelStep curve to Step. It technically can handle any 4 point shape.

Changelog

  • Add a borderRadius property to FunnelChart. All funnels have a 8px borderRadius by default

    Screenshot 2025-05-06 at 14 00 20

@JCQuintas JCQuintas added new feature New feature or request component: charts This is the name of the generic UI component, not the React module! labels May 2, 2025
@JCQuintas JCQuintas self-assigned this May 2, 2025
@@ -20,6 +20,11 @@ export interface FunnelPlotProps extends FunnelPlotSlotExtension {
* @default 0
*/
gap?: number;
/**
* The radius, in pixels, of the corners of the funnel sections.
* @default 0
Copy link
Member Author

Choose a reason for hiding this comment

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

@noraleonte Should we make the default >0? Maybe also the gap? The funnel with border radius look quite nice to me 😄

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, I think we should. We can make it an 8 by default for a softer look 💙

@mui-bot
Copy link

mui-bot commented May 2, 2025

Deploy preview: https://deploy-preview-17660--material-ui-x.netlify.app/

Updated pages:

Generated by 🚫 dangerJS against bfab8a6

@JCQuintas JCQuintas changed the title [charts-pro] Add borderRadius property to FunnelChart [charts-pro] Add a borderRadius property to FunnelChart May 2, 2025
Copy link

codspeed-hq bot commented May 2, 2025

CodSpeed Performance Report

Merging #17660 will not alter performance

Comparing JCQuintas:funnel-border-radius (bfab8a6) with master (94211fd)

Summary

✅ 8 untouched benchmarks

@bernardobelchior
Copy link
Member

The border radius doesn't seem to be working:

image

@JCQuintas
Copy link
Member Author

The border radius doesn't seem to be working:

image

I forgot to run scripts 🫠, locally they work cause they use the tsx file, on deploy they use js 😢

Co-authored-by: Bernardo Belchior <[email protected]>
Signed-off-by: Jose C Quintas Jr <[email protected]>
@alexfauquette
Copy link
Member

alexfauquette commented May 5, 2025

Not exactly about the borderRadius, but I missed the PR about gap

The math are correct, but it feels weird. For mee slices seems to be disconnected.

image

A solution I found is to do an average between the correct position and the one if we ignored the gap.

const xGap = point.x + (index === 0 || index === 3 ? this.gap : -this.gap);

return {
-   y: yGetter(xGap),
+   y: yGetter((point.x + xGap) / 2),

image

The reason why it looks better to me is the alignment.

With their current placement, the slices join in the middle of the gap, and my brain is not evolved enough to notice when lines cross a middle point.
With the second option, my brain realizes without difficulty that their is some alignement

image

I also noticed that doing the opposite (making the larges item align on the smaller one) seems weird too.

image

I went with an average, but that's impact somewhat the link between data and visualisation.

A simpler solution is to tweak params you pass to your interpolator as follow:

const xGetter = xFromY(
  slopeStart.x,
  slopeStart.y - this.gap,
  slopeEnd.x,
  slopeEnd.y - this.gap,
);

The idea is to modify the interpolator such that bottom points are the one actually drawn by the chart, but to point are targeting the one of the previous item:

image

Playing with those let me notice what's probably an error in slope start/end:

- const slopeStart = this.points.at(index <= 1 ? 0 : 2)!;
+ const slopeStart = this.points.at(index <= 1 ? 0 : 3)!;
- const slopeEnd = this.points.at(index <= 1 ? 1 : 3)!;
+ const slopeStart = this.points.at(index <= 1 ? 0 : 3)!;

Other

By the way, I'm not a big fan of the fact that gap adds space between the initial/final itams and the drawing area. For me it should only add space between items. What paddingInner does in D3JS But I don't see an easy way to do that correctly 🤔

@JCQuintas
Copy link
Member Author

JCQuintas commented May 5, 2025

I went with an average, but that's impact somewhat the link between data and visualisation.

A simpler solution is to tweak params you pass to your interpolator as follow:

const xGetter = xFromY(
  slopeStart.x,
  slopeStart.y - this.gap,
  slopeEnd.x,
  slopeEnd.y - this.gap,
);

Will try this out. This will probably only affect this curve 🤔, so might be ok to do. I have pyramid and step-pyramid curves lined for review after this PR 😆

Playing with those let me notice what's probably an error in slope start/end:

- const slopeStart = this.points.at(index <= 1 ? 0 : 2)!;
+ const slopeStart = this.points.at(index <= 1 ? 0 : 3)!;
- const slopeEnd = this.points.at(index <= 1 ? 1 : 3)!;
+ const slopeStart = this.points.at(index <= 1 ? 0 : 3)!;

Why would the original be wrong? Points are:

vertical

3 ----- 0
 \     /
  2 - 1

horizontal

0 
| \
|  1
|  |
|  2
| /
3

So slopes are always 0-1 and 2-3 no?

Other

By the way, I'm not a big fan of the fact that gap adds space between the initial/final itams and the drawing area. For me it should only add space between items. What paddingInner does in D3JS But I don't see an easy way to do that correctly 🤔

Yeah, I couldn't offset that easily. Gap uses pixel, while the paddingInner implementation has the problem that it doesn't support pixels, which would give us the same issues we have in the bar, where we cant control the exact spacing.

@alexfauquette
Copy link
Member

I have pyramid and step-pyramid curves lined for review after this PR 😆

Pyramids aren't an issue because they all align on the same line :)

So slopes are always 0-1 and 2-3 no?

There is a mistake in my diff

But the current implementation is

current correct
0 -> 1 0 -> 1
2 -> 3 3 -> 2

Always starting from the larger to smaller edge.

By the way If you can make your ASCII scheme in the codebase that would be nice :)

@JCQuintas
Copy link
Member Author

Always starting from the larger to smaller edge.

Doesn't seem like it affects the output of the graph

@JCQuintas
Copy link
Member Author

By the way If you can make your ASCII scheme in the codebase that would be nice :)

I'm not sure where to put it, since it is true for all funnel curves

@alexfauquette
Copy link
Member

Doesn't seem like it affects the output of the graph

Try the following interpolation ;)

xFromY(
  slopeStart.x,
  slopeStart.y - this.gap,
  slopeEnd.x,
  slopeEnd.y - 10 * this.gap,
);

Copy link
Member

@bernardobelchior bernardobelchior left a comment

Choose a reason for hiding this comment

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

I think the previous funnel chart looked better (Argos). Here's the current state:

image

The imaginary line connecting the 180 section to the 200 section doesn't seem to line up, which looks a bit weird to me 🤔

Previously, I think it looked better:

image

If it's just me, it's fine to go ahead with this PR.

Comment on lines +123 to +134
const getBorderRadius = () => {
if (this.gap > 0) {
return this.borderRadius;
}
}
if (this.position === 0) {
return [0, 0, this.borderRadius, this.borderRadius];
}
if (this.position === this.sections - 1) {
return [this.borderRadius, this.borderRadius];
}
return 0;
};
Copy link
Member

Choose a reason for hiding this comment

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

Should we move this to a private class method? It doesn't reference any variable in scope.

Comment on lines +82 to +85
});

// Add gaps where they are needed.
this.points = this.points.map((point, index) => {
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
});
// Add gaps where they are needed.
this.points = this.points.map((point, index) => {
}).map((point, index) => {
// Add gaps where they are needed.

Copy link
Member Author

Choose a reason for hiding this comment

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

I prefer to leave it above the var assignment rather than in the fn

Copy link
Member

Choose a reason for hiding this comment

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

Not a deal breaker, but seems weird to me that we're assigning a variable just to re-assign it later.

@JCQuintas
Copy link
Member Author

I think the previous funnel chart looked better (Argos). Here's the current state:

image

The imaginary line connecting the 180 section to the 200 section doesn't seem to line up, which looks a bit weird to me 🤔

Previously, I think it looked better:

image

If it's just me, it's fine to go ahead with this PR.

I think both look ok. I might prefer the original, but I have also been looking at these for a long time 🤔

@bernardobelchior
Copy link
Member

I think the previous funnel chart looked better (Argos). Here's the current state:
image
The imaginary line connecting the 180 section to the 200 section doesn't seem to line up, which looks a bit weird to me 🤔
Previously, I think it looked better:
image
If it's just me, it's fine to go ahead with this PR.

I think both look ok. I might prefer the original, but I have also been looking at these for a long time 🤔

Now that I look at it with the border radius, it looks much better. Looks good to me 👍

Copy link
Member

@bernardobelchior bernardobelchior left a comment

Choose a reason for hiding this comment

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

Nice job 👍

@JCQuintas JCQuintas merged commit e9148f8 into mui:master May 6, 2025
22 checks passed
@JCQuintas JCQuintas deleted the funnel-border-radius branch May 6, 2025 12:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
component: charts This is the name of the generic UI component, not the React module! new feature New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants