Skip to content

Commit

Permalink
feat: primitive testing (#1192)
Browse files Browse the repository at this point in the history
  • Loading branch information
StereoPT authored Mar 7, 2023
1 parent c30932f commit 2cf196a
Show file tree
Hide file tree
Showing 16 changed files with 514 additions and 219 deletions.
55 changes: 55 additions & 0 deletions frontend/src/components/Primitives/AlertBox.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { renderWithProviders } from '@/utils/testing/renderWithProviders';
import AlertBox, { AlertBoxProps } from './AlertBox';
import Button from './Button';

const render = (props: AlertBoxProps, children?: React.ReactNode) =>
renderWithProviders(<AlertBox {...props}>{children}</AlertBox>);

describe('Components/Primitives/AlertBox', () => {
it('should render correctly', () => {
// Arrange
const alertBoxProps: AlertBoxProps = { type: 'info' };

// Act
const { getByTestId } = render(alertBoxProps);

// Assert
expect(getByTestId('alertBox')).toBeInTheDocument();
});

it('should render title, text and type', () => {
// Arrange
const alertBoxProps: AlertBoxProps = { type: 'error', title: 'Title', text: 'This is Text' };

// Act
const { getByTestId, getByText } = render(alertBoxProps);
const alertBox = getByTestId('alertBox');
const icon = alertBox.querySelector('use')?.getAttribute('href');

// Assert
expect(alertBox).toBeInTheDocument();

expect(icon).toBe(`#blob-${alertBoxProps.type}`);

if (alertBoxProps.title) {
expect(getByText(alertBoxProps.title)).toBeInTheDocument();
}

if (alertBoxProps.text) {
expect(getByText(alertBoxProps.text)).toBeInTheDocument();
}
});

it('should render children', () => {
// Arrange
const alertBoxProps: AlertBoxProps = { type: 'error', title: 'Title', text: 'This is Text' };
const alertBoxChild = <Button>Button</Button>;

// Act
const { getByTestId, getByText } = render(alertBoxProps, alertBoxChild);

// Assert
expect(getByTestId('alertBox')).toBeInTheDocument();
expect(getByText('Button')).toBeInTheDocument();
});
});
60 changes: 60 additions & 0 deletions frontend/src/components/Primitives/AlertBox.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { CSSProps } from '@/styles/stitches/stitches.config';
import { styled } from '@stitches/react';

import Flex from '@/components/Primitives/Flex';
import Text from '@/components/Primitives/Text';
import Box from '@/components/Primitives/Box';
import Icon from '@/components/Primitives/Icon';

const AlertStyle = styled(Flex, Box, {
padding: '$16 $40',
border: '1px solid',
borderRadius: '$12',
variants: {
type: {
warning: {
backgroundColor: '$warningLightest',
border: '1px solid $colors$warningBase',
},
error: {
backgroundColor: '$dangerLightest',
border: '1px solid $colors$highlight4Base',
},
info: {
backgroundColor: '$infoLightest',
border: '1px solid $colors$infoBase',
},
},
},
});

export type AlertBoxProps = CSSProps & {
type: 'warning' | 'info' | 'error';
children?: React.ReactNode;
title?: string;
text?: string;
};

const AlertBox = ({ type, title = undefined, text = undefined, children, css }: AlertBoxProps) => (
<AlertStyle
align="center"
justify="between"
type={type}
elevation="1"
gap="24"
css={css}
data-testid="alertBox"
>
<Flex align="center" gap="24">
<Icon size={32} name={`blob-${type}`} />
<Flex direction="column" align="start" gap="4">
{!!title && <Text heading="5">{title}</Text>}

{!!text && <Text size="sm">{text}</Text>}
</Flex>
</Flex>
{children}
</AlertStyle>
);

export default AlertBox;
34 changes: 0 additions & 34 deletions frontend/src/components/Primitives/AlertBox/index.tsx

This file was deleted.

47 changes: 0 additions & 47 deletions frontend/src/components/Primitives/AlertBox/styles.tsx

This file was deleted.

88 changes: 88 additions & 0 deletions frontend/src/components/Primitives/AlertDialog.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { renderWithProviders } from '@/utils/testing/renderWithProviders';
import { fireEvent, waitFor } from '@testing-library/dom';
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogProps,
AlertDialogTrigger,
} from './AlertDialog';
import Button from './Button';
import Flex from './Flex';

