mirror of
https://github.com/suitenumerique/docs.git
synced 2026-10-19 14:31:49 +02:00
♿️(frontend) improve the accessibility of the upload loader block
The loader used Card, adding an unnamed region per upload, and neither the analysis start nor the antivirus warning was announced. Use Box again, announce both through announce(), hide the decorative warning icon and keep upload error toasts 10s so the cause can be read.
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
aria-hidden="true"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
|
||||
|
Before Width: | Height: | Size: 425 B After Width: | Height: | Size: 446 B |
+12
-6
@@ -6,10 +6,11 @@ import {
|
||||
StyleSchema,
|
||||
} from '@blocknote/core';
|
||||
import { createReactBlockSpec } from '@blocknote/react';
|
||||
import { announce } from '@react-aria/live-announcer';
|
||||
import { t } from 'i18next';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
import { Card, Icon, Text } from '@/components';
|
||||
import { Box, Icon, Text } from '@/components';
|
||||
import { useMediaUrl } from '@/core';
|
||||
import { isSafeUrl } from '@/utils/url';
|
||||
|
||||
@@ -114,16 +115,19 @@ const UploadLoaderBlockComponent = ({
|
||||
|
||||
console.error('Error analyzing file:', error);
|
||||
|
||||
const information = t(
|
||||
'The antivirus has detected an anomaly in your file.',
|
||||
);
|
||||
|
||||
try {
|
||||
editor.updateBlock(block.id, {
|
||||
type: 'uploadLoader',
|
||||
props: {
|
||||
type: 'warning',
|
||||
information: t(
|
||||
'The antivirus has detected an anomaly in your file.',
|
||||
),
|
||||
information,
|
||||
},
|
||||
});
|
||||
announce(information, 'assertive');
|
||||
} catch {
|
||||
/* During collaboration, another user might have updated the block */
|
||||
}
|
||||
@@ -135,14 +139,16 @@ const UploadLoaderBlockComponent = ({
|
||||
}, [block, editor, mediaUrl, isEditable]);
|
||||
|
||||
return (
|
||||
<Card
|
||||
<Box
|
||||
className="bn-visual-media-wrapper"
|
||||
$direction="row"
|
||||
$gap="xs"
|
||||
$withThemeBG
|
||||
$border="none"
|
||||
$radius="var(--c--globals--spacings--st)"
|
||||
$theme="neutral"
|
||||
$scope="semantic"
|
||||
$variation="tertiary"
|
||||
$width="100%"
|
||||
$padding="xs"
|
||||
>
|
||||
@@ -158,7 +164,7 @@ const UploadLoaderBlockComponent = ({
|
||||
<Text $variation="tertiary" $weight={500}>
|
||||
{block.props.information}
|
||||
</Text>
|
||||
</Card>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
+2
@@ -69,6 +69,7 @@ describe('useUploadFile', () => {
|
||||
expect(mockToast).toHaveBeenCalledWith(
|
||||
'The file "video.mp4" is too large. Maximum file size is 1KB.',
|
||||
'error',
|
||||
{ duration: 10000 },
|
||||
);
|
||||
});
|
||||
|
||||
@@ -89,6 +90,7 @@ describe('useUploadFile', () => {
|
||||
expect(mockToast).toHaveBeenCalledWith(
|
||||
'A problem occurred while uploading the file, cause: File type not allowed.',
|
||||
'error',
|
||||
{ duration: 10000 },
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Block } from '@blocknote/core';
|
||||
import { VariantType } from '@gouvfr-lasuite/ui-components';
|
||||
import { announce } from '@react-aria/live-announcer';
|
||||
import { captureException } from '@sentry/nextjs';
|
||||
import { useCallback, useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@@ -16,6 +17,9 @@ import { DocsBlockNoteEditor } from '../types';
|
||||
|
||||
const DEFAULT_MAX_FILE_SIZE = 10 * 1024 * 1024; // Default to 10MB
|
||||
|
||||
// Upload errors carry a cause the user has to read: keep them longer than the default toast.
|
||||
const UPLOAD_ERROR_TOAST_DURATION = 10000;
|
||||
|
||||
const TEXT_ALIGNMENTS = ['left', 'center', 'right', 'justify'] as const;
|
||||
|
||||
type TextAlignment = (typeof TEXT_ALIGNMENTS)[number];
|
||||
@@ -63,6 +67,7 @@ export const useUploadFile = (docId: string) => {
|
||||
},
|
||||
),
|
||||
VariantType.ERROR,
|
||||
{ duration: UPLOAD_ERROR_TOAST_DURATION },
|
||||
);
|
||||
|
||||
throw new Error('File is too large');
|
||||
@@ -88,6 +93,7 @@ export const useUploadFile = (docId: string) => {
|
||||
'A problem occurred while uploading the file, please try again.',
|
||||
),
|
||||
VariantType.ERROR,
|
||||
{ duration: UPLOAD_ERROR_TOAST_DURATION },
|
||||
);
|
||||
|
||||
throw error;
|
||||
@@ -114,7 +120,7 @@ export const useUploadStatus = (editor: DocsBlockNoteEditor) => {
|
||||
* Replace the resource block by a uploadLoader block to show analyzing status
|
||||
*/
|
||||
const replaceBlockWithUploadLoader = useCallback(
|
||||
(block: Block) => {
|
||||
(block: Block, shouldAnnounce = false) => {
|
||||
if (
|
||||
!block ||
|
||||
!('url' in block.props) ||
|
||||
@@ -133,13 +139,15 @@ export const useUploadStatus = (editor: DocsBlockNoteEditor) => {
|
||||
const blockUploadTextAlignment = fileTextAlignment(block.props);
|
||||
|
||||
try {
|
||||
const information = t('Analyzing the file');
|
||||
|
||||
editor.replaceBlocks(
|
||||
[block.id],
|
||||
[
|
||||
{
|
||||
type: 'uploadLoader',
|
||||
props: {
|
||||
information: t('Analyzing the file'),
|
||||
information,
|
||||
type: 'loading',
|
||||
blockUploadName,
|
||||
blockUploadType,
|
||||
@@ -151,6 +159,12 @@ export const useUploadStatus = (editor: DocsBlockNoteEditor) => {
|
||||
},
|
||||
],
|
||||
);
|
||||
|
||||
// Only the user who uploaded the file hears it, not the collaborators
|
||||
// nor a page reload.
|
||||
if (shouldAnnounce) {
|
||||
announce(information, 'polite');
|
||||
}
|
||||
} catch (error) {
|
||||
captureException(error, {
|
||||
extra: { info: 'Error replacing block for upload loader' },
|
||||
@@ -194,7 +208,7 @@ export const useUploadStatus = (editor: DocsBlockNoteEditor) => {
|
||||
const innerTimeoutId = setTimeout(() => {
|
||||
const block = editor.getBlock({ id: blockId });
|
||||
|
||||
replaceBlockWithUploadLoader(block as Block);
|
||||
replaceBlockWithUploadLoader(block as Block, true);
|
||||
}, 300);
|
||||
|
||||
return () => {
|
||||
|
||||
Reference in New Issue
Block a user