@agentero/design-system 0.28.0 → 0.29.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.
@@ -4822,6 +4822,13 @@
4822
4822
  "description": "Several errors render as a list, de-duplicated by message.",
4823
4823
  "summary": "Several errors rendered as a de-duplicated list"
4824
4824
  },
4825
+ {
4826
+ "id": "components-field--error-with-types",
4827
+ "name": "Error With Types",
4828
+ "snippet": "const ErrorWithTypes = () => <Field.Root\n orientation=\"vertical\"\n invalid\n disabled={false}\n readOnly={false}\n required={false}>\n <Label>Password</Label>\n <DemoInput type=\"password\" defaultValue=\"short\" />\n <Field.Error\n errors={[\n {\n message: 'At least one uppercase letter.',\n types: {\n uppercase: 'At least one uppercase letter.',\n number: 'At least one number.',\n length: ['At least 10 characters.'],\n unnamed: true\n }\n }\n ]} />\n</Field.Root>;",
4829
+ "description": "One error whose `types` lists every rule that failed renders as that same\nlist. A form library fills it when it collects all the failures instead of\nstopping at the first (react-hook-form's `criteriaMode: 'all'`), leaving only\nthe first one in `message`, so the messages take precedence over it. A rule\nthat failed without a message of its own contributes nothing.",
4830
+ "summary": "Every rule a single error collected, rendered as a list"
4831
+ },
4825
4832
  {
4826
4833
  "id": "components-field--no-error",
4827
4834
  "name": "No Error",
@@ -4888,6 +4895,532 @@
4888
4895
  ],
4889
4896
  "import": "import { Field, FieldContext, Label } from \"@agentero/design-system\";"
4890
4897
  },
