Skip to content

Commit

Permalink
feat: ion alert with styled-components
Browse files Browse the repository at this point in the history
  • Loading branch information
danilo-moreira-brisa committed Apr 17, 2024
1 parent 3841285 commit 73b2617
Show file tree
Hide file tree
Showing 4 changed files with 331 additions and 122 deletions.
245 changes: 245 additions & 0 deletions src/components/alert/__snapshots__/alert.test.tsx.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,245 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`IonAlert should render alert with type: info 1`] = `
.c0 {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
font-size: 1.4rem;
line-height: 2rem;
font-weight: 400;
color: #282b33;
border-radius: 8px;
height: 4rem;
padding: 8px 16px 8px 12px;
border: 1px solid #0bb2cb;
background-color: #e2f9fd;
border-left: 8px solid #0bb2cb;
}
.c0 .c1 svg {
fill: #0bb2cb;
}
.c2 {
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
}
<div>
<div
class="c0"
data-testid="ion-alert"
>
<div
class="c1 c2"
>
<svg
class="c-PJLV"
data-testid="ion-icon-info-solid"
fill="#282B33"
height="24"
viewBox="0 0 24 24"
width="24"
>
<path
clip-rule="evenodd"
d="M12 3C7.02944 3 3 7.02944 3 12C3 16.9706 7.02944 21 12 21C16.9706 21 21 16.9706 21 12C21 7.02944 16.9706 3 12 3ZM12 18C11.4477 18 11 17.5523 11 17V11C11 10.4477 11.4477 10 12 10C12.5523 10 13 10.4477 13 11V17C13 17.5523 12.5523 18 12 18ZM10.75 7.25C10.75 6.55964 11.3096 6 12 6C12.6904 6 13.25 6.55964 13.25 7.25C13.25 7.94036 12.6904 8.5 12 8.5C11.3096 8.5 10.75 7.94036 10.75 7.25Z"
fill-rule="evenodd"
/>
</svg>
<span>
Example message
</span>
</div>
</div>
</div>
`;

exports[`IonAlert should render alert with type: negative 1`] = `
.c0 {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
font-size: 1.4rem;
line-height: 2rem;
font-weight: 400;
color: #282b33;
border-radius: 8px;
height: 4rem;
padding: 8px 16px 8px 12px;
border: 1px solid #d6293a;
background-color: #faeaec;
border-left: 8px solid #d6293a;
}
.c0 .c1 svg {
fill: #d6293a;
}
.c2 {
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
}
<div>
<div
class="c0"
data-testid="ion-alert"
>
<div
class="c1 c2"
>
<svg
class="c-PJLV"
data-testid="ion-icon-close-solid"
fill="#282B33"
height="24"
viewBox="0 0 24 24"
width="24"
>
<path
clip-rule="evenodd"
d="M5.63604 18.364C9.15076 21.8787 14.8492 21.8787 18.364 18.364C21.8787 14.8492 21.8787 9.15076 18.364 5.63604C14.8492 2.12132 9.15076 2.12132 5.63604 5.63604C2.12132 9.15076 2.12132 14.8492 5.63604 18.364ZM15.5355 9.87868C15.9261 9.48816 15.9261 8.85499 15.5355 8.46447C15.145 8.07394 14.5118 8.07394 14.1213 8.46447L12 10.5858L9.87868 8.46447C9.48816 8.07394 8.85499 8.07394 8.46447 8.46447C8.07394 8.85499 8.07394 9.48816 8.46447 9.87868L10.5858 12L8.46447 14.1213C8.07394 14.5118 8.07394 15.145 8.46447 15.5355C8.85499 15.9261 9.48816 15.9261 9.87868 15.5355L12 13.4142L14.1213 15.5355C14.5118 15.9261 15.145 15.9261 15.5355 15.5355C15.9261 15.145 15.9261 14.5118 15.5355 14.1213L13.4142 12L15.5355 9.87868Z"
fill-rule="evenodd"
/>
</svg>
<span>
Example message
</span>
</div>
</div>
</div>
`;

exports[`IonAlert should render alert with type: success 1`] = `
.c0 {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
font-size: 1.4rem;
line-height: 2rem;
font-weight: 400;
color: #282b33;
border-radius: 8px;
height: 4rem;
padding: 8px 16px 8px 12px;
border: 1px solid #2d9f70;
background-color: #e7f9f1;
border-left: 8px solid #2d9f70;
}
.c0 .c1 svg {
fill: #2d9f70;
}
.c2 {
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
}
<div>
<div
class="c0"
data-testid="ion-alert"
>
<div
class="c1 c2"
>
<svg
class="c-PJLV"
data-testid="ion-icon-check-solid"
fill="#282B33"
height="24"
viewBox="0 0 24 24"
width="24"
>
<path
clip-rule="evenodd"
d="M12 21C16.9706 21 21 16.9706 21 12C21 7.02944 16.9706 3 12 3C7.02944 3 3 7.02944 3 12C3 16.9706 7.02944 21 12 21ZM16.7071 10.7071C17.0976 10.3166 17.0976 9.68342 16.7071 9.29289C16.3166 8.90237 15.6834 8.90237 15.2929 9.29289L11 13.5858L8.70711 11.2929C8.31658 10.9024 7.68342 10.9024 7.29289 11.2929C6.90237 11.6834 6.90237 12.3166 7.29289 12.7071L10.2929 15.7071C10.4804 15.8946 10.7348 16 11 16C11.2652 16 11.5196 15.8946 11.7071 15.7071L16.7071 10.7071Z"
fill-rule="evenodd"
/>
</svg>
<span>
Example message
</span>
</div>
</div>
</div>
`;

