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

[TreeView] Add support for checkbox selection #11452

Merged
merged 43 commits into from
May 13, 2024

Conversation

flaviendelangle
Copy link
Member

@flaviendelangle flaviendelangle commented Dec 19, 2023

Fixes #6019
Doc preview

The main inspiration is https://mui.com/x/react-data-grid/row-selection/#checkbox-selection

  • Add checkbox selection on TreeItem
  • Add checkbox selection on TreeItem2
  • Add doc
  • Add tests

@flaviendelangle flaviendelangle self-assigned this Dec 19, 2023
@flaviendelangle flaviendelangle added the component: tree view TreeView, TreeItem. This is the name of the generic UI component, not the React module! label Dec 19, 2023
@mui-bot
Copy link

mui-bot commented Dec 19, 2023

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

github-actions bot commented Jan 2, 2024

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 Jan 2, 2024
Copy link
Member

@joserodolfofreitas joserodolfofreitas left a comment

Choose a reason for hiding this comment

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

Nice! This is looking quite solid. I'd say we can release it soon and quickly bring value for those willing to install a pre-release.

To have a good clean implementation we need

  1. To add a baseCheckbox slot like on the grid to allow using other design systems

I agree, and I suppose users can style the default checkbox, which probably cover the majority of customization use cases, so maybe we can release the slot on a follow up?

  1. To add improve the plugin system so that each plugin can pass stuff to the context, having to add stuff to useTreeViewContextValueBuilder is bad because we have one plugin that is aware of the existing of all the others.

I suppose this is also a separate PR. Must it be delivered first?

@flaviendelangle
Copy link
Member Author

I agree, and I suppose users can style the default checkbox, which probably cover the majority of customization use cases, so maybe we can release the slot on a follow up?

Right now they can't pass props to the default checkbox specifically on the tree view (they'd need a lot to do it)
All they can do it use the theme to pass default props to all their checkbox throughout their application (even the one not in tree view).

Their is nothing preventing us from adding the slot in a follow up though. I think it will quickly be useful, but there is no reason to be blocking.

I suppose this is also a separate PR. Must it be delivered first?

For now this is only used internally since nobody except us can build plugins, so nothing blocking here 👍

@flaviendelangle flaviendelangle marked this pull request as ready for review January 3, 2024 15:53
@github-actions github-actions bot added the PR: out-of-date The pull request has merge conflicts and can't be merged label Jan 3, 2024
Copy link

github-actions bot commented Jan 3, 2024

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 Jan 3, 2024
};
};

useTreeViewFocus.getInitialState = () => ({ focusedNodeId: null });

