-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
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
[TreeView] Add support for checkbox selection #11452
Conversation
4e7ee8d
to
4fabbc7
Compare
This pull request has conflicts, please resolve those before we can evaluate the pull request. |
8cd9ba0
to
19a94fe
Compare
There was a problem hiding this 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
- 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?
- 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?
Right now they can't pass props to the default checkbox specifically on the tree view (they'd need a lot to do it) 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.
For now this is only used internally since nobody except us can build plugins, so nothing blocking here 👍 |
This pull request has conflicts, please resolve those before we can evaluate the pull request. |
}; | ||
}; | ||
|
||
useTreeViewFocus.getInitialState = () => ({ focusedNodeId: null }); | ||
|
||
useTreeViewFocus.getDefaultizedParams = (params) => ({ |
There was a problem hiding this comment.
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...
13780c9
to
3486d4f
Compare
b8b79f8
to
d38cd25
Compare
...ee-view/src/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.ts
Show resolved
Hide resolved
@@ -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)) { |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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? 🤔
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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. 👍
There was a problem hiding this comment.
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}
/>
)}
aa4bf28
to
59c5454
Compare
There was a problem hiding this 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); |
There was a problem hiding this comment.
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. 🤔
...ee-view/src/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.ts
Show resolved
Hide resolved
@@ -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)) { |
There was a problem hiding this comment.
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? 🤔
Fixes #6019
Doc preview
The main inspiration is https://mui.com/x/react-data-grid/row-selection/#checkbox-selection
TreeItem
TreeItem2