Skip to content

Commit e8958bb

Browse files
authored
fix: auto close with maxCount not work (#136)
* fix: Notice close should keep new key * test: move pos * test: Update test case
1 parent 3390d96 commit e8958bb

3 files changed

Lines changed: 136 additions & 85 deletions

File tree

src/Notice.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,13 @@ export interface NoticeProps {
1515
duration?: number | null;
1616
children?: React.ReactNode;
1717
updateMark?: string;
18+
/** Mark as final key since set maxCount may keep the key but user pass key is different */
19+
noticeKey: React.Key;
1820
closeIcon?: React.ReactNode;
1921
closable?: boolean;
2022
props?: DivProps;
2123
onClick?: React.MouseEventHandler<HTMLDivElement>;
22-
onClose?: () => void;
24+
onClose?: (key: React.Key) => void;
2325

2426
/** @private Only for internal usage. We don't promise that we will refactor this */
2527
holder?: HTMLDivElement;
@@ -55,9 +57,9 @@ export default class Notice extends Component<NoticeProps> {
5557
e.stopPropagation();
5658
}
5759
this.clearCloseTimer();
58-
const { onClose } = this.props;
60+
const { onClose, noticeKey } = this.props;
5961
if (onClose) {
60-
onClose();
62+
onClose(noticeKey);
6163
}
6264
};
6365

src/Notification.tsx

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import { Component, ReactText } from 'react';
33
import ReactDOM from 'react-dom';
44
import classNames from 'classnames';
55
import { CSSMotionList } from 'rc-motion';
6-
import createChainedFunction from 'rc-util/lib/createChainedFunction';
76
import Notice, { NoticeProps } from './Notice';
87
import useNotification from './useNotification';
98

@@ -16,11 +15,13 @@ function getUuid() {
1615
return `rcNotification_${now}_${id}`;
1716
}
1817

19-
export interface NoticeContent extends Omit<NoticeProps, 'prefixCls' | 'children'> {
18+
export interface NoticeContent
19+
extends Omit<NoticeProps, 'prefixCls' | 'children' | 'noticeKey' | 'onClose'> {
2020
prefixCls?: string;
2121
key?: React.Key;
2222
updateMark?: string;
2323
content?: React.ReactNode;
24+
onClose?: () => void;
2425
}
2526

2627
export type NoticeFunc = (noticeProps: NoticeContent) => void;
@@ -94,9 +95,9 @@ class Notification extends Component<NotificationProps, NotificationState> {
9495
key,
9596
};
9697
const { maxCount } = this.props;
97-
this.setState((previousState) => {
98+
this.setState(previousState => {
9899
const { notices } = previousState;
99-
const noticeIndex = notices.map((v) => v.notice.key).indexOf(key);
100+
const noticeIndex = notices.map(v => v.notice.key).indexOf(key);
100101
const updatedNotices = notices.concat();
101102
if (noticeIndex !== -1) {
102103
updatedNotices.splice(noticeIndex, 1, { notice, holderCallback });
@@ -155,18 +156,20 @@ class Notification extends Component<NotificationProps, NotificationState> {
155156

156157
notices.forEach(({ notice, holderCallback }, index) => {
157158
const updateMark = index === notices.length - 1 ? notice.updateMark : undefined;
158-
const { key } = notice;
159-
160-
const onClose = createChainedFunction(this.remove.bind(this, key), notice.onClose) as any;
159+
const { key, userPassKey } = notice;
161160

162161
const noticeProps = {
163162
prefixCls,
164163
closeIcon,
165164
...notice,
166165
...notice.props,
167166
key,
167+
noticeKey: userPassKey || key,
168168
updateMark,
169-
onClose,
169+
onClose: (noticeKey: React.Key) => {
170+
this.remove(noticeKey);
171+
notice.onClose?.();
172+
},
170173
onClick: notice.onClick,
171174
children: notice.content,
172175
} as NoticeProps & { key: ReactText };
@@ -195,7 +198,7 @@ class Notification extends Component<NotificationProps, NotificationState> {
195198
key={key}
196199
className={classNames(motionClassName, `${prefixCls}-hook-holder`)}
197200
style={{ ...motionStyle }}
198-
ref={(div) => {
201+
ref={div => {
199202
if (typeof key === 'undefined') {
200203
return;
201204
}

tests/index.test.js

Lines changed: 119 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -176,46 +176,6 @@ describe('Notification.Basic', () => {
176176
);
177177
});
178178

179-
it('remove work when maxCount set', done => {
180-
let wrapper;
181-
182-
Notification.newInstance(
183-
{
184-
TEST_RENDER: node => {
185-
wrapper = mount(<div>{node}</div>);
186-
},
187-
maxCount: 1,
188-
},
189-
notification => {
190-
// First
191-
notification.notice({
192-
content: <div className="max-count">bamboo</div>,
193-
key: 'bamboo',
194-
duration: 0,
195-
});
196-
197-
// Next
198-
notification.notice({
199-
content: <div className="max-count">bamboo</div>,
200-
key: 'bamboo',
201-
duration: 0,
202-
});
203-
204-
setTimeout(() => {
205-
expect(wrapper.find('.max-count')).toHaveLength(1);
206-
notification.removeNotice('bamboo');
207-
208-
setTimeout(() => {
209-
wrapper.update();
210-
expect(wrapper.find('.max-count')).toHaveLength(0);
211-
notification.destroy();
212-
done();
213-
}, 500);
214-
}, 10);
215-
},
216-
);
217-
});
218-
219179
it('update notification by key with multi instance', done => {
220180
let wrapper;
221181

@@ -341,47 +301,133 @@ describe('Notification.Basic', () => {
341301
mount(<Test />, container);
342302
});
343303

344-
it('drop first notice when items limit exceeds', () => {
345-
jest.useFakeTimers();
304+
describe('maxCount', () => {
305+
it('remove work when maxCount set', done => {
306+
let wrapper;
346307

347-
let wrapper;
348-
349-
let notificationInstance;
350-
Notification.newInstance(
351-
{
352-
maxCount: 1,
353-
TEST_RENDER: node => {
354-
wrapper = mount(<div>{node}</div>);
308+
Notification.newInstance(
309+
{
310+
TEST_RENDER: node => {
311+
wrapper = mount(<div>{node}</div>);
312+
},
313+
maxCount: 1,
355314
},
356-
},
357-
notification => {
358-
notificationInstance = notification;
359-
},
360-
);
315+
notification => {
316+
// First
317+
notification.notice({
318+
content: <div className="max-count">bamboo</div>,
319+
key: 'bamboo',
320+
duration: 0,
321+
});
361322

362-
const value = 'updated last';
363-
notificationInstance.notice({
364-
content: <span className="test-maxcount">simple show</span>,
365-
duration: 0,
366-
});
367-
notificationInstance.notice({
368-
content: <span className="test-maxcount">simple show</span>,
369-
duration: 0,
370-
});
371-
notificationInstance.notice({
372-
content: <span className="test-maxcount">{value}</span>,
373-
duration: 0,
323+
// Next
324+
notification.notice({
325+
content: <div className="max-count">bamboo</div>,
326+
key: 'bamboo',
327+
duration: 0,
328+
});
329+
330+
setTimeout(() => {
331+
expect(wrapper.find('.max-count')).toHaveLength(1);
332+
notification.removeNotice('bamboo');
333+
334+
setTimeout(() => {
335+
wrapper.update();
336+
expect(wrapper.find('.max-count')).toHaveLength(0);
337+
notification.destroy();
338+
done();
339+
}, 500);
340+
}, 10);
341+
},
342+
);
374343
});
375344

376-
act(() => {
377-
jest.runAllTimers();
378-
wrapper.update();
345+
it('drop first notice when items limit exceeds', () => {
346+
jest.useFakeTimers();
347+
348+
let wrapper;
349+
350+
let notificationInstance;
351+
Notification.newInstance(
352+
{
353+
maxCount: 1,
354+
TEST_RENDER: node => {
355+
wrapper = mount(<div>{node}</div>);
356+
},
357+
},
358+
notification => {
359+
notificationInstance = notification;
360+
},
361+
);
362+
363+
const value = 'updated last';
364+
notificationInstance.notice({
365+
content: <span className="test-maxcount">simple show</span>,
366+
duration: 0,
367+
});
368+
notificationInstance.notice({
369+
content: <span className="test-maxcount">simple show</span>,
370+
duration: 0,
371+
});
372+
notificationInstance.notice({
373+
content: <span className="test-maxcount">{value}</span>,
374+
duration: 0,
375+
});
376+
377+
act(() => {
378+
jest.runAllTimers();
379+
wrapper.update();
380+
});
381+
382+
expect(wrapper.find('.test-maxcount')).toHaveLength(1);
383+
expect(wrapper.find('.test-maxcount').text()).toEqual(value);
384+
385+
jest.useRealTimers();
379386
});
380387

381-
expect(wrapper.find('.test-maxcount')).toHaveLength(1);
382-
expect(wrapper.find('.test-maxcount').text()).toEqual(value);
388+
it('duration should work', done => {
389+
let wrapper;
390+
391+
let notificationInstance;
392+
Notification.newInstance(
393+
{
394+
maxCount: 1,
395+
TEST_RENDER: node => {
396+
wrapper = mount(<div>{node}</div>);
397+
},
398+
},
399+
notification => {
400+
notificationInstance = notification;
401+
402+
notificationInstance.notice({
403+
content: <span className="auto-remove">bamboo</span>,
404+
duration: 99,
405+
});
406+
407+
setTimeout(() => {
408+
wrapper.update();
409+
expect(wrapper.find('.auto-remove').text()).toEqual('bamboo');
410+
411+
notificationInstance.notice({
412+
content: <span className="auto-remove">light</span>,
413+
duration: 0.5,
414+
});
383415

384-
jest.useRealTimers();
416+
setTimeout(() => {
417+
wrapper.update();
418+
expect(wrapper.find('.auto-remove').text()).toEqual('light');
419+
420+
setTimeout(() => {
421+
wrapper.update();
422+
expect(wrapper.find('.auto-remove')).toHaveLength(0);
423+
notification.destroy();
424+
done();
425+
}, 500);
426+
}, 10);
427+
}, 10);
428+
},
429+
);
430+
});
385431
});
386432

387433
it('onClick trigger', done => {

0 commit comments

Comments
 (0)