4898
+ "components-formtext": {
4899
+ "id": "components-formtext",
4900
+ "name": "FormText",
4901
+ "path": "./src/form-text/form-text.stories.tsx",
4902
+ "jsDocTags": {
4903
+ "summary": [
4904
+ "Label, Input, description and error bound to one react-hook-form field"
4905
+ ],
4906
+ "example": [
4907
+ "<FormText\n name=\"taxId\"\n label=\"Tax ID\"\n tooltip=\"The EIN the IRS issued to the agency.\"\n description=\"Nine digits, with or without the dash.\"\n required\n rules={{ required: 'Enter the tax ID.', pattern: { value: /^\\d{2}-?\\d{7}$/, message: 'Nine digits.' } }}\n inputProps={{ inputMode: 'numeric', autoComplete: 'off' }}\n/>"
4908
+ ]
4909
+ },
4910
+ "description": "FormText is a complete text field bound to one react-hook-form value: label,\n`Input`, helper text and validation error in the standard order, wired\nthrough `useController`. It renders inside a `Form.Root` and needs only a\n`name`; `required`, `invalid` and the error reach the label, the input's\n`aria-*` attributes and the message with no ids written by hand.\n\nIt covers the common case. For another layout — an input with addons, a\ndifferent control, a message rendered elsewhere — compose `useController`\nwith the `FieldText`, `Label`, `Input` and `Field.*` primitives directly.",
4911
+ "summary": "Label, Input, description and error bound to one react-hook-form field",
4912
+ "reactComponentMeta": {
4913
+ "displayName": "FormText",
4914
+ "exportName": "FormText",
4915
+ "filePath": "/home/runner/work/design-system/design-system/src/form-text/index.ts",
4916
+ "description": "A complete text field bound to one react-hook-form value: label, `Input`,\nhelper text and validation error, laid out in the standard order and wired\nthrough `useController`. It renders inside a `Form.Root`, which provides the\nform, and needs only a `name`, which on a typed form must point at a string\n(`FormTextPath`). The `required`, `invalid` and error states\nreach the label, the input's `aria-*` attributes and the message with no\nids written by hand.\n\nUse it for the common case: a labelled single-line input in a form. When\nthe layout is different — an input with addons, another control, a\nmessage rendered elsewhere — compose the primitives yourself:\n`useController` with [FieldText](?path=/docs/components-fieldtext--docs),\n`Label`, `Input`, `Field.Description` and `Field.Error`. `FormText` adds no\nbehaviour of its own over that composition.\n\nErrors come from `useController`'s per-field state, never from\n`formState.errors`, so the message stays fresh under the React Compiler.\nWith `criteriaMode: 'all'` on the form the error carries every rule that\nfailed and `Field.Error` lists them all. An error on an array or object path\n(`error.root`) is not this field's: render a `Field.Error` for it where the\ngroup lives.",
4917
+ "jsDocTags": {
4918
+ "summary": [
4919
+ "Label, Input, description and error bound to one react-hook-form field"
4920
+ ],
4921
+ "example": [
4922
+ "<FormText\n name=\"taxId\"\n label=\"Tax ID\"\n tooltip=\"The EIN the IRS issued to the agency.\"\n description=\"Nine digits, with or without the dash.\"\n required\n rules={{ required: 'Enter the tax ID.', pattern: { value: /^\\d{2}-?\\d{7}$/, message: 'Nine digits.' } }}\n inputProps={{ inputMode: 'numeric', autoComplete: 'off' }}\n/>"
4923
+ ]
4924
+ },
4925
+ "props": {
4926
+ "disabled": {
4927
+ "name": "disabled",
4928
+ "required": false,
4929
+ "type": {
4930
+ "name": "boolean"
4931
+ },
4932
+ "description": "Disables the control through its context and sets `data-disabled` on the root.\nDisables the input and marks the field. Unlike react-hook-form's own\n`disabled` option, the value stays in the submitted data: disabling a\nfield is a presentation decision, not a change to the form's values.",
4933
+ "defaultValue": null,
4934
+ "parent": {
4935
+ "name": "FieldRootProps",
4936
+ "fileName": "src/field/field.tsx"
4937
+ },
4938
+ "declarations": [
4939
+ {
4940
+ "name": "TypeLiteral",
4941
+ "fileName": "src/field/field.tsx"
4942
+ },
4943
+ {
4944
+ "name": "TypeLiteral",
4945
+ "fileName": "src/form-text/form-text.tsx"
4946
+ }
4947
+ ]
4948
+ },
4949
+ "orientation": {
4950
+ "name": "orientation",
4951
+ "required": false,
4952
+ "type": {
4953
+ "name": "enum",
4954
+ "raw": "\"horizontal\" | \"vertical\" | \"responsive\"",
4955
+ "value": [
4956
+ {
4957
+ "value": "\"horizontal\""
4958
+ },
4959
+ {
4960
+ "value": "\"vertical\""
4961
+ },
4962
+ {
4963
+ "value": "\"responsive\""
4964
+ }
4965
+ ]
4966
+ },
4967
+ "description": "Layout of the field. `vertical` (default) stacks label, control and\nmessages. `horizontal` is one row at every width: the first child fills it\nand the rest keep their natural width, aligned to the right, so the\ncontrols of stacked fields line up. `responsive` stacks below `28rem` and\nbehaves like `horizontal` from there, measured on the field itself, so it\nneeds no particular wrapper. In both, `Field.Content` groups a control with\nits messages, or a label with its description.",
4968
+ "defaultValue": null,
4969
+ "parent": {
4970
+ "name": "FieldRootProps",
4971
+ "fileName": "src/field/field.tsx"
4972
+ },
4973
+ "declarations": [
4974
+ {
4975
+ "name": "TypeLiteral",
4976
+ "fileName": "src/field/field.tsx"
4977
+ }
4978
+ ]
4979
+ },
4980
+ "required": {
4981
+ "name": "required",
4982
+ "required": false,
4983
+ "type": {
4984
+ "name": "boolean"
4985
+ },
4986
+ "description": "Single source of truth for a required field: the `Label` shows its\nasterisk and the control receives `required`, both through context.\nMarks the field required: an asterisk on the label and the native\n`required` attribute on the input, which assistive technology announces.\nIt does not validate — pair it with `rules.required` or a resolver — and it\nnever triggers the browser bubble, since `Form.Root` renders `noValidate`.",
4987
+ "defaultValue": null,
4988
+ "parent": {
4989
+ "name": "FieldRootProps",
4990
+ "fileName": "src/field/field.tsx"
4991
+ },
4992
+ "declarations": [
4993
+ {
4994
+ "name": "TypeLiteral",
4995
+ "fileName": "src/field/field.tsx"
4996
+ },
4997
+ {
4998
+ "name": "TypeLiteral",
4999
+ "fileName": "src/form-text/form-text.tsx"
5000
+ }
5001
+ ]
5002
+ },
5003
+ "readOnly": {
5004
+ "name": "readOnly",
5005
+ "required": false,
5006
+ "type": {
5007
+ "name": "boolean"
5008
+ },
5009
+ "description": "Makes the control read-only through its context (focusable and copyable,\nnot editable, still submitted) and sets `data-readonly` on the root.",
5010
+ "defaultValue": null,
5011
+ "parent": {
5012
+ "name": "FieldRootProps",
5013
+ "fileName": "src/field/field.tsx"
5014
+ },
5015
+ "declarations": [
5016
+ {
5017
+ "name": "TypeLiteral",
5018
+ "fileName": "src/field/field.tsx"
5019
+ }
5020
+ ]
5021
+ },
5022
+ "controlId": {
5023
+ "name": "controlId",
5024
+ "required": false,
5025
+ "type": {
5026
+ "name": "string"
5027
+ },
5028
+ "description": "`id` for the control. Generated when omitted. Set it here, not on the\ncontrol, so the label and the messages keep pointing at the right element.",
5029
+ "defaultValue": null,
5030
+ "parent": {
5031
+ "name": "FieldRootProps",
5032
+ "fileName": "src/field/field.tsx"
5033
+ },
5034
+ "declarations": [
5035
+ {
5036
+ "name": "TypeLiteral",
5037
+ "fileName": "src/field/field.tsx"
5038
+ }
5039
+ ]
5040
+ },
5041
+ "name": {
5042
+ "name": "name",
5043
+ "required": true,
5044
+ "type": {
5045
+ "name": "\"agencyName\""
5046
+ },
5047
+ "description": "Path of the value in the form, nested paths included (`'agency.npn'`).\nType it against the form's values with the generic\n(`<FormText<Values> name=\"agency.npn\" />`) or by passing `control`: the\npath must then exist and hold a string (`FormTextPath`).",
5048
+ "defaultValue": null,
5049
+ "parent": {
5050
+ "name": "FormTextProps",
5051
+ "fileName": "src/form-text/form-text.tsx"
5052
+ },
5053
+ "declarations": [
5054
+ {
5055
+ "name": "TypeLiteral",
5056
+ "fileName": "src/form-text/form-text.tsx"
5057
+ }
5058
+ ]
5059
+ },
5060
+ "control": {
5061
+ "name": "control",
5062
+ "required": false,
5063
+ "type": {
5064
+ "name": "Control<FieldValues, any, FieldValues>"
5065
+ },
5066
+ "description": "The form's `control`, from `useForm()`. Defaults to the one the\nsurrounding `Form.Root` provides; pass it explicitly to get `name`\ninference without writing the generic, or when the field renders outside\na `Form.Root`. A `control` whose resolver transforms the values is\naccepted as is.",
5067
+ "defaultValue": null,
5068
+ "parent": {
5069
+ "name": "FormTextProps",
5070
+ "fileName": "src/form-text/form-text.tsx"
5071
+ },
5072
+ "declarations": [
5073
+ {
5074
+ "name": "TypeLiteral",
5075
+ "fileName": "src/form-text/form-text.tsx"
5076
+ }
5077
+ ]
5078
+ },
5079
+ "rules": {
5080
+ "name": "rules",
5081
+ "required": false,
5082
+ "type": {
5083
+ "name": "Omit<RegisterOptions<FieldValues, \"agencyName\">, \"disabled\" | \"valueAsNumber\" | \"valueAsDate\" | \"setValueAs\">"
5084
+ },
5085
+ "description": "react-hook-form validation rules for this field (`required`, `pattern`,\n`validate`…), same as `register`'s. They only validate: `rules.required`\ndoes not mark the field `required` — that is a separate, visible decision.",
5086
+ "defaultValue": null,
5087
+ "parent": {
5088
+ "name": "FormTextProps",
5089
+ "fileName": "src/form-text/form-text.tsx"
5090
+ },
5091
+ "declarations": [
5092
+ {
5093
+ "name": "TypeLiteral",
5094
+ "fileName": "src/form-text/form-text.tsx"
5095
+ }
5096
+ ]
5097
+ },
5098
+ "shouldUnregister": {
5099
+ "name": "shouldUnregister",
5100
+ "required": false,
5101
+ "type": {
5102
+ "name": "boolean"
5103
+ },
5104
+ "description": "Drops the value from the form when the field unmounts, react-hook-form's\n`shouldUnregister`. Defaults to `false`: a field revealed by another one\nkeeps its value while hidden.",
5105
+ "defaultValue": null,
5106
+ "parent": {
5107
+ "name": "FormTextProps",
5108
+ "fileName": "src/form-text/form-text.tsx"
5109
+ },
5110
+ "declarations": [
5111
+ {
5112
+ "name": "TypeLiteral",
5113
+ "fileName": "src/form-text/form-text.tsx"
5114
+ }
5115
+ ]
5116
+ },
5117
+ "label": {
5118
+ "name": "label",
5119
+ "required": true,
5120
+ "type": {
5121
+ "name": "ReactNode"
5122
+ },
5123
+ "description": "The caption, rendered as the field's label. Text in almost every case.",
5124
+ "defaultValue": null,
5125
+ "parent": {
5126
+ "name": "FormTextProps",
5127
+ "fileName": "src/form-text/form-text.tsx"
5128
+ },
5129
+ "declarations": [
5130
+ {
5131
+ "name": "TypeLiteral",
5132
+ "fileName": "src/form-text/form-text.tsx"
5133
+ }
5134
+ ]
5135
+ },
5136
+ "description": {
5137
+ "name": "description",
5138
+ "required": false,
5139
+ "type": {
5140
+ "name": "ReactNode"
5141
+ },
5142
+ "description": "Helper text shown under the control and announced through its\n`aria-describedby`. Use it for guidance the user needs before typing; put\ndetails worth a click in `tooltip` instead.",
5143
+ "defaultValue": null,
5144
+ "parent": {
5145
+ "name": "FormTextProps",
5146
+ "fileName": "src/form-text/form-text.tsx"
5147
+ },
5148
+ "declarations": [
5149
+ {
5150
+ "name": "TypeLiteral",
5151
+ "fileName": "src/form-text/form-text.tsx"
5152
+ }
5153
+ ]
5154
+ },
5155
+ "tooltip": {
5156
+ "name": "tooltip",
5157
+ "required": false,
5158
+ "type": {
5159
+ "name": "ReactNode"
5160
+ },
5161
+ "description": "Content of an info tooltip rendered beside the label, for details that\nwould clutter the description. The trigger is a sibling of the label, so\nthe field's accessible name stays the label text.",
5162
+ "defaultValue": null,
5163
+ "parent": {
5164
+ "name": "FormTextProps",
5165
+ "fileName": "src/form-text/form-text.tsx"
5166
+ },
5167
+ "declarations": [
5168
+ {
5169
+ "name": "TypeLiteral",
5170
+ "fileName": "src/form-text/form-text.tsx"
5171
+ }
5172
+ ]
5173
+ },
5174
+ "tooltipSide": {
5175
+ "name": "tooltipSide",
5176
+ "required": false,
5177
+ "type": {
5178
+ "name": "enum",
5179
+ "raw": "\"right\" | \"top\" | \"bottom\" | \"left\"",
5180
+ "value": [
5181
+ {
5182
+ "value": "\"right\""
5183
+ },
5184
+ {
5185
+ "value": "\"top\""
5186
+ },
5187
+ {
5188
+ "value": "\"bottom\""
5189
+ },
5190
+ {
5191
+ "value": "\"left\""
5192
+ }
5193
+ ]
5194
+ },
5195
+ "description": "Preferred side of the tooltip. Defaults to `'top'`.",
5196
+ "defaultValue": null,
5197
+ "parent": {
5198
+ "name": "FormTextProps",
5199
+ "fileName": "src/form-text/form-text.tsx"
5200
+ },
5201
+ "declarations": [
5202
+ {
5203
+ "name": "TypeLiteral",
5204
+ "fileName": "src/form-text/form-text.tsx"
5205
+ }
5206
+ ]
5207
+ },
5208
+ "optional": {
5209
+ "name": "optional",
5210
+ "required": false,
5211
+ "type": {
5212
+ "name": "boolean"
5213
+ },
5214
+ "description": "Appends a muted \" (optional)\" to the label. The forms mark optional fields\nrather than required ones; ignored when `required` is set.",
5215
+ "defaultValue": null,
5216
+ "parent": {
5217
+ "name": "FormTextProps",
5218
+ "fileName": "src/form-text/form-text.tsx"
5219
+ },
5220
+ "declarations": [
5221
+ {
5222
+ "name": "TypeLiteral",
5223
+ "fileName": "src/form-text/form-text.tsx"
5224
+ }
5225
+ ]
5226
+ },
5227
+ "inputProps": {
5228
+ "name": "inputProps",
5229
+ "required": false,
5230
+ "type": {
5231
+ "name": "FormTextInputProps"
5232
+ },
5233
+ "description": "Everything else the `Input` should receive: `type`, `placeholder`,\n`maxLength`, `autoComplete`, `size`, `className`… Handlers such as\n`onChange` and `onBlur` run after react-hook-form's, which are already\nwired.",
5234
+ "defaultValue": null,
5235
+ "parent": {
5236
+ "name": "FormTextProps",
5237
+ "fileName": "src/form-text/form-text.tsx"
5238
+ },
5239
+ "declarations": [
5240
+ {
5241
+ "name": "TypeLiteral",
5242
+ "fileName": "src/form-text/form-text.tsx"
5243
+ }
5244
+ ]
5245
+ }
5246
+ }
5247
+ },
5248
+ "stories": [
5249
+ {
5250
+ "id": "components-formtext--default",
5251
+ "name": "Default",
5252
+ "snippet": "const Default = () => <AgencyNameForm\n name=\"agencyName\"\n label=\"Agency name\"\n orientation=\"vertical\"\n required\n optional={false}\n disabled={false}\n readOnly={false} />;",
5253
+ "description": "The field consumers write: a `name`, a `label` and the validation rule.\nSubmitting empty renders the error and marks the input invalid; typing\nclears both and the value reaches `onSubmit`.",
5254
+ "summary": "Required text field validating on submit"
5255
+ },
5256
+ {
5257
+ "id": "components-formtext--with-description-and-tooltip",
5258
+ "name": "With Description And Tooltip",
5259
+ "snippet": "const WithDescriptionAndTooltip = () => <TaxIdForm />;",
5260
+ "description": "`description` is helper text under the input, announced through\n`aria-describedby`. `tooltip` is an info button beside the label for details\nworth a click; it is a sibling of the `<label>`, so the field's accessible\nname stays the label text.",
5261
+ "summary": "Helper text under the input and an info tooltip beside the label"
5262
+ },
5263
+ {
5264
+ "id": "components-formtext--optional",
5265
+ "name": "Optional",
5266
+ "snippet": "const Optional = () => <PhoneForm />;",
5267
+ "description": "The forms mark optional fields rather than required ones: `optional`\nappends a muted suffix to the label and leaves the input without the\n`required` attribute.",
5268
+ "summary": "Optional field with the \" (optional)\" label suffix"
5269
+ },
5270
+ {
5271
+ "id": "components-formtext--with-rules",
5272
+ "name": "With Rules",
5273
+ "snippet": "const WithRules = () => <NpnForm />;",
5274
+ "description": "`rules` are react-hook-form's, and `name` accepts nested paths typed against\nthe form's values through the generic. `rules.required` validates but does\nnot mark the field required: that stays an explicit `required` prop.",
5275
+ "summary": "Nested path with validation rules, typed through the generic"
5276
+ },
5277
+ {
5278
+ "id": "components-formtext--all-errors",
5279
+ "name": "All Errors",
5280
+ "snippet": "const AllErrors = () => <PasswordRulesForm />;",
5281
+ "description": "A form set to react-hook-form's `criteriaMode: 'all'` collects every rule\nthat failed instead of stopping at the first, and the field lists them all.\nWith the default `'firstError'` a single message is rendered, as in the other\nstories.",
5282
+ "summary": "Every failed rule listed under criteriaMode 'all'"
5283
+ },
5284
+ {
5285
+ "id": "components-formtext--input-props-chaining",
5286
+ "name": "Input Props Chaining",
5287
+ "snippet": "const InputPropsChaining = () => <MobileForm />;",
5288
+ "description": "`inputProps` carries the input's own attributes — `type`, `placeholder`,\n`maxLength`, `size`… — and its handlers. A consumer `onChange` runs after\nreact-hook-form's: by the time it fires, `getValues` already returns the new\nvalue.",
5289
+ "summary": "Input attributes and a consumer onChange chained after the form's"
5290
+ },
5291
+ {
5292
+ "id": "components-formtext--horizontal",
5293
+ "name": "Horizontal",
5294
+ "snippet": "const Horizontal = () => <ProfileForm />;",
5295
+ "description": "Root props pass through: `orientation=\"horizontal\"` lays the label and the\ncontrol in a row, and `className` on the root or in `inputProps` sizes them.\nThe `Input` is full-width by default, so a settings row gives it a width.\n`responsive` stacks the field below 28rem and rows it from there.",
5296
+ "summary": "Horizontal orientation and class names through the root props"
5297
+ },
5298
+ {
5299
+ "id": "components-formtext--disabled",
5300
+ "name": "Disabled",
5301
+ "snippet": "const Disabled = () => <AccountForm />;",
5302
+ "description": "`disabled` disables the input and marks the field, and the value still\nreaches `onSubmit` — unlike react-hook-form's own `disabled` option, which\ndrops it. Use `readOnly` for a value the user can copy but not edit.",
5303
+ "summary": "Disabled field whose value is still submitted"
5304
+ },
5305
+ {
5306
+ "id": "components-formtext--set-focus",
5307
+ "name": "Set Focus",
5308
+ "snippet": "const SetFocus = () => <SetFocusForm />;",
5309
+ "description": "react-hook-form's `ref` reaches the `<input>`, so `setFocus` and the focus\non the first invalid field after submit both work with no ref written by\nhand.",
5310
+ "summary": "setFocus reaches the input through the merged ref"
5311
+ }
5312
+ ],
5313
+ "import": "import { Button, Field, Form, FormText } from \"@agentero/design-system\";"
5314
+ },
5315
+ "components-form": {
5316
+ "id": "components-form",
5317
+ "name": "Form.Root",
5318
+ "path": "./src/form/form.stories.tsx",
5319
+ "jsDocTags": {
5320
+ "summary": [
5321
+ "Form element bound to a react-hook-form instance, shared with its fields"
5322
+ ],
5323
+ "example": [
5324
+ "const methods = useForm<Values>({ defaultValues: { agencyName: '', email: '' } });\n\n<Form.Root methods={methods} onSubmit={saveAgency} aria-label=\"Agency profile\">\n <FormText name=\"agencyName\" label=\"Agency name\" required rules={{ required: 'Enter the agency name.' }} />\n <FormText name=\"email\" label=\"Email\" inputProps={{ type: 'email' }} />\n <Button type=\"submit\">Save</Button>\n</Form.Root>"
5325
+ ]
5326
+ },
5327
+ "description": "Form is the react-hook-form binding of the design system. `Form.Root` renders\nthe `<form>` and provides the `useForm()` instance to the form fields inside\nit — `FormText` and the other `Form<X>` components — which bind themselves by\n`name`. The submit goes through `handleSubmit`, so `onSubmit` only runs with\nvalid, typed values, and the element is always `noValidate`: validation is\nreact-hook-form's, and the `required` a field sets is announced without the\nbrowser's own bubble.\n\n`react-hook-form` is an optional peer dependency: install it in the app and\nkeep a single copy, since the provider and the fields share one context.",
5328
+ "summary": "Form element bound to a react-hook-form instance, shared with its fields",
5329
+ "reactComponentMeta": {
5330
+ "displayName": "Form.Root",
5331
+ "exportName": "Form",
5332
+ "filePath": "/home/runner/work/design-system/design-system/src/form/index.ts",
5333
+ "description": "The `<form>` element of a react-hook-form form: it provides the `useForm()`\ninstance to the form fields inside it (`FormText` and the other `Form<X>`\ncomponents) and routes the submit through `handleSubmit`. It always renders\n`noValidate`, so the `required` attribute a field sets reaches assistive\ntechnology without the browser's own validation bubble getting in the way:\nvalidation is react-hook-form's, from `rules` or a resolver.\n\nUse it around any form built with the `Form<X>` fields. Without\nreact-hook-form, render a plain `<form>` with the presentational primitives\n([FieldText](?path=/docs/components-fieldtext--docs)) instead — this\ncomponent is only the binding.\n\n`react-hook-form` is an optional peer dependency: the design system never\nbundles its own copy, because `FormProvider` and the fields must share one\nReact context. Two copies in an app (a nested version under a workspace,\nsay) make every field throw from `useController`; `yarn why react-hook-form`\nshould list a single instance.",
5334
+ "jsDocTags": {
5335
+ "summary": [
5336
+ "Form element bound to a react-hook-form instance, shared with its fields"
5337
+ ],
5338
+ "example": [
5339
+ "const methods = useForm<Values>({ defaultValues: { agencyName: '', email: '' } });\n\n<Form.Root methods={methods} onSubmit={saveAgency} aria-label=\"Agency profile\">\n <FormText name=\"agencyName\" label=\"Agency name\" required rules={{ required: 'Enter the agency name.' }} />\n <FormText name=\"email\" label=\"Email\" inputProps={{ type: 'email' }} />\n <Button type=\"submit\">Save</Button>\n</Form.Root>"
5340
+ ]
5341
+ },
5342
+ "props": {
5343
+ "aria-label": {
5344
+ "name": "aria-label",
5345
+ "required": true,
5346
+ "type": {
5347
+ "name": "string"
5348
+ },
5349
+ "description": "Defines a string value that labels the current element.\nAccessible name of the form. A `<form>` only becomes a landmark when it is\nnamed, and screen readers list landmarks by name, so it is required.",
5350
+ "defaultValue": null,
5351
+ "parent": {
5352
+ "name": "AriaAttributes",
5353
+ "fileName": "node_modules/@types/react/index.d.ts"
5354
+ },
5355
+ "declarations": [
5356
+ {
5357
+ "name": "AriaAttributes",
5358
+ "fileName": "node_modules/@types/react/index.d.ts"
5359
+ },
5360
+ {
5361
+ "name": "TypeLiteral",
5362
+ "fileName": "src/form/form.tsx"
5363
+ }
5364
+ ]
5365
+ },
5366
+ "methods": {
5367
+ "name": "methods",
5368
+ "required": true,
5369
+ "type": {
5370
+ "name": "UseFormReturn<AgencyValues, any, AgencyValues>"
5371
+ },
5372
+ "description": "The object `useForm()` returns. `Form.Root` hands it to every form field\ninside through react-hook-form's `FormProvider`, so a `FormText` needs only\nits `name`. Create it in the component that owns the form and pass\n`defaultValues`: a field whose value is `undefined` on the first render\nstarts uncontrolled. A form whose resolver transforms the values\n(`useForm<Input, Context, Output>`) is accepted as is.",
5373
+ "defaultValue": null,
5374
+ "parent": {
5375
+ "name": "FormRootProps",
5376
+ "fileName": "src/form/form.tsx"
5377
+ },
5378
+ "declarations": [
5379
+ {
5380
+ "name": "TypeLiteral",
5381
+ "fileName": "src/form/form.tsx"
5382
+ }
5383
+ ]
5384
+ },
5385
+ "onSubmit": {
5386
+ "name": "onSubmit",
5387
+ "required": false,
5388
+ "type": {
5389
+ "name": "SubmitHandler<AgencyValues>"
5390
+ },
5391
+ "description": "Called with the validated values once validation passes: the resolver's\noutput when it transforms them, the form's values otherwise. Wrapped in\n`methods.handleSubmit`, which prevents the native submit and focuses the\nfirst invalid field; when it is omitted the form validates and does nothing\nelse.",
5392
+ "defaultValue": null,
5393
+ "parent": {
5394
+ "name": "FormRootProps",
5395
+ "fileName": "src/form/form.tsx"
5396
+ },
5397
+ "declarations": [
5398
+ {
5399
+ "name": "TypeLiteral",
5400
+ "fileName": "src/form/form.tsx"
5401
+ }
5402
+ ]
5403
+ }
5404
+ }
5405
+ },
5406
+ "stories": [
5407
+ {
5408
+ "id": "components-form--default",
5409
+ "name": "Default",
5410
+ "snippet": "const Default = () => <AgencyForm />;",
5411
+ "description": "A form with two required fields and a submit button. Submitting empty shows\nboth errors, marks both inputs invalid and focuses the first one — proof that\nreact-hook-form's `ref` reached the inputs. Once filled in, `onSubmit`\nreceives the typed values.",
5412
+ "summary": "Form.Root with FormText fields validating on submit"
5413
+ },
5414
+ {
5415
+ "id": "components-form--no-validate",
5416
+ "name": "No Validate",
5417
+ "snippet": "const NoValidate = () => <NicknameForm />;",
5418
+ "description": "`required` on a field reaches the native attribute, so assistive technology\nannounces it, but the form renders `noValidate`: without `rules`, submitting\nthe field empty is not blocked by the browser and `onSubmit` still runs.\nValidation is react-hook-form's alone.",
5419
+ "summary": "Native required never triggers the browser's validation bubble"
5420
+ }
5421
+ ],
5422
+ "import": "import { Button, Field, Form, FormText } from \"@agentero/design-system\";"
5423
+ },
4891
5424
  "components-hovercard": {
4892
5425
  "id": "components-hovercard",
4893
5426
  "name": "HoverCard.Root",
@@ -8073,6 +8606,6 @@
8073
8606
  },
