Skip to content

fix(History): trap the keyboard focus inside the desktop popup - #250

Draft
vladimirfilosof wants to merge 2 commits into
mainfrom
CLOUDFRONT-62526-assistant-history-popup-focus
Draft

vladimirfilosof wants to merge 2 commits into
mainfrom
CLOUDFRONT-62526-assistant-history-popup-focus

Conversation

@vladimirfilosof

Copy link
Copy Markdown
Contributor

The chat history popup cannot be reached with the keyboard on desktop. It is rendered in a portal at the end of the chat container, and the underlying uikit Popup is non-modal by default, so opening the history leaves the focus on the trigger button: Tab walks through the rest of the chat — the remaining header actions, the message list, the prompt input — before it ever reaches the chat list, and Shift + Tab never reaches it at all.

History now renders the desktop popup as a modal dialog: the focus moves inside when the popup opens, Tab and Shift + Tab cycle within it, and Escape closes it and returns the focus to the trigger button. The popup reuses the sheet title as its accessible name. Pass modal={false} to keep the previous behaviour, or the new initialFocus prop to choose the element focused on open. The mobile sheet is unchanged.

Covered by four component tests: the focus entering the popup, staying inside while tabbing, returning to the trigger on Escape, and staying on the trigger with modal={false}.

Console: CLOUDFRONT-62526

The desktop popup is rendered in a portal at the end of the chat container, so with
the default non-modal Popup the focus stayed on the trigger button when the popup
opened, and `Tab` walked through the rest of the chat before ever reaching the chat
list. `Shift + Tab` never reached it at all, which left the history unusable from the
keyboard.

Render the popup as a modal dialog instead: the focus moves inside on open, `Tab` and
`Shift + Tab` cycle within the popup, and `Escape` closes it and returns the focus to
the trigger button. The popup reuses the sheet title as its accessible name. Pass
`modal={false}` to keep the previous behaviour, or `initialFocus` to choose the
element focused on open.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@gravity-ui

gravity-ui Bot commented Sep 16, 2026

Copy link
Copy Markdown
Contributor

🚀 Prerelease version published!

Install this PR version:

npm i --save-dev @gravity-ui/aikit@2.20.2-beta.c5ae09a2cb6c7029ac0e61b52d84d93c6200e7aa.0

@gravity-ui-bot

Copy link
Copy Markdown

Preview is ready.

@gravity-ui

gravity-ui Bot commented Sep 16, 2026

Copy link
Copy Markdown
Contributor

🎭 Component Tests Report is ready.

`Popup` runs its props through uikit's `filterDOMProps` without the `labelable`
option, so an `aria-label` passed to it is dropped before it reaches the floating
element, and the dialog ends up nameless. Set the attribute on the element through
`floatingRef` instead.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants