From 115e5070c90d2af973e90dfe48273cf3c50c93cc Mon Sep 17 00:00:00 2001 From: Dmytro Kirpa Date: Wed, 11 Dec 2024 15:37:05 +0100 Subject: [PATCH 01/10] feat(eslint-plugin-react-components): add prefer-fluentui-v9 rule --- .../eslint-plugin-react-components/README.md | 33 ++- .../etc/eslint-plugin-react-components.api.md | 16 +- .../src/index.ts | 9 +- .../src/rules/prefer-fluentui-v9.spec.ts | 44 ++++ .../src/rules/prefer-fluentui-v9.ts | 192 ++++++++++++++++++ .../src/rules/utils/create-rule.ts | 9 + 6 files changed, 291 insertions(+), 12 deletions(-) create mode 100644 packages/react-components/eslint-plugin-react-components/src/rules/prefer-fluentui-v9.spec.ts create mode 100644 packages/react-components/eslint-plugin-react-components/src/rules/prefer-fluentui-v9.ts create mode 100644 packages/react-components/eslint-plugin-react-components/src/rules/utils/create-rule.ts diff --git a/packages/react-components/eslint-plugin-react-components/README.md b/packages/react-components/eslint-plugin-react-components/README.md index 5da83b63a80507..b53b66bcdd2a1d 100644 --- a/packages/react-components/eslint-plugin-react-components/README.md +++ b/packages/react-components/eslint-plugin-react-components/README.md @@ -40,21 +40,46 @@ module.exports = { }; ``` -1. Or configure individual rules manually: +2. Or configure individual rules manually: ```js module.exports = { plugins: ['@fluentui/react-components'], rules: { - '@fluentui/react-components/rule-name-1': 'error', - '@fluentui/react-components/rule-name-2': 'warn', + '@fluentui/react-components/prefer-fluentui-v9': 'warn', }, }; ``` ## Available Rules -TBD +### prefer-fluentui-v9 + +This rule ensures the use of Fluent UI v9 counterparts for Fluent UI v8 components. + +#### Options + +- `unstable` (boolean): Whether to enforce Fluent UI v9 preview component migrations. + +#### Examples + +**✅ Do** + +```js +// Import and use components that have been already migrated to Fluent UI v9 +import { Button } from '@fluentui/react-components'; + +const Component = () => ; +``` + +**❌ Don't** + +```js +// Avoid importing and using Fluent UI V8 components that have already been migrated to Fluent UI V9. +import { DefaultButton } from '@fluentui/react'; + +const Component = () => ...; +``` ## License diff --git a/packages/react-components/eslint-plugin-react-components/etc/eslint-plugin-react-components.api.md b/packages/react-components/eslint-plugin-react-components/etc/eslint-plugin-react-components.api.md index ebe584c246463f..97074aa5c04be0 100644 --- a/packages/react-components/eslint-plugin-react-components/etc/eslint-plugin-react-components.api.md +++ b/packages/react-components/eslint-plugin-react-components/etc/eslint-plugin-react-components.api.md @@ -4,8 +4,11 @@ ```ts +import { RuleListener } from '@typescript-eslint/utils/dist/ts-eslint'; +import { RuleModule } from '@typescript-eslint/utils/dist/ts-eslint'; + // @public (undocumented) -const plugin: { +export const plugin: { meta: { name: string; version: string; @@ -13,12 +16,17 @@ const plugin: { configs: { recommended: { plugins: string[]; - rules: {}; + rules: { + "@fluentui/react-components/prefer-fluentui-v9": string; + }; }; }; - rules: {}; + rules: { + "prefer-fluentui-v9": RuleModule<"replaceFluent8With9" | "replaceFluent8With9Unstable" | "replaceIconWithJsx" | "replaceStackWithFlex", { + unstable?: boolean | undefined; + }[], unknown, RuleListener>; + }; }; -export default plugin; // (No @packageDocumentation comment for this package) diff --git a/packages/react-components/eslint-plugin-react-components/src/index.ts b/packages/react-components/eslint-plugin-react-components/src/index.ts index 821e8d8738ab9e..b8860ee09fe63d 100644 --- a/packages/react-components/eslint-plugin-react-components/src/index.ts +++ b/packages/react-components/eslint-plugin-react-components/src/index.ts @@ -1,20 +1,21 @@ import { name, version } from '../package.json'; +import { RULE_NAME as preferFluentUIV9Name, rule as preferFluentUIV9 } from './rules/prefer-fluentui-v9'; const allRules = { - // add all rules here + [preferFluentUIV9Name]: preferFluentUIV9, }; const configs = { recommended: { plugins: [name], rules: { - // add all recommended rules here + [`@fluentui/react-components/${preferFluentUIV9Name}`]: 'warn', }, }, }; // Plugin definition -const plugin = { +export const plugin = { meta: { name, version, @@ -33,4 +34,4 @@ Object.assign(configs, { }, }); -export default plugin; +module.exports = plugin; diff --git a/packages/react-components/eslint-plugin-react-components/src/rules/prefer-fluentui-v9.spec.ts b/packages/react-components/eslint-plugin-react-components/src/rules/prefer-fluentui-v9.spec.ts new file mode 100644 index 00000000000000..91a569d0f0e9d7 --- /dev/null +++ b/packages/react-components/eslint-plugin-react-components/src/rules/prefer-fluentui-v9.spec.ts @@ -0,0 +1,44 @@ +import { RuleTester } from '@typescript-eslint/rule-tester'; +import { RULE_NAME, rule } from './prefer-fluentui-v9'; + +const ruleTester = new RuleTester(); + +ruleTester.run(RULE_NAME, rule, { + valid: [ + { + code: `import type { IDropdownOption } from '@fluentui/react';`, + }, + { + code: `import type { ITheme } from '@fluentui/react';`, + }, + { + code: `import { ThemeProvider } from '@fluentui/react';`, + }, + { + code: `import { Button } from '@fluentui/react-components';`, + }, + { + code: `import { Rating } from '@fluentui/react';`, + options: [{ unstable: false }], + }, + ], + invalid: [ + { + code: `import { Dropdown, Icon } from '@fluentui/react';`, + errors: [{ messageId: 'replaceFluent8With9' }, { messageId: 'replaceIconWithJsx' }], + }, + { + code: `import { Stack } from '@fluentui/react';`, + errors: [{ messageId: 'replaceStackWithFlex' }], + }, + { + code: `import { DatePicker } from '@fluentui/react';`, + errors: [{ messageId: 'replaceFluent8With9Unstable' }], + }, + { + code: `import { Rating } from '@fluentui/react';`, + options: [{ unstable: true }], + errors: [{ messageId: 'replaceFluent8With9Unstable' }], + }, + ], +}); diff --git a/packages/react-components/eslint-plugin-react-components/src/rules/prefer-fluentui-v9.ts b/packages/react-components/eslint-plugin-react-components/src/rules/prefer-fluentui-v9.ts new file mode 100644 index 00000000000000..6beec5db3c5e2c --- /dev/null +++ b/packages/react-components/eslint-plugin-react-components/src/rules/prefer-fluentui-v9.ts @@ -0,0 +1,192 @@ +import { createRule } from './utils/create-rule'; + +export const RULE_NAME = 'prefer-fluentui-v9'; + +type Options = Array<{ + /** Whether to enforce Fluent UI v9 preview component migrations. */ + unstable?: boolean; +}>; + +type MessageIds = 'replaceFluent8With9' | 'replaceFluent8With9Unstable' | 'replaceIconWithJsx' | 'replaceStackWithFlex'; + +export const rule = createRule({ + name: RULE_NAME, + meta: { + type: 'problem', + docs: { + description: 'This rule ensures the use of Fluent UI v9 counterparts for Fluent UI v8 components.', + }, + schema: [ + { + type: 'object', + properties: { + unstable: { type: 'boolean' }, + }, + additionalProperties: false, + }, + ], + messages: { + replaceFluent8With9: `Avoid importing {{ fluent8 }} from '@fluentui/react', as this package has already migrated to Fluent UI 9. Import {{ fluent9 }} from '@fluentui/react-components' instead.`, + replaceFluent8With9Unstable: `Avoid importing {{ fluent8 }} from '@fluentui/react', as this package has started migration to Fluent UI 9. Import {{ fluent9 }} from '{{ package }}' instead.`, + replaceIconWithJsx: `Avoid using Icon from '@fluentui/react', as this package has already migrated to Fluent UI 9. Use a JSX SVG icon from '@fluentui/react-icons' instead.`, + replaceStackWithFlex: `Avoid using Stack from '@fluentui/react', as this package has already migrated to Fluent UI 9. Use native CSS flexbox instead.`, + }, + }, + defaultOptions: [], + create(context) { + const { unstable = false } = context.options[0] ?? {}; + + return { + // eslint-disable-next-line @typescript-eslint/naming-convention + ImportDeclaration(node) { + if (node.source.value !== '@fluentui/react') { + return; + } + + for (const specifier of node.specifiers) { + if (specifier.type === 'ImportSpecifier' && specifier.imported.type === 'Identifier') { + const name = specifier.imported.name; + + switch (name) { + case 'Icon': + context.report({ node, messageId: 'replaceIconWithJsx' }); + break; + case 'Stack': + context.report({ node, messageId: 'replaceStackWithFlex' }); + break; + default: + if (isMigration(name)) { + context.report({ + node, + messageId: 'replaceFluent8With9', + data: { + fluent8: name, + fluent9: MIGRATIONS[name], + }, + }); + } else if (isCompatMigration(name)) { + context.report({ + node, + messageId: 'replaceFluent8With9Unstable', + data: { + fluent8: name, + fluent9: COMPAT_MIGRATIONS[name].component, + package: COMPAT_MIGRATIONS[name].package, + }, + }); + } else if (isPreviewMigration(name) && unstable) { + context.report({ + node, + messageId: 'replaceFluent8With9Unstable', + data: { + fluent8: name, + fluent9: PREVIEW_MIGRATIONS[name].component, + package: PREVIEW_MIGRATIONS[name].package, + }, + }); + } + break; + } + } + } + }, + }; + }, +}); + +type Migration = string | { component: string; package: string }; + +/** + * Migrations from Fluent 8 components to Fluent 9 components as of Fluent UI 9.46.3 + * @see https://react.fluentui.dev/?path=/docs/concepts-migration-from-v8-component-mapping--page + */ +const MIGRATIONS = { + mergeStyles: 'makeStyles', + ActionButton: 'Button', + Announced: 'useAnnounce', + Breadcrumb: 'Breadcrumb', + CommandBar: 'Toolbar', + CommandBarButton: 'ToolbarButton', + CommandButton: 'MenuButton', + CompoundButton: 'CompoundButton', + Callout: 'Popover', + Checkbox: 'Checkbox', + ChoiceGroup: 'Radio', + ComboBox: 'Dropdown', + ContextualMenu: 'Menu', + DefaultButton: 'Button', + DetailsList: 'DataGrid', + Dialog: 'Dialog', + DocumentCard: 'Card', + Dropdown: 'Dropdown', + Facepile: 'AvatarGroup', + GroupedList: 'Tree', + IconButton: 'Button', + Image: 'Image', + Label: 'Label', + Layer: 'Portal', + Link: 'Link', + MessageBar: 'MessageBar', + Modal: 'Dialog', + OverflowSet: 'Overflow', + Overlay: 'Portal', + Panel: 'Drawer', + Popup: 'Dialog', + PrimaryButton: 'Button', + Persona: 'Avatar', + Pivot: 'TabList', + PivotItem: 'Tab', + ProgressIndicator: 'ProgressBar', + Separator: 'Divider', + Shimmer: 'Skeleton', + Slider: 'Slider', + SplitButton: 'SplitButton', + SpinButton: 'SpinButton', + Spinner: 'Spinner', + Text: 'Text', + TextField: 'Input', + ToggleButton: 'ToggleButton', + Toggle: 'Switch', + Tooltip: 'Tooltip', + TooltipHost: 'Tooltip', + VerticalDivider: 'Divider', +} satisfies Record; + +/** + * Compatibility migrations for certain components. + * @see https://react.fluentui.dev/?path=/docs/concepts-migration-from-v8-component-mapping--page + */ +const COMPAT_MIGRATIONS = { + DatePicker: { component: 'DatePicker', package: '@fluentui/react-datepicker-compat' }, + TimePicker: { component: 'TeachingPopover', package: '@fluentui/react-timepicker-compat' }, +} satisfies Record; + +/** + * Preview migrations for certain components. + * @see https://react.fluentui.dev/?path=/docs/concepts-migration-from-v8-component-mapping--page + */ +const PREVIEW_MIGRATIONS = { + Rating: { component: 'Rating', package: '@fluentui/react-rating-preview' }, + TeachingBubble: { component: 'TeachingPopover', package: '@fluentui/react-teaching-popover-preview' }, +} satisfies Record; + +/** + * Checks if a component name is in the MIGRATIONS list. + * @param name - The name of the component. + * @returns True if the component is in the MIGRATIONS list, false otherwise. + */ +const isMigration = (name: string): name is keyof typeof MIGRATIONS => name in MIGRATIONS; + +/** + * Checks if a component name is in the COMPAT_MIGRATIONS list. + * @param name - The name of the component. + * @returns True if the component is in the COMPAT_MIGRATIONS list, false otherwise. + */ +const isCompatMigration = (name: string): name is keyof typeof COMPAT_MIGRATIONS => name in COMPAT_MIGRATIONS; + +/** + * Checks if a component name is in the PREVIEW_MIGRATIONS list. + * @param name - The name of the component. + * @returns True if the component is in the PREVIEW_MIGRATIONS list, false otherwise. + */ +const isPreviewMigration = (name: string): name is keyof typeof PREVIEW_MIGRATIONS => name in PREVIEW_MIGRATIONS; diff --git a/packages/react-components/eslint-plugin-react-components/src/rules/utils/create-rule.ts b/packages/react-components/eslint-plugin-react-components/src/rules/utils/create-rule.ts new file mode 100644 index 00000000000000..0365797e179f62 --- /dev/null +++ b/packages/react-components/eslint-plugin-react-components/src/rules/utils/create-rule.ts @@ -0,0 +1,9 @@ +import { ESLintUtils } from '@typescript-eslint/utils'; + +/** + * Creates an ESLint rule with a pre-configured URL pointing to the rule's documentation. + */ +export const createRule = ESLintUtils.RuleCreator( + name => + `https://github.com/microsoft/fluentui/blob/master/packages/react-components/eslint-plugin-react-components/README.md#${name}`, +); From 39acd070abda7415c61a65f9cfa7a9f1561eaaec Mon Sep 17 00:00:00 2001 From: Dmytro Kirpa Date: Wed, 11 Dec 2024 15:47:02 +0100 Subject: [PATCH 02/10] change file --- ...ct-components-1c2bc691-c857-43cf-9806-ffe8d0d058c0.json | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 change/@fluentui-eslint-plugin-react-components-1c2bc691-c857-43cf-9806-ffe8d0d058c0.json diff --git a/change/@fluentui-eslint-plugin-react-components-1c2bc691-c857-43cf-9806-ffe8d0d058c0.json b/change/@fluentui-eslint-plugin-react-components-1c2bc691-c857-43cf-9806-ffe8d0d058c0.json new file mode 100644 index 00000000000000..5c360689a6808b --- /dev/null +++ b/change/@fluentui-eslint-plugin-react-components-1c2bc691-c857-43cf-9806-ffe8d0d058c0.json @@ -0,0 +1,7 @@ +{ + "type": "minor", + "comment": "feat: add prefer-fluentui-v9 rule", + "packageName": "@fluentui/eslint-plugin-react-components", + "email": "dmytrokirpa@microsoft.com", + "dependentChangeType": "patch" +} From 85a4320d3bf83d45e4df2086540f40928625ab8d Mon Sep 17 00:00:00 2001 From: Dmytro Kirpa Date: Thu, 12 Dec 2024 11:26:47 +0100 Subject: [PATCH 03/10] update components mapping with recent info from docsite --- .../src/rules/prefer-fluentui-v9.spec.ts | 26 +++-- .../src/rules/prefer-fluentui-v9.ts | 104 +++++++++--------- 2 files changed, 71 insertions(+), 59 deletions(-) diff --git a/packages/react-components/eslint-plugin-react-components/src/rules/prefer-fluentui-v9.spec.ts b/packages/react-components/eslint-plugin-react-components/src/rules/prefer-fluentui-v9.spec.ts index 91a569d0f0e9d7..be821113236e9f 100644 --- a/packages/react-components/eslint-plugin-react-components/src/rules/prefer-fluentui-v9.spec.ts +++ b/packages/react-components/eslint-plugin-react-components/src/rules/prefer-fluentui-v9.spec.ts @@ -18,8 +18,8 @@ ruleTester.run(RULE_NAME, rule, { code: `import { Button } from '@fluentui/react-components';`, }, { - code: `import { Rating } from '@fluentui/react';`, - options: [{ unstable: false }], + code: `import { ColorPicker } from '@fluentui/react';`, + options: [{ preview: false }], }, ], invalid: [ @@ -33,12 +33,22 @@ ruleTester.run(RULE_NAME, rule, { }, { code: `import { DatePicker } from '@fluentui/react';`, - errors: [{ messageId: 'replaceFluent8With9Unstable' }], - }, - { - code: `import { Rating } from '@fluentui/react';`, - options: [{ unstable: true }], - errors: [{ messageId: 'replaceFluent8With9Unstable' }], + errors: [ + { + messageId: 'replaceFluent8With9', + data: { fluent8: 'DatePicker', fluent9: 'DatePicker', package: '@fluentui/react-datepicker-compat' }, + }, + ], + }, + { + code: `import { List } from '@fluentui/react';`, + options: [{ preview: true }], + errors: [ + { + messageId: 'replaceFluent8With9', + data: { fluent8: 'List', fluent9: 'List', package: '@fluentui/react-list-preview' }, + }, + ], }, ], }); diff --git a/packages/react-components/eslint-plugin-react-components/src/rules/prefer-fluentui-v9.ts b/packages/react-components/eslint-plugin-react-components/src/rules/prefer-fluentui-v9.ts index 6beec5db3c5e2c..ae7d79ee4819ce 100644 --- a/packages/react-components/eslint-plugin-react-components/src/rules/prefer-fluentui-v9.ts +++ b/packages/react-components/eslint-plugin-react-components/src/rules/prefer-fluentui-v9.ts @@ -4,10 +4,10 @@ export const RULE_NAME = 'prefer-fluentui-v9'; type Options = Array<{ /** Whether to enforce Fluent UI v9 preview component migrations. */ - unstable?: boolean; + preview?: boolean; }>; -type MessageIds = 'replaceFluent8With9' | 'replaceFluent8With9Unstable' | 'replaceIconWithJsx' | 'replaceStackWithFlex'; +type MessageIds = 'replaceFluent8With9' | 'replaceIconWithJsx' | 'replaceStackWithFlex' | 'replaceFocusZoneWithTabster'; export const rule = createRule({ name: RULE_NAME, @@ -20,21 +20,21 @@ export const rule = createRule({ { type: 'object', properties: { - unstable: { type: 'boolean' }, + preview: { type: 'boolean' }, }, additionalProperties: false, }, ], messages: { - replaceFluent8With9: `Avoid importing {{ fluent8 }} from '@fluentui/react', as this package has already migrated to Fluent UI 9. Import {{ fluent9 }} from '@fluentui/react-components' instead.`, - replaceFluent8With9Unstable: `Avoid importing {{ fluent8 }} from '@fluentui/react', as this package has started migration to Fluent UI 9. Import {{ fluent9 }} from '{{ package }}' instead.`, + replaceFluent8With9: `Avoid importing {{ fluent8 }} from '@fluentui/react', as this package has started migration to Fluent UI 9. Import {{ fluent9 }} from '{{ package }}' instead.`, replaceIconWithJsx: `Avoid using Icon from '@fluentui/react', as this package has already migrated to Fluent UI 9. Use a JSX SVG icon from '@fluentui/react-icons' instead.`, - replaceStackWithFlex: `Avoid using Stack from '@fluentui/react', as this package has already migrated to Fluent UI 9. Use native CSS flexbox instead.`, + replaceStackWithFlex: `Avoid using Stack from '@fluentui/react', as this package has already migrated to Fluent UI 9. Use native CSS flexbox instead. More details are available at https://react.fluentui.dev/?path=/docs/concepts-migration-from-v8-components-flex-stack--docs`, + replaceFocusZoneWithTabster: `Avoid using {{ fluent8 }} from '@fluentui/react', as this package has already migrated to Fluent UI 9. Use the equivalent [Tabster](https://tabster.io/) hook instead.`, }, }, defaultOptions: [], create(context) { - const { unstable = false } = context.options[0] ?? {}; + const { preview = false } = context.options[0] ?? {}; return { // eslint-disable-next-line @typescript-eslint/naming-convention @@ -54,30 +54,27 @@ export const rule = createRule({ case 'Stack': context.report({ node, messageId: 'replaceStackWithFlex' }); break; + case 'FocusTrapZone': + case 'FocusZone': + context.report({ node, messageId: 'replaceFocusZoneWithTabster', data: { fluent8: name } }); + break; default: if (isMigration(name)) { + const migration = getMigrationData(MIGRATIONS[name]); + context.report({ node, messageId: 'replaceFluent8With9', data: { fluent8: name, - fluent9: MIGRATIONS[name], - }, - }); - } else if (isCompatMigration(name)) { - context.report({ - node, - messageId: 'replaceFluent8With9Unstable', - data: { - fluent8: name, - fluent9: COMPAT_MIGRATIONS[name].component, - package: COMPAT_MIGRATIONS[name].package, + fluent9: migration.component, + package: migration.package, }, }); - } else if (isPreviewMigration(name) && unstable) { + } else if (isPreviewMigration(name) && preview) { context.report({ node, - messageId: 'replaceFluent8With9Unstable', + messageId: 'replaceFluent8With9', data: { fluent8: name, fluent9: PREVIEW_MIGRATIONS[name].component, @@ -97,32 +94,41 @@ export const rule = createRule({ type Migration = string | { component: string; package: string }; /** - * Migrations from Fluent 8 components to Fluent 9 components as of Fluent UI 9.46.3 - * @see https://react.fluentui.dev/?path=/docs/concepts-migration-from-v8-component-mapping--page + * Migrations from Fluent 8 components to Fluent 9 components. + * @see https://react.fluentui.dev/?path=/docs/concepts-migration-from-v8-component-mapping--docs */ const MIGRATIONS = { - mergeStyles: 'makeStyles', + makeStyles: 'makeStyles', ActionButton: 'Button', Announced: 'useAnnounce', Breadcrumb: 'Breadcrumb', + Button: 'Button', + Callout: 'Popover', + Calendar: { component: 'Calendar', package: '@fluentui/react-calendar-compat' }, CommandBar: 'Toolbar', - CommandBarButton: 'ToolbarButton', + CommandBarButton: 'Toolbar', CommandButton: 'MenuButton', CompoundButton: 'CompoundButton', - Callout: 'Popover', Checkbox: 'Checkbox', - ChoiceGroup: 'Radio', - ComboBox: 'Dropdown', + ChoiceGroup: 'RadioGroup', + Coachmark: 'TeachingPopover', + ComboBox: 'Combobox', ContextualMenu: 'Menu', DefaultButton: 'Button', + DatePicker: { component: 'DatePicker', package: '@fluentui/react-datepicker-compat' }, DetailsList: 'DataGrid', Dialog: 'Dialog', DocumentCard: 'Card', Dropdown: 'Dropdown', + Fabric: 'FluentProvider', Facepile: 'AvatarGroup', + FocusTrapZone: 'Tabster', + FocusZone: 'Tabster', GroupedList: 'Tree', + HoverCard: 'Popover', // Not a direct equivalent; but could be used with custom behavior. IconButton: 'Button', Image: 'Image', + Keytips: { component: 'Keytips', package: '@fluentui-contrib/react-keytips' }, Label: 'Label', Layer: 'Portal', Link: 'Link', @@ -131,43 +137,39 @@ const MIGRATIONS = { OverflowSet: 'Overflow', Overlay: 'Portal', Panel: 'Drawer', - Popup: 'Dialog', - PrimaryButton: 'Button', - Persona: 'Avatar', + PeoplePicker: 'TagPicker', + Persona: 'Persona', Pivot: 'TabList', PivotItem: 'Tab', ProgressIndicator: 'ProgressBar', + Rating: 'Rating', + SearchBox: 'SearchBox', Separator: 'Divider', Shimmer: 'Skeleton', Slider: 'Slider', SplitButton: 'SplitButton', SpinButton: 'SpinButton', Spinner: 'Spinner', + Stack: 'StackShim', + SwatchColorPicker: 'SwatchPicker', + TagPicker: 'TagPicker', + TeachingBubble: 'TeachingPopover', Text: 'Text', TextField: 'Input', + TimePicker: { component: 'TimePicker', package: '@fluentui/react-timepicker-compat' }, ToggleButton: 'ToggleButton', Toggle: 'Switch', Tooltip: 'Tooltip', - TooltipHost: 'Tooltip', - VerticalDivider: 'Divider', -} satisfies Record; - -/** - * Compatibility migrations for certain components. - * @see https://react.fluentui.dev/?path=/docs/concepts-migration-from-v8-component-mapping--page - */ -const COMPAT_MIGRATIONS = { - DatePicker: { component: 'DatePicker', package: '@fluentui/react-datepicker-compat' }, - TimePicker: { component: 'TeachingPopover', package: '@fluentui/react-timepicker-compat' }, } satisfies Record; /** * Preview migrations for certain components. - * @see https://react.fluentui.dev/?path=/docs/concepts-migration-from-v8-component-mapping--page + * @see https://react.fluentui.dev/?path=/docs/concepts-migration-from-v8-component-mapping--docs */ const PREVIEW_MIGRATIONS = { - Rating: { component: 'Rating', package: '@fluentui/react-rating-preview' }, - TeachingBubble: { component: 'TeachingPopover', package: '@fluentui/react-teaching-popover-preview' }, + ColorPicker: { component: 'ColorPicker', package: '@fluentui/react-color-picker-preview' }, + List: { component: 'List', package: '@fluentui/react-list-preview' }, + Virtualizer: { component: 'Virtualizer', package: '@fluentui/react-virtualizer-preview' }, } satisfies Record; /** @@ -177,16 +179,16 @@ const PREVIEW_MIGRATIONS = { */ const isMigration = (name: string): name is keyof typeof MIGRATIONS => name in MIGRATIONS; -/** - * Checks if a component name is in the COMPAT_MIGRATIONS list. - * @param name - The name of the component. - * @returns True if the component is in the COMPAT_MIGRATIONS list, false otherwise. - */ -const isCompatMigration = (name: string): name is keyof typeof COMPAT_MIGRATIONS => name in COMPAT_MIGRATIONS; - /** * Checks if a component name is in the PREVIEW_MIGRATIONS list. * @param name - The name of the component. * @returns True if the component is in the PREVIEW_MIGRATIONS list, false otherwise. */ const isPreviewMigration = (name: string): name is keyof typeof PREVIEW_MIGRATIONS => name in PREVIEW_MIGRATIONS; + +/** + * Get the component and package name to use for a migration. + */ +const getMigrationData = (migration: Migration) => { + return typeof migration === 'string' ? { component: migration, package: '@fluentui/react-components' } : migration; +}; From f36e15974dfd071600841c0f6eefb62cf264c5e8 Mon Sep 17 00:00:00 2001 From: Dmytro Kirpa Date: Thu, 12 Dec 2024 12:06:29 +0100 Subject: [PATCH 04/10] fix type-check issue and update api doc --- .../etc/eslint-plugin-react-components.api.md | 4 ++-- .../eslint-plugin-react-components/tsconfig.spec.json | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/react-components/eslint-plugin-react-components/etc/eslint-plugin-react-components.api.md b/packages/react-components/eslint-plugin-react-components/etc/eslint-plugin-react-components.api.md index 97074aa5c04be0..1c5903fb43dd25 100644 --- a/packages/react-components/eslint-plugin-react-components/etc/eslint-plugin-react-components.api.md +++ b/packages/react-components/eslint-plugin-react-components/etc/eslint-plugin-react-components.api.md @@ -22,8 +22,8 @@ export const plugin: { }; }; rules: { - "prefer-fluentui-v9": RuleModule<"replaceFluent8With9" | "replaceFluent8With9Unstable" | "replaceIconWithJsx" | "replaceStackWithFlex", { - unstable?: boolean | undefined; + "prefer-fluentui-v9": RuleModule<"replaceFluent8With9" | "replaceIconWithJsx" | "replaceStackWithFlex" | "replaceFocusZoneWithTabster", { + preview?: boolean | undefined; }[], unknown, RuleListener>; }; }; diff --git a/packages/react-components/eslint-plugin-react-components/tsconfig.spec.json b/packages/react-components/eslint-plugin-react-components/tsconfig.spec.json index 469fcba4d7ba75..18aba1c9375371 100644 --- a/packages/react-components/eslint-plugin-react-components/tsconfig.spec.json +++ b/packages/react-components/eslint-plugin-react-components/tsconfig.spec.json @@ -1,7 +1,8 @@ { "extends": "./tsconfig.json", "compilerOptions": { - "module": "CommonJS", + "module": "NodeNext", + "moduleResolution": "NodeNext", "outDir": "dist", "types": ["jest", "node"] }, From 1b5a7656f79f3872583708af090ccb700956e3ee Mon Sep 17 00:00:00 2001 From: Dmytro Kirpa Date: Thu, 12 Dec 2024 12:10:54 +0100 Subject: [PATCH 05/10] do not enforce to use preview components --- .../src/rules/prefer-fluentui-v9.spec.ts | 14 ------- .../src/rules/prefer-fluentui-v9.ts | 40 +------------------ 2 files changed, 2 insertions(+), 52 deletions(-) diff --git a/packages/react-components/eslint-plugin-react-components/src/rules/prefer-fluentui-v9.spec.ts b/packages/react-components/eslint-plugin-react-components/src/rules/prefer-fluentui-v9.spec.ts index be821113236e9f..22f79c07d8055a 100644 --- a/packages/react-components/eslint-plugin-react-components/src/rules/prefer-fluentui-v9.spec.ts +++ b/packages/react-components/eslint-plugin-react-components/src/rules/prefer-fluentui-v9.spec.ts @@ -17,10 +17,6 @@ ruleTester.run(RULE_NAME, rule, { { code: `import { Button } from '@fluentui/react-components';`, }, - { - code: `import { ColorPicker } from '@fluentui/react';`, - options: [{ preview: false }], - }, ], invalid: [ { @@ -40,15 +36,5 @@ ruleTester.run(RULE_NAME, rule, { }, ], }, - { - code: `import { List } from '@fluentui/react';`, - options: [{ preview: true }], - errors: [ - { - messageId: 'replaceFluent8With9', - data: { fluent8: 'List', fluent9: 'List', package: '@fluentui/react-list-preview' }, - }, - ], - }, ], }); diff --git a/packages/react-components/eslint-plugin-react-components/src/rules/prefer-fluentui-v9.ts b/packages/react-components/eslint-plugin-react-components/src/rules/prefer-fluentui-v9.ts index ae7d79ee4819ce..125a4b564de9c3 100644 --- a/packages/react-components/eslint-plugin-react-components/src/rules/prefer-fluentui-v9.ts +++ b/packages/react-components/eslint-plugin-react-components/src/rules/prefer-fluentui-v9.ts @@ -2,10 +2,7 @@ import { createRule } from './utils/create-rule'; export const RULE_NAME = 'prefer-fluentui-v9'; -type Options = Array<{ - /** Whether to enforce Fluent UI v9 preview component migrations. */ - preview?: boolean; -}>; +type Options = Array<{}>; type MessageIds = 'replaceFluent8With9' | 'replaceIconWithJsx' | 'replaceStackWithFlex' | 'replaceFocusZoneWithTabster'; @@ -19,10 +16,7 @@ export const rule = createRule({ schema: [ { type: 'object', - properties: { - preview: { type: 'boolean' }, - }, - additionalProperties: false, + properties: {}, }, ], messages: { @@ -34,8 +28,6 @@ export const rule = createRule({ }, defaultOptions: [], create(context) { - const { preview = false } = context.options[0] ?? {}; - return { // eslint-disable-next-line @typescript-eslint/naming-convention ImportDeclaration(node) { @@ -71,18 +63,7 @@ export const rule = createRule({ package: migration.package, }, }); - } else if (isPreviewMigration(name) && preview) { - context.report({ - node, - messageId: 'replaceFluent8With9', - data: { - fluent8: name, - fluent9: PREVIEW_MIGRATIONS[name].component, - package: PREVIEW_MIGRATIONS[name].package, - }, - }); } - break; } } } @@ -162,16 +143,6 @@ const MIGRATIONS = { Tooltip: 'Tooltip', } satisfies Record; -/** - * Preview migrations for certain components. - * @see https://react.fluentui.dev/?path=/docs/concepts-migration-from-v8-component-mapping--docs - */ -const PREVIEW_MIGRATIONS = { - ColorPicker: { component: 'ColorPicker', package: '@fluentui/react-color-picker-preview' }, - List: { component: 'List', package: '@fluentui/react-list-preview' }, - Virtualizer: { component: 'Virtualizer', package: '@fluentui/react-virtualizer-preview' }, -} satisfies Record; - /** * Checks if a component name is in the MIGRATIONS list. * @param name - The name of the component. @@ -179,13 +150,6 @@ const PREVIEW_MIGRATIONS = { */ const isMigration = (name: string): name is keyof typeof MIGRATIONS => name in MIGRATIONS; -/** - * Checks if a component name is in the PREVIEW_MIGRATIONS list. - * @param name - The name of the component. - * @returns True if the component is in the PREVIEW_MIGRATIONS list, false otherwise. - */ -const isPreviewMigration = (name: string): name is keyof typeof PREVIEW_MIGRATIONS => name in PREVIEW_MIGRATIONS; - /** * Get the component and package name to use for a migration. */ From 6c2e6cffd54b431a6124c0bf7190259ffcdd9a03 Mon Sep 17 00:00:00 2001 From: Dmytro Kirpa Date: Fri, 13 Dec 2024 09:45:09 +0100 Subject: [PATCH 06/10] review feedback --- .../eslint-plugin-react-components/README.md | 4 ---- .../etc/eslint-plugin-react-components.api.md | 4 +--- .../src/rules/prefer-fluentui-v9.ts | 7 +------ 3 files changed, 2 insertions(+), 13 deletions(-) diff --git a/packages/react-components/eslint-plugin-react-components/README.md b/packages/react-components/eslint-plugin-react-components/README.md index b53b66bcdd2a1d..0b22544de65533 100644 --- a/packages/react-components/eslint-plugin-react-components/README.md +++ b/packages/react-components/eslint-plugin-react-components/README.md @@ -57,10 +57,6 @@ module.exports = { This rule ensures the use of Fluent UI v9 counterparts for Fluent UI v8 components. -#### Options - -- `unstable` (boolean): Whether to enforce Fluent UI v9 preview component migrations. - #### Examples **✅ Do** diff --git a/packages/react-components/eslint-plugin-react-components/etc/eslint-plugin-react-components.api.md b/packages/react-components/eslint-plugin-react-components/etc/eslint-plugin-react-components.api.md index 1c5903fb43dd25..df77b926b27757 100644 --- a/packages/react-components/eslint-plugin-react-components/etc/eslint-plugin-react-components.api.md +++ b/packages/react-components/eslint-plugin-react-components/etc/eslint-plugin-react-components.api.md @@ -22,9 +22,7 @@ export const plugin: { }; }; rules: { - "prefer-fluentui-v9": RuleModule<"replaceFluent8With9" | "replaceIconWithJsx" | "replaceStackWithFlex" | "replaceFocusZoneWithTabster", { - preview?: boolean | undefined; - }[], unknown, RuleListener>; + "prefer-fluentui-v9": RuleModule<"replaceFluent8With9" | "replaceIconWithJsx" | "replaceStackWithFlex" | "replaceFocusZoneWithTabster", {}[], unknown, RuleListener>; }; }; diff --git a/packages/react-components/eslint-plugin-react-components/src/rules/prefer-fluentui-v9.ts b/packages/react-components/eslint-plugin-react-components/src/rules/prefer-fluentui-v9.ts index 125a4b564de9c3..a2f1002d3f097b 100644 --- a/packages/react-components/eslint-plugin-react-components/src/rules/prefer-fluentui-v9.ts +++ b/packages/react-components/eslint-plugin-react-components/src/rules/prefer-fluentui-v9.ts @@ -13,12 +13,7 @@ export const rule = createRule({ docs: { description: 'This rule ensures the use of Fluent UI v9 counterparts for Fluent UI v8 components.', }, - schema: [ - { - type: 'object', - properties: {}, - }, - ], + schema: [], messages: { replaceFluent8With9: `Avoid importing {{ fluent8 }} from '@fluentui/react', as this package has started migration to Fluent UI 9. Import {{ fluent9 }} from '{{ package }}' instead.`, replaceIconWithJsx: `Avoid using Icon from '@fluentui/react', as this package has already migrated to Fluent UI 9. Use a JSX SVG icon from '@fluentui/react-icons' instead.`, From 36d5675d0ccb96cb75c8b7353ee6e9718596e586 Mon Sep 17 00:00:00 2001 From: Dmytro Kirpa Date: Fri, 13 Dec 2024 09:51:33 +0100 Subject: [PATCH 07/10] refactor --- .../src/rules/prefer-fluentui-v9.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/react-components/eslint-plugin-react-components/src/rules/prefer-fluentui-v9.ts b/packages/react-components/eslint-plugin-react-components/src/rules/prefer-fluentui-v9.ts index a2f1002d3f097b..f486ae42a41f29 100644 --- a/packages/react-components/eslint-plugin-react-components/src/rules/prefer-fluentui-v9.ts +++ b/packages/react-components/eslint-plugin-react-components/src/rules/prefer-fluentui-v9.ts @@ -47,7 +47,7 @@ export const rule = createRule({ break; default: if (isMigration(name)) { - const migration = getMigrationData(MIGRATIONS[name]); + const migration = getMigrationDetails(MIGRATIONS[name]); context.report({ node, @@ -148,6 +148,10 @@ const isMigration = (name: string): name is keyof typeof MIGRATIONS => name in M /** * Get the component and package name to use for a migration. */ -const getMigrationData = (migration: Migration) => { - return typeof migration === 'string' ? { component: migration, package: '@fluentui/react-components' } : migration; +const getMigrationDetails = (migration: Migration) => { + if (typeof migration === 'string') { + return { component: migration, package: '@fluentui/react-components' }; + } + + return migration; }; From a59dca90b40fa2eea4fe88a7bf405c45fc1770b4 Mon Sep 17 00:00:00 2001 From: Dmytro Kirpa Date: Tue, 31 Dec 2024 13:10:03 +0100 Subject: [PATCH 08/10] use enum instead of hardcoded literals --- .../src/rules/prefer-fluentui-v9.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/react-components/eslint-plugin-react-components/src/rules/prefer-fluentui-v9.ts b/packages/react-components/eslint-plugin-react-components/src/rules/prefer-fluentui-v9.ts index f486ae42a41f29..b248947d40de9c 100644 --- a/packages/react-components/eslint-plugin-react-components/src/rules/prefer-fluentui-v9.ts +++ b/packages/react-components/eslint-plugin-react-components/src/rules/prefer-fluentui-v9.ts @@ -1,3 +1,5 @@ +import { AST_NODE_TYPES } from '@typescript-eslint/utils'; + import { createRule } from './utils/create-rule'; export const RULE_NAME = 'prefer-fluentui-v9'; @@ -31,7 +33,10 @@ export const rule = createRule({ } for (const specifier of node.specifiers) { - if (specifier.type === 'ImportSpecifier' && specifier.imported.type === 'Identifier') { + if ( + specifier.type === AST_NODE_TYPES.ImportSpecifier && + specifier.imported.type === AST_NODE_TYPES.Identifier + ) { const name = specifier.imported.name; switch (name) { From 13b62f8d6dffb65712fbf4fabf98943ba5449309 Mon Sep 17 00:00:00 2001 From: Dmytro Kirpa Date: Thu, 2 Jan 2025 16:08:58 +0100 Subject: [PATCH 09/10] adress review feedback --- ...-1c2bc691-c857-43cf-9806-ffe8d0d058c0.json | 4 +- .../.eslintrc.json | 10 +- .../src/rules/prefer-fluentui-v9.ts | 144 ++++++++---------- 3 files changed, 76 insertions(+), 82 deletions(-) diff --git a/change/@fluentui-eslint-plugin-react-components-1c2bc691-c857-43cf-9806-ffe8d0d058c0.json b/change/@fluentui-eslint-plugin-react-components-1c2bc691-c857-43cf-9806-ffe8d0d058c0.json index 5c360689a6808b..5459e40ce0c429 100644 --- a/change/@fluentui-eslint-plugin-react-components-1c2bc691-c857-43cf-9806-ffe8d0d058c0.json +++ b/change/@fluentui-eslint-plugin-react-components-1c2bc691-c857-43cf-9806-ffe8d0d058c0.json @@ -1,7 +1,7 @@ { - "type": "minor", + "type": "patch", "comment": "feat: add prefer-fluentui-v9 rule", "packageName": "@fluentui/eslint-plugin-react-components", "email": "dmytrokirpa@microsoft.com", - "dependentChangeType": "patch" + "dependentChangeType": "none" } diff --git a/packages/react-components/eslint-plugin-react-components/.eslintrc.json b/packages/react-components/eslint-plugin-react-components/.eslintrc.json index 63b4e8d8a8963a..700ea69cb4542d 100644 --- a/packages/react-components/eslint-plugin-react-components/.eslintrc.json +++ b/packages/react-components/eslint-plugin-react-components/.eslintrc.json @@ -1,5 +1,13 @@ { "extends": ["plugin:@fluentui/eslint-plugin/node", "plugin:eslint-plugin/recommended"], "plugins": ["eslint-plugin"], - "root": true + "root": true, + "overrides": [ + { + "files": ["src/rules/*.ts"], + "rules": { + "@typescript-eslint/naming-convention": "off" + } + } + ] } diff --git a/packages/react-components/eslint-plugin-react-components/src/rules/prefer-fluentui-v9.ts b/packages/react-components/eslint-plugin-react-components/src/rules/prefer-fluentui-v9.ts index b248947d40de9c..bf43866a6e90dd 100644 --- a/packages/react-components/eslint-plugin-react-components/src/rules/prefer-fluentui-v9.ts +++ b/packages/react-components/eslint-plugin-react-components/src/rules/prefer-fluentui-v9.ts @@ -26,7 +26,6 @@ export const rule = createRule({ defaultOptions: [], create(context) { return { - // eslint-disable-next-line @typescript-eslint/naming-convention ImportDeclaration(node) { if (node.source.value !== '@fluentui/react') { return; @@ -52,14 +51,14 @@ export const rule = createRule({ break; default: if (isMigration(name)) { - const migration = getMigrationDetails(MIGRATIONS[name]); + const migration = MIGRATIONS[name]; context.report({ node, messageId: 'replaceFluent8With9', data: { fluent8: name, - fluent9: migration.component, + fluent9: migration.import, package: migration.package, }, }); @@ -72,76 +71,74 @@ export const rule = createRule({ }, }); -type Migration = string | { component: string; package: string }; - /** * Migrations from Fluent 8 components to Fluent 9 components. * @see https://react.fluentui.dev/?path=/docs/concepts-migration-from-v8-component-mapping--docs */ const MIGRATIONS = { - makeStyles: 'makeStyles', - ActionButton: 'Button', - Announced: 'useAnnounce', - Breadcrumb: 'Breadcrumb', - Button: 'Button', - Callout: 'Popover', - Calendar: { component: 'Calendar', package: '@fluentui/react-calendar-compat' }, - CommandBar: 'Toolbar', - CommandBarButton: 'Toolbar', - CommandButton: 'MenuButton', - CompoundButton: 'CompoundButton', - Checkbox: 'Checkbox', - ChoiceGroup: 'RadioGroup', - Coachmark: 'TeachingPopover', - ComboBox: 'Combobox', - ContextualMenu: 'Menu', - DefaultButton: 'Button', - DatePicker: { component: 'DatePicker', package: '@fluentui/react-datepicker-compat' }, - DetailsList: 'DataGrid', - Dialog: 'Dialog', - DocumentCard: 'Card', - Dropdown: 'Dropdown', - Fabric: 'FluentProvider', - Facepile: 'AvatarGroup', - FocusTrapZone: 'Tabster', - FocusZone: 'Tabster', - GroupedList: 'Tree', - HoverCard: 'Popover', // Not a direct equivalent; but could be used with custom behavior. - IconButton: 'Button', - Image: 'Image', - Keytips: { component: 'Keytips', package: '@fluentui-contrib/react-keytips' }, - Label: 'Label', - Layer: 'Portal', - Link: 'Link', - MessageBar: 'MessageBar', - Modal: 'Dialog', - OverflowSet: 'Overflow', - Overlay: 'Portal', - Panel: 'Drawer', - PeoplePicker: 'TagPicker', - Persona: 'Persona', - Pivot: 'TabList', - PivotItem: 'Tab', - ProgressIndicator: 'ProgressBar', - Rating: 'Rating', - SearchBox: 'SearchBox', - Separator: 'Divider', - Shimmer: 'Skeleton', - Slider: 'Slider', - SplitButton: 'SplitButton', - SpinButton: 'SpinButton', - Spinner: 'Spinner', - Stack: 'StackShim', - SwatchColorPicker: 'SwatchPicker', - TagPicker: 'TagPicker', - TeachingBubble: 'TeachingPopover', - Text: 'Text', - TextField: 'Input', - TimePicker: { component: 'TimePicker', package: '@fluentui/react-timepicker-compat' }, - ToggleButton: 'ToggleButton', - Toggle: 'Switch', - Tooltip: 'Tooltip', -} satisfies Record; + makeStyles: { import: 'makeStyles', package: '@fluentui/react-components' }, + ActionButton: { import: 'Button', package: '@fluentui/react-components' }, + Announced: { import: 'useAnnounce', package: '@fluentui/react-components' }, + Breadcrumb: { import: 'Breadcrumb', package: '@fluentui/react-components' }, + Button: { import: 'Button', package: '@fluentui/react-components' }, + Callout: { import: 'Popover', package: '@fluentui/react-components' }, + Calendar: { import: 'Calendar', package: '@fluentui/react-calendar-compat' }, + CommandBar: { import: 'Toolbar', package: '@fluentui/react-components' }, + CommandBarButton: { import: 'Toolbar', package: '@fluentui/react-components' }, + CommandButton: { import: 'MenuButton', package: '@fluentui/react-components' }, + CompoundButton: { import: 'CompoundButton', package: '@fluentui/react-components' }, + Checkbox: { import: 'Checkbox', package: '@fluentui/react-components' }, + ChoiceGroup: { import: 'RadioGroup', package: '@fluentui/react-components' }, + Coachmark: { import: 'TeachingPopover', package: '@fluentui/react-components' }, + ComboBox: { import: 'Combobox', package: '@fluentui/react-components' }, + ContextualMenu: { import: 'Menu', package: '@fluentui/react-components' }, + DefaultButton: { import: 'Button', package: '@fluentui/react-components' }, + DatePicker: { import: 'DatePicker', package: '@fluentui/react-datepicker-compat' }, + DetailsList: { import: 'DataGrid', package: '@fluentui/react-components' }, + Dialog: { import: 'Dialog', package: '@fluentui/react-components' }, + DocumentCard: { import: 'Card', package: '@fluentui/react-components' }, + Dropdown: { import: 'Dropdown', package: '@fluentui/react-components' }, + Fabric: { import: 'FluentProvider', package: '@fluentui/react-components' }, + Facepile: { import: 'AvatarGroup', package: '@fluentui/react-components' }, + FocusTrapZone: { import: 'Tabster', package: '@fluentui/react-components' }, + FocusZone: { import: 'Tabster', package: '@fluentui/react-components' }, + GroupedList: { import: 'Tree', package: '@fluentui/react-components' }, + HoverCard: { import: 'Popover', package: '@fluentui/react-components' }, // Not a direct equivalent; but could be used with custom behavior. + IconButton: { import: 'Button', package: '@fluentui/react-components' }, + Image: { import: 'Image', package: '@fluentui/react-components' }, + Keytips: { import: 'Keytips', package: '@fluentui-contrib/react-keytips' }, + Label: { import: 'Label', package: '@fluentui/react-components' }, + Layer: { import: 'Portal', package: '@fluentui/react-components' }, + Link: { import: 'Link', package: '@fluentui/react-components' }, + MessageBar: { import: 'MessageBar', package: '@fluentui/react-components' }, + Modal: { import: 'Dialog', package: '@fluentui/react-components' }, + OverflowSet: { import: 'Overflow', package: '@fluentui/react-components' }, + Overlay: { import: 'Portal', package: '@fluentui/react-components' }, + Panel: { import: 'Drawer', package: '@fluentui/react-components' }, + PeoplePicker: { import: 'TagPicker', package: '@fluentui/react-components' }, + Persona: { import: 'Persona', package: '@fluentui/react-components' }, + Pivot: { import: 'TabList', package: '@fluentui/react-components' }, + PivotItem: { import: 'Tab', package: '@fluentui/react-components' }, + ProgressIndicator: { import: 'ProgressBar', package: '@fluentui/react-components' }, + Rating: { import: 'Rating', package: '@fluentui/react-components' }, + SearchBox: { import: 'SearchBox', package: '@fluentui/react-components' }, + Separator: { import: 'Divider', package: '@fluentui/react-components' }, + Shimmer: { import: 'Skeleton', package: '@fluentui/react-components' }, + Slider: { import: 'Slider', package: '@fluentui/react-components' }, + SplitButton: { import: 'SplitButton', package: '@fluentui/react-components' }, + SpinButton: { import: 'SpinButton', package: '@fluentui/react-components' }, + Spinner: { import: 'Spinner', package: '@fluentui/react-components' }, + Stack: { import: 'StackShim', package: '@fluentui/react-components' }, + SwatchColorPicker: { import: 'SwatchPicker', package: '@fluentui/react-components' }, + TagPicker: { import: 'TagPicker', package: '@fluentui/react-components' }, + TeachingBubble: { import: 'TeachingPopover', package: '@fluentui/react-components' }, + Text: { import: 'Text', package: '@fluentui/react-components' }, + TextField: { import: 'Input', package: '@fluentui/react-components' }, + TimePicker: { import: 'TimePicker', package: '@fluentui/react-timepicker-compat' }, + ToggleButton: { import: 'ToggleButton', package: '@fluentui/react-components' }, + Toggle: { import: 'Switch', package: '@fluentui/react-components' }, + Tooltip: { import: 'Tooltip', package: '@fluentui/react-components' }, +}; /** * Checks if a component name is in the MIGRATIONS list. @@ -149,14 +146,3 @@ const MIGRATIONS = { * @returns True if the component is in the MIGRATIONS list, false otherwise. */ const isMigration = (name: string): name is keyof typeof MIGRATIONS => name in MIGRATIONS; - -/** - * Get the component and package name to use for a migration. - */ -const getMigrationDetails = (migration: Migration) => { - if (typeof migration === 'string') { - return { component: migration, package: '@fluentui/react-components' }; - } - - return migration; -}; From 4e1f423bd89a6c3ccbe1ab2b70198ae6332c5075 Mon Sep 17 00:00:00 2001 From: Dmytro Kirpa Date: Tue, 7 Jan 2025 14:43:31 +0100 Subject: [PATCH 10/10] remove the rule from recommended preset --- .../etc/eslint-plugin-react-components.api.md | 4 +--- .../eslint-plugin-react-components/src/index.ts | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/react-components/eslint-plugin-react-components/etc/eslint-plugin-react-components.api.md b/packages/react-components/eslint-plugin-react-components/etc/eslint-plugin-react-components.api.md index df77b926b27757..d4416ee6720ef5 100644 --- a/packages/react-components/eslint-plugin-react-components/etc/eslint-plugin-react-components.api.md +++ b/packages/react-components/eslint-plugin-react-components/etc/eslint-plugin-react-components.api.md @@ -16,9 +16,7 @@ export const plugin: { configs: { recommended: { plugins: string[]; - rules: { - "@fluentui/react-components/prefer-fluentui-v9": string; - }; + rules: {}; }; }; rules: { diff --git a/packages/react-components/eslint-plugin-react-components/src/index.ts b/packages/react-components/eslint-plugin-react-components/src/index.ts index b8860ee09fe63d..e3ed57c99b71fd 100644 --- a/packages/react-components/eslint-plugin-react-components/src/index.ts +++ b/packages/react-components/eslint-plugin-react-components/src/index.ts @@ -9,7 +9,7 @@ const configs = { recommended: { plugins: [name], rules: { - [`@fluentui/react-components/${preferFluentUIV9Name}`]: 'warn', + // add all recommended rules here }, }, };