exports[`IonAlert should render alert with type: warning 1`] = `
.c0 {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
font-size: 1.4rem;
line-height: 2rem;
font-weight: 400;
color: #282b33;
border-radius: 8px;
height: 4rem;
padding: 8px 16px 8px 12px;
border: 1px solid #f9a915;
background-color: #fff5e0;
border-left: 8px solid #f9a915;
}
.c0 .c1 svg {
fill: #f9a915;
}
.c2 {
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
}
<div>
<div
class="c0"
data-testid="ion-alert"
>
<div
class="c1 c2"
>
<svg
class="c-PJLV"
data-testid="ion-icon-exclamation-solid"
fill="#282B33"
height="24"
viewBox="0 0 24 24"
width="24"
>
<path
clip-rule="evenodd"
d="M12 21C16.9706 21 21 16.9706 21 12C21 7.02944 16.9706 3 12 3C7.02944 3 3 7.02944 3 12C3 16.9706 7.02944 21 12 21ZM12 6C12.5523 6 13 6.44772 13 7V13C13 13.5523 12.5523 14 12 14C11.4477 14 11 13.5523 11 13V7C11 6.44772 11.4477 6 12 6ZM13.25 16.75C13.25 17.4404 12.6904 18 12 18C11.3096 18 10.75 17.4404 10.75 16.75C10.75 16.0596 11.3096 15.5 12 15.5C12.6904 15.5 13.25 16.0596 13.25 16.75Z"
fill-rule="evenodd"
/>
</svg>
<span>
Example message
</span>
</div>
</div>
</div>
`;
28 changes: 15 additions & 13 deletions src/components/alert/alert.test.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import { screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';

import { IonAlert, AlertProps } from './alert';
import { StatusType } from '../../core/types/status';
import { renderWithTheme } from '../utils/test-utils';
import { AlertProps, IonAlert } from './alert';

const defaultAlert: AlertProps = {
message: 'Example message',
};

const sut = (props = defaultAlert) => render(<IonAlert {...props} />);
const sut = (props = defaultAlert) => renderWithTheme(<IonAlert {...props} />);
const alertId = 'ion-alert';
const getAlert = () => screen.getByTestId(alertId);

Expand All @@ -36,14 +36,17 @@ describe('IonAlert', () => {
it.each(['success', 'info', 'warning', 'negative'] as StatusType[])(
'should render alert with type: %s',
(type) => {
sut({ ...defaultAlert, type });
expect(getAlert().className).toContain(`type-${type}`);
const { container } = sut({ ...defaultAlert, type });
expect(container).toMatchSnapshot();
}
);

it('should not render background alert when hideBackground is true', async () => {
await sut({ ...defaultAlert, hideBackground: true });
expect(getAlert().className).toContain('hideBackground-true');
expect(getAlert()).not.toHaveStyleRule(
'background-color',
expect.any(String)
);
});

it('should render icon close when closable is true', async () => {
Expand All @@ -52,12 +55,11 @@ describe('IonAlert', () => {
expect(iconClose).toBeTruthy();
});

it('should remove alert when click on close icon', async () => {
await sut({ ...defaultAlert, closable: true });
const iconClose = screen.getByTestId('ion-icon-close');
expect(getAlert()).toBeTruthy();
it('should emit onClose function when click on close icon', async () => {
const onClose = jest.fn();
await sut({ ...defaultAlert, closable: true, onClose });

await userEvent.click(iconClose);
expect(screen.queryByTestId(alertId)).not.toBeTruthy();
await userEvent.click(screen.getByTestId('ion-icon-close'));
expect(onClose).toHaveBeenCalledTimes(1);
});
});
44 changes: 23 additions & 21 deletions src/components/alert/alert.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
import { useState } from 'react';
import { StatusType } from '../../core/types/status';
import { IonButton } from '../button';
import ErrorBoundary from '../error/error-boundary';

import { IonIcon } from '../icons/icons';
import isValidLabel from '../utils/isValidLabel';
import { AlertStyled } from './styled';
import { Alert, Wrapper } from './styled';

export interface AlertProps {
message: string;
type?: StatusType;
closable?: boolean;
hideBackground?: boolean;
onClose?: () => void;
}

type iconType =
Expand All @@ -19,47 +20,48 @@ type iconType =
| 'info-solid'
| 'close-solid';

const icons = {
const icons: Record<StatusType, iconType> = {
info: 'info-solid',
warning: 'exclamation-solid',
negative: 'close-solid',
success: 'check-solid',
};

const sizeIcon = 24;
const SIZE_ICON = 24;

const getIcon = (alertType: StatusType) => icons[alertType] as iconType;
const getIcon = (alertType: StatusType): iconType => icons[alertType];

export const IonAlert = ({
message,
type = 'success',
closable = false,
hideBackground = false,
onClose,
}: AlertProps) => {
const [showAlert, setShowAlert] = useState(true);
const icon = getIcon(type);

if (!isValidLabel(message)) {
return <ErrorBoundary msg='Message cannot be empty' />;
}

if (!showAlert) {
return <></>;
}

return (
<AlertStyled
<Alert
data-testid='ion-alert'
type={type}
hideBackground={hideBackground}
$type={type}
$hideBackground={hideBackground}
$closable={closable}
>
<IonIcon type={icon} size={sizeIcon}></IonIcon>
<span>{message}</span>
<Wrapper>
<IonIcon type={getIcon(type)} size={SIZE_ICON}></IonIcon>
<span>{message}</span>
</Wrapper>
{closable && (
<div onClick={() => setShowAlert(false)}>
<IonIcon type='close' size={sizeIcon}></IonIcon>
</div>
<IonButton
circular
variant='ghost'
icon='close'
size='sm'
onClick={onClose}
/>
)}
</AlertStyled>
</Alert>
);
};
Loading

0 comments on commit 73b2617

Please sign in to comment.