Skip to content

Commit

Permalink
Merge pull request #2070 from GSA/1904-polite-a11y-page-titles
Browse files Browse the repository at this point in the history
1904 polite a11y page titles
  • Loading branch information
scottqueen-bixal authored Jan 22, 2025
2 parents 59623bb + 03556fa commit 6c5cbb7
Show file tree
Hide file tree
Showing 9 changed files with 56 additions and 11 deletions.
3 changes: 3 additions & 0 deletions benefit-finder/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@
</script>
</head>
<body>
<div id="live-region" aria-live="polite" class="usa-sr-only" tabindex="-1">
Benefit finder: death of a loved one | USAGov
</div>
<script type="module" src="./src/index.jsx"></script>
<noscript>You need to enable JavaScript to run this app.</noscript>
<main>
Expand Down
3 changes: 2 additions & 1 deletion benefit-finder/src/Routes/Intro/index.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useEffect, useContext } from 'react'
import { useNavigate, useLocation } from 'react-router'
import { RouteContext } from '@/App'
import { dataLayerUtils } from '@utils'
import { dataLayerUtils, politeTitles } from '@utils'
import { useResetElement } from '@hooks'
import PropTypes from 'prop-types'
import {
Expand Down Expand Up @@ -52,6 +52,7 @@ const Intro = ({ content, ui, hasQueryParams }) => {
event: intro.event,
bfData: { pageView: intro.bfData.pageView, viewTitle: title },
})
politeTitles(title)
}, [hasQueryParams])

return (
Expand Down
3 changes: 3 additions & 0 deletions benefit-finder/src/Routes/LifeEventSection/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
dataLayerUtils,
errorHandling,
handleSurvey,
politeTitles,
} from '@utils'
import { useHandleUnload, useResetElement } from '@hooks'
import * as apiCalls from '@api/apiCalls'
Expand Down Expand Up @@ -51,6 +52,7 @@ const LifeEventSection = ({ data, handleData, ui }) => {
const resetElement = useResetElement()
const ROUTES = useContext(RouteContext)
const navigate = useNavigate()
const locale = apiCalls.GET.Language()

let location = useLocation() // ignore prefer-const

Expand Down Expand Up @@ -262,6 +264,7 @@ const LifeEventSection = ({ data, handleData, ui }) => {
viewTitle: data[index]?.section.heading,
},
})
politeTitles(location.pathname, locale)
resetElement.current?.focus()
window.scrollTo(0, 0)
}, [location])
Expand Down
4 changes: 3 additions & 1 deletion benefit-finder/src/Routes/ResultsView/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { useResetElement } from '@hooks'
import * as apiCalls from '@api/apiCalls'
import PropTypes from 'prop-types'
import { Results } from './components/index'
import { dataLayerUtils, handleSurvey, domReady } from '@utils'
import { dataLayerUtils, handleSurvey, domReady, politeTitles } from '@utils'

// Results View is a single view with three states, eligible, not eligible, and zero benefits

