Skip to content

Commit 6fed45d

Browse files
author
Kubit
committed
New props to NavigationCard
Include props url and component to change the component of NavigationCard between Link or Button
1 parent 8031c47 commit 6fed45d

File tree

7 files changed

+94
-20
lines changed

7 files changed

+94
-20
lines changed

src/components/navigationCard/__tests__/navigationCard.test.tsx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { ICONS, ILLUSTRATIONS } from '@/assets';
77
import { DecorativePropsType, DecorativeType } from '@/components/decorativeElement';
88
import { IIconHighlighted, IconHighlightedSizeType } from '@/components/iconHighlighted';
99
import { renderProvider } from '@/tests/renderProvider/renderProvider.utility';
10+
import { ROLES } from '@/types';
1011

1112
import { NavigationCard } from '../index';
1213

@@ -175,4 +176,16 @@ describe('NavigationCard component', () => {
175176

176177
expect(iconHighlighted).toBeInTheDocument();
177178
});
179+
180+
it('When no url is specified, the rendered element is a button', async () => {
181+
const { url, ..._mockProps } = mockProps;
182+
const { container } = renderProvider(<NavigationCard {..._mockProps} variant="PRIMARY_ICON" />);
183+
184+
const navigationCard = screen.getByRole(ROLES.BUTTON);
185+
expect(navigationCard).toBeInTheDocument();
186+
187+
const results = await axe(container);
188+
expect(container).toHTMLValidate();
189+
expect(results).toHaveNoViolations();
190+
});
178191
});

src/components/navigationCard/fragments/navigationCardInfo.tsx

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919

