diff --git a/CHANGELOG.md b/CHANGELOG.md index 6eed33d2..95db35eb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,8 +25,9 @@ and this project adheres to - #1244 - #1270 - #1282 + - #1261 - ♻️(backend) fallback to email identifier when no name #1298 -- 🐛(backend) allow ASCII characters in user sub field #1295 +- 🐛(backend) allow ASCII characters in user sub field #1295 - ⚡️(frontend) improve fallback width calculation #1333 ### Fixed diff --git a/src/frontend/apps/e2e/__tests__/app-impress/utils-sub-pages.ts b/src/frontend/apps/e2e/__tests__/app-impress/utils-sub-pages.ts index 92a900ab..142f6250 100644 --- a/src/frontend/apps/e2e/__tests__/app-impress/utils-sub-pages.ts +++ b/src/frontend/apps/e2e/__tests__/app-impress/utils-sub-pages.ts @@ -63,5 +63,6 @@ export const clickOnAddRootSubPage = async (page: Page) => { const rootItem = page.getByTestId('doc-tree-root-item'); await expect(rootItem).toBeVisible(); await rootItem.hover(); - await rootItem.getByRole('button', { name: 'add_box' }).click(); + + await rootItem.getByTestId('add-child-doc').click(); }; diff --git a/src/frontend/apps/impress/src/components/dropdown-menu/DropdownMenu.tsx b/src/frontend/apps/impress/src/components/dropdown-menu/DropdownMenu.tsx index 55101d4d..ec754185 100644 --- a/src/frontend/apps/impress/src/components/dropdown-menu/DropdownMenu.tsx +++ b/src/frontend/apps/impress/src/components/dropdown-menu/DropdownMenu.tsx @@ -2,6 +2,7 @@ import { HorizontalSeparator } from '@gouvfr-lasuite/ui-kit'; import { Fragment, PropsWithChildren, + ReactNode, useCallback, useEffect, useRef, @@ -15,7 +16,7 @@ import { useCunninghamTheme } from '@/cunningham'; import { useDropdownKeyboardNav } from './hook/useDropdownKeyboardNav'; export type DropdownMenuOption = { - icon?: string; + icon?: string | ReactNode; label: string; testId?: string; value?: string; @@ -81,14 +82,28 @@ export const DropdownMenu = ({ // Focus selected menu item when menu opens useEffect(() => { - if (isOpen && menuItemRefs.current.length > 0) { - const selectedIndex = options.findIndex((option) => option.isSelected); - if (selectedIndex !== -1) { - setFocusedIndex(selectedIndex); - setTimeout(() => { - menuItemRefs.current[selectedIndex]?.focus(); - }, 0); - } + if (!isOpen || menuItemRefs.current.length === 0) { + return; + } + + const selectedIndex = options.findIndex((option) => option.isSelected); + if (selectedIndex !== -1) { + setFocusedIndex(selectedIndex); + setTimeout(() => { + menuItemRefs.current[selectedIndex]?.focus(); + }, 0); + return; + } + + // Fallback: focus first enabled/visible option + const firstEnabledIndex = options.findIndex( + (opt) => opt.show !== false && !opt.disabled, + ); + if (firstEnabledIndex !== -1) { + setFocusedIndex(firstEnabledIndex); + setTimeout(() => { + menuItemRefs.current[firstEnabledIndex]?.focus(); + }, 0); } }, [isOpen, options]); @@ -156,7 +171,6 @@ export const DropdownMenu = ({ return; } const isDisabled = option.disabled !== undefined && option.disabled; - const isFocused = index === focusedIndex; return ( @@ -207,17 +221,8 @@ export const DropdownMenu = ({ } &:focus-visible { - outline: 2px solid var(--c--theme--colors--primary-500); - outline-offset: -2px; background-color: var(--c--theme--colors--greyscale-050); } - - ${isFocused && - css` - outline: 2px solid var(--c--theme--colors--primary-500); - outline-offset: -2px; - background-color: var(--c--theme--colors--greyscale-050); - `} `} > - {option.icon && ( - - )} + {option.icon && + (typeof option.icon === 'string' ? ( + + ) : ( + option.icon + ))} {option.label} diff --git a/src/frontend/apps/impress/src/features/docs/doc-tree/components/DocSubPageItem.tsx b/src/frontend/apps/impress/src/features/docs/doc-tree/components/DocSubPageItem.tsx index d9b7ecfc..720facd6 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-tree/components/DocSubPageItem.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-tree/components/DocSubPageItem.tsx @@ -95,13 +95,19 @@ export const DocSubPageItem = (props: TreeViewNodeProps) => { : 'var(--c--theme--colors--greyscale-000)'}; } + &:focus-within .light-doc-item-actions { + display: flex; + background: var(--c--theme--colors--greyscale-100); + } + .c__tree-view--node.isSelected { .light-doc-item-actions { background: var(--c--theme--colors--greyscale-100); } } - &:hover { + &:hover, + &:focus-within { background-color: var(--c--theme--colors--greyscale-100); border-radius: 4px; diff --git a/src/frontend/apps/impress/src/features/docs/doc-tree/components/DocTree.tsx b/src/frontend/apps/impress/src/features/docs/doc-tree/components/DocTree.tsx index 3c8e63d7..cea3162b 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-tree/components/DocTree.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-tree/components/DocTree.tsx @@ -191,7 +191,8 @@ export const DocTree = ({ currentDoc }: DocTreeProps) => { opacity: 1; } } - &:hover { + &:hover, + &:focus-within { .doc-tree-root-item-actions { opacity: 1; } diff --git a/src/frontend/apps/impress/src/features/docs/doc-tree/components/DocTreeItemActions.tsx b/src/frontend/apps/impress/src/features/docs/doc-tree/components/DocTreeItemActions.tsx index dc851b4e..12cd2dfa 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-tree/components/DocTreeItemActions.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-tree/components/DocTreeItemActions.tsx @@ -150,6 +150,25 @@ export const DocTreeItemActions = ({ $align="center" className="--docs--doc-tree-item-actions" $gap="4px" + $css={css` + &:focus-within { + opacity: 1; + visibility: visible; + } + button:focus-visible, + [role='button']:focus-visible { + outline: 2px solid var(--c--theme--colors--primary-500); + outline-offset: 2px; + background-color: var(--c--theme--colors--greyscale-050); + border-radius: 4px; + } + .icon-button:focus-visible { + outline: 2px solid var(--c--theme--colors--primary-500); + outline-offset: 2px; + background-color: var(--c--theme--colors--greyscale-050); + border-radius: 4px; + } + `} > { + if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault(); + e.stopPropagation(); + onOpenChange?.(!isOpen); + } + }} /> {doc.abilities.children_create && ( { e.stopPropagation(); e.preventDefault(); @@ -179,6 +210,7 @@ export const DocTreeItemActions = ({ }); }} color="primary" + aria-label={t('Add child document')} > Django Rest Framework y <6>Next.js. También utilizamos <9>Yjs y <13>BlockNote.js, dos proyectos que estamos orgullosos de patrocinar.", "home-content-open-source-part2": "Puede autoalojar fácilmente Docs (consulte nuestra <2>documentación de instalación).
Docs utiliza una <7>licencia (MIT) adecuada para la innovación y las empresas.
Se aceptan contribuciones (consulte nuestra hoja de ruta <13>aquí).", "home-content-open-source-part3": "Docs es el resultado de un esfuerzo conjunto llevado a cabo por los gobiernos francés 🇫🇷🥖 <1>(DINUM) y alemán 🇩🇪🥨 <5>(ZenDiS)." @@ -712,6 +717,8 @@ "Move": "Déplacer", "Move document": "Déplacer le document", "Move to my docs": "Déplacer vers mes docs", + "More options": "Plus d'options", + "Add child document": "Ajouter un document enfant", "My docs": "Mes documents", "Name": "Nom", "New doc": "Nouveau doc", @@ -1151,6 +1158,8 @@ "You are the sole owner of this group, make another member the group owner before you can change your own role or be removed from your document.": "U bent de enige eigenaar van deze groep, maak een ander lid de groepseigenaar voordat u uw eigen rol kunt wijzigen of kan worden verwijderd van het document.", "Your current document will revert to this version.": "Uw huidige document wordt teruggezet naar deze versie.", "Your {{format}} was downloaded succesfully": "Jouw {{format}} is succesvol gedownload", + "More options": "Meer opties", + "Add child document": "Onderliggend document toevoegen", "home-content-open-source-part1": "Docs is gebouwd op <2>Django Rest Framework en <6>Next.js. We gebruiken ook <9>Yjs en <13>BlockNote.js, twee projecten die we met trots sponsoren.", "home-content-open-source-part2": "U kunt Docs eenvoudig zelf hosten (zie onze <2>installatiedocumentatie).
Docs gebruikt een <7>licentie (MIT) die is afgestemd op innovatie en ondernemingen.
Bijdragen zijn welkom (zie onze routekaart <13>hier).", "home-content-open-source-part3": "Docs is het resultaat van een gezamenlijke inspanning geleid door de Franse 🇫🇷🥖 <1>(DINUM) en Duitse 🇩🇪🥨 <5>(ZenDiS) overheden."