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'; `,
- 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, TextInput } from '@patternfly/react-core'; `,
- output: `import { InputGroup, TextInput, InputGroupItem } from '@patternfly/react-core'; `,
+ output: `import { InputGroup, TextInput, 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, TextArea } from '@patternfly/react-core'; `,
- output: `import { InputGroup, TextArea, InputGroupItem } from '@patternfly/react-core'; `,
+ output: `import { InputGroup, TextArea, 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",
},
],
},
- // Child that is InputGroupText
+ // InputGroupText child and non-InputGroupText child
{
- code: `import { InputGroup, InputGroupText } from '@patternfly/react-core'; `,
- output: `import { InputGroup, InputGroupText, InputGroupItem } from '@patternfly/react-core'; `,
+ code: `import { InputGroup, InputGroupText } from '@patternfly/react-core'; Some text`,
+ output: `import { InputGroup, InputGroupText, InputGroupItem } from '@patternfly/react-core'; Some text`,
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",
},
],
@@ -109,7 +112,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",
},
],
@@ -124,7 +127,21 @@ ruleTester.run("inputGroup-add-inputGroupItem", rule, {
type: "ImportDeclaration",
},
{
- message: `Each child passed to PFInputGroup 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 PFInputGroup must now be wrapped within an InputGroupItem. The InputGroupItem may need the "isFill", "isPlain", and/or "isBox" props adjusted.`,
+ type: "JSXElement",
+ },
+ ],
+ },
+ {
+ code: `import { InputGroup, TextArea as PFTA } from '@patternfly/react-core'; `,
+ output: `import { InputGroup, TextArea as PFTA, InputGroupItem } from '@patternfly/react-core'; `,
+ errors: [
+ {
+ message: `add missing import InputGroupItem from @patternfly/react-core`,
+ type: "ImportDeclaration",
+ },
+ {
+ 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",
},
],
diff --git a/packages/pf-codemods/README.md b/packages/pf-codemods/README.md
index 5d4de7d52..d2490d0ce 100644
--- a/packages/pf-codemods/README.md
+++ b/packages/pf-codemods/README.md
@@ -1465,9 +1465,10 @@ Out:
We've updated the default value of the `aria-label` attribute for Nav with a `horizontal-subnav` variant to "local" (previously the default value was "Global").
-### inputGroup-add-inputGroupItem [(#9074)](https://github.com/patternfly/patternfly-react/pull/9074)
+### inputGroup-update-api [(#9074)](https://github.com/patternfly/patternfly-react/pull/9074) and [(#9176)](https://github.com/patternfly/patternfly-react/pull/9176)
+
+We've added the InputGroupItem component, which must wrap all non-InputGroupText children passed to an InputGroup. The InputGroupItem component may need to have the `isFill`, `isBox`, and/or `isPlain` props adjusted to retain styling.
-We've added the InputGroupItem component, which must wrap each child passed to an InputGroup. Additionally, the InputGroupItem component may need to have the `isFill`, `isBox`, and/or `isPlain` props passed in.
#### Examples
@@ -1479,7 +1480,7 @@ In:
-
+ Some text
```
@@ -1492,7 +1493,7 @@ Out:
-
+ Some text
```
diff --git a/test/test.tsx b/test/test.tsx
index e1801b5f6..dd27650d3 100644
--- a/test/test.tsx
+++ b/test/test.tsx
@@ -55,6 +55,7 @@ import {
FileUploadField,
FormSelect,
InputGroup,
+ InputGroupItem,
InputGroupText,
KebabToggle,
KEY_CODES,
@@ -230,7 +231,7 @@ const alertVariantOption = AlertVariant.default;
-
+ Text