diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 312475501649..b91a6df9e58f 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -27,6 +27,7 @@ Provide an overview of your changes here. Include screenshots, photos or links i - [ ] Create or update documentation on the website - [ ] Create or update stories for Storybook - [ ] Create or update stories for Playroom snippets +- [ ] Verify hard-coded anchor links and hashes are correct. Check existing links when renaming pages and headings. **Creating new component** diff --git a/RELEASE.md b/RELEASE.md index 59db89b3c0dc..c44a0e5754b1 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -14,13 +14,13 @@ These instructions capture the internal process for making a release of the `@ag - The new package release version from above. - A link to the Version Packages PR. -5. Add a minor docs changeset of ‘docs: AgDS Beta v[version-number] release notes.‘ +5. Add a minor docs changeset of ‘docs: AgDS v[version-number] release notes.‘ 6. Commit these changes with a description ‘Release notes v[version number]’, create a PR and merge it to `develop` when approved. ## npm release 1. Merge the PR named `Version Packages` into `develop` on GitHub once all checks pass. -2. Create and merge a new PR titled ‘Release v[version-number]’ which merges `develop` into `main`. +2. Create and merge a new PR titled ‘Release v[version-number]’ which merges (not squashes) `develop` into `main`. 3. After `main` finishes building and deploying, on the command line, run `git pull` on the `main` branch to fetch latest changes. 4. Run `yarn fresh` to rebuild `node_modules` and clean-up any build output. 5. Run `yarn publish-changed` to find packages where the version listed in the `package.json` is ahead of the version published on npm, and publish just those packages. @@ -32,6 +32,6 @@ These instructions capture the internal process for making a release of the `@ag ## Github release notes 1. Create a draft release on GitHub based on the new release’s tag (such as @ag.ds-next/react@[version-number]). -2. Title the release with the version number, for example, ‘AgDS Beta v[version-number] release’. +2. Title the release with the version number, for example, ‘AgDS v[version-number] release’. 3. Copy the latest docs release notes content without the frontmatter and with all urls updated to include the protocol and domain. 4. Publish the release. diff --git a/docs/.env.production b/docs/.env.production index 675d275ee7c6..0af836d68bc5 100644 --- a/docs/.env.production +++ b/docs/.env.production @@ -1,4 +1,4 @@ -SITE_URL=https://design-system.agriculture.gov.au +NEXT_PUBLIC_SITE_URL=https://design-system.agriculture.gov.au NEXT_PUBLIC_FIGMA_URL=https://www.figma.com/file/MivOblJvHi3nJ4bQMDfnf3/AgDS---gallery NEXT_PUBLIC_GA_MEASUREMENT_ID=G-FNXK77EZLQ NEXT_PUBLIC_PLAYROOM_URL=/playroom diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index fff7ad42542f..65055ba19375 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -1,5 +1,187 @@ # @ag.ds-next/docs +## 0.18.0 + +### Minor Changes + +- 2590b8778ff: patterns: Add ‘Selecting an option from a list’. +- 5e2b99dd4c0: docs: Remove beta badge & update description to reflect AgDS’s use-case. +- b5635a629fd: conditional-field-container: Update style to use a left border instead of a horizontal divider. + + field: ``: Update style to support invalid fields within ``. + + grouped-fields: Update style to support invalid fields within ``. + +- dd3c366a455: checkbox: Add `onFocus` as valid TypeScript prop. + + docs: Add 'Responsive preview' page designed to view components and examples with selectable document widths. Add responsive preview link to documentation code snippets. Modify code snippet components with padding option. + + header: Remove padding from preview examples. + + radio: Add `onFocus` as valid TypeScript prop. + +- 45d54c8f8e3: docs: Update Getting Started guide to support Next.js. +- a9c3de75979: docs: AgDS v1.30.0 release notes. +- de35d8cb112: foundations/accessibility: Add new Foundations > Accessibility content pages. +- 5e2b99dd4c0: docs: Change all ‘Confirm and submit’ references to ‘Review and submit’. + + docs: Remove all contact CTAs. + + example-site: Change all ‘Confirm and submit’ references to ‘Review and submit’. + + yourgov: Change all ‘Confirm and submit’ references to ‘Review and submit’. + +### Patch Changes + +- f0142309238: docs: Add pattern documentation page 'Warn before leaving'. +- 8e0945eda9b: docs: Update copyright to year 2025 in code snippets. + + footer: Update copyright to year 2025 in docs. + +- 0055b768eee: docs: Add ’Ask users for dates and times’ pattern page. +- 7cb232595ca: summary-list: Update docs with `VisuallyHidden` link and import reference. + + table: Fix and lint code sample examples. Update docs with `VisuallyHidden` link and import reference. + +- 9daa408bce2: docs: Add 'Review and submit' pattern page. +- bbb49667d74: accordion: Update colour section in docs. + + ag-branding: Add colour section in docs. + + autocomplete: Add colour section in docs. + + avatar: Add colour section in docs. + + breadcrumbs: Add colour section in docs. + + button: Update colour section in docs. + + call-to-action: Add colour section in docs. + + callout: Update colour section in docs. + + card: Update colour section in current and legacy docs. + + checkbox: Add colour section in docs. + + combobox: Add colour section in docs. + + date-picker-next: Add colour section in docs. + + date-picker: Add colour section in docs. + + date-range-picker-next: Add colour section in docs. + + date-range-picker: Add colour section in docs. + + details: Add colour section in docs. + + direction-link: Add colour section in docs. + + divider-with-text: Update colour section in docs. + + divider: Add colour section in docs. + + docs: Add support for colour component support for code snippets in documentation. + + dropdown-menu: Add colour section in docs. + + feature-link-list: Add colour section in docs. + + file-input: Add colour section in docs. + + file-upload: Add colour section in docs. + + filter-sidebar: Add colour section in docs. + + footer: Add colour section in docs. + + global-alert: Add colour section in docs. + + grouped-fields: Add colour section in docs. + + header: Add colour section in docs. + + hero-banner: Add colour section in docs. + + icon: Add colour section in docs. + + indicator-dot: Add colour section in docs. + + inpage-nav: Add colour section in docs. + + link-list: Add colour section in docs. + + main-nav: Add colour section in docs. + + notification-badge: Add colour section in docs. + + page-alert: Add colour section in docs. + + pagination: Add colour section in docs. + + password-input: Add colour section in docs. + + progress-indicator: Add colour section in docs. + + radio: Add colour section in docs. + + search-box: Update colour section in docs. + + search-input: Add colour section in docs. + + section-alert: Update colour section in docs. + + select: Add colour section in docs. + + side-nav: Add colour section in docs. + + skeleton: Add colour section in docs. + + status-badge: Add colour section in docs. + + sub-nav: Add colour section in docs. + + summary-list: Add colour section in docs. + + switch: Add colour section in docs. + + table: Add colour section in docs. + + tabs: Add colour section in docs. + + tags: Add colour section in docs. + + task-list: Add colour section in docs. + + text-input: Add colour section in docs. + + text-link: Add colour section in docs. + + textarea: Add colour section in docs. + + time-input: Add colour section in docs. + + time-picker: Add colour section in docs. + + toggle-button: Add colour section in docs. + +- 3daabdcd795: docs: Add CRUD tables documentation in patterns. +- d4fd9d01741: docs: Add ’Helpful form content’ pattern page. +- 5516760620d: docs: Add 'yourGov example application' pattern page. +- Updated dependencies [a898848e32e] +- Updated dependencies [5cd422a9fe2] +- Updated dependencies [b5635a629fd] +- Updated dependencies [dd3c366a455] +- Updated dependencies [5e2b99dd4c0] +- Updated dependencies [2a6cb20bca0] +- Updated dependencies [736ad093ff5] +- Updated dependencies [687d9dba075] +- Updated dependencies [5cbdd2f97c1] +- Updated dependencies [d297f81fe25] +- Updated dependencies [48661f6da5b] + - @ag.ds-next/react@1.30.0 + ## 0.17.0 ### Minor Changes diff --git a/docs/components/AccessibilityLayout.tsx b/docs/components/AccessibilityLayout.tsx new file mode 100644 index 000000000000..5afeb9ca7fe8 --- /dev/null +++ b/docs/components/AccessibilityLayout.tsx @@ -0,0 +1,140 @@ +import { PropsWithChildren } from 'react'; +import { SiteLayout } from './SiteLayout'; +import { PageLayout } from './PageLayout'; +import { PageTitle } from './PageTitle'; + +export const ACCESSIBILITY_PAGES = { + 'accessibility-overview': { + label: 'Accessibility overview', + pageTitle: 'Accessibility overview', + description: + 'Our approach to building components that provide a great user experience for all users.', + }, + 'aria-and-semantic-code': { + label: 'ARIA and semantic code', + pageTitle: 'ARIA and semantic code', + description: + 'The Accessible Rich Internet Application (ARIA) specification helps you to create accessible digital products and services.', + }, + 'clear-communication': { + label: 'Clear communication', + pageTitle: 'Clear communication', + description: + 'Write in plain language and organise content logically. This makes reading easier for everyone, but it’s essential for anyone with English as a second language.', + }, + 'colour-and-contrast': { + label: 'Colour and contrast', + pageTitle: 'Colour and contrast', + description: + 'Provide good contrast and avoid using colour alone to convey meaning. This enhances readability for everyone, especially those with colour vision differences.', + }, + 'consistent-design': { + label: 'Consistent design', + pageTitle: 'Consistent design', + description: + 'Use the design system components predictably. It reduces cognitive load for everyone, but it’s essential for users with learning disabilities.', + }, + 'error-management': { + label: 'Error management', + pageTitle: 'Error management', + description: + 'Provide clear, specific error messages. It helps all users recover from errors, but it’s essential for those with cognitive disabilities.', + }, + 'focus-management': { + label: 'Focus management', + pageTitle: 'Focus management', + description: + 'The focus indicator shows which element has focus. Good focus management helps all users track their position, but it’s essential for people with low vision.', + }, + 'keyboard-accessibility': { + label: 'Keyboard accessibility', + pageTitle: 'Keyboard accessibility', + description: + 'Make all functionality available via keyboard. It’s helpful for power users, but essential for people who use assistive technology.', + }, + 'motion-and-animation': { + label: 'Motion and animation', + pageTitle: 'Motion and animation', + description: + 'Include controls to reduce or disable motion and avoid flashing content. This provides a comfortable experience, especially for those with vestibular or seizure conditions.', + }, + 'states-and-status': { + label: 'States and status', + pageTitle: 'States and status', + description: + 'Clearly communicate change in state or status to all users. This keeps users informed about what’s happening, but it’s essential for people using assistive technologies.', + }, + 'text-sizing-and-spacing': { + label: 'Text sizing and spacing', + pageTitle: 'Text sizing and spacing', + description: + 'Ensure adequate text size and spacing. This improves readability and reduces eye strain for everyone, but it’s essential for people with low vision or dyslexia.', + }, + timing: { + label: 'Timing', + pageTitle: 'Timing', + description: + 'Allow users to extend, pause or turn off time limits. This reduces pressure for everyone, but it’s essential for people who need more time to interact.', + }, +} as const; + +export const ACCESSIBILITY_NAV_LINKS = Object.entries(ACCESSIBILITY_PAGES).map( + ([key, item]) => ({ + href: `/foundations/accessibility/${key}`, + ...item, + }) +); + +const getBreadcrumbs = (currentPageTitle: string) => { + return [ + { + label: 'Home', + href: '/', + }, + { + label: 'Foundations', + href: '/foundations', + }, + { + label: 'Accessibility', + href: '/foundations/accessibility', + }, + { + label: currentPageTitle, + }, + ]; +}; + +type AccessibilityLayoutProps = PropsWithChildren<{ + description: string; + editPath: string; + title: string; +}>; + +export const AccessibilityLayout = ({ + children, + description, + editPath, + title, +}: AccessibilityLayoutProps) => { + return ( + + ({ + href, + label, + })), + }} + > + + {children} + + + ); +}; diff --git a/docs/components/Code.tsx b/docs/components/Code.tsx index 1acab14e7d48..8524a8d6ca6d 100644 --- a/docs/components/Code.tsx +++ b/docs/components/Code.tsx @@ -1,51 +1,14 @@ -import React, { - ReactNode, - useState, - useCallback, - Fragment, - useRef, - KeyboardEvent, - useContext, - useEffect, -} from 'react'; -import { LiveProvider, LiveEditor, LivePreview, LiveContext } from 'react-live'; -import { createUrl } from 'playroom/utils'; -import { Highlight, Prism } from 'prism-react-renderer'; -import copy from 'clipboard-copy'; -import { ExternalLinkCallout } from '@ag.ds-next/react/a11y'; -import { CardHeader } from '@ag.ds-next/react/card'; -import { - globalPalette, - mapSpacing, - packs, - tokens, - useId, - useToggleState, -} from '@ag.ds-next/react/core'; -import { Box } from '@ag.ds-next/react/box'; -import { Flex } from '@ag.ds-next/react/flex'; -import { Heading } from '@ag.ds-next/react/heading'; -import { - unsetProseStylesClassname, - proseBlockClassname, -} from '@ag.ds-next/react/prose'; -import { Button, ButtonLink } from '@ag.ds-next/react/button'; -import { - CopyIcon, - ChevronDownIcon, - ChevronUpIcon, -} from '@ag.ds-next/react/icon'; +import { usePathname } from 'next/navigation'; +import React, { Fragment, type ReactNode, useState } from 'react'; +import { LiveProvider } from 'react-live'; import { withBasePath } from '../lib/img'; +import { Render } from '../playroom/components'; +import { createTitleFromPathname } from './code/utils'; +import { ResponsivePreviewLink } from './code/ResponsivePreviewLink'; +import { StaticCode } from './code/StaticCode'; +import { LiveCode } from './code/LiveCode'; import * as designSystemComponents from './designSystemComponents'; -import { prismTheme } from './prism-theme'; - -// Find multi-line comments at start `/** ... */` -const multiLineCommentRegex = /^\/\*[\s\S]*?\*\//gi; - -// Add support for diff language support -// https://github.com/FormidableLabs/prism-react-renderer#custom-language-support -(typeof global !== 'undefined' ? global : window).Prism = Prism; -require('prismjs/components/prism-diff'); +import { type MDXComponentsPageData } from './mdxComponents'; export const PlaceholderImage = () => ( ( ); -function LiveCode({ - showCode = false, - enableProse = false, - exampleContentHeading, - exampleContentHeadingType, -}: { - showCode?: boolean; - enableProse?: boolean; - exampleContentHeading?: string; - exampleContentHeadingType?: 'h2' | 'h3' | 'h4'; -}) { - const liveEditorRef = useRef(null); - const liveCodeToggleButton = useRef(null); - const live = useContext(LiveContext); - - const liveOnChange = live.onChange; - const [localCopy, setLocalCopy] = useState(live.code); - const [isCodeVisible, toggleIsCodeVisible] = useToggleState( - showCode, - !showCode - ); - - const copyLiveCode = useCallback(() => { - copy(localCopy); - }, [localCopy]); - - const handleChange = useCallback( - (code: string) => { - liveOnChange(code); - setLocalCopy(code); - }, - [liveOnChange] - ); - - const codeUrl = useCallback(() => { - // Wrap `/* ... */` comments with brackets `{ .. }` - let code = live.code.replaceAll(multiLineCommentRegex, '{$&}'); - // No formatting required for JSX only - if (code.startsWith('<') || code.endsWith('>')) return code; - - // Remove `;` from the end of `() => {};` - if (code.endsWith(';')) { - code = code.slice(0, -1); - } - - const formattedCode = code.split('\n').join('\n '); - return `\n {${formattedCode}}\n`; - }, [live.code]); - - const playroomUrl = createUrl({ - baseUrl: process.env.NEXT_PUBLIC_PLAYROOM_URL, - code: codeUrl(), - }); - - const id = useId(); - const codeId = `live-code-${id}`; - - const onLiveEditorContainerKeyDown = useCallback( - (event: KeyboardEvent) => { - if (event.code === 'Escape') { - toggleIsCodeVisible(); - liveCodeToggleButton.current?.focus(); - } - }, - [toggleIsCodeVisible] - ); - - // LiveEditor doesn't support aria-label, so we have to do it the DOM way - useEffect(() => { - const pre = liveEditorRef.current?.querySelector('pre'); - if (pre) { - pre.ariaLabel = `Live code editor ${id}`; - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore Property 'role' does not exist on type 'HTMLPreElement' - pre.role = 'region'; - } - }, [id]); - - // Using `Box` here instead of Code snippets with popovers (date picker, combobox, dropdown menu etc) need overflow - return ( - - {exampleContentHeadingType && ( - - - {exampleContentHeading} - - - )} - - - - - - Open in Playroom - - - - - - - {live.error ? ( - - {live.error} - - ) : null} - - ); -} - -const StaticCode = ({ - code, - language = '', // By default render as plain text (ie. no language) -}: { - code: string; - language?: string; -}) => { - return ( - - - - {({ className, style, tokens, getLineProps, getTokenProps }) => ( -
-							
-								{tokens.map((line, lineKey) => (
-									
- {line.map((token, tokenKey) => ( - - ))} -
- ))} -
-
- )} -
-
- - - -
- ); -}; - const LIVE_SCOPE = { ...designSystemComponents, + Fragment, PlaceholderImage, PlaceholderPictogram, - useState, - Fragment, React, + Render, + useState, }; type CodeProps = { children?: ReactNode; className?: string; - live?: boolean; - showCode?: boolean; enableProse?: boolean; exampleContentHeading?: string; exampleContentHeadingType?: 'h2' | 'h3' | 'h4'; + live?: boolean; + padding?: boolean; + pageData?: MDXComponentsPageData; + previewHeading?: string; + referrerLabel?: string; + showCode?: boolean; }; export function Code({ children, - live, - showCode, - enableProse, className, + enableProse, exampleContentHeading = 'Example', exampleContentHeadingType, + live, + padding, + pageData, + previewHeading, + referrerLabel, + showCode, }: CodeProps) { const childrenAsString = children?.toString().trim(); const language = className?.replace(/language-/, ''); + const pathname = usePathname(); if (!childrenAsString) return null; + const defaultTitle = pageData?.title || createTitleFromPathname(pathname); + + const responsivePreviewTitle = previewHeading || `${defaultTitle} preview`; + const responsiveReferrerLabel = referrerLabel || defaultTitle; + + // Standalone link, inline docs + if (previewHeading && !live) { + return ( + + {previewHeading} + + ); + } + + // Live preview code with CTAs if (live) { return ( diff --git a/docs/components/ColorComponentSection.tsx b/docs/components/ColorComponentSection.tsx new file mode 100644 index 000000000000..de43f9867870 --- /dev/null +++ b/docs/components/ColorComponentSection.tsx @@ -0,0 +1,89 @@ +import { Code } from './Code'; +import { checkAndModifyCode } from './code/utils'; + +/** + * We will be wrapping the code in a Box with the palette + * Add one tab to each line for Playroom and code preview + */ +const formatCodeString = (originalCode: string) => { + const code = checkAndModifyCode(originalCode.trim()); + return code + .split('\n') + .map((line) => '\t' + line) // Add indent to each line + .join('\n'); +}; + +/** + * Code examples require `light` and `dark` text + * The below string will be replaced by the appropriate palette label in their respective code blocks + */ +const paletteReplacedString = '[%_PALETTE_%]'; + +export const ColorComponentSection = ({ + children: ReactChildren, + enableProse, + exampleContentHeading, + exampleContentHeadingType, + live, + shadeAlt = false, + showCode, +}: { + children: React.ReactNode; + enableProse?: boolean; + exampleContentHeading?: string; + exampleContentHeadingType?: 'h2' | 'h3' | 'h4'; + live?: boolean; + shadeAlt?: boolean; + showCode?: boolean; +}) => { + const children = formatCodeString(ReactChildren?.toString() || ''); + const lightCode = children.replaceAll(paletteReplacedString, 'light'); + const darkCode = children.replaceAll(paletteReplacedString, 'dark'); + + return ( + <> +

+ AgDS foreground components respond to the background colour of their + parent container. When placed on a dark palette background, the dark + palette variant of the component is displayed. When placed on a light + palette background the light palette variant is displayed. +

+

+ This logic ensures sufficient contrast between foreground and background + elements is maintained to meet WCAG 2.1 AA, 4:5:1 contrast ratio for + text (WCAG 1.4.3) and 3:1 for graphic elements that convey information + (WCAG 1.4.11). +

+ {shadeAlt && ( +

+ This component has a 'bodyAlt' variant. + Components with a 'bodyAlt' variant utilise + shadeAlt to highlight interface components and content + that sit on 'bodyAlt' background.{' '} + shadeAlt is also used to fill interactive components, + ensuring sufficient contrast is maintained for hover states. +

+ )} + +

Light palette

+ {`\n${lightCode}\n`} + +

Dark palette

+ {`\n${darkCode}\n`} + + ); +}; diff --git a/docs/components/SiteHeader.tsx b/docs/components/SiteHeader.tsx index 4e8ffd1b8166..5ff33e993e25 100644 --- a/docs/components/SiteHeader.tsx +++ b/docs/components/SiteHeader.tsx @@ -30,10 +30,9 @@ export const SiteHeader = () => {
} - subline="Design System for the Export Service" + subline="Design System for import and export services" /> (null); + const liveCodeToggleButton = useRef(null); + const live = useContext(LiveContext); + + const liveOnChange = live.onChange; + const [localCopy, setLocalCopy] = useState(live.code); + const [isCodeVisible, toggleIsCodeVisible] = useToggleState( + showCode, + !showCode + ); + + const handleChange = useCallback( + (code: string) => { + liveOnChange(code); + setLocalCopy(code); + }, + [liveOnChange] + ); + + const copyLiveCode = useCallback(() => { + copy(localCopy); + }, [localCopy]); + const codeUrl = useCallback(() => checkAndModifyCode(live.code), [live.code]); + + const playroomUrl = createUrl({ + baseUrl: process.env.NEXT_PUBLIC_PLAYROOM_URL, + code: codeUrl(), + }); + + const id = useId(); + const codeId = `live-code-${id}`; + + const onLiveEditorContainerKeyDown = useCallback( + (event: KeyboardEvent) => { + if (event.code === 'Escape') { + toggleIsCodeVisible(); + liveCodeToggleButton.current?.focus(); + } + }, + [toggleIsCodeVisible] + ); + + // LiveEditor doesn't support aria-label, so we have to do it the DOM way + useEffect(() => { + const pre = liveEditorRef.current?.querySelector('pre'); + if (pre) { + pre.ariaLabel = `Live code editor ${id}`; + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore Property 'role' does not exist on type 'HTMLPreElement' + pre.role = 'region'; + } + }, [id]); + + // Using `Box` here instead of Code snippets with popovers (date picker, combobox, dropdown menu etc) need overflow + return ( + + {exampleContentHeadingType && ( + + + {exampleContentHeading} + + + )} + + + + + + Open in Playroom + + + + Open responsive preview + + + + + + {live.error ? ( + + {live.error} + + ) : null} + + ); +} diff --git a/docs/components/code/ResponsivePreviewLink.tsx b/docs/components/code/ResponsivePreviewLink.tsx new file mode 100644 index 000000000000..dc5c5a2b2430 --- /dev/null +++ b/docs/components/code/ResponsivePreviewLink.tsx @@ -0,0 +1,89 @@ +import { usePathname } from 'next/navigation'; +import { createPreviewUrl } from 'playroom'; +import { Box } from '@ag.ds-next/react/box'; +import { ButtonLink } from '@ag.ds-next/react/button'; +import { mapSpacing } from '@ag.ds-next/react/core'; +import { ChevronRightIcon } from '@ag.ds-next/react/icon'; +import { Text } from '@ag.ds-next/react/text'; +import { TextLink } from '@ag.ds-next/react/text-link'; +import { checkAndModifyCode } from './utils'; + +// Query param keys consumed by the responsive preview page +export const responsivePreviewQueryKeys = { + /** iframe source used for the responsive preview. */ + frameSrc: 'src', + /** + * Option to enable/disable padding on top, left and right of the frame. + * Only use for full page or already padded previews, not component previews. + */ + padding: 'padding', + /** Label text for the back button. Back button is prefixed with `Back to {referrerLabel}`. */ + referrerLabel: 'referrer-label', + /** Back button link href. */ + referrerLink: 'referrer-link', + /** Sizes are set by the user on the responsive preview page. */ + previewSize: 'size', + /** H1 title on responsive preview page. */ + title: 'title', +}; + +export function ResponsivePreviewLink({ + children, + code, + referrerLabel, + src, + padding = true, + standalone = false, + title, +}: { + children: React.ReactNode; + code?: string; + padding?: boolean; + referrerLabel?: string; + src?: string; + standalone?: boolean; + title: string; +}) { + const urlParams = new URLSearchParams(); + + if (title) urlParams.append(responsivePreviewQueryKeys.title, title); + if (!padding) urlParams.append(responsivePreviewQueryKeys.padding, 'false'); + + if (src) urlParams.append(responsivePreviewQueryKeys.frameSrc, src); + else if (code) { + const playroomPreviewUrl = createPreviewUrl({ + baseUrl: process.env.NEXT_PUBLIC_PLAYROOM_URL, + code: checkAndModifyCode(code), + }); + urlParams.append(responsivePreviewQueryKeys.frameSrc, playroomPreviewUrl); + } + + const pathname = usePathname(); + urlParams.append(responsivePreviewQueryKeys.referrerLink, pathname); + if (referrerLabel) { + urlParams.append(responsivePreviewQueryKeys.referrerLabel, referrerLabel); + } + + const href = `/responsive-preview?${urlParams.toString()}`; + + // Link without live preview + if (standalone) { + return ( + + + + {children} + + + + + ); + } + + // Inline with code actions + return ( + + {children} + + ); +} diff --git a/docs/components/code/StaticCode.tsx b/docs/components/code/StaticCode.tsx new file mode 100644 index 000000000000..79f8053aad34 --- /dev/null +++ b/docs/components/code/StaticCode.tsx @@ -0,0 +1,79 @@ +import copy from 'clipboard-copy'; +import { Highlight, Prism } from 'prism-react-renderer'; +import { Box } from '@ag.ds-next/react/box'; +import { Button } from '@ag.ds-next/react/button'; +import { globalPalette, mapSpacing } from '@ag.ds-next/react/core'; +import { Flex } from '@ag.ds-next/react/flex'; +import { CopyIcon } from '@ag.ds-next/react/icon'; +import { unsetProseStylesClassname } from '@ag.ds-next/react/prose'; +import { prismTheme } from './prism-theme'; + +// Add support for diff language support +// https://github.com/FormidableLabs/prism-react-renderer#custom-language-support +(typeof global !== 'undefined' ? global : window).Prism = Prism; +require('prismjs/components/prism-diff'); + +export const StaticCode = ({ + code, + language = '', // By default render as plain text (ie. no language) +}: { + code: string; + language?: string; +}) => { + return ( + + + + {({ className, getLineProps, getTokenProps, style, tokens }) => ( +
+							
+								{tokens.map((line, lineKey) => (
+									
+ {line.map((token, tokenKey) => ( + + ))} +
+ ))} +
+
+ )} +
+
+ + + +
+ ); +}; diff --git a/docs/components/prism-theme.ts b/docs/components/code/prism-theme.ts similarity index 100% rename from docs/components/prism-theme.ts rename to docs/components/code/prism-theme.ts diff --git a/docs/components/code/utils.tsx b/docs/components/code/utils.tsx new file mode 100644 index 000000000000..20eb520e4e62 --- /dev/null +++ b/docs/components/code/utils.tsx @@ -0,0 +1,50 @@ +// Find multi-line comments at start `/** ... */` +const multiLineCommentRegex = /^\/\*[\s\S]*?\*\//gi; + +// Checks if the code requires React support or is JSX +// Returned value is for playroom +export function checkAndModifyCode(liveCode: string) { + // Wrap `/* ... */` comments with brackets `{ .. }` + let code = liveCode.replaceAll(multiLineCommentRegex, '{$&}'); + // No formatting required for JSX only + if (code.startsWith('<') || code.endsWith('>')) return code; + + // Remove `;` from the end of `() => {};` + if (code.endsWith(';')) { + code = code.slice(0, -1); + } + + const formattedCode = code.split('\n').join('\n '); + return `\n {${formattedCode}}\n`; +} + +const removeParentPaths = [ + '/components/', + '/content/', + '/foundations/', + '/guides/', + '/patterns/', + '/templates/', +]; + +// Create a display heading based on the pathname +export function createTitleFromPathname(pathname: string) { + // Remove known path prefixing + const title = removeParentPaths.reduce((acc, str) => { + if (acc.startsWith(str)) { + return acc.replace(str, ''); + } + return acc; + }, pathname); + + return title + .split('/') // Nested paths + .map((str) => { + // Remove `-` from file names + const newStr = str.replaceAll('-', ' '); + + // Capitalise first letter + return newStr.charAt(0).toUpperCase() + newStr.slice(1); + }) + .join(': '); +} diff --git a/docs/components/mdxComponents.tsx b/docs/components/mdxComponents.tsx index 193fdf4e68ed..79ef55e3c29a 100644 --- a/docs/components/mdxComponents.tsx +++ b/docs/components/mdxComponents.tsx @@ -1,23 +1,29 @@ import { - Fragment, Children, + Fragment, isValidElement, - HTMLAttributes, - AnchorHTMLAttributes, - ImgHTMLAttributes, - ReactNode, - PropsWithChildren, + type AnchorHTMLAttributes, + type HTMLAttributes, + type ImgHTMLAttributes, + type PropsWithChildren, + type ReactNode, } from 'react'; -import type { MDXRemoteProps } from 'next-mdx-remote'; +import { type MDXRemoteProps } from 'next-mdx-remote'; import Link from 'next/link'; import { Box } from '@ag.ds-next/react/box'; -import { proseBlockClassname } from '@ag.ds-next/react/prose'; +import { ButtonLink } from '@ag.ds-next/react/button'; +import { Callout } from '@ag.ds-next/react/callout'; +import { Card, CardHeader, CardInner } from '@ag.ds-next/react/card'; +import { boxPalette, fontGrid, mapSpacing } from '@ag.ds-next/react/core'; +import { DirectionLink } from '@ag.ds-next/react/direction-link'; +import { H3, H4 } from '@ag.ds-next/react/heading'; import { PageAlert, - PageAlertProps, PageAlertTitle, + type PageAlertProps, } from '@ag.ds-next/react/page-alert'; -import { ButtonLink } from '@ag.ds-next/react/button'; +import { proseBlockClassname } from '@ag.ds-next/react/prose'; +import { Stack } from '@ag.ds-next/react/stack'; import { SummaryList, SummaryListItem, @@ -25,38 +31,40 @@ import { SummaryListItemTerm, } from '@ag.ds-next/react/summary-list'; import { - Table as TableComponent, TableBody, TableCaption, - TableRow, TableCell, + Table as TableComponent, TableHead, TableHeader, + TableRow, TableWrapper, } from '@ag.ds-next/react/table'; -import { boxPalette, fontGrid, mapSpacing } from '@ag.ds-next/react/core'; -import { Callout } from '@ag.ds-next/react/callout'; -import { Stack } from '@ag.ds-next/react/stack'; import { Text } from '@ag.ds-next/react/text'; import { TextLink } from '@ag.ds-next/react/text-link'; -import { DirectionLink } from '@ag.ds-next/react/direction-link'; -import { Card, CardHeader, CardInner } from '@ag.ds-next/react/card'; -import { H3, H4 } from '@ag.ds-next/react/heading'; -import { slugify } from '../lib/slugify'; -import { withBasePath } from '../lib/img'; import generatedComponentPropsData from '../__generated__/componentProps.json'; +import { withBasePath } from '../lib/img'; +import { slugify } from '../lib/slugify'; +import { AllIconsPlayground } from './AllIconsPlayground'; import { Code } from './Code'; +import { ResponsivePreviewLink } from './code/ResponsivePreviewLink'; +import { ColorComponentSection } from './ColorComponentSection'; import { ComponentPropsTable } from './ComponentPropsTable'; import { DoHeading, DontHeading } from './DoDontHeading'; -import { AllIconsPlayground } from './AllIconsPlayground'; import { BreakpointsTokenChart, + ShadowTokenChart, SpacingTokenChart, ZIndexTokenChart, - ShadowTokenChart, } from './TokenCharts'; -export const mdxComponents: MDXRemoteProps['components'] = { +export interface MDXComponentsPageData { + title?: string; +} + +export const mdxComponents = ( + pageData: MDXComponentsPageData +): MDXRemoteProps['components'] => ({ Fragment, blockquote: ({ children }) => (
@@ -65,18 +73,56 @@ export const mdxComponents: MDXRemoteProps['components'] = { ), pre: ({ children, - live, - showCode, + colorSection, enableProse, exampleContentHeading = 'Example', exampleContentHeadingType, + live, + padding, + previewHeading, + referrerLabel, + shadeAlt, + showCode, }: HTMLAttributes & { - live?: boolean; - showCode?: boolean; + /** + * If `true`, returns colour documentation and two code blocks wrapped in a light and dark palette + * Used on the component documentation page + * */ + colorSection?: boolean; enableProse?: boolean; exampleContentHeading?: string; exampleContentHeadingType?: 'h2' | 'h3' | 'h4'; + live?: boolean; + padding?: boolean; + /** Heading for responsive preview page and standalone link label */ + previewHeading?: string; + /** Back link label for responsive preview page */ + referrerLabel?: string; + /** If `true` and `colorSelection={true}`, returns an additional colour documentation paragraph */ + shadeAlt?: boolean; + showCode?: boolean; }) => { + if (colorSection) { + return ( + + {Children.map(children, (element) => { + if (!isValidElement(element)) return null; + return ( + + ); + })} + + ); + } return ( {Children.map(children, (element) => { @@ -88,6 +134,10 @@ export const mdxComponents: MDXRemoteProps['components'] = { exampleContentHeadingType={exampleContentHeadingType} key={element.key} live={live} + padding={padding} + pageData={pageData} + previewHeading={previewHeading} + referrerLabel={referrerLabel} showCode={showCode} {...element.props} /> @@ -162,6 +212,13 @@ export const mdxComponents: MDXRemoteProps['components'] = { ); }, + h4: ({ children }: HTMLAttributes) => { + return ( +

+ {children} +

+ ); + }, H3, H4, PageAlert: (props: PageAlertProps) => ( @@ -261,4 +318,27 @@ export const mdxComponents: MDXRemoteProps['components'] = {
), -}; + ResponsivePreview: ({ + href, + label, + padding = false, + referrerLabel, + title, + }: { + href: string; + label: string; + padding?: boolean; + title: string; + referrerLabel?: string; + }) => ( + + {label} + + ), +}); diff --git a/docs/content/about.mdx b/docs/content/about.mdx index 59a034c3c7f1..ef3c33b23bb3 100644 --- a/docs/content/about.mdx +++ b/docs/content/about.mdx @@ -21,10 +21,6 @@ AgDS is based on the [GOLD Design System](https://gold.designsystemau.org) which The Agriculture Design System is the best way to ensure we meet our legal obligations to ensure accessibility. Under the Disability Discrimination Act 1992, Australian Government agencies are required to ensure information and services are provided in a non-discriminatory accessible manner. That is why AgDS’s suite of components, templates and guides are designed with the highest accessibility standards. -## Connect with us - -Department of Agriculture Fisheries and Forestry staff can connect with us in the ‘Design System’ chat in Microsoft Teams, or we can be reached at [designsystem@agriculture.gov.au](mailto:designsystem@agriculture.gov.au). - ## License This work is licensed under the [MIT license](https://github.com/agriculturegovau/agds-next/blob/main/LICENSE). diff --git a/docs/content/content/index.mdx b/docs/content/content/index.mdx index 0ed6dc29c1d5..ce8a004d4332 100644 --- a/docs/content/content/index.mdx +++ b/docs/content/content/index.mdx @@ -16,5 +16,3 @@ You can use this guidance to create content for the Export Service that is: Our guidance on content will grow as the Export Service expands and changes. For more help to create content, see [How to create guidance in the Export Service](/guides/how-to-write-guidance). - -If you have questions about this guidance or would like to suggest changes, you can contact us at [guidanceandsupport@aff.gov.au](mailto:guidanceandsupport@aff.gov.au). diff --git a/docs/content/foundations/accessibility.mdx b/docs/content/foundations/accessibility.mdx index 02b6d09888f7..713fb420cdeb 100644 --- a/docs/content/foundations/accessibility.mdx +++ b/docs/content/foundations/accessibility.mdx @@ -1,62 +1,4 @@ --- title: Accessibility -description: How we build components that provide a great user experience for all users. +description: AgDS strives to enable digital services that are usable and useful to all users, including those with disabilities. We recognise that disabilities can affect how people move, see, hear, communicate, learn, understand and process information. --- - -AgDS strives to be usable and useful to all users of the Export Service, including those with disabilities. Disabilities may affect how people move, see, hear, communicate, learn, understand and process information. - -## Simplicity by design - -Accessibility has always been a top priority for the Agriculture Design System (AgDS). - -AgDS is inspired by the original Australian Government Design System [GOLD](https://gold.designsystemau.org/), which was built on principles of simplicity and accessibility. - -We have an obligation to make our government services simple and fast to use, inclusive and accessible to all users in the location and context they need to use them. - -## Accessibility audit - -In late 2022, the AgDS website, components and templates were reviewed by an independent third-party specialist. This review included usability testing sessions with users who heavily rely on assistive technologies. - -AgDS achieved AA-level compliance against the Web Content Accessibility Guideline (WCAG) 2.1 success criteria and is now considered by this agency an exemplary implementation of an accessible, React-based design system. - -## Meeting the Web Content Accessibility Guidelines (WCAG) - -AgDS targets the WCAG 2.1 Level A and Level AA success criteria and seeks to provide a highly usable experience for everyone. - -## Assistive technology support - -AgDS components are tested for accessibility with automated and manual techniques to ensure support for: - -- screen readers -- screen magnifiers -- alternative input mechanisms -- low vision and colour blindness. - -## Browser support - -AgDS supports the latest two versions of the following browsers: - -- Google Chrome -- Mozilla Firefox -- Apple Safari for MacOS -- Microsoft Edge -- Apple Safari for iOS -- Google Chrome for Android - -## Coding standards - -AgDS components are built using modern web standards for HTML, CSS and JavaScript. - -Features from the [Accessible Rich Internet Applications (ARIA)](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA) specification are used to build functionality that is not available natively to web browsers. - -Where possible, components are built according to specifications found on the [ARIA Authoring Practices Guide (APG)](https://www.w3.org/WAI/ARIA/apg/). - -## Making your product accessible - -Product teams need to conduct their own research and testing sessions to ensure that the compositions of AgDS components are accessibile in the context of their product. - -For information about getting started with accessibility, we recommend visiting the [Accessibility Fundamentals Overview](https://www.w3.org/WAI/fundamentals/) from the Web Accessibility Initiative (WAI). - -## Feedback - -The AgDS team is always looking at ways to improve the accessibility of our components. If you find a problem or think that AgDS is not meeting accessibility requirements, please reach out to the AgDS team via Microsoft Teams or by [creating a Github issue](https://github.com/agriculturegovau/agds-next/issues). diff --git a/docs/content/foundations/accessibility/accessibility-overview.mdx b/docs/content/foundations/accessibility/accessibility-overview.mdx new file mode 100644 index 000000000000..08eed334225b --- /dev/null +++ b/docs/content/foundations/accessibility/accessibility-overview.mdx @@ -0,0 +1,62 @@ +## Simplicity by design + +Accessibility has always been a top priority for the Agriculture Design System (AgDS). +AgDS is inspired by the original Australian Government Design System GOLD, which was built on principles of simplicity and accessibility. + +We have an obligation to make our government services simple and fast to use, inclusive and accessible to all users in the location and context they need to use them. + +## Accessibility audit + +The system is tested by the team for accessibility at every stage. From visual design, automated code testing, to testing with assistive technology. The system has also been audited for WCAG conformance twice in 2022 and 2024 and both times achieved a certification of full WCAG conformance. + +Our statements of conformance: + +- [Statement of conformance 2022]() +- [Statement of conformance 2024]() + +## Meeting the Web Content Accessibility Guidelines (WCAG) + +AgDS targets the WCAG 2.1 Level A and Level AA success criteria and seeks to provide a highly usable experience for everyone. + +In these instances, we exceed those standards: + +- The purpose of page elements is understood and conveyed by assistive technologies – [WCAG success criterion 1.3.6 Identify purpose (level AAA)](https://www.w3.org/WAI/WCAG21/Understanding/identify-purpose.html). +- Headings and body text have a contrast ratio above 7:1 – [WCAG 2.1 success criterion 1.4.6 Contrast (enhanced) (level AAA)](https://www.w3.org/WAI/WCAG21/Understanding/contrast-enhanced.html). +- Text can be altered by users to meet their preferences – [WCAG 2.1 success criterion 1.4.8 Visual presentation (level AAA)](https://www.w3.org/WAI/WCAG21/Understanding/visual-presentation.html). +- All functionality is operable via keyboard – [WCAG 2.1 success criterion 2.1.3 Keyboard (no exception) (level AAA)](https://www.w3.org/WAI/WCAG21/Understanding/keyboard-no-exception.html). +- Users can identify the purpose of a link from the link text alone – [ + WCAG 2.1 success criterion 2.4.9 Link purpose (link only) (level AAA)](https://www.w3.org/WAI/WCAG21/Understanding/link-purpose-link-only.html). +- Section headings are used to organise page content – [WCAG 2.1 success criterion 2.4.10 Section headings (level AAA)](https://www.w3.org/WAI/WCAG21/Understanding/section-headings.html). +- We use pointer target sizes of at least 24 by 24 pixels – [WCAG 2.2 success criterion 2.5.8 Target size (minimum) (level AA)](https://www.w3.org/WAI/WCAG22/Understanding/target-size-minimum.html). +- Users can check, reverse or confirm their input on forms – [WCAG 2.1 success criterion 3.3.6 Error prevention (all) (level AAA)](https://www.w3.org/WAI/WCAG21/Understanding/error-prevention-all.html). + +## Assistive technology support + +AgDS components are tested for accessibility with automated and manual techniques to ensure support for: + +- screen readers (JAWS, NVDA, VoiceOver, TalkBack) +- screen magnifiers +- alternative input mechanisms +- low vision and colour blindness. + +## Browser support + +AgDS supports the latest two versions of the following browsers: + +- Google Chrome +- Mozilla Firefox +- Apple Safari for MacOS +- Microsoft Edge +- Apple Safari for iOS +- Google Chrome for Android + +## Coding standards + +AgDS components are built using modern web standards for HTML, CSS and JavaScript. +Features from the [Accessible Rich Internet Applications (ARIA)](https://www.w3.org/WAI/ARIA/apg/) specification are used to build functionality that is not available natively to web browsers. +Where possible, components are built according to specifications found on the [ARIA Authoring Practices Guide (APG)](https://www.w3.org/WAI/ARIA/apg/patterns/). + +## Making your product accessible + +Product teams need to conduct their own research and testing sessions to ensure that the compositions of AgDS components are accessible in the context of their product. +For information about getting started with accessibility, we recommend visiting the [Accessibility Fundamentals Overview from the Web Accessibility Initiative (WAI)](https://www.w3.org/WAI/fundamentals/). diff --git a/docs/content/foundations/accessibility/aria-and-semantic-code.mdx b/docs/content/foundations/accessibility/aria-and-semantic-code.mdx new file mode 100644 index 000000000000..d45b05fe67dd --- /dev/null +++ b/docs/content/foundations/accessibility/aria-and-semantic-code.mdx @@ -0,0 +1,68 @@ +## ARIA in AgDS + +ARIA has been used throughout AgDS to ensure that our components and design patterns are fully accessible to everyone. This means you can get started on building accessibility into your digital products and services right away. + +The ARIA usage in our components achieved full WCAG conformance in accessibility audits in 2022 and 2024. Our implementation of ARIA is also demonstrated in the [yourGov](/yourgov) application. + +## What is semantic markup? + +Semantic markup means using HTML elements according to their intended meaning, structure and purpose, rather than just for visual styling. + +Using the correct semantic elements provides built-in accessibility, keyboard support, and screen reader compatibility. This also improves the readability and maintainability of your code. + +In addition to native HTML, AgDS provides a suite of components that apply semantic markup alongside appropriate visual styling, performance optimisations, and accessibility features. + +## What is ARIA? + +ARIA (Accessible Rich Internet Applications) is a set of HTML attributes that define roles, states, and properties to help make web content and applications more accessible. This is particularly important for people using assistive technologies like screen readers. + +[ARIA roles](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Roles) describe what an element is – for example, `role="combobox"`, `role="dialog"`. This is important when that element doesn’t exist natively in HTML. + +[ARIA states and properties](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Attributes) and properties describe the current state or behaviour of an element – for example, `aria-busy`, `aria-expanded`, `aria-invalid`, `aria-labelledby`. + +## Before using ARIA + + + “If you can use a native HTML element or attribute with the semantics and + behaviour you require already built in, instead of re-purposing an element and + adding an ARIA role, state or property to make it accessible, then do so.” + + From [Using ARIA – First rule of + ARIA](https://www.w3.org/TR/using-aria/#rule1) + + + +You may also hear the phrase: + +> “No ARIA is better than bad ARIA.” + +In [WebAim’s survey of over one million home pages](https://webaim.org/projects/million/#aria), they found that pages using ARIA averaged 41% more detected accessibility errors than those without it. While ARIA is intended to improve accessibility, incorrect use can significantly worsen the user experience. This is especially true for people relying on assistive technologies. + +## Requirements + +- Use semantic HTML wherever possible – for example, `