From 9830dfeed44fc1969e67056fc3572ae260dbe864 Mon Sep 17 00:00:00 2001 From: Leisure <675258207@qq.com> Date: Tue, 27 Aug 2024 18:35:02 +0800 Subject: [PATCH] fix(Toast): multiple toasts should show last one --- src/components/toast/methods.tsx | 11 ++++++--- src/components/toast/tests/toast.test.tsx | 28 ++++++++++++++++++++--- src/utils/render-imperatively.tsx | 6 ++++- 3 files changed, 38 insertions(+), 7 deletions(-) diff --git a/src/components/toast/methods.tsx b/src/components/toast/methods.tsx index eb465ecb4b..073aa33207 100644 --- a/src/components/toast/methods.tsx +++ b/src/components/toast/methods.tsx @@ -1,10 +1,10 @@ import React from 'react' -import { InternalToast, ToastProps } from './toast' -import { mergeProps } from '../../utils/with-default-props' import { ImperativeHandler, renderImperatively, } from '../../utils/render-imperatively' +import { mergeProps } from '../../utils/with-default-props' +import { InternalToast, ToastProps } from './toast' let currentHandler: ImperativeHandler | null = null let currentTimeout: number | null = null @@ -42,7 +42,12 @@ export function show(p: ToastShowProps | string) { /> ) if (currentHandler) { - currentHandler.replace(element) + if (currentHandler.isRendered?.()) { + currentHandler.replace(element) + } else { + currentHandler.close() + currentHandler = renderImperatively(element) + } } else { currentHandler = renderImperatively(element) } diff --git a/src/components/toast/tests/toast.test.tsx b/src/components/toast/tests/toast.test.tsx index 0d17950bb6..ead30b541e 100644 --- a/src/components/toast/tests/toast.test.tsx +++ b/src/components/toast/tests/toast.test.tsx @@ -1,11 +1,11 @@ import React, { useRef } from 'react' import { - render, + act, fireEvent, + render, + screen, waitFor, waitForElementToBeRemoved, - act, - screen, } from 'testing' import Toast, { ToastHandler } from '..' @@ -255,4 +255,26 @@ describe('Toast', () => { await waitForContentShow('content2') expect(document.querySelectorAll(`.${classPrefix}-main`).length) }) + + test('multiple toasts should show last one', async () => { + const { getByText } = render( + + ) + fireEvent.click(getByText('btn')) + await waitForContentShow('content2') + expect(document.querySelectorAll(`.${classPrefix}-main`).length) + }) }) diff --git a/src/utils/render-imperatively.tsx b/src/utils/render-imperatively.tsx index 00a6fac198..bf5c8ec37f 100644 --- a/src/utils/render-imperatively.tsx +++ b/src/utils/render-imperatively.tsx @@ -1,5 +1,5 @@ -import React, { useEffect, useImperativeHandle, useRef, useState } from 'react' import type { ReactElement } from 'react' +import React, { useEffect, useImperativeHandle, useRef, useState } from 'react' import { renderToBody } from './render-to-body' type ImperativeProps = { @@ -13,6 +13,7 @@ type TargetElement = ReactElement export type ImperativeHandler = { close: () => void replace: (element: TargetElement) => void + isRendered?: () => boolean } export function renderImperatively(element: TargetElement) { @@ -60,6 +61,8 @@ export function renderImperatively(element: TargetElement) { if (!wrapperRef.current) { // it means the wrapper is not mounted yet, call `unmount` directly unmount() + // call `afterClose` to make sure the callback is called + element.props.afterClose?.() } else { wrapperRef.current?.close() } @@ -67,5 +70,6 @@ export function renderImperatively(element: TargetElement) { replace: element => { wrapperRef.current?.replace(element) }, + isRendered: () => !!wrapperRef.current, } as ImperativeHandler }