From d0c9de9d9679227be7be075a5a57fbb617d8aa55 Mon Sep 17 00:00:00 2001 From: Cyril Date: Mon, 11 Aug 2025 12:22:04 +0200 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8(frontend)=20set=20empty=20alt=20for?= =?UTF-8?q?=20decorative=20images=20in=20blocknote=20editor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ensure decorative images have empty alt to comply with RGAA 1.2 accessibility Signed-off-by: Cyril --- CHANGELOG.md | 2 +- .../e2e/__tests__/app-impress/config.spec.ts | 4 +- .../__tests__/app-impress/doc-editor.spec.ts | 9 +++- .../__tests__/app-impress/doc-export.spec.ts | 8 +++- .../doc-editor/components/BlockNoteEditor.tsx | 7 ++- .../custom-blocks/AccessibleImageBlock.tsx | 43 +++++++++++++++++++ .../components/custom-blocks/index.ts | 1 + 7 files changed, 68 insertions(+), 6 deletions(-) create mode 100644 src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/AccessibleImageBlock.tsx diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ebdfd40..b7fc30cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,13 +18,13 @@ and this project adheres to - #1262 - #1244 - #1270 + - #1282 ### Fixed - 🐛(makefile) Windows compatibility fix for Docker volume mounting #1264 - 🐛(minio) fix user permission error with Minio and Windows #1264 - ## [3.5.0] - 2025-07-31 ### Added diff --git a/src/frontend/apps/e2e/__tests__/app-impress/config.spec.ts b/src/frontend/apps/e2e/__tests__/app-impress/config.spec.ts index 250f2d6f..c7186139 100644 --- a/src/frontend/apps/e2e/__tests__/app-impress/config.spec.ts +++ b/src/frontend/apps/e2e/__tests__/app-impress/config.spec.ts @@ -43,7 +43,9 @@ test.describe('Config', () => { path.join(__dirname, 'assets/logo-suite-numerique.png'), ); - const image = page.getByRole('img', { name: 'logo-suite-numerique.png' }); + const image = page + .locator('.--docs--editor-container img.bn-visual-media') + .first(); await expect(image).toBeVisible(); diff --git a/src/frontend/apps/e2e/__tests__/app-impress/doc-editor.spec.ts b/src/frontend/apps/e2e/__tests__/app-impress/doc-editor.spec.ts index 15dec9b7..3cc3fee6 100644 --- a/src/frontend/apps/e2e/__tests__/app-impress/doc-editor.spec.ts +++ b/src/frontend/apps/e2e/__tests__/app-impress/doc-editor.spec.ts @@ -272,7 +272,9 @@ test.describe('Doc Editor', () => { path.join(__dirname, 'assets/logo-suite-numerique.png'), ); - const image = page.getByRole('img', { name: 'logo-suite-numerique.png' }); + const image = page + .locator('.--docs--editor-container img.bn-visual-media') + .first(); await expect(image).toBeVisible(); @@ -284,6 +286,11 @@ test.describe('Doc Editor', () => { expect(await image.getAttribute('src')).toMatch( /http:\/\/localhost:8083\/media\/.*\/attachments\/.*.png/, ); + + await expect(image).toHaveAttribute('role', 'presentation'); + await expect(image).toHaveAttribute('alt', ''); + await expect(image).toHaveAttribute('tabindex', '-1'); + await expect(image).toHaveAttribute('aria-hidden', 'true'); }); test('it checks the AI buttons', async ({ page, browserName }) => { diff --git a/src/frontend/apps/e2e/__tests__/app-impress/doc-export.spec.ts b/src/frontend/apps/e2e/__tests__/app-impress/doc-export.spec.ts index 1e14c42e..610fdbf3 100644 --- a/src/frontend/apps/e2e/__tests__/app-impress/doc-export.spec.ts +++ b/src/frontend/apps/e2e/__tests__/app-impress/doc-export.spec.ts @@ -122,7 +122,9 @@ test.describe('Doc Export', () => { const fileChooser = await fileChooserPromise; await fileChooser.setFiles(path.join(__dirname, 'assets/test.svg')); - const image = page.getByRole('img', { name: 'test.svg' }); + const image = page + .locator('.--docs--editor-container img.bn-visual-media') + .first(); await expect(image).toBeVisible(); @@ -182,7 +184,9 @@ test.describe('Doc Export', () => { const fileChooser = await fileChooserPromise; await fileChooser.setFiles(path.join(__dirname, 'assets/test.svg')); - const image = page.getByRole('img', { name: 'test.svg' }); + const image = page + .locator('.--docs--editor-container img.bn-visual-media') + .first(); await expect(image).toBeVisible(); diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteEditor.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteEditor.tsx index 6b7b77a4..04238f48 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteEditor.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteEditor.tsx @@ -33,7 +33,11 @@ import { randomColor } from '../utils'; import { BlockNoteSuggestionMenu } from './BlockNoteSuggestionMenu'; import { BlockNoteToolbar } from './BlockNoteToolBar/BlockNoteToolbar'; -import { CalloutBlock, DividerBlock } from './custom-blocks'; +import { + AccessibleImageBlock, + CalloutBlock, + DividerBlock, +} from './custom-blocks'; import { InterlinkingLinkInlineContent, InterlinkingSearchInlineContent, @@ -50,6 +54,7 @@ const baseBlockNoteSchema = withPageBreak( ...defaultBlockSpecs, callout: CalloutBlock, divider: DividerBlock, + image: AccessibleImageBlock, }, inlineContentSpecs: { ...defaultInlineContentSpecs, 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 new file mode 100644 index 00000000..59b2f349 --- /dev/null +++ b/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/AccessibleImageBlock.tsx @@ -0,0 +1,43 @@ +import { + BlockFromConfig, + BlockNoteEditor, + BlockSchemaWithBlock, + InlineContentSchema, + StyleSchema, + createBlockSpec, + imageBlockConfig, + imageParse, + imageRender, + imageToExternalHTML, +} from '@blocknote/core'; + +type ImageBlockConfig = typeof imageBlockConfig; + +export const accessibleImageRender = ( + block: BlockFromConfig, + editor: BlockNoteEditor< + BlockSchemaWithBlock, + InlineContentSchema, + StyleSchema + >, +) => { + const imageRenderComputed = imageRender(block, editor); + 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'); + + return { + ...imageRenderComputed, + dom, + }; +}; + +export const AccessibleImageBlock = createBlockSpec(imageBlockConfig, { + render: accessibleImageRender, + parse: imageParse, + toExternalHTML: imageToExternalHTML, +}); diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/index.ts b/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/index.ts index 34a8c459..99c1ee27 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/index.ts +++ b/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/index.ts @@ -1,2 +1,3 @@ +export * from './AccessibleImageBlock'; export * from './CalloutBlock'; export * from './DividerBlock';