@cdx-ui/utils 0.2.0 → 0.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/README.md CHANGED
@@ -46,13 +46,14 @@ Used internally by `@cdx-ui/components` to propagate variant props (size, color,
46
46
 
47
47
  Low-level form-field state used by the `Field` and `Input` primitives.
48
48
 
49
- | Export | Description |
50
- | ------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
51
- | `useFormControlRoot(props)` | Creates the root state object for a form field (id generation, tracks `hasFeedbackText`/`hasHelpText` for describedby assembly). |
52
- | `useFormControl(props)` | Merges component-level props with inherited form-control context. Assembles `aria-describedby` from feedback/help text ids. |
53
- | `useFormControlContext()` | Reads form-control state from the nearest provider. |
54
- | `useReportFormControlLabelFocus(isActive)` | Reports focus/active state to the parent `FormControlContext` — used by `Input`/`Select.Trigger` to trigger `isFocused` styling on the associated label. |
55
- | `FormControlContext` | The React context object for form-control state propagation. |
49
+ | Export | Description |
50
+ | --------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
51
+ | `useFormControlRoot(props)` | Creates the root state object for a form field (id generation, tracks `hasFeedbackText`/`hasHelpText` for describedby assembly). |
52
+ | `useFormControl(props)` | Merges component-level props with inherited form-control context. Assembles `aria-describedby` from feedback/help text ids. |
53
+ | `useFormControlContext()` | Reads form-control state from the nearest provider. |
54
+ | `useReportFormControlLabelFocus(isActive)` | Reports focus/active state to the parent `FormControlContext` — used by `Input`/`Select.Trigger` to trigger `isFocused` styling on the associated label. |
55
+ | `useReportFormControlValidation(isInvalid, errorMessage)` | Reports a self-validating control's invalid state and message to the parent `Field` — used by `DatePicker` so `Field.Error` / `Field.Helper` anywhere in the field reflect it. |
56
+ | `FormControlContext` | The React context object for form-control state propagation. |
56
57
 
57
58
  ## Exported types
58
59
 
@@ -66,6 +67,7 @@ Low-level form-field state used by the `Field` and `Input` primitives.
66
67
  | `FormControlRootProps` | form-control |
67
68
  | `FormControlRootReturn` | form-control |
68
69
  | `FormControlState` | form-control |
70
+ | `FormControlValidation` | form-control |
69
71
  | `StyleContextMap` | style |
70
72
 
71
73
  ## Testing
@@ -33,5 +33,11 @@ Object.defineProperty(exports, "useReportFormControlLabelFocus", {
33
33
  return _useFormControl.useReportFormControlLabelFocus;
34
34
  }
35
35
  });
36
+ Object.defineProperty(exports, "useReportFormControlValidation", {
37
+ enumerable: true,
38
+ get: function () {
39
+ return _useFormControl.useReportFormControlValidation;
40
+ }
41
+ });
36
42
  var _useFormControl = require("./useFormControl");
