Skip to content

Commit 70dd777

Browse files
authored
bugfix(react-tree): actions lose visibility when mouse and keyboard interactions are mixed (#33731)
1 parent 61d6bbe commit 70dd777

File tree

3 files changed

+49
-2
lines changed

3 files changed

+49
-2
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"type": "patch",
3+
"comment": "bugfix: actions lose visibility when mouse and keyboard interactions are mixed",
4+
"packageName": "@fluentui/react-tree",
5+
"email": "bernardo.sunderhus@gmail.com",
6+
"dependentChangeType": "patch"
7+
}

packages/react-components/react-tree/library/src/components/Tree/Tree.cy.tsx

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,31 @@ describe('Tree', () => {
325325
});
326326
});
327327
});
328+
describe('Keyboard + Mouse interactions', () => {
329+
it('actions should remain visible whenever a focused treeitem is hovered in/out', () => {
330+
mount(
331+
<TreeTest defaultOpenItems={['item1']} id="tree" aria-label="Tree">
332+
<TreeItem itemType="branch" value="item1" data-testid="item1">
333+
<TreeItemLayout actions={<Button id="action">action</Button>}>level 1, item 1</TreeItemLayout>
334+
<Tree>
335+
<TreeItem itemType="leaf" value="item1__item1" data-testid="item1__item1">
336+
<TreeItemLayout>level 2, item 1</TreeItemLayout>
337+
</TreeItem>
338+
</Tree>
339+
</TreeItem>
340+
<TreeItem itemType="leaf" value="item2" data-testid="item2">
341+
<TreeItemLayout>level 2, item 1</TreeItemLayout>
342+
</TreeItem>
343+
</TreeTest>,
344+
);
345+
cy.focused().should('not.exist');
346+
cy.document().realPress('Tab');
347+
cy.get('[data-testid="item1"]').should('be.focused');
348+
cy.get('#action').should('be.visible').realHover();
349+
cy.get('[data-testid=item2]').realHover();
350+
cy.get('#action').should('be.visible');
351+
});
352+
});
328353

329354
describe('Control open state per item', () => {
330355
it('should remain open when opening/closing a controlled item', () => {

packages/react-components/react-tree/library/src/components/TreeItemLayout/useTreeItemLayout.tsx

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ import type {
1818
import { Checkbox, CheckboxProps } from '@fluentui/react-checkbox';
1919
import { Radio, RadioProps } from '@fluentui/react-radio';
2020
import { TreeItemChevron } from '../TreeItemChevron';
21-
import { useArrowNavigationGroup } from '@fluentui/react-tabster';
21+
import { useArrowNavigationGroup, useIsNavigatingWithKeyboard } from '@fluentui/react-tabster';
22+
import { useFluent_unstable as useFluent } from '@fluentui/react-shared-contexts';
2223

2324
/**
2425
* Create the state required to render TreeItemLayout.
@@ -88,6 +89,9 @@ export const useTreeItemLayout_unstable = (
8889
[subtreeRef, setIsActionsVisible, onActionVisibilityChange],
8990
);
9091

92+
const { targetDocument } = useFluent();
93+
const isNavigatingWithKeyboard = useIsNavigatingWithKeyboard();
94+
9195
const setActionsInvisibleIfNotFromSubtree = React.useCallback(
9296
(event: FocusEvent | MouseEvent) => {
9397
const isRelatedTargetFromActions = () =>
@@ -110,6 +114,17 @@ export const useTreeItemLayout_unstable = (
110114
if (isTargetFromActions() && isRelatedTargetFromTreeItem()) {
111115
return;
112116
}
117+
// when a mouseout event happens during keyboard interaction
118+
// we should not hide the actions if the activeElement is the treeitem or an action
119+
// as the focus on the treeitem takes precedence over the mouseout event
120+
if (
121+
event.type === 'mouseout' &&
122+
isNavigatingWithKeyboard() &&
123+
(targetDocument?.activeElement === treeItemRef.current ||
124+
elementContains(actionsRefInternal.current, targetDocument?.activeElement as Node))
125+
) {
126+
return;
127+
}
113128
onActionVisibilityChange?.(event, {
114129
visible: false,
115130
event,
@@ -120,7 +135,7 @@ export const useTreeItemLayout_unstable = (
120135
}
121136
setIsActionsVisible(false);
122137
},
123-
[setIsActionsVisible, onActionVisibilityChange, treeItemRef],
138+
[setIsActionsVisible, onActionVisibilityChange, treeItemRef, isNavigatingWithKeyboard, targetDocument],
124139
);
125140

126141
const expandIcon = slot.optional(props.expandIcon, {

0 commit comments

Comments
 (0)