Skip to content

Commit ff8537d

Browse files
authored
Add an optional paid plans badge for secondary navigation links (#2957)
1 parent 8a7ddc7 commit ff8537d

File tree

5 files changed

+62
-1
lines changed

5 files changed

+62
-1
lines changed

.changeset/shy-tips-roll.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@sumup-oss/circuit-ui": minor
3+
---
4+
5+
Added a `tier` prop to the SideNavigation component's secondary links to show an optional paid tier indicator.

packages/circuit-ui/components/SideNavigation/SideNavigation.stories.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ export const baseArgs: SideNavigationProps = {
7474
label: 'Pants',
7575
href: '/shop/pants',
7676
onClick: action('Shop → Pants'),
77+
tier: { variant: 'plus' },
7778
},
7879
{
7980
label: 'Socks',

packages/circuit-ui/components/SideNavigation/components/SecondaryLinks/SecondaryLinks.spec.tsx

+40-1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import {
2323
screen,
2424
type RenderFn,
2525
} from '../../../../util/test-utils.js';
26+
import { CircuitError } from '../../../../util/errors.js';
2627

2728
import { SecondaryLinks, type SecondaryLinksProps } from './SecondaryLinks.js';
2829

@@ -34,7 +35,7 @@ describe('SecondaryLinks', () => {
3435
return renderFn(<SecondaryLinks {...props} />);
3536
}
3637

37-
const baseProps = {
38+
const baseProps: SecondaryLinksProps = {
3839
secondaryGroups: [
3940
{
4041
secondaryLinks: [
@@ -54,6 +55,7 @@ describe('SecondaryLinks', () => {
5455
href: '/shop/socks',
5556
onClick: vi.fn(),
5657
isActive: true,
58+
tier: { variant: 'plus' },
5759
},
5860
],
5961
},
@@ -99,6 +101,43 @@ describe('SecondaryLinks', () => {
99101
expect(onClick).toHaveBeenCalledTimes(1);
100102
});
101103

104+
it('should show a badge when the badge prop is passed', () => {
105+
renderSecondaryLinks(render, baseProps);
106+
expect(screen.getByText('New')).toBeVisible();
107+
});
108+
109+
it('should show a tier indicator when the tier prop is passed', () => {
110+
renderSecondaryLinks(render, baseProps);
111+
expect(screen.getByText('plus')).toBeVisible();
112+
});
113+
114+
it('should throw an error if passed both badge and tier props', () => {
115+
const invalidProps: SecondaryLinksProps = {
116+
secondaryGroups: [
117+
{
118+
secondaryLinks: [
119+
{
120+
label: 'Shirts',
121+
href: '/shop/shirts',
122+
onClick: vi.fn(),
123+
badge: {},
124+
tier: { variant: 'plus' },
125+
},
126+
],
127+
},
128+
],
129+
};
130+
131+
const expectedError = new CircuitError(
132+
'SideNavigation',
133+
'The `badge` and `tier` props cannot be used simultaneously.',
134+
);
135+
136+
expect(() => renderSecondaryLinks(render, invalidProps)).toThrow(
137+
expectedError,
138+
);
139+
});
140+
102141
it('should have no accessibility violations', async () => {
103142
const { container } = renderSecondaryLinks(render, baseProps);
104143
const actual = await axe(container);

packages/circuit-ui/components/SideNavigation/components/SecondaryLinks/SecondaryLinks.tsx

+11
Original file line numberDiff line numberDiff line change
@@ -32,15 +32,25 @@ import type { SecondaryGroupProps, SecondaryLinkProps } from '../../types.js';
3232
import { clsx } from '../../../../styles/clsx.js';
3333
import { utilClasses } from '../../../../styles/utility.js';
3434
import { sharedClasses } from '../../../../styles/shared.js';
35+
import { TierIndicator } from '../../../TierIndicator/TierIndicator.js';
36+
import { CircuitError } from '../../../../util/errors.js';
3537

3638
import classes from './SecondaryLinks.module.css';
3739

3840
function SecondaryLink({
3941
label,
4042
badge,
43+
tier,
4144
isActive,
4245
...props
4346
}: SecondaryLinkProps) {
47+
if (process.env.NODE_ENV !== 'production' && tier && badge) {
48+
throw new CircuitError(
49+
'SideNavigation',
50+
'The `badge` and `tier` props cannot be used simultaneously.',
51+
);
52+
}
53+
4454
const { Link } = useComponents();
4555

4656
const Element = props.href ? (Link as AsPropType) : 'button';
@@ -62,6 +72,7 @@ function SecondaryLink({
6272
</Body>
6373
</Skeleton>
6474
{badge && <Badge variant="promo" as="span" {...badge} />}
75+
{tier && <TierIndicator {...tier} size="s" />}
6576
</Element>
6677
</li>
6778
);

packages/circuit-ui/components/SideNavigation/types.ts

+5
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import type { MouseEvent, KeyboardEvent, AnchorHTMLAttributes } from 'react';
1717
import type { IconComponentType } from '@sumup-oss/icons';
1818

1919
import type { BadgeProps } from '../Badge/index.js';
20+
import type { TierIndicatorProps } from '../TierIndicator/TierIndicator.js';
2021

2122
export interface PrimaryLinkProps
2223
extends AnchorHTMLAttributes<HTMLAnchorElement> {
@@ -105,4 +106,8 @@ export interface SecondaryLinkProps {
105106
* a new link or to indicate new content.
106107
*/
107108
badge?: BadgeProps;
109+
/**
110+
* An optional badge to highlight elements belonging to a specific tier.
111+
*/
112+
tier?: Omit<TierIndicatorProps, 'size'>;
108113
}

0 commit comments

Comments
 (0)