diff --git a/change/@fluentui-react-tree-84a7e55a-0a40-4b0e-8bb8-a3feb88fa234.json b/change/@fluentui-react-tree-84a7e55a-0a40-4b0e-8bb8-a3feb88fa234.json new file mode 100644 index 00000000000000..f42640c8d48098 --- /dev/null +++ b/change/@fluentui-react-tree-84a7e55a-0a40-4b0e-8bb8-a3feb88fa234.json @@ -0,0 +1,7 @@ +{ + "type": "prerelease", + "comment": "feat: makes useFlatTree generic", + "packageName": "@fluentui/react-tree", + "email": "bernardo.sunderhus@gmail.com", + "dependentChangeType": "patch" +} diff --git a/packages/react-components/react-tree/etc/react-tree.api.md b/packages/react-components/react-tree/etc/react-tree.api.md index 1635f372aff3f6..6a473c6814b21c 100644 --- a/packages/react-components/react-tree/etc/react-tree.api.md +++ b/packages/react-components/react-tree/etc/react-tree.api.md @@ -30,33 +30,41 @@ import type { SlotClassNames } from '@fluentui/react-utilities'; import { SlotRenderFunction } from '@fluentui/react-utilities'; // @public -export const flattenTree_unstable: (items: NestedTreeItem[]) => FlatTreeItemProps[]; +export const flattenTree_unstable: >(items: NestedTreeItem[]) => FlattenedTreeItem[]; // @public -export type FlatTree = { - getTreeProps(): FlatTreeProps; - navigate(data: TreeNavigationData_unstable): void; - getNextNavigableItem(visibleItems: FlatTreeItem[], data: TreeNavigationData_unstable): FlatTreeItem | undefined; - items(): IterableIterator>; +export type FlatTree = FlatTreeItemProps> = { + getTreeProps(): FlatTreeProps; + navigate(data: TreeNavigationData_unstable): void; + getNextNavigableItem(visibleItems: FlatTreeItem[], data: TreeNavigationData_unstable): FlatTreeItem | undefined; + items(): IterableIterator>; }; -// @public (undocumented) -export type FlatTreeItem = Readonly>; +// @public +export type FlatTreeItem = FlatTreeItemProps> = { + index: number; + level: number; + childrenSize: number; + value: Props['value']; + parentValue: Props['parentValue']; + ref: React_2.RefObject; + getTreeItemProps(): Required> & Omit; +}; // @public (undocumented) -export type FlatTreeItemProps = Omit & { +export type FlatTreeItemProps = TreeItemProps & { value: Value; parentValue?: Value; }; // @public (undocumented) -export type FlatTreeProps = Required, 'openItems' | 'onOpenChange' | 'onNavigation_unstable'> & { +export type FlatTreeProps = Required, 'openItems' | 'onOpenChange' | 'onNavigation_unstable'>> & { ref: React_2.Ref; -}>; +}; // @public (undocumented) -export type NestedTreeItem = Omit, 'subtree'> & { - subtree?: NestedTreeItem[]; +export type NestedTreeItem> = Omit & { + subtree?: NestedTreeItem[]; }; // @public (undocumented) @@ -281,7 +289,7 @@ export type TreeSlots = { export type TreeState = ComponentState & TreeContextValue; // @public -export function useFlatTree_unstable(flatTreeItemProps: FlatTreeItemProps[], options?: Pick, 'openItems' | 'defaultOpenItems' | 'onOpenChange' | 'onNavigation_unstable'>): FlatTree; +export function useFlatTree_unstable = FlatTreeItemProps>(flatTreeItemProps: Props[], options?: FlatTreeOptions): FlatTree; // @public export const useTree_unstable: (props: TreeProps, ref: React_2.Ref) => TreeState; diff --git a/packages/react-components/react-tree/src/hooks/useFlatTree.ts b/packages/react-components/react-tree/src/hooks/useFlatTree.ts index b3def6b57e043b..f373c683ed87a6 100644 --- a/packages/react-components/react-tree/src/hooks/useFlatTree.ts +++ b/packages/react-components/react-tree/src/hooks/useFlatTree.ts @@ -13,34 +13,34 @@ import type { } from '../Tree'; import type { TreeItemProps } from '../TreeItem'; -export type FlatTreeItemProps = Omit & { +export type FlatTreeItemProps = TreeItemProps & { value: Value; parentValue?: Value; }; -export type FlatTreeItem = Readonly>; - /** - * @internal - * Used internally on createFlatTreeItems and VisibleFlatTreeItemGenerator - * to ensure required properties when building a FlatTreeITem + * The item that is returned by `useFlatTree`, it represents a wrapper around the properties provided to + * `useFlatTree` but with extra information that might be useful on flat tree scenarios */ -export type MutableFlatTreeItem = { - parentValue?: Value; - childrenSize: number; +export type FlatTreeItem = FlatTreeItemProps> = { index: number; - value: Value; level: number; + childrenSize: number; + value: Props['value']; + parentValue: Props['parentValue']; + /** + * A reference to the element that will render the `TreeItem`, + * this is necessary for nodes with parents (to ensure child to parent navigation), + * if a node has no parent then this reference will be null. + */ ref: React.RefObject; - getTreeItemProps(): Required< - Pick, 'value' | 'aria-setsize' | 'aria-level' | 'aria-posinset' | 'leaf'> - > & - TreeItemProps; + getTreeItemProps(): Required> & + Omit; }; export type FlatTreeProps = Required< - Pick, 'openItems' | 'onOpenChange' | 'onNavigation_unstable'> & { ref: React.Ref } ->; + Pick, 'openItems' | 'onOpenChange' | 'onNavigation_unstable'> +> & { ref: React.Ref }; /** * FlatTree API to manage all required mechanisms to convert a list of items into renderable TreeItems @@ -52,13 +52,13 @@ export type FlatTreeProps = Required< * * On simple scenarios it is advised to simply use a nested structure instead. */ -export type FlatTree = { +export type FlatTree = FlatTreeItemProps> = { /** * returns the properties required for the Tree component to work properly. * That includes: * `openItems`, `onOpenChange`, `onNavigation_unstable` and `ref` */ - getTreeProps(): FlatTreeProps; + getTreeProps(): FlatTreeProps; /** * internal method used to react to an `onNavigation` event. * This method ensures proper navigation on keyboard and mouse interaction. @@ -82,7 +82,7 @@ export type FlatTree = { * }; *``` */ - navigate(data: TreeNavigationData_unstable): void; + navigate(data: TreeNavigationData_unstable): void; /** * returns next item to be focused on a navigation. * This method is provided to decouple the element that needs to be focused from @@ -91,15 +91,20 @@ export type FlatTree = { * On the case of TypeAhead navigation this method returns the current item. */ getNextNavigableItem( - visibleItems: FlatTreeItem[], - data: TreeNavigationData_unstable, - ): FlatTreeItem | undefined; + visibleItems: FlatTreeItem[], + data: TreeNavigationData_unstable, + ): FlatTreeItem | undefined; /** * an iterable containing all visually available flat tree items */ - items(): IterableIterator>; + items(): IterableIterator>; }; +type FlatTreeOptions = FlatTreeItemProps> = Pick< + TreeProps, + 'openItems' | 'defaultOpenItems' | 'onOpenChange' | 'onNavigation_unstable' +>; + /** * this hook provides FlatTree API to manage all required mechanisms to convert a list of items into renderable TreeItems * in multiple scenarios including virtualization. @@ -112,15 +117,15 @@ export type FlatTree = { * @param flatTreeItemProps - a list of tree items * @param options - in case control over the internal openItems is required */ -export function useFlatTree_unstable( - flatTreeItemProps: FlatTreeItemProps[], - options: Pick, 'openItems' | 'defaultOpenItems' | 'onOpenChange' | 'onNavigation_unstable'> = {}, -): FlatTree { - const [openItems, updateOpenItems] = useOpenItemsState(options); +export function useFlatTree_unstable = FlatTreeItemProps>( + flatTreeItemProps: Props[], + options: FlatTreeOptions = {}, +): FlatTree { + const [openItems, updateOpenItems] = useOpenItemsState(options); const flatTreeItems = React.useMemo(() => createFlatTreeItems(flatTreeItemProps), [flatTreeItemProps]); const [navigate, navigationRef] = useFlatTreeNavigation(flatTreeItems); - const handleOpenChange = useEventCallback((event: TreeOpenChangeEvent, data: TreeOpenChangeData) => { + const handleOpenChange = useEventCallback((event: TreeOpenChangeEvent, data: TreeOpenChangeData) => { options.onOpenChange?.(event, data); if (!event.isDefaultPrevented()) { updateOpenItems(data); @@ -129,7 +134,7 @@ export function useFlatTree_unstable( }); const handleNavigation = useEventCallback( - (event: TreeNavigationEvent_unstable, data: TreeNavigationData_unstable) => { + (event: TreeNavigationEvent_unstable, data: TreeNavigationData_unstable) => { options.onNavigation_unstable?.(event, data); if (!event.isDefaultPrevented()) { navigate(data); @@ -139,7 +144,7 @@ export function useFlatTree_unstable( ); const getNextNavigableItem = useEventCallback( - (visibleItems: FlatTreeItem[], data: TreeNavigationData_unstable) => { + (visibleItems: FlatTreeItem[], data: TreeNavigationData_unstable) => { const item = flatTreeItems.get(data.value); if (item) { switch (data.type) { @@ -175,7 +180,7 @@ export function useFlatTree_unstable( ); const items = React.useCallback( - () => VisibleFlatTreeItemGenerator(openItems, flatTreeItems), + () => VisibleFlatTreeItemGenerator(openItems, flatTreeItems), [openItems, flatTreeItems], ); diff --git a/packages/react-components/react-tree/src/hooks/useFlatTreeNavigation.ts b/packages/react-components/react-tree/src/hooks/useFlatTreeNavigation.ts index b1b9e6d55cc7c0..afb4d6a35fe348 100644 --- a/packages/react-components/react-tree/src/hooks/useFlatTreeNavigation.ts +++ b/packages/react-components/react-tree/src/hooks/useFlatTreeNavigation.ts @@ -7,13 +7,16 @@ import { treeDataTypes } from '../utils/tokens'; import { treeItemFilter } from '../utils/treeItemFilter'; import { HTMLElementWalker, useHTMLElementWalkerRef } from './useHTMLElementWalker'; import { useRovingTabIndex } from './useRovingTabIndexes'; +import { FlatTreeItemProps } from './useFlatTree'; -export function useFlatTreeNavigation(flatTreeItems: FlatTreeItems) { +export function useFlatTreeNavigation = FlatTreeItemProps>( + flatTreeItems: FlatTreeItems, +) { const { targetDocument } = useFluent_unstable(); const [treeItemWalkerRef, treeItemWalkerRootRef] = useHTMLElementWalkerRef(treeItemFilter); const [{ rove }, rovingRootRef] = useRovingTabIndex(treeItemFilter); - function getNextElement(data: TreeNavigationData_unstable) { + function getNextElement(data: TreeNavigationData_unstable) { if (!targetDocument || !treeItemWalkerRef.current) { return null; } @@ -43,7 +46,7 @@ export function useFlatTreeNavigation(flatTreeItems: FlatTreeIte return treeItemWalker.previousElement(); } } - const navigate = useEventCallback((data: TreeNavigationData_unstable) => { + const navigate = useEventCallback((data: TreeNavigationData_unstable) => { const nextElement = getNextElement(data); if (nextElement) { rove(nextElement); @@ -66,7 +69,7 @@ function firstChild(target: HTMLElement, treeWalker: HTMLElementWalker): HTMLEle return null; } -function parentElement(flatTreeItems: FlatTreeItems, value: Value) { +function parentElement(flatTreeItems: FlatTreeItems>, value: unknown) { const flatTreeItem = flatTreeItems.get(value); if (flatTreeItem?.parentValue) { const parentItem = flatTreeItems.get(flatTreeItem.parentValue); diff --git a/packages/react-components/react-tree/src/utils/createFlatTreeItems.ts b/packages/react-components/react-tree/src/utils/createFlatTreeItems.ts index 1cb3d00eb1fe40..6643c14990f5eb 100644 --- a/packages/react-components/react-tree/src/utils/createFlatTreeItems.ts +++ b/packages/react-components/react-tree/src/utils/createFlatTreeItems.ts @@ -1,33 +1,33 @@ import type { ImmutableSet } from './ImmutableSet'; -import type { FlatTreeItem, FlatTreeItemProps, MutableFlatTreeItem } from '../hooks/useFlatTree'; +import type { FlatTreeItem, FlatTreeItemProps } from '../hooks/useFlatTree'; import * as React from 'react'; /** * @internal */ -export type FlatTreeItems = { +export type FlatTreeItems> = { size: number; - root: FlatTreeItem; - get(key: Value): FlatTreeItem | undefined; - set(key: Value, value: FlatTreeItem): void; - getByIndex(index: number): FlatTreeItem; + root: FlatTreeItem; + get(key: Props['value']): FlatTreeItem | undefined; + set(key: Props['value'], value: FlatTreeItem): void; + getByIndex(index: number): FlatTreeItem; }; /** * creates a list of flat tree items * and provides a map to access each item by id */ -export function createFlatTreeItems( - flatTreeItemProps: FlatTreeItemProps[], -): FlatTreeItems { - const root = createFlatTreeRootItem(); - const itemsPerValue = new Map>([[flatTreeRootId as Value, root]]); - const items: MutableFlatTreeItem[] = []; +export function createFlatTreeItems>( + flatTreeItemProps: Props[], +): FlatTreeItems { + const root = createFlatTreeRootItem(); + const itemsPerValue = new Map>>([[root.value, root]]); + const items: FlatTreeItem>[] = []; for (let index = 0; index < flatTreeItemProps.length; index++) { - const { parentValue = flatTreeRootId as Value, ...treeItemProps } = flatTreeItemProps[index]; + const { parentValue = flatTreeRootId, ...treeItemProps } = flatTreeItemProps[index]; - const nextItemProps: FlatTreeItemProps | undefined = flatTreeItemProps[index + 1]; + const nextItemProps: Props | undefined = flatTreeItemProps[index + 1]; const currentParent = itemsPerValue.get(parentValue); if (!currentParent) { if (process.env.NODE_ENV === 'development') { @@ -38,12 +38,13 @@ export function createFlatTreeItems( } break; } - const isLeaf = nextItemProps?.parentValue !== treeItemProps.value; + const isLeaf = + treeItemProps.leaf ?? (treeItemProps.value === undefined || nextItemProps?.parentValue !== treeItemProps.value); const currentLevel = (currentParent.level ?? 0) + 1; const currentChildrenSize = ++currentParent.childrenSize; const ref = React.createRef(); - const flatTreeItem: MutableFlatTreeItem = { + const flatTreeItem: FlatTreeItem> = { value: treeItemProps.value, getTreeItemProps: () => ({ ...treeItemProps, @@ -64,27 +65,30 @@ export function createFlatTreeItems( items.push(flatTreeItem); } - return { + const flatTreeItems: FlatTreeItems> = { root, size: items.length, getByIndex: index => items[index], - get: id => itemsPerValue.get(id), - set: (id, value) => itemsPerValue.set(id, value), + get: key => itemsPerValue.get(key), + set: (key, value) => itemsPerValue.set(key, value), }; + + return flatTreeItems as FlatTreeItems; } -export const flatTreeRootId = '__fuiFlatTreeRoot' as unknown; +export const flatTreeRootId = '__fuiFlatTreeRoot'; -function createFlatTreeRootItem(): FlatTreeItem { +function createFlatTreeRootItem(): FlatTreeItem { return { ref: { current: null }, - value: flatTreeRootId as Value, + value: flatTreeRootId, + parentValue: undefined, getTreeItemProps: () => { if (process.env.NODE_ENV !== 'production') { // eslint-disable-next-line no-console console.error('useFlatTree: internal error, trying to access treeitem props from invalid root element'); } - return { value: flatTreeRootId as Value, 'aria-setsize': -1, 'aria-level': -1, 'aria-posinset': -1, leaf: true }; + return { value: flatTreeRootId, 'aria-setsize': -1, 'aria-level': -1, 'aria-posinset': -1, leaf: true }; }, childrenSize: 0, get index() { @@ -99,12 +103,12 @@ function createFlatTreeRootItem(): FlatTreeItem { } // eslint-disable-next-line @typescript-eslint/naming-convention -export function* VisibleFlatTreeItemGenerator( - openItems: ImmutableSet, - flatTreeItems: FlatTreeItems, +export function* VisibleFlatTreeItemGenerator>( + openItems: ImmutableSet, + flatTreeItems: FlatTreeItems, ) { for (let index = 0, visibleIndex = 0; index < flatTreeItems.size; index++) { - const item: MutableFlatTreeItem = flatTreeItems.getByIndex(index); + const item = flatTreeItems.getByIndex(index) as FlatTreeItem; const parent = item.parentValue ? flatTreeItems.get(item.parentValue) ?? flatTreeItems.root : flatTreeItems.root; if (isItemVisible(item, openItems, flatTreeItems)) { item.index = visibleIndex++; @@ -115,10 +119,10 @@ export function* VisibleFlatTreeItemGenerator( } } -function isItemVisible( - item: FlatTreeItem, - openItems: ImmutableSet, - flatTreeItems: FlatTreeItems, +function isItemVisible( + item: FlatTreeItem>, + openItems: ImmutableSet, + flatTreeItems: FlatTreeItems>, ) { if (item.level === 1) { return true; diff --git a/packages/react-components/react-tree/src/utils/flattenTree.ts b/packages/react-components/react-tree/src/utils/flattenTree.ts index d41b65ea8ce39c..035d147f449d8e 100644 --- a/packages/react-components/react-tree/src/utils/flattenTree.ts +++ b/packages/react-components/react-tree/src/utils/flattenTree.ts @@ -2,30 +2,33 @@ import * as React from 'react'; import { FlatTreeItemProps } from '../hooks/useFlatTree'; import { TreeItemProps } from '../TreeItem'; -export type NestedTreeItem = Omit, 'subtree'> & { - subtree?: NestedTreeItem[]; +export type NestedTreeItem> = Omit & { + subtree?: NestedTreeItem[]; }; +export type FlattenedTreeItem> = FlatTreeItemProps> & + Props; + let count = 1; -function flattenTreeRecursive( - items: NestedTreeItem[], - parent?: FlatTreeItemProps, +function flattenTreeRecursive>( + items: NestedTreeItem[], + parent?: FlatTreeItemProps & Props, level = 1, -): FlatTreeItemProps[] { - return items.reduce[]>((acc, { subtree, ...item }, index) => { +): FlattenedTreeItem[] { + return items.reduce[]>((acc, { subtree, ...item }, index) => { const id = item.id ?? `fui-FlatTreeItem-${count++}`; - const flatTreeItem: FlatTreeItemProps = { + const flatTreeItem = { 'aria-level': level, 'aria-posinset': index + 1, 'aria-setsize': items.length, parentValue: parent?.value, - value: item.value ?? (id as unknown as Value), + value: item.value ?? (id as unknown as Props['value']), leaf: subtree === undefined, ...item, - }; + } as FlattenedTreeItem; acc.push(flatTreeItem); if (subtree !== undefined) { - acc.push(...flattenTreeRecursive(subtree, flatTreeItem, level + 1)); + acc.push(...flattenTreeRecursive(subtree, flatTreeItem, level + 1)); } return acc; }, []); @@ -72,8 +75,9 @@ function flattenTreeRecursive( * ``` */ // eslint-disable-next-line @typescript-eslint/naming-convention -export const flattenTree_unstable = (items: NestedTreeItem[]): FlatTreeItemProps[] => - flattenTreeRecursive(items); +export const flattenTree_unstable = >( + items: NestedTreeItem[], +): FlattenedTreeItem[] => flattenTreeRecursive(items); /** * @internal diff --git a/packages/react-components/react-tree/stories/D_flatTree/TreeItemAddRemove.stories.tsx b/packages/react-components/react-tree/stories/D_flatTree/TreeItemAddRemove.stories.tsx index ede8cbcd248d49..e99aa65ed5b5ea 100644 --- a/packages/react-components/react-tree/stories/D_flatTree/TreeItemAddRemove.stories.tsx +++ b/packages/react-components/react-tree/stories/D_flatTree/TreeItemAddRemove.stories.tsx @@ -12,15 +12,17 @@ import { Delete20Regular } from '@fluentui/react-icons'; import { Button } from '@fluentui/react-components'; import story from './TreeItemAddRemove.md'; -const defaultSubTrees: FlatTreeItemProps[][] = [ +type ItemProps = FlatTreeItemProps & { content: string }; + +const defaultSubTrees: ItemProps[][] = [ [ - { value: '1', children: Level 1, item 1 }, - { value: '1-1', parentValue: '1', children: Item 1-1 }, - { value: '1-2', parentValue: '1', children: Item 1-2 }, + { value: '1', content: 'Level 1, item 1' }, + { value: '1-1', parentValue: '1', content: 'Item 1-1' }, + { value: '1-2', parentValue: '1', content: 'Item 1-2' }, ], [ - { value: '2', children: Level 1, item 2 }, - { value: '2-1', parentValue: '2', children: Item 2-1 }, + { value: '2', content: 'Level 1, item 2' }, + { value: '2-1', parentValue: '2', content: 'Item 2-1' }, ], ]; @@ -38,12 +40,12 @@ export const AddRemoveTreeItem = () => { setTrees(currentTrees => { const lastItem = currentTrees[subtreeIndex][currentTrees[subtreeIndex].length - 1]; const newItemValue = `${subtreeIndex + 1}-${Number(lastItem.value.slice(2)) + 1}`; - const nextSubTree: FlatTreeItemProps[] = [ + const nextSubTree: ItemProps[] = [ ...currentTrees[subtreeIndex], { value: newItemValue, parentValue: currentTrees[subtreeIndex][0].value, - children: New item {newItemValue}, + content: `New item ${newItemValue}`, }, ]; return [...currentTrees.slice(0, subtreeIndex), nextSubTree, ...currentTrees.slice(subtreeIndex + 1)]; @@ -63,13 +65,13 @@ export const AddRemoveTreeItem = () => { { value: '1-btn', parentValue: '1', - children: Add new item, + content: 'Add new item', }, ...trees[1], { value: '2-btn', parentValue: '2', - children: Add new item, + content: 'Add new item', }, ], [trees], @@ -81,10 +83,11 @@ export const AddRemoveTreeItem = () => { {Array.from(flatTree.items(), item => { const isUndeletable = item.level === 1 || item.value.endsWith('-btn'); + const { content, ...treeItemProps } = item.getTreeItemProps(); return ( { /> ) } - /> + > + {content} + ); })} diff --git a/packages/react-components/react-tree/stories/D_flatTree/Virtualization.stories.tsx b/packages/react-components/react-tree/stories/D_flatTree/Virtualization.stories.tsx index cefd20fde42133..27e4a3441c7828 100644 --- a/packages/react-components/react-tree/stories/D_flatTree/Virtualization.stories.tsx +++ b/packages/react-components/react-tree/stories/D_flatTree/Virtualization.stories.tsx @@ -18,28 +18,30 @@ import { FixedSizeList, FixedSizeListProps, ListChildComponentProps } from 'reac import { ForwardRefComponent, getSlots } from '@fluentui/react-components'; import story from './Virtualization.md'; -const defaultItems: FlatTreeItemProps[] = [ +type ItemProps = FlatTreeItemProps & { content: string }; + +const defaultItems: ItemProps[] = [ { id: 'flatTreeItem_lvl-1_item-1', value: 'flatTreeItem_lvl-1_item-1', - children: Level 1, item 1, + content: `Level 1, item 1`, }, ...Array.from({ length: 300 }, (_, i) => ({ id: `flatTreeItem_lvl-1_item-1--child:${i}`, value: `flatTreeItem_lvl-1_item-1--child:${i}`, parentValue: 'flatTreeItem_lvl-1_item-1', - children: Item {i + 1}, + content: `Item ${i + 1}`, })), { id: 'flatTreeItem_lvl-1_item-2', value: 'flatTreeItem_lvl-1_item-2', - children: Level 1, item 2, + content: `Level 1, item 2`, }, ...Array.from({ length: 300 }, (_, index) => ({ id: `flatTreeItem_lvl-1_item-2--child:${index}`, value: `flatTreeItem_lvl-1_item-2--child:${index}`, parentValue: 'flatTreeItem_lvl-1_item-2', - children: Item {index + 1}, + content: `Item ${index + 1}`, })), ]; @@ -67,9 +69,10 @@ interface FixedSizeTreeItemProps extends ListChildComponentProps { const FixedSizeTreeItem = (props: FixedSizeTreeItemProps) => { const flatTreeItem = props.data[props.index]; + const { content, ...treeItemProps } = flatTreeItem.getTreeItemProps(); return ( - - Item {props.index} + + {content} ); }; @@ -91,6 +94,7 @@ export const Virtualization = () => { } flatTree.navigate(data); }; + return ( ([ +type Item = TreeItemProps & { layout: string }; + +const defaultItems = flattenTree_unstable([ { - children: level 1, item 1, + layout: 'level 1, item 1', subtree: [ + { layout: 'level 2, item 1' }, { - children: level 2, item 1, - }, - { - children: level 2, item 2, + layout: 'level 2, item 2', }, { - children: level 2, item 3, + layout: 'level 2, item 3', }, ], }, { - children: level 1, item 2, + layout: 'level 1, item 2', subtree: [ { - children: level 2, item 1, + layout: 'level 2, item 1', subtree: [ { - children: level 3, item 1, + layout: 'level 3, item 1', subtree: [ { - children: level 4, item 1, + layout: 'level 4, item 1', }, ], }, @@ -38,12 +45,17 @@ const defaultItems = flattenTree_unstable([ ]); export const FlattenTree = () => { - const flatTree = useFlatTree_unstable(defaultItems); + const flatTree = useFlatTree_unstable(defaultItems); return ( - {Array.from(flatTree.items(), item => ( - - ))} + {Array.from(flatTree.items(), item => { + const { layout, ...itemProps } = item.getTreeItemProps(); + return ( + + {layout} + + ); + })} ); }; diff --git a/packages/react-components/react-tree/stories/D_flatTree/useFlatTree.stories.tsx b/packages/react-components/react-tree/stories/D_flatTree/useFlatTree.stories.tsx index 89edcaa09579bc..cb9f9e5d60d5fb 100644 --- a/packages/react-components/react-tree/stories/D_flatTree/useFlatTree.stories.tsx +++ b/packages/react-components/react-tree/stories/D_flatTree/useFlatTree.stories.tsx @@ -2,64 +2,21 @@ import * as React from 'react'; import { Tree, TreeItem, TreeItemLayout, useFlatTree_unstable, FlatTreeItemProps } from '@fluentui/react-tree'; import story from './useFlatTree.md'; -const defaultItems: FlatTreeItemProps[] = [ - { - value: '1', - children: Level 1, item 1, - }, - { - value: '1-1', - parentValue: '1', - children: Level 2, item 1, - }, - { - value: '1-2', - parentValue: '1', - children: Level 2, item 2, - }, - { - value: '1-3', - parentValue: '1', - children: Level 2, item 3, - }, - { - value: '2', - children: Level 1, item 2, - }, - { - value: '2-1', - parentValue: '2', - children: Level 2, item 1, - }, - { - value: '2-1-1', - parentValue: '2-1', - children: Level 3, item 1, - }, - { - value: '2-2', - parentValue: '2', - children: Level 2, item 2, - }, - { - value: '2-2-1', - parentValue: '2-2', - children: Level 3, item 1, - }, - { - value: '2-2-2', - parentValue: '2-2', - children: Level 3, item 2, - }, - { - value: '2-2-3', - parentValue: '2-2', - children: Level 3, item 3, - }, - { - value: '3', - children: Level 1, item 3, - }, +type Item = FlatTreeItemProps & { content: string }; + +const defaultItems: Item[] = [ + { value: '1', content: 'Level 1, item 1' }, + { value: '1-1', parentValue: '1', content: 'Level 2, item 1' }, + { value: '1-2', parentValue: '1', content: 'Level 2, item 2' }, + { value: '1-3', parentValue: '1', content: 'Level 2, item 3' }, + { value: '2', content: 'Level 1, item 2' }, + { value: '2-1', parentValue: '2', content: 'Level 2, item 1' }, + { value: '2-1-1', parentValue: '2-1', content: 'Level 3, item 1' }, + { value: '2-2', parentValue: '2', content: 'Level 2, item 2' }, + { value: '2-2-1', parentValue: '2-2', content: 'Level 3, item 1' }, + { value: '2-2-2', parentValue: '2-2', content: 'Level 3, item 2' }, + { value: '2-2-3', parentValue: '2-2', content: 'Level 3, item 3' }, + { value: '3', content: 'Level 1, item 3' }, ]; export const UseFlatTree = () => { @@ -67,9 +24,14 @@ export const UseFlatTree = () => { return ( - {Array.from(flatTree.items(), flatTreeItem => ( - - ))} + {Array.from(flatTree.items(), flatTreeItem => { + const { content, ...treeItemProps } = flatTreeItem.getTreeItemProps(); + return ( + + {content} + + ); + })} ); };