Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
### pageToggleButton-replace-barsIcon-with-isHamburgerButton [(#11861)](https://github.com/patternfly/patternfly-react/pull/11861)

The `BarsIcon` child component should be replaced with the `isHamburgerButton` prop on PageToggleButton. This simplifies the API and provides a more semantic way to indicate that the toggle button should render as a hamburger menu button. It also allows for the button to use animation.

#### Examples

In:

```jsx
%inputExample%
```

Out:

```jsx
%outputExample%
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
const ruleTester = require("../../ruletester");
import * as rule from "./pageToggleButton-replace-barsIcon-with-isHamburgerButton";

ruleTester.run("pageToggleButton-replace-barsIcon-with-isHamburgerButton", rule, {
valid: [
{
code: `<PageToggleButton isHamburgerButton />`,
},
{
code: `import { PageToggleButton } from '@patternfly/react-core'; <PageToggleButton isHamburgerButton />`,
},
{
code: `import { PageToggleButton } from '@patternfly/react-core'; <PageToggleButton><SomeOtherIcon /></PageToggleButton>`,
},
{
code: `import { PageToggleButton } from '@patternfly/react-core'; <PageToggleButton>Some text content</PageToggleButton>`,
},
{
code: `import { PageToggleButton } from '@patternfly/react-core'; <PageToggleButton />`,
},
// PageToggleButton not imported from PatternFly, but BarsIcon is
{
code: `import { BarsIcon } from '@patternfly/react-icons'; <PageToggleButton><BarsIcon /></PageToggleButton>`,
},
// Neither PageToggleButton nor BarsIcon imported from PatternFly
{
code: `import { BarsIcon } from 'some-other-package'; <PageToggleButton><BarsIcon /></PageToggleButton>`,
},
// PageToggleButton imported from PatternFly, but BarsIcon not imported from PatternFly
{
code: `import { PageToggleButton } from '@patternfly/react-core'; import { BarsIcon } from 'some-other-package'; <PageToggleButton><BarsIcon /></PageToggleButton>`,
},
],
invalid: [
{
code: `import { PageToggleButton } from '@patternfly/react-core'; import { BarsIcon } from '@patternfly/react-icons'; <PageToggleButton><BarsIcon /></PageToggleButton>`,
output: `import { PageToggleButton } from '@patternfly/react-core'; import { BarsIcon } from '@patternfly/react-icons'; <PageToggleButton isHamburgerButton />`,
errors: [
{
message: `The BarsIcon child component should be replaced with the \`isHamburgerButton\` prop on PageToggleButton.`,
type: "JSXElement",
},
],
},
{
code: `import { PageToggleButton } from '@patternfly/react-core/dist/esm/components/PageToggleButton/index.js'; import { BarsIcon } from '@patternfly/react-icons'; <PageToggleButton><BarsIcon /></PageToggleButton>`,
output: `import { PageToggleButton } from '@patternfly/react-core/dist/esm/components/PageToggleButton/index.js'; import { BarsIcon } from '@patternfly/react-icons'; <PageToggleButton isHamburgerButton />`,
errors: [
{
message: `The BarsIcon child component should be replaced with the \`isHamburgerButton\` prop on PageToggleButton.`,
type: "JSXElement",
},
],
},
{
code: `import { PageToggleButton } from '@patternfly/react-core/dist/js/components/PageToggleButton/index.js'; import { BarsIcon } from '@patternfly/react-icons'; <PageToggleButton><BarsIcon /></PageToggleButton>`,
output: `import { PageToggleButton } from '@patternfly/react-core/dist/js/components/PageToggleButton/index.js'; import { BarsIcon } from '@patternfly/react-icons'; <PageToggleButton isHamburgerButton />`,
errors: [
{
message: `The BarsIcon child component should be replaced with the \`isHamburgerButton\` prop on PageToggleButton.`,
type: "JSXElement",
},
],
},
{
code: `import { PageToggleButton } from '@patternfly/react-core/dist/dynamic/components/PageToggleButton/index.js'; import { BarsIcon } from '@patternfly/react-icons'; <PageToggleButton><BarsIcon /></PageToggleButton>`,
output: `import { PageToggleButton } from '@patternfly/react-core/dist/dynamic/components/PageToggleButton/index.js'; import { BarsIcon } from '@patternfly/react-icons'; <PageToggleButton isHamburgerButton />`,
errors: [
{
message: `The BarsIcon child component should be replaced with the \`isHamburgerButton\` prop on PageToggleButton.`,
type: "JSXElement",
},
],
},
{
code: `import { PageToggleButton } from '@patternfly/react-core'; import { BarsIcon } from '@patternfly/react-icons'; <PageToggleButton><BarsIcon /><SomeOtherContent /></PageToggleButton>`,
output: `import { PageToggleButton } from '@patternfly/react-core'; import { BarsIcon } from '@patternfly/react-icons'; <PageToggleButton isHamburgerButton><SomeOtherContent /></PageToggleButton>`,
errors: [
{
message: `The BarsIcon child component should be replaced with the \`isHamburgerButton\` prop on PageToggleButton.`,
type: "JSXElement",
},
],
},
],
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { Rule } from "eslint";
import { JSXElement } from "estree-jsx";
import {
getFromPackage,
getChildJSXElementByName,
isReactIcon,
getAttribute,
removeElement,
makeJSXElementSelfClosing,
} from "../../helpers";

