From 4c2771fd0cc3b76db5a8d35cc43bd3e05401a165 Mon Sep 17 00:00:00 2001 From: Douglas Fabris <devfabris@gmail.com> Date: Mon, 22 Jan 2024 13:58:33 -0300 Subject: [PATCH] feat: Composer keyboard navigability (#31510) Co-authored-by: Guilherme Gazzo <guilhermegazzo@gmail.com> --- .changeset/four-eels-compete.md | 8 +++++ apps/meteor/package.json | 1 + .../meteor/tests/e2e/message-composer.spec.ts | 29 +++++++++++++++++-- .../tests/e2e/page-objects/home-channel.ts | 6 +++- packages/ui-composer/package.json | 5 ++-- .../MessageComposerToolbarActions.tsx | 16 +++++++--- yarn.lock | 2 ++ 7 files changed, 56 insertions(+), 11 deletions(-) create mode 100644 .changeset/four-eels-compete.md diff --git a/.changeset/four-eels-compete.md b/.changeset/four-eels-compete.md new file mode 100644 index 00000000000..65b2d0c495f --- /dev/null +++ b/.changeset/four-eels-compete.md @@ -0,0 +1,8 @@ +--- +'@rocket.chat/ui-composer': minor +'@rocket.chat/meteor': minor +--- + +Composer keyboard navigability + + diff --git a/apps/meteor/package.json b/apps/meteor/package.json index 008d757c3a8..4a7ee0f1147 100644 --- a/apps/meteor/package.json +++ b/apps/meteor/package.json @@ -222,6 +222,7 @@ "@nivo/line": "0.84.0", "@nivo/pie": "0.84.0", "@react-aria/color": "^3.0.0-beta.15", + "@react-aria/toolbar": "^3.0.0-beta.1", "@react-pdf/renderer": "^3.1.14", "@rocket.chat/account-utils": "workspace:^", "@rocket.chat/agenda": "workspace:^", diff --git a/apps/meteor/tests/e2e/message-composer.spec.ts b/apps/meteor/tests/e2e/message-composer.spec.ts index 861ee17de21..9ed3e42b941 100644 --- a/apps/meteor/tests/e2e/message-composer.spec.ts +++ b/apps/meteor/tests/e2e/message-composer.spec.ts @@ -5,7 +5,7 @@ import { expect, test } from './utils/test'; test.use({ storageState: Users.user1.state }); -test.describe.serial('Composer', () => { +test.describe.serial('message-composer', () => { let poHomeChannel: HomeChannel; let targetChannel: string; @@ -23,7 +23,7 @@ test.describe.serial('Composer', () => { await poHomeChannel.sidenav.openChat(targetChannel); await poHomeChannel.content.sendMessage('hello composer'); - await expect(poHomeChannel.composerToolboxActions).toHaveCount(11); + await expect(poHomeChannel.composerToolbarActions).toHaveCount(11); }); test('should have only the main formatter and the main action', async ({ page }) => { @@ -32,6 +32,29 @@ test.describe.serial('Composer', () => { await poHomeChannel.sidenav.openChat(targetChannel); await poHomeChannel.content.sendMessage('hello composer'); - await expect(poHomeChannel.composerToolboxActions).toHaveCount(5); + await expect(poHomeChannel.composerToolbarActions).toHaveCount(5); + }); + + test('should navigate on toolbar using arrow keys', async ({ page }) => { + await poHomeChannel.sidenav.openChat(targetChannel); + await poHomeChannel.content.sendMessage('hello composer'); + + await page.keyboard.press('Tab'); + await page.keyboard.press('ArrowRight'); + await page.keyboard.press('ArrowRight'); + await expect(poHomeChannel.composerToolbar.getByRole('button', { name: 'Italic' })).toBeFocused(); + + await page.keyboard.press('ArrowLeft'); + await expect(poHomeChannel.composerToolbar.getByRole('button', { name: 'Bold' })).toBeFocused(); + }); + + test('should move the focus away from toolbar using tab key', async ({ page }) => { + await poHomeChannel.sidenav.openChat(targetChannel); + await poHomeChannel.content.sendMessage('hello composer'); + + await page.keyboard.press('Tab'); + await page.keyboard.press('Tab'); + + await expect(poHomeChannel.composerToolbar.getByRole('button', { name: 'Emoji' })).not.toBeFocused(); }); }); diff --git a/apps/meteor/tests/e2e/page-objects/home-channel.ts b/apps/meteor/tests/e2e/page-objects/home-channel.ts index 8cc5d324569..78b044fa48f 100644 --- a/apps/meteor/tests/e2e/page-objects/home-channel.ts +++ b/apps/meteor/tests/e2e/page-objects/home-channel.ts @@ -41,7 +41,11 @@ export class HomeChannel { await this.page.mouse.move(0, 0); } - get composerToolboxActions(): Locator { + get composerToolbar(): Locator { + return this.page.locator('[role=toolbar][aria-label="Composer Primary Actions"]'); + } + + get composerToolbarActions(): Locator { return this.page.locator('[role=toolbar][aria-label="Composer Primary Actions"] button'); } } diff --git a/packages/ui-composer/package.json b/packages/ui-composer/package.json index b3eb0ce53bd..076ed68a08e 100644 --- a/packages/ui-composer/package.json +++ b/packages/ui-composer/package.json @@ -4,6 +4,7 @@ "private": true, "devDependencies": { "@babel/core": "~7.22.20", + "@react-aria/toolbar": "^3.0.0-beta.1", "@rocket.chat/eslint-config": "workspace:^", "@rocket.chat/fuselage": "^0.44.2", "@rocket.chat/icons": "^0.33.0", @@ -26,6 +27,7 @@ "typescript": "~5.3.2" }, "peerDependencies": { + "@react-aria/toolbar": "*", "@rocket.chat/fuselage": "*", "@rocket.chat/icons": "*", "react": "^17.0.2", @@ -46,8 +48,5 @@ ], "volta": { "extends": "../../package.json" - }, - "dependencies": { - "@react-aria/toolbar": "^3.0.0-beta.1" } } diff --git a/packages/ui-composer/src/MessageComposer/MessageComposerToolbarActions.tsx b/packages/ui-composer/src/MessageComposer/MessageComposerToolbarActions.tsx index 23edd5e5d0f..bc462178c7f 100644 --- a/packages/ui-composer/src/MessageComposer/MessageComposerToolbarActions.tsx +++ b/packages/ui-composer/src/MessageComposer/MessageComposerToolbarActions.tsx @@ -1,8 +1,16 @@ +import { useToolbar } from '@react-aria/toolbar'; import { ButtonGroup } from '@rocket.chat/fuselage'; -import type { ComponentProps, ReactElement } from 'react'; +import { useRef, type ComponentProps, type ReactElement } from 'react'; -const MessageComposerToolbarActions = (props: ComponentProps<typeof ButtonGroup>): ReactElement => ( - <ButtonGroup role='toolbar' small {...props} /> -); +const MessageComposerToolbarActions = (props: ComponentProps<typeof ButtonGroup>): ReactElement => { + const ref = useRef(null); + const { toolbarProps } = useToolbar(props, ref); + + return ( + <ButtonGroup role='toolbar' small ref={ref} {...toolbarProps}> + {props.children} + </ButtonGroup> + ); +}; export default MessageComposerToolbarActions; diff --git a/yarn.lock b/yarn.lock index d7ec705086e..535a69f809a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9591,6 +9591,7 @@ __metadata: "@nivo/pie": 0.84.0 "@playwright/test": ^1.40.1 "@react-aria/color": ^3.0.0-beta.15 + "@react-aria/toolbar": ^3.0.0-beta.1 "@react-pdf/renderer": ^3.1.14 "@rocket.chat/account-utils": "workspace:^" "@rocket.chat/agenda": "workspace:^" @@ -10521,6 +10522,7 @@ __metadata: ts-jest: ~29.1.1 typescript: ~5.3.2 peerDependencies: + "@react-aria/toolbar": "*" "@rocket.chat/fuselage": "*" "@rocket.chat/icons": "*" react: ^17.0.2 -- GitLab