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';
", + 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';
`, - 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 */} - - + + + + +