diff --git a/package-lock.json b/package-lock.json index 75fdbd668..31da6d1da 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,9 +8,9 @@ "name": "code-sandbox", "version": "0.0.0", "dependencies": { - "@abgov/react-components": "6.2.1", - "@abgov/ui-components-common": "1.2.1", - "@abgov/web-components": "1.32.1", + "@abgov/react-components": "6.3.0-alpha.6", + "@abgov/ui-components-common": "1.3.0-alpha.2", + "@abgov/web-components": "1.33.0-alpha.17", "@faker-js/faker": "^8.3.1", "highlight.js": "^11.8.0", "js-cookie": "^3.0.5", @@ -67,24 +67,24 @@ } }, "node_modules/@abgov/react-components": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/@abgov/react-components/-/react-components-6.2.1.tgz", - "integrity": "sha512-CQ2Grpv5R/QR2UifwGP9VvAGDFxCwCpUlfSkcOKMXFUvwT+IQ6FbHPjgKkGViF2Td813/m7Qz7woo8DvoIIQfQ==", + "version": "6.3.0-alpha.6", + "resolved": "https://registry.npmjs.org/@abgov/react-components/-/react-components-6.3.0-alpha.6.tgz", + "integrity": "sha512-XQeNm6U47zfoAL23jIib23oH2zdxXAoflUUsD50MS7lucQz7/2VFftRinXpG+9m8Gk5km8+g7NKS2XMmwB1m7g==", "peerDependencies": { - "@types/react": "^17.0.0 || ^18.0.0", - "react": "^17.0.0 || ^18.0.0", - "react-dom": "^17.0.0 || ^18.0.0" + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" } }, "node_modules/@abgov/ui-components-common": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@abgov/ui-components-common/-/ui-components-common-1.2.1.tgz", - "integrity": "sha512-RIvetCp7das7VmxiOqudQ2CGFIsWlt9poL65R03SOhpgHPco5PqDNYLF+QiFlHu1Aeq9YMhIk5miHjpNBUWLSQ==" + "version": "1.3.0-alpha.2", + "resolved": "https://registry.npmjs.org/@abgov/ui-components-common/-/ui-components-common-1.3.0-alpha.2.tgz", + "integrity": "sha512-UtoqKgjRq+JkGO8dC5/WE6roaaOsf0NRvGE2Njlyf+I2Mx8LlrwFuYOGJ1ehLBwUkzn1bMT70x7YFDVrCIWJog==" }, "node_modules/@abgov/web-components": { - "version": "1.32.1", - "resolved": "https://registry.npmjs.org/@abgov/web-components/-/web-components-1.32.1.tgz", - "integrity": "sha512-Ezj17LYk5RgnNN3TVazuvlPjyOSfo4ArtpOE2VrnC0XJ0wIBKfmtcXwhdFATtLCtR/ILix829QUU0kxXzkRcSA==", + "version": "1.33.0-alpha.17", + "resolved": "https://registry.npmjs.org/@abgov/web-components/-/web-components-1.33.0-alpha.17.tgz", + "integrity": "sha512-/eydqQahruF455ktVQsMDXkOKYG9akdQ90KQ5SNCppG8R/doPQ7gW0o08hWe35UQPRmGu7mfTyhhTYmin9abiQ==", "peerDependencies": { "@sveltejs/vite-plugin-svelte": "3.x", "glob": "10.x", diff --git a/package.json b/package.json index 664fd95f5..6c8f65649 100644 --- a/package.json +++ b/package.json @@ -12,9 +12,9 @@ "prettier": "npx prettier . --write" }, "dependencies": { - "@abgov/react-components": "6.2.1", - "@abgov/ui-components-common": "1.2.1", - "@abgov/web-components": "1.32.1", + "@abgov/react-components": "6.3.0-alpha.6", + "@abgov/ui-components-common": "1.3.0-alpha.2", + "@abgov/web-components": "1.33.0-alpha.17", "@faker-js/faker": "^8.3.1", "highlight.js": "^11.8.0", "js-cookie": "^3.0.5", diff --git a/public/images/accordion.png b/public/images/accordion.png index 02a424e44..704071917 100644 Binary files a/public/images/accordion.png and b/public/images/accordion.png differ diff --git a/public/images/badge.png b/public/images/badge.png index 214d5de6e..dab4f2a9e 100644 Binary files a/public/images/badge.png and b/public/images/badge.png differ diff --git a/public/images/block.png b/public/images/block.png index 44f0da916..76c210030 100644 Binary files a/public/images/block.png and b/public/images/block.png differ diff --git a/public/images/button.png b/public/images/button.png index 62b8f7268..2e7cd603b 100644 Binary files a/public/images/button.png and b/public/images/button.png differ diff --git a/public/images/callout.png b/public/images/callout.png index 7c29f3f3d..90cf6c421 100644 Binary files a/public/images/callout.png and b/public/images/callout.png differ diff --git a/public/images/chip.png b/public/images/chip.png deleted file mode 100644 index e12ea16fa..000000000 Binary files a/public/images/chip.png and /dev/null differ diff --git a/public/images/date-picker.png b/public/images/date-picker.png index 7bfd25075..a63845cd0 100644 Binary files a/public/images/date-picker.png and b/public/images/date-picker.png differ diff --git a/public/images/details.png b/public/images/details.png index 1bdc5760f..72f7c6322 100644 Binary files a/public/images/details.png and b/public/images/details.png differ diff --git a/public/images/drawer.png b/public/images/drawer.png new file mode 100644 index 000000000..b39fa0b89 Binary files /dev/null and b/public/images/drawer.png differ diff --git a/public/images/dropdown.png b/public/images/dropdown.png index 547a01a8e..ebc60948d 100644 Binary files a/public/images/dropdown.png and b/public/images/dropdown.png differ diff --git a/public/images/filter-chip.png b/public/images/filter-chip.png new file mode 100644 index 000000000..870f7f7f4 Binary files /dev/null and b/public/images/filter-chip.png differ diff --git a/public/images/footer.png b/public/images/footer.png index bda48aac0..aad145e7a 100644 Binary files a/public/images/footer.png and b/public/images/footer.png differ diff --git a/public/images/footerb.png b/public/images/footerb.png deleted file mode 100644 index f6d29aa6e..000000000 Binary files a/public/images/footerb.png and /dev/null differ diff --git a/public/images/form-stepper.png b/public/images/form-stepper.png index ff7e5569a..c42c7a625 100644 Binary files a/public/images/form-stepper.png and b/public/images/form-stepper.png differ diff --git a/public/images/grid.png b/public/images/grid.png index 60aacd641..5aaab1423 100644 Binary files a/public/images/grid.png and b/public/images/grid.png differ diff --git a/public/images/header.png b/public/images/header.png index 7893337ed..f89c9a175 100644 Binary files a/public/images/header.png and b/public/images/header.png differ diff --git a/public/images/icon.png b/public/images/icon.png new file mode 100644 index 000000000..0b72c8eff Binary files /dev/null and b/public/images/icon.png differ diff --git a/public/images/icons.png b/public/images/icons.png index 17866e948..e46efa22f 100644 Binary files a/public/images/icons.png and b/public/images/icons.png differ diff --git a/public/images/link.png b/public/images/link.png new file mode 100644 index 000000000..dc7b96b3e Binary files /dev/null and b/public/images/link.png differ diff --git a/public/images/list.png b/public/images/list.png index 7fdcb767e..f45231cf4 100644 Binary files a/public/images/list.png and b/public/images/list.png differ diff --git a/public/images/microsite-header.png b/public/images/microsite-header.png index c0deb5fb0..23f855ca4 100644 Binary files a/public/images/microsite-header.png and b/public/images/microsite-header.png differ diff --git a/public/images/modal.png b/public/images/modal.png index 6128c3f4d..434d16239 100644 Binary files a/public/images/modal.png and b/public/images/modal.png differ diff --git a/public/images/not-yet-available.png b/public/images/not-yet-available.png new file mode 100644 index 000000000..0491bd80d Binary files /dev/null and b/public/images/not-yet-available.png differ diff --git a/public/images/notification-banner.png b/public/images/notification-banner.png index 74909d5cd..9856b5b48 100644 Binary files a/public/images/notification-banner.png and b/public/images/notification-banner.png differ diff --git a/public/images/radio.png b/public/images/radio.png index ff65c51ce..356a8c30d 100644 Binary files a/public/images/radio.png and b/public/images/radio.png differ diff --git a/public/images/side-menu.png b/public/images/side-menu.png index 34b1d1a75..2316d882a 100644 Binary files a/public/images/side-menu.png and b/public/images/side-menu.png differ diff --git a/public/images/skeleton-loader.png b/public/images/skeleton-loader.png new file mode 100644 index 000000000..5c23ee43b Binary files /dev/null and b/public/images/skeleton-loader.png differ diff --git a/public/images/skeleton-loading.png b/public/images/skeleton-loading.png deleted file mode 100644 index 87f8a8472..000000000 Binary files a/public/images/skeleton-loading.png and /dev/null differ diff --git a/public/images/spacer.png b/public/images/spacer.png index 02477f3ff..b9dc424e5 100644 Binary files a/public/images/spacer.png and b/public/images/spacer.png differ diff --git a/public/images/table.png b/public/images/table.png index f7411460d..d03be77f2 100644 Binary files a/public/images/table.png and b/public/images/table.png differ diff --git a/public/images/tabs.png b/public/images/tabs.png index a1a5ddb65..75804976c 100644 Binary files a/public/images/tabs.png and b/public/images/tabs.png differ diff --git a/public/images/text-area.png b/public/images/text-area.png index 32a869c80..dd7f5f7da 100644 Binary files a/public/images/text-area.png and b/public/images/text-area.png differ diff --git a/src/components/code-snippet/CodeSnippet.css b/src/components/code-snippet/CodeSnippet.css index fcae4cc87..8a70af51d 100644 --- a/src/components/code-snippet/CodeSnippet.css +++ b/src/components/code-snippet/CodeSnippet.css @@ -3,19 +3,22 @@ border-radius: 4px; margin-top: var(--goa-space-m); overflow: hidden; - max-height: 25rem; + max-height: 20rem; position: relative; + font: var(--goa-typography-number-s); } .goa-code-snippet pre { margin: 0; white-space: pre-wrap; word-wrap: break-word; + padding-right: var(--goa-space-2xl); /** to not overlap with copy button **/ + background-color: var(--goa-color-greyscale-100); } .goa-code-snippet.overflow pre { padding-bottom: var(--goa-space-xl); /** to not overlap with show more/less button **/ - padding-right: var(--goa-space-2xl); /** to not overlap with copy button **/ + padding-right: var(--goa-space-3xl); /** to not overlap with copy button **/ background-color: var(--goa-color-greyscale-100); } diff --git a/src/components/code-snippet/CodeSnippet.tsx b/src/components/code-snippet/CodeSnippet.tsx index 395e46067..893b676ab 100644 --- a/src/components/code-snippet/CodeSnippet.tsx +++ b/src/components/code-snippet/CodeSnippet.tsx @@ -132,7 +132,7 @@ export const CodeSnippet: FC = ({ lang, allowCopy, code, children, tags } {allowCopy && (
- +
diff --git a/src/components/component-card/ComponentCard.css b/src/components/component-card/ComponentCard.css index 4d3e9fb3c..998329079 100644 --- a/src/components/component-card/ComponentCard.css +++ b/src/components/component-card/ComponentCard.css @@ -7,7 +7,7 @@ .card-image { width: 100%; height: 200px; - background-size: contain; + background-size: cover; background-repeat: no-repeat; background-position: center; border-bottom: 1px solid var(--goa-color-greyscale-200); @@ -20,6 +20,22 @@ .card-content a { font-size: var(--goa-font-size-7); - display: block; - margin-bottom: 1rem; + display: flex; } + +.card-content a.github-link { + font: var(--goa-typography-body-s); + margin-top: 1rem; + margin-bottom: 0; +} + +.card-content a:focus { + outline: none; + box-shadow: none; + background: none; +} + +.card:focus-within { + outline: 3px solid var(--goa-color-interactive-focus); + border-radius: 4px; +} \ No newline at end of file diff --git a/src/components/component-card/ComponentCard.tsx b/src/components/component-card/ComponentCard.tsx index a11faa5ec..9e1d9daa9 100644 --- a/src/components/component-card/ComponentCard.tsx +++ b/src/components/component-card/ComponentCard.tsx @@ -1,12 +1,31 @@ import { Link } from "react-router-dom"; import "./ComponentCard.css"; -import { toKebabCase } from '../../utils/index'; +import { toKebabCase } from "../../utils"; +import { GoabBadge, GoabText } from "@abgov/react-components"; +import { useState, useEffect } from "react"; + +export type ComponentStatus = "Published" | "Not Published" | "In Progress"; + +// Define allowed group options as a union type +type Group = + | "Content layout" + | "Feedback and alerts" + | "Inputs and actions" + | "Structure and navigation" + | "Utilities"; +import { useContext } from "react"; +import { LanguageVersionContext } from "@contexts/LanguageVersionContext.tsx"; +import { ANGULAR_VERSIONS, REACT_VERSIONS } from "@components/version-language-switcher/version-language-constants.ts"; export interface Props { name: string; description: string; - groups: string[]; + groups: Group[]; // Use the Group type here tags?: string[]; + status: ComponentStatus; + githubLink?: string; + openIssues?: number; + isNew?: boolean; // if true, show a badge on the component card to let users know the component is available in the latest version } function dasherize(value: string): string { @@ -14,17 +33,80 @@ function dasherize(value: string): string { } export function ComponentCard(props: Props) { + const [imageUrl, setImageUrl] = useState(`/images/${dasherize(props.name)}.png`); + + useEffect(() => { + const testImage = new Image(); + testImage.src = imageUrl; + testImage.onerror = () => setImageUrl("/images/not-yet-available.png"); + }, [imageUrl]); + + const getBadgeType = (status: ComponentStatus) => { + switch (status) { + case "Published": + return null; // No badge for "Published" + case "Not Published": + return "information"; + case "In Progress": + return "important"; + default: + return "information"; // Fallback for unknown status + } + }; + + const badgeType = getBadgeType(props.status); + + const {language} = useContext(LanguageVersionContext); return (
-
-
- - {`${props.name.substring(0, 1).toUpperCase()}${props.name.substring(1)}`} + {props.status === "Published" ? ( + +
- {props.description} + ) : ( +
+ )} +
+
+ {badgeType && } + + {props.status === "Published" ? ( + + + {`${props.name.substring(0, 1).toUpperCase()}${props.name.substring(1)}`} + + + ) : ( + + + {`${props.name.substring(0, 1).toUpperCase()}${props.name.substring(1)}`} + + + )} +
+ + {props.description} + + {props.status !== "Published" && props.githubLink && ( + + + View issues{typeof props.openIssues === 'number' && ` (${props.openIssues})`} + + + )} + {props.isNew && }
); diff --git a/src/components/component-content/ComponentContent.tsx b/src/components/component-content/ComponentContent.tsx index e44d95ecc..8b1e47070 100644 --- a/src/components/component-content/ComponentContent.tsx +++ b/src/components/component-content/ComponentContent.tsx @@ -1,5 +1,6 @@ import TOC from "@components/table-of-contents/TOC"; import "./component-content.css"; +import { GoabDivider } from "@abgov/react-components"; type Props = { children: React.ReactNode; @@ -11,8 +12,10 @@ export function ComponentContent({tocCssQuery, contentClassName, children}: Prop return <>
-
+
{children} +
{tocCssQuery && }
diff --git a/src/components/component-header/ComponentHeader.css b/src/components/component-header/ComponentHeader.css index 8c6032a55..fcfbd9b7a 100644 --- a/src/components/component-header/ComponentHeader.css +++ b/src/components/component-header/ComponentHeader.css @@ -1,11 +1,34 @@ .component-header { - margin-bottom: 3rem; - margin-top: 2rem; + margin-bottom: var(--goa-space-l); + margin-top: var(--goa-space-xl); } .component-header h1 { - margin-bottom: 1rem; - margin-top: 1.5rem; + margin-bottom: var(--goa-space-m); + margin-top: var(--goa-space-l); +} + +.component-header h3 { + margin-bottom: var(--goa-space-xs); +} + +.component-header span.small, +.component-header a.small { + font: var(--goa-typography-body-s); + margin-bottom: var(--goa-space-xs); +} + +.component-header span.x-small, +.component-header a.x-small { + font: var(--goa-typography-body-xs); +} + +.related-links { + color: var(--goa-color-text-secondary); +} + +.grey { + color: var(--goa-color-text-secondary); } @media screen and (max-width: 623px) { diff --git a/src/components/component-header/ComponentHeader.tsx b/src/components/component-header/ComponentHeader.tsx index 0dd5c8f95..76f3a955b 100644 --- a/src/components/component-header/ComponentHeader.tsx +++ b/src/components/component-header/ComponentHeader.tsx @@ -1,42 +1,122 @@ -import { GoabBadge } from "@abgov/react-components"; +import { GoabBadge, GoabBlock, GoabText } from "@abgov/react-components"; import "./ComponentHeader.css"; import { Link } from "react-router-dom"; +import { useEffect, useState } from "react"; +import { toSentenceCase, fetchIssueCount } from "../../utils"; +export enum Category { + CONTENT_AND_LAYOUT = "Content and layout", + FEEDBACK_AND_ALERTS = "Feedback and alerts", + STRUCTURE_AND_NAVIGATION = "Structure and navigation", + INPUTS_AND_ACTIONS = "Inputs and actions", + UTILITIES = "Utilities", +} interface Props { category?: Category; name: string; description?: string; relatedComponents?: { link: string; name: string }[]; + githubLink?: string; + figmaLink?: string; } -export enum Category { - CONTENT_AND_LAYOUT = "Content and layout", - FEEDBACK_AND_ALERTS = "Feedback and alerts", - STRUCTURE_AND_NAVIGATION = "Structure and navigation", - INPUTS_AND_ACTIONS = "Inputs and actions", - UTILITIES = "Utilities", -} +export const ComponentHeader: React.FC = (props) => { + const [issueCount, setIssueCount] = useState(null); + + useEffect(() => { + if (!props.githubLink) return; + + const getCount = async () => { + const label = toSentenceCase(props.githubLink!); + const count = await fetchIssueCount(label); + setIssueCount(count); + }; + + getCount(); + }, [props.githubLink]); -export const ComponentHeader: React.FC = (props: Props) => { return ( -
+ +
-

{props.name}

- -

+ + + + {props.name} + + {(props.githubLink || props.figmaLink) && ( + + {/* GitHub Issues link, if we have a "githubLink" */} + + {props.githubLink && ( + <> + + + GitHub issues{issueCount !== null ? ` (${issueCount})` : ""} + + + )} + + {/* Figma link, if we have a "figmaLink" */} + + {props.figmaLink && ( + <> +
+ + + + + + + +
+ + + Figma + + + )} +
+
+ )} +
+ + + {props.description} + {props.relatedComponents?.length && ( - <> - Related components: - {props.relatedComponents.map((relatedComponent, index, array) => ( - - {relatedComponent.name} + + Related: + {props.relatedComponents.map((rc, index, array) => ( + + {rc.name} {index < array.length - 1 && ", "} ))} - + )} + +
); }; diff --git a/src/components/component-properties/ComponentProperties.tsx b/src/components/component-properties/ComponentProperties.tsx index 6d129939d..df79a0799 100644 --- a/src/components/component-properties/ComponentProperties.tsx +++ b/src/components/component-properties/ComponentProperties.tsx @@ -1,4 +1,4 @@ -import { GoabAccordion, GoabBadge } from "@abgov/react-components"; +import { GoabBadge, GoabContainer, GoabText } from "@abgov/react-components"; import { useContext, useEffect, useState } from "react"; import css from "./ComponentProperties.module.css"; @@ -44,21 +44,25 @@ export const ComponentProperties = (props: Props) => { return ( <> - - + + {props.heading || "Properties"} + +
{filteredProperties.map((props, index) => ( ))}
-
+ ); }; @@ -86,9 +90,11 @@ function ComponentProperty({ props }: ComponentPropertyProps) { {props.description} {props.defaultValue && ( +
{" "} Defaults to {props.defaultValue}.
+ )}
diff --git a/src/components/empty-states/accessibility-empty/AccessibilityEmpty.tsx b/src/components/empty-states/accessibility-empty/AccessibilityEmpty.tsx new file mode 100644 index 000000000..73d3af5a0 --- /dev/null +++ b/src/components/empty-states/accessibility-empty/AccessibilityEmpty.tsx @@ -0,0 +1,22 @@ +import { GoabContainer, GoabText } from "@abgov/react-components"; + +export interface Props { + figmaLink: string; +} +export function AccessibilityEmpty(props: Props) { + return ( +
+ + + Accessibility documentation for this component can be found on its {" "} + + component page in Figma. + {" "} + + + More accessibility guidance for design and development is coming soon. + + +
+ ); +} \ No newline at end of file diff --git a/src/components/empty-states/design-empty/DesignEmpty.tsx b/src/components/empty-states/design-empty/DesignEmpty.tsx new file mode 100644 index 000000000..769db5f91 --- /dev/null +++ b/src/components/empty-states/design-empty/DesignEmpty.tsx @@ -0,0 +1,20 @@ +import { GoabContainer, GoabText } from "@abgov/react-components"; + +export interface Props { + figmaLink: string; +} +export function DesignEmpty(props: Props) { + return ( +
+ + + + Detailed design documentation for this component can be found on its {" "} + + component page in Figma. + {" "} + + +
+ ); +} \ No newline at end of file diff --git a/src/components/empty-states/examples-empty/ExamplesEmpty.tsx b/src/components/empty-states/examples-empty/ExamplesEmpty.tsx new file mode 100644 index 000000000..a7ec215eb --- /dev/null +++ b/src/components/empty-states/examples-empty/ExamplesEmpty.tsx @@ -0,0 +1,16 @@ +import { GoabContainer, GoabText } from "@abgov/react-components"; + +export function ExamplesEmpty( ) { + return ( +
+ + + We are currently collecting contextual service examples for this component to provide as a starting point in both Figma and code libraries. To contribute examples from your service {" "} + + talk to the design system team. + {" "} + + +
+ ); +} \ No newline at end of file diff --git a/src/components/icon-snippet/IconSnippet.css b/src/components/icon-snippet/IconSnippet.css index d0219a2ad..db6a97032 100644 --- a/src/components/icon-snippet/IconSnippet.css +++ b/src/components/icon-snippet/IconSnippet.css @@ -3,7 +3,7 @@ div.icon-snippet { border: 1px var(--goa-color-greyscale-200) solid; border-radius: 4px; padding: var(--goa-space-m); - color: var(--goa-color-text-secondary); + color: var(--goa-color-greyscale-black); cursor: pointer; /*Below to make icons and text on the same row*/ display: inline-flex; @@ -11,6 +11,7 @@ div.icon-snippet { gap: var(--goa-space-m); align-items: center; justify-content: start; + position: relative; } div.icon-snippet goa-icon { @@ -24,8 +25,15 @@ div.icon-snippet span { div.icon-snippet:hover, div.icon-snippet:focus { + border-color: var(--goa-color-interactive-hover); + color: var(--goa-color-interactive-hover); + background-color: var(--goa-color-greyscale-100); +} + +div.icon-snippet.active { border-color: var(--goa-color-greyscale-black); color: var(--goa-color-greyscale-black); + background-color: var(--goa-color-greyscale-200); } /*Will remove when we have temporary notification component*/ @@ -37,7 +45,10 @@ div.icon-snippet div.copy-feedback { padding: var(--goa-space-m) var(--goa-space-l) var(--goa-space-m) var(--goa-space-l); z-index: 10000; position: absolute; - margin-top: 100px; + top: 100%; + left: 0; + margin-top: var(--goa-space-xs); + font: var(--goa-typography-body-s); } div.icon-snippet div.copy-feedback span { diff --git a/src/components/icon-snippet/IconSnippet.tsx b/src/components/icon-snippet/IconSnippet.tsx index bcb27bfe7..83db402bd 100644 --- a/src/components/icon-snippet/IconSnippet.tsx +++ b/src/components/icon-snippet/IconSnippet.tsx @@ -13,13 +13,13 @@ export const IconSnippet: FC = ({ type }) => { function copyIcon() { navigator.clipboard.writeText(type).then(() => { setCopied(true); - setTimeout(() => setCopied(false), 1000); + setTimeout(() => setCopied(false), 2000); }); } return ( <> -
+
{type}
{ + return ( + + This component is only available in {language == "angular" ? ANGULAR_VERSIONS.NEW.label.substring(0,2).toUpperCase() + : REACT_VERSIONS.NEW.label.substring(0,2).toUpperCase()} of the design system components. +
+ View upgrade guide{" "} +
+ ) +} diff --git a/src/components/sandbox/Sandbox.css b/src/components/sandbox/Sandbox.css index 8be83f371..017ba6f83 100644 --- a/src/components/sandbox/Sandbox.css +++ b/src/components/sandbox/Sandbox.css @@ -7,7 +7,6 @@ border: var(--goa-border-width-s) solid var(--goa-color-greyscale-200); background: var(--goa-color-greyscale-white); padding: var(--goa-space-3xl); - margin: var(--goa-space-xs) 0 0; display: flex; justify-content: space-around; } @@ -18,9 +17,8 @@ .sandbox-container { display: flex; - gap: 1rem 2rem; + gap: 1.5rem 2.5rem; flex-flow: wrap; - margin-top: var(--goa-space-l); } .sandbox-render-centered { @@ -31,10 +29,6 @@ .sandbox-render { /*make sure preview element will inherit component width*/ display: block; - padding: var(--goa-space-xs); - } - .sandbox-container { - gap: 0; - display: block; + padding: var(--goa-space-xl); } } diff --git a/src/components/sandbox/Sandbox.tsx b/src/components/sandbox/Sandbox.tsx index 4fb227253..59e7902b6 100644 --- a/src/components/sandbox/Sandbox.tsx +++ b/src/components/sandbox/Sandbox.tsx @@ -13,6 +13,7 @@ import React from "react"; import { LanguageVersionContext } from "@contexts/LanguageVersionContext.tsx"; import { AngularTemplateDrivenSerializer } from "@components/sandbox/AngularTemplateDrivenSerializer.ts"; import { LanguageVersion } from "@components/version-language-switcher/version-language-constants.ts"; +import { GoabAccordion } from "@abgov/react-components"; type Flag = "reactive" | "template-driven" | "event"; type ComponentType = "goa" | "codesnippet"; @@ -28,15 +29,18 @@ interface SandboxProps { flags?: Flag[]; skipRender?: boolean; // prevent rendering the snippet, to allow custom code to be shown skipRenderOnly?: string; // prevent rendering the snippet for a specific language. Ex: react/angular + skipRenderDom?: boolean; // rendering code snippets, but display none for the DOM because it isn't working as expected if inside sandbox allow?: string[]; // Be default the Sandbox is selective to what it renders out. This adds // additional elements to what is allowed to be rendered out variableNames?: string[]; // If we want to assign a variable such as step={step} render in code snippet, provides variableNames=["step"] children: ReactNode; + background?: string; } type SandboxViewProps = { fullWidth?: boolean; sandboxProps: SandboxProps; + background?: string; }; export const Sandbox = (props: SandboxProps) => { @@ -100,7 +104,9 @@ export const Sandbox = (props: SandboxProps) => { } function SandboxView(props: SandboxViewProps): ReactElement { - return
+ const { background } = props; + + return
@@ -109,15 +115,29 @@ export const Sandbox = (props: SandboxProps) => { return ( <> - - {props.formItemProperties && props.formItemProperties.length > 0 && ( - - )} + {props.skipRenderDom ? null : } + + {/* Only render the GoAAccordion if props.properties is provided */} {props.properties && props.properties.length > 0 && ( - + + {props.formItemProperties && props.formItemProperties.length > 0 && ( + + )} + + + )} {props.note && (
{props.note}
)} diff --git a/src/components/sandbox/sandbox-header/sandboxHeader.css b/src/components/sandbox/sandbox-header/sandboxHeader.css new file mode 100644 index 000000000..43d534f75 --- /dev/null +++ b/src/components/sandbox/sandbox-header/sandboxHeader.css @@ -0,0 +1,91 @@ +.sandbox-header { + display: flex; + justify-content: space-between; /* Distributes items across available space */ + align-items: center; /* Ensures vertical alignment */ + width: 100%; + margin-top: var(--goa-space-xl); + margin-bottom: var(--goa-space-s); +} + +.icon-link { + display: inline-block; + position: relative; /* Make this the reference for absolute positioning */ + width: var(--goa-icon-size-l); /* Explicitly set width to prevent shifting */ + height: var(--goa-icon-size-l); +} + +/* Both icons should be positioned absolutely and stack on top of each other */ +.icon-default, +.icon-hover { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + transition: opacity 0.2s ease-in-out; /* Smooth transition */ +} + +/* Default icon visible by default */ +.icon-default { + opacity: 1; +} + +/* Hover icon hidden by default */ +.icon-hover { + opacity: 0; +} + +/* When hovering, swap opacity */ +.icon-link:hover .icon-default { + opacity: 0; +} + +.icon-link:hover .icon-hover { + opacity: 1; +} + +.header-container { + display: flex; + align-items: end; + justify-content: space-between; /* Ensures full width is used */ + width: 100%; /* Expands to fill the full content area */ +} + +.heading-group { + display: flex; + align-items: end; + gap: 8px; /* Space between heading and copy icon */ + margin: 0; /* Ensures consistent spacing */ +} + +.heading-group h3 { + word-wrap: break-word; /* Wraps text when reaching the container edge */ + overflow-wrap: break-word; + flex-grow: 1; /* Allows heading to expand */ + min-width: 0; /* Prevents flexbox shrinking issues */ + white-space: normal; /* Allows multi-line wrapping */ + width: 100%; + margin-bottom: 10px; +} + +.figma-icon { + margin-left: auto; /* Pushes Figma icon to the right */ + flex-shrink: 0; /* Ensures it does not shrink */ +} + +.copy-icon-button { + background: none; + border: none; + outline: none; + box-shadow: none; + padding: 0; + cursor: pointer; + display: inline-flex; + align-items: center; + justify-content: center; + +} + +.copy-icon-button:hover { + color: var(--goa-color-interactive-default); +} diff --git a/src/components/sandbox/sandbox-header/sandboxHeader.tsx b/src/components/sandbox/sandbox-header/sandboxHeader.tsx new file mode 100644 index 000000000..ab9edc9d1 --- /dev/null +++ b/src/components/sandbox/sandbox-header/sandboxHeader.tsx @@ -0,0 +1,99 @@ +import { GoabTooltip } from "@abgov/react-components"; +import { useState, useRef } from "react"; +import "./sandboxHeader.css"; +import { toKebabCase } from "../../../utils"; + +export interface Props { + exampleTitle: string; + figmaExample?: string; +} + +export function SandboxHeader(props: Props) { + const [isCopied, setIsCopied] = useState(false); + const headingRef = useRef(null); + const [isHovering, setIsHovering] = useState(false); + const anchorId = toKebabCase(props.exampleTitle); + + function copyAnchorLink() { + const currentHash = window.location.hash; + const tabPart = currentHash.includes("#tab-") ? currentHash.split("#")[1]: ""; + const url = `${window.location.origin}${window.location.pathname}${tabPart ? `#${tabPart}` : ''}#${anchorId}`; + navigator.clipboard.writeText(url).then(() => { + setIsCopied(true); + setTimeout(() => setIsCopied(false), 2000); // Reset tooltip text after 2s + }); + } + + return ( +
+ {/* Heading with an ID for linking */} +
+ {/* Heading + Copy Button (Grouped Together) */} +
+

+ {props.exampleTitle} +

+ + {/* Copy link icon (consistent with CodeSnippet.tsx) */} + + + +
+ + {/* Figma link tooltip */} + {props.figmaExample && ( + + + {/* Default SVG (Shown by default) */} + + + + + + + + {/* Hover SVG */} + + + + + + + + + + )} +
+
+ ); +} diff --git a/src/components/support-info/SupportInfo.css b/src/components/support-info/SupportInfo.css index 58d8bf79f..429c5ee1e 100644 --- a/src/components/support-info/SupportInfo.css +++ b/src/components/support-info/SupportInfo.css @@ -1,11 +1,4 @@ .support-info { - margin-right: var(--goa-space-3xl); max-width: 700px; margin-top: var(--goa-space-3xl); -} - -@media screen and (max-width: 623px) { - .support-info { - margin-right: var(--goa-space-m); - } -} +} \ No newline at end of file diff --git a/src/components/table-of-contents/TOC.tsx b/src/components/table-of-contents/TOC.tsx index 7ef21ed7e..e568ada62 100644 --- a/src/components/table-of-contents/TOC.tsx +++ b/src/components/table-of-contents/TOC.tsx @@ -17,13 +17,24 @@ export function TOC(props: TOCProps) { const [activeByScroll, setActiveByScroll] = useState(null); const [previousUrl, setPreviousUrl] = useState(""); + const getAnchorIdFromHash = (hash: string) => { + if (hash.includes("#tab-")) { + const parts = hash.split("#"); + if (parts.length > 2) { + return parts[2]; + } + } + return hash.replace("#", ""); + } useEffect(() => { // set first link as current by default if no hash exists setTimeout(() => { - if (document.location.hash === "") { + const hash = document.location.hash; + if (hash === "") { setActiveByScroll(queryItems()?.[0]); } else { - const el = document.querySelector(document.location.hash); + const idPart = getAnchorIdFromHash(hash); + const el = document.getElementById(idPart); el?.scrollIntoView(); } }, 100) @@ -100,8 +111,30 @@ export function TOC(props: TOCProps) { function isActive(id: string, index: number): boolean { if (activeByScroll == null && index === 0) return true; if (activeByScroll?.id === id) return true; - if (!activeByScroll && location.hash === "#"+id) return true; - return false; + const currentHash = location.hash; + const idPart = getAnchorIdFromHash(currentHash); + return !activeByScroll && idPart === id; + } + + function getAnchorLink(id: string): string { + const currentHash = window.location.hash; + + if (currentHash.includes('#tab-')) { + // If URL contains #tab-, append our ID after it + return `${currentHash}#${id}`; + } + + return `#${id}`; + } + + const handleClick = (e: React.MouseEvent, id: string) => { + e.preventDefault(); + const el = document.getElementById(id); + if (el) { + el.scrollIntoView({behavior: "smooth"}); + const newHash = getAnchorLink(id); + window.history.replaceState(null, "", newHash); + } } return ( @@ -113,7 +146,8 @@ export function TOC(props: TOCProps) { ${css[`toc-item-${element.toLowerCase()}`]} ${isActive(id, index) ? css["active"] : ""} `} - href={`#${id}`} + href={getAnchorLink(id)} + onClick={(e) => handleClick(e, id)} > {title} diff --git a/src/components/table-of-contents/toc.module.css b/src/components/table-of-contents/toc.module.css index 30e77214e..3ff037423 100644 --- a/src/components/table-of-contents/toc.module.css +++ b/src/components/table-of-contents/toc.module.css @@ -5,7 +5,7 @@ overscroll-behavior: contain; width: 12rem; height: fit-content; - @media screen and (max-width: 1152px) { + @media screen and (max-width: 1230px) { display: none; } } diff --git a/src/components/token-snippet/TokenSnippet.css b/src/components/token-snippet/TokenSnippet.css index 83404a128..bdf7693d1 100644 --- a/src/components/token-snippet/TokenSnippet.css +++ b/src/components/token-snippet/TokenSnippet.css @@ -1,17 +1,27 @@ .goa-token-snippet { display: flex; - align-items: center; + flex-wrap: nowrap; + align-items: start; + white-space: nowrap; + max-width: 260px; } .goa-token-snippet a { + font-family: var(--goa-font-family-sans), serif; display: flex; - font-family: var(--goa-font-family-sans); + align-items: center; + text-decoration: none; } .goa-token-snippet .copy-feedback { background-color: var(--goa-color-greyscale-100); color: var(--goa-color-interactive-default); - padding: 0; - margin: -50px 0 0 -40px; border-radius: 4px; + padding: 4px 8px; + z-index: 9999; + margin-left: 8px; } + +.mobile-token-view { + margin-top: 16px; +} \ No newline at end of file diff --git a/src/components/token-snippet/TokenSnippet.tsx b/src/components/token-snippet/TokenSnippet.tsx index fbcff33c0..1c5100602 100644 --- a/src/components/token-snippet/TokenSnippet.tsx +++ b/src/components/token-snippet/TokenSnippet.tsx @@ -6,9 +6,10 @@ import { DesignTokensLanguageContext } from "@contexts/DesignTokensLanguageConte interface Props { code: string; + className?: string; } -export const TokenSnippet: FC = ({ code }) => { +export const TokenSnippet: FC = ({ code, className }) => { const [isCopied, setIsCopied] = useState(false); const lang = useContext(DesignTokensLanguageContext); @@ -16,23 +17,19 @@ export const TokenSnippet: FC = ({ code }) => { let codeToCopy = lang === "css" ? `--${code}` : `$${code}`; navigator.clipboard.writeText(codeToCopy).then(() => { setIsCopied(true); - setTimeout(() => setIsCopied(false), 1000); + setTimeout(() => setIsCopied(false), 2000); }); } return ( -
-
+    
{ const {language, version, setLanguage, setVersion} = useContext(LanguageVersionContext); const location = useLocation(); const navigate = useNavigate(); + const [hash, setHash] = React.useState(window.location.hash); + + useEffect(() => { + function setLanguageAndVersionFromUrl() { + if (location.pathname.split("/").length < 4) return; + const versLangSegment = location.pathname.split("/")[2]; + if (VERSIONED_REACT_URL_SEGMENT === versLangSegment) { + setLanguage("react"); + setVersion("new"); + } else if (VERSIONED_ANGULAR_URL_SEGMENT === versLangSegment) { + setLanguage("angular"); + setVersion("new"); + } + } + setLanguageAndVersionFromUrl(); + }); + + // popover collapses when Url hash tag changes + const generateHyperlink = (newValue: LanguageVersion | Language) => { + return hash === newValue ? "#" : `#${newValue}`; + }; const updateURL = (key: "language" | "version", newValue: LanguageVersion | Language) => { const isComponentRoute = @@ -27,28 +48,37 @@ export const VersionLanguageSwitcher = () => { if (key === "version") newVersionValue = newValue as LanguageVersion; const combineSegment = getVersionedUrlPath(newVersionValue, newLanguageValue); + const newHash = hash === newValue ? "" : newValue; if (isComponentRoute) { const pathSegments = location.pathname.split("/"); - const componentName = pathSegments[pathSegments.length -1]; - if (VERSIONED_REACT_URL_SEGMENT === combineSegment || VERSIONED_ANGULAR_URL_SEGMENT === combineSegment) { - const newPath = `/components/${combineSegment}/${componentName}${location.hash}`; - navigate(newPath, {replace: true}); + const componentName = pathSegments[pathSegments.length - 1]; + if ( + VERSIONED_REACT_URL_SEGMENT === combineSegment || + VERSIONED_ANGULAR_URL_SEGMENT === combineSegment + ) { + const newPath = `/components/${combineSegment}/${componentName}#${newHash}`; + navigate(newPath, { replace: true }); } else { - const newPath = `/components/${componentName}${location.hash}`; - navigate(newPath, {replace: true}); + const newPath = `/components/${componentName}#${newHash}`; + navigate(newPath, { replace: true }); } } - } + setHash(newHash); // related to popover collapse + }; const updateLanguage = (newValue: "react" | "angular") => { - setLanguage(newValue); - updateURL("language", newValue); - } + setTimeout(() => { + setLanguage(newValue); + updateURL("language", newValue); + }, 0); // timeout related to popover collapse + }; const updateVersion = (newValue: "old" | "new") => { - setVersion(newValue); - updateURL("version", newValue); - } + setTimeout(() => { + setVersion(newValue); + updateURL("version", newValue); + }, 0); // timeout related to popover collapse + }; const capitalizeFirstLetter = (str: string): string => { if (!str) return str; @@ -82,7 +112,7 @@ export const VersionLanguageSwitcher = () => { padded={false}> <> {["angular", "react"].map(lang => ( - updateLanguage(lang as "react" | "angular")}> + updateLanguage(lang as Language)}> {capitalizeFirstLetter(lang)} )) } @@ -95,13 +125,13 @@ export const VersionLanguageSwitcher = () => { {getCurrentVersionLabel(language, version)} } padded={false}> - <> - {["new", "old"].map(ver => ( - updateVersion(ver as "old" | "new")}> - {getCurrentVersionLabel(language, ver)} - - ))} - + <> + {["new", "old"].map(ver => ( + updateVersion(ver as LanguageVersion)}> + {getCurrentVersionLabel(language, ver)} + + ))} + diff --git a/src/examples/accordion/AccordionExamples.tsx b/src/examples/accordion/AccordionExamples.tsx index 29b35537a..64728fd16 100644 --- a/src/examples/accordion/AccordionExamples.tsx +++ b/src/examples/accordion/AccordionExamples.tsx @@ -1,15 +1,21 @@ import "./accordion-example.css"; import { AccordionExpandOrCollapseExample } from "@examples/accordion/AccordionExpandOrCollapseExample.tsx"; import { AccordionHideOrShowSectionExample } from "@examples/accordion/AccordionHideOrShowSectionExample.tsx"; +import { SandboxHeader } from "@components/sandbox/sandbox-header/sandboxHeader.tsx"; export default function AccordionExamples() { return ( <> - -

Expand or collapse part of a form

+ + -

Hide and show many sections of information (FAQ)

+ + ); diff --git a/src/examples/app-footer/AppFooterExamples.tsx b/src/examples/app-footer/AppFooterExamples.tsx index 8f6e5eee3..ab5da1c1b 100644 --- a/src/examples/app-footer/AppFooterExamples.tsx +++ b/src/examples/app-footer/AppFooterExamples.tsx @@ -1,18 +1,20 @@ import { AppFooterShowQuickLinkExample } from "@examples/app-footer/AppFooterShowQuickLinkExample.tsx"; import { AppFooterShowNavigationItemsExample } from "@examples/app-footer/AppFooterShowNavigationItemsExample.tsx"; +import { SandboxHeader } from "@components/sandbox/sandbox-header/sandboxHeader.tsx"; export const AppFooterExamples = () => { return ( <> - - + + - + + ) diff --git a/src/examples/app-footer/AppFooterShowQuickLinkExample.tsx b/src/examples/app-footer/AppFooterShowQuickLinkExample.tsx index a6b131f00..f62cd2baa 100644 --- a/src/examples/app-footer/AppFooterShowQuickLinkExample.tsx +++ b/src/examples/app-footer/AppFooterShowQuickLinkExample.tsx @@ -16,17 +16,17 @@ export const AppFooterShowQuickLinkExample = () => { code={` - - Privacy - - - Disclaimer + + Give feedback - + Accessibility - - Using Alberta.ca + + Privacy + + + Contact us @@ -40,17 +40,17 @@ export const AppFooterShowQuickLinkExample = () => { code={` - - Privacy - - - Disclaimer + + Give feedback - + Accessibility - - Using Alberta.ca + + Privacy + + + Contact us @@ -66,17 +66,17 @@ export const AppFooterShowQuickLinkExample = () => { code={` - - Privacy - - - Disclaimer + + Give feedback - + Accessibility - - Using Alberta.ca + + Privacy + + + Contact us @@ -90,17 +90,17 @@ export const AppFooterShowQuickLinkExample = () => { code={` - - Privacy - - - Disclaimer + + Give feedback - + Accessibility - - Using Alberta.ca + + Privacy + + + Contact us @@ -109,10 +109,10 @@ export const AppFooterShowQuickLinkExample = () => { - Privacy - Disclaimer - Accessibility - Using Alberta.ca + Give feedback + Accessibility + Privacy + Contact us diff --git a/src/examples/app-header/AppHeaderExamples.tsx b/src/examples/app-header/AppHeaderExamples.tsx index 8ecccb849..26f3bd5da 100644 --- a/src/examples/app-header/AppHeaderExamples.tsx +++ b/src/examples/app-header/AppHeaderExamples.tsx @@ -1,17 +1,20 @@ import { AppHeaderWithNavigationExample } from "@examples/app-header/AppHeaderWithNavigationExample.tsx"; import { AppHeaderWithMenuClickEventExample } from "@examples/app-header/AppHeaderWithMenuClickEventExample.tsx"; +import { SandboxHeader } from "@components/sandbox/sandbox-header/sandboxHeader.tsx"; export const AppHeaderExamples = () => { return ( <> - - -

Header with navigation

+ + -

Header with menu click event

+ + ); diff --git a/src/examples/app-header/AppHeaderWithMenuClickEventExample.tsx b/src/examples/app-header/AppHeaderWithMenuClickEventExample.tsx index cbafb55e5..3831f5de8 100644 --- a/src/examples/app-header/AppHeaderWithMenuClickEventExample.tsx +++ b/src/examples/app-header/AppHeaderWithMenuClickEventExample.tsx @@ -25,12 +25,12 @@ export const AppHeaderWithMenuClickEventExample = () => { heading="Design System" onMenuClick={handleMenuClick} fullMenuBreakpoint={+deviceWidth}> - Support - + Cases Payments Outstanding + Support Sign in @@ -88,12 +88,12 @@ export const AppHeaderWithMenuClickEventExample = () => { - Support - + Cases Payments Outstanding + Support Sign in `} @@ -121,12 +121,12 @@ export const AppHeaderWithMenuClickEventExample = () => { [fullMenuBreakpoint]="+deviceWidth" (onMenuClick)="handleMenuClick()" > - Support - + Cases Payments Outstanding + Support Sign in `} @@ -162,12 +162,12 @@ export const AppHeaderWithMenuClickEventExample = () => { heading="Design System" onMenuClick={handleMenuClick} fullMenuBreakpoint={+deviceWidth}> - Support - + Cases Payments Outstanding + Support Sign in `} @@ -189,12 +189,12 @@ export const AppHeaderWithMenuClickEventExample = () => { heading="Design System" onMenuClick={handleMenuClick} fullMenuBreakpoint={+deviceWidth}> - Support - + Cases Payments Outstanding + Support Sign in `} diff --git a/src/examples/app-header/AppHeaderWithNavigationExample.tsx b/src/examples/app-header/AppHeaderWithNavigationExample.tsx index 43c1380dd..00698cbc0 100644 --- a/src/examples/app-header/AppHeaderWithNavigationExample.tsx +++ b/src/examples/app-header/AppHeaderWithNavigationExample.tsx @@ -17,13 +17,13 @@ export const AppHeaderWithNavigationExample = () => { allowCopy={true} code={` - - Support - + + Cases Payments Outstanding + Support Sign in `} @@ -36,13 +36,13 @@ export const AppHeaderWithNavigationExample = () => { allowCopy={true} code={` - - Support - + + Cases Payments Outstanding + Support Sign in `} @@ -56,13 +56,13 @@ export const AppHeaderWithNavigationExample = () => { tags="react" allowCopy={true} code={` - - Support - + + Cases Payments Outstanding + Support Sign in `} @@ -74,26 +74,26 @@ export const AppHeaderWithNavigationExample = () => { tags="react" allowCopy={true} code={` - - Support - + + Cases Payments Outstanding + Support Sign in `} /> )} - - Support - + + Cases Payments Outstanding + Support Sign in diff --git a/src/examples/badge/BadgeExamples.tsx b/src/examples/badge/BadgeExamples.tsx index a98d74e68..196242953 100644 --- a/src/examples/badge/BadgeExamples.tsx +++ b/src/examples/badge/BadgeExamples.tsx @@ -5,27 +5,34 @@ import { import { Sandbox } from "@components/sandbox"; import { BadgeShowStatusInTableExample } from "@examples/badge/BadgeShowStatusInTableExample.tsx"; import { BadgeShowStatusOnCardExample } from "@examples/badge/BadgeShowStatusOnCardExample.tsx"; +import { SandboxHeader } from "@components/sandbox/sandbox-header/sandboxHeader.tsx"; export default function BadgeExamples() { return ( <> - -

Show status in a table

- + + + -

Show multiple tags together

+ + - + -

Show a status on a card

+ + ); diff --git a/src/examples/badge/BadgeShowStatusInTableExample.tsx b/src/examples/badge/BadgeShowStatusInTableExample.tsx index dedd06e77..1e633f38b 100644 --- a/src/examples/badge/BadgeShowStatusInTableExample.tsx +++ b/src/examples/badge/BadgeShowStatusInTableExample.tsx @@ -288,7 +288,7 @@ export const BadgeShowStatusInTableExample = () => { 1234567890 - + Assign @@ -317,7 +317,7 @@ export const BadgeShowStatusInTableExample = () => { Lorem ipsum dolor sit amet consectetur 1234567890 - + Assign diff --git a/src/examples/button/ButtonAskUserAddressExample.tsx b/src/examples/button/ButtonAskUserAddressExample.tsx index 89c557280..b8c4f432c 100644 --- a/src/examples/button/ButtonAskUserAddressExample.tsx +++ b/src/examples/button/ButtonAskUserAddressExample.tsx @@ -7,7 +7,7 @@ import { GoabDropdown, GoabDropdownItem, GoabFormItem, - GoabInput, + GoabInput, GoabText } from "@abgov/react-components"; import { LanguageVersionContext } from "@contexts/LanguageVersionContext.tsx"; import { useContext } from "react"; @@ -91,25 +91,23 @@ export const ButtonAskUserAddressExample = () => { `} />} - - + What is your address? -
- + - - + + - + @@ -122,15 +120,14 @@ export const ButtonAskUserAddressExample = () => { - + - - Submit and continue + Save and continue Cancel diff --git a/src/examples/button/ButtonExamples.tsx b/src/examples/button/ButtonExamples.tsx index 1b04bbc12..d7c560f7b 100644 --- a/src/examples/button/ButtonExamples.tsx +++ b/src/examples/button/ButtonExamples.tsx @@ -1,21 +1,30 @@ import { ButtonAskUserAddressExample } from "@examples/button/ButtonAskUserAddressExample.tsx"; import { ButtonConfirmDestructiveActionExample } from "@examples/button/ButtonConfirmDestructiveActionExample.tsx"; import { ButtonDisabledWithRequiredFieldExample } from "@examples/button/ButtonDisabledWithRequiredFieldExample.tsx"; +import { SandboxHeader } from "@components/sandbox/sandbox-header/sandboxHeader.tsx"; export const ButtonExamples = () => { return <> - {/*Button Example 1*/} -

Ask a user for an address

+ + {/*Button example 2*/} -

Confirm a destructive action

+ + {/*Button example */} -

Disabled button with a required field

+ + ; }; diff --git a/src/examples/callout/CalloutExamples.tsx b/src/examples/callout/CalloutExamples.tsx index c4ae797bb..da8c8f5be 100644 --- a/src/examples/callout/CalloutExamples.tsx +++ b/src/examples/callout/CalloutExamples.tsx @@ -3,12 +3,16 @@ import { CodeSnippet } from "@components/code-snippet/CodeSnippet.tsx"; import { GoabButton, GoabButtonGroup, GoabCallout, GoabSpacer } from "@abgov/react-components"; import { useContext } from "react"; import { LanguageVersionContext } from "@contexts/LanguageVersionContext.tsx"; +import { SandboxHeader } from "@components/sandbox/sandbox-header/sandboxHeader.tsx"; export const CalloutExamples = () => { const {version} = useContext(LanguageVersionContext); return ( <> -

Confirm that an application was submitted

+ + {/*Angular code*/} {version === "old" && - -

Use tags in the description

+ <> + + {/*Angular*/} @@ -18,12 +21,12 @@ export default function CheckboxExamples () { tags="angular" allowCopy={true} code={` - - + + Help text with a link. - - + + `} />} @@ -33,14 +36,14 @@ export default function CheckboxExamples () { tags="angular" allowCopy={true} code={` - - + + Help text with a link. - - + + `} />} @@ -51,15 +54,15 @@ export default function CheckboxExamples () { tags="react" allowCopy={true} code={` - + Help text with a link.
} /> - - + + `} />} @@ -69,28 +72,28 @@ export default function CheckboxExamples () { tags="react" allowCopy={true} code={` - + Help text with a link.} /> - - + + `} />} - + Help text with a link.} /> - - + + diff --git a/src/examples/container/ContainerExamples.tsx b/src/examples/container/ContainerExamples.tsx index 2024aa90b..898c301c8 100644 --- a/src/examples/container/ContainerExamples.tsx +++ b/src/examples/container/ContainerExamples.tsx @@ -2,24 +2,36 @@ import { ContainerUserInformationExample } from "@examples/container/ContainerUs import { ContainerCaseFilesExample } from "@examples/container/ContainerCaseFilesExample.tsx"; import { ContainerCardGridExample } from "@examples/container/ContainerCardGridExample.tsx"; import { ContainerReviewActionExample } from "@examples/container/ContainerReviewActionExample.tsx"; +import { SandboxHeader } from "@components/sandbox/sandbox-header/sandboxHeader.tsx"; export default function ContainerExamples() { return ( <> {/*Container examples*/} - -

User information

+ + + -

Card view of case files

+ + -

Card grid that links to different sections

+ + -

Review and action

+ + + ); diff --git a/src/examples/date-picker/DatePickerExamples.tsx b/src/examples/date-picker/DatePickerExamples.tsx index d7bdc2e8c..90dee0aa4 100644 --- a/src/examples/date-picker/DatePickerExamples.tsx +++ b/src/examples/date-picker/DatePickerExamples.tsx @@ -1,13 +1,13 @@ import { DatePickerResetExample } from "@examples/date-picker/DatePickerResetExample.tsx"; +import { SandboxHeader } from "@components/sandbox/sandbox-header/sandboxHeader.tsx"; export const DatePickerExamples = () => { return ( <> - - -

Reset example

+ + ) diff --git a/src/examples/details/DetailsExamples.tsx b/src/examples/details/DetailsExamples.tsx index 7268c6fc7..d69324546 100644 --- a/src/examples/details/DetailsExamples.tsx +++ b/src/examples/details/DetailsExamples.tsx @@ -7,25 +7,27 @@ import { import { DetailsShowDirectDepositInformationExample } from "@examples/details/DetailsShowDirectDepositInformationExample.tsx"; +import { SandboxHeader } from "@components/sandbox/sandbox-header/sandboxHeader.tsx"; export const DetailsExamples = () => { return ( <> - - -

- Show more information for a basic question -

+ + -

- Additional information to help a user understand and answer a question -

+ + -

- Show more information to help a user fill out direct deposit information -

+ + ) diff --git a/src/examples/drawer/DrawerAddRecordExample.tsx b/src/examples/drawer/DrawerAddRecordExample.tsx new file mode 100644 index 000000000..a58dc3656 --- /dev/null +++ b/src/examples/drawer/DrawerAddRecordExample.tsx @@ -0,0 +1,267 @@ +import { + GoabBlock, + GoabButton, GoabCheckbox, GoabContainer, GoabDatePicker, + GoabDrawer, + GoabDropdown, + GoabDropdownItem, + GoabFormItem, GoabInput, GoabRadioGroup, GoabRadioItem +} from "@abgov/react-components"; +import { useState } from "react"; +import { CodeSnippet } from "@components/code-snippet/CodeSnippet.tsx"; + +export const DrawerAddRecordExample = () => { + const [open, setOpen] = useState(false); + const noop = () => {/* do something */} + return ( + <> + {/*Don't use a Sandbox because the animation slowing displaying the drawer isn't working if it is inside sandbox*/} + +
+ setOpen(true)}> + Add Record + +
+
+ setOpen(false)} + actions={ + setOpen(false)}> + Add + + } + > + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {/*React*/} + { + setOpen(true); + } + const drawerOnClose = () => { + setOpen(false); + } + const dropdownOnChange = (event: GoabDropdownOnChangeDetail) => { + console.log(event.value); + } + const radioGroupOnChange = (event: GoabRadioGroupOnChangeDetail) => { + console.log(event.value); + } + const inputOnChange = (event: GoabInputOnChangeDetail) => { + console.log(event.value); + } + const datePickerOnChange = (event: GoabDatePickerOnChangeDetail) => { + console.log(event.value); + } + `} + /> + + + Add Record + + setOpen(false)}> + Add + + }> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + `} + /> + + {/*Angular */} + + + Add Record + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Save + + + `} + /> + + ); +}; diff --git a/src/examples/drawer/DrawerExamples.tsx b/src/examples/drawer/DrawerExamples.tsx new file mode 100644 index 000000000..cd20609f5 --- /dev/null +++ b/src/examples/drawer/DrawerExamples.tsx @@ -0,0 +1,17 @@ +import { DrawerFiltersExample } from "@examples/drawer/DrawerFiltersExample.tsx"; +import { DrawerAddRecordExample } from "@examples/drawer/DrawerAddRecordExample.tsx"; + +export const DrawerExamples = () => { + return ( + <> + +

Filters

+ + +

Add record

+ + + ); +}; diff --git a/src/examples/drawer/DrawerFiltersExample.tsx b/src/examples/drawer/DrawerFiltersExample.tsx new file mode 100644 index 000000000..194229457 --- /dev/null +++ b/src/examples/drawer/DrawerFiltersExample.tsx @@ -0,0 +1,231 @@ +import { + GoabButton, + GoabCheckbox, + GoabContainer, + GoabDrawer, + GoabDropdown, + GoabDropdownItem, + GoabFormItem, + GoabRadioGroup, + GoabRadioItem, +} from "@abgov/react-components"; +import { useState } from "react"; +import { CodeSnippet } from "@components/code-snippet/CodeSnippet.tsx"; + +export const DrawerFiltersExample = () => { + const [open, setOpen] = useState(false); + // const noop = () => {/* do something */}; + return ( + <> + {/*Don't use a Sandbox because the animation slowing displaying the drawer isn't working if it is inside sandbox*/} + +
+ setOpen(true)}>Filters +
+ setOpen(false)} + position="right" + actions={ setOpen(false)}>Apply}> + + + + + + + + + + + + + { + /* do something */ + }}> + + + + + + + + + + + + + { + /* do something */ + }}> + + + + + + { + /** do something **/ + }}> + + + + + +
+ + {/*React*/} + { + setOpen(true); + } + const drawerOnClose = () => { + setOpen(false); + } + const radioGroupOnChange = (event: GoabRadioGroupOnChangeDetail) => { + console.log(event.value); + } + `} + /> + + Filters + + setOpen(false)}>Apply}> + + + + + + + + + + + + + {/* do something */}}> + + + + + + + + + + + + + {/* do something */}}> + + + + + + + + + + + + `} + /> + + {/*Angular*/} + + Filters + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Apply + + + `} + /> + + ); +}; diff --git a/src/examples/dropdown/DropdownAddItemExample.tsx b/src/examples/dropdown/DropdownAddItemExample.tsx index ef919f2e3..541ba23b3 100644 --- a/src/examples/dropdown/DropdownAddItemExample.tsx +++ b/src/examples/dropdown/DropdownAddItemExample.tsx @@ -71,7 +71,7 @@ export const DropdownAddItemExample = () => { return ( <> - +
{ }; return ( <> - +
{ return ( <> - -

Dynamically add an item to a dropdown list

+ + -

- Dynamically change items in a dropdown -

+ + + ); diff --git a/src/examples/filter-chip/FilterChipExamples.tsx b/src/examples/filter-chip/FilterChipExamples.tsx index 488c9a5ca..363e87c77 100644 --- a/src/examples/filter-chip/FilterChipExamples.tsx +++ b/src/examples/filter-chip/FilterChipExamples.tsx @@ -1,21 +1,34 @@ +import { TableWithGlobalFiltersExample } from "@examples/filter-chip/TableWithGlobalFiltersExample.tsx"; import { FilterChipDeleteEventExample } from "@examples/filter-chip/FilterChipDeleteEventExample.tsx"; import { FilterChipInteractiveExample } from "@examples/filter-chip/FilterChipInteractiveExample.tsx"; import { FilterChipTypedChipExample } from "@examples/filter-chip/FilterChipTypedChipExample.tsx"; +import { SandboxHeader } from "@components/sandbox/sandbox-header/sandboxHeader.tsx"; export const FilterChipExamples = () => { return ( <> - -

Delete Event

+ + -

Interactive Example

+ + -

Typed Chips Example

+ + + + + +

Filter data in a table

+ ) } diff --git a/src/examples/filter-chip/FilterChipTypedChipExample.tsx b/src/examples/filter-chip/FilterChipTypedChipExample.tsx index 2ae8f0f6c..9c892bcf3 100644 --- a/src/examples/filter-chip/FilterChipTypedChipExample.tsx +++ b/src/examples/filter-chip/FilterChipTypedChipExample.tsx @@ -1,68 +1,63 @@ -import { GoabContainer, GoabFilterChip, GoabInput } from "@abgov/react-components"; +import { GoabContainer, GoabFilterChip, GoabFormItem, GoabInput } from "@abgov/react-components"; import { CodeSnippet } from "@components/code-snippet/CodeSnippet.tsx"; import { useContext, useState } from "react"; import { LanguageVersionContext } from "@contexts/LanguageVersionContext.tsx"; +import { GoabInputOnChangeDetail, GoabInputOnKeyPressDetail } from "@abgov/ui-components-common"; export const FilterChipTypedChipExample = () => { const {version} = useContext(LanguageVersionContext); - const [typedChips, setTypedChips] = useState([ - "Typed Chip 1", - "Typed Chip 2", - "Typed Chip 3", - ]); - + const [typedChips, setTypedChips] = useState([]); const [inputValue, setInputValue] = useState(""); - const handleInputChange = (_name: string, value: string) => { - setInputValue(value); - }; - const handleInputKeyDown = (_name: string, value: string, key: string) => { - if (key === "Enter" && value.trim() !== "") { - setTypedChips(prevChips => [...prevChips, value.trim()]); - setTimeout(() => { - setInputValue(""); - }, 0); - } else if (key === "Backspace" && value === "" && typedChips.length > 0) { - setTypedChips(prevChips => prevChips.slice(0, -1)); - } - }; + + const handleInputChange = (detail: GoabInputOnChangeDetail) => { + const newValue = detail.value.trim(); + setInputValue(newValue); + }; + + const handleInputKeyPress = (detail: GoabInputOnKeyPressDetail) => { + const newValue = detail.value.trim(); + if (detail.key === "Enter" && newValue !== "") { + setTypedChips(prevChips => [...prevChips, newValue]); + setTimeout(() => { + setInputValue(""); + }, 0); + } else if (detail.key === "Backspace" && newValue === "" && typedChips.length > 0) { + setTypedChips(prevChips => prevChips.slice(0, -1)); + } + }; const removeTypedChip = (chip: string) => { setTypedChips(prevChips => prevChips.filter(c => c !== chip)); }; + return ( + // NOTE: Input onKeyPress functionality breaks when wrapped in Sandbox component + // <> -
- handleInputChange(detail.name, detail.value)} - onKeyPress={detail => handleInputKeyDown(detail.name, detail.value, detail.key)} - width="30ch" - mr="s" - /> - {typedChips.map((chip, index) => ( - removeTypedChip(chip)} - mr="s" - mt="s" - mb="s" - /> - ))} -
-
+ + + +
+ {typedChips.length > 0 && + typedChips.map((typedChip, index) => ( + removeTypedChip(typedChip)} + /> + ))} +
+ + {version === "old" && ( <> { tags="angular" allowCopy={true} code={` +export class FilterChipComponent { + typedChips: string[] = []; + inputValue = ""; - import { Component } from "@angular/core"; - - @Component({ - selector: "abgov-chip", - templateUrl: "./filter-chip.component.html", - styleUrl: "./filter-chip.component.css", - }) - export class FilterChipComponent { - - typedChips: string[] = ["Typed Chip 1", "Typed Chip 2", "Typed Chip 3"]; - inputValue = ""; + handleInputChange(event: Event): void { + const newValue = (event.target as HTMLInputElement).value.trim(); + this.inputValue = newValue; + } + handleInputKeyPress(event: KeyboardEvent): void { + const newValue = (event.target as HTMLInputElement).value.trim(); + if (event.key === "Enter" && newValue !== "") { + this.addChip(); + } else if (!this.inputValue && this.typedChips.length > 0 && event.key === "Backspace") { + this.typedChips.pop(); + } + } - onInput(event: Event): void { - this.inputValue = (event.target as HTMLInputElement).value; - } - - addChip(): void { - if (this.inputValue.trim()) { - this.typedChips.push(this.inputValue.trim()); - this.inputValue = ""; - } - } - - removeTypedChip(chip: string): void { - this.typedChips = this.typedChips.filter((c) => c !== chip); - } + addChip(): void { + if (this.inputValue.trim()) { + this.typedChips.push(this.inputValue.trim()); + this.inputValue = ""; + } + } - handleBackspace(event: KeyboardEvent): void { - if (!this.inputValue && this.typedChips.length > 0 && event.key === "Backspace") { - this.typedChips.pop(); - event.preventDefault(); - } - } - } - + removeTypedChip(chip: string): void { + this.typedChips = this.typedChips.filter((c) => c !== chip); + } +} `} /> { tags="angular" allowCopy={true} code={` - - - - + + + + +
+ + +
`} /> @@ -144,151 +133,193 @@ export const FilterChipTypedChipExample = () => { tags="angular" allowCopy={true} code={` - export class FilterChipComponent { - typedChips: string[] = ["Typed Chip 1", "Typed Chip 2", "Typed Chip 3"]; - inputValue = ""; +export class FilterChipComponent { + typedChips: string[] = []; + inputValue = ""; - onInput(detail: GoabInputOnChangeDetail): void { - this.inputValue = detail.value; - } + handleInputChange(detail: GoabInputOnChangeDetail): void { + const newValue = detail.value.trim(); + this.inputValue = newValue; + } - addChip(): void { - if (this.inputValue.trim()) { - this.typedChips.push(this.inputValue.trim()); - this.inputValue = ""; - } - } + handleInputKeyPress(detail: GoabInputOnKeyPressDetail): void { + const newValue = detail.value.trim(); + if (detail.key === "Enter" && newValue !== "") { + this.addChip(); + } else if (!this.inputValue && this.typedChips.length > 0 && detail.key === "Backspace") { + this.typedChips.pop(); + } + } - removeTypedChip(chip: string): void { - this.typedChips = this.typedChips.filter((c) => c !== chip); - } + addChip(): void { + if (this.inputValue.trim()) { + this.typedChips.push(this.inputValue.trim()); + this.inputValue = ""; + } + } - handleBackspace(event: KeyboardEvent): void { - if (!this.inputValue && this.typedChips.length > 0 && event.key === "Backspace") { - this.typedChips.pop(); - event.preventDefault(); - } - } - `} + removeTypedChip(chip: string): void { + this.typedChips = this.typedChips.filter((c) => c !== chip); + } +} + `} /> Typed Chip - - - - - `} + + + + +
+ + +
+ `} /> )} - ([ - "Typed Chip 1", - "Typed Chip 2", - "Typed Chip 3", - ]); - const [inputValue, setInputValue] = useState(""); - const handleInputChange = (name: string, value: string) => { - setInputValue(value); - }; + {version === "old" && ( + <> + ([]); + const [inputValue, setInputValue] = useState(""); + + const handleInputChange = (_name: string, value: string) => { + const newValue = value.trim(); + setInputValue(newValue); + }; - const handleInputKeyDown = (name: string, - value: string, - key: string) => { - if (key === "Enter" && value.trim() !== "") { - setTypedChips((prevChips) => [...prevChips, value.trim()]); - setTimeout(() => { - setInputValue(""); - }, 0); - } else if (key === "Backspace" - && value === "" - && typedChips.length > 0) { - setTypedChips((prevChips) => prevChips.slice(0, -1)); - } - }; + const handleInputKeyPress = (_name: string, value: string, key: string) => { + const newValue = value.trim(); + if (key === "Enter" && newValue !== "") { + setTypedChips(prevChips => [...prevChips, newValue]); + setTimeout(() => { + setInputValue(""); + }, 0); + } else if (key === "Backspace" && newValue === "" && typedChips.length > 0) { + setTypedChips(prevChips => prevChips.slice(0, -1)); + } + }; - const removeTypedChip = (chip: string) => { - setTypedChips((prevChips) => prevChips.filter((c) => c !== chip)); - }; - `} - /> - {version === "old" && ( - - {typedChips.map((chip, index) => ( - removeTypedChip(chip)} - mr="s" - mt="s" - mb="s"/> - ))} - `} + const removeTypedChip = (chip: string) => { + setTypedChips(prevChips => prevChips.filter(c => c !== chip)); + }; + `} + /> + + + +
+ {typedChips.length > 0 && + typedChips.map((typedChip, index) => ( + removeTypedChip(typedChip)} + /> + ))} +
+ `} + /> + )} + {version === "new" && ( - handleInputChange(detail.name, detail.value)} - onKeyPress={(detail) => handleInputKeyDown(detail.name, detail.value, detail.key)} - width="30ch" - mr="s" - /> - {typedChips.map((chip, index) => ( - removeTypedChip(chip)} - mr="s" - mt="s" - mb="s"/> - ))} - `} + <> + ([]); + const [inputValue, setInputValue] = useState(""); + + const handleInputChange = (detail: GoabInputOnChangeDetail) => { + const newValue = detail.value.trim(); + setInputValue(newValue); + }; + + const handleInputKeyPress = (detail: GoabInputOnKeyPressDetail) => { + const newValue = detail.value.trim(); + if (detail.key === "Enter" && newValue !== "") { + setTypedChips(prevChips => [...prevChips, newValue]); + setTimeout(() => { + setInputValue(""); + }, 0); + } else if (detail.key === "Backspace" && newValue === "" && typedChips.length > 0) { + setTypedChips(prevChips => prevChips.slice(0, -1)); + } + }; + + const removeTypedChip = (chip: string) => { + setTypedChips(prevChips => prevChips.filter(c => c !== chip)); + }; + `} + /> + + +
+
+ {typedChips.length > 0 && + typedChips.map((typedChip, index) => ( + removeTypedChip(typedChip)} + /> + ))} +
+ `} + /> + )} + // ); } diff --git a/src/examples/filter-chip/TableWithGlobalFiltersExample.tsx b/src/examples/filter-chip/TableWithGlobalFiltersExample.tsx new file mode 100644 index 000000000..918f38c31 --- /dev/null +++ b/src/examples/filter-chip/TableWithGlobalFiltersExample.tsx @@ -0,0 +1,914 @@ +import { useCallback, useContext, useEffect, useMemo, useState } from "react"; +import { + GoabBadge, + GoabBlock, + GoabButton, + GoabContainer, + GoabFilterChip, + GoabFormItem, + GoabInput, + GoabTable, + GoabText, +} from "@abgov/react-components"; +import type { + GoabBadgeType, + GoabInputOnChangeDetail, + GoabInputOnKeyPressDetail, +} from "@abgov/ui-components-common"; +import { LanguageVersionContext } from "@contexts/LanguageVersionContext.tsx"; +import { CodeSnippet } from "@components/code-snippet/CodeSnippet.tsx"; + +export const TableWithGlobalFiltersExample = () => { + const { version } = useContext(LanguageVersionContext); + + const [typedChips, setTypedChips] = useState([]); + const [inputValue, setInputValue] = useState(""); + const [inputError, setInputError] = useState(""); + const errorEmpty = "Empty filter"; + const errorDuplicate = "Enter a unique filter"; + const data = useMemo( + () => [ + { + status: { type: "information" as GoabBadgeType, text: "In progress" }, + name: "Ivan Schmidt", + id: "7838576954", + }, + { + status: { type: "success" as GoabBadgeType, text: "Completed" }, + name: "Luz Lakin", + id: "8576953364", + }, + { + status: { type: "information" as GoabBadgeType, text: "In progress" }, + name: "Keith McGlynn", + id: "9846041345", + }, + { + status: { type: "success" as GoabBadgeType, text: "Completed" }, + name: "Melody Frami", + id: "7385256175", + }, + { + status: { type: "important" as GoabBadgeType, text: "Updated" }, + name: "Frederick Skiles", + id: "5807570418", + }, + { + status: { type: "success" as GoabBadgeType, text: "Completed" }, + name: "Dana Pfannerstill", + id: "5736306857", + }, + ], + [] + ); + const [dataFiltered, setDataFiltered] = useState(data); + + const handleInputChange = (detail: GoabInputOnChangeDetail) => { + const newValue = detail.value.trim(); + setInputValue(newValue); + }; + + const handleInputKeyPress = (detail: GoabInputOnKeyPressDetail) => { + if (detail.key === "Enter") { + applyFilter(); + } + }; + + const applyFilter = () => { + if (inputValue === "") { + setInputError(errorEmpty); + return; + } + if (typedChips.length > 0 && typedChips.includes(inputValue)) { + setInputError(errorDuplicate); + return; + } + setTypedChips([...typedChips, inputValue]); + setTimeout(() => { + setInputValue(""); + }, 0); + setInputError(""); + }; + + const removeTypedChip = (chip: string) => { + setTypedChips(typedChips.filter(c => c !== chip)); + setInputError(""); + }; + + const checkNested = useCallback((obj: object, chip: string): boolean => { + return Object.values(obj).some(value => + typeof value === "object" && value !== null + ? checkNested(value, chip) + : typeof value === "string" && value.toLowerCase().includes(chip.toLowerCase()) + ); + }, []); + + const getFilteredData = useCallback( + (typedChips: string[]) => { + if (typedChips.length === 0) { + return data; + } + const filteredData = data.filter((item: object) => + typedChips.every(chip => checkNested(item, chip)) + ); + + return filteredData; + }, + [checkNested, data] + ); + + useEffect(() => { + setDataFiltered(getFilteredData(typedChips)); + }, [getFilteredData, typedChips]); + + return ( + // NOTE: Input onKeyPress functionality breaks when wrapped in Sandbox component + // + <> + + + + + + Filter + + + + + {typedChips.length > 0 && ( +
+ + Filter: + + {typedChips.length > 0 && + typedChips.map((typedChip, index) => ( + removeTypedChip(typedChip)} + /> + ))} + setTypedChips([])}> + Clear all + +
+ )} + + + + + Status + Name + ID Number + + + + {dataFiltered.map(item => ( + + + + + {item.name} + {item.id} + + ))} + + + + {dataFiltered.length === 0 && data.length > 0 && ( + + No results found + + )} +
+ + {version === "old" && ( + <> + c !== chip); + this.dataFiltered = this.getFilteredData(this.typedChips); + this.inputError = ""; + } + + removeAllTypedChips() { + this.typedChips = []; + this.dataFiltered = this.getFilteredData(this.typedChips); + this.inputError = ""; + } + + getFilteredData(typedChips: string[]) { + if (typedChips.length === 0) { + return this.data; + } + const filteredData = this.data.filter((item) => + typedChips.every((chip) => this.checkNested(item, chip)), + ); + return filteredData; + } + + checkNested(obj: object, chip: string): boolean { + return Object.values(obj).some((value) => + typeof value === "object" && value !== null + ? this.checkNested(value, chip) + : typeof value === "string" && value.toLowerCase().includes(chip.toLowerCase()), + ); + } +} + `} + /> + + + + + + Filter + + + + + + + Filter: + + + + Clear all + + + + + + + Status + Name + ID Number + + + + + + + + {{ item.name }} + {{ item.id }} + + + + + + No results found + + `} + /> + ([]); + const [inputValue, setInputValue] = useState(""); + const [inputError, setInputError] = useState(""); + const errorEmpty = "Empty filter"; + const errorDuplicate = "Enter a unique filter"; + const data = useMemo( + () => [ + { + status: { type: "information" as GoABadgeType, text: "In progress" }, + name: "Ivan Schmidt", + id: "7838576954", + }, + { + status: { type: "success" as GoABadgeType, text: "Completed" }, + name: "Luz Lakin", + id: "8576953364", + }, + { + status: { type: "information" as GoABadgeType, text: "In progress" }, + name: "Keith McGlynn", + id: "9846041345", + }, + { + status: { type: "success" as GoABadgeType, text: "Completed" }, + name: "Melody Frami", + id: "7385256175", + }, + { + status: { type: "important" as GoABadgeType, text: "Updated" }, + name: "Frederick Skiles", + id: "5807570418", + }, + { + status: { type: "success" as GoABadgeType, text: "Completed" }, + name: "Dana Pfannerstill", + id: "5736306857", + }, + ], + [], + ); + const [dataFiltered, setDataFiltered] = useState(data); + + const handleInputChange = (_name: string, value: string) => { + const newValue = value.trim(); + setInputValue(newValue); + }; + + const handleInputKeyPress = (_name: string, _value: string, key: string) => { + if (key === "Enter") { + applyFilter(); + } + }; + + const applyFilter = () => { + if (inputValue === "") { + setInputError(errorEmpty); + return; + } + if (typedChips.length > 0 && typedChips.includes(inputValue)) { + setInputError(errorDuplicate); + return; + } + setTypedChips([...typedChips, inputValue]); + setTimeout(() => { + setInputValue(""); + }, 0); + setInputError(""); + }; + + const removeTypedChip = (chip: string) => { + setTypedChips(typedChips.filter((c) => c !== chip)); + setInputError(""); + }; + + const checkNested = useCallback((obj: object, chip: string): boolean => { + return Object.values(obj).some((value) => + typeof value === "object" && value !== null + ? checkNested(value, chip) + : typeof value === "string" && value.toLowerCase().includes(chip.toLowerCase()), + ); + }, []); + + const getFilteredData = useCallback( + (typedChips: string[]) => { + if (typedChips.length === 0) { + return data; + } + const filteredData = data.filter((item: object) => + typedChips.every((chip) => checkNested(item, chip)), + ); + + return filteredData; + }, + [checkNested, data], + ); + + useEffect(() => { + setDataFiltered(getFilteredData(typedChips)); + }, [getFilteredData, typedChips]); + `} + /> + + + + + + + Filter + + + + + {typedChips.length > 0 && ( +
+ + Filter: + + {typedChips.length > 0 && + typedChips.map((typedChip, index) => ( + removeTypedChip(typedChip)} + /> + ))} + setTypedChips([])} + > + Clear all + +
+ )} + + + + + Status + Name + ID Number + + + + {dataFiltered.map((item) => ( + + + + + {item.name} + {item.id} + + ))} + + + + {dataFiltered.length === 0 && data.length > 0 && ( + No results found + )} + + `} + /> + + )} + + {version === "new" && ( + <> + c !== chip); + this.dataFiltered = this.getFilteredData(this.typedChips); + this.inputError = ""; + } + + removeAllTypedChips() { + this.typedChips = []; + this.dataFiltered = this.getFilteredData(this.typedChips); + this.inputError = ""; + } + + getFilteredData(typedChips: string[]) { + if (typedChips.length === 0) { + return this.data; + } + const filteredData = this.data.filter((item) => + typedChips.every((chip) => this.checkNested(item, chip)), + ); + return filteredData; + } + + checkNested(obj: object, chip: string): boolean { + return Object.values(obj).some((value) => + typeof value === "object" && value !== null + ? this.checkNested(value, chip) + : typeof value === "string" && value.toLowerCase().includes(chip.toLowerCase()), + ); + } +} + `} + /> + + + + + + Filter + + + + + + + Filter: + + + + Clear all + + + + + + + Status + Name + ID Number + + + + + + + + {{ item.name }} + {{ item.id }} + + + + + + No results found + + `} + /> + ([]); + const [inputValue, setInputValue] = useState(""); + const [inputError, setInputError] = useState(""); + const errorEmpty = "Empty filter"; + const errorDuplicate = "Enter a unique filter"; + const data = useMemo( + () => [ + { + status: { type: "information" as GoabBadgeType, text: "In progress" }, + name: "Ivan Schmidt", + id: "7838576954", + }, + { + status: { type: "success" as GoabBadgeType, text: "Completed" }, + name: "Luz Lakin", + id: "8576953364", + }, + { + status: { type: "information" as GoabBadgeType, text: "In progress" }, + name: "Keith McGlynn", + id: "9846041345", + }, + { + status: { type: "success" as GoabBadgeType, text: "Completed" }, + name: "Melody Frami", + id: "7385256175", + }, + { + status: { type: "important" as GoabBadgeType, text: "Updated" }, + name: "Frederick Skiles", + id: "5807570418", + }, + { + status: { type: "success" as GoabBadgeType, text: "Completed" }, + name: "Dana Pfannerstill", + id: "5736306857", + }, + ], + [] + ); + const [dataFiltered, setDataFiltered] = useState(data); + + const handleInputChange = (detail: GoabInputOnChangeDetail) => { + const newValue = detail.value.trim(); + setInputValue(newValue); + }; + + const handleInputKeyPress = (detail: GoabInputOnKeyPressDetail) => { + if (detail.key === "Enter") { + applyFilter(); + } + }; + + const applyFilter = () => { + if (inputValue === "") { + setInputError(errorEmpty); + return; + } + if (typedChips.length > 0 && typedChips.includes(inputValue)) { + setInputError(errorDuplicate); + return; + } + setTypedChips([...typedChips, inputValue]); + setTimeout(() => { + setInputValue(""); + }, 0); + setInputError(""); + }; + + const removeTypedChip = (chip: string) => { + setTypedChips(typedChips.filter(c => c !== chip)); + setInputError(""); + }; + + const checkNested = useCallback((obj: object, chip: string): boolean => { + return Object.values(obj).some(value => + typeof value === "object" && value !== null + ? checkNested(value, chip) + : typeof value === "string" && value.toLowerCase().includes(chip.toLowerCase()) + ); + }, []); + + const getFilteredData = useCallback( + (typedChips: string[]) => { + if (typedChips.length === 0) { + return data; + } + const filteredData = data.filter((item: object) => + typedChips.every(chip => checkNested(item, chip)) + ); + + return filteredData; + }, + [checkNested, data] + ); + + useEffect(() => { + setDataFiltered(getFilteredData(typedChips)); + }, [getFilteredData, typedChips]); + `} + /> + + + + + + + Filter + + + + + {typedChips.length > 0 && ( +
+ + Filter: + + {typedChips.length > 0 && + typedChips.map((typedChip, index) => ( + removeTypedChip(typedChip)} + /> + ))} + setTypedChips([])}> + Clear all + +
+ )} + + + + + Status + Name + ID Number + + + + {dataFiltered.map(item => ( + + + + + {item.name} + {item.id} + + ))} + + + + {dataFiltered.length === 0 && data.length > 0 && ( + No results found + )} + + `} + /> + + )} + + //
+ ); +}; diff --git a/src/examples/form-item/FormItemExamples.tsx b/src/examples/form-item/FormItemExamples.tsx index cab52ed8b..a409a01d6 100644 --- a/src/examples/form-item/FormItemExamples.tsx +++ b/src/examples/form-item/FormItemExamples.tsx @@ -1,17 +1,20 @@ import { FormItemSlottedHelperTextExample } from "@examples/form-item/FormItemSlottedHelperTextExample.tsx"; import { FormItemSlottedErrorTextExample } from "@examples/form-item/FormItemSlottedErrorTextExample.tsx"; +import { SandboxHeader } from "@components/sandbox/sandbox-header/sandboxHeader.tsx"; export const FormItemExamples = () => { return ( <> - - -

Slotted Helper Text

+ + -

Slotted Error Text

+ + ) diff --git a/src/examples/form-stepper/FormStepperExamples.tsx b/src/examples/form-stepper/FormStepperExamples.tsx index c69cef041..1cc2e0420 100644 --- a/src/examples/form-stepper/FormStepperExamples.tsx +++ b/src/examples/form-stepper/FormStepperExamples.tsx @@ -2,17 +2,21 @@ import { FormStepperControlledNavigationExample } from "@examples/form-stepper/FormStepperControlledNavigationExample.tsx"; import { FormStepperStepStatusExample } from "@examples/form-stepper/FormStepperStepStatusExample.tsx"; +import { SandboxHeader } from "@components/sandbox/sandbox-header/sandboxHeader.tsx"; export const FormStepperExamples = () => { return ( <> - -

Controlled Navigation

+ + -

Step status

+ + ) diff --git a/src/examples/link/LinkExamples.tsx b/src/examples/link/LinkExamples.tsx new file mode 100644 index 000000000..1b123de1d --- /dev/null +++ b/src/examples/link/LinkExamples.tsx @@ -0,0 +1,18 @@ +import { Sandbox } from "@components/sandbox"; +import { SandboxHeader } from "@components/sandbox/sandbox-header/sandboxHeader.tsx"; +import { GoabLink } from "@abgov/react-components"; + +export const LinkExamples = () => { + return ( + <> + + + + + + External link + + + + ); +} \ No newline at end of file diff --git a/src/examples/microsite-header/MicrositeHeaderExamples.tsx b/src/examples/microsite-header/MicrositeHeaderExamples.tsx index 0ea2b2e70..65834edd8 100644 --- a/src/examples/microsite-header/MicrositeHeaderExamples.tsx +++ b/src/examples/microsite-header/MicrositeHeaderExamples.tsx @@ -3,6 +3,7 @@ import { Sandbox } from "@components/sandbox"; import { CodeSnippet } from "@components/code-snippet/CodeSnippet.tsx"; import { useContext } from "react"; import { LanguageVersionContext } from "@contexts/LanguageVersionContext.tsx"; +import { SandboxHeader } from "@components/sandbox/sandbox-header/sandboxHeader.tsx"; export default function MicrositeHeaderExamples() { const { version } = useContext(LanguageVersionContext); @@ -12,10 +13,10 @@ export default function MicrositeHeaderExamples() { }; return ( <> - -

Custom click event handler (for feedback)

+ + {/*Angular code*/} @@ -133,7 +134,11 @@ export default function MicrositeHeaderExamples() { /> )} -

Slotted version

+ + + {/*Angular*/} {version === "old" && { return ( <> {/*Don't use a Sandbox because the Dropdown inside a modal will make the modal shifts everytime we tab from the dropdown*/} - + setAddItemModalOpen(true)}> Add another item diff --git a/src/examples/modal/ModalConfirmDestructiveActionExample.tsx b/src/examples/modal/ModalConfirmDestructiveActionExample.tsx index bf0fce3a0..c275e25e6 100644 --- a/src/examples/modal/ModalConfirmDestructiveActionExample.tsx +++ b/src/examples/modal/ModalConfirmDestructiveActionExample.tsx @@ -13,10 +13,10 @@ export const ModalConfirmDestructiveActionExample = () => { type="tertiary" leadingIcon="trash" onClick={() => setDestructiveModalOpen(true)}> - Delete my application + Delete record { type="primary" variant="destructive" onClick={() => setDestructiveModalOpen(false)}> - Delete application + Delete record }> @@ -56,13 +56,13 @@ export const ModalConfirmDestructiveActionExample = () => { tags="angular" allowCopy={true} code={` - Delete my application - + Delete record +

This action cannot be undone.

Cancel - Delete application + Delete record
@@ -75,13 +75,13 @@ export const ModalConfirmDestructiveActionExample = () => { tags="angular" allowCopy={true} code={` - Delete my application - + Delete record +

This action cannot be undone.

Cancel - Delete application + Delete record
@@ -105,9 +105,9 @@ export const ModalConfirmDestructiveActionExample = () => { tags="react" allowCopy={true} code={` - setOpen(true)}>Delete my application + setOpen(true)}>Delete record setOpen(false)} @@ -117,7 +117,7 @@ export const ModalConfirmDestructiveActionExample = () => { Cancel setOpen(false)}> - Delete application + Delete record } @@ -134,9 +134,9 @@ export const ModalConfirmDestructiveActionExample = () => { tags="react" allowCopy={true} code={` - setOpen(true)}>Delete my application + setOpen(true)}>Delete record setOpen(false)} @@ -146,7 +146,7 @@ export const ModalConfirmDestructiveActionExample = () => { Cancel setOpen(false)}> - Delete application + Delete record } diff --git a/src/examples/modal/ModalConfirmRecordChangeExample.tsx b/src/examples/modal/ModalConfirmRecordChangeExample.tsx index d8c039f51..edac7aeb1 100644 --- a/src/examples/modal/ModalConfirmRecordChangeExample.tsx +++ b/src/examples/modal/ModalConfirmRecordChangeExample.tsx @@ -22,7 +22,7 @@ export const ModalConfirmRecordChangeExample = () => { return ( <> {/*Don't use a Sandbox because Datepicker inside a modal will make the modal shifts everytime we click on datepicker*/} - + setInputModalOpen(true)}>Save and continue diff --git a/src/examples/modal/ModalExamples.tsx b/src/examples/modal/ModalExamples.tsx index 38822fc4b..0b896922b 100644 --- a/src/examples/modal/ModalExamples.tsx +++ b/src/examples/modal/ModalExamples.tsx @@ -5,29 +5,46 @@ import { ModalWarnUserDeadlineExample } from "@examples/modal/ModalWarnUserDeadl import { ModalConfirmRecordChangeExample } from "@examples/modal/ModalConfirmRecordChangeExample.tsx"; import { ModalAddAnotherItemExample } from "@examples/modal/ModalAddAnotherItemExample.tsx"; import { ModalRouteChangeExample } from "@examples/modal/ModalRouteChangeExample.tsx"; +import { SandboxHeader } from "@components/sandbox/sandbox-header/sandboxHeader.tsx"; export default function ModalExamples() { return ( <> - -

Basic Modal

+ + + -

Confirm a destructive action

+ + -

Warn a user of a deadline

+ + -

Confirm record change

+ + -

Add another item

+ + -

Route changes

+ + ); diff --git a/src/examples/pagination/PaginationExamples.tsx b/src/examples/pagination/PaginationExamples.tsx index 8a98352bc..f87f3615c 100644 --- a/src/examples/pagination/PaginationExamples.tsx +++ b/src/examples/pagination/PaginationExamples.tsx @@ -12,6 +12,7 @@ import { useContext, useEffect, useState } from "react"; import { LanguageVersionContext } from "@contexts/LanguageVersionContext.tsx"; import { GoabDropdownOnChangeDetail } from "@abgov/ui-components-common"; import { faker } from "@faker-js/faker"; +import { SandboxHeader } from "@components/sandbox/sandbox-header/sandboxHeader.tsx"; interface User { id: string; @@ -58,11 +59,11 @@ export const PaginationExamples = () => { return ( <> - -

Show X per page

+ + {/*============= React code ==============*/} {version === "old" && ( diff --git a/src/examples/radio/RadioExamples.tsx b/src/examples/radio/RadioExamples.tsx index c9fa0b997..291076480 100644 --- a/src/examples/radio/RadioExamples.tsx +++ b/src/examples/radio/RadioExamples.tsx @@ -1,16 +1,21 @@ import { RadioSlottedDescriptionExample } from "@examples/radio/RadioSlottedDescriptionExample.tsx"; import { RadioMaxWidthExample } from "@examples/radio/RadioMaxWidthExample.tsx"; +import { SandboxHeader } from "@components/sandbox/sandbox-header/sandboxHeader.tsx"; export default function RadioExamples () { return ( <> - -

Use tags in the description

+ + -

Radio item with max width

+ + + ); diff --git a/src/examples/tabs/TabsDifferentViewsTableExample.tsx b/src/examples/tabs/TabsDifferentViewsTableExample.tsx index c12f15530..063442ced 100644 --- a/src/examples/tabs/TabsDifferentViewsTableExample.tsx +++ b/src/examples/tabs/TabsDifferentViewsTableExample.tsx @@ -10,7 +10,7 @@ export const TabsDifferentViewsTableExample = () => { return ( <> - +
diff --git a/src/examples/tabs/TabsExamples.tsx b/src/examples/tabs/TabsExamples.tsx index 2671a3864..31f4161fb 100644 --- a/src/examples/tabs/TabsExamples.tsx +++ b/src/examples/tabs/TabsExamples.tsx @@ -1,17 +1,20 @@ import { TabsDifferentViewsTableExample } from "@examples/tabs/TabsDifferentViewsTableExample.tsx"; import { TabsSetSpecificTabActiveExample } from "@examples/tabs/TabsSetSpecificTabActiveExample.tsx"; +import { SandboxHeader } from "@components/sandbox/sandbox-header/sandboxHeader.tsx"; export const TabsExamples = () => { return ( <> - -

- Show different views of data in a table -

+ + -

Set a specific tab to be active

+ + + diff --git a/src/examples/tabs/TabsSetSpecificTabActiveExample.tsx b/src/examples/tabs/TabsSetSpecificTabActiveExample.tsx index 4bfe8bbd2..013e24101 100644 --- a/src/examples/tabs/TabsSetSpecificTabActiveExample.tsx +++ b/src/examples/tabs/TabsSetSpecificTabActiveExample.tsx @@ -10,9 +10,9 @@ export const TabsSetSpecificTabActiveExample = () => { return ( <> - +
- + @@ -113,7 +113,7 @@ export const TabsSetSpecificTabActiveExample = () => { tags="angular" allowCopy={true} code={` - +
All
@@ -208,7 +208,7 @@ export const TabsSetSpecificTabActiveExample = () => { tags="angular" allowCopy={true} code={` - + @@ -312,7 +312,7 @@ export const TabsSetSpecificTabActiveExample = () => { tags="react" allowCopy={true} code={` - + @@ -411,7 +411,7 @@ export const TabsSetSpecificTabActiveExample = () => { tags="react" allowCopy={true} code={` - + diff --git a/src/examples/text-field/TextFieldAskBirthdayExample.tsx b/src/examples/text-field/TextFieldAskBirthdayExample.tsx index 45b81c2d1..6e2be2c72 100644 --- a/src/examples/text-field/TextFieldAskBirthdayExample.tsx +++ b/src/examples/text-field/TextFieldAskBirthdayExample.tsx @@ -30,10 +30,7 @@ export const TextFieldAskBirthdayExample = () => { code={` For example, 27 November 2004}> - - setDay(event.value)} value={day} name="day" width="6ch"> - - + setMonth(event.value)} name="month" value={month}> @@ -49,8 +46,11 @@ export const TextFieldAskBirthdayExample = () => { + + setDay(event.value)} value={day} name="day" width="2ch"> + - setYear(event.value)} value={year} name="year" width="10ch"> + setYear(event.value)} value={year} name="year" width="4ch"> @@ -81,14 +81,6 @@ export const TextFieldAskBirthdayExample = () => { labelSize="large" helpText={"For example, 27 November 2004"}> - - - @@ -105,12 +97,20 @@ export const TextFieldAskBirthdayExample = () => { + + + diff --git a/src/examples/text-field/TextFieldAskUserAddressExample.tsx b/src/examples/text-field/TextFieldAskUserAddressExample.tsx deleted file mode 100644 index 1acc36cd9..000000000 --- a/src/examples/text-field/TextFieldAskUserAddressExample.tsx +++ /dev/null @@ -1,152 +0,0 @@ -import { Sandbox } from "@components/sandbox"; -import { GoabBlock, GoabDropdown, GoabDropdownItem, GoabFormItem, GoabInput } from "@abgov/react-components"; -import { useContext } from "react"; -import { LanguageVersionContext } from "@contexts/LanguageVersionContext.tsx"; -import { CodeSnippet } from "@components/code-snippet/CodeSnippet.tsx"; - -export const TextFieldAskUserAddressExample = () => { - const {version} = useContext(LanguageVersionContext); - const noop = () => {} - return ( - - {/*React code*/} - {version === "new" && (''); - const [suite, setSuite] = useState(''); - const [city, setCity] = useState(''); - const [province, setProvince] = useState('alberta'); - const [postalCode, setPostalCode] = useState(''); - `} - />} - - {version === "new" && - setStreet(event.value)} width="100%"> - - - setSuite(event.value)} width="100%"> - - - setCity(event.value)} width="100%"> - - - - setProvince(event.value)}> - - - - - - - - - - - - - - setPostalCode(event.value)}> - - - `} - />} - - {version === "old" && - - - - - - - - - - - - - - - - - - - - - - - - - - - - `} - />} - - {/*Angular code*/} - {version === "new" && } - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- ) -} diff --git a/src/examples/text-field/TextFieldExamples.tsx b/src/examples/text-field/TextFieldExamples.tsx index 9963f7fb7..a18809032 100644 --- a/src/examples/text-field/TextFieldExamples.tsx +++ b/src/examples/text-field/TextFieldExamples.tsx @@ -1,34 +1,45 @@ -import { TextFieldAskUserAddressExample } from "@examples/text-field/TextFieldAskUserAddressExample.tsx"; import { TextFieldAskBirthdayExample } from "@examples/text-field/TextFieldAskBirthdayExample.tsx"; import { TextFieldSearchExample } from "@examples/text-field/TextFieldSearchExample.tsx"; import { TextFieldAskUserAmountExample } from "@examples/text-field/TextFieldAskUserAmountExample.tsx"; import { TextFieldAskUserIndianRegistrationExample } from "@examples/text-field/TextFieldAskUserIndianRegistrationExample.tsx"; +import { SandboxHeader } from "@components/sandbox/sandbox-header/sandboxHeader.tsx"; +import { ButtonAskUserAddressExample } from "@examples/button/ButtonAskUserAddressExample.tsx"; + export default function TextFieldExamples() { return ( <> {/*Examples*/} - - -

Ask a user for an address

- + + + -

Ask a user for their birthday

+ + - + + -

Ask a user for dollar amounts or costs

+ + -

- Ask a user for their indian registration number -

+ + ); diff --git a/src/examples/textarea/TextAreaAskQuestionMoreInformationExample.tsx b/src/examples/textarea/TextAreaAskQuestionMoreInformationExample.tsx index 1012fac7d..ccd4f6d11 100644 --- a/src/examples/textarea/TextAreaAskQuestionMoreInformationExample.tsx +++ b/src/examples/textarea/TextAreaAskQuestionMoreInformationExample.tsx @@ -37,22 +37,22 @@ export const TextAreaAskQuestionMoreInformationExample = () => { Back -

Description

+

Submit a question about your benefits

- List all components and include a description, including the number of hours for - each. + If you need clarification about your benefit eligibility, payment schedule, or application status, submit your + question here.

- + label="Provide details about your situation" + helpText="Include specific details to help us answer your question quickly."> +
- +

- Break down your outline into easily digestible sections. This can help to ensure - that the document is well-organized and easy to navigate. + Include your benefit program name, mention any recent correspondence you received and/or provide any + relevant case or reference numbers.

diff --git a/src/examples/textarea/TextAreaExamples.tsx b/src/examples/textarea/TextAreaExamples.tsx index 6453d9590..bb3585941 100644 --- a/src/examples/textarea/TextAreaExamples.tsx +++ b/src/examples/textarea/TextAreaExamples.tsx @@ -1,14 +1,15 @@ import { TextAreaAskQuestionMoreInformationExample } from "@examples/textarea/TextAreaAskQuestionMoreInformationExample.tsx"; +import { SandboxHeader } from "@components/sandbox/sandbox-header/sandboxHeader.tsx"; export const TextAreaExamples = () => { return ( <> - -

Ask a question and give more information

+ + ) diff --git a/src/examples/tooltip/TooltipExamples.tsx b/src/examples/tooltip/TooltipExamples.tsx index 87384f962..559ea0a59 100644 --- a/src/examples/tooltip/TooltipExamples.tsx +++ b/src/examples/tooltip/TooltipExamples.tsx @@ -2,21 +2,29 @@ import { TooltipShowFullDateExample } from "@examples/tooltip/TooltipShowFullDat import { TooltipShowLabelForIconOnlyButtonExample } from "@examples/tooltip/TooltipShowLabelForIconOnlyButtonExample.tsx"; +import { SandboxHeader } from "@components/sandbox/sandbox-header/sandboxHeader.tsx"; export const TooltipExamples = () => { return ( <> - -

Use a tooltip to show a full date when shortened

+ + + -

Show a label on an icon only button

+ + + {/* This example won't import from styling.ts for some reason...

Click to copy something to your clipboard

- + + */} + ) } diff --git a/src/index.css b/src/index.css index 76a153ace..3c09f8730 100644 --- a/src/index.css +++ b/src/index.css @@ -12,8 +12,8 @@ html { a[href^="http"]::after, a[href^="https://"]::after { content: ""; - width: var(--goa-icon-size-l); - height: var(--goa-icon-size-l); + width: var(--goa-icon-size-m); + height: var(--goa-icon-size-m); background-color: var(--goa-color-interactive-default); display: inline-block; margin-left: var(--goa-space-xs); @@ -26,12 +26,18 @@ a[href^="https://"]:hover:after { background-color: var(--goa-color-interactive-hover); } +/* Hide external link icon when the class .no-external-icon is present */ +a.no-external-icon::after { + content: none !important; + display: none !important; +} + /* Open in new tab */ a[target="_blank"]::after { content: ""; display: inline-block; - width: 1.5rem; - height: 1.5rem; + width: var(--goa-icon-size-m); + height: var(--goa-icon-size-m); vertical-align: middle; margin-left: var(--goa-space-2xs); background: url('data:image/svg+xml,Open') center bottom no-repeat; diff --git a/src/routes/components/Accordion.tsx b/src/routes/components/Accordion.tsx index 0d004b60b..6390402b5 100644 --- a/src/routes/components/Accordion.tsx +++ b/src/routes/components/Accordion.tsx @@ -20,6 +20,8 @@ import { LegacyTestIdProperties, MarginProperty, TestIdProperty } from "@components/component-properties/common-properties.ts"; +import { DesignEmpty } from "@components/empty-states/design-empty/DesignEmpty.tsx"; +import { AccessibilityEmpty } from "@components/empty-states/accessibility-empty/AccessibilityEmpty.tsx"; // == Page props == const componentName = "Accordion"; @@ -29,6 +31,8 @@ const relatedComponents = [ { link: "/components/details", name: "Details" }, { link: "/components/tabs", name: "Tabs" }, ]; +const FIGMA_LINK = "https://www.figma.com/design/3pb2IK8s2QUqWieH79KdN7/%E2%9D%96-Component-library-%7C-DDD?node-id=15931-553576"; + type ComponentPropsType = GoabAccordionProps; type CastingType = { @@ -41,18 +45,12 @@ type CastingType = { export default function AccordionPage() { const [accordionProps, setAccordionProps] = useState({ - heading: "Heading", + heading: "Accordion", headingSize: "medium", children: <>, }); const [accordionBindings, setAccordionBindings] = useState([ - { - label: "Heading", - type: "string", - name: "heading", - value: "Accordion heading", - }, { label: "Heading Size", type: "radio", @@ -60,6 +58,12 @@ export default function AccordionPage() { options: ["small", "medium"], value: "medium", }, + { + label: "Heading", + type: "string", + name: "heading", + value: "Accordion heading", + }, { label: "Secondary Text", type: "string", @@ -67,12 +71,6 @@ export default function AccordionPage() { requirement: "optional", value: "", }, - { - label: "Open", - type: "boolean", - name: "open", - value: false, - }, { label: "Max Width", type: "string", @@ -80,6 +78,12 @@ export default function AccordionPage() { requirement: "optional", value: "", }, + { + label: "Open", + type: "boolean", + name: "open", + value: false, + } ]); const oldComponentProperties: ComponentProperty[] = [ @@ -223,35 +227,42 @@ export default function AccordionPage() { category={category} description={description} relatedComponents={relatedComponents} + githubLink="Accordion" + figmaLink={FIGMA_LINK} /> - - + +

- Component + Playground

- Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor - incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud - exercitation ullamco laboris nisi + This is the content in an accordion item. This content can be anything that you want including rich + text, components, and more. +
+ + Examples + + } + > + + + + + + - - Design guidelines - - - }>
diff --git a/src/routes/components/AllComponents.tsx b/src/routes/components/AllComponents.tsx index 2ba71dcae..5b60143bd 100644 --- a/src/routes/components/AllComponents.tsx +++ b/src/routes/components/AllComponents.tsx @@ -1,436 +1,655 @@ -import { GoabGrid, GoabInput, GoabFormItem } from "@abgov/react-components"; +import { useEffect, useState } from "react"; +import { ComponentStatus } from "@components/component-card/ComponentCard"; +import { toSentenceCase, fetchAllIssueCounts } from "../../utils"; +import { + GoabTable, + GoabTableSortHeader, + GoabTabs, + GoabTab, + GoabGrid, + GoabText, + GoabFormItem, + GoabInput, + GoabBadge +} from "@abgov/react-components"; import { ComponentCard, Props as ComponentProps } from "@components/component-card/ComponentCard"; -import { ReactNode, useState } from "react"; -import "./AllComponents.css"; -import { ComponentContent } from "@components/component-content/ComponentContent"; -import { GoabInputOnChangeDetail } from "@abgov/ui-components-common"; -export default function AllComponentsPage() { - const cards: ComponentProps[] = [ - { - name: "accordion", - groups: ["content"], - tags: ["blind", "collapse", "content layout", "expandable panel", "expand"], - description: "Let users show and hide sections of related content on a page.", - }, - { - name: "callout", - groups: ["content"], - tags: ["alert", "feedback and alerts", "show more information"], - description: "Communicate important information through a strong visual emphasis.", - }, - { - name: "container", - groups: ["content"], - tags: ["card", "content", "content layout", "group", "structure"], - description: "Group information, create hierarchy, and show related information.", - }, - { - name: "details", - groups: ["content"], - tags: [ - "accordion details", - "additional info", - "additional information", - "content layout", - "detail accordion", - "details expander", - "details toggle", - "disclosure", - "expand", - "expander", - "expanding detail", - "expandable details", - "expandable help text", - "more info", - "see more", - "show more", - "show more information", - ], - description: "Let users reveal more detailed information when they need it.", - }, - { - name: "hero banner", - groups: ["content"], - tags: ["promotion banner", "hero panel", "structure and navigation"], - description: "A visual band of text, including an image and a call to action.", - }, - { - name: "list", - groups: ["content"], - tags: ["content layout"], - description: "Organize information into brief and clear groups.", - }, - { - name: "popover", - groups: ["content"], - tags: ["content layout", "show more information"], - description: "A small overlay that opens on demand, used in other components.", - }, - { - name: "table", - groups: ["content"], - tags: ["data table", "content layout", "interactive table", "sortable table"], - description: - "A set of structured data that is easy for a user to scan, examine, and compare.", - }, - { - name: "badge", - groups: ["feedback"], - tags: ["feedback and alerts", "label", "lozenge", "status", "tag", "token"], - description: - "Small labels which hold small amounts of information, system feedback, or states.", - }, - { - name: "chip", - groups: ["feedback"], - tags: ["inputs and actions", "pill"], - description: "Allow the user to enter information, filter content, and make selections.", - }, - { - name: "modal", - groups: ["feedback"], - tags: [ - "feedback and alerts", - "popup modal", - "popup box", - "modal dialog", - "show more information", - ], - description: "An overlay that appears in front of all other content, and requires a user to take an action before continuing.", - }, - { - name: "notification banner", - groups: ["feedback"], - tags: ["alert banner", "banner", "feedback and alerts"], - description: "Display important page level information or notifications.", - }, - { - name: "progress indicator", - groups: ["feedback"], - tags: [ - "feedback and alerts", - "loading", - "loader", - "loading indicator", - "progress spinner", - "process timer", - "spinner", - ], - description: "Provide feedback of progress to users while loading.", - }, - { - name: "skeleton loading", - groups: ["feedback"], - tags: ["content layout", "loading"], - description: - "Provide visual feedback to users while loading a content heavy page or page element.", - }, - { - name: "tooltip", - groups: ["feedback"], - tags: ["feedback and alerts"], - description: "A small popover that displays more information about an item.", - }, - { - name: "button", - groups: ["inputs"], - tags: ["action", "inputs and actions", "submit"], - description: "Carry out an important action or navigate to another page.", - }, - { - name: "button group", - groups: ["inputs"], - tags: ["action", "inputs and actions", "submit"], - description: - "Display multiple related actions stacked or in a horizontal row to help with arrangement and spacing.", - }, - { - name: "checkbox", - groups: ["inputs"], - tags: ["checkbox", "checklist", "input", "inputs and actions", "options"], - description: "Let the user select one or more options.", - }, - { - name: "date picker", - groups: ["inputs"], - description: - "Lets users select a date through a calendar without the need to manually type it in a field.", - }, - { - name: "dropdown", - groups: ["inputs"], - tags: ["inputs and actions", "select", "single select dropdown"], - description: "Present a list of options to the user to select from.", - }, - { - name: "file uploader", - groups: ["inputs"], - tags: ["drag and drop", "file upload", "inputs and actions", "uploader"], - description: "Help users select and upload a file.", - }, - { - name: "icon button", - groups: ["inputs"], - tags: ["action", "inputs and actions", "submit"], - description: "A compact button with an icon and no text.", - }, - { - name: "input", - groups: ["inputs"], - tags: ["inputs and actions", "text field", "text box"], - description: "A single-line field where users can input and edit text.", - }, - { - name: "radio", - groups: ["inputs"], - tags: ["inputs and actions", "option button", "radio button group", "radio buttons"], - description: "Allow users to select one option from a set.", - }, - { - name: "text area", - groups: ["inputs"], - tags: [ - "inputs and actions", - "long answer input field", - "multi line text input", - "multiple text box", - "text box", - "text input area", - ], - description: "A multi-line field where users can input and edit text.", - }, - { - name: "footer", - groups: ["structure"], - tags: ["page footer", "structure and navigation"], - description: "Provides information related your service at the bottom of every page.", - }, - { - name: "form stepper", - groups: ["structure"], - tags: [ - "form stepper", - "horizontal step navigation", - "process overview", - "progress indicator", - "steps", - "structure and navigation", - "wizard", - ], - description: "Provides a visual representation of a form through a series of steps.", - }, - { - name: "header", - groups: ["structure"], - tags: [ - "app header", - "global navigation", - "header", - "header and navigation", - "main navigation", - "navigation header", - "navigation menu", - "primary navigation", - "service header", - "structure and navigation", - "top navigation", - ], - description: "Provide structure to help users find their way around the service.", - }, - { - name: "microsite header", - groups: ["structure"], - tags: [ - "banner", - "development header", - "feedback bar", - "microheader", - "microsite banner", - "service header", - "service bar", - "service stage banner", - "service state banner", - "status bar", - "structure and navigation", - ], - description: "Communicate what stage the service is at, connect to Alberta.ca, and gather feedback on your service.", - }, - { - name: "pagination", - groups: ["structure"], - tags: ["pager", "pagination controls", "structure and navigation"], - description: "Help users navigation between multiple pages or screens as part of a set.", - }, - { - name: "side menu", - groups: ["structure"], - tags: ["secondary navigation", "side nav", "side bar", "structure and navigation"], - description: "A side navigation that helps the user navigate between pages.", - }, - { - name: "tabs", - groups: ["structure"], - tags: ["sections", "structure and navigation", "tabbed interface"], - description: - "Let users navigate between related sections of content, displaying one section at a time.", - }, - { - name: "block", - groups: ["utilities"], - tags: ["utility"], - description: "Used when grouping components into a block with consistent space between.", - }, - { - name: "divider", - groups: ["utilities"], - tags: ["dividing line", "page divider", "utilities"], - description: - "Indicate a separation of layout, or to distinguish large chunks of information on a page.", - }, - { - name: "form item", - groups: ["utilities"], - tags: ["form", "input", "inputs and actions"], - description: - "Wraps an input control with a text label, requirement label, helper text, and error text.", - }, - { - name: "grid", - groups: ["utilities"], - tags: ["utilities"], - description: - "Arrange a number of components into a responsive grid pattern.", - }, - { - name: "icons", - groups: ["utilities"], - tags: ["utilities"], - description: - "A simple and universal graphic symbol representing an action, object, or concept to help guide the user.", - }, - { - name: "spacer", - groups: ["utilities"], - tags: ["gap", "margin", "padding", "space", "utilities"], - description: "Negative area between the components and the interface.", - }, - { - name: "text", - groups: ["utilities"], - tags: ["heading", "font", "typography", "utilities"], - description: "Provides consistent sizing, spacing, and colour to written content." - } - ]; +const AllComponents = () => { + const [filter, setFilter] = useState(""); + const [issueCounts, setIssueCounts] = useState>({}); + const [cards, setCards] = useState(() => { + const initialCards: ComponentProps[] = [ + { + name: "accordion", + groups: ["Content layout"], + tags: ["blind", "collapse", "content layout", "expandable panel", "expand"], + description: "Let users show and hide sections of related content on a page.", + status: "Published", + }, + { + name: "callout", + groups: ["Content layout"], + tags: ["alert", "feedback and alerts", "show more information"], + description: "Communicate important information through a strong visual emphasis.", + status: "Published", + }, + { + name: "container", + groups: ["Content layout"], + tags: ["card", "content", "content layout", "group", "structure"], + description: "Group information, create hierarchy, and show related information.", + status: "Published", + }, + { + name: "details", + groups: ["Content layout"], + tags: [ + "accordion details", + "additional info", + "additional information", + "content layout", + "detail accordion", + "details expander", + "details toggle", + "disclosure", + "expand", + "expander", + "expanding detail", + "expandable details", + "expandable help text", + "more info", + "see more", + "show more", + "show more information", + ], + description: "Let users reveal more detailed information when they need it.", + status: "Published", + }, + { + name: "hero banner", + groups: ["Content layout"], + tags: ["promotion banner", "hero panel", "structure and navigation"], + description: "A visual band of text, including an image and a call to action.", + status: "Published", + }, + { + name: "list", + groups: ["Content layout"], + tags: ["content layout"], + description: "Organize information into brief and clear groups.", + status: "Published", + }, + { + name: "popover", + groups: ["Content layout"], + tags: ["content layout", "show more information"], + description: "A small overlay that opens on demand, used in other components.", + status: "Published", + }, + { + name: "table", + groups: ["Content layout"], + tags: ["data table", "content layout", "interactive table", "sortable table"], + description: + "A set of structured data that is easy for a user to scan, examine, and compare.", + status: "Published", + }, + { + name: "badge", + groups: ["Feedback and alerts"], + tags: ["feedback and alerts", "label", "lozenge", "status", "tag", "token"], + description: + "Small labels which hold small amounts of information, system feedback, or states.", + status: "Published", + }, + { + name: "filter chip", + groups: ["Feedback and alerts"], + tags: ["inputs and actions", "pill"], + description: "Allow the user to enter information, filter content, and make selections.", + status: "Published", + }, + { + name: "modal", + groups: ["Feedback and alerts"], + tags: [ + "feedback and alerts", + "popup modal", + "popup box", + "modal dialog", + "show more information", + ], + description: + "An overlay that appears in front of all other content, and requires a user to take an action before continuing.", + status: "Published", + }, + { + name: "notification banner", + groups: ["Feedback and alerts"], + tags: ["alert banner", "banner", "feedback and alerts"], + description: "Display important page level information or notifications.", + status: "Published", + }, + { + name: "progress indicator", + groups: ["Feedback and alerts"], + tags: [ + "feedback and alerts", + "loading", + "loader", + "loading indicator", + "progress spinner", + "process timer", + "spinner", + ], + description: "Provide feedback of progress to users while loading.", + status: "Published", + }, + { + name: "skeleton loader", + groups: ["Feedback and alerts"], + tags: ["content layout", "loading"], + description: + "Provide visual feedback to users while loading a content heavy page or page element.", + status: "Published", + }, + { + name: "tooltip", + groups: ["Feedback and alerts"], + tags: ["feedback and alerts"], + description: "A small popover that displays more information about an item.", + status: "Published", + }, + { + name: "button", + groups: ["Inputs and actions"], + tags: ["action", "inputs and actions", "submit"], + description: "Carry out an important action or navigate to another page.", + status: "Published", + }, + { + name: "button group", + groups: ["Inputs and actions"], + tags: ["action", "inputs and actions", "submit"], + description: + "Display multiple related actions stacked or in a horizontal row to help with arrangement and spacing.", + status: "Published", + }, + { + name: "checkbox", + groups: ["Inputs and actions"], + tags: ["checkbox", "checklist", "input", "inputs and actions", "options"], + description: "Let the user select one or more options.", + status: "Published", + }, + { + name: "date picker", + groups: ["Inputs and actions"], + tags: ["calendar, date, date picker, inputs and actions, input"], + description: + "Lets users select a date through a calendar without the need to manually type it in a field.", + status: "Published", + }, + { + name: "drawer", + groups: ["Structure and navigation"], + tags: ["sections", "structure and navigation", "tabbed interface"], + description: + "A panel that slides in from the side of the screen to display additional content or actions without navigating away from the current view.", + status: "Published", + isNew: true, + }, + { + name: "dropdown", + groups: ["Inputs and actions"], + tags: ["inputs and actions", "select", "single select dropdown"], + description: "Present a list of options to the user to select from.", + status: "Published", + }, + { + name: "file uploader", + groups: ["Inputs and actions"], + tags: ["drag and drop", "file upload", "inputs and actions", "uploader"], + description: "Help users select and upload a file.", + status: "Published", + }, + { + name: "icon button", + groups: ["Inputs and actions"], + tags: ["action", "inputs and actions", "submit"], + description: "A compact button with an icon and no text.", + status: "Published", + }, + { + name: "input", + groups: ["Inputs and actions"], + tags: ["inputs and actions", "text field", "text box", "search", "text input"], + description: "A single-line field where users can input and edit text.", + status: "Published", + }, + { + name: "radio", + groups: ["Inputs and actions"], + tags: ["inputs and actions", "option button", "radio button group", "radio buttons"], + description: "Allow users to select one option from a set.", + status: "Published", + }, + { + name: "text area", + groups: ["Inputs and actions"], + tags: [ + "inputs and actions", + "long answer input field", + "multi line text input", + "multiple text box", + "text box", + "text input area", + ], + description: "A multi-line field where users can input and edit text.", + status: "Published", + }, + { + name: "footer", + groups: ["Structure and navigation"], + tags: ["page footer", "structure and navigation"], + description: "Provides information related your service at the bottom of every page.", + status: "Published", + }, + { + name: "form stepper", + groups: ["Structure and navigation"], + tags: [ + "form stepper", + "horizontal step navigation", + "process overview", + "progress indicator", + "steps", + "structure and navigation", + "wizard", + ], + description: "Provides a visual representation of a form through a series of steps.", + status: "Published", + }, + { + name: "header", + groups: ["Structure and navigation"], + tags: [ + "app header", + "global navigation", + "header", + "header and navigation", + "main navigation", + "navigation header", + "navigation menu", + "primary navigation", + "service header", + "structure and navigation", + "top navigation", + ], + description: "Provide structure to help users find their way around the service.", + status: "Published", + }, + { + name: "microsite header", + groups: ["Structure and navigation"], + tags: [ + "banner", + "development header", + "feedback bar", + "microheader", + "microsite banner", + "service header", + "service bar", + "service stage banner", + "service state banner", + "status bar", + "structure and navigation", + ], + description: + "Communicate what stage the service is at, connect to Alberta.ca, and gather feedback on your service.", + status: "Published", + }, + { + name: "pagination", + groups: ["Structure and navigation"], + tags: ["pager", "pagination controls", "structure and navigation"], + description: + "Help users navigation between multiple pages or screens as part of a set.", + status: "Published", + }, + { + name: "side menu", + groups: ["Structure and navigation"], + tags: ["secondary navigation", "side nav", "side bar", "structure and navigation"], + description: "A side navigation that helps the user navigate between pages.", + status: "Published", + }, + { + name: "tabs", + groups: ["Structure and navigation"], + tags: ["sections", "structure and navigation", "tabbed interface"], + description: + "Let users navigate between related sections of content, displaying one section at a time.", + status: "Published", + }, + { + name: "block", + groups: ["Utilities"], + tags: ["utility"], + description: + "Used when grouping components into a block with consistent space between.", + status: "Published", + }, + { + name: "divider", + groups: ["Utilities"], + tags: ["dividing line", "page divider", "utilities"], + description: + "Indicate a separation of layout, or to distinguish large chunks of information on a page.", + status: "Published", + }, + { + name: "form item", + groups: ["Utilities"], + tags: ["form", "input", "inputs and actions"], + description: + "Wraps an input control with a text label, requirement label, helper text, and error text.", + status: "Published", + }, + { + name: "grid", + groups: ["Utilities"], + tags: ["utilities"], + description: "Arrange a number of components into a responsive grid pattern.", + status: "Published", + }, + { + name: "icons", + groups: ["Utilities"], + tags: ["utilities"], + description: + "A simple and universal graphic symbol representing an action, object, or concept to help guide the user.", + status: "Published", + }, + { + name: "link", + groups: ["Utilities"], + tags: ["utilities"], + description: "Wraps an anchor element to add icons or margins.", + status: "Published", + }, + { + name: "spacer", + groups: ["Utilities"], + tags: ["gap", "margin", "padding", "space", "utilities"], + description: "Negative area between the components and the interface.", + status: "Published", + }, + { + name: "toggle button", + groups: ["Inputs and actions"], + tags: ["button", "inputs and actions"], + description: "Currently tracking this need in services.", + status: "Not Published", + }, + { + name: "back button", + groups: ["Inputs and actions"], + tags: ["button", "inputs and actions"], + description: "Related to form pattern, currently tracking this need in services.", + status: "Not Published", + }, + { + name: "breadcrumb", + groups: ["Structure and navigation"], + tags: ["breadcrumb"], + description: "Currently tracking this need in services.", + status: "Not Published", + }, + { + name: "drawer", + groups: ["Structure and navigation"], + tags: ["Drawer", "Sheet", "Sidesheet", "Sidepanel"], + description: "There is currently an experimental drawer in development.", + status: "In Progress", + }, + { + name: "error summary", + groups: ["Feedback and alerts"], + tags: ["Error"], + description: "Related to form pattern, currently tracking this need in services.", + status: "Not Published", + }, + { + name: "floating action button", + groups: ["Inputs and actions"], + tags: ["FAB"], + description: "Currently tracking this need in services.", + status: "Not Published", + }, + { + name: "quick exit", + groups: ["Structure and navigation"], + tags: ["Quick exit"], + description: "Currently tracking this need in services.", + status: "Not Published", + }, + { + name: "skip to content", + groups: ["Structure and navigation"], + tags: ["Skip to content", "accessibility"], + description: "Currently tracking this need in services.", + status: "Not Published", + }, + { + name: "rich text editor", + groups: ["Inputs and actions"], + tags: ["Skip to content", "accessibility"], + description: "Currently tracking this need in services.", + status: "Not Published", + }, + { + name: "temporary notification", + groups: ["Feedback and alerts"], + tags: ["Snackbar", "Toast", "Temporary notification"], + description: "Planned for development", + status: "Not Published", + }, + { + name: "menu button", + groups: ["Inputs and actions"], + tags: ["Menu button", "split button"], + description: "Planned for development.", + status: "Not Published", + }, + { + name: "time picker", + groups: ["Inputs and actions"], + tags: ["Time picker", "date picker"], + description: "Currently tracking this need in services.", + status: "Not Published", + }, + { + name: "text", + groups: ["Content layout"], + tags: ["text", "body", "copy"], + description: "Provides consistent sizing, spacing, and colour to written content.", + status: "Published" + }, + ]; - function getComponentsByGroup(group: string): ReactNode[] { - return cards - .filter(card => card.groups.includes(group)) - .map((card: ComponentProps) => ( - - )); - } + return initialCards.sort((a, b) => { + const statusOrder: ComponentStatus[] = ["Published", "In Progress", "Not Published"]; + const statusComparison = statusOrder.indexOf(a.status) - statusOrder.indexOf(b.status); + if (statusComparison !== 0) return statusComparison; + return a.name.localeCompare(b.name); + }); + }); + const [sortDirection, setSortDirection] = useState<{ [key: string]: number }>({ + status: -1, + name: 1, + }); - function getComponentsByFilter(): ReactNode[] { - return cards - .filter( - card => - card.name.includes(filter.toLowerCase()) || (card.tags && card.tags.some(tag => tag.includes(filter.toLowerCase()))) - ) - .map((card: ComponentProps) => ( - - )); - } + const filteredCards = cards.filter((card) => { + const filterLowerCase = (filter || "").toLowerCase(); + const nameMatches = card.name.toLowerCase().includes(filterLowerCase); + const tagsMatch = card.tags?.some((tag) => tag.toLowerCase().includes(filterLowerCase)); + return !filter || nameMatches || tagsMatch; + }); - const [mode, setMode] = useState<"list" | "search">("list"); - const [filter, setFilter] = useState(""); + const sortData = (detailOrSortBy: string | { sortBy: string }) => { + // If a string is passed, use it directly; otherwise extract the sortBy property + const sortBy = typeof detailOrSortBy === "string" ? detailOrSortBy : detailOrSortBy.sortBy; - function filterComponents(value: string) { - if (value === "") { - setMode("list"); - return; - } - setFilter(value); - setMode("search"); - } + const newDirection = sortDirection[sortBy] === 1 ? -1 : 1; + const sorted = [...cards].sort((a, b) => { + if (sortBy === "status") { + const statusOrder: ComponentStatus[] = ["Published", "In Progress", "Not Published"]; + const statusComparison = statusOrder.indexOf(a.status) - statusOrder.indexOf(b.status); + if (statusComparison !== 0) return statusComparison * newDirection; + } + const key = sortBy as keyof ComponentProps; + const aField = a[key]; + const bField = b[key]; - return ( - <> - -
-

Components

-

- Components are reusable parts of the user interface that have been made to support a variety - of applications. You can use individual components in many different patterns and contexts. -

-
+ const aValue = + sortBy === "name" + ? a.name.toLowerCase() + : Array.isArray(aField) + ? (aField.length > 0 ? aField[0] : "") + : (aField ?? ""); + const bValue = + sortBy === "name" + ? b.name.toLowerCase() + : Array.isArray(bField) + ? (bField.length > 0 ? bField[0] : "") + : (bField ?? ""); - - filterComponents(event.value)} - /> - + if (aValue > bValue) return newDirection; + if (aValue < bValue) return -newDirection; + return 0; + }); - {mode === "search" && {getComponentsByFilter()}} + setCards(sorted); + setSortDirection({ [sortBy]: newDirection }); + }; - {mode === "list" && ( - -

Content layout

- - {getComponentsByGroup("content")} - -
+ // Helper to format the label query for REST URLs + const getLabelQuery = (name: string) => { + const formatted = toSentenceCase(name); + return formatted.includes(" ") ? `"${formatted}"` : formatted; + }; -

Feedback and alerts

- - {getComponentsByGroup("feedback")} - - + // Helper to format the label query for GraphQL (escaping inner quotes) + useEffect(() => { + const loadIssueCounts = async () => { + const issueCounts = await fetchAllIssueCounts(cards); + setIssueCounts(issueCounts); + }; -

Inputs and actions

- - {getComponentsByGroup("inputs")} - - + loadIssueCounts(); + }, [cards]); -

Structure and navigation

- - {getComponentsByGroup("structure")} - - + const renderTable = () => ( + + + + + + Status + + + + + Name + + + + + Category + + + Open issues + + + + {filteredCards.map((card) => ( + + + + + + {card.status === "Published" ? ( + + {toSentenceCase(card.name)} + + ) : ( + {toSentenceCase(card.name)} + )} + + {card.groups.join(", ")} + + + View + {issueCounts[card.name] !== undefined && ` (${issueCounts[card.name]})`} + + + + ))} + + + ); -

Utilities

+ return ( +
+ + Components + + + Components are reusable parts of the user interface that have been made to support a variety of applications. You can use individual components in many different patterns and contexts. + + + + setFilter(value || "")} /> + + + + - {getComponentsByGroup("utilities")} + {filteredCards.map((card) => ( + + ))} - - - )} - + + {renderTable()} + +
); -} +}; + +export default AllComponents; diff --git a/src/routes/components/AppFooter.tsx b/src/routes/components/AppFooter.tsx index b9d76ed6c..64db3a71f 100644 --- a/src/routes/components/AppFooter.tsx +++ b/src/routes/components/AppFooter.tsx @@ -15,9 +15,12 @@ import { ComponentContent } from "@components/component-content/ComponentContent import { useState, useContext } from "react"; import { LanguageVersionContext } from "@contexts/LanguageVersionContext.tsx"; import { AppFooterExamples } from "@examples/app-footer/AppFooterExamples.tsx"; +import { DesignEmpty } from "@components/empty-states/design-empty/DesignEmpty.tsx"; +import { AccessibilityEmpty } from "@components/empty-states/accessibility-empty/AccessibilityEmpty.tsx"; // == Page props == +const FIGMA_LINK = "https://www.figma.com/design/3pb2IK8s2QUqWieH79KdN7/%E2%9D%96-Component-library-%7C-DDD?node-id=582-5939"; const componentName = "Footer"; const description = "Provides information related your service at the bottom of every page."; const componentCategory = Category.STRUCTURE_AND_NAVIGATION; @@ -97,12 +100,14 @@ export default function AppFooterPage() { category={componentCategory} description={description} relatedComponents={relatedComponents} + figmaLink={FIGMA_LINK} + githubLink="Footer" /> - - + +

- Component + Playground

Basic Footer

@@ -110,32 +115,36 @@ export default function AppFooterPage() { {/*Component properties table*/} - + {language === "angular" && ( )} - {/* Examples*/} - -
- Design guidelines - + Examples + }> -

Coming Soon

+ +
+ + + + + +
diff --git a/src/routes/components/AppHeader.tsx b/src/routes/components/AppHeader.tsx index 1f274907a..09eb0d853 100644 --- a/src/routes/components/AppHeader.tsx +++ b/src/routes/components/AppHeader.tsx @@ -15,7 +15,10 @@ import { useState } from "react"; import { ComponentContent } from "@components/component-content/ComponentContent"; import { LegacyTestIdProperties, TestIdProperty } from "@components/component-properties/common-properties.ts"; import { AppHeaderExamples } from "@examples/app-header/AppHeaderExamples.tsx"; +import { DesignEmpty } from "@components/empty-states/design-empty/DesignEmpty.tsx"; +import { AccessibilityEmpty } from "@components/empty-states/accessibility-empty/AccessibilityEmpty.tsx"; +const FIGMA_LINK = "https://www.figma.com/design/3pb2IK8s2QUqWieH79KdN7/%E2%9D%96-Component-library-%7C-DDD?node-id=4576-224884"; const componentName = "Header"; const description = "Provide structure to help users find their way around the service."; @@ -156,13 +159,15 @@ export default function AppHeaderPage() { category={componentCategory} description={description} relatedComponents={relatedComponents} + figmaLink={FIGMA_LINK} + githubLink="Header" /> - - + +

- Component + Playground

@@ -171,17 +176,24 @@ export default function AppHeaderPage() { {/*Component properties*/} -
- Design guidelines - + Examples + - }> -

Coming Soon

+ } + > + +
+ + + + + +
diff --git a/src/routes/components/Badge.tsx b/src/routes/components/Badge.tsx index 5e5ccbaa4..41b3c9c87 100644 --- a/src/routes/components/Badge.tsx +++ b/src/routes/components/Badge.tsx @@ -9,23 +9,29 @@ import { import { ComponentContent } from "@components/component-content/ComponentContent"; import BadgeExamples from "@examples/badge/BadgeExamples.tsx"; import { GoabBadgeType } from "@abgov/ui-components-common"; +import { DesignEmpty } from "@components/empty-states/design-empty/DesignEmpty.tsx"; +import { AccessibilityEmpty } from "@components/empty-states/accessibility-empty/AccessibilityEmpty.tsx"; // == Page props == +const FIGMA_LINK = "https://www.figma.com/design/3pb2IK8s2QUqWieH79KdN7/%E2%9D%96-Component-library-%7C-DDD?node-id=458-16984"; const componentName = "Badge"; const description = "Small labels which hold small amounts of information, system feedback, or states."; const category = Category.FEEDBACK_AND_ALERTS; const relatedComponents = [ { - link: "/components/filter-chip", name: "Filter Chip" + link: "/components/filter-chip", + name: "Filter chip", }, { - link: "/components/icons", name: "Icons" + link: "/components/icons", + name: "Icons", }, { - link: "/components/table", name: "Table" - } + link: "/components/table", + name: "Table", + }, ]; type ComponentPropsType = GoabBadgeProps; @@ -152,7 +158,7 @@ export default function BadgePage() { type: "Spacing(none | 3xs | 2xs | xs | s | m | l | xl | 2xl | 3xl | 4xl)", description: "Apply margin to the top, right, bottom, and/or left of the component.", }, - ] + ]; function onSandboxChange(badgeBindings: ComponentBinding[], props: Record) { setBadgeBindings(badgeBindings); @@ -161,27 +167,45 @@ export default function BadgePage() { return ( <> - - + - -

Component

+ /> + + + +

Playground

- +
- Design guidelines - + Examples + } - > + > + +
+ + + + + + + + +
diff --git a/src/routes/components/Block.tsx b/src/routes/components/Block.tsx index dc1f70f43..bcc00597b 100644 --- a/src/routes/components/Block.tsx +++ b/src/routes/components/Block.tsx @@ -5,7 +5,7 @@ import { ComponentProperty, } from "@components/component-properties/ComponentProperties.tsx"; import { Category, ComponentHeader } from "@components/component-header/ComponentHeader.tsx"; -import { GoabBadge, GoabBlock, GoabTab, GoabTabs } from "@abgov/react-components"; +import { GoabBlock, GoabContainer, GoabTab, GoabTabs, GoabText } from "@abgov/react-components"; import { ComponentContent } from "@components/component-content/ComponentContent"; export default function BlockPage() { @@ -60,7 +60,7 @@ export default function BlockPage() { name: "mt,mr,mb,ml", type: "none | 3xs | 2xs | xs | s | m | l | xl | 2xl | 3xl | 4xl", description: "Apply margin to the top, right, bottom, and/or left of the component.", - }, + }, ]; const componentProperties: ComponentProperty[] = [ @@ -112,13 +112,13 @@ export default function BlockPage() { { link: "/patterns/layout", name: "Layout" }, { link: "/components/spacer", name: "Spacer" }, ]} + githubLink="Block" /> - - - -

Component

+ + +

Playground

+ }} + >
Item 2
Item 2
@@ -153,18 +154,22 @@ export default function BlockPage() {
- {/*Block table properties*/} - - Design guidelines - - - } - > + + + + To use Block in design, use Figma's built-in{" "} + + auto layout + .{" "} This will group multiple things with consistent spacing between them, similar to the Block + component in code. + + + + diff --git a/src/routes/components/Button.tsx b/src/routes/components/Button.tsx index 8ee72232c..2ab63d3d7 100644 --- a/src/routes/components/Button.tsx +++ b/src/routes/components/Button.tsx @@ -21,6 +21,10 @@ import { MarginProperty, TestIdProperty } from "@components/component-properties/common-properties.ts"; +import { DesignEmpty } from "@components/empty-states/design-empty/DesignEmpty.tsx"; +import { AccessibilityEmpty } from "@components/empty-states/accessibility-empty/AccessibilityEmpty.tsx"; + +const FIGMA_LINK = "https://www.figma.com/design/3pb2IK8s2QUqWieH79KdN7/%E2%9D%96-Component-library-%7C-DDD?node-id=420-6810"; export default function ButtonPage() { const [buttonProps, setButtonProps] = useState({}); @@ -199,15 +203,14 @@ export default function ButtonPage() { { link: "/components/button-group", name: "Button group" }, { link: "/components/icon-button", name: "Icon button" }, ]} + figmaLink={FIGMA_LINK} + githubLink="Button" /> - - - {/*Button Sandbox*/} -

- Component -

+ + +

Playground

- - Primary Button - - {/*Button Table Properties*/} - -
- Design guidelines - + Examples + - }> -

Coming Soon

+ } + > + +
+ + + + + + +
diff --git a/src/routes/components/ButtonGroup.tsx b/src/routes/components/ButtonGroup.tsx index dd9463118..056b0cc13 100644 --- a/src/routes/components/ButtonGroup.tsx +++ b/src/routes/components/ButtonGroup.tsx @@ -9,6 +9,11 @@ import { GoabBadge, GoabButton, GoabButtonGroup, GoabTab, GoabTabs } from "@abgo import { ComponentContent } from "@components/component-content/ComponentContent"; import { GoabButtonGroupAlignment } from "@abgov/ui-components-common"; import { CodeSnippet } from "@components/code-snippet/CodeSnippet.tsx"; +import { DesignEmpty } from "@components/empty-states/design-empty/DesignEmpty.tsx"; +import { AccessibilityEmpty } from "@components/empty-states/accessibility-empty/AccessibilityEmpty.tsx"; +import { ExamplesEmpty } from "@components/empty-states/examples-empty/ExamplesEmpty.tsx"; + +const FIGMA_LINK = "https://www.figma.com/design/3pb2IK8s2QUqWieH79KdN7/%E2%9D%96-Component-library-%7C-DDD?node-id=27301-302108"; export default function ButtonGroupPage() { const [buttonGroupProps, setButtonGroupProps] = useState({ @@ -102,24 +107,19 @@ export default function ButtonGroupPage() { - - - {/*Button Group Sandbox*/} -

Component

+ + +

Playground

- - - Button - - - Button - - - Button - + Button + Button + Button -
+ - Design guidelines - + Examples + } - > + > + +
+ + + + + + + +
diff --git a/src/routes/components/Callout.tsx b/src/routes/components/Callout.tsx index fae50771a..ec7334e04 100644 --- a/src/routes/components/Callout.tsx +++ b/src/routes/components/Callout.tsx @@ -21,12 +21,15 @@ import { TestIdProperty } from "@components/component-properties/common-properties.ts"; import { CalloutExamples } from "@examples/callout/CalloutExamples.tsx"; +import { DesignEmpty } from "@components/empty-states/design-empty/DesignEmpty.tsx"; +import { AccessibilityEmpty } from "@components/empty-states/accessibility-empty/AccessibilityEmpty.tsx"; // == Page props == +const FIGMA_LINK = "https://www.figma.com/design/3pb2IK8s2QUqWieH79KdN7/%E2%9D%96-Component-library-%7C-DDD?node-id=434-14122"; const componentName = "Callout"; const description = "Communicate important information through a strong visual emphasis."; -const category = Category.FEEDBACK_AND_ALERTS; +const category = Category.CONTENT_AND_LAYOUT; const relatedComponents = [ { link: "/components/notification-banner", name: "Notification banner" }, ]; @@ -172,33 +175,40 @@ export default function CalloutPage() { category={category} description={description} relatedComponents={relatedComponents} + githubLink="Callout" + figmaLink={FIGMA_LINK} /> - - + +

- Component + Playground

- Lorem ipsum dolor sit amet, consectetur adipiscing elit. + Callout important information for the user. +
- - + + Examples + + } + > + + + + - - Design guidelines - - - }> + + +
diff --git a/src/routes/components/Checkbox.tsx b/src/routes/components/Checkbox.tsx index 5c0deb2cf..f8b126e01 100644 --- a/src/routes/components/Checkbox.tsx +++ b/src/routes/components/Checkbox.tsx @@ -16,6 +16,8 @@ import { MarginProperty, TestIdProperty } from "@components/component-properties/common-properties.ts"; +import { DesignEmpty } from "@components/empty-states/design-empty/DesignEmpty.tsx"; +import { AccessibilityEmpty } from "@components/empty-states/accessibility-empty/AccessibilityEmpty.tsx"; // == Page props == const componentName = "Checkbox"; @@ -26,6 +28,7 @@ const relatedComponents = [ { link: "/components/form-item", name: "Form item" }, { link: "/components/radio", name: "Radio" } ]; +const FIGMA_LINK = "https://www.figma.com/design/3pb2IK8s2QUqWieH79KdN7/%E2%9D%96-Component-library-%7C-DDD?node-id=183-219"; type ComponentPropsType = GoabCheckboxProps; type CastingType = { name: string; @@ -223,12 +226,19 @@ export default function CheckboxPage() { return ( <> - + - - -

Component

+ + +

Playground

-
- Design guidelines - + Examples + - }> + } + > + +
+ + + + + + + +
diff --git a/src/routes/components/Components.tsx b/src/routes/components/Components.tsx index 92e9360ab..36ff1aada 100644 --- a/src/routes/components/Components.tsx +++ b/src/routes/components/Components.tsx @@ -1,10 +1,17 @@ -import { GoabNotification, GoabSideMenu } from "@abgov/react-components"; +import { + GoabBadge, + GoabBlock, + GoabNotification, + GoabSideMenu, + GoabSideMenuGroup, + GoabSpacer +} from "@abgov/react-components"; import { Link, Outlet } from "react-router-dom"; import { SupportInfo } from "@components/support-info/SupportInfo.tsx"; import { useContext } from "react"; import { LanguageVersionContext } from "@contexts/LanguageVersionContext.tsx"; -import { getVersionedUrlPath } from "@components/version-language-switcher/version-language-constants.ts"; -import { DEFAULT_VERSION } from "../../global-constants"; +import { getVersionedUrlPath, ANGULAR_VERSIONS, REACT_VERSIONS } from "@components/version-language-switcher/version-language-constants.ts"; +import { MAX_CONTENT_WIDTH } from "../../global-constants.ts"; export function Components() { const { language, version } = useContext(LanguageVersionContext); @@ -15,68 +22,91 @@ export function Components() { return prefixUrl.length > 0 ? `${prefixUrl}/${path}` : path; }; + + const newComponentLabel = (componentName: string) => { + const getBadgeLabel = () => { + if (version === "new") return "New"; + return language === "angular" + ? ANGULAR_VERSIONS.NEW.label.substring(0, 2).toUpperCase() + : REACT_VERSIONS.NEW.label.substring(0, 2).toUpperCase(); + }; + return ( + + {componentName} + + ); + }; + return ( <> - {version !== DEFAULT_VERSION && ( - - {DEFAULT_VERSION === "old" ? ( - <> - Most teams should use the Long-term Support (LTS) version of the design system. If - you're ready to explore the latest features, check out our{" "} - upgrade guide - - ) : ( - <> - You are not on the latest version of the design system.{" "} - Upgrade to latest version - - )} + {version === "old" && ( + + Support for the Long Term Support (LTS) version of the Design system will be available until September 2025. View the upgrade guide + + )} + {version === "new" && ( + + Upgrading to the latest version of the design system?{" "} + View the upgrade guide )} -
+ All - Accordion - Badge - Block - Button - Button group - Callout - Checkbox - Container - Date picker - Details - Divider - Dropdown - File uploader - Filter chip - Footer - Form item - Form stepper - Grid - Header - Hero banner - Icons - Icon button - Input - List - Microsite header - Modal - Notification banner - Pagination - Popover - Progress indicator - Radio - Side menu - Skeleton loading - Spacer - Table - Tabs - Text - Text area - Tooltip + + Accordion + Callout + Container + Details + Hero banner + List + Popover + Table + Text + + + + Badge + Filter chip + Modal + Notification banner + Progress indicator + Skeleton loader + Tooltip + + + Button + Button group + Checkbox + Date picker + Dropdown + File uploader + Icon button + Input + Radio + Text area + + + {newComponentLabel("Drawer")} + Footer + Form stepper + Header + Microsite header + Pagination + Side menu + Tabs + + + Block + Divider + Form item + Grid + Icons + Link + Spacer +
diff --git a/src/routes/components/Container.tsx b/src/routes/components/Container.tsx index c5f67abdb..1a1c9e493 100644 --- a/src/routes/components/Container.tsx +++ b/src/routes/components/Container.tsx @@ -13,11 +13,14 @@ import { } from "@abgov/react-components"; import ContainerExamples from "@examples/container/ContainerExamples.tsx"; import { ComponentContent } from "@components/component-content/ComponentContent"; +const FIGMA_LINK = "https://www.figma.com/design/3pb2IK8s2QUqWieH79KdN7/%E2%9D%96-Component-library-%7C-DDD?node-id=1789-12623"; +import { DesignEmpty } from "@components/empty-states/design-empty/DesignEmpty.tsx"; +import { AccessibilityEmpty } from "@components/empty-states/accessibility-empty/AccessibilityEmpty.tsx"; // == Page props == const componentName = "Container"; const description = "Group information, create hierarchy, and show related information."; -const category = Category.FEEDBACK_AND_ALERTS; +const category = Category.CONTENT_AND_LAYOUT; const relatedComponents = [ { link: "/components/accordion", name: "Accordion" }, { link: "/components/details", name: "Details" }, @@ -121,7 +124,7 @@ export default function ContainerPage() { type: "string", description: "Sets the maximum width of the container.", lang: "angular", - }, + }, { name: "testId", type: "string", @@ -222,34 +225,41 @@ export default function ContainerPage() { category={category} description={description} relatedComponents={relatedComponents} + figmaLink={FIGMA_LINK} + githubLink="Container" /> - - -

Component

+ + +

Playground

Detach to use

Add things inside me

- - {/*Container Table Properties*/} - -
- Design guidelines - + Examples + - }> -

Coming Soon

+ } + > + +
+ + + + + + +
diff --git a/src/routes/components/DatePicker.tsx b/src/routes/components/DatePicker.tsx index 999c82919..694193bd8 100644 --- a/src/routes/components/DatePicker.tsx +++ b/src/routes/components/DatePicker.tsx @@ -21,14 +21,17 @@ import { GoabDatePickerOnChangeDetail } from "@abgov/ui-components-common"; import { LanguageVersionContext } from "@contexts/LanguageVersionContext.tsx"; import { LegacyMarginProperty, MarginProperty } from "@components/component-properties/common-properties.ts"; import { DatePickerExamples } from "@examples/date-picker/DatePickerExamples.tsx"; +import { DesignEmpty } from "@components/empty-states/design-empty/DesignEmpty.tsx"; +import { AccessibilityEmpty } from "@components/empty-states/accessibility-empty/AccessibilityEmpty.tsx"; // == Page props == +const FIGMA_LINK = "https://www.figma.com/design/3pb2IK8s2QUqWieH79KdN7/%E2%9D%96-Component-library-%7C-DDD?node-id=33054-33175"; const componentName = "Date picker"; const category = Category.INPUTS_AND_ACTIONS; const relatedComponents = [ - // { link: "/content/date-format", name: "Date format" }, - { link: "/components/form-item", name: "Form item" }, + { link: "/content/date-format", name: "Date format" }, + { link: "/components/form-item", name: "Form item" } ]; const description = "Lets users select a date through a calendar without the need to manually type it in a field."; @@ -179,13 +182,15 @@ export default function DatePickerPage() { category={category} description={description} relatedComponents={relatedComponents} + figmaLink={FIGMA_LINK} + githubLink="Date picker" /> - - -

Component

+ + +

Playground

-
- - Design guidelines - + Examples + - }> + } + > + +
+ + + + + + + +
diff --git a/src/routes/components/Details.tsx b/src/routes/components/Details.tsx index 0629e764c..c5c6d0b92 100644 --- a/src/routes/components/Details.tsx +++ b/src/routes/components/Details.tsx @@ -18,6 +18,10 @@ import { TestIdProperty, } from "@components/component-properties/common-properties.ts"; import { DetailsExamples } from "@examples/details/DetailsExamples.tsx"; +import { DesignEmpty } from "@components/empty-states/design-empty/DesignEmpty.tsx"; +import { AccessibilityEmpty } from "@components/empty-states/accessibility-empty/AccessibilityEmpty.tsx"; + +const FIGMA_LINK = "https://www.figma.com/design/3pb2IK8s2QUqWieH79KdN7/%E2%9D%96-Component-library-%7C-DDD?node-id=15931-553576"; export default function DetailsPage() { const [detailsProps, setDetailsProps] = useState({ @@ -100,6 +104,8 @@ export default function DetailsPage() { name="Details" category={Category.CONTENT_AND_LAYOUT} description="Let users reveal more detailed information when they need it." + figmaLink={FIGMA_LINK} + githubLink="Details" relatedComponents={[ { link: "/components/accordion", name: "Accordion" }, { link: "/components/form-item", name: "Form item" }, @@ -107,10 +113,10 @@ export default function DetailsPage() { /> - - + +

- Component + Playground

@@ -124,23 +130,29 @@ export default function DetailsPage() { facilisi. - - -
- Design guidelines - + Examples + - }> -

Coming Soon

+ } + > + +
+ + + + + + +
diff --git a/src/routes/components/Divider.tsx b/src/routes/components/Divider.tsx index d754bd432..88d73429c 100644 --- a/src/routes/components/Divider.tsx +++ b/src/routes/components/Divider.tsx @@ -8,7 +8,12 @@ import { Category, ComponentHeader } from "@components/component-header/Componen import { GoabBadge, GoabDivider, GoabTab, GoabTabs } from "@abgov/react-components"; import { ComponentContent } from "@components/component-content/ComponentContent"; import Sandbox from "@components/sandbox/Sandbox.tsx"; +import { DesignEmpty } from "@components/empty-states/design-empty/DesignEmpty.tsx"; +import { AccessibilityEmpty } from "@components/empty-states/accessibility-empty/AccessibilityEmpty.tsx"; import "./AllComponents.css"; +import { ExamplesEmpty } from "@components/empty-states/examples-empty/ExamplesEmpty.tsx"; + +const FIGMA_LINK = "https://www.figma.com/design/3pb2IK8s2QUqWieH79KdN7/%E2%9D%96-Component-library-%7C-DDD?node-id=46-115"; export default function DividerPage() { const [dividerProps, setDividerProps] = useState({}); @@ -76,28 +81,39 @@ export default function DividerPage() { relatedComponents={[ { link: "/components/container", name: "Container" }, ]} + figmaLink={FIGMA_LINK} + githubLink="Divider" /> - - -

Component

+ + +

Playground

-
- Design guidelines - + Examples + } - > + > + +
+ + + + + + + +
diff --git a/src/routes/components/Drawer.tsx b/src/routes/components/Drawer.tsx new file mode 100644 index 000000000..bdbadfadb --- /dev/null +++ b/src/routes/components/Drawer.tsx @@ -0,0 +1,228 @@ +import { + GoabBadge, + GoabButton, + GoabContainer, + GoabDrawer, + GoabDrawerProps, + GoabTab, + GoabTabs, +} from "@abgov/react-components"; +import { Sandbox, ComponentBinding } from "@components/sandbox"; +import { Category, ComponentHeader } from "@components/component-header/ComponentHeader"; +import { + ComponentProperties, + ComponentProperty, +} from "@components/component-properties/ComponentProperties"; +import { ComponentContent } from "@components/component-content/ComponentContent"; +import { LanguageVersionContext } from "@contexts/LanguageVersionContext.tsx"; +import { useContext, useState } from "react"; +import { TestIdProperty } from "@components/component-properties/common-properties.ts"; +import { CodeSnippet } from "@components/code-snippet/CodeSnippet.tsx"; +import { DrawerExamples } from "@examples/drawer/DrawerExamples.tsx"; +import { OldComponentBanner } from "@components/old-component-banner/OldComponentBanner.tsx"; +import { DesignEmpty } from "@components/empty-states/design-empty/DesignEmpty.tsx"; +import { AccessibilityEmpty } from "@components/empty-states/accessibility-empty/AccessibilityEmpty.tsx"; + +// == Page props == + +const FIGMA_LINK = "https://www.figma.com/design/XwdpAM2ejssrXlzSuPiycT/Component---Drawer?node-id=2332-98665&m=dev"; +const ACCESSIBILITY_FIGMA_LINK = "https://www.figma.com/design/XwdpAM2ejssrXlzSuPiycT/Component---Drawer?node-id=2488-93239&m=dev"; +const componentName = "Drawer"; +const description = + "A panel that slides in from the side of the screen to display additional content or actions without navigating away from the current view."; +const category = Category.STRUCTURE_AND_NAVIGATION; +const relatedComponents = [ + { link: "/components/modal", name: "Modal" } +]; + +type ComponentPropsType = Omit; + +export const DrawerPage = () => { + const {version, language} = useContext(LanguageVersionContext); + const [open, setOpen] = useState(false); + + const [drawerProps, setDrawerProps] = useState({ + heading: "Drawer heading", + position: "right", + onClose: () => {}, + children: null + }); + + const [drawerBindings, setDrawerBindings] = useState([ + { + label: "Position", + type: "dropdown", + name: "position", + options: ["left", "right", "bottom"], + value: "right" + }, + { + label: "Heading", + type: "string", + name: "heading", + value: "Drawer heading", + }, + { + label: "Max Size", + helpText: "Sets max height on bottom position, sets width on left and right position", + type: "string", + name: "maxSize", + value: "", + }, + ]); + + const componentProperties: ComponentProperty[] = [ + { + name: "open", + type: "boolean", + description: "Whether the drawer is open.", + required: true, + }, + { + name: "position", + type: "GoabDrawerPosition (right|left|bottom)", + description: "The position of the drawer.", + required: true, + }, + { + name: "heading", + type: "string | ReactNode", + description: "Heading of the drawer.", + lang: "react", + }, + { + name: "heading", + type: "string | TemplateRef", + description: "Heading of the drawer.", + lang: "angular", + }, + { + name: "maxSize", + type: "GoabDrawerSize (px|rem|ch|vh|vw) (ex: 300px)", + description: "Sets max height on bottom position, sets width on left and right position", + }, + { + name: "actions", + type: "ReactNode", + description: "Actions to display in the drawer.", + lang: "react", + }, + { + name: "actions", + type: "TemplateRef", + description: "Actions to display in the drawer.", + lang: "angular", + }, + { + name: "onClose", + type: "() => void", + description: "Callback function to handle the close event.", + }, + TestIdProperty + ]; + + function SandboxOnChange(bindings: ComponentBinding[], props: Record) { + setDrawerBindings(bindings); + setDrawerProps({ + ...drawerProps, + ...props + } as ComponentPropsType); + } + + return ( + <> + + + {version === "old" && } + + {version === "new" && ( + + + +

+ Component +

+ {/*Don't use a Sandbox because the animation slowing displaying the drawer isn't working if it is inside sandbox*/} + +
+ setOpen(true)}>Open Drawer +
+ setOpen(false)}> +

+ This is a drawer. It is a panel that slides in from the edge of the screen to + provide users access to secondary content and actions without leaving the + current page. +

+
+
+ + + + + + setOpen(true)}>Open Drawer + setOpen(false)}> +

+ This is a drawer. It is a panel that slides in from the edge of the screen to + provide users access to secondary content and actions without leaving the + current page. +

+
+
+ + + + +
+ Examples + }> + + + + + + + + + + +
+
+ )} + + ); +} diff --git a/src/routes/components/Dropdown.tsx b/src/routes/components/Dropdown.tsx index 3f13f2083..e1747fbf4 100644 --- a/src/routes/components/Dropdown.tsx +++ b/src/routes/components/Dropdown.tsx @@ -7,7 +7,6 @@ import { GoabTab, GoabTabs } from "@abgov/react-components"; -import { CodeSnippet } from "@components/code-snippet/CodeSnippet"; import { ComponentBinding, Sandbox } from "@components/sandbox"; import ICONS from "./icons.json"; import { Category, ComponentHeader } from "@components/component-header/ComponentHeader.tsx"; @@ -21,6 +20,10 @@ import { DropdownExamples } from "@examples/dropdown/DropdownExamples"; import { GoabDropdownOnChangeDetail } from "@abgov/ui-components-common"; import { LanguageVersionContext } from "@contexts/LanguageVersionContext.tsx"; import { LegacyMarginProperty, MarginProperty } from "@components/component-properties/common-properties.ts"; +import { DesignEmpty } from "@components/empty-states/design-empty/DesignEmpty.tsx"; +import { AccessibilityEmpty } from "@components/empty-states/accessibility-empty/AccessibilityEmpty.tsx"; + +const FIGMA_LINK = "https://www.figma.com/design/3pb2IK8s2QUqWieH79KdN7/%E2%9D%96-Component-library-%7C-DDD?node-id=105-42"; // == Page props == const componentName = "Dropdown"; @@ -364,112 +367,26 @@ export default function DropdownPage() { return ( <> - + - - -

Component

+ + +

Playground

- {version === "old" && } - {version === "new" && } - - {version === "old" && } - - {version === "new" && } - - {version === "old" && } - - {version === "new" && } - - {version === "new" && } - + {/* Keep all existing CodeSnippets and form content as-is */} @@ -486,18 +403,25 @@ export default function DropdownPage() { properties={dropdownItemProperties} oldProperties={oldDropdownItemProperties} /> -
- Design guidelines - + Examples + } > -

Coming Soon

+ +
+ + + + + + +
diff --git a/src/routes/components/FileUploader.tsx b/src/routes/components/FileUploader.tsx index 49cd8a5f6..b7371139e 100644 --- a/src/routes/components/FileUploader.tsx +++ b/src/routes/components/FileUploader.tsx @@ -3,12 +3,13 @@ import { ComponentBinding, Sandbox } from "@components/sandbox"; import { Category, ComponentHeader } from "@components/component-header/ComponentHeader"; import { propsToString } from "@components/sandbox/BaseSerializer" import { + GoabBadge, GoabFileUploadCard, GoabFileUploadInput, GoabFileUploadInputProps, GoabFormItem, GoabTab, - GoabTabs, + GoabTabs } from "@abgov/react-components"; import { ComponentProperties, @@ -18,6 +19,9 @@ import { CodeSnippet } from "@components/code-snippet/CodeSnippet"; import { ComponentContent } from "@components/component-content/ComponentContent"; import { LanguageVersionContext } from "@contexts/LanguageVersionContext.tsx"; import { MarginProperty, TestIdProperty } from "@components/component-properties/common-properties.ts"; +import { DesignEmpty } from "@components/empty-states/design-empty/DesignEmpty.tsx"; +import { AccessibilityEmpty } from "@components/empty-states/accessibility-empty/AccessibilityEmpty.tsx"; +import { ExamplesEmpty } from "@components/empty-states/examples-empty/ExamplesEmpty.tsx"; interface Uploader { upload: (url: string | ArrayBuffer) => void; @@ -63,6 +67,7 @@ const relatedComponents = [ { link: "/components/container", name: "Container" }, { link: "/components/progress-indicator", name: "Progress indicator" } ]; +const FIGMA_LINK = "https://www.figma.com/design/3pb2IK8s2QUqWieH79KdN7/%E2%9D%96-Component-library-%7C-DDD?node-id=804-5767"; type ComponentPropsType = Omit; type CastingType = { maxFileSize: string; @@ -301,12 +306,19 @@ export default function FileUploaderPage() { return ( <> - + - - -

Component

+ + +

Playground

{/* ******* */} @@ -658,6 +670,24 @@ export default function FileUploaderPage() { />
+ + Examples + + + } + > + + + + + + + + + +
diff --git a/src/routes/components/FilterChip.tsx b/src/routes/components/FilterChip.tsx index 924a9fb20..2b43dc48b 100644 --- a/src/routes/components/FilterChip.tsx +++ b/src/routes/components/FilterChip.tsx @@ -1,9 +1,10 @@ import { Category, ComponentHeader } from "@components/component-header/ComponentHeader.tsx"; import { GoabBadge, - GoabFilterChip, GoabFilterChipProps, + GoabFilterChip, + GoabFilterChipProps, GoabTab, - GoabTabs + GoabTabs, } from "@abgov/react-components"; import { ComponentBinding, Sandbox } from "@components/sandbox"; import { useState } from "react"; @@ -15,9 +16,11 @@ import { ComponentContent } from "@components/component-content/ComponentContent import { LegacyTestIdProperties, MarginProperty, - TestIdProperty + TestIdProperty, } from "@components/component-properties/common-properties.ts"; import { FilterChipExamples } from "@examples/filter-chip/FilterChipExamples.tsx"; +import { DesignEmpty } from "@components/empty-states/design-empty/DesignEmpty.tsx"; +import { AccessibilityEmpty } from "@components/empty-states/accessibility-empty/AccessibilityEmpty.tsx"; // Page props const componentName = "Filter Chip"; @@ -26,8 +29,10 @@ const category = Category.FEEDBACK_AND_ALERTS; const relatedComponents = [ { link: "/components/badge", name: "Badge" }, { link: "/components/popover", name: "Popover" }, + { link: "/components/input", name: "Input" }, { link: "/components/table", name: "Table" }, ]; +const FIGMA_LINK = "https://www.figma.com/design/3pb2IK8s2QUqWieH79KdN7/%E2%9D%96-Component-library-%7C-DDD?node-id=911-5909"; type ComponentPropsType = GoabFilterChipProps; type CastingType = { @@ -115,13 +120,13 @@ export default function FilterChipPage() { category={category} description={description} relatedComponents={relatedComponents} + figmaLink={FIGMA_LINK} + githubLink="Filter Chip" /> - - -

- Component -

+ + +

Playground

@@ -130,16 +135,26 @@ export default function FilterChipPage() { oldProperties={oldComponentProperties} /> -
- Design guidelines - + Examples + - }> + } + > + +
+ + + + + + + +
diff --git a/src/routes/components/FormItemPage.tsx b/src/routes/components/FormItemPage.tsx index 67d654efb..1f3ae4173 100644 --- a/src/routes/components/FormItemPage.tsx +++ b/src/routes/components/FormItemPage.tsx @@ -15,6 +15,10 @@ import { TestIdProperty } from "@components/component-properties/common-properties.ts"; import { FormItemExamples } from "@examples/form-item/FormItemExamples.tsx"; +import { DesignEmpty } from "@components/empty-states/design-empty/DesignEmpty.tsx"; +import { AccessibilityEmpty } from "@components/empty-states/accessibility-empty/AccessibilityEmpty.tsx"; + +const FIGMA_LINK = "https://www.figma.com/design/3pb2IK8s2QUqWieH79KdN7/%E2%9D%96-Component-library-%7C-DDD?node-id=27284-300347"; export default function FormItemPage() { const {version} = useContext(LanguageVersionContext); @@ -209,7 +213,7 @@ export default function FormItemPage() { <> - - + +

- Component + Playground

-
- Design guidelines - + Examples + - }> -

Coming Soon

+ } + > + +
+ + + + +
diff --git a/src/routes/components/FormStepper.tsx b/src/routes/components/FormStepper.tsx index 2dbfff0cb..e44f92bda 100644 --- a/src/routes/components/FormStepper.tsx +++ b/src/routes/components/FormStepper.tsx @@ -22,7 +22,10 @@ import { TestIdProperty } from "@components/component-properties/common-properties.ts"; import { FormStepperExamples } from "@examples/form-stepper/FormStepperExamples.tsx"; +import { DesignEmpty } from "@components/empty-states/design-empty/DesignEmpty.tsx"; +import { AccessibilityEmpty } from "@components/empty-states/accessibility-empty/AccessibilityEmpty.tsx"; +const FIGMA_LINK = "https://www.figma.com/design/3pb2IK8s2QUqWieH79KdN7/%E2%9D%96-Component-library-%7C-DDD?node-id=1014-6629"; const componentName = "Form Stepper"; const description = "Provides a visual representation of a form through a series of steps."; const componentCategory = Category.STRUCTURE_AND_NAVIGATION; @@ -109,12 +112,14 @@ export default function FormStepperPage() { category={componentCategory} description={description} relatedComponents={relatedComponents} + figmaLink={FIGMA_LINK} + githubLink="Form Stepper" /> - - + +

- Component + Playground

{/*Must use Container because Form Stepper cannot be rendered correctly inside sandbox*/} @@ -254,18 +259,25 @@ export default function FormStepperPage() { - -
- Design guidelines - + Examples + - }> -

Coming Soon

+ } + > + +
+ + + + + + +
diff --git a/src/routes/components/Grid.tsx b/src/routes/components/Grid.tsx index 198b13581..3b97876b6 100644 --- a/src/routes/components/Grid.tsx +++ b/src/routes/components/Grid.tsx @@ -5,7 +5,7 @@ import { ComponentProperty, } from "@components/component-properties/ComponentProperties"; import { Category, ComponentHeader } from "@components/component-header/ComponentHeader"; -import { GoabBadge, GoabGrid, GoabTab, GoabTabs } from "@abgov/react-components"; +import { GoabContainer, GoabGrid, GoabTab, GoabTabs, GoabText } from "@abgov/react-components"; import { ComponentContent } from "@components/component-content/ComponentContent"; export default function GridPage() { @@ -97,14 +97,17 @@ export default function GridPage() { { link: "/patterns/layout", name: "Layout" }, { link: "/components/spacer", name: "Spacer" }, ]} + githubLink="Grid" /> - - + + +

+ Playground +

{/*Grid sandbox*/} -

Component

- - Design guidelines - - - } - > + + + + To use grid in design, set Figma's built-in{" "} + + auto layout + {" "} + to wrap. This will create a responsive grid pattern that can respond to different screen + sizes, similar to the Grid component in code. + + + diff --git a/src/routes/components/HeroBanner.tsx b/src/routes/components/HeroBanner.tsx index 0dc828226..d976e37db 100644 --- a/src/routes/components/HeroBanner.tsx +++ b/src/routes/components/HeroBanner.tsx @@ -11,16 +11,25 @@ import { GoabHeroBannerActions, GoabTab, GoabTabs, + GoabBadge, } from "@abgov/react-components"; import { ComponentContent } from "@components/component-content/ComponentContent"; import { CodeSnippet } from "@components/code-snippet/CodeSnippet.tsx"; import { LanguageVersionContext } from "@contexts/LanguageVersionContext.tsx"; +import { DesignEmpty } from "@components/empty-states/design-empty/DesignEmpty.tsx"; +import { AccessibilityEmpty } from "@components/empty-states/accessibility-empty/AccessibilityEmpty.tsx"; +import { SandboxHeader } from "@components/sandbox/sandbox-header/sandboxHeader.tsx"; + +const FIGMA_LINK = "https://www.figma.com/design/3pb2IK8s2QUqWieH79KdN7/%E2%9D%96-Component-library-%7C-DDD?node-id=622-14412"; export default function HeroBannerPage() { const {version} = useContext(LanguageVersionContext); const [heroBannerProps, setHeroBannerProps] = useState({ heading: "Heading", }); + const relatedComponents = [ + { link: "/components/header", name: "Header" }, + ]; const [heroBannerBindings, setHeroBannerBindings] = useState([ { label: "Heading", @@ -201,32 +210,42 @@ export default function HeroBannerPage() { <> - - - {/*Hero Banner Sandbox*/} -

- Component -

+ + +

Playground

Resources are available to help Alberta entrepreneurs and small businesses start, grow and succeed. + +
- - - {/* Examples*/} - + + Examples + + + } + > -

Hero Banner with actions

+ + {version === "old" &&
+ + + + + + + +
diff --git a/src/routes/components/IconButton.tsx b/src/routes/components/IconButton.tsx index 3bc562e09..dbf60c244 100644 --- a/src/routes/components/IconButton.tsx +++ b/src/routes/components/IconButton.tsx @@ -16,10 +16,10 @@ import { useState } from "react"; import ICONS from "./icons.json"; import { ComponentContent } from "@components/component-content/ComponentContent"; import { GoabIconType } from "@abgov/ui-components-common"; +import { DesignEmpty } from "@components/empty-states/design-empty/DesignEmpty.tsx"; +import { AccessibilityEmpty } from "@components/empty-states/accessibility-empty/AccessibilityEmpty.tsx"; +import { SandboxHeader } from "@components/sandbox/sandbox-header/sandboxHeader.tsx"; -const componentName = "Icon button"; -const description = "A compact button with an icon and no text."; -const componentCategory = Category.INPUTS_AND_ACTIONS; type ComponentPropsType = GoabIconButtonProps; type CastingType = { // add any required props here @@ -31,6 +31,7 @@ export default function IconButtonPage() { icon: "refresh" as GoabIconType, ariaLabel: "Refresh icon", }); + const FIGMA_LINK = "https://www.figma.com/design/3pb2IK8s2QUqWieH79KdN7/%E2%9D%96-Component-library-%7C-DDD?node-id=27301-302107"; const [iconButtonBindings, setIconButtonBindings] = useState([ { label: "Variant", @@ -206,16 +207,22 @@ export default function IconButtonPage() { return ( <> - - -

Component

+ + +

Playground

@@ -223,10 +230,21 @@ export default function IconButtonPage() { {/*Component properties table*/} - {/* Examples */} - +
+ + + Examples + + + } + > -

Show multiple actions in a compact table

+ + @@ -333,14 +351,12 @@ export default function IconButtonPage() {
- - Design guidelines - - - }> -

Coming Soon

+ + + + + +
diff --git a/src/routes/components/Icons.tsx b/src/routes/components/Icons.tsx index 303c3325d..a921d4195 100644 --- a/src/routes/components/Icons.tsx +++ b/src/routes/components/Icons.tsx @@ -5,7 +5,7 @@ import { ComponentProperties, ComponentProperty, } from "@components/component-properties/ComponentProperties.tsx"; -import { GoabGrid, GoabIcon, GoabTab, GoabTabs } from "@abgov/react-components"; +import { GoabContainer, GoabGrid, GoabIcon, GoabTab, GoabTabs, GoabText } from "@abgov/react-components"; import { Category, ComponentHeader } from "@components/component-header/ComponentHeader.tsx"; import { IconSnippet } from "@components/icon-snippet/IconSnippet.tsx"; import { ComponentContent } from "@components/component-content/ComponentContent"; @@ -16,6 +16,8 @@ import { TestIdProperty } from "@components/component-properties/common-properties.ts"; +const FIGMA_LINK = "https://www.figma.com/design/3pb2IK8s2QUqWieH79KdN7/%E2%9D%96-Component-library-%7C-DDD?node-id=24019-471310"; + export default function IconsPage() { const [iconsProps, setIconsProps] = useState({ type: ICONS[0] as GoabIconType, @@ -210,14 +212,16 @@ export default function IconsPage() { { link: "/components/icon-button", name: "Icon button" }, { link: "/components/tooltip", name: "Tooltip" }, ]} + figmaLink={FIGMA_LINK} + githubLink="Icons" /> - - + + {/*Icons Sandbox*/}

- Component + Playground

@@ -225,14 +229,12 @@ export default function IconsPage() { {/*Icons Properties*/} +
- {/*Icons example*/} - +

Alert and messaging

- + @@ -244,7 +246,7 @@ export default function IconsPage() {

Basic

- + @@ -252,7 +254,7 @@ export default function IconsPage() {

Direction

- + @@ -268,7 +270,7 @@ export default function IconsPage() {

Interactions

- + @@ -290,13 +292,29 @@ export default function IconsPage() {

Accounts

- +
+ + + + + The extended icon set includes the full + {" "} + Ionicon library. + {" "} + When you need additional icons outside of the core icon set, use these icons to maintain a consistent + visual language. + + + + +
diff --git a/src/routes/components/Link.tsx b/src/routes/components/Link.tsx new file mode 100644 index 000000000..f86c04b91 --- /dev/null +++ b/src/routes/components/Link.tsx @@ -0,0 +1,134 @@ +import { useState } from "react"; +import { ComponentBinding, Sandbox } from "@components/sandbox"; +import { + ComponentProperties, + ComponentProperty, +} from "@components/component-properties/ComponentProperties.tsx"; +import { Category, ComponentHeader } from "@components/component-header/ComponentHeader.tsx"; +import { GoabBadge, GoabTab, GoabTabs, GoabLink } from "@abgov/react-components"; +import { LinkExamples } from "@examples/link/LinkExamples.tsx"; +import { DesignEmpty } from "@components/empty-states/design-empty/DesignEmpty.tsx"; +import { AccessibilityEmpty } from "@components/empty-states/accessibility-empty/AccessibilityEmpty.tsx"; +import ICONS from "@routes/components/icons.json"; + + +export default function LinkPage() { + const FIGMA_LINK = "https://www.figma.com/design/3pb2IK8s2QUqWieH79KdN7/%E2%9D%96-Component-library-%7C-DDD?node-id=27301-303448"; + const [linkProps, setLinkProps] = useState({}); + + const [linkBindings, setLinkBindings] = useState([ + { + label: "Leading Icon", + type: "combobox", + name: "leadingIcon", + options: [""].concat(ICONS), + value: "", + }, + { + label: "Trailing Icon", + type: "combobox", + name: "trailingIcon", + options: [""].concat(ICONS), + value: "", + }, + { + label: "Top Margin", + type: "list", + name: "mt", + options: ["none", "3xs", "2xs", "xs", "s", "m", "l", "xl", "2xl", "3xl", "4xl"], + value: "none", + }, + { + label: "Bottom Margin", + type: "list", + name: "mb", + options: ["none", "3xs", "2xs", "xs", "s", "m", "l", "xl", "2xl", "3xl", "4xl"], + value: "none", + }, + ]); + + const componentProperties: ComponentProperty[] = [ + { + name: "leadingIcon", + type: "GoAIconType", + lang: "react", + description: "Shows an icon to the left of the link.", + }, + { + name: "leadingicon", + type: "GoAIconType", + lang: "angular", + description: "Shows an icon to the left of the link.", + }, + { + name: "trailingIcon", + type: "GoAIconType", + lang: "react", + description: "Shows an icon to the right of the link.", + }, + { + name: "trailingicon", + type: "GoAIconType", + lang: "angular", + description: "Shows an icon to the right of the link.", + }, + { + name: "mt,mr,mb,ml", + type: "none | 3xs | 2xs | xs | s | m | l | xl | 2xl | 3xl | 4xl", + description: "Apply margin to the top, right, bottom, and/or left of the component.", + }, + ]; + + function onSandboxChange(bindings: ComponentBinding[], props: Record) { + setLinkBindings(bindings); + setLinkProps(props); + } + + return ( + <> + + + + + + + + Link + + + + + + + + + Examples + + + } + > + + + + + + + + + + + + + ); +} \ No newline at end of file diff --git a/src/routes/components/List.tsx b/src/routes/components/List.tsx index e8198f1d2..b40a29f8e 100644 --- a/src/routes/components/List.tsx +++ b/src/routes/components/List.tsx @@ -2,6 +2,10 @@ import { Category, ComponentHeader } from "@components/component-header/Componen import { GoabBadge, GoabContainer, GoabTab, GoabTabs } from "@abgov/react-components"; import { CodeSnippet } from "@components/code-snippet/CodeSnippet.tsx"; import { ComponentContent } from "@components/component-content/ComponentContent"; +import { DesignEmpty } from "@components/empty-states/design-empty/DesignEmpty.tsx"; +import { SandboxHeader } from "@components/sandbox/sandbox-header/sandboxHeader.tsx"; + +const FIGMA_LINK = "https://www.figma.com/design/3pb2IK8s2QUqWieH79KdN7/%E2%9D%96-Component-library-%7C-DDD?node-id=27301-303450"; export default function ListPage() { return ( @@ -10,12 +14,26 @@ export default function ListPage() { name="List" category={Category.CONTENT_AND_LAYOUT} description="Organize information into brief and clear groups." - /> + githubLink="List" + figmaLink={FIGMA_LINK} + relatedComponents={[ + { link: "/components/details", name: "Details" }, + ]} /> - - - {/*We don't use sandbox because it isn't starting with "GoA" components*/} - + + + Examples + + + } + > + + +
  1. An ordered item @@ -125,14 +143,12 @@ export default function ListPage() {
`} /> - {/* Examples*/} - - -

Unordered list

- + + +
  • Milk
  • @@ -144,6 +160,7 @@ export default function ListPage() {
+
- - Design guidelines - - - }> -

Coming Soon

+ + +
diff --git a/src/routes/components/MicrositeHeader.tsx b/src/routes/components/MicrositeHeader.tsx index 1095fb080..bb8583fd1 100644 --- a/src/routes/components/MicrositeHeader.tsx +++ b/src/routes/components/MicrositeHeader.tsx @@ -15,10 +15,14 @@ import { useState } from "react"; import { ComponentContent } from "@components/component-content/ComponentContent"; import MicrositeHeaderExamples from "@examples/microsite-header/MicrositeHeaderExamples.tsx"; import { GoabServiceLevel } from "@abgov/ui-components-common"; +import { DesignEmpty } from "@components/empty-states/design-empty/DesignEmpty.tsx"; +import { AccessibilityEmpty } from "@components/empty-states/accessibility-empty/AccessibilityEmpty.tsx"; + +const FIGMA_LINK = "https://www.figma.com/design/3pb2IK8s2QUqWieH79KdN7/%E2%9D%96-Component-library-%7C-DDD?node-id=2-81"; const componentName = "Microsite header"; const description = - "Communicate what stage the service is at, connect to Alberta.ca, and gather feedback on your service."; + "Communicate what stage the service is at, connect to Alberta.ca, and gather feedback on your service."; const componentCategory = Category.STRUCTURE_AND_NAVIGATION; type ComponentPropsType = GoabHeaderProps; const relatedComponents = [{ link: "/components/header", name: "Header" }]; @@ -214,7 +218,6 @@ export default function MicrositeHeaderPage() { }, ]; - function onSandboxChange(bindings: ComponentBinding[], props: Record) { setMicrositeHeaderProps(props as CastingType); setMicrositeHeaderBindings(bindings); @@ -227,30 +230,38 @@ export default function MicrositeHeaderPage() { category={componentCategory} description={description} relatedComponents={relatedComponents} + figmaLink={FIGMA_LINK} + githubLink="Microsite header" /> - - -

Component

+ + +

Playground

- - {/*Component properties table*/} -
- Design guidelines - + Examples + - }> -

Coming Soon

+ } + > + +
+ + + + + + +
diff --git a/src/routes/components/Modal.tsx b/src/routes/components/Modal.tsx index 3a36b9f56..ccdd5b582 100644 --- a/src/routes/components/Modal.tsx +++ b/src/routes/components/Modal.tsx @@ -20,9 +20,12 @@ import { CodeSnippet } from "@components/code-snippet/CodeSnippet.tsx"; import ModalExamples from "@examples/modal/ModalExamples.tsx"; import { LanguageVersionContext } from "@contexts/LanguageVersionContext.tsx"; import { TestIdProperty } from "@components/component-properties/common-properties.ts"; +import { DesignEmpty } from "@components/empty-states/design-empty/DesignEmpty.tsx"; +import { AccessibilityEmpty } from "@components/empty-states/accessibility-empty/AccessibilityEmpty.tsx"; // == Page props == +const FIGMA_LINK = "https://www.figma.com/design/3pb2IK8s2QUqWieH79KdN7/%E2%9D%96-Component-library-%7C-DDD?node-id=622-13874"; const componentName = "Modal"; const description = "An overlay that appears in front of all other content, and requires a user to take an action before continuing."; @@ -285,14 +288,14 @@ export default function ModalPage() { category={category} description={description} relatedComponents={relatedComponents} + figmaLink={FIGMA_LINK} + githubLink="Modal" /> - - -

- Component -

+ + +

Playground

)} - -
- Design guidelines - + Examples + - }> + } + > + +
+ + + + + + + +
diff --git a/src/routes/components/Notificationbanner.tsx b/src/routes/components/Notificationbanner.tsx index 984af7ab2..5ea2b2118 100644 --- a/src/routes/components/Notificationbanner.tsx +++ b/src/routes/components/Notificationbanner.tsx @@ -13,9 +13,13 @@ import { } from "@components/component-properties/ComponentProperties.tsx"; import { ComponentContent } from "@components/component-content/ComponentContent"; import { GoabNotificationType } from "@abgov/ui-components-common"; +import { DesignEmpty } from "@components/empty-states/design-empty/DesignEmpty.tsx"; +import { AccessibilityEmpty } from "@components/empty-states/accessibility-empty/AccessibilityEmpty.tsx"; +import {ExamplesEmpty} from "@components/empty-states/examples-empty/ExamplesEmpty.tsx"; // == Page props == +const FIGMA_LINK = "https://www.figma.com/design/3pb2IK8s2QUqWieH79KdN7/%E2%9D%96-Component-library-%7C-DDD?node-id=622-12949"; const componentName = "Notification Banner"; const description = "Display important page level information or notifications."; const category = Category.FEEDBACK_AND_ALERTS; @@ -133,13 +137,20 @@ export default function NotificationBannerPage() { return ( <> - + - - -

Component

+ + +

Playground

Notification banner message @@ -149,11 +160,21 @@ export default function NotificationBannerPage() { - Design guidelines - + Examples + } - > + > + +
+ + + + + + + +
diff --git a/src/routes/components/Pagination.tsx b/src/routes/components/Pagination.tsx index 6dce347d7..89e4ca71c 100644 --- a/src/routes/components/Pagination.tsx +++ b/src/routes/components/Pagination.tsx @@ -23,6 +23,10 @@ import { TestIdProperty } from "@components/component-properties/common-properties.ts"; import { PaginationExamples } from "@examples/pagination/PaginationExamples.tsx"; +import { DesignEmpty } from "@components/empty-states/design-empty/DesignEmpty.tsx"; +import { AccessibilityEmpty } from "@components/empty-states/accessibility-empty/AccessibilityEmpty.tsx"; + +const FIGMA_LINK = "https://www.figma.com/design/3pb2IK8s2QUqWieH79KdN7/%E2%9D%96-Component-library-%7C-DDD?node-id=622-13964"; type ComponentPropsType = Omit; type CastingType = { @@ -212,13 +216,15 @@ export default function PaginationPage() { category={componentCategory} description={description} relatedComponents={relatedComponents} + figmaLink={FIGMA_LINK} + githubLink="Pagination" /> - - + +

- Component + Playground

-
- - Design guidelines - + Examples + - }> -

Coming Soon

+ } + > + +
+ + + + + +
diff --git a/src/routes/components/Popover.tsx b/src/routes/components/Popover.tsx index 84c57f1fa..f2e254328 100644 --- a/src/routes/components/Popover.tsx +++ b/src/routes/components/Popover.tsx @@ -15,6 +15,11 @@ import { LegacyTestIdProperties, MarginProperty, TestIdProperty } from "@components/component-properties/common-properties.ts"; +import { DesignEmpty } from "@components/empty-states/design-empty/DesignEmpty.tsx"; +import { AccessibilityEmpty } from "@components/empty-states/accessibility-empty/AccessibilityEmpty.tsx"; +import { ExamplesEmpty } from "@components/empty-states/examples-empty/ExamplesEmpty.tsx"; + +const FIGMA_LINK = "https://www.figma.com/design/3pb2IK8s2QUqWieH79KdN7/%E2%9D%96-Component-library-%7C-DDD?node-id=27301-302109"; export default function PopoverPage() { const {version} = useContext(LanguageVersionContext); @@ -173,14 +178,15 @@ export default function PopoverPage() { { link: "/components/header", name: "Header" }, { link: "/components/tooltip", name: "Tooltip" }, ]} + figmaLink={FIGMA_LINK} + githubLink="Popover" /> - - - {/*Popover sandbox*/} -

Component

+ + +

Playground

{/*Must be skipRender because Sandbox doesn't support slot target*/} {/*Angular*/} @@ -277,19 +283,27 @@ export default function PopoverPage() { It can be used for a number of different contexts. - - {/*Popover table properties*/}
- Design guidelines - + Examples + } - > + > + +
+ + + + + + + +
diff --git a/src/routes/components/ProgressIndicator.tsx b/src/routes/components/ProgressIndicator.tsx index adcda0636..4723be94f 100644 --- a/src/routes/components/ProgressIndicator.tsx +++ b/src/routes/components/ProgressIndicator.tsx @@ -17,9 +17,13 @@ import { resetScrollbars } from "../../utils/styling"; import { ComponentContent } from "@components/component-content/ComponentContent"; import { TestIdProperty } from "@components/component-properties/common-properties.ts"; import { CodeSnippet } from "@components/code-snippet/CodeSnippet.tsx"; +import { DesignEmpty } from "@components/empty-states/design-empty/DesignEmpty.tsx"; +import { AccessibilityEmpty } from "@components/empty-states/accessibility-empty/AccessibilityEmpty.tsx"; +import {ExamplesEmpty} from "@components/empty-states/examples-empty/ExamplesEmpty.tsx"; // == Page props == +const FIGMA_LINK = "https://www.figma.com/design/3pb2IK8s2QUqWieH79KdN7/%E2%9D%96-Component-library-%7C-DDD?node-id=622-13604"; const componentName = "Progress indicator"; const description = "Provide visual feedback to users while loading."; const category = Category.FEEDBACK_AND_ALERTS; @@ -155,26 +159,33 @@ export default function ProgressIndicatorPage() { return ( <> - + - - -

Component

+ + +

Playground

{ - setVisible(false); - }, 3000); - }` : ''} + const [visible, setVisible] = useState(${visible}); + ${componentProps.variant === 'fullscreen' ? ` + function onClick() { + setVisible(true); + setTimeout(() => { + setVisible(false); + }, 3000); + }` : ''} `} /> { this.visible = false; @@ -202,11 +213,21 @@ export default function ProgressIndicatorPage() { - Design guidelines - + Examples + } - > + > + +
+ + + + + + + +
diff --git a/src/routes/components/Radio.tsx b/src/routes/components/Radio.tsx index 62708c8e2..90159077a 100644 --- a/src/routes/components/Radio.tsx +++ b/src/routes/components/Radio.tsx @@ -24,6 +24,8 @@ import { TestIdProperty } from "@components/component-properties/common-properties.ts"; import { GoabRadioGroupOnChangeDetail } from "@abgov/ui-components-common"; +import { DesignEmpty } from "@components/empty-states/design-empty/DesignEmpty.tsx"; +import { AccessibilityEmpty } from "@components/empty-states/accessibility-empty/AccessibilityEmpty.tsx"; // == Page props == const componentName = "Radio"; @@ -34,6 +36,7 @@ const relatedComponents = [ { link: "/components/dropdown", name: "Dropdown" }, { link: "/components/form-item", name: "Form item" } ]; +const FIGMA_LINK = "https://www.figma.com/design/3pb2IK8s2QUqWieH79KdN7/%E2%9D%96-Component-library-%7C-DDD?node-id=102-26"; type ComponentPropsType = GoabRadioGroupProps; type CastingType = { name: string; @@ -47,6 +50,7 @@ export default function RadioPage() { const [radioProps, setRadioProps] = useState({ name: "item", value: "", + onChange: () => {}, }); const [radioBindings, setRadioBindings] = useState([ { @@ -290,14 +294,16 @@ export default function RadioPage() { description={description} category={category} relatedComponents={relatedComponents} + figmaLink={FIGMA_LINK} + githubLink="Radio" /> - - + + {/*Radio sandbox*/}

- Component + Playground

-
- Design guidelines - + Examples + - }> + } + > + +
+ + + + + + + +
diff --git a/src/routes/components/SideMenu.tsx b/src/routes/components/SideMenu.tsx index c814a8cb2..a21aa1915 100644 --- a/src/routes/components/SideMenu.tsx +++ b/src/routes/components/SideMenu.tsx @@ -16,6 +16,11 @@ import { Sandbox } from "@components/sandbox"; import { ComponentContent } from "@components/component-content/ComponentContent"; import { LanguageVersionContext } from "@contexts/LanguageVersionContext.tsx"; import { useContext } from "react"; +import { DesignEmpty } from "@components/empty-states/design-empty/DesignEmpty.tsx"; +import { AccessibilityEmpty } from "@components/empty-states/accessibility-empty/AccessibilityEmpty.tsx"; +import { ExamplesEmpty } from "@components/empty-states/examples-empty/ExamplesEmpty.tsx"; + +const FIGMA_LINK = "https://www.figma.com/design/3pb2IK8s2QUqWieH79KdN7/%E2%9D%96-Component-library-%7C-DDD?node-id=24089-474089"; const componentName = "Side menu"; const description = @@ -122,13 +127,15 @@ export default function SideMenuPage() { category={componentCategory} description={description} relatedComponents={relatedComponents} + figmaLink={FIGMA_LINK} + githubLink="Side menu" /> - - -

Component

+ + +

Playground

{/*Angular code*/} @@ -284,11 +291,20 @@ export default function SideMenuPage() { - Design guidelines - + Examples + - }> -

Coming Soon

+ } + > + +
+ + + + + + +
diff --git a/src/routes/components/Skeleton.tsx b/src/routes/components/Skeleton.tsx index 1cc589535..78bf91575 100644 --- a/src/routes/components/Skeleton.tsx +++ b/src/routes/components/Skeleton.tsx @@ -6,11 +6,10 @@ import { } from "@components/component-properties/ComponentProperties.tsx"; import { Category, ComponentHeader } from "@components/component-header/ComponentHeader.tsx"; import { - GoabBadge, GoabSkeleton, GoabTab, GoabTabs, - GoabSkeletonProps, + GoabSkeletonProps, GoabBlock, GoabText } from "@abgov/react-components"; import { ComponentContent } from "@components/component-content/ComponentContent"; import { GoabSkeletonType } from "@abgov/ui-components-common"; @@ -19,12 +18,15 @@ import { MarginProperty, TestIdProperty } from "@components/component-properties/common-properties.ts"; +import { DesignEmpty } from "@components/empty-states/design-empty/DesignEmpty.tsx"; +import { AccessibilityEmpty } from "@components/empty-states/accessibility-empty/AccessibilityEmpty.tsx"; // == Page props == -const componentName = "Skeleton loading"; +const FIGMA_LINK = "https://www.figma.com/design/3pb2IK8s2QUqWieH79KdN7/%E2%9D%96-Component-library-%7C-DDD?node-id=27301-303445"; +const componentName = "Skeleton loader"; const description = "Provide visual feedback to users while loading a content heavy page or page element."; -const category = Category.CONTENT_AND_LAYOUT; +const category = Category.FEEDBACK_AND_ALERTS; const relatedComponents = [{ link: "/components/progress-indicator", name: "Progress indicator" }]; type ComponentPropsType = GoabSkeletonProps; type CastingType = { @@ -119,7 +121,7 @@ export default function SkeletonPage() { description: "Set component maximum width. Currently only used in card skeleton type", defaultValue: "320px", lang: "react", - }, + }, ]; const componentProperties: ComponentProperty[] = [ { @@ -163,30 +165,124 @@ export default function SkeletonPage() { category={category} description={description} relatedComponents={relatedComponents} + figmaLink={FIGMA_LINK} + githubLink="Skeleton loader" /> - - - {/*Skeleton Sandbox*/} -