8074
8607
  "meta": {
8075
8608
  "docgen": "react-component-meta",
8076
- "durationMs": 4116
8609
+ "durationMs": 4273
8077
8610
  }
8078
8611
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentero/design-system",
3
- "version": "0.28.0",
3
+ "version": "0.29.0",
4
4
  "description": "A React component library built with Tailwind CSS v4 and Radix UI primitives",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -73,6 +73,14 @@
73
73
  "types": "./src/field-text/index.d.ts",
74
74
  "import": "./src/field-text/index.js"
75
75
  },
76
+ "./form": {
77
+ "types": "./src/form/index.d.ts",
78
+ "import": "./src/form/index.js"
79
+ },
80
+ "./form-text": {
81
+ "types": "./src/form-text/index.d.ts",
82
+ "import": "./src/form-text/index.js"
83
+ },
76
84
  "./hover-card": {
77
85
  "types": "./src/hover-card/index.d.ts",
78
86
  "import": "./src/hover-card/index.js"
@@ -152,11 +160,15 @@
152
160
  "@tanstack/react-table": "^8.21.3",
153
161
  "react": "^19.2.4",
154
162
  "react-dom": "^19.2.4",
163
+ "react-hook-form": "^7.88.0",
155
164
  "tailwindcss": "^4.0.0"
156
165
  },
