Skip to content

Commit d26758a

Browse files
authored
chore: optimize hover value experience (#139)
1 parent 335ddfc commit d26758a

File tree

4 files changed

+81
-34
lines changed

4 files changed

+81
-34
lines changed

src/RangePicker.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -564,9 +564,9 @@ function InnerRangePicker<DateType>(props: RangePickerProps<DateType>) {
564564
const onDateMouseLeave = () => {
565565
setHoverRangedValue(updateValues(selectedValue, null, mergedActivePickerIndex));
566566
if (mergedActivePickerIndex === 0) {
567-
onStartLeave(null);
567+
onStartLeave();
568568
} else {
569-
onEndLeave(null);
569+
onEndLeave();
570570
}
571571
};
572572

@@ -1013,9 +1013,9 @@ function InnerRangePicker<DateType>(props: RangePickerProps<DateType>) {
10131013
triggerChange(values, mergedActivePickerIndex);
10141014
// clear hover value style
10151015
if (mergedActivePickerIndex === 0) {
1016-
onStartLeave(null);
1016+
onStartLeave();
10171017
} else {
1018-
onEndLeave(null);
1018+
onEndLeave();
10191019
}
10201020
} else {
10211021
setSelectedValue(values);

src/hooks/useHoverValue.ts

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,23 @@
1-
import { useState, useEffect } from 'react';
1+
import { useState, useEffect, useRef } from 'react';
22
import useValueTexts, { ValueTextConfig } from './useValueTexts';
33

44
export default function useHoverValue<DateType>(
55
valueText: string,
66
{ formatList, generateConfig, locale }: ValueTextConfig<DateType>,
7-
): [string, (date: DateType) => void, (date: DateType) => void] {
8-
const [value, setValue] = useState(null);
7+
): [string, (date: DateType) => void, (immediately?: boolean) => void] {
8+
const [value, internalSetValue] = useState<DateType>(null);
9+
const raf = useRef(null);
10+
11+
function setValue(val: DateType, immediately: boolean = false) {
12+
cancelAnimationFrame(raf.current);
13+
if (immediately) {
14+
internalSetValue(val);
15+
return;
16+
}
17+
raf.current = requestAnimationFrame(() => {
18+
internalSetValue(val);
19+
});
20+
}
921

1022
const [, firstText] = useValueTexts(value, {
1123
formatList,
@@ -17,13 +29,15 @@ export default function useHoverValue<DateType>(
1729
setValue(date);
1830
}
1931

20-
function onLeave() {
21-
setValue(null);
32+
function onLeave(immediately: boolean = false) {
33+
setValue(null, immediately);
2234
}
2335

2436
useEffect(() => {
25-
onLeave();
37+
onLeave(true);
2638
}, [valueText]);
2739

40+
useEffect(() => () => cancelAnimationFrame(raf.current), []);
41+
2842
return [firstText, onEnter, onLeave];
2943
}

tests/picker.spec.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -740,14 +740,24 @@ describe('Picker.Basic', () => {
740740
});
741741

742742
describe('hover value', () => {
743+
beforeEach(() => {
744+
jest.useFakeTimers();
745+
});
746+
afterEach(() => {
747+
jest.useRealTimers();
748+
});
743749
it('should restore when leave', () => {
744750
const wrapper = mount(<MomentPicker open defaultValue={getMoment('2020-07-22')} />);
745751
const cell = wrapper.findCell(24);
746752
cell.simulate('mouseEnter');
753+
jest.runAllTimers();
754+
wrapper.update();
747755
expect(wrapper.find('input').prop('value')).toBe('2020-07-24');
748756
expect(wrapper.find('.rc-picker-input').hasClass('rc-picker-input-placeholder')).toBeTruthy();
749757

750758
cell.simulate('mouseLeave');
759+
jest.runAllTimers();
760+
wrapper.update();
751761
expect(wrapper.find('input').prop('value')).toBe('2020-07-22');
752762
expect(wrapper.find('.rc-picker-input').hasClass('rc-picker-input-placeholder')).toBeFalsy();
753763
});
@@ -757,6 +767,8 @@ describe('Picker.Basic', () => {
757767
wrapper.openPicker();
758768
const cell = wrapper.findCell(24);
759769
cell.simulate('mouseEnter');
770+
jest.runAllTimers();
771+
wrapper.update();
760772
expect(wrapper.find('input').prop('value')).toBe('2020-07-24');
761773
expect(wrapper.find('.rc-picker-input').hasClass('rc-picker-input-placeholder')).toBeTruthy();
762774

@@ -770,6 +782,8 @@ describe('Picker.Basic', () => {
770782
wrapper.openPicker();
771783
const cell = wrapper.findCell(24);
772784
cell.simulate('mouseEnter');
785+
jest.runAllTimers();
786+
wrapper.update();
773787
expect(wrapper.find('input').prop('value')).toBe('2020-07-24');
774788
expect(wrapper.find('.rc-picker-input').hasClass('rc-picker-input-placeholder')).toBeTruthy();
775789

tests/range.spec.tsx

Lines changed: 43 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1430,6 +1430,13 @@ describe('Picker.Range', () => {
14301430
});
14311431

14321432
describe('hover placeholder', () => {
1433+
beforeEach(() => {
1434+
jest.useFakeTimers();
1435+
});
1436+
afterEach(() => {
1437+
jest.useRealTimers();
1438+
});
1439+
14331440
const defaultValue: [Moment, Moment] = [getMoment('2020-07-22'), getMoment('2020-08-22')];
14341441

14351442
it('should restore when leave', () => {
@@ -1439,6 +1446,8 @@ describe('Picker.Range', () => {
14391446
wrapper.openPicker(0);
14401447
const leftCell = wrapper.findCell(24);
14411448
leftCell.simulate('mouseEnter');
1449+
jest.runAllTimers();
1450+
wrapper.update();
14421451
expect(
14431452
wrapper
14441453
.find('input')
@@ -1465,6 +1474,8 @@ describe('Picker.Range', () => {
14651474
).toBeFalsy();
14661475

14671476
leftCell.simulate('mouseLeave');
1477+
jest.runAllTimers();
1478+
wrapper.update();
14681479
expect(
14691480
wrapper
14701481
.find('input')
@@ -1496,6 +1507,8 @@ describe('Picker.Range', () => {
14961507
wrapper.openPicker(1);
14971508
const rightCell = wrapper.findCell(24, 1);
14981509
rightCell.simulate('mouseEnter');
1510+
jest.runAllTimers();
1511+
wrapper.update();
14991512
expect(
15001513
wrapper
15011514
.find('input')
@@ -1522,6 +1535,8 @@ describe('Picker.Range', () => {
15221535
).toBeTruthy();
15231536

15241537
rightCell.simulate('mouseLeave');
1538+
jest.runAllTimers();
1539+
wrapper.update();
15251540
expect(
15261541
wrapper
15271542
.find('input')
@@ -1556,6 +1571,8 @@ describe('Picker.Range', () => {
15561571
wrapper.openPicker(0);
15571572
const leftCell = wrapper.findCell(24, 0);
15581573
leftCell.simulate('mouseEnter');
1574+
jest.runAllTimers();
1575+
wrapper.update();
15591576
expect(
15601577
wrapper
15611578
.find('input')
@@ -1610,6 +1627,8 @@ describe('Picker.Range', () => {
16101627
// right
16111628
const rightCell = wrapper.findCell(24, 1);
16121629
rightCell.simulate('mouseEnter');
1630+
jest.runAllTimers();
1631+
wrapper.update();
16131632
expect(
16141633
wrapper
16151634
.find('input')
@@ -1661,6 +1680,30 @@ describe('Picker.Range', () => {
16611680
.hasClass('rc-picker-input-placeholder'),
16621681
).toBeFalsy();
16631682
});
1683+
1684+
// https://github.com/ant-design/ant-design/issues/26544
1685+
it('should clean hover style when selecting the same value with last value', () => {
1686+
const wrapper = mount(
1687+
<MomentRangePicker defaultValue={[getMoment('2020-07-24'), getMoment('2020-08-24')]} />,
1688+
);
1689+
1690+
wrapper.openPicker();
1691+
1692+
wrapper.selectCell(24, 0);
1693+
expect(
1694+
wrapper
1695+
.find('input')
1696+
.first()
1697+
.prop('value'),
1698+
).toBe('2020-07-24');
1699+
expect(
1700+
wrapper
1701+
.find('input')
1702+
.first()
1703+
.hasClass('rc-picker-input-placeholder'),
1704+
).toBeFalsy();
1705+
expect(wrapper.isOpen()).toBeTruthy();
1706+
});
16641707
});
16651708

16661709
// https://github.com/ant-design/ant-design/issues/25746
@@ -1699,30 +1742,6 @@ describe('Picker.Range', () => {
16991742
expect(wrapper.find('.rc-picker-ok button').props().disabled).toBeTruthy();
17001743
});
17011744

1702-
// https://github.com/ant-design/ant-design/issues/26544
1703-
it('should clean hover style when selecting the same value with last value', () => {
1704-
const wrapper = mount(
1705-
<MomentRangePicker defaultValue={[getMoment('2020-07-24'), getMoment('2020-08-24')]} />,
1706-
);
1707-
1708-
wrapper.openPicker();
1709-
1710-
wrapper.selectCell(24, 0);
1711-
expect(
1712-
wrapper
1713-
.find('input')
1714-
.first()
1715-
.prop('value'),
1716-
).toBe('2020-07-24');
1717-
expect(
1718-
wrapper
1719-
.find('input')
1720-
.first()
1721-
.hasClass('rc-picker-input-placeholder'),
1722-
).toBeFalsy();
1723-
expect(wrapper.isOpen()).toBeTruthy();
1724-
});
1725-
17261745
// https://github.com/ant-design/ant-design/issues/26024
17271746
it('panel should keep open when nextValue is empty', () => {
17281747
const wrapper = mount(<MomentRangePicker />);

0 commit comments

Comments
 (0)