@khanacademy/wonder-blocks-birthday-picker 1.0.0 → 1.2.1
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/CHANGELOG.md +37 -4
- package/dist/es/index.js +64 -20
- package/dist/index.js +64 -20
- package/package.json +6 -6
- package/src/components/__docs__/birthday-picker.argtypes.js +0 -24
- package/src/components/__docs__/birthday-picker.stories.js +50 -0
- package/src/components/__tests__/birthday-picker.test.js +93 -3
- package/src/components/birthday-picker.js +112 -50
package/CHANGELOG.md
CHANGED
|
@@ -1,12 +1,45 @@
|
|
|
1
1
|
# @khanacademy/wonder-blocks-birthday-picker
|
|
2
2
|
|
|
3
|
+
## 1.2.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies [246a921d]
|
|
8
|
+
- @khanacademy/wonder-blocks-core@4.3.0
|
|
9
|
+
- @khanacademy/wonder-blocks-dropdown@2.6.6
|
|
10
|
+
- @khanacademy/wonder-blocks-icon@1.2.26
|
|
11
|
+
- @khanacademy/wonder-blocks-layout@1.4.8
|
|
12
|
+
- @khanacademy/wonder-blocks-typography@1.1.30
|
|
13
|
+
|
|
14
|
+
## 1.2.0
|
|
15
|
+
|
|
16
|
+
### Minor Changes
|
|
17
|
+
|
|
18
|
+
- 5536d12e: Add `monthYearOnly` prop to hide the `day` field from the UI. Passes the first day of the selected month for this case.
|
|
19
|
+
|
|
20
|
+
### Patch Changes
|
|
21
|
+
|
|
22
|
+
- @khanacademy/wonder-blocks-dropdown@2.6.5
|
|
23
|
+
|
|
24
|
+
## 1.1.0
|
|
25
|
+
|
|
26
|
+
### Minor Changes
|
|
27
|
+
|
|
28
|
+
- 17fa61fd: Add `disabled` prop
|
|
29
|
+
|
|
30
|
+
### Patch Changes
|
|
31
|
+
|
|
32
|
+
- Updated dependencies [77e7523c]
|
|
33
|
+
- @khanacademy/wonder-blocks-dropdown@2.6.4
|
|
34
|
+
|
|
3
35
|
## 1.0.0
|
|
36
|
+
|
|
4
37
|
### Major Changes
|
|
5
38
|
|
|
6
|
-
-
|
|
7
|
-
|
|
39
|
+
- 11c87db3: - Added `wonder-blocks-birthday-picker`.
|
|
40
|
+
- Dropdown: Updated some styles from `backgroundColor` to `background` to avoid some warnings on unit tests.
|
|
8
41
|
|
|
9
42
|
### Patch Changes
|
|
10
43
|
|
|
11
|
-
-
|
|
12
|
-
|
|
44
|
+
- Updated dependencies [11c87db3]
|
|
45
|
+
- @khanacademy/wonder-blocks-dropdown@2.6.3
|
package/dist/es/index.js
CHANGED
|
@@ -18,7 +18,12 @@ const defaultLabels = Object.freeze({
|
|
|
18
18
|
month: "Month",
|
|
19
19
|
year: "Year",
|
|
20
20
|
day: "Day"
|
|
21
|
-
});
|
|
21
|
+
}); // Default minWidth value when we include the full DOB.
|
|
22
|
+
|
|
23
|
+
const FIELD_MIN_WIDTH_FULL = 110; // Alternative minWidth value when we hide the day field.
|
|
24
|
+
// See: https://www.figma.com/file/uJZi9ZvuEz5N8GJ3HqKFAa/(2021)-Account-records?node-id=20%3A398
|
|
25
|
+
|
|
26
|
+
const FIELD_MIN_WIDTH_MONTH_YEAR = 167;
|
|
22
27
|
/**
|
|
23
28
|
* Birthday Picker. Similar to a datepicker, but specifically for birthdates.
|
|
24
29
|
* We don't want to show a calendar in this case as it can be quite tedious to
|
|
@@ -127,11 +132,12 @@ class BirthdayPicker extends React.Component {
|
|
|
127
132
|
|
|
128
133
|
getStateFromDefault() {
|
|
129
134
|
const {
|
|
130
|
-
defaultValue
|
|
135
|
+
defaultValue,
|
|
136
|
+
monthYearOnly
|
|
131
137
|
} = this.props;
|
|
132
138
|
const initialState = {
|
|
133
139
|
month: null,
|
|
134
|
-
day: null,
|
|
140
|
+
day: monthYearOnly ? "1" : null,
|
|
135
141
|
year: null,
|
|
136
142
|
error: null
|
|
137
143
|
}; // merge custom labels with the default ones
|
|
@@ -191,32 +197,48 @@ class BirthdayPicker extends React.Component {
|
|
|
191
197
|
}, error)));
|
|
192
198
|
}
|
|
193
199
|
|
|
194
|
-
|
|
200
|
+
renderMonth() {
|
|
195
201
|
const {
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
202
|
+
disabled,
|
|
203
|
+
monthYearOnly
|
|
204
|
+
} = this.props;
|
|
205
|
+
const {
|
|
206
|
+
month
|
|
199
207
|
} = this.state;
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
flexDirection: "row"
|
|
204
|
-
}
|
|
205
|
-
}, /*#__PURE__*/React.createElement(SingleSelect, {
|
|
208
|
+
const minWidth = monthYearOnly ? FIELD_MIN_WIDTH_MONTH_YEAR : FIELD_MIN_WIDTH_FULL;
|
|
209
|
+
return /*#__PURE__*/React.createElement(SingleSelect, {
|
|
210
|
+
disabled: disabled,
|
|
206
211
|
placeholder: this.labels.month,
|
|
207
212
|
onChange: this.handleMonthChange,
|
|
208
213
|
selectedValue: month,
|
|
209
214
|
style: {
|
|
210
|
-
minWidth
|
|
215
|
+
minWidth
|
|
211
216
|
},
|
|
212
217
|
testId: "birthday-picker-month"
|
|
213
218
|
}, moment.monthsShort().map((month, i) => /*#__PURE__*/React.createElement(OptionItem, {
|
|
214
219
|
key: month,
|
|
215
220
|
label: month,
|
|
216
221
|
value: String(i)
|
|
217
|
-
})))
|
|
222
|
+
})));
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
maybeRenderDay() {
|
|
226
|
+
const {
|
|
227
|
+
disabled,
|
|
228
|
+
monthYearOnly
|
|
229
|
+
} = this.props;
|
|
230
|
+
const {
|
|
231
|
+
day
|
|
232
|
+
} = this.state; // Hide the day field if the month/year only mode is enabled.
|
|
233
|
+
|
|
234
|
+
if (monthYearOnly) {
|
|
235
|
+
return null;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Strut, {
|
|
218
239
|
size: Spacing.xSmall_8
|
|
219
240
|
}), /*#__PURE__*/React.createElement(SingleSelect, {
|
|
241
|
+
disabled: disabled,
|
|
220
242
|
placeholder: this.labels.day,
|
|
221
243
|
onChange: this.handleDayChange,
|
|
222
244
|
selectedValue: day,
|
|
@@ -228,21 +250,43 @@ class BirthdayPicker extends React.Component {
|
|
|
228
250
|
key: String(day + 1),
|
|
229
251
|
label: String(day + 1),
|
|
230
252
|
value: String(day + 1)
|
|
231
|
-
})))
|
|
232
|
-
|
|
233
|
-
|
|
253
|
+
}))));
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
renderYear() {
|
|
257
|
+
const {
|
|
258
|
+
disabled,
|
|
259
|
+
monthYearOnly
|
|
260
|
+
} = this.props;
|
|
261
|
+
const {
|
|
262
|
+
year
|
|
263
|
+
} = this.state;
|
|
264
|
+
const minWidth = monthYearOnly ? FIELD_MIN_WIDTH_MONTH_YEAR : FIELD_MIN_WIDTH_FULL;
|
|
265
|
+
return /*#__PURE__*/React.createElement(SingleSelect, {
|
|
266
|
+
disabled: disabled,
|
|
234
267
|
placeholder: this.labels.year,
|
|
235
268
|
onChange: this.handleYearChange,
|
|
236
269
|
selectedValue: year,
|
|
237
270
|
style: {
|
|
238
|
-
minWidth
|
|
271
|
+
minWidth
|
|
239
272
|
},
|
|
240
273
|
testId: "birthday-picker-year"
|
|
241
274
|
}, Array.from(Array(120)).map((_, yearOffset) => /*#__PURE__*/React.createElement(OptionItem, {
|
|
242
275
|
key: String(CUR_YEAR - yearOffset),
|
|
243
276
|
label: String(CUR_YEAR - yearOffset),
|
|
244
277
|
value: String(CUR_YEAR - yearOffset)
|
|
245
|
-
})))
|
|
278
|
+
})));
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
render() {
|
|
282
|
+
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(View, {
|
|
283
|
+
testId: "birthday-picker",
|
|
284
|
+
style: {
|
|
285
|
+
flexDirection: "row"
|
|
286
|
+
}
|
|
287
|
+
}, this.renderMonth(), this.maybeRenderDay(), /*#__PURE__*/React.createElement(Strut, {
|
|
288
|
+
size: Spacing.xSmall_8
|
|
289
|
+
}), this.renderYear()), this.maybeRenderError());
|
|
246
290
|
}
|
|
247
291
|
|
|
248
292
|
}
|
package/dist/index.js
CHANGED
|
@@ -176,7 +176,12 @@ const defaultLabels = Object.freeze({
|
|
|
176
176
|
month: "Month",
|
|
177
177
|
year: "Year",
|
|
178
178
|
day: "Day"
|
|
179
|
-
});
|
|
179
|
+
}); // Default minWidth value when we include the full DOB.
|
|
180
|
+
|
|
181
|
+
const FIELD_MIN_WIDTH_FULL = 110; // Alternative minWidth value when we hide the day field.
|
|
182
|
+
// See: https://www.figma.com/file/uJZi9ZvuEz5N8GJ3HqKFAa/(2021)-Account-records?node-id=20%3A398
|
|
183
|
+
|
|
184
|
+
const FIELD_MIN_WIDTH_MONTH_YEAR = 167;
|
|
180
185
|
/**
|
|
181
186
|
* Birthday Picker. Similar to a datepicker, but specifically for birthdates.
|
|
182
187
|
* We don't want to show a calendar in this case as it can be quite tedious to
|
|
@@ -285,11 +290,12 @@ class BirthdayPicker extends react__WEBPACK_IMPORTED_MODULE_1__["Component"] {
|
|
|
285
290
|
|
|
286
291
|
getStateFromDefault() {
|
|
287
292
|
const {
|
|
288
|
-
defaultValue
|
|
293
|
+
defaultValue,
|
|
294
|
+
monthYearOnly
|
|
289
295
|
} = this.props;
|
|
290
296
|
const initialState = {
|
|
291
297
|
month: null,
|
|
292
|
-
day: null,
|
|
298
|
+
day: monthYearOnly ? "1" : null,
|
|
293
299
|
year: null,
|
|
294
300
|
error: null
|
|
295
301
|
}; // merge custom labels with the default ones
|
|
@@ -351,32 +357,48 @@ class BirthdayPicker extends react__WEBPACK_IMPORTED_MODULE_1__["Component"] {
|
|
|
351
357
|
}, error)));
|
|
352
358
|
}
|
|
353
359
|
|
|
354
|
-
|
|
360
|
+
renderMonth() {
|
|
355
361
|
const {
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
362
|
+
disabled,
|
|
363
|
+
monthYearOnly
|
|
364
|
+
} = this.props;
|
|
365
|
+
const {
|
|
366
|
+
month
|
|
359
367
|
} = this.state;
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
flexDirection: "row"
|
|
364
|
-
}
|
|
365
|
-
}, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_khanacademy_wonder_blocks_dropdown__WEBPACK_IMPORTED_MODULE_8__["SingleSelect"], {
|
|
368
|
+
const minWidth = monthYearOnly ? FIELD_MIN_WIDTH_MONTH_YEAR : FIELD_MIN_WIDTH_FULL;
|
|
369
|
+
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_khanacademy_wonder_blocks_dropdown__WEBPACK_IMPORTED_MODULE_8__["SingleSelect"], {
|
|
370
|
+
disabled: disabled,
|
|
366
371
|
placeholder: this.labels.month,
|
|
367
372
|
onChange: this.handleMonthChange,
|
|
368
373
|
selectedValue: month,
|
|
369
374
|
style: {
|
|
370
|
-
minWidth
|
|
375
|
+
minWidth
|
|
371
376
|
},
|
|
372
377
|
testId: "birthday-picker-month"
|
|
373
378
|
}, moment__WEBPACK_IMPORTED_MODULE_0___default.a.monthsShort().map((month, i) => /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_khanacademy_wonder_blocks_dropdown__WEBPACK_IMPORTED_MODULE_8__["OptionItem"], {
|
|
374
379
|
key: month,
|
|
375
380
|
label: month,
|
|
376
381
|
value: String(i)
|
|
377
|
-
})))
|
|
382
|
+
})));
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
maybeRenderDay() {
|
|
386
|
+
const {
|
|
387
|
+
disabled,
|
|
388
|
+
monthYearOnly
|
|
389
|
+
} = this.props;
|
|
390
|
+
const {
|
|
391
|
+
day
|
|
392
|
+
} = this.state; // Hide the day field if the month/year only mode is enabled.
|
|
393
|
+
|
|
394
|
+
if (monthYearOnly) {
|
|
395
|
+
return null;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1__["createElement"](react__WEBPACK_IMPORTED_MODULE_1__["Fragment"], null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_khanacademy_wonder_blocks_layout__WEBPACK_IMPORTED_MODULE_3__["Strut"], {
|
|
378
399
|
size: _khanacademy_wonder_blocks_spacing__WEBPACK_IMPORTED_MODULE_4___default.a.xSmall_8
|
|
379
400
|
}), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_khanacademy_wonder_blocks_dropdown__WEBPACK_IMPORTED_MODULE_8__["SingleSelect"], {
|
|
401
|
+
disabled: disabled,
|
|
380
402
|
placeholder: this.labels.day,
|
|
381
403
|
onChange: this.handleDayChange,
|
|
382
404
|
selectedValue: day,
|
|
@@ -388,21 +410,43 @@ class BirthdayPicker extends react__WEBPACK_IMPORTED_MODULE_1__["Component"] {
|
|
|
388
410
|
key: String(day + 1),
|
|
389
411
|
label: String(day + 1),
|
|
390
412
|
value: String(day + 1)
|
|
391
|
-
})))
|
|
392
|
-
|
|
393
|
-
|
|
413
|
+
}))));
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
renderYear() {
|
|
417
|
+
const {
|
|
418
|
+
disabled,
|
|
419
|
+
monthYearOnly
|
|
420
|
+
} = this.props;
|
|
421
|
+
const {
|
|
422
|
+
year
|
|
423
|
+
} = this.state;
|
|
424
|
+
const minWidth = monthYearOnly ? FIELD_MIN_WIDTH_MONTH_YEAR : FIELD_MIN_WIDTH_FULL;
|
|
425
|
+
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_khanacademy_wonder_blocks_dropdown__WEBPACK_IMPORTED_MODULE_8__["SingleSelect"], {
|
|
426
|
+
disabled: disabled,
|
|
394
427
|
placeholder: this.labels.year,
|
|
395
428
|
onChange: this.handleYearChange,
|
|
396
429
|
selectedValue: year,
|
|
397
430
|
style: {
|
|
398
|
-
minWidth
|
|
431
|
+
minWidth
|
|
399
432
|
},
|
|
400
433
|
testId: "birthday-picker-year"
|
|
401
434
|
}, Array.from(Array(120)).map((_, yearOffset) => /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_khanacademy_wonder_blocks_dropdown__WEBPACK_IMPORTED_MODULE_8__["OptionItem"], {
|
|
402
435
|
key: String(CUR_YEAR - yearOffset),
|
|
403
436
|
label: String(CUR_YEAR - yearOffset),
|
|
404
437
|
value: String(CUR_YEAR - yearOffset)
|
|
405
|
-
})))
|
|
438
|
+
})));
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
render() {
|
|
442
|
+
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1__["createElement"](react__WEBPACK_IMPORTED_MODULE_1__["Fragment"], null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_2__["View"], {
|
|
443
|
+
testId: "birthday-picker",
|
|
444
|
+
style: {
|
|
445
|
+
flexDirection: "row"
|
|
446
|
+
}
|
|
447
|
+
}, this.renderMonth(), this.maybeRenderDay(), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_khanacademy_wonder_blocks_layout__WEBPACK_IMPORTED_MODULE_3__["Strut"], {
|
|
448
|
+
size: _khanacademy_wonder_blocks_spacing__WEBPACK_IMPORTED_MODULE_4___default.a.xSmall_8
|
|
449
|
+
}), this.renderYear()), this.maybeRenderError());
|
|
406
450
|
}
|
|
407
451
|
|
|
408
452
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@khanacademy/wonder-blocks-birthday-picker",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"design": "v1",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -15,12 +15,12 @@
|
|
|
15
15
|
"dependencies": {
|
|
16
16
|
"@babel/runtime": "^7.16.3",
|
|
17
17
|
"@khanacademy/wonder-blocks-color": "^1.1.20",
|
|
18
|
-
"@khanacademy/wonder-blocks-core": "^4.
|
|
19
|
-
"@khanacademy/wonder-blocks-dropdown": "^2.6.
|
|
20
|
-
"@khanacademy/wonder-blocks-icon": "^1.2.
|
|
21
|
-
"@khanacademy/wonder-blocks-layout": "^1.4.
|
|
18
|
+
"@khanacademy/wonder-blocks-core": "^4.3.0",
|
|
19
|
+
"@khanacademy/wonder-blocks-dropdown": "^2.6.6",
|
|
20
|
+
"@khanacademy/wonder-blocks-icon": "^1.2.26",
|
|
21
|
+
"@khanacademy/wonder-blocks-layout": "^1.4.8",
|
|
22
22
|
"@khanacademy/wonder-blocks-spacing": "^3.0.5",
|
|
23
|
-
"@khanacademy/wonder-blocks-typography": "^1.1.
|
|
23
|
+
"@khanacademy/wonder-blocks-typography": "^1.1.30"
|
|
24
24
|
},
|
|
25
25
|
"peerDependencies": {
|
|
26
26
|
"aphrodite": "^1.2.5",
|
|
@@ -2,37 +2,13 @@
|
|
|
2
2
|
import {defaultLabels} from "../birthday-picker.js";
|
|
3
3
|
|
|
4
4
|
export default {
|
|
5
|
-
defaultValue: {
|
|
6
|
-
control: {type: "text"},
|
|
7
|
-
description:
|
|
8
|
-
"The default value to populate the birthdate with. Should be in the format: `YYYY-MM-DD` (e.g. 2021-05-26). It's only used to populate the initial value as this is an uncontrolled component.",
|
|
9
|
-
table: {
|
|
10
|
-
type: {
|
|
11
|
-
summary: "string",
|
|
12
|
-
},
|
|
13
|
-
},
|
|
14
|
-
},
|
|
15
5
|
labels: {
|
|
16
|
-
control: {type: "object"},
|
|
17
|
-
description:
|
|
18
|
-
"The object containing the custom labels used inside this component.",
|
|
19
6
|
defaultValue: defaultLabels,
|
|
20
|
-
table: {
|
|
21
|
-
type: {
|
|
22
|
-
summary: "Labels",
|
|
23
|
-
detail: "{month?: string, year?: string, day?: string, errorMessage?: string}",
|
|
24
|
-
},
|
|
25
|
-
},
|
|
26
7
|
},
|
|
27
8
|
onChange: {
|
|
28
9
|
action: "onChanged",
|
|
29
|
-
description:
|
|
30
|
-
"Listen for changes to the birthdate. Could be a string in the `YYYY-MM-DD` format or `null`.",
|
|
31
10
|
table: {
|
|
32
11
|
category: "Events",
|
|
33
|
-
type: {
|
|
34
|
-
summary: "(date: ?string) => mixed",
|
|
35
|
-
},
|
|
36
12
|
},
|
|
37
13
|
},
|
|
38
14
|
};
|
|
@@ -64,6 +64,10 @@ BirthdayPickerWithDefaultValue.parameters = {
|
|
|
64
64
|
chromatic: {
|
|
65
65
|
disableSnapshot: true,
|
|
66
66
|
},
|
|
67
|
+
docs: {
|
|
68
|
+
storyDescription:
|
|
69
|
+
"This component is empty by default, but we can pass in a defined birthday by using the `defaultValue` prop.",
|
|
70
|
+
},
|
|
67
71
|
};
|
|
68
72
|
|
|
69
73
|
export const InvalidBirthdayPicker: StoryComponentType = Template.bind({});
|
|
@@ -73,6 +77,13 @@ InvalidBirthdayPicker.args = {
|
|
|
73
77
|
defaultValue: "2030-07-19",
|
|
74
78
|
};
|
|
75
79
|
|
|
80
|
+
InvalidBirthdayPicker.parameters = {
|
|
81
|
+
docs: {
|
|
82
|
+
storyDescription:
|
|
83
|
+
"In case the birthday is invalid, we display an error message indicating this problem.",
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
|
|
76
87
|
export const BirthdayPickerWithCustomLabels: StoryComponentType = Template.bind(
|
|
77
88
|
{},
|
|
78
89
|
);
|
|
@@ -87,3 +98,42 @@ BirthdayPickerWithCustomLabels.args = {
|
|
|
87
98
|
errorMessage: "Por favor seleccione una fecha válida.",
|
|
88
99
|
},
|
|
89
100
|
};
|
|
101
|
+
|
|
102
|
+
BirthdayPickerWithCustomLabels.parameters = {
|
|
103
|
+
docs: {
|
|
104
|
+
storyDescription:
|
|
105
|
+
"We can pass custom labels to the component. This can be helpful when we need to pass in translated strings. The default labels are: `Day`, `Month`, `Year` used as placeholders and 'Please select a valid birthdate.' to indicate an `errorMessage` when the validation fails. For more info about how to use this, refer to the `labels` prop in the Props table documentation above.",
|
|
106
|
+
},
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
export const DisabledBirthdayPicker: StoryComponentType = Template.bind({});
|
|
110
|
+
|
|
111
|
+
DisabledBirthdayPicker.args = {
|
|
112
|
+
onChange: () => {},
|
|
113
|
+
disabled: true,
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
DisabledBirthdayPicker.parameters = {
|
|
117
|
+
docs: {
|
|
118
|
+
storyDescription:
|
|
119
|
+
"A BirthdayPicker can be disabled by passing the `disabled` prop. This will disable all the dropdown controls and prevent them from being interacted with.",
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
export const BirthdayPickerWithYearAndMonthOnly: StoryComponentType =
|
|
124
|
+
Template.bind({});
|
|
125
|
+
|
|
126
|
+
BirthdayPickerWithYearAndMonthOnly.args = {
|
|
127
|
+
monthYearOnly: true,
|
|
128
|
+
onChange: (date: ?string) => {
|
|
129
|
+
// eslint-disable-next-line no-console
|
|
130
|
+
console.log("Date selected: ", date);
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
BirthdayPickerWithYearAndMonthOnly.parameters = {
|
|
135
|
+
docs: {
|
|
136
|
+
storyDescription:
|
|
137
|
+
"A BirthdayPicker can be configured to only show the year and month dropdowns. This can be useful when we want to display and collect a birthday that doesn't require the full DOB for privacy reasons.",
|
|
138
|
+
},
|
|
139
|
+
};
|
|
@@ -34,6 +34,18 @@ describe("BirthdayPicker", () => {
|
|
|
34
34
|
expect(yearPicker).toHaveTextContent("Year");
|
|
35
35
|
});
|
|
36
36
|
|
|
37
|
+
it("renders without the day field if monthYearOnly is set", () => {
|
|
38
|
+
// Arrange
|
|
39
|
+
|
|
40
|
+
// Act
|
|
41
|
+
render(<BirthdayPicker monthYearOnly={true} onChange={() => {}} />);
|
|
42
|
+
|
|
43
|
+
const dayPicker = screen.queryByTestId("birthday-picker-day");
|
|
44
|
+
|
|
45
|
+
// Assert
|
|
46
|
+
expect(dayPicker).not.toBeInTheDocument();
|
|
47
|
+
});
|
|
48
|
+
|
|
37
49
|
it("renders with a valid default value", () => {
|
|
38
50
|
// Arrange
|
|
39
51
|
const date = moment(today);
|
|
@@ -122,6 +134,22 @@ describe("BirthdayPicker", () => {
|
|
|
122
134
|
expect(yearPicker).toHaveTextContent("Year");
|
|
123
135
|
});
|
|
124
136
|
|
|
137
|
+
it("renders correctly the disabled state", () => {
|
|
138
|
+
// Arrange
|
|
139
|
+
|
|
140
|
+
// Act
|
|
141
|
+
render(<BirthdayPicker disabled={true} onChange={() => {}} />);
|
|
142
|
+
|
|
143
|
+
const monthPicker = screen.getByTestId("birthday-picker-month");
|
|
144
|
+
const dayPicker = screen.getByTestId("birthday-picker-day");
|
|
145
|
+
const yearPicker = screen.getByTestId("birthday-picker-year");
|
|
146
|
+
|
|
147
|
+
// Assert
|
|
148
|
+
expect(monthPicker).toBeDisabled();
|
|
149
|
+
expect(dayPicker).toBeDisabled();
|
|
150
|
+
expect(yearPicker).toBeDisabled();
|
|
151
|
+
});
|
|
152
|
+
|
|
125
153
|
it("renders an error with an invalid default value", async () => {
|
|
126
154
|
// Arrange
|
|
127
155
|
const defaultValue = "2021-02-31";
|
|
@@ -153,9 +181,9 @@ describe("BirthdayPicker", () => {
|
|
|
153
181
|
// Arrange
|
|
154
182
|
const onChange = jest.fn();
|
|
155
183
|
|
|
156
|
-
// Act
|
|
157
184
|
render(<BirthdayPicker onChange={onChange} />);
|
|
158
185
|
|
|
186
|
+
// Act
|
|
159
187
|
userEvent.click(screen.getByTestId("birthday-picker-month"));
|
|
160
188
|
const monthOption = await screen.findByRole("option", {
|
|
161
189
|
name: "Jul",
|
|
@@ -242,7 +270,6 @@ describe("BirthdayPicker", () => {
|
|
|
242
270
|
// Arrange
|
|
243
271
|
const onChange = jest.fn();
|
|
244
272
|
|
|
245
|
-
// Act
|
|
246
273
|
render(
|
|
247
274
|
<BirthdayPicker
|
|
248
275
|
defaultValue="2017-07-17"
|
|
@@ -250,6 +277,7 @@ describe("BirthdayPicker", () => {
|
|
|
250
277
|
/>,
|
|
251
278
|
);
|
|
252
279
|
|
|
280
|
+
// Act
|
|
253
281
|
userEvent.click(screen.getByTestId("birthday-picker-month"));
|
|
254
282
|
const monthOption = await screen.findByRole("option", {
|
|
255
283
|
name: "Aug",
|
|
@@ -311,7 +339,6 @@ describe("BirthdayPicker", () => {
|
|
|
311
339
|
// Arrange
|
|
312
340
|
const onChange = jest.fn();
|
|
313
341
|
|
|
314
|
-
// Act
|
|
315
342
|
render(
|
|
316
343
|
<BirthdayPicker
|
|
317
344
|
defaultValue="2021-02-31"
|
|
@@ -319,6 +346,7 @@ describe("BirthdayPicker", () => {
|
|
|
319
346
|
/>,
|
|
320
347
|
);
|
|
321
348
|
|
|
349
|
+
// Act
|
|
322
350
|
userEvent.click(screen.getByTestId("birthday-picker-year"));
|
|
323
351
|
const yearOption = await screen.findByRole("option", {
|
|
324
352
|
name: "2020",
|
|
@@ -330,6 +358,68 @@ describe("BirthdayPicker", () => {
|
|
|
330
358
|
// Assert
|
|
331
359
|
expect(onChange).toHaveBeenCalledTimes(1);
|
|
332
360
|
});
|
|
361
|
+
|
|
362
|
+
it("onChange triggers the first day of the month when monthYearOnly is set", async () => {
|
|
363
|
+
// Arrange
|
|
364
|
+
const onChange = jest.fn();
|
|
365
|
+
|
|
366
|
+
render(<BirthdayPicker monthYearOnly={true} onChange={onChange} />);
|
|
367
|
+
|
|
368
|
+
// Act
|
|
369
|
+
userEvent.click(screen.getByTestId("birthday-picker-month"));
|
|
370
|
+
const monthOption = await screen.findByRole("option", {
|
|
371
|
+
name: "Aug",
|
|
372
|
+
});
|
|
373
|
+
userEvent.click(monthOption, undefined, {
|
|
374
|
+
skipPointerEventsCheck: true,
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
userEvent.click(screen.getByTestId("birthday-picker-year"));
|
|
378
|
+
const yearOption = await screen.findByRole("option", {
|
|
379
|
+
name: "2018",
|
|
380
|
+
});
|
|
381
|
+
userEvent.click(yearOption, undefined, {
|
|
382
|
+
skipPointerEventsCheck: true,
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
// Assert
|
|
386
|
+
// Verify that we passed the first day of the month
|
|
387
|
+
expect(onChange).toHaveBeenCalledWith("2018-08-01");
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
it("onChange triggers the passed-in day intact when defaultValue and monthYearOnly are set", async () => {
|
|
391
|
+
// Arrange
|
|
392
|
+
const onChange = jest.fn();
|
|
393
|
+
|
|
394
|
+
render(
|
|
395
|
+
<BirthdayPicker
|
|
396
|
+
defaultValue="2017-07-17"
|
|
397
|
+
monthYearOnly={true}
|
|
398
|
+
onChange={onChange}
|
|
399
|
+
/>,
|
|
400
|
+
);
|
|
401
|
+
|
|
402
|
+
// Act
|
|
403
|
+
userEvent.click(screen.getByTestId("birthday-picker-month"));
|
|
404
|
+
const monthOption = await screen.findByRole("option", {
|
|
405
|
+
name: "Aug",
|
|
406
|
+
});
|
|
407
|
+
userEvent.click(monthOption, undefined, {
|
|
408
|
+
skipPointerEventsCheck: true,
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
userEvent.click(screen.getByTestId("birthday-picker-year"));
|
|
412
|
+
const yearOption = await screen.findByRole("option", {
|
|
413
|
+
name: "2018",
|
|
414
|
+
});
|
|
415
|
+
userEvent.click(yearOption, undefined, {
|
|
416
|
+
skipPointerEventsCheck: true,
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
// Assert
|
|
420
|
+
// Verify that we passed the same day originally passed in.
|
|
421
|
+
expect(onChange).toHaveBeenCalledWith("2018-08-17");
|
|
422
|
+
});
|
|
333
423
|
});
|
|
334
424
|
|
|
335
425
|
describe("labels", () => {
|
|
@@ -39,11 +39,26 @@ type Props = {|
|
|
|
39
39
|
*/
|
|
40
40
|
defaultValue?: string,
|
|
41
41
|
|
|
42
|
+
/**
|
|
43
|
+
* Whether the birthdate fields are disabled.
|
|
44
|
+
*/
|
|
45
|
+
disabled?: boolean,
|
|
46
|
+
|
|
42
47
|
/**
|
|
43
48
|
* The object containing the custom labels used inside this component.
|
|
44
49
|
*/
|
|
45
50
|
labels?: Labels,
|
|
46
51
|
|
|
52
|
+
/**
|
|
53
|
+
* Whether we want to hide the day field.
|
|
54
|
+
*
|
|
55
|
+
* **NOTE:** We will set the day to the _first_ day of the _selected_ month
|
|
56
|
+
* if the day field is hidden. Please make sure to modify the passed date
|
|
57
|
+
* value to fit different needs (e.g. if you want to set the _first_ day of
|
|
58
|
+
* the _following_ month instead).
|
|
59
|
+
*/
|
|
60
|
+
monthYearOnly?: boolean,
|
|
61
|
+
|
|
47
62
|
/**
|
|
48
63
|
* Listen for changes to the birthdate. Could be a string in the YYYY-MM-DD
|
|
49
64
|
* format or `null`.
|
|
@@ -82,6 +97,13 @@ export const defaultLabels: Labels = Object.freeze({
|
|
|
82
97
|
day: "Day",
|
|
83
98
|
});
|
|
84
99
|
|
|
100
|
+
// Default minWidth value when we include the full DOB.
|
|
101
|
+
const FIELD_MIN_WIDTH_FULL = 110;
|
|
102
|
+
|
|
103
|
+
// Alternative minWidth value when we hide the day field.
|
|
104
|
+
// See: https://www.figma.com/file/uJZi9ZvuEz5N8GJ3HqKFAa/(2021)-Account-records?node-id=20%3A398
|
|
105
|
+
const FIELD_MIN_WIDTH_MONTH_YEAR = 167;
|
|
106
|
+
|
|
85
107
|
/**
|
|
86
108
|
* Birthday Picker. Similar to a datepicker, but specifically for birthdates.
|
|
87
109
|
* We don't want to show a calendar in this case as it can be quite tedious to
|
|
@@ -129,10 +151,10 @@ export default class BirthdayPicker extends React.Component<Props, State> {
|
|
|
129
151
|
* Calculates the initial state values based on the default value.
|
|
130
152
|
*/
|
|
131
153
|
getStateFromDefault(): State {
|
|
132
|
-
const {defaultValue} = this.props;
|
|
154
|
+
const {defaultValue, monthYearOnly} = this.props;
|
|
133
155
|
const initialState: State = {
|
|
134
156
|
month: null,
|
|
135
|
-
day: null,
|
|
157
|
+
day: monthYearOnly ? "1" : null,
|
|
136
158
|
year: null,
|
|
137
159
|
error: null,
|
|
138
160
|
};
|
|
@@ -247,59 +269,99 @@ export default class BirthdayPicker extends React.Component<Props, State> {
|
|
|
247
269
|
);
|
|
248
270
|
}
|
|
249
271
|
|
|
250
|
-
|
|
251
|
-
const {
|
|
272
|
+
renderMonth(): React.Node {
|
|
273
|
+
const {disabled, monthYearOnly} = this.props;
|
|
274
|
+
const {month} = this.state;
|
|
275
|
+
const minWidth = monthYearOnly
|
|
276
|
+
? FIELD_MIN_WIDTH_MONTH_YEAR
|
|
277
|
+
: FIELD_MIN_WIDTH_FULL;
|
|
278
|
+
|
|
279
|
+
return (
|
|
280
|
+
<SingleSelect
|
|
281
|
+
disabled={disabled}
|
|
282
|
+
placeholder={this.labels.month}
|
|
283
|
+
onChange={this.handleMonthChange}
|
|
284
|
+
selectedValue={month}
|
|
285
|
+
style={{minWidth}}
|
|
286
|
+
testId="birthday-picker-month"
|
|
287
|
+
>
|
|
288
|
+
{moment.monthsShort().map((month, i) => (
|
|
289
|
+
<OptionItem key={month} label={month} value={String(i)} />
|
|
290
|
+
))}
|
|
291
|
+
</SingleSelect>
|
|
292
|
+
);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
maybeRenderDay(): ?React.Node {
|
|
296
|
+
const {disabled, monthYearOnly} = this.props;
|
|
297
|
+
const {day} = this.state;
|
|
298
|
+
|
|
299
|
+
// Hide the day field if the month/year only mode is enabled.
|
|
300
|
+
if (monthYearOnly) {
|
|
301
|
+
return null;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
return (
|
|
305
|
+
<>
|
|
306
|
+
<Strut size={Spacing.xSmall_8} />
|
|
307
|
+
<SingleSelect
|
|
308
|
+
disabled={disabled}
|
|
309
|
+
placeholder={this.labels.day}
|
|
310
|
+
onChange={this.handleDayChange}
|
|
311
|
+
selectedValue={day}
|
|
312
|
+
style={{minWidth: 100}}
|
|
313
|
+
testId="birthday-picker-day"
|
|
314
|
+
>
|
|
315
|
+
{Array.from(Array(31)).map((_, day) => (
|
|
316
|
+
<OptionItem
|
|
317
|
+
key={String(day + 1)}
|
|
318
|
+
label={String(day + 1)}
|
|
319
|
+
value={String(day + 1)}
|
|
320
|
+
/>
|
|
321
|
+
))}
|
|
322
|
+
</SingleSelect>
|
|
323
|
+
</>
|
|
324
|
+
);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
renderYear(): React.Node {
|
|
328
|
+
const {disabled, monthYearOnly} = this.props;
|
|
329
|
+
const {year} = this.state;
|
|
330
|
+
const minWidth = monthYearOnly
|
|
331
|
+
? FIELD_MIN_WIDTH_MONTH_YEAR
|
|
332
|
+
: FIELD_MIN_WIDTH_FULL;
|
|
333
|
+
|
|
334
|
+
return (
|
|
335
|
+
<SingleSelect
|
|
336
|
+
disabled={disabled}
|
|
337
|
+
placeholder={this.labels.year}
|
|
338
|
+
onChange={this.handleYearChange}
|
|
339
|
+
selectedValue={year}
|
|
340
|
+
style={{minWidth}}
|
|
341
|
+
testId="birthday-picker-year"
|
|
342
|
+
>
|
|
343
|
+
{Array.from(Array(120)).map((_, yearOffset) => (
|
|
344
|
+
<OptionItem
|
|
345
|
+
key={String(CUR_YEAR - yearOffset)}
|
|
346
|
+
label={String(CUR_YEAR - yearOffset)}
|
|
347
|
+
value={String(CUR_YEAR - yearOffset)}
|
|
348
|
+
/>
|
|
349
|
+
))}
|
|
350
|
+
</SingleSelect>
|
|
351
|
+
);
|
|
352
|
+
}
|
|
252
353
|
|
|
354
|
+
render(): React.Element<any> {
|
|
253
355
|
return (
|
|
254
356
|
<>
|
|
255
357
|
<View testId="birthday-picker" style={{flexDirection: "row"}}>
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
style={{minWidth: 110}}
|
|
261
|
-
testId="birthday-picker-month"
|
|
262
|
-
>
|
|
263
|
-
{moment.monthsShort().map((month, i) => (
|
|
264
|
-
<OptionItem
|
|
265
|
-
key={month}
|
|
266
|
-
label={month}
|
|
267
|
-
value={String(i)}
|
|
268
|
-
/>
|
|
269
|
-
))}
|
|
270
|
-
</SingleSelect>
|
|
271
|
-
<Strut size={Spacing.xSmall_8} />
|
|
272
|
-
<SingleSelect
|
|
273
|
-
placeholder={this.labels.day}
|
|
274
|
-
onChange={this.handleDayChange}
|
|
275
|
-
selectedValue={day}
|
|
276
|
-
style={{minWidth: 100}}
|
|
277
|
-
testId="birthday-picker-day"
|
|
278
|
-
>
|
|
279
|
-
{Array.from(Array(31)).map((_, day) => (
|
|
280
|
-
<OptionItem
|
|
281
|
-
key={String(day + 1)}
|
|
282
|
-
label={String(day + 1)}
|
|
283
|
-
value={String(day + 1)}
|
|
284
|
-
/>
|
|
285
|
-
))}
|
|
286
|
-
</SingleSelect>
|
|
358
|
+
{this.renderMonth()}
|
|
359
|
+
|
|
360
|
+
{this.maybeRenderDay()}
|
|
361
|
+
|
|
287
362
|
<Strut size={Spacing.xSmall_8} />
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
onChange={this.handleYearChange}
|
|
291
|
-
selectedValue={year}
|
|
292
|
-
style={{minWidth: 110}}
|
|
293
|
-
testId="birthday-picker-year"
|
|
294
|
-
>
|
|
295
|
-
{Array.from(Array(120)).map((_, yearOffset) => (
|
|
296
|
-
<OptionItem
|
|
297
|
-
key={String(CUR_YEAR - yearOffset)}
|
|
298
|
-
label={String(CUR_YEAR - yearOffset)}
|
|
299
|
-
value={String(CUR_YEAR - yearOffset)}
|
|
300
|
-
/>
|
|
301
|
-
))}
|
|
302
|
-
</SingleSelect>
|
|
363
|
+
|
|
364
|
+
{this.renderYear()}
|
|
303
365
|
</View>
|
|
304
366
|
{this.maybeRenderError()}
|
|
305
367
|
</>
|