const render = ({ children, ...props }: AlertDialogProps) =>
renderWithProviders(
<AlertDialog>
{/* Button to Open the Dialog */}
<AlertDialogTrigger asChild data-testid="alertDialogTrigger">
<Button>Open Alert Dialog</Button>
</AlertDialogTrigger>

{/* Actual Dialog */}
<AlertDialogContent {...props} data-testid="alertDialog">
{children}
<Flex justify="end" gap="16">
<AlertDialogCancel variant="primaryOutline">Cancel</AlertDialogCancel>
<AlertDialogAction>Action</AlertDialogAction>
</Flex>
</AlertDialogContent>
</AlertDialog>,
);

describe('Components/Primitives/AlertDialog', () => {
it('should render the trigger correctly', () => {
// Act
const { getByTestId } = render({});

// Assert
expect(getByTestId('alertDialogTrigger')).toBeInTheDocument();
});

it('should open the alert dialog when trigger is clicked', async () => {
// Act
const { getByTestId } = render({});
fireEvent.click(getByTestId('alertDialogTrigger'));

// Assert
await waitFor(() => {
expect(getByTestId('alertDialog')).toBeInTheDocument();
});
});

it('should render the title', async () => {
// Arrange
const alertDialogProps = {
title: 'Title',
};

// Act
const { getByText, getByTestId } = render(alertDialogProps);
fireEvent.click(getByTestId('alertDialogTrigger'));

// Assert
await waitFor(() => {
expect(getByTestId('alertDialog')).toBeInTheDocument();
expect(getByText(alertDialogProps.title)).toBeInTheDocument();
});
});

it('should close the dialog when the x is clicked', async () => {
// Arrange
const mockCloseFn = jest.fn();
const alertDialogProps = {
title: 'Title',
handleClose: mockCloseFn,
};

// Act
const { getByTestId } = render(alertDialogProps);
fireEvent.click(getByTestId('alertDialogTrigger'));
fireEvent.click(getByTestId('alertDialog').querySelector('svg')!);

// Assert
await waitFor(() => {
expect(mockCloseFn).toBeCalled();
});
});
});
63 changes: 27 additions & 36 deletions frontend/src/components/Primitives/AlertDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,47 +37,38 @@ const StyledContent = styled(AlertDialogPrimitive.Content, {
'&:focus': { outline: 'none' },
});

const StyledTitleContainer = styled(Flex, { px: '$32', py: '$24' });
const StyledDescription = styled('div', {
px: '$32',
py: '$24',
display: 'flex',
flexDirection: 'column',
gap: '$8',
});

export const AlertDialogTrigger = styled(AlertDialogPrimitive.Trigger, {});
export const AlertDialogAction = styled(AlertDialogPrimitive.Action, Button, {});
export const AlertDialogCancel = styled(AlertDialogPrimitive.Cancel, Button, {});

type ContentProps = { children?: ReactNode; css?: CSS; handleClose?: () => void; title?: string };

const Content: React.FC<ContentProps> = ({ children, css, handleClose, title, ...props }) => {
Content.defaultProps = {
css: undefined,
children: undefined,
handleClose: undefined,
};
return (
<AlertDialogPrimitive.Portal>
<StyledOverlay />
<StyledContent css={css} onCloseAutoFocus={handleClose} {...props}>
{title && (
<>
<StyledTitleContainer align="center" justify="between">
<Text heading="4">{title}</Text>
<AlertDialogCancel isIcon onClick={handleClose}>
<Icon name="close" css={{ color: '$primary400' }} />
</AlertDialogCancel>
</StyledTitleContainer>
<Separator />
</>
)}
<StyledDescription>{children}</StyledDescription>
</StyledContent>
</AlertDialogPrimitive.Portal>
);
export type AlertDialogProps = {
children?: ReactNode;
css?: CSS;
handleClose?: () => void;
title?: string;
};

const Content = ({ children, css, handleClose, title, ...props }: AlertDialogProps) => (
<AlertDialogPrimitive.Portal>
<StyledOverlay />
<StyledContent css={css} onCloseAutoFocus={handleClose} {...props}>
{title && (
<>
<Flex align="center" justify="between" css={{ px: '$32', py: '$24' }}>
<Text heading="4">{title}</Text>
<AlertDialogCancel isIcon onClick={handleClose}>
<Icon name="close" css={{ color: '$primary400' }} />
</AlertDialogCancel>
</Flex>
<Separator />
</>
)}
<Flex direction="column" gap="8" css={{ px: '$32', py: '$24' }}>
{children}
</Flex>
</StyledContent>
</AlertDialogPrimitive.Portal>
);

export const AlertDialog = AlertDialogPrimitive.Root;
export const AlertDialogContent = Content;
Loading

0 comments on commit 2cf196a

Please sign in to comment.