From 664ec3831d44640e29f44f44fc0a3a0fe02c0ea6 Mon Sep 17 00:00:00 2001
From: Linus Rath <139418639+rathlinus@users.noreply.github.com>
Date: Wed, 4 Nov 2026 12:36:16 +0100
Subject: [PATCH] fix: open a recipient's menu when its chip is clicked
---
components/email/__tests__/composer-a11y.test.tsx | 15 +++++++++++++++
components/email/email-composer.tsx | 8 ++++++++
2 files changed, 23 insertions(+)
diff --git a/components/email/__tests__/composer-a11y.test.tsx b/components/email/__tests__/composer-a11y.test.tsx
index f71f24a0d..52783b107 100644
--- a/components/email/__tests__/composer-a11y.test.tsx
+++ b/components/email/__tests__/composer-a11y.test.tsx
@@ -394,6 +394,21 @@ describe('moving a recipient by keyboard (MO-07)', () => {
expect(liveRegion()).toHaveTextContent('recipient_moved:Anna Berg|cc');
});
+ // compose-p2-16: a click did nothing visible, and Escape then closed the composer.
+ it('opens the chip menu on a click, and Escape closes only the menu', async () => {
+ const onClose = vi.fn();
+ render();
+ const chip = within(screen.getByRole('list', { name: 'to:' })).getByRole('listitem');
+ fireEvent.click(chip.querySelector('.truncate')!);
+
+ const move = await screen.findByTestId('recipient-move-bcc');
+ fireEvent.keyDown(move, { key: 'Escape' });
+
+ await waitFor(() => expect(screen.queryByTestId('recipient-move-bcc')).toBeNull());
+ expect(onClose).not.toHaveBeenCalled();
+ expect(screen.getAllByRole('listitem')).toHaveLength(1);
+ });
+
it('opens the chip menu with Shift+F10 too', async () => {
render();
const chip = within(screen.getByRole('list', { name: 'to:' })).getByRole('listitem');
diff --git a/components/email/email-composer.tsx b/components/email/email-composer.tsx
index d0d922b13..615dfd910 100644
--- a/components/email/email-composer.tsx
+++ b/components/email/email-composer.tsx
@@ -6860,6 +6860,14 @@ export function RecipientChipInput({
!isEditing && "focus:outline-none focus:ring-2 focus:ring-primary focus:border-primary"
)}
onContextMenu={isEditing ? undefined : (e) => handleContextMenu(e, i, chip)}
+ // A click opens the chip's menu too (address, Edit, Move to,
+ // Remove), as Gmail and Outlook do; it did nothing visible.
+ onClick={isEditing ? undefined : (e) => {
+ if (e.target instanceof Element && e.target.closest('button')) return;
+ // Not on to the field, whose click puts the caret in the input.
+ e.stopPropagation();
+ openChipMenu(i);
+ }}
data-invalid={isInvalid || undefined}
title={isInvalid ? t('invalid_recipient') : undefined}
role="listitem"