From 235ea21a548d39b6ebf22e31d9037abc83eb9fcd Mon Sep 17 00:00:00 2001 From: Alexey Date: Wed, 9 Oct 2024 17:03:00 +0300 Subject: [PATCH] fix(Toaster): change spacing before actions (#1891) --- src/components/Button/Button.tsx | 24 +---- src/components/Button/constants.ts | 23 +++++ src/components/Toaster/Toast/Toast.scss | 21 +++-- .../Toaster/__stories__/Toaster.stories.tsx | 93 +++++++++++++++---- src/components/Toaster/constants.ts | 1 + src/components/Toaster/types.ts | 4 +- 6 files changed, 116 insertions(+), 50 deletions(-) create mode 100644 src/components/Button/constants.ts create mode 100644 src/components/Toaster/constants.ts diff --git a/src/components/Button/Button.tsx b/src/components/Button/Button.tsx index 02b295d71c..6ffa7adef4 100644 --- a/src/components/Button/Button.tsx +++ b/src/components/Button/Button.tsx @@ -9,31 +9,11 @@ import {eventBroker} from '../utils/event-broker'; import {isOfType} from '../utils/isOfType'; import {ButtonIcon, getIconSide} from './ButtonIcon'; +import type {BUTTON_VIEWS} from './constants'; import './Button.scss'; -export type ButtonView = - | 'normal' // Grey background, no border - | 'action' // Branded background, no border - | 'outlined' // No background, grey border - | 'outlined-info' // No background, with info-type border color - | 'outlined-success' // No background, with success-type border color - | 'outlined-warning' // No background, with warning-type border color - | 'outlined-danger' // No background, with danger-type border color - | 'outlined-utility' // No background, with utility-type border color - | 'outlined-action' // No background, with branded border color - | 'raised' // With white background and shadow - | 'flat' // No background, no border - | 'flat-secondary' // No background, no border, secondary-type text color - | 'flat-info' // No background, no border, info-type text color - | 'flat-success' // No background, no border, success-type text color - | 'flat-warning' // No background, no border, warning-type text color - | 'flat-danger' // No background, no border, danger-type text color - | 'flat-utility' // No background, no border, utility-type text color - | 'flat-action' // No background, no border, branded text color - | 'normal-contrast' // normal button appearance with contrast background - | 'outlined-contrast' // outlined button appearance with contrast background - | 'flat-contrast'; // flat button appearance with contrast background +export type ButtonView = (typeof BUTTON_VIEWS)[number]; export type ButtonSize = 'xs' | 's' | 'm' | 'l' | 'xl'; diff --git a/src/components/Button/constants.ts b/src/components/Button/constants.ts new file mode 100644 index 0000000000..16c26c1e2f --- /dev/null +++ b/src/components/Button/constants.ts @@ -0,0 +1,23 @@ +export const BUTTON_VIEWS = [ + 'normal', // Grey background, no border + 'action', // Branded background, no border + 'outlined', // No background, grey border + 'outlined-info', // No background, with info-type border color + 'outlined-success', // No background, with success-type border color + 'outlined-warning', // No background, with warning-type border color + 'outlined-danger', // No background, with danger-type border color + 'outlined-utility', // No background, with utility-type border color + 'outlined-action', // No background, with branded border color + 'raised', // With white background and shadow + 'flat', // No background, no border + 'flat-secondary', // No background, no border, secondary-type text color + 'flat-info', // No background, no border, info-type text color + 'flat-success', // No background, no border, success-type text color + 'flat-warning', // No background, no border, warning-type text color + 'flat-danger', // No background, no border, danger-type text color + 'flat-utility', // No background, no border, utility-type text color + 'flat-action', // No background, no border, branded text color + 'normal-contrast', // normal button appearance with contrast background + 'outlined-contrast', // outlined button appearance with contrast background + 'flat-contrast', // flat button appearance with contrast background +] as const; diff --git a/src/components/Toaster/Toast/Toast.scss b/src/components/Toaster/Toast/Toast.scss index 2a470a87fb..a6330f463e 100644 --- a/src/components/Toaster/Toast/Toast.scss +++ b/src/components/Toaster/Toast/Toast.scss @@ -62,13 +62,10 @@ $block: '.#{variables.$ns}toast'; &__container { flex: 1 1 auto; - display: grid; - row-gap: 8px; - width: 100%; - height: 100%; + display: flex; + flex-flow: column nowrap; min-height: var(--g-text-body-2-line-height); min-width: 0; - grid-template-columns: 100%; &:before { content: ''; @@ -88,6 +85,7 @@ $block: '.#{variables.$ns}toast'; padding-inline-end: 8px; padding-block-start: 2px; color: var(--_--icon-color); + min-width: 0; } &__title { @@ -96,8 +94,17 @@ $block: '.#{variables.$ns}toast'; padding-inline-end: $closeButtonTitleSpacing + $closeButtonSize; } - &__content_without-title { - padding-inline-end: $closeButtonTitleSpacing + $closeButtonSize; + &__content { + margin-block-start: var(--g-spacing-2); + + &_without-title { + margin-block-start: 0; + padding-inline-end: $closeButtonTitleSpacing + $closeButtonSize; + } + } + + &__actions { + margin-block-start: var(--g-spacing-3); } &__action { diff --git a/src/components/Toaster/__stories__/Toaster.stories.tsx b/src/components/Toaster/__stories__/Toaster.stories.tsx index 4403a62a94..2c16a06bc9 100644 --- a/src/components/Toaster/__stories__/Toaster.stories.tsx +++ b/src/components/Toaster/__stories__/Toaster.stories.tsx @@ -1,10 +1,16 @@ import React from 'react'; +import {faker} from '@faker-js/faker/locale/en'; import type {Meta, StoryObj} from '@storybook/react'; import type {ButtonView} from '../../Button'; +import {BUTTON_VIEWS} from '../../Button/constants'; import {ToasterProvider} from '../Provider/ToasterProvider'; import {Toast} from '../Toast/Toast'; +import {ToasterComponent} from '../ToasterComponent/ToasterComponent'; +import {TOAST_THEMES} from '../constants'; +import {useToaster} from '../hooks/useToaster'; +import type {ToastAction} from '../types'; import {ToasterDemo} from './ToasterShowcase'; @@ -27,7 +33,7 @@ const views: ButtonView[] = [ function viewSelect(name: string) { return { name, - control: 'select', + control: 'select' as const, defaultValue: 'outlined', options: views, if: {arg: 'setActions'}, @@ -43,13 +49,34 @@ const disabledControl = { function booleanControl(label: string) { return { name: label, - control: 'boolean', + control: 'boolean' as const, }; } export default { title: 'Components/Feedback/Toaster', component: Toast, + decorators: [ + function withToasters(Story) { + return ( + + + + ); + }, + ], +} as Meta; + +type Story = StoryObj< + React.ComponentProps & React.ComponentProps +>; + +export const Default: Story = { + args: { + setTitle: true, + showCloseIcon: true, + allowAutoHiding: true, + }, argTypes: { mobile: disabledControl, name: disabledControl, @@ -57,7 +84,7 @@ export default { className: disabledControl, autoHiding: disabledControl, content: disabledControl, - type: disabledControl, + theme: disabledControl, isClosable: disabledControl, actions: disabledControl, removeCallback: disabledControl, @@ -71,25 +98,51 @@ export default { action1View: viewSelect('Action 1 view'), action2View: viewSelect('Action 2 view'), }, + render: (props) => , +}; + +function getAction(): ToastAction { + return { + onClick: () => {}, + label: faker.lorem.words(1), + view: faker.helpers.arrayElement(BUTTON_VIEWS), + removeAfterClick: false, + }; +} + +export const ToastPlayground: Story = { + name: 'Toast (Playground)', args: { - setTitle: true, - showCloseIcon: true, - allowAutoHiding: true, + mobile: false, + autoHiding: false, + isClosable: faker.datatype.boolean(), + title: faker.lorem.words(5), + content: faker.lorem.sentences(2), + theme: faker.helpers.arrayElement(TOAST_THEMES), + actions: faker.helpers.uniqueArray(getAction, faker.number.int({min: 1, max: 2})), }, - decorators: [ - function withToasters(Story) { - return ( - - - - ); - }, - ], -} as Meta; + argTypes: { + name: disabledControl, + addedAt: disabledControl, + renderIcon: disabledControl, + removeCallback: disabledControl, + }, + render: (args) => { + // eslint-disable-next-line react-hooks/rules-of-hooks + const toaster = useToaster(); -type Story = StoryObj; + // eslint-disable-next-line react-hooks/rules-of-hooks + React.useEffect(() => { + const toastId = 'demo-toast'; -export const Default: Story = { - args: {}, - render: (props) => , + toaster.add({ + ...args, + name: toastId, + }); + + return () => toaster.remove(toastId); + }, [args, toaster]); + + return ; + }, }; diff --git a/src/components/Toaster/constants.ts b/src/components/Toaster/constants.ts new file mode 100644 index 0000000000..1435319989 --- /dev/null +++ b/src/components/Toaster/constants.ts @@ -0,0 +1 @@ +export const TOAST_THEMES = ['normal', 'info', 'success', 'warning', 'danger', 'utility'] as const; diff --git a/src/components/Toaster/types.ts b/src/components/Toaster/types.ts index 72dce8616a..5beb50758e 100644 --- a/src/components/Toaster/types.ts +++ b/src/components/Toaster/types.ts @@ -1,11 +1,13 @@ import type {ButtonView} from '../Button'; +import type {TOAST_THEMES} from './constants'; + export type ToasterArgs = { className?: string; mobile?: boolean; }; -export type ToastTheme = 'normal' | 'info' | 'success' | 'warning' | 'danger' | 'utility'; +export type ToastTheme = (typeof TOAST_THEMES)[number]; export type ToastAction = { onClick: VoidFunction;