From 7802a97f13a6e1a25db894ec691ebecb1277b8dc Mon Sep 17 00:00:00 2001 From: Sergey Garin Date: Mon, 25 Jul 2022 19:26:59 +0400 Subject: [PATCH 1/5] fix: preset in Description component --- .../NotificationView/NotificationDescription.tsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/components/overlays/NewNotifications/NotificationView/NotificationDescription.tsx b/src/components/overlays/NewNotifications/NotificationView/NotificationDescription.tsx index bc6a19c9..74c452ea 100644 --- a/src/components/overlays/NewNotifications/NotificationView/NotificationDescription.tsx +++ b/src/components/overlays/NewNotifications/NotificationView/NotificationDescription.tsx @@ -1,4 +1,4 @@ -import { HTMLAttributes, memo, ReactNode } from 'react'; +import { HTMLAttributes, memo } from 'react'; import { tasty } from '../../../../tasty'; import { Paragraph } from '../../../content/Paragraph'; import { NotificationProps } from './types'; @@ -23,5 +23,9 @@ export const NotificationDescription = memo(function NotificationDescription( ) { const { description, ...descriptionProps } = props; - return {description}; + return ( + + {description} + + ); }); From d94b98534ac80465f8ae6b93959df40ca4a80779 Mon Sep 17 00:00:00 2001 From: Sergey Garin Date: Tue, 26 Jul 2022 13:18:58 +0400 Subject: [PATCH 2/5] Create dull-parents-exercise.md --- .changeset/dull-parents-exercise.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/dull-parents-exercise.md diff --git a/.changeset/dull-parents-exercise.md b/.changeset/dull-parents-exercise.md new file mode 100644 index 00000000..a3504814 --- /dev/null +++ b/.changeset/dull-parents-exercise.md @@ -0,0 +1,5 @@ +--- +"@cube-dev/ui-kit": patch +--- + +Fixed description preset in notificaiton From 3baa165432167f821322584729d1f6732e0f6b5a Mon Sep 17 00:00:00 2001 From: Sergey Garin Date: Tue, 26 Jul 2022 16:13:54 +0400 Subject: [PATCH 3/5] fix: minor layout fixes --- .../NotificationCloseButton.tsx | 6 +++--- .../NotificationView/NotificationHeader.tsx | 17 +++++++++++------ .../NotificationView/NotificationView.tsx | 12 +----------- 3 files changed, 15 insertions(+), 20 deletions(-) diff --git a/src/components/overlays/NewNotifications/NotificationView/NotificationCloseButton.tsx b/src/components/overlays/NewNotifications/NotificationView/NotificationCloseButton.tsx index 3a09220d..8c07323e 100644 --- a/src/components/overlays/NewNotifications/NotificationView/NotificationCloseButton.tsx +++ b/src/components/overlays/NewNotifications/NotificationView/NotificationCloseButton.tsx @@ -16,9 +16,9 @@ const CloseButton = tasty(Button, { top: '-0.75x', display: 'flex', placeItems: 'center', - padding: '0.75x', - width: '3.5x', - height: '3.5x', + padding: '0.625x', + width: '3x', + height: '3x', fill: '#white', shadow: '0 0.5x 2x #shadow', color: { '': '#dark-02', hovered: '#dark-03', pressed: '#dark-02' }, diff --git a/src/components/overlays/NewNotifications/NotificationView/NotificationHeader.tsx b/src/components/overlays/NewNotifications/NotificationView/NotificationHeader.tsx index af980a79..9b0324cb 100644 --- a/src/components/overlays/NewNotifications/NotificationView/NotificationHeader.tsx +++ b/src/components/overlays/NewNotifications/NotificationView/NotificationHeader.tsx @@ -7,16 +7,21 @@ export type NotificationHeaderProps = { header: NotificationProps['header']; } & HTMLAttributes; -const Header = tasty(Title, { gridArea: 'header', margin: '0.25x 0 0.5x' }); +const Header = tasty(Title, { + as: 'div', + preset: 'h6', + styles: { + gridArea: 'header', + '&:not(:empty)': { + margin: '0.25x 0 0.5x', + }, + }, +}); export const NotificationHeader = memo(function NotificationHeader( props: NotificationHeaderProps, ): JSX.Element { const { header, ...headerProps } = props; - return ( -
- {header} -
- ); + return
{header}
; }); diff --git a/src/components/overlays/NewNotifications/NotificationView/NotificationView.tsx b/src/components/overlays/NewNotifications/NotificationView/NotificationView.tsx index 2930c165..810b4160 100644 --- a/src/components/overlays/NewNotifications/NotificationView/NotificationView.tsx +++ b/src/components/overlays/NewNotifications/NotificationView/NotificationView.tsx @@ -33,16 +33,6 @@ const NotificationContainer = tasty({ '': '0 0 0 4bw #purple-04.0 inset', focused: '0 0 0 4bw #purple-04 inset', }, - '&::before': { - zIndex: 0, - content: '""', - position: 'absolute', - top: '-8px', - left: '0', - right: '-8px', - bottom: '0', - display: { '': 'none', 'is-dismissible': 'flex' }, - }, }, }); @@ -105,7 +95,7 @@ export const NotificationView = forwardRef(function NotificationView( > - {header && } + {description && ( Date: Tue, 26 Jul 2022 23:28:33 +0400 Subject: [PATCH 4/5] fix: missing onDismiss, add null to duration in toast, remove duplicate module --- .../Bar/FloatingNotification.tsx | 1 + .../use-notifications-api.ts | 14 -------- .../NotificationsContext/use-notifications.ts | 2 +- src/components/overlays/Toasts/Toast.tsx | 3 +- src/components/overlays/Toasts/toast.test.tsx | 30 +++++++++++++++++ src/components/overlays/Toasts/types.ts | 2 +- .../overlays/Toasts/use-toasts-api.ts | 32 +++++++++++-------- 7 files changed, 53 insertions(+), 31 deletions(-) delete mode 100644 src/components/overlays/NewNotifications/NotificationsContext/use-notifications-api.ts create mode 100644 src/components/overlays/Toasts/toast.test.tsx diff --git a/src/components/overlays/NewNotifications/Bar/FloatingNotification.tsx b/src/components/overlays/NewNotifications/Bar/FloatingNotification.tsx index 1d5fdafd..7f3d4b92 100644 --- a/src/components/overlays/NewNotifications/Bar/FloatingNotification.tsx +++ b/src/components/overlays/NewNotifications/Bar/FloatingNotification.tsx @@ -44,6 +44,7 @@ export const FloatingNotification = memo(function FloatingNotification( const chainedOnDismiss = useChainedCallback( () => onDismissNotification(id), () => onRemoveNotification(id), + notificationProps.onDismiss, ); const onKeyDown = useEvent>(({ key }) => { diff --git a/src/components/overlays/NewNotifications/NotificationsContext/use-notifications-api.ts b/src/components/overlays/NewNotifications/NotificationsContext/use-notifications-api.ts deleted file mode 100644 index 1df15342..00000000 --- a/src/components/overlays/NewNotifications/NotificationsContext/use-notifications-api.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { useContext } from 'react'; -import invariant from 'tiny-invariant'; -import { NotificationsContext } from './NotificationsProvider'; - -export function useNotificationsApi() { - const context = useContext(NotificationsContext); - - invariant( - context !== null, - "You can't use Notifications outside of the component. Please, check if your component is descendant of component", - ); - - return context.api; -} diff --git a/src/components/overlays/NewNotifications/NotificationsContext/use-notifications.ts b/src/components/overlays/NewNotifications/NotificationsContext/use-notifications.ts index 02eb2a01..166f7fe1 100644 --- a/src/components/overlays/NewNotifications/NotificationsContext/use-notifications.ts +++ b/src/components/overlays/NewNotifications/NotificationsContext/use-notifications.ts @@ -29,7 +29,7 @@ export function useNotifications( newToasts.set(id, { id, isDismissible, - duration: isDismissible ? 5_000 : null, + duration: duration ?? isDismissible ? 5_000 : null, ...rest, } as CubeNotifyApiPropsWithID); diff --git a/src/components/overlays/Toasts/Toast.tsx b/src/components/overlays/Toasts/Toast.tsx index b455bfdb..e1f07d6d 100644 --- a/src/components/overlays/Toasts/Toast.tsx +++ b/src/components/overlays/Toasts/Toast.tsx @@ -2,6 +2,7 @@ import { useEffect } from 'react'; import { useId } from '@react-aria/utils'; import { useToastsApi } from './use-toasts-api'; import { CubeToastsApiProps } from './types'; +import { CubeNotifyApiProps } from '../NewNotifications'; export function Toast(props: CubeToastsApiProps) { const { id: propsId } = props; @@ -17,7 +18,7 @@ export function Toast(props: CubeToastsApiProps) { }, [id]); useEffect(() => { - update(id, props); + update(id, props as CubeNotifyApiProps); }); return null; diff --git a/src/components/overlays/Toasts/toast.test.tsx b/src/components/overlays/Toasts/toast.test.tsx new file mode 100644 index 00000000..d0073e20 --- /dev/null +++ b/src/components/overlays/Toasts/toast.test.tsx @@ -0,0 +1,30 @@ +import { Toast } from './Toast'; +import { renderWithRoot, screen } from '../../../test'; + +describe('useToastsApi', () => { + beforeAll(() => jest.useFakeTimers('modern')); + afterAll(() => jest.useRealTimers()); + + it('should add and dismiss toast on timeout', async () => { + const dismiss = jest.fn(); + renderWithRoot(); + + jest.runAllTimers(); + + expect( + screen.queryByTestId('floating-notification'), + ).not.toBeInTheDocument(); + expect(dismiss).toBeCalledTimes(1); + }); + + it('should not unmount if duration is null', async () => { + const dismiss = jest.fn(); + renderWithRoot( + , + ); + + jest.runAllTimers(); + expect(screen.getByTestId('floating-notification')).toBeInTheDocument(); + expect(dismiss).not.toBeCalled(); + }); +}); diff --git a/src/components/overlays/Toasts/types.ts b/src/components/overlays/Toasts/types.ts index d4556755..6d6e64f4 100644 --- a/src/components/overlays/Toasts/types.ts +++ b/src/components/overlays/Toasts/types.ts @@ -9,7 +9,7 @@ export type CubeToastsApiProps = { header?: ReactChild | ReactFragment; id?: Key; onDismiss?: () => void; - duration?: number; + duration?: number | null; icon?: ReactNode; type?: CubeNotificationType; }; diff --git a/src/components/overlays/Toasts/use-toasts-api.ts b/src/components/overlays/Toasts/use-toasts-api.ts index 107db533..36fb44fc 100644 --- a/src/components/overlays/Toasts/use-toasts-api.ts +++ b/src/components/overlays/Toasts/use-toasts-api.ts @@ -1,6 +1,6 @@ import { ReactChild, ReactFragment, useMemo } from 'react'; import { isElement, isFragment } from 'react-is'; -import { useNotificationsApi } from '../NewNotifications'; +import { CubeNotifyApiProps, useNotificationsApi } from '../NewNotifications'; import type { CubeToastsApiProps, CubeToastsApiToastCallback } from './types'; import { CubeToastsApiToastShortcuts } from './types'; @@ -10,13 +10,12 @@ export function useToastsApi() { const toast = useMemo( () => Object.assign( - (props) => - notify({ - isDismissible: true, + (props) => { + return notify({ putNotificationInDropdownOnDismiss: false, - duration: 5_000, ...unwrapProps(props), - }), + } as CubeNotifyApiProps); + }, { success: (props) => toast({ type: 'success', ...unwrapProps(props) }), danger: (props) => toast({ type: 'danger', ...unwrapProps(props) }), @@ -32,19 +31,24 @@ export function useToastsApi() { function unwrapProps(props: CubeToastsApiProps | ReactChild | ReactFragment) { return { - ...(propsIsDescription(props) - ? { description: props } - : (props as CubeToastsApiProps)), + ...(propsIsToastProps(props) + ? { + isDismissible: props.duration !== null, + duration: 5_000, + ...(props as CubeToastsApiProps), + } + : { description: props, isDismissible: true, duration: 5_000 }), }; } -function propsIsDescription( +function propsIsToastProps( props: CubeToastsApiProps | ReactChild | ReactFragment, -) { - return ( +): props is CubeToastsApiProps { + const isReactNode = isElement(props) || isFragment(props) || typeof props === 'string' || - typeof props === 'number' - ); + typeof props === 'number'; + + return !isReactNode; } From f086e9a84e5214c8f56ab187fe3d42b384bc6089 Mon Sep 17 00:00:00 2001 From: Sergey Garin Date: Thu, 28 Jul 2022 11:47:00 +0400 Subject: [PATCH 5/5] fix: review issues --- .../overlays/NewNotifications/types.ts | 19 +------------------ .../overlays/Toasts/use-toasts-api.ts | 9 ++++----- 2 files changed, 5 insertions(+), 23 deletions(-) diff --git a/src/components/overlays/NewNotifications/types.ts b/src/components/overlays/NewNotifications/types.ts index fa80fd8c..e3ca65ad 100644 --- a/src/components/overlays/NewNotifications/types.ts +++ b/src/components/overlays/NewNotifications/types.ts @@ -70,24 +70,7 @@ export type BaseNotificationProps = { | NotificationActionType; }; -export type CubeNotificationProps = BaseNotificationProps & - EitherDismissibleOrNotNotification; - -export type EitherDismissibleOrNotNotification = - | DismissibleNotification - | NonDismissibleNotification; - -type DismissibleNotification = { - isDismissible?: true; - onDismiss?: () => void; - duration?: number; -}; - -type NonDismissibleNotification = { - isDismissible: false; - onDismiss?: never | undefined; - duration?: null; -}; +export type CubeNotificationProps = BaseNotificationProps; export type CubeNotifyApiProps = { meta?: CubeNotificationMeta; diff --git a/src/components/overlays/Toasts/use-toasts-api.ts b/src/components/overlays/Toasts/use-toasts-api.ts index 36fb44fc..8f7599d9 100644 --- a/src/components/overlays/Toasts/use-toasts-api.ts +++ b/src/components/overlays/Toasts/use-toasts-api.ts @@ -1,6 +1,6 @@ import { ReactChild, ReactFragment, useMemo } from 'react'; import { isElement, isFragment } from 'react-is'; -import { CubeNotifyApiProps, useNotificationsApi } from '../NewNotifications'; +import { useNotificationsApi } from '../NewNotifications'; import type { CubeToastsApiProps, CubeToastsApiToastCallback } from './types'; import { CubeToastsApiToastShortcuts } from './types'; @@ -10,12 +10,11 @@ export function useToastsApi() { const toast = useMemo( () => Object.assign( - (props) => { - return notify({ + (props) => + notify({ putNotificationInDropdownOnDismiss: false, ...unwrapProps(props), - } as CubeNotifyApiProps); - }, + }), { success: (props) => toast({ type: 'success', ...unwrapProps(props) }), danger: (props) => toast({ type: 'danger', ...unwrapProps(props) }),