468833e0b4
As the comment mentioned, React 18 with Strict Mode enabled might cause double invocation of lifecycle methods. This makes the `<RouteAnnouncer/>` being non-empty for the initial page, which is a bug (it should only announce page change whenever a navigation happens). ## Bug - [ ] Related issues linked using `fixes #number` - [x] Integration tests added - [ ] Errors have helpful link attached, see `contributing.md` ## Feature - [ ] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Documentation added - [ ] Telemetry added. In case of a feature if it's used or not. - [ ] Errors have helpful link attached, see `contributing.md` ## Documentation / Examples - [ ] Make sure the linting passes by running `yarn lint`
62 lines
1.9 KiB
TypeScript
62 lines
1.9 KiB
TypeScript
import React from 'react'
|
||
import { useRouter } from './router'
|
||
|
||
export function RouteAnnouncer() {
|
||
const { asPath } = useRouter()
|
||
const [routeAnnouncement, setRouteAnnouncement] = React.useState('')
|
||
|
||
// Only announce the path change, but not for the first load because screen
|
||
// reader will do that automatically.
|
||
const previouslyLoadedPath = React.useRef(asPath)
|
||
|
||
// Every time the path changes, announce the new page’s title following this
|
||
// priority: first the document title (from head), otherwise the first h1, or
|
||
// if none of these exist, then the pathname from the URL. This methodology is
|
||
// inspired by Marcy Sutton’s accessible client routing user testing. More
|
||
// information can be found here:
|
||
// https://www.gatsbyjs.com/blog/2019-07-11-user-testing-accessible-client-routing/
|
||
React.useEffect(
|
||
() => {
|
||
// If the path hasn't change, we do nothing.
|
||
if (previouslyLoadedPath.current === asPath) return
|
||
previouslyLoadedPath.current = asPath
|
||
|
||
if (document.title) {
|
||
setRouteAnnouncement(document.title)
|
||
} else {
|
||
const pageHeader = document.querySelector('h1')
|
||
const content = pageHeader?.innerText ?? pageHeader?.textContent
|
||
|
||
setRouteAnnouncement(content || asPath)
|
||
}
|
||
},
|
||
// TODO: switch to pathname + query object of dynamic route requirements
|
||
[asPath]
|
||
)
|
||
|
||
return (
|
||
<p
|
||
aria-live="assertive" // Make the announcement immediately.
|
||
id="__next-route-announcer__"
|
||
role="alert"
|
||
style={{
|
||
border: 0,
|
||
clip: 'rect(0 0 0 0)',
|
||
height: '1px',
|
||
margin: '-1px',
|
||
overflow: 'hidden',
|
||
padding: 0,
|
||
position: 'absolute',
|
||
width: '1px',
|
||
|
||
// https://medium.com/@jessebeach/beware-smushed-off-screen-accessible-text-5952a4c2cbfe
|
||
whiteSpace: 'nowrap',
|
||
wordWrap: 'normal',
|
||
}}
|
||
>
|
||
{routeAnnouncement}
|
||
</p>
|
||
)
|
||
}
|
||
|
||
export default RouteAnnouncer
|