diff --git a/packages/eslint-plugin-pf-codemods/lib/rules/v5/inputGroup-add-inputGroupItem.js b/packages/eslint-plugin-pf-codemods/lib/rules/v5/inputGroup-add-inputGroupItem.js deleted file mode 100644 index 3da614655..000000000 --- a/packages/eslint-plugin-pf-codemods/lib/rules/v5/inputGroup-add-inputGroupItem.js +++ /dev/null @@ -1,79 +0,0 @@ -const { getFromPackage } = require("../../helpers"); - -// https://github.com/patternfly/patternfly-react/pull/9074 -module.exports = { - meta: { fixable: "code" }, - create: function (context) { - const inputGroupImport = getFromPackage( - context, - "@patternfly/react-core" - ).imports.find((specifier) => specifier.imported?.name == "InputGroup"); - - return !inputGroupImport - ? {} - : { - ImportDeclaration(node) { - const getMatchingSpecifier = (name) => - node.specifiers.find( - (specifier) => specifier.imported?.name === name - ); - if ( - getMatchingSpecifier(inputGroupImport.imported?.name) && - !getMatchingSpecifier("InputGroupItem") - ) { - context.report({ - node, - message: `add missing import InputGroupItem from @patternfly/react-core`, - fix(fixer) { - return fixer.insertTextAfter( - node.specifiers[node.specifiers.length - 1], - ", InputGroupItem" - ); - }, - }); - } - }, - JSXElement(node) { - const parentName = node.openingElement?.name?.name; - if (parentName !== inputGroupImport.local?.name) { - return; - } - - node.children?.forEach((child) => { - const childName = child.openingElement?.name?.name; - if ( - childName === "InputGroupItem" || - ["JSXText", "Literal"].includes(child.type) - ) { - return; - } - - let inputGroupItemProps = ""; - - if ( - ["input", "textarea", "TextArea", "TextInput"].includes( - childName - ) - ) { - inputGroupItemProps = " isFill"; - } else if (childName === "InputGroupText") { - inputGroupItemProps = " isBox"; - } - - context.report({ - node, - message: `Each child passed to ${parentName} must now be wrapped within an InputGroupItem. The InputGroupItem may also need the "isFill", "isPlain", and/or "isBox" props passed in.`, - fix(fixer) { - return fixer.replaceText( - child, - `${context - .getSourceCode() - .getText(child)}` - ); - }, - }); - }); - }, - }; - }, -}; diff --git a/packages/eslint-plugin-pf-codemods/lib/rules/v5/inputGroup-update-api.js b/packages/eslint-plugin-pf-codemods/lib/rules/v5/inputGroup-update-api.js new file mode 100644 index 000000000..12f37fd7b --- /dev/null +++ b/packages/eslint-plugin-pf-codemods/lib/rules/v5/inputGroup-update-api.js @@ -0,0 +1,111 @@ +const { getFromPackage, getAllJSXElements } = require("../../helpers"); + +// https://github.com/patternfly/patternfly-react/pull/9074 +// https://github.com/patternfly/patternfly-react/pull/9176 +module.exports = { + meta: { fixable: "code" }, + create: function (context) { + const { imports } = getFromPackage(context, "@patternfly/react-core"); + + const inputGroupImport = imports.find( + (specifier) => specifier.imported?.name == "InputGroup" + ); + const inputGroupItemImport = imports.find( + (specifier) => specifier.imported?.name == "InputGroupItem" + ); + const inputGroupTextImport = imports.find( + (specifier) => specifier.imported?.name == "InputGroupText" + ); + + return !inputGroupImport + ? {} + : { + ImportDeclaration(node) { + const getMatchingSpecifier = (name) => + node.specifiers.find( + (specifier) => specifier.imported?.name === name + ); + + const inputGroupElements = getAllJSXElements(context).filter( + (elem) => + elem.openingElement?.name?.name === inputGroupImport.local?.name + ); + + const needsInputGroupItemImport = inputGroupElements.some( + (inputGroup) => { + return inputGroup.children?.some( + (inputGroupChild) => + ![ + inputGroupTextImport?.local?.name, + "InputGroupText", + ].includes(inputGroupChild.openingElement?.name?.name) + ); + } + ); + + if ( + getMatchingSpecifier(inputGroupImport.imported?.name) && + !inputGroupItemImport && + needsInputGroupItemImport + ) { + context.report({ + node, + message: `add missing import InputGroupItem from @patternfly/react-core`, + fix(fixer) { + return fixer.insertTextAfter( + node.specifiers[node.specifiers.length - 1], + ", InputGroupItem" + ); + }, + }); + } + }, + JSXElement(node) { + const parentName = node.openingElement?.name?.name; + if (parentName !== inputGroupImport.local?.name) { + return; + } + const inputGroupTextName = inputGroupTextImport?.local?.name; + + node.children?.forEach((child) => { + const childName = child.openingElement?.name?.name; + if ( + [ + "InputGroupItem", + inputGroupItemImport?.local?.name, + inputGroupTextName, + ].includes(childName) || + ["JSXText", "Literal"].includes(child.type) + ) { + return; + } + + const matchingChildImport = imports.find( + (imp) => imp.local?.name === childName + ); + const shouldFill = + ["input", "textarea"].includes(childName) || + ["TextArea", "TextInput"].includes( + matchingChildImport?.imported?.name + ); + + context.report({ + node, + message: `Any non-InputGroupText child passed to ${parentName} must now be wrapped within an InputGroupItem. The InputGroupItem may need the "isFill", "isPlain", and/or "isBox" props adjusted.`, + fix(fixer) { + const sourceCode = context.getSourceCode(); + const newChild = sourceCode.getText(child); + + return fixer.replaceText( + child, + `${newChild}` + ); + }, + }); + }); + }, + }; + }, +}; diff --git a/packages/eslint-plugin-pf-codemods/test/rules/v5/inputGroup-add-inputGroupItem.js b/packages/eslint-plugin-pf-codemods/test/rules/v5/inputGroup-update-api.js similarity index 55% rename from packages/eslint-plugin-pf-codemods/test/rules/v5/inputGroup-add-inputGroupItem.js rename to packages/eslint-plugin-pf-codemods/test/rules/v5/inputGroup-update-api.js index 172d324e7..2abf28af9 100644 --- a/packages/eslint-plugin-pf-codemods/test/rules/v5/inputGroup-add-inputGroupItem.js +++ b/packages/eslint-plugin-pf-codemods/test/rules/v5/inputGroup-update-api.js @@ -1,10 +1,13 @@ const ruleTester = require("../../ruletester"); -const rule = require("../../../lib/rules/v5/inputGroup-add-inputGroupItem"); +const rule = require("../../../lib/rules/v5/inputGroup-update-api"); -ruleTester.run("inputGroup-add-inputGroupItem", rule, { +ruleTester.run("inputGroup-update-api", rule, { valid: [ { - code: `import { InputGroup, InputGroupItem } from '@patternfly/react-core'; `, + code: `import { InputGroup, InputGroupItem } from '@patternfly/react-core'; `, + }, + { + code: `import { InputGroup, InputGroupText } from '@patternfly/react-core'; Some text`, }, // no @patternfly/react-core import { @@ -22,7 +25,7 @@ ruleTester.run("inputGroup-add-inputGroupItem", rule, { type: "ImportDeclaration", }, { - message: `Each child passed to InputGroup must now be wrapped within an InputGroupItem. The InputGroupItem may also need the "isFill", "isPlain", and/or "isBox" props passed in.`, + message: `Any non-InputGroupText child passed to InputGroup must now be wrapped within an InputGroupItem. The InputGroupItem may need the "isFill", "isPlain", and/or "isBox" props adjusted.`, type: "JSXElement", }, ], @@ -30,71 +33,71 @@ ruleTester.run("inputGroup-add-inputGroupItem", rule, { // Child that is input-like, e.g. textarea, input, TextInput, or TextArea { code: `import { InputGroup } from '@patternfly/react-core'; `, - output: `import { InputGroup, InputGroupItem } from '@patternfly/react-core'; `, + output: `import { InputGroup, InputGroupItem } from '@patternfly/react-core'; `, errors: [ { message: `add missing import InputGroupItem from @patternfly/react-core`, type: "ImportDeclaration", }, { - message: `Each child passed to InputGroup must now be wrapped within an InputGroupItem. The InputGroupItem may also need the "isFill", "isPlain", and/or "isBox" props passed in.`, + message: `Any non-InputGroupText child passed to InputGroup must now be wrapped within an InputGroupItem. The InputGroupItem may need the "isFill", "isPlain", and/or "isBox" props adjusted.`, type: "JSXElement", }, ], }, { code: `import { InputGroup } from '@patternfly/react-core';