Skip to content

Commit f5ddcbc

Browse files
author
Hector Arce De Las Heras
committed
Add NavigationRow component
1 parent 0ce23fc commit f5ddcbc

File tree

13 files changed

+634
-0
lines changed

13 files changed

+634
-0
lines changed
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import userEvent from '@testing-library/user-event';
2+
3+
import { screen } from '@testing-library/react';
4+
import * as React from 'react';
5+
6+
import { axe } from 'jest-axe';
7+
8+
import { IconHighlightedSizeType, IconHighlightedType } from '@/components/iconHighlighted';
9+
import { renderProvider } from '@/tests/renderProvider/renderProvider.utility';
10+
11+
import { NavigationRow } from '../index';
12+
13+
const mockProps = {
14+
variant: 'DEFAULT',
15+
altText: 'NavigationRow',
16+
text: { content: 'text' },
17+
arrowIcon: { icon: 'RIGHTICON', altText: 'arrowIconAltText' },
18+
};
19+
20+
describe('NavigationRow component', () => {
21+
it('Should show component', async () => {
22+
const { container } = renderProvider(<NavigationRow {...mockProps} />);
23+
const text = screen.getByText(mockProps.text.content);
24+
expect(text).toBeInTheDocument();
25+
const arrowIcon = screen.getByLabelText(mockProps.arrowIcon.altText);
26+
expect(arrowIcon).toBeInTheDocument();
27+
const results = await axe(container);
28+
expect(container).toHTMLValidate();
29+
expect(results).toHaveNoViolations();
30+
});
31+
32+
it('Should call onClick function when provided', async () => {
33+
const onClick = jest.fn();
34+
renderProvider(<NavigationRow {...mockProps} onClick={onClick} />);
35+
const navigationRow = screen.getByTestId('NavigationRow');
36+
await userEvent.click(navigationRow);
37+
expect(onClick).toHaveBeenCalled();
38+
});
39+
40+
it('Should show component with description', () => {
41+
renderProvider(<NavigationRow {...mockProps} description={{ content: 'Description' }} />);
42+
expect(screen.getByText('Description')).toBeInTheDocument();
43+
});
44+
45+
it('Should show component with decorativeIcon', () => {
46+
renderProvider(
47+
<NavigationRow
48+
{...mockProps}
49+
decorativeIcon={{ icon: 'LEFTARROW', altText: 'decorativeIconAltText' }}
50+
description={{ content: 'Description' }}
51+
/>
52+
);
53+
const decorativeIcon = screen.getByLabelText('decorativeIconAltText');
54+
expect(decorativeIcon).toBeDefined();
55+
});
56+
57+
it('Should show component with iconHighlighted', () => {
58+
renderProvider(
59+
<NavigationRow
60+
{...mockProps}
61+
decorativeIcon={undefined}
62+
description={{ content: 'Description' }}
63+
iconHighlighted={{
64+
size: IconHighlightedSizeType.EXTRA_SMALL,
65+
icon: 'CLOSE',
66+
type: IconHighlightedType.INFORMATIVE,
67+
altText: 'Alt Text',
68+
}}
69+
/>
70+
);
71+
const iconHighlighted = screen.getByRole('img', { name: 'Alt Text', hidden: false });
72+
expect(iconHighlighted).toBeDefined();
73+
});
74+
});