157
166
  "peerDependenciesMeta": {
158
167
  "@tanstack/react-table": {
159
168
  "optional": true
169
+ },
170
+ "react-hook-form": {
171
+ "optional": true
160
172
  }
161
173
  },
162
174
  "dependencies": {
@@ -2,6 +2,14 @@ import { ComponentPropsWithRef, ReactNode } from 'react';
2
2
  /** A validation error. Structurally matches a react-hook-form `FieldError`. */
3
3
  export type FieldErrorLike = {
4
4
  message?: string;
5
+ /**
6
+ * Every rule that failed, keyed by rule name. A form library fills it
7
+ * when it is set to collect all of them rather than stop at the first
8
+ * (react-hook-form's `criteriaMode: 'all'`), leaving only the first
9
+ * message in `message`. `Field.Error` renders all of them when it is
10
+ * there.
11
+ */
12
+ types?: Record<string, string | string[] | boolean | undefined>;
5
13
  } | undefined;
6
14
  /**
7
15
  * State of the surrounding `Field.Root`, shared with the Field family
@@ -203,7 +203,9 @@ export declare const Description: {
203
203
  * set `invalid` on `Field.Root` alongside it. Takes `children` or an `errors`
204
204
  * array, and a form adapter can supply `errors` through `FieldContext` so a
205
205
  * bare `<Field.Error />` renders them. One per field: it takes the field's
206
- * single error id, and several errors render as one list.
206
+ * single error id, and several errors render as one list — several, here,
207
+ * meaning either several entries or one entry whose `types` lists every rule
208
+ * that failed.
207
209
  *
208
210
  * @summary Validation feedback for the field, announced as an alert
209
211
  */
@@ -109,8 +109,11 @@ var b = ({ className: t, id: n, ...r }) => {
109
109
  });
