Skip to content

Commit c26ea49

Browse files
authored
fix: format length check logic (#730)
* test: test driven * fix: format logic * refactor: adjust filled logic * chore: comment * chore: unique code * chore: simple code
1 parent 3785d1b commit c26ea49

File tree

6 files changed

+158
-75
lines changed

6 files changed

+158
-75
lines changed

src/PickerInput/hooks/useFilledProps.ts

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { warning } from 'rc-util';
22
import * as React from 'react';
3-
import { fillLocale } from '../../hooks/useLocale';
4-
import { getTimeConfig } from '../../hooks/useTimeConfig';
3+
import useLocale from '../../hooks/useLocale';
4+
import { fillShowTimeConfig, getTimeProps } from '../../hooks/useTimeConfig';
55
import type { FormatType, InternalMode, PickerMode } from '../../interface';
66
import { toArray } from '../../utils/miscUtil';
77
import type { RangePickerProps } from '../RangePicker';
@@ -133,16 +133,17 @@ export default function useFilledProps<
133133
const complexPicker = internalPicker === 'time' || internalPicker === 'datetime' || multiple;
134134
const mergedNeedConfirm = needConfirm ?? complexPicker;
135135

136+
// ========================== Time ==========================
137+
// Auto `format` need to check `showTime.showXXX` first.
138+
// And then merge the `locale` into `mergedShowTime`.
139+
const [timeProps, localeTimeProps, showTimeFormat, propFormat] = getTimeProps(props);
140+
136141
// ======================= Locales ========================
137-
const mergedLocale = fillLocale(locale);
142+
const mergedLocale = useLocale(locale, localeTimeProps);
143+
138144
const mergedShowTime = React.useMemo(
139-
() =>
140-
getTimeConfig({
141-
...props,
142-
picker: internalPicker,
143-
locale: mergedLocale,
144-
}),
145-
[props, internalPicker, mergedLocale],
145+
() => fillShowTimeConfig(internalPicker, showTimeFormat, propFormat, timeProps, mergedLocale),
146+
[internalPicker, showTimeFormat, propFormat, timeProps, mergedLocale],
146147
);
147148

148149
// ======================= Warning ========================

src/PickerPanel/index.tsx

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import classNames from 'classnames';
22
import { useEvent, useMergedState, warning } from 'rc-util';
33
import * as React from 'react';
44
import useLocale from '../hooks/useLocale';
5-
import { getTimeConfig } from '../hooks/useTimeConfig';
5+
import { fillShowTimeConfig, getTimeProps } from '../hooks/useTimeConfig';
66
import useToggleDates from '../hooks/useToggleDates';
77
import type {
88
CellRender,
@@ -190,21 +190,21 @@ function PickerPanel<DateType extends object = any>(
190190
nativeElement: rootRef.current,
191191
}));
192192

193+
// ========================== Time ==========================
194+
// Auto `format` need to check `showTime.showXXX` first.
195+
// And then merge the `locale` into `mergedShowTime`.
196+
const [timeProps, localeTimeProps, showTimeFormat, propFormat] = getTimeProps(props);
197+
193198
// ========================= Locale =========================
194-
const filledLocale = useLocale(locale);
199+
const filledLocale = useLocale(locale, localeTimeProps);
195200

196201
// ========================= Picker =========================
197202
const internalPicker: InternalMode = picker === 'date' && showTime ? 'datetime' : picker;
198203

199204
// ======================== ShowTime ========================
200205
const mergedShowTime = React.useMemo(
201-
() =>
202-
getTimeConfig({
203-
...props,
204-
picker: internalPicker,
205-
locale: filledLocale,
206-
}),
207-
[props, internalPicker, filledLocale],
206+
() => fillShowTimeConfig(internalPicker, showTimeFormat, propFormat, timeProps, filledLocale),
207+
[internalPicker, showTimeFormat, propFormat, timeProps, filledLocale],
208208
);
209209

