diff --git a/components/email/__tests__/email-hover-actions-placement.test.tsx b/components/email/__tests__/email-hover-actions-placement.test.tsx index 6a7b0ba13..4130b8c55 100644 --- a/components/email/__tests__/email-hover-actions-placement.test.tsx +++ b/components/email/__tests__/email-hover-actions-placement.test.tsx @@ -37,3 +37,15 @@ describe('row quick action placement', () => { expect(el.className).not.toContain('h-10'); }); }); + +// Clicking a row focuses the row, so group-focus-within kept the actions on +// the open message after the pointer left it. Only keyboard focus on a control +// inside the row may show them. +describe('row quick action visibility', () => { + it.each(['inline', 'floating'])('shows the %s actions on hover and keyboard focus only', (mode) => { + useSettingsStore.setState({ hoverActionsMode: mode } as never); + const classes = toolbar().className.split(/\s+/); + expect(classes).toEqual(expect.arrayContaining(['hidden', 'group-hover:flex', 'group-has-[:focus-visible]:flex'])); + expect(classes).not.toContain('group-focus-within:flex'); + }); +}); diff --git a/components/email/__tests__/thread-list-item.test.tsx b/components/email/__tests__/thread-list-item.test.tsx index 089fc98dd..64210cc04 100644 --- a/components/email/__tests__/thread-list-item.test.tsx +++ b/components/email/__tests__/thread-list-item.test.tsx @@ -887,7 +887,7 @@ describe('ThreadListItem hover actions', () => { expect(screen.getByTestId('hover-action-delete')).toBeTruthy(); }); - // Tabbing to a row's checkbox shows them too (group-focus-within), so they + // Tabbing to a row's checkbox shows them too (group-has-[:focus-visible]), so they // have to be there without a pointer. it.each([ ['a single message', () => groupEmailsByThread([makeEmail()])[0]], diff --git a/components/email/email-hover-actions.tsx b/components/email/email-hover-actions.tsx index 7aef1a4a4..53628952a 100644 --- a/components/email/email-hover-actions.tsx +++ b/components/email/email-hover-actions.tsx @@ -318,8 +318,10 @@ export function EmailHoverActions({ // While the picker is open the row is no longer hovered, so the toolbar has to // stay mounted and visible or the picker would close under its own anchor. - // Shown on keyboard focus too (R7), not only on hover. - const visibilityClass = tagPickerPos ? "flex" : "hidden group-hover:flex group-focus-within:flex"; + // Shown on keyboard focus too (R7), not only on hover. Only a control inside + // the row counts, and only from the keyboard: a click focuses the row itself + // (or the button clicked), and focus-within kept the toolbar on the open row. + const visibilityClass = tagPickerPos ? "flex" : "hidden group-hover:flex group-has-[:focus-visible]:flex"; if (hoverActionsMode === 'floating') { return (