From 99c486571de1514b150b22a549d8a44f46661a25 Mon Sep 17 00:00:00 2001 From: Cyril Date: Wed, 18 Mar 2026 15:01:22 +0100 Subject: [PATCH] =?UTF-8?q?=E2=99=BF=EF=B8=8F(frontend)=20fix=20more=20opt?= =?UTF-8?q?ions=20menu=20feedback=20for=20screen=20readers?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pin/unpin: vocal announce only. Duplicate, copy: toast only. --- CHANGELOG.md | 1 + .../__tests__/app-impress/doc-header.spec.ts | 4 +++- .../hooks/useCopyCurrentEditorToClipboard.tsx | 17 ++++++++++++++--- .../doc-management/api/useCreateFavoriteDoc.tsx | 12 ++++++++++++ .../doc-management/api/useDeleteFavoriteDoc.tsx | 12 ++++++++++++ .../docs/doc-management/api/useDuplicateDoc.tsx | 6 ++++-- .../apps/impress/src/hooks/useClipboard.tsx | 3 --- 7 files changed, 46 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e995146..92eed1cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to - 💫(frontend) fix the help button to the bottom in tree #2073 - ♿️(frontend) improve version history list accessibility #2033 +- ♿️(frontend) fix more options menu feedback for screen readers #2071 ## [v4.8.2] - 2026-03-19 diff --git a/src/frontend/apps/e2e/__tests__/app-impress/doc-header.spec.ts b/src/frontend/apps/e2e/__tests__/app-impress/doc-header.spec.ts index 98ed2731..7496792c 100644 --- a/src/frontend/apps/e2e/__tests__/app-impress/doc-header.spec.ts +++ b/src/frontend/apps/e2e/__tests__/app-impress/doc-header.spec.ts @@ -474,7 +474,9 @@ test.describe('Doc Header', () => { // Copy content to clipboard await page.getByLabel('Open the document options').click(); await getMenuItem(page, 'Copy as Markdown').click(); - await expect(page.getByText('Copied to clipboard')).toBeVisible(); + await expect( + page.getByText('Copied as Markdown to clipboard'), + ).toBeVisible(); // Test that clipboard is in Markdown format const handle = await page.evaluateHandle(() => diff --git a/src/frontend/apps/impress/src/features/docs/doc-header/hooks/useCopyCurrentEditorToClipboard.tsx b/src/frontend/apps/impress/src/features/docs/doc-header/hooks/useCopyCurrentEditorToClipboard.tsx index a7f7a08a..d0a745b7 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-header/hooks/useCopyCurrentEditorToClipboard.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-header/hooks/useCopyCurrentEditorToClipboard.tsx @@ -13,7 +13,8 @@ export const useCopyCurrentEditorToClipboard = () => { return async (asFormat: 'html' | 'markdown') => { if (!editor) { - toast(t('Editor unavailable'), VariantType.ERROR, { duration: 3000 }); + const message = t('Editor unavailable'); + toast(message, VariantType.ERROR, { duration: 3000 }); return; } @@ -23,10 +24,20 @@ export const useCopyCurrentEditorToClipboard = () => { ? await editor.blocksToHTMLLossy() : await editor.blocksToMarkdownLossy(); await navigator.clipboard.writeText(editorContentFormatted); - toast(t('Copied to clipboard'), VariantType.SUCCESS, { duration: 3000 }); + const successMessage = + asFormat === 'markdown' + ? t('Copied as Markdown to clipboard') + : t('Copied to clipboard'); + + toast(successMessage, VariantType.SUCCESS, { duration: 3000 }); } catch (error) { console.error(error); - toast(t('Failed to copy to clipboard'), VariantType.ERROR, { + const errorMessage = + asFormat === 'markdown' + ? t('Failed to copy as Markdown to clipboard') + : t('Failed to copy to clipboard'); + + toast(errorMessage, VariantType.ERROR, { duration: 3000, }); } diff --git a/src/frontend/apps/impress/src/features/docs/doc-management/api/useCreateFavoriteDoc.tsx b/src/frontend/apps/impress/src/features/docs/doc-management/api/useCreateFavoriteDoc.tsx index cd7353db..a78f0402 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-management/api/useCreateFavoriteDoc.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-management/api/useCreateFavoriteDoc.tsx @@ -1,4 +1,6 @@ +import { announce } from '@react-aria/live-announcer'; import { useMutation, useQueryClient } from '@tanstack/react-query'; +import { useTranslation } from 'react-i18next'; import { APIError, errorCauses, fetchAPI } from '@/api'; @@ -29,6 +31,8 @@ export function useCreateFavoriteDoc({ listInvalidQueries, }: CreateFavoriteDocProps) { const queryClient = useQueryClient(); + const { t } = useTranslation(); + return useMutation({ mutationFn: createFavoriteDoc, onSuccess: () => { @@ -37,7 +41,15 @@ export function useCreateFavoriteDoc({ queryKey: [queryKey], }); }); + + const message = t('Document pinned successfully!'); + announce(message, 'polite'); + onSuccess?.(); }, + onError: () => { + const message = t('Failed to pin the document.'); + announce(message, 'assertive'); + }, }); } diff --git a/src/frontend/apps/impress/src/features/docs/doc-management/api/useDeleteFavoriteDoc.tsx b/src/frontend/apps/impress/src/features/docs/doc-management/api/useDeleteFavoriteDoc.tsx index fa4dd021..b8372d51 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-management/api/useDeleteFavoriteDoc.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-management/api/useDeleteFavoriteDoc.tsx @@ -1,4 +1,6 @@ +import { announce } from '@react-aria/live-announcer'; import { useMutation, useQueryClient } from '@tanstack/react-query'; +import { useTranslation } from 'react-i18next'; import { APIError, errorCauses, fetchAPI } from '@/api'; @@ -29,6 +31,8 @@ export function useDeleteFavoriteDoc({ listInvalidQueries, }: DeleteFavoriteDocProps) { const queryClient = useQueryClient(); + const { t } = useTranslation(); + return useMutation({ mutationFn: deleteFavoriteDoc, onSuccess: () => { @@ -37,7 +41,15 @@ export function useDeleteFavoriteDoc({ queryKey: [queryKey], }); }); + + const message = t('Document unpinned successfully!'); + announce(message, 'polite'); + onSuccess?.(); }, + onError: () => { + const message = t('Failed to unpin the document.'); + announce(message, 'assertive'); + }, }); } diff --git a/src/frontend/apps/impress/src/features/docs/doc-management/api/useDuplicateDoc.tsx b/src/frontend/apps/impress/src/features/docs/doc-management/api/useDuplicateDoc.tsx index ab268a85..7085cb4d 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-management/api/useDuplicateDoc.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-management/api/useDuplicateDoc.tsx @@ -88,14 +88,16 @@ export function useDuplicateDoc(options?: DuplicateDocOptions) { queryKey: [KEY_LIST_DOC], }); - toast(t('Document duplicated successfully!'), VariantType.SUCCESS, { + const message = t('Document duplicated successfully!'); + toast(message, VariantType.SUCCESS, { duration: 3000, }); void options?.onSuccess?.(data, variables, onMutateResult, context); }, onError: (error, variables, onMutateResult, context) => { - toast(t('Failed to duplicate the document...'), VariantType.ERROR, { + const message = t('Failed to duplicate the document...'); + toast(message, VariantType.ERROR, { duration: 3000, }); diff --git a/src/frontend/apps/impress/src/hooks/useClipboard.tsx b/src/frontend/apps/impress/src/hooks/useClipboard.tsx index 9550714e..78b0f78f 100644 --- a/src/frontend/apps/impress/src/hooks/useClipboard.tsx +++ b/src/frontend/apps/impress/src/hooks/useClipboard.tsx @@ -2,7 +2,6 @@ import { VariantType, useToastProvider, } from '@gouvfr-lasuite/cunningham-react'; -import { announce } from '@react-aria/live-announcer'; import { useCallback } from 'react'; import { useTranslation } from 'react-i18next'; @@ -19,14 +18,12 @@ export const useClipboard = () => { toast(message, VariantType.SUCCESS, { duration: 3000, }); - announce(message, 'polite'); }) .catch(() => { const message = errorMessage ?? t('Failed to copy to clipboard'); toast(message, VariantType.ERROR, { duration: 3000, }); - announce(message, 'assertive'); }); }, [t, toast],