Skip to content

Commit

Permalink
feat: supprt custom format (#142)
Browse files Browse the repository at this point in the history
* ✨ feat: supprt custom format

* test: add test case

* chore: update picker input readonly
  • Loading branch information
kerm1it authored Sep 18, 2020
1 parent c7e1123 commit f547043
Show file tree
Hide file tree
Showing 8 changed files with 111 additions and 23 deletions.
14 changes: 7 additions & 7 deletions src/Picker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ import PickerPanel, {
PickerPanelTimeProps,
} from './PickerPanel';
import PickerTrigger from './PickerTrigger';
import { isEqual } from './utils/dateUtil';
import { formatValue, isEqual } from './utils/dateUtil';
import getDataOrAriaProps, { toArray } from './utils/miscUtil';
import PanelContext, { ContextOperationRefProps } from './PanelContext';
import { PickerMode } from './interface';
import { CustomFormat, PickerMode } from './interface';
import { getDefaultFormat, getInputSize, elementsContains } from './utils/uiUtil';
import usePickerInput from './hooks/usePickerInput';
import useTextValueMapping from './hooks/useTextValueMapping';
Expand Down Expand Up @@ -54,7 +54,7 @@ export interface PickerSharedProps<DateType> extends React.AriaAttributes {
id?: string;

// Value
format?: string | string[];
format?: string | CustomFormat<DateType> | Array<string | CustomFormat<DateType>>;

// Render
suffixIcon?: React.ReactNode;
Expand Down Expand Up @@ -225,7 +225,7 @@ function InnerPicker<DateType>(props: PickerProps<DateType>) {
const [text, triggerTextChange, resetText] = useTextValueMapping({
valueTexts,
onTextChange: newText => {
const inputDate = generateConfig.locale.parse(locale.locale, newText, formatList);
const inputDate = generateConfig.locale.parse(locale.locale, newText, formatList as string[]);
if (inputDate && (!disabledDate || !disabledDate(inputDate))) {
setSelectedValue(inputDate);
}
Expand All @@ -240,7 +240,7 @@ function InnerPicker<DateType>(props: PickerProps<DateType>) {
if (onChange && !isEqual(generateConfig, mergedValue, newValue)) {
onChange(
newValue,
newValue ? generateConfig.locale.format(locale.locale, newValue, formatList[0]) : '',
newValue ? formatValue(newValue, { generateConfig, locale, format: formatList[0] }) : '',
);
}
};
Expand Down Expand Up @@ -491,7 +491,7 @@ function InnerPicker<DateType>(props: PickerProps<DateType>) {
id={id}
tabIndex={tabIndex}
disabled={disabled}
readOnly={inputReadOnly || !typing}
readOnly={inputReadOnly || typeof formatList[0] === 'function' || !typing}
value={hoverValue || text}
onChange={e => {
triggerTextChange(e.target.value);
Expand All @@ -501,7 +501,7 @@ function InnerPicker<DateType>(props: PickerProps<DateType>) {
ref={inputRef}
title={text}
{...inputProps}
size={getInputSize(picker, formatList[0])}
size={getInputSize(picker, formatList[0], generateConfig)}
{...getDataOrAriaProps(props)}
autoComplete={autoComplete}
/>
Expand Down
15 changes: 8 additions & 7 deletions src/RangePicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
isSameDate,
isSameWeek,
isSameQuarter,
formatValue,
} from './utils/dateUtil';
import useValueTexts from './hooks/useValueTexts';
import useTextValueMapping from './hooks/useTextValueMapping';
Expand Down Expand Up @@ -229,7 +230,7 @@ function InnerRangePicker<DateType>(props: RangePickerProps<DateType>) {
const endInputRef = useRef<HTMLInputElement>(null);

// ============================= Misc ==============================
const formatList = toArray(getDefaultFormat(format, picker, showTime, use12Hours));
const formatList = toArray(getDefaultFormat<DateType>(format, picker, showTime, use12Hours));

// Active picker
const [mergedActivePickerIndex, setMergedActivePickerIndex] = useMergedState<0 | 1>(0, {
Expand Down Expand Up @@ -425,11 +426,11 @@ function InnerRangePicker<DateType>(props: RangePickerProps<DateType>) {

const startStr =
values && values[0]
? generateConfig.locale.format(locale.locale, values[0], formatList[0])
? formatValue(values[0], { generateConfig, locale, format: formatList[0] })
: '';
const endStr =
values && values[1]
? generateConfig.locale.format(locale.locale, values[1], formatList[0])
? formatValue(values[1], { generateConfig, locale, format: formatList[0] })
: '';

if (onCalendarChange) {
Expand Down Expand Up @@ -515,7 +516,7 @@ function InnerRangePicker<DateType>(props: RangePickerProps<DateType>) {
);

const onTextChange = (newText: string, index: 0 | 1) => {
const inputDate = generateConfig.locale.parse(locale.locale, newText, formatList);
const inputDate = generateConfig.locale.parse(locale.locale, newText, formatList as string[]);

const disabledFunc = index === 0 ? disabledStartDate : disabledEndDate;

Expand Down Expand Up @@ -989,7 +990,7 @@ function InnerRangePicker<DateType>(props: RangePickerProps<DateType>) {
}

const inputSharedProps = {
size: getInputSize(picker, formatList[0]),
size: getInputSize(picker, formatList[0], generateConfig),
};

let activeBarLeft: number = 0;
Expand Down Expand Up @@ -1068,7 +1069,7 @@ function InnerRangePicker<DateType>(props: RangePickerProps<DateType>) {
<input
id={id}
disabled={mergedDisabled[0]}
readOnly={inputReadOnly || !startTyping}
readOnly={inputReadOnly || typeof formatList[0] === 'function' || !startTyping}
value={startHoverValue || startText}
onChange={e => {
triggerStartTextChange(e.target.value);
Expand All @@ -1093,7 +1094,7 @@ function InnerRangePicker<DateType>(props: RangePickerProps<DateType>) {
>
<input
disabled={mergedDisabled[1]}
readOnly={inputReadOnly || !endTyping}
readOnly={inputReadOnly || typeof formatList[0] === 'function' || !endTyping}
value={endHoverValue || endText}
onChange={e => {
triggerEndTextChange(e.target.value);
Expand Down
7 changes: 4 additions & 3 deletions src/hooks/useValueTexts.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import shallowEqual from 'shallowequal';
import useMemo from 'rc-util/lib/hooks/useMemo';
import { GenerateConfig } from '../generate';
import { Locale } from '../interface';
import { CustomFormat, Locale } from '../interface';
import { formatValue } from '../utils/dateUtil';

export interface ValueTextConfig<DateType> {
formatList: string[];
formatList: Array<string | CustomFormat<DateType>>;
generateConfig: GenerateConfig<DateType>;
locale: Locale;
}
Expand All @@ -25,7 +26,7 @@ export default function useValueTexts<DateType>(

for (let i = 0; i < formatList.length; i += 1) {
const format = formatList[i];
const formatStr = generateConfig.locale.format(locale.locale, value, format);
const formatStr = formatValue(value, { generateConfig, locale, format });
fullValueTexts.push(formatStr);

if (i === 0) {
Expand Down
2 changes: 2 additions & 0 deletions src/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,3 +105,5 @@ export type RangeList = {
onMouseEnter: () => void;
onMouseLeave: () => void;
}[];

export type CustomFormat<DateType> = (value: DateType) => string;
19 changes: 18 additions & 1 deletion src/utils/dateUtil.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { GenerateConfig } from '../generate';
import { NullableDateType, PickerMode } from '../interface';
import { NullableDateType, PickerMode, Locale, CustomFormat } from '../interface';

export const WEEK_DAY_COUNT = 7;

Expand Down Expand Up @@ -192,3 +192,20 @@ export function getClosingViewDate<DateType>(
return generateConfig.addMonth(viewDate, offset);
}
}

export function formatValue<DateType>(
value: DateType,
{
generateConfig,
locale,
format,
}: {
generateConfig: GenerateConfig<DateType>;
locale: Locale;
format: string | CustomFormat<DateType>;
},
) {
return typeof format === 'function'
? format(value)
: generateConfig.locale.format(locale.locale, value, format);
}
17 changes: 12 additions & 5 deletions src/utils/uiUtil.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import KeyCode from 'rc-util/lib/KeyCode';
import { PanelMode, PickerMode } from '../interface';
import { GenerateConfig } from '../generate';
import { CustomFormat, PanelMode, PickerMode } from '../interface';

const scrollIds = new Map<HTMLElement, number>();

Expand Down Expand Up @@ -120,8 +121,8 @@ export function createKeyDownHandler(
}

// ===================== Format =====================
export function getDefaultFormat(
format: string | string[] | undefined,
export function getDefaultFormat<DateType>(
format: string | CustomFormat<DateType> | Array<string | CustomFormat<DateType>> | undefined,
picker: PickerMode | undefined,
showTime: boolean | object | undefined,
use12Hours: boolean | undefined,
Expand Down Expand Up @@ -157,9 +158,15 @@ export function getDefaultFormat(
return mergedFormat;
}

export function getInputSize(picker: PickerMode | undefined, format: string) {
export function getInputSize<DateType>(
picker: PickerMode | undefined,
format: string | CustomFormat<DateType>,
generateConfig: GenerateConfig<DateType>,
) {
const defaultSize = picker === 'time' ? 8 : 10;
return Math.max(defaultSize, format.length) + 2;
const length =
typeof format === 'function' ? format(generateConfig.getNow()).length : format.length;
return Math.max(defaultSize, length) + 2;
}

// ===================== Window =====================
Expand Down
15 changes: 15 additions & 0 deletions tests/picker.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { act } from 'react-dom/test-utils';
import { spyElementPrototypes } from 'rc-util/lib/test/domHook';
import KeyCode from 'rc-util/lib/KeyCode';
import { resetWarned } from 'rc-util/lib/warning';
import { Moment } from 'moment';
import { PanelMode, PickerMode } from '../src/interface';
import { mount, getMoment, isSame, MomentPicker } from './util/commonUtil';

Expand Down Expand Up @@ -725,6 +726,20 @@ describe('Picker.Basic', () => {
expect(wrapper.find('input').prop('value')).toEqual('20000101');
});

it('custom format', () => {
const wrapper = mount(
<MomentPicker
defaultValue={getMoment('2020-09-17')}
format={[(val: Moment) => `custom format:${val.format('YYYYMMDD')}`, 'YYYY-MM-DD']}
/>,
);
expect(wrapper.find('input').prop('readOnly')).toBeTruthy();
wrapper.openPicker();
wrapper.selectCell(24);
wrapper.closePicker();
expect(wrapper.find('input').prop('value')).toEqual('custom format:20200924');
});

it('panelRender', () => {
const wrapper = mount(<MomentPicker open panelRender={() => <h1>Light</h1>} />);
expect(wrapper.render()).toMatchSnapshot();
Expand Down
45 changes: 45 additions & 0 deletions tests/range.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1253,6 +1253,51 @@ describe('Picker.Range', () => {
).toEqual('19901128');
});

it('custom format', () => {
const wrapper = mount(
<MomentRangePicker
format={[(val: Moment) => `custom format:${val.format('YYYYMMDD')}`, 'YYYY-MM-DD']}
defaultValue={[getMoment('2020-09-17'), getMoment('2020-10-17')]}
/>,
);

expect(
wrapper
.find('input')
.first()
.prop('readOnly'),
).toBeTruthy();
expect(
wrapper
.find('input')
.last()
.prop('readOnly'),
).toBeTruthy();

// Start date
wrapper.openPicker();
wrapper.selectCell(24);
wrapper.closePicker();

// end date
wrapper.openPicker(1);
wrapper.selectCell(24, 1);
wrapper.closePicker(1);

expect(
wrapper
.find('input')
.first()
.prop('value'),
).toEqual('custom format:20200924');
expect(
wrapper
.find('input')
.last()
.prop('value'),
).toEqual('custom format:20201024');
});

describe('auto open', () => {
it('empty: start -> end -> close', () => {
const wrapper = mount(<MomentRangePicker />);
Expand Down

1 comment on commit f547043

@vercel
Copy link

@vercel vercel bot commented on f547043 Sep 18, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.