@atlaskit/datetime-picker 15.1.4 → 15.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.
@@ -4,22 +4,29 @@ import _defineProperty from "@babel/runtime/helpers/defineProperty";
4
4
  * @jsxRuntime classic
5
5
  * @jsx jsx
6
6
  */
7
- import { Component } from 'react';
7
+ import { Component, createRef } from 'react';
8
8
 
9
9
  // eslint-disable-next-line @atlaskit/ui-styling-standard/use-compiled -- Ignored via go/DSP-18766
10
- import { jsx } from '@emotion/react';
10
+ import { css, jsx } from '@emotion/react';
11
11
  import { isValid, parseISO } from 'date-fns';
12
+ // This is a deprecated component but we will be able to use the actual hook
13
+ // version very soon from converting this to functional. And also React 18 is on
14
+ // the horizon
15
+ import { UID } from 'react-uid';
12
16
  import { createAndFireEvent, withAnalyticsContext, withAnalyticsEvents } from '@atlaskit/analytics-next';
13
17
  import CalendarIcon from '@atlaskit/icon/glyph/calendar';
14
18
  import { createLocalizationProvider } from '@atlaskit/locale';
19
+ import { Pressable, xcss } from '@atlaskit/primitives';
15
20
  import Select, { mergeStyles } from '@atlaskit/select';
21
+ import { N500, N70 } from '@atlaskit/theme/colors';
22
+ import VisuallyHidden from '@atlaskit/visually-hidden';
16
23
  import { EmptyComponent } from '../internal';
17
24
  import { formatDate, getParsedISO, getPlaceholder, isDateDisabled, parseDate } from '../internal/date-picker-migration';
18
25
  import { Menu } from '../internal/menu';
19
26
  import { getSafeCalendarValue, getShortISOString } from '../internal/parse-date';
20
27
  import { makeSingleValue } from '../internal/single-value';
21
28
  const packageName = "@atlaskit/datetime-picker";
