@docsvision/material-ui-pickers 3.2.0-beta.24 → 3.2.0-beta.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (150) hide show
  1. package/.size-snapshot.json +11 -11
  2. package/9db1cba1a61cb58db53f38425bbd1f95.7z +0 -0
  3. package/build/DatePicker/DatePicker.d.ts +2 -2
  4. package/build/DatePicker/DatePicker.js +1 -1
  5. package/build/DateTimePicker/DateTimePicker.js +1 -1
  6. package/build/Picker/Picker.js +1 -1
  7. package/build/TimePicker/TimePicker.js +1 -1
  8. package/build/_helpers/text-field-helper.d.ts +1 -1
  9. package/build/_shared/KeyboardDateInput.js +1 -1
  10. package/build/_shared/ModalDialog.d.ts +2 -2
  11. package/build/_shared/PureDateInput.js +1 -1
  12. package/build/_shared/ToolbarButton.d.ts +2 -2
  13. package/build/_shared/hooks/useKeyboardPickerState.js +2 -2
  14. package/build/constants/prop-types.js +2 -2
  15. package/build/dist/material-ui-pickers.esm.js +12 -12
  16. package/build/dist/material-ui-pickers.esm.js.map +1 -1
  17. package/build/dist/material-ui-pickers.js +62 -62
  18. package/build/dist/material-ui-pickers.js.map +1 -1
  19. package/build/dist/material-ui-pickers.umd.js +5790 -4511
  20. package/build/dist/material-ui-pickers.umd.min.js +1 -1
  21. package/build/package.json +1 -1
  22. package/build/views/Calendar/Calendar.js +2 -2
  23. package/build/views/Clock/Clock.d.ts +1 -1
  24. package/build/views/Clock/ClockPointer.d.ts +1 -1
  25. package/package.json +1 -1
  26. package/src/DatePicker/DatePicker.tsx +91 -0
  27. package/src/DatePicker/DatePickerToolbar.tsx +75 -0
  28. package/src/DatePicker/index.ts +7 -0
  29. package/src/DateTimePicker/DateTimePicker.tsx +72 -0
  30. package/src/DateTimePicker/DateTimePickerTabs.tsx +86 -0
  31. package/src/DateTimePicker/DateTimePickerToolbar.tsx +123 -0
  32. package/src/DateTimePicker/index.ts +7 -0
  33. package/src/MuiPickersUtilsProvider.tsx +39 -0
  34. package/src/Picker/Picker.tsx +275 -0
  35. package/src/Picker/WrappedKeyboardPicker.tsx +136 -0
  36. package/src/Picker/WrappedPurePicker.tsx +128 -0
  37. package/src/Rifm/Rifm.ts +213 -0
  38. package/src/TimePicker/TimePicker.tsx +67 -0
  39. package/src/TimePicker/TimePickerToolbar.tsx +180 -0
  40. package/src/TimePicker/index.tsx +7 -0
  41. package/src/__tests__/DatePicker/Calendar.test.tsx +51 -0
  42. package/src/__tests__/DatePicker/Month.test.tsx +36 -0
  43. package/src/__tests__/DatePicker/MonthSelection.test.tsx +37 -0
  44. package/src/__tests__/MuiPickersUtilsProvider.test.tsx +21 -0
  45. package/src/__tests__/TimePicker/Clock.test.tsx +68 -0
  46. package/src/__tests__/_helpers/date-utils.test.tsx +197 -0
  47. package/src/__tests__/_helpers/text-field-helper.test.tsx +34 -0
  48. package/src/__tests__/_shared/ModalDialog.test.tsx +109 -0
  49. package/src/__tests__/e2e/DatePicker.test.tsx +233 -0
  50. package/src/__tests__/e2e/DatePickerProps.test.tsx +18 -0
  51. package/src/__tests__/e2e/DatePickerRoot.test.tsx +154 -0
  52. package/src/__tests__/e2e/DateTimePicker.test.tsx +86 -0
  53. package/src/__tests__/e2e/DateTimePickerRoot.test.tsx +72 -0
  54. package/src/__tests__/e2e/KeyboardDatePicker.test.tsx +140 -0
  55. package/src/__tests__/e2e/KeyboardDateTimePicker.test.tsx +48 -0
  56. package/src/__tests__/e2e/Theme.test.tsx +30 -0
  57. package/src/__tests__/e2e/TimePicker.test.tsx +121 -0
  58. package/src/__tests__/e2e/commands.tsx +8 -0
  59. package/src/__tests__/setup.js +26 -0
  60. package/src/__tests__/test-utils.tsx +45 -0
  61. package/src/__tests__/tsconfig.json +5 -0
  62. package/src/_helpers/date-utils.ts +92 -0
  63. package/src/_helpers/text-field-helper.ts +239 -0
  64. package/src/_helpers/time-utils.ts +83 -0
  65. package/src/_helpers/utils.ts +8 -0
  66. package/src/_shared/KeyboardDateInput.tsx +161 -0
  67. package/src/_shared/ModalDialog.tsx +114 -0
  68. package/src/_shared/PickerToolbar.tsx +52 -0
  69. package/src/_shared/PureDateInput.tsx +45 -0
  70. package/src/_shared/ToolbarButton.tsx +63 -0
  71. package/src/_shared/ToolbarText.tsx +49 -0
  72. package/src/_shared/WithUtils.tsx +19 -0
  73. package/src/_shared/hooks/useIsLandscape.tsx +27 -0
  74. package/src/_shared/hooks/useKeyDown.ts +34 -0
  75. package/src/_shared/hooks/useKeyboardPickerState.ts +72 -0
  76. package/src/_shared/hooks/useOpenState.ts +22 -0
  77. package/src/_shared/hooks/usePickerState.ts +134 -0
  78. package/src/_shared/hooks/useUtils.ts +20 -0
  79. package/src/_shared/hooks/useViews.tsx +31 -0
  80. package/src/_shared/icons/ArrowLeftIcon.tsx +11 -0
  81. package/src/_shared/icons/ArrowRightIcon.tsx +11 -0
  82. package/src/_shared/icons/DateRangeIcon.tsx +11 -0
  83. package/src/_shared/icons/KeyboardIcon.tsx +11 -0
  84. package/src/_shared/icons/TimeIcon.tsx +12 -0
  85. package/src/constants/ClockType.ts +11 -0
  86. package/src/constants/dimensions.ts +5 -0
  87. package/src/constants/prop-types.ts +37 -0
  88. package/src/index.ts +48 -0
  89. package/src/typings/BasePicker.tsx +60 -0
  90. package/src/typings/date.ts +3 -0
  91. package/src/typings/extendMui.ts +10 -0
  92. package/src/typings/overrides.ts +44 -0
  93. package/src/views/Calendar/Calendar.tsx +361 -0
  94. package/src/views/Calendar/CalendarHeader.tsx +141 -0
  95. package/src/views/Calendar/Day.tsx +97 -0
  96. package/src/views/Calendar/DayWrapper.tsx +33 -0
  97. package/src/views/Calendar/SlideTransition.tsx +100 -0
  98. package/src/views/Clock/Clock.tsx +172 -0
  99. package/src/views/Clock/ClockNumber.tsx +91 -0
  100. package/src/views/Clock/ClockNumbers.tsx +76 -0
  101. package/src/views/Clock/ClockPointer.tsx +104 -0
  102. package/src/views/Clock/ClockView.tsx +105 -0
  103. package/src/views/Month/Month.tsx +76 -0
  104. package/src/views/Month/MonthView.tsx +94 -0
  105. package/src/views/Year/Year.tsx +77 -0
  106. package/src/views/Year/YearView.tsx +91 -0
  107. package/src/wrappers/InlineWrapper.tsx +80 -0
  108. package/src/wrappers/ModalWrapper.tsx +113 -0
  109. package/src/wrappers/StaticWrapper.tsx +22 -0
  110. package/src/wrappers/Wrapper.tsx +75 -0
  111. package/coverage/clover.xml +0 -601
  112. package/coverage/coverage-final.json +0 -22
  113. package/coverage/lcov-report/base.css +0 -224
  114. package/coverage/lcov-report/block-navigation.js +0 -79
  115. package/coverage/lcov-report/index.html +0 -201
  116. package/coverage/lcov-report/prettify.css +0 -1
  117. package/coverage/lcov-report/prettify.js +0 -2
  118. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  119. package/coverage/lcov-report/sorter.js +0 -170
  120. package/coverage/lcov-report/src/MuiPickersUtilsProvider.tsx.html +0 -186
  121. package/coverage/lcov-report/src/_helpers/date-utils.ts.html +0 -345
  122. package/coverage/lcov-report/src/_helpers/index.html +0 -123
  123. package/coverage/lcov-report/src/_helpers/time-utils.ts.html +0 -318
  124. package/coverage/lcov-report/src/_helpers/utils.ts.html +0 -93
  125. package/coverage/lcov-report/src/_shared/ModalDialog.tsx.html +0 -411
  126. package/coverage/lcov-report/src/_shared/WithUtils.tsx.html +0 -126
  127. package/coverage/lcov-report/src/_shared/hooks/index.html +0 -110
  128. package/coverage/lcov-report/src/_shared/hooks/useKeyDown.ts.html +0 -171
  129. package/coverage/lcov-report/src/_shared/hooks/useUtils.ts.html +0 -129
  130. package/coverage/lcov-report/src/_shared/icons/ArrowLeftIcon.tsx.html +0 -102
  131. package/coverage/lcov-report/src/_shared/icons/ArrowRightIcon.tsx.html +0 -102
  132. package/coverage/lcov-report/src/_shared/icons/index.html +0 -110
  133. package/coverage/lcov-report/src/_shared/index.html +0 -110
  134. package/coverage/lcov-report/src/constants/ClockType.ts.html +0 -102
  135. package/coverage/lcov-report/src/constants/dimensions.ts.html +0 -84
  136. package/coverage/lcov-report/src/constants/index.html +0 -110
  137. package/coverage/lcov-report/src/index.html +0 -97
  138. package/coverage/lcov-report/src/views/Calendar/Calendar.tsx.html +0 -1152
  139. package/coverage/lcov-report/src/views/Calendar/CalendarHeader.tsx.html +0 -492
  140. package/coverage/lcov-report/src/views/Calendar/Day.tsx.html +0 -360
  141. package/coverage/lcov-report/src/views/Calendar/DayWrapper.tsx.html +0 -168
  142. package/coverage/lcov-report/src/views/Calendar/SlideTransition.tsx.html +0 -369
  143. package/coverage/lcov-report/src/views/Calendar/index.html +0 -149
  144. package/coverage/lcov-report/src/views/Clock/Clock.tsx.html +0 -585
  145. package/coverage/lcov-report/src/views/Clock/ClockPointer.tsx.html +0 -381
  146. package/coverage/lcov-report/src/views/Clock/index.html +0 -110
  147. package/coverage/lcov-report/src/views/Month/Month.tsx.html +0 -297
  148. package/coverage/lcov-report/src/views/Month/MonthView.tsx.html +0 -351
  149. package/coverage/lcov-report/src/views/Month/index.html +0 -110
  150. package/coverage/lcov.info +0 -1094
