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 16a8f9d8..43a465ed 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
@@ -18,6 +18,30 @@ const valid = [
];
const invalid = [
+ // --- Without includeTable (default) ---
+ {
+ 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"
+ }
+ ]
+ },
+ // --- With includeTable: true ---
+ {
+ 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"
+ }
+ ],
+ options: [{ includeTable: true }]
+ },
+ // --- Core components (unchanged) ---
{
code: "import { AlertGroup } from '@patternfly/react-core'; ",
output: "import { AlertGroup } from '@patternfly/react-core'; ",
@@ -78,27 +102,11 @@ const invalid = [
}
]
},
- {
- 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.",
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 a9f75491..b473702a 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,12 +1,18 @@
import { Rule } from "eslint";
import { JSXOpeningElement, JSXAttribute } from "estree-jsx";
-import { getFromPackage, checkMatchingJSXOpeningElement } from "../../helpers";
+import { getFromPackage, checkMatchingJSXOpeningElement, getNodeName } from "../../helpers";
import { getAttribute, getAttributeValue } from "../../helpers/JSXAttributes";
// Rule to add hasAnimations prop to components that support animations
module.exports = {
meta: { fixable: "code" },
create: function (context: Rule.RuleContext) {
+ // Get options from context (set by CLI)
+ const includeTable =
+ (context.settings && context.settings.enableAnimationsIncludeTable) ||
+ (context.options && context.options[0] && context.options[0].includeTable) ||
+ false;
+
// Get imports from both react-core and react-table packages
const { imports: coreImports } = getFromPackage(
context,
@@ -34,6 +40,7 @@ module.exports = {
targetComponents.includes(specifier.imported.name)
);
+ // Always include Table imports for detection, but only add hasAnimations if includeTable is true
const targetTableImports = tableImports.filter((specifier) =>
tableComponents.includes(specifier.imported.name)
);
@@ -68,20 +75,14 @@ module.exports = {
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);
+ const componentName = getNodeName(node);
// Special handling for DualListSelector - only add hasAnimations if isTree is true
if (componentName === "DualListSelector" && !hasValidIsTreeProp(node)) {
@@ -98,10 +99,13 @@ module.exports = {
// Only add prop if it doesn't already exist
if (!hasAnimationsAttribute) {
+ // For Table, only apply the fix if includeTable is true
+ const shouldApplyFix = componentName !== "Table" || includeTable;
+
context.report({
node,
message,
- fix(fixer) {
+ fix: shouldApplyFix ? (fixer) => {
// Insert hasAnimations at the end of existing attributes
if (node.attributes.length > 0) {
const lastAttribute = node.attributes[node.attributes.length - 1];
@@ -110,7 +114,7 @@ module.exports = {
// No existing attributes, insert after component name
return fixer.insertTextAfter(node.name, " hasAnimations");
}
- },
+ } : undefined,
});
}
}
diff --git a/packages/pf-codemods/index.js b/packages/pf-codemods/index.js
index f368bb9f..bf3e421d 100755
--- a/packages/pf-codemods/index.js
+++ b/packages/pf-codemods/index.js
@@ -73,7 +73,13 @@ async function getRulesToRemove(options) {
const pfVersions = ["v6", "v5", "v4"];
let selectedVersion = pfVersions.find((version) => options[version]);
- if (!selectedVersion) {
+ // If only enable-animations is being run, skip the PatternFly version prompt and default to v6 logic
+ let skipVersionPrompt = false;
+ if (options.only && options.only.split(",").length === 1 && options.only.includes("enable-animations")) {
+ skipVersionPrompt = true;
+ }
+
+ if (!selectedVersion && !skipVersionPrompt) {
const inquirer = await import("inquirer");
const answer = await inquirer.default.prompt([
{
@@ -87,8 +93,9 @@ async function getRulesToRemove(options) {
],
},
]);
-
selectedVersion = answer.version;
+ } else if (skipVersionPrompt) {
+ return [];
}
return pfVersions.flatMap((version) =>
@@ -99,6 +106,33 @@ async function getRulesToRemove(options) {
async function runCodemods(path, otherPaths, options) {
const prefix = "@patternfly/pf-codemods/";
+ // Determine if enable-animations is being run
+ let enableAnimationsRule = false;
+ if (options.only) {
+ enableAnimationsRule = options.only.split(",").includes("enable-animations");
+ } else {
+ enableAnimationsRule = Object.keys(configs.recommended.rules).includes(prefix + "enable-animations");
+ }
+
+ let enableAnimationsIncludeTable = false;
+ if (enableAnimationsRule) {
+ const inquirer = await import("inquirer");
+ const answer = await inquirer.default.prompt([
+ {
+ type: "list",
+ name: "includeTable",
+ message:
+ "This will update several React Core components. Would you like to include Table? (Note: Opting into table animations may require structural updates in your codebase. See our release highlights for more information.)",
+ choices: [
+ { name: "Just React Core components", value: false },
+ { name: "React Core and Table components", value: true },
+ ],
+ default: 0,
+ },
+ ]);
+ enableAnimationsIncludeTable = answer.includeTable;
+ }
+
if (options.only) {
// Set rules to error like eslint likes
configs.recommended.rules = options.only.split(",").reduce((acc, rule) => {
@@ -159,6 +193,9 @@ async function runCodemods(path, otherPaths, options) {
{},
...rulesArr.filter(filterFunc).map((r) => r.rule)
),
+ settings: enableAnimationsRule ? {
+ enableAnimationsIncludeTable: enableAnimationsIncludeTable
+ } : {}
},
});
});