- Component -

+ + +

Playground

+ +
+ + + + + Card + +

Card

+ + + + + + + Image + +

Image

+ + + + + + + Text + +

Text

+ + + + + + + Small text + +

Small text

+ + + + + + + Title + +

Title

+ + + + - {/*Skeleton Properties*/} - + + Header + +

Header

+ + + + + + + Paragraph + +

Paragraph

+ + + + + + + Thumbnail + +

Thumbnail

+ + + + + + + Avatar + +

Avatar

+ + + + + + + Profile + +

Profile

+ + + + +
- - Design guidelines - - - }> + + + + + + +
diff --git a/src/routes/components/Spacer.tsx b/src/routes/components/Spacer.tsx index ddfe5b769..eec8017ab 100644 --- a/src/routes/components/Spacer.tsx +++ b/src/routes/components/Spacer.tsx @@ -7,6 +7,10 @@ import { ComponentProperty, } from "@components/component-properties/ComponentProperties.tsx"; import { ComponentContent } from "@components/component-content/ComponentContent"; +import { DesignEmpty } from "@components/empty-states/design-empty/DesignEmpty.tsx"; +import { AccessibilityEmpty } from "@components/empty-states/accessibility-empty/AccessibilityEmpty.tsx"; + +const FIGMA_LINK = "https://www.figma.com/design/3pb2IK8s2QUqWieH79KdN7/%E2%9D%96-Component-library-%7C-DDD?node-id=27301-303446"; export default function SpacerPage() { const [hSpacerProps, setHSpacerProps] = useState({}); @@ -111,13 +115,15 @@ export default function SpacerPage() { { link: "/components/grid", name: "Grid" }, { link: "/patterns/layout", name: "Layout" }, ]} + figmaLink={FIGMA_LINK} + githubLink="Spacer" /> - - -