@@ -0,0 +1,213 @@
1
+ import * as React from 'react';
2
+
3
+ type Props = {
4
+ value: string;
5
+ maskChar: string;
6
+ onChange: (value: string) => void;
7
+ format: (str: string, oldValue: string, currentCaretPosition?: number) => string;
8
+ mask?: boolean;
9
+ replace?: (value: string) => string;
10
+ accept?: RegExp;
11
+ letterPosition?: number;
12
+ children: (value: {
13
+ value: string;
14
+ onChange: React.ChangeEventHandler<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>;
15
+ }) => any;
16
+ };
17
+
18
+ export const Rifm = (props: Props) => {
19
+ const [, refresh] = React.useReducer(c => c + 1, 0);
20
+ const valueRef = React.useRef<any>(null);
21
+ const { replace, maskChar } = props;
22
+ const [previousValue, setPreviousValue] = React.useState({ value: '' });
23
+ const userValue = replace
24
+ ? replace(props.format(props.value, previousValue.value))
25
+ : props.format(props.value, previousValue.value);
26
+
27
+ const currentPreviousValue = previousValue.value;
28
+
29
+ // state of delete button see comments below about inputType support
30
+ const isDeleleteButtonDownRef = React.useRef(false);
31
+
32
+ const onChange = (evt: any) => {
33
+ const eventValue = evt.target.value;
34
+ const selectionStart = evt.target.selectionStart;
35
+
36
+ valueRef.current = [
37
+ eventValue, // eventValue
38
+ evt.target, // input
39
+ eventValue.length > userValue.length, // isSizeIncreaseOperation
40
+ isDeleleteButtonDownRef.current, // isDeleleteButtonDown
41
+ selectionStart, // selectionStart
42
+ userValue === props.format(eventValue, currentPreviousValue, selectionStart), // isNoOperation
43
+ ];
44
+
45
+ // The main trick is to update underlying input with non formatted value (= eventValue)
46
+ // that allows us to calculate right cursor position after formatting (see getCursorPosition)
47
+ // then we format new value and call props.onChange with masked/formatted value
48
+ // and finally we are able to set cursor position into right place
49
+ refresh(null);
50
+ };
51
+
52
+ // React prints warn on server in non production mode about useLayoutEffect usage
53
+ // in both cases it's noop
54
+ if (typeof window !== 'undefined') {
55
+ React.useLayoutEffect(() => {
56
+ if (valueRef.current == null) return;
57
+
58
+ let [
59
+ eventValue,
60
+ input,
61
+ isSizeIncreaseOperation,
62
+ isDeleleteButtonDown,
63
+ selectionStart,
64
+ // No operation means that value itself hasn't been changed, BTW cursor, selection etc can be changed
65
+ isNoOperation,
66
+ ] = valueRef.current;
67
+ valueRef.current = null;
68
+
69
+ // this usually occurs on deleting special symbols like ' here 123'123.00
70
+ // in case of isDeleleteButtonDown cursor should move differently vs backspace
71
+ const deleteWasNoOp = isDeleleteButtonDown && isNoOperation;
72
+
73
+ // Create string from only accepted symbols
74
+ const clean = (str: string) => (str.match(props.accept || /\d|\w/g) || []).join('');
75
+
76
+ const valueBeforeSelectionStart = clean(eventValue.substr(0, input.selectionStart));
77
+
78
+ // trying to find cursor position in formatted value having knowledge about valueBeforeSelectionStart
79
+ // This works because we assume that format doesn't change the order of accepted symbols.
80
+ // Imagine we have formatter which adds ' symbol between numbers, and by default we refuse all non numeric symbols
81
+ // for example we had input = 1'2|'4 (| means cursor position) then user entered '3' symbol
82
+ // inputValue = 1'23'|4 so valueBeforeSelectionStart = 123 and formatted value = 1'2'3'4
83
+ // calling getCursorPosition("1'2'3'4") will give us position after 3, 1'2'3|'4
84
+ // so for formatting just this function to determine cursor position after formatting is enough
85
+ // with masking we need to do some additional checks see `mask` below
86
+ const getCursorPosition = (val: any) => {
87
+ let start = 0;
88
+ let cleanPos = 0;
89
+
90
+ for (let i = 0; i !== valueBeforeSelectionStart.length; ++i) {
91
+ let newPos = val.indexOf(valueBeforeSelectionStart[i], start) + 1;
92
+
93
+ let newCleanPos = clean(val).indexOf(valueBeforeSelectionStart[i], cleanPos) + 1;
94
+
95
+ // this skips position change if accepted symbols order was broken
96
+ // For example fixes edge case with fixed point numbers:
97
+ // You have '0|.00', then press 1, it becomes 01|.00 and after format 1.00, this breaks our assumption
98
+ // that order of accepted symbols is not changed after format,
99
+ // so here we don't update start position if other accepted symbols was inbetween current and new position
100
+ if (newCleanPos - cleanPos > 1) {
101
+ newPos = start;
102
+ newCleanPos = cleanPos;
103
+ }
104
+
105
+ cleanPos = Math.max(newCleanPos, cleanPos);
106
+ start = Math.max(start, newPos);
107
+ }
108
+ return start;
109
+ };
110
+
111
+ //let start = getCursorPosition(eventValue);
112
+
113
+ // // Masking part, for masks if size of mask is above some value
114
+ // // we need to replace symbols instead of do nothing as like in format
115
+ // if (props.mask === true && isSizeIncreaseOperation && !isNoOperation) {
116
+ // const c = clean(eventValue.substr(start))[0];
117
+ // start = eventValue.indexOf(c, start);
118
+
119
+ // eventValue = `${eventValue.substr(0, start)}${eventValue.substr(
120
+ // start + 1
121
+ // )}`;
122
+ // }
123
+
124
+ const formattedValue = props.format(eventValue, currentPreviousValue, selectionStart);
125
+ let replacedValue = replace ? replace(formattedValue) : formattedValue;
126
+
127
+ let start = getCursorPosition(formattedValue);
128
+ let inputCharPosition = start - 1;
129
+ if (inputCharPosition > -1) {
130
+ if (
131
+ props.letterPosition &&
132
+ props.letterPosition > -1 &&
133
+ replacedValue[inputCharPosition].match(/[a-zA-Z]/) &&
134
+ (inputCharPosition < props.letterPosition || inputCharPosition > props.letterPosition + 1)
135
+ ) {
136
+ replacedValue = currentPreviousValue;
137
+ } else if (
138
+ props.letterPosition &&
139
+ props.letterPosition > -1 &&
140
+ replacedValue[inputCharPosition].match(/\d/) &&
141
+ (inputCharPosition >= props.letterPosition &&
142
+ inputCharPosition <= props.letterPosition + 1)
143
+ ) {
144
+ replacedValue = currentPreviousValue;
145
+ }
146
+ }
147
+
148
+ if (userValue === replacedValue) {
149
+ // if nothing changed for formatted value, just refresh so userValue will be used at render
150
+ refresh(null);
151
+ } else {
152
+ props.onChange(replacedValue);
153
+ }
154
+
155
+ return () => {
156
+ // Visually improves working with masked values,
157
+ // like cursor jumping over refused symbols
158
+ // as an example date mask: was "5|1-24-3" then user pressed "6"
159
+ // it becomes "56-|12-43" with this code, and "56|-12-43" without
160
+ if (
161
+ props.mask != null &&
162
+ (isSizeIncreaseOperation || (isDeleleteButtonDown && !deleteWasNoOp))
163
+ ) {
164
+ while (
165
+ formattedValue[start] &&
166
+ clean(formattedValue[start]) === '' &&
167
+ formattedValue[start] != maskChar
168
+ ) {
169
+ start += 1;
170
+ }
171
+ }
172
+
173
+ //input.selectionStart = input.selectionEnd = start + (deleteWasNoOp ? 1 : 0);
174
+ };
175
+ });
176
+ }
177
+
178
+ React.useEffect(() => {
179
+ // until https://developer.mozilla.org/en-US/docs/Web/API/InputEvent/inputType will be supported
180
+ // by all major browsers (now supported by: +chrome, +safari, ?edge, !firefox)
181
+ // there is no way I found to distinguish in onChange
182
+ // backspace or delete was called in some situations
183
+ // firefox track https://bugzilla.mozilla.org/show_bug.cgi?id=1447239
184
+ const handleKeyDown = (evt: KeyboardEvent) => {
185
+ if (evt.code === 'Delete') {
186
+ isDeleleteButtonDownRef.current = true;
187
+ }
188
+ };
189
+
190
+ const handleKeyUp = (evt: KeyboardEvent) => {
191
+ if (evt.code === 'Delete') {
192
+ isDeleleteButtonDownRef.current = false;
193
+ }
194
+ };
195
+
196
+ document.addEventListener('keydown', handleKeyDown);
197
+ document.addEventListener('keyup', handleKeyUp);
198
+
199
+ return () => {
200
+ document.removeEventListener('keydown', handleKeyDown);
201
+ document.removeEventListener('keyup', handleKeyUp);
202
+ };
203
+ }, []);
204
+
205
+ let value = valueRef.current != null ? valueRef.current[0] : userValue;
206
+ // value = props.format(value, currentPreviousValue);
207
+ previousValue.value = value;
208
+
209
+ return props.children({
210
+ value: value,
211
+ onChange,
212
+ });
213
+ };
@@ -0,0 +1,67 @@
1
+ import TimePickerToolbar from './TimePickerToolbar';
2
+ import { useUtils } from '../_shared/hooks/useUtils';
3
+ import { timePickerDefaultProps } from '../constants/prop-types';
4
+ import { pick12hOr24hFormat } from '../_helpers/text-field-helper';
5
+ import { WrappedPurePickerProps, makePurePicker } from '../Picker/WrappedPurePicker';
6
+ import { makeKeyboardPicker, WrappedKeyboardPickerProps } from '../Picker/WrappedKeyboardPicker';
7
+
8
+ type TimePickerView = 'hours' | 'minutes' | 'seconds';
9
+
10
+ export interface BaseTimePickerProps {
11
+ /**
12
+ * 12h/24h view for hour selection clock
13
+ * @default true
14
+ */
15
+ ampm?: boolean;
16
+ /**
17
+ * Step over minutes
18
+ * @default 1
19
+ */
20
+ minutesStep?: number;
21
+ }
22
+
23
+ export interface TimePickerViewsProps extends BaseTimePickerProps {
24
+ /** Array of views to show */
25
+ views?: ('hours' | 'minutes' | 'seconds')[];
26
+ /** Open to timepicker */
27
+ openTo?: 'hours' | 'minutes' | 'seconds';
28
+ }
29
+
30
+ export type TimePickerProps = WrappedPurePickerProps & TimePickerViewsProps;
31
+
32
+ export type KeyboardTimePickerProps = WrappedKeyboardPickerProps & TimePickerViewsProps;
33
+
34
+ const defaultProps = {
35
+ ...timePickerDefaultProps,
36
+ openTo: 'hours' as TimePickerView,
37
+ views: ['hours', 'minutes'] as TimePickerView[],
38
+ };
39
+
40
+ function useOptions(props: TimePickerProps | KeyboardTimePickerProps) {
41
+ const utils = useUtils();
42
+
43
+ return {
44
+ getDefaultFormat: () =>
45
+ pick12hOr24hFormat(props.format, props.ampm, {
46
+ '12h': utils.time12hFormat,
47
+ '24h': utils.time24hFormat,
48
+ }),
49
+ };
50
+ }
51
+
52
+ export const TimePicker = makePurePicker<TimePickerViewsProps>({
53
+ useOptions,
54
+ DefaultToolbarComponent: TimePickerToolbar,
55
+ });
56
+
57
+ export const KeyboardTimePicker = makeKeyboardPicker<TimePickerViewsProps>({
58
+ useOptions,
59
+ DefaultToolbarComponent: TimePickerToolbar,
60
+ getCustomProps: props => ({
61
+ refuse: props.ampm ? /[^\dap]+/gi : /[^\d]+/gi,
62
+ }),
63
+ });
64
+
65
+ TimePicker.defaultProps = defaultProps;
66
+
67
+ KeyboardTimePicker.defaultProps = defaultProps;
@@ -0,0 +1,180 @@
1
+ import * as React from 'react';
2
+ import clsx from 'clsx';
3
+ import ClockType from '../constants/ClockType';
4
+ import ToolbarText from '../_shared/ToolbarText';
5
+ import ToolbarButton from '../_shared/ToolbarButton';
6
+ import PickerToolbar from '../_shared/PickerToolbar';
7
+ import { arrayIncludes } from '../_helpers/utils';
8
+ import { useUtils } from '../_shared/hooks/useUtils';
9
+ import { MaterialUiPickersDate } from '../typings/date';
10
+ import { ToolbarComponentProps } from '../Picker/Picker';
11
+ import { useTheme, makeStyles } from '@material-ui/core/styles';
12
+ import { convertToMeridiem, getMeridiem } from '../_helpers/time-utils';
13
+
14
+ export const useStyles = makeStyles(
15
+ {
16
+ toolbarLandscape: {
17
+ flexWrap: 'wrap',
18
+ },
19
+ toolbarAmpmLeftPadding: {
20
+ paddingLeft: 50,
21
+ },
22
+ separator: {
23
+ margin: '0 4px 0 2px',
24
+ cursor: 'default',
25
+ },
26
+ hourMinuteLabel: {
27
+ display: 'flex',
28
+ justifyContent: 'flex-end',
29
+ alignItems: 'flex-end',
30
+ },
31
+ hourMinuteLabelLandscape: {
32
+ marginTop: 'auto',
33
+ },
34
+ hourMinuteLabelReverse: {
35
+ flexDirection: 'row-reverse',
36
+ },
37
+ ampmSelection: {
38
+ marginLeft: 20,
39
+ marginRight: -20,
40
+ display: 'flex',
41
+ flexDirection: 'column',
42
+ },
43
+ ampmLandscape: {
44
+ margin: '4px 0 auto',
45
+ flexDirection: 'row',
46
+ justifyContent: 'space-around',
47
+ flexBasis: '100%',
48
+ },
49
+ ampmSelectionWithSeconds: {
50
+ marginLeft: 15,
51
+ marginRight: 10,
52
+ },
53
+ ampmLabel: {
54
+ fontSize: 18,
55
+ },
56
+ },
57
+ { name: 'MuiPickersTimePickerToolbar' }
58
+ );
59
+
60
+ export function useMeridiemMode(
61
+ date: MaterialUiPickersDate,
62
+ ampm: boolean | undefined,
63
+ onChange: (date: MaterialUiPickersDate, isFinished?: boolean | undefined) => void
64
+ ) {
65
+ const utils = useUtils();
66
+ const meridiemMode = getMeridiem(date, utils);
67
+
68
+ const handleMeridiemChange = React.useCallback(
69
+ (mode: 'am' | 'pm') => {
70
+ const timeWithMeridiem = convertToMeridiem(date, mode, Boolean(ampm), utils);
71
+ onChange(timeWithMeridiem, false);
72
+ },
73
+ [ampm, date, onChange, utils]
74
+ );
75
+
76
+ return { meridiemMode, handleMeridiemChange };
77
+ }
78
+
79
+ const TimePickerToolbar: React.FC<ToolbarComponentProps> = ({
80
+ date,
81
+ views,
82
+ ampm,
83
+ openView,
84
+ onChange,
85
+ isLandscape,
86
+ setOpenView,
87
+ }) => {
88
+ const utils = useUtils();
89
+ const theme = useTheme();
90
+ const classes = useStyles();
91
+ const { meridiemMode, handleMeridiemChange } = useMeridiemMode(date, ampm, onChange);
92
+
93
+ const clockTypographyVariant = isLandscape ? 'h3' : 'h2';
94
+
95
+ return (
96
+ <PickerToolbar
97
+ isLandscape={isLandscape}
98
+ className={clsx({
99
+ [classes.toolbarLandscape]: isLandscape,
100
+ [classes.toolbarAmpmLeftPadding]: ampm && !isLandscape,
101
+ })}
102
+ >
103
+ <div
104
+ className={clsx(classes.hourMinuteLabel, {
105
+ [classes.hourMinuteLabelLandscape]: isLandscape,
106
+ [classes.hourMinuteLabelReverse]: theme.direction === 'rtl',
107
+ })}
108
+ >
109
+ {arrayIncludes(views, 'hours') && (
110
+ <ToolbarButton
111
+ variant={clockTypographyVariant}
112
+ onClick={() => setOpenView(ClockType.HOURS)}
113
+ selected={openView === ClockType.HOURS}
114
+ label={utils.getHourText(date, Boolean(ampm))}
115
+ />
116
+ )}
117
+
118
+ {arrayIncludes(views, ['hours', 'minutes']) && (
119
+ <ToolbarText
120
+ label=":"
121
+ variant={clockTypographyVariant}
122
+ selected={false}
123
+ className={classes.separator}
124
+ />
125
+ )}
126
+
127
+ {arrayIncludes(views, 'minutes') && (
128
+ <ToolbarButton
129
+ variant={clockTypographyVariant}
130
+ onClick={() => setOpenView(ClockType.MINUTES)}
131
+ selected={openView === ClockType.MINUTES}
132
+ label={utils.getMinuteText(date)}
133
+ />
134
+ )}
135
+
136
+ {arrayIncludes(views, ['minutes', 'seconds']) && (
137
+ <ToolbarText variant="h2" label=":" selected={false} className={classes.separator} />
138
+ )}
139
+
140
+ {arrayIncludes(views, 'seconds') && (
141
+ <ToolbarButton
142
+ variant="h2"
143
+ onClick={() => setOpenView(ClockType.SECONDS)}
144
+ selected={openView === ClockType.SECONDS}
145
+ label={utils.getSecondText(date)}
146
+ />
147
+ )}
148
+ </div>
149
+
150
+ {ampm && (
151
+ <div
152
+ className={clsx(classes.ampmSelection, {
153
+ [classes.ampmLandscape]: isLandscape,
154
+ [classes.ampmSelectionWithSeconds]: arrayIncludes(views, 'seconds'),
155
+ })}
156
+ >
157
+ <ToolbarButton
158
+ disableRipple
159
+ variant="subtitle1"
160
+ selected={meridiemMode === 'am'}
161
+ typographyClassName={classes.ampmLabel}
162
+ label={utils.getMeridiemText('am')}
163
+ onClick={() => handleMeridiemChange('am')}
164
+ />
165
+
166
+ <ToolbarButton
167
+ disableRipple
168
+ variant="subtitle1"
169
+ selected={meridiemMode === 'pm'}
170
+ typographyClassName={classes.ampmLabel}
171
+ label={utils.getMeridiemText('pm')}
172
+ onClick={() => handleMeridiemChange('pm')}
173
+ />
174
+ </div>
175
+ )}
176
+ </PickerToolbar>
177
+ );
178
+ };
179
+
180
+ export default TimePickerToolbar;
@@ -0,0 +1,7 @@
1
+ import { KeyboardTimePickerProps, TimePickerProps } from './TimePicker';
2
+
3
+ export type TimePickerProps = TimePickerProps;
4
+
5
+ export type KeyboardTimePickerProps = KeyboardTimePickerProps;
6
+
7
+ export { KeyboardTimePicker, TimePicker } from './TimePicker';
@@ -0,0 +1,51 @@
1
+ import * as React from 'react';
2
+ import { ShallowWrapper } from 'enzyme';
3
+ import { shallowRender, utilsToUse } from '../test-utils';
4
+ import { Calendar, CalendarProps } from '../../views/Calendar/Calendar';
5
+
6
+ describe('Calendar', () => {
7
+ let component: ShallowWrapper<CalendarProps>;
8
+ const onChangeMock = jest.fn();
9
+ const onMonthChangeMock = jest.fn();
10
+
11
+ beforeEach(() => {
12
+ component = shallowRender(props => (
13
+ <Calendar
14
+ date={utilsToUse.date('01-01-2017')}
15
+ onMonthChange={onMonthChangeMock}
16
+ onChange={onChangeMock}
17
+ {...props}
18
+ />
19
+ ));
20
+ });
21
+
22
+ it('Should swipe between month', () => {
23
+ component.find('CalendarHeader').prop<any>('onMonthChange')(utilsToUse.date());
24
+ expect(onMonthChangeMock).toHaveBeenCalled();
25
+ });
26
+ });
27
+
28
+ describe('Calendar - disabled selected date on mount', () => {
29
+ let component: ShallowWrapper<any, any, any>;
30
+
31
+ beforeEach(() => {
32
+ component = shallowRender(props => (
33
+ <Calendar
34
+ date={utilsToUse.date('01-01-2017')}
35
+ minDate={new Date('01-01-2018')}
36
+ onChange={jest.fn()}
37
+ utils={utilsToUse}
38
+ {...props}
39
+ />
40
+ ));
41
+ });
42
+
43
+ it('Should dispatch onDateSelect with isFinish = false on mount', () => {
44
+ const { onChange } = component.instance().props;
45
+ if (process.env.UTILS === 'moment') {
46
+ return expect(onChange).toHaveBeenCalled();
47
+ }
48
+
49
+ expect(onChange).toHaveBeenCalledWith(utilsToUse.date('01-01-2018'), false);
50
+ });
51
+ });
@@ -0,0 +1,36 @@
1
+ import * as React from 'react';
2
+ import { ShallowWrapper } from 'enzyme';
3
+ import { shallow, utilsToUse } from '../test-utils';
4
+ import { Month, MonthProps } from '../../views/Month/Month';
5
+
6
+ describe('Month', () => {
7
+ let component: ShallowWrapper<MonthProps>;
8
+
9
+ beforeEach(() => {
10
+ component = shallow(
11
+ <Month value={utilsToUse.date('01-01-2017')} onSelect={jest.fn()}>
12
+ Oct
13
+ </Month>
14
+ );
15
+ });
16
+
17
+ it('Should render', () => {
18
+ expect(component).toBeTruthy();
19
+ });
20
+ });
21
+
22
+ describe('Month - disabled state', () => {
23
+ let component: ShallowWrapper<MonthProps>;
24
+
25
+ beforeEach(() => {
26
+ component = shallow(
27
+ <Month disabled value={utilsToUse.date('01-01-2017')} onSelect={jest.fn()}>
28
+ Oct
29
+ </Month>
30
+ );
31
+ });
32
+
33
+ it('Should render in disabled state', () => {
34
+ expect(component.prop('tabIndex')).toBe(-1);
35
+ });
36
+ });
@@ -0,0 +1,37 @@
1
+ import * as React from 'react';
2
+ import Month from '../../views/Month/Month';
3
+ import { ReactWrapper } from 'enzyme';
4
+ import { mount, utilsToUse } from '../test-utils';
5
+ import { MonthSelection, MonthSelectionProps } from '../../views/Month/MonthView';
6
+
7
+ describe('MonthSelection', () => {
8
+ let component: ReactWrapper<MonthSelectionProps>;
9
+
10
+ beforeEach(() => {
11
+ component = mount(
12
+ <MonthSelection
13
+ minDate={new Date('03-01-2017')}
14
+ maxDate={new Date('05-01-2017')}
15
+ date={utilsToUse.date('04-01-2017')}
16
+ onChange={jest.fn()}
17
+ />
18
+ );
19
+ });
20
+
21
+ it('Should render disabled months before min date and after max date', () => {
22
+ expect(component.find(Month).map(month => month.prop('disabled'))).toEqual([
23
+ true,
24
+ true,
25
+ false,
26
+ false,
27
+ false,
28
+ true,
29
+ true,
30
+ true,
31
+ true,
32
+ true,
33
+ true,
34
+ true,
35
+ ]);
36
+ });
37
+ });
@@ -0,0 +1,21 @@
1
+ // required to use just shallow here because utils prop override
2
+ import * as React from 'react';
3
+ import DateFnsUtils from '@date-io/date-fns';
4
+ import MuiPickersUtilsProvider, { MuiPickersUtilsProviderProps } from '../MuiPickersUtilsProvider';
5
+ import { shallow, ShallowWrapper } from 'enzyme'; // required to use just shallow here because utils prop override
6
+
7
+ describe('MuiPickersUtilsProvider', () => {
8
+ let component: ShallowWrapper<MuiPickersUtilsProviderProps>;
9
+
10
+ beforeEach(() => {
11
+ component = shallow(
12
+ <MuiPickersUtilsProvider utils={DateFnsUtils}>
13
+ <div />
14
+ </MuiPickersUtilsProvider>
15
+ );
16
+ });
17
+
18
+ it('Should render context provider', () => {
19
+ expect(component).toBeTruthy();
20
+ });
21
+ });
@@ -0,0 +1,68 @@
1
+ import * as React from 'react';
2
+ import { ShallowWrapper } from 'enzyme';
3
+ import { shallow } from '../test-utils';
4
+ import { Clock, ClockProps } from '../../views/Clock/Clock';
5
+
6
+ const mouseClockEvent = {
7
+ preventDefault: jest.fn(),
8
+ stopPropagation: jest.fn(),
9
+ buttons: 1,
10
+ nativeEvent: {
11
+ offsetX: 10,
12
+ offsetY: 25,
13
+ },
14
+ };
15
+
16
+ describe('Clock', () => {
17
+ let component: ShallowWrapper<ClockProps>;
18
+ const onChangeMock = jest.fn();
19
+
20
+ beforeEach(() => {
21
+ onChangeMock.mockReset();
22
+ component = shallow(
23
+ <Clock
24
+ type="minutes"
25
+ onChange={onChangeMock}
26
+ classes={{} as any}
27
+ value={12}
28
+ children={[<div key="foo">foo</div>]}
29
+ />
30
+ );
31
+ });
32
+
33
+ it('Should renders', () => {
34
+ // console.log(component.debug());
35
+ expect(component).toBeTruthy();
36
+ });
37
+
38
+ it('Should set time on mouse move with click', () => {
39
+ component.find('[role="menu"]').simulate('mouseMove', mouseClockEvent);
40
+
41
+ expect(onChangeMock).toHaveBeenCalledWith(52, false);
42
+ });
43
+
44
+ it('Should set isMoving = false on mouse up', () => {
45
+ (component.instance() as Clock).isMoving = true;
46
+ component.find('[role="menu"]').simulate('mouseUp', mouseClockEvent);
47
+
48
+ expect((component.instance() as Clock).isMoving).toBeFalsy();
49
+ });
50
+
51
+ it('Should set time on touch move', () => {
52
+ component.find('[role="menu"]').simulate('touchMove', {
53
+ preventDefault: jest.fn(),
54
+ stopPropagation: jest.fn(),
55
+ changedTouches: [{ clientX: 10, clientY: 15 }],
56
+ target: {
57
+ getBoundingClientRect: () => ({ left: 0, top: 0 }),
58
+ },
59
+ });
60
+
61
+ expect(onChangeMock).toHaveBeenCalledWith(52, false);
62
+ });
63
+
64
+ it('Should set isMoving = false on touch end', () => {
65
+ component.find('[role="menu"]').simulate('touchEnd');
66
+ expect((component.instance() as Clock).isMoving).toBeFalsy();
67
+ });
68
+ });