@coopdigital/react 0.46.0 → 0.47.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 (190) hide show
  1. package/dist/components/AlertBanner/AlertBanner.d.ts +4 -2
  2. package/dist/components/AlertBanner/AlertBanner.js +2 -2
  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 +2 -2
  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 +3 -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 +5 -6
  113. package/dist/components/Searchbox/Searchbox.d.ts +4 -2
  114. package/dist/components/Searchbox/Searchbox.js +14 -9
  115. package/dist/components/Signpost/Signpost.d.ts +6 -4
  116. package/dist/components/Signpost/Signpost.js +3 -6
  117. package/dist/components/SkipNav/SkipNav.d.ts +4 -2
  118. package/dist/components/SkipNav/SkipNav.js +2 -2
  119. package/dist/components/Squircle/Squircle.d.ts +4 -2
  120. package/dist/components/Squircle/Squircle.js +2 -2
  121. package/dist/components/Tag/Tag.d.ts +5 -3
  122. package/dist/components/Tag/Tag.js +3 -6
  123. package/dist/components/TextInput/TextInput.d.ts +4 -2
  124. package/dist/components/TextInput/TextInput.js +2 -2
  125. package/dist/components/Textarea/Textarea.d.ts +4 -2
  126. package/dist/components/Textarea/Textarea.js +4 -5
  127. package/package.json +2 -2
  128. package/src/components/AlertBanner/AlertBanner.tsx +6 -2
  129. package/src/components/Author/Author.tsx +5 -2
  130. package/src/components/Button/Button.tsx +2 -3
  131. package/src/components/Card/Card.tsx +5 -2
  132. package/src/components/Checkbox/Checkbox.tsx +6 -2
  133. package/src/components/Expandable/Expandable.tsx +10 -3
  134. package/src/components/Field/Field.tsx +6 -5
  135. package/src/components/FieldMarkers/Error.tsx +9 -3
  136. package/src/components/FieldMarkers/Hint.tsx +10 -4
  137. package/src/components/FieldMarkers/Label.tsx +9 -3
  138. package/src/components/FieldMarkers/Legend.tsx +9 -3
  139. package/src/components/Fieldset/Fieldset.tsx +9 -4
  140. package/src/components/Flourish/Flourish.tsx +5 -2
  141. package/src/components/Icon/AddIcon.tsx +9 -6
  142. package/src/components/Icon/ArrowDownIcon.tsx +9 -6
  143. package/src/components/Icon/ArrowLeftIcon.tsx +9 -6
  144. package/src/components/Icon/ArrowRightIcon.tsx +9 -6
  145. package/src/components/Icon/ArrowUpIcon.tsx +9 -6
  146. package/src/components/Icon/AvatarAltIcon.tsx +9 -6
  147. package/src/components/Icon/AvatarIcon.tsx +9 -6
  148. package/src/components/Icon/BasketIcon.tsx +9 -6
  149. package/src/components/Icon/CalendarIcon.tsx +9 -6
  150. package/src/components/Icon/ChevronDownIcon.tsx +9 -6
  151. package/src/components/Icon/ChevronLeftIcon.tsx +9 -6
  152. package/src/components/Icon/ChevronRightIcon.tsx +9 -6
  153. package/src/components/Icon/ChevronUpIcon.tsx +9 -6
  154. package/src/components/Icon/ClockIcon.tsx +9 -6
  155. package/src/components/Icon/CloseAltIcon.tsx +9 -6
  156. package/src/components/Icon/CloseIcon.tsx +9 -6
  157. package/src/components/Icon/CoopCardIcon.tsx +9 -6
  158. package/src/components/Icon/CoopIcon.tsx +9 -6
  159. package/src/components/Icon/CoopLocationIcon.tsx +9 -6
  160. package/src/components/Icon/DownloadIcon.tsx +9 -6
  161. package/src/components/Icon/HomeIcon.tsx +9 -6
  162. package/src/components/Icon/InformationIcon.tsx +9 -6
  163. package/src/components/Icon/LoadingIcon.tsx +9 -6
  164. package/src/components/Icon/LocationIcon.tsx +9 -6
  165. package/src/components/Icon/MailIcon.tsx +9 -6
  166. package/src/components/Icon/MenuIcon.tsx +9 -6
  167. package/src/components/Icon/MessageIcon.tsx +9 -6
  168. package/src/components/Icon/MinusIcon.tsx +9 -6
  169. package/src/components/Icon/OpenNewIcon.tsx +9 -6
  170. package/src/components/Icon/PencilIcon.tsx +9 -6
  171. package/src/components/Icon/PhoneIcon.tsx +9 -6
  172. package/src/components/Icon/QuestionIcon.tsx +9 -6
  173. package/src/components/Icon/ScooterIcon.tsx +9 -6
  174. package/src/components/Icon/SearchIcon.tsx +9 -6
  175. package/src/components/Icon/SettingsIcon.tsx +9 -6
  176. package/src/components/Icon/TickAltIcon.tsx +9 -6
  177. package/src/components/Icon/TickIcon.tsx +9 -6
  178. package/src/components/Icon/VanIcon.tsx +9 -6
  179. package/src/components/Icon/WarningIcon.tsx +9 -6
  180. package/src/components/Icon/WriteIcon.tsx +9 -6
  181. package/src/components/Image/Image.tsx +5 -2
  182. package/src/components/Pill/Pill.tsx +8 -7
  183. package/src/components/Radio/Radio.tsx +10 -7
  184. package/src/components/Searchbox/Searchbox.tsx +22 -19
  185. package/src/components/Signpost/Signpost.tsx +8 -8
  186. package/src/components/SkipNav/SkipNav.tsx +5 -2
  187. package/src/components/Squircle/Squircle.tsx +5 -2
  188. package/src/components/Tag/Tag.tsx +9 -8
  189. package/src/components/TextInput/TextInput.tsx +5 -2
  190. package/src/components/Textarea/Textarea.tsx +8 -6
