@react-aria/datepicker 3.8.1 → 3.9.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/types.d.ts CHANGED
@@ -1,15 +1,15 @@
1
1
  import { AriaDateFieldProps, AriaTimeFieldProps, DateValue, TimeValue, AriaDatePickerProps, AriaDateRangePickerProps } from "@react-types/datepicker";
2
2
  import { DateFieldState, TimeFieldState, DatePickerState, DateSegment, DateRangePickerState } from "@react-stately/datepicker";
3
- import { DOMAttributes, GroupDOMAttributes } from "@react-types/shared";
3
+ import { DOMAttributes, GroupDOMAttributes, ValidationResult } from "@react-types/shared";
4
4
  import { InputHTMLAttributes, RefObject } from "react";
5
5
  import { AriaButtonProps } from "@react-types/button";
6
6
  import { AriaDialogProps } from "@react-types/dialog";
7
7
  import { CalendarProps, RangeCalendarProps } from "@react-types/calendar";
8
- export interface AriaDateFieldOptions<T extends DateValue> extends Omit<AriaDateFieldProps<T>, 'value' | 'defaultValue' | 'onChange' | 'minValue' | 'maxValue' | 'placeholderValue'> {
8
+ export interface AriaDateFieldOptions<T extends DateValue> extends Omit<AriaDateFieldProps<T>, 'value' | 'defaultValue' | 'onChange' | 'minValue' | 'maxValue' | 'placeholderValue' | 'validate'> {
9
9
  /** A ref for the hidden input element for HTML form submission. */
10
10
  inputRef?: RefObject<HTMLInputElement>;
11
11
  }
12
- export interface DateFieldAria {
12
+ export interface DateFieldAria extends ValidationResult {
13
13
  /** Props for the field's visible label element, if any. */
14
14
  labelProps: DOMAttributes;
15
15
  /** Props for the field grouping element. */
@@ -37,7 +37,7 @@ interface AriaTimeFieldOptions<T extends TimeValue> extends AriaTimeFieldProps<T
37
37
  * Each part of a time value is displayed in an individually editable segment.
38
38
  */
39
39
  export function useTimeField<T extends TimeValue>(props: AriaTimeFieldOptions<T>, state: TimeFieldState, ref: RefObject<Element>): DateFieldAria;
40
- export interface DatePickerAria {
40
+ export interface DatePickerAria extends ValidationResult {
41
41
  /** Props for the date picker's visible label element, if any. */
42
42
  labelProps: DOMAttributes;
43
43
  /** Props for the grouping element containing the date field and button. */
@@ -76,7 +76,7 @@ export interface DateSegmentAria {
76
76
  * the value by typing or using the arrow keys to increment and decrement.
77
77
  */
78
78
  export function useDateSegment(segment: DateSegment, state: DateFieldState, ref: RefObject<HTMLElement>): DateSegmentAria;
79
- export interface DateRangePickerAria {
79
+ export interface DateRangePickerAria extends ValidationResult {
80
80
  /** Props for the date range picker's visible label element, if any. */
81
81
  labelProps: DOMAttributes;
82
82
  /** Props for the grouping element containing the date fields and button. */
@@ -1 +1 @@
1
- {"mappings":"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","sources":["packages/@react-aria/datepicker/src/packages/@react-aria/datepicker/src/useDatePickerGroup.ts","packages/@react-aria/datepicker/src/packages/@react-aria/datepicker/src/useDateField.ts","packages/@react-aria/datepicker/src/packages/@react-aria/datepicker/src/useDatePicker.ts","packages/@react-aria/datepicker/src/packages/@react-aria/datepicker/src/useDisplayNames.ts","packages/@react-aria/datepicker/src/packages/@react-aria/datepicker/src/useDateSegment.ts","packages/@react-aria/datepicker/src/packages/@react-aria/datepicker/src/useDateRangePicker.ts","packages/@react-aria/datepicker/src/packages/@react-aria/datepicker/src/index.ts","packages/@react-aria/datepicker/src/index.ts"],"sourcesContent":[null,null,null,null,null,null,null,"/*\n * Copyright 2020 Adobe. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\n\nexport {useDatePicker} from './useDatePicker';\nexport {useDateSegment} from './useDateSegment';\nexport {useDateField, useTimeField} from './useDateField';\nexport {useDateRangePicker} from './useDateRangePicker';\nexport {useDisplayNames} from './useDisplayNames';\n\nexport type {AriaDateFieldProps, AriaDatePickerProps, AriaDateRangePickerProps, DateRange, DateValue, TimeValue} from '@react-types/datepicker';\nexport type {AriaDateFieldOptions, DateFieldAria} from './useDateField';\nexport type {DatePickerAria} from './useDatePicker';\nexport type {DateRangePickerAria} from './useDateRangePicker';\nexport type {DateSegmentAria} from './useDateSegment';\nexport type {AriaTimeFieldProps} from '@react-types/datepicker';\n"],"names":[],"version":3,"file":"types.d.ts.map"}
1
+ {"mappings":"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","sources":["packages/@react-aria/datepicker/src/packages/@react-aria/datepicker/src/useDatePickerGroup.ts","packages/@react-aria/datepicker/src/packages/@react-aria/datepicker/src/useDateField.ts","packages/@react-aria/datepicker/src/packages/@react-aria/datepicker/src/useDatePicker.ts","packages/@react-aria/datepicker/src/packages/@react-aria/datepicker/src/useDisplayNames.ts","packages/@react-aria/datepicker/src/packages/@react-aria/datepicker/src/useDateSegment.ts","packages/@react-aria/datepicker/src/packages/@react-aria/datepicker/src/useDateRangePicker.ts","packages/@react-aria/datepicker/src/packages/@react-aria/datepicker/src/index.ts","packages/@react-aria/datepicker/src/index.ts"],"sourcesContent":[null,null,null,null,null,null,null,"/*\n * Copyright 2020 Adobe. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\n\nexport {useDatePicker} from './useDatePicker';\nexport {useDateSegment} from './useDateSegment';\nexport {useDateField, useTimeField} from './useDateField';\nexport {useDateRangePicker} from './useDateRangePicker';\nexport {useDisplayNames} from './useDisplayNames';\n\nexport type {AriaDateFieldProps, AriaDatePickerProps, AriaDateRangePickerProps, DateRange, DateValue, TimeValue} from '@react-types/datepicker';\nexport type {AriaDateFieldOptions, DateFieldAria} from './useDateField';\nexport type {DatePickerAria} from './useDatePicker';\nexport type {DateRangePickerAria} from './useDateRangePicker';\nexport type {DateSegmentAria} from './useDateSegment';\nexport type {AriaTimeFieldProps} from '@react-types/datepicker';\n"],"names":[],"version":3,"file":"types.d.ts.map"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@react-aria/datepicker",
3
- "version": "3.8.1",
3
+ "version": "3.9.0",
4
4
  "description": "Spectrum UI components in React",
5
5
  "license": "Apache-2.0",
6
6
  "main": "dist/main.js",
@@ -23,20 +23,22 @@
23
23
  },
24
24
  "dependencies": {
25
25
  "@internationalized/date": "^3.5.0",
26
- "@internationalized/number": "^3.3.0",
26
+ "@internationalized/number": "^3.4.0",
27
27
  "@internationalized/string": "^3.1.1",
28
- "@react-aria/focus": "^3.14.3",
29
- "@react-aria/i18n": "^3.8.4",
30
- "@react-aria/interactions": "^3.19.1",
31
- "@react-aria/label": "^3.7.2",
32
- "@react-aria/spinbutton": "^3.5.4",
33
- "@react-aria/utils": "^3.21.1",
34
- "@react-stately/datepicker": "^3.8.0",
35
- "@react-types/button": "^3.9.0",
36
- "@react-types/calendar": "^3.4.1",
37
- "@react-types/datepicker": "^3.6.1",
38
- "@react-types/dialog": "^3.5.6",
39
- "@react-types/shared": "^3.21.0",
28
+ "@react-aria/focus": "^3.15.0",
29
+ "@react-aria/form": "^3.0.0",
30
+ "@react-aria/i18n": "^3.9.0",
31
+ "@react-aria/interactions": "^3.20.0",
32
+ "@react-aria/label": "^3.7.3",
33
+ "@react-aria/spinbutton": "^3.6.0",
34
+ "@react-aria/utils": "^3.22.0",
35
+ "@react-stately/datepicker": "^3.9.0",
36
+ "@react-stately/form": "^3.0.0",
37
+ "@react-types/button": "^3.9.1",
38
+ "@react-types/calendar": "^3.4.2",
39
+ "@react-types/datepicker": "^3.7.0",
40
+ "@react-types/dialog": "^3.5.7",
41
+ "@react-types/shared": "^3.22.0",
40
42
  "@swc/helpers": "^0.5.0"
41
43
  },
42
44
  "peerDependencies": {
@@ -46,5 +48,5 @@
46
48
  "publishConfig": {
47
49
  "access": "public"
48
50
  },
49
- "gitHead": "3ecf62dcceea437cb12df1851636dd491bb6591c"
51
+ "gitHead": "9ce2f674eab2cc8912800d3162dcf00a1ce94274"
50
52
  }
@@ -13,23 +13,24 @@
13
13
  import {AriaDateFieldProps as AriaDateFieldPropsBase, AriaTimeFieldProps, DateValue, TimeValue} from '@react-types/datepicker';
14
14
  import {createFocusManager, FocusManager} from '@react-aria/focus';
15
15
  import {DateFieldState, TimeFieldState} from '@react-stately/datepicker';
16
- import {DOMAttributes, GroupDOMAttributes, KeyboardEvent} from '@react-types/shared';
16
+ import {DOMAttributes, GroupDOMAttributes, KeyboardEvent, ValidationResult} from '@react-types/shared';
17
17
  import {filterDOMProps, mergeProps, useDescription, useFormReset} from '@react-aria/utils';
18
- import {FocusEvent, InputHTMLAttributes, RefObject, useEffect, useMemo, useRef} from 'react';
18
+ import {InputHTMLAttributes, RefObject, useEffect, useMemo, useRef} from 'react';
19
19
  // @ts-ignore
20
20
  import intlMessages from '../intl/*.json';
21
21
  import {useDatePickerGroup} from './useDatePickerGroup';
22
22
  import {useField} from '@react-aria/label';
23
23
  import {useFocusWithin} from '@react-aria/interactions';
24
+ import {useFormValidation} from '@react-aria/form';
24
25
  import {useLocalizedStringFormatter} from '@react-aria/i18n';
25
26
 
26
27
  // Allows this hook to also be used with TimeField
27
- export interface AriaDateFieldOptions<T extends DateValue> extends Omit<AriaDateFieldPropsBase<T>, 'value' | 'defaultValue' | 'onChange' | 'minValue' | 'maxValue' | 'placeholderValue'> {
28
+ export interface AriaDateFieldOptions<T extends DateValue> extends Omit<AriaDateFieldPropsBase<T>, 'value' | 'defaultValue' | 'onChange' | 'minValue' | 'maxValue' | 'placeholderValue' | 'validate'> {
28
29
  /** A ref for the hidden input element for HTML form submission. */
29
30
  inputRef?: RefObject<HTMLInputElement>
30
31
  }
31
32
 
32
- export interface DateFieldAria {
33
+ export interface DateFieldAria extends ValidationResult {
33
34
  /** Props for the field's visible label element, if any. */
34
35
  labelProps: DOMAttributes,
35
36
  /** Props for the field grouping element. */
@@ -63,21 +64,28 @@ export const focusManagerSymbol = '__focusManager_' + Date.now();
63
64
  * Each part of a date value is displayed in an individually editable segment.
64
65
  */
65
66
  export function useDateField<T extends DateValue>(props: AriaDateFieldOptions<T>, state: DateFieldState, ref: RefObject<Element>): DateFieldAria {
67
+ let {isInvalid, validationErrors, validationDetails} = state.displayValidation;
66
68
  let {labelProps, fieldProps, descriptionProps, errorMessageProps} = useField({
67
69
  ...props,
68
- labelElementType: 'span'
70
+ labelElementType: 'span',
71
+ isInvalid,
72
+ errorMessage: props.errorMessage || validationErrors
69
73
  });
70
74
 
75
+ let valueOnFocus = useRef<DateValue | null>(null);
71
76
  let {focusWithinProps} = useFocusWithin({
72
77
  ...props,
73
- onBlurWithin: (e: FocusEvent) => {
78
+ onFocusWithin(e) {
79
+ valueOnFocus.current = state.value;
80
+ props.onFocus?.(e);
81
+ },
82
+ onBlurWithin: (e) => {
74
83
  state.confirmPlaceholder();
75
-
76
- if (props.onBlur) {
77
- props.onBlur(e);
84
+ if (state.value !== valueOnFocus.current) {
85
+ state.commitValidation();
78
86
  }
87
+ props.onBlur?.(e);
79
88
  },
80
- onFocusWithin: props.onFocus,
81
89
  onFocusWithinChange: props.onFocusChange
82
90
  });
83
91
 
@@ -131,6 +139,28 @@ export function useDateField<T extends DateValue>(props: AriaDateFieldOptions<T>
131
139
  }, [focusManager]);
132
140
 
133
141
  useFormReset(props.inputRef, state.value, state.setValue);
142
+ useFormValidation({
143
+ ...props,
144
+ focus() {
145
+ focusManager.focusFirst();
146
+ }
147
+ }, state, props.inputRef);
148
+
149
+ let inputProps: InputHTMLAttributes<HTMLInputElement> = {
150
+ type: 'hidden',
151
+ name: props.name,
152
+ value: state.value?.toString() || ''
153
+ };
154
+
155
+ if (props.validationBehavior === 'native') {
156
+ // Use a hidden <input type="text"> rather than <input type="hidden">
157
+ // so that an empty value blocks HTML form submission when the field is required.
158
+ inputProps.type = 'text';
159
+ inputProps.hidden = true;
160
+ inputProps.required = props.isRequired;
161
+ // Ignore react warning.
162
+ inputProps.onChange = () => {};
163
+ }
134
164
 
135
165
  let domProps = filterDOMProps(props);
136
166
  return {
@@ -152,13 +182,12 @@ export function useDateField<T extends DateValue>(props: AriaDateFieldOptions<T>
152
182
  }
153
183
  }
154
184
  }),
155
- inputProps: {
156
- type: 'hidden',
157
- name: props.name,
158
- value: state.value?.toString() || ''
159
- },
185
+ inputProps,
160
186
  descriptionProps,
161
- errorMessageProps
187
+ errorMessageProps,
188
+ isInvalid,
189
+ validationErrors,
190
+ validationDetails
162
191
  };
163
192
  }
164
193
 
@@ -16,10 +16,11 @@ import {AriaDialogProps} from '@react-types/dialog';
16
16
  import {CalendarProps} from '@react-types/calendar';
17
17
  import {createFocusManager} from '@react-aria/focus';
18
18
  import {DatePickerState} from '@react-stately/datepicker';
19
- import {DOMAttributes, GroupDOMAttributes, KeyboardEvent} from '@react-types/shared';
19
+ import {DOMAttributes, GroupDOMAttributes, KeyboardEvent, ValidationResult} from '@react-types/shared';
20
20
  import {filterDOMProps, mergeProps, useDescription, useId} from '@react-aria/utils';
21
21
  // @ts-ignore
22
22
  import intlMessages from '../intl/*.json';
23
+ import {privateValidationStateProp} from '@react-stately/form';
23
24
  import {RefObject, useMemo} from 'react';
24
25
  import {roleSymbol} from './useDateField';
25
26
  import {useDatePickerGroup} from './useDatePickerGroup';
@@ -27,7 +28,7 @@ import {useField} from '@react-aria/label';
27
28
  import {useFocusWithin} from '@react-aria/interactions';
28
29
  import {useLocale, useLocalizedStringFormatter} from '@react-aria/i18n';
29
30
 
30
- export interface DatePickerAria {
31
+ export interface DatePickerAria extends ValidationResult {
31
32
  /** Props for the date picker's visible label element, if any. */
32
33
  labelProps: DOMAttributes,
33
34
  /** Props for the grouping element containing the date field and button. */
@@ -56,9 +57,12 @@ export function useDatePicker<T extends DateValue>(props: AriaDatePickerProps<T>
56
57
  let fieldId = useId();
57
58
  let stringFormatter = useLocalizedStringFormatter(intlMessages);
58
59
 
60
+ let {isInvalid, validationErrors, validationDetails} = state.displayValidation;
59
61
  let {labelProps, fieldProps, descriptionProps, errorMessageProps} = useField({
60
62
  ...props,
61
- labelElementType: 'span'
63
+ labelElementType: 'span',
64
+ isInvalid,
65
+ errorMessage: props.errorMessage || validationErrors
62
66
  });
63
67
 
64
68
  let groupProps = useDatePickerGroup(state, ref);
@@ -119,8 +123,6 @@ export function useDatePicker<T extends DateValue>(props: AriaDatePickerProps<T>
119
123
  'aria-describedby': ariaDescribedBy,
120
124
  value: state.value,
121
125
  onChange: state.setValue,
122
- minValue: props.minValue,
123
- maxValue: props.maxValue,
124
126
  placeholderValue: props.placeholderValue,
125
127
  hideTimeZone: props.hideTimeZone,
126
128
  hourCycle: props.hourCycle,
@@ -129,7 +131,9 @@ export function useDatePicker<T extends DateValue>(props: AriaDatePickerProps<T>
129
131
  isDisabled: props.isDisabled,
130
132
  isReadOnly: props.isReadOnly,
131
133
  isRequired: props.isRequired,
132
- isInvalid: state.isInvalid,
134
+ validationBehavior: props.validationBehavior,
135
+ // DatePicker owns the validation state for the date field.
136
+ [privateValidationStateProp]: state,
133
137
  autoFocus: props.autoFocus,
134
138
  name: props.name
135
139
  },
@@ -160,7 +164,10 @@ export function useDatePicker<T extends DateValue>(props: AriaDatePickerProps<T>
160
164
  isDateUnavailable: props.isDateUnavailable,
161
165
  defaultFocusedValue: state.dateValue ? undefined : props.placeholderValue,
162
166
  isInvalid: state.isInvalid,
163
- errorMessage: props.errorMessage
164
- }
167
+ errorMessage: typeof props.errorMessage === 'function' ? props.errorMessage(state.displayValidation) : (props.errorMessage || state.displayValidation.validationErrors.join(' '))
168
+ },
169
+ isInvalid,
170
+ validationErrors,
171
+ validationDetails
165
172
  };
166
173
  }
@@ -15,19 +15,20 @@ import {AriaDatePickerProps, AriaDateRangePickerProps, DateValue} from '@react-t
15
15
  import {AriaDialogProps} from '@react-types/dialog';
16
16
  import {createFocusManager} from '@react-aria/focus';
17
17
  import {DateRangePickerState} from '@react-stately/datepicker';
18
- import {DOMAttributes, GroupDOMAttributes, KeyboardEvent} from '@react-types/shared';
18
+ import {DEFAULT_VALIDATION_RESULT, mergeValidation, privateValidationStateProp} from '@react-stately/form';
19
+ import {DOMAttributes, GroupDOMAttributes, KeyboardEvent, ValidationResult} from '@react-types/shared';
19
20
  import {filterDOMProps, mergeProps, useDescription, useId} from '@react-aria/utils';
20
21
  import {focusManagerSymbol, roleSymbol} from './useDateField';
21
22
  // @ts-ignore
22
23
  import intlMessages from '../intl/*.json';
23
24
  import {RangeCalendarProps} from '@react-types/calendar';
24
- import {RefObject, useMemo} from 'react';
25
+ import {RefObject, useMemo, useRef} from 'react';
25
26
  import {useDatePickerGroup} from './useDatePickerGroup';
26
27
  import {useField} from '@react-aria/label';
27
28
  import {useFocusWithin} from '@react-aria/interactions';
28
29
  import {useLocale, useLocalizedStringFormatter} from '@react-aria/i18n';
29
30
 
30
- export interface DateRangePickerAria {
31
+ export interface DateRangePickerAria extends ValidationResult {
31
32
  /** Props for the date range picker's visible label element, if any. */
32
33
  labelProps: DOMAttributes,
33
34
  /** Props for the grouping element containing the date fields and button. */
@@ -55,9 +56,12 @@ export interface DateRangePickerAria {
55
56
  */
56
57
  export function useDateRangePicker<T extends DateValue>(props: AriaDateRangePickerProps<T>, state: DateRangePickerState, ref: RefObject<Element>): DateRangePickerAria {
57
58
  let stringFormatter = useLocalizedStringFormatter(intlMessages);
59
+ let {isInvalid, validationErrors, validationDetails} = state.displayValidation;
58
60
  let {labelProps, fieldProps, descriptionProps, errorMessageProps} = useField({
59
61
  ...props,
60
- labelElementType: 'span'
62
+ labelElementType: 'span',
63
+ isInvalid,
64
+ errorMessage: props.errorMessage || validationErrors
61
65
  });
62
66
 
63
67
  let labelledBy = fieldProps['aria-labelledby'] || fieldProps.id;
@@ -92,8 +96,6 @@ export function useDateRangePicker<T extends DateValue>(props: AriaDateRangePick
92
96
  [focusManagerSymbol]: focusManager,
93
97
  [roleSymbol]: 'presentation',
94
98
  'aria-describedby': ariaDescribedBy,
95
- minValue: props.minValue,
96
- maxValue: props.maxValue,
97
99
  placeholderValue: props.placeholderValue,
98
100
  hideTimeZone: props.hideTimeZone,
99
101
  hourCycle: props.hourCycle,
@@ -102,7 +104,7 @@ export function useDateRangePicker<T extends DateValue>(props: AriaDateRangePick
102
104
  isDisabled: props.isDisabled,
103
105
  isReadOnly: props.isReadOnly,
104
106
  isRequired: props.isRequired,
105
- isInvalid: state.isInvalid
107
+ validationBehavior: props.validationBehavior
106
108
  };
107
109
 
108
110
  let domProps = filterDOMProps(props);
@@ -115,6 +117,9 @@ export function useDateRangePicker<T extends DateValue>(props: AriaDateRangePick
115
117
  onFocusWithinChange: props.onFocusChange
116
118
  });
117
119
 
120
+ let startFieldValidation = useRef(DEFAULT_VALIDATION_RESULT);
121
+ let endFieldValidation = useRef(DEFAULT_VALIDATION_RESULT);
122
+
118
123
  return {
119
124
  groupProps: mergeProps(domProps, groupProps, fieldProps, descProps, focusWithinProps, {
120
125
  role: 'group' as const,
@@ -165,14 +170,34 @@ export function useDateRangePicker<T extends DateValue>(props: AriaDateRangePick
165
170
  value: state.value?.start,
166
171
  onChange: start => state.setDateTime('start', start),
167
172
  autoFocus: props.autoFocus,
168
- name: props.startName
173
+ name: props.startName,
174
+ [privateValidationStateProp]: {
175
+ realtimeValidation: state.realtimeValidation,
176
+ displayValidation: state.displayValidation,
177
+ updateValidation(e) {
178
+ startFieldValidation.current = e;
179
+ state.updateValidation(mergeValidation(e, endFieldValidation.current));
180
+ },
181
+ resetValidation: state.resetValidation,
182
+ commitValidation: state.commitValidation
183
+ }
169
184
  },
170
185
  endFieldProps: {
171
186
  ...endFieldProps,
172
187
  ...commonFieldProps,
173
188
  value: state.value?.end,
174
189
  onChange: end => state.setDateTime('end', end),
175
- name: props.endName
190
+ name: props.endName,
191
+ [privateValidationStateProp]: {
192
+ realtimeValidation: state.realtimeValidation,
193
+ displayValidation: state.displayValidation,
194
+ updateValidation(e) {
195
+ endFieldValidation.current = e;
196
+ state.updateValidation(mergeValidation(startFieldValidation.current, e));
197
+ },
198
+ resetValidation: state.resetValidation,
199
+ commitValidation: state.commitValidation
200
+ }
176
201
  },
177
202
  descriptionProps,
178
203
  errorMessageProps,
@@ -188,7 +213,10 @@ export function useDateRangePicker<T extends DateValue>(props: AriaDateRangePick
188
213
  allowsNonContiguousRanges: props.allowsNonContiguousRanges,
189
214
  defaultFocusedValue: state.dateRange ? undefined : props.placeholderValue,
190
215
  isInvalid: state.isInvalid,
191
- errorMessage: props.errorMessage
192
- }
216
+ errorMessage: typeof props.errorMessage === 'function' ? props.errorMessage(state.displayValidation) : (props.errorMessage || state.displayValidation.validationErrors.join(' '))
217
+ },
218
+ isInvalid,
219
+ validationErrors,
220
+ validationDetails
193
221
  };
194
222
  }