110
110
  };
111
111
  b.displayName = "Field.Description";
112
- var x = ({ className: t, children: n, errors: r, id: i, ...o }) => {
113
- let s = a(), c = i ?? s?.errorId, l = r ?? s?.errors, d = s?.registerMessage, p = [...new Set(l?.flatMap((e) => e?.message || []))], m = n ?? (p.length > 1 ? /* @__PURE__ */ f("ul", { children: p.map((e) => /* @__PURE__ */ f("li", { children: e }, e)) }) : p[0]), g = !!m;
112
+ var x = (e) => {
113
+ let t = Object.values(e?.types ?? {}).flat().filter((e) => typeof e == "string" && e.length > 0);
114
+ return t.length ? t : e?.message ? [e.message] : [];
115
+ }, S = ({ className: t, children: n, errors: r, id: i, ...o }) => {
116
+ let s = a(), c = i ?? s?.errorId, l = r ?? s?.errors, d = s?.registerMessage, p = [...new Set(l?.flatMap(x))], m = n ?? (p.length > 1 ? /* @__PURE__ */ f("ul", { children: p.map((e) => /* @__PURE__ */ f("li", { children: e }, e)) }) : p[0]), g = !!m;
114
117
  return u(() => g && c ? d?.(c) : void 0, [
115
118
  d,
116
119
  g,
@@ -124,6 +127,6 @@ var x = ({ className: t, children: n, errors: r, id: i, ...o }) => {
124
127
  children: m
125
128
  }) : null;
126
129
  };
127
- x.displayName = "Field.Error";
130
+ S.displayName = "Field.Error";
128
131
  //#endregion
129
- export { v as Content, b as Description, x as FieldError, g as Group, y as Label, _ as Root, m as fieldRecipe };
132
+ export { v as Content, b as Description, S as FieldError, g as Group, y as Label, _ as Root, m as fieldRecipe };