✨(frontend) set empty alt for decorative images in blocknote editor
ensure decorative images have empty alt to comply with RGAA 1.2 accessibility Signed-off-by: Cyril <c.gromoff@gmail.com>
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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 }) => {
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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<ImageBlockConfig, InlineContentSchema, StyleSchema>,
|
||||
editor: BlockNoteEditor<
|
||||
BlockSchemaWithBlock<ImageBlockConfig['type'], ImageBlockConfig>,
|
||||
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,
|
||||
});
|
||||
@@ -1,2 +1,3 @@
|
||||
export * from './AccessibleImageBlock';
|
||||
export * from './CalloutBlock';
|
||||
export * from './DividerBlock';
|
||||
|
||||
Reference in New Issue
Block a user