Skip to content

Commit

Permalink
BREAKING CHANGE(web-react): Drop HeaderDesktopActions color prop …
Browse files Browse the repository at this point in the history
…in favor of `isAtEnd` #DS-1059

See the Header: HeaderDesktopActions `isAtEnd` prop section
in the web-react package Migration Guide to version 2.
  • Loading branch information
crishpeen committed Jun 4, 2024
1 parent 2a9a878 commit 0ba662f
Show file tree
Hide file tree
Showing 28 changed files with 199 additions and 65 deletions.
26 changes: 26 additions & 0 deletions docs/migrations/web-react/MIGRATION-v2.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Introducing version 2 of the _spirit-web-react_ package
- [Dropdown: Refactored](#dropdown-refactored)
- [Grid: Breakpoint Props](#grid-breakpoint-props)
- [Grid: GridSpan Component](#grid-gridspan-component)
- [Header: HeaderDesktopActions `isAtEnd` prop](#header-headerdesktopactions-isatend-prop)
- [Modal: ModalDialog `isExpandedOnMobile` Prop](#modal-modaldialog-isexpandedonmobile-prop)
- [Modal: ModalDialog `isScrollable` Prop](#modal-modaldialog-isscrollable-prop)
- [Modal: ModalDialog Custom Height](#modal-modaldialog-custom-height)
Expand Down Expand Up @@ -237,6 +238,31 @@ Examples:
- `columnStart` = 1 + (12 - 6) / 2 = 4
- `columnStart` = 1 + (12 - 4) / 2 = 5

### Header: HeaderDesktopActions `isAtEnd` prop

The `HeaderDesktopActions` component slots were simplified and the second slot alignment is the now
available by using the `isAtEnd` prop.

The `HeaderDesktopActions` prop `color` was removed.

#### Migration Guide

Use our codemod to automatically migrate your code.

```sh
npx @lmc-eu/spirit-codemods -p <path> -t v2/web-react/header-headerdesktopactions-isatend
```

See [Codemods documentation][readme-codemods] for more details.

Or follow these steps:

Use the `HeaderDesktopActions` with `isAtEnd` prop instead of the `color="secondary"` prop.
Remove the `color` prop from the `HeaderDesktopActions` component.

- `<HeaderDesktopActions color="secondary" … />``<HeaderDesktopActions isAtEnd … />`
- `<HeaderDesktopActions color="primary" … />``<HeaderDesktopActions … />`

### Modal: ModalDialog `isExpandedOnMobile` Prop

The `isExpandedOnMobile` prop is set to `true` by default and the ModalDialog is expanded on mobile
Expand Down
22 changes: 22 additions & 0 deletions packages/codemods/src/transforms/v2/web-react/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,28 @@ npx @lmc-eu/spirit-codemods -p <path> -t v2/web-react/grid-gridspan
+ <GridItem columnStart={5} columnEnd="span 4" … />
```

### `v2/web-react/header-headerdesktopactions-isatend` — HeaderDesktopActions isAtEnd Prop

This codemod sets the `isAtEnd` prop instead of the removed `color="secondary"` prop.
Also it removes the `color="primary"` prop from the `HeaderDesktopActions` component
because it is not needed anymore.

#### Usage

```sh
npx @lmc-eu/spirit-codemods -p <path> -t v2/web-react/header-headerdesktopactions-isatend
```

#### Example

```diff
- <HeaderDesktopActions color="secondary" … />
+ <HeaderDesktopActions isAtEnd … />

- <HeaderDesktopActions color="primary" … />
+ <HeaderDesktopActions … />
```

### `v2/web-react/modal-custom-height` — Modal Custom Height

This codemod updates the `ModalDialog` component to use the `height` and
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import React from 'react';
// @ts-ignore: No declaration -- The library is not installed; we don't need to install it for fixtures.
import { HeaderDesktopActions } from '@lmc-eu/spirit-web-react';

export const MyComponent = () => (
<>
<HeaderDesktopActions />
<HeaderDesktopActions color="primary" />
<HeaderDesktopActions color="secondary" />
</>
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import React from 'react';
// @ts-ignore: No declaration -- The library is not installed; we don't need to install it for fixtures.
import { HeaderDesktopActions } from '@lmc-eu/spirit-web-react';

export const MyComponent = () => (
<>
<HeaderDesktopActions />
<HeaderDesktopActions />
<HeaderDesktopActions isAtEnd />
</>
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { testTransform } from '../../../../../tests/testUtils';

testTransform(__dirname, 'header-headerdesktopactions-isatend');
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { API, FileInfo } from 'jscodeshift';

const transform = (fileInfo: FileInfo, api: API) => {
const j = api.jscodeshift;
const root = j(fileInfo.source);

// Find import statements for the specific module and HeaderDesktopActions specifier
const importStatements = root.find(j.ImportDeclaration, {
source: {
value: (value: string) => /^@lmc-eu\/spirit-web-react(\/.*)?$/.test(value),
},
});

// Check if the module is imported
if (importStatements.length > 0) {
const componentSpecifier = importStatements.find(j.ImportSpecifier, {
imported: {
type: 'Identifier',
name: 'HeaderDesktopActions',
},
});

// Check if HeaderDesktopActions specifier is present
if (componentSpecifier.length > 0) {
// Find HeaderDesktopActions components in the module
const components = root.find(j.JSXOpeningElement, {
name: {
type: 'JSXIdentifier',
name: 'HeaderDesktopActions',
},
});

// Replace color prop
components
.find(j.JSXAttribute, {
name: {
type: 'JSXIdentifier',
name: 'color',
},
})
.forEach((attributePath) => {
if (attributePath.value.value?.type === 'StringLiteral') {
if (attributePath.value.value.value === 'primary') {
attributePath.prune();
} else if (attributePath.value.value.value === 'secondary') {
// Replace color prop with isAtEnd prop without value
attributePath.node.name.name = 'isAtEnd';
attributePath.node.value = null;
}
}
});
}
}

return root.toSource();
};

export default transform;
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,11 @@ import classNames from 'classnames';
import { useStyleProps } from '../../hooks';
import { HeaderDesktopActionsProps } from '../../types';
import { useHeaderStyleProps } from './useHeaderStyleProps';
import { HEADER_ACTIONS_COLOR_DEFAULT } from './constants';

const HeaderDesktopActions = (props: HeaderDesktopActionsProps) => {
const { color = HEADER_ACTIONS_COLOR_DEFAULT, ...restProps } = props;
const { isAtEnd = false, ...restProps } = props;

const { classProps } = useHeaderStyleProps({ actionsColor: color });
const { classProps } = useHeaderStyleProps({ isActionAtEnd: isAtEnd });
const { styleProps, props: otherProps } = useStyleProps(restProps);

return (
Expand Down
23 changes: 11 additions & 12 deletions packages/web-react/src/components/Header/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,28 +140,27 @@ and [escape hatches][readme-escape-hatches].
As the name suggests, desktop-only actions are intended to display on desktop screens only. They generally work as flex
containers that define vertical alignment and spacing.

There are two slots you can use: primary actions (aligned to left in LTR documents), and secondary actions (aligned to
right).
If you need to align actions to the end of the Header, use the `isAtEnd` prop.

👉 It is critical to **make sure all your actions fit the Header on the
desktop breakpoint**. Spirit intentionally does not provide any overflow
control here.

```jsx
<HeaderDesktopActions aria-label="Main navigation">
{/* Desktop-only primary actions */}
{/* Desktop-only actions */}
</HeaderDesktopActions>
<HeaderDesktopActions color="secondary">
{/* Desktop-only secondary actions */}
<HeaderDesktopActions isAtEnd>
{/* Desktop-only actions aligned to the end */}
</HeaderDesktopActions>
```

#### API

| Name | Type | Default | Required | Description |
| ---------- | -------------------------- | --------- | -------- | --------------------------- |
| `children` | `ReactNode` | | | Children node |
| `color` | [`primary` \| `secondary`] | `primary` || Color and alignment variant |
| Name | Type | Default | Required | Description |
| ---------- | ----------- | ------- | -------- | ------------------------------------------- |
| `children` | `ReactNode` | | | Children node |
| `isAtEnd` | `bool` | `false` || If true, the actions are aligned to the end |

The component implements the [`HTMLElement`][mdn-api-html-element] interface.

Expand Down Expand Up @@ -218,7 +217,7 @@ You can avoid using the [HeaderNav](#navigation) for standalone links. That way,
the same container:

```jsx
<HeaderDesktopActions color="secondary">
<HeaderDesktopActions isAtEnd>
<HeaderButton>Marian</HeaderButton>
<Button color="primary">Sign in</Button>
</HeaderDesktopActions>
Expand Down Expand Up @@ -476,7 +475,7 @@ composition:
{/**/}
</HeaderNav>
</HeaderDesktopActions>
<HeaderDesktopActions color="secondary">{/* Desktop-only secondary actions */}</HeaderDesktopActions>
<HeaderDesktopActions isAtEnd>{/* Desktop-only secondary actions */}</HeaderDesktopActions>
</Header>
```

Expand Down Expand Up @@ -531,7 +530,7 @@ const handleClose = () => setOpen(false);
</HeaderNavItem>
</HeaderNav>
</HeaderDesktopActions>
<HeaderDesktopActions color="secondary">
<HeaderDesktopActions isAtEnd>
<ButtonLink color="primary" href="/">Sign in</ButtonLink>
<ButtonLink color="inverted" href="/">Enterprise</ButtonLink>
</HeaderDesktopActions>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import '@testing-library/jest-dom';
import React from 'react';
import { render } from '@testing-library/react';
import { render, screen } from '@testing-library/react';
import { classNamePrefixProviderTest } from '../../../../tests/providerTests/classNamePrefixProviderTest';
import HeaderDesktopActions from '../HeaderDesktopActions';
import { stylePropsTest } from '../../../../tests/providerTests/stylePropsTest';
Expand All @@ -17,9 +17,14 @@ describe('HeaderDesktopActions', () => {
restPropsTest((props) => <HeaderDesktopActions {...props} />, 'nav');

it('should render text children', () => {
const dom = render(<HeaderDesktopActions id="test">Hello World</HeaderDesktopActions>);
render(<HeaderDesktopActions id="test">Hello World</HeaderDesktopActions>);

const element = dom.container.querySelector('nav') as HTMLElement;
expect(element.textContent).toBe('Hello World');
expect(screen.getByRole('navigation')).toHaveTextContent('Hello World');
});

it('should have isAtEnd class', () => {
render(<HeaderDesktopActions isAtEnd />);

expect(screen.getByRole('navigation')).toHaveClass('HeaderDesktopActions--isAtEnd');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@ describe('useHeaderStyleProps', () => {
expect(result.current.classProps).toBeDefined();
expect(result.current.classProps.root).toBe(`Header Header--${HEADER_COLOR_DEFAULT}`);
expect(result.current.classProps.headerButton).toBe('HeaderLink');
expect(result.current.classProps.headerDesktopActions).toBe(
`HeaderDesktopActions HeaderDesktopActions--${HEADER_ACTIONS_COLOR_DEFAULT}`,
);
expect(result.current.classProps.headerDesktopActions).toBe('HeaderDesktopActions');
expect(result.current.classProps.headerDialog).toBeDefined();
expect(result.current.classProps.headerDialog.root).toBe('HeaderDialog');
expect(result.current.classProps.headerDialog.panel).toBe('HeaderDialog__panel');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const HeaderInvertedWithActions = () => {
<SpiritLogo />
</Link>
<HeaderMobileActions dialogId="header_dialog_example" isOpen={isOpen} onOpen={handleOpen} />
<HeaderDesktopActions color="primary" aria-label="Main navigation">
<HeaderDesktopActions aria-label="Main navigation">
<HeaderNav>
<HeaderNavItem>
<HeaderLink isCurrent>Job offers</HeaderLink>
Expand All @@ -49,7 +49,7 @@ const HeaderInvertedWithActions = () => {
</HeaderNavItem>
</HeaderNav>
</HeaderDesktopActions>
<HeaderDesktopActions color="secondary">
<HeaderDesktopActions isAtEnd>
<Button color="primary">Sign in</Button>
<Button color="inverted">Enterprise</Button>
</HeaderDesktopActions>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ const HeaderInvertedWithActionsAndDialog = () => {
<SpiritLogo />
</Link>
<HeaderMobileActions dialogId="header_dialog_example_1" isOpen={isMenuOpen} onOpen={handleMenuOpen} />
<HeaderDesktopActions color="primary" aria-label="Main navigation">
<HeaderDesktopActions aria-label="Main navigation">
<HeaderNav>
<HeaderNavItem>
<HeaderLink isCurrent aria-current="page">
Expand All @@ -59,7 +59,7 @@ const HeaderInvertedWithActionsAndDialog = () => {
</HeaderNavItem>
</HeaderNav>
</HeaderDesktopActions>
<HeaderDesktopActions color="secondary">
<HeaderDesktopActions isAtEnd>
<HeaderButton
onClick={handleUserMenuOpen}
aria-controls="header_dialog_example_2"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ const HeaderWithHooks = (args: HeaderProps) => {
<SpiritLogo />
</Link>
<HeaderMobileActions dialogId="header_dialog_example_1" isOpen={isMenuOpen} onOpen={handleMenuOpen} />
<HeaderDesktopActions color="primary" aria-label="Main navigation">
<HeaderDesktopActions aria-label="Main navigation">
<HeaderNav>
<HeaderNavItem>
<HeaderLink isCurrent aria-current="page">
Expand All @@ -92,7 +92,7 @@ const HeaderWithHooks = (args: HeaderProps) => {
</HeaderNavItem>
</HeaderNav>
</HeaderDesktopActions>
<HeaderDesktopActions color="secondary" aria-label="User area">
<HeaderDesktopActions isAtEnd aria-label="User area">
<HeaderNav>
<HeaderNavItem>
<HeaderDialogButton
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,15 @@ const meta: Meta<typeof HeaderDesktopActions> = {
children: {
control: 'object',
},
color: {
control: 'radio',
options: ['primary', 'secondary'],
isAtEnd: {
control: 'boolean',
table: {
defaultValue: { summary: 'primary' },
defaultValue: { summary: false },
},
},
},
args: {
color: 'primary',
isAtEnd: false,
},
};

Expand Down Expand Up @@ -81,7 +80,7 @@ const HeaderWithHooks = (args: HeaderDesktopActionsProps) => {
</HeaderNavItem>
</HeaderNav>
</HeaderDesktopActions>
<HeaderDesktopActions color="secondary" aria-label="User area">
<HeaderDesktopActions isAtEnd aria-label="User area">
<HeaderNav>
<HeaderNavItem>
<HeaderDialogButton
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ const HeaderWithHooks = (args: HeaderDialogProps) => {
<SpiritLogo />
</Link>
<HeaderMobileActions dialogId="header_dialog_example_1" isOpen={isMenuOpen} onOpen={handleMenuOpen} />
<HeaderDesktopActions color="primary" aria-label="Main navigation">
<HeaderDesktopActions aria-label="Main navigation">
<HeaderNav>
<HeaderNavItem>
<HeaderLink isCurrent aria-current="page">
Expand All @@ -86,7 +86,7 @@ const HeaderWithHooks = (args: HeaderDialogProps) => {
</HeaderNavItem>
</HeaderNav>
</HeaderDesktopActions>
<HeaderDesktopActions color="secondary" aria-label="User area">
<HeaderDesktopActions isAtEnd aria-label="User area">
<HeaderNav>
<HeaderNavItem>
<HeaderDialogButton
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ const HeaderWithHooks = (args: HeaderDialogActionsProps) => {
<SpiritLogo />
</Link>
<HeaderMobileActions dialogId="header_dialog_example_1" isOpen={isMenuOpen} onOpen={handleMenuOpen} />
<HeaderDesktopActions color="primary" aria-label="Main navigation">
<HeaderDesktopActions aria-label="Main navigation">
<HeaderNav>
<HeaderNavItem>
<HeaderLink isCurrent aria-current="page">
Expand All @@ -81,7 +81,7 @@ const HeaderWithHooks = (args: HeaderDialogActionsProps) => {
</HeaderNavItem>
</HeaderNav>
</HeaderDesktopActions>
<HeaderDesktopActions color="secondary" aria-label="User area">
<HeaderDesktopActions isAtEnd aria-label="User area">
<HeaderNav>
<HeaderNavItem>
<HeaderDialogButton
Expand Down
Loading

0 comments on commit 0ba662f

Please sign in to comment.