Component

+ + +

Playground

@@ -152,6 +158,14 @@ export default function SpacerPage() { + + + + + + + + diff --git a/src/routes/components/Table.tsx b/src/routes/components/Table.tsx index 4c3c2d4c8..991f0620a 100644 --- a/src/routes/components/Table.tsx +++ b/src/routes/components/Table.tsx @@ -8,7 +8,6 @@ import { Category, ComponentHeader } from "@components/component-header/Componen import { GoabBadge, GoabButton, - GoabContainer, GoabTab, GoabTable, GoabTableSortHeader, @@ -19,7 +18,11 @@ import { GoabTableProps } from "@abgov/react-components"; import { ComponentContent } from "@components/component-content/ComponentContent"; import { GoabTableOnSortDetail } from "@abgov/ui-components-common"; import { LanguageVersionContext } from "@contexts/LanguageVersionContext.tsx"; +import { TableWithGlobalFiltersExample } from "@examples/filter-chip/TableWithGlobalFiltersExample.tsx"; import { omit } from "lodash"; +import { DesignEmpty } from "@components/empty-states/design-empty/DesignEmpty.tsx"; +import { AccessibilityEmpty } from "@components/empty-states/accessibility-empty/AccessibilityEmpty.tsx"; +import { SandboxHeader } from "@components/sandbox/sandbox-header/sandboxHeader.tsx"; interface User { firstName: string; @@ -27,15 +30,16 @@ interface User { age: number; } -type ComponentPropsType = Omit & { +type ComponentPropsType = Omit & { onSort?: (sortBy: string, sortDir: number) => void; }; export default function TablePage() { - const {version} = useContext(LanguageVersionContext); + const { version } = useContext(LanguageVersionContext); const [tableProps, setTableProps] = useState({ - width: "100%" + width: "100%", }); + const FIGMA_LINK = "https://www.figma.com/design/3pb2IK8s2QUqWieH79KdN7/%E2%9D%96-Component-library-%7C-DDD?node-id=3785-18038"; const [tableBindings, setTableBindings] = useState([ { label: "Width", @@ -83,6 +87,7 @@ export default function TablePage() { description: "Apply margin to the top, right, bottom, and/or left of the component.", }, ]; + const componentProperties: ComponentProperty[] = [ { name: "width", @@ -162,24 +167,26 @@ export default function TablePage() { relatedComponents={[ { link: "/components/button", name: "Button" }, { link: "/components/dropdown", name: "Dropdown" }, + { link: "/components/filter-chip", name: "Filter chip" }, { link: "/components/pagination", name: "Pagination" }, { link: "/components/tabs", name: "Tabs" }, ]} + githubLink="Table" + figmaLink={FIGMA_LINK} /> - - - + + +

Playground

{ if (tableProps.onSort) { tableProps.onSort(detail.sortBy, detail.sortDir); } - }} - > + }}> Status @@ -234,11 +241,22 @@ export default function TablePage() { - +
+ + Examples + + + } + > + -

