@coopdigital/react 0.32.0 → 0.33.1

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.
@@ -0,0 +1,9 @@
1
+ import type { HTMLAttributes, JSX, ReactNode } from "react";
2
+ export interface FieldProps extends HTMLAttributes<HTMLDivElement> {
3
+ /** **(Optional)** Main content inside the component. It can be any valid JSX or string. */
4
+ children?: string | ReactNode;
5
+ /** **(Optional)** Specify additional CSS classes to be applied to the component. */
6
+ className?: string;
7
+ }
8
+ export declare const Field: ({ children, className, ...props }: FieldProps) => JSX.Element;
9
+ export default Field;
@@ -0,0 +1,12 @@
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import { clsx } from '../../node_modules/clsx/dist/clsx.js';
3
+
4
+ const Field = ({ children, className, ...props }) => {
5
+ const componentProps = {
6
+ className: clsx("coop-field ", className),
7
+ ...props,
8
+ };
9
+ return jsx("div", { ...componentProps, children: children });
10
+ };
11
+
12
+ export { Field, Field as default };
@@ -0,0 +1,4 @@
1
+ import Field from "./Field";
2
+ export default Field;
3
+ export { Field };
4
+ export * from "./Field";
@@ -0,0 +1,9 @@
1
+ import type { HTMLAttributes, JSX, ReactNode } from "react";
2
+ export interface FieldErrorProps extends HTMLAttributes<HTMLSpanElement> {
3
+ /** **(Optional)** Main content inside the component. It can be any valid JSX or string. */
4
+ children?: string | ReactNode;
5
+ /** **(Optional)** Specify additional CSS classes to be applied to the component. */
6
+ className?: string;
7
+ }
8
+ export declare const FieldError: ({ children, className, ...props }: FieldErrorProps) => JSX.Element;
9
+ export default FieldError;
@@ -0,0 +1,12 @@
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import { clsx } from '../../node_modules/clsx/dist/clsx.js';
3
+
4
+ const FieldError = ({ children, className, ...props }) => {
5
+ const componentProps = {
6
+ className: clsx("coop-field-error ", className),
7
+ ...props,
8
+ };
9
+ return jsx("span", { ...componentProps, children: children });
10
+ };
11
+
12
+ export { FieldError, FieldError as default };
@@ -0,0 +1,4 @@
1
+ import FieldError from "./FieldError";
2
+ export default FieldError;
3
+ export { FieldError };
4
+ export * from "./FieldError";
@@ -0,0 +1,9 @@
1
+ import type { HTMLAttributes, JSX, ReactNode } from "react";
2
+ export interface FieldHintProps extends HTMLAttributes<HTMLParagraphElement> {
3
+ /** Main content inside the component. It can be any valid JSX or string. */
4
+ children: string | ReactNode;
5
+ /** **(Optional)** Specify additional CSS classes to be applied to the component. */
6
+ className?: string;
7
+ }
8
+ export declare const FieldHint: ({ children, className, ...props }: FieldHintProps) => JSX.Element | null;
9
+ export default FieldHint;
@@ -0,0 +1,12 @@
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import { clsx } from '../../node_modules/clsx/dist/clsx.js';
3
+
4
+ const FieldHint = ({ children, className, ...props }) => {
5
+ const componentProps = {
6
+ className: clsx("coop-field-hint ", className),
7
+ ...props,
8
+ };
9
+ return children ? jsx("p", { ...componentProps, children: children }) : null;
10
+ };
11
+
12
+ export { FieldHint, FieldHint as default };
@@ -0,0 +1,4 @@
1
+ import FieldHint from "./FieldHint";
2
+ export default FieldHint;
3
+ export { FieldHint };
4
+ export * from "./FieldHint";
@@ -0,0 +1,13 @@
1
+ import type { JSX, LabelHTMLAttributes, ReactNode } from "react";
2
+ export interface FieldLabelProps extends LabelHTMLAttributes<HTMLLabelElement> {
3
+ /** **(Optional)** Main content inside the component. It can be any valid JSX or string. */
4
+ children?: string | ReactNode;
5
+ /** **(Optional)** Specify additional CSS classes to be applied to the component. */
6
+ className?: string;
7
+ /** Specify the field ID to connect FieldLabel to the field itself. */
8
+ htmlFor: string;
9
+ /** **(Optional)** Specify whether the FieldLabel is visible for humans or only for screen readers. */
10
+ isVisible?: boolean;
11
+ }
12
+ export declare const FieldLabel: ({ children, className, htmlFor, isVisible, ...props }: FieldLabelProps) => JSX.Element | null;
13
+ export default FieldLabel;
@@ -0,0 +1,13 @@
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import { clsx } from '../../node_modules/clsx/dist/clsx.js';
3
+
4
+ const FieldLabel = ({ children, className, htmlFor, isVisible = true, ...props }) => {
5
+ const componentProps = {
6
+ className: clsx("coop-field-label ", isVisible ? "" : "sr-only", className),
7
+ htmlFor,
8
+ ...props,
9
+ };
10
+ return children && htmlFor ? jsx("label", { ...componentProps, children: children }) : null;
11
+ };
12
+
13
+ export { FieldLabel, FieldLabel as default };
@@ -0,0 +1,4 @@
1
+ import FieldLabel from "./FieldLabel";
2
+ export default FieldLabel;
3
+ export { FieldLabel };
4
+ export * from "./FieldLabel";
@@ -1,13 +1,10 @@
1
1
  import type { InputHTMLAttributes, JSX } from "react";
