mirror of
https://github.com/UiO-Gaming/uiogaming.no.git
synced 2026-11-05 13:56:47 +01:00
Make the "Other" dropdown accessible
Expose its state with aria-expanded, close it on outside clicks, Escape and when focus leaves, restore the focus outline, and move the inline margins into CSS. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
eab0e0bbab
commit
633237f262
2 files changed
+61
-13
No files matched your search
@@ -39,13 +39,10 @@
|
||||
.dropdownLink {
|
||||
display: block;
|
||||
width: 100%;
|
||||
margin-top: 4px;
|
||||
color: black;
|
||||
background-color: #cccccc;
|
||||
border: none;
|
||||
border-radius: 999em !important;
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
border-radius: 10px;
|
||||
border-radius: 999em;
|
||||
text-align: left;
|
||||
font-size: 1.6rem;
|
||||
box-shadow: var(--shadow);
|
||||
@@ -53,6 +50,10 @@
|
||||
padding-right: 2em;
|
||||
}
|
||||
|
||||
.dropdownLink:first-child {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.dropdownLink:hover {
|
||||
color: black;
|
||||
background-color: #e6e6e6;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client"
|
||||
|
||||
import { useState } from "react"
|
||||
import { useEffect, useId, useRef, useState } from "react"
|
||||
|
||||
import { useLocale, useTranslations } from "next-intl"
|
||||
import * as styles from "./otherButton.module.css"
|
||||
@@ -11,7 +11,37 @@ const OtherButton = () => {
|
||||
const locale = useLocale()
|
||||
|
||||
const [isOpen, setIsOpen] = useState(false)
|
||||
const toggleDropdown = () => setIsOpen(!isOpen)
|
||||
const toggleDropdown = () => setIsOpen(open => !open)
|
||||
|
||||
const containerRef = useRef<HTMLDivElement>(null)
|
||||
const buttonRef = useRef<HTMLButtonElement>(null)
|
||||
const dropdownId = useId()
|
||||
|
||||
// Close when clicking outside the dropdown or pressing Escape
|
||||
useEffect(() => {
|
||||
if (!isOpen) return
|
||||
|
||||
const handlePointerDown = (event: PointerEvent) => {
|
||||
if (!containerRef.current?.contains(event.target as Node)) {
|
||||
setIsOpen(false)
|
||||
}
|
||||
}
|
||||
|
||||
const handleKeyDown = (event: KeyboardEvent) => {
|
||||
if (event.key === "Escape") {
|
||||
setIsOpen(false)
|
||||
buttonRef.current?.focus()
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener("pointerdown", handlePointerDown)
|
||||
document.addEventListener("keydown", handleKeyDown)
|
||||
|
||||
return () => {
|
||||
document.removeEventListener("pointerdown", handlePointerDown)
|
||||
document.removeEventListener("keydown", handleKeyDown)
|
||||
}
|
||||
}, [isOpen])
|
||||
|
||||
const statutesUrl =
|
||||
locale === "en"
|
||||
@@ -28,21 +58,38 @@ const OtherButton = () => {
|
||||
]
|
||||
|
||||
return (
|
||||
<div className={styles.dropdownContainer}>
|
||||
<button onClick={toggleDropdown} className={styles.button}>
|
||||
<div
|
||||
ref={containerRef}
|
||||
className={styles.dropdownContainer}
|
||||
// Close when keyboard focus moves out of the dropdown
|
||||
onBlur={event => {
|
||||
if (!event.currentTarget.contains(event.relatedTarget)) {
|
||||
setIsOpen(false)
|
||||
}
|
||||
}}
|
||||
>
|
||||
<button
|
||||
ref={buttonRef}
|
||||
onClick={toggleDropdown}
|
||||
className={styles.button}
|
||||
aria-expanded={isOpen}
|
||||
aria-controls={dropdownId}
|
||||
>
|
||||
{t("other")}{" "}
|
||||
<span className={isOpen ? styles.arrowUp : styles.arrowDown}></span>
|
||||
<span
|
||||
className={isOpen ? styles.arrowUp : styles.arrowDown}
|
||||
aria-hidden="true"
|
||||
></span>
|
||||
</button>
|
||||
{isOpen && (
|
||||
<div className={styles.dropdown}>
|
||||
{links.map((link, index) => (
|
||||
<div id={dropdownId} className={styles.dropdown}>
|
||||
{links.map(link => (
|
||||
<a
|
||||
key={link.url}
|
||||
href={link.url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className={styles.dropdownLink}
|
||||
style={{ marginTop: index === 0 ? "8px" : "4px" }}
|
||||
>
|
||||
{link.label}
|
||||
</a>
|
||||
|
||||
Reference in new issue
Block a user