@northlight/ui 2.43.1 → 2.43.3

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.
@@ -1,24 +1,44 @@
1
- import React, { useRef } from 'react'
1
+ import React, { KeyboardEventHandler, useRef } from 'react'
2
2
  import { CalendarDuo } from '@northlight/icons'
3
- import { useButton } from '@react-aria/button'
4
3
  import { Button } from '../../../button'
5
4
  import { TriggerProps } from './types'
6
5
  import { Icon } from '../../../icon'
7
6
 
8
7
  export const Trigger = (props: TriggerProps) => {
9
- const { isDisabled, handleClick } = props
8
+ const {
9
+ id,
10
+ 'aria-haspopup': ariaHasPopup,
11
+ 'aria-label': ariaLabel,
12
+ 'aria-labelledby': ariaLabelledBy,
13
+ 'aria-describedby': ariaDescribedBy,
14
+ 'aria-expanded': ariaExpanded,
15
+ isDisabled,
16
+ handleClick,
17
+ } = props
10
18
  const ref = useRef<HTMLButtonElement>(null)
11
- const { buttonProps } = useButton(props, ref)
19
+
20
+ const handleKeyDown: KeyboardEventHandler<HTMLButtonElement> = (e) => {
21
+ if (e.key === 'Enter') {
22
+ e.preventDefault()
23
+ handleClick()
24
+ }
25
+ }
12
26
 
13
27
  return (
14
28
  <Button
15
- { ...buttonProps }
16
29
  ref={ ref }
30
+ id={ id }
31
+ aria-haspopup={ ariaHasPopup }
32
+ aria-label={ ariaLabel }
33
+ aria-labelledby={ ariaLabelledBy }
34
+ aria-describedby={ ariaDescribedBy }
35
+ aria-expanded={ ariaExpanded }
17
36
  size="sm"
18
37
  boxSize={ 8 }
19
38
  variant="ghost"
20
39
  isDisabled={ isDisabled }
21
- onPointerDown={ handleClick }
40
+ onClick={ handleClick }
41
+ onKeyDown={ handleKeyDown }
22
42
  pointerEvents={ isDisabled ? 'none' : 'auto' }
23
43
  >
24
44
  <Icon as={ CalendarDuo } />
@@ -20,6 +20,7 @@ export interface StyledFieldProps extends HTMLAttributes<HTMLElement> {
20
20
  children: ReactNode
21
21
  variant?: 'outline' | 'filled'
22
22
  }
23
+
23
24
  export interface TriggerProps extends AriaButtonProps {
24
25
  handleClick: () => void
25
26
  }
@@ -1,5 +1,5 @@
1
1
  import React, { useRef } from 'react'
2
- import { useMultiStyleConfig } from '@chakra-ui/react'
2
+ import { useMultiStyleConfig, useOutsideClick } from '@chakra-ui/react'
3
3
  import { useDatePicker } from '@react-aria/datepicker'
4
4
  import { useDatePickerState } from '@react-stately/datepicker'
5
5
  import { FocusScope } from '@react-aria/focus'
@@ -63,6 +63,7 @@ import { DatePickerLocaleWrapper } from './date-picker-locale-wrapper'
63
63
  */
64
64
  export const DatePicker = (props: DatePickerProps) => {
65
65
  const {
66
+ 'data-testid': dataTestId,
66
67
  isDisabled,
67
68
  isClearable = true,
68
69
  resetDate,
@@ -71,49 +72,243 @@ export const DatePicker = (props: DatePickerProps) => {
71
72
  minValue,
72
73
  variant = 'outline',
73
74
  firstDayOfWeek = 'monday',
75
+ value,
76
+ defaultValue,
77
+ onChange,
78
+ maxValue,
79
+ isReadOnly,
80
+ isRequired,
81
+ placeholderValue,
82
+ isDateUnavailable,
83
+ granularity,
84
+ hourCycle,
85
+ shouldForceLeadingZeros,
86
+ pageBehavior,
87
+ defaultOpen,
88
+ onOpenChange,
89
+ label,
90
+ description,
91
+ errorMessage,
92
+ id,
93
+ name,
94
+ autoFocus,
95
+ onFocus,
96
+ onBlur,
97
+ onFocusChange,
98
+ onKeyDown,
99
+ onKeyUp,
100
+ 'aria-label': ariaLabel,
101
+ 'aria-labelledby': ariaLabelledBy,
102
+ 'aria-describedby': ariaDescribedBy,
103
+ 'aria-details': ariaDetails,
74
104
  } = props
75
105
  const ref = useRef() as React.MutableRefObject<HTMLInputElement>
106
+ const dialogRef = useRef() as React.MutableRefObject<HTMLDivElement>
76
107
  const { group } = useMultiStyleConfig('DatePicker')
77
108
 
78
109
  const state = useDatePickerState({
79
- ...props,
110
+ value,
111
+ defaultValue,
112
+ onChange,
113
+ minValue,
114
+ maxValue,
115
+ isDisabled,
116
+ isReadOnly,
117
+ isRequired,
118
+ isInvalid,
119
+ placeholderValue,
120
+ isDateUnavailable,
121
+ granularity,
122
+ hourCycle,
123
+ shouldForceLeadingZeros,
124
+ defaultOpen,
125
+ onOpenChange,
80
126
  shouldCloseOnSelect: false,
81
127
  hideTimeZone: true,
82
128
  })
83
129
 
84
130
  const { buttonProps, fieldProps, calendarProps, groupProps, dialogProps } =
85
131
  useDatePicker(
86
- { ...props, minValue: minValue || parseDate('1994-03-08') },
132
+ {
133
+ value,
134
+ defaultValue,
135
+ onChange,
136
+ minValue: minValue || parseDate('1994-03-08'),
137
+ maxValue,
138
+ isDisabled,
139
+ isReadOnly,
140
+ isRequired,
141
+ isInvalid,
142
+ placeholderValue,
143
+ isDateUnavailable,
144
+ granularity,
145
+ hourCycle,
146
+ shouldForceLeadingZeros,
147
+ pageBehavior,
148
+ defaultOpen,
149
+ onOpenChange,
150
+ label,
151
+ description,
152
+ errorMessage,
153
+ id,
154
+ name,
155
+ autoFocus,
156
+ onFocus,
157
+ onBlur,
158
+ onFocusChange,
159
+ onKeyDown,
160
+ onKeyUp,
161
+ 'aria-label': ariaLabel,
162
+ 'aria-labelledby': ariaLabelledBy,
163
+ 'aria-describedby': ariaDescribedBy,
164
+ 'aria-details': ariaDetails,
165
+ },
87
166
  state,
88
167
  ref
89
168
  )
90
169
 
91
- const togglePopup = () => state.setOpen(!state.isOpen)
170
+ const {
171
+ id: buttonId,
172
+ 'aria-haspopup': buttonAriaHasPopup,
173
+ 'aria-label': buttonAriaLabel,
174
+ 'aria-labelledby': buttonAriaLabelledBy,
175
+ 'aria-describedby': buttonAriaDescribedBy,
176
+ 'aria-expanded': buttonAriaExpanded,
177
+ } = buttonProps
178
+
179
+ const {
180
+ id: dialogId,
181
+ 'aria-labelledby': dialogAriaLabelledBy,
182
+ } = dialogProps
183
+
184
+ const {
185
+ autoFocus: calAutoFocus,
186
+ value: calValue,
187
+ onChange: calOnChange,
188
+ minValue: calMinValue,
189
+ maxValue: calMaxValue,
190
+ isDisabled: calIsDisabled,
191
+ isReadOnly: calIsReadOnly,
192
+ isDateUnavailable: calIsDateUnavailable,
193
+ defaultFocusedValue: calDefaultFocusedValue,
194
+ isInvalid: calIsInvalid,
195
+ errorMessage: calErrorMessage,
196
+ } = calendarProps
197
+
198
+ const {
199
+ id: fieldId,
200
+ 'aria-describedby': fieldAriaDescribedBy,
201
+ 'aria-labelledby': fieldAriaLabelledBy,
202
+ value: fieldValue,
203
+ onChange: fieldOnChange,
204
+ minValue: fieldMinValue,
205
+ maxValue: fieldMaxValue,
206
+ placeholderValue: fieldPlaceholderValue,
207
+ hideTimeZone: fieldHideTimeZone,
208
+ hourCycle: fieldHourCycle,
209
+ shouldForceLeadingZeros: fieldShouldForceLeadingZeros,
210
+ granularity: fieldGranularity,
211
+ isDisabled: fieldIsDisabled,
212
+ isReadOnly: fieldIsReadOnly,
213
+ isRequired: fieldIsRequired,
214
+ isInvalid: fieldIsInvalid,
215
+ autoFocus: fieldAutoFocus,
216
+ name: fieldName,
217
+ ...restFieldProps
218
+ } = fieldProps
219
+
220
+ const {
221
+ role: groupRole,
222
+ id: groupId,
223
+ 'aria-disabled': groupAriaDisabled,
224
+ 'aria-labelledby': groupAriaLabelledBy,
225
+ 'aria-describedby': groupAriaDescribedBy,
226
+ onKeyDown: groupOnKeyDown,
227
+ onKeyUp: groupOnKeyUp,
228
+ onFocus: groupOnFocus,
229
+ onBlur: groupOnBlur,
230
+ onPointerDown: groupOnPointerDown,
231
+ onClick: groupOnClick,
232
+ } = groupProps
233
+
234
+ const togglePopup = () => {
235
+ state.setOpen(!state.isOpen)
236
+ }
237
+
238
+ useOutsideClick({
239
+ ref: dialogRef,
240
+ handler: (event) => {
241
+ if (ref.current?.contains(event.target as Node)) return
242
+ state.setOpen(false)
243
+ },
244
+ })
92
245
 
93
246
  return (
94
247
  <Popover
95
248
  isOpen={ state.isOpen }
96
249
  onClose={ () => state.setOpen(false) }
250
+ closeOnBlur={ false }
97
251
  placement="bottom-end"
98
252
  >
99
253
  <PopoverAnchor>
100
- <HStack minW={ 56 }>
101
- <InputGroup { ...groupProps } ref={ ref } __css={ group }>
254
+ <HStack minW={ 56 } data-testid={ dataTestId }>
255
+ <InputGroup
256
+ role={ groupRole }
257
+ id={ groupId }
258
+ aria-disabled={ groupAriaDisabled }
259
+ aria-labelledby={ groupAriaLabelledBy }
260
+ aria-describedby={ groupAriaDescribedBy }
261
+ onKeyDown={ groupOnKeyDown }
262
+ onKeyUp={ groupOnKeyUp }
263
+ onFocus={ groupOnFocus }
264
+ onBlur={ groupOnBlur }
265
+ onPointerDown={ groupOnPointerDown }
266
+ onClick={ groupOnClick }
267
+ ref={ ref }
268
+ __css={ group }
269
+ >
102
270
  <StyledField
103
271
  isDisabled={ isDisabled }
104
272
  isInvalid={ isInvalid }
105
273
  variant={ variant }
106
274
  >
107
- <Box paddingInlineStart="1a" paddingInlineEnd={ 10 }>
108
- <DateField { ...fieldProps } dateFormat={ dateFormat } />
275
+ <Box paddingInlineStart="1a" data-testid="date-picker-input-field" paddingInlineEnd={ 10 }>
276
+ <DateField
277
+ { ...restFieldProps }
278
+ id={ fieldId }
279
+ aria-describedby={ fieldAriaDescribedBy }
280
+ aria-labelledby={ fieldAriaLabelledBy }
281
+ value={ fieldValue }
282
+ onChange={ fieldOnChange }
283
+ minValue={ fieldMinValue }
284
+ maxValue={ fieldMaxValue }
285
+ placeholderValue={ fieldPlaceholderValue }
286
+ hideTimeZone={ fieldHideTimeZone }
287
+ hourCycle={ fieldHourCycle }
288
+ shouldForceLeadingZeros={ fieldShouldForceLeadingZeros }
289
+ granularity={ fieldGranularity }
290
+ isDisabled={ fieldIsDisabled }
291
+ isReadOnly={ fieldIsReadOnly }
292
+ isRequired={ fieldIsRequired }
293
+ isInvalid={ fieldIsInvalid }
294
+ autoFocus={ fieldAutoFocus }
295
+ name={ fieldName }
296
+ dateFormat={ dateFormat }
297
+ />
109
298
  </Box>
110
299
  </StyledField>
111
300
  <InputRightElement
112
301
  sx={ { height: '100%', paddingRight: '1' } }
113
302
  zIndex={ 0 }
303
+ data-testid="date-picker-trigger"
114
304
  >
115
305
  <Trigger
116
- { ...buttonProps }
306
+ id={ buttonId }
307
+ aria-haspopup={ buttonAriaHasPopup }
308
+ aria-label={ buttonAriaLabel }
309
+ aria-labelledby={ buttonAriaLabelledBy }
310
+ aria-describedby={ buttonAriaDescribedBy }
311
+ aria-expanded={ buttonAriaExpanded }
117
312
  isDisabled={ isDisabled }
118
313
  handleClick={ togglePopup }
119
314
  />
@@ -132,10 +327,29 @@ export const DatePicker = (props: DatePickerProps) => {
132
327
  </HStack>
133
328
  </PopoverAnchor>
134
329
  { state.isOpen && (
135
- <PopoverContent { ...dialogProps } ref={ ref } w={ 64 } border="none">
330
+ <PopoverContent
331
+ id={ dialogId }
332
+ aria-labelledby={ dialogAriaLabelledBy }
333
+ ref={ dialogRef }
334
+ w={ 64 }
335
+ border="none"
336
+ >
136
337
  <FocusScope contain={ true } restoreFocus={ true }>
137
338
  <DatePickerLocaleWrapper firstDayOfWeek={ firstDayOfWeek }>
138
- <Calendar { ...calendarProps } firstDayOfWeek={ firstDayOfWeek } />
339
+ <Calendar
340
+ autoFocus={ calAutoFocus }
341
+ value={ calValue }
342
+ onChange={ calOnChange }
343
+ minValue={ calMinValue }
344
+ maxValue={ calMaxValue }
345
+ isDisabled={ calIsDisabled }
346
+ isReadOnly={ calIsReadOnly }
347
+ isDateUnavailable={ calIsDateUnavailable }
348
+ defaultFocusedValue={ calDefaultFocusedValue }
349
+ isInvalid={ calIsInvalid }
350
+ errorMessage={ calErrorMessage }
351
+ firstDayOfWeek={ firstDayOfWeek }
352
+ />
139
353
  </DatePickerLocaleWrapper>
140
354
  </FocusScope>
141
355
  </PopoverContent>
@@ -1,7 +1,8 @@
1
1
  import React, { useRef } from 'react'
2
+ import { FocusScope } from '@react-aria/focus'
2
3
  import { useDateRangePickerState } from '@react-stately/datepicker'
3
4
  import { useDateRangePicker } from '@react-aria/datepicker'
4
- import { useMultiStyleConfig } from '@chakra-ui/react'
5
+ import { useMultiStyleConfig, useOutsideClick } from '@chakra-ui/react'
5
6
  import { CheckSolid, XCloseSolid } from '@northlight/icons'
6
7
  import { identity, isNil } from 'ramda'
7
8
  import { DateValue, parseDate } from '@internationalized/date'
@@ -116,8 +117,14 @@ export const DateRangePicker = (props: DateRangePickerProps) => {
116
117
  'data-testid': dataTestId,
117
118
  defaultOpen = false,
118
119
  onOpenChange,
120
+ placeholderValue,
121
+ isDateUnavailable,
122
+ allowsNonContiguousRanges,
123
+ startName,
124
+ endName,
119
125
  } = props
120
126
  const ref = useRef() as React.MutableRefObject<HTMLInputElement>
127
+ const dialogRef = useRef() as React.MutableRefObject<HTMLDivElement>
121
128
  const { group } = useMultiStyleConfig('DatePicker')
122
129
  const parsedProps = {
123
130
  onChange: (date: DateRange) => {
@@ -137,9 +144,9 @@ export const DateRangePicker = (props: DateRangePickerProps) => {
137
144
  maxValue: parsedProps.maxValue,
138
145
  isDisabled,
139
146
  isInvalid,
140
- placeholderValue: props.placeholderValue,
141
- isDateUnavailable: props.isDateUnavailable,
142
- allowsNonContiguousRanges: props.allowsNonContiguousRanges,
147
+ placeholderValue,
148
+ isDateUnavailable,
149
+ allowsNonContiguousRanges,
143
150
  shouldCloseOnSelect: false,
144
151
  hideTimeZone: true,
145
152
  defaultOpen,
@@ -164,26 +171,121 @@ export const DateRangePicker = (props: DateRangePickerProps) => {
164
171
  value: parseValue(value) as { start: DateValue, end: DateValue },
165
172
  minValue: parsedProps.minValue || parseDate('1994-03-08'),
166
173
  maxValue: parsedProps.maxValue,
167
- placeholderValue: props.placeholderValue,
168
- isDateUnavailable: props.isDateUnavailable,
169
- allowsNonContiguousRanges: props.allowsNonContiguousRanges,
174
+ placeholderValue,
175
+ isDateUnavailable,
176
+ allowsNonContiguousRanges,
170
177
  isDisabled,
171
178
  isInvalid,
172
- startName: props.startName,
173
- endName: props.endName,
179
+ startName,
180
+ endName,
174
181
  'aria-label': 'Date range picker',
175
- ...(!isNil(dataTestId) && { 'data-testid': dataTestId }),
176
182
  },
177
183
  state,
178
184
  ref
179
185
  )
180
186
 
181
- const togglePopup = () => state.setOpen(!state.isOpen)
187
+ const {
188
+ id: buttonId,
189
+ 'aria-haspopup': buttonAriaHasPopup,
190
+ 'aria-label': buttonAriaLabel,
191
+ 'aria-labelledby': buttonAriaLabelledBy,
192
+ 'aria-describedby': buttonAriaDescribedBy,
193
+ 'aria-expanded': buttonAriaExpanded,
194
+ } = buttonProps
195
+
196
+ const {
197
+ role: groupRole,
198
+ id: groupId,
199
+ 'aria-disabled': groupAriaDisabled,
200
+ 'aria-labelledby': groupAriaLabelledBy,
201
+ 'aria-describedby': groupAriaDescribedBy,
202
+ onKeyDown: groupOnKeyDown,
203
+ onKeyUp: groupOnKeyUp,
204
+ onFocus: groupOnFocus,
205
+ onBlur: groupOnBlur,
206
+ onPointerDown: groupOnPointerDown,
207
+ onClick: groupOnClick,
208
+ } = groupProps
209
+
210
+ const {
211
+ id: startFieldId,
212
+ 'aria-describedby': startFieldAriaDescribedBy,
213
+ 'aria-labelledby': startFieldAriaLabelledBy,
214
+ value: startFieldValue,
215
+ onChange: startFieldOnChange,
216
+ minValue: startFieldMinValue,
217
+ maxValue: startFieldMaxValue,
218
+ placeholderValue: startFieldPlaceholderValue,
219
+ hideTimeZone: startFieldHideTimeZone,
220
+ hourCycle: startFieldHourCycle,
221
+ shouldForceLeadingZeros: startFieldShouldForceLeadingZeros,
222
+ granularity: startFieldGranularity,
223
+ isDisabled: startFieldIsDisabled,
224
+ isReadOnly: startFieldIsReadOnly,
225
+ isRequired: startFieldIsRequired,
226
+ isInvalid: startFieldIsInvalid,
227
+ autoFocus: startFieldAutoFocus,
228
+ name: startFieldName,
229
+ ...restStartFieldProps
230
+ } = startFieldProps
231
+
232
+ const {
233
+ id: endFieldId,
234
+ 'aria-describedby': endFieldAriaDescribedBy,
235
+ 'aria-labelledby': endFieldAriaLabelledBy,
236
+ value: endFieldValue,
237
+ onChange: endFieldOnChange,
238
+ minValue: endFieldMinValue,
239
+ maxValue: endFieldMaxValue,
240
+ placeholderValue: endFieldPlaceholderValue,
241
+ hideTimeZone: endFieldHideTimeZone,
242
+ hourCycle: endFieldHourCycle,
243
+ shouldForceLeadingZeros: endFieldShouldForceLeadingZeros,
244
+ granularity: endFieldGranularity,
245
+ isDisabled: endFieldIsDisabled,
246
+ isReadOnly: endFieldIsReadOnly,
247
+ isRequired: endFieldIsRequired,
248
+ isInvalid: endFieldIsInvalid,
249
+ name: endFieldName,
250
+ ...restEndFieldProps
251
+ } = endFieldProps
252
+
253
+ const {
254
+ id: dialogId,
255
+ 'aria-labelledby': dialogAriaLabelledBy,
256
+ } = dialogProps
257
+
258
+ const {
259
+ autoFocus: calAutoFocus,
260
+ value: calValue,
261
+ onChange: calOnChange,
262
+ minValue: calMinValue,
263
+ maxValue: calMaxValue,
264
+ isDisabled: calIsDisabled,
265
+ isReadOnly: calIsReadOnly,
266
+ isDateUnavailable: calIsDateUnavailable,
267
+ allowsNonContiguousRanges: calAllowsNonContiguousRanges,
268
+ defaultFocusedValue: calDefaultFocusedValue,
269
+ isInvalid: calIsInvalid,
270
+ errorMessage: calErrorMessage,
271
+ } = calendarProps
272
+
273
+ const togglePopup = () => {
274
+ state.setOpen(!state.isOpen)
275
+ }
182
276
 
183
277
  const handleClose = () => {
184
278
  state.setOpen(false)
185
279
  }
186
280
 
281
+ useOutsideClick({
282
+ ref: dialogRef,
283
+ handler: (event) => {
284
+ if (ref.current?.contains(event.target as Node)) return
285
+ state.setOpen(false)
286
+ },
287
+ })
288
+
187
289
  const ResetButton = CustomResetButton || (
188
290
  <IconButton
189
291
  aria-label="reset-date"
@@ -237,25 +339,86 @@ export const DateRangePicker = (props: DateRangePickerProps) => {
237
339
  <Popover
238
340
  isOpen={ state.isOpen }
239
341
  onClose={ handleModalClose }
342
+ closeOnBlur={ false }
240
343
  placement="bottom-start"
241
344
  >
242
345
  <PopoverAnchor>
243
- <HStack>
244
- <InputGroup { ...groupProps } ref={ ref } __css={ group }>
346
+ <HStack data-testid={ dataTestId }>
347
+ <InputGroup
348
+ role={ groupRole }
349
+ id={ groupId }
350
+ aria-disabled={ groupAriaDisabled }
351
+ aria-labelledby={ groupAriaLabelledBy }
352
+ aria-describedby={ groupAriaDescribedBy }
353
+ onKeyDown={ groupOnKeyDown }
354
+ onKeyUp={ groupOnKeyUp }
355
+ onFocus={ groupOnFocus }
356
+ onBlur={ groupOnBlur }
357
+ onPointerDown={ groupOnPointerDown }
358
+ onClick={ groupOnClick }
359
+ ref={ ref }
360
+ __css={ group }
361
+ >
245
362
  <StyledField
246
363
  isDisabled={ isDisabled }
247
364
  isInvalid={ isInvalid }
248
365
  variant={ variant }
249
366
  >
250
- <HStack paddingInlineStart="1a" paddingInlineEnd={ 10 }>
251
- <DateField { ...startFieldProps } dateFormat={ dateFormat } />
367
+ <HStack paddingInlineStart="1a" data-test-id="daterange-picker-input-field" paddingInlineEnd={ 10 }>
368
+ <DateField
369
+ { ...restStartFieldProps }
370
+ id={ startFieldId }
371
+ aria-describedby={ startFieldAriaDescribedBy }
372
+ aria-labelledby={ startFieldAriaLabelledBy }
373
+ value={ startFieldValue }
374
+ onChange={ startFieldOnChange }
375
+ minValue={ startFieldMinValue }
376
+ maxValue={ startFieldMaxValue }
377
+ placeholderValue={ startFieldPlaceholderValue }
378
+ hideTimeZone={ startFieldHideTimeZone }
379
+ hourCycle={ startFieldHourCycle }
380
+ shouldForceLeadingZeros={ startFieldShouldForceLeadingZeros }
381
+ granularity={ startFieldGranularity }
382
+ isDisabled={ startFieldIsDisabled }
383
+ isReadOnly={ startFieldIsReadOnly }
384
+ isRequired={ startFieldIsRequired }
385
+ isInvalid={ startFieldIsInvalid }
386
+ autoFocus={ startFieldAutoFocus }
387
+ name={ startFieldName }
388
+ dateFormat={ dateFormat }
389
+ />
252
390
  <P>-</P>
253
- <DateField { ...endFieldProps } dateFormat={ dateFormat } />
391
+ <DateField
392
+ { ...restEndFieldProps }
393
+ id={ endFieldId }
394
+ aria-describedby={ endFieldAriaDescribedBy }
395
+ aria-labelledby={ endFieldAriaLabelledBy }
396
+ value={ endFieldValue }
397
+ onChange={ endFieldOnChange }
398
+ minValue={ endFieldMinValue }
399
+ maxValue={ endFieldMaxValue }
400
+ placeholderValue={ endFieldPlaceholderValue }
401
+ hideTimeZone={ endFieldHideTimeZone }
402
+ hourCycle={ endFieldHourCycle }
403
+ shouldForceLeadingZeros={ endFieldShouldForceLeadingZeros }
404
+ granularity={ endFieldGranularity }
405
+ isDisabled={ endFieldIsDisabled }
406
+ isReadOnly={ endFieldIsReadOnly }
407
+ isRequired={ endFieldIsRequired }
408
+ isInvalid={ endFieldIsInvalid }
409
+ name={ endFieldName }
410
+ dateFormat={ dateFormat }
411
+ />
254
412
  </HStack>
255
413
  </StyledField>
256
- <InputRightElement sx={ { height: '100%', paddingRight: '1' } }>
414
+ <InputRightElement data-test-id="daterange-picker-trigger" sx={ { height: '100%', paddingRight: '1' } }>
257
415
  <Trigger
258
- { ...buttonProps }
416
+ id={ buttonId }
417
+ aria-haspopup={ buttonAriaHasPopup }
418
+ aria-label={ buttonAriaLabel }
419
+ aria-labelledby={ buttonAriaLabelledBy }
420
+ aria-describedby={ buttonAriaDescribedBy }
421
+ aria-expanded={ buttonAriaExpanded }
259
422
  isDisabled={ isDisabled }
260
423
  handleClick={ togglePopup }
261
424
  />
@@ -294,21 +457,39 @@ export const DateRangePicker = (props: DateRangePickerProps) => {
294
457
  </PopoverAnchor>
295
458
  <PortalWrapper renderInPortal={ renderInPortal }>
296
459
  { state.isOpen && (
297
- <PopoverContent { ...dialogProps } ref={ ref } w="max-content">
298
- <DatePickerLocaleWrapper firstDayOfWeek={ firstDayOfWeek }>
299
- <RangeCalendar
300
- { ...calendarProps }
301
- resetDate={ cancelOrResetDateChange }
302
- handleClose={ handleModalClose }
303
- fiscalStartMonth={ fiscalStartMonth || 0 }
304
- fiscalStartDay={ fiscalStartDay || 0 }
305
- isClearable={ isClearable }
306
- firstDayOfWeek={ firstDayOfWeek }
307
- onSave={ onSave }
308
- clearButtonLabel={ clearButtonLabel }
309
- buttonLabel={ buttonLabel }
310
- />
311
- </DatePickerLocaleWrapper>
460
+ <PopoverContent
461
+ id={ dialogId }
462
+ aria-labelledby={ dialogAriaLabelledBy }
463
+ ref={ dialogRef }
464
+ w="max-content"
465
+ >
466
+ <FocusScope contain={ true } restoreFocus={ true }>
467
+ <DatePickerLocaleWrapper firstDayOfWeek={ firstDayOfWeek }>
468
+ <RangeCalendar
469
+ autoFocus={ calAutoFocus }
470
+ value={ calValue }
471
+ onChange={ calOnChange }
472
+ minValue={ calMinValue }
473
+ maxValue={ calMaxValue }
474
+ isDisabled={ calIsDisabled }
475
+ isReadOnly={ calIsReadOnly }
476
+ isDateUnavailable={ calIsDateUnavailable }
477
+ allowsNonContiguousRanges={ calAllowsNonContiguousRanges }
478
+ defaultFocusedValue={ calDefaultFocusedValue }
479
+ isInvalid={ calIsInvalid }
480
+ errorMessage={ calErrorMessage }
481
+ resetDate={ cancelOrResetDateChange }
482
+ handleClose={ handleModalClose }
483
+ fiscalStartMonth={ fiscalStartMonth || 0 }
484
+ fiscalStartDay={ fiscalStartDay || 0 }
485
+ isClearable={ isClearable }
486
+ firstDayOfWeek={ firstDayOfWeek }
487
+ onSave={ onSave }
488
+ clearButtonLabel={ clearButtonLabel }
489
+ buttonLabel={ buttonLabel }
490
+ />
491
+ </DatePickerLocaleWrapper>
492
+ </FocusScope>
312
493
  </PopoverContent>
313
494
  ) }
314
495
  </PortalWrapper>
@@ -29,7 +29,9 @@ interface DatePickerSettings {
29
29
 
30
30
  export interface DatePickerProps
31
31
  extends Omit<AriaDatePickerProps<DateValue>, 'firstDayOfWeek'>,
32
- DatePickerSettings {}
32
+ DatePickerSettings {
33
+ 'data-testid': string
34
+ }
33
35
 
34
36
  export interface DateRangePickerProps
35
37
  extends Omit<AriaDateRangePickerProps<DateValue>, 'firstDayOfWeek' | 'onChange' | 'value' | 'minValue' | 'maxValue'>,
@@ -6,7 +6,7 @@ export const useArrowFocus = (columns: number) => {
6
6
  focusPrevious: previous,
7
7
  focusFirst,
8
8
  focusLast,
9
- } = useFocusManager()
9
+ } = useFocusManager()!
10
10
 
11
11
  const defaultOpts = {
12
12
  ArrowRight: {