From ae26530734c14eb60a69099164b442c571b4655e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E8=B4=A7=E7=88=B1=E5=90=83=E7=99=BD=E8=90=9D?= =?UTF-8?q?=E5=8D=9C?= Date: Tue, 23 Jan 2024 16:05:12 +0800 Subject: [PATCH] fix: m1 pro effect merge & modify API design (#744) * fix: m1 pro behaivor * test: update test case * refactor: align to type is mask * test: update test case --- docs/examples/debug.tsx | 6 ++++++ src/PickerInput/hooks/useDelayState.ts | 28 +++++++++---------------- src/PickerInput/hooks/useFieldFormat.ts | 2 +- src/interface.tsx | 4 ++-- tests/keyboard.spec.tsx | 5 +++++ tests/new-range.spec.tsx | 6 +++++- tests/picker.spec.tsx | 5 +++++ tests/range.spec.tsx | 1 + tests/util/commonUtil.tsx | 9 +++++--- 9 files changed, 41 insertions(+), 25 deletions(-) diff --git a/docs/examples/debug.tsx b/docs/examples/debug.tsx index 17cd37e3e..ad7ac5e95 100644 --- a/docs/examples/debug.tsx +++ b/docs/examples/debug.tsx @@ -158,6 +158,10 @@ export default () => { defaultOpenValue={dayjs()} // open picker="time" + format={{ + format: 'HH:mm:ss.SSS A', + type: 'mask', + }} // showTime={{ // defaultValue: dayjs(), // }} @@ -171,6 +175,8 @@ export default () => { // }), // }} // showTime={{}} + // disabled + open onChange={(...args) => { console.log('🔥 Change:', ...args); }} diff --git a/src/PickerInput/hooks/useDelayState.ts b/src/PickerInput/hooks/useDelayState.ts index 1af230fae..d64f19639 100644 --- a/src/PickerInput/hooks/useDelayState.ts +++ b/src/PickerInput/hooks/useDelayState.ts @@ -1,13 +1,7 @@ import { useEvent, useMergedState } from 'rc-util'; +import raf from 'rc-util/lib/raf'; import React from 'react'; -// We need wait for outside state updated. -// Which means need 2 effect: -// 1. Outside sync state -// 2. Still may be old state -// 3. Safe to sync state -const DELAY_TIMES = 2; - /** * Will be `true` immediately for next effect. * But will be `false` for a delay of effect. @@ -21,10 +15,14 @@ export default function useDelayState( value, }); - const [times, setTimes] = React.useState(false); const nextValueRef = React.useRef(value); // ============================= Update ============================= + const rafRef = React.useRef(); + const cancelRaf = () => { + raf.cancel(rafRef.current); + }; + const doUpdate = useEvent(() => { setState(nextValueRef.current); @@ -34,24 +32,18 @@ export default function useDelayState( }); const updateValue = useEvent((next: T, immediately?: boolean) => { + cancelRaf(); + nextValueRef.current = next; if (next || immediately) { doUpdate(); - setTimes(false); } else { - setTimes(0); + rafRef.current = raf(doUpdate); } }); - // ============================= Effect ============================= - React.useEffect(() => { - if (times === DELAY_TIMES) { - doUpdate(); - } else if (times !== false && times < DELAY_TIMES) { - setTimes(times + 1); - } - }, [times, doUpdate]); + React.useEffect(() => cancelRaf, []); return [state, updateValue]; } diff --git a/src/PickerInput/hooks/useFieldFormat.ts b/src/PickerInput/hooks/useFieldFormat.ts index d0b5ed348..da1e6e785 100644 --- a/src/PickerInput/hooks/useFieldFormat.ts +++ b/src/PickerInput/hooks/useFieldFormat.ts @@ -14,7 +14,7 @@ export function useFieldFormat( const firstFormat = formatList[0]; const maskFormat = - typeof firstFormat === 'object' && firstFormat.align ? firstFormat.format : null; + typeof firstFormat === 'object' && firstFormat.type === 'mask' ? firstFormat.format : null; return [ // Format list diff --git a/src/interface.tsx b/src/interface.tsx index ca273a639..d8396dbd8 100644 --- a/src/interface.tsx +++ b/src/interface.tsx @@ -331,7 +331,7 @@ export interface SharedPickerProps showWeek?: boolean; /** * Config the input field parse and format. - * When set `format.align`, it will force user input align with your input, + * When set `format.type`, it will force user input type with your input, * it's only support basic format mask: YYYY, MM, DD, HH, mm, ss, SSS. * Once use config mode, it must be fill with format your config. */ @@ -340,7 +340,7 @@ export interface SharedPickerProps | FormatType[] | { format: string; - align?: boolean; + type?: 'mask'; }; // Icons diff --git a/tests/keyboard.spec.tsx b/tests/keyboard.spec.tsx index edb713ca9..e7a09648c 100644 --- a/tests/keyboard.spec.tsx +++ b/tests/keyboard.spec.tsx @@ -111,6 +111,11 @@ describe('Picker.Keyboard', () => { fireEvent.keyDown(container.querySelector('input'), { key: 'Escape', }); + + act(() => { + jest.runAllTimers(); + }); + expect(isOpen()).toBeFalsy(); }); diff --git a/tests/new-range.spec.tsx b/tests/new-range.spec.tsx index 9cb0fa602..4855267e4 100644 --- a/tests/new-range.spec.tsx +++ b/tests/new-range.spec.tsx @@ -683,6 +683,10 @@ describe('NewPicker.Range', () => { // Close panel to auto focus next end field fireEvent.click(document.body); + act(() => { + jest.runAllTimers(); + }); + expect(container.querySelectorAll('input')[1]).toHaveFocus(); expect(isOpen()).toBeTruthy(); @@ -846,7 +850,7 @@ describe('NewPicker.Range', () => { diff --git a/tests/picker.spec.tsx b/tests/picker.spec.tsx index 505cd0a2e..ddf8edcc4 100644 --- a/tests/picker.spec.tsx +++ b/tests/picker.spec.tsx @@ -168,6 +168,7 @@ describe('Picker.Basic', () => { for (let i = 0; i < 10; i += 1) { act(() => { fireEvent.click(document.body); + jest.runAllTimers(); }); expect(onOpenChange).toHaveBeenCalledTimes(i + 1); } @@ -1064,6 +1065,10 @@ describe('Picker.Basic', () => { expect(isOpen()).toBeTruthy(); keyDown(KeyCode.ESC); + act(() => { + jest.runAllTimers(); + }); + expect(onKeyDown).toHaveBeenCalled(); expect(isOpen()).toBeFalsy(); onKeyDown.mockClear(); diff --git a/tests/range.spec.tsx b/tests/range.spec.tsx index ddb8cb0bb..94712c3fb 100644 --- a/tests/range.spec.tsx +++ b/tests/range.spec.tsx @@ -1082,6 +1082,7 @@ describe('Picker.Range', () => { defaultValue={[getDay(defaultValue[0]), getDay(defaultValue[1] || defaultValue[0])]} />, ); + openPicker(container, 1); selectCell(targetCell); closePicker(container, 1); diff --git a/tests/util/commonUtil.tsx b/tests/util/commonUtil.tsx index 6e671e8a7..513dc746e 100644 --- a/tests/util/commonUtil.tsx +++ b/tests/util/commonUtil.tsx @@ -123,9 +123,12 @@ export function closePicker(container: HTMLElement, index = 0) { const input = container.querySelectorAll('input')[index]; fireEvent.blur(input); - act(() => { - jest.runAllTimers(); - }); + // Loop to pass all the timer (includes raf) + for (let i = 0; i < 5; i += 1) { + act(() => { + jest.runAllTimers(); + }); + } } export function isOpen() {