Skip to content
Draft
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
30 changes: 30 additions & 0 deletions .changeset/sixty-brooms-study.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
---
'@commercetools-uikit/async-creatable-select-field': major
'@commercetools-uikit/localized-rich-text-input': major
'@commercetools-uikit/async-select-field': major
'@commercetools-uikit/secondary-button': major
'@commercetools-uikit/primary-action-dropdown': major
'@commercetools-uikit/link-button': major
'@commercetools-uikit/select-input': major
'@commercetools-uikit/icons': major
'@commercetools-uikit/card': major
'@commercetools-uikit/link': major
'@commercetools-uikit/tag': major
'visual-testing-app': major
'@commercetools-uikit/buttons': major
'@commercetools-uikit/fields': major
'@commercetools-uikit/inputs': major
'@commercetools-frontend/ui-kit': major
---

These changes introduce a migration from react-router v5 to v6. The most obvious change is how the <Link to> component is now used. Unlike the the pattern in v5 where the `to` props in a nested route requires you to manually interpolate `match.url` for a relative route, v6 accepts a string where all urls are automatically relative routes.

```jsx
// v5: relative path requires you to manually interpolate
<Link to={`${match.url}/me`}>My Profile</Link>
```

```jsx
// v6: directly passed string is automatically interpreted as relative path
<Link to="me">My Profile</Link>
```
10 changes: 5 additions & 5 deletions design-system/src/theme-provider.visualroute.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useState } from 'react';
import { useTheme, designTokens } from '@commercetools-uikit/design-system';
import { Switch, Route } from 'react-router';
import { Routes, Route } from 'react-router';
import kebabCase from 'lodash/kebabCase';
import PropTypes from 'prop-types';
import {
Expand Down Expand Up @@ -191,8 +191,8 @@ const InteractiveRoute = () => {
};

export const component = () => (
<Switch>
<Route path={`${routePath}/interactive`} component={InteractiveRoute} />
<Route path={routePath} component={DefaultRoute} />
</Switch>
<Routes>
<Route path="interactive/*" element={<InteractiveRoute />} />
<Route path="/*" element={<DefaultRoute />} />
</Routes>
);
3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@
"react": "17.0.2",
"react-dom": "17.0.2",
"react-intl": "^6.3.2",
"react-router-dom": "5.3.4",
"react-router-dom": "6",
"react-test-renderer": "17.0.2",
"react-value": "0.2.0",
"replace": "1.2.2",
Expand All @@ -163,7 +163,6 @@
"@types/eslint": "^9.0.0",
"@types/react": "17.0.83",
"@types/react-dom": "17.0.25",
"@types/react-router": "5.1.20",
"@types/unist": "3.0.3",
"@typescript-eslint/eslint-plugin": "8.9.0",
"@typescript-eslint/parser": "8.9.0",
Expand Down
4 changes: 2 additions & 2 deletions packages/components/buttons/link-button/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,11 @@
"devDependencies": {
"react": "17.0.2",
"react-intl": "^6.3.2",
"react-router-dom": "5.3.4"
"react-router-dom": "6"
},
"peerDependencies": {
"react": "17.x",
"react-intl": "6.x",
"react-router-dom": "5.x"
"react-router-dom": "6.x"
}
}
22 changes: 18 additions & 4 deletions packages/components/buttons/link-button/src/link-button.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
fireEvent,
waitFor,
} from '../../../../../test/test-utils';
import { Routes, Route } from 'react-router-dom';
import LinkButton from './link-button';

