-
Notifications
You must be signed in to change notification settings - Fork 1.1k
a11y - separate nav expander from link #7868
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 4 commits
ef98507
43de576
2e7d363
05b5ea7
bbba01e
6b41d06
0235a96
f4b3336
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,13 @@ | ||
import { usePathWithoutHash } from '@/utils/usePathWithoutHash'; | ||
import { ReactElement, useContext, useEffect, useState, useMemo } from 'react'; | ||
import { Link as AmplifyUILink, Flex } from '@aws-amplify/ui-react'; | ||
import { | ||
ReactElement, | ||
useContext, | ||
useEffect, | ||
useState, | ||
useMemo, | ||
useRef | ||
} from 'react'; | ||
import { Link as AmplifyUILink, Button, Flex } from '@aws-amplify/ui-react'; | ||
import { IconExternalLink, IconChevron } from '@/components/Icons'; | ||
import Link from 'next/link'; | ||
import { JS_PLATFORMS, Platform, JSPlatform } from '@/data/platforms'; | ||
|
@@ -19,6 +26,7 @@ type MenuItemProps = { | |
level: number; | ||
currentPlatform?: Platform; | ||
hideChildren?: boolean; | ||
tabIndex?: number; | ||
}; | ||
|
||
function getPathname(route, currentPlatform: Platform | undefined) { | ||
|
@@ -43,11 +51,38 @@ export function MenuItem({ | |
const { menuOpen, toggleMenuOpen } = useContext(LayoutContext); | ||
const asPathWithoutHash = usePathWithoutHash(); | ||
const [open, setOpen] = useState(false); | ||
const ref = useRef(null); | ||
const children = useMemo( | ||
() => (hideChildren ? [] : pageNode.children), | ||
[hideChildren, pageNode.children] | ||
); | ||
|
||
const setSelectability = () => { | ||
const current = ref.current; | ||
const items = current?.parentElement?.nextSibling?.children; | ||
if (items) { | ||
for (const item of items) { | ||
const links = item.getElementsByTagName('a'); | ||
const buttons = item.getElementsByTagName('button'); | ||
if (links[0].getAttribute('tabIndex') == 0) { | ||
for (const link of links) { | ||
link.setAttribute('tabIndex', -1); | ||
} | ||
for (const button of buttons) { | ||
button.setAttribute('tabIndex', -1); | ||
} | ||
} else if (-links[0].getAttribute('tabIndex')) { | ||
links[0]?.setAttribute('tabIndex', 0); | ||
|
||
buttons[0]?.setAttribute('tabIndex', 0); | ||
} | ||
} | ||
} | ||
current?.focus(); | ||
}; | ||
|
||
const onLinkClick = () => { | ||
setSelectability(); | ||
|
||
// Category shouldn't be collapsible | ||
if ( | ||
level > Levels.Category && | ||
|
@@ -62,6 +97,18 @@ export function MenuItem({ | |
} | ||
}; | ||
|
||
const onCheveronClick = () => { | ||
setSelectability(); | ||
setOpen((prevOpen) => !prevOpen); | ||
|
||
if (menuOpen) { | ||
// Close the menu after clicking a link (applies to the mobile menu) | ||
toggleMenuOpen(false); | ||
} else { | ||
toggleMenuOpen(true); | ||
} | ||
}; | ||
|
||
const handleFocus = () => { | ||
if (parentSetOpen) { | ||
parentSetOpen(true); | ||
|
@@ -157,6 +204,7 @@ export function MenuItem({ | |
href={pageNode.route} | ||
isExternal={true} | ||
onClick={onLinkClick} | ||
tabIndex={level > Levels.Subcategory ? -1 : 0} | ||
> | ||
<Flex | ||
className={`menu__list-item__link__inner ${listItemLinkInnerStyle}`} | ||
|
@@ -188,22 +236,35 @@ export function MenuItem({ | |
key={pageNode.route} | ||
className={`menu__list-item ${listItemStyle}`} | ||
> | ||
<Link | ||
className={`menu__list-item__link ${listItemLinkStyle} ${currentStyle}`} | ||
aria-current={current ? 'page' : null} | ||
href={href} | ||
onClick={onLinkClick} | ||
passHref | ||
> | ||
<Flex | ||
className={`menu__list-item__link__inner ${listItemLinkInnerStyle}`} | ||
<Flex className={`menu__list-item__inner`}> | ||
<Link | ||
className={`menu__list-item__link ${listItemLinkStyle} ${current ? currentStyle : null}`} | ||
aria-current={current ? 'page' : null} | ||
href={href} | ||
onClick={onLinkClick} | ||
ref={ref} | ||
tabIndex={level > Levels.Subcategory ? -1 : 0} | ||
passHref | ||
> | ||
{pageNode.title} | ||
{children && hasVisibleChildren && level !== Levels.Category && ( | ||
<Flex | ||
className={`menu__list-item__link__inner ${listItemLinkInnerStyle}`} | ||
> | ||
{pageNode.title} | ||
</Flex> | ||
</Link> | ||
{children && hasVisibleChildren && level !== Levels.Category && ( | ||
<Button | ||
className={`${listItemLinkStyle} expand-button`} | ||
onClick={onCheveronClick} | ||
ref={ref} | ||
aria-expanded="true" | ||
aria-labelledby="li" | ||
tabIndex={level > Levels.Subcategory ? -1 : 0} | ||
> | ||
<IconChevron className={open ? '' : 'icon-rotate-90-reverse'} /> | ||
)} | ||
</Flex> | ||
</Link> | ||
</Button> | ||
)} | ||
</Flex> | ||
{children && ( | ||
<ul | ||
className={`menu__list ${ | ||
|
@@ -217,6 +278,7 @@ export function MenuItem({ | |
parentSetOpen={setOpen} | ||
level={level + 1} | ||
currentPlatform={currentPlatform} | ||
tabIndex={level > Levels.Subcategory ? -1 : 0} | ||
/> | ||
))} | ||
</ul> | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like the desired tab behavior only works when opening the link after landing on a page, reloading while already on a page leaves these visible menu links untabbable
Screen.Recording.2024-08-15.at.9.18.18.AM.mov