Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
516 changes: 395 additions & 121 deletions skills/fluentui-migrate-v8-to-v9/SKILL.md

Large diffs are not rendered by default.

6 changes: 4 additions & 2 deletions skills/fluentui-migrate-v8-to-v9/references/avatargroup.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@ Use `partitionAvatarGroupItems` to split a list into inline vs overflow items ba

## Before / After Example

### Before

```tsx
// v8
import { Facepile, IFacepilePersona } from '@fluentui/react';
import { facepilePersonas } from '@fluentui/example-data';
import { PersonaSize } from '@fluentui/react/lib/Persona';
Expand All @@ -26,8 +27,9 @@ const overflowPersonas = facepilePersonas.slice(3);
<Facepile personaSize={PersonaSize.size32} personas={personas} overflowPersonas={overflowPersonas} />;
```

### After

```tsx
// v9
import {
AvatarGroup,
AvatarGroupItem,
Expand Down
96 changes: 96 additions & 0 deletions skills/fluentui-migrate-v8-to-v9/references/breadcrumb.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# Breadcrumb Migration

v9 `Breadcrumb` uses composable JSX children (`BreadcrumbItem`, `BreadcrumbButton`, `BreadcrumbLink`, `BreadcrumbDivider`) instead of an `items` array.

## Architecture Shift

| v8 | v9 |
| -------------------------------------- | -------------------------------------------------------------------- |
| `items` array prop | Declarative JSX children |
| `IBreadcrumbItem.text` | `<BreadcrumbButton>` children |
| `IBreadcrumbItem.href` | `<BreadcrumbLink href="...">` |
| `IBreadcrumbItem.onClick` | `onClick` on `<BreadcrumbButton>` |
| `IBreadcrumbItem.isCurrentItem` | `current` on `<BreadcrumbButton>` |
| `maxDisplayedItems` + `overflowIndex` | Use `<Overflow>` + `<OverflowItem>` — see [overflow.md](overflow.md) |
| `onRenderItem` / `onRenderItemContent` | Render `<BreadcrumbItem>` directly |
| `dividerAs` | `<BreadcrumbDivider>` (place manually between items) |

## Component Tree

```tsx
import {
Breadcrumb,
BreadcrumbItem,
BreadcrumbButton,
BreadcrumbLink,
BreadcrumbDivider,
} from '@fluentui/react-components';
```

| v9 Component | Purpose |
| ------------------- | ------------------------------------------------------------------------------- |
| `Breadcrumb` | Container (`<nav>` element) |
| `BreadcrumbItem` | `<li>` wrapper — use around every item |
| `BreadcrumbLink` | An `<a>` tag — use for navigable crumbs |
| `BreadcrumbDivider` | Separator between items (must be placed manually between each `BreadcrumbItem`) |
| `BreadcrumbButton` | A `<button>` — use for the current/last crumb or click handlers |

## Prop Mapping

| v8 `IBreadcrumbProps` | v9 equivalent | Notes |
| --------------------- | ---------------------------------- | --------------------------------------- |
| `items` | JSX children | |
| `maxDisplayedItems` | `<Overflow>` wrapper | See overflow.md |
| `overflowIndex` | `<OverflowItem>` with `priority` | See overflow.md |
| `onReduceData` | `useOverflowMenu` hook | See overflow.md |
| `dividerAs` | `<BreadcrumbDivider>` | Place between `BreadcrumbItem` wrappers |
| `onRenderItem` | Render `<BreadcrumbItem>` directly | |
| `focusMode` | `focusMode` on `<Breadcrumb>` | `"tab"` (default) \| `"arrow"` |
| `size` | `size` on `<Breadcrumb>` | `"small"` \| `"medium"` \| `"large"` |
| `styles` | `className` + `makeStyles` | |
| `theme` | — | Use `FluentProvider` |

## Before / After

### Before

```tsx
import { Breadcrumb } from '@fluentui/react';
<Breadcrumb
items={[
{ text: 'Home', key: 'home', href: '/' },
{ text: 'Docs', key: 'docs', href: '/docs' },
{ text: 'Components', key: 'components', isCurrentItem: true },
]}
/>;
```

### After

```tsx
import {
Breadcrumb,
BreadcrumbItem,
BreadcrumbButton,
BreadcrumbLink,
BreadcrumbDivider,
} from '@fluentui/react-components';

