Skip to content

Commit

Permalink
Merge pull request #3966 from Royal-Navy/feat/expose-dismissToast-fun…
Browse files Browse the repository at this point in the history
…ction

feat(Toast): Expose dismissToast function
  • Loading branch information
m7kvqbe1 authored Nov 26, 2024
2 parents 427642b + 71189d8 commit 41b91e5
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 35 deletions.
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import React, { useEffect } from 'react'
import { Meta, StoryFn } from '@storybook/react'

import { TOAST_APPEARANCE, Toast, ToastProps, showToast } from '.'
import { TOAST_APPEARANCE, Toast, ToastProps, showToast, dismissToast } from '.'
import { Button } from '../Button'
import { Group } from '../Group'

export default {
component: Toast,
Expand Down Expand Up @@ -31,13 +32,22 @@ const ToastButton = (props: ToastProps) => {
return (
<div style={{ minHeight: '10rem' }}>
<Toast {...props} />
<Button
onClick={(_: React.FormEvent<HTMLButtonElement>) => {
showToast({ label: 'another', message: DESCRIPTION })
}}
>
Show Toast
</Button>
<Group gap="6">
<Button
onClick={(_: React.FormEvent<HTMLButtonElement>) => {
showToast({ label: 'Another Toast', message: DESCRIPTION })
}}
>
Show Toast
</Button>
<Button
onClick={(_: React.FormEvent<HTMLButtonElement>) => {
dismissToast()
}}
>
Dismiss All Toasts
</Button>
</Group>
</div>
)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import React from 'react'
import { color } from '@royalnavy/design-tokens'
import { render, screen, waitFor, within } from '@testing-library/react'
import { render, screen, within, waitFor } from '@testing-library/react'

import { TOAST_APPEARANCE, Toast, showToast } from '.'
import { TOAST_APPEARANCE, Toast, dismissToast, showToast } from '.'

const LABEL = 'Example label'
const MESSAGE = 'This is an example toast message'
Expand All @@ -19,12 +19,20 @@ function setup() {

/* Added this function to resolve an issue when running tests for
* the whole of this file and more than one toast is shown */
async function getLastToast(lastToastLabel: string) {
await waitFor(() => {
expect(screen.getByText(lastToastLabel)).toBeInTheDocument()
})

return screen.getAllByRole('status')[0]
async function getLastToast(label?: string) {
try {
const toasts = await screen.findAllByRole('status', {
name: label ? new RegExp(label, 'i') : undefined,
})

return toasts[0]
} catch (error) {
if (error instanceof Error && error.message.includes('Unable to find')) {
return null
}

throw error
}
}

it('renders the toast', async () => {
Expand All @@ -41,11 +49,11 @@ it('renders the toast', async () => {
const contentId = screen.getByText(MESSAGE).getAttribute('id')
expect(lastToast).toHaveAttribute('aria-describedby', contentId)

expect(within(lastToast).getByTestId('icon')).toHaveAttribute(
expect(within(lastToast!).getByTestId('icon')).toHaveAttribute(
'aria-hidden',
'true'
)
expect(within(lastToast).getByTestId('icon')).toHaveStyle({
expect(within(lastToast!).getByTestId('icon')).toHaveStyle({
color: color('action', '500'),
})

Expand All @@ -66,9 +74,9 @@ it('sets new props when `showToast` is called with new props', async () => {

const lastToast = await getLastToast(expectedNewLabel)

expect(within(lastToast).getByText(expectedNewLabel)).toBeInTheDocument()
expect(within(lastToast).getByText(expectedNewMessage)).toBeInTheDocument()
expect(within(lastToast).getByTestId('icon')).toHaveStyle({
expect(within(lastToast!).getByText(expectedNewLabel)).toBeInTheDocument()
expect(within(lastToast!).getByText(expectedNewMessage)).toBeInTheDocument()
expect(within(lastToast!).getByTestId('icon')).toHaveStyle({
color: color('danger', '500'),
})
})
Expand All @@ -91,8 +99,8 @@ it('sets the message when the message is JSX', async () => {

const lastToast = await getLastToast(expectedNewLabel)

expect(within(lastToast).getByText(expectedNewLabel)).toBeInTheDocument()
expect(within(lastToast).getAllByRole('paragraph')).toHaveLength(2)
expect(within(lastToast!).getByText(expectedNewLabel)).toBeInTheDocument()
expect(within(lastToast!).getAllByRole('paragraph')).toHaveLength(2)
})

it('sets unique IDs when there are multiple toasts', async () => {
Expand Down Expand Up @@ -121,3 +129,18 @@ it('sets an accessible name when there is no message', async () => {

expect(toast).toHaveAccessibleName(new RegExp(label))
})

it('dismisses all toasts when `dismissToast` is called', async () => {
setup()

showToast(MESSAGE)
showToast(MESSAGE)

expect(screen.queryAllByRole('status').length).toBeGreaterThan(0)

dismissToast()

await waitFor(() => {
expect(screen.queryAllByRole('status')).toHaveLength(0)
})
})
20 changes: 10 additions & 10 deletions packages/react-component-library/src/components/Toast/Toast.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export const Toast = (props: ToastProps) => {
const { dateTime, label, appearance = TOAST_APPEARANCE.INFO, ...rest } = props

const { toasts, handlers } = useToaster()
const { startPause, endPause, updateHeight, calculateOffset } = handlers
const { startPause, endPause, updateHeight } = handlers

const [time] = useState<string>(
(dateTime || new Date()).toLocaleTimeString('en-GB', {
Expand All @@ -85,10 +85,12 @@ export const Toast = (props: ToastProps) => {
onMouseLeave={endPause}
style={{
position: 'fixed',
top: 0,
right: 0,
padding: spacing('4'),
top: spacing('4'),
right: spacing('4'),
zIndex: zIndex('overlay', 999),
display: 'flex',
flexDirection: 'column',
gap: spacing('4'),
}}
>
{toasts.map((item: HotToast & ToastProps) => {
Expand All @@ -98,11 +100,6 @@ export const Toast = (props: ToastProps) => {
const toastTitleId = `${titleId}-${item.id}`
const toastDescriptionId = `${descriptionId}-${item.id}`

const offset = calculateOffset(item, {
reverseOrder: true,
gutter: 1,
})

const ref = (el: HTMLDivElement) => {
if (el && typeof height !== 'number') {
updateHeight(id, el.getBoundingClientRect().height)
Expand All @@ -115,7 +112,6 @@ export const Toast = (props: ToastProps) => {
style={{
transition: 'all 0.5s ease-out',
opacity: visible ? 1 : 0,
transform: `translateY(${offset - height}px)`,
}}
ref={ref}
>
Expand Down Expand Up @@ -183,3 +179,7 @@ export const showToast = (
})
}
}

export const dismissToast = (id?: string) => {
toast.dismiss(id)
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import styled, { css } from 'styled-components'
import { color, shadow, spacing } from '@royalnavy/design-tokens'
import { color, shadow } from '@royalnavy/design-tokens'

import { StyledLabel } from './StyledLabel'
import { Appearance } from '../Toast'
Expand All @@ -22,7 +22,6 @@ export const StyledToast = styled.div<StyledToastProps>`
border: 1px solid ${color('neutral', '100')};
border-radius: 4px;
width: 340px;
margin-bottom: ${spacing('6')};
background-color: ${color('neutral', 'white')};
${({ $appearance }) => css`
Expand Down

0 comments on commit 41b91e5

Please sign in to comment.