diff --git a/CHANGELOG.md b/CHANGELOG.md index 72471c30..dba3b3c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/src/frontend/apps/e2e/__tests__/app-impress/404.spec.ts b/src/frontend/apps/e2e/__tests__/app-impress/404.spec.ts index e55acfe5..79b4be2a 100644 --- a/src/frontend/apps/e2e/__tests__/app-impress/404.spec.ts +++ b/src/frontend/apps/e2e/__tests__/app-impress/404.spec.ts @@ -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'); }); diff --git a/src/frontend/apps/e2e/__tests__/app-impress/doc-create.spec.ts b/src/frontend/apps/e2e/__tests__/app-impress/doc-create.spec.ts index f05e1f24..43a855cf 100644 --- a/src/frontend/apps/e2e/__tests__/app-impress/doc-create.spec.ts +++ b/src/frontend/apps/e2e/__tests__/app-impress/doc-create.spec.ts @@ -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(); diff --git a/src/frontend/apps/e2e/__tests__/app-impress/doc-export.spec.ts b/src/frontend/apps/e2e/__tests__/app-impress/doc-export.spec.ts index c081da74..41ef9dca 100644 --- a/src/frontend/apps/e2e/__tests__/app-impress/doc-export.spec.ts +++ b/src/frontend/apps/e2e/__tests__/app-impress/doc-export.spec.ts @@ -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', diff --git a/src/frontend/apps/e2e/__tests__/app-impress/doc-grid-dnd.spec.ts b/src/frontend/apps/e2e/__tests__/app-impress/doc-grid-dnd.spec.ts index 68ed84d9..7298c5ee 100644 --- a/src/frontend/apps/e2e/__tests__/app-impress/doc-grid-dnd.spec.ts +++ b/src/frontend/apps/e2e/__tests__/app-impress/doc-grid-dnd.spec.ts @@ -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) => diff --git a/src/frontend/apps/e2e/__tests__/app-impress/doc-member-create.spec.ts b/src/frontend/apps/e2e/__tests__/app-impress/doc-member-create.spec.ts index 9922f288..4d69f772 100644 --- a/src/frontend/apps/e2e/__tests__/app-impress/doc-member-create.spec.ts +++ b/src/frontend/apps/e2e/__tests__/app-impress/doc-member-create.spec.ts @@ -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, }); diff --git a/src/frontend/apps/e2e/__tests__/app-impress/doc-visibility.spec.ts b/src/frontend/apps/e2e/__tests__/app-impress/doc-visibility.spec.ts index 8ec86fc0..fb275d39 100644 --- a/src/frontend/apps/e2e/__tests__/app-impress/doc-visibility.spec.ts +++ b/src/frontend/apps/e2e/__tests__/app-impress/doc-visibility.spec.ts @@ -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); diff --git a/src/frontend/apps/e2e/__tests__/app-impress/header.spec.ts b/src/frontend/apps/e2e/__tests__/app-impress/header.spec.ts index f5ce439a..9ad56269 100644 --- a/src/frontend/apps/e2e/__tests__/app-impress/header.spec.ts +++ b/src/frontend/apps/e2e/__tests__/app-impress/header.spec.ts @@ -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, diff --git a/src/frontend/apps/e2e/__tests__/app-impress/utils-common.ts b/src/frontend/apps/e2e/__tests__/app-impress/utils-common.ts index 79d46f55..09dac792 100644 --- a/src/frontend/apps/e2e/__tests__/app-impress/utils-common.ts +++ b/src/frontend/apps/e2e/__tests__/app-impress/utils-common.ts @@ -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(); diff --git a/src/frontend/apps/impress/src/features/header/components/Header.tsx b/src/frontend/apps/impress/src/features/header/components/Header.tsx index 39f3ce63..096ea6df 100644 --- a/src/frontend/apps/impress/src/features/header/components/Header.tsx +++ b/src/frontend/apps/impress/src/features/header/components/Header.tsx @@ -39,7 +39,7 @@ export const Header = () => { className="--docs--header" > {!isDesktop && } - + { $margin={{ top: 'auto' }} > - + <Title headingLevel="h1" /> </Box> </StyledLink> {!isDesktop ? ( diff --git a/src/frontend/apps/impress/src/features/header/components/Title.tsx b/src/frontend/apps/impress/src/features/header/components/Title.tsx index 4d294def..451b8220 100644 --- a/src/frontend/apps/impress/src/features/header/components/Title.tsx +++ b/src/frontend/apps/impress/src/features/header/components/Title.tsx @@ -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" diff --git a/src/frontend/apps/impress/src/features/home/components/HomeBanner.tsx b/src/frontend/apps/impress/src/features/home/components/HomeBanner.tsx index 2f47d62f..5f176775 100644 --- a/src/frontend/apps/impress/src/features/home/components/HomeBanner.tsx +++ b/src/frontend/apps/impress/src/features/home/components/HomeBanner.tsx @@ -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']} /> diff --git a/src/frontend/apps/impress/src/features/left-panel/components/LeftPanelHeader.tsx b/src/frontend/apps/impress/src/features/left-panel/components/LeftPanelHeader.tsx index d3621e06..ac9a2a06 100644 --- a/src/frontend/apps/impress/src/features/left-panel/components/LeftPanelHeader.tsx +++ b/src/frontend/apps/impress/src/features/left-panel/components/LeftPanelHeader.tsx @@ -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" + /> } /> )} diff --git a/src/frontend/apps/impress/src/features/left-panel/components/LeftPanelHeaderButton.tsx b/src/frontend/apps/impress/src/features/left-panel/components/LeftPanelHeaderButton.tsx index fbf0a937..b39af899 100644 --- a/src/frontend/apps/impress/src/features/left-panel/components/LeftPanelHeaderButton.tsx +++ b/src/frontend/apps/impress/src/features/left-panel/components/LeftPanelHeaderButton.tsx @@ -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')} diff --git a/src/frontend/apps/impress/src/i18n/translations.json b/src/frontend/apps/impress/src/i18n/translations.json index 0687339f..cded5eac 100644 --- a/src/frontend/apps/impress/src/i18n/translations.json +++ b/src/frontend/apps/impress/src/i18n/translations.json @@ -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",