Skip to content

Commit 1e104e6

Browse files
authored
feat: add previewValue option (#934)
* feat: add showPreviewValue option * feat: update test * feat: update * feat: update onPanelHover * feat: update test case * feat: update api key * feat: update setHoverSource onHover code * feat: reset generateWithTZ test * feat: update code
1 parent 95da35c commit 1e104e6

File tree

10 files changed

+141
-11
lines changed

10 files changed

+141
-11
lines changed

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ render(<Picker />, mountNode);
5454
| autoFocus | boolean | false | whether auto focus |
5555
| showTime | boolean \| Object | [showTime options](#showTime-options) | to provide an additional time selection |
5656
| picker | time \| date \| week \| month \| year | | control which kind of panel should be shown |
57+
| previewValue | false \| hover | hover | When the user selects the date hover option, the value of the input field undergoes a temporary change |
5758
| format | String \| String[] | depends on whether you set timePicker and your locale | use to format/parse date(without time) value to/from input. When an array is provided, all values are used for parsing and first value for display |
5859
| use12Hours | boolean | false | 12 hours display mode |
5960
| value | moment | | current value like input's value |
@@ -102,7 +103,7 @@ render(<Picker />, mountNode);
102103
### RangePicker
103104

104105
| Property | Type | Default | Description |
105-
| --- | --- | --- | --- |
106+
| --- | --- | --- | --- | --- |
106107
| prefixCls | String | rc-picker | prefixCls of this component |
107108
| className | String | '' | additional css class of root dom |
108109
| style | React.CSSProperties | | additional style of root dom node |
@@ -112,6 +113,7 @@ render(<Picker />, mountNode);
112113
| defaultPickerValue | moment | | Set default display picker view date |
113114
| separator | String | '~' | set separator between inputs |
114115
| picker | time \| date \| week \| month \| year | | control which kind of panel |
116+
| previewValue | false \| hover | hover | When the user selects the date hover option, the value of the input field undergoes a temporary change |
115117
| placeholder | [String, String] | | placeholder of date input |
116118
| showTime | boolean \| Object | [showTime options](#showTime-options) | to provide an additional time selection |
117119
| showTime.defaultValue | [moment, moment] | | to set default time of selected date |

docs/examples/basic.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,10 @@ export default () => {
154154
<h3>Keyboard event with prevent default behaviors</h3>
155155
<Picker<Moment> {...sharedProps} locale={enUS} onKeyDown={keyDown} />
156156
</div>
157+
<div style={{ margin: '0 8px' }}>
158+
<h3>PreviewValue is false</h3>
159+
<Picker<Moment> {...sharedProps} locale={enUS} onKeyDown={keyDown} previewValue={false} />
160+
</div>
157161
</div>
158162
</div>
159163
);

docs/examples/range.tsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,17 @@ export default () => {
202202
disabledDate={disabledDate}
203203
/>
204204
</div>
205+
<div style={{ margin: '0 8px' }}>
206+
<h3>PreviewValue is false</h3>
207+
<RangePicker<Moment>
208+
{...sharedProps}
209+
previewValue={false}
210+
value={undefined}
211+
locale={zhCN}
212+
placeholder={['start...', 'end...']}
213+
disabledDate={disabledDate}
214+
/>
215+
</div>
205216
</div>
206217
</div>
207218
);

docs/examples/time.tsx

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ const testClassNames = {
1212
suffix: 'test-suffix',
1313
popupContent: 'test-popup-content',
1414
popupItem: 'test-popup-item',
15-
}
15+
};
1616

1717
export default () => {
1818
return (
@@ -53,6 +53,18 @@ export default () => {
5353
disabledHours: () => (type === 'start' ? [now.hours()] : [now.hours() - 5]),
5454
})}
5555
/>
56+
57+
<h3>PreviewValue is false</h3>
58+
<RangePicker
59+
defaultValue={[defaultValue, defaultValue]}
60+
picker="time"
61+
locale={zhCN}
62+
previewValue={false}
63+
generateConfig={momentGenerateConfig}
64+
disabledTime={(now, type) => ({
65+
disabledHours: () => (type === 'start' ? [now.hours()] : [now.hours() - 5]),
66+
})}
67+
/>
5668
</div>
5769
);
5870
};