2
- import { StandardSizes } from "../../../src/types";
2
+ import { FormFieldError, StandardSizes } from "../../../src/types";
3
3
  export interface InputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, "size" | "type"> {
4
4
  /** **(Optional)** Specify additional CSS classes to be applied to the component. */
5
5
  className?: string;
6
- /** **(Optional)** Specify the Input error message. */
7
- error?: {
8
- message: string;
9
- type?: string;
10
- } | false | null | undefined;
6
+ /** **(Optional)** Specify the Input error. */
7
+ error?: FormFieldError;
11
8
  /** **(Optional)** Specify the Input hint.
12
9
  *
13
10
  * This text is rendered under the label to provide further guidance for users.
@@ -19,6 +16,8 @@ export interface InputProps extends Omit<InputHTMLAttributes<HTMLInputElement>,
19
16
  *
20
17
  * This property is optional in case you need to render your own label, but all form elements *must* provide a label. */
21
18
  label?: string;
19
+ /** **(Optional)** Specify whether the label should be visible to humans or screen readers. */
20
+ labelVisible?: boolean;
22
21
  /** Specify the Input name. */
23
22
  name: string;
24
23
  /** **(Optional)** Specify the Input placeholder text. Do not use in place of a form label. */
@@ -28,5 +27,5 @@ export interface InputProps extends Omit<InputHTMLAttributes<HTMLInputElement>,
28
27
  /** **(Optional)** Specify the Input type. */
29
28
  type?: "text" | "email" | "number" | "password" | "search" | "tel" | "url";
30
29
  }
31
- export declare const Input: ({ "aria-placeholder": ariaPlaceholder, className, error, hint, id, label, name, placeholder, size, type, ...props }: InputProps) => JSX.Element;
30
+ export declare const Input: ({ "aria-placeholder": ariaPlaceholder, className, error, hint, id, label, labelVisible, name, placeholder, size, type, ...props }: InputProps) => JSX.Element;
32
31
  export default Input;
@@ -1,8 +1,11 @@
1
- import { jsxs, jsx } from 'react/jsx-runtime';
1
+ import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
2
2
  import { clsx } from '../../node_modules/clsx/dist/clsx.js';
3
3
  import { useId } from 'react';
4
+ import { FieldError } from '../FieldError/FieldError.js';
5
+ import { FieldHint } from '../FieldHint/FieldHint.js';
6
+ import { FieldLabel } from '../FieldLabel/FieldLabel.js';
4
7
 
