@coopdigital/react 0.8.2 → 0.10.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/dist/components/Button/Button.d.ts +10 -5
- package/dist/components/Button/Button.js +22 -5
- package/dist/components/Card/Card.d.ts +38 -0
- package/dist/components/Card/Card.js +29 -0
- package/dist/components/Card/index.d.ts +4 -0
- package/dist/components/Image/Image.d.ts +3 -0
- package/dist/components/Image/Image.js +3 -0
- package/dist/components/Pill/Pill.d.ts +3 -0
- package/dist/components/Pill/Pill.js +3 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/node_modules/tslib/tslib.es6.js +11 -1
- package/package.json +3 -3
- package/src/components/Button/Button.tsx +65 -9
- package/src/components/Card/Card.tsx +118 -0
- package/src/components/Card/index.ts +5 -0
- package/src/components/Image/Image.tsx +3 -0
- package/src/components/Pill/Pill.tsx +3 -0
- package/src/index.ts +1 -1
- package/dist/components/EditorialCard/EditorialCard.d.ts +0 -23
- package/dist/components/EditorialCard/EditorialCard.js +0 -27
- package/dist/components/EditorialCard/index.d.ts +0 -4
- package/src/components/EditorialCard/EditorialCard.tsx +0 -80
- package/src/components/EditorialCard/index.ts +0 -5
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
1
|
+
import type { AnchorHTMLAttributes, ButtonHTMLAttributes, ForwardRefExoticComponent, JSX } from "react";
|
|
2
|
+
import React from "react";
|
|
3
|
+
export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
|
3
4
|
/** **(Optional)** Specifies a custom aria-label. */
|
|
4
5
|
ariaLabel?: string;
|
|
5
6
|
/** **(Optional)** Specifies the custom element to override default `a` or `button`. */
|
|
@@ -10,19 +11,23 @@ export interface ButtonProps extends DetailedHTMLProps<ButtonHTMLAttributes<HTML
|
|
|
10
11
|
className?: string;
|
|
11
12
|
/** **(Optional)** Specifies the URL that the Button component will link to when clicked. */
|
|
12
13
|
href?: string;
|
|
13
|
-
/** **(Optional)** Specifies whether the Button should
|
|
14
|
+
/** **(Optional)** Specifies whether the Button should be disabled. This will not actually disable the button, just make it look disabled. Disabled buttons can be bad UX. */
|
|
14
15
|
isDisabled?: boolean;
|
|
15
16
|
/** **(Optional)** Specifies whether the Button should be full width. */
|
|
16
17
|
isFullWidth?: boolean;
|
|
17
18
|
/** **(Optional)** Specifies whether the Button is loading. */
|
|
18
19
|
isLoading?: boolean;
|
|
20
|
+
/** **(Optional)** Specifies loading text to show when the Button is in a pending state */
|
|
21
|
+
loadingText?: string;
|
|
22
|
+
/** **(Optional)** Callback to run when the button is pressed. If this is an async function, it will be awaited and the button will be in a pending state until the promise is resolved. */
|
|
23
|
+
onClick?: (event: React.MouseEvent<HTMLButtonElement | HTMLAnchorElement>) => void;
|
|
19
24
|
/** **(Optional)** Specifies the Button size. */
|
|
20
25
|
size?: "sm" | "md" | "lg" | "xl";
|
|
21
26
|
/** **(Optional)** Specifies the Button variant. */
|
|
22
|
-
variant?: "primary" | "secondary" | "white" | "grey" | "text";
|
|
27
|
+
variant?: "primary" | "secondary" | "white" | "grey" | "ghost" | "text";
|
|
23
28
|
}
|
|
24
29
|
/**
|
|
25
30
|
* The Button component is an interactive element that people can use to take an action.
|
|
26
31
|
*/
|
|
27
|
-
export declare const Button: ({ ariaLabel, as, children, className, href, isDisabled, isFullWidth, isLoading, size, variant, ...props }: ButtonProps) => JSX.Element;
|
|
32
|
+
export declare const Button: ({ ariaLabel, as, children, className, href, isDisabled, isFullWidth, isLoading, loadingText, onClick, size, variant, ...props }: ButtonProps) => JSX.Element;
|
|
28
33
|
export default Button;
|
|
@@ -1,17 +1,34 @@
|
|
|
1
|
-
import { __rest } from '../../node_modules/tslib/tslib.es6.js';
|
|
2
|
-
import
|
|
1
|
+
import { __rest, __awaiter } from '../../node_modules/tslib/tslib.es6.js';
|
|
2
|
+
import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
|
|
3
|
+
import React, { useState, useCallback } from 'react';
|
|
3
4
|
|
|
5
|
+
const LoadingIcon = () => {
|
|
6
|
+
return (jsx("svg", { fill: "none", stroke: "currentColor", strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2", viewBox: "0 0 24 24", xmlns: "http://www.w3.org/2000/svg", children: jsx("path", { d: "M21 12a9 9 0 1 1-6.219-8.56" }) }));
|
|
7
|
+
};
|
|
4
8
|
/**
|
|
5
9
|
* The Button component is an interactive element that people can use to take an action.
|
|
6
10
|
*/
|
|
7
11
|
const Button = (_a) => {
|
|
8
|
-
var { ariaLabel, as, children, className = "", href, isDisabled = false, isFullWidth = false, isLoading = false, size = "md", variant = "primary" } = _a, props = __rest(_a, ["ariaLabel", "as", "children", "className", "href", "isDisabled", "isFullWidth", "isLoading", "size", "variant"]);
|
|
12
|
+
var { ariaLabel, as, children, className = "", href, isDisabled = false, isFullWidth = false, isLoading = false, loadingText = "Loading", onClick, size = "md", variant = "primary" } = _a, props = __rest(_a, ["ariaLabel", "as", "children", "className", "href", "isDisabled", "isFullWidth", "isLoading", "loadingText", "onClick", "size", "variant"]);
|
|
9
13
|
let element = href ? "a" : "button";
|
|
10
14
|
if (as) {
|
|
11
15
|
element = as;
|
|
12
16
|
}
|
|
13
|
-
const
|
|
14
|
-
|
|
17
|
+
const [isPending, setIsPending] = useState(false);
|
|
18
|
+
const handleClick = useCallback((event) => __awaiter(void 0, void 0, void 0, function* () {
|
|
19
|
+
if (isPending || !onClick)
|
|
20
|
+
return;
|
|
21
|
+
setIsPending(true);
|
|
22
|
+
try {
|
|
23
|
+
yield Promise.resolve(onClick(event));
|
|
24
|
+
}
|
|
25
|
+
finally {
|
|
26
|
+
setIsPending(false);
|
|
27
|
+
}
|
|
28
|
+
}), [onClick, isPending]);
|
|
29
|
+
const componentProps = Object.assign({ "aria-disabled": isDisabled ? true : undefined, "aria-label": ariaLabel, "aria-live": "assertive", className: `${variant == "text" ? "coop-link" : "coop-button"} ${className}`, "data-loading": isLoading || isPending ? true : undefined, "data-size": size.length && size != "md" ? size : undefined, "data-variant": variant !== "text" ? variant : undefined, "data-width": isFullWidth ? "full" : undefined, href, onClick: handleClick }, props);
|
|
30
|
+
const finalChildren = isPending || isLoading ? (jsxs(Fragment, { children: [loadingText, jsx(LoadingIcon, {})] })) : (children);
|
|
31
|
+
return React.createElement(element, Object.assign({}, componentProps), finalChildren);
|
|
15
32
|
};
|
|
16
33
|
|
|
17
34
|
export { Button, Button as default };
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { AnchorHTMLAttributes, ForwardRefExoticComponent, HTMLAttributes, JSX } from "react";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { ImageProps } from "../Image";
|
|
4
|
+
export interface CardProps extends HTMLAttributes<HTMLDivElement> {
|
|
5
|
+
/** **(Optional)** Specifies the custom element to override default `a` */
|
|
6
|
+
as?: React.FC<AnchorHTMLAttributes<HTMLElement>> | ForwardRefExoticComponent<any> | string;
|
|
7
|
+
/** **(Optional)** Specifies the background color of the card. */
|
|
8
|
+
background?: "purple" | "pink" | "green" | "orange" | "red" | "yellow" | "lilac" | "blue";
|
|
9
|
+
/** **(Optional)** Represents the content inside the badge. */
|
|
10
|
+
badge?: React.ReactNode;
|
|
11
|
+
/** **(Optional)** Specify badge position relative to top right corner. */
|
|
12
|
+
badgePosition?: "inset" | "popout";
|
|
13
|
+
/** **(Optional)** Specifies if chevron will be visible. */
|
|
14
|
+
chevron?: boolean;
|
|
15
|
+
/** **(Optional)** Represents the content inside the Card component. It can be any valid JSX or string. */
|
|
16
|
+
children?: React.ReactNode;
|
|
17
|
+
/** **(Optional)** Specifies the heading level of the card's title. */
|
|
18
|
+
headingLevel?: "h2" | "h3" | "h4" | "h5" | "h6";
|
|
19
|
+
/** **(Optional)** Specifies the URL that the Card component will link to when clicked. */
|
|
20
|
+
href?: string;
|
|
21
|
+
/** Specifies the image URL and alt text of the Card */
|
|
22
|
+
image: ImageProps;
|
|
23
|
+
/** **(Optional)** Specifies the position of the image in the Card */
|
|
24
|
+
imagePosition?: "left" | "right";
|
|
25
|
+
/** **(Optional)** Specifies the label of the Card */
|
|
26
|
+
label?: string;
|
|
27
|
+
/** **(Optional)** Specifies the label background of the Card */
|
|
28
|
+
labelBackground?: "yellow" | "green" | "pink" | "purple" | "orange" | "red" | "lilac" | "blue";
|
|
29
|
+
/** **(Optional)** Specifies the layout of the Card */
|
|
30
|
+
layout?: "vertical" | "horizontal";
|
|
31
|
+
/** Specifies the title of the Card */
|
|
32
|
+
title: string;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* A Card lets you highlight and link to more in-depth content on another page. The component typically includes a title, image, and an optional description.
|
|
36
|
+
*/
|
|
37
|
+
export declare const Card: ({ as, background, badge, badgePosition, chevron, children, headingLevel, href, image, imagePosition, label, labelBackground, layout, title, ...props }: CardProps) => JSX.Element;
|
|
38
|
+
export default Card;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { __rest } from '../../node_modules/tslib/tslib.es6.js';
|
|
2
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { Image } from '../Image/Image.js';
|
|
5
|
+
|
|
6
|
+
function getCardLinkElement(as, href) {
|
|
7
|
+
let element = href ? "a" : "div";
|
|
8
|
+
if (as) {
|
|
9
|
+
element = as;
|
|
10
|
+
}
|
|
11
|
+
return {
|
|
12
|
+
element,
|
|
13
|
+
props: {
|
|
14
|
+
href,
|
|
15
|
+
},
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* A Card lets you highlight and link to more in-depth content on another page. The component typically includes a title, image, and an optional description.
|
|
20
|
+
*/
|
|
21
|
+
const Card = (_a) => {
|
|
22
|
+
var { as, background, badge, badgePosition = "inset", chevron = false, children, headingLevel = "h3", href, image, imagePosition = "left", label = "", labelBackground, layout = "vertical", title } = _a, props = __rest(_a, ["as", "background", "badge", "badgePosition", "chevron", "children", "headingLevel", "href", "image", "imagePosition", "label", "labelBackground", "layout", "title"]);
|
|
23
|
+
const linkElement = getCardLinkElement(as, href);
|
|
24
|
+
const imageProps = Object.assign({ crop: "wide" }, image);
|
|
25
|
+
const componentProps = Object.assign({ className: "coop-card", "data-badge-pos": badgePosition, "data-bg": background, "data-image-pos": imagePosition, "data-label-bg": labelBackground, "data-layout": layout }, props);
|
|
26
|
+
return (jsxs("article", Object.assign({}, componentProps, { children: [image && jsx(Image, Object.assign({}, imageProps)), badge && jsx("div", { className: "coop-card--badge", children: badge }), jsxs("div", { className: "coop-card--inner", children: [jsxs("div", { className: "coop-card--content", children: [label && jsx("span", { className: "coop-card--label", children: label }), React.createElement(linkElement.element, linkElement.props, React.createElement(headingLevel, {}, title)), children] }), chevron && (jsx("span", { "aria-hidden": "true", className: "coop-card--icon", role: "presentation", children: jsx("svg", { viewBox: "0 0 16 29", children: jsx("path", { d: "M2 28.1a1.6 1.6 0 0 1-1.2-2.7l11-10.9-11-11A1.6 1.6 0 1 1 3 1.5l12 12a1.6 1.6 0 0 1 0 2.2l-12 12c-.3.4-.7.5-1 .5z" }) }) }))] })] })));
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export { Card, Card as default };
|
|
@@ -11,5 +11,8 @@ export interface ImageProps {
|
|
|
11
11
|
/** **(Optional)** Specifies width of the Image */
|
|
12
12
|
width?: string;
|
|
13
13
|
}
|
|
14
|
+
/**
|
|
15
|
+
* A wrapper around the image HTML tag, to help implement best practices like lazy loading, and assist with cropping and sizing images.
|
|
16
|
+
*/
|
|
14
17
|
export declare const Image: ({ alt, crop, height, src, width }: ImageProps) => JSX.Element;
|
|
15
18
|
export default Image;
|
|
@@ -20,6 +20,9 @@ const getContentfulParams = (src, width, height) => {
|
|
|
20
20
|
width: (_b = url.searchParams.get("w")) !== null && _b !== void 0 ? _b : width,
|
|
21
21
|
};
|
|
22
22
|
};
|
|
23
|
+
/**
|
|
24
|
+
* A wrapper around the image HTML tag, to help implement best practices like lazy loading, and assist with cropping and sizing images.
|
|
25
|
+
*/
|
|
23
26
|
const Image = ({ alt, crop, height, src, width = "640" }) => {
|
|
24
27
|
let params = { height, src, width };
|
|
25
28
|
if (src.includes("images.ctfassets.net")) {
|
|
@@ -20,5 +20,8 @@ export interface PillProps {
|
|
|
20
20
|
/** **(Optional)** Specifies what should be the Pill size. */
|
|
21
21
|
size?: "sm" | "md" | "lg" | "xl";
|
|
22
22
|
}
|
|
23
|
+
/**
|
|
24
|
+
* The Pill component is a small, rounded label used to link and highlight information such as categories or articles. It can be customised with different content and styles to suit various use cases.
|
|
25
|
+
*/
|
|
23
26
|
export declare const Pill: ({ ariaLabel, as, badge, badgeColor, children, className, href, pillColor, size, }: PillProps) => JSX.Element;
|
|
24
27
|
export default Pill;
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* The Pill component is a small, rounded label used to link and highlight information such as categories or articles. It can be customised with different content and styles to suit various use cases.
|
|
5
|
+
*/
|
|
3
6
|
const Pill = ({ ariaLabel, as, badge, badgeColor = "red", children, className = "", href, pillColor = "blue", size = "md", }) => {
|
|
4
7
|
let element = href ? "a" : "span";
|
|
5
8
|
if (as) {
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export * from "./components/AlertBanner";
|
|
2
2
|
export * from "./components/Button";
|
|
3
|
-
export * from "./components/
|
|
3
|
+
export * from "./components/Card";
|
|
4
4
|
export * from "./components/Image";
|
|
5
5
|
export * from "./components/Pill";
|
|
6
6
|
export * from "./components/SkipNav";
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { AlertBanner } from './components/AlertBanner/AlertBanner.js';
|
|
2
2
|
export { Button } from './components/Button/Button.js';
|
|
3
|
-
export {
|
|
3
|
+
export { Card } from './components/Card/Card.js';
|
|
4
4
|
export { Image } from './components/Image/Image.js';
|
|
5
5
|
export { Pill } from './components/Pill/Pill.js';
|
|
6
6
|
export { SkipNav } from './components/SkipNav/SkipNav.js';
|
|
@@ -27,9 +27,19 @@ function __rest(s, e) {
|
|
|
27
27
|
return t;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
function __awaiter(thisArg, _arguments, P, generator) {
|
|
31
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
32
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
33
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
34
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
35
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
36
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
30
40
|
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
31
41
|
var e = new Error(message);
|
|
32
42
|
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
33
43
|
};
|
|
34
44
|
|
|
35
|
-
export { __rest };
|
|
45
|
+
export { __awaiter, __rest };
|
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.10.0",
|
|
5
5
|
"private": false,
|
|
6
6
|
"publishConfig": {
|
|
7
7
|
"access": "public"
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"description": "",
|
|
45
45
|
"devDependencies": {
|
|
46
46
|
"@axe-core/playwright": "^4.10.1",
|
|
47
|
-
"@coopdigital/styles": "^0.
|
|
47
|
+
"@coopdigital/styles": "^0.10.0",
|
|
48
48
|
"@playwright/test": "^1.51.1",
|
|
49
49
|
"@storybook/addon-a11y": "^8.6.11",
|
|
50
50
|
"@storybook/addon-essentials": "^8.6.11",
|
|
@@ -67,5 +67,5 @@
|
|
|
67
67
|
"react": "^19.0.0",
|
|
68
68
|
"react-dom": "^19.0.0"
|
|
69
69
|
},
|
|
70
|
-
"gitHead": "
|
|
70
|
+
"gitHead": "a5c63ef95571bbc32cf93d44b561f7050866dbce"
|
|
71
71
|
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import
|
|
1
|
+
import type {
|
|
2
2
|
AnchorHTMLAttributes,
|
|
3
3
|
ButtonHTMLAttributes,
|
|
4
|
-
DetailedHTMLProps,
|
|
5
4
|
ForwardRefExoticComponent,
|
|
6
5
|
JSX,
|
|
7
6
|
} from "react"
|
|
8
7
|
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
import React, { useCallback, useState } from "react"
|
|
9
|
+
|
|
10
|
+
export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
|
11
11
|
/** **(Optional)** Specifies a custom aria-label. */
|
|
12
12
|
ariaLabel?: string
|
|
13
13
|
/** **(Optional)** Specifies the custom element to override default `a` or `button`. */
|
|
@@ -19,16 +19,40 @@ export interface ButtonProps
|
|
|
19
19
|
className?: string
|
|
20
20
|
/** **(Optional)** Specifies the URL that the Button component will link to when clicked. */
|
|
21
21
|
href?: string
|
|
22
|
-
/** **(Optional)** Specifies whether the Button should
|
|
22
|
+
/** **(Optional)** Specifies whether the Button should be disabled. This will not actually disable the button, just make it look disabled. Disabled buttons can be bad UX. */
|
|
23
23
|
isDisabled?: boolean
|
|
24
24
|
/** **(Optional)** Specifies whether the Button should be full width. */
|
|
25
25
|
isFullWidth?: boolean
|
|
26
26
|
/** **(Optional)** Specifies whether the Button is loading. */
|
|
27
27
|
isLoading?: boolean
|
|
28
|
+
/** **(Optional)** Specifies loading text to show when the Button is in a pending state */
|
|
29
|
+
loadingText?: string
|
|
30
|
+
/** **(Optional)** Callback to run when the button is pressed. If this is an async function, it will be awaited and the button will be in a pending state until the promise is resolved. */
|
|
31
|
+
onClick?: (event: React.MouseEvent<HTMLButtonElement | HTMLAnchorElement>) => void
|
|
28
32
|
/** **(Optional)** Specifies the Button size. */
|
|
29
33
|
size?: "sm" | "md" | "lg" | "xl"
|
|
30
34
|
/** **(Optional)** Specifies the Button variant. */
|
|
31
|
-
variant?: "primary" | "secondary" | "white" | "grey" | "text"
|
|
35
|
+
variant?: "primary" | "secondary" | "white" | "grey" | "ghost" | "text"
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
type OnClickHandler =
|
|
39
|
+
| React.MouseEventHandler<HTMLButtonElement | HTMLAnchorElement>
|
|
40
|
+
| ((event: React.MouseEvent<HTMLButtonElement | HTMLAnchorElement>) => Promise<void>)
|
|
41
|
+
|
|
42
|
+
const LoadingIcon = () => {
|
|
43
|
+
return (
|
|
44
|
+
<svg
|
|
45
|
+
fill="none"
|
|
46
|
+
stroke="currentColor"
|
|
47
|
+
strokeLinecap="round"
|
|
48
|
+
strokeLinejoin="round"
|
|
49
|
+
strokeWidth="2"
|
|
50
|
+
viewBox="0 0 24 24"
|
|
51
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
52
|
+
>
|
|
53
|
+
<path d="M21 12a9 9 0 1 1-6.219-8.56" />
|
|
54
|
+
</svg>
|
|
55
|
+
)
|
|
32
56
|
}
|
|
33
57
|
|
|
34
58
|
/**
|
|
@@ -43,6 +67,8 @@ export const Button = ({
|
|
|
43
67
|
isDisabled = false,
|
|
44
68
|
isFullWidth = false,
|
|
45
69
|
isLoading = false,
|
|
70
|
+
loadingText = "Loading",
|
|
71
|
+
onClick,
|
|
46
72
|
size = "md",
|
|
47
73
|
variant = "primary",
|
|
48
74
|
...props
|
|
@@ -52,17 +78,47 @@ export const Button = ({
|
|
|
52
78
|
element = as
|
|
53
79
|
}
|
|
54
80
|
|
|
81
|
+
const [isPending, setIsPending] = useState(false)
|
|
82
|
+
|
|
83
|
+
const handleClick: OnClickHandler = useCallback(
|
|
84
|
+
async (event) => {
|
|
85
|
+
if (isPending || !onClick) return
|
|
86
|
+
|
|
87
|
+
setIsPending(true)
|
|
88
|
+
|
|
89
|
+
try {
|
|
90
|
+
await Promise.resolve(onClick(event as React.MouseEvent<HTMLButtonElement>))
|
|
91
|
+
} finally {
|
|
92
|
+
setIsPending(false)
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
[onClick, isPending]
|
|
96
|
+
)
|
|
97
|
+
|
|
55
98
|
const componentProps = {
|
|
56
99
|
"aria-disabled": isDisabled ? true : undefined,
|
|
57
100
|
"aria-label": ariaLabel,
|
|
58
|
-
|
|
59
|
-
"
|
|
101
|
+
"aria-live": "assertive" as keyof ButtonHTMLAttributes<HTMLButtonElement>["aria-live"],
|
|
102
|
+
className: `${variant == "text" ? "coop-link" : "coop-button"} ${className}`,
|
|
103
|
+
"data-loading": isLoading || isPending ? true : undefined,
|
|
60
104
|
"data-size": size.length && size != "md" ? size : undefined,
|
|
61
105
|
"data-variant": variant !== "text" ? variant : undefined,
|
|
62
106
|
"data-width": isFullWidth ? "full" : undefined,
|
|
63
107
|
href,
|
|
108
|
+
onClick: handleClick,
|
|
64
109
|
...props,
|
|
65
110
|
}
|
|
66
|
-
|
|
111
|
+
|
|
112
|
+
const finalChildren =
|
|
113
|
+
isPending || isLoading ? (
|
|
114
|
+
<>
|
|
115
|
+
{loadingText}
|
|
116
|
+
<LoadingIcon />
|
|
117
|
+
</>
|
|
118
|
+
) : (
|
|
119
|
+
children
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
return React.createElement(element, { ...componentProps }, finalChildren)
|
|
67
123
|
}
|
|
68
124
|
export default Button
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import type { AnchorHTMLAttributes, ForwardRefExoticComponent, HTMLAttributes, JSX } from "react"
|
|
2
|
+
|
|
3
|
+
import React from "react"
|
|
4
|
+
|
|
5
|
+
import { Image, ImageProps } from "../Image"
|
|
6
|
+
|
|
7
|
+
export interface CardProps extends HTMLAttributes<HTMLDivElement> {
|
|
8
|
+
//export interface CardProps {
|
|
9
|
+
/** **(Optional)** Specifies the custom element to override default `a` */
|
|
10
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
11
|
+
as?: React.FC<AnchorHTMLAttributes<HTMLElement>> | ForwardRefExoticComponent<any> | string
|
|
12
|
+
/** **(Optional)** Specifies the background color of the card. */
|
|
13
|
+
background?: "purple" | "pink" | "green" | "orange" | "red" | "yellow" | "lilac" | "blue"
|
|
14
|
+
/** **(Optional)** Represents the content inside the badge. */
|
|
15
|
+
badge?: React.ReactNode
|
|
16
|
+
/** **(Optional)** Specify badge position relative to top right corner. */
|
|
17
|
+
badgePosition?: "inset" | "popout"
|
|
18
|
+
/** **(Optional)** Specifies if chevron will be visible. */
|
|
19
|
+
chevron?: boolean
|
|
20
|
+
/** **(Optional)** Represents the content inside the Card component. It can be any valid JSX or string. */
|
|
21
|
+
children?: React.ReactNode
|
|
22
|
+
/** **(Optional)** Specifies the heading level of the card's title. */
|
|
23
|
+
headingLevel?: "h2" | "h3" | "h4" | "h5" | "h6"
|
|
24
|
+
/** **(Optional)** Specifies the URL that the Card component will link to when clicked. */
|
|
25
|
+
href?: string
|
|
26
|
+
/** Specifies the image URL and alt text of the Card */
|
|
27
|
+
image: ImageProps
|
|
28
|
+
/** **(Optional)** Specifies the position of the image in the Card */
|
|
29
|
+
imagePosition?: "left" | "right"
|
|
30
|
+
/** **(Optional)** Specifies the label of the Card */
|
|
31
|
+
label?: string
|
|
32
|
+
/** **(Optional)** Specifies the label background of the Card */
|
|
33
|
+
labelBackground?: "yellow" | "green" | "pink" | "purple" | "orange" | "red" | "lilac" | "blue"
|
|
34
|
+
/** **(Optional)** Specifies the layout of the Card */
|
|
35
|
+
layout?: "vertical" | "horizontal"
|
|
36
|
+
/** Specifies the title of the Card */
|
|
37
|
+
title: string
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function getCardLinkElement(as: CardProps["as"], href?: string) {
|
|
41
|
+
let element: CardProps["as"] = href ? "a" : "div"
|
|
42
|
+
|
|
43
|
+
if (as) {
|
|
44
|
+
element = as
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
element,
|
|
49
|
+
props: {
|
|
50
|
+
href,
|
|
51
|
+
},
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* A Card lets you highlight and link to more in-depth content on another page. The component typically includes a title, image, and an optional description.
|
|
57
|
+
*/
|
|
58
|
+
export const Card = ({
|
|
59
|
+
as,
|
|
60
|
+
background,
|
|
61
|
+
badge,
|
|
62
|
+
badgePosition = "inset",
|
|
63
|
+
chevron = false,
|
|
64
|
+
children,
|
|
65
|
+
headingLevel = "h3",
|
|
66
|
+
href,
|
|
67
|
+
image,
|
|
68
|
+
imagePosition = "left",
|
|
69
|
+
label = "",
|
|
70
|
+
labelBackground,
|
|
71
|
+
layout = "vertical",
|
|
72
|
+
title,
|
|
73
|
+
...props
|
|
74
|
+
}: CardProps): JSX.Element => {
|
|
75
|
+
const linkElement = getCardLinkElement(as, href)
|
|
76
|
+
|
|
77
|
+
const imageProps: ImageProps = {
|
|
78
|
+
crop: "wide",
|
|
79
|
+
...image,
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const componentProps = {
|
|
83
|
+
className: "coop-card",
|
|
84
|
+
"data-badge-pos": badgePosition,
|
|
85
|
+
"data-bg": background,
|
|
86
|
+
"data-image-pos": imagePosition,
|
|
87
|
+
"data-label-bg": labelBackground,
|
|
88
|
+
"data-layout": layout,
|
|
89
|
+
...props,
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return (
|
|
93
|
+
<article {...componentProps}>
|
|
94
|
+
{image && <Image {...imageProps} />}
|
|
95
|
+
{badge && <div className="coop-card--badge">{badge}</div>}
|
|
96
|
+
<div className="coop-card--inner">
|
|
97
|
+
<div className="coop-card--content">
|
|
98
|
+
{label && <span className="coop-card--label">{label}</span>}
|
|
99
|
+
{React.createElement(
|
|
100
|
+
linkElement.element,
|
|
101
|
+
linkElement.props,
|
|
102
|
+
React.createElement(headingLevel, {}, title)
|
|
103
|
+
)}
|
|
104
|
+
{children}
|
|
105
|
+
</div>
|
|
106
|
+
{chevron && (
|
|
107
|
+
<span aria-hidden="true" className="coop-card--icon" role="presentation">
|
|
108
|
+
<svg viewBox="0 0 16 29">
|
|
109
|
+
<path d="M2 28.1a1.6 1.6 0 0 1-1.2-2.7l11-10.9-11-11A1.6 1.6 0 1 1 3 1.5l12 12a1.6 1.6 0 0 1 0 2.2l-12 12c-.3.4-.7.5-1 .5z" />
|
|
110
|
+
</svg>
|
|
111
|
+
</span>
|
|
112
|
+
)}
|
|
113
|
+
</div>
|
|
114
|
+
</article>
|
|
115
|
+
)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export default Card
|
|
@@ -34,6 +34,9 @@ const getContentfulParams = (src: string, width: string, height: string | undefi
|
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
/**
|
|
38
|
+
* A wrapper around the image HTML tag, to help implement best practices like lazy loading, and assist with cropping and sizing images.
|
|
39
|
+
*/
|
|
37
40
|
export const Image = ({ alt, crop, height, src, width = "640" }: ImageProps): JSX.Element => {
|
|
38
41
|
let params = { height, src, width }
|
|
39
42
|
|
|
@@ -24,6 +24,9 @@ export interface PillProps {
|
|
|
24
24
|
size?: "sm" | "md" | "lg" | "xl"
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
/**
|
|
28
|
+
* The Pill component is a small, rounded label used to link and highlight information such as categories or articles. It can be customised with different content and styles to suit various use cases.
|
|
29
|
+
*/
|
|
27
30
|
export const Pill = ({
|
|
28
31
|
ariaLabel,
|
|
29
32
|
as,
|
package/src/index.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export * from "./components/AlertBanner"
|
|
2
2
|
export * from "./components/Button"
|
|
3
|
-
export * from "./components/
|
|
3
|
+
export * from "./components/Card"
|
|
4
4
|
export * from "./components/Image"
|
|
5
5
|
export * from "./components/Pill"
|
|
6
6
|
export * from "./components/SkipNav"
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import type { AnchorHTMLAttributes, ForwardRefExoticComponent, JSX } from "react";
|
|
2
|
-
import React from "react";
|
|
3
|
-
import { ImageProps } from "../Image";
|
|
4
|
-
export interface EditorialCardProps {
|
|
5
|
-
/** **(Optional)** Specifies the custom element to override default `a` */
|
|
6
|
-
as?: React.FC<AnchorHTMLAttributes<HTMLElement>> | ForwardRefExoticComponent<any> | string;
|
|
7
|
-
/** **(Optional)** Represents the content inside the EditorialCard component. It can be any valid JSX or string. */
|
|
8
|
-
children?: React.ReactNode;
|
|
9
|
-
/** **(Optional)** Specifies the heading level of the card's title. */
|
|
10
|
-
headingLevel?: "h2" | "h3" | "h4" | "h5" | "h6";
|
|
11
|
-
/** **(Optional)** Specifies the URL that the EditorialCard component will link to when clicked. */
|
|
12
|
-
href?: string;
|
|
13
|
-
/** Specifies the image URL and alt text of the EditorialCard */
|
|
14
|
-
image: ImageProps;
|
|
15
|
-
/** **(Optional)** Specifies the label of the EditorialCard */
|
|
16
|
-
label?: string;
|
|
17
|
-
/** **(Optional)** Specifies the layout of the EditorialCard */
|
|
18
|
-
layout?: "vertical" | "horizontal";
|
|
19
|
-
/** Specifies the title of the EditorialCard */
|
|
20
|
-
title: string;
|
|
21
|
-
}
|
|
22
|
-
export declare const EditorialCard: ({ as, children, headingLevel, href, image, label, layout, title, }: EditorialCardProps) => JSX.Element;
|
|
23
|
-
export default EditorialCard;
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
|
-
import React from 'react';
|
|
3
|
-
import { Image } from '../Image/Image.js';
|
|
4
|
-
|
|
5
|
-
function getCardLinkElement(as, href) {
|
|
6
|
-
let element = href ? "a" : "div";
|
|
7
|
-
if (as) {
|
|
8
|
-
element = as;
|
|
9
|
-
}
|
|
10
|
-
return {
|
|
11
|
-
element,
|
|
12
|
-
props: {
|
|
13
|
-
href,
|
|
14
|
-
},
|
|
15
|
-
};
|
|
16
|
-
}
|
|
17
|
-
const EditorialCard = ({ as, children, headingLevel = "h3", href, image, label = "", layout = "vertical", title, }) => {
|
|
18
|
-
const linkElement = getCardLinkElement(as, href);
|
|
19
|
-
const imageProps = Object.assign({ crop: "wide" }, image);
|
|
20
|
-
const componentProps = {
|
|
21
|
-
className: "coop-editorial-card",
|
|
22
|
-
"data-layout": layout,
|
|
23
|
-
};
|
|
24
|
-
return (jsxs("article", Object.assign({}, componentProps, { children: [jsx(Image, Object.assign({}, imageProps)), jsxs("div", { className: "coop-editorial-card--content", children: [label && jsx("span", { children: label }), React.createElement(linkElement.element, linkElement.props, React.createElement(headingLevel, {}, title)), children] })] })));
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
export { EditorialCard, EditorialCard as default };
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
import type { AnchorHTMLAttributes, ForwardRefExoticComponent, JSX } from "react"
|
|
2
|
-
|
|
3
|
-
import React from "react"
|
|
4
|
-
|
|
5
|
-
import { Image, ImageProps } from "../Image"
|
|
6
|
-
|
|
7
|
-
export interface EditorialCardProps {
|
|
8
|
-
/** **(Optional)** Specifies the custom element to override default `a` */
|
|
9
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
10
|
-
as?: React.FC<AnchorHTMLAttributes<HTMLElement>> | ForwardRefExoticComponent<any> | string
|
|
11
|
-
/** **(Optional)** Represents the content inside the EditorialCard component. It can be any valid JSX or string. */
|
|
12
|
-
children?: React.ReactNode
|
|
13
|
-
/** **(Optional)** Specifies the heading level of the card's title. */
|
|
14
|
-
headingLevel?: "h2" | "h3" | "h4" | "h5" | "h6"
|
|
15
|
-
/** **(Optional)** Specifies the URL that the EditorialCard component will link to when clicked. */
|
|
16
|
-
href?: string
|
|
17
|
-
/** Specifies the image URL and alt text of the EditorialCard */
|
|
18
|
-
image: ImageProps
|
|
19
|
-
/** **(Optional)** Specifies the label of the EditorialCard */
|
|
20
|
-
label?: string
|
|
21
|
-
/** **(Optional)** Specifies the layout of the EditorialCard */
|
|
22
|
-
layout?: "vertical" | "horizontal"
|
|
23
|
-
/** Specifies the title of the EditorialCard */
|
|
24
|
-
title: string
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function getCardLinkElement(as: EditorialCardProps["as"], href?: string) {
|
|
28
|
-
let element: EditorialCardProps["as"] = href ? "a" : "div"
|
|
29
|
-
|
|
30
|
-
if (as) {
|
|
31
|
-
element = as
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
return {
|
|
35
|
-
element,
|
|
36
|
-
props: {
|
|
37
|
-
href,
|
|
38
|
-
},
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export const EditorialCard = ({
|
|
43
|
-
as,
|
|
44
|
-
children,
|
|
45
|
-
headingLevel = "h3",
|
|
46
|
-
href,
|
|
47
|
-
image,
|
|
48
|
-
label = "",
|
|
49
|
-
layout = "vertical",
|
|
50
|
-
title,
|
|
51
|
-
}: EditorialCardProps): JSX.Element => {
|
|
52
|
-
const linkElement = getCardLinkElement(as, href)
|
|
53
|
-
|
|
54
|
-
const imageProps: ImageProps = {
|
|
55
|
-
crop: "wide",
|
|
56
|
-
...image,
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const componentProps = {
|
|
60
|
-
className: "coop-editorial-card",
|
|
61
|
-
"data-layout": layout,
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
return (
|
|
65
|
-
<article {...componentProps}>
|
|
66
|
-
<Image {...imageProps} />
|
|
67
|
-
<div className="coop-editorial-card--content">
|
|
68
|
-
{label && <span>{label}</span>}
|
|
69
|
-
{React.createElement(
|
|
70
|
-
linkElement.element,
|
|
71
|
-
linkElement.props,
|
|
72
|
-
React.createElement(headingLevel, {}, title)
|
|
73
|
-
)}
|
|
74
|
-
{children}
|
|
75
|
-
</div>
|
|
76
|
-
</article>
|
|
77
|
-
)
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
export default EditorialCard
|