<Breadcrumb aria-label="Breadcrumb">
<BreadcrumbItem>
<BreadcrumbLink href="/">Home</BreadcrumbLink>
</BreadcrumbItem>
<BreadcrumbDivider />
<BreadcrumbItem>
<BreadcrumbLink href="/docs">Docs</BreadcrumbLink>
</BreadcrumbItem>
<BreadcrumbDivider />
<BreadcrumbItem>
<BreadcrumbButton current>Components</BreadcrumbButton>
</BreadcrumbItem>
</Breadcrumb>;
```

## Overflow (maxDisplayedItems equivalent)

Combine with `<Overflow>` and `useOverflowMenu` to collapse items when the container is too narrow — see [overflow.md](overflow.md) for the full pattern.
82 changes: 68 additions & 14 deletions skills/fluentui-migrate-v8-to-v9/references/button.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,16 @@

### PrimaryButton

**Before:**

```tsx
// v8
import { PrimaryButton } from '@fluentui/react';
<PrimaryButton text="Submit" onClick={handleSubmit} />;
```

**After:**

// v9
```tsx
import { Button } from '@fluentui/react-components';
<Button appearance="primary" onClick={handleSubmit}>
Submit
Expand All @@ -32,12 +36,16 @@ import { Button } from '@fluentui/react-components';

### ActionButton with icon

**Before:**

```tsx
// v8
import { ActionButton } from '@fluentui/react';
<ActionButton iconProps={{ iconName: 'Add' }}>Add item</ActionButton>;
```

**After:**

// v9
```tsx
import { Button } from '@fluentui/react-components';
import { AddRegular } from '@fluentui/react-icons';
<Button appearance="transparent" icon={<AddRegular />}>
Expand All @@ -47,27 +55,35 @@ import { AddRegular } from '@fluentui/react-icons';

### IconButton

**Before:**

```tsx
// v8
import { IconButton } from '@fluentui/react';
<IconButton iconProps={{ iconName: 'Settings' }} title="Settings" />;
```

// v9
**After:**

```tsx
import { Button } from '@fluentui/react-components';
import { SettingsRegular } from '@fluentui/react-icons';
<Button icon={<SettingsRegular />} aria-label="Settings" />;
```

### Button with href (navigation)

**Before:**

```tsx
// v8 — button that navigates via href
import { DefaultButton, PrimaryButton } from '@fluentui/react';
<DefaultButton href="/dashboard" target="_blank">
Go to dashboard
</DefaultButton>;
```

**After:**

// v9 option A — use Link for inline text-style navigation
```tsx
import { Link } from '@fluentui/react-components';
<Link href="/dashboard" target="_blank">
Go to dashboard
Expand All @@ -84,25 +100,63 @@ import { Button } from '@fluentui/react-components';
supports `href`, `target`, `rel`). Use `Link` for inline prose links; use `Button as="a"` when the
design needs a button shape that navigates.

### Button with dropdown menu (`menuProps`)

**Before:**

```tsx
import { DefaultButton } from '@fluentui/react';
<DefaultButton
text="Options"
menuProps={{
items: [
{ key: 'edit', text: 'Edit', onClick: handleEdit },
{ key: 'delete', text: 'Delete', onClick: handleDelete },
],
}}
/>;
```

**After:**

```tsx
import { Menu, MenuTrigger, MenuPopover, MenuList, MenuItem, MenuButton } from '@fluentui/react-components';
<Menu>
<MenuTrigger disableButtonEnhancement>
<MenuButton>Options</MenuButton>
</MenuTrigger>
<MenuPopover>
<MenuList>
<MenuItem onClick={handleEdit}>Edit</MenuItem>
<MenuItem onClick={handleDelete}>Delete</MenuItem>
</MenuList>
</MenuPopover>
</Menu>;
```

For a primary-styled dropdown button: `<MenuButton appearance="primary">`.

### SplitButton

**Before:**

```tsx
// v8
import { DefaultButton } from '@fluentui/react';
<DefaultButton split menuProps={{ items: [...] }}>Action</DefaultButton>
```

**After:**

