✨(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
|
- #1262
|
||||||
- #1244
|
- #1244
|
||||||
- #1270
|
- #1270
|
||||||
|
- #1282
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- 🐛(makefile) Windows compatibility fix for Docker volume mounting #1264
|
- 🐛(makefile) Windows compatibility fix for Docker volume mounting #1264
|
||||||
- 🐛(minio) fix user permission error with Minio and Windows #1264
|
- 🐛(minio) fix user permission error with Minio and Windows #1264
|
||||||
|
|
||||||
|
|
||||||
## [3.5.0] - 2025-07-31
|
## [3.5.0] - 2025-07-31
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
@@ -43,7 +43,9 @@ test.describe('Config', () => {
|
|||||||
path.join(__dirname, 'assets/logo-suite-numerique.png'),
|
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();
|
await expect(image).toBeVisible();
|
||||||
|
|
||||||
|
|||||||
@@ -272,7 +272,9 @@ test.describe('Doc Editor', () => {
|
|||||||
path.join(__dirname, 'assets/logo-suite-numerique.png'),
|
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();
|
await expect(image).toBeVisible();
|
||||||
|
|
||||||
@@ -284,6 +286,11 @@ test.describe('Doc Editor', () => {
|
|||||||
expect(await image.getAttribute('src')).toMatch(
|
expect(await image.getAttribute('src')).toMatch(
|
||||||
/http:\/\/localhost:8083\/media\/.*\/attachments\/.*.png/,
|
/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 }) => {
|
test('it checks the AI buttons', async ({ page, browserName }) => {
|
||||||
|
|||||||
@@ -122,7 +122,9 @@ test.describe('Doc Export', () => {
|
|||||||
const fileChooser = await fileChooserPromise;
|
const fileChooser = await fileChooserPromise;
|
||||||
await fileChooser.setFiles(path.join(__dirname, 'assets/test.svg'));
|
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();
|
await expect(image).toBeVisible();
|
||||||
|
|
||||||
@@ -182,7 +184,9 @@ test.describe('Doc Export', () => {
|
|||||||
const fileChooser = await fileChooserPromise;
|
const fileChooser = await fileChooserPromise;
|
||||||
await fileChooser.setFiles(path.join(__dirname, 'assets/test.svg'));
|
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();
|
await expect(image).toBeVisible();
|
||||||
|
|
||||||
|
|||||||
@@ -33,7 +33,11 @@ import { randomColor } from '../utils';
|
|||||||
|
|
||||||
import { BlockNoteSuggestionMenu } from './BlockNoteSuggestionMenu';
|
import { BlockNoteSuggestionMenu } from './BlockNoteSuggestionMenu';
|
||||||
import { BlockNoteToolbar } from './BlockNoteToolBar/BlockNoteToolbar';
|
import { BlockNoteToolbar } from './BlockNoteToolBar/BlockNoteToolbar';
|
||||||
import { CalloutBlock, DividerBlock } from './custom-blocks';
|
import {
|
||||||
|
AccessibleImageBlock,
|
||||||
|
CalloutBlock,
|
||||||
|
DividerBlock,
|
||||||
|
} from './custom-blocks';
|
||||||
import {
|
import {
|
||||||
InterlinkingLinkInlineContent,
|
InterlinkingLinkInlineContent,
|
||||||
InterlinkingSearchInlineContent,
|
InterlinkingSearchInlineContent,
|
||||||
@@ -50,6 +54,7 @@ const baseBlockNoteSchema = withPageBreak(
|
|||||||
...defaultBlockSpecs,
|
...defaultBlockSpecs,
|
||||||
callout: CalloutBlock,
|
callout: CalloutBlock,
|
||||||
divider: DividerBlock,
|
divider: DividerBlock,
|
||||||
|
image: AccessibleImageBlock,
|
||||||
},
|
},
|
||||||
inlineContentSpecs: {
|
inlineContentSpecs: {
|
||||||
...defaultInlineContentSpecs,
|
...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 './CalloutBlock';
|
||||||
export * from './DividerBlock';
|
export * from './DividerBlock';
|
||||||
|
|||||||
Reference in New Issue
Block a user