From 74d822bb27c75bad37112afbbedbd920ffa43038 Mon Sep 17 00:00:00 2001 From: Anthony LC Date: Mon, 21 Sep 2026 11:40:05 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=9A=B8(frontend)=20add=20shortcut=20indic?= =?UTF-8?q?ation=20on=20doc=20option?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For users to know the keyboard shortcut for opening the presenter mode, we add a visual indication of the shortcut in the doc option menu. --- CHANGELOG.md | 1 + .../app-impress/presenter-mode.spec.ts | 6 +- .../e2e/__tests__/app-impress/utils-common.ts | 7 ++- .../doc-management/components/DocToolBox.tsx | 58 ++++++++++++++++--- .../apps/impress/src/utils/userAgent.ts | 4 ++ 5 files changed, 65 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f4415cca..8f79be1e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ and this project adheres to - 🐛(frontend) reduce PostHog volume from web vitals and opt_in spam #2701 - ✨(backend) expose the attachment max size in the config endpoint #2577 - ✨(frontend) warn before uploading an attachment over the size limit #2577 +- ✨(frontend) add keyboard shortcut to open presentation mode #2697 ### Fixed diff --git a/src/frontend/apps/e2e/__tests__/app-impress/presenter-mode.spec.ts b/src/frontend/apps/e2e/__tests__/app-impress/presenter-mode.spec.ts index aaa343b29..cbb0d428d 100644 --- a/src/frontend/apps/e2e/__tests__/app-impress/presenter-mode.spec.ts +++ b/src/frontend/apps/e2e/__tests__/app-impress/presenter-mode.spec.ts @@ -12,8 +12,10 @@ import { const openPresenter = async (page: Page) => { await page.getByLabel('Open the document options').first().click(); - await page.getByRole('menuitem', { name: 'Present' }).click(); - + await page + .getByRole('menuitem') + .getByText(/Present/) + .click(); const overlay = page.getByRole('dialog', { name: 'Presenter mode' }); await expect(overlay).toBeVisible(); return overlay; 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 eef64fb35..e17ffaf9a 100644 --- a/src/frontend/apps/e2e/__tests__/app-impress/utils-common.ts +++ b/src/frontend/apps/e2e/__tests__/app-impress/utils-common.ts @@ -445,7 +445,10 @@ export const clickInEditorShareButton = async (page: Page) => { .click(); }; -export const clickInEditorMenu = async (page: Page, textButton: string) => { +export const clickInEditorMenu = async ( + page: Page, + textButton: string | RegExp, +) => { await clickInDocOptionMenu( page, page.getByTestId('floating-bar'), @@ -456,7 +459,7 @@ export const clickInEditorMenu = async (page: Page, textButton: string) => { export const clickInDocOptionMenu = async ( page: Page, selector: Locator, - textButton: string, + textButton: string | RegExp, ) => { await selector .getByRole('button', { name: /Open the document options/ }) diff --git a/src/frontend/apps/impress/src/features/docs/doc-management/components/DocToolBox.tsx b/src/frontend/apps/impress/src/features/docs/doc-management/components/DocToolBox.tsx index 3ac38441e..e610fadcd 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-management/components/DocToolBox.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-management/components/DocToolBox.tsx @@ -3,14 +3,17 @@ import { ButtonProps, DropdownMenu, DropdownMenuItem, + DropdownMenuOption, + MenuItemSeparator, } from '@gouvfr-lasuite/ui-components'; import { Present } from '@gouvfr-lasuite/ui-components/icons'; import { announce } from '@react-aria/live-announcer'; import dynamic from 'next/dynamic'; import { useRouter } from 'next/router'; -import { memo, useEffect, useMemo, useState } from 'react'; +import { ReactNode, memo, useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; +import { Box } from '@/components/Box'; import { Text } from '@/components/Text'; import { useEditorStore } from '@/docs/doc-editor/stores/useEditorStore'; import { getWordCount } from '@/docs/doc-editor/utils'; @@ -37,6 +40,7 @@ import StarSlashIcon from '@/icons/star-slash.svg'; import StarIcon from '@/icons/star.svg'; import DeleteIcon from '@/icons/trash.svg'; import { useFocusStore, useResponsiveStore } from '@/stores'; +import { isMacOS } from '@/utils/userAgent'; import { KEY_DOC, @@ -108,6 +112,18 @@ const ModalExport = dynamic( { ssr: false }, ); +/** + * We widen the type of `DropdownMenuOption.label` to accept + * `ReactNode` instead of just `string`. + * @todo Widen the label type in the ui-kit package itself. + * This is a temporary workaround until the ui-kit package is updated. + */ +type MenuOptionWithNodeLabel = Omit & { + label: ReactNode; +}; +type DropdownMenuItemWithNodeLabel = + MenuOptionWithNodeLabel | MenuItemSeparator; + interface DocToolBoxProps { doc: Doc; isCurrentDoc: boolean; @@ -176,7 +192,7 @@ const DocToolBoxComponent = ({ listInvalidQueries: [KEY_LIST_DOC, KEY_DOC, KEY_LIST_FAVORITE_DOC], }); - const options: DropdownMenuItem[] = [ + const options: DropdownMenuItemWithNodeLabel[] = [ { label: t('Copy link', { description: 'Dropdown menu item to copy the document link', @@ -199,10 +215,38 @@ const DocToolBoxComponent = ({ showSeparator: isCurrentDoc, }, { - label: t('Present', { - description: - 'Dropdown menu item to open the document in presentation mode', - }), + label: ( + + + {t('Present', { + description: + 'Dropdown menu item to open the document in presentation mode', + })} + + + {isMacOS + ? t('Cmd+Alt+P', { + description: + 'Dropdown menu item to open the document in presentation mode, macOS shortcut', + }) + : t('Ctrl+Alt+P', { + description: + 'Dropdown menu item to open the document in presentation mode, Windows/Linux shortcut', + })} + + + ), icon: