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,