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

[DataGrid] New Toolbar component #14611

Merged
merged 195 commits into from
Mar 3, 2025
Merged

Conversation

KenanYusuf
Copy link
Member

@KenanYusuf KenanYusuf commented Sep 13, 2024

Adds a redesigned Toolbar component through a new composition API, documented here:

There are several related components that users can use to build a custom toolbar:

Note

This PR only adds the building blocks to create custom toolbars, and documentation for those components. I'm hoping we can release these in v7 for users to try out. There will be a follow up to update the default grid toolbar for v8 to use the new components. #15823

Closes #11584


Follow-up tasks:

@KenanYusuf KenanYusuf added component: data grid This is the name of the generic UI component, not the React module! design This is about UI or UX design, please involve a designer proof of concept Studying and/or experimenting with a to be validated approach labels Sep 13, 2024
@github-actions github-actions bot added the PR: out-of-date The pull request has merge conflicts and can't be merged label Oct 18, 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 1, 2024
Comment on lines +16 to +18
<ColumnsPanelTrigger render={<ToolbarButton />}>
<ViewColumnIcon fontSize="small" />
</ColumnsPanelTrigger>
Copy link
Member

Choose a reason for hiding this comment

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

Are these 2 snippets equal?

<ColumnsPanelTrigger render={<ToolbarButton />}>
  <ViewColumnIcon fontSize="small" />
</ColumnsPanelTrigger>
<ColumnsPanelTrigger render={<ToolbarButton><ViewColumnIcon fontSize="small" /></ToolbarButton>} />

Copy link
Member Author

Choose a reason for hiding this comment

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

They are, it comes down to preference. Do you think it's worth documenting this on the usage page?

Copy link
Member

Choose a reason for hiding this comment

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

Yes, I think it's worth clarifying that children are automatically passed to the render element. Otherwise, it might be a bit "magical" for an average user 😅

Copy link
Member Author

Choose a reason for hiding this comment

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

Mentioned it here - let me know how that sounds.

Copy link
Member

Choose a reason for hiding this comment

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

If I understood correctly, it makes more sense to use render prop if there's a need to access props or state.

<ColumnsPanelTrigger render={(props) => <ToolbarButton {...props} />}>
  <ViewColumnIcon fontSize="small" />
</ColumnsPanelTrigger>

Is it possible to skip the render prop for simple use cases?

<ColumnsPanelTrigger>
  <ToolbarButton>
    <ViewColumnIcon fontSize="small" />
  </ToolbarButton>
</ColumnsPanelTrigger>

Copy link
Member Author

@KenanYusuf KenanYusuf Feb 28, 2025

Choose a reason for hiding this comment

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

If I understood correctly, it makes more sense to use render prop if there's a need to access props or state.

That's a reason to pass a function to the render prop. The other case is to merge functionality and change the element rendered by a component.

Is it possible to skip the render prop for simple use cases?

In this example, no. The <ColumnsPanelTrigger /> and <ToolbarButton /> both render a <button />, so you would end up with this:

<button>
  <button>
     <svg />
  </button>
</button>

By using the render prop like: <ColumnsPanelTrigger render={<ToolbarButton />}><ViewColumnIcon /></ColumnsPanelTrigger>, the styled button element rendered by <ToolbarButton /> is the only element that gets rendered but with the merged attributes from both elements:

<button>
  <svg />
</button>

I will update the usage doc to try to explain this better.

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've made some updates to the Usage doc. Let me know if there's anything that could be clarified further.

Copy link
Member

@MBilalShafi MBilalShafi Mar 3, 2025

Choose a reason for hiding this comment

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

Thanks for the clarification, I understand it's one DX vs the other.

The other case is to merge functionality and change the element rendered by a component.

Would doing something like the following here avoid creating the nested button elements while supporting the third case below: 🤔

const { children, ...rest } = props;
if (React.isValidElement(children)) {
  return React.cloneElement(children, rest);
}

My point was to skip the default <button /> element if there's no render prop and children are passed. In that case <ColumnsPanelTrigger /> would serve as a logic layer and the view layer would be delegated to the user.

So these would be the possible options for the users rendering the same output.

<ColumnsPanelTrigger render={<ToolbarButton />}>
  <ViewColumnIcon fontSize="small" />
</ColumnsPanelTrigger>
<ColumnsPanelTrigger render={<ToolbarButton><ViewColumnIcon fontSize="small" /></ToolbarButton>} />
<ColumnsPanelTrigger>
  <ToolbarButton>
    <ViewColumnIcon fontSize="small" />
  </ToolbarButton>
</ColumnsPanelTrigger>

I'm fine if we want to stick to the render prop only, it's just that "only with children" use-case could be really nice DX wise and pretty self-explanatory.
Consider this thread unblocking for the PR merge.

Copy link
Member Author

@KenanYusuf KenanYusuf Mar 3, 2025

Choose a reason for hiding this comment

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

My point was to skip the default element if there's no render prop and children are passed

I agree it might be a nice DX and less verbose, however, with these components we are also trying to ensure that the rendered HTML is using the correct semantics and aria attributes. Users may want to use the <ColumnsPanelTrigger /> outside of the <Toolbar /> and drop the <ToolbarButton />:

<ColumnsPanelTrigger>
  <ViewColumnIcon fontSize="small" />
</ColumnsPanelTrigger>

If we were to use the third option you suggested, it would result in invalid HTML; an <svg /> gets rendered with a bunch of attributes intended for a button.

Copy link
Member

Choose a reason for hiding this comment

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

Fair enough, let's not risk semantic inconsistencies.

@arminmeh
Copy link
Contributor

Besides the latest open points LGTM
Really like the demos in the docs 💯

Comment on lines +16 to +18
<ColumnsPanelTrigger render={<ToolbarButton />}>
<ViewColumnIcon fontSize="small" />
</ColumnsPanelTrigger>
Copy link
Member

Choose a reason for hiding this comment

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

If I understood correctly, it makes more sense to use render prop if there's a need to access props or state.

<ColumnsPanelTrigger render={(props) => <ToolbarButton {...props} />}>
  <ViewColumnIcon fontSize="small" />
</ColumnsPanelTrigger>

Is it possible to skip the render prop for simple use cases?

<ColumnsPanelTrigger>
  <ToolbarButton>
    <ViewColumnIcon fontSize="small" />
  </ToolbarButton>
</ColumnsPanelTrigger>

@KenanYusuf KenanYusuf merged commit ad5f90d into mui:master Mar 3, 2025
16 checks passed
@KenanYusuf KenanYusuf deleted the experimental-toolbar branch March 3, 2025 11:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
component: data grid This is the name of the generic UI component, not the React module! customization: dom Component's DOM customizability, e.g. slot design This is about UI or UX design, please involve a designer new feature New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[data grid] Improved Toolbar
8 participants