Expand All @@ -32,6 +32,7 @@ const ResultsView = ({
const navigate = useNavigate()
const location = useLocation()
const ROUTES = useContext(RouteContext)
const locale = apiCalls.GET.Language()

/**
* a hook that handles our open state of the accordions in our group
Expand Down Expand Up @@ -105,6 +106,7 @@ const ResultsView = ({
...eligibilityCount,
},
})
politeTitles(location.pathname, locale)
}, [notEligibleView, eligibilityCount])

useEffect(() => {
Expand Down
7 changes: 4 additions & 3 deletions benefit-finder/src/Routes/VerifySelectionsView/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { useEffect, useContext } from 'react'
import { useNavigate, useLocation } from 'react-router'
import PropTypes from 'prop-types'
import { RouteContext } from '@/App'
import { parseDate, dataLayerUtils, handleSurvey } from '@utils'
import { parseDate, dataLayerUtils, handleSurvey, politeTitles } from '@utils'
import { useResetElement } from '@hooks'
import * as apiCalls from '@api/apiCalls'
import { Heading, Button } from '@components'
Expand All @@ -19,7 +19,7 @@ import './_index.scss'
const VerifySelectionsView = ({ ui, data }) => {
const { verifySelectionsView, buttonGroup } = ui
const { verifySelections } = dataLayerUtils.dataLayerStructure
const local = apiCalls.GET.Language()
const locale = apiCalls.GET.Language()
const dateFormatOptions = {
year: 'numeric',
month: 'long',
Expand Down Expand Up @@ -63,7 +63,7 @@ const VerifySelectionsView = ({ ui, data }) => {
<div className="bf-verify-criteria-legend">{legend}</div>
{typeof selected?.value === 'object'
? `${parseDate(selected.value).toLocaleDateString(
local,
locale,
dateFormatOptions
)}`
: selected?.value}
Expand Down Expand Up @@ -118,6 +118,7 @@ const VerifySelectionsView = ({ ui, data }) => {
viewTitle: verifySelectionsView?.heading,
},
})
politeTitles(location.pathname, locale)
}, [])

useEffect(() => {
Expand Down
2 changes: 2 additions & 0 deletions benefit-finder/src/shared/utils/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import dataLayerUtils from './dataLayerUtils'
import dateInputValidation from './dateInputValidation'
import handleSurvey from './handleSurvey'
import parseDate from './parseDate'
import politeTitles from './politeTitles'
import scrollLock from './scrollLock'
import windowSize from './windowSize'

Expand All @@ -20,6 +21,7 @@ export {
dateInputValidation,
handleSurvey,
parseDate,
politeTitles,
scrollLock,
windowSize,
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import parseDate from '../index'

let mockLocal
let mockLocale
const dateFormatOptions = {
year: 'numeric',
month: 'long',
Expand All @@ -18,17 +18,17 @@ const expectedEnValue = 'January 22, 2022'
const expectedEsValue = '22 de enero de 2022'

describe('test parseDate utility', () => {
it('returns a localized string from a date value based on local', () => {
mockLocal = 'en'
it('returns a localized string from a date value based on locale', () => {
mockLocale = 'en'

expect(
parseDate(selectedValue).toLocaleDateString(mockLocal, dateFormatOptions)
parseDate(selectedValue).toLocaleDateString(mockLocale, dateFormatOptions)
).toBe(expectedEnValue)

mockLocal = 'es'
mockLocale = 'es'

expect(
parseDate(selectedValue).toLocaleDateString(mockLocal, dateFormatOptions)
parseDate(selectedValue).toLocaleDateString(mockLocale, dateFormatOptions)
).toBe(expectedEsValue)
})
})
32 changes: 32 additions & 0 deletions benefit-finder/src/shared/utils/politeTitles/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
const politeTitles = (location, locale) => {
// create a hidden liveRegion to announce titles to screen-readers
const liveRegion = document.getElementById('live-region')

// get the last part of the location Path
// remove the hyphen
const pathValue = location.split('/').pop()
const title = pathValue.replace('-', ' ')

// create language specific titles
const enTitle = `Entering Benefit Finder: ${title.toLowerCase()} | USAGov`
const esTitle = `Buscador de beneficios: ${title.toLowerCase()} | USAGov`
const defaultTitle = `${title} | USAGov`

// assign title based on locale
const politeTitle = !locale
? defaultTitle
: locale === 'es'
? esTitle
: enTitle

// update the live region
if (liveRegion) {
liveRegion.textContent = politeTitle
}
// update the page title
document.title = politeTitle

return
}

export default politeTitles
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@
{% endif %}

<div class="usa-overlay"></div>
<div id="live-region" aria-live="assertive" class="usa-sr-only" tabindex="-1">{{ node.label }}</div>

<header class="{{ header_classes }}" id="header">

Expand Down

0 comments on commit 6c5cbb7

Please sign in to comment.