diff --git a/packages/eslint-plugin-pf-codemods/lib/rules/v5/td-TdSelectType-TdActionsType-rename-disable.js b/packages/eslint-plugin-pf-codemods/lib/rules/v5/td-TdSelectType-TdActionsType-rename-disable.js new file mode 100644 index 000000000..3ab0c62a5 --- /dev/null +++ b/packages/eslint-plugin-pf-codemods/lib/rules/v5/td-TdSelectType-TdActionsType-rename-disable.js @@ -0,0 +1,128 @@ +const { getPackageImports } = require("../../helpers"); + +// https://github.com/patternfly/patternfly-react/pull/8861 +// https://github.com/patternfly/patternfly-react/pull/8904 +module.exports = { + meta: { fixable: "code" }, + create: function (context) { + const tableTdImport = getPackageImports( + context, + "@patternfly/react-table" + ).find((specifier) => "Td" === specifier.imported.name); + + return !tableTdImport + ? {} + : { + JSXOpeningElement(node) { + if (tableTdImport.local.name !== node.name.name) { + return; + } + + const attributesToUpdate = node.attributes.filter( + (attr) => + attr.type === "JSXAttribute" && + attr.value.type === "JSXExpressionContainer" && + ["select", "actions"].includes(attr.name?.name) + ); + + const getObjectProp = (expr, propName) => + expr.properties.find( + (prop) => + (prop.key.type === "Identifier" && + prop.key.name === propName) || + (prop.key.type === "Literal" && prop.key.value === propName) + ); + + const findVariableDeclaration = (name, scope) => { + while (scope !== null) { + const variable = scope.variables.find((v) => v.name === name); + + if (variable) { + return variable; + } + + scope = scope.upper; + } + return undefined; + }; + + const handleObjectExpression = (expr, toReplace) => { + const disableProp = getObjectProp(expr, "disable"); + disableProp && toReplace.push(disableProp.key); + + if (disableProp?.shorthand) { + const variable = findVariableDeclaration( + "disable", + context.getSourceCode().getScope(expr) + ); + + variable && toReplace.push(variable.defs[0].node.id); + } + }; + + for (const attr of attributesToUpdate) { + const expr = attr.value.expression; + + let toReplace = []; + + if (expr.type === "ObjectExpression") { + handleObjectExpression(expr, toReplace); + } + + if (expr.type === "Identifier") { + const variable = findVariableDeclaration( + expr.name, + context.getSourceCode().getScope(node) + ); + + const variableValue = variable.defs[0].node.init; + + if (variableValue?.type === "ObjectExpression") { + handleObjectExpression(variableValue, toReplace); + } + + const nodesUsingVariable = variable.references.map( + (ref) => ref.identifier.parent + ); + + nodesUsingVariable.forEach((n) => { + if ( + n.type === "MemberExpression" && + (n.property.name === "disable" || + n.property.value === "disable") + ) { + toReplace.push(n.property); + } + + if ( + n.type === "AssignmentExpression" && + n.right.type === "ObjectExpression" + ) { + handleObjectExpression(n.right, toReplace); + } + }); + } + + toReplace.length && + context.report({ + message: `'disable' prop of interface ${ + { + select: "TdSelectType", + actions: "TdActionsType", + }[attr.name.name] + } has been renamed to 'isDisabled'`, + node, + fix: function (fixer) { + return toReplace.map((val) => + fixer.replaceText( + val, + val.type === "Literal" ? '"isDisabled"' : "isDisabled" + ) + ); + }, + }); + } + }, + }; + }, +}; diff --git a/packages/eslint-plugin-pf-codemods/test/rules/v5/td-TdSelectType-TdActionsType-rename-disable.js b/packages/eslint-plugin-pf-codemods/test/rules/v5/td-TdSelectType-TdActionsType-rename-disable.js new file mode 100644 index 000000000..71925f697 --- /dev/null +++ b/packages/eslint-plugin-pf-codemods/test/rules/v5/td-TdSelectType-TdActionsType-rename-disable.js @@ -0,0 +1,99 @@ +const ruleTester = require("../../ruletester"); +const rule = require("../../../lib/rules/v5/td-TdSelectType-TdActionsType-rename-disable"); + +ruleTester.run("td-TdSelectType-TdActionsType-rename-disable", rule, { + valid: [ + { + code: `import { Td } from "@patternfly/react-table"; + + let myObj = { isDisabled: true }; + myObj["isDisabled"] = false; + myObj.isDisabled = true; + myObj = { isDisabled: false }; + + ; + + const isDisabled = true; + const obj = { isDisabled }; + <> + + + ;`, + }, + { + // No @patternfly/react-core import + code: ` + let myObj = { disable: true }; + myObj["disable"] = false; + myObj.disable = true; + myObj = { disable: false }; + + ; + + const disable = true; + const obj = { disable }; + <> + + + ;`, + }, + ], + invalid: [ + { + code: ` + import { Td } from "@patternfly/react-table"; + + let myObj = { disable: true }; + myObj["disable"] = false; + myObj.disable = true; + myObj = { disable: false }; + + ; + + const disable = true; + const obj = { disable }; + <> + + + ;`, + output: ` + import { Td } from "@patternfly/react-table"; + + let myObj = { isDisabled: true }; + myObj["isDisabled"] = false; + myObj.isDisabled = true; + myObj = { isDisabled: false }; + + ; + + const isDisabled = true; + const obj = { isDisabled }; + <> + + + ;`, + errors: [ + { + message: `'disable' prop of interface TdSelectType has been renamed to 'isDisabled'`, + type: "JSXOpeningElement", + }, + { + message: `'disable' prop of interface TdActionsType has been renamed to 'isDisabled'`, + type: "JSXOpeningElement", + }, + { + message: `'disable' prop of interface TdSelectType has been renamed to 'isDisabled'`, + type: "JSXOpeningElement", + }, + { + message: `'disable' prop of interface TdActionsType has been renamed to 'isDisabled'`, + type: "JSXOpeningElement", + }, + { + message: `'disable' prop of interface TdActionsType has been renamed to 'isDisabled'`, + type: "JSXOpeningElement", + } + ], + }, + ], +}); diff --git a/packages/pf-codemods/README.md b/packages/pf-codemods/README.md index 883d037e6..c1d8e4422 100644 --- a/packages/pf-codemods/README.md +++ b/packages/pf-codemods/README.md @@ -1768,6 +1768,48 @@ We've restricted the type of elements that can be passed to the `Tabs` component This rule will raise a warning when `Tabs` is imported in a file, even if the children passed to it are already of the appropriate type. It will not make any code changes. +### td-TdSelectType-TdActionsType-rename-disable [(#8861,](https://github.com/patternfly/patternfly-react/pull/8861) [#8904)](https://github.com/patternfly/patternfly-react/pull/8904) + +We've renamed `disable` prop to `isDisabled` inside `TdSelectType` and `TdActionsType` interfaces. These interfaces are used as types of `Td` component's props `select` (`TdSelectType`) and `actions` (`TdActionsType`). + +#### Examples + +In: + +```jsx +let myObj = { disable: true }; +myObj["disable"] = false; +myObj.disable = true; +myObj = { disable: false }; + +; + +const disable = true; +const obj = { disable }; +<> + + +; +``` + +Out: + +```jsx +let myObj = { isDisabled: true }; +myObj["isDisabled"] = false; +myObj.isDisabled = true; +myObj = { isDisabled: false }; + +; + +const isDisabled = true; +const obj = { isDisabled }; +<> + + +; +``` + ### toggle-remove-isprimary [(#8179)](https://github.com/patternfly/patternfly-react/pull/8179) We've removed the deprecated `isPrimary` prop. This rule wil replace it with the "primary" value on the `toggleVariant` prop. diff --git a/test/test.tsx b/test/test.tsx index 172618a02..ed3768e68 100644 --- a/test/test.tsx +++ b/test/test.tsx @@ -79,6 +79,10 @@ import { } from "@patternfly/react-core"; import { SelectOption, WizardBody as WizardBodyNext, WizardFooter } from "@patternfly/react-core/next"; +import { Td } from "@patternfly/react-table"; + +const tdSelectTypeObj = {"disable": true}; + //following type of import was causing errors for rules that checked specifiers before import package import foo from Bar; @@ -172,6 +176,7 @@ const backgroundImgSrcObj: BackgroundImageSrcMap = {}; +