Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
3f87e54
adds a11y testing package, runner, and gh action
werdnanoslen Mar 9, 2024
7b3c752
Require aria-hidden/label on Icons, small fix to story's size control
werdnanoslen Mar 12, 2024
086b47e
applied previous commit changes to Icons.stories as well
werdnanoslen Mar 12, 2024
4e9ead6
Added label to all select stories, other minor fixes and improvements
werdnanoslen Mar 12, 2024
11991a2
change card header from semantic header to div
werdnanoslen Mar 13, 2024
918a01f
change tag story color and className to fix a11y violation
werdnanoslen Mar 13, 2024
055a649
added bg-ink to text styles that didn't pass a11y on white bg
werdnanoslen Mar 13, 2024
8b2ca43
Added labels to all TextInput stories to fix a11y warnings there, and…
werdnanoslen Mar 13, 2024
15ba009
Added label to all RangeInput stories, other minor fixes and improvem…
werdnanoslen Mar 13, 2024
c7cf59a
hide SocialLinks' decorative svgs from SRs
werdnanoslen Mar 14, 2024
944fb58
Added label to all textarea stories, tidied
werdnanoslen Mar 14, 2024
4e117b8
added aria-hidden="true" to all decorative IconListIcon stories
werdnanoslen Mar 14, 2024
2e2e371
Added label to all prefix/suffix stories, tidied
werdnanoslen Mar 14, 2024
f73bbcb
Added label to all DatePicker stories, tidied
werdnanoslen Mar 14, 2024
54addb4
aria-hide decorative icon in search button
werdnanoslen Mar 15, 2024
05c6842
changed Footers' h3 to p elements — class already handles styling
werdnanoslen Mar 15, 2024
2cb2456
removed test that shouldn't exist given a11y context
werdnanoslen Mar 15, 2024
b436f93
aria-hid Pagination Icons, tidied stories
werdnanoslen Mar 15, 2024
aa499a8
replaced presentaiton role with aria-label to fix a11y issue
werdnanoslen Mar 15, 2024
e8fd227
disabled skip-link rule for ExtendedNav, out of context
werdnanoslen Mar 15, 2024
d35dc87
use aria-controls only when appropriate
werdnanoslen Mar 15, 2024
f94b1cb
allow local rules to apply
werdnanoslen Mar 15, 2024
0ae68d9
Merge branch 'an-storybooka11y' into an-storybooka11y-fixes
werdnanoslen Mar 15, 2024
904f711
Merge branch 'main' into an-storybooka11y-fixes
werdnanoslen Mar 15, 2024
12a8688
check a11y after building storybook
werdnanoslen Mar 15, 2024
86f210b
Merge branch 'an-storybooka11y-fixes' of github.com:trussworks/react-…
werdnanoslen Mar 15, 2024
db09b00
removed CI, moving to another branch
werdnanoslen Mar 15, 2024
83fff72
Merge branch 'main' into an-storybooka11y-fixes
werdnanoslen Mar 15, 2024
69a93f6
update uswds package
werdnanoslen Mar 22, 2024
6039237
Added styles for indeterminate checkboxes
werdnanoslen Mar 22, 2024
339a37a
Added styles for indeterminate radios
werdnanoslen Mar 22, 2024
66b78da
Added the ability to customize which headings will be pulled into the…
werdnanoslen Mar 25, 2024
53d6f50
change class name and selection criteria for bolded heading
werdnanoslen Mar 25, 2024
3a929a9
Added a sticky header variant to the table component
werdnanoslen May 10, 2024
2372e71
prettier
werdnanoslen May 10, 2024
da8625d
updated sidenav example in documentation per new theme setting
werdnanoslen May 13, 2024
15215dc
Merge branch 'main' into an-3.8.0
werdnanoslen Jun 20, 2024
c9e4973
Make tooltip content hoverable
werdnanoslen Jun 24, 2024
b4fc992
upgrade uswds
werdnanoslen Jun 24, 2024
35d84e2
Make tooltip dismissible with escape key
werdnanoslen Jun 24, 2024
f11255b
Merge branch 'main' into an-3.8.0
werdnanoslen Jun 24, 2024
e99a67d
Merge branch 'an-3.8.1' into an-3.8.0
werdnanoslen Jun 24, 2024
59123ca
Merge remote-tracking branch 'origin/main' into an-3.8.0
mdmower-csnw Sep 20, 2025
9a66a8b
Storybook: fix differences introduced
mdmower-csnw Sep 20, 2025
102c05f
Update to USWDS 3.8.2
mdmower-csnw Sep 20, 2025
846401e
Maintain centering of back button in modal stories
mdmower-csnw Sep 20, 2025
f553c15
Use buttons with utility classes for pagination
mdmower-csnw Sep 20, 2025
1f75096
Remove unnecessary override
mdmower-csnw Sep 20, 2025
06b8b74
Update pagination button utility styles
mdmower-csnw Sep 20, 2025
fb22d9d
Add story for textarea validation
mdmower-csnw Sep 20, 2025
6872d86
Change centering strategy for modal buttons
mdmower-csnw Sep 21, 2025
de794cb
Undo rename of default checkbox/radio story items
mdmower-csnw Sep 21, 2025
0fde7f2
Fix element IDs in textarea validation story
mdmower-csnw Sep 21, 2025
ca153e7
Use classnames utility to set pagination classes
mdmower-csnw Sep 21, 2025
8da7911
Update to USWDS 3.9.0
mdmower-csnw Sep 21, 2025
cfb050f
Update storybook entry for alert with multiple children
mdmower-csnw Sep 21, 2025
2513233
Align modal unstyled buttons in stories (again)
mdmower-csnw Sep 21, 2025
9261fea
Apply heading selection to intersection observer
mdmower-csnw Sep 21, 2025
554bc21
Merge branch 'an-mdm-3.8.1' into mdm-3.9
mdmower-csnw Sep 21, 2025
31174d0
Fix aria-disabled check in date picker
mdmower-csnw Sep 22, 2025
a7ab672
Reliability improvements for in page nav
mdmower-csnw Sep 22, 2025
221b9a6
Merge branch 'an-mdm-3.8.1' into mdm-3.9
mdmower-csnw Sep 22, 2025
cd645c1
Reliability improvements for in page nav
mdmower-csnw Sep 22, 2025
39c765f
Merge branch 'an-mdm-3.8.1' into mdm-3.9
mdmower-csnw Sep 22, 2025
3110c12
Merge remote-tracking branch 'upstream/main' into an-mdm-3.8.1
mdmower-csnw Sep 22, 2025
b4bfbd4
Merge branch 'an-mdm-3.8.1' into mdm-3.9
mdmower-csnw Sep 22, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@
"peerDependencies": {
"@types/react": "^16.x || ^17.x || ^18.x || ^19.x",
"@types/react-dom": "^16.x || ^17.x || ^18.x || ^19.x",
"@uswds/uswds": "^3.7.1",
"@uswds/uswds": "^3.9.0",
"focus-trap-react": "^10.2.3",
"react": "^16.x || ^17.x || ^18.x || ^19.x",
"react-dom": "^16.x || ^17.x || ^18.x || ^19.x"
Expand Down Expand Up @@ -97,7 +97,7 @@
"@types/react-dom": "^19.0.4",
"@typescript-eslint/eslint-plugin": "^8.29.0",
"@typescript-eslint/parser": "^8.29.0",
"@uswds/uswds": "3.7.1",
"@uswds/uswds": "3.9.0",
"@vitejs/plugin-react": "^5.0.0",
"@vitest/coverage-istanbul": "^3.1.3",
"@vitest/eslint-plugin": "^1.1.44",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,17 @@ export default {
title: {
control: 'text',
},
headingElements: {
control: 'check',
options: ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'],
},
},
args: {
headingLevel: 'h4',
rootMargin: '0px 0px 0px 0px',
threshold: 1,
title: 'On this page',
headingElements: ['h2', 'h3'],
},
parameters: {
docs: {
Expand All @@ -46,6 +51,7 @@ type StorybookArguments = {
scrollOffset: string
threshold: number
title: string
headingElements: HeadingLevel[]
}

export const Default = (argTypes: StorybookArguments): JSX.Element => (
Expand All @@ -56,6 +62,7 @@ export const Default = (argTypes: StorybookArguments): JSX.Element => (
rootMargin={argTypes.rootMargin}
threshold={argTypes.threshold}
title={argTypes.title}
headingElements={argTypes.headingElements}
/>
)

Expand All @@ -70,5 +77,6 @@ export const ScrollOffset = (argTypes: StorybookArguments): JSX.Element => (
scrollOffset="2rem"
threshold={argTypes.threshold}
title={argTypes.title}
headingElements={argTypes.headingElements}
/>
)
23 changes: 21 additions & 2 deletions src/components/InPageNavigation/InPageNavigation.test.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react'
import { screen, render, getByRole } from '@testing-library/react'
import { screen, render, getByRole, within } from '@testing-library/react'
import { userEvent } from '@testing-library/user-event'
import { InPageNavigation } from './InPageNavigation'
import { HeadingLevel } from '../../types/headingLevel'
Expand All @@ -13,14 +13,15 @@ describe('InPageNavigation component', () => {
title: 'What do we have <i>here</i>?',
}

const setup = (plain?: boolean) => {
const setup = (plain?: boolean, headingElements?: HeadingLevel[]) => {
const utils = plain
? render(<InPageNavigation content={props.content} />)
: render(
<InPageNavigation
content={props.content}
headingLevel={props.headingLevel}
title={props.title}
headingElements={headingElements}
/>
)
const nav = screen.getByTestId('InPageNavigation')
Expand Down Expand Up @@ -62,4 +63,22 @@ describe('InPageNavigation component', () => {
})
expect(heading).toBeInTheDocument()
})

describe('lists the right heading types if', () => {
it('is undefined', () => {
const { nav } = setup(true)
const contentHeadingsTwo = screen.getAllByRole('heading', { level: 2 })
const contentHeadingsThree = screen.getAllByRole('heading', { level: 3 })
const contentHeadings = contentHeadingsTwo.concat(contentHeadingsThree)
const headingLinks = within(nav).getAllByRole('link')
expect(contentHeadings.length).toBe(headingLinks.length)
})

it('is defined', () => {
const { nav } = setup(false, ['h2' as HeadingLevel])
const contentHeadingsTwo = screen.getAllByRole('heading', { level: 2 })
const headingLinks = within(nav).getAllByRole('link')
expect(contentHeadingsTwo.length).toBe(headingLinks.length)
})
})
})
20 changes: 14 additions & 6 deletions src/components/InPageNavigation/InPageNavigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export type InPageNavigationProps = {
scrollOffset?: string
threshold?: number
title?: string
headingElements?: HeadingLevel[]
} & Omit<JSX.IntrinsicElements['div'], 'content'>

export const InPageNavigation = ({
Expand All @@ -26,6 +27,7 @@ export const InPageNavigation = ({
scrollOffset,
threshold = 1,
title = 'On this page',
headingElements = ['h2', 'h3'],
...divProps
}: InPageNavigationProps): JSX.Element => {
const asideClasses = classnames('usa-in-page-nav', styles.target, className)
Expand All @@ -38,8 +40,11 @@ export const InPageNavigation = ({
'--margin-offset': scrollOffset,
} as React.CSSProperties
const [currentSection, setCurrentSection] = useState('')
headingElements = !headingElements.length
? ['h2', 'h3']
: headingElements.sort()
const sectionHeadings: JSX.Element[] = content.props.children.filter(
(el: JSX.Element) => el.type === 'h2' || el.type === 'h3'
(el: JSX.Element) => headingElements.includes(el.type)
)
const handleIntersection = (entries: IntersectionObserverEntry[]) => {
entries.forEach((entry) => {
Expand All @@ -55,7 +60,9 @@ export const InPageNavigation = ({
}
const observer = new IntersectionObserver(handleIntersection, observerOptions)
useEffect(() => {
document.querySelectorAll('h2,h3').forEach((h) => observer.observe(h))
document
.querySelectorAll(headingElements.join(','))
.forEach((h) => observer.observe(h))
document.querySelector('html')?.classList.add(styles['smooth-scroll'])
return () => {
document.querySelector('html')?.classList.remove(styles['smooth-scroll'])
Expand All @@ -75,16 +82,17 @@ export const InPageNavigation = ({
<ul className="usa-in-page-nav__list">
{sectionHeadings.map((el: JSX.Element) => {
const heading: JSX.Element = el.props.children
const href: string = el.props.id
const href: string = el.props.id ?? ''
const hClass = classnames('usa-in-page-nav__item', {
'usa-in-page-nav__item--sub-item': el.type === 'h3',
'usa-in-page-nav__item--primary':
el.type === headingElements[0],
})
const lClass = classnames('usa-in-page-nav__link', {
'usa-current': href === currentSection,
'usa-current': !!href && href === currentSection,
})
return (
<li key={`usa-in-page-nav__item_${heading}`} className={hClass}>
<Link href={`#${href}`} className={lClass}>
<Link href={`#${CSS.escape(href)}`} className={lClass}>
{heading}
</Link>
</li>
Expand Down
57 changes: 42 additions & 15 deletions src/components/Pagination/Pagination.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import React, { type JSX } from 'react'
import classnames from 'classnames'
import { Icon } from '../Icon/Icons'
import { Link } from '../Link/Link'
import { Button } from '../Button/Button'

export type PaginationProps = {
pathname: string // pathname of results page
Expand Down Expand Up @@ -34,24 +33,29 @@ const PaginationPage = ({
const linkClasses = classnames('usa-pagination__button', {
'usa-current': isCurrent,
})
const buttonClasses = classnames(
linkClasses,
'bg-transparent',
'cursor-pointer',
'text-underline'
)

return (
<li
key={`pagination_page_${page}`}
className="usa-pagination__item usa-pagination__page-no">
{onClickPageNumber ? (
<Button
<button
type="button"
unstyled
data-testid="pagination-page-number"
className={linkClasses}
className={buttonClasses}
aria-label={`Page ${page}`}
aria-current={isCurrent ? 'page' : undefined}
onClick={(event) => {
onClickPageNumber(event, page)
}}>
{page}
</Button>
</button>
) : (
<Link
href={`${pathname}?page=${page}`}
Expand Down Expand Up @@ -161,26 +165,50 @@ export const Pagination = ({
const prevPage = !isOnFirstPage && currentPage - 1
const nextPage = !isOnLastPage && currentPage + 1

const prevLinkClasses = classnames(
'usa-pagination__link',
'usa-pagination__previous-page'
)
const prevButtonClasses = classnames(
prevLinkClasses,
'border-0',
'padding-0',
'bg-transparent',
'cursor-pointer',
'text-underline'
)
const nextLinkClasses = classnames(
'usa-pagination__link',
'usa-pagination__next-page'
)
const nextButtonClasses = classnames(
nextLinkClasses,
'border-0',
'padding-0',
'bg-transparent',
'cursor-pointer',
'text-underline'
)

return (
<nav aria-label="Pagination" className={navClasses} {...props}>
<ul className="usa-pagination__list">
{prevPage && (
<li className="usa-pagination__item usa-pagination__arrow">
{onClickPrevious ? (
<Button
<button
type="button"
unstyled
className="usa-pagination__link usa-pagination__previous-page"
className={prevButtonClasses}
aria-label="Previous page"
data-testid="pagination-previous"
onClick={onClickPrevious}>
<Icon.NavigateBefore aria-hidden={true} />
<span className="usa-pagination__link-text">Previous</span>
</Button>
</button>
) : (
<Link
href={`${pathname}?page=${prevPage}`}
className="usa-pagination__link usa-pagination__previous-page"
className={prevLinkClasses}
aria-label="Previous page">
<Icon.NavigateBefore aria-hidden={true} />
<span className="usa-pagination__link-text">Previous</span>
Expand All @@ -206,20 +234,19 @@ export const Pagination = ({
{nextPage && (
<li className="usa-pagination__item usa-pagination__arrow">
{onClickNext ? (
<Button
<button
type="button"
unstyled
className="usa-pagination__link usa-pagination__next-page"
className={nextButtonClasses}
aria-label="Next page"
data-testid="pagination-next"
onClick={onClickNext}>
<span className="usa-pagination__link-text">Next</span>
<Icon.NavigateNext aria-hidden={true} />
</Button>
</button>
) : (
<Link
href={`${pathname}?page=${nextPage}`}
className="usa-pagination__link usa-pagination__next-page"
className={nextLinkClasses}
aria-label="Next page">
<span className="usa-pagination__link-text">Next</span>
<Icon.NavigateNext aria-hidden={true} />
Expand Down
7 changes: 5 additions & 2 deletions src/components/SiteAlert/SiteAlert.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,10 @@ export const AlertWithStringContent = (): JSX.Element => (
export const AlertWithMultipleChildContent = (): JSX.Element => (
<SiteAlert variant="info">
<p className="usa-alert__text">Alert content</p>
<em>which includes</em> <strong>formatting tags</strong> and{' '}
<Link href="#">links</Link>.
<p className="usa-alert__text">
More content{' '}
<em className="usa-alert__text display-inline">which includes</em>{' '}
<strong>formatting tags</strong> and <Link href="#">links</Link>.
</p>
</SiteAlert>
)
19 changes: 17 additions & 2 deletions src/components/Table/Table.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,15 @@ Source: https://designsystem.digital.gov/components/table/
stackedStyle: {
control: {
type: 'select',
options: ['default', 'headers'],
options: ['default', 'headers', 'none'],
},
},
stickyHeader: {
control: {
type: 'boolean',
},
description: 'This is not compatible with stacked and scrollable',
},
},
args: {
stackedStyle: 'default',
Expand All @@ -41,7 +47,8 @@ Source: https://designsystem.digital.gov/components/table/
type StorybookArguments = {
bordered: boolean
striped: boolean
stackedStyle: 'default' | 'headers'
stackedStyle: 'default' | 'headers' | 'none'
stickyHeader: boolean
}

const testContent = (
Expand Down Expand Up @@ -341,6 +348,14 @@ export const Scrollable = (): JSX.Element => (
</>
)

export const StickyHeader = {
render: (argTypes: StorybookArguments): React.ReactElement => (
<Table stickyHeader={argTypes.stickyHeader} bordered={argTypes.bordered}>
{testContent}
</Table>
),
}

export const Striped = {
render: (argTypes: StorybookArguments): JSX.Element => (
<Table
Expand Down
6 changes: 6 additions & 0 deletions src/components/Table/Table.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,12 @@ describe('Table component', () => {
)
})

it('renders sticky header table', () => {
const { getByRole } = render(<Table stickyHeader>{testContent}</Table>)

expect(getByRole('table')).toHaveClass('usa-table--sticky-header')
})

it('passes the class onto the root table element', () => {
const { getByRole } = render(
<Table className="custom-class">{testContent}</Table>
Expand Down
9 changes: 9 additions & 0 deletions src/components/Table/Table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export type TableProps = {
striped?: boolean
compact?: boolean
stackedStyle?: 'none' | 'default' | 'headers'
stickyHeader?: boolean
}

export const Table = ({
Expand All @@ -27,6 +28,7 @@ export const Table = ({
striped,
compact,
stackedStyle = 'none',
stickyHeader,
}: TableProps): JSX.Element => {
const classes = classnames(
'usa-table',
Expand All @@ -38,6 +40,7 @@ export const Table = ({
'usa-table--compact': compact,
'usa-table--stacked': stackedStyle === 'default',
'usa-table--stacked-header': stackedStyle === 'headers',
'usa-table--sticky-header': stickyHeader,
},
className
)
Expand All @@ -48,6 +51,12 @@ export const Table = ({
)
}

if (stickyHeader && (scrollable || stackedStyle !== 'none')) {
console.warn(
'USWDS states that sticky headers are not compatible with scrollable or stacked variants. See USWDS Table component, Table variants for more information: https://designsystem.digital.gov/components/table'
)
}

const table = (
<table className={classes} data-testid="table">
{caption && <caption>{caption}</caption>}
Expand Down
Loading