@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
@@ -1,24 +1,27 @@
1
1
  import clsx from "clsx"
2
- import { type SVGProps, useId } from "react"
2
+ import { type SVGProps } from "react"
3
+
4
+ import { useId } from "../../hooks/useId"
3
5
 
4
6
  interface IconProps extends SVGProps<SVGSVGElement> {
5
7
  alt?: string
6
8
  }
7
9
 
8
- export const TickAltIcon = ({ alt, className, fill, ...props }: IconProps) => {
9
- const id = useId()
10
+ export const TickAltIcon = ({ alt, className, fill, id, ref, ...props }: IconProps) => {
11
+ const uid = useId(id)
10
12
  const componentProps = {
11
- "aria-labelledby": alt ? id : undefined,
13
+ "aria-labelledby": alt ? uid + "-title" : undefined,
12
14
  className: clsx("coop-icon", className),
13
15
  "data-icon": "tick-alt",
14
16
  fill: fill ?? "none",
17
+ id: uid,
15
18
  role: alt ? "img" : undefined,
16
19
  viewBox: "0 0 32 32",
17
20
  ...props,
18
21
  }
19
22
  return (
20
- <svg {...componentProps}>
21
- {alt ? <title id={id}>{alt}</title> : null}
23
+ <svg {...componentProps} ref={ref}>
24
+ {alt ? <title id={uid + "-title"}>{alt}</title> : null}
22
25
  <path
23
26
  d="M12.7 22a1 1 0 0 1-.71-.29L8.3 18a1 1 0 0 1 0-1.42 1 1 0 0 1 1.41 0l3 3 9.41-9.38a1 1 0 0 1 1.42 0 1 1 0 0 1 0 1.41L13.4 21.69a1 1 0 0 1-.7.31"
24
27
  fill={fill ?? "currentColor"}
@@ -1,24 +1,27 @@
1
1
  import clsx from "clsx"
2
- import { type SVGProps, useId } from "react"
2
+ import { type SVGProps } from "react"
3
+
4
+ import { useId } from "../../hooks/useId"
3
5
 
4
6
  interface IconProps extends SVGProps<SVGSVGElement> {
5
7
  alt?: string
6
8
  }
7
9
 
8
- export const TickIcon = ({ alt, className, fill, ...props }: IconProps) => {
9
- const id = useId()
10
+ export const TickIcon = ({ alt, className, fill, id, ref, ...props }: IconProps) => {
11
+ const uid = useId(id)
10
12
  const componentProps = {
11
- "aria-labelledby": alt ? id : undefined,
13
+ "aria-labelledby": alt ? uid + "-title" : undefined,
12
14
  className: clsx("coop-icon", className),
13
15
  "data-icon": "tick",
14
16
  fill: fill ?? "none",
17
+ id: uid,
15
18
  role: alt ? "img" : undefined,
16
19
  viewBox: "0 0 32 32",
17
20
  ...props,
18
21
  }
19
22
  return (
20
- <svg {...componentProps}>
21
- {alt ? <title id={id}>{alt}</title> : null}
23
+ <svg {...componentProps} ref={ref}>
24
+ {alt ? <title id={uid + "-title"}>{alt}</title> : null}
22
25
  <path
23
26
  d="M10.41 26a1 1 0 0 1-.71-.3l-6.42-6.59a1 1 0 0 1 1.44-1.39l5.69 5.85L27.28 6.3a1 1 0 1 1 1.44 1.4l-17.59 18a1 1 0 0 1-.72.3"
24
27
  fill={fill ?? "currentColor"}
@@ -1,24 +1,27 @@
1
1
  import clsx from "clsx"
2
- import { type SVGProps, useId } from "react"
2
+ import { type SVGProps } from "react"
3
+
4
+ import { useId } from "../../hooks/useId"
3
5
 
4
6
  interface IconProps extends SVGProps<SVGSVGElement> {
5
7
  alt?: string
6
8
  }
7
9
 
8
- export const VanIcon = ({ alt, className, fill, ...props }: IconProps) => {
9
- const id = useId()
10
+ export const VanIcon = ({ alt, className, fill, id, ref, ...props }: IconProps) => {
11
+ const uid = useId(id)
10
12
  const componentProps = {
11
- "aria-labelledby": alt ? id : undefined,
13
+ "aria-labelledby": alt ? uid + "-title" : undefined,
12
14
  className: clsx("coop-icon", className),
13
15
  "data-icon": "van",
14
16
  fill: fill ?? "none",
17
+ id: uid,
15
18
  role: alt ? "img" : undefined,
16
19
  viewBox: "0 0 32 32",
17
20
  ...props,
18
21
  }
19
22
  return (
20
- <svg {...componentProps}>
21
- {alt ? <title id={id}>{alt}</title> : null}
23
+ <svg {...componentProps} ref={ref}>
24
+ {alt ? <title id={uid + "-title"}>{alt}</title> : null}
22
25
  <path
23
26
  d="M31.05 3H9.8a1 1 0 0 0-1 1v.2H3.06a1 1 0 0 0-1 .86L.89 12.64a1 1 0 0 0-.89 1v10a1 1 0 0 0 1 1h3a5 5 0 0 0 9.9 0H18a5 5 0 0 0 9.89 0h3.21a1 1 0 0 0 .72-.3 1 1 0 0 0 .18-.72V4a1 1 0 0 0-.95-1M3.91 6.24H8.8v6.38H2.93ZM8.92 27A3 3 0 0 1 6 24.71a2.9 2.9 0 0 1-.12-.83 3.1 3.1 0 0 1 .25-1.22 3 3 0 0 1 5.53 0 3.3 3.3 0 0 1 .24 1.22 3.3 3.3 0 0 1-.11.83A3 3 0 0 1 8.92 27M23 27a3 3 0 0 1-2.89-2.24 2.9 2.9 0 0 1-.12-.83 3.1 3.1 0 0 1 .25-1.22 3 3 0 0 1 5.53 0 3.3 3.3 0 0 1 .23 1.17 3.3 3.3 0 0 1-.11.83A3 3 0 0 1 23 27m4.87-4.29a5 5 0 0 0-9.74 0H13.8a5 5 0 0 0-9.75 0H2v-8h7.8a1 1 0 0 0 1-1V5.05H30v17.61Z"
24
27
  fill={fill ?? "currentColor"}
@@ -1,24 +1,27 @@
1
1
  import clsx from "clsx"
2
- import { type SVGProps, useId } from "react"
2
+ import { type SVGProps } from "react"
3
+
4
+ import { useId } from "../../hooks/useId"
3
5
 
4
6
  interface IconProps extends SVGProps<SVGSVGElement> {
5
7
  alt?: string
6
8
  }
7
9
 
8
- export const WarningIcon = ({ alt, className, fill, ...props }: IconProps) => {
9
- const id = useId()
10
+ export const WarningIcon = ({ alt, className, fill, id, ref, ...props }: IconProps) => {
11
+ const uid = useId(id)
10
12
  const componentProps = {
11
- "aria-labelledby": alt ? id : undefined,
13
+ "aria-labelledby": alt ? uid + "-title" : undefined,
12
14
  className: clsx("coop-icon", className),
13
15
  "data-icon": "warning",
14
16
  fill: fill ?? "none",
17
+ id: uid,
15
18
  role: alt ? "img" : undefined,
16
19
  viewBox: "0 0 32 32",
17
20
  ...props,
18
21
  }
19
22
  return (
20
- <svg {...componentProps}>
21
- {alt ? <title id={id}>{alt}</title> : null}
23
+ <svg {...componentProps} ref={ref}>
24
+ {alt ? <title id={uid + "-title"}>{alt}</title> : null}
22
25
  <path
23
26
  d="M2.35 28.08 16.06 4.46l13.71 23.62Zm29.32-.7L17.56 3.07A1.73 1.73 0 0 0 16.06 2a1.94 1.94 0 0 0-1.5 1.1L.35 27.38C-.45 28.77.15 30 1.85 30h28.32a1.6 1.6 0 0 0 1.5-2.62"
24
27
  fill={fill ?? "currentColor"}
@@ -1,24 +1,27 @@
1
1
  import clsx from "clsx"
2
- import { type SVGProps, useId } from "react"
2
+ import { type SVGProps } from "react"
3
+
4
+ import { useId } from "../../hooks/useId"
3
5
 
4
6
  interface IconProps extends SVGProps<SVGSVGElement> {
5
7
  alt?: string
6
8
  }
7
9
 
8
- export const WriteIcon = ({ alt, className, fill, ...props }: IconProps) => {
9
- const id = useId()
10
+ export const WriteIcon = ({ alt, className, fill, id, ref, ...props }: IconProps) => {
11
+ const uid = useId(id)
10
12
  const componentProps = {
11
- "aria-labelledby": alt ? id : undefined,
13
+ "aria-labelledby": alt ? uid + "-title" : undefined,
12
14
  className: clsx("coop-icon", className),
13
15
  "data-icon": "write",
14
16
  fill: fill ?? "none",
17
+ id: uid,
15
18
  role: alt ? "img" : undefined,
16
19
  viewBox: "0 0 32 32",
17
20
  ...props,
18
21
  }
19
22
  return (
20
- <svg {...componentProps}>
21
- {alt ? <title id={id}>{alt}</title> : null}
23
+ <svg {...componentProps} ref={ref}>
24
+ {alt ? <title id={uid + "-title"}>{alt}</title> : null}
22
25
  <path
23
26
  d="M19.67 10.13 17.9 11.9H7a1 1 0 0 1 0-2h12a1 1 0 0 1 .67.23M14.9 14.9l-2 2H7a1 1 0 0 1 0-2ZM20 15.9a1 1 0 0 1-1 1h-1.87l2-2a1 1 0 0 1 .82.59.85.85 0 0 1 .05.41M19 21.9H7a1 1 0 0 1 0-2h12a1 1 0 1 1 0 2"
24
27
  fill={fill ?? "currentColor"}
@@ -1,4 +1,4 @@
1
- import type { HTMLAttributes, JSX } from "react"
1
+ import type { HTMLAttributes, JSX, Ref } from "react"
2
2
 
3
3
  export interface ImageProps extends HTMLAttributes<HTMLImageElement> {
4
4
  /** Specify text to describe the image. Refer to Experience Library guidelines on writing good alt text. */
@@ -7,6 +7,8 @@ export interface ImageProps extends HTMLAttributes<HTMLImageElement> {
7
7
  crop?: "square" | "wide" | "none"
8
8
  /** **(Optional)** Specify the height of the image. */
9
9
  height?: string
10
+ /** **(Optional)** Specify a custom React ref for this component. */
11
+ ref?: Ref<HTMLImageElement>
10
12
  /** Specify the Image source URL. */
11
13
  src: string
12
14
  /** **(Optional)** Specify the width of the image. */
@@ -38,6 +40,7 @@ export const Image = ({
38
40
  alt,
39
41
  crop,
40
42
  height,
43
+ ref,
41
44
  src,
42
45
  width = "640",
43
46
  ...props
@@ -56,7 +59,7 @@ export const Image = ({
56
59
 
57
60
  return (
58
61
  <picture>
59
- <img alt={alt} loading="lazy" src={params.src} {...dimensions} {...props} />
62
+ <img alt={alt} loading="lazy" src={params.src} {...dimensions} {...props} ref={ref} />
60
63
  </picture>
61
64
  )
62
65
  }
@@ -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"
@@ -17,6 +17,8 @@ export interface PillProps extends HTMLAttributes<HTMLAnchorElement> {
17
17
  className?: string
18
18
  /** **(Optional)** Specify the URL that the Pill component will link to when clicked. */
19
19
  href?: string
20
+ /** **(Optional)** Specify a custom React ref for this component. */
21
+ ref?: Ref<HTMLElement>
20
22
  /** **(Optional)** Specify the Pill size. */
21
23
  size?: StandardSizes
22
24
  }
@@ -38,17 +40,14 @@ export const Pill = ({
38
40
  children,
39
41
  className,
40
42
  href,
43
+ ref,
41
44
  size = "md",
42
45
  ...props
43
46
  }: PillProps): JSX.Element => {
44
- let element: PillProps["as"] = href ? "a" : "span"
47
+ const element: PillProps["as"] = as ?? (href ? "a" : "span")
45
48
 
46
49
  const slots = useSlots(componentSlots, children)
47
50
 
48
- if (as) {
49
- element = as
50
- }
51
-
52
51
  const componentProps = {
53
52
  className: clsx("coop-pill", !hasUserBg(className) && "bg-tint-grey", className),
54
53
  "data-size": size.length && size !== "md" ? size : undefined,
@@ -56,7 +55,9 @@ export const Pill = ({
56
55
  ...props,
57
56
  }
58
57
 
59
- return React.createElement(element, { ...componentProps }, slots.PillBadge, slots.Children)
58
+ // https://github.com/facebook/react/issues/34775
59
+ // eslint-disable-next-line react-hooks/refs
60
+ return React.createElement(element, { ...componentProps, ref }, slots.PillBadge, slots.Children)
60
61
  }
61
62
 
62
63
  const PillBadge = ({ children, className }: PillBadgeProps) => {
@@ -1,6 +1,8 @@
1
1
  import clsx from "clsx"
2
- import { type InputHTMLAttributes, type JSX, useId } from "react"
3
- import { StandardSizes } from "src/types"
2
+ import { type InputHTMLAttributes, type JSX, Ref } from "react"
3
+
4
+ import { useId } from "../../hooks/useId"
5
+ import { StandardSizes } from "../../types"
4
6
 
5
7
  export interface RadioProps extends Omit<InputHTMLAttributes<HTMLInputElement>, "size" | "type"> {
6
8
  /** **(Optional)** Specify additional CSS classes to be applied to the component. */
@@ -13,6 +15,8 @@ export interface RadioProps extends Omit<InputHTMLAttributes<HTMLInputElement>,
13
15
  id?: string
14
16
  /** Specify the Radio name. */
15
17
  name: string
18
+ /** **(Optional)** Specify a custom React ref for this component. */
19
+ ref?: Ref<HTMLInputElement>
16
20
  /** **(Optional)** Specify the Radio size. */
17
21
  size?: StandardSizes
18
22
  }
@@ -23,25 +27,24 @@ export const Radio = ({
23
27
  error = false,
24
28
  id,
25
29
  name,
30
+ ref,
26
31
  size = "md",
27
32
  ...props
28
33
  }: RadioProps): JSX.Element => {
29
- const internalId = useId()
30
-
31
- id = id ?? internalId
34
+ const uid = useId(id)
32
35
 
33
36
  const componentProps = {
34
- className: clsx("coop-radio", className),
37
+ className: clsx("coop-radio", "coop-field-element", className),
35
38
  "data-error": error || undefined,
36
39
  "data-size": size.length && size !== "md" ? size : undefined,
37
40
  disabled,
38
- id,
41
+ id: uid,
39
42
  name,
40
43
  type: "radio",
41
44
  ...props,
42
45
  }
43
46
  //const formItemProps = { "aria-disabled": disabled ? true : undefined }
44
- return <input {...componentProps} />
47
+ return <input {...componentProps} ref={ref} />
45
48
  }
46
49
 
47
50
  export default Radio
@@ -1,18 +1,21 @@
1
1
  "use client"
2
2
 
3
- import type { InputHTMLAttributes, JSX } from "react"
3
+ import type { InputHTMLAttributes, JSX, Ref } from "react"
4
4
 
5
5
  import clsx from "clsx"
6
- import React, { useCallback, useId, useState } from "react"
6
+ import React, { useCallback, useState } from "react"
7
7
 
8
+ import { useId } from "../../hooks/useId"
8
9
  import { StandardSizes } from "../../types"
9
10
  import { Button, type ButtonProps } from "../Button"
10
11
  import { Label as FieldLabel } from "../FieldMarkers/Label"
11
12
  import { SearchIcon } from "../Icon"
12
13
  import TextInput, { TextInputProps } from "../TextInput"
13
14
 
14
- export interface SearchboxProps
15
- extends Omit<InputHTMLAttributes<HTMLInputElement>, "size" | "type"> {
15
+ export interface SearchboxProps extends Omit<
16
+ InputHTMLAttributes<HTMLInputElement>,
17
+ "size" | "type"
18
+ > {
16
19
  /** **(Optional)** Specify a server endpoint to submit the form. Will be ignored if onSubmit is also set. */
17
20
  action?: string
18
21
  /** **(Optional)** Specify props to forward to the Button element. Use `label` to set Button text. */
@@ -31,6 +34,8 @@ export interface SearchboxProps
31
34
  onSubmit?: React.FormEventHandler<HTMLElement> | undefined
32
35
  /** **(Optional)** Specify the TextInput placeholder text Do not use in place of a form label. */
33
36
  placeholder?: string
37
+ /** **(Optional)** Specify a custom React ref for this component. */
38
+ ref?: Ref<HTMLFormElement>
34
39
  /** **(Optional)** Specify the Searchbox size. */
35
40
  size?: StandardSizes
36
41
  /** **(Optional)** Specify the Searchbox variant. */
@@ -64,14 +69,13 @@ export const Searchbox = ({
64
69
  name = "query",
65
70
  onSubmit,
66
71
  placeholder,
72
+ ref,
67
73
  size = "md",
68
74
  variant = "green",
69
75
  ...props
70
76
  }: SearchboxProps): JSX.Element => {
71
77
  const [isPending, setIsPending] = useState(false)
72
- const internalId = useId()
73
-
74
- id = id ?? internalId
78
+ const uid = useId(id)
75
79
 
76
80
  const handleSubmit = useCallback(
77
81
  async (event: React.FormEvent<HTMLFormElement>) => {
@@ -95,39 +99,40 @@ export const Searchbox = ({
95
99
  className: clsx("coop-searchbox", className),
96
100
  "data-size": size && size !== "md" ? size : undefined,
97
101
  "data-variant": variant.length && variant !== "green" ? variant : undefined,
102
+ id: uid,
98
103
  onSubmit: onSubmit ? handleSubmit : undefined,
99
104
  }
100
105
 
101
- const buttonProps = {
106
+ const buttonProps: ButtonProps = {
102
107
  className: button?.className,
103
108
  isLoading: isPending,
104
109
  loadingText: button?.loadingText ?? "",
105
- size: size as keyof ButtonProps["size"],
106
- variant: variant as keyof ButtonProps["variant"],
110
+ size,
111
+ variant,
107
112
  }
108
113
 
109
- const inputProps = {
114
+ const inputProps: TextInputProps = {
110
115
  "aria-placeholder": placeholder ?? ariaPlaceholder ?? undefined,
111
116
  autoCapitalize,
112
117
  autoComplete,
113
- id,
118
+ id: uid + "--input",
114
119
  name,
115
120
  placeholder,
116
- size: size as keyof TextInputProps["size"],
117
- type: "search" as keyof TextInputProps["type"],
121
+ size,
122
+ type: "search",
118
123
  ...props,
119
124
  }
120
125
 
126
+ const labelProps = {
127
+ htmlFor: uid + "--input",
128
+ isVisible: labelVisible,
129
+ }
130
+
121
131
  return (
122
- <form {...formProps}>
123
- {label && (
124
- <FieldLabel htmlFor={id} isVisible={labelVisible}>
125
- {label}
126
- </FieldLabel>
127
- )}
132
+ <form {...formProps} ref={ref}>
133
+ {label && <FieldLabel {...labelProps}>{label}</FieldLabel>}
128
134
  <div className="coop-searchbox--inner">
129
135
  <TextInput {...inputProps} />
130
-
131
136
  <Button {...buttonProps}>{button.label}</Button>
132
137
  </div>
133
138
  </form>
@@ -0,0 +1,97 @@
1
+ import type {
2
+ JSX,
3
+ OptgroupHTMLAttributes,
4
+ OptionHTMLAttributes,
5
+ ReactNode,
6
+ Ref,
7
+ SelectHTMLAttributes,
8
+ } from "react"
9
+
10
+ import clsx from "clsx"
11
+
12
+ import { StandardSizes } from "../../../src/types"
13
+ import { useId } from "../../hooks/useId"
14
+
15
+ export interface SelectProps extends Omit<SelectHTMLAttributes<HTMLSelectElement>, "size"> {
16
+ /** **(Optional)** Options inside the Select. This should be a set of `Select.Option` or `Select.OptionGroup` components. */
17
+ children?: ReactNode
18
+ /** **(Optional)** Specify additional CSS classes to be applied to the component. */
19
+ className?: string
20
+ /** **(Optional)** Specify whether the Select should be disabled. Refer to Experience Library guidance on disabled form controls and accessibility. */
21
+ disabled?: boolean
22
+ /** **(Optional)** Specify the Select error state. */
23
+ error?: boolean
24
+ /** **(Optional)** Specify the Select id. Will be auto-generated if not set. */
25
+ id?: string
26
+ /** Specify the Select name. */
27
+ name: string
28
+ /** **(Optional)** Specify a custom React ref for this component. */
29
+ ref?: Ref<HTMLSelectElement>
30
+ /** **(Optional)** Specify the Select size. */
31
+ size?: StandardSizes
32
+ }
33
+
34
+ interface SelectOptionGroupProps extends OptgroupHTMLAttributes<HTMLOptGroupElement> {
35
+ /** **(Optional)** Main content inside the Select option. It can be any valid JSX or string. */
36
+ children?: ReactNode
37
+ /** Specify the Select option group label. */
38
+ label: string
39
+ }
40
+
41
+ interface SelectOptionProps extends OptionHTMLAttributes<HTMLOptionElement> {
42
+ /** **(Optional)** Main content inside the Select option. It can be any valid JSX or string. */
43
+ children?: ReactNode
44
+ /** **(Optional)** Specify additional CSS classes to be applied to the Select option. */
45
+ className?: string
46
+ /** Value of Select option. */
47
+ value: string
48
+ }
49
+
50
+ export const Select = ({
51
+ children,
52
+ className,
53
+ disabled,
54
+ error,
55
+ id,
56
+ name,
57
+ ref,
58
+ size = "md",
59
+ ...props
60
+ }: SelectProps): JSX.Element => {
61
+ const uid = useId(id)
62
+ const componentProps = {
63
+ className: clsx("coop-select", "coop-field-element", className),
64
+ "data-error": error ?? undefined,
65
+ "data-size": size.length && size !== "md" ? size : undefined,
66
+ disabled,
67
+ id: uid,
68
+ name,
69
+ ...props,
70
+ }
71
+
72
+ return (
73
+ <select {...componentProps} ref={ref}>
74
+ {children}
75
+ </select>
76
+ )
77
+ }
78
+
79
+ const SelectOptionGroup = ({ children, ...props }: SelectOptionGroupProps): JSX.Element => {
80
+ return <optgroup {...props}>{children}</optgroup>
81
+ }
82
+
83
+ const SelectOption = ({ children, className, ...props }: SelectOptionProps): JSX.Element => {
84
+ return (
85
+ <option className={clsx("coop-select--option", className)} {...props}>
86
+ {children}
87
+ </option>
88
+ )
89
+ }
90
+
91
+ SelectOptionGroup.displayName = "Select.OptionGroup"
92
+ SelectOption.displayName = "Select.Option"
93
+
94
+ Select.OptionGroup = SelectOptionGroup
95
+ Select.Option = SelectOption
96
+
97
+ export default Select
@@ -0,0 +1,5 @@
1
+ import Select from "./Select"
2
+
3
+ export default Select
4
+ export { Select }
5
+ export * from "./Select"
@@ -1,4 +1,4 @@
1
- import type { AnchorHTMLAttributes, 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"
@@ -9,7 +9,7 @@ import { Image, ImageProps } from "../Image"
9
9
  export interface SignpostProps extends HTMLAttributes<HTMLDivElement> {
10
10
  /** **(Optional)** Specify a custom element to override default `a`. */
11
11
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
12
- as?: React.FC<AnchorHTMLAttributes<HTMLElement>> | ForwardRefExoticComponent<any> | string
12
+ as?: React.FC<any> | ForwardRefExoticComponent<any> | string
13
13
  /** Main content inside the component. It can be any valid JSX or string. */
14
14
  children: React.ReactNode
15
15
  /** **(Optional)** Specify additional CSS classes to be applied to the component. */
@@ -17,9 +17,11 @@ export interface SignpostProps extends HTMLAttributes<HTMLDivElement> {
17
17
  /** **(Optional)** Specify the level of the Signpost heading. */
18
18
  headingLevel?: "h2" | "h3" | "h4" | "h5" | "h6"
19
19
  /** Specify the URL that the Signpost component will link to when clicked. */
20
- href: string
20
+ href?: string
21
21
  /** **(Optional)** Specify properties of the Signpost Image. */
22
22
  image?: ImageProps
23
+ /** **(Optional)** Specify a custom React ref for this component. */
24
+ ref?: Ref<HTMLDivElement>
23
25
  }
24
26
 
25
27
  export const Signpost = ({
@@ -29,12 +31,10 @@ export const Signpost = ({
29
31
  headingLevel = "h3",
30
32
  href,
31
33
  image,
34
+ ref,
32
35
  ...props
33
36
  }: SignpostProps): JSX.Element => {
34
- let element: SignpostProps["as"] = "a"
35
- if (as) {
36
- element = as
37
- }
37
+ const element: SignpostProps["as"] = as ?? "a"
38
38
 
39
39
  const componentProps = {
40
40
  className: clsx("coop-signpost", className),
@@ -42,7 +42,7 @@ export const Signpost = ({
42
42
  }
43
43
 
44
44
  return (
45
- <div {...componentProps}>
45
+ <div {...componentProps} ref={ref}>
46
46
  {image && <Image crop="wide" {...image} />}
47
47
  {React.createElement(
48
48
  element,
@@ -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 SkipNavProps extends HTMLAttributes<HTMLDivElement> {
15
15
  /** **(Optional)** Specify links to show in the SkipNav.
16
16
  * Each item needs a `href` and `label`. Defaults to a single link anchored to `#main`. */
17
17
  links?: SkipNavLink[]
18
+ /** **(Optional)** Specify a custom React ref for this component. */
19
+ ref?: Ref<HTMLDivElement>
18
20
  }
19
21
 
20
22
  const defaultLinks = [{ href: "#main", label: "Skip to main content" }]
@@ -23,6 +25,7 @@ export const SkipNav = ({
23
25
  className,
24
26
  isVisible = false,
25
27
  links = defaultLinks,
28
+ ref,
26
29
  ...props
27
30
  }: SkipNavProps): JSX.Element => {
28
31
  const componentProps = {
@@ -31,7 +34,7 @@ export const SkipNav = ({
31
34
  }
32
35
  const navLinks = links.length > 0 ? links : defaultLinks
33
36
  return (
34
- <nav {...componentProps}>
37
+ <nav {...componentProps} ref={ref}>
35
38
  <ul>
36
39
  {navLinks.map((link) => {
37
40
  const linkProps = {
@@ -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
 
@@ -8,6 +8,8 @@ export interface SquircleProps extends HTMLAttributes<HTMLDivElement> {
8
8
  children?: React.ReactNode
9
9
  /** **(Optional)** Specify additional CSS classes to be applied to the component. */
10
10
  className?: string
11
+ /** **(Optional)** Specify a custom React ref for this component. */
12
+ ref?: Ref<HTMLDivElement>
11
13
  /** **(Optional)** Specify the Squircle size. */
12
14
  size?: "sm" | "md" | "lg"
13
15
  }
@@ -15,6 +17,7 @@ export interface SquircleProps extends HTMLAttributes<HTMLDivElement> {
15
17
  export const Squircle = ({
16
18
  children,
17
19
  className,
20
+ ref,
18
21
  size = "lg",
19
22
  ...props
20
23
  }: SquircleProps): JSX.Element => {
@@ -25,7 +28,7 @@ export const Squircle = ({
25
28
  }
26
29
 
27
30
  return (
28
- <figure {...componentProps}>
31
+ <figure {...componentProps} ref={ref}>
29
32
  <figcaption>{children}</figcaption>
30
33
  </figure>
31
34
  )