diff --git a/.vscode/settings.json b/.vscode/settings.json index 80f50b4ff..c12c908f1 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,13 @@ { - "cSpell.words": ["Formik", "Notistack", "Sourcable", "Unparsed", "ampm", "patreon"], + "cSpell.words": [ + "Formik", + "Notistack", + "Sourcable", + "Unparsed", + "ampm", + "datetimepicker", + "patreon" + ], "eslint.validate": ["javascript", "javascriptreact", "typescript", "typescriptreact"], "typescript.tsdk": "node_modules/typescript/lib", "javascript.implicitProjectConfig.checkJs": true diff --git a/docs/pages/demo/datepicker/DatePickers.example.jsx b/docs/pages/demo/datepicker/DatePickers.example.jsx index b49da8912..a5db731bf 100644 --- a/docs/pages/demo/datepicker/DatePickers.example.jsx +++ b/docs/pages/demo/datepicker/DatePickers.example.jsx @@ -8,34 +8,35 @@ function DatePickersVariants(props) { return ( } clearable label="For mobile" - value={selectedDate} - onChange={date => handleDateChange(date)} inputFormat={props.__willBeReplacedGetFormatString({ moment: 'MM/DD/YYYY', dateFns: 'MM/dd/yyyy', })} + toolbarPlaceholder="Enter Date" + value={selectedDate} + onChange={date => handleDateChange(date)} + renderInput={props => } /> } minDate={new Date('2017-01-01')} value={selectedDate} onChange={date => handleDateChange(date)} + renderInput={props => } /> } label="Responsive" openTo="year" views={['year', 'month', 'date']} value={selectedDate} onChange={date => handleDateChange(date)} + renderInput={props => } /> ); diff --git a/docs/pages/demo/timepicker/InlineTimePicker.example.jsx b/docs/pages/demo/timepicker/TimePickers.example.jsx similarity index 100% rename from docs/pages/demo/timepicker/InlineTimePicker.example.jsx rename to docs/pages/demo/timepicker/TimePickers.example.jsx diff --git a/docs/pages/demo/timepicker/index.mdx b/docs/pages/demo/timepicker/index.mdx index d31172780..09b5fe242 100644 --- a/docs/pages/demo/timepicker/index.mdx +++ b/docs/pages/demo/timepicker/index.mdx @@ -6,7 +6,7 @@ import { Hidden } from '@material-ui/core'; import * as TimeValidation from './TimeValidation.example'; import * as BasicTimePicker from './BasicTimePicker.example'; -import * as InlineTimePicker from './InlineTimePicker.example'; +import * as TimePickers from './TimePickers.example'; import * as StaticTimePicker from './StaticTimePicker.example'; import * as SecondsTimePicker from './SecondsTimePicker.example'; @@ -27,7 +27,7 @@ A time picker should adjust to a user’s preferred time setting, i.e. the 12-ho #### Responsiveness - + #### Time Validation diff --git a/docs/prop-types.json b/docs/prop-types.json index 2b426f14c..31fd045ed 100644 --- a/docs/prop-types.json +++ b/docs/prop-types.json @@ -698,19 +698,6 @@ "name": "boolean" } }, - "defaultHighlight": { - "defaultValue": null, - "description": "Date that will be initially highlighted if null was passed", - "name": "defaultHighlight", - "parent": { - "fileName": "material-ui-pickers/lib/src/typings/BasePicker.tsx", - "name": "BasePickerProps" - }, - "required": false, - "type": { - "name": "any" - } - }, "onAccept": { "defaultValue": null, "description": "Callback fired when date is accepted", @@ -817,6 +804,21 @@ "name": "ReactNode" } }, + "toolbarPlaceholder": { + "defaultValue": { + "value": "\"–\"" + }, + "description": "Mobile picker date value placeholder, displaying if `value` === `null`", + "name": "toolbarPlaceholder", + "parent": { + "fileName": "material-ui-pickers/lib/src/typings/BasePicker.tsx", + "name": "BasePickerProps" + }, + "required": false, + "type": { + "name": "ReactNode" + } + }, "toolbarFormat": { "defaultValue": null, "description": "Date format, that is displaying in toolbar", @@ -1230,7 +1232,7 @@ }, "minTime": { "defaultValue": null, - "description": "Min time, date part by default, will be ignored", + "description": "Min time acceptable time. For input validation date part of passed object will be ignored if `disableTimeValidationIgnoreDatePart` not specified.", "name": "minTime", "parent": { "fileName": "material-ui-pickers/lib/src/_helpers/time-utils.ts", @@ -1243,7 +1245,7 @@ }, "maxTime": { "defaultValue": null, - "description": "Max time, date part by default, will be ignored", + "description": "Max time acceptable time. For input validation date part of passed object will be ignored if `disableTimeValidationIgnoreDatePart` not specified.", "name": "maxTime", "parent": { "fileName": "material-ui-pickers/lib/src/_helpers/time-utils.ts", @@ -1256,7 +1258,7 @@ }, "shouldDisableTime": { "defaultValue": null, - "description": "Dynamically check if time is disabled or not", + "description": "Dynamically check if time is disabled or not. If returns `false` appropriate time point will ot be acceptable.", "name": "shouldDisableTime", "parent": { "fileName": "material-ui-pickers/lib/src/_helpers/time-utils.ts", @@ -1268,7 +1270,9 @@ } }, "disableTimeValidationIgnoreDatePart": { - "defaultValue": null, + "defaultValue": { + "value": "false" + }, "description": "Do not ignore date part when validating min/max time", "name": "disableTimeValidationIgnoreDatePart", "parent": { @@ -1399,19 +1403,6 @@ "name": "boolean" } }, - "defaultHighlight": { - "defaultValue": null, - "description": "Date that will be initially highlighted if null was passed", - "name": "defaultHighlight", - "parent": { - "fileName": "material-ui-pickers/lib/src/typings/BasePicker.tsx", - "name": "BasePickerProps" - }, - "required": false, - "type": { - "name": "any" - } - }, "onAccept": { "defaultValue": null, "description": "Callback fired when date is accepted", @@ -1518,6 +1509,21 @@ "name": "ReactNode" } }, + "toolbarPlaceholder": { + "defaultValue": { + "value": "\"–\"" + }, + "description": "Mobile picker date value placeholder, displaying if `value` === `null`", + "name": "toolbarPlaceholder", + "parent": { + "fileName": "material-ui-pickers/lib/src/typings/BasePicker.tsx", + "name": "BasePickerProps" + }, + "required": false, + "type": { + "name": "ReactNode" + } + }, "toolbarFormat": { "defaultValue": null, "description": "Date format, that is displaying in toolbar", @@ -2048,7 +2054,7 @@ }, "minTime": { "defaultValue": null, - "description": "Min time, date part by default, will be ignored", + "description": "Min time acceptable time. For input validation date part of passed object will be ignored if `disableTimeValidationIgnoreDatePart` not specified.", "name": "minTime", "parent": { "fileName": "material-ui-pickers/lib/src/_helpers/time-utils.ts", @@ -2061,7 +2067,7 @@ }, "maxTime": { "defaultValue": null, - "description": "Max time, date part by default, will be ignored", + "description": "Max time acceptable time. For input validation date part of passed object will be ignored if `disableTimeValidationIgnoreDatePart` not specified.", "name": "maxTime", "parent": { "fileName": "material-ui-pickers/lib/src/_helpers/time-utils.ts", @@ -2074,7 +2080,7 @@ }, "shouldDisableTime": { "defaultValue": null, - "description": "Dynamically check if time is disabled or not", + "description": "Dynamically check if time is disabled or not. If returns `false` appropriate time point will ot be acceptable.", "name": "shouldDisableTime", "parent": { "fileName": "material-ui-pickers/lib/src/_helpers/time-utils.ts", @@ -2086,7 +2092,9 @@ } }, "disableTimeValidationIgnoreDatePart": { - "defaultValue": null, + "defaultValue": { + "value": "false" + }, "description": "Do not ignore date part when validating min/max time", "name": "disableTimeValidationIgnoreDatePart", "parent": { @@ -2450,19 +2458,6 @@ "name": "boolean" } }, - "defaultHighlight": { - "defaultValue": null, - "description": "Date that will be initially highlighted if null was passed", - "name": "defaultHighlight", - "parent": { - "fileName": "material-ui-pickers/lib/src/typings/BasePicker.tsx", - "name": "BasePickerProps" - }, - "required": false, - "type": { - "name": "any" - } - }, "onAccept": { "defaultValue": null, "description": "Callback fired when date is accepted", @@ -2569,6 +2564,21 @@ "name": "ReactNode" } }, + "toolbarPlaceholder": { + "defaultValue": { + "value": "\"–\"" + }, + "description": "Mobile picker date value placeholder, displaying if `value` === `null`", + "name": "toolbarPlaceholder", + "parent": { + "fileName": "material-ui-pickers/lib/src/typings/BasePicker.tsx", + "name": "BasePickerProps" + }, + "required": false, + "type": { + "name": "ReactNode" + } + }, "className": { "defaultValue": null, "description": "className applied to the root component", @@ -3308,19 +3318,6 @@ "name": "boolean" } }, - "defaultHighlight": { - "defaultValue": null, - "description": "Date that will be initially highlighted if null was passed", - "name": "defaultHighlight", - "parent": { - "fileName": "material-ui-pickers/lib/src/typings/BasePicker.tsx", - "name": "BasePickerProps" - }, - "required": false, - "type": { - "name": "any" - } - }, "onAccept": { "defaultValue": null, "description": "Callback fired when date is accepted", @@ -3427,6 +3424,21 @@ "name": "ReactNode" } }, + "toolbarPlaceholder": { + "defaultValue": { + "value": "\"–\"" + }, + "description": "Mobile picker date value placeholder, displaying if `value` === `null`", + "name": "toolbarPlaceholder", + "parent": { + "fileName": "material-ui-pickers/lib/src/typings/BasePicker.tsx", + "name": "BasePickerProps" + }, + "required": false, + "type": { + "name": "ReactNode" + } + }, "toolbarFormat": { "defaultValue": null, "description": "Date format, that is displaying in toolbar", @@ -4223,7 +4235,7 @@ }, "minTime": { "defaultValue": null, - "description": "Min time, date part by default, will be ignored", + "description": "Min time acceptable time. For input validation date part of passed object will be ignored if `disableTimeValidationIgnoreDatePart` not specified.", "name": "minTime", "parent": { "fileName": "material-ui-pickers/lib/src/_helpers/time-utils.ts", @@ -4236,7 +4248,7 @@ }, "maxTime": { "defaultValue": null, - "description": "Max time, date part by default, will be ignored", + "description": "Max time acceptable time. For input validation date part of passed object will be ignored if `disableTimeValidationIgnoreDatePart` not specified.", "name": "maxTime", "parent": { "fileName": "material-ui-pickers/lib/src/_helpers/time-utils.ts", @@ -4249,7 +4261,7 @@ }, "shouldDisableTime": { "defaultValue": null, - "description": "Dynamically check if time is disabled or not", + "description": "Dynamically check if time is disabled or not. If returns `false` appropriate time point will ot be acceptable.", "name": "shouldDisableTime", "parent": { "fileName": "material-ui-pickers/lib/src/_helpers/time-utils.ts", @@ -4261,7 +4273,9 @@ } }, "disableTimeValidationIgnoreDatePart": { - "defaultValue": null, + "defaultValue": { + "value": "false" + }, "description": "Do not ignore date part when validating min/max time", "name": "disableTimeValidationIgnoreDatePart", "parent": { diff --git a/lib/.size-snapshot.json b/lib/.size-snapshot.json index a48f2f19b..e797557ba 100644 --- a/lib/.size-snapshot.json +++ b/lib/.size-snapshot.json @@ -1,21 +1,26 @@ { "build/dist/material-ui-pickers.esm.js": { - "bundled": 190258, - "minified": 102356, - "gzipped": 26649, + "bundled": 191749, + "minified": 102786, + "gzipped": 26840, "treeshaked": { "rollup": { - "code": 83408, + "code": 83738, "import_statements": 2121 }, "webpack": { - "code": 92884 + "code": 93230 } } }, "build/dist/material-ui-pickers.umd.js": { - "bundled": 301162, - "minified": 117618, - "gzipped": 33567 + "bundled": 302846, + "minified": 118066, + "gzipped": 33767 + }, + "build/dist/material-ui-pickers.umd.min.js": { + "bundled": 260318, + "minified": 108307, + "gzipped": 30847 } } diff --git a/lib/src/DatePicker/DatePickerToolbar.tsx b/lib/src/DatePicker/DatePickerToolbar.tsx index b442f6bde..306f5f16c 100644 --- a/lib/src/DatePicker/DatePickerToolbar.tsx +++ b/lib/src/DatePicker/DatePickerToolbar.tsx @@ -28,12 +28,17 @@ export const DatePickerToolbar: React.FC = ({ isMobileKeyboardViewOpen, toggleMobileKeyboardView, toolbarFormat, + toolbarPlaceholder = '––', toolbarTitle = 'SELECT DATE', }) => { const utils = useUtils(); const classes = useStyles(); const dateText = React.useMemo(() => { + if (!date) { + return toolbarPlaceholder; + } + if (toolbarFormat) { return utils.formatByString(date, toolbarFormat); } @@ -52,7 +57,7 @@ export const DatePickerToolbar: React.FC = ({ return /en/.test(utils.getCurrentLocaleCode()) ? utils.format(date, 'normalDateWithWeekday') : utils.format(date, 'normalDate'); - }, [date, toolbarFormat, utils, views]); + }, [date, toolbarFormat, toolbarPlaceholder, utils, views]); return ( (Wrapper: TWrapper) } ); + const rangePickerValueManager: PickerStateValueManager = { + emptyValue: [null, null], + parseInput: parseRangeInputValue, + areValuesEqual: (utils, a, b) => utils.isEqual(a[0], b[0]) && utils.isEqual(a[1], b[1]), + }; + function RangePickerWithStateAndWrapper({ calendars, value, @@ -93,11 +99,7 @@ export function makeRangePicker(Wrapper: TWrapper) const { pickerProps, inputProps, wrapperProps } = usePickerState( pickerStateProps, - { - parseInput: parseRangeInputValue, - areValuesEqual: (a, b) => utils.isEqual(a[0], b[0]) && utils.isEqual(a[1], b[1]), - emptyValue: [null, null], - } + rangePickerValueManager ); const validationError = useDateRangeValidation(value, restProps); diff --git a/lib/src/DateTimePicker/DateTimePickerTabs.tsx b/lib/src/DateTimePicker/DateTimePickerTabs.tsx index 8aa46e8b5..95faa995c 100644 --- a/lib/src/DateTimePicker/DateTimePickerTabs.tsx +++ b/lib/src/DateTimePicker/DateTimePickerTabs.tsx @@ -47,7 +47,7 @@ export const useStyles = makeStyles( { name: 'MuiPickerDTTabs' } ); -export const DateTimePickerTabs: React.SFC = ({ +export const DateTimePickerTabs: React.FC = ({ view, onChange, dateRangeIcon, diff --git a/lib/src/DateTimePicker/DateTimePickerToolbar.tsx b/lib/src/DateTimePicker/DateTimePickerToolbar.tsx index 0631b3e00..08c7e22dd 100644 --- a/lib/src/DateTimePicker/DateTimePickerToolbar.tsx +++ b/lib/src/DateTimePicker/DateTimePickerToolbar.tsx @@ -6,6 +6,7 @@ import DateTimePickerTabs from './DateTimePickerTabs'; import { useUtils } from '../_shared/hooks/useUtils'; import { DateTimePickerView } from './DateTimePicker'; import { makeStyles } from '@material-ui/core/styles'; +import { MaterialUiPickersDate } from '../typings/date'; import { ToolbarComponentProps } from '../Picker/Picker'; export const useStyles = makeStyles( @@ -46,6 +47,7 @@ export const DateTimePickerToolbar: React.FC = ({ dateRangeIcon, timeIcon, toolbarFormat, + toolbarPlaceholder = '––', isMobileKeyboardViewOpen, toggleMobileKeyboardView, toolbarTitle = 'SELECT DATE & TIME', @@ -54,6 +56,21 @@ export const DateTimePickerToolbar: React.FC = ({ const classes = useStyles(); const showTabs = !hideTabs && typeof window !== 'undefined' && window.innerHeight > 667; + const formatHours = (time: MaterialUiPickersDate) => + ampm ? utils.format(time, 'hours12h') : utils.format(time, 'hours24h'); + + const dateText = React.useMemo(() => { + if (!date) { + return toolbarPlaceholder; + } + + if (toolbarFormat) { + return utils.formatByString(date, toolbarFormat); + } + + return utils.format(date, 'shortDate'); + }, [date, toolbarFormat, toolbarPlaceholder, utils]); + return ( <> = ({ variant="subtitle1" onClick={() => setOpenView('year')} selected={openView === 'year'} - value={utils.format(date, 'year')} + value={date ? utils.format(date, 'year') : '–'} /> = ({ data-mui-test="datetimepicker-toolbar-date" onClick={() => setOpenView('date')} selected={openView === 'date'} - value={ - toolbarFormat - ? utils.formatByString(date, toolbarFormat) - : utils.format(date, 'shortDate') - } + value={dateText} /> @@ -91,9 +104,10 @@ export const DateTimePickerToolbar: React.FC = ({ setOpenView('hours')} selected={openView === 'hours'} - value={ampm ? utils.format(date, 'hours12h') : utils.format(date, 'hours24h')} + value={date ? formatHours(date) : '--'} typographyClassName={classes.timeTypography} /> @@ -102,9 +116,10 @@ export const DateTimePickerToolbar: React.FC = ({ setOpenView('minutes')} selected={openView === 'minutes'} - value={utils.format(date, 'minutes')} + value={date ? utils.format(date, 'minutes') : '--'} typographyClassName={classes.timeTypography} /> diff --git a/lib/src/Picker/Picker.tsx b/lib/src/Picker/Picker.tsx index 92a59683d..405ccae63 100644 --- a/lib/src/Picker/Picker.tsx +++ b/lib/src/Picker/Picker.tsx @@ -28,6 +28,7 @@ export type ToolbarComponentProps< setOpenView: (view: TView) => void; onChange: (date: TDate, isFinish?: boolean) => void; toolbarTitle?: React.ReactNode; + toolbarPlaceholder?: React.ReactNode; toolbarFormat?: string; // TODO move out, cause it is DateTimePickerOnly hideTabs?: boolean; @@ -99,6 +100,7 @@ export function Picker({ toggleMobileKeyboardView, toolbarFormat, className, + toolbarPlaceholder, ...other }: PickerProps) { const classes = useStyles(); @@ -139,6 +141,7 @@ export function Picker({ openView={openView} toolbarTitle={toolbarTitle} toolbarFormat={toolbarFormat} + toolbarPlaceholder={toolbarPlaceholder} isMobileKeyboardViewOpen={isMobileKeyboardViewOpen} toggleMobileKeyboardView={toggleMobileKeyboardView} /> diff --git a/lib/src/Picker/makePickerWithState.tsx b/lib/src/Picker/makePickerWithState.tsx index 6adf4d9b2..f22c0206b 100644 --- a/lib/src/Picker/makePickerWithState.tsx +++ b/lib/src/Picker/makePickerWithState.tsx @@ -1,16 +1,16 @@ import * as React from 'react'; -import { useUtils } from '../_shared/hooks/useUtils'; import { ParsableDate } from '../constants/prop-types'; import { MaterialUiPickersDate } from '../typings/date'; +import { MuiPickersAdapter } from '../_shared/hooks/useUtils'; import { parsePickerInputValue } from '../_helpers/date-utils'; import { KeyboardDateInput } from '../_shared/KeyboardDateInput'; -import { usePickerState } from '../_shared/hooks/usePickerState'; import { ResponsiveWrapper } from '../wrappers/ResponsiveWrapper'; import { withDateAdapterProp } from '../_shared/withDateAdapterProp'; import { makeWrapperComponent } from '../wrappers/makeWrapperComponent'; import { PureDateInput, DateInputProps } from '../_shared/PureDateInput'; import { AnyPickerView, AllSharedPickerProps } from './SharedPickerProps'; import { SomeWrapper, ExtendWrapper, WrapperProps } from '../wrappers/Wrapper'; +import { usePickerState, PickerStateValueManager } from '../_shared/hooks/usePickerState'; import { Picker, ToolbarComponentProps, ExportedPickerProps, PickerProps } from './Picker'; type AllAvailableForOverrideProps = ExportedPickerProps; @@ -27,6 +27,13 @@ export interface MakePickerOptions { DefaultToolbarComponent: React.ComponentType; } +const valueManager: PickerStateValueManager = { + emptyValue: null, + parseInput: parsePickerInputValue, + areValuesEqual: (utils: MuiPickersAdapter, a: MaterialUiPickersDate, b: MaterialUiPickersDate) => + utils.isEqual(a, b), +}; + export function makePickerWithStateAndWrapper< T extends AllAvailableForOverrideProps, TWrapper extends SomeWrapper = typeof ResponsiveWrapper @@ -43,18 +50,13 @@ export function makePickerWithStateAndWrapper< ); function PickerWithState(__props: T & AllSharedPickerProps & ExtendWrapper) { - const utils = useUtils(); const allProps = useInterceptProps(__props) as AllPickerProps; const validationError = useValidation(allProps.value, allProps) !== null; const { pickerProps, inputProps, wrapperProps } = usePickerState< ParsableDate, MaterialUiPickersDate - >(allProps, { - emptyValue: null, - parseInput: parsePickerInputValue, - areValuesEqual: (a, b) => utils.isEqual(a, b), - }); + >(allProps, valueManager); // Note that we are passing down all the value without spread. // It saves us >1kb gzip and make any prop available automatically on any level down. diff --git a/lib/src/TimePicker/TimePickerToolbar.tsx b/lib/src/TimePicker/TimePickerToolbar.tsx index 9162ee0c3..44e37cdad 100644 --- a/lib/src/TimePicker/TimePickerToolbar.tsx +++ b/lib/src/TimePicker/TimePickerToolbar.tsx @@ -87,9 +87,12 @@ export const TimePickerToolbar: React.FC = ({ const utils = useUtils(); const theme = useTheme(); const classes = useStyles(); - const showAmPmControl = ampm && !ampmInClock; + const showAmPmControl = Boolean(ampm && !ampmInClock); const { meridiemMode, handleMeridiemChange } = useMeridiemMode(date, ampm, onChange); + const formatHours = (time: MaterialUiPickersDate) => + ampm ? utils.format(time, 'hours12h') : utils.format(time, 'hours24h'); + const separator = ( = ({ variant={clockTypographyVariant} onClick={() => setOpenView('hours')} selected={openView === 'hours'} - value={ampm ? utils.format(date, 'hours12h') : utils.format(date, 'hours24h')} + value={date ? formatHours(date) : '--'} /> )} @@ -135,7 +138,7 @@ export const TimePickerToolbar: React.FC = ({ variant={clockTypographyVariant} onClick={() => setOpenView('minutes')} selected={openView === 'minutes'} - value={utils.format(date, 'minutes')} + value={date ? utils.format(date, 'minutes') : '--'} /> )} @@ -147,7 +150,7 @@ export const TimePickerToolbar: React.FC = ({ variant={clockTypographyVariant} onClick={() => setOpenView('seconds')} selected={openView === 'seconds'} - value={utils.format(date, 'seconds')} + value={date ? utils.format(date, 'seconds') : '--'} /> )} diff --git a/lib/src/__tests__/DatePicker.test.tsx b/lib/src/__tests__/DatePicker.test.tsx index 3d6d485aa..05c33067e 100644 --- a/lib/src/__tests__/DatePicker.test.tsx +++ b/lib/src/__tests__/DatePicker.test.tsx @@ -2,12 +2,14 @@ import * as React from 'react'; import { ReactWrapper } from 'enzyme'; import { Picker } from '../Picker/Picker'; import { TextField } from '@material-ui/core'; -import { mount, utilsToUse } from './test-utils'; +import { MaterialUiPickersDate } from '../typings/date'; +import { mount, utilsToUse, mountPickerWithState } from './test-utils'; import { DatePicker, MobileDatePicker, DesktopDatePicker, DatePickerProps, + StaticDatePicker, } from '../DatePicker/DatePicker'; describe('e2e - DatePicker default year format', () => { @@ -77,7 +79,7 @@ describe('e2e - DatePicker default year month day format', () => { }); }); -describe.only('e2e - DatePicker inline variant', () => { +describe('e2e - DatePicker inline variant', () => { let component: ReactWrapper; const onChangeMock = jest.fn(); const onCloseMock = jest.fn(); @@ -259,3 +261,20 @@ test('Selected date is disabled', () => { .text() ).toBe('January'); }); + +test('Should not add to loading queue when synchronous', () => { + const component = mountPickerWithState(null as MaterialUiPickersDate, props => ( + + )); + + expect(component.find('h4[data-mui-test="datepicker-toolbar-date"]').text()).toBe('Enter Date'); + + component + .find('button[data-mui-test="day"]') + .at(0) + .simulate('click'); + + expect(component.find('h4[data-mui-test="datepicker-toolbar-date"]').text()).not.toBe( + 'Enter Date' + ); +}); diff --git a/lib/src/__tests__/DateTimePicker.test.tsx b/lib/src/__tests__/DateTimePicker.test.tsx index 9f898e0f0..46b7dbbe6 100644 --- a/lib/src/__tests__/DateTimePicker.test.tsx +++ b/lib/src/__tests__/DateTimePicker.test.tsx @@ -1,9 +1,10 @@ import * as React from 'react'; import { ReactWrapper } from 'enzyme'; import { TextField } from '@material-ui/core'; -import { mount, utilsToUse } from './test-utils'; import { mount as enzymeDefaultMount } from 'enzyme'; +import { MaterialUiPickersDate } from '../typings/date'; import { ThemeProvider, createMuiTheme } from '@material-ui/core'; +import { mount, utilsToUse, mountPickerWithState } from './test-utils'; import { DateTimePicker, DateTimePickerProps } from '../DateTimePicker/DateTimePicker'; const format = process.env.UTILS === 'moment' ? 'MM/DD/YYYY HH:mm' : 'MM/dd/yyyy hh:mm'; @@ -103,3 +104,28 @@ describe('e2e -- Override utils using `dateAdapter`', () => { expect(component.find('[data-mui-test="datetimepicker-toolbar-date"] h4').text()).toBe('Jan 1'); }); }); + +test('e2e - DateTimePicker empty date', () => { + const component = mountPickerWithState(null as MaterialUiPickersDate, props => ( + + )); + + expect(component.find('button[data-mui-test="datetimepicker-toolbar-date"]').text()).toBe( + 'Enter Date' + ); + + expect(component.find('button[data-mui-test="hours"]').text()).toBe('--'); + expect(component.find('button[data-mui-test="minutes"]').text()).toBe('--'); + + component + .find('button[data-mui-test="day"]') + .at(0) + .simulate('click'); + + expect(component.find('button[data-mui-test="datetimepicker-toolbar-date"]').text()).not.toBe( + 'Enter Date' + ); + + expect(component.find('button[data-mui-test="hours"]').text()).not.toBe('--'); + expect(component.find('button[data-mui-test="minutes"]').text()).not.toBe('--'); +}); diff --git a/lib/src/__tests__/TimePicker.test.tsx b/lib/src/__tests__/TimePicker.test.tsx index 5106f866d..e1ef78130 100644 --- a/lib/src/__tests__/TimePicker.test.tsx +++ b/lib/src/__tests__/TimePicker.test.tsx @@ -2,7 +2,13 @@ import * as React from 'react'; import { ReactWrapper } from 'enzyme'; import { clickOKButton } from './commands'; import { TextField } from '@material-ui/core'; -import { mount, utilsToUse, toHaveBeenCalledExceptMoment } from './test-utils'; +import { MaterialUiPickersDate } from '../typings/date'; +import { + mount, + utilsToUse, + toHaveBeenCalledExceptMoment, + mountPickerWithState, +} from './test-utils'; import { MobileTimePicker, DesktopTimePicker, @@ -40,8 +46,8 @@ describe('e2e - TimePicker', () => { }); it('Should submit onChange on moving', () => { - component.find('Clock div[role="menu"]').simulate('mouseMove', fakeTouchEvent); - component.find('Clock div[role="menu"]').simulate('mouseUp', fakeTouchEvent); + component.find('div[role="menu"]').simulate('mouseMove', fakeTouchEvent); + component.find('div[role="menu"]').simulate('mouseUp', fakeTouchEvent); expect( component @@ -57,7 +63,7 @@ describe('e2e - TimePicker', () => { .at(1) .simulate('click'); - component.find('Clock div[role="menu"]').simulate('touchMove', { + component.find('div[role="menu"]').simulate('touchMove', { buttons: 1, changedTouches: [ { @@ -118,7 +124,7 @@ describe('e2e - TimePicker with seconds', () => { .at(2) .simulate('click'); - component.find('Clock div[role="menu"]').simulate('touchMove', { + component.find('div[role="menu"]').simulate('touchMove', { buttons: 1, changedTouches: [ { @@ -251,3 +257,25 @@ describe('e2e - TimePicker time validation', () => { expect(component.find('button[data-mui-test="seconds"] h3').text()).toBe('10'); }); }); + +test('e2e - TimePicker empty date', () => { + const component = mountPickerWithState(null as MaterialUiPickersDate, props => ( + + )); + + expect(component.find('button[data-mui-test="hours"]').text()).toBe('--'); + expect(component.find('button[data-mui-test="minutes"]').text()).toBe('--'); + + component.find('div[role="menu"]').simulate('touchMove', { + buttons: 1, + changedTouches: [ + { + clientX: 20, + clientY: 15, + }, + ], + }); + + expect(component.find('button[data-mui-test="hours"]').text()).not.toBe('--'); + expect(component.find('button[data-mui-test="minutes"]').text()).not.toBe('--'); +}); diff --git a/lib/src/_helpers/date-utils.ts b/lib/src/_helpers/date-utils.ts index 709a79a49..06dac481d 100644 --- a/lib/src/_helpers/date-utils.ts +++ b/lib/src/_helpers/date-utils.ts @@ -100,17 +100,15 @@ export const getFormatByViews = ( }; export function parsePickerInputValue( - now: MaterialUiPickersDate, utils: MuiPickersAdapter, - { value, defaultHighlight }: Pick + { value }: BasePickerProps ): MaterialUiPickersDate | null { - const parsedValue = utils.date(value || defaultHighlight || now); + const parsedValue = utils.date(value); - return parsedValue && utils.isValid(parsedValue) ? parsedValue : now; + return utils.isValid(parsedValue) ? parsedValue : null; } export function parseRangeInputValue( - now: MaterialUiPickersDate, utils: MuiPickersAdapter, { value = [null, null] }: BasePickerProps ) { diff --git a/lib/src/_helpers/time-utils.ts b/lib/src/_helpers/time-utils.ts index f7ba70066..2da9f60a1 100644 --- a/lib/src/_helpers/time-utils.ts +++ b/lib/src/_helpers/time-utils.ts @@ -3,14 +3,20 @@ import { ParsableDate } from '../constants/prop-types'; import { MaterialUiPickersDate } from '../typings/date'; import { MuiPickersAdapter } from '../_shared/hooks/useUtils'; +type Meridiem = 'am' | 'pm' | null; + export const getMeridiem = ( date: MaterialUiPickersDate, utils: IUtils -): 'am' | 'pm' => { +): Meridiem => { + if (!date) { + return null; + } + return utils.getHours(date) >= 12 ? 'pm' : 'am'; }; -export const convertValueToMeridiem = (value: number, meridiem: 'am' | 'pm', ampm: boolean) => { +export const convertValueToMeridiem = (value: number, meridiem: Meridiem, ampm: boolean) => { if (ampm) { const currentMeridiem = value >= 12 ? 'pm' : 'am'; if (currentMeridiem !== meridiem) { @@ -103,13 +109,15 @@ export const createIsAfterIgnoreDatePart = ( }; export interface TimeValidationProps { - /** Min time, date part by default, will be ignored */ + /** Min time acceptable time. For input validation date part of passed object will be ignored if `disableTimeValidationIgnoreDatePart` not specified. */ minTime?: MaterialUiPickersDate; - /** Max time, date part by default, will be ignored */ + /** Max time acceptable time. For input validation date part of passed object will be ignored if `disableTimeValidationIgnoreDatePart` not specified. */ maxTime?: MaterialUiPickersDate; - /** Dynamically check if time is disabled or not */ + /** Dynamically check if time is disabled or not. If returns `false` appropriate time point will ot be acceptable. */ shouldDisableTime?: (timeValue: number, clockType: 'hours' | 'minutes' | 'seconds') => boolean; - /** Do not ignore date part when validating min/max time */ + /** Do not ignore date part when validating min/max time + * @default false + */ disableTimeValidationIgnoreDatePart?: boolean; } @@ -124,6 +132,10 @@ export const validateTime = ( utils ); + if (value === null) { + return null; + } + switch (true) { case !utils.isValid(value): return 'invalidDate'; diff --git a/lib/src/_shared/hooks/usePickerState.ts b/lib/src/_shared/hooks/usePickerState.ts index 162ae627a..2c9c2f067 100644 --- a/lib/src/_shared/hooks/usePickerState.ts +++ b/lib/src/_shared/hooks/usePickerState.ts @@ -1,23 +1,24 @@ +import * as React from 'react'; import { useOpenState } from './useOpenState'; import { WrapperVariant } from '../../wrappers/Wrapper'; import { BasePickerProps } from '../../typings/BasePicker'; -import { MaterialUiPickersDate } from '../../typings/date'; import { useUtils, useNow, MuiPickersAdapter } from './useUtils'; -import { useCallback, useDebugValue, useEffect, useMemo, useState } from 'react'; export const FORCE_FINISH_PICKER = Symbol('Force closing picker, useful for accessibility'); +export interface PickerStateValueManager { + parseInput: (utils: MuiPickersAdapter, props: BasePickerProps) => TDateValue; + emptyValue: TDateValue; + areValuesEqual: ( + utils: MuiPickersAdapter, + valueLeft: TDateValue, + valueRight: TDateValue + ) => boolean; +} + export function usePickerState( props: BasePickerProps, - valueManager: { - parseInput: ( - now: MaterialUiPickersDate, - utils: MuiPickersAdapter, - props: BasePickerProps - ) => TDateValue; - emptyValue: TDateValue; - areValuesEqual: (valueLeft: TDateValue, valueRight: TDateValue) => boolean; - } + valueManager: PickerStateValueManager ) { const { autoOk, inputFormat, disabled, readOnly, onAccept, onChange, value } = props; @@ -27,18 +28,18 @@ export function usePickerState( const now = useNow(); const utils = useUtils(); - const date = valueManager.parseInput(now, utils, props); - const [pickerDate, setPickerDate] = useState(date); + const { isOpen, setIsOpen } = useOpenState(props); + const [pickerDate, setPickerDate] = React.useState(valueManager.parseInput(utils, props)); // Mobile keyboard view is a special case. // When it's open picker should work like closed, cause we are just showing text field - const [isMobileKeyboardViewOpen, setMobileKeyboardViewOpen] = useState(false); - const { isOpen, setIsOpen } = useOpenState(props); + const [isMobileKeyboardViewOpen, setMobileKeyboardViewOpen] = React.useState(false); - useEffect(() => { + React.useEffect(() => { + const parsedDateValue = valueManager.parseInput(utils, props); setPickerDate(currentPickerDate => { - if (!valueManager.areValuesEqual(currentPickerDate, date)) { - return date; + if (!valueManager.areValuesEqual(utils, currentPickerDate, parsedDateValue)) { + return parsedDateValue; } return currentPickerDate; @@ -46,7 +47,7 @@ export function usePickerState( // We need to react only on value change, because `date` could potentially return new Date() on each render }, [value, utils]); // eslint-disable-line - const acceptDate = useCallback( + const acceptDate = React.useCallback( (acceptedDate: TDateValue, needClosePicker: boolean) => { onChange(acceptedDate); @@ -61,7 +62,7 @@ export function usePickerState( [onAccept, onChange, setIsOpen] ); - const wrapperProps = useMemo( + const wrapperProps = React.useMemo( () => ({ open: isOpen, onClear: () => acceptDate(valueManager.emptyValue, true), @@ -76,13 +77,13 @@ export function usePickerState( [acceptDate, autoOk, isOpen, now, pickerDate, setIsOpen, valueManager.emptyValue] ); - const pickerProps = useMemo( + const pickerProps = React.useMemo( () => ({ date: pickerDate, isMobileKeyboardViewOpen, toggleMobileKeyboardView: () => { if (!isMobileKeyboardViewOpen) { - // accept any partial input done by user + // accept any partial input done by React.user setPickerDate(pickerDate); } @@ -112,7 +113,7 @@ export function usePickerState( [acceptDate, autoOk, isMobileKeyboardViewOpen, pickerDate] ); - const inputProps = useMemo( + const inputProps = React.useMemo( () => ({ onChange, inputFormat, @@ -124,10 +125,9 @@ export function usePickerState( ); const pickerState = { pickerProps, inputProps, wrapperProps }; - useDebugValue(pickerState, () => ({ + React.useDebugValue(pickerState, () => ({ MuiPickerState: { pickerDate, - parsedDate: date, other: pickerState, }, })); diff --git a/lib/src/typings/BasePicker.tsx b/lib/src/typings/BasePicker.tsx index b32bc65f6..924eaadb6 100644 --- a/lib/src/typings/BasePicker.tsx +++ b/lib/src/typings/BasePicker.tsx @@ -21,11 +21,8 @@ export interface BasePickerProps< disabled?: boolean; /** Make picker read only */ readOnly?: boolean; - /** Date that will be initially highlighted if null was passed */ - defaultHighlight?: ParsableDate; /** Callback fired when date is accepted @DateIOType */ onAccept?: (date: TDateValue) => void; - /** On open callback */ onOpen?: () => void; /** On close callback */ @@ -45,6 +42,11 @@ export interface BasePickerProps< * @default "SELECT DATE" */ toolbarTitle?: React.ReactNode; + /** + * Mobile picker date value placeholder, displaying if `value` === `null` + * @default "–" + */ + toolbarPlaceholder?: React.ReactNode; /** Date format, that is displaying in toolbar */ toolbarFormat?: string; /** className applied to the root component */ diff --git a/lib/src/views/Calendar/Calendar.tsx b/lib/src/views/Calendar/Calendar.tsx index 1c98f0de2..d1c0b81a8 100644 --- a/lib/src/views/Calendar/Calendar.tsx +++ b/lib/src/views/Calendar/Calendar.tsx @@ -114,14 +114,14 @@ export const Calendar: React.FC = ({ const handleDaySelect = React.useCallback( (day: MaterialUiPickersDate, isFinish: boolean | symbol = true) => { - onChange(Array.isArray(date) ? day : utils.mergeDateAndTime(day, date), isFinish); + onChange(Array.isArray(date) ? day : utils.mergeDateAndTime(day, date || now), isFinish); }, - [date, onChange, utils] + [date, now, onChange, utils] ); const initialDate = Array.isArray(date) ? date[0] : date; - const nowFocusedDay = focusedDay || initialDate; + const nowFocusedDay = focusedDay || initialDate || now; useGlobalKeyDown(Boolean(allowKeyboardControl), { [keycode.ArrowUp]: () => changeFocusedDay(utils.addDays(nowFocusedDay, -7)), [keycode.ArrowDown]: () => changeFocusedDay(utils.addDays(nowFocusedDay, 7)), diff --git a/lib/src/views/Calendar/CalendarView.tsx b/lib/src/views/Calendar/CalendarView.tsx index 9a83a2fbe..5dc6ccf06 100644 --- a/lib/src/views/Calendar/CalendarView.tsx +++ b/lib/src/views/Calendar/CalendarView.tsx @@ -132,12 +132,6 @@ export const CalendarView: React.FC = ({ changeMonth(date); }, [date]); // eslint-disable-line - React.useEffect(() => { - if (view === 'date') { - changeFocusedDay(date); - } - }, [view]); // eslint-disable-line - return ( <> = ({ isDateDisabled={isDateDisabled} allowKeyboardControl={allowKeyboardControl} shouldDisableYear={shouldDisableYear} + changeFocusedDay={changeFocusedDay} /> )} diff --git a/lib/src/views/Calendar/YearSelection.tsx b/lib/src/views/Calendar/YearSelection.tsx index 8d3550a0c..bfbd18a85 100644 --- a/lib/src/views/Calendar/YearSelection.tsx +++ b/lib/src/views/Calendar/YearSelection.tsx @@ -22,6 +22,7 @@ export interface YearSelectionProps extends ExportedYearSelectionProps { disableFuture?: boolean | null | undefined; allowKeyboardControl?: boolean; isDateDisabled: (day: MaterialUiPickersDate) => boolean; + changeFocusedDay: (day: MaterialUiPickersDate) => void; } export const useStyles = makeStyles( @@ -47,6 +48,7 @@ export const YearSelection: React.FC = ({ disableFuture, isDateDisabled, shouldDisableYear, + changeFocusedDay, allowKeyboardControl, }) => { const now = useNow(); @@ -73,7 +75,7 @@ export const YearSelection: React.FC = ({ const handleYearSelection = React.useCallback( (year: number, isFinish = true) => { - const newDate = utils.setYear(date, year); + const newDate = utils.setYear(date || now, year); if (isDateDisabled(newDate)) { return; } @@ -83,8 +85,9 @@ export const YearSelection: React.FC = ({ } onChange(newDate, isFinish); + changeFocusedDay(newDate); }, - [date, isDateDisabled, onChange, onYearChange, utils] + [changeFocusedDay, date, isDateDisabled, now, onChange, onYearChange, utils] ); const focusYear = React.useCallback( diff --git a/lib/src/views/Calendar/useCalendarState.tsx b/lib/src/views/Calendar/useCalendarState.tsx index b204596bd..c3d9cd58e 100644 --- a/lib/src/views/Calendar/useCalendarState.tsx +++ b/lib/src/views/Calendar/useCalendarState.tsx @@ -3,9 +3,9 @@ import { CalendarViewProps } from './CalendarView'; import { SlideDirection } from './SlideTransition'; import { validateDate } from '../../_helpers/date-utils'; import { MaterialUiPickersDate } from '../../typings/date'; -import { MuiPickersAdapter, useUtils } from '../../_shared/hooks/useUtils'; +import { MuiPickersAdapter, useUtils, useNow } from '../../_shared/hooks/useUtils'; -interface State { +interface CalendarState { isMonthSwitchingAnimating: boolean; loadingQueue: number; currentMonth: MaterialUiPickersDate; @@ -25,14 +25,14 @@ export const createCalendarStateReducer = ( disableSwitchToMonthOnDayFocus: boolean, utils: MuiPickersAdapter ) => ( - state: State, + state: CalendarState, action: | ReducerAction<'popLoadingQueue'> | ReducerAction<'finishMonthSwitchingAnimation'> | ReducerAction<'changeMonth', ChangeMonthPayload> | ReducerAction<'changeMonthLoading', ChangeMonthPayload> | ReducerAction<'changeFocusedDay', { focusedDay: MaterialUiPickersDate }> -): State => { +): CalendarState => { switch (action.type) { case 'changeMonthLoading': { return { @@ -65,8 +65,10 @@ export const createCalendarStateReducer = ( } case 'changeFocusedDay': { const needMonthSwitch = + Boolean(action.focusedDay) && !disableSwitchToMonthOnDayFocus && !utils.isSameMonth(state.currentMonth, action.focusedDay); + return { ...state, focusedDay: action.focusedDay, @@ -103,16 +105,18 @@ export function useCalendarState({ shouldDisableDate, disableSwitchToMonthOnDayFocus = false, }: CalendarStateInput) { + const now = useNow(); const utils = useUtils(); + const dateForMonth = date || now; const reducerFn = React.useRef( createCalendarStateReducer(Boolean(reduceAnimations), disableSwitchToMonthOnDayFocus, utils) - ); + ).current; - const [{ loadingQueue, ...calendarState }, dispatch] = React.useReducer(reducerFn.current, { + const [{ loadingQueue, ...calendarState }, dispatch] = React.useReducer(reducerFn, { isMonthSwitchingAnimating: false, loadingQueue: 0, focusedDay: date, - currentMonth: utils.startOfMonth(date), + currentMonth: utils.startOfMonth(dateForMonth), slideDirection: 'left', }); @@ -138,31 +142,31 @@ export function useCalendarState({ ); const changeMonth = React.useCallback( - (date: MaterialUiPickersDate) => { - if (utils.isSameMonth(date, calendarState.currentMonth)) { + (newDate: MaterialUiPickersDate) => { + const newDateRequested = newDate ?? now; + if (utils.isSameMonth(newDateRequested, calendarState.currentMonth)) { return; } handleChangeMonth({ - newMonth: utils.startOfMonth(date), - direction: utils.isAfterDay(date, calendarState.currentMonth) ? 'left' : 'right', + newMonth: utils.startOfMonth(newDateRequested), + direction: utils.isAfterDay(newDateRequested, calendarState.currentMonth) + ? 'left' + : 'right', }); }, - [calendarState.currentMonth, handleChangeMonth, utils] + [calendarState.currentMonth, handleChangeMonth, now, utils] ); const isDateDisabled = React.useCallback( - (day: MaterialUiPickersDate) => { - return ( - validateDate(utils, day, { - disablePast, - disableFuture, - minDate, - maxDate, - shouldDisableDate, - }) !== null - ); - }, + (day: MaterialUiPickersDate) => + validateDate(utils, day, { + disablePast, + disableFuture, + minDate, + maxDate, + shouldDisableDate, + }) !== null, [disableFuture, disablePast, maxDate, minDate, shouldDisableDate, utils] ); diff --git a/lib/src/views/Clock/Clock.tsx b/lib/src/views/Clock/Clock.tsx index 7e28f7a5b..d0f95f33a 100644 --- a/lib/src/views/Clock/Clock.tsx +++ b/lib/src/views/Clock/Clock.tsx @@ -209,14 +209,16 @@ export const Clock: React.FC = ({ <>
- + {date && ( + + )} )} @@ -228,6 +230,7 @@ export const Clock: React.FC = ({ handleMeridiemChange('am')} + disabled={meridiemMode === null} className={clsx(classes.amButton, { [classes.meridiemButtonSelected]: meridiemMode === 'am', })} @@ -235,6 +238,7 @@ export const Clock: React.FC = ({ AM handleMeridiemChange('pm')} className={clsx(classes.pmButton, { diff --git a/lib/src/views/Clock/ClockNumbers.tsx b/lib/src/views/Clock/ClockNumbers.tsx index 6f2dc3bef..a16cda49b 100644 --- a/lib/src/views/Clock/ClockNumbers.tsx +++ b/lib/src/views/Clock/ClockNumbers.tsx @@ -3,6 +3,15 @@ import ClockNumber from './ClockNumber'; import { IUtils } from '@date-io/core/IUtils'; import { MaterialUiPickersDate } from '../../typings/date'; +interface GetHourNumbersOptions { + ampm: boolean; + utils: IUtils; + date: MaterialUiPickersDate; + onChange: (value: number, isFinish?: boolean) => void; + getClockNumberText: (hour: string) => string; + isDisabled: (value: number) => boolean; +} + export const getHourNumbers = ({ ampm, date, @@ -10,21 +19,18 @@ export const getHourNumbers = ({ onChange, isDisabled, getClockNumberText, -}: { - ampm: boolean; - utils: IUtils; - date: MaterialUiPickersDate; - onChange: (value: number, isFinish?: boolean) => void; - getClockNumberText: (hour: string) => string; - isDisabled: (value: number) => boolean; -}) => { - const currentHours = utils.getHours(date); +}: GetHourNumbersOptions) => { + const currentHours = date ? utils.getHours(date) : null; const hourNumbers: JSX.Element[] = []; const startHour = ampm ? 1 : 0; const endHour = ampm ? 12 : 23; const isSelected = (hour: number) => { + if (currentHours === null) { + return false; + } + if (ampm) { if (hour === 12) { return currentHours === 12 || currentHours === 0; diff --git a/lib/src/views/Clock/ClockView.tsx b/lib/src/views/Clock/ClockView.tsx index 05854f71a..dead06cc6 100644 --- a/lib/src/views/Clock/ClockView.tsx +++ b/lib/src/views/Clock/ClockView.tsx @@ -3,8 +3,8 @@ import * as PropTypes from 'prop-types'; import Clock from './Clock'; import { pipe } from '../../_helpers/utils'; import { makeStyles } from '@material-ui/core/styles'; -import { useUtils } from '../../_shared/hooks/useUtils'; import { MaterialUiPickersDate } from '../../typings/date'; +import { useUtils, useNow } from '../../_shared/hooks/useUtils'; import { PickerOnChangeFn } from '../../_shared/hooks/useViews'; import { getHourNumbers, getMinutesNumbers } from './ClockNumbers'; import { useMeridiemMode } from '../../TimePicker/TimePickerToolbar'; @@ -103,6 +103,7 @@ export const ClockView: React.FC = ({ previousViewAvailable, disableTimeValidationIgnoreDatePart, }) => { + const now = useNow(); const utils = useUtils(); const classes = useStyles(); const { meridiemMode, handleMeridiemChange } = useMeridiemMode(date, ampm, onDateChange); @@ -158,17 +159,18 @@ export const ClockView: React.FC = ({ ] ); + const dateOrNow = date || now; const viewProps = React.useMemo(() => { switch (type) { case 'hours': const handleHoursChange = (value: number, isFinish?: boolean | symbol) => { const valueWithMeridiem = convertValueToMeridiem(value, meridiemMode, Boolean(ampm)); - onChange(utils.setHours(date, valueWithMeridiem), isFinish); + onChange(utils.setHours(dateOrNow, valueWithMeridiem), isFinish); }; return { onChange: handleHoursChange, - value: utils.getHours(date), + value: utils.getHours(dateOrNow), children: getHourNumbers({ date, utils, @@ -180,9 +182,9 @@ export const ClockView: React.FC = ({ }; case 'minutes': - const minutesValue = utils.getMinutes(date); + const minutesValue = utils.getMinutes(dateOrNow); const handleMinutesChange = (value: number, isFinish?: boolean | symbol) => { - onChange(utils.setMinutes(date, value), isFinish); + onChange(utils.setMinutes(dateOrNow, value), isFinish); }; return { @@ -198,9 +200,9 @@ export const ClockView: React.FC = ({ }; case 'seconds': - const secondsValue = utils.getSeconds(date); + const secondsValue = utils.getSeconds(dateOrNow); const handleSecondsChange = (value: number, isFinish?: boolean | symbol) => { - onChange(utils.setSeconds(date, value), isFinish); + onChange(utils.setSeconds(dateOrNow, value), isFinish); }; return { @@ -228,6 +230,7 @@ export const ClockView: React.FC = ({ getSecondsClockNumberText, meridiemMode, onChange, + dateOrNow, isTimeDisabled, ]); @@ -267,7 +270,7 @@ export const ClockView: React.FC = ({ }; ClockView.propTypes = { - date: PropTypes.object.isRequired, + date: PropTypes.object, onChange: PropTypes.func.isRequired, ampm: PropTypes.bool, minutesStep: PropTypes.number, diff --git a/lib/src/wrappers/makeWrapperComponent.tsx b/lib/src/wrappers/makeWrapperComponent.tsx index 5ab45ccb4..31a212464 100644 --- a/lib/src/wrappers/makeWrapperComponent.tsx +++ b/lib/src/wrappers/makeWrapperComponent.tsx @@ -36,7 +36,6 @@ export function makeWrapperComponent< value, autoOk, inputFormat, - defaultHighlight, onChange, children, clearable,