2020
interface INavigationCardInfo {
2121
styles: NavigationCardStylesPropsType;
22+
innerContainersComponent?: 'div' | 'span';
2223
title?: NavigationCardTextType;
2324
description?: NavigationCardTextType;
2425
tag?: NavigationCardTagType;
@@ -28,12 +29,17 @@ interface INavigationCardInfo {
2829
}
2930

3031
export const NavigationCardInfo = (props: INavigationCardInfo): JSX.Element => (
31-
<NavigationCardContentStyled>
32+
<NavigationCardContentStyled as={props.innerContainersComponent}>
3233
<NavigationCardLinesTextStyled
34+
as={props.innerContainersComponent}
3335
lines={props.styles.title?.[props.device]?.linesNumber ?? props.styles.title?.linesNumber}
3436
>
3537
<Text
36-
component={props.styles.title?.[props.device]?.component ?? props.styles.title?.component}
38+
component={
39+
props.styles.title?.[props.device]?.component ??
40+
props.styles.title?.component ??
41+
TextComponentType.SPAN
42+
}
3743
customTypography={props.styles.title}
3844
dataTestId={`${props.dataTestId}Text`}
3945
decoration={TextDecorationType.NONE}
@@ -43,8 +49,12 @@ export const NavigationCardInfo = (props: INavigationCardInfo): JSX.Element => (
4349
</Text>
4450
</NavigationCardLinesTextStyled>
4551
{props.description && (
46-
<NavigationCardDescriptionContainerStyled styles={props.styles}>
52+
<NavigationCardDescriptionContainerStyled
53+
as={props.innerContainersComponent}
54+
styles={props.styles}
55+
>
4756
<NavigationCardLinesTextStyled
57+
as={props.innerContainersComponent}
4858
lines={
4959
props.styles.description?.[props.device]?.linesNumber ??
5060
props.styles.description?.linesNumber
@@ -63,7 +73,7 @@ export const NavigationCardInfo = (props: INavigationCardInfo): JSX.Element => (
6373
</NavigationCardDescriptionContainerStyled>
6474
)}
6575
{props.tag && props.styles.containerExpandedContent && (
66-
<NavigationCardTagContainer styles={props.styles}>
76+
<NavigationCardTagContainer as={props.innerContainersComponent} styles={props.styles}>
6777
<Tag
6878
dataTestId={`${props.dataTestId}Tag`}
6979
option={props.styles.tag?.option}

src/components/navigationCard/navigationCard.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { INavigationCard, INavigationCardStandAlone, NavigationCardStylesPropsTy
1111
const NavigationCardComponent = React.forwardRef(
1212
<V extends string | unknown>(
1313
{ variant, ctv, ...props }: INavigationCard<V>,
14-
ref: React.ForwardedRef<HTMLAnchorElement> | undefined | null
14+
ref: React.ForwardedRef<HTMLAnchorElement | HTMLButtonElement> | undefined | null
1515
): JSX.Element => {
1616
const styles = useStyles<NavigationCardStylesPropsType, V>(
1717
STYLES_NAME.NAVIGATION_CARD,
@@ -36,7 +36,7 @@ NavigationCardComponent.displayName = 'NavigationCardComponent';
3636

3737
const NavigationCardBoundary = <V extends string | unknown>(
3838
props: INavigationCard<V>,
39-
ref: React.ForwardedRef<HTMLAnchorElement> | undefined | null
39+
ref: React.ForwardedRef<HTMLAnchorElement | HTMLButtonElement> | undefined | null
4040
): JSX.Element => (
4141
<ErrorBoundary
4242
fallBackComponent={
@@ -51,7 +51,7 @@ const NavigationCardBoundary = <V extends string | unknown>(
5151

5252
const NavigationCard = React.forwardRef(NavigationCardBoundary) as <V extends string | unknown>(
5353
props: React.PropsWithChildren<INavigationCard<V>> & {
54-
ref?: React.ForwardedRef<HTMLAnchorElement> | undefined | null;
54+
ref?: React.ForwardedRef<HTMLAnchorElement | HTMLButtonElement> | undefined | null;
5555
}
5656
) => ReturnType<typeof NavigationCardBoundary>;
5757

src/components/navigationCard/navigationCardStandAlone.tsx

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@ import * as React from 'react';
22

33
import { DecorativeElement } from '@/components/decorativeElement';
44
import { ElementOrIcon } from '@/components/elementOrIcon';
5-
import { Text } from '@/components/text';
5+
import { Text, TextComponentType } from '@/components/text';
66
import { useId } from '@/hooks';
77

8+
import { ButtonType } from '../button';
89
import { NavigationCardInfo } from './fragments';
910
import { buildProps } from './helpers';
1011
import {
@@ -17,8 +18,8 @@ import {
1718
import { INavigationCardStandAlone } from './types';
1819

1920
const NavigationCardStandaloneComponent = (
20-
props: INavigationCardStandAlone,
21-
ref: React.ForwardedRef<HTMLAnchorElement> | undefined | null
21+
{ type = ButtonType.BUTTON, ...props }: INavigationCardStandAlone,
22+
ref: React.ForwardedRef<HTMLAnchorElement | HTMLButtonElement> | undefined | null
2223
): JSX.Element => {
2324
const infoId = useId('infoId');
2425

@@ -29,18 +30,21 @@ const NavigationCardStandaloneComponent = (
2930
props.dataTestId
3031
);
3132

33+
const innerContainersComponent = props.url ? 'div' : 'span';
34+
3235
return (
3336
// Can not be spread -> styled component breaks
3437
<NavigationCardStyled
35-
ref={ref}
38+
ref={ref as React.ForwardedRef<HTMLButtonElement>}
3639
aria-disabled={props['aria-disabled']}
37-
as={props.component}
40+
as={props.url ? props.component : 'button'}
3841
className={props.className}
3942
dataTestId={props.dataTestId}
4043
draggable={props.draggable}
4144
role={props.role}
4245
styles={props.styles}
4346
target={props.target}
47+
type={props.url ? undefined : type}
4448
url={props.url}
4549
onClick={props.onClick}
4650
onFocus={props.onFocus}
@@ -49,27 +53,44 @@ const NavigationCardStandaloneComponent = (
4953
>
5054
<>
5155
{props.decorative && (
52-
<NavigationCardDecorativeContainer marginRight={marginRight} styles={props.styles}>
56+
<NavigationCardDecorativeContainer
57+
as={innerContainersComponent}
58+
marginRight={marginRight}
59+
styles={props.styles}
60+
>
5361
<DecorativeElement additionalProps={additionalProps} element={props.decorative} />
5462
</NavigationCardDecorativeContainer>
5563
)}
5664
<NavigationCardInfoContentStyled
65+
as={innerContainersComponent}
5766
id={infoId}
5867
isExpanded={props.styles.containerExpandedContent}
5968
>
60-
<NavigationCardLeftContentStyled isExpanded={props.styles.containerExpandedContent}>
69+
<NavigationCardLeftContentStyled
70+
as={innerContainersComponent}
71+
isExpanded={props.styles.containerExpandedContent}
72+
>
6173
<NavigationCardInfo
6274
dataTestId={props.dataTestId}
6375
description={props.description}
6476
device={props.device}
77+
innerContainersComponent={innerContainersComponent}
6578
styles={props.styles}
6679
tag={props.tag}
6780
title={props.title}
6881
/>
6982
</NavigationCardLeftContentStyled>
70-
<NavigationCardRightContentStyled aria-hidden={true} styles={props.styles}>
83+
<NavigationCardRightContentStyled
84+
aria-hidden={true}
85+
as={innerContainersComponent}
86+
styles={props.styles}
87+
>
7188
{props.styles.containerExpandedContent && (
72-
<Text customTypography={props.styles.arrowIconText} {...props.arrowIconText}>
89+
<Text
90+
component={TextComponentType.SPAN}
91+
customTypography={props.styles.arrowIconText}
92+
{...props.arrowIconText}
93+
>
7394
{props.arrowIconText?.content}
7495
</Text>
7596
)}

src/components/navigationCard/stories/argtypes.ts

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { ButtonType } from '@/components/button';
12
import { LinkTargetType } from '@/components/link';
23
import { CATEGORY_CONTROL } from '@/constants/categoryControl';
34
import { IThemeObjectVariants } from '@/designSystem/themesObject';
@@ -92,16 +93,32 @@ export const argtypes = (variants: IThemeObjectVariants, themeSelected: string):
9293
},
9394
},
9495
url: {
95-
description: 'URL (part of the IGenericLink interface)',
96+
description:
97+
'URL (part of the IGenericLink interface), when no specify, the render component will be a button',
9698
control: { type: 'text' },
97-
type: { name: 'string', required: true },
99+
type: { name: 'string' },
98100
table: {
99101
type: {
100102
summary: 'string',
101103
},
102104
category: CATEGORY_CONTROL.CONTENT,
103105
},
104106
},
107+
tye: {
108+
description: 'Type of the button (when no url is specified)',
109+
control: { type: 'text' },
110+
type: { name: 'string' },
111+
table: {
112+
defaultValue: {
113+
summary: 'ButtonType.BUTTON',
114+
},
115+
type: {
116+
summary: 'ButtonType',
117+
detail: Object.keys(ButtonType).join(', '),
118+
},
119+
category: CATEGORY_CONTROL.MODIFIERS,
120+
},
121+
},
105122
target: {
106123
description:
107124
'The target attribute specifies where to open the linked document (part of the IGenericLink interface)',
@@ -132,7 +149,7 @@ export const argtypes = (variants: IThemeObjectVariants, themeSelected: string):
132149
control: false,
133150
table: {
134151
type: {
135-
summary: '() => void',
152+
summary: 'React.MouseEventHandler<HTMLButtonElement | HTMLAnchorElement>',
136153
},
137154
category: CATEGORY_CONTROL.FUNCTIONS,
138155
},

src/components/navigationCard/stories/navigationCard.stories.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,14 @@ export const NavigationCard: Story = {
120120
},
121121
};
122122

123+
export const NavigationCardButton: Story = {
124+
args: {
125+
...commonArgs,
126+
url: undefined,
127+
themeArgs: themesObject[themeSelected][STYLES_NAME.NAVIGATION_CARD],
128+
},
129+
};
130+
123131
export const NavigationCardWithCtv: Story = {
124132
args: {
125133
...commonArgs,

src/components/navigationCard/types/navigationCard.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { ReactNode } from 'react';
22

3+
import { ButtonType } from '@/components/button';
34
import { DecorativePropsType } from '@/components/decorativeElement';
45
import { IElementOrIcon } from '@/components/elementOrIcon';
56
import { ITag } from '@/components/tag';
@@ -24,7 +25,8 @@ export type NavigationCardTagType = Omit<ITag, 'variant' | 'option' | 'children'
2425
screenReaderText?: string;
2526
};
2627

27-
export interface INavigationCardStandAlone extends Omit<IGenericLink, 'children' | 'classname'> {
28+
export interface INavigationCardStandAlone
29+
extends Omit<IGenericLink, 'children' | 'classname' | 'url' | 'onClick'> {
2830
styles: NavigationCardStylesPropsType;
2931
device: DeviceBreakpointsType;
3032
component: GenericLinkType;
@@ -34,7 +36,10 @@ export interface INavigationCardStandAlone extends Omit<IGenericLink, 'children'
3436
tag?: NavigationCardTagType;
3537
arrowIcon?: IElementOrIcon;
3638
arrowIconText?: NavigationCardTextType;
39+
url?: string;
40+
type?: ButtonType;
3741
dataTestId?: string;
42+
onClick?: React.MouseEventHandler<HTMLButtonElement | HTMLAnchorElement>;
3843
}
3944

4045
export interface INavigationCard<V = undefined extends string ? unknown : string>

0 commit comments

Comments
 (0)