diff --git a/change/@fluentui-react-tree-bbd9207d-d370-4635-b213-bbe2b2d53a24.json b/change/@fluentui-react-tree-bbd9207d-d370-4635-b213-bbe2b2d53a24.json new file mode 100644 index 00000000000000..e92497b67532bc --- /dev/null +++ b/change/@fluentui-react-tree-bbd9207d-d370-4635-b213-bbe2b2d53a24.json @@ -0,0 +1,8 @@ +{ + "type": "patch", + "comment": "Export useTreeItemPersonaLayoutContextValues_unstable and TreeItemPersonaLayoutContextValues from @fluentui/react-tree", + "packageName": "@fluentui/react-tree", + "email": "dmytrokirpa@microsoft.com", + "dependentChangeType": "patch", + "date": "2026-04-26T20:21:03.202Z" +} 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 31e4ba8fa97dc3..6c9f576074260d 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 @@ -267,6 +267,11 @@ export type TreeItemPersonaLayoutSlots = Pick; }; +// @public +export type TreeItemPersonaLayoutContextValues = { + avatar: AvatarContextValue; +}; + // @public export type TreeItemPersonaLayoutState = ComponentState & { avatarSize: AvatarSize; @@ -459,6 +464,9 @@ export const useTreeItemLayoutStyles_unstable: (state: TreeItemLayoutState) => T // @public export const useTreeItemPersonaLayout_unstable: (props: TreeItemPersonaLayoutProps, ref: React_2.Ref) => TreeItemPersonaLayoutState; +// @public +export function useTreeItemPersonaLayoutContextValues_unstable(state: TreeItemPersonaLayoutState): TreeItemPersonaLayoutContextValues; + // @public export const useTreeItemPersonaLayoutStyles_unstable: (state: TreeItemPersonaLayoutState) => TreeItemPersonaLayoutState; diff --git a/packages/react-components/react-tree/library/src/components/FlatTree/useFlatTree.test.ts b/packages/react-components/react-tree/library/src/components/FlatTree/useFlatTree.test.ts new file mode 100644 index 00000000000000..393696d054e1fa --- /dev/null +++ b/packages/react-components/react-tree/library/src/components/FlatTree/useFlatTree.test.ts @@ -0,0 +1,84 @@ +import * as React from 'react'; +import { renderHook } from '@testing-library/react-hooks'; +import { useFlatTree_unstable } from './useFlatTree'; +import { useFlatTreeContextValues_unstable } from './useFlatTreeContextValues'; + +describe('useFlatTree_unstable', () => { + let ref: React.RefObject; + + beforeEach(() => { + ref = React.createRef(); + }); + + it('returns default state with div root element', () => { + const { result } = renderHook(() => useFlatTree_unstable({}, ref)); + + expect(result.current).toMatchObject({ + components: { root: 'div' }, + contextType: 'root', + level: 1, + }); + }); + + it('reflects size prop in state', () => { + const { result } = renderHook(() => useFlatTree_unstable({ size: 'small' }, ref)); + + expect(result.current.size).toBe('small'); + }); + + it('reflects appearance prop in state', () => { + const { result } = renderHook(() => useFlatTree_unstable({ appearance: 'subtle' }, ref)); + + expect(result.current.appearance).toBe('subtle'); + }); + + it('reflects selectionMode prop in state', () => { + const { result } = renderHook(() => useFlatTree_unstable({ selectionMode: 'multiselect' }, ref)); + + expect(result.current.selectionMode).toBe('multiselect'); + }); + + it('defaults navigationMode to tree', () => { + const { result } = renderHook(() => useFlatTree_unstable({}, ref)); + + expect(result.current.navigationMode).toBe('tree'); + }); +}); + +describe('useFlatTreeContextValues_unstable', () => { + let ref: React.RefObject; + + beforeEach(() => { + ref = React.createRef(); + }); + + it('returns tree context with root contextType', () => { + const { result } = renderHook(() => { + const state = useFlatTree_unstable({}, ref); + return useFlatTreeContextValues_unstable(state); + }); + + expect(result.current.tree).toMatchObject({ + contextType: 'root', + level: 1, + }); + }); + + it('reflects selectionMode in tree context', () => { + const { result } = renderHook(() => { + const state = useFlatTree_unstable({ selectionMode: 'single' }, ref); + return useFlatTreeContextValues_unstable(state); + }); + + expect(result.current.tree.selectionMode).toBe('single'); + }); + + it('reflects size in tree context', () => { + const { result } = renderHook(() => { + const state = useFlatTree_unstable({ size: 'small' }, ref); + return useFlatTreeContextValues_unstable(state); + }); + + expect(result.current.tree.size).toBe('small'); + }); +}); diff --git a/packages/react-components/react-tree/library/src/components/Tree/useTree.test.ts b/packages/react-components/react-tree/library/src/components/Tree/useTree.test.ts new file mode 100644 index 00000000000000..87bccb3d6dc218 --- /dev/null +++ b/packages/react-components/react-tree/library/src/components/Tree/useTree.test.ts @@ -0,0 +1,90 @@ +import * as React from 'react'; +import { renderHook } from '@testing-library/react-hooks'; +import { useTree_unstable } from './useTree'; +import { useTreeContextValues_unstable } from './useTreeContextValues'; + +describe('useTree_unstable', () => { + let ref: React.RefObject; + + beforeEach(() => { + ref = React.createRef(); + }); + + it('returns default state with div root element', () => { + const { result } = renderHook(() => useTree_unstable({}, ref)); + + expect(result.current).toMatchObject({ + components: { root: 'div' }, + contextType: 'root', + level: 1, + }); + }); + + it('reflects size prop in state', () => { + const { result } = renderHook(() => useTree_unstable({ size: 'small' }, ref)); + + expect(result.current.size).toBe('small'); + }); + + it('reflects appearance prop in state', () => { + const { result } = renderHook(() => useTree_unstable({ appearance: 'subtle-alpha' }, ref)); + + expect(result.current.appearance).toBe('subtle-alpha'); + }); + + it('reflects selectionMode prop in state', () => { + const { result } = renderHook(() => useTree_unstable({ selectionMode: 'multiselect' }, ref)); + + expect(result.current.selectionMode).toBe('multiselect'); + }); + + it('defaults navigationMode to tree', () => { + const { result } = renderHook(() => useTree_unstable({}, ref)); + + expect(result.current.navigationMode).toBe('tree'); + }); + + it('reflects navigationMode prop in state', () => { + const { result } = renderHook(() => useTree_unstable({ navigationMode: 'treegrid' }, ref)); + + expect(result.current.navigationMode).toBe('treegrid'); + }); +}); + +describe('useTreeContextValues_unstable', () => { + let ref: React.RefObject; + + beforeEach(() => { + ref = React.createRef(); + }); + + it('returns tree context with root contextType', () => { + const { result } = renderHook(() => { + const state = useTree_unstable({}, ref); + return useTreeContextValues_unstable(state); + }); + + expect(result.current.tree).toMatchObject({ + contextType: 'root', + level: 1, + }); + }); + + it('reflects selectionMode in tree context', () => { + const { result } = renderHook(() => { + const state = useTree_unstable({ selectionMode: 'single' }, ref); + return useTreeContextValues_unstable(state); + }); + + expect(result.current.tree.selectionMode).toBe('single'); + }); + + it('reflects appearance in tree context', () => { + const { result } = renderHook(() => { + const state = useTree_unstable({ appearance: 'transparent' }, ref); + return useTreeContextValues_unstable(state); + }); + + expect(result.current.tree.appearance).toBe('transparent'); + }); +}); diff --git a/packages/react-components/react-tree/library/src/components/TreeItem/useTreeItem.test.tsx b/packages/react-components/react-tree/library/src/components/TreeItem/useTreeItem.test.tsx new file mode 100644 index 00000000000000..7c0035859f91a7 --- /dev/null +++ b/packages/react-components/react-tree/library/src/components/TreeItem/useTreeItem.test.tsx @@ -0,0 +1,69 @@ +import * as React from 'react'; +import { renderHook } from '@testing-library/react-hooks'; +import { useTreeItem_unstable } from './useTreeItem'; +import { useTreeItemContextValues_unstable } from './useTreeItemContextValues'; + +describe('useTreeItem_unstable', () => { + let ref: React.RefObject; + + beforeEach(() => { + ref = React.createRef(); + }); + + it('returns default state with div root element', () => { + const { result } = renderHook(() => useTreeItem_unstable({ value: 'item-1', itemType: 'leaf' }, ref)); + + expect(result.current).toMatchObject({ + components: { root: 'div' }, + itemType: 'leaf', + value: 'item-1', + }); + }); + + it('reflects branch itemType in state', () => { + const { result } = renderHook(() => useTreeItem_unstable({ value: 'item-1', itemType: 'branch' }, ref)); + + expect(result.current.itemType).toBe('branch'); + }); + + it('reflects value prop in state', () => { + const { result } = renderHook(() => useTreeItem_unstable({ value: 'custom-value', itemType: 'leaf' }, ref)); + + expect(result.current.value).toBe('custom-value'); + }); + + it('defaults open to false for leaf items', () => { + const { result } = renderHook(() => useTreeItem_unstable({ value: 'item-1', itemType: 'leaf' }, ref)); + + expect(result.current.open).toBe(false); + }); +}); + +describe('useTreeItemContextValues_unstable', () => { + let ref: React.RefObject; + + beforeEach(() => { + ref = React.createRef(); + }); + + it('returns treeItem context with value and itemType', () => { + const { result } = renderHook(() => { + const state = useTreeItem_unstable({ value: 'item-1', itemType: 'leaf' }, ref); + return useTreeItemContextValues_unstable(state); + }); + + expect(result.current.treeItem).toMatchObject({ + value: 'item-1', + itemType: 'leaf', + }); + }); + + it('reflects open state in treeItem context', () => { + const { result } = renderHook(() => { + const state = useTreeItem_unstable({ value: 'item-1', itemType: 'branch', open: true }, ref); + return useTreeItemContextValues_unstable(state); + }); + + expect(result.current.treeItem.open).toBe(true); + }); +}); diff --git a/packages/react-components/react-tree/library/src/index.ts b/packages/react-components/react-tree/library/src/index.ts index 114aa2c1627cdc..65e47d69fb2c1f 100644 --- a/packages/react-components/react-tree/library/src/index.ts +++ b/packages/react-components/react-tree/library/src/index.ts @@ -91,11 +91,13 @@ export { renderTreeItemPersonaLayout_unstable, useTreeItemPersonaLayoutStyles_unstable, useTreeItemPersonaLayout_unstable, + useTreeItemPersonaLayoutContextValues_unstable, } from './TreeItemPersonaLayout'; export type { TreeItemPersonaLayoutProps, TreeItemPersonaLayoutSlots, TreeItemPersonaLayoutState, + TreeItemPersonaLayoutContextValues, } from './TreeItemPersonaLayout'; export { flattenTree_unstable } from './utils/flattenTree';