Sortable columns

- -
+ + + @@ -265,9 +283,8 @@ export default function TablePage() { ))} -
-
+ {/*React code*/} {version === "old" && ( )} -

Number column

+ + @@ -602,15 +622,18 @@ export default function TablePage() { + +

Filter data in a table

+ +
+ + + + + + - - Design guidelines - - - }>
diff --git a/src/routes/components/Tabs.tsx b/src/routes/components/Tabs.tsx index 1a2e8b3be..e5b0b1f9b 100644 --- a/src/routes/components/Tabs.tsx +++ b/src/routes/components/Tabs.tsx @@ -15,12 +15,15 @@ import { LanguageVersionContext } from "@contexts/LanguageVersionContext.tsx"; import { useContext } from "react"; import { TestIdProperty } from "@components/component-properties/common-properties.ts"; import { TabsExamples } from "@examples/tabs/TabsExamples.tsx"; +import { DesignEmpty } from "@components/empty-states/design-empty/DesignEmpty.tsx"; +import { AccessibilityEmpty } from "@components/empty-states/accessibility-empty/AccessibilityEmpty.tsx"; // == Page props == const componentName = "Tabs"; const description = "Let users navigate between related sections of content, displaying one section at a time."; const category = Category.STRUCTURE_AND_NAVIGATION; +const FIGMA_LINK = "https://www.figma.com/design/3pb2IK8s2QUqWieH79KdN7/%E2%9D%96-Component-library-%7C-DDD?node-id=25293-519360"; export default function TabsPage() { const { version } = useContext(LanguageVersionContext); const oldComponentProperties: ComponentProperty[] = [ @@ -91,13 +94,19 @@ export default function TabsPage() { return ( <> - + - - + +

