@coopdigital/react 0.5.2 → 0.6.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.
@@ -0,0 +1,25 @@
1
+ import type { ReactNode, JSX } from "react";
2
+ export interface AlertBannerProps {
3
+ /** **(Optional)** Specifies a custom aria-label. */
4
+ ariaLabel?: string;
5
+ /** **(Optional)** Represents the content inside the AlertBanner component. It can be any valid JSX or string. */
6
+ children?: string | ReactNode;
7
+ /** **(Optional)** Receives any className to be applied to AlertBanner component. */
8
+ className?: string;
9
+ /** Specifies the AlertBanner title. */
10
+ title: string;
11
+ /** **(Optional)** Specifies the AlertBanner variant. */
12
+ variant?: "black" | "default";
13
+ }
14
+ /**
15
+ * The Alert Banner component is used to highlight events that might limit availability of a service we provide.
16
+ * <br />
17
+ * - the service the user is trying to access is experiencing problems
18
+ * - planned system maintenance is coming soon
19
+ * - the user’s login session is about to expire
20
+ * <br />
21
+ * <p>Only use alert notifications when absolutely necessary. Using them too often could make the problem worse.<p/>
22
+ *
23
+ */
24
+ export declare const AlertBanner: ({ ariaLabel, children, className, title, variant, }: AlertBannerProps) => JSX.Element;
25
+ export default AlertBanner;
@@ -0,0 +1,22 @@
1
+ import { jsx, jsxs } from 'react/jsx-runtime';
2
+
3
+ /**
4
+ * The Alert Banner component is used to highlight events that might limit availability of a service we provide.
5
+ * <br />
6
+ * - the service the user is trying to access is experiencing problems
7
+ * - planned system maintenance is coming soon
8
+ * - the user’s login session is about to expire
9
+ * <br />
10
+ * <p>Only use alert notifications when absolutely necessary. Using them too often could make the problem worse.<p/>
11
+ *
12
+ */
13
+ const AlertBanner = ({ ariaLabel, children, className = "", title, variant = "default", }) => {
14
+ const componentProps = {
15
+ "aria-label": ariaLabel,
16
+ className: `coop-alert-banner ${className}`,
17
+ "data-variant": variant,
18
+ };
19
+ return (jsx("aside", Object.assign({}, componentProps, { children: jsxs("div", { className: "coop-alert-banner--inner", children: [jsx("h3", { id: "coop-alert-banner--headline", children: title }), children] }) })));
20
+ };
21
+
22
+ export { AlertBanner, AlertBanner as default };
@@ -0,0 +1,4 @@
1
+ import AlertBanner from "./AlertBanner";
2
+ export default AlertBanner;
3
+ export { AlertBanner };
4
+ export * from "./AlertBanner";
@@ -0,0 +1,21 @@
1
+ import { ImageProps } from "../Image";
2
+ import type { AnchorHTMLAttributes, ForwardRefExoticComponent, JSX } from "react";
3
+ import React from "react";
4
+ export interface EditorialCardProps {
5
+ /** Specifies the custom element to override default `a` or `span` */
6
+ as?: React.FC<AnchorHTMLAttributes<HTMLElement>> | ForwardRefExoticComponent<any> | string;
7
+ /** **(Optional)** Represents the content inside the Pill component. It can be any valid JSX or string. */
8
+ children?: React.ReactNode;
9
+ /** Specifies the URL that the EditorialCard component will link to when clicked. */
10
+ href?: string;
11
+ /** Specifies the image URL and alt text of the editorial card */
12
+ image: ImageProps;
13
+ /** Specifies the label of the editorial card */
14
+ label?: string;
15
+ /** Specifies the layout of the editorial card */
16
+ layout?: string;
17
+ /** Specifies the title of the editorial card */
18
+ title: string;
19
+ }
20
+ export declare const EditorialCard: ({ as, children, href, label, layout, image, title, }: EditorialCardProps) => JSX.Element;
21
+ export default EditorialCard;
@@ -0,0 +1,29 @@
1
+ import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
2
+ import { Image } from '../Image/Image.js';
3
+ import React from 'react';
4
+
5
+ function getCardContainer(as, href) {
6
+ let element = href ? "a" : "div";
7
+ if (as) {
8
+ element = as;
9
+ }
10
+ return {
11
+ element,
12
+ props: {
13
+ href,
14
+ className: "coop-editorial-card--inner",
15
+ },
16
+ };
17
+ }
18
+ const EditorialCard = ({ as, children, href, label = "", layout, image, title, }) => {
19
+ const container = getCardContainer(as, href);
20
+ const imageProps = Object.assign({ crop: "wide" }, image);
21
+ const componentProps = {
22
+ className: "coop-editorial-card",
23
+ "data-layout": layout,
24
+ };
25
+ const cardInner = (jsxs(Fragment, { children: [jsx(Image, Object.assign({}, imageProps)), jsxs("div", { className: "coop-editorial-card--content", children: [label && jsx("span", { children: label }), jsx("h3", { children: title }), children] })] }));
26
+ return (jsx("article", Object.assign({}, componentProps, { children: React.createElement(container.element, container.props, cardInner) })));
27
+ };
28
+
29
+ export { EditorialCard, EditorialCard as default };
@@ -0,0 +1,4 @@
1
+ import EditorialCard from "./EditorialCard";
2
+ export default EditorialCard;
3
+ export { EditorialCard };
4
+ export * from "./EditorialCard";
@@ -0,0 +1,15 @@
1
+ import type { JSX } from "react";
2
+ export interface ImageProps {
3
+ /** Specifies text that can replace the image in the page */
4
+ alt: string;
5
+ /** Specifies the image URL */
6
+ src: string;
7
+ /** Specifies width of the image */
8
+ width?: string;
9
+ /** Specifies height of the image */
10
+ height?: string;
11
+ /** Specifies the image crop mode */
12
+ crop?: "square" | "wide" | "none";
13
+ }
14
+ export declare const Image: ({ src, alt, width, height, crop }: ImageProps) => JSX.Element;
15
+ export default Image;
@@ -0,0 +1,36 @@
1
+ import { jsx } from 'react/jsx-runtime';
2
+
3
+ const getContentfulParams = (src, width, height) => {
4
+ var _a, _b;
5
+ const url = !src.startsWith("http") ? new URL("https://" + src) : new URL(src);
6
+ const params = [
7
+ ["fm", "webp"],
8
+ ["q", "70"],
9
+ ["fit", "fill"],
10
+ ["w", width],
11
+ ...(height ? [["h", height]] : []),
12
+ ];
13
+ params.forEach(([k, v]) => {
14
+ var _a;
15
+ (_a = url.searchParams.get(k)) !== null && _a !== void 0 ? _a : url.searchParams.set(k, v);
16
+ });
17
+ return {
18
+ src: url.toString(),
19
+ width: (_a = url.searchParams.get("w")) !== null && _a !== void 0 ? _a : width,
20
+ height: (_b = url.searchParams.get("h")) !== null && _b !== void 0 ? _b : height,
21
+ };
22
+ };
23
+ const Image = ({ src, alt, width = "640", height, crop }) => {
24
+ let params = { src, width, height };
25
+ if (src.includes("images.ctfassets.net")) {
26
+ params = getContentfulParams(src, width, height);
27
+ }
28
+ const dimensions = {
29
+ width: params.width,
30
+ height: params.height,
31
+ "data-crop": crop,
32
+ };
33
+ return (jsx("picture", { children: jsx("img", Object.assign({ loading: "lazy", alt: alt, src: params.src }, dimensions)) }));
34
+ };
35
+
36
+ export { Image, Image as default };
@@ -0,0 +1,4 @@
1
+ import Image from "./Image";
2
+ export default Image;
3
+ export { Image };
4
+ export * from "./Image";
@@ -1,4 +1,5 @@
1
- import React, { AnchorHTMLAttributes, ForwardRefExoticComponent } from "react";
1
+ import type { AnchorHTMLAttributes, ForwardRefExoticComponent, JSX } from "react";
2
+ import React from "react";
2
3
  export interface PillProps {
3
4
  /** **(Optional)** Specifies the custom element to override default `a` or `span`. */
4
5
  as?: React.FC<AnchorHTMLAttributes<HTMLElement>> | ForwardRefExoticComponent<any> | string;
@@ -19,5 +20,5 @@ export interface PillProps {
19
20
  /** **(Optional)** Specifies what should be the Pill size. */
20
21
  size?: "sm" | "md" | "lg" | "xl";
21
22
  }
22
- export declare const Pill: ({ ariaLabel, as, badge, badgeColor, children, className, href, pillColor, size, }: PillProps) => React.ReactElement<React.AnchorHTMLAttributes<HTMLElement>, string | React.JSXElementConstructor<any>>;
23
+ export declare const Pill: ({ ariaLabel, as, badge, badgeColor, children, className, href, pillColor, size, }: PillProps) => JSX.Element;
23
24
  export default Pill;
@@ -1,3 +1,4 @@
1
+ import type { JSX } from "react";
1
2
  interface SkipNavLink {
2
3
  href: string;
3
4
  title: string;
@@ -32,5 +33,5 @@ export interface SkipNavProps {
32
33
  * - Pressing enter will take the user to the correct part of the page and apply the focus ring to the first interactive element in that section.
33
34
  * - If the user tabs through the skip navigation without selecting an option the Co‑op logo should be the next element that has the focus ring applied.
34
35
  */
35
- export declare const SkipNav: ({ ariaLabel, className, isVisible, links, }: SkipNavProps) => import("react/jsx-runtime").JSX.Element;
36
+ export declare const SkipNav: ({ ariaLabel, className, isVisible, links, }: SkipNavProps) => JSX.Element;
36
37
  export default SkipNav;
@@ -0,0 +1,34 @@
1
+ import { jsx } from 'react/jsx-runtime';
2
+
3
+ const defaultLinks = [{ href: "#main", title: "Skip to main content" }];
4
+ /**
5
+ * The Skip Nav component allows screen reader and keyboard users to go directly to the main content.
6
+ *
7
+ * ### Links
8
+ * Links within the skip navigation component should take the user directly to the main areas of the page. These are usually the:
9
+ * - navigation
10
+ * - main content
11
+ * - footer
12
+ * - search (if applicable)
13
+ *
14
+ *
15
+ * ### Using skip navigation on a page
16
+ * - The skip navigation function should be the first thing that appears on a page when it has loaded and a user presses the tab key.
17
+ * - Pressing the tab key again should then cycle through 'skip to main content', 'skip to navigation' and 'skip to footer' links.
18
+ * - Pressing enter will take the user to the correct part of the page and apply the focus ring to the first interactive element in that section.
19
+ * - If the user tabs through the skip navigation without selecting an option the Co‑op logo should be the next element that has the focus ring applied.
20
+ */
21
+ const SkipNav = ({ ariaLabel = "", className = "", isVisible = false, links = defaultLinks, }) => {
22
+ const navLinks = links.length > 0 ? links : defaultLinks;
23
+ return (jsx("nav", { className: "coop-skip-nav", "aria-label": ariaLabel, children: jsx("ul", { children: navLinks.map((link) => {
24
+ const linkProps = {
25
+ href: link.href,
26
+ title: link.title,
27
+ className: `${className}`,
28
+ "data-visible": isVisible,
29
+ };
30
+ return (jsx("li", { children: jsx("a", Object.assign({}, linkProps, { children: link.title })) }, link.href));
31
+ }) }) }));
32
+ };
33
+
34
+ export { SkipNav, SkipNav as default };
package/dist/index.d.ts CHANGED
@@ -1 +1,5 @@
1
+ export * from "./components/AlertBanner";
2
+ export * from "./components/EditorialCard";
3
+ export * from "./components/Image";
1
4
  export * from "./components/Pill";
5
+ export * from "./components/SkipNav";
package/dist/index.js CHANGED
@@ -1 +1,5 @@
1
+ export { AlertBanner } from './components/AlertBanner/AlertBanner.js';
2
+ export { EditorialCard } from './components/EditorialCard/EditorialCard.js';
3
+ export { Image } from './components/Image/Image.js';
1
4
  export { Pill } from './components/Pill/Pill.js';
5
+ export { SkipNav } from './components/SkipNav/SkipNav.js';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@coopdigital/react",
3
3
  "type": "module",
4
- "version": "0.5.2",
4
+ "version": "0.6.0",
5
5
  "main": "dist/index.js",
6
6
  "private": false,
7
7
  "publishConfig": {
@@ -25,10 +25,11 @@
25
25
  "test": "vitest run",
26
26
  "test:watch": "vitest",
27
27
  "test:coverage": "vitest run --coverage",
28
- "build": "tsx scripts build",
29
- "build:storybook": "storybook build --disable-telemetry && npm run storybook:fix-paths",
30
28
  "test:e2e": "npx playwright test",
31
29
  "test:e2e:ui": "npx playwright test --ui",
30
+ "test:e2e:ci": "npx playwright test --shard=$CI_NODE_INDEX/$CI_NODE_TOTAL",
31
+ "build": "tsx scripts build",
32
+ "build:storybook": "storybook build --disable-telemetry && npm run storybook:fix-paths",
32
33
  "storybook": "storybook dev -p 6006",
33
34
  "storybook:playwright": "storybook dev -p 9000",
34
35
  "storybook:fix-paths": "tsx scripts/storybook-fix-paths.ts"
@@ -38,31 +39,31 @@
38
39
  "description": "",
39
40
  "devDependencies": {
40
41
  "@axe-core/playwright": "^4.10.1",
41
- "@coopdigital/styles": "^0.5.5",
42
- "@playwright/test": "^1.50.1",
43
- "@rollup/plugin-node-resolve": "^16.0.0",
42
+ "@coopdigital/styles": "^0.5.7",
43
+ "@playwright/test": "^1.51.1",
44
+ "@rollup/plugin-node-resolve": "^16.0.1",
44
45
  "@rollup/plugin-typescript": "^12.1.2",
45
- "@storybook/addon-a11y": "^8.6.4",
46
- "@storybook/addon-essentials": "^8.6.4",
47
- "@storybook/addon-interactions": "^8.6.4",
48
- "@storybook/addon-mdx-gfm": "^8.6.4",
49
- "@storybook/addon-onboarding": "^8.6.4",
50
- "@storybook/blocks": "^8.6.4",
51
- "@storybook/manager-api": "^8.6.4",
52
- "@storybook/react": "^8.6.4",
53
- "@storybook/react-vite": "^8.6.4",
54
- "@storybook/test": "^8.6.4",
55
- "@storybook/theming": "^8.6.4",
46
+ "@storybook/addon-a11y": "^8.6.6",
47
+ "@storybook/addon-essentials": "^8.6.6",
48
+ "@storybook/addon-interactions": "^8.6.6",
49
+ "@storybook/addon-mdx-gfm": "^8.6.6",
50
+ "@storybook/addon-onboarding": "^8.6.6",
51
+ "@storybook/blocks": "^8.6.6",
52
+ "@storybook/manager-api": "^8.6.6",
53
+ "@storybook/react": "^8.6.6",
54
+ "@storybook/react-vite": "^8.6.6",
55
+ "@storybook/test": "^8.6.6",
56
+ "@storybook/theming": "^8.6.6",
56
57
  "@testing-library/jest-dom": "^6.6.3",
57
58
  "@testing-library/react": "^16.2.0",
58
- "@types/react": "^19.0.10",
59
+ "@types/react": "^19.0.12",
59
60
  "@types/react-dom": "^19.0.4",
60
- "rollup": "^4.34.8",
61
- "storybook": "^8.6.4"
61
+ "rollup": "^4.36.0",
62
+ "storybook": "^8.6.6"
62
63
  },
63
64
  "peerDependencies": {
64
65
  "react": "^19.0.0",
65
66
  "react-dom": "^19.0.0"
66
67
  },
67
- "gitHead": "f292a4bba122b73db52ff17fa53eb90e7420c651"
68
+ "gitHead": "618022f347d8099ec9aed4413057ed91cce96778"
68
69
  }
@@ -0,0 +1,48 @@
1
+ import type { ReactNode, JSX } from "react"
2
+
3
+ export interface AlertBannerProps {
4
+ /** **(Optional)** Specifies a custom aria-label. */
5
+ ariaLabel?: string
6
+ /** **(Optional)** Represents the content inside the AlertBanner component. It can be any valid JSX or string. */
7
+ children?: string | ReactNode
8
+ /** **(Optional)** Receives any className to be applied to AlertBanner component. */
9
+ className?: string
10
+ /** Specifies the AlertBanner title. */
11
+ title: string
12
+ /** **(Optional)** Specifies the AlertBanner variant. */
13
+ variant?: "black" | "default"
14
+ }
15
+
16
+ /**
17
+ * The Alert Banner component is used to highlight events that might limit availability of a service we provide.
18
+ * <br />
19
+ * - the service the user is trying to access is experiencing problems
20
+ * - planned system maintenance is coming soon
21
+ * - the user’s login session is about to expire
22
+ * <br />
23
+ * <p>Only use alert notifications when absolutely necessary. Using them too often could make the problem worse.<p/>
24
+ *
25
+ */
26
+ export const AlertBanner = ({
27
+ ariaLabel,
28
+ children,
29
+ className = "",
30
+ title,
31
+ variant = "default",
32
+ }: AlertBannerProps): JSX.Element => {
33
+ const componentProps = {
34
+ "aria-label": ariaLabel,
35
+ className: `coop-alert-banner ${className}`,
36
+ "data-variant": variant,
37
+ }
38
+ return (
39
+ <aside {...componentProps}>
40
+ <div className="coop-alert-banner--inner">
41
+ <h3 id="coop-alert-banner--headline">{title}</h3>
42
+ {children}
43
+ </div>
44
+ </aside>
45
+ )
46
+ }
47
+
48
+ export default AlertBanner
@@ -0,0 +1,5 @@
1
+ import AlertBanner from "./AlertBanner"
2
+
3
+ export default AlertBanner
4
+ export { AlertBanner }
5
+ export * from "./AlertBanner"
@@ -0,0 +1,78 @@
1
+ import { Image, ImageProps } from "../Image"
2
+ import type { AnchorHTMLAttributes, ForwardRefExoticComponent, JSX } from "react"
3
+ import React from "react"
4
+
5
+ export interface EditorialCardProps {
6
+ /** Specifies the custom element to override default `a` or `span` */
7
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
8
+ as?: React.FC<AnchorHTMLAttributes<HTMLElement>> | ForwardRefExoticComponent<any> | string
9
+ /** **(Optional)** Represents the content inside the Pill component. It can be any valid JSX or string. */
10
+ children?: React.ReactNode
11
+ /** 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 editorial card */
14
+ image: ImageProps
15
+ /** Specifies the label of the editorial card */
16
+ label?: string
17
+ /** Specifies the layout of the editorial card */
18
+ layout?: string
19
+ /** Specifies the title of the editorial card */
20
+ title: string
21
+ }
22
+
23
+ function getCardContainer(as: EditorialCardProps["as"], href?: string) {
24
+ let element: EditorialCardProps["as"] = href ? "a" : "div"
25
+
26
+ if (as) {
27
+ element = as
28
+ }
29
+
30
+ return {
31
+ element,
32
+ props: {
33
+ href,
34
+ className: "coop-editorial-card--inner",
35
+ },
36
+ }
37
+ }
38
+
39
+ export const EditorialCard = ({
40
+ as,
41
+ children,
42
+ href,
43
+ label = "",
44
+ layout,
45
+ image,
46
+ title,
47
+ }: EditorialCardProps): JSX.Element => {
48
+ const container = getCardContainer(as, href)
49
+
50
+ const imageProps: ImageProps = {
51
+ crop: "wide",
52
+ ...image,
53
+ }
54
+
55
+ const componentProps = {
56
+ className: "coop-editorial-card",
57
+ "data-layout": layout,
58
+ }
59
+
60
+ const cardInner = (
61
+ <>
62
+ <Image {...imageProps} />
63
+ <div className="coop-editorial-card--content">
64
+ {label && <span>{label}</span>}
65
+ <h3>{title}</h3>
66
+ {children}
67
+ </div>
68
+ </>
69
+ )
70
+
71
+ return (
72
+ <article {...componentProps}>
73
+ {React.createElement(container.element, container.props, cardInner)}
74
+ </article>
75
+ )
76
+ }
77
+
78
+ export default EditorialCard
@@ -0,0 +1,5 @@
1
+ import EditorialCard from "./EditorialCard"
2
+
3
+ export default EditorialCard
4
+ export { EditorialCard }
5
+ export * from "./EditorialCard"
@@ -0,0 +1,57 @@
1
+ import type { JSX } from "react"
2
+
3
+ export interface ImageProps {
4
+ /** Specifies text that can replace the image in the page */
5
+ alt: string
6
+ /** Specifies the image URL */
7
+ src: string
8
+ /** Specifies width of the image */
9
+ width?: string
10
+ /** Specifies height of the image */
11
+ height?: string
12
+ /** Specifies the image crop mode */
13
+ crop?: "square" | "wide" | "none"
14
+ }
15
+
16
+ const getContentfulParams = (src: string, width: string, height: string | undefined) => {
17
+ const url = !src.startsWith("http") ? new URL("https://" + src) : new URL(src)
18
+ const params = [
19
+ ["fm", "webp"],
20
+ ["q", "70"],
21
+ ["fit", "fill"],
22
+ ["w", width],
23
+ ...(height ? [["h", height]] : []),
24
+ ]
25
+
26
+ params.forEach(([k, v]) => {
27
+ url.searchParams.get(k) ?? url.searchParams.set(k, v)
28
+ })
29
+
30
+ return {
31
+ src: url.toString(),
32
+ width: url.searchParams.get("w") ?? width,
33
+ height: url.searchParams.get("h") ?? height,
34
+ }
35
+ }
36
+
37
+ export const Image = ({ src, alt, width = "640", height, crop }: ImageProps): JSX.Element => {
38
+ let params = { src, width, height }
39
+
40
+ if (src.includes("images.ctfassets.net")) {
41
+ params = getContentfulParams(src, width, height)
42
+ }
43
+
44
+ const dimensions = {
45
+ width: params.width,
46
+ height: params.height,
47
+ "data-crop": crop,
48
+ }
49
+
50
+ return (
51
+ <picture>
52
+ <img loading="lazy" alt={alt} src={params.src} {...dimensions} />
53
+ </picture>
54
+ )
55
+ }
56
+
57
+ export default Image
@@ -0,0 +1,5 @@
1
+ import Image from "./Image"
2
+
3
+ export default Image
4
+ export { Image }
5
+ export * from "./Image"
@@ -1,4 +1,5 @@
1
- import React, { AnchorHTMLAttributes, ForwardRefExoticComponent } from "react"
1
+ import type { AnchorHTMLAttributes, ForwardRefExoticComponent, JSX } from "react"
2
+ import React from "react"
2
3
 
3
4
  export interface PillProps {
4
5
  /** **(Optional)** Specifies the custom element to override default `a` or `span`. */
@@ -32,7 +33,7 @@ export const Pill = ({
32
33
  href,
33
34
  pillColor = "blue",
34
35
  size = "md",
35
- }: PillProps) => {
36
+ }: PillProps): JSX.Element => {
36
37
  let element: PillProps["as"] = href ? "a" : "span"
37
38
 
38
39
  if (as) {
@@ -1,7 +1,10 @@
1
+ import type { JSX } from "react"
2
+
1
3
  interface SkipNavLink {
2
4
  href: string
3
5
  title: string
4
6
  }
7
+
5
8
  export interface SkipNavProps {
6
9
  /** **(Optional)** Specifies a custom aria-label. */
7
10
  ariaLabel?: string
@@ -40,24 +43,24 @@ export const SkipNav = ({
40
43
  className = "",
41
44
  isVisible = false,
42
45
  links = defaultLinks,
43
- }: SkipNavProps) => {
46
+ }: SkipNavProps): JSX.Element => {
44
47
  const navLinks = links.length > 0 ? links : defaultLinks
45
-
46
48
  return (
47
49
  <nav className="coop-skip-nav" aria-label={ariaLabel}>
48
- <ul className="coop-skip-nav__list">
49
- {navLinks.map((link) => (
50
- <li key={link.href}>
51
- <a
52
- href={link.href}
53
- title={link.title}
54
- className={`coop-skip-nav__link ${className}`}
55
- data-visible={isVisible}
56
- >
57
- {link.title}
58
- </a>
59
- </li>
60
- ))}
50
+ <ul>
51
+ {navLinks.map((link) => {
52
+ const linkProps = {
53
+ href: link.href,
54
+ title: link.title,
55
+ className: `${className}`,
56
+ "data-visible": isVisible,
57
+ }
58
+ return (
59
+ <li key={link.href}>
60
+ <a {...linkProps}>{link.title}</a>
61
+ </li>
62
+ )
63
+ })}
61
64
  </ul>
62
65
  </nav>
63
66
  )
package/src/index.ts CHANGED
@@ -1 +1,5 @@
1
+ export * from "./components/AlertBanner"
2
+ export * from "./components/EditorialCard"
3
+ export * from "./components/Image"
1
4
  export * from "./components/Pill"
5
+ export * from "./components/SkipNav"