-
Notifications
You must be signed in to change notification settings - Fork 543
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
Add TreeView.LeadingAction
sub-component
#4546
Conversation
🦋 Changeset detectedLatest commit: 29348d6 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
size-limit report 📦
|
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 have some initial feedback, but I really think we need somebody from a11y to take a look. For example, I'm not sure the "right"/accessible way to add interactive controls inside of a role="treeitem"
.
It also looks like consumers will have to write their own drag-and-drop functionality since there are no props to handle onDragStart
/onDragEnd
callbacks. Also, we should agree on the right designs/interactions for a tree view when it's actively being dragged. For example:
- What does the node look like when the user is dragging it? Probably just knock the opacity down to make it look like a "ghost" of the original node.
- How do we indicate the prospective new position of the node while it's being dragged? Probably a blue line that acts like a cursor to show what position the node will be in after it's dropped.
const [isLoading, setIsLoading] = React.useState(false) | ||
const [asyncItems, setAsyncItems] = React.useState<string[]>([]) | ||
|
||
let state: SubTreeState = 'initial' | ||
|
||
if (isLoading) { | ||
state = 'loading' | ||
} else if (asyncItems.length > 0) { | ||
state = 'done' | ||
} |
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 don't think we need to include the loading functionality for this story. We can just focus on the drag-and-drop behavior.
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.
+1
{dragHandle ? ( | ||
// eslint-disable-next-line jsx-a11y/no-static-element-interactions | ||
<div className="PRIVATE_TreeView-item-drag-handle" onMouseDown={() => setIsExpanded(false)}> | ||
{dragHandle} |
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 don't think we should require folks to style their own drag handles. We should ship with a default recommended style.
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.
+1 to this idea. The internal consistency will help users understand what the UI does.
@@ -337,6 +358,7 @@ export type TreeViewItemProps = { | |||
onExpandedChange?: (expanded: boolean) => void | |||
onSelect?: (event: React.MouseEvent<HTMLElement> | React.KeyboardEvent<HTMLElement>) => void | |||
className?: string | |||
dragHandle?: ReactElement |
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'm not sure we need to require folks to pass dragHandle
if they're passing dragAndDrop
on the parent.
If we have a tree view where some nodes are draggable and others are not, we could assume all nodes are draggable by default. Then, we could add a prop to TreeView.Item
that allows people to disable drag-and-drop functionality.
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.
+1 to this one as well. I don't think we'll have a scenario where individual tree nodes would be prevented from being dragged and dropped?
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 don't think we'll have a scenario where individual tree nodes would be prevented from being dragged and dropped?
@ericwbailey we could. If the user didn't have access to see an item, they wouldn't be able to drag it.
@@ -488,6 +511,12 @@ const Item = React.forwardRef<HTMLElement, TreeViewItemProps>( | |||
<div style={{gridArea: 'spacer', display: 'flex'}}> | |||
<LevelIndicatorLines level={level} /> | |||
</div> | |||
{dragHandle ? ( | |||
// eslint-disable-next-line jsx-a11y/no-static-element-interactions | |||
<div className="PRIVATE_TreeView-item-drag-handle" onMouseDown={() => setIsExpanded(false)}> |
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.
How would a keyboard user drag and drop?
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 think we can resolve this one for now, with this overall approach being understood.
@@ -488,6 +511,12 @@ const Item = React.forwardRef<HTMLElement, TreeViewItemProps>( | |||
<div style={{gridArea: 'spacer', display: 'flex'}}> | |||
<LevelIndicatorLines level={level} /> | |||
</div> | |||
{dragHandle ? ( | |||
// eslint-disable-next-line jsx-a11y/no-static-element-interactions | |||
<div className="PRIVATE_TreeView-item-drag-handle" onMouseDown={() => setIsExpanded(false)}> |
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.
After I interact with the drag handle, I can't get focus to go back into the tree view. Maybe I'm just doing something wrong - so you and other reviewers should check to make sure you can reproduce this bug.
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.
Not just you, I can reproduce this as well. The focus shifts to body after interacting with the drag handle
focus.shifts.to.body.mov
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.
only show dragHandle when user hovers over TreeView.Item
One small thing I'd love to sneak in: can we also show the DragHandle via :focus-visible
? That way it will also be shown to keyboard-only user.
I have some initial feedback, but I really think we need somebody from a11y to take a look. For example, I'm not sure the "right"/accessible way to add interactive controls inside of a role="treeitem".
This interaction pattern is really unique to the web, so it's going to take some time to research this. The other thought here is that my understanding of the shape of this work is getting initial functionality in for demo purposes, and then follow up with accessibility tweaks so it does not block the work.
With that context, I think we can consider this merge-able once other things folks reviewing have pointed out have been addressed. I simply don't know what will be needed yet. We have a plan in place for discovering that, but it will take time.
So, I'd consider this an approval from accessibility at a high level with the idea that there may need to be larger structural updates to follow as we learn what they'll be.
@mperrotti @ericwbailey the initial idea was to just have some space before the expand/collapse toggle where users can place any element. I used If we were to ship with a default recommended style to always have the I think the better way to put this is: do you have recommendations on what we could do if we just wanted to place an element before the TreeView.Item toggle? The consumer would handle what that element does (i.e clicking on it, dragging it, etc). |
@@ -45,6 +45,7 @@ const ItemContext = React.createContext<{ | |||
setIsExpanded: (isExpanded: boolean) => void | |||
leadingVisualId: string | |||
trailingVisualId: string | |||
leadingActionId: string |
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.
@ayy-bc Don't think we need this anymore either, removed it :)
@mperrotti Hi 👋 Can I get a review for the snapshots? |
* add grid area for drag handle when data-drag-and-drop is true * add test to verify dnd attribute * css updates * add docs * test(vrt): update snapshots * Sid/treeview leading action (#4569) * wip: leading action slot * clean up a little * change from prop to subcomponent * remove outdated test * spacer should come before leadingAction * merge snapshots from main * merge package-lock from main * add visual tests * use IconButton for leadingAction * add example of drag handle on hover * Create tame-nails-live.md * test(vrt): update snapshots * change LeadingActio type to React.FC<TreeViewVisualProps> and accept children * change LeadingAction of type React.FC<TreeViewVisualProps> * typo * add `variant="invisible"` to icon button in stories * add leadingActionId and aria-hidden to LeadingAction subcomponent * remove `leadingActionId` to describe the tree view item * remove unused leadingActionId * remove docs from changeset --------- Co-authored-by: ayy-bc <ayy-bc@users.noreply.github.com> Co-authored-by: Siddharth Kshetrapal <siddharthkp@github.com> Co-authored-by: siddharthkp <siddharthkp@users.noreply.github.com>
Closes #4542
Changelog
This PR adds
TreeView.LeadingAction
sub component (like the leading visual, trailing visual) to create a slot for elements that go before the toggle and can be used for various use cases such as a drag handle.Rollout strategy
Testing & Reviewing
Merge checklist