From f3071a758869708cd60d6e03eee107242705135b Mon Sep 17 00:00:00 2001 From: Bernardo Sunderhus Date: Thu, 18 Jul 2024 09:17:01 +0200 Subject: [PATCH 1/2] chore(react-tree): adds test to ensure action visibility on click --- .../library/src/components/Tree/Tree.cy.tsx | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/packages/react-components/react-tree/library/src/components/Tree/Tree.cy.tsx b/packages/react-components/react-tree/library/src/components/Tree/Tree.cy.tsx index bedc02f65b466f..89ad779f9b9c1e 100644 --- a/packages/react-components/react-tree/library/src/components/Tree/Tree.cy.tsx +++ b/packages/react-components/react-tree/library/src/components/Tree/Tree.cy.tsx @@ -128,6 +128,27 @@ describe('Tree', () => { cy.get(`#action`).realClick(); cy.get('[data-testid="item1__item1"]').should('not.exist'); }); + it('should show actions when clicked', () => { + mount( + + + action!}>level 1, item 1 + + + action!}>level 2, item 1 + + + + , + ); + cy.get(`#branch-action`).should('not.exist'); + cy.get('[data-testid="item1"]').realClick(); + cy.get(`#branch-action`).should('exist'); + cy.get(`#leaf-action`).should('not.exist'); + cy.get('[data-testid="item1__item1"]').realClick(); + cy.get(`#branch-action`).should('not.exist'); + cy.get(`#leaf-action`).should('exist'); + }); }); describe('Keyboard interactions', () => { it('should expand/collapse item on Enter key', () => { From 69bc9739cbf9b277993a77c53abf728b6e3ff4f7 Mon Sep 17 00:00:00 2001 From: Bernardo Sunderhus Date: Thu, 18 Jul 2024 10:00:01 +0200 Subject: [PATCH 2/2] bugfix: TreeNavigation deprecates Click events in favor of Focus events --- ...-4b093b38-bed9-4cce-b5d4-cae84f81077d.json | 6 ++ ...-db00a5cf-8503-47b5-b592-4dee6eea25a9.json | 7 ++ .../react-tree/library/etc/react-tree.api.md | 10 +++ .../library/src/components/Tree/Tree.types.ts | 7 ++ .../src/components/TreeItem/useTreeItem.tsx | 90 +++++++++++-------- .../react-tree/library/src/utils/tokens.ts | 1 + 6 files changed, 82 insertions(+), 39 deletions(-) create mode 100644 change/@fluentui-react-tree-4b093b38-bed9-4cce-b5d4-cae84f81077d.json create mode 100644 change/@fluentui-react-tree-db00a5cf-8503-47b5-b592-4dee6eea25a9.json diff --git a/change/@fluentui-react-tree-4b093b38-bed9-4cce-b5d4-cae84f81077d.json b/change/@fluentui-react-tree-4b093b38-bed9-4cce-b5d4-cae84f81077d.json new file mode 100644 index 00000000000000..0dfe54dd6a5938 --- /dev/null +++ b/change/@fluentui-react-tree-4b093b38-bed9-4cce-b5d4-cae84f81077d.json @@ -0,0 +1,6 @@ +{ + "type": "minor", + "packageName": "@fluentui/react-tree", + "email": "bernardo.sunderhus@gmail.com", + "dependentChangeType": "patch" +} diff --git a/change/@fluentui-react-tree-db00a5cf-8503-47b5-b592-4dee6eea25a9.json b/change/@fluentui-react-tree-db00a5cf-8503-47b5-b592-4dee6eea25a9.json new file mode 100644 index 00000000000000..80d12e9581ed6e --- /dev/null +++ b/change/@fluentui-react-tree-db00a5cf-8503-47b5-b592-4dee6eea25a9.json @@ -0,0 +1,7 @@ +{ + "type": "minor", + "comment": "bugfix: TreeNavigation deprecates Click events in favor of Focus events", + "packageName": "@fluentui/react-tree", + "email": "bernardo.sunderhus@gmail.com", + "dependentChangeType": "patch" +} diff --git a/packages/react-components/react-tree/library/etc/react-tree.api.md b/packages/react-components/react-tree/library/etc/react-tree.api.md index 629bdc3fe7fc3d..706504f4a2e08e 100644 --- a/packages/react-components/react-tree/library/etc/react-tree.api.md +++ b/packages/react-components/react-tree/library/etc/react-tree.api.md @@ -301,6 +301,16 @@ export type TreeNavigationData_unstable = { value: TreeItemValue; parentValue: TreeItemValue | undefined; } & ({ + event: React_2.FocusEvent; + type: 'Focus'; +} +/** +* @deprecated +* +* Use `type: 'Focus'` instead of Click, +* a real click will trigger a focus event, which will trigger a navigation event, +*/ +| { event: React_2.MouseEvent; type: 'Click'; } | { diff --git a/packages/react-components/react-tree/library/src/components/Tree/Tree.types.ts b/packages/react-components/react-tree/library/src/components/Tree/Tree.types.ts index 5f039445d7350c..cef2c6dc0a7f58 100644 --- a/packages/react-components/react-tree/library/src/components/Tree/Tree.types.ts +++ b/packages/react-components/react-tree/library/src/components/Tree/Tree.types.ts @@ -20,6 +20,13 @@ export type TreeNavigationData_unstable = { value: TreeItemValue; parentValue: TreeItemValue | undefined; } & ( + | { event: React.FocusEvent; type: 'Focus' } + /** + * @deprecated + * + * Use `type: 'Focus'` instead of Click, + * a real click will trigger a focus event, which will trigger a navigation event, + */ | { event: React.MouseEvent; type: 'Click' } | { event: React.KeyboardEvent; type: 'TypeAhead' } | { event: React.KeyboardEvent; type: typeof ArrowRight } diff --git a/packages/react-components/react-tree/library/src/components/TreeItem/useTreeItem.tsx b/packages/react-components/react-tree/library/src/components/TreeItem/useTreeItem.tsx index 940245603a7229..bc207aa1413c48 100644 --- a/packages/react-components/react-tree/library/src/components/TreeItem/useTreeItem.tsx +++ b/packages/react-components/react-tree/library/src/components/TreeItem/useTreeItem.tsx @@ -1,5 +1,4 @@ import * as React from 'react'; -import * as ReactDOM from 'react-dom'; import { getIntrinsicElementProps, useId, @@ -50,6 +49,7 @@ export function useTreeItem_unstable(props: TreeItemProps, ref: React.Ref ctx.selectionMode); const checked = useTreeContext_unstable(ctx => ctx.checkedItems.get(value) ?? false); - const handleClick = useEventCallback((event: React.MouseEvent) => { - onClick?.(event); - if (event.isDefaultPrevented()) { - return; - } - if (itemType === 'leaf') { - return; - } - const isEventFromActions = actionsRef.current && elementContains(actionsRef.current, event.target as Node); - if (isEventFromActions) { - return; - } - const isEventFromSubtree = subtreeRef.current && elementContains(subtreeRef.current, event.target as Node); - if (isEventFromSubtree) { + const handleFocus = useEventCallback((event: React.FocusEvent) => { + onFocus?.(event); + if ( + event.isDefaultPrevented() || + // isEventFromActions + elementContains(actionsRef.current, event.target) || + // isEventFromSubtree + elementContains(subtreeRef.current, event.target) || + // isEventFromSelection + elementContains(selectionRef.current, event.target) + ) { return; } - const isEventFromSelection = selectionRef.current && elementContains(selectionRef.current, event.target as Node); - if (isEventFromSelection) { + + requestTreeResponse({ + event, + value, + itemType, + parentValue, + requestType: 'navigate', + type: treeDataTypes.Focus, + target: event.currentTarget, + }); + }); + + const handleClick = useEventCallback((event: React.MouseEvent) => { + onClick?.(event); + if ( + event.isDefaultPrevented() || + itemType === 'leaf' || + // isEventFromActions + elementContains(actionsRef.current, event.target as Node) || + // isEventFromSubtree + elementContains(subtreeRef.current, event.target as Node) || + // isEventFromSelection + elementContains(selectionRef.current, event.target as Node) + ) { return; } - const isEventFromExpandIcon = expandIconRef.current && elementContains(expandIconRef.current, event.target as Node); - ReactDOM.unstable_batchedUpdates(() => { - const data = { - event, - value, - open: getNextOpen(), - target: event.currentTarget, - type: isEventFromExpandIcon ? treeDataTypes.ExpandIconClick : treeDataTypes.Click, - } as const; - props.onOpenChange?.(event, data); - requestTreeResponse({ - ...data, - itemType, - requestType: 'open', - }); - requestTreeResponse({ - ...data, - itemType, - parentValue, - requestType: 'navigate', - type: treeDataTypes.Click, - }); + const isEventFromExpandIcon = elementContains(expandIconRef.current, event.target as Node); + + const data = { + event, + value, + open: getNextOpen(), + target: event.currentTarget, + type: isEventFromExpandIcon ? treeDataTypes.ExpandIconClick : treeDataTypes.Click, + } as const; + props.onOpenChange?.(event, data); + requestTreeResponse({ + ...data, + itemType, + requestType: 'open', }); }); @@ -291,6 +302,7 @@ export function useTreeItem_unstable(props: TreeItemProps, ref: React.Ref