From 66fc26f8dd22104d4accf6adb3998707b421c15d Mon Sep 17 00:00:00 2001 From: SamKirkland Date: Thu, 11 Jun 2020 14:15:56 -0500 Subject: [PATCH] Adding format array support --- README.md | 2 +- examples/format.js | 8 +++++ index.d.ts | 2 +- src/Combobox.jsx | 2 +- src/Header.jsx | 30 +++++++++++++++--- src/TimePicker.jsx | 22 +++++++++---- tests/Select.spec.jsx | 72 +++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 124 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 1098b6ad..5760dee8 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,7 @@ API | showHour | Boolean | true | whether show hour | | | showMinute | Boolean | true | whether show minute | | showSecond | Boolean | true | whether show second | -| format | String | - | moment format | +| format | String \| String[] | - | moment format. When an array is provided, all values are used for parsing and first value for display | | disabledHours | Function | - | disabled hour options | | disabledMinutes | Function | - | disabled minute options | | disabledSeconds | Function | - | disabled second options | diff --git a/examples/format.js b/examples/format.js index 6b6d1126..3cb99f5e 100644 --- a/examples/format.js +++ b/examples/format.js @@ -12,6 +12,14 @@ const App = () => ( + + + ); diff --git a/index.d.ts b/index.d.ts index 101a6eec..cd31035f 100644 --- a/index.d.ts +++ b/index.d.ts @@ -20,7 +20,7 @@ declare module "rc-time-picker" { showHour?: boolean; showMinute?: boolean; showSecond?: boolean; - format?: string; + format?: string | string[]; disabledHours?: () => number[]; disabledMinutes?: (hour: number) => number[]; disabledSeconds?: (hour: number, minute: number) => number[]; diff --git a/src/Combobox.jsx b/src/Combobox.jsx index 77dc4119..964d9e4e 100644 --- a/src/Combobox.jsx +++ b/src/Combobox.jsx @@ -161,7 +161,7 @@ class Combobox extends Component { } const AMPMOptions = ['am', 'pm'] // If format has A char, then we should uppercase AM/PM - .map(c => (format.match(/\sA/) ? c.toUpperCase() : c)) + .map(c => (format[0].match(/\sA/) ? c.toUpperCase() : c)) .map(c => ({ value: c })); const selected = isAM ? 0 : 1; diff --git a/src/Header.jsx b/src/Header.jsx index 4932382b..abcc3ff8 100644 --- a/src/Header.jsx +++ b/src/Header.jsx @@ -11,7 +11,7 @@ class Header extends Component { super(props); const { value, format } = props; this.state = { - str: (value && value.format(format)) || '', + str: (value && value.format(format[0])) || '', invalid: false, }; } @@ -35,7 +35,7 @@ class Header extends Component { if (value !== prevProps.value) { // eslint-disable-next-line react/no-did-update-set-state this.setState({ - str: (value && value.format(format)) || '', + str: (value && value.format(format[0])) || '', invalid: false, }); } @@ -53,7 +53,6 @@ class Header extends Component { str, }); const { - format, hourOptions, minuteOptions, secondOptions, @@ -66,13 +65,16 @@ class Header extends Component { if (str) { const { value: originalValue } = this.props; const value = this.getProtoValue().clone(); - const parsed = moment(str, format, true); - if (!parsed.isValid()) { + + const parsed = this.parseValue(str); + + if (parsed === null) { this.setState({ invalid: true, }); return; } + value .hour(parsed.hour()) .minute(parsed.minute()) @@ -163,6 +165,24 @@ class Header extends Component { ); } + parseValue = str => { + const { format } = this.props; + + let parsedDate = null; + format.find(singleFormat => { + const date = moment(str, singleFormat, true); + + if (date.isValid()) { + parsedDate = date; + return true; + } + + return false; + }); + + return parsedDate; + }; + render() { const { prefixCls } = this.props; return
{this.getInput()}
; diff --git a/src/TimePicker.jsx b/src/TimePicker.jsx index 3cb29ed9..179be29f 100644 --- a/src/TimePicker.jsx +++ b/src/TimePicker.jsx @@ -11,6 +11,14 @@ function refFn(field, component) { this[field] = component; } +function toArray(val) { + if (val === null || val === undefined) { + return []; + } + + return Array.isArray(val) ? val : [val]; +} + class Picker extends Component { static defaultProps = { clearText: 'clear', @@ -115,7 +123,7 @@ class Picker extends Component { getFormat() { const { format, showHour, showMinute, showSecond, use12Hours } = this.props; if (format) { - return format; + return toArray(format); } if (use12Hours) { @@ -123,12 +131,14 @@ class Picker extends Component { .filter(item => !!item) .join(':'); - return fmtString.concat(' a'); + return toArray(fmtString.concat(' a')); } - return [showHour ? 'HH' : '', showMinute ? 'mm' : '', showSecond ? 'ss' : ''] - .filter(item => !!item) - .join(':'); + return toArray( + [showHour ? 'HH' : '', showMinute ? 'mm' : '', showSecond ? 'ss' : ''] + .filter(item => !!item) + .join(':'), + ); } getPanelElement() { @@ -314,7 +324,7 @@ class Picker extends Component { name={name} onKeyDown={this.onKeyDown} disabled={disabled} - value={(value && value.format(this.getFormat())) || ''} + value={(value && value.format(this.getFormat()[0])) || ''} autoComplete={autoComplete} onFocus={onFocus} onBlur={onBlur} diff --git a/tests/Select.spec.jsx b/tests/Select.spec.jsx index eb25614c..6c790d0f 100644 --- a/tests/Select.spec.jsx +++ b/tests/Select.spec.jsx @@ -199,6 +199,32 @@ describe('Select', () => { expect(picker.state().open).toBeTruthy(); }); + it('ampm correctly format value', async () => { + const onAmPmChange = jest.fn(); + const picker = renderPicker({ + onAmPmChange, + defaultValue: moment() + .hour(0) + .minute(0) + .second(0), + format: 'hh:mm a', + showSecond: false, + use12Hours: true, + }); + expect(picker.state().open).toBeFalsy(); + clickInput(picker); + + expect(picker.state().open).toBeTruthy(); + + matchValue(picker, '12:00 am'); + clickSelectItem(picker, 2, 1); + + expect(onAmPmChange).toBeCalled(); + expect(onAmPmChange.mock.calls[0][0]).toBe('PM'); + matchValue(picker, '12:00 pm'); + expect(picker.state().open).toBeTruthy(); + }); + it('disabled correctly', async () => { const onChange = jest.fn(); const picker = renderPicker({ @@ -269,6 +295,52 @@ describe('Select', () => { }); }); + describe('multi format parsing mode', () => { + it('renders correctly', async () => { + const picker = renderPicker({ + use12Hours: true, + defaultValue: moment() + .hour(14) + .minute(0) + .second(0), + format: ['h:mm a', 'h-mm a'], + showSecond: false, + }); + + expect(picker.state().open).toBeFalsy(); + clickInput(picker); + expect(picker.state().open).toBeTruthy(); + + matchValue(picker, '2:00 pm'); + + expect(picker.find('.rc-time-picker-panel-select').length).toBe(3); + }); + + it('renders 12am/pm correctly', async () => { + const picker = renderPicker({ + use12Hours: true, + defaultValue: moment() + .hour(0) + .minute(0) + .second(0), + showSecond: false, + format: ['hh:mm a', 'hh-mm a'], + }); + + expect(picker.state().open).toBeFalsy(); + clickInput(picker); + expect(picker.state().open).toBeTruthy(); + + matchValue(picker, '12:00 am'); + + clickSelectItem(picker, 2, 1); + matchValue(picker, '12:00 pm'); + + clickSelectItem(picker, 2, 0); + matchValue(picker, '12:00 am'); + }); + }); + describe('select in 12 hours mode', () => { it('renders correctly', async () => { const picker = renderPicker({