# Vertical Apps - V9 Migration
The purpose of this design doc is to present and describe the different decisions and research required to achieve a smooth migration from Fluent UI Northstar (v0) to Fluent UI v9 in the Teams verticals monorepo.
## Encapsulating v9 usage
### Styling
Currently the packages inside the verticals apps repo are using `mergeStyleSets` from `@uifabric`:
```javascript
import { mergeStyleSets } from "@uifabric/merge-styles";
```
It's convenient, since the syntax is very similar to `@griffel`
All style files are under the pattern `ComponentName.styles.ts`, what can facilitate automation with codemodes.
Let's take the `FacePile` styles for example, currently we have:
```typescript
import { avatarClassName, pxToRem } from "@fluentui/react-northstar";
import { mergeStyleSets } from "@uifabric/merge-styles";
import { ComponentClassNames } from "../../styles/classNames";
type IFacePileClasses = ComponentClassNames<"facePile" | "userName">;
/**
* Get class names.
* @returns Class names.
*/
export const getClassNames = (): IFacePileClasses => {
return mergeStyleSets({
facePile: {
selectors: {
[`.${avatarClassName}`]: {
marginRight: pxToRem(3)
},
[`.${avatarClassName}.disabled`]: {
filter: "grayscale(1)"
}
}
},
userName: {
marginLeft: pxToRem(2),
display: "inline-block",
overflow: "hidden",
textOverflow: "ellipsis"
}
});
};
```
By convernting it to `griffel` it will look like:
```typescript
import { avatarClassName, pxToRem } from "@fluentui/react-northstar";
import { makeStyles, shorthands } from "@griffel/react";
/**
* Get class names.
* @returns Class names.
*/
export const getClassNames = makeStyles({
facePile: {
selectors: {
[`.${avatarClassName}`]: {
...shorthands.margin("0", pxToRem(3), "0", "0")
},
[`.${avatarClassName}.disabled`]: {
filter: "grayscale(1)"
}
}
},
userName: {
...shorthands.margin("0", "0", "0", pxToRem(2)),
...shorthands.overflow("hidden"),
display: "inline-block",
textOverflow: "ellipsis"
}
});
```
### Component's Migratoin
All northstar components are exported from `@microsoft/modernworkplace-ui-core`, in order to use v9 components without affection production and allowing less invasive migration, it would be ideal to create a new exporting package for v9 such as `@microsoft/modernworkplace-ui-core-v9`. Once we have that set up, we need to use `FluentProvider` so tokens will be available, lets take again `FacePile` as example and see what would need to be done in order to migrate `Avatar` used inside.
First we would need to use `FluentProvider` in the following places:
**packages\modernworkplace-ui-core\src\testing\renderWithCoreContext.tsx**
```diff=
@@ -5,6 +5,7 @@
import { Provider } from "@fluentui/react-northstar";
import { LegacyTranslationsProvider, LocalizersProvider } from "@microsoft/modernworkplace-i18n";
import { TeamsContextProvider } from "@microsoft/modernworkplace-react-hooks";
+import { FluentProvider, teamsLightTheme } from "@microsoft/modernworkplace-ui-core-v9";
import { RenderOptions, RenderResult, render } from "@testing-library/react";
import React from "react";
@@ -33,9 +34,11 @@ export const wrapInProviders = (
return (
<Provider>
- <TeamsContextProvider>
- <CoreUITranslationsProvider>{contentWithLocalizers}</CoreUITranslationsProvider>
- </TeamsContextProvider>
+ <FluentProvider theme={teamsLightTheme}>
+ <TeamsContextProvider>
+ <CoreUITranslationsProvider>{contentWithLocalizers}</CoreUITranslationsProvider>
+ </TeamsContextProvider>
+ </FluentProvider>
</Provider>
);
};
```
**packages/modernworkplace-ui-core/.storybook/preview.js**
```diff=
@@ -1,6 +1,7 @@
import * as React from "react";
import { LocalizersProvider } from "@microsoft/modernworkplace-i18n"
+import {FluentProvider, teamsLightTheme} from "@microsoft/modernworkplace-ui-core-v9";
import { TeamsContext, TeamsThemeType, getComposedTeamsTheme, TextDirection } from "@microsoft/modernworkplace-react-hooks";
import { Provider as NorthstarThemeProvider } from "@fluentui/react-northstar";
import { withPerformance } from "storybook-addon-performance";
@@ -42,6 +43,7 @@ const withNecessaryProviders = (Story, context) => {
const isRTL = context.globals.direction === TextDirection.RightToLeft;
return (
+ <FluentProvider theme={teamsLightTheme}>
<TeamsContext.Provider value={{ theme, themeType }}>
<NorthstarThemeProvider theme={theme} rtl={isRTL}>
<LocalizersProvider>
@@ -51,6 +53,7 @@ const withNecessaryProviders = (Story, context) => {
</LocalizersProvider>
</NorthstarThemeProvider>
</TeamsContext.Provider>
+ </FluentProvider>
);
}
```
So the replacement in **packages\modernworkplace-ui-core\src\components\facePile\FacePile.tsx** is simple enough as:
```diff=
-import { Avatar, Flex, Text } from "@fluentui/react-northstar";
+import { Avatar, AvatarSizes } from "@fluentui/react-components";
+import { Flex, SizeValue, Text } from "@fluentui/react-northstar";
import { useTeamsContext } from "@microsoft/modernworkplace-react-hooks";
import * as React from "react";
@@ -14,6 +15,16 @@ import { IFacePile } from "./IFacePile";
// Default, maximum number of users to display.
const defaultVisibleMax = 11;
+const sizesMap: Record<SizeValue, AvatarSizes> = {
+ smallest: 20,
+ smaller: 24,
+ small: 28,
+ medium: 32,
+ large: 64,
+ larger: 64,
+ largest: 96
+};
+
/**
* Renders face pile component.
* @param props Component properties.
@@ -27,7 +38,7 @@ export const FacePile: React.FC<IFacePile> = props => {
const classes = getClassNames();
const maxVisible = props.maxNumberOfVisibleAvatars ?? defaultVisibleMax;
const overflow = props.users.length > maxVisible;
- const overflowLabel = (name: string) => `+${props.users.length - maxVisible}`;
+ const overflowLabel = `+${props.users.length - maxVisible}`;
const avatarClass = props.disabled ? "disabled" : "";
const size = props.size ?? "medium";
@@ -39,11 +50,9 @@ export const FacePile: React.FC<IFacePile> = props => {
return (
<Flex vAlign="center" className={classes.facePile}>
{props.users.slice(0, maxVisible).map((user, idx) => {
- return (
- <Avatar className={avatarClass} key={`${idx}-${user.id}`} name={user.displayName} image={user.imageUrl} size={size} />
- );
+ return <Avatar key={`${idx}-${user.id}`} name={user.displayName} image={{ src: user.imageUrl }} size={sizesMap[size]} />;
})}
- {overflow && <Avatar getInitials={overflowLabel} size={size} className={avatarClass} />}
+ {overflow && <Avatar initials={overflowLabel} size={sizesMap[size]} className={avatarClass} />}
{props.users.length === 1 && (
<Text
variables={textVariables}
```
One final touch, we need one more update in the styles since we are not exporting `avatarClassName` in v9 but `avatarClassNames` which is an object with class names for each slots. So the final change in the `styles` file would be:
```diff=
- import { avatarClassName } from "@fluentui/react-northstar";
+ import { avatarClassNames } from "@microsoft/modernworkplace-ui-core-v9";
- [`.${avatarClassName}`]: {
+ [`.${avatarClassNames.root}`]: {
```
We have migrations instructions for several components, here we used [Avatar Migrition](https://react.fluentui.dev/?path=/docs/concepts-migration-from-v0-components-avatar-migration--page)