// https://github.com/patternfly/patternfly-react/pull/11861
module.exports = {
meta: { fixable: "code" },
create: function (context: Rule.RuleContext) {
const source = context.getSourceCode();
const { imports } = getFromPackage(context, "@patternfly/react-core");

const pageToggleButtonImport = imports.find(
(specifier) => specifier.imported.name === "PageToggleButton"
);

return !pageToggleButtonImport
? {}
: {
JSXElement(node: JSXElement) {
if (
node.openingElement.name.type === "JSXIdentifier" &&
pageToggleButtonImport.local.name === node.openingElement.name.name
) {
// Check if isHamburgerButton prop already exists
const isHamburgerButtonProp = getAttribute(node, "isHamburgerButton");

if (isHamburgerButtonProp) {
return; // Already has the prop, skip
}

// Check if BarsIcon is a direct child
const barsIconChild = getChildJSXElementByName(node, "BarsIcon");

if (!barsIconChild) {
return; // No BarsIcon child found
}

// Check if BarsIcon is imported from PatternFly
if (!isReactIcon(context, barsIconChild)) {
return; // BarsIcon is not from PatternFly, skip
}

context.report({
node,
message: `The BarsIcon child component should be replaced with the \`isHamburgerButton\` prop on PageToggleButton.`,
fix(fixer) {
// Check if we need to make the element self-closing
const otherChildren = node.children.filter(child => child !== barsIconChild);
const shouldBeSelfClosing = otherChildren.length === 0 && node.closingElement;

// Always add the isHamburgerButton prop
const addPropFix = fixer.insertTextAfter(
node.openingElement.name,
" isHamburgerButton"
);

if (shouldBeSelfClosing) {
// For self-closing case: add prop and make self-closing (which removes children)
return [
addPropFix,
...makeJSXElementSelfClosing(node, context, fixer, true)
];
} else {
// For non-self-closing case: add prop and remove the specific child
return [
addPropFix,
...removeElement(fixer, barsIconChild)
];
}
},
});
}
},
};
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { PageToggleButton } from "@patternfly/react-core";
import { BarsIcon } from "@patternfly/react-icons";

export const PageToggleButtonReplaceBarsIconWithIsHamburgerButtonInput = () => (
<PageToggleButton>
<BarsIcon />
</PageToggleButton>
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { PageToggleButton } from "@patternfly/react-core";

export const PageToggleButtonReplaceBarsIconWithIsHamburgerButtonOutput = () => (
<PageToggleButton isHamburgerButton />
);
29 changes: 29 additions & 0 deletions packages/pf-codemods/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2206,6 +2206,35 @@ export const PageSidebarRemoveThemePropInput = () => (

The markup for Page has changed. When either the `horizontalSubnav` or `breadcrumb` props are passed, a PageBody component will always wrap the contents.

### pageToggleButton-replace-barsIcon-with-isHamburgerButton [(#11861)](https://github.com/patternfly/patternfly-react/pull/11861)

The `BarsIcon` child component should be replaced with the `isHamburgerButton` prop on PageToggleButton. This simplifies the API and provides a more semantic way to indicate that the toggle button should render as a hamburger menu button. It also allows for the button to use animation.

#### Examples

In:

```jsx
import { PageToggleButton } from "@patternfly/react-core";
import { BarsIcon } from "@patternfly/react-icons";

export const PageToggleButtonReplaceBarsIconWithIsHamburgerButtonInput = () => (
<PageToggleButton>
<BarsIcon />
</PageToggleButton>
);
```

Out:

```jsx
import { PageToggleButton } from "@patternfly/react-core";

export const PageToggleButtonReplaceBarsIconWithIsHamburgerButtonOutput = () => (
<PageToggleButton isHamburgerButton />
);
```

### pagination-warn-markup-changed [(#10662)](https://github.com/patternfly/patternfly-react/pull/10662)

The markup for Pagination has changed. There is now a wrapper element rendered around the PaginationOptionsMenu toggle. This rule does not provide a fixer, but will throw a warning.
Expand Down
3 changes: 2 additions & 1 deletion test/test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
ChartThemeVariant,
} from "@patternfly/react-charts";
import { CodeEditor } from "@patternfly/react-code-editor";
import { FrogIcon } from "@patternfly/react-icons";
import { BarsIcon, FrogIcon } from "@patternfly/react-icons";

import {
AboutModal,
Expand Down Expand Up @@ -193,6 +193,7 @@ const themeClassName = 'pf-theme-dark';
direction={DropdownDirection.up}
/>
<DropdownContext.Provider />
<PageToggleButton><BarsIcon /></PageToggleButton>
<DropdownItem isHovered={true} />
<DropdownToggle isPrimary onToggle={} />
<DropdownToggleCheckbox
Expand Down
Loading