@coopdigital/react 0.46.0 → 0.48.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 (198) hide show
  1. package/dist/components/AlertBanner/AlertBanner.d.ts +16 -4
  2. package/dist/components/AlertBanner/AlertBanner.js +8 -3
  3. package/dist/components/Author/Author.d.ts +4 -2
  4. package/dist/components/Author/Author.js +2 -2
  5. package/dist/components/Button/Button.d.ts +1 -1
  6. package/dist/components/Button/Button.js +1 -2
  7. package/dist/components/Card/Card.d.ts +4 -2
  8. package/dist/components/Card/Card.js +2 -2
  9. package/dist/components/Checkbox/Checkbox.d.ts +4 -2
  10. package/dist/components/Checkbox/Checkbox.js +3 -3
  11. package/dist/components/Expandable/Expandable.d.ts +4 -2
  12. package/dist/components/Expandable/Expandable.js +2 -2
  13. package/dist/components/Field/Field.d.ts +4 -2
  14. package/dist/components/Field/Field.js +4 -4
  15. package/dist/components/FieldMarkers/Error.d.ts +4 -2
  16. package/dist/components/FieldMarkers/Error.js +2 -2
  17. package/dist/components/FieldMarkers/Hint.d.ts +5 -3
  18. package/dist/components/FieldMarkers/Hint.js +2 -2
  19. package/dist/components/FieldMarkers/Label.d.ts +4 -2
  20. package/dist/components/FieldMarkers/Label.js +6 -2
  21. package/dist/components/FieldMarkers/Legend.d.ts +4 -2
  22. package/dist/components/FieldMarkers/Legend.js +2 -2
  23. package/dist/components/Fieldset/Fieldset.d.ts +4 -2
  24. package/dist/components/Fieldset/Fieldset.js +2 -4
  25. package/dist/components/Flourish/Flourish.d.ts +4 -2
  26. package/dist/components/Flourish/Flourish.js +2 -2
  27. package/dist/components/Icon/AddIcon.d.ts +1 -1
  28. package/dist/components/Icon/AddIcon.js +6 -5
  29. package/dist/components/Icon/ArrowDownIcon.d.ts +1 -1
  30. package/dist/components/Icon/ArrowDownIcon.js +6 -5
  31. package/dist/components/Icon/ArrowLeftIcon.d.ts +1 -1
  32. package/dist/components/Icon/ArrowLeftIcon.js +6 -5
  33. package/dist/components/Icon/ArrowRightIcon.d.ts +1 -1
  34. package/dist/components/Icon/ArrowRightIcon.js +6 -5
  35. package/dist/components/Icon/ArrowUpIcon.d.ts +1 -1
  36. package/dist/components/Icon/ArrowUpIcon.js +6 -5
  37. package/dist/components/Icon/AvatarAltIcon.d.ts +1 -1
  38. package/dist/components/Icon/AvatarAltIcon.js +6 -5
  39. package/dist/components/Icon/AvatarIcon.d.ts +1 -1
  40. package/dist/components/Icon/AvatarIcon.js +6 -5
  41. package/dist/components/Icon/BasketIcon.d.ts +1 -1
  42. package/dist/components/Icon/BasketIcon.js +6 -5
  43. package/dist/components/Icon/CalendarIcon.d.ts +1 -1
  44. package/dist/components/Icon/CalendarIcon.js +6 -5
  45. package/dist/components/Icon/ChevronDownIcon.d.ts +1 -1
  46. package/dist/components/Icon/ChevronDownIcon.js +6 -5
  47. package/dist/components/Icon/ChevronLeftIcon.d.ts +1 -1
  48. package/dist/components/Icon/ChevronLeftIcon.js +6 -5
  49. package/dist/components/Icon/ChevronRightIcon.d.ts +1 -1
  50. package/dist/components/Icon/ChevronRightIcon.js +6 -5
  51. package/dist/components/Icon/ChevronUpIcon.d.ts +1 -1
  52. package/dist/components/Icon/ChevronUpIcon.js +6 -5
  53. package/dist/components/Icon/ClockIcon.d.ts +1 -1
  54. package/dist/components/Icon/ClockIcon.js +6 -5
  55. package/dist/components/Icon/CloseAltIcon.d.ts +1 -1
  56. package/dist/components/Icon/CloseAltIcon.js +6 -5
  57. package/dist/components/Icon/CloseIcon.d.ts +1 -1
  58. package/dist/components/Icon/CloseIcon.js +6 -5
  59. package/dist/components/Icon/CoopCardIcon.d.ts +1 -1
  60. package/dist/components/Icon/CoopCardIcon.js +6 -5
  61. package/dist/components/Icon/CoopIcon.d.ts +1 -1
  62. package/dist/components/Icon/CoopIcon.js +6 -5
  63. package/dist/components/Icon/CoopLocationIcon.d.ts +1 -1
  64. package/dist/components/Icon/CoopLocationIcon.js +6 -5
  65. package/dist/components/Icon/DownloadIcon.d.ts +1 -1
  66. package/dist/components/Icon/DownloadIcon.js +6 -5
  67. package/dist/components/Icon/HomeIcon.d.ts +1 -1
  68. package/dist/components/Icon/HomeIcon.js +6 -5
  69. package/dist/components/Icon/InformationIcon.d.ts +1 -1
  70. package/dist/components/Icon/InformationIcon.js +6 -5
  71. package/dist/components/Icon/LoadingIcon.d.ts +1 -1
  72. package/dist/components/Icon/LoadingIcon.js +6 -5
  73. package/dist/components/Icon/LocationIcon.d.ts +1 -1
  74. package/dist/components/Icon/LocationIcon.js +6 -5
  75. package/dist/components/Icon/MailIcon.d.ts +1 -1
  76. package/dist/components/Icon/MailIcon.js +6 -5
  77. package/dist/components/Icon/MenuIcon.d.ts +1 -1
  78. package/dist/components/Icon/MenuIcon.js +6 -5
  79. package/dist/components/Icon/MessageIcon.d.ts +1 -1
  80. package/dist/components/Icon/MessageIcon.js +6 -5
  81. package/dist/components/Icon/MinusIcon.d.ts +1 -1
  82. package/dist/components/Icon/MinusIcon.js +6 -5
  83. package/dist/components/Icon/OpenNewIcon.d.ts +1 -1
  84. package/dist/components/Icon/OpenNewIcon.js +6 -5
  85. package/dist/components/Icon/PencilIcon.d.ts +1 -1
  86. package/dist/components/Icon/PencilIcon.js +6 -5
  87. package/dist/components/Icon/PhoneIcon.d.ts +1 -1
  88. package/dist/components/Icon/PhoneIcon.js +6 -5
  89. package/dist/components/Icon/QuestionIcon.d.ts +1 -1
  90. package/dist/components/Icon/QuestionIcon.js +6 -5
  91. package/dist/components/Icon/ScooterIcon.d.ts +1 -1
  92. package/dist/components/Icon/ScooterIcon.js +6 -5
  93. package/dist/components/Icon/SearchIcon.d.ts +1 -1
  94. package/dist/components/Icon/SearchIcon.js +6 -5
  95. package/dist/components/Icon/SettingsIcon.d.ts +1 -1
  96. package/dist/components/Icon/SettingsIcon.js +6 -5
  97. package/dist/components/Icon/TickAltIcon.d.ts +1 -1
  98. package/dist/components/Icon/TickAltIcon.js +6 -5
  99. package/dist/components/Icon/TickIcon.d.ts +1 -1
  100. package/dist/components/Icon/TickIcon.js +6 -5
  101. package/dist/components/Icon/VanIcon.d.ts +1 -1
  102. package/dist/components/Icon/VanIcon.js +6 -5
  103. package/dist/components/Icon/WarningIcon.d.ts +1 -1
  104. package/dist/components/Icon/WarningIcon.js +6 -5
  105. package/dist/components/Icon/WriteIcon.d.ts +1 -1
  106. package/dist/components/Icon/WriteIcon.js +6 -5
  107. package/dist/components/Image/Image.d.ts +4 -2
  108. package/dist/components/Image/Image.js +2 -2
  109. package/dist/components/Pill/Pill.d.ts +4 -2
  110. package/dist/components/Pill/Pill.js +5 -6
  111. package/dist/components/Radio/Radio.d.ts +5 -3
  112. package/dist/components/Radio/Radio.js +6 -7
  113. package/dist/components/Searchbox/Searchbox.d.ts +4 -2
  114. package/dist/components/Searchbox/Searchbox.js +14 -9
  115. package/dist/components/Select/Select.d.ts +46 -0
  116. package/dist/components/Select/Select.js +29 -0
  117. package/dist/components/Select/index.d.ts +4 -0
  118. package/dist/components/Signpost/Signpost.d.ts +6 -4
  119. package/dist/components/Signpost/Signpost.js +3 -6
  120. package/dist/components/SkipNav/SkipNav.d.ts +4 -2
  121. package/dist/components/SkipNav/SkipNav.js +2 -2
  122. package/dist/components/Squircle/Squircle.d.ts +4 -2
  123. package/dist/components/Squircle/Squircle.js +2 -2
  124. package/dist/components/Tag/Tag.d.ts +5 -3
  125. package/dist/components/Tag/Tag.js +3 -6
  126. package/dist/components/TextInput/TextInput.d.ts +4 -2
  127. package/dist/components/TextInput/TextInput.js +3 -3
  128. package/dist/components/Textarea/Textarea.d.ts +4 -2
  129. package/dist/components/Textarea/Textarea.js +5 -6
  130. package/dist/index.d.ts +1 -0
  131. package/dist/index.js +1 -0
  132. package/package.json +10 -10
  133. package/src/components/AlertBanner/AlertBanner.tsx +22 -9
  134. package/src/components/Author/Author.tsx +5 -2
  135. package/src/components/Button/Button.tsx +2 -3
  136. package/src/components/Card/Card.tsx +5 -2
  137. package/src/components/Checkbox/Checkbox.tsx +11 -5
  138. package/src/components/Expandable/Expandable.tsx +10 -3
  139. package/src/components/Field/Field.tsx +7 -5
  140. package/src/components/FieldMarkers/Error.tsx +9 -3
  141. package/src/components/FieldMarkers/Hint.tsx +10 -4
  142. package/src/components/FieldMarkers/Label.tsx +9 -3
  143. package/src/components/FieldMarkers/Legend.tsx +9 -3
  144. package/src/components/Fieldset/Fieldset.tsx +9 -4
  145. package/src/components/Flourish/Flourish.tsx +5 -2
  146. package/src/components/Icon/AddIcon.tsx +9 -6
  147. package/src/components/Icon/ArrowDownIcon.tsx +9 -6
  148. package/src/components/Icon/ArrowLeftIcon.tsx +9 -6
  149. package/src/components/Icon/ArrowRightIcon.tsx +9 -6
  150. package/src/components/Icon/ArrowUpIcon.tsx +9 -6
  151. package/src/components/Icon/AvatarAltIcon.tsx +9 -6
  152. package/src/components/Icon/AvatarIcon.tsx +9 -6
  153. package/src/components/Icon/BasketIcon.tsx +9 -6
  154. package/src/components/Icon/CalendarIcon.tsx +9 -6
  155. package/src/components/Icon/ChevronDownIcon.tsx +9 -6
  156. package/src/components/Icon/ChevronLeftIcon.tsx +9 -6
  157. package/src/components/Icon/ChevronRightIcon.tsx +9 -6
  158. package/src/components/Icon/ChevronUpIcon.tsx +9 -6
  159. package/src/components/Icon/ClockIcon.tsx +9 -6
  160. package/src/components/Icon/CloseAltIcon.tsx +9 -6
  161. package/src/components/Icon/CloseIcon.tsx +9 -6
  162. package/src/components/Icon/CoopCardIcon.tsx +9 -6
  163. package/src/components/Icon/CoopIcon.tsx +9 -6
  164. package/src/components/Icon/CoopLocationIcon.tsx +9 -6
  165. package/src/components/Icon/DownloadIcon.tsx +9 -6
  166. package/src/components/Icon/HomeIcon.tsx +9 -6
  167. package/src/components/Icon/InformationIcon.tsx +9 -6
  168. package/src/components/Icon/LoadingIcon.tsx +9 -6
  169. package/src/components/Icon/LocationIcon.tsx +9 -6
  170. package/src/components/Icon/MailIcon.tsx +9 -6
  171. package/src/components/Icon/MenuIcon.tsx +9 -6
  172. package/src/components/Icon/MessageIcon.tsx +9 -6
  173. package/src/components/Icon/MinusIcon.tsx +9 -6
  174. package/src/components/Icon/OpenNewIcon.tsx +9 -6
  175. package/src/components/Icon/PencilIcon.tsx +9 -6
  176. package/src/components/Icon/PhoneIcon.tsx +9 -6
  177. package/src/components/Icon/QuestionIcon.tsx +9 -6
  178. package/src/components/Icon/ScooterIcon.tsx +9 -6
  179. package/src/components/Icon/SearchIcon.tsx +9 -6
  180. package/src/components/Icon/SettingsIcon.tsx +9 -6
  181. package/src/components/Icon/TickAltIcon.tsx +9 -6
  182. package/src/components/Icon/TickIcon.tsx +9 -6
  183. package/src/components/Icon/VanIcon.tsx +9 -6
  184. package/src/components/Icon/WarningIcon.tsx +9 -6
  185. package/src/components/Icon/WriteIcon.tsx +9 -6
  186. package/src/components/Image/Image.tsx +5 -2
  187. package/src/components/Pill/Pill.tsx +8 -7
  188. package/src/components/Radio/Radio.tsx +11 -8
  189. package/src/components/Searchbox/Searchbox.tsx +26 -21
  190. package/src/components/Select/Select.tsx +97 -0
  191. package/src/components/Select/index.ts +5 -0
  192. package/src/components/Signpost/Signpost.tsx +8 -8
  193. package/src/components/SkipNav/SkipNav.tsx +5 -2
  194. package/src/components/Squircle/Squircle.tsx +5 -2
  195. package/src/components/Tag/Tag.tsx +9 -8
  196. package/src/components/TextInput/TextInput.tsx +10 -5
  197. package/src/components/Textarea/Textarea.tsx +9 -7
  198. package/src/index.ts +1 -0
