mirror of
https://github.com/Novaloop-AG/healthchat-web.git
synced 2026-11-07 06:58:25 +00:00
Add FocusLock to emoji picker (#31146)
* Add focus lock to emoji picker and e2e test. * Remove direct use of FocusLock in favour of the ContextMenu prop. * Apply returnFocus for ContextMenu focusLocks * Remove import
This commit is contained in:
@@ -92,6 +92,41 @@ test.describe("Composer", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("should have focus lock in emoji picker", async ({ page, app }) => {
|
||||||
|
const emojiButton = app.getComposer(false).getByRole("button", { name: "Emoji" });
|
||||||
|
|
||||||
|
// Open emoji picker by clicking the button
|
||||||
|
await emojiButton.click();
|
||||||
|
|
||||||
|
// Wait for emoji picker to be visible
|
||||||
|
const emojiPicker = page.getByTestId("mx_EmojiPicker");
|
||||||
|
await expect(emojiPicker).toBeVisible();
|
||||||
|
|
||||||
|
// Get initial focused element (should be search input)
|
||||||
|
const searchInput = emojiPicker.getByRole("textbox", { name: "Search" });
|
||||||
|
await expect(searchInput).toBeFocused();
|
||||||
|
|
||||||
|
// Try to tab multiple times - focus should stay within emoji picker
|
||||||
|
await page.keyboard.press("Tab");
|
||||||
|
await page.keyboard.press("Tab");
|
||||||
|
await page.keyboard.press("Tab");
|
||||||
|
await page.keyboard.press("Tab");
|
||||||
|
await page.keyboard.press("Tab");
|
||||||
|
|
||||||
|
// Verify we're still within the emoji picker (not back to composer)
|
||||||
|
const focusedElement = await page.evaluate(() => document.activeElement?.closest(".mx_EmojiPicker"));
|
||||||
|
expect(focusedElement).not.toBeNull();
|
||||||
|
|
||||||
|
// Close with Escape key
|
||||||
|
await page.keyboard.press("Escape");
|
||||||
|
|
||||||
|
// Verify emoji picker is closed
|
||||||
|
await expect(emojiPicker).not.toBeVisible();
|
||||||
|
|
||||||
|
// Verify focus returns to emoji button
|
||||||
|
await expect(emojiButton).toBeFocused();
|
||||||
|
});
|
||||||
|
|
||||||
test.describe("when Control+Enter is required to send", () => {
|
test.describe("when Control+Enter is required to send", () => {
|
||||||
test.beforeEach(async ({ app }) => {
|
test.beforeEach(async ({ app }) => {
|
||||||
await app.settings.setValue("MessageComposerInput.ctrlEnterToSend", null, SettingLevel.ACCOUNT, true);
|
await app.settings.setValue("MessageComposerInput.ctrlEnterToSend", null, SettingLevel.ACCOUNT, true);
|
||||||
|
|||||||
@@ -405,7 +405,7 @@ export default class ContextMenu extends React.PureComponent<React.PropsWithChil
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (focusLock) {
|
if (focusLock) {
|
||||||
body = <FocusLock>{body}</FocusLock>;
|
body = <FocusLock returnFocus>{body}</FocusLock>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// filter props that are invalid for DOM elements
|
// filter props that are invalid for DOM elements
|
||||||
|
|||||||
@@ -735,7 +735,7 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
|
|||||||
if (this.state.reactionPickerDisplayed) {
|
if (this.state.reactionPickerDisplayed) {
|
||||||
const buttonRect = (this.reactButtonRef.current as HTMLElement)?.getBoundingClientRect();
|
const buttonRect = (this.reactButtonRef.current as HTMLElement)?.getBoundingClientRect();
|
||||||
reactionPicker = (
|
reactionPicker = (
|
||||||
<ContextMenu {...toRightOf(buttonRect)} onFinished={this.closeMenu} managed={false}>
|
<ContextMenu {...toRightOf(buttonRect)} onFinished={this.closeMenu} managed={false} focusLock>
|
||||||
<ReactionPicker mxEvent={mxEvent} onFinished={this.onCloseReactionPicker} reactions={reactions} />
|
<ReactionPicker mxEvent={mxEvent} onFinished={this.onCloseReactionPicker} reactions={reactions} />
|
||||||
</ContextMenu>
|
</ContextMenu>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -156,7 +156,7 @@ const ReactButton: React.FC<IReactButtonProps> = ({ mxEvent, reactions, onFocusC
|
|||||||
if (menuDisplayed && buttonRef.current) {
|
if (menuDisplayed && buttonRef.current) {
|
||||||
const buttonRect = buttonRef.current.getBoundingClientRect();
|
const buttonRect = buttonRef.current.getBoundingClientRect();
|
||||||
contextMenu = (
|
contextMenu = (
|
||||||
<ContextMenu {...aboveLeftOf(buttonRect)} onFinished={closeMenu} managed={false}>
|
<ContextMenu {...aboveLeftOf(buttonRect)} onFinished={closeMenu} managed={false} focusLock>
|
||||||
<ReactionPicker mxEvent={mxEvent} reactions={reactions} onFinished={closeMenu} />
|
<ReactionPicker mxEvent={mxEvent} reactions={reactions} onFinished={closeMenu} />
|
||||||
</ContextMenu>
|
</ContextMenu>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ const ReactButton: React.FC<IProps> = ({ mxEvent, reactions }) => {
|
|||||||
if (menuDisplayed && button.current) {
|
if (menuDisplayed && button.current) {
|
||||||
const buttonRect = button.current.getBoundingClientRect();
|
const buttonRect = button.current.getBoundingClientRect();
|
||||||
contextMenu = (
|
contextMenu = (
|
||||||
<ContextMenu {...aboveLeftOf(buttonRect)} onFinished={closeMenu} managed={false}>
|
<ContextMenu {...aboveLeftOf(buttonRect)} onFinished={closeMenu} managed={false} focusLock>
|
||||||
<ReactionPicker mxEvent={mxEvent} reactions={reactions} onFinished={closeMenu} />
|
<ReactionPicker mxEvent={mxEvent} reactions={reactions} onFinished={closeMenu} />
|
||||||
</ContextMenu>
|
</ContextMenu>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ export function EmojiButton({ addEmoji, menuPosition, className }: IEmojiButtonP
|
|||||||
};
|
};
|
||||||
|
|
||||||
contextMenu = (
|
contextMenu = (
|
||||||
<ContextMenu {...position} onFinished={onFinished} managed={false}>
|
<ContextMenu {...position} onFinished={onFinished} managed={false} focusLock>
|
||||||
<EmojiPicker onChoose={addEmoji} onFinished={onFinished} />
|
<EmojiPicker onChoose={addEmoji} onFinished={onFinished} />
|
||||||
</ContextMenu>
|
</ContextMenu>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user