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
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,6 @@
"@types/eslint": "^9.0.0",
"@types/react": "^19.0.3",
"@types/react-dom": "19.2.1",
"@types/react-router": "5.1.20",
"@types/unist": "3.0.3",
"@typescript-eslint/eslint-plugin": "8.46.0",
"@typescript-eslint/parser": "8.46.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
import { Link } from 'react-router-dom';
import { PlusThinIcon } from '@commercetools-uikit/icons';
import { screen, render } from '../../../../../test/test-utils';
import FlatButton from './flat-button';

// A simple test link component to test the polymorphic `as` prop
const TestLink = ({ to, children, ...rest }) => (
<a href={to} {...rest}>
{children}
</a>
);

const createTestProps = (props) => ({
tone: 'primary',
label: 'Add',
Expand Down Expand Up @@ -84,7 +90,7 @@ describe('rendering', () => {
describe('when as is a React component', () => {
it('should render as that component', () => {
render(
<FlatButton {...props} as={Link} to="foo/bar" target="_BLANK" />
<FlatButton {...props} as={TestLink} to="/foo/bar" target="_BLANK" />
);

const linkButton = screen.getByLabelText('Add');
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
import { Link } from 'react-router-dom';
import { PlusBoldIcon } from '@commercetools-uikit/icons';
import { screen, render } from '../../../../../test/test-utils';
import IconButton from './icon-button';

// A simple test link component to test the polymorphic `as` prop
const TestLink = ({ to, children, ...rest }) => (
<a href={to} {...rest}>
{children}
</a>
);

const createTestProps = (custom) => ({
type: 'button',
label: 'test-button',
Expand Down Expand Up @@ -63,7 +69,7 @@ describe('rendering', () => {
describe('when as is a React component', () => {
it('should render as that component', () => {
render(
<IconButton {...props} as={Link} to="foo/bar" target="_BLANK" />
<IconButton {...props} as={TestLink} to="/foo/bar" target="_BLANK" />
);

const linkButton = screen.getByLabelText('test-button');
Expand Down
7 changes: 3 additions & 4 deletions packages/components/buttons/link-button/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"@babel/runtime-corejs3": "^7.20.13",
"@commercetools-uikit/accessible-button": "20.5.0",
"@commercetools-uikit/design-system": "20.5.0",
"@commercetools-uikit/router-provider": "20.5.0",
"@commercetools-uikit/spacings-inline": "20.5.0",
"@commercetools-uikit/text": "20.5.0",
"@commercetools-uikit/utils": "20.5.0",
Expand All @@ -32,12 +33,10 @@
},
"devDependencies": {
"react": "19.2.0",
"react-intl": "^7.1.4",
"react-router-dom": "5.3.4"
"react-intl": "^7.1.4"
},
"peerDependencies": {
"react": "19.x",
"react-intl": "7.x",
"react-router-dom": "5.x"
"react-intl": "7.x"
}
}
37 changes: 24 additions & 13 deletions packages/components/buttons/link-button/src/link-button.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
import type { LocationDescriptor } from 'history';
import type { TLocationDescriptor } from '@commercetools-uikit/router-provider';

import { cloneElement, ReactElement } from 'react';
import { Link as ReactRouterLink } from 'react-router-dom';
import { css } from '@emotion/react';
import styled from '@emotion/styled';
import {
useNavigate,
locationDescriptorToString,
shouldNavigate,
} from '@commercetools-uikit/router-provider';
import {
designTokens,
type TIconProps,
} from '@commercetools-uikit/design-system';
import { css } from '@emotion/react';
import styled from '@emotion/styled';
import {
useWarnDeprecatedComponent,
filterInvalidAttributes,
Expand All @@ -24,7 +28,7 @@ export type TLinkButtonProps = {
/**
* A string or an object representing the link location.
*/
to: string | LocationDescriptor;
to: string | TLocationDescriptor;

/**
* The icon of the button.
Expand Down Expand Up @@ -58,9 +62,7 @@ const hoverStyles = css`
}
`;

const StyledExternalLink = styled.a<
Pick<TLinkButtonProps, 'to'> & { disabled?: boolean }
>`
const StyledExternalLink = styled.a<{ disabled?: boolean }>`
display: inline-flex;
align-items: center;
font-size: 1rem;
Expand Down Expand Up @@ -102,6 +104,7 @@ const LinkButton = ({
...props
}: TLinkButtonProps) => {
useWarnDeprecatedComponent('LinkButton');
const navigate = useNavigate();
const remainingProps = filterInvalidAttributes({
isDisabled,
isExternal,
Expand All @@ -114,8 +117,6 @@ const LinkButton = ({
}

return (
// @ts-ignore: the `to` prop in this case is not required
// to be provided, instead the `href` is.
<StyledExternalLink
href={props.to}
onClick={isDisabled ? (event) => event.preventDefault() : undefined}
Expand All @@ -135,10 +136,20 @@ const LinkButton = ({

return (
<StyledExternalLink
as={ReactRouterLink}
to={props.to}
href={locationDescriptorToString(props.to)}
disabled={isDisabled}
onClick={isDisabled ? (event) => event.preventDefault() : undefined}
onClick={
isDisabled
? (event) => event.preventDefault()
: navigate
? (event) => {
if (shouldNavigate(event)) {
event.preventDefault();
navigate(props.to);
}
}
: undefined
}
data-track-component="LinkButton"
aria-label={props.label}
{...remainingProps}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
import { Link } from 'react-router-dom';
import { PlusBoldIcon } from '@commercetools-uikit/icons';
import { screen, render } from '../../../../../test/test-utils';
import PrimaryButton from './primary-button';

// A simple test link component to test the polymorphic `as` prop
const TestLink = ({ to, children, ...rest }) => (
<a href={to} {...rest}>
{children}
</a>
);

const createTestProps = (custom) => ({
label: 'Add',
iconLeft: <PlusBoldIcon data-testid="icon" />,
Expand Down Expand Up @@ -111,7 +117,12 @@ describe('rendering', () => {
describe('when as is a React component', () => {
it('should render as that component', () => {
render(
<PrimaryButton {...props} as={Link} to="foo/bar" target="_BLANK" />
<PrimaryButton
{...props}
as={TestLink}
to="/foo/bar"
target="_BLANK"
/>
);

const linkButton = screen.getByLabelText('Add');
Expand Down
7 changes: 3 additions & 4 deletions packages/components/buttons/secondary-button/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"@babel/runtime-corejs3": "^7.20.13",
"@commercetools-uikit/accessible-button": "20.5.0",
"@commercetools-uikit/design-system": "20.5.0",
"@commercetools-uikit/router-provider": "20.5.0",
"@commercetools-uikit/spacings-inline": "20.5.0",
"@commercetools-uikit/text": "20.5.0",
"@commercetools-uikit/utils": "20.5.0",
Expand All @@ -32,12 +33,10 @@
},
"devDependencies": {
"react": "19.2.0",
"react-intl": "^7.1.4",
"react-router-dom": "5.3.4"
"react-intl": "^7.1.4"
},
"peerDependencies": {
"react": "19.x",
"react-intl": "7.x",
"react-router-dom": "5.x"
"react-intl": "7.x"
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { Link } from 'react-router-dom';
import { PlusBoldIcon } from '@commercetools-uikit/icons';
import {
screen,
Expand Down Expand Up @@ -93,10 +92,10 @@ describe('rendering', () => {
expect(screen.getByLabelText('Add')).toHaveAttribute('type', 'reset');
});
});
describe('when using as', () => {
describe('when using to', () => {
it('should navigate to link when clicked', async () => {
const { history } = render(
<SecondaryButton {...props} onClick={null} as={Link} to="/foo/bar" />
<SecondaryButton {...props} onClick={null} to="/foo/bar" />
);
fireEvent.click(screen.getByLabelText('Add'));
await waitFor(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,12 @@ import {
ComponentPropsWithRef,
cloneElement,
} from 'react';
import { Link } from 'react-router-dom';
import { css } from '@emotion/react';
import {
useNavigate,
locationDescriptorToString,
shouldNavigate,
} from '@commercetools-uikit/router-provider';
import { designTokens } from '@commercetools-uikit/design-system';
import Inline from '@commercetools-uikit/spacings-inline';
import {
Expand Down Expand Up @@ -163,6 +167,7 @@ export const SecondaryButton = <
isToggleButton = false,
...props
}: TSecondaryButtonProps<TStringOrComponent>) => {
const navigate = useNavigate();
const isActive = Boolean(isToggleButton && props.isToggled);
const shouldUseLinkTag = !props.isDisabled && Boolean(props.to);
const buttonAttributes = {
Expand All @@ -175,7 +180,11 @@ export const SecondaryButton = <
isToggleButton,
...props,
}),
...(shouldUseLinkTag ? { to: props.to } : {}),
...(shouldUseLinkTag
? props.as
? { to: props.to }
: { href: locationDescriptorToString(props.to as string) }
: {}),
};

warning(
Expand Down Expand Up @@ -215,11 +224,20 @@ export const SecondaryButton = <

return (
<AccessibleButton
as={(shouldUseLinkTag ? Link : props.as) as ComponentType}
as={(shouldUseLinkTag ? props.as ?? 'a' : props.as) as ComponentType}
type={type}
buttonAttributes={buttonAttributes}
label={props.label}
onClick={props.onClick}
onClick={
shouldUseLinkTag && !props.as && navigate
? (((event: MouseEvent<HTMLElement>) => {
if (shouldNavigate(event)) {
event.preventDefault();
navigate(props.to);
}
}) as TSecondaryButtonProps['onClick'])
: props.onClick
}
isToggleButton={isToggleButton}
isToggled={props.isToggled}
isDisabled={props.isDisabled}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
import { Link } from 'react-router-dom';
import { PlusBoldIcon } from '@commercetools-uikit/icons';
import { screen, render } from '../../../../../test/test-utils';
import SecondaryIconButton from './secondary-icon-button';

// A simple test link component to test the polymorphic `as` prop
const TestLink = ({ to, children, ...rest }) => (
<a href={to} {...rest}>
{children}
</a>
);

const createTestProps = (custom) => ({
label: 'test-button',
icon: <PlusBoldIcon data-testid="icon" />,
Expand Down Expand Up @@ -113,8 +119,8 @@ describe('rendering', () => {
render(
<SecondaryIconButton
{...props}
as={Link}
to="foo/bar"
as={TestLink}
to="/foo/bar"
target="_BLANK"
/>
);
Expand Down
10 changes: 4 additions & 6 deletions packages/components/card/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,16 @@
"@babel/runtime": "^7.20.13",
"@babel/runtime-corejs3": "^7.20.13",
"@commercetools-uikit/design-system": "20.5.0",
"@commercetools-uikit/router-provider": "20.5.0",
"@commercetools-uikit/spacings-inset": "20.5.0",
"@commercetools-uikit/utils": "20.5.0",
"@emotion/react": "^11.10.5",
"@emotion/styled": "^11.10.5",
"@types/react-router-dom": "^5.3.3"
"@emotion/styled": "^11.10.5"
},
"devDependencies": {
"react": "19.2.0",
"react-router-dom": "5.3.4"
"react": "19.2.0"
},
"peerDependencies": {
"react": "19.x",
"react-router-dom": "5.x"
"react": "19.x"
}
}
9 changes: 2 additions & 7 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 @@ -32,13 +31,9 @@ it('should not call `onClick` when the card is disabled', () => {
expect(handleClick).not.toHaveBeenCalled();
});

it('should render as a react-router `Link` when `to` prop is provided', () => {
it('should render as a 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
9 changes: 6 additions & 3 deletions packages/components/card/src/card.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ import type { ComponentProps } from 'react';
import type { Meta, StoryFn } from '@storybook/react';

import Card from './card';
import { BrowserRouter as Router } from 'react-router-dom';
import { UIKitProvider } from '@commercetools-uikit/ui-kit-provider';

// no-op navigate for storybook
const storyRouter = { navigate: () => {} };

type CardProps = ComponentProps<typeof Card>;

Expand All @@ -20,9 +23,9 @@ export default meta;

export const BasicExample: StoryFn<CardProps> = (args) => {
return (
<Router>
<UIKitProvider router={storyRouter}>
<Card {...args} />
</Router>
</UIKitProvider>
);
};

Expand Down
Loading
Loading