- Component + Playground

{/*Cannot use Sandbox because if we change the language/version, the tabs inside the tabs will make the below bugs:*/} {/*1. Scroll down to an example*/} @@ -275,15 +284,23 @@ export default function TabsPage() { oldProperties={oldTabProperties} /> -
- Design guidelines - + Examples + - }> + } + > + +
+ + + + + +
diff --git a/src/routes/components/TemporaryNotification.tsx b/src/routes/components/TemporaryNotification.tsx index 74c657133..2e4b9ee68 100644 --- a/src/routes/components/TemporaryNotification.tsx +++ b/src/routes/components/TemporaryNotification.tsx @@ -59,7 +59,7 @@ export default function TEMPLATE_Page() { relatedComponents={relatedComponents} /> - + <> diff --git a/src/routes/components/Text.tsx b/src/routes/components/Text.tsx index 2d5fe7545..aa00b559d 100644 --- a/src/routes/components/Text.tsx +++ b/src/routes/components/Text.tsx @@ -6,6 +6,11 @@ import { } from "@components/component-properties/ComponentProperties.tsx"; import { Category, ComponentHeader } from "@components/component-header/ComponentHeader.tsx"; import { GoabText, GoabTab, GoabTabs, GoabBadge } from "@abgov/react-components"; +import { DesignEmpty } from "@components/empty-states/design-empty/DesignEmpty.tsx"; +import { AccessibilityEmpty } from "@components/empty-states/accessibility-empty/AccessibilityEmpty.tsx"; +import { ExamplesEmpty } from "@components/empty-states/examples-empty/ExamplesEmpty.tsx"; + +const FIGMA_LINK = "https://www.figma.com/design/3pb2IK8s2QUqWieH79KdN7/%E2%9D%96-Component-library-%7C-DDD?node-id=27301-303449"; export default function TextPage() { const [textProps, setTextProps] = useState({}); @@ -19,9 +24,9 @@ export default function TextPage() { value: "", }, { - label: "As", + label: "Tag", type: "list", - name: "as", + name: "tag", options: ["", "h1", "h2", "h3", "h4", "h5", "span", "div", "p" ], value: "", }, @@ -30,7 +35,7 @@ export default function TextPage() { type: "string", name: "maxWidth", value: "" - }, + }, { label: "Color", type: "list", @@ -56,7 +61,7 @@ export default function TextPage() { const componentProperties: ComponentProperty[] = [ { - name: "as", + name: "tag", type: "h1 | h2 | h3 | h4 | h5 | span | div | p", description: "Sets the tag and text size.", defaultValue: "div" @@ -96,28 +101,40 @@ export default function TextPage() { name="Text" category={Category.CONTENT_AND_LAYOUT} description="Provides consistent sizing, spacing, and colour to written content." + figmaLink={FIGMA_LINK} + githubLink="Text" /> - - + + +

Playground

- - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et + dolore magna aliqua. -
- + - Design guidelines - + Examples + } - > + > + +
+ + + + + + + +
); diff --git a/src/routes/components/TextArea.tsx b/src/routes/components/TextArea.tsx index 041bed539..278a1db5c 100644 --- a/src/routes/components/TextArea.tsx +++ b/src/routes/components/TextArea.tsx @@ -9,8 +9,8 @@ import { GoabBadge, GoabFormItem, GoabTab, - GoabTabs, GoabTextarea, - GoabTextAreaProps + GoabTabs, + GoabTextarea, } from "@abgov/react-components"; import { CodeSnippet } from "@components/code-snippet/CodeSnippet.tsx"; import { useSandboxFormItem } from "@hooks/useSandboxFormItem.tsx"; @@ -23,9 +23,12 @@ import { TestIdProperty } from "@components/component-properties/common-properties.ts"; import { TextAreaExamples } from "@examples/textarea/TextAreaExamples.tsx"; +import { DesignEmpty } from "@components/empty-states/design-empty/DesignEmpty.tsx"; +import { AccessibilityEmpty } from "@components/empty-states/accessibility-empty/AccessibilityEmpty.tsx"; // == Page props == +const FIGMA_LINK = "https://www.figma.com/design/3pb2IK8s2QUqWieH79KdN7/%E2%9D%96-Component-library-%7C-DDD?node-id=133-186"; const componentName = "Text area"; const description = "A multi-line field where users can input and edit text."; const category = Category.INPUTS_AND_ACTIONS; @@ -33,7 +36,7 @@ const relatedComponents = [ { link: "/components/form-item", name: "Form item" }, { link: "/components/input", name: "Input" }, ]; -type ComponentPropsType = GoabTextAreaProps; + type CastingType = { name: string; value: string; @@ -43,7 +46,7 @@ type CastingType = { export default function TextAreaPage() { const {version} = useContext(LanguageVersionContext); - const [componentProps, setComponentProps] = useState({ + const [componentProps, setComponentProps] = useState({ name: "item", value: "", onChange: () => {}, @@ -342,13 +345,15 @@ export default function TextAreaPage() { category={category} description={description} relatedComponents={relatedComponents} + figmaLink={FIGMA_LINK} + githubLink="Text area" /> - - + +

- Component + Playground

- - {/*Examples*/} -
- Design guidelines - + Examples + - }> -

Coming Soon

+ } + > + +
+ + + + + + +
diff --git a/src/routes/components/TextField.tsx b/src/routes/components/TextField.tsx index bf9d97b10..97cb0902d 100644 --- a/src/routes/components/TextField.tsx +++ b/src/routes/components/TextField.tsx @@ -27,6 +27,10 @@ import { } from "@components/component-properties/common-properties.ts"; import { omit } from "lodash"; +const FIGMA_LINK = "https://www.figma.com/design/3pb2IK8s2QUqWieH79KdN7/%E2%9D%96-Component-library-%7C-DDD?node-id=33054-34641"; +import { DesignEmpty } from "@components/empty-states/design-empty/DesignEmpty.tsx"; +import { AccessibilityEmpty } from "@components/empty-states/accessibility-empty/AccessibilityEmpty.tsx"; + // == Page props == const componentName = "Input"; const description = "A single-line field where users can input and edit text."; @@ -590,14 +594,16 @@ export default function TextFieldPage() { category={category} description={description} relatedComponents={relatedComponents} + figmaLink={FIGMA_LINK} + githubLink="Input" /> - - + + {/*Input sandbox*/} -

Component

+

Playground

{/*Examples*/} -
- Design guidelines - + Examples + - }> -

Coming Soon

+ } + > + +
+ + + + + +
); } + diff --git a/src/routes/components/Tooltip.tsx b/src/routes/components/Tooltip.tsx index 16936e81d..467fbc9d1 100644 --- a/src/routes/components/Tooltip.tsx +++ b/src/routes/components/Tooltip.tsx @@ -21,9 +21,12 @@ import { TestIdProperty } from "@components/component-properties/common-properties.ts"; import { TooltipExamples } from "@examples/tooltip/TooltipExamples.tsx"; +import { DesignEmpty } from "@components/empty-states/design-empty/DesignEmpty.tsx"; +import { AccessibilityEmpty } from "@components/empty-states/accessibility-empty/AccessibilityEmpty.tsx"; // == Page props == +const FIGMA_LINK = "https://www.figma.com/design/3pb2IK8s2QUqWieH79KdN7/%E2%9D%96-Component-library-%7C-DDD?node-id=21932-557049"; const componentName = "Tooltip"; const description = "A small popover that displays more information about an item."; const relatedComponents = [ @@ -114,8 +117,6 @@ export default function TooltipPage() { setComponentProps(props as CastingType); } - - return ( <> - - -

