diff --git a/change/@fluentui-react-tree-tree-deep-level-fix.json b/change/@fluentui-react-tree-tree-deep-level-fix.json new file mode 100644 index 00000000000000..ac7e881abae4b5 --- /dev/null +++ b/change/@fluentui-react-tree-tree-deep-level-fix.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "fix(react-tree): support indentation for TreeItem levels greater than 10 via inline CSS variable fallback", + "packageName": "@fluentui/react-tree", + "email": "198982749+Copilot@users.noreply.github.com", + "dependentChangeType": "patch" +} diff --git a/packages/react-components/react-tree/library/src/components/TreeItem/TreeItem.test.tsx b/packages/react-components/react-tree/library/src/components/TreeItem/TreeItem.test.tsx index a23f3ca2f506c4..24cd6eb305f3d7 100644 --- a/packages/react-components/react-tree/library/src/components/TreeItem/TreeItem.test.tsx +++ b/packages/react-components/react-tree/library/src/components/TreeItem/TreeItem.test.tsx @@ -5,6 +5,7 @@ import { isConformant } from '../../testing/isConformant'; import type { TreeItemProps } from './TreeItem.types'; import { treeItemClassNames } from './useTreeItemStyles.styles'; import { Tree } from '../../Tree'; +import { treeItemLevelToken } from '../../utils/tokens'; describe('TreeItem', () => { isConformant({ @@ -51,4 +52,38 @@ describe('TreeItem', () => { fireEvent.click(result.getByText('Default TreeItem')); expect(handleOpenChange).not.toHaveBeenCalled(); }); + + it('should set the level CSS variable via inline style for levels greater than 10', () => { + const depth = 12; + const openItems: string[] = []; + const renderNestedTree = (current: number): React.ReactElement => { + if (current > 1) { + openItems.push(`item-${current}`); + } + return ( + + {`level ${current}`} + {current > 1 ? {renderNestedTree(current - 1)} : null} + + ); + }; + const treeContent = renderNestedTree(depth); + + const result = render({treeContent}); + + const treeItems = result.container.querySelectorAll(`.${treeItemClassNames.root}`); + expect(treeItems).toHaveLength(depth); + + // Levels 1..10 are handled by static classes and should not set the inline CSS variable + // Levels > 10 fall back to an inline CSS variable for the indentation + treeItems.forEach(item => { + const ariaLevel = Number(item.getAttribute('aria-level')); + const inlineLevel = item.style.getPropertyValue(treeItemLevelToken); + if (ariaLevel > 10) { + expect(inlineLevel).toBe(String(ariaLevel)); + } else { + expect(inlineLevel).toBe(''); + } + }); + }); }); diff --git a/packages/react-components/react-tree/library/src/components/TreeItem/useTreeItemStyles.styles.ts b/packages/react-components/react-tree/library/src/components/TreeItem/useTreeItemStyles.styles.ts index adc5d672a8528f..fa990acdbe28a3 100644 --- a/packages/react-components/react-tree/library/src/components/TreeItem/useTreeItemStyles.styles.ts +++ b/packages/react-components/react-tree/library/src/components/TreeItem/useTreeItemStyles.styles.ts @@ -77,6 +77,17 @@ export const useTreeItemStyles_unstable = (state: TreeItemState): TreeItemState state.root.className, ); + // For levels beyond the statically generated classes (> 10), fall back to an + // inline style that sets the indentation CSS variable dynamically. This avoids + // generating an unbounded number of atomic classes while still supporting + // arbitrarily deep trees. User-provided inline styles take precedence. + if (!isStaticallyDefinedLevel(level)) { + state.root.style = { + [treeItemLevelToken]: level, + ...state.root.style, + }; + } + return state; }; diff --git a/packages/react-components/react-tree/stories/src/Tree/TreeInlineStylingTreeItemLevel.stories.tsx b/packages/react-components/react-tree/stories/src/Tree/TreeInlineStylingTreeItemLevel.stories.tsx index 4f054505663ff6..060a7571b92c9f 100644 --- a/packages/react-components/react-tree/stories/src/Tree/TreeInlineStylingTreeItemLevel.stories.tsx +++ b/packages/react-components/react-tree/stories/src/Tree/TreeInlineStylingTreeItemLevel.stories.tsx @@ -28,7 +28,7 @@ InlineStylingTreeItemLevel.parameters = { docs: { description: { story: ` -The tree component supports nested styling up to 10 levels, limited by performance considerations. For more than 10 levels of nesting, use dynamic styling instead. Below is an example of how to apply custom inline styles to create dynamic tree item levels, overriding the default static styles. +The tree component generates static styles for the first 10 nesting levels (for performance reasons) and automatically falls back to an inline CSS variable for deeper levels, so arbitrarily deep trees indent correctly out of the box. Below is an example of how to apply custom inline styles to create dynamic tree item levels, overriding the default static styles. `, }, },