@@ -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
37
  className: clsx("coop-radio", 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,10 +1,11 @@
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"
@@ -31,6 +32,8 @@ export interface SearchboxProps
31
32
  onSubmit?: React.FormEventHandler<HTMLElement> | undefined
32
33
  /** **(Optional)** Specify the TextInput placeholder text Do not use in place of a form label. */
33
34
  placeholder?: string
35
+ /** **(Optional)** Specify a custom React ref for this component. */
36
+ ref?: Ref<HTMLFormElement>
34
37
  /** **(Optional)** Specify the Searchbox size. */
35
38
  size?: StandardSizes
36
39
  /** **(Optional)** Specify the Searchbox variant. */
@@ -64,14 +67,13 @@ export const Searchbox = ({
64
67
  name = "query",
65
68
  onSubmit,
66
69
  placeholder,
70
+ ref,
67
71
  size = "md",
68
72
  variant = "green",
69
73
  ...props
70
74
  }: SearchboxProps): JSX.Element => {
71
75
  const [isPending, setIsPending] = useState(false)
72
- const internalId = useId()
73
-
74
- id = id ?? internalId
76
+ const uid = useId(id)
75
77
 
76
78
  const handleSubmit = useCallback(
77
79
  async (event: React.FormEvent<HTMLFormElement>) => {
@@ -95,39 +97,40 @@ export const Searchbox = ({
95
97
  className: clsx("coop-searchbox", className),
96
98
  "data-size": size && size !== "md" ? size : undefined,
97
99
  "data-variant": variant.length && variant !== "green" ? variant : undefined,
100
+ id: uid,
98
101
  onSubmit: onSubmit ? handleSubmit : undefined,
99
102
  }
100
103
 
101
- const buttonProps = {
104
+ const buttonProps: ButtonProps = {
102
105
  className: button?.className,
103
106
  isLoading: isPending,
104
107
  loadingText: button?.loadingText ?? "",
105
- size: size as keyof ButtonProps["size"],
106
- variant: variant as keyof ButtonProps["variant"],
108
+ size,
109
+ variant,
107
110
  }
108
111
 
109
- const inputProps = {
112
+ const inputProps: TextInputProps = {
110
113
  "aria-placeholder": placeholder ?? ariaPlaceholder ?? undefined,
111
114
  autoCapitalize,
112
115
  autoComplete,
113
- id,
116
+ id: uid + "--input",
114
117
  name,
115
118
  placeholder,
116
- size: size as keyof TextInputProps["size"],
117
- type: "search" as keyof TextInputProps["type"],
119
+ size,
120
+ type: "search",
118
121
  ...props,
119
122
  }
120
123
 
124
+ const labelProps = {
125
+ htmlFor: uid + "--input",
126
+ isVisible: labelVisible,
127
+ }
128
+
121
129
  return (
122
- <form {...formProps}>
123
- {label && (
124
- <FieldLabel htmlFor={id} isVisible={labelVisible}>
125
- {label}
126
- </FieldLabel>
127
- )}
130
+ <form {...formProps} ref={ref}>
131
+ {label && <FieldLabel {...labelProps}>{label}</FieldLabel>}
128
132
  <div className="coop-searchbox--inner">
129
133
  <TextInput {...inputProps} />
130
-
131
134
  <Button {...buttonProps}>{button.label}</Button>
132
135
  </div>
133
136
  </form>
@@ -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
  )
@@ -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"
@@ -8,13 +8,15 @@ import { hasUserBg } from "../../../src/utils"
8
8
  export interface TagProps extends HTMLAttributes<HTMLAnchorElement> {
9
9
  /** **(Optional)** Specify a custom element to override default `a` or `span`. */
10
10
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
11
- as?: React.FC<AnchorHTMLAttributes<HTMLElement>> | ForwardRefExoticComponent<any> | string
11
+ as?: React.FC<any> | ForwardRefExoticComponent<any> | string
12
12
  /** **(Optional)** Main content inside the component. It can be any valid JSX or string. */
13
13
  children?: React.ReactNode
14
14
  /** **(Optional)** Specify additional CSS classes to be applied to the component. */
15
15
  className?: string
16
16
  /** **(Optional)** Specify the URL that the Tag component will link to when clicked. */
17
17
  href?: string
18
+ /** **(Optional)** Specify a custom React ref for this component. */
19
+ ref?: Ref<HTMLElement>
18
20
  /** **(Optional)** Specify the Tag size. */
19
21
  size?: "sm" | "md"
20
22
  }
@@ -24,21 +26,20 @@ export const Tag = ({
24
26
  children,
25
27
  className,
26
28
  href,
29
+ ref,
27
30
  size = "md",
28
31
  ...props
29
32
  }: TagProps): JSX.Element => {
30
- let element: TagProps["as"] = href ? "a" : "span"
33
+ const element: TagProps["as"] = as ?? (href ? "a" : "span")
34
+
31
35
  const componentProps = {
32
36
  className: clsx("coop-tag", !hasUserBg(className) && "bg-tint-grey", className),
33
37
  "data-size": size.length && size !== "md" ? size : undefined,
34
38
  href,
35
-
36
39
  ...props,
37
40
  }
38
- if (as) {
39
- element = as
40
- }
41
- return React.createElement(element, { ...componentProps }, children)
41
+
42
+ return React.createElement(element, { ...componentProps, ref }, children)
42
43
  }
43
44
 
44
45
  export default Tag
@@ -1,4 +1,4 @@
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
 
@@ -21,6 +21,8 @@ export interface TextInputProps
21
21
  placeholder?: string
22
22
  /** **(Optional)** Specify the prefix. It can be any valid JSX or string. */
23
23
  prefix?: React.ReactNode
24
+ /** **(Optional)** Specify a custom React ref for this component. */
25
+ ref?: Ref<HTMLInputElement>
24
26
  /** **(Optional)** Specify the TextInput size. */
25
27
  size?: StandardSizes
26
28
  /** **(Optional)** Specify the suffix. It can be any valid JSX or string. */
@@ -50,6 +52,7 @@ export const TextInput = ({
50
52
  name,
51
53
  placeholder,
52
54
  prefix,
55
+ ref,
53
56
  size = "md",
54
57
  suffix,
55
58
  type = "text",
@@ -73,7 +76,7 @@ export const TextInput = ({
73
76
  return (
74
77
  <div className="coop-text-input-wrapper">
75
78
  {prefix && <span className="coop-text-input--prefix">{prefix}</span>}
76
- <input {...componentProps} />
79
+ <input {...componentProps} ref={ref} />
77
80
  {suffix && <span className="coop-text-input--suffix">{suffix}</span>}
78
81
  </div>
79
82
  )
@@ -1,4 +1,4 @@
1
- import type { ChangeEvent, JSX, TextareaHTMLAttributes } from "react"
1
+ import type { ChangeEvent, JSX, Ref, TextareaHTMLAttributes } from "react"
2
2
 
3
3
  import clsx from "clsx"
4
4
  import { useState } from "react"
@@ -39,6 +39,8 @@ export interface TextareaProps extends TextareaHTMLAttributes<HTMLTextAreaElemen
39
39
  name: string
40
40
  /** **(Optional)** Specify the Textarea placeholder text. Do not use in place of a form label. */
41
41
  placeholder?: string
42
+ /** **(Optional)** Specify a custom React ref for this component. */
43
+ ref?: Ref<HTMLTextAreaElement>
42
44
  /** Specify the number of rows (lines of text) in the Textarea. Defaults to `4`. */
43
45
  rows?: number
44
46
  /** **(Optional)** Specify the Textarea size. */
@@ -58,13 +60,12 @@ export const Textarea = ({
58
60
  name,
59
61
  onChange: userOnChange = undefined,
60
62
  placeholder,
63
+ ref,
61
64
  rows = 4,
62
65
  size = "md",
63
66
  ...props
64
67
  }: TextareaProps): JSX.Element => {
65
- const internalId = useId()
66
-
67
- id = id ?? internalId
68
+ const uid = useId(id)
68
69
 
69
70
  const componentProps = {
70
71
  "aria-placeholder": placeholder ?? ariaPlaceholder ?? undefined,
@@ -73,7 +74,7 @@ export const Textarea = ({
73
74
  "data-error": error ?? undefined,
74
75
  "data-size": size.length && size !== "md" ? size : undefined,
75
76
  disabled,
76
- id,
77
+ id: uid,
77
78
  maxLength: cutoff ? maxLength : undefined,
78
79
  name,
79
80
  placeholder,
@@ -98,7 +99,8 @@ export const Textarea = ({
98
99
  userOnChange?.(e)
99
100
  handleChange(e)
100
101
  }}
101
- ></textarea>
102
+ ref={ref}
103
+ />
102
104
  {showCounter && (
103
105
  <>
104
106
  <small