diff --git a/packages/eslint-plugin-pf-codemods/src/ruleCustomization.ts b/packages/eslint-plugin-pf-codemods/src/ruleCustomization.ts index 821807f9..98278099 100644 --- a/packages/eslint-plugin-pf-codemods/src/ruleCustomization.ts +++ b/packages/eslint-plugin-pf-codemods/src/ruleCustomization.ts @@ -4,6 +4,7 @@ */ export const betaRuleNames: string[] = [ "data-codemods-cleanup", + "enable-animations", "kebabToggle-replace-with-menuToggle", ]; 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 new file mode 100644 index 00000000..4b446912 --- /dev/null +++ b/packages/eslint-plugin-pf-codemods/src/rules/v6/enableAnimations/enable-animations.md @@ -0,0 +1,49 @@ +### enable-animations + +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. + +The following components will have `hasAnimations` added: +- AlertGroup +- DualListSelector +- FormFieldGroupExpandable +- SearchInput +- TreeView +- Table (from @patternfly/react-table) + +This rule can only run using the `--only enable-animations` option. + +#### Examples + +In: + +```jsx +import { AlertGroup, TreeView } from '@patternfly/react-core'; +import { Table } from '@patternfly/react-table'; + +export const Example = () => ( + <> + + {/* alerts */} + + + + +); +``` + +Out: + +```jsx +import { AlertGroup, TreeView } from '@patternfly/react-core'; +import { Table } from '@patternfly/react-table'; + +export const Example = () => ( + <> + + {/* alerts */} + + +
+ +); +``` \ 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 new file mode 100644 index 00000000..70aeaa0a --- /dev/null +++ b/packages/eslint-plugin-pf-codemods/src/rules/v6/enableAnimations/enable-animations.test.ts @@ -0,0 +1,152 @@ +const ruleTester = require("../../ruletester"); +import * as rule from "./enable-animations"; + +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", + }, + ], + }, + ], +}); \ 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 new file mode 100644 index 00000000..40ff3a17 --- /dev/null +++ b/packages/eslint-plugin-pf-codemods/src/rules/v6/enableAnimations/enable-animations.ts @@ -0,0 +1,75 @@ +import { Rule } from "eslint"; +import { JSXOpeningElement } from "estree-jsx"; +import { getFromPackage, checkMatchingJSXOpeningElement } from "../../helpers"; + +// Rule to add hasAnimations prop to components that support animations +module.exports = { + meta: { fixable: "code" }, + create: function (context: Rule.RuleContext) { + // Get imports from both react-core and react-table packages + const { imports: coreImports } = getFromPackage( + context, + "@patternfly/react-core" + ); + const { imports: tableImports } = getFromPackage( + context, + "@patternfly/react-table" + ); + + // Components that support hasAnimations prop + const targetComponents = [ + "FormFieldGroupExpandable", + "DualListSelector", + "TreeView", + "AlertGroup", + "SearchInput" + ]; + + // Table comes from react-table package + const tableComponents = ["Table"]; + + // Filter imports to only include target components + const targetCoreImports = coreImports.filter((specifier) => + targetComponents.includes(specifier.imported.name) + ); + + const targetTableImports = tableImports.filter((specifier) => + tableComponents.includes(specifier.imported.name) + ); + + const allTargetImports = [...targetCoreImports, ...targetTableImports]; + + const message = + "Consider adding hasAnimations prop to enable component animations."; + + return allTargetImports.length === 0 + ? {} + : { + JSXOpeningElement(node: JSXOpeningElement) { + if (checkMatchingJSXOpeningElement(node, allTargetImports)) { + // Check if hasAnimations prop already exists + const hasAnimationsAttribute = node.attributes.find( + (attr) => + attr.type === "JSXAttribute" && + attr.name.type === "JSXIdentifier" && + attr.name.name === "hasAnimations" + ); + + // Only add prop if it doesn't already exist + if (!hasAnimationsAttribute) { + context.report({ + node, + message, + fix(fixer) { + return fixer.insertTextAfter( + node.name, + " hasAnimations" + ); + }, + }); + } + } + }, + }; + }, +}; \ No newline at end of file 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 new file mode 100644 index 00000000..8b129e58 --- /dev/null +++ b/packages/eslint-plugin-pf-codemods/src/rules/v6/enableAnimations/enableAnimationsInput.tsx @@ -0,0 +1,15 @@ +import { AlertGroup, TreeView, DualListSelector, SearchInput, FormFieldGroupExpandable } from "@patternfly/react-core"; +import { Table } from "@patternfly/react-table"; + +export const EnableAnimationsInput = () => ( + <> + + {/* alerts */} + + + + + +
+ +); \ No newline at end of file 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 new file mode 100644 index 00000000..3c111b5e --- /dev/null +++ b/packages/eslint-plugin-pf-codemods/src/rules/v6/enableAnimations/enableAnimationsOutput.tsx @@ -0,0 +1,15 @@ +import { AlertGroup, TreeView, DualListSelector, SearchInput, FormFieldGroupExpandable } from "@patternfly/react-core"; +import { Table } from "@patternfly/react-table"; + +export const EnableAnimationsOutput = () => ( + <> + + {/* alerts */} + + + + + +
+ +); \ No newline at end of file