gaufre: follow disclosure aria pattern

- add aria attributes on load with the gaufre api script so that people
already using la gaufre don't necessarely *have* to update their code
- add the aria patterns in given code examples/react components. In some
cases, our small page load JS code isn't enough: for example on SPAs
where gaufre buttons might be loaded after page load.

thanks @inseo
This commit is contained in:
Emmanuel Pelletier
2025-04-17 15:26:57 +02:00
parent 2bd3e9b194
commit e47ac65e80
3 changed files with 29 additions and 9 deletions

View File

@@ -23,6 +23,8 @@ export const Gaufre = ({ variant }: Props) => {
type="button" type="button"
className={`lasuite-gaufre-btn ${variantClass} lasuite-gaufre-btn--vanilla js-lasuite-gaufre-btn`} className={`lasuite-gaufre-btn ${variantClass} lasuite-gaufre-btn--vanilla js-lasuite-gaufre-btn`}
title={t("gaufre.label")} title={t("gaufre.label")}
aria-expanded="false"
aria-controls="lasuite-gaufre-popup"
> >
{t("gaufre.label")} {t("gaufre.label")}
</button> </button>

View File

@@ -1,5 +1,6 @@
;(function () { ;(function () {
const BUTTON_CLASS = "js-lasuite-gaufre-btn" const BUTTON_CLASS = "js-lasuite-gaufre-btn"
const POPUP_ID = "lasuite-gaufre-popup"
const DIMENSIONS = { width: 304, height: 352, margin: 8 } const DIMENSIONS = { width: 304, height: 352, margin: 8 }
let lastFocusedButton = null let lastFocusedButton = null
@@ -9,13 +10,20 @@
if ("requestIdleCallback" in window) { if ("requestIdleCallback" in window) {
requestIdleCallback(() => { requestIdleCallback(() => {
appendPopup() appendPopup()
enhanceButtonsA11y()
})
}
const enhanceButtonsA11y = () => {
const buttons = document.querySelectorAll(`.${BUTTON_CLASS}`)
buttons.forEach((b) => {
b.setAttribute("aria-controls", POPUP_ID)
b.setAttribute("aria-expanded", "false")
}) })
} }
document.body.addEventListener("click", (event) => { document.body.addEventListener("click", (event) => {
if (!event.target.classList || !event.target.classList.contains(BUTTON_CLASS)) { if (!event.target.classList || !event.target.classList.contains(BUTTON_CLASS)) {
const buttons = document.querySelectorAll(`.${BUTTON_CLASS}`)
buttons.forEach((b) => b.classList.remove("lasuite--gaufre-opened"))
hidePopup() hidePopup()
return return
} }
@@ -36,7 +44,7 @@
}) })
window.addEventListener("resize", () => { window.addEventListener("resize", () => {
const popup = document.querySelector(`#lasuite-gaufre-popup.lasuite--gaufre-opened`) const popup = document.querySelector(`#${POPUP_ID}.lasuite--gaufre-opened`)
if (!popup) { if (!popup) {
return return
} }
@@ -48,8 +56,8 @@
}) })
const appendPopup = () => { const appendPopup = () => {
if (document.querySelector(`#lasuite-gaufre-popup`)) { if (document.getElementById(POPUP_ID)) {
return Promise.resolve(document.querySelector(`#lasuite-gaufre-popup`)) return Promise.resolve(document.getElementById(POPUP_ID))
} }
const scriptTag = document.querySelector(`#lasuite-gaufre-script`) const scriptTag = document.querySelector(`#lasuite-gaufre-script`)
if (!scriptTag) { if (!scriptTag) {
@@ -59,7 +67,7 @@
return return
} }
const popup = document.createElement("div") const popup = document.createElement("div")
popup.id = "lasuite-gaufre-popup" popup.id = POPUP_ID
popup.style.cssText = "display: none !important" popup.style.cssText = "display: none !important"
const { host, protocol, searchParams, origin } = new URL(scriptTag.src) const { host, protocol, searchParams, origin } = new URL(scriptTag.src)
@@ -183,10 +191,11 @@
} }
const showPopup = (button) => { const showPopup = (button) => {
let popup = document.querySelector(`#lasuite-gaufre-popup`) let popup = document.getElementById(POPUP_ID)
const show = (el) => { const show = (el) => {
updatePopupStyle(el, button) updatePopupStyle(el, button)
el.classList.add("lasuite--gaufre-opened") el.classList.add("lasuite--gaufre-opened")
button.setAttribute("aria-expanded", "true")
lastFocusedButton = button lastFocusedButton = button
setTimeout(() => { setTimeout(() => {
el.querySelector(".js-lagaufre-keyboard-anchor").focus() el.querySelector(".js-lagaufre-keyboard-anchor").focus()
@@ -200,13 +209,16 @@
} }
const hidePopup = () => { const hidePopup = () => {
const popup = document.querySelector(`#lasuite-gaufre-popup`) const popup = document.getElementById(POPUP_ID)
if (popup) { if (popup) {
popup.style.cssText = "display: none !important" popup.style.cssText = "display: none !important"
popup.classList.remove("lasuite--gaufre-opened") popup.classList.remove("lasuite--gaufre-opened")
} }
document.querySelectorAll(`.${BUTTON_CLASS}`).forEach((b) => {
b.classList.remove("lasuite--gaufre-opened")
b.setAttribute("aria-expanded", "false")
})
if (lastFocusedButton) { if (lastFocusedButton) {
lastFocusedButton.classList.remove("lasuite--gaufre-opened")
lastFocusedButton.focus() lastFocusedButton.focus()
lastFocusedButton = null lastFocusedButton = null
} }

View File

@@ -62,6 +62,8 @@ import gaufreCssUrl from "@gouvfr-lasuite/integration/dist/css/gaufre.css?url"
type="button" type="button"
class="lasuite-gaufre-btn lasuite-gaufre-btn--vanilla js-lasuite-gaufre-btn" class="lasuite-gaufre-btn lasuite-gaufre-btn--vanilla js-lasuite-gaufre-btn"
title="Les services de La Suite numérique" title="Les services de La Suite numérique"
aria-expanded="false"
aria-controls="lasuite-gaufre-popup"
> >
Les services de La Suite numérique Les services de La Suite numérique
</button> </button>
@@ -71,6 +73,8 @@ import gaufreCssUrl from "@gouvfr-lasuite/integration/dist/css/gaufre.css?url"
type="button" type="button"
class="lasuite-gaufre-btn lasuite-gaufre-btn--vanilla js-lasuite-gaufre-btn" class="lasuite-gaufre-btn lasuite-gaufre-btn--vanilla js-lasuite-gaufre-btn"
title="Les services de La Suite numérique" title="Les services de La Suite numérique"
aria-expanded="false"
aria-controls="lasuite-gaufre-popup"
> >
Les services de La Suite numérique Les services de La Suite numérique
</button> </button>
@@ -108,6 +112,8 @@ import gaufreCssUrl from "@gouvfr-lasuite/integration/dist/css/gaufre.css?url"
type="button" type="button"
class="lasuite-gaufre-btn lasuite-gaufre-btn--vanilla js-lasuite-gaufre-btn" class="lasuite-gaufre-btn lasuite-gaufre-btn--vanilla js-lasuite-gaufre-btn"
title="Les services de La Suite numérique" title="Les services de La Suite numérique"
aria-expanded="false"
aria-controls="lasuite-gaufre-popup"
> >
Les services de La Suite numérique Les services de La Suite numérique
</button> </button>