5
- const Input = ({ "aria-placeholder": ariaPlaceholder, className, error = false, hint, id, label, name, placeholder, size = "md", type = "text", ...props }) => {
8
+ const Input = ({ "aria-placeholder": ariaPlaceholder, className, error = false, hint, id, label, labelVisible = true, name, placeholder, size = "md", type = "text", ...props }) => {
6
9
  var _a;
7
10
  const internalId = useId();
8
11
  id = id !== null && id !== void 0 ? id : internalId;
@@ -17,7 +20,7 @@ const Input = ({ "aria-placeholder": ariaPlaceholder, className, error = false,
17
20
  type,
18
21
  ...props,
19
22
  };
20
- return (jsxs("div", { className: "coop-field", children: [label && (jsx("label", { className: "coop-form--label", htmlFor: id, children: label })), hint && jsx("p", { className: "coop-form--hint", children: hint }), error && jsx("span", { className: "coop-form--error", children: error.message }), jsx("input", { ...componentProps })] }));
23
+ return (jsxs(Fragment, { children: [label && (jsx(FieldLabel, { htmlFor: id, isVisible: labelVisible, children: label })), hint && jsx(FieldHint, { children: hint }), typeof error === "object" && (error === null || error === void 0 ? void 0 : error.message) && jsx(FieldError, { children: error.message }), jsx("div", { className: "coop-field-control", children: jsx("input", { ...componentProps }) })] }));
21
24
  };
22
25
 
23
26
  export { Input, Input as default };
@@ -4,6 +4,7 @@ import { jsxs, jsx } from 'react/jsx-runtime';
4
4
  import { clsx } from '../../node_modules/clsx/dist/clsx.js';
5
5
  import React, { useState, useId, useCallback } from 'react';
6
6
  import { Button } from '../Button/Button.js';
7
+ import { FieldLabel } from '../FieldLabel/FieldLabel.js';
7
8
  import { SearchIcon } from '../Icon/SearchIcon.js';
8
9
  import { Input } from '../Input/Input.js';
9
10
 
@@ -53,7 +54,7 @@ const SearchBox = ({ action, "aria-placeholder": ariaPlaceholder, autoCapitalize
53
54
  type: "search",
54
55
  ...props,
55
56
  };
56
- return (jsxs("form", { ...formProps, children: [jsx("label", { className: labelVisible ? "" : "sr-only", htmlFor: id, children: label }), jsxs("div", { className: "coop-search-box--inner", children: [jsx(Input, { ...inputProps }), jsx(Button, { ...buttonProps, children: button.label })] })] }));
57
+ return (jsxs("form", { ...formProps, children: [label && (jsx(FieldLabel, { htmlFor: id, isVisible: labelVisible, children: label })), jsxs("div", { className: "coop-search-box--inner", children: [jsx(Input, { ...inputProps }), jsx(Button, { ...buttonProps, children: button.label })] })] }));
57
58
  };
58
59
 
59
60
  export { SearchBox, SearchBox as default };
package/dist/index.d.ts CHANGED
@@ -3,6 +3,10 @@ export * from "./components/Author";
3
3
  export * from "./components/Button";
4
4
  export * from "./components/Card";
5
5
  export * from "./components/Expandable";
6
+ export * from "./components/Field";
7
+ export * from "./components/FieldError";
8
+ export * from "./components/FieldHint";
9
+ export * from "./components/FieldLabel";
6
10
  export * from "./components/Flourish";
7
11
  export * from "./components/Image";
8
12
  export * from "./components/Input";
package/dist/index.js CHANGED
@@ -3,6 +3,10 @@ export { Author } from './components/Author/Author.js';
3
3
  export { Button } from './components/Button/Button.js';
4
4
  export { Card } from './components/Card/Card.js';
5
5
  export { Expandable } from './components/Expandable/Expandable.js';
6
+ export { Field } from './components/Field/Field.js';
7
+ export { FieldError } from './components/FieldError/FieldError.js';
8
+ export { FieldHint } from './components/FieldHint/FieldHint.js';
9
+ export { FieldLabel } from './components/FieldLabel/FieldLabel.js';
6
10
  export { Flourish } from './components/Flourish/Flourish.js';
7
11
  export { Image } from './components/Image/Image.js';
8
12
  export { Input } from './components/Input/Input.js';
@@ -1 +1,5 @@
1
1
  export type StandardSizes = "sm" | "md" | "lg";
2
+ export type FormFieldError = {
3
+ message: string;
4
+ type?: string;
5
+ } | boolean | null | undefined;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@coopdigital/react",
3
3
  "type": "module",
4
- "version": "0.32.0",
4
+ "version": "0.33.1",
5
5
  "private": false,
6
6
  "publishConfig": {
7
7
  "access": "public"
@@ -63,8 +63,8 @@
63
63
  "@storybook/react-vite": "^9.1.3",
64
64
  "@testing-library/jest-dom": "^6.8.0",
65
65
  "@testing-library/react": "^16.3.0",
66
- "@types/react": "^19.1.10",
67
- "@types/react-dom": "^19.1.7",
66
+ "@types/react": "^19.1.12",
67
+ "@types/react-dom": "^19.1.9",
68
68
  "clsx": "^2.1.1",
69
69
  "react": "^19.1.1",
70
70
  "react-dom": "^19.1.1",
@@ -80,7 +80,7 @@
80
80
  "storybook": "$storybook"
81
81
  },
82
82
  "dependencies": {
83
- "@coopdigital/styles": "^0.28.0"
83
+ "@coopdigital/styles": "^0.29.0"
84
84
  },
85
- "gitHead": "dfdb5d4d591c47722d4969c5221a3e389d8a4fab"
85
+ "gitHead": "765010c67a092c8501c3ea6327ffaef67df7113f"
86
86
  }
@@ -0,0 +1,20 @@
1
+ import type { HTMLAttributes, JSX, ReactNode } from "react"
2
+
3
+ import clsx from "clsx"
4
+
5
+ export interface FieldProps extends HTMLAttributes<HTMLDivElement> {
6
+ /** **(Optional)** Main content inside the component. It can be any valid JSX or string. */
7
+ children?: string | ReactNode
8
+ /** **(Optional)** Specify additional CSS classes to be applied to the component. */
9
+ className?: string
10
+ }
11
+
12
+ export const Field = ({ children, className, ...props }: FieldProps): JSX.Element => {
13
+ const componentProps = {
14
+ className: clsx("coop-field ", className),
15
+ ...props,
16
+ }
17
+ return <div {...componentProps}>{children}</div>
18
+ }
19
+
20
+ export default Field
@@ -0,0 +1,5 @@
1
+ import Field from "./Field"
2
+
3
+ export default Field
4
+ export { Field }
5
+ export * from "./Field"
@@ -0,0 +1,20 @@
1
+ import type { HTMLAttributes, JSX, ReactNode } from "react"
2
+
3
+ import clsx from "clsx"
4
+
5
+ export interface FieldErrorProps extends HTMLAttributes<HTMLSpanElement> {
6
+ /** **(Optional)** Main content inside the component. It can be any valid JSX or string. */
7
+ children?: string | ReactNode
8
+ /** **(Optional)** Specify additional CSS classes to be applied to the component. */
9
+ className?: string
10
+ }
11
+
12
+ export const FieldError = ({ children, className, ...props }: FieldErrorProps): JSX.Element => {
13
+ const componentProps = {
14
+ className: clsx("coop-field-error ", className),
15
+ ...props,
16
+ }
17
+ return <span {...componentProps}>{children}</span>
18
+ }
19
+
20
+ export default FieldError
@@ -0,0 +1,5 @@
1
+ import FieldError from "./FieldError"
2
+
3
+ export default FieldError
4
+ export { FieldError }
5
+ export * from "./FieldError"
@@ -0,0 +1,25 @@
1
+ import type { HTMLAttributes, JSX, ReactNode } from "react"
2
+
3
+ import clsx from "clsx"
4
+
5
+ export interface FieldHintProps extends HTMLAttributes<HTMLParagraphElement> {
6
+ /** Main content inside the component. It can be any valid JSX or string. */
7
+ children: string | ReactNode
8
+ /** **(Optional)** Specify additional CSS classes to be applied to the component. */
9
+ className?: string
10
+ }
11
+
12
+ export const FieldHint = ({
13
+ children,
14
+ className,
15
+ ...props
16
+ }: FieldHintProps): JSX.Element | null => {
17
+ const componentProps = {
18
+ className: clsx("coop-field-hint ", className),
19
+ ...props,
20
+ }
21
+
22
+ return children ? <p {...componentProps}>{children}</p> : null
23
+ }
24
+
25
+ export default FieldHint
@@ -0,0 +1,5 @@
1
+ import FieldHint from "./FieldHint"
2
+
3
+ export default FieldHint
4
+ export { FieldHint }
5
+ export * from "./FieldHint"
@@ -0,0 +1,31 @@
1
+ import type { JSX, LabelHTMLAttributes, ReactNode } from "react"
2
+
3
+ import clsx from "clsx"
4
+
5
+ export interface FieldLabelProps extends LabelHTMLAttributes<HTMLLabelElement> {
6
+ /** **(Optional)** Main content inside the component. It can be any valid JSX or string. */
7
+ children?: string | ReactNode
8
+ /** **(Optional)** Specify additional CSS classes to be applied to the component. */
9
+ className?: string
10
+ /** Specify the field ID to connect FieldLabel to the field itself. */
11
+ htmlFor: string
12
+ /** **(Optional)** Specify whether the FieldLabel is visible for humans or only for screen readers. */
13
+ isVisible?: boolean
14
+ }
15
+
16
+ export const FieldLabel = ({
17
+ children,
18
+ className,
19
+ htmlFor,
20
+ isVisible = true,
21
+ ...props
22
+ }: FieldLabelProps): JSX.Element | null => {
23
+ const componentProps = {
24
+ className: clsx("coop-field-label ", isVisible ? "" : "sr-only", className),
25
+ htmlFor,
26
+ ...props,
27
+ }
28
+ return children && htmlFor ? <label {...componentProps}>{children}</label> : null
29
+ }
30
+
31
+ export default FieldLabel
@@ -0,0 +1,5 @@
1
+ import FieldLabel from "./FieldLabel"
2
+
3
+ export default FieldLabel
4
+ export { FieldLabel }
5
+ export * from "./FieldLabel"
@@ -3,13 +3,16 @@ import type { InputHTMLAttributes, JSX } from "react"
3
3
  import clsx from "clsx"
4
4
  import { useId } from "react"
5
5
 
6
- import { StandardSizes } from "../../../src/types"
6
+ import { FormFieldError, StandardSizes } from "../../../src/types"
7
+ import { FieldError } from "../FieldError"
8
+ import { FieldHint } from "../FieldHint"
9
+ import { FieldLabel } from "../FieldLabel"
7
10
 
8
11
  export interface InputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, "size" | "type"> {
9
12
  /** **(Optional)** Specify additional CSS classes to be applied to the component. */
10
13
  className?: string
11
- /** **(Optional)** Specify the Input error message. */
12
- error?: { message: string; type?: string } | false | null | undefined // TODO: Extract this into a FieldError type
14
+ /** **(Optional)** Specify the Input error. */
15
+ error?: FormFieldError
13
16
  /** **(Optional)** Specify the Input hint.
14
17
  *
15
18
  * This text is rendered under the label to provide further guidance for users.
@@ -21,6 +24,8 @@ export interface InputProps extends Omit<InputHTMLAttributes<HTMLInputElement>,
21
24
  *
22
25
  * This property is optional in case you need to render your own label, but all form elements *must* provide a label. */
23
26
  label?: string
27
+ /** **(Optional)** Specify whether the label should be visible to humans or screen readers. */
28
+ labelVisible?: boolean
24
29
  /** Specify the Input name. */
25
30
  name: string
26
31
  /** **(Optional)** Specify the Input placeholder text. Do not use in place of a form label. */
@@ -38,6 +43,7 @@ export const Input = ({
38
43
  hint,
39
44
  id,
40
45
  label,
46
+ labelVisible = true,
41
47
  name,
42
48
  placeholder,
43
49
  size = "md",
@@ -61,16 +67,20 @@ export const Input = ({
61
67
  }
62
68
 
63
69
  return (
64
- <div className="coop-field">
70
+ <>
65
71
  {label && (
66
- <label className="coop-form--label" htmlFor={id}>
72
+ <FieldLabel htmlFor={id} isVisible={labelVisible}>
67
73
  {label}
68
- </label>
74
+ </FieldLabel>
69
75
  )}
70
- {hint && <p className="coop-form--hint">{hint}</p>}
71
- {error && <span className="coop-form--error">{error.message}</span>}
72
- <input {...componentProps} />
73
- </div>
76
+
77
+ {hint && <FieldHint>{hint}</FieldHint>}
78
+
79
+ {typeof error === "object" && error?.message && <FieldError>{error.message}</FieldError>}
80
+ <div className="coop-field-control">
81
+ <input {...componentProps} />
82
+ </div>
83
+ </>
74
84
  )
75
85
  }
76
86
 
@@ -7,6 +7,7 @@ import React, { useCallback, useId, useState } from "react"
7
7
 
8
8
  import { StandardSizes } from "../../../src/types"
9
9
  import { Button, type ButtonProps } from "../Button"
10
+ import { FieldLabel } from "../FieldLabel"
10
11
  import { SearchIcon } from "../Icon"
11
12
  import Input, { InputProps } from "../Input"
12
13
 
@@ -118,9 +119,11 @@ export const SearchBox = ({
118
119
 
119
120
  return (
120
121
  <form {...formProps}>
121
- <label className={labelVisible ? "" : "sr-only"} htmlFor={id}>
122
- {label}
123
- </label>
122
+ {label && (
123
+ <FieldLabel htmlFor={id} isVisible={labelVisible}>
124
+ {label}
125
+ </FieldLabel>
126
+ )}
124
127
  <div className="coop-search-box--inner">
125
128
  <Input {...inputProps} />
126
129
  <Button {...buttonProps}>{button.label}</Button>
package/src/index.ts CHANGED
@@ -3,6 +3,10 @@ export * from "./components/Author"
3
3
  export * from "./components/Button"
4
4
  export * from "./components/Card"
5
5
  export * from "./components/Expandable"
6
+ export * from "./components/Field"
7
+ export * from "./components/FieldError"
8
+ export * from "./components/FieldHint"
9
+ export * from "./components/FieldLabel"
6
10
  export * from "./components/Flourish"
7
11
  export * from "./components/Image"
8
12
  export * from "./components/Input"
@@ -1 +1,3 @@
1
1
  export type StandardSizes = "sm" | "md" | "lg"
2
+
3
+ export type FormFieldError = { message: string; type?: string } | boolean | null | undefined