210210
// ========================== Now ===========================
@@ -244,14 +244,7 @@ function PickerPanel<DateType extends object = any>(
244244
(nextValue === null ||
245245
mergedValue.length !== nextValue.length ||
246246
mergedValue.some(
247-
(ori, index) =>
248-
!isSame(
249-
generateConfig,
250-
locale,
251-
ori,
252-
nextValue[index],
253-
picker === 'date' && mergedShowTime ? 'datetime' : picker,
254-
),
247+
(ori, index) => !isSame(generateConfig, locale, ori, nextValue[index], internalPicker),
255248
))
256249
) {
257250
onChange?.(multiple ? nextValue : nextValue[0]);

src/hooks/useLocale.ts

Lines changed: 62 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,54 @@
11
import React from 'react';
2-
import type { Locale } from '../interface';
2+
import type { Locale, SharedTimeProps } from '../interface';
3+
4+
export function fillTimeFormat(
5+
showHour: boolean,
6+
showMinute: boolean,
7+
showSecond: boolean,
8+
showMillisecond: boolean,
9+
showMeridiem: boolean,
10+
) {
11+
let timeFormat = '';
12+
13+
// Base HH:mm:ss
14+
const cells = [];
15+
16+
if (showHour) {
17+
cells.push(showMeridiem ? 'hh' : 'HH');
18+
}
19+
if (showMinute) {
20+
cells.push('mm');
21+
}
22+
if (showSecond) {
23+
cells.push('ss');
24+
}
25+
26+
timeFormat = cells.join(':');
27+
28+
// Millisecond
29+
if (showMillisecond) {
30+
timeFormat += '.SSS';
31+
}
32+
33+
// Meridiem
34+
if (showMeridiem) {
35+
timeFormat += ' A';
36+
}
37+
38+
return timeFormat;
39+
}
340

441
/**
542
* Used for `useFilledProps` since it already in the React.useMemo
643
*/
7-
export function fillLocale(locale: Locale): Locale {
44+
function fillLocale(
45+
locale: Locale,
46+
showHour: boolean,
47+
showMinute: boolean,
48+
showSecond: boolean,
49+
showMillisecond: boolean,
50+
use12Hours: boolean,
51+
): Locale {
852
// Not fill `monthFormat` since `locale.shortMonths` handle this
953
// Not fill `cellMeridiemFormat` since AM & PM by default
1054
const {
@@ -30,12 +74,14 @@ export function fillLocale(locale: Locale): Locale {
3074
// cellMeridiemFormat,
3175
} = locale;
3276

77+
const timeFormat = fillTimeFormat(showHour, showMinute, showSecond, showMillisecond, use12Hours);
78+
3379
return {
3480
...locale,
3581

36-
fieldDateTimeFormat: fieldDateTimeFormat || 'YYYY-MM-DD HH:mm:ss',
82+
fieldDateTimeFormat: fieldDateTimeFormat || `YYYY-MM-DD ${timeFormat}`,
3783
fieldDateFormat: fieldDateFormat || 'YYYY-MM-DD',
38-
fieldTimeFormat: fieldTimeFormat || 'HH:mm:ss',
84+
fieldTimeFormat: fieldTimeFormat || timeFormat,
3985
fieldMonthFormat: fieldMonthFormat || 'YYYY-MM',
4086
fieldYearFormat: fieldYearFormat || 'YYYY',
4187
fieldWeekFormat: fieldWeekFormat || 'gggg-wo',
@@ -52,6 +98,16 @@ export function fillLocale(locale: Locale): Locale {
5298
/**
5399
* Fill locale format as start up
54100
*/
55-
export default function useLocale(locale: Locale) {
56-
return React.useMemo<Locale>(() => fillLocale(locale), [locale]);
101+
export default function useLocale<DateType extends object>(
102+
locale: Locale,
103+
showProps: Pick<
104+
SharedTimeProps<DateType>,
105+
'showHour' | 'showMinute' | 'showSecond' | 'showMillisecond' | 'use12Hours'
106+
>,
107+
) {
108+
const { showHour, showMinute, showSecond, showMillisecond, use12Hours } = showProps;
109+
return React.useMemo<Locale>(
110+
() => fillLocale(locale, showHour, showMinute, showSecond, showMillisecond, use12Hours),
111+
[locale, showHour, showMinute, showSecond, showMillisecond, use12Hours],
112+
);
57113
}

src/hooks/useTimeConfig.ts

Lines changed: 64 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { InternalMode, Locale, SharedPickerProps, SharedTimeProps } from '../interface';
22
import { getRowFormat, pickProps, toArray } from '../utils/miscUtil';
3+
import { fillTimeFormat } from './useLocale';
34

45
function checkShow(format: string, keywords: string[], show?: boolean) {
56
return show ?? keywords.some((keyword) => format.includes(keyword));
@@ -48,28 +49,76 @@ function isStringFormat(format: any): format is string {
4849
return format && typeof format === 'string';
4950
}
5051

51-
export function getTimeConfig<DateType extends object>(componentProps: {
52+
export interface ComponentProps<DateType extends object> {
5253
picker?: InternalMode;
5354
showTime?: boolean | Partial<SharedTimeProps<DateType>>;
5455
locale: Locale;
5556
format?: SharedPickerProps['format'];
56-
}): SharedTimeProps<DateType> {
57-
const { showTime, picker, locale, format: propFormat } = componentProps;
57+
}
5858

59-
const isTimePicker = picker === 'time';
59+
/**
60+
* Get `showHour`, `showMinute`, `showSecond` or other from the props.
61+
* This is pure function, will not get `showXXX` from the `format` prop.
62+
*/
63+
export function getTimeProps<DateType extends object>(
64+
componentProps: ComponentProps<DateType>,
65+
): [
66+
showTimeProps: SharedTimeProps<DateType>,
67+
showTimePropsForLocale: SharedTimeProps<DateType>,
68+
showTimeFormat: string,
69+
propFormat: string,
70+
] {
71+
const { showTime, picker } = componentProps;
72+
73+
const pickedProps = pickTimeProps(componentProps);
74+
const isShowTimeConfig = showTime && typeof showTime === 'object';
75+
const timeConfig = isShowTimeConfig ? showTime : pickedProps;
76+
77+
const { showMillisecond } = timeConfig;
78+
let { showHour, showMinute, showSecond } = timeConfig;
79+
80+
if (!showHour && !showMinute && !showSecond && !showMillisecond) {
81+
showHour = true;
82+
showMinute = true;
83+
showSecond = true;
84+
}
85+
86+
const mergedFormat = isShowTimeConfig
87+
? showTime.format
88+
: picker === 'time'
89+
? pickedProps.format
90+
: null;
6091

61-
if (showTime || isTimePicker) {
62-
const isShowTimeConfig = showTime && typeof showTime === 'object';
92+
return [
93+
timeConfig,
94+
{
95+
...timeConfig,
96+
showHour,
97+
showMinute,
98+
showSecond,
99+
showMillisecond,
100+
},
101+
mergedFormat,
102+
pickedProps.format,
103+
];
104+
}
63105

64-
const timeConfig = isShowTimeConfig ? showTime : pickTimeProps(componentProps);
106+
export function fillShowTimeConfig<DateType extends object>(
107+
picker: InternalMode,
108+
showTimeFormat: string,
109+
propFormat: string,
110+
timeConfig: SharedTimeProps<DateType>,
111+
locale: Locale,
112+
): SharedTimeProps<DateType> {
113+
const isTimePicker = picker === 'time';
65114

66-
const pickedProps = pickProps(timeConfig);
115+
if (picker === 'datetime' || isTimePicker) {
116+
const pickedProps = timeConfig;
67117

68118
// ====================== BaseFormat ======================
69-
const showTimeFormat = isShowTimeConfig ? showTime.format : isTimePicker && propFormat;
70-
const defaultFormat = getRowFormat(picker, locale, null) as string;
119+
const defaultLocaleFormat = getRowFormat(picker, locale, null) as string;
71120

72-
let baselineFormat = defaultFormat;
121+
let baselineFormat = defaultLocaleFormat;
73122

74123
const formatList = [showTimeFormat, propFormat];
75124
for (let i = 0; i < formatList.length; i += 1) {
@@ -85,7 +134,7 @@ export function getTimeConfig<DateType extends object>(componentProps: {
85134
let { showHour, showMinute, showSecond, showMillisecond } = pickedProps;
86135
const { use12Hours } = pickedProps;
87136

88-
const showMeridiem = checkShow(baselineFormat, ['a', 'A', 'LT', 'LLL'], use12Hours);
137+
const showMeridiem = checkShow(baselineFormat, ['a', 'A', 'LT', 'LLL', 'LTS'], use12Hours);
89138

90139
const hasShowConfig = [showHour, showMinute, showSecond, showMillisecond].some(
91140
(show) => show !== undefined,
@@ -107,36 +156,9 @@ export function getTimeConfig<DateType extends object>(componentProps: {
107156
}
108157

109158
// ======================== Format ========================
110-
let timeFormat = isStringFormat(showTimeFormat) ? showTimeFormat : null;
111-
112-
if (!timeFormat) {
113-
timeFormat = '';
114-
115-
// Base HH:mm:ss
116-
const cells = [];
117-
118-
if (showHour) {
119-
cells.push('HH');
120-
}
121-
if (showMinute) {
122-
cells.push('mm');
123-
}
124-
if (showSecond) {
125-
cells.push('ss');
126-
}
127-
128-
timeFormat = cells.join(':');
129-
130-
// Millisecond
131-
if (showMillisecond) {
132-
timeFormat += '.SSS';
133-
}
134-
135-
// Meridiem
136-
if (showMeridiem) {
137-
timeFormat += ' A';
138-
}
139-
}
159+
const timeFormat =
160+
showTimeFormat ||
161+
fillTimeFormat(showHour, showMinute, showSecond, showMillisecond, showMeridiem);
140162

141163
// ======================== Props =========================
142164
return {

tests/new-range.spec.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,7 @@ describe('NewPicker.Range', () => {
277277
const { rerender } = render(<DayRangePicker picker="time" format="LT" open />);
278278
expect(document.querySelectorAll('.rc-picker-time-panel-column')).toHaveLength(3);
279279

280+
console.log('~~~~~~~~~~~~~~~~');
280281
// With second
281282
rerender(<DayRangePicker picker="time" format="LTS" open />);
282283
expect(document.querySelectorAll('.rc-picker-time-panel-column')).toHaveLength(4);

tests/picker.spec.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1343,4 +1343,14 @@ describe('Picker.Basic', () => {
13431343

13441344
expect(document.querySelectorAll('.rc-picker-header-view')[1].textContent).toEqual('01:02:03');
13451345
});
1346+
1347+
it('use12Hours has longer size', () => {
1348+
const { container } = render(
1349+
<DayPicker use12Hours picker="time" defaultValue={getDay('18:03:04')} />,
1350+
);
1351+
1352+
const inputEle = container.querySelector('input');
1353+
expect(inputEle.size).toBe(12);
1354+
expect(inputEle).toHaveValue('06:03:04 PM');
1355+
});
13461356
});

0 commit comments

Comments
 (0)