// v9
```tsx
import { SplitButton, Menu, MenuItem, MenuList, MenuPopover, MenuTrigger } from '@fluentui/react-components';
<Menu positioning="below-end">
<MenuTrigger>
{(triggerProps) => <SplitButton menuButton={triggerProps}>Action</SplitButton>}
</MenuTrigger>
<MenuTrigger>{triggerProps => <SplitButton menuButton={triggerProps}>Action</SplitButton>}</MenuTrigger>
<MenuPopover>
<MenuList>
<MenuItem>Option 1</MenuItem>
</MenuList>
</MenuPopover>
</Menu>
</Menu>;
```

## Button Shims (Incremental Migration)
Expand Down
6 changes: 4 additions & 2 deletions skills/fluentui-migrate-v8-to-v9/references/callout.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ entirely — the trigger/surface relationship is established by co-location in J

## Before / After Example

### Before

```tsx
// v8
import { Callout, DirectionalHint } from '@fluentui/react';

const targetRef = React.useRef<HTMLButtonElement>(null);
Expand All @@ -36,8 +37,9 @@ const [isVisible, setIsVisible] = React.useState(false);
</>;
```

### After

```tsx
// v9
import { Popover, PopoverTrigger, PopoverSurface, Button } from '@fluentui/react-components';

<Popover withArrow positioning="below-start">
Expand Down
83 changes: 83 additions & 0 deletions skills/fluentui-migrate-v8-to-v9/references/card.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# Card Migration

v8 `DocumentCard` and its variants map to v9's composable `Card` family in `@fluentui/react-components`.

## Component Renames

| v8 | v9 | Notes |
| -------------------------------------- | ------------- | ------------------------------------------------- |
| `DocumentCard` | `Card` | More generic, less opinionated |
| `DocumentCardDetails` | `CardHeader` | |
| `DocumentCardTitle` | — | Use `Text` component |
| `DocumentCardActivity` (activity prop) | `CardHeader` | Use `description` prop |
| `DocumentCardActivity` (people prop) | `CardHeader` | Use `image` + `header` props |
| `DocumentCardActions` | `CardFooter` | |
| `DocumentCardPreview` | `CardPreview` | No `previewImages` array — pass children directly |

## Prop Mapping — DocumentCard → Card

| v8 | v9 | Notes |
| --------------- | ----------- | ---------------------------------------------------- |
| `className` | `className` | |
| `componentRef` | `ref` | |
| `onClick` | `onClick` | |
| `onClickHref` | — | Implement via `onClick` |
| `onClickTarget` | — | Implement via `onClick` |
| `role` | `role` | |
| `styles` | `className` | |
| `theme` | — | Use `FluentProvider` |
| `type` | — | Use `orientation="horizontal"` for horizontal layout |

## Prop Mapping — DocumentCardActions → CardFooter

| v8 | v9 | Notes |
| -------------- | ----------- | ----------------------------------------------------- |
| `className` | `className` | |
| `componentRef` | `ref` | |
| `views` | — | Create a view-count element and pass to `action` prop |
| `role` | `role` | |
| `styles` | `className` | |
| `theme` | — | Use `FluentProvider` |

## Prop Mapping — DocumentCardPreview → CardPreview

| v8 | v9 | Notes |
| ------------------------------ | ----------- | ------------------------------------------------ |
| `className` | `className` | |
| `componentRef` | `ref` | |
| `previewImages` | `children` | Pass `<img>` or any element as children directly |
| `maxDisplayCount` | — | Implement custom overflow via `Card` composition |
| `getOverflowDocumentCountText` | — | Implement custom overflow via `Card` composition |
| `styles` | `className` | |
| `theme` | — | Use `FluentProvider` |

## Before / After

### Before

```tsx
import { DocumentCard, DocumentCardPreview, DocumentCardTitle, DocumentCardActivity } from '@fluentui/react';

<DocumentCard onClick={handleClick}>
<DocumentCardPreview previewImages={[{ previewImageSrc: imgSrc }]} />
<DocumentCardTitle title="Report.pdf" />
<DocumentCardActivity activity="Modified Jan 1" people={[{ name: 'Alice', profileImageSrc: avatarSrc }]} />
</DocumentCard>;
```

### After

```tsx
import { Card, CardHeader, CardPreview } from '@fluentui/react-components';

<Card onClick={handleClick}>
<CardPreview>
<img src={imgSrc} alt="Report preview" />
</CardPreview>
<CardHeader
image={<img src={avatarSrc} alt="Alice" />}
header={<Text weight="semibold">Report.pdf</Text>}
description="Modified Jan 1 · Alice"
/>
</Card>;
```
Loading
Loading