⚡️(frontend) add correct attributes to decorative and interactive icons
Add aria-hidden and aria-label to improve screen reader accessibility Signed-off-by: Cyril <c.gromoff@gmail.com>
This commit is contained in:
@@ -31,7 +31,9 @@ and this project adheres to
|
||||
- ♻️(frontend) redirect to doc after duplicate #1175
|
||||
- 🔧(project) change env.d system by using local files #1200
|
||||
- ⚡️(frontend) improve tree stability #1207
|
||||
- ⚡️(frontend) improve accessibility #1232
|
||||
- ⚡️(frontend) improve accessibility
|
||||
- #1232
|
||||
- #1255
|
||||
- 🛂(frontend) block drag n drop when not desktop #1239
|
||||
|
||||
### Fixed
|
||||
|
||||
@@ -3,7 +3,7 @@ import { expect, test } from '@playwright/test';
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('/');
|
||||
await expect(
|
||||
page.locator('header').first().locator('h2').getByText('Docs'),
|
||||
page.locator('header').first().locator('h1').getByText('Docs'),
|
||||
).toBeVisible();
|
||||
await page.goto('unknown-page404');
|
||||
});
|
||||
|
||||
@@ -22,7 +22,7 @@ test.describe('Doc Create', () => {
|
||||
);
|
||||
|
||||
const header = page.locator('header').first();
|
||||
await header.locator('h2').getByText('Docs').click();
|
||||
await header.locator('h1').getByText('Docs').click();
|
||||
|
||||
const docsGrid = page.getByTestId('docs-grid');
|
||||
await expect(docsGrid).toBeVisible();
|
||||
|
||||
@@ -429,7 +429,7 @@ test.describe('Doc Export', () => {
|
||||
await page.waitForLoadState('domcontentloaded');
|
||||
|
||||
const header = page.locator('header').first();
|
||||
await header.locator('h2').getByText('Docs').click();
|
||||
await header.locator('h1').getByText('Docs').click();
|
||||
|
||||
const randomDocFrench = randomName(
|
||||
'doc-language-export-french',
|
||||
|
||||
@@ -8,9 +8,9 @@ test.describe('Doc grid dnd', () => {
|
||||
await page.goto('/');
|
||||
const header = page.locator('header').first();
|
||||
await createDoc(page, 'Draggable doc', browserName, 1);
|
||||
await header.locator('h2').getByText('Docs').click();
|
||||
await header.locator('h1').getByText('Docs').click();
|
||||
await createDoc(page, 'Droppable doc', browserName, 1);
|
||||
await header.locator('h2').getByText('Docs').click();
|
||||
await header.locator('h1').getByText('Docs').click();
|
||||
|
||||
const response = await page.waitForResponse(
|
||||
(response) =>
|
||||
|
||||
@@ -244,9 +244,7 @@ test.describe('Document create member: Multiple login', () => {
|
||||
|
||||
await keyCloakSignIn(page, otherBrowser!);
|
||||
|
||||
await expect(
|
||||
page.getByRole('link', { name: 'Docs Logo Docs' }),
|
||||
).toBeVisible();
|
||||
await expect(page.getByTestId('header-logo-link')).toBeVisible();
|
||||
|
||||
await page.goto(urlDoc);
|
||||
|
||||
@@ -271,9 +269,7 @@ test.describe('Document create member: Multiple login', () => {
|
||||
await page.goto('/');
|
||||
await keyCloakSignIn(page, browserName);
|
||||
|
||||
await expect(
|
||||
page.getByRole('link', { name: 'Docs Logo Docs' }),
|
||||
).toBeVisible({
|
||||
await expect(page.getByTestId('header-logo-link')).toBeVisible({
|
||||
timeout: 10000,
|
||||
});
|
||||
|
||||
@@ -334,9 +330,7 @@ test.describe('Document create member: Multiple login', () => {
|
||||
|
||||
await keyCloakSignIn(page, otherBrowser!);
|
||||
|
||||
await expect(
|
||||
page.getByRole('link', { name: 'Docs Logo Docs' }),
|
||||
).toBeVisible({
|
||||
await expect(page.getByTestId('header-logo-link')).toBeVisible({
|
||||
timeout: 10000,
|
||||
});
|
||||
|
||||
|
||||
@@ -122,9 +122,7 @@ test.describe('Doc Visibility: Restricted', () => {
|
||||
|
||||
await keyCloakSignIn(page, otherBrowser!);
|
||||
|
||||
await expect(
|
||||
page.getByRole('link', { name: 'Docs Logo Docs' }),
|
||||
).toBeVisible({
|
||||
await expect(page.getByTestId('header-logo-link')).toBeVisible({
|
||||
timeout: 10000,
|
||||
});
|
||||
|
||||
@@ -178,9 +176,7 @@ test.describe('Doc Visibility: Restricted', () => {
|
||||
|
||||
await keyCloakSignIn(page, otherBrowser!);
|
||||
|
||||
await expect(
|
||||
page.getByRole('link', { name: 'Docs Logo Docs' }),
|
||||
).toBeVisible();
|
||||
await expect(page.getByTestId('header-logo-link')).toBeVisible();
|
||||
|
||||
await page.goto(urlDoc);
|
||||
|
||||
@@ -455,9 +451,7 @@ test.describe('Doc Visibility: Authenticated', () => {
|
||||
const otherBrowser = BROWSERS.find((b) => b !== browserName);
|
||||
await keyCloakSignIn(page, otherBrowser!);
|
||||
|
||||
await expect(
|
||||
page.getByRole('link', { name: 'Docs Logo Docs' }),
|
||||
).toBeVisible({
|
||||
await expect(page.getByTestId('header-logo-link')).toBeVisible({
|
||||
timeout: 10000,
|
||||
});
|
||||
|
||||
@@ -545,9 +539,7 @@ test.describe('Doc Visibility: Authenticated', () => {
|
||||
const otherBrowser = BROWSERS.find((b) => b !== browserName);
|
||||
await keyCloakSignIn(page, otherBrowser!);
|
||||
|
||||
await expect(
|
||||
page.getByRole('link', { name: 'Docs Logo Docs' }),
|
||||
).toBeVisible();
|
||||
await expect(page.getByTestId('header-logo-link')).toBeVisible();
|
||||
|
||||
await page.goto(urlDoc);
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ test.describe('Header', () => {
|
||||
|
||||
const header = page.locator('header').first();
|
||||
|
||||
await expect(header.getByLabel('Docs Logo')).toBeVisible();
|
||||
await expect(header.getByLabel('Back to homepage')).toBeVisible();
|
||||
await expect(header.locator('h2').getByText('Docs')).toHaveCSS(
|
||||
'font-family',
|
||||
/Roboto/i,
|
||||
|
||||
@@ -154,7 +154,7 @@ export const goToGridDoc = async (
|
||||
{ nthRow = 1, title }: GoToGridDocOptions = {},
|
||||
) => {
|
||||
const header = page.locator('header').first();
|
||||
await header.locator('h2').getByText('Docs').click();
|
||||
await header.locator('h1').getByText('Docs').click();
|
||||
|
||||
const docsGrid = page.getByTestId('docs-grid');
|
||||
await expect(docsGrid).toBeVisible();
|
||||
|
||||
@@ -39,7 +39,7 @@ export const Header = () => {
|
||||
className="--docs--header"
|
||||
>
|
||||
{!isDesktop && <ButtonTogglePanel />}
|
||||
<StyledLink href="/">
|
||||
<StyledLink href="/" data-testid="header-logo-link">
|
||||
<Box
|
||||
$align="center"
|
||||
$gap={spacingsTokens['3xs']}
|
||||
@@ -49,11 +49,11 @@ export const Header = () => {
|
||||
$margin={{ top: 'auto' }}
|
||||
>
|
||||
<IconDocs
|
||||
aria-label={t('Docs Logo')}
|
||||
aria-label={t('Back to homepage')}
|
||||
width={32}
|
||||
color={colorsTokens['primary-text']}
|
||||
/>
|
||||
<Title />
|
||||
<Title headingLevel="h1" />
|
||||
</Box>
|
||||
</StyledLink>
|
||||
{!isDesktop ? (
|
||||
|
||||
@@ -3,7 +3,11 @@ import { useTranslation } from 'react-i18next';
|
||||
import { Box, Text } from '@/components/';
|
||||
import { useCunninghamTheme } from '@/cunningham';
|
||||
|
||||
export const Title = () => {
|
||||
type TitleSemanticsProps = {
|
||||
headingLevel?: 'h1' | 'h2' | 'h3';
|
||||
};
|
||||
|
||||
export const Title = ({ headingLevel = 'h2' }: TitleSemanticsProps) => {
|
||||
const { t } = useTranslation();
|
||||
const { spacingsTokens, colorsTokens } = useCunninghamTheme();
|
||||
|
||||
@@ -16,7 +20,7 @@ export const Title = () => {
|
||||
>
|
||||
<Text
|
||||
$margin="none"
|
||||
as="h2"
|
||||
as={headingLevel}
|
||||
$color={colorsTokens['primary-text']}
|
||||
$zIndex={1}
|
||||
$size="1.375rem"
|
||||
|
||||
@@ -48,7 +48,7 @@ export default function HomeBanner() {
|
||||
$gap={spacingsTokens['sm']}
|
||||
>
|
||||
<IconDocs
|
||||
aria-label={t('Docs Logo')}
|
||||
aria-label={t('Back to homepage')}
|
||||
width={64}
|
||||
color={colorsTokens['primary-text']}
|
||||
/>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Button } from '@openfun/cunningham-react';
|
||||
import { t } from 'i18next';
|
||||
import { useRouter } from 'next/router';
|
||||
import { PropsWithChildren, useCallback, useState } from 'react';
|
||||
|
||||
@@ -54,10 +55,16 @@ export const LeftPanelHeader = ({ children }: PropsWithChildren) => {
|
||||
<Box $direction="row" $gap="2px">
|
||||
<Button
|
||||
onClick={goToHome}
|
||||
aria-label={t('Back to homepage')}
|
||||
size="medium"
|
||||
color="tertiary-text"
|
||||
icon={
|
||||
<Icon $variation="800" $theme="primary" iconName="house" />
|
||||
<Icon
|
||||
$variation="800"
|
||||
$theme="primary"
|
||||
iconName="house"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
}
|
||||
/>
|
||||
{authenticated && (
|
||||
@@ -65,8 +72,14 @@ export const LeftPanelHeader = ({ children }: PropsWithChildren) => {
|
||||
onClick={openSearchModal}
|
||||
size="medium"
|
||||
color="tertiary-text"
|
||||
aria-label={t('Search docs')}
|
||||
icon={
|
||||
<Icon $variation="800" $theme="primary" iconName="search" />
|
||||
<Icon
|
||||
$variation="800"
|
||||
$theme="primary"
|
||||
iconName="search"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -21,7 +21,7 @@ export const LeftPanelHeaderButton = () => {
|
||||
<Button
|
||||
color="primary"
|
||||
onClick={() => createDoc()}
|
||||
icon={<Icon $variation="000" iconName="add" />}
|
||||
icon={<Icon $variation="000" iconName="add" aria-hidden="true" />}
|
||||
disabled={isDocCreating}
|
||||
>
|
||||
{t('New doc')}
|
||||
|
||||
@@ -207,6 +207,7 @@
|
||||
"Doc visibility card": "Dokumenten-Sichtbarkeitskarte",
|
||||
"Docs": "Docs",
|
||||
"Docs Logo": "Docs-Logo",
|
||||
"Back to homepage": "Zurück zur Startseite",
|
||||
"Docs is already available, log in to use it now.": "Docs ist bereits verfügbar. Melden Sie sich an, um es jetzt zu nutzen.",
|
||||
"Docs makes real-time collaboration simple. Invite collaborators - public officials or external partners - with one click to see their changes live, while maintaining precise access control for data security.": "Docs macht die Zusammenarbeit in Echtzeit einfach. Laden Sie Mitarbeiter — Beamte oder externe Partner — mit einem Klick ein, um ihre Änderungen live zu sehen und dabei die genaue Zugangskontrolle zwecks Datensicherheit beizubehalten.",
|
||||
"Docs offers an intuitive writing experience. Its minimalist interface favors content over layout, while offering the essentials: media import, offline mode and keyboard shortcuts for greater efficiency.": "Docs bietet ein intuitives Schreiberlebnis. Seine minimalistische Oberfläche bevorzugt Inhalte über Layout, bietet aber das Wesentliche: Medien-Import, Offline-Modus und Tastaturkürzel für mehr Effizienz.",
|
||||
@@ -306,6 +307,7 @@
|
||||
"Reset": "Zurücksetzen",
|
||||
"Restore": "Wiederherstellen",
|
||||
"Search": "Suchen",
|
||||
"Search docs": "Dokumente durchsuchen",
|
||||
"Search modal": "Suche Modal",
|
||||
"Search user result": "Suchergebnis",
|
||||
"Select a document": "Dokument auswählen",
|
||||
@@ -364,6 +366,7 @@
|
||||
},
|
||||
"en": {
|
||||
"translation": {
|
||||
"Search docs": "Search docs",
|
||||
"Share with {{count}} users_one": "Share with {{count}} user",
|
||||
"Shared with {{count}} users_many": "Shared with {{count}} users",
|
||||
"Shared with {{count}} users_one": "Shared with {{count}} user",
|
||||
@@ -418,6 +421,7 @@
|
||||
"Doc visibility card": "Accesos al documento",
|
||||
"Docs": "Docs",
|
||||
"Docs Logo": "Logo de Docs",
|
||||
"Back to homepage": "Volver a la página de inicio",
|
||||
"Docs is already available, log in to use it now.": "Docs ya está disponible, inicia sesión para empezar a usarlo.",
|
||||
"Docs makes real-time collaboration simple. Invite collaborators - public officials or external partners - with one click to see their changes live, while maintaining precise access control for data security.": "Docs simplifica la colaboración en tiempo real. Invitar colaboradores - funcionarios públicos o socios externos - con un solo clic para ver sus cambios en vivo, manteniendo un control de acceso preciso para la seguridad de los datos.",
|
||||
"Docs offers an intuitive writing experience. Its minimalist interface favors content over layout, while offering the essentials: media import, offline mode and keyboard shortcuts for greater efficiency.": "Docs ofrece una experiencia de escritura intuitiva. Su interfaz minimalista favorece el contenido sobre el diseño, mientras ofrece lo esencial: importación de medios, modo sin conexión y atajos de teclado para una mayor eficiencia.",
|
||||
@@ -502,6 +506,7 @@
|
||||
"Request access": "Solicitar acceso",
|
||||
"Restore": "Recuperar",
|
||||
"Search": "Buscar",
|
||||
"Search docs": "Buscar documentos",
|
||||
"Search modal": "Modal de búsqueda",
|
||||
"Search user result": "Resultado de la búsqueda de usuarios",
|
||||
"Select a document": "Selecciona un documento",
|
||||
@@ -608,6 +613,7 @@
|
||||
"Doc visibility card": "Carte de visibilité du doc",
|
||||
"Docs": "Docs",
|
||||
"Docs Logo": "Logo Docs",
|
||||
"Back to homepage": "Retour page d'accueil",
|
||||
"Docs is already available, log in to use it now.": "Docs est déjà disponible, connectez-vous pour l’utiliser dès maintenant.",
|
||||
"Docs makes real-time collaboration simple. Invite collaborators - public officials or external partners - with one click to see their changes live, while maintaining precise access control for data security.": "Docs simplifie la collaboration en temps réel. Invitez des collaborateurs - agents publics ou partenaires externes - d'un clic pour voir leurs modifications en direct, tout en gardant un contrôle précis des accès pour la sécurité des données.",
|
||||
"Docs offers an intuitive writing experience. Its minimalist interface favors content over layout, while offering the essentials: media import, offline mode and keyboard shortcuts for greater efficiency.": "Docs propose une expérience d'écriture intuitive. Son interface minimaliste privilégie le contenu sur la mise en page, tout en offrant l'essentiel : import de médias, mode hors-ligne et raccourcis clavier pour plus d'efficacité.",
|
||||
@@ -719,6 +725,7 @@
|
||||
"Restore": "Restaurer",
|
||||
"Search": "Rechercher",
|
||||
"Search by title": "Recherchez par titre",
|
||||
"Search docs": "Rechercher un document",
|
||||
"Search modal": "Modale de partage",
|
||||
"Search user result": "Résultat de la recherche utilisateur",
|
||||
"Select a doc": "Sélectionnez un doc",
|
||||
@@ -1055,6 +1062,7 @@
|
||||
"Rephrase": "Herschrijf",
|
||||
"Restore": "Herstel",
|
||||
"Search": "Zoeken",
|
||||
"Search docs": "Documenten zoeken",
|
||||
"Search modal": "Zoek modal",
|
||||
"Search user result": "Zoek resultaat",
|
||||
"Select a document": "Selecteer een document",
|
||||
|
||||
Reference in New Issue
Block a user