src/components/navigationRow/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export { NavigationRow } from './navigationRow';
2+
export * from './types';
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import styled from 'styled-components';
2+
3+
import {
4+
LineSeparatorLinePropsStylesType,
5+
LineSeparatorPositionType,
6+
} from '@/components/lineSeparator';
7+
import { getStyles } from '@/utils';
8+
9+
import { INavigationRowStyled } from './types';
10+
11+
type NavigationRowStylesProps = {
12+
topLine?: boolean;
13+
bottomLine?: boolean;
14+
lineSeparatorLineStyles?: LineSeparatorLinePropsStylesType;
15+
};
16+
17+
export const NavigationRowStyled = styled.button<INavigationRowStyled & NavigationRowStylesProps>`
18+
${({ styles }) => getStyles(styles?.container)};
19+
${({ lineSeparatorLineStyles, topLine }) =>
20+
topLine && lineSeparatorLineStyles?.buildLineStyles?.(LineSeparatorPositionType.TOP)}
21+
${({ lineSeparatorLineStyles, bottomLine }) =>
22+
bottomLine && lineSeparatorLineStyles?.buildLineStyles?.(LineSeparatorPositionType.BOTTOM)}
23+
`;
24+
25+
export const TextSectionStyled = styled.span<INavigationRowStyled>`
26+
${({ styles }) => getStyles(styles?.textSectionContainer)}
27+
`;
28+
29+
export const IconAndIconHighlightedContainerStyled = styled.span<INavigationRowStyled>`
30+
${({ styles }) => getStyles(styles?.iconTextContainer)}
31+
`;
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import * as React from 'react';
2+
3+
import { LineSeparatorLinePropsStylesType } from '@/components/lineSeparator';
4+
import { STYLES_NAME } from '@/constants';
5+
import { useStyles, useStylesV2 } from '@/hooks';
6+
import { ErrorBoundary, FallbackComponent } from '@/provider/errorBoundary';
7+
8+
import { NavigationRowStandalone } from './navigationRowStandAlone';
9+
import { INavigationRow, INavigationRowStandAlone, NavigationRowStylesPropsType } from './types';
10+
11+
const NavigationRowComponent = React.forwardRef(
12+
<V extends string | unknown>(
13+
{ variant, ctv, extraCt, ...props }: INavigationRow<V>,
14+
ref: React.ForwardedRef<HTMLButtonElement> | undefined | null
15+
): JSX.Element => {
16+
const styles = useStyles<NavigationRowStylesPropsType, V>(
17+
STYLES_NAME.NAVIGATION_ROW,
18+
variant,
19+
ctv
20+
);
21+
const lineSeparatorLineStyles = useStylesV2<LineSeparatorLinePropsStylesType>({
22+
styleName: STYLES_NAME.LINE_SEPARATOR,
23+
variantName: styles.lineSeparatorLineVariant,
24+
customTokens: extraCt,
25+
isOptional: true,
26+
});
27+
28+
return (
29+
<NavigationRowStandalone
30+
ref={ref}
31+
lineSeparatorLineStyles={lineSeparatorLineStyles}
32+
styles={styles}
33+
{...props}
34+
/>
35+
);
36+
}
37+
);
38+
NavigationRowComponent.displayName = 'NavigationRowComponent';
39+
40+
const NavigationRowBoundary = <V extends string | unknown>(
41+
props: INavigationRow<V>,
42+
ref: React.ForwardedRef<HTMLButtonElement> | undefined | null
43+
): JSX.Element => (
44+
<ErrorBoundary
45+
fallBackComponent={
46+
<FallbackComponent>
47+
<NavigationRowStandalone {...(props as unknown as INavigationRowStandAlone)} ref={ref} />
48+
</FallbackComponent>
49+
}
50+
>
51+
<NavigationRowComponent {...props} ref={ref} />
52+
</ErrorBoundary>
53+
);
54+
55+
const NavigationRow = React.forwardRef(NavigationRowBoundary) as <V extends string | unknown>(
56+
props: React.PropsWithChildren<INavigationRow<V>> & {
57+
ref?: React.ForwardedRef<HTMLButtonElement> | undefined | null;
58+
}
59+
) => ReturnType<typeof NavigationRowBoundary>;
60+
61+
export { NavigationRow };
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import React, { forwardRef } from 'react';
2+
3+
import { ButtonType } from '@/components/button';
4+
import { ElementOrIcon } from '@/components/elementOrIcon';
5+
import { Text, TextComponentType } from '@/components/text';
6+
7+
import { IconHighlighted } from '../iconHighlighted';
8+
import {
9+
IconAndIconHighlightedContainerStyled,
10+
NavigationRowStyled,
11+
TextSectionStyled,
12+
} from './navigationRow.styled';
13+
import { INavigationRowStandAlone } from './types';
14+
15+
const NavigationRowStandaloneComponent = (
16+
{
17+
dataTestId = 'NavigationRow',
18+
topLine = false,
19+
bottomLine = false,
20+
...props
21+
}: INavigationRowStandAlone,
22+
ref: React.ForwardedRef<HTMLButtonElement> | undefined | null
23+
): JSX.Element => {
24+
const iconHighlightedSize = props.iconHighlighted?.size ?? props.styles.iconHighlighted?.size;
25+
return (
26+
<NavigationRowStyled
27+
ref={ref}
28+
bottomLine={bottomLine}
29+
data-testid={dataTestId}
30+
lineSeparatorLineStyles={props.lineSeparatorLineStyles}
31+
styles={props.styles}
32+
topLine={topLine}
33+
type={ButtonType.BUTTON}
34+
onClick={props.onClick}
35+
>
36+
<IconAndIconHighlightedContainerStyled styles={props.styles}>
37+
{props.decorativeIcon && !props.iconHighlighted && (
38+
<ElementOrIcon
39+
customIconStyles={props.styles.decorativeIcon}
40+
dataTestId={`${dataTestId}DecorativeIcon`}
41+
{...props.decorativeIcon}
42+
/>
43+
)}
44+
{props.iconHighlighted && props.styles.iconHighlighted && iconHighlightedSize && (
45+
<IconHighlighted
46+
backgroundColor={props.styles.iconHighlighted?.backgroundColor}
47+
color={props.styles.iconHighlighted?.color}
48+
dataTestId={`${dataTestId}IconHighlighted`}
49+
variant={props.styles.iconHighlighted?.variant}
50+
{...props.iconHighlighted}
51+
size={iconHighlightedSize}
52+
/>
53+
)}
54+
</IconAndIconHighlightedContainerStyled>
55+
<TextSectionStyled styles={props.styles}>
56+
<Text
57+
component={TextComponentType.SPAN}
58+
customTypography={props.styles.text}
59+
dataTestId={`${dataTestId}Text`}
60+
{...props.text}
61+
>
62+
{props.text.content}
63+
</Text>
64+
<Text
65+
component={TextComponentType.SPAN}
66+
customTypography={props.styles.description}
67+
dataTestId={`${dataTestId}Description`}
68+
{...props.description}
69+
>
70+
{props.description?.content}
71+
</Text>
72+
</TextSectionStyled>
73+
<ElementOrIcon
74+
customIconStyles={props.styles.arrowIcon}
75+
dataTestId={`${dataTestId}ArrowIcon`}
76+
{...props.arrowIcon}
77+
/>
78+
</NavigationRowStyled>
79+
);
80+
};
81+
82+
export const NavigationRowStandalone = forwardRef(NavigationRowStandaloneComponent);

0 commit comments

Comments
 (0)