@coopdigital/react 1.27.2 → 1.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.
Files changed (65) hide show
  1. package/dist/components/Button/Button.js +1 -1
  2. package/dist/components/Card/Card.js +1 -1
  3. package/dist/components/Checkbox/Checkbox.d.ts +3 -3
  4. package/dist/components/Checkbox/Checkbox.js +5 -3
  5. package/dist/components/Combobox/Combobox.js +6 -3
  6. package/dist/components/DatePicker/DatePicker.js +5 -3
  7. package/dist/components/Dialog/Dialog.js +1 -1
  8. package/dist/components/Expandable/Expandable.js +1 -1
  9. package/dist/components/Field/Field.context.js +5 -6
  10. package/dist/components/Field/Field.d.ts +2 -2
  11. package/dist/components/Field/Field.js +40 -20
  12. package/dist/components/FieldMarkers/Error.js +10 -3
  13. package/dist/components/FieldMarkers/Label.js +1 -5
  14. package/dist/components/FieldMarkers/fieldmarkers-utils.js +13 -0
  15. package/dist/components/Fieldset/Fieldset.context.js +7 -0
  16. package/dist/components/Fieldset/Fieldset.d.ts +3 -1
  17. package/dist/components/Fieldset/Fieldset.js +34 -7
  18. package/dist/components/Pill/Pill.js +1 -1
  19. package/dist/components/Popover/Popover.js +1 -1
  20. package/dist/components/Radio/Radio.d.ts +1 -1
  21. package/dist/components/Radio/Radio.js +5 -3
  22. package/dist/components/Searchbox/Searchbox.js +2 -1
  23. package/dist/components/Select/Select.d.ts +3 -3
  24. package/dist/components/Select/Select.js +5 -3
  25. package/dist/components/Tag/Tag.js +1 -1
  26. package/dist/components/TextInput/TextInput.d.ts +3 -3
  27. package/dist/components/TextInput/TextInput.js +7 -3
  28. package/dist/components/Textarea/Textarea.d.ts +3 -3
  29. package/dist/components/Textarea/Textarea.js +5 -3
  30. package/dist/components/ValidationSummary/ValidationSummary.context.js +10 -0
  31. package/dist/components/ValidationSummary/ValidationSummary.d.ts +1 -8
  32. package/dist/components/ValidationSummary/ValidationSummary.js +36 -17
  33. package/dist/components/ValidationSummary/validationsummary-utils.d.ts +11 -0
  34. package/dist/components/ValidationSummary/validationsummary-utils.js +29 -6
  35. package/dist/hooks/useSlots.js +2 -26
  36. package/dist/types/index.d.ts +15 -0
  37. package/dist/utils/classes.d.ts +6 -0
  38. package/dist/utils/classes.js +13 -0
  39. package/dist/utils/index.d.ts +3 -7
  40. package/dist/utils/index.js +3 -17
  41. package/dist/utils/slots.d.ts +9 -0
  42. package/dist/utils/slots.js +37 -0
  43. package/package.json +3 -3
  44. package/src/components/Checkbox/Checkbox.tsx +8 -5
  45. package/src/components/Combobox/Combobox.context.ts +1 -0
  46. package/src/components/Combobox/Combobox.tsx +7 -3
  47. package/src/components/DatePicker/DatePicker.tsx +5 -3
  48. package/src/components/Field/Field.context.ts +14 -8
  49. package/src/components/Field/Field.tsx +39 -20
  50. package/src/components/FieldMarkers/Error.tsx +2 -1
  51. package/src/components/FieldMarkers/Label.tsx +0 -7
  52. package/src/components/FieldMarkers/fieldmarkers-utils.ts +26 -0
  53. package/src/components/Fieldset/Fieldset.context.ts +13 -0
  54. package/src/components/Fieldset/Fieldset.tsx +39 -5
  55. package/src/components/Radio/Radio.tsx +6 -3
  56. package/src/components/Select/Select.tsx +7 -4
  57. package/src/components/TextInput/TextInput.tsx +9 -8
  58. package/src/components/Textarea/Textarea.tsx +8 -7
  59. package/src/components/ValidationSummary/ValidationSummary.context.ts +18 -0
  60. package/src/components/ValidationSummary/ValidationSummary.tsx +38 -24
  61. package/src/components/ValidationSummary/validationsummary-utils.ts +81 -13
  62. package/src/hooks/useSlots.ts +1 -41
  63. package/src/utils/classes.ts +14 -0
  64. package/src/utils/index.ts +2 -24
  65. package/src/utils/slots.ts +63 -0
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
  import { LoadingSpinner } from "../LoadingSpinner/LoadingSpinner.js";
3
- import { hasUserBg } from "../../utils/index.js";
3
+ import { hasUserBg } from "../../utils/classes.js";
4
4
  import clsx from "clsx";
5
5
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
6
6
  import React, { useCallback, useState } from "react";