useTreeViewFocus.getDefaultizedParams = (params) => ({
Copy link
Member Author

Choose a reason for hiding this comment

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

Would be great if TS would have caught this one...

@flaviendelangle flaviendelangle changed the title [tree view] Add support for checkbox selection [TreeView] Add support for checkbox selection Apr 23, 2024
@@ -72,12 +74,15 @@ export const useTreeItem2 = <TPlugins extends DefaultTreeViewPlugins = DefaultTr
const createContentHandleClick =
(otherHandlers: EventHandlers) => (event: React.MouseEvent & MuiCancellableEvent) => {
otherHandlers.onClick?.(event);
if (event.defaultMuiPrevented) {
if (event.defaultMuiPrevented || checkboxRef.current?.contains(event.target as HTMLElement)) {
Copy link
Member Author

Choose a reason for hiding this comment

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

We don't want to toggle the expansion when clicking on the checkbox

Copy link
Member

Choose a reason for hiding this comment

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

Have you tried setting event = defaultMuiPrevented in the checkbox change handler? 🤔
Maybe it would allow us to ditch the || case? 🤔

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, the onChange event is not shared with the onClick one so it does not work
I could add an onClick on the checkbox and prevent there of course, I'll see if it works. If it allow us to skip the ref then it's probably an improvement

Copy link
Member

Choose a reason for hiding this comment

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

Ah, damn, indeed, those are different kinds of events. 🙈
In that case, I'm fine with both approaches. However, naturally, if ref is used only for this check, then it probably makes sense to intercept the onClick on the checkbox and avoid this problem that way. 👍

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 tried, the defaultMuiPrevented works.
The only problem is that it's usually a property our users set to disable our behavior and that we very rarely set ourselves (I only found one occurence in the core codebase).
I don't know if this would have side effects, to be honest I didn't find any...

Here is the diff for TreeItem:

diff --git a/packages/x-tree-view/src/TreeItem/TreeItemContent.tsx b/packages/x-tree-view/src/TreeItem/TreeItemContent.tsx
index 161d3d3af..c16d94c5b 100644
--- a/packages/x-tree-view/src/TreeItem/TreeItemContent.tsx
+++ b/packages/x-tree-view/src/TreeItem/TreeItemContent.tsx
@@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
 import clsx from 'clsx';
 import Checkbox from '@mui/material/Checkbox';
 import { useTreeItemState } from './useTreeItemState';
+import { MuiCancellableEvent } from '../internals/models/MuiCancellableEvent';
 
 export interface TreeItemContentProps extends React.HTMLAttributes<HTMLElement> {
   className?: string;
@@ -85,7 +86,6 @@ const TreeItemContent = React.forwardRef(function TreeItemContent(
   } = useTreeItemState(itemId);
 
   const icon = iconProp || expansionIcon || displayIcon;
-  const checkboxRef = React.useRef<HTMLButtonElement>(null);
 
   const handleMouseDown = (event: React.MouseEvent<HTMLDivElement>) => {
     preventSelection(event);
@@ -95,8 +95,8 @@ const TreeItemContent = React.forwardRef(function TreeItemContent(
     }
   };
 
-  const handleClick = (event: React.MouseEvent<HTMLDivElement>) => {
-    if (checkboxRef.current?.contains(event.target as HTMLElement)) {
+  const handleClick = (event: React.MouseEvent<HTMLDivElement> & MuiCancellableEvent) => {
+    if (event.defaultMuiPrevented) {
       return;
     }
 
@@ -111,6 +111,12 @@ const TreeItemContent = React.forwardRef(function TreeItemContent(
     }
   };
 
+  const handleCheckboxClick = (
+    event: React.MouseEvent<HTMLButtonElement> & MuiCancellableEvent,
+  ) => {
+    event.defaultMuiPrevented = true;
+  };
+
   return (
     /* eslint-disable-next-line jsx-a11y/click-events-have-key-events,jsx-a11y/no-static-element-interactions -- Key event is handled by the TreeView */
     <div
@@ -131,8 +137,8 @@ const TreeItemContent = React.forwardRef(function TreeItemContent(
           className={classes.checkbox}
           checked={selected}
           onChange={handleCheckboxSelection}
+          onClick={handleCheckboxClick}
           disabled={disabled || disableSelection}
-          ref={checkboxRef}
           tabIndex={-1}
         />
       )}

@LukasTy LukasTy added the new feature New feature or request label Apr 25, 2024
Copy link
Member

@LukasTy LukasTy left a comment

Choose a reason for hiding this comment

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

Great feature and superb execution! 💯 🎉
Leaving last few questions / discussion points. 👍

handleExpansion,
handleSelection,
preventSelection,
} = useTreeItemState(nodeId);

const icon = iconProp || expansionIcon || displayIcon;
const checkboxRef = React.useRef<HTMLButtonElement>(null);
Copy link
Member

Choose a reason for hiding this comment

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

Gotcha. It makes sense given the existing implementation. 👌
I'd just be curious to hear if @mui/material-ui thinks that this is correct. 🤔

@@ -72,12 +74,15 @@ export const useTreeItem2 = <TPlugins extends DefaultTreeViewPlugins = DefaultTr
const createContentHandleClick =
(otherHandlers: EventHandlers) => (event: React.MouseEvent & MuiCancellableEvent) => {
otherHandlers.onClick?.(event);
if (event.defaultMuiPrevented) {
if (event.defaultMuiPrevented || checkboxRef.current?.contains(event.target as HTMLElement)) {
Copy link
Member

Choose a reason for hiding this comment

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

Have you tried setting event = defaultMuiPrevented in the checkbox change handler? 🤔
Maybe it would allow us to ditch the || case? 🤔

@flaviendelangle flaviendelangle merged commit 9317717 into mui:master May 13, 2024
15 checks passed
@flaviendelangle flaviendelangle deleted the checkbox-selection branch May 13, 2024 08:45
arthurbalduini pushed a commit to arthurbalduini/mui-x that referenced this pull request May 23, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
component: tree view TreeView, TreeItem. 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.

[TreeView] Add checkbox support
9 participants