️(frontend) fix more options menu feedback for screen readers

Pin/unpin: vocal announce only. Duplicate, copy: toast only.
This commit is contained in:
Cyril
2026-03-19 18:34:24 +01:00
parent cdf3161869
commit 99c486571d
7 changed files with 46 additions and 9 deletions
+1
View File
@@ -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
@@ -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(() =>
@@ -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,
});
}
@@ -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<void, APIError, CreateFavoriteDocParams>({
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');
},
});
}
@@ -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<void, APIError, DeleteFavoriteDocParams>({
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');
},
});
}
@@ -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,
});
@@ -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],