Component

+ + +

Playground

- - - - - - - -
- Design guidelines - + Examples + } - > + > + +
+ + + + + + + +
diff --git a/src/routes/content/ContentLayout.tsx b/src/routes/content/ContentLayout.tsx index b82e47c65..24a64c32e 100644 --- a/src/routes/content/ContentLayout.tsx +++ b/src/routes/content/ContentLayout.tsx @@ -1,4 +1,4 @@ -import { GoabSideMenu, GoabSideMenuHeading } from "@abgov/react-components"; +import { GoabSideMenu, GoabSpacer } from "@abgov/react-components"; import { Link, Outlet } from "react-router-dom"; import "./content.css"; import { SupportInfo } from "@components/support-info/SupportInfo"; @@ -8,7 +8,7 @@ export default function ContentLayout() {
- Content + Capitalization Date format Error messages diff --git a/src/routes/design-tokens/DesignTokenLayout.tsx b/src/routes/design-tokens/DesignTokenLayout.tsx index 52ecb72a5..a81f448ba 100644 --- a/src/routes/design-tokens/DesignTokenLayout.tsx +++ b/src/routes/design-tokens/DesignTokenLayout.tsx @@ -39,10 +39,10 @@ export function DesignTokenLayout() { All - Border Radius - Border Width + Border radius + Border width Color - Icon Size + Icon size Opacity Shadow Spacing diff --git a/src/routes/design-tokens/border-radius/BorderRadius.css b/src/routes/design-tokens/border-radius/BorderRadius.css index d74225ef2..2cfa86e6d 100644 --- a/src/routes/design-tokens/border-radius/BorderRadius.css +++ b/src/routes/design-tokens/border-radius/BorderRadius.css @@ -2,4 +2,5 @@ height: 24px; width: 64px; background-color: var(--goa-color-greyscale-400); + margin-top: 2px; } diff --git a/src/routes/design-tokens/border-radius/BorderRadius.tsx b/src/routes/design-tokens/border-radius/BorderRadius.tsx index bfb85a69e..ebff7fd56 100644 --- a/src/routes/design-tokens/border-radius/BorderRadius.tsx +++ b/src/routes/design-tokens/border-radius/BorderRadius.tsx @@ -4,7 +4,7 @@ import { TokenSnippet } from "@components/token-snippet/TokenSnippet"; import "./BorderRadius.css"; import { Token } from "../token"; import { getTokenGroups } from "../getTokenGroups"; -import { DeviceWidthContext } from "../../../contexts/DeviceWidthContext"; +import { DeviceWidthContext } from "@contexts/DeviceWidthContext.tsx"; import { getCssVarValue } from "../../../utils/styling"; import { ComponentContent } from "@components/component-content/ComponentContent"; @@ -14,13 +14,43 @@ export default function BorderRadiusPage() { tokenName: "goa-border-radius-none", rem: "0rem", px: "0px", - figmaUsage: "Border Radius/None.", + figmaUsage: "Border-radius/None", + }, + { + tokenName: "goa-border-radius-s", + rem: "0.125rem", + px: "2px", + figmaUsage: "Border-radius/Small", }, { tokenName: "goa-border-radius-m", rem: "0.25rem", px: "4px", - figmaUsage: "Border Radius/Medium", + figmaUsage: "Border-radius/Medium", + }, + { + tokenName: "goa-border-radius-l", + rem: "0.375rem", + px: "6px", + figmaUsage: "Border-radius/Large", + }, + { + tokenName: "goa-border-radius-xl", + rem: "0.5rem", + px: "8px", + figmaUsage: "Border-radius/XLarge", + }, + { + tokenName: "goa-border-radius-2xl", + rem: "0.625rem", + px: "10px", + figmaUsage: "Border-radius/2XLarge", + }, + { + tokenName: "goa-border-radius-3xl", + rem: "0.75rem", + px: "12px", + figmaUsage: "Border-radius/3XLarge", }, ]; const { isDesktop } = useContext(DeviceWidthContext); @@ -31,10 +61,10 @@ export default function BorderRadiusPage() { - Token name + Design token rem px - Figma variable + Figma @@ -63,7 +93,7 @@ export default function BorderRadiusPage() { const renderMobile = () => { return ( - + {getTokenGroups(tokens).map(group => group.map((token, idx) => ( <> @@ -74,11 +104,11 @@ export default function BorderRadiusPage() { borderRadius: getCssVarValue(`--${token.tokenName}`), }} /> - +
-
rem
{token.rem}
-
px
{token.px}
-
Figma variable
{token.figmaUsage}
+
rem
{token.rem}
+
px
{token.px}
+
Figma variable
{token.figmaUsage}
diff --git a/src/routes/design-tokens/border-width/BorderWidth.css b/src/routes/design-tokens/border-width/BorderWidth.css index c099fa16c..adecfd585 100644 --- a/src/routes/design-tokens/border-width/BorderWidth.css +++ b/src/routes/design-tokens/border-width/BorderWidth.css @@ -1,4 +1,5 @@ .border-width-page div.token-block { width: 64px; background-color: #333333; + margin-top: 12px; } diff --git a/src/routes/design-tokens/border-width/BorderWidth.tsx b/src/routes/design-tokens/border-width/BorderWidth.tsx index 44c1f8ff0..3299e4f3b 100644 --- a/src/routes/design-tokens/border-width/BorderWidth.tsx +++ b/src/routes/design-tokens/border-width/BorderWidth.tsx @@ -10,23 +10,47 @@ import { ComponentContent } from "@components/component-content/ComponentContent export default function BorderWidthPage() { const tokens: Token[] = [ + { + tokenName: "goa-border-width-none", + rem: "0rem", + px: "0px", + figmaUsage: "Border-width/None", + }, + { + tokenName: "goa-border-width-2xs", + rem: "0.03125rem", + px: "0.5px", + figmaUsage: "Border-width/2XSmall", + }, + { + tokenName: "goa-border-width-xs", + rem: "0.04375rem", + px: "0.7px", + figmaUsage: "Border-width/XSmall", + }, { tokenName: "goa-border-width-s", rem: "0.0625rem", px: "1px", - figmaUsage: "Use a border width of 1px.", + figmaUsage: "Border-width/Small", }, { tokenName: "goa-border-width-m", rem: "0.125rem", px: "2px", - figmaUsage: "Use a border width of 2px.", + figmaUsage: "Border-width/Medium", }, { tokenName: "goa-border-width-l", rem: "0.1875rem", px: "3px", - figmaUsage: "Use a border width of 3px.", + figmaUsage: "Border-width/Large", + }, + { + tokenName: "goa-border-width-xl", + rem: "0.25rem", + px: "4px", + figmaUsage: "Border-width/XLarge", }, ]; const { isDesktop } = useContext(DeviceWidthContext); @@ -37,10 +61,10 @@ export default function BorderWidthPage() { - Token name + Design token rem px - Figma usage + Figma @@ -69,7 +93,7 @@ export default function BorderWidthPage() { const renderMobile = () => { return ( - + {getTokenGroups(tokens).map(group => group.map((token, idx) => ( @@ -79,11 +103,11 @@ export default function BorderWidthPage() { height: getCssVarValue(`--${token.tokenName}`), }} /> - +
-
rem
{token.rem}
-
px
{token.px}
-
Figma usage
{token.figmaUsage}
+
rem
{token.rem}
+
px
{token.px}
+
Figma usage
{token.figmaUsage}
)) diff --git a/src/routes/design-tokens/color/Color.css b/src/routes/design-tokens/color/Color.css index ed109b4bf..314ab092b 100644 --- a/src/routes/design-tokens/color/Color.css +++ b/src/routes/design-tokens/color/Color.css @@ -1,12 +1,20 @@ .colors-page div.token-block { height: 22px; width: 24px; - border-radius: var(--goa-border-radius-m); + border-radius: var(--goa-border-radius-l); + margin-top: 4px; } .colors-page a { margin-right: 0.5rem; } +.dd-style { + margin-left: 0; + margin-bottom: 12px; +} +.dd-style:last-of-type { + margin-bottom: 0; +} .colors-page p.category { margin-bottom: 1rem; @@ -16,10 +24,6 @@ line-height: var(--goa-line-height-4); } -.colors-page td[rowspan] { - border-right: 1px solid #dcdcdc; -} - /*Tablet*/ @media screen and (max-width: 1231px) { .colors-page div.token-block { @@ -30,9 +34,7 @@ /*Mobile*/ @media screen and (max-width: 623px) { .colors-page p.category { - font-size: var(--goa-font-size-4); - font-weight: var(--goa-font-weight-bold); - line-height: var(--goa-line-height-3); margin-top: 0; + margin-left: 0; } } diff --git a/src/routes/design-tokens/color/Color.tsx b/src/routes/design-tokens/color/Color.tsx index 1d66c4388..eab8cd91f 100644 --- a/src/routes/design-tokens/color/Color.tsx +++ b/src/routes/design-tokens/color/Color.tsx @@ -1,4 +1,4 @@ -import { GoabContainer, GoabGrid, GoabTable } from "@abgov/react-components"; +import { GoabContainer, GoabGrid, GoabTable, GoabText } from "@abgov/react-components"; import { TokenSnippet } from "@components/token-snippet/TokenSnippet"; import "./Color.css"; import COLORS from "./colors.json"; @@ -25,57 +25,66 @@ export default function ColorPage() { const renderDesktop = () => { return ( - - - - Type - - Token - Hex value - Figma color style - Description - - - - {colors.map((color, index) => ( - - {color.tokens.map((token, tokenIndex) => ( - - {tokenIndex === 0 && {color.name}} - -
- - - - - {token.value} - {token.figmaColorStyle} - {token.description} + <> + {colors.map((color, index) => ( +
+ {color.name} + + + + + Design token + Hex code + Figma - ))} - - ))} - - + + + {color.tokens.map((token, tokenIndex) => ( + + +
+ + + + + {token.value} + {token.figmaColorStyle} + + {/* + + {token.description && ( + + {token.description} + + )} + + */} + + + ))} + + +
+ ))} + ); }; const renderTablet = () => { return (
- <> {colors.map((color, index) => (

{color.name}

- + {color.tokens.map((token, tokenIndex) => (
- +
-
Hex value
{token.value}
-
Figma colour style
{token.figmaColorStyle}
-
Description
{token.description}
+
Hex value
{token.value}
+
Figma colour style
{token.figmaColorStyle}
+
Description
{token.description}
))} ))} -
); }; diff --git a/src/routes/design-tokens/color/colors.json b/src/routes/design-tokens/color/colors.json index 8d75702d7..98ffe9876 100644 --- a/src/routes/design-tokens/color/colors.json +++ b/src/routes/design-tokens/color/colors.json @@ -14,6 +14,18 @@ "figmaColorStyle": "Interactive/Hover", "description": "Hover state for interactive elements." }, + { + "code": "goa-color-interactive-disabled", + "value": "#80B7E1", + "figmaColorStyle": "Interactive/Disabled", + "description": "Disabled state for interactive elements." + }, + { + "code": "goa-color-interactive-visited", + "value": "#756693", + "figmaColorStyle": "Interactive/Visited", + "description": "Visited state for links." + }, { "code": "goa-color-interactive-focus", "value": "#FEBA35", @@ -26,12 +38,6 @@ "figmaColorStyle": "Interactive/Error", "description": "Error state for interactive elements." }, - { - "code": "goa-color-interactive-disabled", - "value": "#80B7E1", - "figmaColorStyle": "Interactive/Disabled", - "description": "Disabled state for interactive elements." - }, { "code": "goa-color-interactive-error-hover", "value": "#BA0000", @@ -114,7 +120,7 @@ "description": "" }, { - "code": "goa-color-brand-light", + "code": "goa-color-info-dark", "value": "#003B70", "figmaColorStyle": "Status/Info/Dark", "description": "" @@ -214,18 +220,36 @@ "figmaColorStyle": "Greyscale/White", "description": "" }, + { + "code": "goa-color-greyscale-50", + "value": "#F8F8F8", + "figmaColorStyle": "Greyscale/50", + "description": "" + }, { "code": "goa-color-greyscale-100", "value": "#F1F1F1", "figmaColorStyle": "Greyscale/100", "description": "" }, + { + "code": "goa-color-greyscale-150", + "value": "#E7E7E7", + "figmaColorStyle": "Greyscale/150", + "description": "" + }, { "code": "goa-color-greyscale-200", "value": "#DCDCDC", "figmaColorStyle": "Greyscale/200", "description": "" }, + { + "code": "goa-color-greyscale-300", + "value": "#C2C2C2", + "figmaColorStyle": "Greyscale/300", + "description": "" + }, { "code": "goa-color-greyscale-400", "value": "#ADADAD", @@ -250,12 +274,127 @@ "figmaColorStyle": "Greyscale/700", "description": "" }, + { + "code": "goa-color-greyscale-800", + "value": "#5C5C5C", + "figmaColorStyle": "Greyscale/800", + "description": "" + }, + { + "code": "goa-color-greyscale-900", + "value": "#474747", + "figmaColorStyle": "Greyscale/900", + "description": "" + }, { "code": "goa-color-greyscale-black", "value": "#333333", "figmaColorStyle": "Greyscale/Black", "description": "" } + + ] + }, + { + "name": "Extended", + "tokens": [ + { + "code": "goa-color-extended-aqua", + "value": "#7FEBE6", + "figmaColorStyle": "Extended/Aqua", + "description": "" + }, + { + "code": "goa-color-extended-light-aqua", + "value": "#DCFBF8", + "figmaColorStyle": "Extended/Light/Aqua", + "description": "" + }, + { + "code": "goa-color-extended-blue", + "value": "#AAC9E7", + "figmaColorStyle": "Extended/Blue", + "description": "" + }, + { + "code": "goa-color-extended-light-blue", + "value": "#DDEFFF", + "figmaColorStyle": "Extended/Light/Blue", + "description": "" + }, + { + "code": "goa-color-extended-green", + "value": "#BBFCB4", + "figmaColorStyle": "Extended/Green", + "description": "" + }, + { + "code": "goa-color-extended-light-green", + "value": "#CCE2D9", + "figmaColorStyle": "Extended/Light/Green", + "description": "" + }, + { + "code": "goa-color-extended-orange", + "value": "#FFC76D", + "figmaColorStyle": "Extended/Orange", + "description": "" + }, + { + "code": "goa-color-extended-light-orange", + "value": "#FCD6C3", + "figmaColorStyle": "Extended/Light/Orange", + "description": "" + }, + { + "code": "goa-color-extended-pink", + "value": "#FF8FC5", + "figmaColorStyle": "Extended/Pink", + "description": "" + }, + { + "code": "goa-color-extended-light-pink", + "value": "#F9E1EB", + "figmaColorStyle": "Extended/Light/Pink", + "description": "" + }, + { + "code": "goa-color-extended-red", + "value": "#ED948D", + "figmaColorStyle": "Extended/Red", + "description": "" + }, + { + "code": "goa-color-extended-light-red", + "value": "#F4CDC6", + "figmaColorStyle": "Extended/Light/Red", + "description": "" + }, + { + "code": "goa-color-extended-violet", + "value": "#D4C2FF", + "figmaColorStyle": "Extended/Violet", + "description": "" + }, + { + "code": "goa-color-extended-light-violet", + "value": "#EFE2FB", + "figmaColorStyle": "Extended/Light/Violet", + "description": "" + }, + { + "code": "goa-color-extended-yellow", + "value": "#FCE796", + "figmaColorStyle": "Extended/Yellow", + "description": "" + }, + { + "code": "goa-color-extended-light-yellow", + "value": "#FFF7BF", + "figmaColorStyle": "Extended/Light/Yellow", + "description": "" + } ] } ] + diff --git a/src/routes/design-tokens/icon-size/IconSize.css b/src/routes/design-tokens/icon-size/IconSize.css index 86882536e..ba3443e6f 100644 --- a/src/routes/design-tokens/icon-size/IconSize.css +++ b/src/routes/design-tokens/icon-size/IconSize.css @@ -1,4 +1,7 @@ .icon-size div.icon { + display: flex; + align-items: center; + justify-content: center; border-radius: var(--goa-border-radius-none); background-color: var(--goa-color-emergency-background); } @@ -17,3 +20,33 @@ height: 32px; width: 32px; } + +.goa-icon-size-1 { + height: 16px; + width: 16px; +} + +.goa-icon-size-2 { + height: 18px; + width: 18px; +} + +.goa-icon-size-3 { + height: 20px; + width: 20px; +} + +.goa-icon-size-4 { + height: 24px; + width: 24px; +} + +.goa-icon-size-5 { + height: 32px; + width: 32px; +} + +.goa-icon-size-6 { + height: 40px; + width: 40px; +} \ No newline at end of file diff --git a/src/routes/design-tokens/icon-size/IconSize.tsx b/src/routes/design-tokens/icon-size/IconSize.tsx index 2bba84369..871af740a 100644 --- a/src/routes/design-tokens/icon-size/IconSize.tsx +++ b/src/routes/design-tokens/icon-size/IconSize.tsx @@ -14,27 +14,70 @@ interface IconSizeToken extends Token { export default function IconSizePage() { const tokens: IconSizeToken[] = [ + { + tokenName: "goa-icon-size-1", + rem: "1rem", + px: "16px", + figmaUsage: "Icon-size/1", + size: "1", + }, + { + tokenName: "goa-icon-size-2", + rem: "1.125rem", + px: "18px", + figmaUsage: "Icon-size/2", + size: "2", + }, + { + tokenName: "goa-icon-size-3", + rem: "1.25rem", + px: "20px", + figmaUsage: "Icon-size/3", + size: "3", + }, + { + tokenName: "goa-icon-size-4", + rem: "1.5rem", + px: "24px", + figmaUsage: "Icon-size/4", + size: "4", + }, + { + tokenName: "goa-icon-size-5", + rem: "2rem", + px: "32px", + figmaUsage: "Icon-size/5", + size: "5", + }, + { + tokenName: "goa-icon-size-6", + rem: "2.5rem", + px: "40px", + figmaUsage: "Icon-size/6", + size: "6", + }, { tokenName: "goa-icon-size-s", rem: "1rem", px: "16px", - figmaUsage: "Icon Size/Small", + figmaUsage: "Icon-size/Small", size: "small", }, { tokenName: "goa-icon-size-m", rem: "1.25rem", px: "20px", - figmaUsage: "Icon Size/Medium", + figmaUsage: "Icon-size/Medium", size: "medium", }, { tokenName: "goa-icon-size-l", rem: "1.5rem", px: "24px", - figmaUsage: "Icon Size/Large", + figmaUsage: "Icon-size/Large", size: "large", }, + ]; const { isDesktop } = useContext(DeviceWidthContext); @@ -44,10 +87,10 @@ export default function IconSizePage() { - Token name + Design token rem px - Figma usage + Figma @@ -55,7 +98,7 @@ export default function IconSizePage() {
- +
@@ -73,18 +116,18 @@ export default function IconSizePage() { const renderMobile = () => { return ( - + {getTokenGroups(tokens).map(group => group.map((token: IconSizeToken, idx: number) => (
- +
-
rem
{token.rem}
-
px
{token.px}
-
Figma usage
{token.figmaUsage}
+
rem
{token.rem}
+
px
{token.px}
+
Figma usage
{token.figmaUsage}
)) diff --git a/src/routes/design-tokens/opacity/Opacity.css b/src/routes/design-tokens/opacity/Opacity.css index 383309508..9a00d4afe 100644 --- a/src/routes/design-tokens/opacity/Opacity.css +++ b/src/routes/design-tokens/opacity/Opacity.css @@ -13,3 +13,7 @@ margin-left: 1rem; margin-top: 0.25rem; } + +.opacity-thumbnail { + margin-top: 2px; +} \ No newline at end of file diff --git a/src/routes/design-tokens/opacity/Opacity.tsx b/src/routes/design-tokens/opacity/Opacity.tsx index 39f3a7b32..373ebb416 100644 --- a/src/routes/design-tokens/opacity/Opacity.tsx +++ b/src/routes/design-tokens/opacity/Opacity.tsx @@ -18,12 +18,12 @@ export default function OpacityPage() { { tokenName: "goa-opacity-background-modal", percentage: "50%", - figmaUsage: "Fill: opacity/background/modal", + figmaUsage: "Fill: Opacity/Background/Modal", }, { tokenName: "goa-opacity-background-loading", percentage: "90%", - figmaUsage: "Fill: opacity/background/loading", + figmaUsage: "Fill: Opacity/Background/Loading", }, ]; const { isDesktop } = useContext(DeviceWidthContext); @@ -34,16 +34,16 @@ export default function OpacityPage() { - Token name + Design token % - Figma style + Figma {tokens.map((token, index) => ( -
+
{ return ( - + {getTokenGroups(tokens).map(group => group.map((token: Token, idx: number) => ( @@ -80,10 +80,10 @@ export default function OpacityPage() { }} >
- +
-
%
{token.percentage}
-
Figma usage
{token.figmaUsage}
+
%
{token.percentage}
+
Figma usage
{token.figmaUsage}
)) diff --git a/src/routes/design-tokens/overview/Overview.tsx b/src/routes/design-tokens/overview/Overview.tsx index 147ea48e7..964933de2 100644 --- a/src/routes/design-tokens/overview/Overview.tsx +++ b/src/routes/design-tokens/overview/Overview.tsx @@ -1,10 +1,12 @@ -import { GoabContainer } from "@abgov/react-components"; +import { GoabContainer, GoabText } from "@abgov/react-components"; import "./Overview.css"; import { ComponentContent } from "@components/component-content/ComponentContent.tsx"; export default function DesignTokensOverviewPage() { return ( -

Styles

+ + Design Tokens +

We use design tokens to communicate design decisions across design and development. These design decisions are a limited set of options for spacing, colour, typography, object @@ -27,13 +29,13 @@ export default function DesignTokensOverviewPage() { product teams.

- -

Design token example

-

+ + Example of a design token + The GoA’s colour for a hover interaction is #004F84. This property is defined as a design token called --goa-color-interactive-hover. This token is used in Figma as a style and used in code as CSS or SASS variables -

+

What this means for...

diff --git a/src/routes/design-tokens/shadow/Shadow.css b/src/routes/design-tokens/shadow/Shadow.css index a2fb354ed..3c84d1eb6 100644 --- a/src/routes/design-tokens/shadow/Shadow.css +++ b/src/routes/design-tokens/shadow/Shadow.css @@ -1,7 +1,41 @@ .shadow-page .token-block { - height: 24px; - width: 24px; + height: 64px; + width: 64px; border-radius: var(--goa-border-radius-none); - background-color: var(--goa-color-greyscale-black); + background-color: var(--goa-color-greyscale-50); + margin-top: 2px; +} + +/* Individual shadow tokens */ + +.goa-shadow-modal { box-shadow: 6px 6px 6px 0 rgba(0, 0, 0, 0.16); } + +.goa-shadow-100 { + box-shadow: 0 1px 0 0 rgba(0, 0, 0, 0.16); +} + +.goa-shadow-150 { + box-shadow: 0 1px 0 0 #1a1a1a40; +} + +.goa-shadow-200 { + box-shadow: 0 3px 1px -1px #1a1a1a12; +} + +.goa-shadow-300 { + box-shadow: 0 4px 6px -2px #1a1a1a33; +} + +.goa-shadow-400 { + box-shadow: 0 8px 16px -4px #1a1a1a38; +} + +.goa-shadow-500 { + box-shadow: 0 12px 20px -8px #1a1a1a3D; +} + +.goa-shadow-600 { + box-shadow: 0 20px 20px -8px #1a1a1a47; +} \ No newline at end of file diff --git a/src/routes/design-tokens/shadow/Shadow.tsx b/src/routes/design-tokens/shadow/Shadow.tsx index cb00423ed..15c7dab36 100644 --- a/src/routes/design-tokens/shadow/Shadow.tsx +++ b/src/routes/design-tokens/shadow/Shadow.tsx @@ -17,8 +17,44 @@ export default function ShadowPage() { { tokenName: "goa-shadow-modal", value: "x=6, y=6, blur=6, spread=0, rgba(0,0,0,0.16)", - figmaUsage: "Effect: Drop Shadow/Modal", + figmaUsage: "Effect: Drop-shadow/Modal", }, + { + tokenName: "goa-shadow-100", + value: "x=0, y=1, blur=0, spread=0, #1A1A1A12", + figmaUsage: "Effect: Drop-shadow/100", + }, + { + tokenName: "goa-shadow-150", + value: "x=0, y=1, blur=0, spread=0, #1A1A1A40", + figmaUsage: "Effect: Drop-shadow/150", + }, + { + tokenName: "goa-shadow-200", + value: "x=0, y=3, blur=1, spread=-1, #1A1A1A12", + figmaUsage: "Effect: Drop-shadow/200", + }, + { + tokenName: "goa-shadow-300", + value: "x=0, y=4, blur=6, spread=-2, #1A1A1A33", + figmaUsage: "Effect: Drop-shadow/300", + }, + { + tokenName: "goa-shadow-400", + value: "x=0, y=8, blur=16, spread=-4, #1A1A1A38", + figmaUsage: "Effect: Drop-shadow/400", + }, + { + tokenName: "goa-shadow-500", + value: "x=0, y=12, blur=20, spread=-8, #1A1A1A3D", + figmaUsage: "Effect: Drop-shadow/500", + }, + { + tokenName: "goa-shadow-600", + value: "x=0, y=20, blur=20, spread=-8, #1A1A1A47", + figmaUsage: "Effect: Drop-shadow/600", + }, + ]; const { isDesktop } = useContext(DeviceWidthContext); @@ -28,16 +64,16 @@ export default function ShadowPage() { - Token name + Design token Value - Figma usage + Figma {tokens.map((token, index) => ( -
+
@@ -53,20 +89,22 @@ export default function ShadowPage() { const renderMobile = () => { return ( - - {getTokenGroups(tokens).map(group => - group.map((token: Token, idx: number) => ( - -
- -
-
Value
{token.value}
-
Figma usage
{token.figmaUsage}
-
- - )) - )} - +
+ + {getTokenGroups(tokens).map(group => + group.map((token: Token, idx: number) => ( + +
+ +
+
Value
{token.value}
+
Figma usage
{token.figmaUsage}
+
+ + )) + )} + +
); }; diff --git a/src/routes/design-tokens/spacing/Spacing.css b/src/routes/design-tokens/spacing/Spacing.css index adc7c28f9..909a08681 100644 --- a/src/routes/design-tokens/spacing/Spacing.css +++ b/src/routes/design-tokens/spacing/Spacing.css @@ -2,6 +2,7 @@ .spacing-page div.represent { display: flex; align-items: center; + margin-top: 4px; } .spacing-page div.grey-circle { diff --git a/src/routes/design-tokens/spacing/Spacing.tsx b/src/routes/design-tokens/spacing/Spacing.tsx index 5ddbab548..b942561d2 100644 --- a/src/routes/design-tokens/spacing/Spacing.tsx +++ b/src/routes/design-tokens/spacing/Spacing.tsx @@ -25,10 +25,10 @@ export default function SpacingPage() { - Token name + Design token rem px - Figma variable + Figma @@ -61,7 +61,7 @@ export default function SpacingPage() { const renderMobile = () => { return ( - + {getTokenGroups(tokens).map(group => group.map((token: Token, idx: number) => ( @@ -75,11 +75,11 @@ export default function SpacingPage() { >
- +
-
REM
{token.rem}
-
PX
{token.px}
-
Figma usage
{token.figmaUsage}
+
REM
{token.rem}
+
PX
{token.px}
+
Figma usage
{token.figmaUsage}
)) diff --git a/src/routes/design-tokens/typography/Typography.tsx b/src/routes/design-tokens/typography/Typography.tsx index f468f0cb3..1814e2ad7 100644 --- a/src/routes/design-tokens/typography/Typography.tsx +++ b/src/routes/design-tokens/typography/Typography.tsx @@ -25,7 +25,7 @@ export default function TypographyPage() { - Figma type style + Design token Type family Weight @@ -55,7 +55,7 @@ export default function TypographyPage() { const renderMobile = () => { return ( - + {getTokenGroups(tokens as Token[]).map(group => group.map((token: TypographyToken, idx: number) => ( @@ -64,12 +64,12 @@ export default function TypographyPage() { {token.figmaTypeStyle}
- +
-
Type family
{token.typeFamily}
-
Weight
{token.weight}
-
Font size
{token.fontSize}
-
Line height
{token.lineHeight}
+
Type family
{token.typeFamily}
+
Weight
{token.weight}
+
Font size
{token.fontSize}
+
Line height
{token.lineHeight}
)) diff --git a/src/routes/design-tokens/typography/typography.json b/src/routes/design-tokens/typography/typography.json index 4688a5193..1c3e2fc14 100644 --- a/src/routes/design-tokens/typography/typography.json +++ b/src/routes/design-tokens/typography/typography.json @@ -40,7 +40,7 @@ "lineHeight": "24px | 1.5rem" }, { - "figmaTypeStyle": "Large text", + "figmaTypeStyle": "Large body", "tokenName": "goa-typography-body-l", "typeFamily": "Acumin Pro SemiCondensed", "weight": "Regular", @@ -48,7 +48,7 @@ "lineHeight": "32px | 2rem" }, { - "figmaTypeStyle": "Medium text", + "figmaTypeStyle": "Medium body", "tokenName": "goa-typography-body-m", "typeFamily": "Acumin Pro SemiCondensed", "weight": "Regular", @@ -56,7 +56,7 @@ "lineHeight": "28px | 1.75rem" }, { - "figmaTypeStyle": "Small text", + "figmaTypeStyle": "Small body", "tokenName": "goa-typography-body-s", "typeFamily": "Acumin Pro SemiCondensed", "weight": "Regular", @@ -64,13 +64,21 @@ "lineHeight": "24px | 1.5rem" }, { - "figmaTypeStyle": "XSmall text", + "figmaTypeStyle": "XSmall body", "tokenName": "goa-typography-body-xs", "typeFamily": "Acumin Pro SemiCondensed", "weight": "Regular", "fontSize": "14px | 0.875rem", "lineHeight": "20px | 1.25rem" }, + { + "figmaTypeStyle": "Large number", + "tokenName": "goa-typography-number-l", + "typeFamily": "Roboto Mono", + "weight": "Regular", + "fontSize": "24px | 1.5rem", + "lineHeight": "32px | 2rem" + }, { "figmaTypeStyle": "Medium number", "tokenName": "goa-typography-number-m", @@ -78,5 +86,13 @@ "weight": "Regular", "fontSize": "18px | 1.125rem", "lineHeight": "28px | 1.75rem" + }, + { + "figmaTypeStyle": "Small number", + "tokenName": "goa-typography-number-s", + "typeFamily": "Roboto Mono", + "weight": "Regular", + "fontSize": "16px | 1rem", + "lineHeight": "24px | 1.5rem" } ] diff --git a/src/routes/get-started/Contribute.tsx b/src/routes/get-started/Contribute.tsx index b71e22472..a322f6b49 100644 --- a/src/routes/get-started/Contribute.tsx +++ b/src/routes/get-started/Contribute.tsx @@ -158,36 +158,37 @@ export default function ContributePage() {

Setup contribution environment

  1. - Go to ui-components page and choose the “Fork” link in the top-right of the page + Go to ui-components repo and clone the repo
  2. -
  3. Create a Fork, choose an owner and a repo name
  4. -
      -
    • Make sure you're forking our repo and not directly cloning our repo
    • -
    -
  5. Clone the newly created repo
  6. Open the repo’s folder in your IDE
  7. +
  8. Create a branch to work on with the name "contributions/story-number"
  9. - Run the following commands in order: + Run the following command: -
  10. Run the bash script:
  11. -
  12. The commands above will create a “playground” folder containing both React and Angular environments. Since nothing in this folder is committed to the repository, you can freely make any changes you want. These environments are designed for testing purposes.
  13. +
  14. The commands above will install all necessary packages and create a “playground” folder containing both React and Angular environments. Since nothing in this folder is committed to the repository, you can freely make any changes you want. These environments are designed for testing purposes.
  15. Use the following commands to run your playground environments:
    Angular - + React - +
-

React and Web wrappers

+

React and Angular wrappers

React wrappers
@@ -197,7 +198,13 @@ export default function ContributePage() {

- Web wrappers
+ Angular wrappers
+ + + Each folder represents a single component with its associated unit tests and wrapper code +

+

Web Components

+

Each folder represents a single component with: @@ -209,22 +216,20 @@ export default function ContributePage() {

  • Component code as *.ts
  • -
  • - Documentation as doc.md  -
  • Testing procedures

    All unit tests must be written in Svelte.

    • - If you're updating/modifying React wrappers, you will need to write unit tests in - React + If you're updating/modifying React and/or Angular wrappers, you will need to write unit tests in + React and/or Angular
    • +
    • It would also be recommended to add proper browser testing using jest for React wrappers and Svelte components.
    • Manually test in React and Angular

    - Additionally, our QA automation developer will perform a comprehensive series of tests to + Additionally, one of our developers will manually test the PR to ensure the components' quality and functionality.

    diff --git a/src/routes/get-started/ReportBug.tsx b/src/routes/get-started/ReportBug.tsx index e7cae9486..7c7e4de7f 100644 --- a/src/routes/get-started/ReportBug.tsx +++ b/src/routes/get-started/ReportBug.tsx @@ -262,10 +262,21 @@ export default function ReportBugPage() { handleChange(event.name, event.value) } rows={ 6 } width="90%" error={ !!replicationError } /> - + Share your code with us in an isolated environment. + + React Stackblitz + Angular Stackblitz + + + } + requirement="optional" + error={ stackblitzError } + > handleChange(event.name, event.value) } width="90%" error={ !!stackblitzError } /> - + The design system team uses StackBlitz to create and share live code examples. It allows us to easily see your code in an environment that is unaffected by the rest of your project. Create a free account and share your work directly with the team. @@ -273,7 +284,7 @@ export default function ReportBugPage() { handleChange(event.name, event.value) } width="90%" error={ !!jamError } /> - + The design system team uses jam.dev to share and report bugs. This gives us a lot of the information we need to understand what's happening and how to fix it. Create a free account and record and share the issue. diff --git a/src/routes/get-started/Support.tsx b/src/routes/get-started/Support.tsx index f94c782b7..8ec064ba5 100644 --- a/src/routes/get-started/Support.tsx +++ b/src/routes/get-started/Support.tsx @@ -72,33 +72,33 @@ export default function SupportPage() {

    Design system team

    - Product Owner + Product Owner

    Mark Elamatha | mark.elamatha@gov.ab.ca

    - Scrum master and DevOps + Scrum master and DevOps

    Dustin Nielsen | dustin.nielsen@gov.ab.ca

    - Digital architect and Lead developer + Digital architect and Lead developer

    Chris Olsen | chris.olsen@gov.ab.ca

    - Developers + Developers

    Vanessa Tran | vanessa.m.tran@gov.ab.ca
    Syed Zeeshan | syed.zeeshan@gov.ab.ca

    - QA Automation Developer + QA Automation Developer

    Ken Li | ken.li@gov.ab.ca

    - Service designer + Service designer

    Ali Nicholls Asikinack | ali.nicholls-asikinack@gov.ab.ca

    - UX designers + UX designers

    Thomas Jeffery | thomas.jeffery@gov.ab.ca
    diff --git a/src/routes/get-started/designers/UxDesigner.tsx b/src/routes/get-started/designers/UxDesigner.tsx index 6a0ac91d9..40171d507 100644 --- a/src/routes/get-started/designers/UxDesigner.tsx +++ b/src/routes/get-started/designers/UxDesigner.tsx @@ -1,5 +1,6 @@ import { Link } from "react-router-dom"; import { ComponentContent } from "@components/component-content/ComponentContent"; +import { GoabCallout } from "@abgov/react-components"; export default function UxDesignerPage() { return ( @@ -35,7 +36,7 @@ export default function UxDesignerPage() { The design system tokens, styles, components and page templates are all available to pull into your file in Figma.

    - When selecting components, prioritize those with a “goa-” prefix in the name such as “goa-input.” These components are available in both design and development, which means that developers can avoid unnecessary custom development. + When selecting components, prioritize those with a “goa-” prefix in the name such as “goa-input.” These components are available in both design and development, which means that developers can avoid unnecessary custom development.

    A structural template that supports consistency across applications by defining visual grids, spacing, and sections. - +

    Basic page layout

    diff --git a/src/routes/patterns/PatternsLayout.tsx b/src/routes/patterns/PatternsLayout.tsx index 8b80b3303..dd518341d 100644 --- a/src/routes/patterns/PatternsLayout.tsx +++ b/src/routes/patterns/PatternsLayout.tsx @@ -19,7 +19,7 @@ export default function PatternsLayout() { All - Simple form + Public form Pages Basic page layout diff --git a/src/routes/patterns/PatternsOverview.tsx b/src/routes/patterns/PatternsOverview.tsx index 3fddddd76..d55083960 100644 --- a/src/routes/patterns/PatternsOverview.tsx +++ b/src/routes/patterns/PatternsOverview.tsx @@ -13,7 +13,7 @@ export default function PatternsOverviewPage() {

    Patterns

    • - Public form + Public form
    diff --git a/src/routes/patterns/SimpleFormPage.tsx b/src/routes/patterns/PublicFormPage.tsx similarity index 99% rename from src/routes/patterns/SimpleFormPage.tsx rename to src/routes/patterns/PublicFormPage.tsx index e26040698..29c49ef3b 100644 --- a/src/routes/patterns/SimpleFormPage.tsx +++ b/src/routes/patterns/PublicFormPage.tsx @@ -3,7 +3,7 @@ import { GoabDetails } from "@abgov/react-components"; import css from "./patterns.module.css"; import { Link } from "react-router-dom"; -export default function SimpleFormPage() { +export default function PublicFormPage() { return (

    Public form

    @@ -11,7 +11,7 @@ export default function SimpleFormPage() { Design forms that help Albertan citizens understand the task, focus on the question and its answer, and complete the form. - +

    Primary users: citizens, public, external
    diff --git a/src/routes/patterns/QuestionPage.tsx b/src/routes/patterns/QuestionPage.tsx index c7df960b6..7c51ce64f 100644 --- a/src/routes/patterns/QuestionPage.tsx +++ b/src/routes/patterns/QuestionPage.tsx @@ -17,7 +17,7 @@ export default function QuestionPage() { - + diff --git a/src/routes/patterns/ResultPage.tsx b/src/routes/patterns/ResultPage.tsx index c65e9efd9..3daa3261c 100644 --- a/src/routes/patterns/ResultPage.tsx +++ b/src/routes/patterns/ResultPage.tsx @@ -16,7 +16,7 @@ export default function ResultPage() { - + diff --git a/src/routes/patterns/ReviewPage.tsx b/src/routes/patterns/ReviewPage.tsx index 70c6fe7a0..f21d2e6c5 100644 --- a/src/routes/patterns/ReviewPage.tsx +++ b/src/routes/patterns/ReviewPage.tsx @@ -14,7 +14,7 @@ export default function ReviewPage() { - + diff --git a/src/routes/patterns/StartPage.tsx b/src/routes/patterns/StartPage.tsx index e98c46fad..537c9a0d8 100644 --- a/src/routes/patterns/StartPage.tsx +++ b/src/routes/patterns/StartPage.tsx @@ -17,7 +17,7 @@ export default function StartPage() { - + diff --git a/src/routes/patterns/TaskListPage.tsx b/src/routes/patterns/TaskListPage.tsx index e9363fb15..e6a0b9d79 100644 --- a/src/routes/patterns/TaskListPage.tsx +++ b/src/routes/patterns/TaskListPage.tsx @@ -19,7 +19,7 @@ export default function TaskListPage() { - + diff --git a/src/routes/root.css b/src/routes/root.css index 69e910012..ab621d846 100644 --- a/src/routes/root.css +++ b/src/routes/root.css @@ -15,7 +15,7 @@ } .content { - max-width: 1360px; + max-width: 1440px; width: 100%; margin: 0 auto; display: flex; @@ -26,7 +26,8 @@ Side Menu ==================*/ .side-menu { - min-width: 192px; + min-width: 180px; + width: 180px; border-right: 1px solid var(--goa-color-greyscale-200); padding-bottom: var(--goa-space-l); } @@ -103,7 +104,7 @@ a.back:hover::before { } code { - font: var(--goa-typography-number-m); + font: var(--goa-typography-number-s); } /*Tablet*/ diff --git a/src/routes/root.tsx b/src/routes/root.tsx index 7dbd1914b..d7d0ff142 100644 --- a/src/routes/root.tsx +++ b/src/routes/root.tsx @@ -60,9 +60,9 @@ export default function Root() { Get started Patterns Components - Styles + Tokens Content - Support + Get support

    @@ -73,12 +73,12 @@ export default function Root() {
    - + Get started Patterns Components - Styles + Tokens Content @@ -86,9 +86,9 @@ export default function Root() { #design-system-support - Contribute to the design system - Give feedback - Release notes + Contribute to the design system + Roadmap + Release notes
    diff --git a/src/utils/index.ts b/src/utils/index.ts index 60b00509a..afb57330a 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -5,3 +5,106 @@ export function toKebabCase(str: string) { .replace(/([a-z])([A-Z])/g, '$1-$2') // Convert camelCase to kebab-case .toLowerCase(); // Convert to lowercase } + +export const toSentenceCase = (str: string) => { + return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase(); +}; + +export async function fetchAllIssueCounts(cards: { name: string }[]): Promise> { + const token = + import.meta.env.VITE_GITHUB_TOKEN || + import.meta.env.VITE_GITHUB_TOKEN_ALTERNATE; + + if (!token) { + console.error("GitHub token not provided"); + return {}; + } + + const queryFields = cards.map((card) => { + const alias = card.name.replace(/\s+/g, "").toLowerCase(); + const label = card.name.charAt(0).toUpperCase() + card.name.slice(1).toLowerCase(); + const labelQuery = label.includes(" ") ? `\\"${label}\\"` : label; + return `${alias}: search(query: "is:issue is:open repo:GovAlta/ui-components label:${labelQuery}", type: ISSUE, first: 1) { issueCount }`; + }).join("\n"); + + const graphQLQuery = ` + query { + ${queryFields} + } + `; + + try { + const response = await fetch("https://api.github.com/graphql", { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${token}` + }, + body: JSON.stringify({ query: graphQLQuery }) + }); + + const result = await response.json(); + if (result.errors) { + console.error("GraphQL errors:", result.errors); + return {}; + } + + const issueCounts: Record = {}; + cards.forEach((card) => { + const alias = card.name.replace(/\s+/g, "").toLowerCase(); + issueCounts[card.name] = + result.data[alias] && result.data[alias].issueCount + ? result.data[alias].issueCount + : 0; + }); + + return issueCounts; + } catch (error) { + console.error("Error fetching issue counts:", error); + return {}; + } +} + +export async function fetchIssueCount(label: string): Promise { + const token = + import.meta.env.VITE_GITHUB_TOKEN || + import.meta.env.VITE_GITHUB_TOKEN_ALTERNATE; + + if (!token) { + console.error("GitHub token not provided"); + return null; + } + + const labelQuery = label.includes(" ") ? `\\"${label}\\"` : label; + + const query = ` + query { + issues: search(query: "is:issue is:open repo:GovAlta/ui-components label:${labelQuery}", type: ISSUE, first: 1) { + issueCount + } + } + `; + + try { + const response = await fetch("https://api.github.com/graphql", { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${token}` + }, + body: JSON.stringify({ query }) + }); + + const result = await response.json(); + + if (result.errors) { + console.error("GraphQL errors:", result.errors); + return null; + } + + return result.data.issues.issueCount; + } catch (error) { + console.error("Error fetching issue count:", error); + return null; + } +} \ No newline at end of file diff --git a/src/versioned-router.tsx b/src/versioned-router.tsx index c61f79b24..007d5c6cd 100644 --- a/src/versioned-router.tsx +++ b/src/versioned-router.tsx @@ -52,9 +52,11 @@ import TaskListPage from "@routes/patterns/TaskListPage.tsx"; import QuestionPage from "@routes/patterns/QuestionPage.tsx"; import ReviewPage from "@routes/patterns/ReviewPage.tsx"; import ResultPage from "@routes/patterns/ResultPage.tsx"; -import SimpleFormPage from "@routes/patterns/SimpleFormPage.tsx"; +import PublicFormPage from "@routes/patterns/PublicFormPage.tsx"; import FilterChipPage from "@routes/components/FilterChip.tsx"; import TextPage from "@routes/components/Text.tsx"; +import { DrawerPage } from "@routes/components/Drawer.tsx"; +import LinkPage from "@routes/components/Link.tsx"; const ComponentRoute: React.FC<{ versionedPaths: Record; @@ -95,6 +97,7 @@ export const ComponentsRouter = () => { "date-picker": , "details": , "divider": , + "drawer": , "dropdown": , "file-uploader": , "filter-chip": , @@ -124,6 +127,7 @@ export const ComponentsRouter = () => { "text-field": , "header": , "footer": , + "link": , }; return ( @@ -155,7 +159,7 @@ export const PatternsRouter = () => { } errorElement={}> {/* Non-versioned paths components */} } /> - } /> + } /> } /> {/* Versioned paths components */} } />