Skip to content
Merged
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
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