@coopdigital/react 0.53.0 → 0.54.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.
- package/README.md +3 -3
- package/dist/components/Button/Button.d.ts +1 -1
- package/dist/components/Button/Button.js +3 -2
- package/dist/components/Card/Card.js +2 -1
- package/dist/components/DatePicker/DatePicker.js +1 -1
- package/dist/components/Expandable/Expandable.js +4 -4
- package/dist/components/Pill/Pill.js +1 -1
- package/dist/components/Searchbox/Searchbox.d.ts +20 -4
- package/dist/components/Searchbox/Searchbox.js +41 -27
- package/dist/components/Squircle/Squircle.js +1 -1
- package/dist/types/colors.d.ts +1 -2
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +4 -1
- package/package.json +11 -11
- package/src/components/Button/Button.tsx +8 -12
- package/src/components/Card/Card.tsx +2 -1
- package/src/components/DatePicker/DatePicker.tsx +1 -1
- package/src/components/Expandable/Expandable.tsx +4 -4
- package/src/components/Pill/Pill.tsx +1 -1
- package/src/components/Searchbox/Searchbox.tsx +65 -55
- package/src/components/Squircle/Squircle.tsx +1 -1
- package/src/types/colors.ts +1 -3
- package/src/utils/index.ts +4 -0
package/README.md
CHANGED
|
@@ -25,18 +25,18 @@ npm install @coopdigital/react
|
|
|
25
25
|
|
|
26
26
|
## Usage
|
|
27
27
|
|
|
28
|
-
Import the components that you need, along with the
|
|
28
|
+
Import the components that you need, along with the foundations stylesheet and the corresponding component styles:
|
|
29
29
|
|
|
30
30
|
```
|
|
31
31
|
import { Pill } from "@coopdigital/react"
|
|
32
|
-
import "@coopdigital/styles/
|
|
32
|
+
import "@coopdigital/styles/foundations.css"
|
|
33
33
|
import "@coopdigital/styles/components/Pill.css"
|
|
34
34
|
```
|
|
35
35
|
|
|
36
36
|
Alternatively if your project uses SASS you can import the source stylesheets:
|
|
37
37
|
|
|
38
38
|
```
|
|
39
|
-
@use "@coopdigital/styles/src/
|
|
39
|
+
@use "@coopdigital/styles/src/foundations.scss"
|
|
40
40
|
@use "@coopdigital/styles/src/components/Pill.scss"
|
|
41
41
|
```
|
|
42
42
|
|
|
@@ -24,7 +24,7 @@ export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
|
|
24
24
|
/** **(Optional)** Specify the Button size. */
|
|
25
25
|
size?: "sm" | "md" | "lg";
|
|
26
26
|
/** **(Optional)** Specify the Button variant. */
|
|
27
|
-
variant?: "
|
|
27
|
+
variant?: "solid" | "ghost" | "text";
|
|
28
28
|
}
|
|
29
29
|
export declare const Button: ({ as, children, className, href, isDisabled, isFullWidth, isLoading, loadingText, onClick, ref, size, variant, ...props }: ButtonProps) => JSX.Element;
|
|
30
30
|
export default Button;
|
|
@@ -3,9 +3,10 @@
|
|
|
3
3
|
import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
|
|
4
4
|
import clsx from 'clsx';
|
|
5
5
|
import React__default, { useState, useCallback } from 'react';
|
|
6
|
+
import { hasUserBg } from '../../utils/index.js';
|
|
6
7
|
import { LoadingIcon } from '../icons/LoadingIcon.js';
|
|
7
8
|
|
|
8
|
-
const Button = ({ as, children, className, href, isDisabled = false, isFullWidth = false, isLoading = false, loadingText = "Loading", onClick, ref, size = "md", variant = "
|
|
9
|
+
const Button = ({ as, children, className, href, isDisabled = false, isFullWidth = false, isLoading = false, loadingText = "Loading", onClick, ref, size = "md", variant = "solid", ...props }) => {
|
|
9
10
|
const element = as !== null && as !== void 0 ? as : (href ? "a" : "button");
|
|
10
11
|
const [isPending, setIsPending] = useState(false);
|
|
11
12
|
const handleClick = useCallback(async (event) => {
|
|
@@ -22,7 +23,7 @@ const Button = ({ as, children, className, href, isDisabled = false, isFullWidth
|
|
|
22
23
|
const componentProps = {
|
|
23
24
|
"aria-disabled": isDisabled ? true : undefined,
|
|
24
25
|
"aria-live": "assertive",
|
|
25
|
-
className: clsx(variant == "text" ? "coop-link" : "coop-button", className),
|
|
26
|
+
className: clsx(variant == "text" ? "coop-link" : "coop-button", !hasUserBg(className) && variant === "solid" && "bg-teal", className),
|
|
26
27
|
"data-loading": isLoading || isPending ? true : undefined,
|
|
27
28
|
"data-size": size.length && size !== "md" ? size : undefined,
|
|
28
29
|
"data-variant": variant !== "text" ? variant : undefined,
|
|
@@ -2,6 +2,7 @@ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
|
2
2
|
import clsx from 'clsx';
|
|
3
3
|
import React__default from 'react';
|
|
4
4
|
import { useSlots } from '../../hooks/useSlots.js';
|
|
5
|
+
import { hasUserBg } from '../../utils/index.js';
|
|
5
6
|
import { ChevronRightIcon } from '../icons/ChevronRightIcon.js';
|
|
6
7
|
import { Image } from '../Image/Image.js';
|
|
7
8
|
|
|
@@ -28,7 +29,7 @@ const Card = ({ chevron = false, children, className, href, hrefAs, imagePositio
|
|
|
28
29
|
const innerProps = { className: "coop-card--inner" };
|
|
29
30
|
const hasLinkWrapper = href && !slots.CardHeading;
|
|
30
31
|
const componentProps = {
|
|
31
|
-
className: clsx("coop-card", className),
|
|
32
|
+
className: clsx("coop-card", !hasUserBg(className) && "bg-white", className),
|
|
32
33
|
"data-image-pos": imagePosition,
|
|
33
34
|
"data-orientation": orientation !== "vertical" ? orientation : undefined,
|
|
34
35
|
...props,
|
|
@@ -130,7 +130,7 @@ const DatePicker = ({ className, closeOnSelect = true, dateFormat = "dd/MM/yyyy"
|
|
|
130
130
|
// eslint-disable-next-line jsx-a11y/no-autofocus
|
|
131
131
|
autoFocus: true, captionLayout: "dropdown",
|
|
132
132
|
//defaultMonth={defaultMonth}
|
|
133
|
-
disabled: disabledDates, endMonth: endDate, month: state.view, onMonthChange: setView, onSelect: updateValues, startMonth: startDate, ...calendarProps[mode] }), jsxs("div", { className: "coop-datepicker-actions", children: [jsxs(Button, { "aria-label": "Cancel", onClick: resetState, size: "sm",
|
|
133
|
+
disabled: disabledDates, endMonth: endDate, month: state.view, onMonthChange: setView, onSelect: updateValues, startMonth: startDate, ...calendarProps[mode] }), jsxs("div", { className: "coop-datepicker-actions", children: [jsxs(Button, { "aria-label": "Cancel", className: "bg-tint-grey", onClick: resetState, size: "sm", children: ["Clear ", jsx(CloseIcon, { stroke: "black", strokeWidth: 1 })] }), jsx(Popover.Close, { asChild: true, children: jsxs(Button, { "aria-label": "Accept", size: "sm", children: ["OK ", jsx(TickIcon, { stroke: "white", strokeWidth: 1 })] }) })] })] })] }), jsx("div", { "aria-live": "assertive", className: "sr-only coop-datepicker-status", role: "status", children: (_g = state.message) !== null && _g !== void 0 ? _g : footerMessage[mode] }), mode === "single" && jsx("input", { ...valueProps, value: (_j = (_h = state.single) === null || _h === void 0 ? void 0 : _h.field) !== null && _j !== void 0 ? _j : "" }), mode === "multiple" && jsx("input", { ...valueProps, value: (_l = (_k = state.multiple) === null || _k === void 0 ? void 0 : _k.field) !== null && _l !== void 0 ? _l : "" }), mode === "range" && (jsxs(Fragment, { children: [jsx("input", { ...baseValueProps, id: `${uid}_start`, name: `${name}_start`, value: (_p = (_o = (_m = state.range) === null || _m === void 0 ? void 0 : _m.field) === null || _o === void 0 ? void 0 : _o.from) !== null && _p !== void 0 ? _p : "" }), jsx("input", { ...baseValueProps, id: `${uid}_end`, name: `${name}_end`, value: (_s = (_r = (_q = state.range) === null || _q === void 0 ? void 0 : _q.field) === null || _r === void 0 ? void 0 : _r.to) !== null && _s !== void 0 ? _s : "" })] }))] }));
|
|
134
134
|
};
|
|
135
135
|
DatePicker.config = componentConfig;
|
|
136
136
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
2
|
import { clsx } from 'clsx';
|
|
3
3
|
import { useSlots } from '../../hooks/useSlots.js';
|
|
4
|
-
import { hasUserBg
|
|
4
|
+
import { hasUserBg } from '../../utils/index.js';
|
|
5
5
|
import 'react';
|
|
6
6
|
import { ChevronDownIcon } from '../icons/ChevronDownIcon.js';
|
|
7
7
|
|
|
@@ -15,9 +15,9 @@ const Expandable = ({ children, className, ref, ...props }) => {
|
|
|
15
15
|
className: clsx("coop-expandable", !hasUserBg(className) && "bg-tint-grey", className),
|
|
16
16
|
...props,
|
|
17
17
|
};
|
|
18
|
-
componentProps.style = {
|
|
19
|
-
|
|
20
|
-
}
|
|
18
|
+
// componentProps.style = {
|
|
19
|
+
// "--bg": `var(--color-${bgClassToColor(componentProps.className)})`,
|
|
20
|
+
// }
|
|
21
21
|
return (jsxs("details", { ...componentProps, ref: ref, children: [slots.ExpandableSummary, slots.ExpandableContent] }));
|
|
22
22
|
};
|
|
23
23
|
const ExpandableSummary = ({ children, className, ...props }) => {
|
|
@@ -22,7 +22,7 @@ const Pill = ({ as, children, className, href, ref, size = "md", ...props }) =>
|
|
|
22
22
|
return React__default.createElement(element, { ...componentProps, ref }, slots.PillBadge, slots.Children);
|
|
23
23
|
};
|
|
24
24
|
const PillBadge = ({ children, className }) => {
|
|
25
|
-
return (jsx("span", { className: clsx("coop-pill--badge", !hasUserBg(className) && "bg-
|
|
25
|
+
return (jsx("span", { className: clsx("coop-pill--badge", !hasUserBg(className) && "bg-red", className), children: children }));
|
|
26
26
|
};
|
|
27
27
|
PillBadge.config = { name: "PillBadge" };
|
|
28
28
|
Pill.Badge = PillBadge;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { FormHTMLAttributes, JSX, Ref } from "react";
|
|
2
2
|
import React from "react";
|
|
3
3
|
import { StandardSizes } from "../../types";
|
|
4
4
|
import { type ButtonProps } from "../Button";
|
|
5
|
-
|
|
5
|
+
import { TextInputProps } from "../TextInput";
|
|
6
|
+
export interface SearchboxProps extends FormHTMLAttributes<HTMLFormElement> {
|
|
6
7
|
/** **(Optional)** Specify a server endpoint to submit the form. Will be ignored if onSubmit is also set. */
|
|
7
8
|
action?: string;
|
|
8
9
|
/** **(Optional)** Specify props to forward to the Button element. Use `label` to set Button text. */
|
|
@@ -28,7 +29,22 @@ export interface SearchboxProps extends Omit<InputHTMLAttributes<HTMLInputElemen
|
|
|
28
29
|
/** **(Optional)** Specify the Searchbox size. */
|
|
29
30
|
size?: StandardSizes;
|
|
30
31
|
/** **(Optional)** Specify the Searchbox variant. */
|
|
31
|
-
variant?: "
|
|
32
|
+
variant?: "solid" | "ghost";
|
|
32
33
|
}
|
|
33
|
-
export declare const Searchbox:
|
|
34
|
+
export declare const Searchbox: {
|
|
35
|
+
({ action, children, className, id, label, labelVisible, onSubmit, ref, size, variant, ...props }: SearchboxProps): JSX.Element;
|
|
36
|
+
Button: {
|
|
37
|
+
({ children, ...props }: ButtonProps): import("react/jsx-runtime").JSX.Element;
|
|
38
|
+
config: {
|
|
39
|
+
name: string;
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
Input: {
|
|
43
|
+
(props: TextInputProps): import("react/jsx-runtime").JSX.Element;
|
|
44
|
+
config: {
|
|
45
|
+
isField: boolean;
|
|
46
|
+
name: string;
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
};
|
|
34
50
|
export default Searchbox;
|
|
@@ -1,21 +1,27 @@
|
|
|
1
1
|
|
|
2
2
|
"use client";
|
|
3
|
-
import {
|
|
3
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
4
4
|
import clsx from 'clsx';
|
|
5
5
|
import React__default, { useState, useCallback } from 'react';
|
|
6
6
|
import { useId } from '../../hooks/useId.js';
|
|
7
|
+
import { useSlots } from '../../hooks/useSlots.js';
|
|
8
|
+
import { hasUserBorder } from '../../utils/index.js';
|
|
7
9
|
import { Button } from '../Button/Button.js';
|
|
8
10
|
import { Field } from '../Field/Field.js';
|
|
9
|
-
import { Label } from '../FieldMarkers/Label.js';
|
|
10
11
|
import { SearchIcon } from '../icons/SearchIcon.js';
|
|
11
12
|
import { TextInput } from '../TextInput/TextInput.js';
|
|
12
13
|
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
const componentSlots = {
|
|
15
|
+
SearchboxButton: null,
|
|
16
|
+
SearchboxInput: null,
|
|
16
17
|
};
|
|
17
|
-
const
|
|
18
|
-
|
|
18
|
+
const defaultButtonText = React__default.createElement(SearchIcon, {
|
|
19
|
+
alt: "Search",
|
|
20
|
+
stroke: "currentColor",
|
|
21
|
+
strokeWidth: 2,
|
|
22
|
+
});
|
|
23
|
+
const Searchbox = ({ action, children, className, id, label, labelVisible = false, onSubmit, ref, size = "md", variant = "solid", ...props }) => {
|
|
24
|
+
const slots = useSlots(componentSlots, children);
|
|
19
25
|
const [isPending, setIsPending] = useState(false);
|
|
20
26
|
const uid = useId(id);
|
|
21
27
|
const handleSubmit = useCallback(async (event) => {
|
|
@@ -30,37 +36,45 @@ const Searchbox = ({ action, "aria-placeholder": ariaPlaceholder, autoCapitalize
|
|
|
30
36
|
setIsPending(false);
|
|
31
37
|
}
|
|
32
38
|
}, [onSubmit, isPending]);
|
|
39
|
+
let borderClass = "";
|
|
40
|
+
if (!hasUserBorder(className)) {
|
|
41
|
+
borderClass = variant === "ghost" ? "border-teal" : "border-grey";
|
|
42
|
+
}
|
|
33
43
|
const formProps = {
|
|
34
44
|
action: action !== null && action !== void 0 ? action : undefined,
|
|
35
|
-
className: clsx("coop-searchbox", className),
|
|
45
|
+
className: clsx("coop-searchbox", borderClass, className),
|
|
36
46
|
"data-size": size && size !== "md" ? size : undefined,
|
|
37
|
-
"data-variant": variant.length && variant !== "
|
|
38
|
-
id: uid,
|
|
47
|
+
"data-variant": variant.length && variant !== "solid" ? variant : undefined,
|
|
48
|
+
id: uid + "-form",
|
|
39
49
|
onSubmit: onSubmit ? handleSubmit : undefined,
|
|
50
|
+
...props,
|
|
40
51
|
};
|
|
41
|
-
|
|
42
|
-
className: button === null || button === void 0 ? void 0 : button.className,
|
|
52
|
+
slots.SearchboxButton = React__default.cloneElement(slots.SearchboxButton, {
|
|
43
53
|
isLoading: isPending,
|
|
44
|
-
loadingText: (_a = button === null || button === void 0 ? void 0 : button.loadingText) !== null && _a !== void 0 ? _a : "",
|
|
45
54
|
size,
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
autoCapitalize,
|
|
51
|
-
autoComplete,
|
|
52
|
-
id: uid + "--input",
|
|
53
|
-
name,
|
|
54
|
-
placeholder,
|
|
55
|
+
});
|
|
56
|
+
slots.SearchboxInput = React__default.cloneElement(slots.SearchboxInput, {
|
|
57
|
+
autoCapitalize: "off",
|
|
58
|
+
id: uid,
|
|
55
59
|
size,
|
|
56
|
-
|
|
57
|
-
...props,
|
|
58
|
-
};
|
|
60
|
+
});
|
|
59
61
|
const labelProps = {
|
|
60
|
-
htmlFor: uid
|
|
62
|
+
htmlFor: uid,
|
|
61
63
|
isVisible: labelVisible,
|
|
62
64
|
};
|
|
63
|
-
return (jsxs("form", { ...formProps, ref: ref, children: [label && jsx(Label, { ...labelProps, children: label }), jsxs("div", { className: "coop-searchbox--inner", children: [jsx(Field, { children:
|
|
65
|
+
return (jsxs("form", { ...formProps, ref: ref, children: [label && jsx(Field.Label, { ...labelProps, children: label }), jsxs("div", { className: clsx("coop-searchbox--inner"), children: [jsx(Field, { children: slots.SearchboxInput }), slots.SearchboxButton] })] }));
|
|
66
|
+
};
|
|
67
|
+
const SearchboxButton = ({ children, ...props }) => {
|
|
68
|
+
const buttonProps = {
|
|
69
|
+
children: children !== null && children !== void 0 ? children : defaultButtonText,
|
|
70
|
+
...props,
|
|
71
|
+
};
|
|
72
|
+
return jsx(Button, { ...buttonProps });
|
|
64
73
|
};
|
|
74
|
+
const SearchboxInput = (props) => jsx(TextInput, { ...props });
|
|
75
|
+
Searchbox.Button = SearchboxButton;
|
|
76
|
+
Searchbox.Input = SearchboxInput;
|
|
77
|
+
SearchboxButton.config = { name: "SearchboxButton" };
|
|
78
|
+
SearchboxInput.config = { isField: true, name: "SearchboxInput" };
|
|
65
79
|
|
|
66
80
|
export { Searchbox, Searchbox as default };
|
|
@@ -4,7 +4,7 @@ import { hasUserBg } from '../../utils/index.js';
|
|
|
4
4
|
|
|
5
5
|
const Squircle = ({ children, className, ref, size = "lg", ...props }) => {
|
|
6
6
|
const componentProps = {
|
|
7
|
-
className: clsx("coop-squircle", !hasUserBg(className) && "bg-offer
|
|
7
|
+
className: clsx("coop-squircle", !hasUserBg(className) && "bg-offer", className),
|
|
8
8
|
"data-size": size.length && size !== "lg" ? size : undefined,
|
|
9
9
|
...props,
|
|
10
10
|
};
|
package/dist/types/colors.d.ts
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
export type Darks = "dark-blue" | "dark-yellow" | "dark-green" | "dark-lilac" | "dark-orange" | "dark-pink" | "dark-purple" | "dark-red";
|
|
2
2
|
export type Tints = "tint-blue" | "tint-brown" | "tint-yellow" | "tint-green" | "tint-grey" | "tint-lilac" | "tint-orange" | "tint-pink" | "tint-purple" | "tint-red";
|
|
3
3
|
export type Lights = "light-blue" | "light-yellow" | "light-green" | "light-lilac" | "light-orange" | "light-pink" | "light-purple" | "light-red";
|
|
4
|
-
export type Greys = "dark-grey" | "mid-dark-grey" | "mid-grey" | "mid-light-grey" | "light-grey";
|
|
5
4
|
export type White = "white";
|
|
6
5
|
export type Black = "black";
|
|
7
6
|
export type None = "none";
|
|
8
7
|
export type BrandBlue = "brand-blue";
|
|
9
|
-
export type OfferRed = "offer
|
|
8
|
+
export type OfferRed = "offer";
|
|
10
9
|
export type Green = "green";
|
|
11
10
|
export type Blue = "blue";
|
package/dist/utils/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
export declare const bgPropToClass: (color: string, userClasses?: string) => string;
|
|
2
2
|
export declare const hasUserBg: (userClasses?: string) => boolean;
|
|
3
|
+
export declare const hasUserBorder: (userClasses?: string) => boolean;
|
|
3
4
|
export declare const bgClassToColor: (userClasses: string) => string | null;
|
package/dist/utils/index.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
const hasUserBg = (userClasses) => {
|
|
2
2
|
return typeof userClasses === "string" && (userClasses === null || userClasses === void 0 ? void 0 : userClasses.includes("bg-")) ? true : false;
|
|
3
3
|
};
|
|
4
|
+
const hasUserBorder = (userClasses) => {
|
|
5
|
+
return typeof userClasses === "string" && (userClasses === null || userClasses === void 0 ? void 0 : userClasses.includes("border-")) ? true : false;
|
|
6
|
+
};
|
|
4
7
|
const bgClassToColor = (userClasses) => {
|
|
5
8
|
var _a, _b;
|
|
6
9
|
return (_b = (_a = /bg-([^\s]+)/.exec(userClasses)) === null || _a === void 0 ? void 0 : _a[1]) !== null && _b !== void 0 ? _b : null;
|
|
@@ -12,4 +15,4 @@ const bgClassToColor = (userClasses) => {
|
|
|
12
15
|
// )
|
|
13
16
|
};
|
|
14
17
|
|
|
15
|
-
export { bgClassToColor, hasUserBg };
|
|
18
|
+
export { bgClassToColor, hasUserBg, hasUserBorder };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@coopdigital/react",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.54.0",
|
|
5
5
|
"private": false,
|
|
6
6
|
"publishConfig": {
|
|
7
7
|
"access": "public"
|
|
@@ -58,22 +58,22 @@
|
|
|
58
58
|
"license": "MIT",
|
|
59
59
|
"description": "React components for the Experience Library design system",
|
|
60
60
|
"devDependencies": {
|
|
61
|
-
"@axe-core/playwright": "^4.11.
|
|
61
|
+
"@axe-core/playwright": "^4.11.1",
|
|
62
62
|
"@playwright/test": "^1.58.1",
|
|
63
|
-
"@storybook/addon-a11y": "^10.2.
|
|
64
|
-
"@storybook/addon-docs": "^10.2.
|
|
65
|
-
"@storybook/addon-onboarding": "^10.2.
|
|
66
|
-
"@storybook/react-vite": "^10.2.
|
|
63
|
+
"@storybook/addon-a11y": "^10.2.7",
|
|
64
|
+
"@storybook/addon-docs": "^10.2.7",
|
|
65
|
+
"@storybook/addon-onboarding": "^10.2.7",
|
|
66
|
+
"@storybook/react-vite": "^10.2.7",
|
|
67
67
|
"@testing-library/jest-dom": "^6.9.1",
|
|
68
68
|
"@testing-library/react": "^16.3.2",
|
|
69
|
-
"@types/react": "^19.2.
|
|
69
|
+
"@types/react": "^19.2.13",
|
|
70
70
|
"@types/react-dom": "^19.2.3",
|
|
71
71
|
"react": "^19.2.4",
|
|
72
72
|
"react-dom": "^19.2.4",
|
|
73
73
|
"resize-observer-polyfill": "^1.5.1",
|
|
74
74
|
"serve": "^14.2.5",
|
|
75
|
-
"storybook": "^10.2.
|
|
76
|
-
"storybook-addon-tag-badges": "^3.0.
|
|
75
|
+
"storybook": "^10.2.7",
|
|
76
|
+
"storybook-addon-tag-badges": "^3.0.6"
|
|
77
77
|
},
|
|
78
78
|
"peerDependencies": {
|
|
79
79
|
"react": "^19.1.0",
|
|
@@ -83,10 +83,10 @@
|
|
|
83
83
|
"storybook": "$storybook"
|
|
84
84
|
},
|
|
85
85
|
"dependencies": {
|
|
86
|
-
"@coopdigital/styles": "^0.
|
|
86
|
+
"@coopdigital/styles": "^0.45.0",
|
|
87
87
|
"@radix-ui/react-popover": "^1.1.15",
|
|
88
88
|
"clsx": "^2.1.1",
|
|
89
89
|
"react-day-picker": "^9.12.0"
|
|
90
90
|
},
|
|
91
|
-
"gitHead": "
|
|
91
|
+
"gitHead": "d15d7ae0d231bf28986a18a9e097537802f845db"
|
|
92
92
|
}
|
|
@@ -5,6 +5,7 @@ import type { ButtonHTMLAttributes, ForwardRefExoticComponent, JSX, Ref } from "
|
|
|
5
5
|
import clsx from "clsx"
|
|
6
6
|
import React, { useCallback, useState } from "react"
|
|
7
7
|
|
|
8
|
+
import { hasUserBg } from "../../utils"
|
|
8
9
|
import { LoadingIcon } from "../icons"
|
|
9
10
|
|
|
10
11
|
export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
|
@@ -32,16 +33,7 @@ export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
|
|
32
33
|
/** **(Optional)** Specify the Button size. */
|
|
33
34
|
size?: "sm" | "md" | "lg"
|
|
34
35
|
/** **(Optional)** Specify the Button variant. */
|
|
35
|
-
variant?:
|
|
36
|
-
| "green"
|
|
37
|
-
| "blue"
|
|
38
|
-
| "white"
|
|
39
|
-
| "grey"
|
|
40
|
-
| "green-ghost"
|
|
41
|
-
| "blue-ghost"
|
|
42
|
-
| "white-ghost"
|
|
43
|
-
| "grey-ghost"
|
|
44
|
-
| "text"
|
|
36
|
+
variant?: "solid" | "ghost" | "text"
|
|
45
37
|
}
|
|
46
38
|
|
|
47
39
|
type OnClickHandler =
|
|
@@ -60,7 +52,7 @@ export const Button = ({
|
|
|
60
52
|
onClick,
|
|
61
53
|
ref,
|
|
62
54
|
size = "md",
|
|
63
|
-
variant = "
|
|
55
|
+
variant = "solid",
|
|
64
56
|
...props
|
|
65
57
|
}: ButtonProps): JSX.Element => {
|
|
66
58
|
const element: ButtonProps["as"] = as ?? (href ? "a" : "button")
|
|
@@ -85,7 +77,11 @@ export const Button = ({
|
|
|
85
77
|
const componentProps = {
|
|
86
78
|
"aria-disabled": isDisabled ? true : undefined,
|
|
87
79
|
"aria-live": "assertive" as keyof ButtonHTMLAttributes<HTMLButtonElement>["aria-live"],
|
|
88
|
-
className: clsx(
|
|
80
|
+
className: clsx(
|
|
81
|
+
variant == "text" ? "coop-link" : "coop-button",
|
|
82
|
+
!hasUserBg(className) && variant === "solid" && "bg-teal",
|
|
83
|
+
className
|
|
84
|
+
),
|
|
89
85
|
"data-loading": isLoading || isPending ? true : undefined,
|
|
90
86
|
"data-size": size.length && size !== "md" ? size : undefined,
|
|
91
87
|
"data-variant": variant !== "text" ? variant : undefined,
|
|
@@ -4,6 +4,7 @@ import clsx from "clsx"
|
|
|
4
4
|
import React from "react"
|
|
5
5
|
|
|
6
6
|
import { useSlots } from "../../hooks/useSlots"
|
|
7
|
+
import { hasUserBg } from "../../utils"
|
|
7
8
|
import { ChevronRightIcon } from "../icons/ChevronRightIcon"
|
|
8
9
|
import { Image, ImageProps } from "../Image"
|
|
9
10
|
|
|
@@ -99,7 +100,7 @@ export const Card = ({
|
|
|
99
100
|
const hasLinkWrapper = href && !slots.CardHeading
|
|
100
101
|
|
|
101
102
|
const componentProps = {
|
|
102
|
-
className: clsx("coop-card", className),
|
|
103
|
+
className: clsx("coop-card", !hasUserBg(className) && "bg-white", className),
|
|
103
104
|
"data-image-pos": imagePosition,
|
|
104
105
|
"data-orientation": orientation !== "vertical" ? orientation : undefined,
|
|
105
106
|
...props,
|
|
@@ -330,7 +330,7 @@ export const DatePicker = ({
|
|
|
330
330
|
{...calendarProps[mode]}
|
|
331
331
|
/>
|
|
332
332
|
<div className="coop-datepicker-actions">
|
|
333
|
-
<Button aria-label="Cancel" onClick={resetState} size="sm"
|
|
333
|
+
<Button aria-label="Cancel" className="bg-tint-grey" onClick={resetState} size="sm">
|
|
334
334
|
Clear <CloseIcon stroke="black" strokeWidth={1} />
|
|
335
335
|
</Button>
|
|
336
336
|
<Popover.Close asChild>
|
|
@@ -3,7 +3,7 @@ import type { DetailsHTMLAttributes, HTMLAttributes, JSX, Ref } from "react"
|
|
|
3
3
|
import { clsx } from "clsx"
|
|
4
4
|
|
|
5
5
|
import { useSlots } from "../../hooks/useSlots"
|
|
6
|
-
import {
|
|
6
|
+
import { hasUserBg } from "../../utils"
|
|
7
7
|
import { ChevronDownIcon } from "../icons"
|
|
8
8
|
|
|
9
9
|
export interface ExpandableProps extends DetailsHTMLAttributes<HTMLDetailsElement> {
|
|
@@ -47,9 +47,9 @@ export const Expandable = ({
|
|
|
47
47
|
...props,
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
componentProps.style = {
|
|
51
|
-
|
|
52
|
-
}
|
|
50
|
+
// componentProps.style = {
|
|
51
|
+
// "--bg": `var(--color-${bgClassToColor(componentProps.className)})`,
|
|
52
|
+
// }
|
|
53
53
|
|
|
54
54
|
return (
|
|
55
55
|
<details {...componentProps} ref={ref}>
|
|
@@ -62,7 +62,7 @@ export const Pill = ({
|
|
|
62
62
|
|
|
63
63
|
const PillBadge = ({ children, className }: PillBadgeProps) => {
|
|
64
64
|
return (
|
|
65
|
-
<span className={clsx("coop-pill--badge", !hasUserBg(className) && "bg-
|
|
65
|
+
<span className={clsx("coop-pill--badge", !hasUserBg(className) && "bg-red", className)}>
|
|
66
66
|
{children}
|
|
67
67
|
</span>
|
|
68
68
|
)
|
|
@@ -1,22 +1,20 @@
|
|
|
1
1
|
"use client"
|
|
2
2
|
|
|
3
|
-
import type {
|
|
3
|
+
import type { FormHTMLAttributes, JSX, Ref } from "react"
|
|
4
4
|
|
|
5
5
|
import clsx from "clsx"
|
|
6
6
|
import React, { useCallback, useState } from "react"
|
|
7
7
|
|
|
8
8
|
import { useId } from "../../hooks/useId"
|
|
9
|
+
import { useSlots } from "../../hooks/useSlots"
|
|
9
10
|
import { StandardSizes } from "../../types"
|
|
11
|
+
import { hasUserBorder } from "../../utils"
|
|
10
12
|
import { Button, type ButtonProps } from "../Button"
|
|
11
13
|
import Field from "../Field"
|
|
12
|
-
import { Label as FieldLabel } from "../FieldMarkers/Label"
|
|
13
14
|
import { SearchIcon } from "../icons"
|
|
14
15
|
import TextInput, { TextInputProps } from "../TextInput"
|
|
15
16
|
|
|
16
|
-
export interface SearchboxProps extends
|
|
17
|
-
InputHTMLAttributes<HTMLInputElement>,
|
|
18
|
-
"size" | "type"
|
|
19
|
-
> {
|
|
17
|
+
export interface SearchboxProps extends FormHTMLAttributes<HTMLFormElement> {
|
|
20
18
|
/** **(Optional)** Specify a server endpoint to submit the form. Will be ignored if onSubmit is also set. */
|
|
21
19
|
action?: string
|
|
22
20
|
/** **(Optional)** Specify props to forward to the Button element. Use `label` to set Button text. */
|
|
@@ -40,46 +38,40 @@ export interface SearchboxProps extends Omit<
|
|
|
40
38
|
/** **(Optional)** Specify the Searchbox size. */
|
|
41
39
|
size?: StandardSizes
|
|
42
40
|
/** **(Optional)** Specify the Searchbox variant. */
|
|
43
|
-
variant?:
|
|
44
|
-
| "green"
|
|
45
|
-
| "blue"
|
|
46
|
-
| "white"
|
|
47
|
-
| "grey"
|
|
48
|
-
| "green-ghost"
|
|
49
|
-
| "blue-ghost"
|
|
50
|
-
| "white-ghost"
|
|
51
|
-
| "grey-ghost"
|
|
41
|
+
variant?: "solid" | "ghost"
|
|
52
42
|
}
|
|
53
43
|
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
44
|
+
const componentSlots = {
|
|
45
|
+
SearchboxButton: null,
|
|
46
|
+
SearchboxInput: null,
|
|
57
47
|
}
|
|
58
48
|
|
|
49
|
+
const defaultButtonText = React.createElement(SearchIcon, {
|
|
50
|
+
alt: "Search",
|
|
51
|
+
stroke: "currentColor",
|
|
52
|
+
strokeWidth: 2,
|
|
53
|
+
})
|
|
54
|
+
|
|
59
55
|
export const Searchbox = ({
|
|
60
56
|
action,
|
|
61
|
-
|
|
62
|
-
autoCapitalize = "off",
|
|
63
|
-
autoComplete = "off",
|
|
64
|
-
button = defaultButtonProps,
|
|
57
|
+
children,
|
|
65
58
|
className,
|
|
66
|
-
|
|
67
59
|
id,
|
|
68
60
|
label,
|
|
69
61
|
labelVisible = false,
|
|
70
|
-
name = "query",
|
|
71
62
|
onSubmit,
|
|
72
|
-
placeholder,
|
|
73
63
|
ref,
|
|
74
64
|
size = "md",
|
|
75
|
-
variant = "
|
|
65
|
+
variant = "solid",
|
|
76
66
|
...props
|
|
77
67
|
}: SearchboxProps): JSX.Element => {
|
|
68
|
+
const slots = useSlots(componentSlots, children)
|
|
69
|
+
|
|
78
70
|
const [isPending, setIsPending] = useState(false)
|
|
79
71
|
const uid = useId(id)
|
|
80
72
|
|
|
81
73
|
const handleSubmit = useCallback(
|
|
82
|
-
async (event: React.
|
|
74
|
+
async (event: React.SubmitEvent<HTMLFormElement>) => {
|
|
83
75
|
event.preventDefault()
|
|
84
76
|
|
|
85
77
|
if (isPending || !onSubmit) return
|
|
@@ -95,51 +87,69 @@ export const Searchbox = ({
|
|
|
95
87
|
[onSubmit, isPending]
|
|
96
88
|
)
|
|
97
89
|
|
|
90
|
+
let borderClass = ""
|
|
91
|
+
if (!hasUserBorder(className)) {
|
|
92
|
+
borderClass = variant === "ghost" ? "border-teal" : "border-grey"
|
|
93
|
+
}
|
|
94
|
+
|
|
98
95
|
const formProps = {
|
|
99
96
|
action: action ?? undefined,
|
|
100
|
-
className: clsx("coop-searchbox", className),
|
|
97
|
+
className: clsx("coop-searchbox", borderClass, className),
|
|
101
98
|
"data-size": size && size !== "md" ? size : undefined,
|
|
102
|
-
"data-variant": variant.length && variant !== "
|
|
103
|
-
id: uid,
|
|
99
|
+
"data-variant": variant.length && variant !== "solid" ? variant : undefined,
|
|
100
|
+
id: uid + "-form",
|
|
104
101
|
onSubmit: onSubmit ? handleSubmit : undefined,
|
|
102
|
+
...props,
|
|
105
103
|
}
|
|
106
104
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
105
|
+
slots.SearchboxButton = React.cloneElement(
|
|
106
|
+
slots.SearchboxButton as React.ReactElement<ButtonProps>,
|
|
107
|
+
{
|
|
108
|
+
isLoading: isPending,
|
|
109
|
+
size,
|
|
110
|
+
}
|
|
111
|
+
)
|
|
114
112
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
type: "search",
|
|
124
|
-
...props,
|
|
125
|
-
}
|
|
113
|
+
slots.SearchboxInput = React.cloneElement(
|
|
114
|
+
slots.SearchboxInput as React.ReactElement<TextInputProps>,
|
|
115
|
+
{
|
|
116
|
+
autoCapitalize: "off",
|
|
117
|
+
id: uid,
|
|
118
|
+
size,
|
|
119
|
+
}
|
|
120
|
+
)
|
|
126
121
|
|
|
127
122
|
const labelProps = {
|
|
128
|
-
htmlFor: uid
|
|
123
|
+
htmlFor: uid,
|
|
129
124
|
isVisible: labelVisible,
|
|
130
125
|
}
|
|
131
126
|
|
|
132
127
|
return (
|
|
133
128
|
<form {...formProps} ref={ref}>
|
|
134
|
-
{label && <
|
|
135
|
-
<div className="coop-searchbox--inner">
|
|
136
|
-
<Field>
|
|
137
|
-
|
|
138
|
-
</Field>
|
|
139
|
-
<Button {...buttonProps}>{button.label}</Button>
|
|
129
|
+
{label && <Field.Label {...labelProps}>{label}</Field.Label>}
|
|
130
|
+
<div className={clsx("coop-searchbox--inner")}>
|
|
131
|
+
<Field>{slots.SearchboxInput}</Field>
|
|
132
|
+
{slots.SearchboxButton}
|
|
140
133
|
</div>
|
|
141
134
|
</form>
|
|
142
135
|
)
|
|
143
136
|
}
|
|
144
137
|
|
|
138
|
+
const SearchboxButton = ({ children, ...props }: ButtonProps) => {
|
|
139
|
+
const buttonProps = {
|
|
140
|
+
children: children ?? defaultButtonText,
|
|
141
|
+
...props,
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return <Button {...buttonProps} />
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const SearchboxInput = (props: TextInputProps) => <TextInput {...props} />
|
|
148
|
+
|
|
149
|
+
Searchbox.Button = SearchboxButton
|
|
150
|
+
Searchbox.Input = SearchboxInput
|
|
151
|
+
|
|
152
|
+
SearchboxButton.config = { name: "SearchboxButton" }
|
|
153
|
+
SearchboxInput.config = { isField: true, name: "SearchboxInput" }
|
|
154
|
+
|
|
145
155
|
export default Searchbox
|
|
@@ -22,7 +22,7 @@ export const Squircle = ({
|
|
|
22
22
|
...props
|
|
23
23
|
}: SquircleProps): JSX.Element => {
|
|
24
24
|
const componentProps = {
|
|
25
|
-
className: clsx("coop-squircle", !hasUserBg(className) && "bg-offer
|
|
25
|
+
className: clsx("coop-squircle", !hasUserBg(className) && "bg-offer", className),
|
|
26
26
|
"data-size": size.length && size !== "lg" ? size : undefined,
|
|
27
27
|
...props,
|
|
28
28
|
}
|
package/src/types/colors.ts
CHANGED
|
@@ -30,12 +30,10 @@ export type Lights =
|
|
|
30
30
|
| "light-purple"
|
|
31
31
|
| "light-red"
|
|
32
32
|
|
|
33
|
-
export type Greys = "dark-grey" | "mid-dark-grey" | "mid-grey" | "mid-light-grey" | "light-grey"
|
|
34
|
-
|
|
35
33
|
export type White = "white"
|
|
36
34
|
export type Black = "black"
|
|
37
35
|
export type None = "none"
|
|
38
36
|
export type BrandBlue = "brand-blue"
|
|
39
|
-
export type OfferRed = "offer
|
|
37
|
+
export type OfferRed = "offer"
|
|
40
38
|
export type Green = "green"
|
|
41
39
|
export type Blue = "blue"
|
package/src/utils/index.ts
CHANGED
|
@@ -5,6 +5,10 @@ export const hasUserBg = (userClasses?: string) => {
|
|
|
5
5
|
return typeof userClasses === "string" && userClasses?.includes("bg-") ? true : false
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
+
export const hasUserBorder = (userClasses?: string) => {
|
|
9
|
+
return typeof userClasses === "string" && userClasses?.includes("border-") ? true : false
|
|
10
|
+
}
|
|
11
|
+
|
|
8
12
|
export const bgClassToColor = (userClasses: string) => {
|
|
9
13
|
return /bg-([^\s]+)/.exec(userClasses)?.[1] ?? null
|
|
10
14
|
|