Skip to content

Commit b604fdc

Browse files
author
Paolo Aliprandi
committed
docs: apply verified v8 to v9 guidance
Sync the final source-verified migration guidance from the 53-component benchmark, including responsive overflow, focus, navigation, selection, and theming corrections. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 39bc09e0-178f-4cdd-ad81-dbae05242afc
1 parent e9cf18e commit b604fdc

13 files changed

Lines changed: 285 additions & 140 deletions

File tree

apps/public-docsite-v9/src/Concepts/Migration/FromV8/ComponentMapping.mdx

Lines changed: 75 additions & 75 deletions
Large diffs are not rendered by default.

apps/public-docsite-v9/src/Concepts/Migration/FromV8/Components/Calendar.mdx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,13 @@ props: day and month pickers are visible, "Go to today" is shown,
9292
six-week, highlight, close-button, and `allFocusable` options default to
9393
`false`.
9494

95-
v8 navigation icons were Fabric icon-name strings. The compatibility package
96-
accepts JSX for each nested picker:
95+
v8 navigation icons were Fabric icon-name strings. `CalendarNavigationIcons`
96+
accepts `upNavigation`, `downNavigation`, and `dismiss`. In the day picker,
97+
`upNavigation` controls previous-month navigation and `downNavigation`
98+
controls next-month navigation. Map v8 `leftNavigation` and
99+
`rightNavigation` to those slots, and map `closeIcon` to `dismiss`. Do not
100+
carry the v8 key names into the compatibility object. The compatibility
101+
package accepts JSX for each nested picker:
97102

98103
```tsx
99104
import { ArrowDownRegular, ArrowUpRegular, DismissRegular } from '@fluentui/react-icons';

apps/public-docsite-v9/src/Concepts/Migration/FromV8/Components/Charts.mdx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,13 @@ export function ChartExample() {
5151
}
5252
```
5353

54+
Do not automatically carry literal v8 series colors into native v9 charts.
55+
When color only differentiates otherwise unordered series, use
56+
`getColorFromToken(DataVizPalette.colorN)` while retaining series order and
57+
contrast. Preserve an explicit literal only when the source establishes that
58+
the exact color represents product, brand, threshold, or domain data rather
59+
than a default presentation choice.
60+
5461
Rename v8 chart types when moving imports: for example, `IChartProps` becomes
5562
`ChartProps` and `IVerticalStackedBarChartProps` becomes
5663
`VerticalStackedBarChartProps`. Recheck each chart's current props rather than

apps/public-docsite-v9/src/Concepts/Migration/FromV8/Components/CommandBar.mdx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,10 @@ from the menu trigger. See the
127127
[Button migration guide](/docs/concepts-migration-from-v8-components-button-migration--docs)
128128
for the complete composition.
129129

130+
When a `SplitButton` is a peer of `ToolbarButton` controls, give it the same
131+
visual treatment, normally `appearance="subtle"` unless the source gives that
132+
command additional emphasis.
133+
130134
When that split item moves into the overflow menu, preserve both actions with
131135
`MenuSplitGroup`; replacing it with one submenu-only `MenuItem` drops the v8
132136
primary action:
@@ -173,6 +177,11 @@ accessible name.
173177

174178
### Overflow Handling
175179

180+
Every `OverflowItem` must receive a DOM element or a component that forwards
181+
the measurement ref and supplied root props. For split-button or menu
182+
compositions, use a real wrapper as the direct child or explicitly forward
183+
those values; do not pass a plain function component that discards them.
184+
176185
Wrap the toolbar in `Overflow`, wrap each measured item in `OverflowItem`,
177186
and compose the overflow menu with `useOverflowMenu` and
178187
`useIsOverflowItemVisible`. Unlike v8, overflow items are detected rather than

apps/public-docsite-v9/src/Concepts/Migration/FromV8/Components/DetailsList.mdx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,12 @@ class.
162162
Set `resizableColumns` and provide widths through `columnSizingOptions`.
163163
The option keys must match the column IDs.
164164

165+
Treat migrated `minWidth` values as column lower bounds, not as a substitute
166+
for container sizing. Place a resizable grid in a container that can shrink
167+
within its parent, typically with `width: 100%`, `min-width: 0`, and
168+
`box-sizing: border-box`. Do not convert v8 minimum widths into fixed CSS
169+
widths.
170+
165171
### Activation, focus, and keyboard behavior
166172