37
43
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["_useFormControl","require"],"sourceRoot":"../../../src","sources":["form-control/index.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,IAAAA,eAAA,GAAAC,OAAA","ignoreList":[]}
1
+ {"version":3,"names":["_useFormControl","require"],"sourceRoot":"../../../src","sources":["form-control/index.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,IAAAA,eAAA,GAAAC,OAAA","ignoreList":[]}
@@ -8,6 +8,7 @@ exports.useFormControl = useFormControl;
8
8
  exports.useFormControlContext = void 0;
9
9
  exports.useFormControlRoot = useFormControlRoot;
10
10
  exports.useReportFormControlLabelFocus = useReportFormControlLabelFocus;
11
+ exports.useReportFormControlValidation = useReportFormControlValidation;
11
12
  var _react = _interopRequireDefault(require("react"));
12
13
  var _reactNative = require("react-native");
13
14
  var _accessibilityUtils = require("../common/accessibilityUtils");
@@ -22,6 +23,8 @@ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e
22
23
  * minimal avoids a typed lie when a non-input bridge is registered.
23
24
  */
24
25
 
26
+ /** Validation a child control reports to its enclosing `Field` (see `useReportFormControlValidation`). */
27
+
25
28
  const FormControlContext = exports.FormControlContext = /*#__PURE__*/_react.default.createContext({});
26
29
 
27
30
  /** Props for the FormControl root; known fields plus passthrough to the styled root box. */
@@ -58,6 +61,7 @@ function useFormControlRoot(props) {
58
61
  */
59
62
  const [hasHelpText, setHasHelpText] = _react.default.useState(false);
60
63
  const [isLabelFocused, setIsLabelFocused] = _react.default.useState(false);
64
+ const [controlValidation, setControlValidation] = _react.default.useState(undefined);
61
65
  const inputRef = _react.default.useRef(null);
62
66
  const focusInput = _react.default.useCallback(() => {
63
67
  const node = inputRef.current;
@@ -67,9 +71,11 @@ function useFormControlRoot(props) {
67
71
  }, []);
68
72
  const context = {
69
73
  isRequired: Boolean(isRequired),
70
- isInvalid: Boolean(isInvalid),
74
+ isInvalid: Boolean(isInvalid) || Boolean(controlValidation?.isInvalid),
71
75
  isReadOnly: Boolean(isReadOnly),
72
76
  isDisabled: Boolean(isDisabled),
77
+ errorMessage: controlValidation?.errorMessage,
78
+ setControlValidation,
73
79
  hasFeedbackText,
74
80
  setHasFeedbackText,
75
81
  hasHelpText,
@@ -109,6 +115,38 @@ function useReportFormControlLabelFocus(isActive) {
109
115
  }, [isActive, setIsLabelFocused]);
110
116
  }
111
117
 
118
+ /**
119
+ * Reports a control's own validation (e.g. `DatePicker` bounds) to the enclosing `Field`, so the
120
+ * field root, `Field.Helper`, and `Field.Error` reflect it wherever they sit inside the field.
121
+ * Runs in a layout effect so the invalid state and message reach the field before paint.
122
+ *
123
+ * @returns `false` outside a `Field` (nothing to report to) — the caller must provide the state itself.
124
+ */
125
+ function useReportFormControlValidation(isInvalid, errorMessage) {
126
+ const {
127
+ setControlValidation
128
+ } = useFormControlContext();
129
+ _react.default.useLayoutEffect(() => {
130
+ if (typeof setControlValidation !== 'function') {
131
+ return undefined;
132
+ }
133
+ setControlValidation(prev => prev?.isInvalid === isInvalid && prev.errorMessage === errorMessage ? prev : {
134
+ isInvalid,
135
+ errorMessage
136
+ });
137
+ return undefined;
138
+ }, [errorMessage, isInvalid, setControlValidation]);
139
+ _react.default.useLayoutEffect(() => {
140
+ if (typeof setControlValidation !== 'function') {
141
+ return undefined;
142
+ }
143
+ return () => {
144
+ setControlValidation(undefined);
145
+ };
146
+ }, [setControlValidation]);
147
+ return typeof setControlValidation === 'function';
148
+ }
149
+
112
150
  /**
113
151
  * Hook that merges local field props with the nearest form field context
114
152
  * and returns normalised HTML / ARIA attributes for input elements.
@@ -1 +1 @@
1
- {"version":3,"names":["_react","_interopRequireDefault","require","_reactNative","_accessibilityUtils","e","__esModule","default","FormControlContext","exports","React","createContext","useFormControlRoot","props","id","idProp","name","isRequired","isInvalid","isDisabled","isReadOnly","htmlProps","reactId","useId","labelId","feedbackId","helpTextId","hasFeedbackText","setHasFeedbackText","useState","hasHelpText","setHasHelpText","isLabelFocused","setIsLabelFocused","inputRef","useRef","focusInput","useCallback","node","current","focus","context","Boolean","Platform","OS","useReportFormControlLabelFocus","isActive","useFormControlContext","useLayoutEffect","undefined","useFormControl","field","describedBy","push","ariaDescribedBy","join","cleanProps","disabled","readOnly","required","ariaAttr","useContext"],"sourceRoot":"../../../src","sources":["form-control/useFormControl.tsx"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["_react","_interopRequireDefault","require","_reactNative","_accessibilityUtils","e","__esModule","default","FormControlContext","exports","React","createContext","useFormControlRoot","props","id","idProp","name","isRequired","isInvalid","isDisabled","isReadOnly","htmlProps","reactId","useId","labelId","feedbackId","helpTextId","hasFeedbackText","setHasFeedbackText","useState","hasHelpText","setHasHelpText","isLabelFocused","setIsLabelFocused","controlValidation","setControlValidation","undefined","inputRef","useRef","focusInput","useCallback","node","current","focus","context","Boolean","errorMessage","Platform","OS","useReportFormControlLabelFocus","isActive","useFormControlContext","useLayoutEffect","useReportFormControlValidation","prev","useFormControl","field","describedBy","push","ariaDescribedBy","join","cleanProps","disabled","readOnly","required","ariaAttr","useContext"],"sourceRoot":"../../../src","sources":["form-control/useFormControl.tsx"],"mappings":"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","ignoreList":[]}
@@ -1,4 +1,4 @@
1
1
  "use strict";
2
2
 
3
- export { FormControlContext, useFormControl, useFormControlContext, useFormControlRoot, useReportFormControlLabelFocus } from './useFormControl';
3
+ export { FormControlContext, useFormControl, useFormControlContext, useFormControlRoot, useReportFormControlLabelFocus, useReportFormControlValidation } from './useFormControl';
4
4
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["FormControlContext","useFormControl","useFormControlContext","useFormControlRoot","useReportFormControlLabelFocus"],"sourceRoot":"../../../src","sources":["form-control/index.ts"],"mappings":";;AAAA,SAOEA,kBAAkB,EAClBC,cAAc,EACdC,qBAAqB,EACrBC,kBAAkB,EAClBC,8BAA8B,QACzB,kBAAkB","ignoreList":[]}
1
+ {"version":3,"names":["FormControlContext","useFormControl","useFormControlContext","useFormControlRoot","useReportFormControlLabelFocus","useReportFormControlValidation"],"sourceRoot":"../../../src","sources":["form-control/index.ts"],"mappings":";;AAAA,SAQEA,kBAAkB,EAClBC,cAAc,EACdC,qBAAqB,EACrBC,kBAAkB,EAClBC,8BAA8B,EAC9BC,8BAA8B,QACzB,kBAAkB","ignoreList":[]}
@@ -14,6 +14,8 @@ import { ariaAttr } from '../common/accessibilityUtils';
14
14
  * minimal avoids a typed lie when a non-input bridge is registered.
15
15
  */
16
16
 
17
+ /** Validation a child control reports to its enclosing `Field` (see `useReportFormControlValidation`). */
18
+
17
19
  export const FormControlContext = /*#__PURE__*/React.createContext({});
18
20
 
19
21
  /** Props for the FormControl root; known fields plus passthrough to the styled root box. */
@@ -50,6 +52,7 @@ export function useFormControlRoot(props) {
50
52
  */
51
53
  const [hasHelpText, setHasHelpText] = React.useState(false);
52
54
  const [isLabelFocused, setIsLabelFocused] = React.useState(false);
55
+ const [controlValidation, setControlValidation] = React.useState(undefined);
53
56
  const inputRef = React.useRef(null);
54
57
  const focusInput = React.useCallback(() => {
55
58
  const node = inputRef.current;
@@ -59,9 +62,11 @@ export function useFormControlRoot(props) {
59
62
  }, []);
60
63
  const context = {
61
64
  isRequired: Boolean(isRequired),
62
- isInvalid: Boolean(isInvalid),
65
+ isInvalid: Boolean(isInvalid) || Boolean(controlValidation?.isInvalid),
63
66
  isReadOnly: Boolean(isReadOnly),
64
67
  isDisabled: Boolean(isDisabled),
68
+ errorMessage: controlValidation?.errorMessage,
69
+ setControlValidation,
65
70
  hasFeedbackText,
66
71
  setHasFeedbackText,
67
72
  hasHelpText,
@@ -101,6 +106,38 @@ export function useReportFormControlLabelFocus(isActive) {
101
106
  }, [isActive, setIsLabelFocused]);
102
107
  }
103
108
 
109
+ /**
110
+ * Reports a control's own validation (e.g. `DatePicker` bounds) to the enclosing `Field`, so the
111
+ * field root, `Field.Helper`, and `Field.Error` reflect it wherever they sit inside the field.
112
+ * Runs in a layout effect so the invalid state and message reach the field before paint.
113
+ *
114
+ * @returns `false` outside a `Field` (nothing to report to) — the caller must provide the state itself.
115
+ */
116
+ export function useReportFormControlValidation(isInvalid, errorMessage) {
117
+ const {
118
+ setControlValidation
119
+ } = useFormControlContext();
120
+ React.useLayoutEffect(() => {
121
+ if (typeof setControlValidation !== 'function') {
122
+ return undefined;
123
+ }
124
+ setControlValidation(prev => prev?.isInvalid === isInvalid && prev.errorMessage === errorMessage ? prev : {
125
+ isInvalid,
126
+ errorMessage
127
+ });
128
+ return undefined;
129
+ }, [errorMessage, isInvalid, setControlValidation]);
130
+ React.useLayoutEffect(() => {
131
+ if (typeof setControlValidation !== 'function') {
132
+ return undefined;
133
+ }
134
+ return () => {
135
+ setControlValidation(undefined);
136
+ };
137
+ }, [setControlValidation]);
138
+ return typeof setControlValidation === 'function';
139
+ }
140
+
104
141
  /**
105
142
  * Hook that merges local field props with the nearest form field context
106
143
  * and returns normalised HTML / ARIA attributes for input elements.
@@ -1 +1 @@
1
- {"version":3,"names":["React","Platform","ariaAttr","FormControlContext","createContext","useFormControlRoot","props","id","idProp","name","isRequired","isInvalid","isDisabled","isReadOnly","htmlProps","reactId","useId","labelId","feedbackId","helpTextId","hasFeedbackText","setHasFeedbackText","useState","hasHelpText","setHasHelpText","isLabelFocused","setIsLabelFocused","inputRef","useRef","focusInput","useCallback","node","current","focus","context","Boolean","OS","useReportFormControlLabelFocus","isActive","useFormControlContext","useLayoutEffect","undefined","useFormControl","field","describedBy","push","ariaDescribedBy","join","cleanProps","disabled","readOnly","required","useContext"],"sourceRoot":"../../../src","sources":["form-control/useFormControl.tsx"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["React","Platform","ariaAttr","FormControlContext","createContext","useFormControlRoot","props","id","idProp","name","isRequired","isInvalid","isDisabled","isReadOnly","htmlProps","reactId","useId","labelId","feedbackId","helpTextId","hasFeedbackText","setHasFeedbackText","useState","hasHelpText","setHasHelpText","isLabelFocused","setIsLabelFocused","controlValidation","setControlValidation","undefined","inputRef","useRef","focusInput","useCallback","node","current","focus","context","Boolean","errorMessage","OS","useReportFormControlLabelFocus","isActive","useFormControlContext","useLayoutEffect","useReportFormControlValidation","prev","useFormControl","field","describedBy","push","ariaDescribedBy","join","cleanProps","disabled","readOnly","required","useContext"],"sourceRoot":"../../../src","sources":["form-control/useFormControl.tsx"],"mappings":"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","ignoreList":[]}
@@ -1,2 +1,2 @@
1
- export { type Focusable, type FormControlContextValue, type FormControlFieldProps, type FormControlRootProps, type FormControlRootReturn, type FormControlState, FormControlContext, useFormControl, useFormControlContext, useFormControlRoot, useReportFormControlLabelFocus, } from './useFormControl';
1
+ export { type Focusable, type FormControlContextValue, type FormControlFieldProps, type FormControlRootProps, type FormControlRootReturn, type FormControlState, type FormControlValidation, FormControlContext, useFormControl, useFormControlContext, useFormControlRoot, useReportFormControlLabelFocus, useReportFormControlValidation, } from './useFormControl';
2
2
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/form-control/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,SAAS,EACd,KAAK,uBAAuB,EAC5B,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,EAC1B,KAAK,gBAAgB,EACrB,kBAAkB,EAClB,cAAc,EACd,qBAAqB,EACrB,kBAAkB,EAClB,8BAA8B,GAC/B,MAAM,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/form-control/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,SAAS,EACd,KAAK,uBAAuB,EAC5B,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,EAC1B,KAAK,gBAAgB,EACrB,KAAK,qBAAqB,EAC1B,kBAAkB,EAClB,cAAc,EACd,qBAAqB,EACrB,kBAAkB,EAClB,8BAA8B,EAC9B,8BAA8B,GAC/B,MAAM,kBAAkB,CAAC"}
@@ -18,7 +18,17 @@ export interface FormControlState {
18
18
  isDisabled: boolean;
19
19
  isReadOnly: boolean;
20
20
  }
21
+ /** Validation a child control reports to its enclosing `Field` (see `useReportFormControlValidation`). */
22
+ export interface FormControlValidation {
23
+ isInvalid: boolean;
24
+ /** Message `Field.Error` renders when it has no children of its own. */
25
+ errorMessage?: string;
26
+ }
21
27
  export interface FormControlContextValue extends FormControlState {
28
+ /** Message reported by the field's control; `Field.Error` falls back to it. */
29
+ errorMessage?: string;
30
+ /** Lets a control that validates itself report into the field. Set by the field root only. */
31
+ setControlValidation?: React.Dispatch<React.SetStateAction<FormControlValidation | undefined>>;
22
32
  hasFeedbackText: boolean;
23
33
  hasHelpText: boolean;
24
34
  setHasFeedbackText: React.Dispatch<React.SetStateAction<boolean>>;
@@ -54,9 +64,12 @@ export type FormControlRootProps = Partial<FormControlState> & {
54
64
  } & HTMLProps;
55
65
  export interface FormControlRootReturn {
56
66
  isRequired: boolean;
67
+ /** `isInvalid` prop OR-ed with the validation a child control reported. */
57
68
  isInvalid: boolean;
58
69
  isReadOnly: boolean;
59
70
  isDisabled: boolean;
71
+ errorMessage?: string;
72
+ setControlValidation: React.Dispatch<React.SetStateAction<FormControlValidation | undefined>>;
60
73
  hasFeedbackText: boolean;
61
74
  setHasFeedbackText: React.Dispatch<React.SetStateAction<boolean>>;
62
75
  hasHelpText: boolean;
@@ -82,6 +95,14 @@ export declare function useFormControlRoot(props: FormControlRootProps): FormCon
82
95
  * No-ops outside a `Field` (no `setIsLabelFocused` on context).
83
96
  */
84
97
  export declare function useReportFormControlLabelFocus(isActive: boolean): void;
98
+ /**
99
+ * Reports a control's own validation (e.g. `DatePicker` bounds) to the enclosing `Field`, so the
100
+ * field root, `Field.Helper`, and `Field.Error` reflect it wherever they sit inside the field.
101
+ * Runs in a layout effect so the invalid state and message reach the field before paint.
102
+ *
103
+ * @returns `false` outside a `Field` (nothing to report to) — the caller must provide the state itself.
104
+ */
105
+ export declare function useReportFormControlValidation(isInvalid: boolean, errorMessage: string | undefined): boolean;
85
106
  /**
86
107
  * Hook that merges local field props with the nearest form field context
87
108
  * and returns normalised HTML / ARIA attributes for input elements.
@@ -1 +1 @@
1
- {"version":3,"file":"useFormControl.d.ts","sourceRoot":"","sources":["../../../src/form-control/useFormControl.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"useFormControl.d.ts","sourceRoot":"","sources":["../../../src/form-control/useFormControl.tsx"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cdx-ui/utils",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -5,9 +5,11 @@ export {
5
5
  type FormControlRootProps,
6
6
  type FormControlRootReturn,
7
7
  type FormControlState,
8
+ type FormControlValidation,
8
9
  FormControlContext,
9
10
  useFormControl,
10
11
  useFormControlContext,
11
12
  useFormControlRoot,
12
13
  useReportFormControlLabelFocus,
14
+ useReportFormControlValidation,
13
15
  } from './useFormControl';
@@ -7,6 +7,7 @@ import {
7
7
  useFormControlContext,
8
8
  useFormControlRoot,
9
9
  useReportFormControlLabelFocus,
10
+ useReportFormControlValidation,
10
11
  } from './useFormControl';
11
12
 
12
13
  function FormControlTestProvider({
@@ -212,3 +213,52 @@ describe('useReportFormControlLabelFocus', () => {
212
213
  }).not.toThrow();
213
214
  });
214
215
  });
216
+
217
+ describe('useReportFormControlValidation', () => {
218
+ it('ORs the reported state into the field and exposes the message', () => {
219
+ const { result, rerender, unmount } = renderHook(
220
+ ({ invalid, message }: { invalid: boolean; message?: string }) => {
221
+ const reported = useReportFormControlValidation(invalid, message);
222
+ const { isInvalid, errorMessage } = useFormControlContext();
223
+ return { reported, isInvalid, errorMessage };
224
+ },
225
+ {
226
+ wrapper: ({ children }) => <FormControlTestProvider>{children}</FormControlTestProvider>,
227
+ initialProps: { invalid: true, message: 'Too early' } as {
228
+ invalid: boolean;
229
+ message?: string;
230
+ },
231
+ },
232
+ );
233
+
234
+ expect(result.current).toEqual({ reported: true, isInvalid: true, errorMessage: 'Too early' });
235
+
236
+ rerender({ invalid: false, message: undefined });
237
+ expect(result.current).toEqual({ reported: true, isInvalid: false, errorMessage: undefined });
238
+
239
+ unmount();
240
+ });
241
+
242
+ it('keeps the field invalid when the root prop is set', () => {
243
+ const { result } = renderHook(
244
+ () => {
245
+ useReportFormControlValidation(false, undefined);
246
+ return useFormControlContext().isInvalid;
247
+ },
248
+ {
249
+ wrapper: ({ children }) => (
250
+ <FormControlTestProvider rootProps={{ id: 'email', isInvalid: true }}>
251
+ {children}
252
+ </FormControlTestProvider>
253
+ ),
254
+ },
255
+ );
256
+
257
+ expect(result.current).toBe(true);
258
+ });
259
+
260
+ it('returns false outside a field', () => {
261
+ const { result } = renderHook(() => useReportFormControlValidation(true, 'Too early'));
262
+ expect(result.current).toBe(false);
263
+ });
264
+ });
@@ -24,7 +24,18 @@ export interface FormControlState {
24
24
  isReadOnly: boolean;
25
25
  }
26
26
 
27
+ /** Validation a child control reports to its enclosing `Field` (see `useReportFormControlValidation`). */
28
+ export interface FormControlValidation {
29
+ isInvalid: boolean;
30
+ /** Message `Field.Error` renders when it has no children of its own. */
31
+ errorMessage?: string;
32
+ }
33
+
27
34
  export interface FormControlContextValue extends FormControlState {
35
+ /** Message reported by the field's control; `Field.Error` falls back to it. */
36
+ errorMessage?: string;
37
+ /** Lets a control that validates itself report into the field. Set by the field root only. */
38
+ setControlValidation?: React.Dispatch<React.SetStateAction<FormControlValidation | undefined>>;
28
39
  hasFeedbackText: boolean;
29
40
  hasHelpText: boolean;
30
41
  setHasFeedbackText: React.Dispatch<React.SetStateAction<boolean>>;
@@ -64,9 +75,12 @@ export type FormControlRootProps = Partial<FormControlState> & {
64
75
 
65
76
  export interface FormControlRootReturn {
66
77
  isRequired: boolean;
78
+ /** `isInvalid` prop OR-ed with the validation a child control reported. */
67
79
  isInvalid: boolean;
68
80
  isReadOnly: boolean;
69
81
  isDisabled: boolean;
82
+ errorMessage?: string;
83
+ setControlValidation: React.Dispatch<React.SetStateAction<FormControlValidation | undefined>>;
70
84
  hasFeedbackText: boolean;
71
85
  setHasFeedbackText: React.Dispatch<React.SetStateAction<boolean>>;
72
86
  hasHelpText: boolean;
@@ -111,6 +125,10 @@ export function useFormControlRoot(props: FormControlRootProps): FormControlRoot
111
125
 
112
126
  const [isLabelFocused, setIsLabelFocused] = React.useState(false);
113
127
 
128
+ const [controlValidation, setControlValidation] = React.useState<
129
+ FormControlValidation | undefined
130
+ >(undefined);
131
+
114
132
  const inputRef = React.useRef<Focusable | null>(null);
115
133
 
116
134
  const focusInput = React.useCallback(() => {
@@ -122,9 +140,11 @@ export function useFormControlRoot(props: FormControlRootProps): FormControlRoot
122
140
 
123
141
  const context: FormControlRootReturn = {
124
142
  isRequired: Boolean(isRequired),
125
- isInvalid: Boolean(isInvalid),
143
+ isInvalid: Boolean(isInvalid) || Boolean(controlValidation?.isInvalid),
126
144
  isReadOnly: Boolean(isReadOnly),
127
145
  isDisabled: Boolean(isDisabled),
146
+ errorMessage: controlValidation?.errorMessage,
147
+ setControlValidation,
128
148
  hasFeedbackText,
129
149
  setHasFeedbackText,
130
150
  hasHelpText,
@@ -161,6 +181,43 @@ export function useReportFormControlLabelFocus(isActive: boolean) {
161
181
  }, [isActive, setIsLabelFocused]);
162
182
  }
163
183
 
184
+ /**
185
+ * Reports a control's own validation (e.g. `DatePicker` bounds) to the enclosing `Field`, so the
186
+ * field root, `Field.Helper`, and `Field.Error` reflect it wherever they sit inside the field.
187
+ * Runs in a layout effect so the invalid state and message reach the field before paint.
188
+ *
189
+ * @returns `false` outside a `Field` (nothing to report to) — the caller must provide the state itself.
190
+ */
191
+ export function useReportFormControlValidation(
192
+ isInvalid: boolean,
193
+ errorMessage: string | undefined,
194
+ ): boolean {
195
+ const { setControlValidation } = useFormControlContext();
196
+
197
+ React.useLayoutEffect(() => {
198
+ if (typeof setControlValidation !== 'function') {
199
+ return undefined;
200
+ }
201
+ setControlValidation((prev) =>
202
+ prev?.isInvalid === isInvalid && prev.errorMessage === errorMessage
203
+ ? prev
204
+ : { isInvalid, errorMessage },
205
+ );
206
+ return undefined;
207
+ }, [errorMessage, isInvalid, setControlValidation]);
208
+
209
+ React.useLayoutEffect(() => {
210
+ if (typeof setControlValidation !== 'function') {
211
+ return undefined;
212
+ }
213
+ return () => {
214
+ setControlValidation(undefined);
215
+ };
216
+ }, [setControlValidation]);
217
+
218
+ return typeof setControlValidation === 'function';
219
+ }
220
+
164
221
  /**
165
222
  * Hook that merges local field props with the nearest form field context
166
223
  * and returns normalised HTML / ARIA attributes for input elements.