From 6cc42636e5ee9e1e814b3304bc1a0b58c570064e Mon Sep 17 00:00:00 2001 From: Cyril Date: Tue, 23 Sep 2025 13:02:28 +0200 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8(frontend)=20convert=20to=20figure/fig?= =?UTF-8?q?caption=20structure=20if=20caption=20exists?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ensure html structure by using figure/figcaption when captions are present Signed-off-by: Cyril --- CHANGELOG.md | 3 +- .../custom-blocks/AccessibleImageBlock.tsx | 72 +++++++++++++++++-- 2 files changed, 69 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a35e49d91c..9a3310b780 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,3 @@ - All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0), @@ -21,6 +20,7 @@ and this project adheres to - ♿ add document visible in list and openable via enter key #1365 - ♿ add pdf outline property to enable bookmarks display #1368 - ♿ hide decorative icons from assistive tech with aria-hidden #1404 + - ♿ fix rgaa 1.9.1: convert to figure/figcaption structure #1426 - ♿ remove redundant aria-label to avoid over-accessibility #1420 - ♿ remove redundant aria-label on hidden icons and update tests #1432 - ♿ improve semantic structure and aria roles of leftpanel #1431 @@ -39,7 +39,6 @@ and this project adheres to - ♿(frontend) improve accessibility: - ♿improve NVDA navigation in DocShareModal #1396 - ## [3.7.0] - 2025-09-12 ### Added diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/AccessibleImageBlock.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/AccessibleImageBlock.tsx index 59b2f349be..1fc2dbe919 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/AccessibleImageBlock.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/AccessibleImageBlock.tsx @@ -10,6 +10,7 @@ import { imageRender, imageToExternalHTML, } from '@blocknote/core'; +import { t } from 'i18next'; type ImageBlockConfig = typeof imageBlockConfig; @@ -25,10 +26,73 @@ export const accessibleImageRender = ( const dom = imageRenderComputed.dom; const imgSelector = dom.querySelector('img'); - imgSelector?.setAttribute('alt', ''); - imgSelector?.setAttribute('role', 'presentation'); - imgSelector?.setAttribute('aria-hidden', 'true'); - imgSelector?.setAttribute('tabindex', '-1'); + const withCaption = + block.props.caption && dom.querySelector('.bn-file-caption'); + + const accessibleImageWithCaption = () => { + imgSelector?.setAttribute('alt', block.props.caption); + imgSelector?.removeAttribute('aria-hidden'); + imgSelector?.setAttribute('tabindex', '0'); + + // Fix RGAA 1.9.1: Convert to figure/figcaption structure if caption exists + const captionElement = dom.querySelector('.bn-file-caption'); + + if (captionElement) { + const figureElement = document.createElement('figure'); + + // Copy all attributes from the original div + figureElement.className = dom.className; + const styleAttr = dom.getAttribute('style'); + if (styleAttr) { + figureElement.setAttribute('style', styleAttr); + } + figureElement.style.setProperty('margin', '0'); + + Array.from(dom.children).forEach((child) => { + figureElement.appendChild(child.cloneNode(true)); + }); + + // Replace the

caption with

+ const figcaptionElement = document.createElement('figcaption'); + const originalCaption = figureElement.querySelector('.bn-file-caption'); + if (originalCaption) { + figcaptionElement.className = originalCaption.className; + figcaptionElement.textContent = originalCaption.textContent; + originalCaption.parentNode?.replaceChild( + figcaptionElement, + originalCaption, + ); + + // Add explicit role and aria-label for better screen reader support + figureElement.setAttribute('role', 'img'); + figureElement.setAttribute( + 'aria-label', + t(`Image: {{title}}`, { title: figcaptionElement.textContent }), + ); + } + + // Return the figure element as the new dom + return { + ...imageRenderComputed, + dom: figureElement, + }; + } + }; + + const accessibleImage = () => { + imgSelector?.setAttribute('alt', ''); + imgSelector?.setAttribute('role', 'presentation'); + imgSelector?.setAttribute('aria-hidden', 'true'); + imgSelector?.setAttribute('tabindex', '-1'); + }; + + // Set accessibility attributes for the image + const result = withCaption ? accessibleImageWithCaption() : accessibleImage(); + + // Return the result if accessibleImageWithCaption created a figure, otherwise return original + if (result) { + return result; + } return { ...imageRenderComputed,