diff --git a/packages/eslint-plugin-pf-codemods/src/rules/helpers/attributeValueIsString.ts b/packages/eslint-plugin-pf-codemods/src/rules/helpers/attributeValueIsString.ts new file mode 100644 index 000000000..649f2166e --- /dev/null +++ b/packages/eslint-plugin-pf-codemods/src/rules/helpers/attributeValueIsString.ts @@ -0,0 +1,12 @@ +import { JSXAttribute } from "estree-jsx"; + +/** Checks if an attribute value is a string, either as a Literal: + * or a Literal in an expression container: */ +export function attributeValueIsString(value: JSXAttribute["value"]) { + return ( + (value?.type === "Literal" && typeof value.value === "string") || + (value?.type === "JSXExpressionContainer" && + value.expression.type === "Literal" && + typeof value.expression.value === "string") + ); +} diff --git a/packages/eslint-plugin-pf-codemods/src/rules/helpers/index.ts b/packages/eslint-plugin-pf-codemods/src/rules/helpers/index.ts index 314f1deb5..4260022ca 100644 --- a/packages/eslint-plugin-pf-codemods/src/rules/helpers/index.ts +++ b/packages/eslint-plugin-pf-codemods/src/rules/helpers/index.ts @@ -1,3 +1,4 @@ +export * from "./attributeValueIsString"; export * from "./childrenIsEmpty"; export * from "./contextReports"; export * from "./findAncestor"; diff --git a/packages/eslint-plugin-pf-codemods/src/rules/v6/toolbarItemReplaceChipGroupVariant/toolbarItem-replace-chipGroup-variant.md b/packages/eslint-plugin-pf-codemods/src/rules/v6/toolbarItemReplaceChipGroupVariant/toolbarItem-replace-chipGroup-variant.md deleted file mode 100644 index ce17d3afd..000000000 --- a/packages/eslint-plugin-pf-codemods/src/rules/v6/toolbarItemReplaceChipGroupVariant/toolbarItem-replace-chipGroup-variant.md +++ /dev/null @@ -1,17 +0,0 @@ -### toolbarItem-replace-chipGroup-variant [(#10649)](https://github.com/patternfly/patternfly-react/pull/10649) - -The "chip-group" variant for ToolbarItem has been replaced with "label-group". - -#### Examples - -In: - -```jsx -%inputExample% -``` - -Out: - -```jsx -%outputExample% -``` diff --git a/packages/eslint-plugin-pf-codemods/src/rules/v6/toolbarItemReplaceChipGroupVariant/toolbarItem-replace-chipGroup-variant.ts b/packages/eslint-plugin-pf-codemods/src/rules/v6/toolbarItemReplaceChipGroupVariant/toolbarItem-replace-chipGroup-variant.ts deleted file mode 100644 index 13d2e094a..000000000 --- a/packages/eslint-plugin-pf-codemods/src/rules/v6/toolbarItemReplaceChipGroupVariant/toolbarItem-replace-chipGroup-variant.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { Rule } from "eslint"; -import { JSXOpeningElement, MemberExpression, Identifier } from "estree-jsx"; -import { getFromPackage, getAttribute, getAttributeValue } from "../../helpers"; - -// https://github.com/patternfly/patternfly-react/pull/10649 -module.exports = { - meta: { fixable: "code" }, - create: function (context: Rule.RuleContext) { - const { imports } = getFromPackage(context, "@patternfly/react-core"); - - const componentImport = imports.find( - (specifier) => specifier.imported.name === "ToolbarItem" - ); - const enumImport = imports.find( - (specifier) => specifier.imported.name === "ToolbarItemVariant" - ); - - return !componentImport - ? {} - : { - MemberExpression(node: MemberExpression) { - if ( - enumImport && - (node.object as Identifier).name === enumImport.local.name && - node.property.type === "Literal" && - node.property.value === "chip-group" - ) { - context.report({ - node, - message: - 'The "chip-group" variant for ToolbarItem has been replaced with "label-group".', - fix(fixer) { - return fixer.replaceText(node.property, '"label-group"'); - }, - }); - } - }, - JSXOpeningElement(node: JSXOpeningElement) { - if ( - node.name.type === "JSXIdentifier" && - componentImport.local.name === node.name.name - ) { - const variant = getAttribute(node, "variant"); - if (!variant || !variant.value) { - return; - } - - const variantValue = getAttributeValue(context, variant.value); - - if ( - variant.value.type === "Literal" && - variantValue === "chip-group" - ) { - context.report({ - node, - message: - 'The "chip-group" variant for ToolbarItem has been replaced with "label-group".', - fix(fixer) { - return fixer.replaceText(variant, `variant="label-group"`); - }, - }); - } - } - }, - }; - }, -}; diff --git a/packages/eslint-plugin-pf-codemods/src/rules/v6/toolbarItemReplaceChipGroupVariant/toolbarItemReplaceChipGroupVariantInput.tsx b/packages/eslint-plugin-pf-codemods/src/rules/v6/toolbarItemReplaceChipGroupVariant/toolbarItemReplaceChipGroupVariantInput.tsx deleted file mode 100644 index dfbbef496..000000000 --- a/packages/eslint-plugin-pf-codemods/src/rules/v6/toolbarItemReplaceChipGroupVariant/toolbarItemReplaceChipGroupVariantInput.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import { ToolbarItem } from "@patternfly/react-core"; - -export const ToolbarItemReplaceChipGroupVariantInput = () => ( - -); diff --git a/packages/eslint-plugin-pf-codemods/src/rules/v6/toolbarItemReplaceChipGroupVariant/toolbarItemReplaceChipGroupVariantOutput.tsx b/packages/eslint-plugin-pf-codemods/src/rules/v6/toolbarItemReplaceChipGroupVariant/toolbarItemReplaceChipGroupVariantOutput.tsx deleted file mode 100644 index cbc5da28e..000000000 --- a/packages/eslint-plugin-pf-codemods/src/rules/v6/toolbarItemReplaceChipGroupVariant/toolbarItemReplaceChipGroupVariantOutput.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import { ToolbarItem } from "@patternfly/react-core"; - -export const ToolbarItemReplaceChipGroupVariantInput = () => ( - -); diff --git a/packages/eslint-plugin-pf-codemods/src/rules/v6/toolbarItemVariantPropUpdates/toolbarItem-variant-prop-updates.md b/packages/eslint-plugin-pf-codemods/src/rules/v6/toolbarItemVariantPropUpdates/toolbarItem-variant-prop-updates.md new file mode 100644 index 000000000..5947c8c74 --- /dev/null +++ b/packages/eslint-plugin-pf-codemods/src/rules/v6/toolbarItemVariantPropUpdates/toolbarItem-variant-prop-updates.md @@ -0,0 +1,17 @@ +### toolbarItem-variant-prop-updates [(#10649)](https://github.com/patternfly/patternfly-react/pull/10649) + +The variant prop for ToolbarItem has been updated: "bulk-select", "overflow-menu" and "search-filter" were removed and "chip-group" was renamed to "label-group". + +#### Examples + +In: + +```jsx +%inputExample% +``` + +Out: + +```jsx +%outputExample% +``` diff --git a/packages/eslint-plugin-pf-codemods/src/rules/v6/toolbarItemReplaceChipGroupVariant/toolbarItem-replace-chipGroup-variant.test.ts b/packages/eslint-plugin-pf-codemods/src/rules/v6/toolbarItemVariantPropUpdates/toolbarItem-variant-prop-updates.test.ts similarity index 52% rename from packages/eslint-plugin-pf-codemods/src/rules/v6/toolbarItemReplaceChipGroupVariant/toolbarItem-replace-chipGroup-variant.test.ts rename to packages/eslint-plugin-pf-codemods/src/rules/v6/toolbarItemVariantPropUpdates/toolbarItem-variant-prop-updates.test.ts index 6f6081707..0d84e27b2 100644 --- a/packages/eslint-plugin-pf-codemods/src/rules/v6/toolbarItemReplaceChipGroupVariant/toolbarItem-replace-chipGroup-variant.test.ts +++ b/packages/eslint-plugin-pf-codemods/src/rules/v6/toolbarItemVariantPropUpdates/toolbarItem-variant-prop-updates.test.ts @@ -1,5 +1,5 @@ const ruleTester = require("../../ruletester"); -import * as rule from "./toolbarItem-replace-chipGroup-variant"; +import * as rule from "./toolbarItem-variant-prop-updates"; ruleTester.run("toolbarItem-replace-chipGroup-variant", rule, { valid: [ @@ -30,6 +30,16 @@ ruleTester.run("toolbarItem-replace-chipGroup-variant", rule, { }, ], }, + { + code: `import { ToolbarItem } from '@patternfly/react-core'; `, + output: `import { ToolbarItem } from '@patternfly/react-core'; `, + errors: [ + { + message: `The "chip-group" variant for ToolbarItem has been replaced with "label-group".`, + type: "JSXOpeningElement", + }, + ], + }, { code: `import { ToolbarItem as CustomItem } from '@patternfly/react-core'; `, output: `import { ToolbarItem as CustomItem } from '@patternfly/react-core'; `, @@ -90,5 +100,86 @@ ruleTester.run("toolbarItem-replace-chipGroup-variant", rule, { }, ], }, + { + code: `import { ToolbarItem } from '@patternfly/react-core'; `, + output: `import { ToolbarItem } from '@patternfly/react-core'; `, + errors: [ + { + message: `The "bulk-select" variant for ToolbarItem has been removed.`, + type: "JSXOpeningElement", + }, + ], + }, + { + code: `import { ToolbarItem } from '@patternfly/react-core'; `, + output: `import { ToolbarItem } from '@patternfly/react-core'; `, + errors: [ + { + message: `The "overflow-menu" variant for ToolbarItem has been removed.`, + type: "JSXOpeningElement", + }, + ], + }, + { + code: `import { ToolbarItem } from '@patternfly/react-core'; `, + output: `import { ToolbarItem } from '@patternfly/react-core'; `, + errors: [ + { + message: `The "search-filter" variant for ToolbarItem has been removed.`, + type: "JSXOpeningElement", + }, + ], + }, + { + code: `import { ToolbarItem } from '@patternfly/react-core'; `, + output: `import { ToolbarItem } from '@patternfly/react-core'; `, + errors: [ + { + message: `The "bulk-select" variant for ToolbarItem has been removed.`, + type: "JSXOpeningElement", + }, + ], + }, + { + code: `import { ToolbarItem, ToolbarItemVariant } from '@patternfly/react-core'; `, + output: `import { ToolbarItem, ToolbarItemVariant } from '@patternfly/react-core'; `, + errors: [ + { + message: `The "bulk-select" variant for ToolbarItem has been removed.`, + type: "JSXOpeningElement", + }, + { + message: `The "bulk-select" variant for ToolbarItem has been removed.`, + type: "MemberExpression", + }, + ], + }, + { + code: `import { ToolbarItem, ToolbarItemVariant } from '@patternfly/react-core'; + const variant = ToolbarItemVariant["bulk-select"]; `, + output: `import { ToolbarItem, ToolbarItemVariant } from '@patternfly/react-core'; + const variant = ToolbarItemVariant["bulk-select"]; `, + errors: [ + { + message: `The "bulk-select" variant for ToolbarItem has been removed.`, + type: "MemberExpression", + }, + { + message: `The "bulk-select" variant for ToolbarItem has been removed.`, + type: "JSXOpeningElement", + }, + ], + }, + // error without the option to fix + { + code: `import { ToolbarItem, ToolbarItemVariant } from '@patternfly/react-core'; const variant = ToolbarItemVariant["bulk-select"];`, + output: `import { ToolbarItem, ToolbarItemVariant } from '@patternfly/react-core'; const variant = ToolbarItemVariant["bulk-select"];`, + errors: [ + { + message: `The "bulk-select" variant for ToolbarItem has been removed.`, + type: "MemberExpression", + }, + ], + }, ], }); diff --git a/packages/eslint-plugin-pf-codemods/src/rules/v6/toolbarItemVariantPropUpdates/toolbarItem-variant-prop-updates.ts b/packages/eslint-plugin-pf-codemods/src/rules/v6/toolbarItemVariantPropUpdates/toolbarItem-variant-prop-updates.ts new file mode 100644 index 000000000..ccd82f0c8 --- /dev/null +++ b/packages/eslint-plugin-pf-codemods/src/rules/v6/toolbarItemVariantPropUpdates/toolbarItem-variant-prop-updates.ts @@ -0,0 +1,111 @@ +import { Rule } from "eslint"; +import { + JSXOpeningElement, + MemberExpression, + Identifier, + Literal, +} from "estree-jsx"; +import { + getFromPackage, + getAttribute, + getAttributeValue, + attributeValueIsString, +} from "../../helpers"; + +// https://github.com/patternfly/patternfly-react/pull/10649 +module.exports = { + meta: { fixable: "code" }, + create: function (context: Rule.RuleContext) { + const { imports } = getFromPackage(context, "@patternfly/react-core"); + + const componentImport = imports.find( + (specifier) => specifier.imported.name === "ToolbarItem" + ); + const enumImport = imports.find( + (specifier) => specifier.imported.name === "ToolbarItemVariant" + ); + + const variantsToRemove = ["bulk-select", "overflow-menu", "search-filter"]; + + const nodeIsEnum = (node: MemberExpression) => + enumImport && + node.object && + node.property && + (node.object as Identifier).name === enumImport.local.name && + node.property.type === "Literal"; + + return !componentImport + ? {} + : { + MemberExpression(node: MemberExpression) { + if (nodeIsEnum(node)) { + const variantValue = (node.property as Literal).value as string; + + if (variantValue === "chip-group") { + context.report({ + node, + message: + 'The "chip-group" variant for ToolbarItem has been replaced with "label-group".', + fix(fixer) { + return fixer.replaceText(node.property, '"label-group"'); + }, + }); + } + + if (variantsToRemove.includes(variantValue)) { + context.report({ + node, + message: `The "${variantValue}" variant for ToolbarItem has been removed.`, + }); + } + } + }, + JSXOpeningElement(node: JSXOpeningElement) { + if ( + node.name.type === "JSXIdentifier" && + componentImport.local.name === node.name.name + ) { + const variant = getAttribute(node, "variant"); + if (!variant || !variant.value) { + return; + } + + const variantValue = getAttributeValue(context, variant.value); + + const variantValueIsLiteral = attributeValueIsString( + variant.value + ); + + if ( + (variantValueIsLiteral && + variantsToRemove.includes(variantValue)) || + (nodeIsEnum(variantValue) && + variantsToRemove.includes(variantValue.property.value)) + ) { + const variantToRemove = + variantValue.property?.value ?? variantValue; + + context.report({ + node, + message: `The "${variantToRemove}" variant for ToolbarItem has been removed.`, + fix(fixer) { + return fixer.remove(variant); + }, + }); + } + + if (variantValueIsLiteral && variantValue === "chip-group") { + context.report({ + node, + message: + 'The "chip-group" variant for ToolbarItem has been replaced with "label-group".', + fix(fixer) { + return fixer.replaceText(variant, `variant="label-group"`); + }, + }); + } + } + }, + }; + }, +}; diff --git a/packages/eslint-plugin-pf-codemods/src/rules/v6/toolbarItemVariantPropUpdates/toolbarItemVariantPropUpdatesInput.tsx b/packages/eslint-plugin-pf-codemods/src/rules/v6/toolbarItemVariantPropUpdates/toolbarItemVariantPropUpdatesInput.tsx new file mode 100644 index 000000000..c948609cd --- /dev/null +++ b/packages/eslint-plugin-pf-codemods/src/rules/v6/toolbarItemVariantPropUpdates/toolbarItemVariantPropUpdatesInput.tsx @@ -0,0 +1,10 @@ +import { ToolbarItem } from "@patternfly/react-core"; + +export const ToolbarItemVariantPropUpdatesInput = () => ( + <> + + + + + +); diff --git a/packages/eslint-plugin-pf-codemods/src/rules/v6/toolbarItemVariantPropUpdates/toolbarItemVariantPropUpdatesOutput.tsx b/packages/eslint-plugin-pf-codemods/src/rules/v6/toolbarItemVariantPropUpdates/toolbarItemVariantPropUpdatesOutput.tsx new file mode 100644 index 000000000..4509a174f --- /dev/null +++ b/packages/eslint-plugin-pf-codemods/src/rules/v6/toolbarItemVariantPropUpdates/toolbarItemVariantPropUpdatesOutput.tsx @@ -0,0 +1,10 @@ +import { ToolbarItem } from "@patternfly/react-core"; + +export const ToolbarItemVariantPropUpdatesInput = () => ( + <> + + + + + +);