bright-components 10.1.1 → 10.2.0
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.
- package/dist/components/DayPicker/NewDayPicker/index.js +207 -0
- package/dist/components/DayPicker/index.js +30 -7
- package/dist/components/DayPickerPanel/index.js +1 -1
- package/dist/components/Time/TimePicker/index.js +1 -0
- package/dist/setupTests.js +5 -0
- package/package.json +3 -2
- package/src/components/DayPicker/NewDayPicker/index.js +208 -0
- package/src/components/DayPicker/examples.md +17 -0
- package/src/components/DayPicker/index.js +43 -17
- package/src/components/DayPicker/test.js +297 -1
- package/src/components/DayPickerPanel/index.js +1 -1
- package/src/components/DayPickerPanel/test.js +1 -1
- package/src/components/DurationInput/test.js +5 -3
- package/src/components/EmployeePicker/FilterBar/test.js +1 -1
- package/src/components/ResponsiveTabs/test.js +3 -3
- package/src/components/Time/TimePicker/index.js +1 -0
- package/src/components/Time/TimePicker/test.js +29 -28
- package/src/setupTests.js +2 -0
@@ -0,0 +1,207 @@
|
|
1
|
+
"use strict";
|
2
|
+
|
3
|
+
exports.__esModule = true;
|
4
|
+
exports.default = void 0;
|
5
|
+
|
6
|
+
var _react = _interopRequireWildcard(require("react"));
|
7
|
+
|
8
|
+
var _dateFns = require("date-fns");
|
9
|
+
|
10
|
+
var _styledComponents = _interopRequireDefault(require("styled-components"));
|
11
|
+
|
12
|
+
var _Input = _interopRequireDefault(require("../../Input"));
|
13
|
+
|
14
|
+
var _ErrorMessage = _interopRequireDefault(require("../../ErrorMessage"));
|
15
|
+
|
16
|
+
var _propTypes = require("prop-types");
|
17
|
+
|
18
|
+
var _colors = _interopRequireDefault(require("../../../constants/colors"));
|
19
|
+
|
20
|
+
var _spacing = _interopRequireDefault(require("../../../constants/spacing"));
|
21
|
+
|
22
|
+
var _Calendar = _interopRequireDefault(require("../../Icons/Calendar"));
|
23
|
+
|
24
|
+
var _reactGa = require("react-ga");
|
25
|
+
|
26
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
27
|
+
|
28
|
+
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
|
29
|
+
|
30
|
+
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
|
31
|
+
|
32
|
+
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
|
33
|
+
|
34
|
+
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
|
35
|
+
|
36
|
+
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
37
|
+
|
38
|
+
const HiddenBox = (0, _styledComponents.default)(_Input.default.withComponent('div'))`
|
39
|
+
display: flex;
|
40
|
+
align-items: center;
|
41
|
+
${props => props.error && `
|
42
|
+
margin-bottom: 0px !important;
|
43
|
+
border-bottom-left-radius: 0px;
|
44
|
+
border-bottom-right-radius: 0px;
|
45
|
+
border-color: ${_colors.default.borderError} !important;
|
46
|
+
`};
|
47
|
+
input {
|
48
|
+
height: 2.5rem !important;
|
49
|
+
min-height: 2.5rem !important;
|
50
|
+
border: 0 !important;
|
51
|
+
margin: 0 !important;
|
52
|
+
padding: 0 !important;
|
53
|
+
box-shadow: none !important;
|
54
|
+
}
|
55
|
+
`;
|
56
|
+
HiddenBox.displayName = 'HiddenBox';
|
57
|
+
const OpenCalendarIcon = (0, _styledComponents.default)(_Calendar.default)`
|
58
|
+
cursor: ${props => props.disabled ? 'not-allowed' : 'pointer'};
|
59
|
+
margin-left: ${_spacing.default.s1};
|
60
|
+
color: ${_colors.default.primary};
|
61
|
+
`;
|
62
|
+
OpenCalendarIcon.displayName = 'OpenCalendarIcon';
|
63
|
+
const formats = {
|
64
|
+
CA: {
|
65
|
+
placeholderDate: 'YY/MM/DD',
|
66
|
+
dateFormat: 'yy/MM/dd'
|
67
|
+
},
|
68
|
+
default: {
|
69
|
+
placeholderDate: 'DD/MM/YY',
|
70
|
+
dateFormat: 'dd/MM/yy'
|
71
|
+
}
|
72
|
+
};
|
73
|
+
|
74
|
+
const makeInnerDate = (date, dateFormat) => {
|
75
|
+
if (date.from) {
|
76
|
+
return (0, _dateFns.format)(date.from, dateFormat);
|
77
|
+
}
|
78
|
+
|
79
|
+
return '';
|
80
|
+
};
|
81
|
+
|
82
|
+
const NewDisplay = _ref => {
|
83
|
+
let {
|
84
|
+
placeholder,
|
85
|
+
datesToDisplay,
|
86
|
+
range,
|
87
|
+
allowClear,
|
88
|
+
yearRange,
|
89
|
+
onSelectedDate,
|
90
|
+
locale,
|
91
|
+
clearValue,
|
92
|
+
disabled,
|
93
|
+
error,
|
94
|
+
toggleDayPicker,
|
95
|
+
GA: {
|
96
|
+
category,
|
97
|
+
actionId
|
98
|
+
},
|
99
|
+
recordValue,
|
100
|
+
dayPickerOpen
|
101
|
+
} = _ref;
|
102
|
+
const {
|
103
|
+
placeholderDate,
|
104
|
+
dateFormat
|
105
|
+
} = formats[locale] || formats.default;
|
106
|
+
const [hasFocus, setHasFocus] = (0, _react.useState)(false);
|
107
|
+
const [hasError, setHasError] = (0, _react.useState)(false);
|
108
|
+
const [innerDate, setInnerDate] = (0, _react.useState)(makeInnerDate(range, dateFormat));
|
109
|
+
return _react.default.createElement(_react.default.Fragment, null, _react.default.createElement(HiddenBox, {
|
110
|
+
error: error,
|
111
|
+
"data-testid": "input-selector"
|
112
|
+
}, _react.default.createElement(_Input.default, {
|
113
|
+
"data-testid": "new input",
|
114
|
+
placeholder: hasFocus ? placeholderDate : placeholder,
|
115
|
+
value: hasFocus ? innerDate : datesToDisplay,
|
116
|
+
fullWidth: true,
|
117
|
+
disabled: disabled,
|
118
|
+
onFocus: () => {
|
119
|
+
setInnerDate(makeInnerDate(range, dateFormat));
|
120
|
+
setHasFocus(true);
|
121
|
+
setHasError(false);
|
122
|
+
},
|
123
|
+
onBlur: _ref2 => {
|
124
|
+
let {
|
125
|
+
target: {
|
126
|
+
value
|
127
|
+
}
|
128
|
+
} = _ref2;
|
129
|
+
setHasFocus(false);
|
130
|
+
if (dayPickerOpen) toggleDayPicker();
|
131
|
+
|
132
|
+
if (!value && (allowClear || !range.from)) {
|
133
|
+
return range.from ? clearValue() : null;
|
134
|
+
}
|
135
|
+
|
136
|
+
const newDate = {
|
137
|
+
from: (0, _dateFns.parse)(value, dateFormat, new Date()),
|
138
|
+
to: (0, _dateFns.parse)(value, dateFormat, new Date())
|
139
|
+
};
|
140
|
+
const fromYear = (0, _dateFns.getYear)(newDate.from);
|
141
|
+
const toYear = (0, _dateFns.getYear)(newDate.to);
|
142
|
+
|
143
|
+
if (!(0, _dateFns.isValid)(newDate.from) || !(0, _dateFns.isValid)(newDate.to) || fromYear < yearRange.min || toYear > yearRange.max) {
|
144
|
+
setHasError(!((0, _dateFns.isValid)(newDate.from) && (0, _dateFns.isValid)(newDate.to)) ? 'invalid' : 'range');
|
145
|
+
return range.from ? onSelectedDate(allowClear ? '' : range) : null;
|
146
|
+
}
|
147
|
+
|
148
|
+
if (category) {
|
149
|
+
const label = `${(0, _dateFns.format)(newDate.from, 'EEE dd MMM')} ${(0, _dateFns.format)(newDate.from, 'yyyy')}`;
|
150
|
+
(0, _reactGa.event)(_objectSpread({
|
151
|
+
category,
|
152
|
+
action: `${actionId} - Set via Typing`
|
153
|
+
}, recordValue ? {
|
154
|
+
label
|
155
|
+
} : {}));
|
156
|
+
}
|
157
|
+
|
158
|
+
return onSelectedDate(newDate);
|
159
|
+
},
|
160
|
+
onChange: _ref3 => {
|
161
|
+
let {
|
162
|
+
target: {
|
163
|
+
value
|
164
|
+
}
|
165
|
+
} = _ref3;
|
166
|
+
setInnerDate(value.replace(/.*\/$/g, text => value.length < innerDate.length ? value.slice(0, -1) : text).replace(/(\d\d)(\d)/g, '$1/$2').replace(/^(\d\/)$/g, '0$1').replace(/\d\d$/g, text => value.length > innerDate.length && value.length < 7 ? `${text}/` : text).replace(/^(\d\d\/)(\d\/)$/g, '$10$2').replace(/[^\d/]/g, '').replace(/^(.{0,8})(.*)$/g, '$1').replace(/\/\/$/g, '/'));
|
167
|
+
}
|
168
|
+
}), _react.default.createElement(OpenCalendarIcon, {
|
169
|
+
"data-testid": "calendarIcon",
|
170
|
+
size: 22,
|
171
|
+
disabled: disabled,
|
172
|
+
onClick: () => {
|
173
|
+
setHasError(false);
|
174
|
+
toggleDayPicker();
|
175
|
+
}
|
176
|
+
})), hasError && _react.default.createElement(_ErrorMessage.default, {
|
177
|
+
showIcon: false
|
178
|
+
}, hasError === 'invalid' ? 'Date is invalid' : `Year is outside of range. Must be between ${yearRange.min} - ${yearRange.max}`));
|
179
|
+
};
|
180
|
+
|
181
|
+
NewDisplay.propTypes = {
|
182
|
+
placeholder: _propTypes.string.isRequired,
|
183
|
+
datesToDisplay: _propTypes.string.isRequired,
|
184
|
+
range: (0, _propTypes.shape)({
|
185
|
+
from: (0, _propTypes.instanceOf)(Date),
|
186
|
+
to: (0, _propTypes.instanceOf)(Date)
|
187
|
+
}).isRequired,
|
188
|
+
allowClear: _propTypes.bool.isRequired,
|
189
|
+
yearRange: (0, _propTypes.shape)({
|
190
|
+
min: _propTypes.number,
|
191
|
+
max: _propTypes.number
|
192
|
+
}).isRequired,
|
193
|
+
onSelectedDate: _propTypes.func.isRequired,
|
194
|
+
locale: _propTypes.string.isRequired,
|
195
|
+
clearValue: _propTypes.func.isRequired,
|
196
|
+
disabled: _propTypes.bool.isRequired,
|
197
|
+
error: _propTypes.bool.isRequired,
|
198
|
+
toggleDayPicker: _propTypes.func.isRequired,
|
199
|
+
GA: (0, _propTypes.shape)({
|
200
|
+
category: _propTypes.string,
|
201
|
+
actionId: _propTypes.string
|
202
|
+
}).isRequired,
|
203
|
+
recordValue: _propTypes.bool.isRequired,
|
204
|
+
dayPickerOpen: _propTypes.bool.isRequired
|
205
|
+
};
|
206
|
+
var _default = NewDisplay;
|
207
|
+
exports.default = _default;
|
@@ -33,6 +33,8 @@ var _breakpoints = _interopRequireDefault(require("../../constants/breakpoints")
|
|
33
33
|
|
34
34
|
var _Calendar = _interopRequireDefault(require("../Icons/Calendar"));
|
35
35
|
|
36
|
+
var _NewDayPicker = _interopRequireDefault(require("./NewDayPicker"));
|
37
|
+
|
36
38
|
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
|
37
39
|
|
38
40
|
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
|
@@ -271,15 +273,17 @@ class DayPicker extends _react.default.Component {
|
|
271
273
|
closeButton,
|
272
274
|
panelAbsoluteOnDesktop,
|
273
275
|
onSelectedDate,
|
274
|
-
isDateSelect
|
276
|
+
isDateSelect,
|
277
|
+
typeable
|
275
278
|
} = _this$props,
|
276
|
-
rest = _objectWithoutProperties(_this$props, ["allowClear", "placeholder", "dateRange", "yearRange", "yearsToDisplay", "style", "error", "disabled", "shortcuts", "disabledDays", "GA", "recordValue", "legacyInputStyle", "closeButton", "panelAbsoluteOnDesktop", "onSelectedDate", "isDateSelect"]);
|
279
|
+
rest = _objectWithoutProperties(_this$props, ["allowClear", "placeholder", "dateRange", "yearRange", "yearsToDisplay", "style", "error", "disabled", "shortcuts", "disabledDays", "GA", "recordValue", "legacyInputStyle", "closeButton", "panelAbsoluteOnDesktop", "onSelectedDate", "isDateSelect", "typeable"]);
|
277
280
|
|
278
281
|
const {
|
279
282
|
dayPickerOpen
|
280
283
|
} = this.state;
|
281
284
|
const {
|
282
|
-
range
|
285
|
+
range,
|
286
|
+
locale
|
283
287
|
} = this.props;
|
284
288
|
const fromDate = range.from ? (0, _dateFns.format)(range.from, this.getDisplayFormat()) : '';
|
285
289
|
const toDate = range.to ? (0, _dateFns.format)(range.to, friendlyShortWithYear) : '';
|
@@ -290,7 +294,7 @@ class DayPicker extends _react.default.Component {
|
|
290
294
|
} = style,
|
291
295
|
otherStyles = _objectWithoutProperties(style, ["width"]);
|
292
296
|
|
293
|
-
return _react.default.createElement(DayPickerInputContainer, rest, _react.default.createElement(InputBox, {
|
297
|
+
return _react.default.createElement(DayPickerInputContainer, rest, !typeable || dateRange ? _react.default.createElement(InputBox, {
|
294
298
|
onClick: this.toggleDayPicker,
|
295
299
|
error: error,
|
296
300
|
disabled: disabled,
|
@@ -299,7 +303,22 @@ class DayPicker extends _react.default.Component {
|
|
299
303
|
}, datesToDisplay ? _react.default.createElement("span", null, datesToDisplay) : _react.default.createElement(Placeholder, null, placeholder), _react.default.createElement(OpenCalendarIcon, {
|
300
304
|
size: 22,
|
301
305
|
disabled: disabled
|
302
|
-
}))
|
306
|
+
})) : _react.default.createElement(_NewDayPicker.default, {
|
307
|
+
placeholder: placeholder,
|
308
|
+
datesToDisplay: datesToDisplay,
|
309
|
+
range: range,
|
310
|
+
allowClear: allowClear,
|
311
|
+
yearRange: yearRange,
|
312
|
+
onSelectedDate: onSelectedDate,
|
313
|
+
locale: locale,
|
314
|
+
clearValue: this.clearValue,
|
315
|
+
disabled: disabled,
|
316
|
+
error: error,
|
317
|
+
toggleDayPicker: this.toggleDayPicker,
|
318
|
+
GA: GA,
|
319
|
+
recordValue: recordValue,
|
320
|
+
dayPickerOpen: dayPickerOpen
|
321
|
+
}), dayPickerOpen && _react.default.createElement(DayRangeContainer, {
|
303
322
|
panelAbsoluteOnDesktop: panelAbsoluteOnDesktop,
|
304
323
|
style: otherStyles,
|
305
324
|
width: width,
|
@@ -360,7 +379,9 @@ DayPicker.propTypes = {
|
|
360
379
|
legacyInputStyle: _propTypes.bool,
|
361
380
|
closeButton: _propTypes.bool,
|
362
381
|
panelAbsoluteOnDesktop: _propTypes.bool,
|
363
|
-
isDateSelect: _propTypes.bool
|
382
|
+
isDateSelect: _propTypes.bool,
|
383
|
+
typeable: _propTypes.bool,
|
384
|
+
locale: _propTypes.string
|
364
385
|
};
|
365
386
|
DayPicker.defaultProps = {
|
366
387
|
recordValue: false,
|
@@ -385,7 +406,9 @@ DayPicker.defaultProps = {
|
|
385
406
|
legacyInputStyle: false,
|
386
407
|
closeButton: false,
|
387
408
|
panelAbsoluteOnDesktop: true,
|
388
|
-
isDateSelect: false
|
409
|
+
isDateSelect: false,
|
410
|
+
typeable: false,
|
411
|
+
locale: 'GB'
|
389
412
|
};
|
390
413
|
|
391
414
|
var _default = (0, _reactOnclickoutside.default)(DayPicker);
|
@@ -207,7 +207,7 @@ class DayPickerPanel extends _react.default.Component {
|
|
207
207
|
to
|
208
208
|
}
|
209
209
|
} = this.state;
|
210
|
-
const actionEvent = selectedDay ? 'Set' : 'Apply';
|
210
|
+
const actionEvent = selectedDay ? 'Set via Date Picker' : 'Apply';
|
211
211
|
|
212
212
|
if (category) {
|
213
213
|
const label = dateRange ? `${(0, _dateFns.format)(from, friendlyShort)} - ${(0, _dateFns.format)(to || from, friendlyShort)} ${(0, _dateFns.format)(to || from, 'yyyy')}` : `${(0, _dateFns.format)(from, friendlyShort)} ${(0, _dateFns.format)(from, 'yyyy')}`;
|
@@ -133,6 +133,7 @@ const TimePicker = _ref => {
|
|
133
133
|
const valueAsString = (0, _durationToTimeString.default)(value);
|
134
134
|
return _react.default.createElement(_react.default.Fragment, null, _react.default.createElement(MobileTimePicker, {
|
135
135
|
type: "time",
|
136
|
+
"data-testid": "mobileTimePicker",
|
136
137
|
onChange: e => {
|
137
138
|
const timeString = e.target.value;
|
138
139
|
|
package/dist/setupTests.js
CHANGED
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
var _enzyme = require("enzyme");
|
4
4
|
|
5
|
+
var _react = require("@testing-library/react");
|
6
|
+
|
5
7
|
var _enzymeAdapterReact = _interopRequireDefault(require("enzyme-adapter-react-16"));
|
6
8
|
|
7
9
|
var _reactGa = _interopRequireDefault(require("react-ga"));
|
@@ -53,4 +55,7 @@ afterEach(() => {
|
|
53
55
|
});
|
54
56
|
(0, _enzyme.configure)({
|
55
57
|
adapter: new _enzymeAdapterReact.default()
|
58
|
+
});
|
59
|
+
(0, _react.configure)({
|
60
|
+
defaultHidden: true
|
56
61
|
});
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "bright-components",
|
3
|
-
"version": "10.
|
3
|
+
"version": "10.2.0",
|
4
4
|
"private": false,
|
5
5
|
"main": "./dist",
|
6
6
|
"repository": {
|
@@ -73,10 +73,11 @@
|
|
73
73
|
"@babel/preset-env": "^7.16.5",
|
74
74
|
"@babel/preset-react": "^7.7.4",
|
75
75
|
"@svgr/webpack": "4.3.2",
|
76
|
+
"@testing-library/dom": "^8.11.2",
|
76
77
|
"@testing-library/jest-dom": "^4.1.0",
|
77
78
|
"@testing-library/react": "^9.1.4",
|
78
79
|
"@testing-library/react-hooks": "^2.0.1",
|
79
|
-
"@testing-library/user-event": "^
|
80
|
+
"@testing-library/user-event": "^12.8.3",
|
80
81
|
"babel-eslint": "10.0.2",
|
81
82
|
"babel-jest": "^24.9.0",
|
82
83
|
"babel-loader": "8.0.6",
|
@@ -0,0 +1,208 @@
|
|
1
|
+
import React, { useState } from 'react';
|
2
|
+
import { format, isValid, parse, getYear } from 'date-fns';
|
3
|
+
import styled from 'styled-components';
|
4
|
+
import Input from 'components/Input';
|
5
|
+
import ErrorMessage from 'components/ErrorMessage';
|
6
|
+
import { string, func, bool, shape, instanceOf, number } from 'prop-types';
|
7
|
+
import colors from 'constants/colors';
|
8
|
+
import spacing from 'constants/spacing';
|
9
|
+
import CalendarIcon from 'components/Icons/Calendar/';
|
10
|
+
import { event } from 'react-ga';
|
11
|
+
|
12
|
+
const HiddenBox = styled(Input.withComponent('div'))`
|
13
|
+
display: flex;
|
14
|
+
align-items: center;
|
15
|
+
${props =>
|
16
|
+
props.error &&
|
17
|
+
`
|
18
|
+
margin-bottom: 0px !important;
|
19
|
+
border-bottom-left-radius: 0px;
|
20
|
+
border-bottom-right-radius: 0px;
|
21
|
+
border-color: ${colors.borderError} !important;
|
22
|
+
`};
|
23
|
+
input {
|
24
|
+
height: 2.5rem !important;
|
25
|
+
min-height: 2.5rem !important;
|
26
|
+
border: 0 !important;
|
27
|
+
margin: 0 !important;
|
28
|
+
padding: 0 !important;
|
29
|
+
box-shadow: none !important;
|
30
|
+
}
|
31
|
+
`;
|
32
|
+
HiddenBox.displayName = 'HiddenBox';
|
33
|
+
|
34
|
+
const OpenCalendarIcon = styled(CalendarIcon)`
|
35
|
+
cursor: ${props => (props.disabled ? 'not-allowed' : 'pointer')};
|
36
|
+
margin-left: ${spacing.s1};
|
37
|
+
color: ${colors.primary};
|
38
|
+
`;
|
39
|
+
OpenCalendarIcon.displayName = 'OpenCalendarIcon';
|
40
|
+
|
41
|
+
const formats = {
|
42
|
+
CA: { placeholderDate: 'YY/MM/DD', dateFormat: 'yy/MM/dd' },
|
43
|
+
default: { placeholderDate: 'DD/MM/YY', dateFormat: 'dd/MM/yy' }
|
44
|
+
};
|
45
|
+
|
46
|
+
const makeInnerDate = (date, dateFormat) => {
|
47
|
+
if (date.from) {
|
48
|
+
return format(date.from, dateFormat);
|
49
|
+
}
|
50
|
+
return '';
|
51
|
+
};
|
52
|
+
|
53
|
+
const NewDisplay = ({
|
54
|
+
placeholder,
|
55
|
+
datesToDisplay,
|
56
|
+
range,
|
57
|
+
allowClear,
|
58
|
+
yearRange,
|
59
|
+
onSelectedDate,
|
60
|
+
locale,
|
61
|
+
clearValue,
|
62
|
+
disabled,
|
63
|
+
error,
|
64
|
+
toggleDayPicker,
|
65
|
+
GA: { category, actionId },
|
66
|
+
recordValue,
|
67
|
+
dayPickerOpen
|
68
|
+
}) => {
|
69
|
+
const { placeholderDate, dateFormat } = formats[locale] || formats.default;
|
70
|
+
|
71
|
+
const [hasFocus, setHasFocus] = useState(false);
|
72
|
+
const [hasError, setHasError] = useState(false);
|
73
|
+
const [innerDate, setInnerDate] = useState(
|
74
|
+
makeInnerDate(range, dateFormat)
|
75
|
+
);
|
76
|
+
|
77
|
+
return (
|
78
|
+
<>
|
79
|
+
<HiddenBox error={error} data-testid="input-selector">
|
80
|
+
<Input
|
81
|
+
data-testid="new input"
|
82
|
+
placeholder={hasFocus ? placeholderDate : placeholder}
|
83
|
+
value={hasFocus ? innerDate : datesToDisplay}
|
84
|
+
fullWidth
|
85
|
+
disabled={disabled}
|
86
|
+
onFocus={() => {
|
87
|
+
setInnerDate(makeInnerDate(range, dateFormat));
|
88
|
+
setHasFocus(true);
|
89
|
+
setHasError(false);
|
90
|
+
}}
|
91
|
+
onBlur={({ target: { value } }) => {
|
92
|
+
setHasFocus(false);
|
93
|
+
if (dayPickerOpen) toggleDayPicker();
|
94
|
+
if (!value && (allowClear || !range.from)) {
|
95
|
+
return range.from ? clearValue() : null;
|
96
|
+
}
|
97
|
+
|
98
|
+
const newDate = {
|
99
|
+
from: parse(value, dateFormat, new Date()),
|
100
|
+
to: parse(value, dateFormat, new Date())
|
101
|
+
};
|
102
|
+
|
103
|
+
const fromYear = getYear(newDate.from);
|
104
|
+
const toYear = getYear(newDate.to);
|
105
|
+
|
106
|
+
if (
|
107
|
+
!isValid(newDate.from) ||
|
108
|
+
!isValid(newDate.to) ||
|
109
|
+
fromYear < yearRange.min ||
|
110
|
+
toYear > yearRange.max
|
111
|
+
) {
|
112
|
+
setHasError(
|
113
|
+
!(isValid(newDate.from) && isValid(newDate.to))
|
114
|
+
? 'invalid'
|
115
|
+
: 'range'
|
116
|
+
);
|
117
|
+
|
118
|
+
return range.from
|
119
|
+
? onSelectedDate(allowClear ? '' : range)
|
120
|
+
: null;
|
121
|
+
}
|
122
|
+
|
123
|
+
if (category) {
|
124
|
+
const label = `${format(
|
125
|
+
newDate.from,
|
126
|
+
'EEE dd MMM'
|
127
|
+
)} ${format(newDate.from, 'yyyy')}`;
|
128
|
+
|
129
|
+
event({
|
130
|
+
category,
|
131
|
+
action: `${actionId} - Set via Typing`,
|
132
|
+
...(recordValue ? { label } : {})
|
133
|
+
});
|
134
|
+
}
|
135
|
+
|
136
|
+
return onSelectedDate(newDate);
|
137
|
+
}}
|
138
|
+
onChange={({ target: { value } }) => {
|
139
|
+
setInnerDate(
|
140
|
+
value
|
141
|
+
.replace(/.*\/$/g, text =>
|
142
|
+
value.length < innerDate.length
|
143
|
+
? value.slice(0, -1)
|
144
|
+
: text
|
145
|
+
)
|
146
|
+
.replace(/(\d\d)(\d)/g, '$1/$2')
|
147
|
+
.replace(/^(\d\/)$/g, '0$1')
|
148
|
+
.replace(/\d\d$/g, text =>
|
149
|
+
value.length > innerDate.length &&
|
150
|
+
value.length < 7
|
151
|
+
? `${text}/`
|
152
|
+
: text
|
153
|
+
)
|
154
|
+
.replace(/^(\d\d\/)(\d\/)$/g, '$10$2')
|
155
|
+
.replace(/[^\d/]/g, '')
|
156
|
+
.replace(/^(.{0,8})(.*)$/g, '$1')
|
157
|
+
.replace(/\/\/$/g, '/')
|
158
|
+
);
|
159
|
+
}}
|
160
|
+
/>
|
161
|
+
<OpenCalendarIcon
|
162
|
+
data-testid="calendarIcon"
|
163
|
+
size={22}
|
164
|
+
disabled={disabled}
|
165
|
+
onClick={() => {
|
166
|
+
setHasError(false);
|
167
|
+
toggleDayPicker();
|
168
|
+
}}
|
169
|
+
/>
|
170
|
+
</HiddenBox>
|
171
|
+
{hasError && (
|
172
|
+
<ErrorMessage showIcon={false}>
|
173
|
+
{hasError === 'invalid'
|
174
|
+
? 'Date is invalid'
|
175
|
+
: `Year is outside of range. Must be between ${yearRange.min} - ${yearRange.max}`}
|
176
|
+
</ErrorMessage>
|
177
|
+
)}
|
178
|
+
</>
|
179
|
+
);
|
180
|
+
};
|
181
|
+
|
182
|
+
NewDisplay.propTypes = {
|
183
|
+
placeholder: string.isRequired,
|
184
|
+
datesToDisplay: string.isRequired,
|
185
|
+
range: shape({
|
186
|
+
from: instanceOf(Date),
|
187
|
+
to: instanceOf(Date)
|
188
|
+
}).isRequired,
|
189
|
+
allowClear: bool.isRequired,
|
190
|
+
yearRange: shape({
|
191
|
+
min: number,
|
192
|
+
max: number
|
193
|
+
}).isRequired,
|
194
|
+
onSelectedDate: func.isRequired,
|
195
|
+
locale: string.isRequired,
|
196
|
+
clearValue: func.isRequired,
|
197
|
+
disabled: bool.isRequired,
|
198
|
+
error: bool.isRequired,
|
199
|
+
toggleDayPicker: func.isRequired,
|
200
|
+
GA: shape({
|
201
|
+
category: string,
|
202
|
+
actionId: string
|
203
|
+
}).isRequired,
|
204
|
+
recordValue: bool.isRequired,
|
205
|
+
dayPickerOpen: bool.isRequired
|
206
|
+
};
|
207
|
+
|
208
|
+
export default NewDisplay;
|
@@ -231,3 +231,20 @@ initialState={dates: {from: new Date(2018, 3, 1), to: undefined}};
|
|
231
231
|
isDateSelect={true}
|
232
232
|
/>
|
233
233
|
```
|
234
|
+
Day Picker - typeable
|
235
|
+
```js
|
236
|
+
initialState={dates: {from: new Date(2018, 3, 1), to: undefined}};
|
237
|
+
<DayPicker
|
238
|
+
range={state.dates}
|
239
|
+
yearRange={{ min: 2000, max: 2030}}
|
240
|
+
style={{
|
241
|
+
width: '70%'
|
242
|
+
}}
|
243
|
+
GA={{
|
244
|
+
category: 'Test',
|
245
|
+
actionId: 'Test'
|
246
|
+
}}
|
247
|
+
onSelectedDate={dates=>setState({dates})}
|
248
|
+
typeable
|
249
|
+
/>
|
250
|
+
```
|
@@ -26,6 +26,8 @@ import breakpoints from 'constants/breakpoints';
|
|
26
26
|
|
27
27
|
import CalendarIcon from 'components/Icons/Calendar/';
|
28
28
|
|
29
|
+
import NewDayPicker from './NewDayPicker';
|
30
|
+
|
29
31
|
const friendlyShort = 'EEE dd MMM';
|
30
32
|
const friendlyShortWithYear = 'EEE dd MMM yyyy';
|
31
33
|
|
@@ -232,11 +234,12 @@ class DayPicker extends React.Component {
|
|
232
234
|
panelAbsoluteOnDesktop,
|
233
235
|
onSelectedDate,
|
234
236
|
isDateSelect,
|
237
|
+
typeable,
|
235
238
|
...rest
|
236
239
|
} = this.props;
|
237
240
|
|
238
241
|
const { dayPickerOpen } = this.state;
|
239
|
-
const { range } = this.props;
|
242
|
+
const { range, locale } = this.props;
|
240
243
|
|
241
244
|
const fromDate = range.from
|
242
245
|
? format(range.from, this.getDisplayFormat())
|
@@ -250,20 +253,39 @@ class DayPicker extends React.Component {
|
|
250
253
|
|
251
254
|
return (
|
252
255
|
<DayPickerInputContainer {...rest}>
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
256
|
+
{!typeable || dateRange ? (
|
257
|
+
<InputBox
|
258
|
+
onClick={this.toggleDayPicker}
|
259
|
+
error={error}
|
260
|
+
disabled={disabled}
|
261
|
+
legacyInputStyle={legacyInputStyle}
|
262
|
+
data-testid="input-selector"
|
263
|
+
>
|
264
|
+
{datesToDisplay ? (
|
265
|
+
<span>{datesToDisplay}</span>
|
266
|
+
) : (
|
267
|
+
<Placeholder>{placeholder}</Placeholder>
|
268
|
+
)}
|
269
|
+
<OpenCalendarIcon size={22} disabled={disabled} />
|
270
|
+
</InputBox>
|
271
|
+
) : (
|
272
|
+
<NewDayPicker
|
273
|
+
placeholder={placeholder}
|
274
|
+
datesToDisplay={datesToDisplay}
|
275
|
+
range={range}
|
276
|
+
allowClear={allowClear}
|
277
|
+
yearRange={yearRange}
|
278
|
+
onSelectedDate={onSelectedDate}
|
279
|
+
locale={locale}
|
280
|
+
clearValue={this.clearValue}
|
281
|
+
disabled={disabled}
|
282
|
+
error={error}
|
283
|
+
toggleDayPicker={this.toggleDayPicker}
|
284
|
+
GA={GA}
|
285
|
+
recordValue={recordValue}
|
286
|
+
dayPickerOpen={dayPickerOpen}
|
287
|
+
/>
|
288
|
+
)}
|
267
289
|
|
268
290
|
{dayPickerOpen && (
|
269
291
|
<DayRangeContainer
|
@@ -330,7 +352,9 @@ DayPicker.propTypes = {
|
|
330
352
|
legacyInputStyle: bool,
|
331
353
|
closeButton: bool,
|
332
354
|
panelAbsoluteOnDesktop: bool,
|
333
|
-
isDateSelect: bool
|
355
|
+
isDateSelect: bool,
|
356
|
+
typeable: bool,
|
357
|
+
locale: string
|
334
358
|
};
|
335
359
|
|
336
360
|
DayPicker.defaultProps = {
|
@@ -356,7 +380,9 @@ DayPicker.defaultProps = {
|
|
356
380
|
legacyInputStyle: false,
|
357
381
|
closeButton: false,
|
358
382
|
panelAbsoluteOnDesktop: true,
|
359
|
-
isDateSelect: false
|
383
|
+
isDateSelect: false,
|
384
|
+
typeable: false,
|
385
|
+
locale: 'GB'
|
360
386
|
};
|
361
387
|
|
362
388
|
export { DayPicker as Unwrapped };
|
@@ -1,5 +1,6 @@
|
|
1
1
|
import React from 'react';
|
2
|
-
import { render, fireEvent } from '@testing-library/react';
|
2
|
+
import { render, fireEvent, wait } from '@testing-library/react';
|
3
|
+
import userEvent from '@testing-library/user-event';
|
3
4
|
import ReactGA from 'react-ga';
|
4
5
|
import DayPicker from '.';
|
5
6
|
|
@@ -406,4 +407,299 @@ describe('<DayPicker />', () => {
|
|
406
407
|
expect(ReactGA.event).toHaveBeenCalledTimes(1);
|
407
408
|
});
|
408
409
|
});
|
410
|
+
|
411
|
+
describe('Typing date', () => {
|
412
|
+
it('should set the correct date when typed in', async () => {
|
413
|
+
wrapper.rerender(
|
414
|
+
<DayPicker
|
415
|
+
{...props}
|
416
|
+
typeable
|
417
|
+
dateRange={false}
|
418
|
+
yearRange={{ min: 2000, max: 2100 }}
|
419
|
+
GA={{
|
420
|
+
category: undefined,
|
421
|
+
actionId: undefined
|
422
|
+
}}
|
423
|
+
/>
|
424
|
+
);
|
425
|
+
const inputSelect = wrapper.getByTestId('new input');
|
426
|
+
|
427
|
+
fireEvent.focus(inputSelect);
|
428
|
+
userEvent.type(inputSelect, `010203`);
|
429
|
+
await wait(() => expect(inputSelect.value).toEqual('01/02/03'));
|
430
|
+
fireEvent.blur(inputSelect);
|
431
|
+
|
432
|
+
expect(onMockSelectedDate).toHaveBeenLastCalledWith({
|
433
|
+
from: new Date('2003-02-01T00:00:00.000Z'),
|
434
|
+
to: new Date('2003-02-01T00:00:00.000Z')
|
435
|
+
});
|
436
|
+
});
|
437
|
+
|
438
|
+
it('should set the correct date when typed in - CA', () => {
|
439
|
+
wrapper.rerender(
|
440
|
+
<DayPicker
|
441
|
+
{...props}
|
442
|
+
typeable
|
443
|
+
dateRange={false}
|
444
|
+
yearRange={{ min: 2000, max: 2100 }}
|
445
|
+
locale="CA"
|
446
|
+
/>
|
447
|
+
);
|
448
|
+
const inputSelect = wrapper.getByTestId('new input');
|
449
|
+
|
450
|
+
fireEvent.focus(inputSelect);
|
451
|
+
userEvent.type(inputSelect, '010203');
|
452
|
+
fireEvent.blur(inputSelect);
|
453
|
+
|
454
|
+
expect(onMockSelectedDate).toHaveBeenLastCalledWith({
|
455
|
+
from: new Date('2001-02-03T00:00:00.000Z'),
|
456
|
+
to: new Date('2001-02-03T00:00:00.000Z')
|
457
|
+
});
|
458
|
+
});
|
459
|
+
|
460
|
+
it('should show the correct date when date pre set', () => {
|
461
|
+
wrapper.rerender(
|
462
|
+
<DayPicker
|
463
|
+
{...props}
|
464
|
+
typeable
|
465
|
+
dateRange={false}
|
466
|
+
yearRange={{ min: 2000, max: 2100 }}
|
467
|
+
range={{
|
468
|
+
from: new Date('2019-08-01'),
|
469
|
+
to: undefined
|
470
|
+
}}
|
471
|
+
/>
|
472
|
+
);
|
473
|
+
const inputSelect = wrapper.getByTestId('new input');
|
474
|
+
|
475
|
+
fireEvent.focus(inputSelect);
|
476
|
+
|
477
|
+
expect(inputSelect.value).toEqual('01/08/19');
|
478
|
+
});
|
479
|
+
|
480
|
+
it('should show the correct date when date pre set - CA', () => {
|
481
|
+
wrapper.rerender(
|
482
|
+
<DayPicker
|
483
|
+
{...props}
|
484
|
+
typeable
|
485
|
+
dateRange={false}
|
486
|
+
yearRange={{ min: 2000, max: 2100 }}
|
487
|
+
range={{
|
488
|
+
from: new Date('2019-08-01'),
|
489
|
+
to: undefined
|
490
|
+
}}
|
491
|
+
locale="CA"
|
492
|
+
/>
|
493
|
+
);
|
494
|
+
const inputSelect = wrapper.getByTestId('new input');
|
495
|
+
|
496
|
+
fireEvent.focus(inputSelect);
|
497
|
+
|
498
|
+
expect(inputSelect.value).toEqual('19/08/01');
|
499
|
+
});
|
500
|
+
|
501
|
+
it('should allow clearing date', () => {
|
502
|
+
wrapper.rerender(
|
503
|
+
<DayPicker
|
504
|
+
{...props}
|
505
|
+
typeable
|
506
|
+
dateRange={false}
|
507
|
+
yearRange={{ min: 2000, max: 2100 }}
|
508
|
+
range={{
|
509
|
+
from: new Date('2019-08-01'),
|
510
|
+
to: undefined
|
511
|
+
}}
|
512
|
+
/>
|
513
|
+
);
|
514
|
+
const inputSelect = wrapper.getByTestId('new input');
|
515
|
+
|
516
|
+
fireEvent.focus(inputSelect);
|
517
|
+
userEvent.type(inputSelect, '{selectall}{backspace}');
|
518
|
+
fireEvent.blur(inputSelect);
|
519
|
+
|
520
|
+
expect(onMockSelectedDate).toHaveBeenLastCalledWith({
|
521
|
+
from: undefined,
|
522
|
+
to: undefined
|
523
|
+
});
|
524
|
+
});
|
525
|
+
|
526
|
+
it('shouldnt save invalid dates', () => {
|
527
|
+
wrapper.rerender(
|
528
|
+
<DayPicker
|
529
|
+
{...props}
|
530
|
+
typeable
|
531
|
+
dateRange={false}
|
532
|
+
yearRange={{ min: 2000, max: 2100 }}
|
533
|
+
range={{
|
534
|
+
from: new Date('2019-08-01'),
|
535
|
+
to: undefined
|
536
|
+
}}
|
537
|
+
/>
|
538
|
+
);
|
539
|
+
const inputSelect = wrapper.getByTestId('new input');
|
540
|
+
|
541
|
+
fireEvent.focus(inputSelect);
|
542
|
+
fireEvent.blur(inputSelect);
|
543
|
+
fireEvent.focus(inputSelect);
|
544
|
+
userEvent.type(inputSelect, '{selectall}{backspace}0101');
|
545
|
+
fireEvent.blur(inputSelect);
|
546
|
+
|
547
|
+
expect(onMockSelectedDate).toHaveBeenLastCalledWith('');
|
548
|
+
expect(wrapper.getByText('Date is invalid')).toBeInTheDocument();
|
549
|
+
});
|
550
|
+
|
551
|
+
it('shouldnt save dates outside the range', () => {
|
552
|
+
wrapper.rerender(
|
553
|
+
<DayPicker
|
554
|
+
{...props}
|
555
|
+
typeable
|
556
|
+
dateRange={false}
|
557
|
+
yearRange={{ min: 2018, max: 2030 }}
|
558
|
+
range={{
|
559
|
+
from: new Date('2019-08-01'),
|
560
|
+
to: undefined
|
561
|
+
}}
|
562
|
+
allowClear={false}
|
563
|
+
/>
|
564
|
+
);
|
565
|
+
const inputSelect = wrapper.getByTestId('new input');
|
566
|
+
|
567
|
+
fireEvent.focus(inputSelect);
|
568
|
+
userEvent.type(inputSelect, '{selectall}{backspace}010131');
|
569
|
+
fireEvent.blur(inputSelect);
|
570
|
+
|
571
|
+
expect(onMockSelectedDate).toHaveBeenLastCalledWith({
|
572
|
+
from: new Date('2019-08-01T00:00:00.000Z'),
|
573
|
+
to: undefined
|
574
|
+
});
|
575
|
+
expect(
|
576
|
+
wrapper.getByText(
|
577
|
+
'Year is outside of range. Must be between 2018 - 2030'
|
578
|
+
)
|
579
|
+
).toBeInTheDocument();
|
580
|
+
});
|
581
|
+
|
582
|
+
it('should revert invalid date to empty if no initial date', () => {
|
583
|
+
onMockSelectedDate.mockClear();
|
584
|
+
wrapper.rerender(
|
585
|
+
<DayPicker
|
586
|
+
{...props}
|
587
|
+
typeable
|
588
|
+
dateRange={false}
|
589
|
+
yearRange={{ min: 2000, max: 2100 }}
|
590
|
+
range={{
|
591
|
+
from: undefined,
|
592
|
+
to: undefined
|
593
|
+
}}
|
594
|
+
allowClear={false}
|
595
|
+
/>
|
596
|
+
);
|
597
|
+
const inputSelect = wrapper.getByTestId('new input');
|
598
|
+
|
599
|
+
fireEvent.focus(inputSelect);
|
600
|
+
fireEvent.blur(inputSelect);
|
601
|
+
fireEvent.focus(inputSelect);
|
602
|
+
userEvent.type(inputSelect, '{selectall}{backspace}0101');
|
603
|
+
fireEvent.blur(inputSelect);
|
604
|
+
|
605
|
+
expect(onMockSelectedDate).not.toHaveBeenCalled();
|
606
|
+
});
|
607
|
+
|
608
|
+
it('backspace / automatically', () => {
|
609
|
+
wrapper.rerender(
|
610
|
+
<DayPicker
|
611
|
+
{...props}
|
612
|
+
typeable
|
613
|
+
dateRange={false}
|
614
|
+
yearRange={{ min: 2000, max: 2100 }}
|
615
|
+
range={{
|
616
|
+
from: new Date('2019-08-01'),
|
617
|
+
to: undefined
|
618
|
+
}}
|
619
|
+
/>
|
620
|
+
);
|
621
|
+
const inputSelect = wrapper.getByTestId('new input');
|
622
|
+
|
623
|
+
fireEvent.focus(inputSelect);
|
624
|
+
userEvent.type(inputSelect, `{backspace}{backspace}/`);
|
625
|
+
expect(inputSelect.value).toEqual('01/08/');
|
626
|
+
});
|
627
|
+
|
628
|
+
it('should open calendar view with the icon', () => {
|
629
|
+
wrapper.rerender(
|
630
|
+
<DayPicker
|
631
|
+
{...props}
|
632
|
+
typeable
|
633
|
+
dateRange={false}
|
634
|
+
yearRange={{ min: 2000, max: 2100 }}
|
635
|
+
range={{
|
636
|
+
from: new Date('2019-08-01'),
|
637
|
+
to: undefined
|
638
|
+
}}
|
639
|
+
/>
|
640
|
+
);
|
641
|
+
|
642
|
+
fireEvent.click(wrapper.getByTestId('calendarIcon'));
|
643
|
+
expect(wrapper.getByTestId('daypicker-panel')).toBeInTheDocument();
|
644
|
+
|
645
|
+
const inputSelect = wrapper.getByTestId('new input');
|
646
|
+
fireEvent.focus(inputSelect);
|
647
|
+
fireEvent.blur(inputSelect);
|
648
|
+
|
649
|
+
expect(
|
650
|
+
wrapper.queryByTestId('daypicker-panel')
|
651
|
+
).not.toBeInTheDocument();
|
652
|
+
});
|
653
|
+
|
654
|
+
it('should apply the error styling if error', () => {
|
655
|
+
wrapper.rerender(
|
656
|
+
<DayPicker {...props} dateRange={false} typeable error />
|
657
|
+
);
|
658
|
+
|
659
|
+
const inputSelect = wrapper.getByTestId('input-selector');
|
660
|
+
expect(inputSelect).toHaveStyleRule(
|
661
|
+
'border-color',
|
662
|
+
'#FF5000 !important'
|
663
|
+
);
|
664
|
+
});
|
665
|
+
|
666
|
+
it('should apply the error styling if disabled', () => {
|
667
|
+
wrapper.rerender(
|
668
|
+
<DayPicker {...props} typeable dateRange={false} disabled />
|
669
|
+
);
|
670
|
+
|
671
|
+
const inputSelect = wrapper.getByTestId('new input');
|
672
|
+
expect(inputSelect).toBeDisabled();
|
673
|
+
});
|
674
|
+
|
675
|
+
it('should call the correct GA tags when the date is typed', async () => {
|
676
|
+
wrapper.rerender(
|
677
|
+
<DayPicker
|
678
|
+
{...props}
|
679
|
+
typeable
|
680
|
+
dateRange={false}
|
681
|
+
range={{
|
682
|
+
from: undefined,
|
683
|
+
to: undefined
|
684
|
+
}}
|
685
|
+
recordValue
|
686
|
+
/>
|
687
|
+
);
|
688
|
+
expect(ReactGA.event).toHaveBeenCalledTimes(0);
|
689
|
+
|
690
|
+
const inputSelect = wrapper.getByTestId('new input');
|
691
|
+
fireEvent.focus(inputSelect);
|
692
|
+
userEvent.type(inputSelect, `201222`);
|
693
|
+
await wait(() => expect(inputSelect.value).toEqual('20/12/22'));
|
694
|
+
fireEvent.blur(inputSelect);
|
695
|
+
|
696
|
+
await wait(() =>
|
697
|
+
expect(ReactGA.event).toHaveBeenCalledWith({
|
698
|
+
action: 'ActionID - Set via Typing',
|
699
|
+
category: 'Test',
|
700
|
+
label: 'Tue 20 Dec 2022'
|
701
|
+
})
|
702
|
+
);
|
703
|
+
});
|
704
|
+
});
|
409
705
|
});
|
@@ -178,7 +178,7 @@ class DayPickerPanel extends React.Component {
|
|
178
178
|
range: { from, to }
|
179
179
|
} = this.state;
|
180
180
|
|
181
|
-
const actionEvent = selectedDay ? 'Set' : 'Apply';
|
181
|
+
const actionEvent = selectedDay ? 'Set via Date Picker' : 'Apply';
|
182
182
|
|
183
183
|
if (category) {
|
184
184
|
const label = dateRange
|
@@ -20,7 +20,7 @@ describe('<DurationInput />', () => {
|
|
20
20
|
});
|
21
21
|
|
22
22
|
it('should select a new days value when the days input is changed', () => {
|
23
|
-
const daysInput = wrapper.getByRole('
|
23
|
+
const daysInput = wrapper.getByRole('spinbutton');
|
24
24
|
|
25
25
|
expect(onChange).not.toHaveBeenCalled();
|
26
26
|
|
@@ -36,7 +36,7 @@ describe('<DurationInput />', () => {
|
|
36
36
|
const blurProps = { ...props, onBlur: jest.fn() };
|
37
37
|
wrapper.rerender(<DurationInput {...blurProps} />);
|
38
38
|
|
39
|
-
const daysInput = wrapper.getByRole('
|
39
|
+
const daysInput = wrapper.getByRole('spinbutton');
|
40
40
|
fireEvent.blur(daysInput);
|
41
41
|
|
42
42
|
expect(blurProps.onBlur).toHaveBeenCalled();
|
@@ -54,7 +54,9 @@ describe('<DurationInput />', () => {
|
|
54
54
|
});
|
55
55
|
|
56
56
|
it('should select a new hours value and a new minutes value when the inputs are changed', () => {
|
57
|
-
const [hoursInput, minutesInput] = wrapper.getAllByRole(
|
57
|
+
const [hoursInput, minutesInput] = wrapper.getAllByRole(
|
58
|
+
'spinbutton'
|
59
|
+
);
|
58
60
|
|
59
61
|
expect(onChange).not.toHaveBeenCalled();
|
60
62
|
|
@@ -33,7 +33,7 @@ describe('EmployeePickerFilterBar', () => {
|
|
33
33
|
});
|
34
34
|
|
35
35
|
const getInput = () => element.getByPlaceholderText('Enter name');
|
36
|
-
const getSelect = () => element.getByRole('
|
36
|
+
const getSelect = () => element.getByRole('combobox');
|
37
37
|
|
38
38
|
it('should call updateType when the user selects a filter type', () => {
|
39
39
|
const input = getInput();
|
@@ -15,7 +15,7 @@ describe('<ResponsiveTabs />', () => {
|
|
15
15
|
</ResponsiveTabs>
|
16
16
|
);
|
17
17
|
|
18
|
-
expect(getByRole('
|
18
|
+
expect(getByRole('combobox')).toBeInTheDocument();
|
19
19
|
expect(getAllByRole('option')).toHaveLength(2);
|
20
20
|
});
|
21
21
|
|
@@ -38,7 +38,7 @@ describe('<ResponsiveTabs />', () => {
|
|
38
38
|
expect(getByTestId('tab1')).toBeVisible();
|
39
39
|
expect(getByTestId('tab2')).not.toBeVisible();
|
40
40
|
|
41
|
-
const dropdown = getByRole('
|
41
|
+
const dropdown = getByRole('combobox');
|
42
42
|
fireEvent.change(dropdown, { target: { value: 'Two' } });
|
43
43
|
|
44
44
|
expect(getByTestId('tab1')).not.toBeVisible();
|
@@ -90,7 +90,7 @@ describe('<ResponsiveTabs />', () => {
|
|
90
90
|
</ResponsiveTabs>
|
91
91
|
);
|
92
92
|
|
93
|
-
fireEvent.change(getByRole('
|
93
|
+
fireEvent.change(getByRole('combobox'), { target: { value: 'One' } });
|
94
94
|
expect(event).not.toHaveBeenCalled();
|
95
95
|
|
96
96
|
fireEvent.click(getByRole('tab'));
|
@@ -7,21 +7,22 @@ describe('<TimePicker />', () => {
|
|
7
7
|
describe('given a null value', () => {
|
8
8
|
it('should render without throwing exceptions', () => {
|
9
9
|
const onChange = jest.fn();
|
10
|
-
const {
|
10
|
+
const { getByRole, getByTestId } = render(
|
11
11
|
<TimePicker value={null} onChange={onChange} />
|
12
12
|
);
|
13
13
|
|
14
|
-
expect(
|
14
|
+
expect(getByRole('textbox')).toBeInTheDocument();
|
15
|
+
expect(getByTestId('mobileTimePicker')).toBeInTheDocument();
|
15
16
|
});
|
16
17
|
|
17
18
|
it('should allow a new value to be input', () => {
|
18
19
|
const onChange = jest.fn();
|
19
|
-
const {
|
20
|
+
const { getByRole } = render(
|
20
21
|
<TimePicker value={null} onChange={onChange} />
|
21
22
|
);
|
22
23
|
|
23
24
|
// check a new value can be input
|
24
|
-
const textBox =
|
25
|
+
const textBox = getByRole('textbox');
|
25
26
|
expect(onChange).not.toHaveBeenCalled();
|
26
27
|
fireEvent.change(textBox, { target: { value: '17:15' } });
|
27
28
|
expect(onChange).toHaveBeenCalledWith({
|
@@ -44,14 +45,14 @@ describe('<TimePicker />', () => {
|
|
44
45
|
|
45
46
|
describe('on initial render', () => {
|
46
47
|
it('should render a native time input for mobile devices', () => {
|
47
|
-
const {
|
48
|
+
const { getByTestId } = render(
|
48
49
|
<TimePicker
|
49
50
|
value={{ hours: 1, minutes: 30 }}
|
50
51
|
onChange={() => {}}
|
51
52
|
/>
|
52
53
|
);
|
53
54
|
|
54
|
-
const textBox =
|
55
|
+
const textBox = getByTestId('mobileTimePicker');
|
55
56
|
expect(textBox.type).toBe('time');
|
56
57
|
expect(textBox.value).toBe('01:30');
|
57
58
|
expect(textBox).toHaveStyleRule('display', 'inline-block');
|
@@ -62,14 +63,14 @@ describe('<TimePicker />', () => {
|
|
62
63
|
});
|
63
64
|
|
64
65
|
it('should render a standard input for desktop-class devices', () => {
|
65
|
-
const {
|
66
|
+
const { getByRole } = render(
|
66
67
|
<TimePicker
|
67
68
|
value={{ hours: 1, minutes: 30 }}
|
68
69
|
onChange={() => {}}
|
69
70
|
/>
|
70
71
|
);
|
71
72
|
|
72
|
-
const textBox =
|
73
|
+
const textBox = getByRole('textbox');
|
73
74
|
expect(textBox.type).toBe('text');
|
74
75
|
expect(textBox.value).toBe('01:30');
|
75
76
|
});
|
@@ -77,7 +78,7 @@ describe('<TimePicker />', () => {
|
|
77
78
|
it('should support onFocus and onBlur props and call down to them', () => {
|
78
79
|
const onFocus = jest.fn();
|
79
80
|
const onBlur = jest.fn();
|
80
|
-
const {
|
81
|
+
const { getByRole } = render(
|
81
82
|
<TimePicker
|
82
83
|
value={{ hours: 1, minutes: 30 }}
|
83
84
|
onFocus={onFocus}
|
@@ -86,7 +87,7 @@ describe('<TimePicker />', () => {
|
|
86
87
|
/>
|
87
88
|
);
|
88
89
|
|
89
|
-
const textBox =
|
90
|
+
const textBox = getByRole('textbox');
|
90
91
|
expect(onFocus).not.toHaveBeenCalled();
|
91
92
|
expect(onBlur).not.toHaveBeenCalled();
|
92
93
|
fireEvent.focus(textBox);
|
@@ -152,14 +153,14 @@ describe('<TimePicker />', () => {
|
|
152
153
|
describe('given a valid value is supplied', () => {
|
153
154
|
it('should call the onChange handler when the mobile input is used', () => {
|
154
155
|
const onChange = jest.fn();
|
155
|
-
const {
|
156
|
+
const { getByTestId } = render(
|
156
157
|
<TimePicker
|
157
158
|
value={{ hours: 1, minutes: 30 }}
|
158
159
|
onChange={onChange}
|
159
160
|
/>
|
160
161
|
);
|
161
162
|
|
162
|
-
const textBox =
|
163
|
+
const textBox = getByTestId('mobileTimePicker');
|
163
164
|
expect(onChange).not.toHaveBeenCalled();
|
164
165
|
fireEvent.focus(textBox);
|
165
166
|
fireEvent.change(textBox, { target: { value: '17:15' } });
|
@@ -171,14 +172,14 @@ describe('<TimePicker />', () => {
|
|
171
172
|
|
172
173
|
it('should call the onChange handler when the desktop-class input is used', () => {
|
173
174
|
const onChange = jest.fn();
|
174
|
-
const {
|
175
|
+
const { getByRole } = render(
|
175
176
|
<TimePicker
|
176
177
|
value={{ hours: 1, minutes: 30 }}
|
177
178
|
onChange={onChange}
|
178
179
|
/>
|
179
180
|
);
|
180
181
|
|
181
|
-
const textBox =
|
182
|
+
const textBox = getByRole('textbox');
|
182
183
|
expect(onChange).not.toHaveBeenCalled();
|
183
184
|
fireEvent.focus(textBox);
|
184
185
|
fireEvent.change(textBox, { target: { value: '17:15' } });
|
@@ -192,14 +193,14 @@ describe('<TimePicker />', () => {
|
|
192
193
|
describe('given an incomplete value is supplied', () => {
|
193
194
|
it('should not call the onChange handler when the mobile input is used', () => {
|
194
195
|
const onChange = jest.fn();
|
195
|
-
const {
|
196
|
+
const { getByTestId } = render(
|
196
197
|
<TimePicker
|
197
198
|
value={{ hours: 1, minutes: 30 }}
|
198
199
|
onChange={onChange}
|
199
200
|
/>
|
200
201
|
);
|
201
202
|
|
202
|
-
const textBox =
|
203
|
+
const textBox = getByTestId('mobileTimePicker');
|
203
204
|
expect(onChange).not.toHaveBeenCalled();
|
204
205
|
fireEvent.change(textBox, { target: { value: '17:1' } });
|
205
206
|
expect(onChange).not.toHaveBeenCalledWith();
|
@@ -207,14 +208,14 @@ describe('<TimePicker />', () => {
|
|
207
208
|
|
208
209
|
it('should not call the onChange handler when the desktop-class input is used', () => {
|
209
210
|
const onChange = jest.fn();
|
210
|
-
const {
|
211
|
+
const { getByRole } = render(
|
211
212
|
<TimePicker
|
212
213
|
value={{ hours: 1, minutes: 30 }}
|
213
214
|
onChange={onChange}
|
214
215
|
/>
|
215
216
|
);
|
216
217
|
|
217
|
-
const textBox =
|
218
|
+
const textBox = getByRole('textbox');
|
218
219
|
expect(onChange).not.toHaveBeenCalled();
|
219
220
|
fireEvent.change(textBox, { target: { value: '17:1' } });
|
220
221
|
expect(onChange).not.toHaveBeenCalledWith();
|
@@ -224,14 +225,14 @@ describe('<TimePicker />', () => {
|
|
224
225
|
describe('given the box is cleared', () => {
|
225
226
|
it('should call the onChange handler with null', () => {
|
226
227
|
const onChange = jest.fn();
|
227
|
-
const {
|
228
|
+
const { getByRole } = render(
|
228
229
|
<TimePicker
|
229
230
|
value={{ hours: 1, minutes: 30 }}
|
230
231
|
onChange={onChange}
|
231
232
|
/>
|
232
233
|
);
|
233
234
|
|
234
|
-
const textBox =
|
235
|
+
const textBox = getByRole('textbox');
|
235
236
|
expect(onChange).not.toHaveBeenCalled();
|
236
237
|
fireEvent.focus(textBox);
|
237
238
|
fireEvent.change(textBox, { target: { value: '' } });
|
@@ -242,14 +243,14 @@ describe('<TimePicker />', () => {
|
|
242
243
|
describe('when the field loses focus', () => {
|
243
244
|
it('should parse reasonable partial values into valid values', () => {
|
244
245
|
const onChange = jest.fn();
|
245
|
-
const {
|
246
|
+
const { getByRole } = render(
|
246
247
|
<TimePicker
|
247
248
|
value={{ hours: 1, minutes: 30 }}
|
248
249
|
onChange={onChange}
|
249
250
|
/>
|
250
251
|
);
|
251
252
|
|
252
|
-
const textBox =
|
253
|
+
const textBox = getByRole('textbox');
|
253
254
|
fireEvent.focus(textBox);
|
254
255
|
fireEvent.change(textBox, { target: { value: '01:3' } });
|
255
256
|
expect(onChange).not.toHaveBeenCalled();
|
@@ -259,14 +260,14 @@ describe('<TimePicker />', () => {
|
|
259
260
|
|
260
261
|
it('should not call onChange if parsed partial value is same as given value', () => {
|
261
262
|
const onChange = jest.fn();
|
262
|
-
const {
|
263
|
+
const { getByRole } = render(
|
263
264
|
<TimePicker
|
264
265
|
value={{ hours: 1, minutes: 3 }}
|
265
266
|
onChange={onChange}
|
266
267
|
/>
|
267
268
|
);
|
268
269
|
|
269
|
-
const textBox =
|
270
|
+
const textBox = getByRole('textbox');
|
270
271
|
fireEvent.focus(textBox);
|
271
272
|
fireEvent.change(textBox, { target: { value: '01:3' } });
|
272
273
|
expect(onChange).not.toHaveBeenCalled();
|
@@ -276,14 +277,14 @@ describe('<TimePicker />', () => {
|
|
276
277
|
|
277
278
|
it('should not parse unreasonable values into valid values', () => {
|
278
279
|
const onChange = jest.fn();
|
279
|
-
const {
|
280
|
+
const { getByRole } = render(
|
280
281
|
<TimePicker
|
281
282
|
value={{ hours: 1, minutes: 30 }}
|
282
283
|
onChange={onChange}
|
283
284
|
/>
|
284
285
|
);
|
285
286
|
|
286
|
-
const textBox =
|
287
|
+
const textBox = getByRole('textbox');
|
287
288
|
fireEvent.focus(textBox);
|
288
289
|
fireEvent.change(textBox, { target: { value: '01' } });
|
289
290
|
expect(onChange).not.toHaveBeenCalled();
|
@@ -297,7 +298,7 @@ describe('<TimePicker />', () => {
|
|
297
298
|
// this is a convoluted test to cover some code that works around an issue with
|
298
299
|
// stores (mobx) that only re-render when actual values change
|
299
300
|
const onChange = jest.fn();
|
300
|
-
const {
|
301
|
+
const { getByRole } = render(
|
301
302
|
<TimePicker
|
302
303
|
value={{ hours: 1, minutes: 30 }}
|
303
304
|
minuteIncrements={5}
|
@@ -305,7 +306,7 @@ describe('<TimePicker />', () => {
|
|
305
306
|
/>
|
306
307
|
);
|
307
308
|
|
308
|
-
const textBox =
|
309
|
+
const textBox = getByRole('textbox');
|
309
310
|
fireEvent.focus(textBox);
|
310
311
|
fireEvent.change(textBox, { target: { value: '01:32' } });
|
311
312
|
expect(onChange).toHaveBeenCalledWith({
|
package/src/setupTests.js
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
import { configure } from 'enzyme';
|
2
|
+
import { configure as RTLConfig } from '@testing-library/react';
|
2
3
|
import Adapter from 'enzyme-adapter-react-16';
|
3
4
|
import ReactGA from 'react-ga';
|
4
5
|
import 'jest-enzyme';
|
@@ -34,3 +35,4 @@ afterEach(() => {
|
|
34
35
|
});
|
35
36
|
|
36
37
|
configure({ adapter: new Adapter() });
|
38
|
+
RTLConfig({ defaultHidden: true });
|