diff --git a/packages/eslint-plugin-pf-codemods/src/rules/v6/emptyStateHeaderMoveIntoEmptyState/emptyStateHeader-move-into-emptyState.md b/packages/eslint-plugin-pf-codemods/src/rules/v6/emptyStateHeaderMoveIntoEmptyState/emptyStateHeader-move-into-emptyState.md index 95d61eae..152574d9 100644 --- a/packages/eslint-plugin-pf-codemods/src/rules/v6/emptyStateHeaderMoveIntoEmptyState/emptyStateHeader-move-into-emptyState.md +++ b/packages/eslint-plugin-pf-codemods/src/rules/v6/emptyStateHeaderMoveIntoEmptyState/emptyStateHeader-move-into-emptyState.md @@ -2,8 +2,6 @@ EmptyStateHeader and EmptyStateIcon are now rendered internally within EmptyState and should only be customized using props. Content passed to the `icon` prop on EmptyState will also be wrapped by EmptyStateIcon automatically. -Additionally, the `titleText` prop is now required on EmptyState. - #### Examples In: @@ -17,4 +15,3 @@ Out: ```jsx %outputExample% ``` - diff --git a/packages/eslint-plugin-pf-codemods/src/rules/v6/emptyStateHeaderMoveIntoEmptyState/emptyStateHeader-move-into-emptyState.test.ts b/packages/eslint-plugin-pf-codemods/src/rules/v6/emptyStateHeaderMoveIntoEmptyState/emptyStateHeader-move-into-emptyState.test.ts index 278e05ac..5f18c491 100644 --- a/packages/eslint-plugin-pf-codemods/src/rules/v6/emptyStateHeaderMoveIntoEmptyState/emptyStateHeader-move-into-emptyState.test.ts +++ b/packages/eslint-plugin-pf-codemods/src/rules/v6/emptyStateHeaderMoveIntoEmptyState/emptyStateHeader-move-into-emptyState.test.ts @@ -15,6 +15,10 @@ ruleTester.run("emptyStateHeader-move-into-emptyState", rule, { { code: `import { EmptyState } from '@patternfly/react-core'; `, }, + { + // without an EmptyStateHeader or Title text + code: `import { EmptyState } from "@patternfly/react-core"; Foo bar`, + }, ], invalid: [ { @@ -106,14 +110,13 @@ ruleTester.run("emptyStateHeader-move-into-emptyState", rule, { } from "@patternfly/react-core"; export const EmptyStateHeaderMoveIntoEmptyStateInput = () => ( - - - + + ); `, errors: [ { - message: `EmptyStateHeader has been moved inside of the EmptyState component and is now only customizable using props, and the titleText prop is now required on EmptyState. You must manually supply a titleText prop to EmptyState, then you can rerun this codemod.`, + message: `EmptyStateHeader has been moved inside of the EmptyState component and is now only customizable using props.`, type: "JSXElement", }, ], @@ -143,7 +146,7 @@ ruleTester.run("emptyStateHeader-move-into-emptyState", rule, { `, errors: [ { - message: `EmptyStateHeader has been moved inside of the EmptyState component and is now only customizable using props, and the titleText prop is now required on EmptyState.`, + message: `EmptyStateHeader has been moved inside of the EmptyState component and is now only customizable using props.`, type: "JSXElement", }, ], @@ -175,7 +178,7 @@ ruleTester.run("emptyStateHeader-move-into-emptyState", rule, { `, errors: [ { - message: `EmptyStateHeader has been moved inside of the EmptyState component and is now only customizable using props, and the titleText prop is now required on EmptyState.`, + message: `EmptyStateHeader has been moved inside of the EmptyState component and is now only customizable using props.`, type: "JSXElement", }, ], @@ -211,7 +214,7 @@ ruleTester.run("emptyStateHeader-move-into-emptyState", rule, { `, errors: [ { - message: `EmptyStateHeader has been moved inside of the EmptyState component and is now only customizable using props, and the titleText prop is now required on EmptyState.`, + message: `EmptyStateHeader has been moved inside of the EmptyState component and is now only customizable using props.`, type: "JSXElement", }, ], @@ -249,32 +252,7 @@ ruleTester.run("emptyStateHeader-move-into-emptyState", rule, { `, errors: [ { - message: `EmptyStateHeader has been moved inside of the EmptyState component and is now only customizable using props, and the titleText prop is now required on EmptyState.`, - type: "JSXElement", - }, - ], - }, - { - // without an EmptyStateHeader or titleText - code: `import { EmptyState } from "@patternfly/react-core"; - - export const EmptyStateHeaderMoveIntoEmptyStateInput = () => ( - - Foo bar - - ); - `, - output: `import { EmptyState } from "@patternfly/react-core"; - - export const EmptyStateHeaderMoveIntoEmptyStateInput = () => ( - - Foo bar - - ); - `, - errors: [ - { - message: `EmptyStateHeader has been moved inside of the EmptyState component and is now only customizable using props, and the titleText prop is now required on EmptyState. You must manually supply a titleText prop to EmptyState`, + message: `EmptyStateHeader has been moved inside of the EmptyState component and is now only customizable using props.`, type: "JSXElement", }, ], @@ -489,7 +467,7 @@ ruleTester.run("emptyStateHeader-move-into-emptyState", rule, { `, errors: [ { - message: `EmptyStateHeader has been moved inside of the EmptyState component and is now only customizable using props, and the titleText prop is now required on EmptyState.`, + message: `EmptyStateHeader has been moved inside of the EmptyState component and is now only customizable using props.`, type: "JSXElement", }, ], @@ -540,7 +518,7 @@ ruleTester.run("emptyStateHeader-move-into-emptyState", rule, { type: "JSXElement", }, { - message: `EmptyStateHeader has been moved inside of the EmptyState component and is now only customizable using props, and the titleText prop is now required on EmptyState. Additionally, the EmptyStateIcon component now wraps content passed to the icon prop automatically.`, + message: `EmptyStateHeader has been moved inside of the EmptyState component and is now only customizable using props. Additionally, the EmptyStateIcon component now wraps content passed to the icon prop automatically.`, type: "JSXElement", }, ], @@ -587,7 +565,7 @@ ruleTester.run("emptyStateHeader-move-into-emptyState", rule, { type: "JSXElement", }, { - message: `EmptyStateHeader has been moved inside of the EmptyState component and is now only customizable using props, and the titleText prop is now required on EmptyState. Additionally, the EmptyStateIcon component now wraps content passed to the icon prop automatically.`, + message: `EmptyStateHeader has been moved inside of the EmptyState component and is now only customizable using props. Additionally, the EmptyStateIcon component now wraps content passed to the icon prop automatically.`, type: "JSXElement", }, ], diff --git a/packages/eslint-plugin-pf-codemods/src/rules/v6/emptyStateHeaderMoveIntoEmptyState/emptyStateHeader-move-into-emptyState.ts b/packages/eslint-plugin-pf-codemods/src/rules/v6/emptyStateHeaderMoveIntoEmptyState/emptyStateHeader-move-into-emptyState.ts index 67fe9e1e..de091b73 100644 --- a/packages/eslint-plugin-pf-codemods/src/rules/v6/emptyStateHeaderMoveIntoEmptyState/emptyStateHeader-move-into-emptyState.ts +++ b/packages/eslint-plugin-pf-codemods/src/rules/v6/emptyStateHeaderMoveIntoEmptyState/emptyStateHeader-move-into-emptyState.ts @@ -17,6 +17,7 @@ import { getFromPackage, getChildrenAsAttributeValueText, getRemoveElementFixes, + childrenIsEmpty, } from "../../helpers"; // https://github.com/patternfly/patternfly-react/pull/9947 @@ -26,29 +27,11 @@ const composeMessage = ( hasChildren?: boolean ) => { let message = - "EmptyStateHeader has been moved inside of the EmptyState component and is now only customizable using props"; - const missingTitleTextMessage = - ", and the titleText prop is now required on EmptyState."; - - if (hasTitleText) { - message += "."; - } else { - message += missingTitleTextMessage; - } + "EmptyStateHeader has been moved inside of the EmptyState component and is now only customizable using props."; if (hasTitleText && hasChildren) { message += - " Because the children for EmptyStateHeader are now inaccessible you must remove either the children or the titleText prop"; - } else if (!hasTitleText && !hasChildren) { - message += " You must manually supply a titleText prop to EmptyState"; - } - - const hasHeader = [hasTitleText, hasIcon, hasChildren].some( - (arg) => typeof arg !== "undefined" - ); - - if (hasTitleText === hasChildren && hasHeader) { - message += ", then you can rerun this codemod."; + " Because the children for EmptyStateHeader are now inaccessible you must remove either the children or the titleText prop, then you can rerun this codemod."; } if (hasIcon) { @@ -175,19 +158,6 @@ module.exports = { const titleChild = getChildJSXElementByName(node, "Title"); - if ( - (!header || header.type !== "JSXElement") && - (!titleChild || titleChild.type !== "JSXElement") - ) { - // report without fixer if there is no header/title or the header/title is not a React element, because - // creating a titleText for the EmptyState in this case is difficult - context.report({ - node, - message: composeMessage(), - }); - return; - } - const newEmptyStateProps: string[] = []; const removeElements: JSXElement[] = []; @@ -204,6 +174,10 @@ module.exports = { "EmptyStateIcon" ); + if (!header && !titleChild && !emptyStateIconChild) { + return; + } + let iconProp: string = ""; if (emptyStateIconChild) { @@ -231,19 +205,12 @@ module.exports = { hasTitleText = !!titleTextAttribute; hasIcon ||= !!headerIconAttribute; - hasChildren ||= header.children.length > 0; + hasChildren ||= !childrenIsEmpty(header.children); const message = composeMessage(hasTitleText, hasIcon, hasChildren); - if (!titleTextAttribute && !hasChildren) { - // report without fixer if there is a header, but it doesn't have titleText or children, because creating a - // titleText for the EmptyState in this case is difficult - context.report({ node, message }); - return; - } - if (titleTextAttribute && hasChildren) { - // report without fixer if there is the header has a titleText and children, because creating an accessible + // report without fixer if the header has both titleText and children, because creating an accessible // titleText for the EmptyState in this case is difficult context.report({ node, message }); return; @@ -267,12 +234,14 @@ module.exports = { titleTextAttribute ); - const titleText = - titleTextPropValue || - `titleText=${getChildrenAsAttributeValueText( - context, - header.children - )}`; + const childrenTitleText = hasChildren + ? `titleText=${getChildrenAsAttributeValueText( + context, + header.children + )}` + : ""; + + const titleText = titleTextPropValue || childrenTitleText; const iconPropValue = getExpression(headerIconAttribute?.value); diff --git a/packages/eslint-plugin-pf-codemods/src/rules/v6/emptyStateHeaderMoveIntoEmptyState/emptyStateHeaderMoveIntoEmptyStateInput.tsx b/packages/eslint-plugin-pf-codemods/src/rules/v6/emptyStateHeaderMoveIntoEmptyState/emptyStateHeaderMoveIntoEmptyStateInput.tsx index 04605a85..ef8915e3 100644 --- a/packages/eslint-plugin-pf-codemods/src/rules/v6/emptyStateHeaderMoveIntoEmptyState/emptyStateHeaderMoveIntoEmptyStateInput.tsx +++ b/packages/eslint-plugin-pf-codemods/src/rules/v6/emptyStateHeaderMoveIntoEmptyState/emptyStateHeaderMoveIntoEmptyStateInput.tsx @@ -26,3 +26,20 @@ export const EmptyStateWithoutHeaderMoveIntoEmptyStateInput = () => ( Body ); + +export const EmptyStateHeaderWithoutTitleTextMoveIntoEmptyStateInput = () => ( + + } + /> + +); + +export const EmptyStateWithoutHeaderAndTitleTextMoveIntoEmptyStateInput = + () => ( + + + Body + + ); diff --git a/packages/eslint-plugin-pf-codemods/src/rules/v6/emptyStateHeaderMoveIntoEmptyState/emptyStateHeaderMoveIntoEmptyStateOutput.tsx b/packages/eslint-plugin-pf-codemods/src/rules/v6/emptyStateHeaderMoveIntoEmptyState/emptyStateHeaderMoveIntoEmptyStateOutput.tsx index 1d4f3f45..1902bb64 100644 --- a/packages/eslint-plugin-pf-codemods/src/rules/v6/emptyStateHeaderMoveIntoEmptyState/emptyStateHeaderMoveIntoEmptyStateOutput.tsx +++ b/packages/eslint-plugin-pf-codemods/src/rules/v6/emptyStateHeaderMoveIntoEmptyState/emptyStateHeaderMoveIntoEmptyStateOutput.tsx @@ -19,3 +19,15 @@ export const EmptyStateWithoutHeaderMoveIntoEmptyStateInput = () => ( Body ); + +export const EmptyStateHeaderWithoutTitleTextMoveIntoEmptyStateInput = () => ( + + +); + +export const EmptyStateWithoutHeaderAndTitleTextMoveIntoEmptyStateInput = + () => ( + + Body + + );