src/PickerInput/RangePicker.tsx

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ function RangePicker<DateType extends object = any>(
164164
styles: propStyles,
165165
classNames: propClassNames,
166166

167+
previewValue,
167168
// Value
168169
defaultValue,
169170
value,
@@ -483,12 +484,19 @@ function RangePicker<DateType extends object = any>(
483484
[activeInputLeft: number, activeInputRight: number, selectorWidth: number]
484485
>([0, 0, 0]);
485486

487+
const onSetHover = (date: RangeValueType<DateType> | null, source: 'cell' | 'preset') => {
488+
if (previewValue !== 'hover') {
489+
return;
490+
}
491+
setInternalHoverValues(date);
492+
setHoverSource(source);
493+
};
494+
486495
// ======================= Presets ========================
487496
const presetList = usePresets(presets, ranges);
488497

489498
const onPresetHover = (nextValues: RangeValueType<DateType> | null) => {
490-
setInternalHoverValues(nextValues);
491-
setHoverSource('preset');
499+
onSetHover(nextValues, 'preset');
492500
};
493501

494502
const onPresetSubmit = (nextValues: RangeValueType<DateType>) => {
@@ -505,8 +513,7 @@ function RangePicker<DateType extends object = any>(
505513

506514
// ======================== Panel =========================
507515
const onPanelHover = (date: DateType) => {
508-
setInternalHoverValues(date ? fillCalendarValue(date, activeIndex) : null);
509-
setHoverSource('cell');
516+
onSetHover(date ? fillCalendarValue(date, activeIndex) : null, 'cell');
510517
};
511518

512519
// >>> Focus

src/PickerInput/SinglePicker.tsx

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,8 @@ function Picker<DateType extends object = any>(
128128
styles: propStyles,
129129
classNames: propClassNames,
130130

131+
previewValue,
132+
131133
// Value
132134
order,
133135
defaultValue,
@@ -406,15 +408,22 @@ function Picker<DateType extends object = any>(
406408
}
407409
}, [mergedOpen]);
408410

411+
const onSetHover = (date: DateType | null, source: 'cell' | 'preset') => {
412+
if (previewValue !== 'hover') {
413+
return;
414+
}
415+
setInternalHoverValue(date);
416+
setHoverSource(source);
417+
};
418+
409419
// ========================================================
410420
// == Panels ==
411421
// ========================================================
412422
// ======================= Presets ========================
413423
const presetList = usePresets(presets);
414424

415425
const onPresetHover = (nextValue: DateType | null) => {
416-
setInternalHoverValue(nextValue);
417-
setHoverSource('preset');
426+
onSetHover(nextValue, 'preset');
418427
};
419428

420429
// TODO: handle this
@@ -433,8 +442,7 @@ function Picker<DateType extends object = any>(
433442

434443
// ======================== Panel =========================
435444
const onPanelHover = (date: DateType | null) => {
436-
setInternalHoverValue(date);
437-
setHoverSource('cell');
445+
onSetHover(date, 'cell');
438446
};
439447

440448
// >>> Focus

src/PickerInput/hooks/useFilledProps.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ type PickedProps<DateType extends object = any> = Pick<
3333
| 'minDate'
3434
| 'maxDate'
3535
| 'defaultOpenValue'
36+
| 'previewValue'
3637
> & {
3738
multiple?: boolean;
3839
// RangePicker showTime definition is different with Picker
@@ -96,6 +97,7 @@ export default function useFilledProps<
9697
locale,
9798
picker = 'date',
9899
prefixCls = 'rc-picker',
100+
previewValue = 'hover',
99101
styles = {},
100102
classNames = {},
101103
order = true,
@@ -161,6 +163,7 @@ export default function useFilledProps<
161163
const filledProps = React.useMemo(
162164
() => ({
163165
...props,
166+
previewValue,
164167
prefixCls,
165168
locale: mergedLocale,
166169
picker,

src/interface.tsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,8 @@ export type LegacyOnKeyDown = (
313313

314314
export type SemanticName = 'root' | 'prefix' | 'input' | 'suffix';
315315

316+
export type PreviewValueType = 'hover';
317+
316318
export type PanelSemanticName = 'root' | 'header' | 'body' | 'content' | 'item' | 'footer';
317319

318320
export interface SharedPickerProps<DateType extends object = any>
@@ -425,6 +427,13 @@ export interface SharedPickerProps<DateType extends object = any>
425427
*/
426428
preserveInvalidOnBlur?: boolean;
427429

430+
/**
431+
* When the user selects the date hover option, the value of the input field undergoes a temporary change.
432+
* `false` will not preview value.
433+
* `hover` will preview value when hover.
434+
*/
435+
previewValue?: false | PreviewValueType;
436+
428437
// Motion
429438
transitionName?: string;
430439

tests/range.spec.tsx

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2120,4 +2120,32 @@ describe('Picker.Range', () => {
21202120
openPicker(container, 1);
21212121
expect(container.querySelectorAll('.rc-picker-input')[0]).toHaveClass('rc-picker-input-active');
21222122
});
2123+
2124+
it('should not update preview value in input when previewValue is false', () => {
2125+
const { container } = render(
2126+
<DayRangePicker
2127+
minDate={dayjs('2024')}
2128+
open
2129+
mode={['year', 'year']}
2130+
showTime
2131+
previewValue={false}
2132+
needConfirm
2133+
value={[dayjs('2024-01-01'), dayjs('2025-01-01')]}
2134+
/>,
2135+
);
2136+
2137+
// 找到第一个输入框并保存初始值
2138+
const inputStart = container.querySelectorAll<HTMLInputElement>('.rc-picker-input input')[0];
2139+
const initialValueStart = inputStart.value;
2140+
2141+
// 打开第一个面板并 hover 一个新值(例如 2028 年)
2142+
const targetCell = document.querySelector('[title="2028"]') as HTMLElement;
2143+
expect(targetCell).toBeTruthy(); // 确保存在
2144+
2145+
// 2. 模拟鼠标移入(hover)
2146+
fireEvent.mouseEnter(targetCell);
2147+
2148+
// 确保值未更新(仍为原值)
2149+
expect(inputStart.value).toBe(initialValueStart);
2150+
});
21232151
});

tests/time.spec.tsx

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import { fireEvent, render } from '@testing-library/react';
22
import { resetWarned } from '@rc-component/util/lib/warning';
33
import React from 'react';
4-
import { DayPicker, getDay, openPicker, selectCell, findCell } from './util/commonUtil';
4+
import dayjs from 'dayjs';
5+
import { DayPicker, getDay, openPicker, selectCell } from './util/commonUtil';
56

67
describe('Picker.Time', () => {
78
beforeEach(() => {
@@ -68,4 +69,49 @@ describe('Picker.Time', () => {
6869
fireEvent.mouseEnter(getColCell(4, 1));
6970
expect(container.querySelector('input')).toHaveValue('1990-09-03 12:00:00.000 PM');
7071
});
72+
73+
it('hover should not update preview value in input when previewValue is false', async () => {
74+
const { container } = render(
75+
<DayPicker
76+
showTime={{
77+
showMillisecond: true,
78+
use12Hours: true,
79+
}}
80+
previewValue={false}
81+
defaultValue={dayjs('1990-09-03 01:02:03')}
82+
/>,
83+
);
84+
openPicker(container);
85+
86+
const getColCell = (colIndex: number, cellIndex: number) => {
87+
const column = document.querySelectorAll('.rc-picker-time-panel-column')[colIndex];
88+
const cell = column.querySelectorAll('.rc-picker-time-panel-cell-inner')[cellIndex];
89+
90+
return cell;
91+
};
92+
93+
// Hour
94+
fireEvent.mouseEnter(getColCell(0, 3));
95+
expect(container.querySelector('input')).toHaveValue('1990-09-03 01:02:03.000 AM');
96+
97+
// Let test for mouse leave
98+
fireEvent.mouseLeave(getColCell(0, 3));
99+
expect(container.querySelector('input')).toHaveValue('1990-09-03 01:02:03.000 AM');
100+
101+
// Minute
102+
fireEvent.mouseEnter(getColCell(1, 2));
103+
expect(container.querySelector('input')).toHaveValue('1990-09-03 01:02:03.000 AM');
104+
105+
// Second
106+
fireEvent.mouseEnter(getColCell(2, 1));
107+
expect(container.querySelector('input')).toHaveValue('1990-09-03 01:02:03.000 AM');
108+
109+
// Millisecond
110+
fireEvent.mouseEnter(getColCell(3, 1));
111+
expect(container.querySelector('input')).toHaveValue('1990-09-03 01:02:03.000 AM');
112+
113+
// Meridiem
114+
fireEvent.mouseEnter(getColCell(4, 1));
115+
expect(container.querySelector('input')).toHaveValue('1990-09-03 01:02:03.000 AM');
116+
});
71117
});

0 commit comments

Comments
 (0)