@@ -1,5 +1,5 @@
1
1
  import { Image } from "../Image/Image.js";
2
- import { hasUserBg } from "../../utils/index.js";
2
+ import { hasUserBg } from "../../utils/classes.js";
3
3
  import Picture_default from "../Picture/index.js";
4
4
  import { useSlots } from "../../hooks/useSlots.js";
5
5
  import clsx from "clsx";
@@ -10,15 +10,15 @@ export interface CheckboxProps extends Omit<InputHTMLAttributes<HTMLInputElement
10
10
  error?: boolean;
11
11
  /** Specify the component id. Will be auto-generated if not set. */
12
12
  id?: string;
13
- /** Specify the component name. */
14
- name: string;
13
+ /** Specify the component name. Without a name, the value is not submitted with a form. */
14
+ name?: string;
15
15
  /** Specify a custom React ref for this component. */
16
16
  ref?: Ref<HTMLInputElement>;
17
17
  /** Specify the component size. */
18
18
  size?: StandardSizes;
19
19
  }
20
20
  declare const Checkbox: {
21
- ({ className, disabled, error, id, name, ref, size, ...props }: CheckboxProps): JSX.Element;
21
+ ({ "aria-describedby": ariaDescribedBy, className, disabled, error, id, name, ref, size, ...props }: CheckboxProps): JSX.Element;
22
22
  slot: {
23
23
  field: boolean;
24
24
  inline: boolean;
@@ -1,13 +1,15 @@
1
1
  "use client";
2
- import { useField } from "../Field/Field.context.js";
2
+ import { getDescribedBy, getFieldError, useField } from "../Field/Field.context.js";
3
3
  import clsx from "clsx";
4
4
  import { jsx } from "react/jsx-runtime";
5
5
  //#region src/components/Checkbox/Checkbox.tsx
6
- const Checkbox = ({ className, disabled, error = false, id, name, ref, size, ...props }) => {
6
+ const Checkbox = ({ "aria-describedby": ariaDescribedBy, className, disabled, error, id, name, ref, size, ...props }) => {
7
7
  const field = useField();
8
+ const hasError = getFieldError(error, field);
8
9
  const componentProps = {
10
+ "aria-describedby": getDescribedBy(ariaDescribedBy, field),
11
+ "aria-invalid": hasError || void 0,
9
12
  className: clsx("coop-checkbox", className),
10
- "data-error": error || void 0,
11
13
  "data-size": size ?? void 0,
12
14
  disabled,
13
15
  id: id ?? field?.id,
@@ -1,8 +1,8 @@
1
1
  "use client";
2
2
  import { ChevronDownIcon } from "../icons/ChevronDownIcon.js";
3
- import { getSlotProp } from "../../utils/index.js";
3
+ import { getSlotProp } from "../../utils/slots.js";
4
4
  import { useSlots } from "../../hooks/useSlots.js";
5
- import { useField } from "../Field/Field.context.js";
5
+ import { getFieldError, useField } from "../Field/Field.context.js";
6
6
  import { defaultFilter, defaultIsItemDisabled, defaultItemToString, normalizeOptions } from "./combobox-utils.js";
7
7
  import { ComboboxContext, useComboboxContext } from "./Combobox.context.js";
8
8
  import { TextInput } from "../TextInput/TextInput.js";
@@ -18,13 +18,14 @@ const componentSlots = {
18
18
  const Combobox = ({ allowCustom = false, children, className, disabled, error, id, inputFilter, isItemDisabled, itemToString, onChange, onHighlight, onSelect, onStateChange, onToggle, openOnClick = true, ref, size, ...props }) => {
19
19
  const field = useField();
20
20
  const uid = id ?? field?.id;
21
+ const hasError = getFieldError(error, field);
21
22
  const slots = useSlots(componentSlots, children);
22
23
  const userOptions = getSlotProp(slots.ComboboxOptions, "options") ?? [];
23
24
  const items = normalizeOptions(userOptions);
24
25
  const [inputItems, setInputItems] = useState(items);
25
26
  const componentProps = {
26
27
  className: clsx("coop-combobox", className),
27
- "data-error": error ?? void 0,
28
+ "data-field": "",
28
29
  "data-size": size ?? void 0,
29
30
  id: uid,
30
31
  ...props
@@ -74,6 +75,7 @@ const Combobox = ({ allowCustom = false, children, className, disabled, error, i
74
75
  return /* @__PURE__ */ jsx(ComboboxContext.Provider, {
75
76
  value: {
76
77
  disabled,
78
+ error: hasError,
77
79
  items: inputItems,
78
80
  props: comboboxProps,
79
81
  size
@@ -90,6 +92,7 @@ const ComboboxInput = ({ className, placeholder, ...props }) => {
90
92
  const componentProps = {
91
93
  className,
92
94
  disabled: combobox.disabled,
95
+ error: combobox.error,
93
96
  placeholder,
94
97
  size: combobox.size,
95
98
  ...props
@@ -3,7 +3,7 @@ import { CalendarIcon } from "../icons/CalendarIcon.js";
3
3
  import { CrossIcon } from "../icons/CrossIcon.js";
4
4
  import { TickIcon } from "../icons/TickIcon.js";
5
5
  import { Button } from "../Button/Button.js";
6
- import { useField } from "../Field/Field.context.js";
6
+ import { getFieldError, useField } from "../Field/Field.context.js";
7
7
  import { TextInput } from "../TextInput/TextInput.js";
8
8
  import { Popover } from "../Popover/Popover.js";
9
9
  import { getDateValues, getDefaultMonth, getEndDate, getInitialDate, getStartDate, shouldClose, shouldSkipParsing } from "./datepicker-utils.js";
@@ -45,8 +45,9 @@ const Calendar = ({ captionLayout = "label", className, disabled, endMonth, form
45
45
  ...props
46
46
  });
47
47
  };
48
- const DatePicker = ({ className, closeOnSelect = true, dateFormat = "dd/MM/yyyy", disabled, disabledDates, endDate, error = false, id, initialDate = now, initialSelected = false, mode = "single", name, onChange, onSelect, ref, required = false, size, startDate, ...props }) => {
48
+ const DatePicker = ({ className, closeOnSelect = true, dateFormat = "dd/MM/yyyy", disabled, disabledDates, endDate, error, id, initialDate = now, initialSelected = false, mode = "single", name, onChange, onSelect, ref, required = false, size, startDate, ...props }) => {
49
49
  const field = useField();
50
+ const hasError = getFieldError(error, field);
50
51
  initialDate = getInitialDate(mode, initialDate);
51
52
  const startDateProp = startDate;
52
53
  const endDateProp = endDate;
@@ -112,7 +113,7 @@ const DatePicker = ({ className, closeOnSelect = true, dateFormat = "dd/MM/yyyy"
112
113
  const componentProps = {
113
114
  className: clsx("coop-datepicker", className),
114
115
  "data-disabled": disabled ?? void 0,
115
- "data-error": error || void 0,
116
+ "data-field": "",
116
117
  "data-mode": mode,
117
118
  ...props
118
119
  };
@@ -165,6 +166,7 @@ const DatePicker = ({ className, closeOnSelect = true, dateFormat = "dd/MM/yyyy"
165
166
  autoComplete: "off",
166
167
  className: "coop-datepicker-input",
167
168
  disabled,
169
+ error: hasError,
168
170
  id: valueProps.id + "_display",
169
171
  name: name + "_display",
170
172
  onChange: updateFromInput,
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
  import { CrossIcon } from "../icons/CrossIcon.js";
3
- import { hasUserBg } from "../../utils/index.js";
3
+ import { hasUserBg } from "../../utils/classes.js";
4
4
  import clsx from "clsx";
5
5
  import { jsx, jsxs } from "react/jsx-runtime";
6
6
  import { createContext, useContext, useEffect, useRef } from "react";
@@ -1,5 +1,5 @@
1
1
  import { ChevronDownIcon } from "../icons/ChevronDownIcon.js";
2
- import { hasUserBg } from "../../utils/index.js";
2
+ import { hasUserBg } from "../../utils/classes.js";
3
3
  import { useSlots } from "../../hooks/useSlots.js";
4
4
  import clsx from "clsx";
5
5
  import { jsx, jsxs } from "react/jsx-runtime";
@@ -1,11 +1,10 @@
1
1
  "use client";
2
+ import { joinIds } from "../FieldMarkers/fieldmarkers-utils.js";
2
3
  import { createContext, useContext } from "react";
3
4
  //#region src/components/Field/Field.context.ts
4
5
  const FieldContext = createContext(null);
5
- const useField = () => {
6
- const context = useContext(FieldContext);
7
- if (!context) return null;
8
- return context;
9
- };
6
+ const useField = () => useContext(FieldContext);
7
+ const getDescribedBy = (describedBy, field) => joinIds(field?.describedBy, describedBy);
8
+ const getFieldError = (error, field) => error ?? field?.error ?? false;
10
9
  //#endregion
11
- export { FieldContext, useField };
10
+ export { FieldContext, getDescribedBy, getFieldError, useField };
@@ -12,7 +12,7 @@ interface FieldProps extends HTMLAttributes<HTMLDivElement> {
12
12
  children?: React.ReactNode;
13
13
  /** Specify additional CSS classes. */
14
14
  classname?: string;
15
- /** Specify the component error state. */
15
+ /** Specify the component error state. Inherits the error state of a Fieldset with `group` set when not specified. */
16
16
  error?: boolean;
17
17
  /** Specify whether the Field should show the left-hand error bar if it is in an error state. */
18
18
  hideErrorBar?: boolean;
@@ -20,7 +20,7 @@ interface FieldProps extends HTMLAttributes<HTMLDivElement> {
20
20
  ref?: Ref<HTMLDivElement>;
21
21
  }
22
22
  declare const Field: {
23
- ({ boxed, children, className, error, hideErrorBar, ref, ...props }: FieldProps): JSX.Element;
23
+ ({ boxed, children, className, error: errorProp, hideErrorBar, ref, ...props }: FieldProps): JSX.Element;
24
24
  slot: {
25
25
  name: string;
26
26
  };
@@ -1,32 +1,35 @@
1
1
  "use client";
2
2
  import { useId as useId$1 } from "../../hooks/useId.js";
3
- import { getSlotProp } from "../../utils/index.js";
4
- import { FieldContext } from "./Field.context.js";
3
+ import { forEachSlot, getSlotProp } from "../../utils/slots.js";
4
+ import { getMarkerIds } from "../FieldMarkers/fieldmarkers-utils.js";
5
+ import { FieldContext, useField } from "./Field.context.js";
5
6
  import { Error } from "../FieldMarkers/Error.js";
6
7
  import { Hint } from "../FieldMarkers/Hint.js";
7
8
  import { Label } from "../FieldMarkers/Label.js";
9
+ import { useFieldset } from "../Fieldset/Fieldset.context.js";
8
10
  import clsx from "clsx";
9
11
  import { jsx } from "react/jsx-runtime";
10
12
  import "react";
11
13
  //#region src/components/Field/Field.tsx
12
- const Field = ({ boxed = false, children, className, error = false, hideErrorBar = false, ref, ...props }) => {
14
+ const Field = ({ boxed = false, children, className, error: errorProp, hideErrorBar = false, ref, ...props }) => {
15
+ const internalId = useId$1();
16
+ const fieldset = useFieldset();
17
+ const error = errorProp ?? fieldset?.error ?? false;
13
18
  const controlProps = {
14
19
  disabled: false,
15
- id: useId$1()
20
+ id: internalId
16
21
  };
17
- Array.from([children].flat()).forEach((child) => {
18
- if (child && typeof child === "object" && "type" in child) {
19
- if (typeof child?.type === "function" && "slot" in child.type) {
20
- const { slot } = child.type;
21
- if (slot.field && child.props) {
22
- controlProps.inline = slot.inline ?? false;
23
- controlProps.disabled = getSlotProp(child, "disabled") ?? false;
24
- controlProps.id = getSlotProp(child, "id") ?? controlProps.id;
25
- controlProps.suffix = slot.suffix ?? void 0;
26
- }
27
- }
28
- }
22
+ const markers = {};
23
+ forEachSlot(children, (slot, child) => {
24
+ if (slot.field) {
25
+ controlProps.inline = slot.inline ?? false;
26
+ controlProps.disabled = getSlotProp(child, "disabled") ?? false;
27
+ controlProps.id = getSlotProp(child, "id") ?? controlProps.id;
28
+ controlProps.suffix = slot.suffix ?? void 0;
29
+ } else if (slot.name === "FieldHint") markers.hint = { id: getSlotProp(child, "id") };
30
+ else if (slot.name === "FieldError") markers.error = { id: getSlotProp(child, "id") };
29
31
  });
32
+ const markerIds = getMarkerIds(markers, controlProps.id);
30
33
  const componentProps = {
31
34
  ...props,
32
35
  "aria-disabled": controlProps.disabled || void 0,
@@ -36,7 +39,11 @@ const Field = ({ boxed = false, children, className, error = false, hideErrorBar
36
39
  "data-hide-error": hideErrorBar || void 0
37
40
  };
38
41
  return /* @__PURE__ */ jsx(FieldContext.Provider, {
39
- value: { ...controlProps },
42
+ value: {
43
+ ...controlProps,
44
+ ...markerIds,
45
+ error
46
+ },
40
47
  children: /* @__PURE__ */ jsx("div", {
41
48
  ...componentProps,
42
49
  ref,
@@ -44,9 +51,22 @@ const Field = ({ boxed = false, children, className, error = false, hideErrorBar
44
51
  })
45
52
  });
46
53
  };
47
- const FieldLabel = (props) => /* @__PURE__ */ jsx(Label, { ...props });
48
- const FieldHint = (props) => /* @__PURE__ */ jsx(Hint, { ...props });
49
- const FieldError = (props) => /* @__PURE__ */ jsx(Error, { ...props });
54
+ const FieldLabel = (props) => {
55
+ const field = useField();
56
+ return /* @__PURE__ */ jsx(Label, {
57
+ htmlFor: field ? field.id + (field.suffix ?? "") : void 0,
58
+ id: field ? `${field.id}_label` : void 0,
59
+ ...props
60
+ });
61
+ };
62
+ const FieldHint = (props) => /* @__PURE__ */ jsx(Hint, {
63
+ id: useField()?.hintId,
64
+ ...props
65
+ });
66
+ const FieldError = (props) => /* @__PURE__ */ jsx(Error, {
67
+ id: useField()?.errorId,
68
+ ...props
69
+ });
50
70
  Field.slot = { name: "Field" };
51
71
  FieldLabel.slot = { name: "FieldLabel" };
52
72
  FieldHint.slot = { name: "FieldHint" };
@@ -1,15 +1,22 @@
1
1
  import clsx from "clsx";
2
- import { jsx } from "react/jsx-runtime";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
3
  //#region src/components/FieldMarkers/Error.tsx
4
4
  const Error = ({ children, className, ref, ...props }) => {
5
5
  const componentProps = {
6
6
  className: clsx("coop-field-error ", className),
7
7
  ...props
8
8
  };
9
- return /* @__PURE__ */ jsx("span", {
9
+ return /* @__PURE__ */ jsxs("span", {
10
10
  ...componentProps,
11
11
  ref,
12
- children
12
+ children: [
13
+ /* @__PURE__ */ jsx("span", {
14
+ className: "sr-only",
15
+ children: "Error:"
16
+ }),
17
+ " ",
18
+ children
19
+ ]
13
20
  });
14
21
  };
15
22
  //#endregion
@@ -1,13 +1,9 @@
1
- import { useField } from "../Field/Field.context.js";
2
1
  import clsx from "clsx";
3
2
  import { jsx } from "react/jsx-runtime";
4
3
  //#region src/components/FieldMarkers/Label.tsx
5
- const Label = ({ children, className, htmlFor, id, isVisible = true, ref, ...props }) => {
6
- const field = useField();
4
+ const Label = ({ children, className, isVisible = true, ref, ...props }) => {
7
5
  const componentProps = {
8
6
  className: clsx("coop-field-label", !isVisible && "sr-only", className),
9
- htmlFor: htmlFor ?? (field?.id ? field.id + (field?.suffix ?? "") : void 0),
10
- id: id ?? (field?.id ? field.id + "_label" : void 0),
11
7
  ...props
12
8
  };
13
9
  return /* @__PURE__ */ jsx("label", {
@@ -0,0 +1,13 @@
1
+ //#region src/components/FieldMarkers/fieldmarkers-utils.ts
2
+ const joinIds = (...ids) => ids.filter(Boolean).join(" ") || void 0;
3
+ const getMarkerIds = (markers, baseId) => {
4
+ const hintId = markers.hint?.id ?? `${baseId}_hint`;
5
+ const errorId = markers.error?.id ?? `${baseId}_error`;
6
+ return {
7
+ describedBy: joinIds(markers.hint && hintId, markers.error && errorId),
8
+ errorId,
9
+ hintId
10
+ };
11
+ };
12
+ //#endregion
13
+ export { getMarkerIds, joinIds };
@@ -0,0 +1,7 @@
1
+ "use client";
2
+ import { createContext, useContext } from "react";
3
+ //#region src/components/Fieldset/Fieldset.context.ts
4
+ const FieldsetContext = createContext(null);
5
+ const useFieldset = () => useContext(FieldsetContext);
6
+ //#endregion
7
+ export { FieldsetContext, useFieldset };
@@ -14,13 +14,15 @@ export interface FieldsetProps extends FieldsetHTMLAttributes<HTMLFieldSetElemen
14
14
  disabled?: boolean;
15
15
  /** Specify the component error state. */
16
16
  error?: boolean;
17
+ /** Specify whether the Fieldset is a single question, such as a group of Radio inputs. Fields inside inherit its error state. */
18
+ group?: boolean;
17
19
  /** Specify whether the Fieldset should show the left-hand error bar if it is in an error state. */
18
20
  hideErrorBar?: boolean;
19
21
  /** Specify a custom React ref for this component. */
20
22
  ref?: Ref<HTMLFieldSetElement>;
21
23
  }
22
24
  declare const Fieldset: {
23
- ({ children, className, disabled, error, hideErrorBar, ref, ...props }: FieldsetProps): JSX.Element;
25
+ ({ "aria-describedby": ariaDescribedBy, children, className, disabled, error, group, hideErrorBar, id, ref, ...props }: FieldsetProps): JSX.Element;
24
26
  slot: {
25
27
  name: string;
26
28
  };
@@ -1,22 +1,43 @@
1
+ "use client";
2
+ import { useId } from "../../hooks/useId.js";
3
+ import { forEachSlot, getSlotProp } from "../../utils/slots.js";
4
+ import { getMarkerIds, joinIds } from "../FieldMarkers/fieldmarkers-utils.js";
1
5
  import { Error } from "../FieldMarkers/Error.js";
2
6
  import { Hint } from "../FieldMarkers/Hint.js";
7
+ import { FieldsetContext, useFieldset } from "./Fieldset.context.js";
3
8
  import { Legend } from "../FieldMarkers/Legend.js";
4
9
  import clsx from "clsx";
5
10
  import { jsx } from "react/jsx-runtime";
6
11
  //#region src/components/Fieldset/Fieldset.tsx
7
- const Fieldset = ({ children, className, disabled, error = false, hideErrorBar = false, ref, ...props }) => {
12
+ const Fieldset = ({ "aria-describedby": ariaDescribedBy, children, className, disabled, error = false, group = false, hideErrorBar = false, id, ref, ...props }) => {
13
+ const uid = useId(id);
14
+ const markers = {};
15
+ forEachSlot(children, (slot, child) => {
16
+ if (slot.name === "FieldsetHint") markers.hint = { id: getSlotProp(child, "id") };
17
+ else if (slot.name === "FieldsetError") markers.error = { id: getSlotProp(child, "id") };
18
+ });
19
+ const { describedBy, errorId, hintId } = getMarkerIds(markers, uid);
8
20
  const componentProps = {
21
+ "aria-describedby": joinIds(describedBy, ariaDescribedBy),
9
22
  "aria-disabled": disabled,
10
23
  className: clsx("coop-fieldset", className),
11
24
  "data-error": error || void 0,
12
25
  "data-hide-error": hideErrorBar || void 0,
13
26
  disabled,
27
+ id,
14
28
  ...props
15
29
  };
16
- return /* @__PURE__ */ jsx("fieldset", {
17
- ...componentProps,
18
- ref,
19
- children
30
+ return /* @__PURE__ */ jsx(FieldsetContext.Provider, {
31
+ value: {
32
+ error: group && error,
33
+ errorId,
34
+ hintId
35
+ },
36
+ children: /* @__PURE__ */ jsx("fieldset", {
37
+ ...componentProps,
38
+ ref,
39
+ children
40
+ })
20
41
  });
21
42
  };
22
43
  const FieldsetFields = ({ children, className, orientation = "vertical", ...props }) => {
@@ -31,8 +52,14 @@ const FieldsetFields = ({ children, className, orientation = "vertical", ...prop
31
52
  });
32
53
  };
33
54
  const FieldsetLegend = (props) => /* @__PURE__ */ jsx(Legend, { ...props });
34
- const FieldsetHint = (props) => /* @__PURE__ */ jsx(Hint, { ...props });
35
- const FieldsetError = (props) => /* @__PURE__ */ jsx(Error, { ...props });
55
+ const FieldsetHint = (props) => /* @__PURE__ */ jsx(Hint, {
56
+ id: useFieldset()?.hintId,
57
+ ...props
58
+ });
59
+ const FieldsetError = (props) => /* @__PURE__ */ jsx(Error, {
60
+ id: useFieldset()?.errorId,
61
+ ...props
62
+ });
36
63
  Fieldset.slot = { name: "Fieldset" };
37
64
  FieldsetFields.slot = { name: "FieldsetField" };
38
65
  FieldsetLegend.slot = { name: "FieldsetLegend" };
@@ -1,4 +1,4 @@
1
- import { hasUserBg } from "../../utils/index.js";
1
+ import { hasUserBg } from "../../utils/classes.js";
2
2
  import { useSlots } from "../../hooks/useSlots.js";
3
3
  import clsx from "clsx";
4
4
  import { jsx } from "react/jsx-runtime";
@@ -1,4 +1,4 @@
1
- import { bgClassToColor, hasUserBg } from "../../utils/index.js";
1
+ import { bgClassToColor, hasUserBg } from "../../utils/classes.js";
2
2
  import { useSlots } from "../../hooks/useSlots.js";
3
3
  import clsx from "clsx";
4
4
  import { jsx, jsxs } from "react/jsx-runtime";
@@ -18,7 +18,7 @@ export interface RadioProps extends Omit<InputHTMLAttributes<HTMLInputElement>,
18
18
  size?: StandardSizes;
19
19
  }
20
20
  declare const Radio: {
21
- ({ className, disabled, error, id, name, ref, size, ...props }: RadioProps): JSX.Element;
21
+ ({ "aria-describedby": ariaDescribedBy, className, disabled, error, id, name, ref, size, ...props }: RadioProps): JSX.Element;
22
22
  slot: {
23
23
  field: boolean;
24
24
  inline: boolean;
@@ -1,13 +1,15 @@
1
1
  "use client";
2
- import { useField } from "../Field/Field.context.js";
2
+ import { getDescribedBy, getFieldError, useField } from "../Field/Field.context.js";
3
3
  import clsx from "clsx";
4
4
  import { jsx } from "react/jsx-runtime";
5
5
  //#region src/components/Radio/Radio.tsx
6
- const Radio = ({ className, disabled, error = false, id, name, ref, size, ...props }) => {
6
+ const Radio = ({ "aria-describedby": ariaDescribedBy, className, disabled, error, id, name, ref, size, ...props }) => {
7
7
  const field = useField();
8
+ const hasError = getFieldError(error, field);
8
9
  const componentProps = {
10
+ "aria-describedby": getDescribedBy(ariaDescribedBy, field),
11
+ "aria-invalid": hasError || void 0,
9
12
  className: clsx("coop-radio", className),
10
- "data-error": error || void 0,
11
13
  "data-size": size ?? void 0,
12
14
  disabled,
13
15
  id: id ?? field?.id,
@@ -1,7 +1,8 @@
1
1
  "use client";
2
2
  import { useId as useId$1 } from "../../hooks/useId.js";
3
3
  import { SearchIcon } from "../icons/SearchIcon.js";
4
- import { getSlotProp, hasUserBorder } from "../../utils/index.js";
4
+ import { hasUserBorder } from "../../utils/classes.js";
5
+ import { getSlotProp } from "../../utils/slots.js";
5
6
  import { Button } from "../Button/Button.js";
6
7
  import { useSlots } from "../../hooks/useSlots.js";
7
8
  import TextInput_default from "../TextInput/index.js";
@@ -19,8 +19,8 @@ export interface SelectProps extends Omit<SelectHTMLAttributes<HTMLSelectElement
19
19
  fit?: boolean;
20
20
  /** Specify the component id. Will be auto-generated if not set. */
21
21
  id?: string;
22
- /** Specify the Select name. */
23
- name: string;
22
+ /** Specify the Select name. Without a name, the value is not submitted with a form. */
23
+ name?: string;
24
24
  /** Specify a custom React ref for this component. */
25
25
  ref?: Ref<HTMLSelectElement>;
26
26
  /** Specify the component size. */
@@ -39,7 +39,7 @@ interface SelectOptionProps extends OptionHTMLAttributes<HTMLOptionElement> {
39
39
  className?: string;
40
40
  }
41
41
  declare const Select: {
42
- ({ children, className, disabled, error, fit, id, name, ref, size, ...props }: SelectProps): JSX.Element;
42
+ ({ "aria-describedby": ariaDescribedBy, children, className, disabled, error, fit, id, name, ref, size, ...props }: SelectProps): JSX.Element;
43
43
  slot: {
44
44
  field: boolean;
45
45
  name: string;
@@ -1,13 +1,15 @@
1
1
  "use client";
2
- import { useField } from "../Field/Field.context.js";
2
+ import { getDescribedBy, getFieldError, useField } from "../Field/Field.context.js";
3
3
  import clsx from "clsx";
4
4
  import { jsx } from "react/jsx-runtime";
5
5
  //#region src/components/Select/Select.tsx
6
- const Select = ({ children, className, disabled, error, fit = false, id, name, ref, size, ...props }) => {
6
+ const Select = ({ "aria-describedby": ariaDescribedBy, children, className, disabled, error, fit = false, id, name, ref, size, ...props }) => {
7
7
  const field = useField();
8
+ const hasError = getFieldError(error, field);
8
9
  const componentProps = {
10
+ "aria-describedby": getDescribedBy(ariaDescribedBy, field),
11
+ "aria-invalid": hasError || void 0,
9
12
  className: clsx("coop-select", className),
10
- "data-error": error ?? void 0,
11
13
  "data-fit": fit || void 0,
12
14
  "data-size": size ?? void 0,
13
15
  disabled,
@@ -1,4 +1,4 @@
1
- import { hasUserBg } from "../../utils/index.js";
1
+ import { hasUserBg } from "../../utils/classes.js";
2
2
  import clsx from "clsx";
3
3
  import React from "react";
4
4
  //#region src/components/Tag/Tag.tsx
@@ -15,8 +15,8 @@ export interface TextInputProps extends Omit<InputHTMLAttributes<HTMLInputElemen
15
15
  fit?: boolean;
16
16
  /** Specify the component id. Will be auto-generated if not set. */
17
17
  id?: string;
18
- /** Specify the component name. */
19
- name: string;
18
+ /** Specify the component name. Without a name, the value is not submitted with a form. */
19
+ name?: string;
20
20
  /** Specify placeholder text. Do not use in place of a form label. */
21
21
  placeholder?: string;
22
22
  /** Specify the prefix. */
@@ -35,7 +35,7 @@ export interface TextInputProps extends Omit<InputHTMLAttributes<HTMLInputElemen
35
35
  type?: "text" | "email" | "number" | "password" | "search" | "tel" | "url" | "date" | "datetime-local" | "week" | "month" | "time";
36
36
  }
37
37
  declare const TextInput: {
38
- ({ className, disabled, error, fit, id, name, placeholder, prefix, prefixInline, ref, size, suffix, suffixInline, type, ...props }: TextInputProps): JSX.Element;
38
+ ({ "aria-describedby": ariaDescribedBy, className, disabled, error, fit, id, name, placeholder, prefix, prefixInline, ref, size, suffix, suffixInline, type, ...props }: TextInputProps): JSX.Element;
39
39
  slot: {
40
40
  field: boolean;
41
41
  name: string;
@@ -1,13 +1,15 @@
1
1
  "use client";
2
- import { useField } from "../Field/Field.context.js";
2
+ import { getDescribedBy, getFieldError, useField } from "../Field/Field.context.js";
3
3
  import clsx from "clsx";
4
4
  import { jsx, jsxs } from "react/jsx-runtime";
5
5
  //#region src/components/TextInput/TextInput.tsx
6
- const TextInput = ({ className, disabled, error = false, fit = false, id, name, placeholder, prefix, prefixInline = false, ref, size, suffix, suffixInline = false, type = "text", ...props }) => {
6
+ const TextInput = ({ "aria-describedby": ariaDescribedBy, className, disabled, error, fit = false, id, name, placeholder, prefix, prefixInline = false, ref, size, suffix, suffixInline = false, type = "text", ...props }) => {
7
7
  const field = useField();
8
+ const hasError = getFieldError(error, field);
8
9
  const componentProps = {
10
+ "aria-describedby": getDescribedBy(ariaDescribedBy, field),
11
+ "aria-invalid": hasError || void 0,
9
12
  className: clsx("coop-text-input", className),
10
- "data-error": error || void 0,
11
13
  "data-fit": fit || void 0,
12
14
  "data-size": size ?? void 0,
13
15
  disabled,
@@ -27,6 +29,8 @@ const TextInput = ({ className, disabled, error = false, fit = false, id, name,
27
29
  } : {};
28
30
  return prefix || suffix ? /* @__PURE__ */ jsxs("div", {
29
31
  className: "coop-text-input-wrapper",
32
+ "data-field": "",
33
+ "data-size": size,
30
34
  children: [
31
35
  prefix && /* @__PURE__ */ jsx("span", {
32
36
  ...prefixProps,
@@ -27,8 +27,8 @@ export interface TextareaProps extends TextareaHTMLAttributes<HTMLTextAreaElemen
27
27
  fit?: boolean;
28
28
  /** Specify the component id. Will be auto-generated if not set. */
29
29
  id?: string;
30
- /** Specify the component name. */
31
- name: string;
30
+ /** Specify the component name. Without a name, the value is not submitted with a form. */
31
+ name?: string;
32
32
  /** Specify placeholder text. Do not use in place of a form label. */
33
33
  placeholder?: string;
34
34
  /** Specify a custom React ref for this component. */
@@ -42,7 +42,7 @@ export interface TextareaProps extends TextareaHTMLAttributes<HTMLTextAreaElemen
42
42
  size?: StandardSizes;
43
43
  }
44
44
  declare const Textarea: {
45
- ({ className, cols, counter, cutoff, disabled, error, fit, id, maxLength, name, onChange: userOnChange, placeholder, ref, rows, size, ...props }: TextareaProps): JSX.Element;
45
+ ({ "aria-describedby": ariaDescribedBy, className, cols, counter, cutoff, disabled, error, fit, id, maxLength, name, onChange: userOnChange, placeholder, ref, rows, size, ...props }: TextareaProps): JSX.Element;
46
46
  slot: {
47
47
  field: boolean;
48
48
  name: string;
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import { useField } from "../Field/Field.context.js";
2
+ import { getDescribedBy, getFieldError, useField } from "../Field/Field.context.js";
3
3
  import { useDebounce } from "../../hooks/useDebounce.js";
4
4
  import clsx from "clsx";
5
5
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
@@ -9,12 +9,14 @@ const DEBOUNCE_DELAY = 750;
9
9
  const charCountMessage = (remaining) => {
10
10
  return `You have ${Math.abs(remaining).toLocaleString()} ${Math.abs(remaining) === 1 ? "character" : "characters"} ${remaining < 0 ? "too many" : "remaining"}`;
11
11
  };
12
- const Textarea = ({ className, cols = 30, counter = false, cutoff = false, disabled = false, error = false, fit = false, id, maxLength, name, onChange: userOnChange, placeholder, ref, rows = 4, size, ...props }) => {
12
+ const Textarea = ({ "aria-describedby": ariaDescribedBy, className, cols = 30, counter = false, cutoff = false, disabled = false, error, fit = false, id, maxLength, name, onChange: userOnChange, placeholder, ref, rows = 4, size, ...props }) => {
13
13
  const field = useField();
14
+ const hasError = getFieldError(error, field);
14
15
  const componentProps = {
16
+ "aria-describedby": getDescribedBy(ariaDescribedBy, field),
17
+ "aria-invalid": hasError || void 0,
15
18
  className: clsx("coop-textarea", className),
16
19
  cols,
17
- "data-error": error ?? void 0,
18
20
  "data-fit": fit || void 0,
19
21
  "data-size": size ?? void 0,
20
22
  disabled,