Skip to content

Commit

Permalink
Feat(web-react): Use Icon in Alert and add centered variant #DS-304
Browse files Browse the repository at this point in the history
  • Loading branch information
crishpeen committed Sep 6, 2022
1 parent 5cf933f commit 09e263e
Show file tree
Hide file tree
Showing 14 changed files with 120 additions and 13 deletions.
1 change: 1 addition & 0 deletions packages/web-react/src/components/Alert/Alert.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,6 @@ export default {

export { default as Alert } from './stories/Alert';
export { default as AlertColors } from './stories/AlertColors';
export { default as AlertIcons } from './stories/AlertIcons';
export { default as HTML } from './stories/AlertHtml';
export { default as Props } from './stories/AlertProps';
7 changes: 6 additions & 1 deletion packages/web-react/src/components/Alert/Alert.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,26 @@
import React, { ElementType } from 'react';
import classNames from 'classnames';
import { useAlertIcon } from './useAlertIcon';
import { useAlertStyleProps } from './useAlertStyleProps';
import { SpiritAlertProps } from '../../types';
import { useStyleProps } from '../../hooks/styleProps';
import { Icon } from '../Icon';

const defaultProps = {
color: 'success',
isCentered: false,
};

export const Alert = <T extends ElementType = 'div'>(props: SpiritAlertProps<T>): JSX.Element => {
const { elementType: ElementTag = 'div', children, ...restProps } = props;
const { classProps, props: modifiedProps } = useAlertStyleProps(restProps);
const { styleProps, props: otherProps } = useStyleProps(modifiedProps);
const iconName = useAlertIcon(restProps);

return (
<ElementTag {...otherProps} {...styleProps} className={classNames(classProps, styleProps.className)} role="alert">
{children}
<Icon name={iconName} />
<div>{children}</div>
</ElementTag>
);
};
Expand Down
12 changes: 8 additions & 4 deletions packages/web-react/src/components/Alert/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,17 @@ import { Alert } from '@lmc-eu/spirit-web-react/components';
<Alert color="success">Hey! Pay attention!</Alert>
<Alert color="danger">Hey! Pay attention!</Alert>
<Alert color="informative">Hey! Pay attention!</Alert>
<Alert color="informative" iconName="warning">Hey! Pay attention!</Alert>
<Alert color="informative" isCentered>Hey! Pay attention!</Alert>
```

## Available props

| Name | Type | Description |
| ---------- | ---------------------------------- | ----------------------------------------- |
| `children` | `any` | Content of the Alert |
| `color` | `success`, `danger`, `informative` | Color of the component, default `success` |
| Name | Type | Description |
| ------------ | ---------------------------------- | ----------------------------------------- |
| `children` | `any` | Content of the Alert |
| `color` | `success`, `danger`, `informative` | Color of the component, default `success` |
| `iconName` | `string` | Icon used in Alert |
| `isCentered` | `boolean` | If true, Alert is centered |

For detailed information see [Alert](https://github.com/lmc-eu/spirit-design-system/blob/main/packages/web/src/components/Alert/README.md) component
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@ describe('Alert', () => {
expect(element).toBeInTheDocument();
});

it('should have icon', () => {
const dom = render(<Alert>Hello World</Alert>);

const element = dom.container.querySelector('svg') as SVGSVGElement;
expect(element).toBeInTheDocument();
});

it.each([['success'], ['danger'], ['informative']])('should render color %s', (color) => {
const dom = render(<Alert color={color as AlertColor}>Hello World</Alert>);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { renderHook } from '@testing-library/react-hooks';
import { SpiritAlertProps } from '../../../types';
import { useAlertIcon } from '../useAlertIcon';

describe('useAlertIcon', () => {
it('should return defaults', () => {
const props = {};
const { result } = renderHook(() => useAlertIcon(props));

expect(result.current).toBe('info');
});

it('danger alert should return warning icon', () => {
const props = { color: 'danger' } as SpiritAlertProps;
const { result } = renderHook(() => useAlertIcon(props));

expect(result.current).toBe('warning');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,11 @@ describe('useAlertStyleProps', () => {

expect(result.current.classProps).toBe(`Alert Alert--${color}`);
});

it('should return centered', () => {
const props = { isCentered: true } as SpiritAlertProps;
const { result } = renderHook(() => useAlertStyleProps(props));

expect(result.current.classProps).toBe(`Alert Alert--center`);
});
});
12 changes: 10 additions & 2 deletions packages/web-react/src/components/Alert/stories/Alert.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
// Because there is no `dist` directory during the CI run
/* eslint-disable import/no-extraneous-dependencies, import/extensions, import/no-unresolved */
import React, { ElementType } from 'react';
import { ComponentStory } from '@storybook/react';
import Alert from '../Alert';
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: No declaration file
import icons from '@lmc-eu/spirit-icons/dist/icons';
import { IconsProvider } from '../../../context';
import { SpiritAlertProps } from '../../../types';
import Alert from '../Alert';

const Story: ComponentStory<typeof Alert> = <T extends ElementType = 'div'>(args: SpiritAlertProps<T>) => (
<Alert {...args} />
<IconsProvider value={icons}>
<Alert {...args} />
</IconsProvider>
);

Story.args = {
Expand Down
10 changes: 8 additions & 2 deletions packages/web-react/src/components/Alert/stories/AlertColors.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
// Because there is no `dist` directory during the CI run
/* eslint-disable import/no-extraneous-dependencies, import/extensions, import/no-unresolved */
import React from 'react';
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: No declaration file
import icons from '@lmc-eu/spirit-icons/dist/icons';
import { IconsProvider } from '../../../context';
import Alert from '../Alert';

// @see: https://github.com/storybookjs/storybook/issues/8104#issuecomment-932310244
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const Story = (props: unknown) => (
<>
<IconsProvider value={icons}>
<Alert color="success">This is a success alert — check it out!</Alert>
<div className="mb-500" />
<Alert color="danger">This is a danger alert — check it out!</Alert>
<div className="mb-500" />
<Alert color="informative">This is an informative alert — check it out!</Alert>
</>
</IconsProvider>
);

export default Story;
28 changes: 28 additions & 0 deletions packages/web-react/src/components/Alert/stories/AlertIcons.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Because there is no `dist` directory during the CI run
/* eslint-disable import/no-extraneous-dependencies, import/extensions, import/no-unresolved */
import React from 'react';
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: No declaration file
import icons from '@lmc-eu/spirit-icons/dist/icons';
import { IconsProvider } from '../../../context';
import Alert from '../Alert';

// @see: https://github.com/storybookjs/storybook/issues/8104#issuecomment-932310244
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const Story = (props: unknown) => (
<IconsProvider value={icons}>
<Alert color="success" iconName="profile">
This is a success alert — check it out!
</Alert>
<div className="mb-500" />
<Alert color="danger" iconName="close">
This is a danger alert — check it out!
</Alert>
<div className="mb-500" />
<Alert color="informative" iconName="warning">
This is an informative alert — check it out!
</Alert>
</IconsProvider>
);

export default Story;
11 changes: 11 additions & 0 deletions packages/web-react/src/components/Alert/useAlertIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { useIconName } from '../../hooks/useIconName';
import { SpiritAlertProps } from '../../types';

export function useAlertIcon({ color, iconName }: SpiritAlertProps) {
const iconNameValue = useIconName(color, {
default: 'info',
danger: 'warning',
});

return iconName || iconNameValue;
}
10 changes: 7 additions & 3 deletions packages/web-react/src/components/Alert/useAlertStyleProps.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import classNames from 'classnames';
import { ElementType } from 'react';
import { useClassNamePrefix } from '../../hooks/useClassNamePrefix';
import { useClassNamePrefix } from '../../hooks';
import { SpiritAlertProps, AlertProps } from '../../types';

export interface AlertStyles {
Expand All @@ -11,11 +11,15 @@ export interface AlertStyles {
}

export function useAlertStyleProps<T extends ElementType = 'div'>(props: SpiritAlertProps<T>): AlertStyles {
const { color, ...modifiedProps } = props;
const { color, isCentered, ...modifiedProps } = props;

const alertClass = useClassNamePrefix('Alert');
const alertColorClass = `${alertClass}--${color}`;
const classProps = classNames(alertClass, { [alertColorClass]: color });
const alertCenteredClass = `${alertClass}--center`;
const classProps = classNames(alertClass, {
[alertColorClass]: color,
[alertCenteredClass]: isCentered,
});

return {
classProps,
Expand Down
2 changes: 1 addition & 1 deletion packages/web-react/src/components/Icon/stories/Icon.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Because of there is no `dist` directory during the CI run
// Because there is no `dist` directory during the CI run
/* eslint-disable import/no-extraneous-dependencies, import/extensions, import/no-unresolved */
import React from 'react';
import { ComponentStory } from '@storybook/react';
Expand Down
3 changes: 3 additions & 0 deletions packages/web-react/src/hooks/useIconName.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function useIconName(key: string | undefined, iconMap: Record<string, string>) {
return key && iconMap[key] ? iconMap[key] : iconMap.default;
}
4 changes: 4 additions & 0 deletions packages/web-react/src/types/alert.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,8 @@ export interface AlertProps extends ChildrenProps, StyleProps {}
export interface SpiritAlertProps<T extends ElementType = 'div'> extends AriaAlertElementTypeProps<T>, AlertProps {
/** The color of the alert. */
color?: AlertColor;
/** Icon used in Alert. */
iconName?: string;
/** Whether the alert should be centered. */
isCentered?: boolean;
}

0 comments on commit 09e263e

Please sign in to comment.