const createTestProps = (custom) => ({
Expand Down Expand Up @@ -38,10 +39,15 @@ describe('rendering', () => {
expect(screen.getByLabelText('test-button')).toBeEnabled();
});
it('should navigate to link when clicked', async () => {
const { history } = render(<LinkButton {...props} />);
render(
<Routes>
<Route path="/" element={<LinkButton {...props} />} />
<Route path="/foo/bar" element={<div>Foo Bar Page</div>} />
</Routes>
);
fireEvent.click(screen.getByLabelText('test-button'));
await waitFor(() => {
expect(history.location.pathname).toBe('/foo/bar');
expect(screen.getByText('Foo Bar Page')).toBeInTheDocument();
});
});
it('should pass aria attributes"', () => {
Expand All @@ -52,10 +58,18 @@ describe('rendering', () => {
);
});
it('should prevent the navigation when "disabled"', async () => {
const { history } = render(<LinkButton {...props} isDisabled={true} />);
render(
<Routes>
<Route path="/" element={<LinkButton {...props} isDisabled={true} />} />
<Route path="/foo/bar" element={<div>Foo Bar Page</div>} />
</Routes>
);
fireEvent.click(screen.getByLabelText('test-button'));
await waitFor(() => {
expect(history.location.pathname).toBe('/');
expect(screen.getByLabelText('test-button')).toBeInTheDocument();
});
await waitFor(() => {
expect(screen.queryByText('Foo Bar Page')).not.toBeInTheDocument();
});
});
it('should render icon', () => {
Expand Down
4 changes: 2 additions & 2 deletions packages/components/buttons/link-button/src/link-button.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import type { LocationDescriptor } from 'history';

import { cloneElement, ReactElement } from 'react';
import { Link as ReactRouterLink } from 'react-router-dom';
import { css } from '@emotion/react';
Expand All @@ -12,6 +10,8 @@ import {
import Inline from '@commercetools-uikit/spacings-inline';
import Text from '@commercetools-uikit/text';

type LocationDescriptor = { pathname: string; search?: string; hash?: string };

export type TLinkButtonProps = {
/**
* Should describe what the button is for.
Expand Down
4 changes: 2 additions & 2 deletions packages/components/buttons/secondary-button/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,11 @@
"devDependencies": {
"react": "17.0.2",
"react-intl": "^6.3.2",
"react-router-dom": "5.3.4"
"react-router-dom": "6"
},
"peerDependencies": {
"react": "17.x",
"react-intl": "6.x",
"react-router-dom": "5.x"
"react-router-dom": "6.x"
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Link } from 'react-router-dom';
import { Routes, Route, Link } from 'react-router-dom';
import { PlusBoldIcon } from '@commercetools-uikit/icons';
import {
screen,
Expand Down Expand Up @@ -95,12 +95,25 @@ describe('rendering', () => {
});
describe('when using as', () => {
it('should navigate to link when clicked', async () => {
const { history } = render(
<SecondaryButton {...props} onClick={null} as={Link} to="/foo/bar" />
render(
<Routes>
<Route
path="/"
element={
<SecondaryButton
{...props}
onClick={null}
as={Link}
to="/foo/bar"
/>
}
/>
<Route path="/foo/bar" element={<div>Foo Bar Page</div>} />
</Routes>
);
fireEvent.click(screen.getByLabelText('Add'));
await waitFor(() => {
expect(history.location.pathname).toBe('/foo/bar');
expect(screen.getByText('Foo Bar Page')).toBeInTheDocument();
});
});
});
Expand Down
5 changes: 2 additions & 3 deletions packages/components/card/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,14 @@
"@commercetools-uikit/utils": "19.16.0",
"@emotion/react": "^11.10.5",
"@emotion/styled": "^11.10.5",
"@types/react-router-dom": "^5.3.3",
"prop-types": "15.8.1"
},
"devDependencies": {
"react": "17.0.2",
"react-router-dom": "5.3.4"
"react-router-dom": "6"
},
"peerDependencies": {
"react": "17.x",
"react-router-dom": "5.x"
"react-router-dom": "6.x"
}
}
7 changes: 1 addition & 6 deletions packages/components/card/src/card.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { screen, render, fireEvent } from '../../../../test/test-utils';
import Card from './card';
import { BrowserRouter } from 'react-router-dom'; // Required for testing <Link>

it('should render children', () => {
render(<Card>Bread</Card>);
Expand Down Expand Up @@ -34,11 +33,7 @@ it('should not call `onClick` when the card is disabled', () => {

it('should render as a react-router `Link` when `to` prop is provided', () => {
const content = 'Internal Link';
render(
<BrowserRouter>
<Card to="/internal-link">{content}</Card>
</BrowserRouter>
);
render(<Card to="/internal-link">{content}</Card>);

const link = screen.getByText(content).closest('a');
expect(link).toHaveAttribute('href', '/internal-link');
Expand Down
5 changes: 2 additions & 3 deletions packages/components/card/src/card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@ import { css } from '@emotion/react';
import { designTokens } from '@commercetools-uikit/design-system';
import { filterDataAttributes, warning } from '@commercetools-uikit/utils';
import Inset from '@commercetools-uikit/spacings-inset';
import { Link } from 'react-router-dom';
import type { LocationDescriptor } from 'history';
import { Link, LinkProps } from 'react-router-dom';

export type TCardProps = {
/**
Expand Down Expand Up @@ -34,7 +33,7 @@ export type TCardProps = {
/**
* The URL that the Card should point to. If provided, the Card will be rendered as an anchor element.
*/
to?: string | LocationDescriptor;
to?: LinkProps['to'];
/**
* A flag to indicate if the Card points to an external source.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Switch, Route } from 'react-router-dom';
import { Routes, Route } from 'react-router-dom';
import { AsyncCreatableSelectField } from '@commercetools-frontend/ui-kit';
import { Suite, Spec } from '../../../../../test/percy';

Expand Down Expand Up @@ -126,10 +126,10 @@ const DefaultRoute = () => (
);

const InteractionRoute = () => (
<Switch>
<Routes>
<Route
path={`${routePath}/interaction/without-default-options`}
render={() => (
path="without-default-options/*"
element={
<Suite>
<Spec label="with defaultOptions disabled">
<AsyncCreatableSelectField
Expand All @@ -142,12 +142,11 @@ const InteractionRoute = () => (
/>
</Spec>
</Suite>
)}
}
/>

<Route
path={`${routePath}/interaction`}
render={() => (
path="/*"
element={
<Suite>
<Spec label="with defaultOptions enabled">
<AsyncCreatableSelectField
Expand All @@ -161,14 +160,14 @@ const InteractionRoute = () => (
/>
</Spec>
</Suite>
)}
}
/>
</Switch>
</Routes>
);

export const component = () => (
<Switch>
<Route path={`${routePath}/interaction`} component={InteractionRoute} />
<Route path={routePath} component={DefaultRoute} />
</Switch>
<Routes>
<Route path="interaction/*" element={<InteractionRoute />} />
<Route path="/*" element={<DefaultRoute />} />
</Routes>
);
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Switch, Route } from 'react-router-dom';
import { Routes, Route } from 'react-router-dom';
import { AsyncSelectField } from '@commercetools-frontend/ui-kit';
import { Suite, Spec } from '../../../../../test/percy';

Expand Down Expand Up @@ -126,10 +126,10 @@ const DefaultRoute = () => (
);

const InteractionRoute = () => (
<Switch>
<Routes>
<Route
path={`${routePath}/interaction/without-default-options`}
render={() => (
path="without-default-options/*"
element={
<Suite>
<Spec omitPropsList label="with defaultOptions disabled">
<AsyncSelectField
Expand All @@ -143,11 +143,11 @@ const InteractionRoute = () => (
/>
</Spec>
</Suite>
)}
}
/>
<Route
path={`${routePath}/interaction`}
render={() => (
path="/*"
element={
<Suite>
<Spec omitPropsList label="with defaultOptions enabled">
<AsyncSelectField
Expand All @@ -161,14 +161,14 @@ const InteractionRoute = () => (
/>
</Spec>
</Suite>
)}
}
/>
</Switch>
</Routes>
);

export const component = () => (
<Switch>
<Route path={`${routePath}/interaction`} component={InteractionRoute} />
<Route exact path={routePath} component={DefaultRoute} />
</Switch>
<Routes>
<Route path="interaction/*" element={<InteractionRoute />} />
<Route path="/*" element={<DefaultRoute />} />
</Routes>
);
Loading
Loading