@@ -2,18 +2,15 @@ import clsx from 'clsx';
2
2
  import React from 'react';
3
3
  import { hasUserBg } from '../../utils/index.js';
4
4
 
5
- const Tag = ({ as, children, className, href, size = "md", ...props }) => {
6
- let element = href ? "a" : "span";
5
+ const Tag = ({ as, children, className, href, ref, size = "md", ...props }) => {
6
+ const element = as !== null && as !== void 0 ? as : (href ? "a" : "span");
7
7
  const componentProps = {
8
8
  className: clsx("coop-tag", !hasUserBg(className) && "bg-tint-grey", className),
9
9
  "data-size": size.length && size !== "md" ? size : undefined,
10
10
  href,
11
11
  ...props,
12
12
  };
13
- if (as) {
14
- element = as;
15
- }
16
- return React.createElement(element, { ...componentProps }, children);
13
+ return React.createElement(element, { ...componentProps, ref }, children);
17
14
  };
18
15
 
19
16
  export { Tag, Tag as default };
@@ -1,4 +1,4 @@
1
- import type { InputHTMLAttributes, JSX } from "react";
1
+ import type { InputHTMLAttributes, JSX, Ref } from "react";
2
2
  import { StandardSizes } from "../../../src/types";
3
3
  export interface TextInputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, "prefix" | "size" | "type"> {
4
4
  /** **(Optional)** Specify additional CSS classes to be applied to the component. */
@@ -15,6 +15,8 @@ export interface TextInputProps extends Omit<InputHTMLAttributes<HTMLInputElemen
15
15
  placeholder?: string;
16
16
  /** **(Optional)** Specify the prefix. It can be any valid JSX or string. */
17
17
  prefix?: React.ReactNode;
18
+ /** **(Optional)** Specify a custom React ref for this component. */
19
+ ref?: Ref<HTMLInputElement>;
18
20
  /** **(Optional)** Specify the TextInput size. */
19
21
  size?: StandardSizes;
20
22
  /** **(Optional)** Specify the suffix. It can be any valid JSX or string. */
@@ -22,5 +24,5 @@ export interface TextInputProps extends Omit<InputHTMLAttributes<HTMLInputElemen
22
24
  /** **(Optional)** Specify the TextInput type. */
23
25
  type?: "text" | "email" | "number" | "password" | "search" | "tel" | "url" | "date" | "datetime-local" | "week" | "month" | "time";
24
26
  }
25
- export declare const TextInput: ({ "aria-placeholder": ariaPlaceholder, className, disabled, error, id, name, placeholder, prefix, size, suffix, type, ...props }: TextInputProps) => JSX.Element;
27
+ export declare const TextInput: ({ "aria-placeholder": ariaPlaceholder, className, disabled, error, id, name, placeholder, prefix, ref, size, suffix, type, ...props }: TextInputProps) => JSX.Element;
26
28
  export default TextInput;
@@ -2,12 +2,12 @@ import { jsxs, jsx } from 'react/jsx-runtime';
2
2
  import clsx from 'clsx';
3
3
  import { useId } from '../../hooks/useId.js';
4
4
 
5
- const TextInput = ({ "aria-placeholder": ariaPlaceholder, className, disabled, error = false, id, name, placeholder, prefix, size = "md", suffix, type = "text", ...props }) => {
5
+ const TextInput = ({ "aria-placeholder": ariaPlaceholder, className, disabled, error = false, id, name, placeholder, prefix, ref, size = "md", suffix, type = "text", ...props }) => {
6
6
  var _a;
7
7
  const uid = useId(id);
8
8
  const componentProps = {
9
9
  "aria-placeholder": (_a = placeholder !== null && placeholder !== void 0 ? placeholder : ariaPlaceholder) !== null && _a !== void 0 ? _a : undefined,
10
- className: clsx("coop-text-input", className),
10
+ className: clsx("coop-text-input", "coop-field-element", className),
11
11
  "data-error": error || undefined,
12
12
  "data-size": size.length && size !== "md" ? size : undefined,
13
13
  disabled,
@@ -18,7 +18,7 @@ const TextInput = ({ "aria-placeholder": ariaPlaceholder, className, disabled, e
18
18
  ...props,
19
19
  };
20
20
  //const formItemProps = { "aria-disabled": disabled ? true : undefined }
21
- return (jsxs("div", { className: "coop-text-input-wrapper", children: [prefix && jsx("span", { className: "coop-text-input--prefix", children: prefix }), jsx("input", { ...componentProps }), suffix && jsx("span", { className: "coop-text-input--suffix", children: suffix })] }));
21
+ return (jsxs("div", { className: "coop-text-input-wrapper", children: [prefix && jsx("span", { className: "coop-text-input--prefix", children: prefix }), jsx("input", { ...componentProps, ref: ref }), suffix && jsx("span", { className: "coop-text-input--suffix", children: suffix })] }));
22
22
  };
23
23
 
24
24
  export { TextInput, TextInput as default };
@@ -1,4 +1,4 @@
1
- import type { JSX, TextareaHTMLAttributes } from "react";
1
+ import type { JSX, Ref, TextareaHTMLAttributes } from "react";
2
2
  import { StandardSizes } from "../../types";
3
3
  export interface TextareaProps extends TextareaHTMLAttributes<HTMLTextAreaElement> {
4
4
  /** **(Optional)** Specify additional CSS classes to be applied to the component. */
@@ -26,10 +26,12 @@ export interface TextareaProps extends TextareaHTMLAttributes<HTMLTextAreaElemen
26
26
  name: string;
27
27
  /** **(Optional)** Specify the Textarea placeholder text. Do not use in place of a form label. */
28
28
  placeholder?: string;
29
+ /** **(Optional)** Specify a custom React ref for this component. */
30
+ ref?: Ref<HTMLTextAreaElement>;
29
31
  /** Specify the number of rows (lines of text) in the Textarea. Defaults to `4`. */
30
32
  rows?: number;
31
33
  /** **(Optional)** Specify the Textarea size. */
32
34
  size?: StandardSizes;
33
35
  }
34
- export declare const Textarea: ({ "aria-placeholder": ariaPlaceholder, className, cols, counter, cutoff, disabled, error, id, maxLength, name, onChange: userOnChange, placeholder, rows, size, ...props }: TextareaProps) => JSX.Element;
36
+ export declare const Textarea: ({ "aria-placeholder": ariaPlaceholder, className, cols, counter, cutoff, disabled, error, id, maxLength, name, onChange: userOnChange, placeholder, ref, rows, size, ...props }: TextareaProps) => JSX.Element;
35
37
  export default Textarea;
@@ -8,18 +8,17 @@ const DEBOUNCE_DELAY = 750;
8
8
  const charCountMessage = (remaining) => {
9
9
  return `You have ${Math.abs(remaining).toLocaleString()} ${Math.abs(remaining) === 1 ? "character" : "characters"} ${remaining < 0 ? "too many" : "remaining"}`;
10
10
  };
11
- const Textarea = ({ "aria-placeholder": ariaPlaceholder, className, cols = 30, counter = false, cutoff = false, disabled = false, error = false, id, maxLength, name, onChange: userOnChange = undefined, placeholder, rows = 4, size = "md", ...props }) => {
11
+ const Textarea = ({ "aria-placeholder": ariaPlaceholder, className, cols = 30, counter = false, cutoff = false, disabled = false, error = false, id, maxLength, name, onChange: userOnChange = undefined, placeholder, ref, rows = 4, size = "md", ...props }) => {
12
12
  var _a;
13
- const internalId = useId();
14
- id = id !== null && id !== void 0 ? id : internalId;
13
+ const uid = useId(id);
15
14
  const componentProps = {
16
15
  "aria-placeholder": (_a = placeholder !== null && placeholder !== void 0 ? placeholder : ariaPlaceholder) !== null && _a !== void 0 ? _a : undefined,
17
- className: clsx("coop-textarea", className),
16
+ className: clsx("coop-textarea", "coop-field-element", className),
18
17
  cols,
19
18
  "data-error": error !== null && error !== void 0 ? error : undefined,
20
19
  "data-size": size.length && size !== "md" ? size : undefined,
21
20
  disabled,
22
- id,
21
+ id: uid,
23
22
  maxLength: cutoff ? maxLength : undefined,
24
23
  name,
25
24
  placeholder,
@@ -36,7 +35,7 @@ const Textarea = ({ "aria-placeholder": ariaPlaceholder, className, cols = 30, c
36
35
  return (jsxs("div", { className: "coop-textarea-wrapper", children: [jsx("textarea", { ...componentProps, onChange: (e) => {
37
36
  userOnChange === null || userOnChange === void 0 ? void 0 : userOnChange(e);
38
37
  handleChange(e);
39
- } }), showCounter && (jsxs(Fragment, { children: [jsx("small", { "aria-hidden": "true", className: "coop-textarea-counter", ...(remaining < 0 && { "data-error": true }), children: charCountMessage(remaining) }), jsx("span", { "aria-live": "polite", className: "sr-only", children: charCountMessage(debouncedRemaining) })] }))] }));
38
+ }, ref: ref }), showCounter && (jsxs(Fragment, { children: [jsx("small", { "aria-hidden": "true", className: "coop-textarea-counter", ...(remaining < 0 && { "data-error": true }), children: charCountMessage(remaining) }), jsx("span", { "aria-live": "polite", className: "sr-only", children: charCountMessage(debouncedRemaining) })] }))] }));
40
39
  };
41
40
 
42
41
  export { Textarea, Textarea as default };
package/dist/index.d.ts CHANGED
@@ -12,6 +12,7 @@ export * from "./components/Pill";
12
12
  export * from "./components/Radio";
13
13
  export * from "./components/RootSVG";
14
14
  export * from "./components/Searchbox";
15
+ export * from "./components/Select";
15
16
  export * from "./components/Signpost";
16
17
  export * from "./components/SkipNav";
17
18
  export * from "./components/Squircle";
package/dist/index.js CHANGED
@@ -12,6 +12,7 @@ export { Pill } from './components/Pill/Pill.js';
12
12
  export { Radio } from './components/Radio/Radio.js';
13
13
  export { RootSVG } from './components/RootSVG/RootSVG.js';
14
14
  export { Searchbox } from './components/Searchbox/Searchbox.js';
15
+ export { Select } from './components/Select/Select.js';
15
16
  export { Signpost } from './components/Signpost/Signpost.js';
16
17
  export { SkipNav } from './components/SkipNav/SkipNav.js';
17
18
  export { Squircle } from './components/Squircle/Squircle.js';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@coopdigital/react",
3
3
  "type": "module",
4
- "version": "0.46.0",
4
+ "version": "0.48.0",
5
5
  "private": false,
6
6
  "publishConfig": {
7
7
  "access": "public"
@@ -56,19 +56,19 @@
56
56
  "description": "",
57
57
  "devDependencies": {
58
58
  "@axe-core/playwright": "^4.11.0",
59
- "@playwright/test": "^1.56.1",
60
- "@storybook/addon-a11y": "^10.0.7",
61
- "@storybook/addon-docs": "^10.0.7",
62
- "@storybook/addon-onboarding": "^10.0.7",
63
- "@storybook/react-vite": "^10.0.7",
59
+ "@playwright/test": "^1.57.0",
60
+ "@storybook/addon-a11y": "^10.1.0",
61
+ "@storybook/addon-docs": "^10.1.0",
62
+ "@storybook/addon-onboarding": "^10.1.0",
63
+ "@storybook/react-vite": "^10.1.0",
64
64
  "@testing-library/jest-dom": "^6.9.1",
65
65
  "@testing-library/react": "^16.3.0",
66
- "@types/react": "^19.2.4",
66
+ "@types/react": "^19.2.7",
67
67
  "@types/react-dom": "^19.2.3",
68
68
  "react": "^19.2.0",
69
69
  "react-dom": "^19.2.0",
70
70
  "serve": "^14.2.5",
71
- "storybook": "^10.0.7"
71
+ "storybook": "^10.1.0"
72
72
  },
73
73
  "peerDependencies": {
74
74
  "react": "^19.1.0",
@@ -78,8 +78,8 @@
78
78
  "storybook": "$storybook"
79
79
  },
80
80
  "dependencies": {
81
- "@coopdigital/styles": "^0.40.0",
81
+ "@coopdigital/styles": "^0.41.0",
82
82
  "clsx": "^2.1.1"
83
83
  },
84
- "gitHead": "9020e79195ead8270e9a2c39209142a02160bae5"
84
+ "gitHead": "96d3459231c76b94984900894ac0ae2bac2f091b"
85
85
  }
@@ -1,4 +1,4 @@
1
- import type { HTMLAttributes, JSX, ReactNode } from "react"
1
+ import type { HTMLAttributes, JSX, ReactNode, Ref } from "react"
2
2
 
3
3
  import clsx from "clsx"
4
4
 
@@ -7,16 +7,23 @@ export interface AlertBannerProps extends HTMLAttributes<HTMLDivElement> {
7
7
  children?: string | ReactNode
8
8
  /** **(Optional)** Specify additional CSS classes to be applied to the component. */
9
9
  className?: string
10
- /** Specify the main title of the banner, rendered as a `h2`. */
11
- heading: string
10
+ /** **(Optional)** Specify a custom React ref for this component. */
11
+ ref?: Ref<HTMLDivElement>
12
12
  /** **(Optional)** Specify the AlertBanner variant. */
13
13
  variant?: "black" | "default"
14
14
  }
15
15
 
16
+ interface AlertBannerHeadingProps extends HTMLAttributes<HTMLHeadingElement> {
17
+ /** **(Optional)** Content inside the Heading. */
18
+ children?: React.ReactNode
19
+ /** **(Optional)** Specify additional CSS classes to be applied to the Heading. */
20
+ className?: string
21
+ }
22
+
16
23
  export const AlertBanner = ({
17
24
  children,
18
25
  className,
19
- heading,
26
+ ref,
20
27
  variant = "default",
21
28
  ...props
22
29
  }: AlertBannerProps): JSX.Element => {
@@ -25,14 +32,20 @@ export const AlertBanner = ({
25
32
  "data-variant": variant,
26
33
  ...props,
27
34
  }
35
+
28
36
  return (
29
- <aside {...componentProps}>
30
- <div className="coop-alert-banner--inner">
31
- <h2 id="coop-alert-banner--headline">{heading}</h2>
32
- {children}
33
- </div>
37
+ <aside {...componentProps} ref={ref}>
38
+ {children}
34
39
  </aside>
35
40
  )
36
41
  }
37
42
 
43
+ const AlertBannerHeading = ({ children, className }: AlertBannerHeadingProps): JSX.Element => {
44
+ return <h2 className={className}>{children}</h2>
45
+ }
46
+
47
+ AlertBannerHeading.displayName = "AlertBanner.Heading"
48
+
49
+ AlertBanner.Heading = AlertBannerHeading
50
+
38
51
  export default AlertBanner
@@ -1,4 +1,4 @@
1
- import type { HTMLAttributes, JSX } from "react"
1
+ import type { HTMLAttributes, JSX, Ref } from "react"
2
2
 
3
3
  import clsx from "clsx"
4
4
 
@@ -15,6 +15,8 @@ export interface AuthorProps extends HTMLAttributes<HTMLDivElement> {
15
15
  datePrefix?: string
16
16
  /** **(Optional)** Specify properties of the Author Image. */
17
17
  image?: ImageProps
18
+ /** **(Optional)** Specify a custom React ref for this component. */
19
+ ref?: Ref<HTMLDivElement>
18
20
  }
19
21
 
20
22
  export const Author = ({
@@ -26,6 +28,7 @@ export const Author = ({
26
28
  alt: "",
27
29
  src: "https://s3.eu-west-1.amazonaws.com/assets.digital.coop.co.uk/oneweb/coop-logo-inverted.svg",
28
30
  },
31
+ ref,
29
32
  ...props
30
33
  }: AuthorProps): JSX.Element => {
31
34
  const imageProps: ImageProps = {
@@ -38,7 +41,7 @@ export const Author = ({
38
41
  ...props,
39
42
  }
40
43
  return (
41
- <div {...componentProps}>
44
+ <div {...componentProps} ref={ref}>
42
45
  {image && <Image {...imageProps} />}
43
46
  <div className="coop-author--content">
44
47
  <span>
@@ -28,7 +28,7 @@ export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
28
28
  /** **(Optional)** Callback to run when the button is pressed. If this is an async function, it will be awaited and the button will be in a pending state until the promise is resolved. */
29
29
  onClick?: React.MouseEventHandler<HTMLButtonElement | HTMLAnchorElement>
30
30
  /** **(Optional)** Specify a custom React ref for this component. */
31
- ref?: Ref<HTMLButtonElement | HTMLAnchorElement>
31
+ ref?: Ref<HTMLElement>
32
32
  /** **(Optional)** Specify the Button size. */
33
33
  size?: "sm" | "md" | "lg"
34
34
  /** **(Optional)** Specify the Button variant. */
@@ -92,7 +92,6 @@ export const Button = ({
92
92
  "data-width": isFullWidth ? "full" : undefined,
93
93
  href,
94
94
  onClick: onClick ? handleClick : undefined,
95
- ref,
96
95
  ...props,
97
96
  }
98
97
 
@@ -106,6 +105,6 @@ export const Button = ({
106
105
  children
107
106
  )
108
107
 
109
- return React.createElement(element, { ...componentProps }, finalChildren)
108
+ return React.createElement(element, { ...componentProps, ref }, finalChildren)
110
109
  }
111
110
  export default Button
@@ -1,4 +1,4 @@
1
- import type { ForwardRefExoticComponent, HTMLAttributes, JSX } from "react"
1
+ import type { ForwardRefExoticComponent, HTMLAttributes, JSX, Ref } from "react"
2
2
 
3
3
  import clsx from "clsx"
4
4
  import React from "react"
@@ -24,6 +24,8 @@ export interface CardProps extends HTMLAttributes<HTMLDivElement> {
24
24
  imagePosition?: "left" | "right"
25
25
  /** **(Optional)** Specify the orientation of the Card. */
26
26
  orientation?: "vertical" | "horizontal"
27
+ /** **(Optional)** Specify a custom React ref for this component. */
28
+ ref?: Ref<HTMLDivElement>
27
29
  }
28
30
 
29
31
  interface CardLabelProps extends HTMLAttributes<HTMLSpanElement> {
@@ -87,6 +89,7 @@ export const Card = ({
87
89
  hrefAs,
88
90
  imagePosition = "left",
89
91
  orientation = "vertical",
92
+ ref,
90
93
  ...props
91
94
  }: CardProps): JSX.Element => {
92
95
  const { element: linkElement, props: linkProps } = getCardLinkElement(hrefAs, href)
@@ -107,7 +110,7 @@ export const Card = ({
107
110
  }
108
111
 
109
112
  return (
110
- <article {...componentProps}>
113
+ <article {...componentProps} ref={ref}>
111
114
  {slots.CardImage}
112
115
  {slots.CardBadge}
113
116
  {React.createElement(
@@ -1,12 +1,14 @@
1
- import type { InputHTMLAttributes, JSX } from "react"
1
+ import type { InputHTMLAttributes, JSX, Ref } from "react"
2
2
 
3
3
  import clsx from "clsx"
4
4
 
5
5
  import { StandardSizes } from "../../../src/types"
6
6
  import { useId } from "../../hooks/useId"
7
7
 
8
- export interface CheckboxProps
9
- extends Omit<InputHTMLAttributes<HTMLInputElement>, "size" | "type"> {
8
+ export interface CheckboxProps extends Omit<
9
+ InputHTMLAttributes<HTMLInputElement>,
10
+ "size" | "type"
11
+ > {
10
12
  /** **(Optional)** Specify additional CSS classes to be applied to the component. */
11
13
  className?: string
12
14
  /** **(Optional)** Specify whether the Checkbox should be disabled. Refer to Experience Library guidance on disabled form controls and accessibility. */
@@ -17,6 +19,8 @@ export interface CheckboxProps
17
19
  id?: string
18
20
  /** Specify the Checkbox name. */
19
21
  name: string
22
+ /** **(Optional)** Specify a custom React ref for this component. */
23
+ ref?: Ref<HTMLInputElement>
20
24
  /** **(Optional)** Specify the Checkbox size. */
21
25
  size?: StandardSizes
22
26
  }
@@ -27,12 +31,13 @@ export const Checkbox = ({
27
31
  error = false,
28
32
  id,
29
33
  name,
34
+ ref,
30
35
  size = "md",
31
36
  ...props
32
37
  }: CheckboxProps): JSX.Element => {
33
38
  const uid = useId(id)
34
39
  const componentProps = {
35
- className: clsx("coop-checkbox", className),
40
+ className: clsx("coop-checkbox", "coop-field-element", className),
36
41
  "data-error": error || undefined,
37
42
  "data-size": size.length && size !== "md" ? size : undefined,
38
43
  disabled,
@@ -41,7 +46,8 @@ export const Checkbox = ({
41
46
  type: "checkbox",
42
47
  ...props,
43
48
  }
44
- return <input {...componentProps} />
49
+
50
+ return <input {...componentProps} ref={ref} />
45
51
  }
46
52
 
47
53
  export default Checkbox
@@ -1,4 +1,4 @@
1
- import type { DetailsHTMLAttributes, HTMLAttributes, JSX } from "react"
1
+ import type { DetailsHTMLAttributes, HTMLAttributes, JSX, Ref } from "react"
2
2
 
3
3
  import { clsx } from "clsx"
4
4
 
@@ -11,6 +11,8 @@ export interface ExpandableProps extends DetailsHTMLAttributes<HTMLDetailsElemen
11
11
  children: React.ReactNode
12
12
  /** **(Optional)** Specify additional CSS classes to be applied to the component. */
13
13
  className?: string
14
+ /** **(Optional)** Specify a custom React ref for this component. */
15
+ ref?: Ref<HTMLDetailsElement>
14
16
  }
15
17
 
16
18
  interface ExpandableSummaryProps extends HTMLAttributes<HTMLElement> {
@@ -25,7 +27,12 @@ const componentSlots = {
25
27
  ExpandableSummary: null,
26
28
  }
27
29
 
28
- export const Expandable = ({ children, className, ...props }: ExpandableProps): JSX.Element => {
30
+ export const Expandable = ({
31
+ children,
32
+ className,
33
+ ref,
34
+ ...props
35
+ }: ExpandableProps): JSX.Element => {
29
36
  const slots = useSlots(componentSlots, children)
30
37
 
31
38
  const componentProps = {
@@ -38,7 +45,7 @@ export const Expandable = ({ children, className, ...props }: ExpandableProps):
38
45
  }
39
46
 
40
47
  return (
41
- <details {...componentProps}>
48
+ <details {...componentProps} ref={ref}>
42
49
  {slots.ExpandableSummary}
43
50
  <div className="coop-expandable--content">{slots.Children}</div>
44
51
  </details>
@@ -1,4 +1,4 @@
1
- import type { HTMLAttributes, HTMLProps, JSX } from "react"
1
+ import type { HTMLAttributes, HTMLProps, JSX, Ref } from "react"
2
2
 
3
3
  import clsx from "clsx"
4
4
  import React from "react"
@@ -17,6 +17,7 @@ const componentSlots = {
17
17
  FieldHint: null,
18
18
  FieldLabel: null,
19
19
  Radio: null,
20
+ Select: null,
20
21
  Textarea: null,
21
22
  TextInput: null,
22
23
  }
@@ -37,6 +38,8 @@ interface FieldProps extends HTMLAttributes<HTMLDivElement> {
37
38
  error?: boolean
38
39
  /** **(Optional)** Specify whether the Field should show the left-hand error bar if it is in an error state. */
39
40
  hideErrorBar?: boolean
41
+ /** **(Optional)** Specify a custom React ref for this component. */
42
+ ref?: Ref<HTMLDivElement>
40
43
  }
41
44
 
42
45
  const Root = ({
@@ -45,19 +48,18 @@ const Root = ({
45
48
  className,
46
49
  error = false,
47
50
  hideErrorBar = false,
51
+ ref,
48
52
  ...props
49
53
  }: FieldProps) => {
50
54
  const slots = { ...useSlots(componentSlots, children) }
51
55
  const slotsArray = Object.entries(slots)
52
56
 
53
- const uid = useId()
54
-
55
57
  const isInlineControl = slotsArray.some((s) => s[1] !== null && inlineFields.includes(s[0]))
56
58
  const hasFieldMarkers = !!(slots.FieldLabel ?? slots.FieldError ?? slots.FieldHint)
57
59
  const [, control] =
58
60
  slotsArray.find((s) => (!standardFields.includes(s[0]) && s[1] !== null ? s : null)) ?? []
59
61
 
60
- let controlId = uid
62
+ let controlId = useId()
61
63
  let controlDisabled = false
62
64
 
63
65
  if (control) {
@@ -88,7 +90,7 @@ const Root = ({
88
90
  }
89
91
 
90
92
  return (
91
- <div {...componentProps}>
93
+ <div {...componentProps} ref={ref}>
92
94
  {!isInlineControl && hasFieldMarkers && (
93
95
  <div className="coop-field-markers">
94
96
  {slots.FieldLabel}
@@ -1,4 +1,4 @@
1
- import type { HTMLAttributes, JSX, ReactNode } from "react"
1
+ import type { HTMLAttributes, JSX, ReactNode, Ref } from "react"
2
2
 
3
3
  import clsx from "clsx"
4
4
 
@@ -7,14 +7,20 @@ export interface ErrorProps extends HTMLAttributes<HTMLSpanElement> {
7
7
  children?: string | ReactNode
8
8
  /** **(Optional)** Specify additional CSS classes to be applied to the component. */
9
9
  className?: string
10
+ /** **(Optional)** Specify a custom React ref for this component. */
11
+ ref?: Ref<HTMLSpanElement>
10
12
  }
11
13
 
12
- export const Error = ({ children, className, ...props }: ErrorProps): JSX.Element => {
14
+ export const Error = ({ children, className, ref, ...props }: ErrorProps): JSX.Element => {
13
15
  const componentProps = {
14
16
  className: clsx("coop-field-error ", className),
15
17
  ...props,
16
18
  }
17
- return <span {...componentProps}>{children}</span>
19
+ return (
20
+ <span {...componentProps} ref={ref}>
21
+ {children}
22
+ </span>
23
+ )
18
24
  }
19
25
 
20
26
  export default Error
@@ -1,21 +1,27 @@
1
- import type { HTMLAttributes, JSX, ReactNode } from "react"
1
+ import type { HTMLAttributes, JSX, ReactNode, Ref } from "react"
2
2
 
3
3
  import clsx from "clsx"
4
4
 
5
- export interface HintProps extends HTMLAttributes<HTMLParagraphElement> {
5
+ export interface HintProps extends HTMLAttributes<HTMLDivElement> {
6
6
  /** **(Optional)** Hint text for the form element. Accepts any valid JSX or string. */
7
7
  children?: string | ReactNode
8
8
  /** **(Optional)** Specify additional CSS classes to be applied to the component. */
9
9
  className?: string
10
+ /** **(Optional)** Specify a custom React ref for this component. */
11
+ ref?: Ref<HTMLDivElement>
10
12
  }
11
13
 
12
- export const Hint = ({ children, className, ...props }: HintProps): JSX.Element | null => {
14
+ export const Hint = ({ children, className, ref, ...props }: HintProps): JSX.Element | null => {
13
15
  const componentProps = {
14
16
  className: clsx("coop-field-hint ", className),
15
17
  ...props,
16
18
  }
17
19
 
18
- return children ? <div {...componentProps}>{children}</div> : null
20
+ return (
21
+ <div {...componentProps} ref={ref}>
22
+ {children}
23
+ </div>
24
+ )
19
25
  }
20
26
 
21
27
  export default Hint
@@ -1,5 +1,5 @@
1
1
  import clsx from "clsx"
2
- import { LabelHTMLAttributes } from "react"
2
+ import { LabelHTMLAttributes, Ref } from "react"
3
3
 
4
4
  export interface LabelProps extends LabelHTMLAttributes<HTMLLabelElement> {
5
5
  /** **(Optional)** Main label for the form element. Accepts any valid JSX or string. */
@@ -8,11 +8,17 @@ export interface LabelProps extends LabelHTMLAttributes<HTMLLabelElement> {
8
8
  className?: string
9
9
  /** **(Optional)** Specify whether the label is visible for humans or only for screen readers. */
10
10
  isVisible?: boolean
11
+ /** **(Optional)** Specify a custom React ref for this component. */
12
+ ref?: Ref<HTMLLabelElement>
11
13
  }
12
14
 
13
- export const Label = ({ children, className, isVisible = true, ...props }: LabelProps) => {
15
+ export const Label = ({ children, className, isVisible = true, ref, ...props }: LabelProps) => {
16
+ const componentProps = {
17
+ className: clsx("coop-field-label", !isVisible && "sr-only", className),
18
+ ...props,
19
+ }
14
20
  return (
15
- <label {...props} className={clsx("coop-field-label", !isVisible && "sr-only", className)}>
21
+ <label {...componentProps} ref={ref}>
16
22
  <span>{children}</span>
17
23
  </label>
18
24
  )
@@ -1,5 +1,5 @@
1
1
  import clsx from "clsx"
2
- import { HTMLAttributes } from "react"
2
+ import { HTMLAttributes, Ref } from "react"
3
3
 
4
4
  export interface LegendProps extends HTMLAttributes<HTMLLegendElement> {
5
5
  /** **(Optional)** Main legend for the fieldset. Accepts any valid JSX or string. */
@@ -8,9 +8,11 @@ export interface LegendProps extends HTMLAttributes<HTMLLegendElement> {
8
8
  className?: string
9
9
  /** **(Optional)** Specify whether the legend should be visible to humans or screen readers. */
10
10
  isVisible?: boolean
11
+ /** **(Optional)** Specify a custom React ref for this component. */
12
+ ref?: Ref<HTMLLegendElement>
11
13
  }
12
14
 
13
- export const Legend = ({ children, className, isVisible = true, ...props }: LegendProps) => {
15
+ export const Legend = ({ children, className, isVisible = true, ref, ...props }: LegendProps) => {
14
16
  const componentProps = {
15
17
  "aria-hidden": true,
16
18
  className: clsx("coop-field-label", !isVisible && "sr-only", className),
@@ -20,7 +22,11 @@ export const Legend = ({ children, className, isVisible = true, ...props }: Lege
20
22
  return (
21
23
  <>
22
24
  <legend className="sr-only">{children}</legend>
23
- {isVisible && <div {...componentProps}>{children}</div>}
25
+ {isVisible && (
26
+ <div {...componentProps} ref={ref}>
27
+ {children}
28
+ </div>
29
+ )}
24
30
  </>
25
31
  )
26
32
  }
@@ -1,5 +1,5 @@
1
1
  import clsx from "clsx"
2
- import { type FieldsetHTMLAttributes, type JSX } from "react"
2
+ import { type FieldsetHTMLAttributes, type JSX, Ref } from "react"
3
3
 
4
4
  import { StandardSizes } from "../../../src/types"
5
5
  import { Error as BaseError, type ErrorProps } from "../FieldMarkers/Error"
@@ -19,6 +19,8 @@ export interface FieldsetProps extends FieldsetHTMLAttributes<HTMLFieldSetElemen
19
19
  error?: boolean
20
20
  /** **(Optional)** Specify whether the Fieldset should show the left-hand error bar if it is in an error state. */
21
21
  hideErrorBar?: boolean
22
+ /** **(Optional)** Specify a custom React ref for this component. */
23
+ ref?: Ref<HTMLFieldSetElement>
22
24
  /** **(Optional)** Specify the size of the Fieldset and all its descendents. */
23
25
  size?: StandardSizes
24
26
  }
@@ -28,6 +30,7 @@ const Root = ({
28
30
  disabled,
29
31
  error = false,
30
32
  hideErrorBar = false,
33
+ ref,
31
34
  size = "md",
32
35
  ...props
33
36
  }: FieldsetProps): JSX.Element => {
@@ -35,15 +38,17 @@ const Root = ({
35
38
  "aria-disabled": disabled,
36
39
  className: clsx("coop-fieldset", className),
37
40
  "data-error": error || undefined,
38
- //"data-variant": variant !== "default" ? variant : undefined,
39
41
  "data-hide-error": hideErrorBar || undefined,
40
- // "data-orientation": orientation !== "vertical" ? orientation : undefined,
41
42
  "data-size": size.length && size !== "md" ? size : undefined,
42
43
  disabled,
43
44
  ...props,
44
45
  }
45
46
 
46
- return <fieldset {...componentProps}>{children}</fieldset>
47
+ return (
48
+ <fieldset {...componentProps} ref={ref}>
49
+ {children}
50
+ </fieldset>
51
+ )
47
52
  }
48
53
 
49
54
  interface FieldsetFieldsProps {