diff --git a/packages/eslint-plugin-pf-codemods/src/rules/v6/enableAnimations/enable-animations.md b/packages/eslint-plugin-pf-codemods/src/rules/v6/enableAnimations/enable-animations.md
index 4b446912..dc2f6bb7 100644
--- a/packages/eslint-plugin-pf-codemods/src/rules/v6/enableAnimations/enable-animations.md
+++ b/packages/eslint-plugin-pf-codemods/src/rules/v6/enableAnimations/enable-animations.md
@@ -2,14 +2,18 @@
This rule adds the `hasAnimations` prop to PatternFly components that support animations. This is an optional enhancement that enables smoother transitions and animations in your UI components.
+**⚠️ Important:** Enabling animations may change the DOM structure of some components. This could affect CSS selectors, testing queries, or other code that relies on the specific DOM structure. Review your code after enabling animations to ensure compatibility.
+
The following components will have `hasAnimations` added:
- AlertGroup
-- DualListSelector
+- DualListSelector (only when `isTree` prop has a truthy value)
- FormFieldGroupExpandable
-- SearchInput
+- SearchInputExpandable
- TreeView
- Table (from @patternfly/react-table)
+**Note:** For DualListSelector, the `hasAnimations` prop will only be added when the `isTree` prop is present and has a truthy value (e.g., `isTree`, `isTree={true}`, `isTree="yes"`, `isTree={1}`). It will not be added when `isTree` is falsy (e.g., `isTree={false}`, `isTree=""`, `isTree={0}`) or when the `isTree` prop is not present at all.
+
This rule can only run using the `--only enable-animations` option.
#### Examples
@@ -17,33 +21,11 @@ This rule can only run using the `--only enable-animations` option.
In:
```jsx
-import { AlertGroup, TreeView } from '@patternfly/react-core';
-import { Table } from '@patternfly/react-table';
-
-export const Example = () => (
- <>
-
- {/* alerts */}
-
-
-
- >
-);
+%inputExample%
```
Out:
```jsx
-import { AlertGroup, TreeView } from '@patternfly/react-core';
-import { Table } from '@patternfly/react-table';
-
-export const Example = () => (
- <>
-
- {/* alerts */}
-
-
-
- >
-);
+%outputExample%
```
\ No newline at end of file
diff --git a/packages/eslint-plugin-pf-codemods/src/rules/v6/enableAnimations/enable-animations.test.ts b/packages/eslint-plugin-pf-codemods/src/rules/v6/enableAnimations/enable-animations.test.ts
index 70aeaa0a..16a8f9d8 100644
--- a/packages/eslint-plugin-pf-codemods/src/rules/v6/enableAnimations/enable-animations.test.ts
+++ b/packages/eslint-plugin-pf-codemods/src/rules/v6/enableAnimations/enable-animations.test.ts
@@ -1,152 +1,128 @@
const ruleTester = require("../../ruletester");
import * as rule from "./enable-animations";
+const valid = [
+ // Components not in target list should be ignored
+ " ",
+ "import { AlertGroup } from '@patternfly/react-core'; ",
+ "import { AlertGroup } from '@patternfly/react-core'; ",
+ "import { Table } from '@patternfly/react-table'; ",
+ // Component not in target components
+ "import { Button } from '@patternfly/react-core'; ",
+ // DualListSelector without isTree should not get hasAnimations
+ "import { DualListSelector } from '@patternfly/react-core'; ",
+ // DualListSelector with isTree={false} should not get hasAnimations
+ "import { DualListSelector } from '@patternfly/react-core'; ",
+ // DualListSelector with isTree and hasAnimations already present
+ "import { DualListSelector } from '@patternfly/react-core'; ",
+];
+
+const invalid = [
+ {
+ code: "import { AlertGroup } from '@patternfly/react-core'; ",
+ output: "import { AlertGroup } from '@patternfly/react-core'; ",
+ errors: [
+ {
+ message: "Consider adding hasAnimations prop to enable component animations.",
+ type: "JSXOpeningElement"
+ }
+ ]
+ },
+ {
+ code: "import { DualListSelector } from '@patternfly/react-core'; ",
+ output: "import { DualListSelector } from '@patternfly/react-core'; ",
+ errors: [
+ {
+ message: "Consider adding hasAnimations prop to enable component animations.",
+ type: "JSXOpeningElement"
+ }
+ ]
+ },
+ {
+ code: "import { DualListSelector } from '@patternfly/react-core'; ",
+ output: "import { DualListSelector } from '@patternfly/react-core'; ",
+ errors: [
+ {
+ message: "Consider adding hasAnimations prop to enable component animations.",
+ type: "JSXOpeningElement"
+ }
+ ]
+ },
+ {
+ code: "import { TreeView } from '@patternfly/react-core'; ",
+ output: "import { TreeView } from '@patternfly/react-core'; ",
+ errors: [
+ {
+ message: "Consider adding hasAnimations prop to enable component animations.",
+ type: "JSXOpeningElement"
+ }
+ ]
+ },
+ {
+ code: "import { SearchInputExpandable } from '@patternfly/react-core'; ",
+ output: "import { SearchInputExpandable } from '@patternfly/react-core'; ",
+ errors: [
+ {
+ message: "Consider adding hasAnimations prop to enable component animations.",
+ type: "JSXOpeningElement"
+ }
+ ]
+ },
+ {
+ code: "import { FormFieldGroupExpandable } from '@patternfly/react-core'; ",
+ output: "import { FormFieldGroupExpandable } from '@patternfly/react-core'; ",
+ errors: [
+ {
+ message: "Consider adding hasAnimations prop to enable component animations.",
+ type: "JSXOpeningElement"
+ }
+ ]
+ },
+ {
+ code: "import { Table } from '@patternfly/react-table'; ",
+ output: "import { Table } from '@patternfly/react-table'; ",
+ errors: [
+ {
+ message: "Consider adding hasAnimations prop to enable component animations.",
+ type: "JSXOpeningElement"
+ }
+ ]
+ },
+ {
+ code: `import { AlertGroup, TreeView } from '@patternfly/react-core';
+ <>
+
+
+ >`,
+ output: `import { AlertGroup, TreeView } from '@patternfly/react-core';
+ <>
+
+
+ >`,
+ errors: [
+ {
+ message: "Consider adding hasAnimations prop to enable component animations.",
+ type: "JSXOpeningElement"
+ },
+ {
+ message: "Consider adding hasAnimations prop to enable component animations.",
+ type: "JSXOpeningElement"
+ }
+ ]
+ },
+ {
+ code: "import { AlertGroup } from '@patternfly/react-core'; ",
+ output: "import { AlertGroup } from '@patternfly/react-core'; ",
+ errors: [
+ {
+ message: "Consider adding hasAnimations prop to enable component animations.",
+ type: "JSXOpeningElement"
+ }
+ ]
+ }
+];
+
ruleTester.run("enable-animations", rule, {
- valid: [
- // No imports, should not trigger
- {
- code: ` `,
- },
- // Already has hasAnimations prop
- {
- code: `import { AlertGroup } from '@patternfly/react-core'; `,
- },
- // Already has hasAnimations with value
- {
- code: `import { AlertGroup } from '@patternfly/react-core'; `,
- },
- // Table already has hasAnimations
- {
- code: `import { Table } from '@patternfly/react-table'; `,
- },
- // Non-target component should not be affected
- {
- code: `import { Button } from '@patternfly/react-core'; `,
- },
- ],
- invalid: [
- // AlertGroup without hasAnimations
- {
- code: `import { AlertGroup } from '@patternfly/react-core'; `,
- output: `import { AlertGroup } from '@patternfly/react-core'; `,
- errors: [
- {
- message: "Consider adding hasAnimations prop to enable component animations.",
- type: "JSXOpeningElement",
- },
- ],
- },
- // DualListSelector without hasAnimations
- {
- code: `import { DualListSelector } from '@patternfly/react-core'; `,
- output: `import { DualListSelector } from '@patternfly/react-core'; `,
- errors: [
- {
- message: "Consider adding hasAnimations prop to enable component animations.",
- type: "JSXOpeningElement",
- },
- ],
- },
- // TreeView without hasAnimations
- {
- code: `import { TreeView } from '@patternfly/react-core'; `,
- output: `import { TreeView } from '@patternfly/react-core'; `,
- errors: [
- {
- message: "Consider adding hasAnimations prop to enable component animations.",
- type: "JSXOpeningElement",
- },
- ],
- },
- // SearchInput without hasAnimations
- {
- code: `import { SearchInput } from '@patternfly/react-core'; `,
- output: `import { SearchInput } from '@patternfly/react-core'; `,
- errors: [
- {
- message: "Consider adding hasAnimations prop to enable component animations.",
- type: "JSXOpeningElement",
- },
- ],
- },
- // FormFieldGroupExpandable without hasAnimations
- {
- code: `import { FormFieldGroupExpandable } from '@patternfly/react-core'; `,
- output: `import { FormFieldGroupExpandable } from '@patternfly/react-core'; `,
- errors: [
- {
- message: "Consider adding hasAnimations prop to enable component animations.",
- type: "JSXOpeningElement",
- },
- ],
- },
- // Table from react-table without hasAnimations
- {
- code: `import { Table } from '@patternfly/react-table'; `,
- output: `import { Table } from '@patternfly/react-table'; `,
- errors: [
- {
- message: "Consider adding hasAnimations prop to enable component animations.",
- type: "JSXOpeningElement",
- },
- ],
- },
- // Multiple components in one file
- {
- code: `import { AlertGroup, TreeView } from '@patternfly/react-core';
- <>
-
-
- >`,
- output: `import { AlertGroup, TreeView } from '@patternfly/react-core';
- <>
-
-
- >`,
- errors: [
- {
- message: "Consider adding hasAnimations prop to enable component animations.",
- type: "JSXOpeningElement",
- },
- {
- message: "Consider adding hasAnimations prop to enable component animations.",
- type: "JSXOpeningElement",
- },
- ],
- },
- // Component with existing props
- {
- code: `import { AlertGroup } from '@patternfly/react-core'; `,
- output: `import { AlertGroup } from '@patternfly/react-core'; `,
- errors: [
- {
- message: "Consider adding hasAnimations prop to enable component animations.",
- type: "JSXOpeningElement",
- },
- ],
- },
- // Self-closing and regular tags
- {
- code: `import { DualListSelector } from '@patternfly/react-core';
- <>
-
-
- >`,
- output: `import { DualListSelector } from '@patternfly/react-core';
- <>
-
-
- >`,
- errors: [
- {
- message: "Consider adding hasAnimations prop to enable component animations.",
- type: "JSXOpeningElement",
- },
- {
- message: "Consider adding hasAnimations prop to enable component animations.",
- type: "JSXOpeningElement",
- },
- ],
- },
- ],
+ valid: valid,
+ invalid: invalid,
});
\ No newline at end of file
diff --git a/packages/eslint-plugin-pf-codemods/src/rules/v6/enableAnimations/enable-animations.ts b/packages/eslint-plugin-pf-codemods/src/rules/v6/enableAnimations/enable-animations.ts
index 40ff3a17..a9f75491 100644
--- a/packages/eslint-plugin-pf-codemods/src/rules/v6/enableAnimations/enable-animations.ts
+++ b/packages/eslint-plugin-pf-codemods/src/rules/v6/enableAnimations/enable-animations.ts
@@ -1,6 +1,7 @@
import { Rule } from "eslint";
-import { JSXOpeningElement } from "estree-jsx";
+import { JSXOpeningElement, JSXAttribute } from "estree-jsx";
import { getFromPackage, checkMatchingJSXOpeningElement } from "../../helpers";
+import { getAttribute, getAttributeValue } from "../../helpers/JSXAttributes";
// Rule to add hasAnimations prop to components that support animations
module.exports = {
@@ -22,7 +23,7 @@ module.exports = {
"DualListSelector",
"TreeView",
"AlertGroup",
- "SearchInput"
+ "SearchInputExpandable"
];
// Table comes from react-table package
@@ -42,11 +43,51 @@ module.exports = {
const message =
"Consider adding hasAnimations prop to enable component animations.";
+ // Helper function to check if isTree prop exists and isn't explicitly false
+ function hasValidIsTreeProp(node: JSXOpeningElement): boolean {
+ const isTreeAttribute = getAttribute(node, "isTree");
+
+ if (!isTreeAttribute) {
+ return false; // No isTree prop found
+ }
+
+ // If isTree has no value, it defaults to true
+ if (!isTreeAttribute.value) {
+ return true;
+ }
+
+ // Get the actual value using the helper
+ const attributeValue = getAttributeValue(context, isTreeAttribute.value);
+
+ // Check for explicit false: isTree={false}
+ if (attributeValue.type === "Literal" && attributeValue.value === false) {
+ return false;
+ }
+
+ // For anything else (including complex expressions), assume it could be truthy
+ return true;
+ }
+
+ // Helper function to get component name from node
+ function getComponentName(node: JSXOpeningElement): string | null {
+ if (node.name.type === "JSXIdentifier") {
+ return node.name.name;
+ }
+ return null;
+ }
+
return allTargetImports.length === 0
? {}
: {
JSXOpeningElement(node: JSXOpeningElement) {
if (checkMatchingJSXOpeningElement(node, allTargetImports)) {
+ const componentName = getComponentName(node);
+
+ // Special handling for DualListSelector - only add hasAnimations if isTree is true
+ if (componentName === "DualListSelector" && !hasValidIsTreeProp(node)) {
+ return; // Skip this DualListSelector as it doesn't have isTree=true
+ }
+
// Check if hasAnimations prop already exists
const hasAnimationsAttribute = node.attributes.find(
(attr) =>
@@ -61,10 +102,14 @@ module.exports = {
node,
message,
fix(fixer) {
- return fixer.insertTextAfter(
- node.name,
- " hasAnimations"
- );
+ // Insert hasAnimations at the end of existing attributes
+ if (node.attributes.length > 0) {
+ const lastAttribute = node.attributes[node.attributes.length - 1];
+ return fixer.insertTextAfter(lastAttribute, " hasAnimations");
+ } else {
+ // No existing attributes, insert after component name
+ return fixer.insertTextAfter(node.name, " hasAnimations");
+ }
},
});
}
diff --git a/packages/eslint-plugin-pf-codemods/src/rules/v6/enableAnimations/enableAnimationsInput.tsx b/packages/eslint-plugin-pf-codemods/src/rules/v6/enableAnimations/enableAnimationsInput.tsx
index 8b129e58..3045f2d0 100644
--- a/packages/eslint-plugin-pf-codemods/src/rules/v6/enableAnimations/enableAnimationsInput.tsx
+++ b/packages/eslint-plugin-pf-codemods/src/rules/v6/enableAnimations/enableAnimationsInput.tsx
@@ -1,4 +1,4 @@
-import { AlertGroup, TreeView, DualListSelector, SearchInput, FormFieldGroupExpandable } from "@patternfly/react-core";
+import { AlertGroup, TreeView, DualListSelector, SearchInputExpandable, FormFieldGroupExpandable } from "@patternfly/react-core";
import { Table } from "@patternfly/react-table";
export const EnableAnimationsInput = () => (
@@ -8,7 +8,10 @@ export const EnableAnimationsInput = () => (
-
+
+
+
+
>
diff --git a/packages/eslint-plugin-pf-codemods/src/rules/v6/enableAnimations/enableAnimationsOutput.tsx b/packages/eslint-plugin-pf-codemods/src/rules/v6/enableAnimations/enableAnimationsOutput.tsx
index 3c111b5e..ac085348 100644
--- a/packages/eslint-plugin-pf-codemods/src/rules/v6/enableAnimations/enableAnimationsOutput.tsx
+++ b/packages/eslint-plugin-pf-codemods/src/rules/v6/enableAnimations/enableAnimationsOutput.tsx
@@ -1,4 +1,4 @@
-import { AlertGroup, TreeView, DualListSelector, SearchInput, FormFieldGroupExpandable } from "@patternfly/react-core";
+import { AlertGroup, TreeView, DualListSelector, SearchInputExpandable, FormFieldGroupExpandable } from "@patternfly/react-core";
import { Table } from "@patternfly/react-table";
export const EnableAnimationsOutput = () => (
@@ -7,8 +7,11 @@ export const EnableAnimationsOutput = () => (
{/* alerts */}
-
-
+
+
+
+
+
>