refactor: popover a11y aria attributes
This commit is contained in:
@@ -52,6 +52,7 @@ const MobilePopoverContent = ({
|
||||
<div
|
||||
ref={mergeRefs([setPopoverElement, addCloseMethod])}
|
||||
className="fixed left-0 top-0 z-modal flex h-full w-full flex-col bg-default pb-safe-bottom pt-safe-top"
|
||||
id={'popover/' + id}
|
||||
data-popover={id}
|
||||
data-mobile-popover
|
||||
>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { MutuallyExclusiveMediaQueryBreakpoints, useMediaQuery } from '@/Hooks/useMediaQuery'
|
||||
import { useAndroidBackHandler } from '@/NativeMobileWeb/useAndroidBackHandler'
|
||||
import { UuidGenerator } from '@standardnotes/snjs'
|
||||
import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { createContext, useCallback, useContext, useEffect, useId, useMemo, useState } from 'react'
|
||||
import MobilePopoverContent from './MobilePopoverContent'
|
||||
import PositionedPopoverContent from './PositionedPopoverContent'
|
||||
import { PopoverProps } from './Types'
|
||||
@@ -62,11 +61,11 @@ const PositionedPopoverContentWithAnimation = (
|
||||
}
|
||||
|
||||
const Popover = (props: PopoverProps) => {
|
||||
const popoverId = useRef(UuidGenerator.GenerateUuid())
|
||||
const popoverId = useId()
|
||||
|
||||
const addAndroidBackHandler = useAndroidBackHandler()
|
||||
|
||||
useRegisterPopoverToParent(popoverId.current)
|
||||
useRegisterPopoverToParent(popoverId)
|
||||
|
||||
const [childPopovers, setChildPopovers] = useState<Set<string>>(new Set())
|
||||
|
||||
@@ -106,6 +105,27 @@ const Popover = (props: PopoverProps) => {
|
||||
}
|
||||
}, [addAndroidBackHandler, props, props.open])
|
||||
|
||||
useEffect(() => {
|
||||
const anchorElement =
|
||||
props.anchorElement && 'current' in props.anchorElement ? props.anchorElement.current : props.anchorElement
|
||||
|
||||
if (anchorElement) {
|
||||
anchorElement.setAttribute('aria-haspopup', 'true')
|
||||
if (props.open) {
|
||||
anchorElement.setAttribute('aria-expanded', 'true')
|
||||
} else {
|
||||
anchorElement.removeAttribute('aria-expanded')
|
||||
}
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (anchorElement) {
|
||||
anchorElement.removeAttribute('aria-haspopup')
|
||||
anchorElement.removeAttribute('aria-expanded')
|
||||
}
|
||||
}
|
||||
}, [props.anchorElement, props.open])
|
||||
|
||||
const isMobileScreen = useMediaQuery(MutuallyExclusiveMediaQueryBreakpoints.sm)
|
||||
|
||||
if (isMobileScreen && !props.disableMobileFullscreenTakeover) {
|
||||
@@ -117,7 +137,7 @@ const Popover = (props: PopoverProps) => {
|
||||
}}
|
||||
title={props.title}
|
||||
className={props.className}
|
||||
id={popoverId.current}
|
||||
id={popoverId}
|
||||
>
|
||||
{props.children}
|
||||
</MobilePopoverContent>
|
||||
@@ -126,7 +146,7 @@ const Popover = (props: PopoverProps) => {
|
||||
|
||||
return (
|
||||
<PopoverContext.Provider value={contextValue}>
|
||||
<PositionedPopoverContentWithAnimation {...props} childPopovers={childPopovers} id={popoverId.current} />
|
||||
<PositionedPopoverContentWithAnimation {...props} childPopovers={childPopovers} id={popoverId} />
|
||||
</PopoverContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -35,7 +35,8 @@ const PositionedPopoverContent = ({
|
||||
}: PopoverContentProps) => {
|
||||
const [popoverElement, setPopoverElement] = useState<HTMLDivElement | null>(null)
|
||||
const popoverRect = useAutoElementRect(popoverElement)
|
||||
const anchorElementRect = useAutoElementRect(anchorElement, {
|
||||
const resolvedAnchorElement = anchorElement && 'current' in anchorElement ? anchorElement.current : anchorElement
|
||||
const anchorElementRect = useAutoElementRect(resolvedAnchorElement, {
|
||||
updateOnWindowResize: true,
|
||||
})
|
||||
const anchorPointRect = DOMRect.fromRect({
|
||||
@@ -75,7 +76,7 @@ const PositionedPopoverContent = ({
|
||||
|
||||
usePopoverCloseOnClickOutside({
|
||||
popoverElement,
|
||||
anchorElement,
|
||||
anchorElement: resolvedAnchorElement,
|
||||
togglePopover,
|
||||
childPopovers,
|
||||
hideOnClickInModal,
|
||||
@@ -122,13 +123,14 @@ const PositionedPopoverContent = ({
|
||||
} as CSSProperties
|
||||
}
|
||||
ref={mergeRefs([setPopoverElement, addCloseMethod])}
|
||||
id={'popover/' + id}
|
||||
data-popover={id}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === KeyboardKey.Escape) {
|
||||
event.stopPropagation()
|
||||
togglePopover?.()
|
||||
if (anchorElement) {
|
||||
anchorElement.focus()
|
||||
if (resolvedAnchorElement) {
|
||||
resolvedAnchorElement.focus()
|
||||
}
|
||||
}
|
||||
}}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ReactNode } from 'react'
|
||||
import { ReactNode, RefObject } from 'react'
|
||||
|
||||
export type PopoverState = 'closed' | 'positioning' | 'open'
|
||||
|
||||
@@ -20,8 +20,10 @@ type Point = {
|
||||
y: number
|
||||
}
|
||||
|
||||
type AnchorElementOrRef = RefObject<HTMLElement | null> | HTMLElement | null
|
||||
|
||||
type PopoverAnchorElementProps = {
|
||||
anchorElement: HTMLElement | null
|
||||
anchorElement: AnchorElementOrRef
|
||||
anchorPoint?: never
|
||||
}
|
||||
|
||||
@@ -49,7 +51,7 @@ type CommonPopoverProps = {
|
||||
}
|
||||
|
||||
export type PopoverContentProps = CommonPopoverProps & {
|
||||
anchorElement?: HTMLElement | null
|
||||
anchorElement?: AnchorElementOrRef
|
||||
anchorPoint?: Point
|
||||
childPopovers: Set<string>
|
||||
togglePopover?: () => void
|
||||
|
||||
Reference in New Issue
Block a user