167173
`onItemInvoked` is an invocation action: v8 invokes it for Enter on the
@@ -177,6 +183,13 @@ row) and call the migrated active-item callback there. `DataGrid` supplies grid
177183
keyboard navigation; keep the row/cell focus model intact when adding handlers
178184
and do not make every cell an independent tab stop.
179185

186+
When a cell contains an independently interactive control such as a button,
187+
link, input, or menu trigger, configure that `DataGridCell` for grouped
188+
interaction, for example `focusMode="group"` where supported. Preserve the
189+
grid's cell-navigation model while allowing keyboard users to enter the cell
190+
and operate the nested control. Continue excluding nested-control events from
191+
row invocation.
192+
180193
### Stable row identity and virtualization
181194

182195
`DataGrid` falls back to the row index when `getRowId` is omitted. Provide

apps/public-docsite-v9/src/Concepts/Migration/FromV8/Components/Focus.mdx

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,26 @@ const CustomToolbar = () => {
4949
Do not use `role="toolbar"` as a generic replacement for `FocusZone`; choose the
5050
ARIA role that matches the widget being implemented.
5151

52+
Do not add a manual roving `tabIndex` implementation around
53+
`useArrowNavigationGroup`. Keep the group's interactive descendants normally
54+
focusable and let Tabster manage the group's single Tab stop and arrow
55+
movement. Setting non-current controls to `tabIndex={-1}` can remove them from
56+
arrow-navigation discovery. Map `defaultTabbableElement` through the verified
57+
default-descendant or focus-finder mechanism instead of manual tab indexes.
58+
5259
### FocusTrapZone
5360

5461
`Dialog`, `Popover`, and other overlay components already manage focus. Do not
5562
wrap them in another focus trap. For a custom modal surface, use
5663
`useModalAttributes`.
5764

65+
If the trapped surface has dialog or modal semantics, migrate the whole
66+
composition to v9 `Dialog`, even when the v8 source used a native
67+
`role="dialog"` element. Reserve `useModalAttributes` for custom modal surfaces
68+
not represented by a shipped owning component. A controlled `Dialog` must pair
69+
`open` with `onOpenChange`; keep its opener in `DialogTrigger`, or use the
70+
documented restore-focus attributes when that is impossible.
71+
5872
```tsx
5973
// v8
6074
<FocusTrapZone forceFocusInsideTrap disableRestoreFocus={false}>
@@ -87,17 +101,17 @@ const CustomModal = () => {
87101

88102
### FocusZone API mapping
89103

90-
| v8 `FocusZone` | v9 approach |
91-
| ---------------------------------------------- | ------------------------------------------------------------------- |
92-
| `direction` | `useArrowNavigationGroup({ axis })` |
93-
| `isCircularNavigation` | `circular` |
94-
| `shouldFocusOnMount`, `defaultTabbableElement` | Owning component's initial-focus behavior or focus-finder hooks |
95-
| Nested zones | Nested semantic components and Tabster mover/grouper hooks |
96-
| `shouldEnterInnerZone` | Model nested widget behavior explicitly; no direct prop |
97-
| `allowTabKey`, `handleTabKey` | `tabbable` where appropriate, otherwise app-owned keyboard behavior |
98-
| `focus()`, `focusLast()`, `focusElement()` | App-owned refs and focus helpers; no direct instance API |
99-
| `setFocusAlignment()` | No direct equivalent |
100-
| `styles`, `theme` | Replace with `className`, `makeStyles`, and v9 design tokens |
104+
| v8 `FocusZone` | v9 approach |
105+
| ---------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
106+
| `direction` | `useArrowNavigationGroup({ axis })` |
107+
| `isCircularNavigation` | `circular` |
108+
| `shouldFocusOnMount`, `defaultTabbableElement` | Owning component's verified initial-focus/default-descendant behavior; do not emulate with manual roving tab indexes |
109+
| Nested zones | Nested semantic components and Tabster mover/grouper hooks |
110+
| `shouldEnterInnerZone` | Model nested widget behavior explicitly; no direct prop |
111+
| `allowTabKey`, `handleTabKey` | `tabbable` where appropriate, otherwise app-owned keyboard behavior |
112+
| `focus()`, `focusLast()`, `focusElement()` | App-owned refs and focus helpers; no direct instance API |
113+
| `setFocusAlignment()` | No direct equivalent |
114+
| `styles`, `theme` | Replace with `className`, `makeStyles`, and v9 design tokens |
101115

102116
`allowFocusRoot`, `shouldRaiseClicks`, `pagingSupportDisabled`,
103117
`checkForNoWrap`, and `preventFocusRestoration` do not have reliable one-to-one

apps/public-docsite-v9/src/Concepts/Migration/FromV8/Components/GroupedList.mdx

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,16 @@ Choose a replacement from both the **shape of the data** and the **interaction a
2424

2525
`Table` is a set of presentational table primitives and does **not** have built-in selection. Selection can be composed separately with `useTableFeatures` and `useTableSelection`, but [`DataGrid`](?path=/docs/components-datagrid--default) is the v9 component with a built-in `selectionMode`, `selectedItems`, and `onSelectionChange` API.
2626

27+
Before choosing `Tree`, inspect the item renderer. Repeated fields arranged as
28+
grid or flex columns are tabular even when no column component is used. If
29+
those fields must align across different hierarchy depths, do not place a
30+
separate grid inside each `TreeItemLayout`: tree indentation changes each
31+
row's available origin and prevents shared column alignment. Choose a Tree
32+
plus details view, flatten to a DataGrid when acceptable, or treat the result
33+
as a custom treegrid migration. When the source requires aligned hierarchical
34+
columns and no product-approved redesign is available, preserve that contract
35+
with a custom treegrid rather than silently falling back to `Tree`.
36+
2737
### Hierarchy only: migrate to `Tree`
2838

2939
Use [`Tree`](?path=/docs/components-tree--default) when the group hierarchy and expand/collapse interaction are the important parts of the experience. A nested `Tree` maps naturally to recursive `IGroup.children`. A `FlatTree` can be easier when searching, reordering, or virtualizing a large hierarchy.
@@ -38,6 +48,15 @@ Use `Table` for static, flat tabular markup. Use `DataGrid` when the flat rows n
3848

3949
Neither `Tree` nor `DataGrid` alone preserves a hierarchy, multiple aligned columns, expandable rows, and `treegrid` keyboard/ARIA behavior. Splitting the experience into a navigation `Tree` and a flat details `DataGrid` is often clearer. If the combined treegrid interaction is essential, treat it as a custom widget migration and include keyboard and assistive-technology testing in the work.
4050

51+
A custom treegrid needs one shared column-track definition across every row;
52+
do not create independently sized grids inside indented branches. At minimum,
53+
preserve `treegrid`, `row`, and `gridcell` semantics; row and column counts and
54+
indexes; hierarchy level, expanded state, and position within each sibling set;
55+
stable roving focus; vertical row navigation; horizontal cell navigation; and
56+
the source's row and group selection actions. Flattening the render model is
57+
often necessary so indentation can be represented inside the first shared
58+
column without shifting later column origins.
59+
4160
## Inventory behavior before migrating
4261

4362
`GroupedList` combines data projection, rendering, selection, imperative control, virtualization, and accessibility. Record every behavior your usage relies on before replacing the component.

apps/public-docsite-v9/src/Concepts/Migration/FromV8/Components/Nav.mdx

Lines changed: 52 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,17 @@ v8 `Nav` used a `groups` array of `INavLinkGroup` with nested `INavLink` items.
1010

1111
Import from `@fluentui/react-components`no separate package required.
1212

13-
| v8 | v9 |
14-
| -------------------------- | -------------------------------------- |
15-
| `groups` array prop | `<NavCategory>` / `<NavItem>` children |
16-
| `INavLink.name` | `<NavItem>` children (text) |
17-
| `INavLink.url` | `href` on `<NavItem>` |
18-
| `INavLink.onClick` | `onClick` on `<NavItem>` |
19-
| `INavLink.links` (nesting) | `<NavCategory>` + `<NavCategoryItem>` |
20-
| `onLinkClick` | `onNavItemSelect` on `<Nav>` |
21-
| `onLinkExpandClick` | `onNavCategoryItemToggle` on `<Nav>` |
22-
| `selectedKey` | `selectedValue` on `<Nav>` |
13+
| v8 | v9 |
14+
| -------------------------- | ---------------------------------------------------- |
15+
| `groups` array prop | `<NavCategory>` / `<NavItem>` children |
16+
| `INavLink.name` | `<NavItem>` children (text) |
17+
| `INavLink.url` | `href` on `<NavItem>` |
18+
| `INavLink.onClick` | `onClick` on `<NavItem>` |
19+
| `INavLink.links` (nesting) | `<NavCategory>` + `<NavCategoryItem>` |
20+
| `onLinkClick` | `onNavItemSelect` on `<Nav>` |
21+
| `onLinkExpandClick` | `onNavCategoryItemToggle`; read `data.categoryValue` |
22+
| `selectedKey` | `selectedValue` on `<Nav>` |
23+
| `INavLink.isExpanded` | Membership in controlled `openCategories` |
2324

2425
```tsx
2526
import {
@@ -82,36 +83,54 @@ import { Nav } from '@fluentui/react';
8283
The equivalent v9 usage is:
8384

8485
```tsx
86+
import * as React from 'react';
8587
import { Nav, NavItem, NavCategory, NavCategoryItem, NavSubItemGroup, NavSubItem } from '@fluentui/react-components';
8688
import { HomeRegular, SettingsRegular } from '@fluentui/react-icons';
8789

88-
<Nav selectedValue="home" onNavItemSelect={(_, data) => navigate(data.value as string)}>
89-
<NavItem icon={<HomeRegular />} value="home" href="/">
90-
Home
91-
</NavItem>
92-
<NavCategory value="settings">
93-
<NavCategoryItem icon={<SettingsRegular />}>Settings</NavCategoryItem>
94-
<NavSubItemGroup>
95-
<NavSubItem value="profile" href="/profile">
96-
Profile
97-
</NavSubItem>
98-
</NavSubItemGroup>
99-
</NavCategory>
100-
</Nav>;
90+
const [openCategories, setOpenCategories] = React.useState<Iterable<string>>(['settings']);
91+
92+
<nav aria-label="Primary">
93+
<Nav
94+
selectedValue="home"
95+
openCategories={openCategories}
96+
onNavItemSelect={(_, data) => navigate(data.value as string)}
97+
onNavCategoryItemToggle={(_, data) =>
98+
setOpenCategories(current => {
99+
const next = new Set(current);
100+
next.has(data.categoryValue) ? next.delete(data.categoryValue) : next.add(data.categoryValue);
101+
return next;
102+
})
103+
}
104+
>
105+
<NavItem icon={<HomeRegular />} value="home" href="/">
106+
Home
107+
</NavItem>
108+
<NavCategory value="settings">
109+
<NavCategoryItem icon={<SettingsRegular />}>Settings</NavCategoryItem>
110+
<NavSubItemGroup>
111+
<NavSubItem value="profile" href="/profile">
112+
Profile
113+
</NavSubItem>
114+
</NavSubItemGroup>
115+
</NavCategory>
116+
</Nav>
117+
</nav>;
101118
```
102119

103120
## Prop Mapping
104121

105-
| v8 `INavProps` | v9 `NavProps` | Notes |
106-
| -------------------- | -------------------------- | ------------------------- |
107-
| `groups` | JSX children | |
108-
| `selectedKey` | `selectedValue` | |
109-
| `initialSelectedKey` | `defaultSelectedValue` | |
110-
| `onLinkClick` | `onNavItemSelect` | `(_, data) => data.value` |
111-
| `onLinkExpandClick` | `onNavCategoryItemToggle` | |
112-
| `isOnTop` | Use `NavDrawer` | |
113-
| `styles` | `className` + `makeStyles` | |
114-
| `theme` || Use `FluentProvider` |
122+
| v8 `INavProps` | v9 `NavProps` | Notes |
123+
| --------------------- | --------------------------- | ------------------------------------------------------------- |
124+
| `groups` | JSX children | |
125+
| `selectedKey` | `selectedValue` | |
126+
| `initialSelectedKey` | `defaultSelectedValue` | |
127+
| `onLinkClick` | `onNavItemSelect` | `(_, data) => data.value` |
128+
| `onLinkExpandClick` | `onNavCategoryItemToggle` | Read `data.categoryValue`, not selected-item `data.value` |
129+
| `INavLink.isExpanded` | `openCategories` membership | Initialize and update controlled category keys |
130+
| `ariaLabel` | Owning `<nav aria-label>` | Standalone `Nav` does not itself create a navigation landmark |
131+
| `isOnTop` | Use `NavDrawer` | |
132+
| `styles` | `className` + `makeStyles` | |
133+
| `theme` || Use `FluentProvider` |
115134

116135
## Accessibility
117136

0 commit comments

Comments
 (0)