feat: Composer keyboard navigability (#31510)

Co-authored-by: Guilherme Gazzo <guilhermegazzo@gmail.com>
pull/31492/head^2
Douglas Fabris 2 years ago committed by GitHub
parent 6e0f3c13f6
commit 4c2771fd0c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 8
      .changeset/four-eels-compete.md
  2. 1
      apps/meteor/package.json
  3. 29
      apps/meteor/tests/e2e/message-composer.spec.ts
  4. 6
      apps/meteor/tests/e2e/page-objects/home-channel.ts
  5. 5
      packages/ui-composer/package.json
  6. 16
      packages/ui-composer/src/MessageComposer/MessageComposerToolbarActions.tsx
  7. 2
      yarn.lock

@ -0,0 +1,8 @@
---
'@rocket.chat/ui-composer': minor
'@rocket.chat/meteor': minor
---
Composer keyboard navigability
![Kapture 2024-01-22 at 11 33 14](https://github.com/RocketChat/Rocket.Chat/assets/27704687/f116c1e6-4ec7-4175-a01b-fa98eade2416)

@ -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:^",

@ -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();
});
});

@ -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');
}
}

@ -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"
}
}

@ -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;

@ -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

Loading…
Cancel
Save