22
- const packageVersion = "15.1.4";
29
+ const packageVersion = "15.2.0";
23
30
  const datePickerDefaultProps = {
24
31
  defaultIsOpen: false,
25
32
  defaultValue: '',
@@ -36,11 +43,45 @@ const datePickerDefaultProps = {
36
43
  // Not including a default prop for value as it will
37
44
  // Make the component a controlled component
38
45
  };
46
+ const pickerContainerStyles = css({
47
+ position: 'relative'
48
+ });
49
+ const iconContainerStyles = css({
50
+ display: 'flex',
51
+ height: '100%',
52
+ position: 'absolute',
53
+ alignItems: 'center',
54
+ flexBasis: 'inherit',
55
+ color: `var(--ds-text-subtlest, ${N70})`,
56
+ insetBlockStart: 0,
57
+ insetInlineEnd: 0,
58
+ transition: `color 150ms`,
59
+ '&:hover': {
60
+ color: `var(--ds-text-subtle, ${N500})`
61
+ }
62
+ });
63
+ const iconSpacingWithClearButtonStyles = css({
64
+ marginInlineEnd: "var(--ds-space-400, 2rem)"
65
+ });
66
+ const iconSpacingWithoutClearButtonStyles = css({
67
+ marginInlineEnd: "var(--ds-space-025, 0.125rem)"
68
+ });
69
+ const calendarButtonStyles = xcss({
70
+ borderRadius: 'border.radius',
71
+ ':hover': {
72
+ backgroundColor: 'color.background.neutral.subtle.hovered'
73
+ },
74
+ ':active': {
75
+ backgroundColor: 'color.background.neutral.subtle.pressed'
76
+ }
77
+ });
39
78
  class DatePickerComponent extends Component {
40
79
  constructor(props) {
41
80
  var _this$props$selectPro2;
42
81
  super(props);
43
82
  _defineProperty(this, "containerRef", null);
83
+ _defineProperty(this, "calendarRef", /*#__PURE__*/createRef());
84
+ _defineProperty(this, "calendarButtonRef", /*#__PURE__*/createRef());
44
85
  // All state needs to be accessed via this function so that the state is mapped from props
45
86
  // correctly to allow controlled/uncontrolled usage.
46
87
  _defineProperty(this, "getValue", () => {
@@ -63,22 +104,29 @@ class DatePickerComponent extends Component {
63
104
  _defineProperty(this, "onCalendarSelect", ({
64
105
  iso
65
106
  }) => {
66
- var _this$containerRef;
67
107
  this.setState({
68
108
  selectInputValue: '',
69
109
  isOpen: false,
70
110
  calendarValue: iso,
71
- value: iso
111
+ value: iso,
112
+ wasOpenedFromCalendarButton: false
72
113
  });
73
114
  this.props.onChange(iso);
74
115
 
75
- // Not ideal, and the alternative is to place a ref on the inner input of the Select
76
- // but that would require a lot of extra work on the Select component just for this
77
- // focus functionality. While that would be the 'right react' way to do it, it doesnt
78
- // post any other benefits; performance wise, we are only searching within the
79
- // container, making it quick.
80
- const innerCombobox = (_this$containerRef = this.containerRef) === null || _this$containerRef === void 0 ? void 0 : _this$containerRef.querySelector('[role="combobox"]');
81
- innerCombobox === null || innerCombobox === void 0 ? void 0 : innerCombobox.focus();
116
+ // Yes, this is not ideal. The alternative is to be able to place a ref
117
+ // on the inner input of Select itself, which would require a lot of
118
+ // extra stuff in the Select component for only this one thing. While
119
+ // this would be more "React-y", it doesn't seem to pose any other
120
+ // benefits. Performance-wise, we are only searching within the
121
+ // container, so it's quick.
122
+ if (this.state.wasOpenedFromCalendarButton) {
123
+ var _this$calendarButtonR;
124
+ (_this$calendarButtonR = this.calendarButtonRef.current) === null || _this$calendarButtonR === void 0 ? void 0 : _this$calendarButtonR.focus();
125
+ } else {
126
+ var _this$containerRef;
127
+ const innerCombobox = (_this$containerRef = this.containerRef) === null || _this$containerRef === void 0 ? void 0 : _this$containerRef.querySelector('[role="combobox"]');
128
+ innerCombobox === null || innerCombobox === void 0 ? void 0 : innerCombobox.focus();
129
+ }
82
130
  this.setState({
83
131
  isOpen: false
84
132
  });
@@ -86,7 +134,8 @@ class DatePickerComponent extends Component {
86
134
  _defineProperty(this, "onInputClick", () => {
87
135
  if (!this.props.isDisabled && !this.getIsOpen()) {
88
136
  this.setState({
89
- isOpen: true
137
+ isOpen: true,
138
+ wasOpenedFromCalendarButton: false
90
139
  });
91
140
  }
92
141
  });
@@ -96,7 +145,8 @@ class DatePickerComponent extends Component {
96
145
  if (!((_this$containerRef2 = this.containerRef) !== null && _this$containerRef2 !== void 0 && _this$containerRef2.contains(newlyFocusedElement))) {
97
146
  this.setState({
98
147
  isOpen: false,
99
- shouldSetFocusOnCurrentDay: false
148
+ shouldSetFocusOnCurrentDay: false,
149
+ wasOpenedFromCalendarButton: false
100
150
  });
101
151
  this.props.onBlur(event);
102
152
  }
@@ -119,7 +169,8 @@ class DatePickerComponent extends Component {
119
169
  // container. Makes keyboard accessibility of calendar possible
120
170
  this.setState({
121
171
  isOpen: false,
122
- isFocused: false
172
+ isFocused: false,
173
+ wasOpenedFromCalendarButton: false
123
174
  });
124
175
  }
125
176
  });
@@ -135,9 +186,11 @@ class DatePickerComponent extends Component {
135
186
  });
136
187
  } else {
137
188
  this.setState({
138
- isOpen: true,
189
+ // Don't open when focused into via keyboard if the calendar button is present
190
+ isOpen: !this.props.shouldShowCalendarButton,
139
191
  calendarValue: value,
140
- isFocused: true
192
+ isFocused: true,
193
+ wasOpenedFromCalendarButton: false
141
194
  });
142
195
  }
143
196
  this.props.onFocus(event);
@@ -160,11 +213,11 @@ class DatePickerComponent extends Component {
160
213
  }
161
214
  }
162
215
  this.setState({
163
- isOpen: true
216
+ isOpen: true,
217
+ wasOpenedFromCalendarButton: false
164
218
  });
165
219
  });
166
220
  _defineProperty(this, "onInputKeyDown", event => {
167
- var _this$containerRef4;
168
221
  const {
169
222
  calendarValue
170
223
  } = this.state;
@@ -174,7 +227,8 @@ class DatePickerComponent extends Component {
174
227
  // If the input is focused and the calendar is not visible, handle space and enter clicks
175
228
  if (!this.state.isOpen && (keyPressed === 'enter' || keyPressed === ' ')) {
176
229
  this.setState({
177
- isOpen: true
230
+ isOpen: true,
231
+ wasOpenedFromCalendarButton: false
178
232
  });
179
233
  }
180
234
  switch (keyPressed) {
@@ -185,11 +239,18 @@ class DatePickerComponent extends Component {
185
239
  // this would be more "React-y", it doesn't seem to pose any other
186
240
  // benefits. Performance-wise, we are only searching within the
187
241
  // container, so it's quick.
188
- const innerCombobox = (_this$containerRef4 = this.containerRef) === null || _this$containerRef4 === void 0 ? void 0 : _this$containerRef4.querySelector('[role="combobox"]');
189
- innerCombobox === null || innerCombobox === void 0 ? void 0 : innerCombobox.focus();
242
+ if (this.state.wasOpenedFromCalendarButton) {
243
+ var _this$calendarButtonR2;
244
+ (_this$calendarButtonR2 = this.calendarButtonRef.current) === null || _this$calendarButtonR2 === void 0 ? void 0 : _this$calendarButtonR2.focus();
245
+ } else {
246
+ var _this$containerRef4;
247
+ const innerCombobox = (_this$containerRef4 = this.containerRef) === null || _this$containerRef4 === void 0 ? void 0 : _this$containerRef4.querySelector('[role="combobox"]');
248
+ innerCombobox === null || innerCombobox === void 0 ? void 0 : innerCombobox.focus();
249
+ }
190
250
  this.setState({
191
251
  isOpen: false,
192
- shouldSetFocusOnCurrentDay: false
252
+ shouldSetFocusOnCurrentDay: false,
253
+ wasOpenedFromCalendarButton: false
193
254
  });
194
255
  break;
195
256
  case 'backspace':
@@ -224,7 +285,8 @@ class DatePickerComponent extends Component {
224
285
  selectInputValue: '',
225
286
  isOpen: false,
226
287
  value: safeCalendarValue,
227
- calendarValue: safeCalendarValue
288
+ calendarValue: safeCalendarValue,
289
+ wasOpenedFromCalendarButton: false
228
290
  });
229
291
  if (valueChanged) {
230
292
  this.props.onChange(safeCalendarValue);
@@ -243,6 +305,38 @@ class DatePickerComponent extends Component {
243
305
  break;
244
306
  }
245
307
  });
308
+ _defineProperty(this, "onCalendarButtonKeyDown", e => {
309
+ // We want to stop this from triggering other keydown events, particularly
310
+ // for space and enter presses. Otherwise, it opens and then closes
311
+ // immediately.
312
+ if (e.type === 'keydown') {
313
+ e.stopPropagation();
314
+ }
315
+ this.setState({
316
+ isKeyDown: true,
317
+ wasOpenedFromCalendarButton: true
318
+ });
319
+ });
320
+ // This event handler is triggered from both keydown and click. It's weird.
321
+ _defineProperty(this, "onCalendarButtonClick", e => {
322
+ this.setState({
323
+ isOpen: !this.state.isOpen,
324
+ wasOpenedFromCalendarButton: true
325
+ }, () => {
326
+ var _this$calendarRef, _this$calendarRef$cur, _this$calendarRef$cur2;
327
+ // We don't want the focus to move if this is a click event
328
+ if (!this.state.isKeyDown) {
329
+ return;
330
+ }
331
+ this.setState({
332
+ isKeyDown: false
333
+ });
334
+
335
+ // Focus on the first button within the calendar
336
+ (_this$calendarRef = this.calendarRef) === null || _this$calendarRef === void 0 ? void 0 : (_this$calendarRef$cur = _this$calendarRef.current) === null || _this$calendarRef$cur === void 0 ? void 0 : (_this$calendarRef$cur2 = _this$calendarRef$cur.querySelector('button')) === null || _this$calendarRef$cur2 === void 0 ? void 0 : _this$calendarRef$cur2.focus();
337
+ });
338
+ e.stopPropagation();
339
+ });
246
340
  _defineProperty(this, "onClear", () => {
247
341
  let changedState = {
248
342
  value: '',
@@ -285,6 +379,7 @@ class DatePickerComponent extends Component {
285
379
  }
286
380
  });
287
381
  this.state = {
382
+ isKeyDown: false,
288
383
  isOpen: this.props.defaultIsOpen,
289
384
  isFocused: false,
290
385
  clearingFromIcon: false,
@@ -293,7 +388,8 @@ class DatePickerComponent extends Component {
293
388
  calendarValue: this.props.value || this.props.defaultValue || getShortISOString(new Date()),
294
389
  l10n: createLocalizationProvider(this.props.locale),
295
390
  locale: this.props.locale,
296
- shouldSetFocusOnCurrentDay: false
391
+ shouldSetFocusOnCurrentDay: false,
392
+ wasOpenedFromCalendarButton: false
297
393
  };
298
394
  }
299
395
  static getDerivedStateFromProps(nextProps, prevState) {
@@ -311,13 +407,15 @@ class DatePickerComponent extends Component {
311
407
  appearance = 'default',
312
408
  'aria-describedby': ariaDescribedBy,
313
409
  autoFocus = false,
410
+ hideIcon = false,
411
+ openCalendarLabel = 'Open calendar',
314
412
  disabled,
315
413
  disabledDateFilter,
316
- hideIcon = false,
317
- // TODO: Resolve this typing to be more intuitive
318
414
  icon = CalendarIcon,
319
415
  id = '',
320
416
  innerProps = {},
417
+ inputLabel = 'Date picker',
418
+ inputLabelId,
321
419
  isDisabled = false,
322
420
  isInvalid = false,
323
421
  label = '',
@@ -328,6 +426,7 @@ class DatePickerComponent extends Component {
328
426
  nextMonthLabel,
329
427
  previousMonthLabel,
330
428
  selectProps = {},
429
+ shouldShowCalendarButton,
331
430
  spacing = 'default',
332
431
  testId,
333
432
  weekStartDay
@@ -346,7 +445,7 @@ class DatePickerComponent extends Component {
346
445
  lang: this.props.locale
347
446
  });
348
447
  const selectComponents = {
349
- DropdownIndicator: dropDownIcon,
448
+ DropdownIndicator: shouldShowCalendarButton ? EmptyComponent : dropDownIcon,
350
449
  Menu,
351
450
  SingleValue,
352
451
  ...(!showClearIndicator && {
@@ -366,6 +465,7 @@ class DatePickerComponent extends Component {
366
465
  calendarDisabledDateFilter: disabledDateFilter,
367
466
  calendarMaxDate: maxDate,
368
467
  calendarMinDate: minDate,
468
+ calendarRef: this.calendarRef,
369
469
  calendarValue: value && getShortISOString(parseISO(value)),
370
470
  calendarView: calendarValue,
371
471
  onCalendarChange: this.onCalendarChange,
@@ -374,7 +474,8 @@ class DatePickerComponent extends Component {
374
474
  calendarWeekStartDay: weekStartDay,
375
475
  shouldSetFocusOnCurrentDay: this.state.shouldSetFocusOnCurrentDay
376
476
  };
377
- //@ts-ignore react-select unsupported props
477
+
478
+ // @ts-ignore -- Argument of type 'StylesConfig<OptionType, false, GroupBase<OptionType>>' is not assignable to parameter of type 'StylesConfig<OptionType, boolean, GroupBase<OptionType>>'.
378
479
  const mergedStyles = mergeStyles(selectStyles, {
379
480
  control: base => ({
380
481
  ...base,
@@ -395,10 +496,14 @@ class DatePickerComponent extends Component {
395
496
  }),
396
497
  value
397
498
  } : null;
499
+
500
+ // `label` takes precedence of the `inputLabel`
501
+ const fullopenCalendarLabel = label || inputLabel ? `${label || inputLabel} , ${openCalendarLabel}` : openCalendarLabel;
398
502
  return (
399
503
  // These event handlers must be on this element because the events come
400
504
  // from different child elements.
401
505
  jsx("div", _extends({}, innerProps, {
506
+ css: pickerContainerStyles,
402
507
  role: "presentation",
403
508
  onBlur: this.onContainerBlur,
404
509
  onFocus: this.onContainerFocus,
@@ -417,7 +522,14 @@ class DatePickerComponent extends Component {
417
522
  "aria-describedby": ariaDescribedBy,
418
523
  "aria-label": label || undefined,
419
524
  autoFocus: autoFocus,
420
- closeMenuOnSelect: true,
525
+ closeMenuOnSelect: true
526
+ // FOr some reason, this and the below `styles` type error _only_ show
527
+ // up when you alter some of the properties in the `selectComponents`
528
+ // object. These errors are still present, and I suspect have always
529
+ // been present, without changing the unrelated code. Ignoring as the
530
+ // component still works as expected despite this error. And also
531
+ // because the select refresh team may solve it later.
532
+ ,
421
533
  components: selectComponents,
422
534
  enableAnimation: false,
423
535
  inputId: id,
@@ -442,7 +554,7 @@ class DatePickerComponent extends Component {
442
554
  spacing: spacing,
443
555
  testId: testId
444
556
  // These aren't part of `Select`'s API, but we're using them here.
445
- //@ts-ignore react-select unsupported props
557
+ // @ts-ignore -- Property 'calendarContainerRef' does not exist on type 'IntrinsicAttributes & LibraryManagedAttributes<(<Option extends unknown = OptionType, IsMulti extends boolean = false>(props: AtlaskitSelectProps<Option, IsMulti> & { ...; }) => Element), AtlaskitSelectProps<...> & { ...; }>'.
446
558
  ,
447
559
  calendarContainerRef: calendarProps.calendarContainerRef,
448
560
  calendarDisabled: calendarProps.calendarDisabled,
@@ -450,6 +562,7 @@ class DatePickerComponent extends Component {
450
562
  calendarLocale: calendarProps.calendarLocale,
451
563
  calendarMaxDate: calendarProps.calendarMaxDate,
452
564
  calendarMinDate: calendarProps.calendarMinDate,
565
+ calendarRef: calendarProps.calendarRef,
453
566
  calendarValue: calendarProps.calendarValue,
454
567
  calendarView: calendarProps.calendarView,
455
568
  calendarWeekStartDay: calendarProps.calendarWeekStartDay,
@@ -458,7 +571,28 @@ class DatePickerComponent extends Component {
458
571
  onCalendarSelect: calendarProps.onCalendarSelect,
459
572
  previousMonthLabel: previousMonthLabel,
460
573
  shouldSetFocusOnCurrentDay: calendarProps.shouldSetFocusOnCurrentDay
461
- })))
574
+ })), shouldShowCalendarButton && !isDisabled ? jsx(UID, {
575
+ name: id => `open-calendar-label--${id}`
576
+ }, openCalendarLabelId => jsx("div", {
577
+ css: [iconContainerStyles, value && !hideIcon ? iconSpacingWithClearButtonStyles : iconSpacingWithoutClearButtonStyles]
578
+ }, inputLabelId && jsx(VisuallyHidden, {
579
+ id: openCalendarLabelId
580
+ }, ", ", openCalendarLabel), jsx(Pressable, _extends({}, inputLabelId ? {
581
+ 'aria-labelledby': `${inputLabelId} ${openCalendarLabelId}`
582
+ } : {
583
+ 'aria-label': fullopenCalendarLabel
584
+ }, {
585
+ onClick: this.onCalendarButtonClick,
586
+ onKeyDown: this.onCalendarButtonKeyDown,
587
+ ref: this.calendarButtonRef,
588
+ testId: testId && `${testId}--open-calendar-button`,
589
+ type: "button",
590
+ backgroundColor: "color.background.neutral.subtle",
591
+ padding: "space.050",
592
+ xcss: calendarButtonStyles
593
+ }), jsx(CalendarIcon, {
594
+ label: ""
595
+ })))) : null)
462
596
  );
463
597
  }
464
598
  }
@@ -18,7 +18,7 @@ import { convertTokens } from '../internal/parse-tokens';
18
18
  import DatePicker from './date-picker';
19
19
  import TimePicker from './time-picker';
20
20
  const packageName = "@atlaskit/datetime-picker";
21
- const packageVersion = "15.1.4";
21
+ const packageVersion = "15.2.0";
22
22
  // Make DatePicker 50% the width of DateTimePicker
23
23
  // If rendering an icon container, shrink the TimePicker
24
24
  const datePickerContainerStyles = css({
@@ -315,6 +315,7 @@ class DateTimePickerComponent extends React.Component {
315
315
  placeholder: datePickerProps.placeholder,
316
316
  previousMonthLabel: datePickerProps.previousMonthLabel,
317
317
  selectProps: mergedDatePickerSelectProps,
318
+ shouldShowCalendarButton: datePickerProps.shouldShowCalendarButton,
318
319
  spacing: datePickerProps.spacing || spacing,
319
320
  testId: testId && `${testId}--datepicker` || datePickerProps.testId,
320
321
  value: dateValue,
@@ -11,7 +11,7 @@ import parseTime from '../internal/parse-time';
11
11
  import { convertTokens } from '../internal/parse-tokens';
12
12
  import { makeSingleValue } from '../internal/single-value';
13
13
  const packageName = "@atlaskit/datetime-picker";
14
- const packageVersion = "15.1.4";
14
+ const packageVersion = "15.2.0";
15
15
  const menuStyles = {
16
16
  /* Need to remove default absolute positioning as that causes issues with position fixed */
17
17
  position: 'static',
@@ -230,7 +230,8 @@ const TimePicker = /*#__PURE__*/forwardRef(({
230
230
  })
231
231
  };
232
232
  const renderIconContainer = Boolean(!hideIcon && value);
233
- // @ts-ignore - https://product-fabric.atlassian.net/browse/DSP-21000
233
+
234
+ // @ts-ignore -- Argument of type 'StylesConfig<OptionType, false, GroupBase<OptionType>>' is not assignable to parameter of type 'StylesConfig<OptionType, boolean, GroupBase<OptionType>>'.
234
235
  const mergedStyles = mergeStyles(selectStyles, {
235
236
  control: base => ({
236
237
  ...base
@@ -69,6 +69,7 @@ export const Menu = ({
69
69
  onChange: selectProps.onCalendarChange,
70
70
  onSelect: selectProps.onCalendarSelect,
71
71
  previousMonthLabel: selectProps.previousMonthLabel,
72
+ ref: selectProps.calendarRef,
72
73
  selected: [selectProps.calendarValue],
73
74
  shouldSetFocusOnCurrentDay: selectProps.shouldSetFocusOnCurrentDay,
74
75
  locale: selectProps.calendarLocale,