From f0c65f07e6452de53a1e2c1664d91693a2164234 Mon Sep 17 00:00:00 2001 From: matuesz-kleszcz Date: Mon, 11 Sep 2023 11:56:44 +0200 Subject: [PATCH] refactor: TET-225 toast --- src/components/Toast/Toast.props.ts | 14 ++- src/components/Toast/Toast.styles.ts | 47 ++++++--- src/components/Toast/Toast.test.tsx | 53 +++++++--- src/components/Toast/Toast.tsx | 60 +++++++----- .../Toast/stylesBuilder/stylesBuilder.ts | 97 +++++-------------- src/components/Toast/types/ToastEmphasis.ts | 1 + src/components/Toast/types/index.ts | 1 + tsconfig.json | 2 +- 8 files changed, 137 insertions(+), 138 deletions(-) create mode 100644 src/components/Toast/types/ToastEmphasis.ts diff --git a/src/components/Toast/Toast.props.ts b/src/components/Toast/Toast.props.ts index fbb60b84..8101c1e8 100644 --- a/src/components/Toast/Toast.props.ts +++ b/src/components/Toast/Toast.props.ts @@ -1,17 +1,15 @@ -import { MouseEvent } from 'react'; +import type { MouseEvent } from 'react'; -import { ToastConfig } from './Toast.styles'; -import { ToastIntent } from './types'; +import type { ToastConfig } from './Toast.styles'; +import type { ToastEmphasis, ToastIntent } from './types'; -import { ActionProp } from '@/types'; -import { Emphasis } from '@/types/Emphasis'; -import { DeepPartial } from '@/utility-types/DeepPartial'; +import type { ActionProp } from '@/types'; export type ToastProps = { text: string; - emphasis?: Emphasis; + emphasis?: ToastEmphasis; intent?: ToastIntent; action?: ActionProp; onCloseClick?: (e: MouseEvent) => void; - custom?: DeepPartial; + custom?: ToastConfig; }; diff --git a/src/components/Toast/Toast.styles.ts b/src/components/Toast/Toast.styles.ts index f3e4e3ce..561f2561 100644 --- a/src/components/Toast/Toast.styles.ts +++ b/src/components/Toast/Toast.styles.ts @@ -1,22 +1,29 @@ -import type { ToastIntent } from './types'; -import { ButtonProps } from '../Button'; +import type { ToastEmphasis, ToastIntent } from './types'; +import type { ButtonProps } from '../Button'; -import { BaseProps } from '@/types/BaseProps'; -import { Emphasis } from '@/types/Emphasis'; +import type { BaseProps } from '@/types/BaseProps'; +import type { IconName } from '@/utility-types/IconName'; export type ToastConfig = { - emphasis: Record; - intent: Record; - closeButton: BaseProps; - innerElements: { - iconContainer: { - intent: Record }>; + emphasis?: Partial>; + intent?: Partial>; + closeButton?: BaseProps; + innerElements?: { + iconContainer?: { + intent: Partial< + Record< + ToastIntent, + { emphasis: Partial> } + > + >; } & BaseProps; - actionContainer: BaseProps; - middleDot: { - emphasis: Record>>; + actionContainer?: BaseProps; + middleDot?: { + emphasis: Partial< + Record>> + >; } & BaseProps; - closeButton: BaseProps; + closeButton?: BaseProps; }; } & BaseProps; @@ -135,3 +142,15 @@ export const defaultConfig = { }, }, } satisfies ToastConfig; + +export const resolveIconName = (intent: ToastIntent): IconName<20> | null => { + const iconConfig: Record | null> = { + neutral: null, + informative: '20-info-fill', + success: '20-check-circle-fill', + warning: '20-warning-fill', + negative: '20-alert-fill', + }; + + return iconConfig[intent]; +}; diff --git a/src/components/Toast/Toast.test.tsx b/src/components/Toast/Toast.test.tsx index 21552511..12bc9384 100644 --- a/src/components/Toast/Toast.test.tsx +++ b/src/components/Toast/Toast.test.tsx @@ -1,7 +1,9 @@ import { vi } from 'vitest'; import { Toast } from './Toast'; -import { render } from '../../tests/render'; +import { fireEvent, render } from '../../tests/render'; + +import { customPropTester } from '@/tests/customPropTester'; const getToast = (jsx: JSX.Element) => { const { getByTestId } = render(jsx); @@ -11,6 +13,32 @@ const getToast = (jsx: JSX.Element) => { const handleEventMock = vi.fn(); describe('Toast', () => { + customPropTester( + , + { + containerId: 'toast', + props: { + emphasis: ['low', 'high'], + intent: ['neutral', 'informative', 'success', 'warning', 'negative'], + }, + innerElements: { + iconContainer: [], + actionContainer: [], + middleDot: ['emphasis'], + closeButton: [], + }, + }, + ); + + beforeEach(() => { + handleEventMock.mockReset(); + }); + it('should render the toast', () => { const toast = getToast(); expect(toast).toBeInTheDocument(); @@ -84,7 +112,9 @@ describe('Toast', () => { />, ); const button = toast.querySelector('button'); - button?.click(); + if (button) { + fireEvent.click(button); + } expect(handleEventMock).toHaveBeenCalled(); }); @@ -96,7 +126,9 @@ describe('Toast', () => { />, ); const button = toast.querySelector('button'); - button?.focus(); + if (button) { + fireEvent.focus(button); + } expect(handleEventMock).toHaveBeenCalled(); }); @@ -108,7 +140,9 @@ describe('Toast', () => { />, ); const button = toast.querySelector('button'); - button?.blur(); + if (button) { + fireEvent.blur(button); + } expect(handleEventMock).toHaveBeenCalled(); }); @@ -128,15 +162,4 @@ describe('Toast', () => { button?.click(); expect(handleEventMock).toHaveBeenCalled(); }); - - it('should propagate a custom prop', () => { - const toast = getToast( - , - ); - - expect(toast).toHaveStyle('color: rgb(254, 245, 245)'); - }); }); diff --git a/src/components/Toast/Toast.tsx b/src/components/Toast/Toast.tsx index b183d5fb..b4345342 100644 --- a/src/components/Toast/Toast.tsx +++ b/src/components/Toast/Toast.tsx @@ -2,16 +2,15 @@ import { Icon } from '@virtuslab/tetrisly-icons'; import { FC, useMemo } from 'react'; import { stylesBuilder } from './stylesBuilder'; -import { ToastProps } from './Toast.props'; +import type { ToastProps } from './Toast.props'; +import { resolveIconName } from './Toast.styles'; import { Button } from '../Button'; import { IconButton } from '../IconButton'; import { tet } from '@/tetrisly'; -import { MarginProps } from '@/types'; +import type { MarginProps } from '@/types'; -type Props = ToastProps & MarginProps; - -export const Toast: FC = ({ +export const Toast: FC = ({ text, emphasis = 'low', intent = 'neutral', @@ -20,22 +19,13 @@ export const Toast: FC = ({ custom, ...restProps }) => { - const { - actionProps, - actionContainerStyles, - closeButtonProps, - closeButtonStyles, - containerStyles, - iconProps, - iconContainerStyles, - middleDotStyles, - } = useMemo( + const styles = useMemo( () => stylesBuilder({ custom, emphasis, intent, - closeButton: !!onCloseClick, + onCloseClick, }), [custom, emphasis, intent, onCloseClick], ); @@ -44,21 +34,38 @@ export const Toast: FC = ({ ? action : [action, undefined]; + const iconName = useMemo(() => resolveIconName(intent), [intent]); + + const appearance = useMemo(() => { + const buttonIntentAppearance = + intent === 'warning' ? 'reverseInverted' : 'inverted'; + return emphasis === 'high' ? buttonIntentAppearance : 'primary'; + }, [intent, emphasis]); + return ( - - {!!iconProps.name && ( - - + + {!!iconName && ( + + )} {text} {firstAction && ( - -