From 43f1b89535b184ad4cbc8677bf0b795c75b1fab3 Mon Sep 17 00:00:00 2001 From: Robin Date: Wed, 20 May 2026 12:38:18 +0200 Subject: [PATCH 1/2] Stop the settings button from appearing while footer is fading out The designs actually never want us to show the settings button in the footer if an app bar is in use (as in Element X mobile apps), so just avoid showing it at all in that case. --- src/room/InCallView.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/room/InCallView.tsx b/src/room/InCallView.tsx index 49e7abfc..87db59bf 100644 --- a/src/room/InCallView.tsx +++ b/src/room/InCallView.tsx @@ -570,8 +570,6 @@ export const InCallView: FC = ({ matrixRoom.roomId, ); - const settingsButtonInAppBar = - headerStyle === HeaderStyle.AppBar && showHeader; useAppBarSecondaryButton( = ({ audioOutputSwitcher={audioOutputSwitcher ?? undefined} // Only pass the openSettings function if the settings button is not in the app bar. // If there is no fn the button will be hidden in the footer. - openSettings={settingsButtonInAppBar ? undefined : openSettings} + openSettings={ + headerStyle === HeaderStyle.AppBar ? undefined : openSettings + } hangup={vm.hangup} //Debug props debugTileLayout={debugTileLayout} From 4b175b814ee0239c71be3aa65a848ed100664dc0 Mon Sep 17 00:00:00 2001 From: Robin Date: Wed, 20 May 2026 13:43:33 +0200 Subject: [PATCH 2/2] Update and simplify tests --- src/room/InCallView.test.tsx | 55 +++++++++++++----------------------- 1 file changed, 19 insertions(+), 36 deletions(-) diff --git a/src/room/InCallView.test.tsx b/src/room/InCallView.test.tsx index c23a9dcb..23642711 100644 --- a/src/room/InCallView.test.tsx +++ b/src/room/InCallView.test.tsx @@ -14,7 +14,12 @@ import { type MockedFunction, vi, } from "vitest"; -import { render, type RenderResult } from "@testing-library/react"; +import { + getByRole, + render, + screen, + type RenderResult, +} from "@testing-library/react"; import { type LocalParticipant } from "livekit-client"; import { BehaviorSubject, of } from "rxjs"; import { BrowserRouter, MemoryRouter } from "react-router-dom"; @@ -189,57 +194,35 @@ describe("InCallView", () => { expect(container).toMatchSnapshot(); }); }); + describe("settings button with AppBar header", () => { - it("mobile landscape, is accessible when showHeader is false", () => { - // windowSize with height <= 600 results in "flat" windowMode, - // which means showHeader$ emits false. - const { getAllByRole } = createInCallView({ + it("mobile portrait, is visible in the header", () => { + createInCallView({ initialRoute: "/?header=app_bar", withAppBar: true, callViewModelOptions: { - // Set windowMode$ to "flat" (height <= 600) - windowSize$: constant({ width: 1000, height: 500 }), + // Narrow like a mobile phone in portrait orientation + windowSize$: constant({ width: 400, height: 700 }), }, }); - // When showHeader is false, hideSettingsButton is false, - // so the settings button is visible in the footer. - const settingsBtn = getAllByRole("button", { name: "Settings" }); - // here we check for two settings buttons because there are two buttons in the bottom bar. One for the - // the narrow layout and another one for the wide layout. - // Their visibility uses @media css queries, which cannot be tested in JSDOM, - // but we can at least check that both buttons are rendered and have the correct classes. - expect(settingsBtn.length).toBe(2); - expect(settingsBtn[0]).toHaveAttribute( - "data-testid", - "settings-bottom-left", - ); - expect(settingsBtn[0]).toBeVisible(); + + getByRole(screen.getByRole("banner"), "button", { name: "Settings" }); }); - it("mobile portrait, is accessible when showHeader is true", () => { - // windowSize with height > 600 and width > 600 results in "normal" windowMode, - // which means showHeader$ emits true. - const { getAllByRole } = createInCallView({ + it("mobile landscape, is not visible anywhere", () => { + const { queryByRole } = createInCallView({ initialRoute: "/?header=app_bar", withAppBar: true, callViewModelOptions: { - // Set windowMode$ to "normal" (height >= 600) - windowSize$: constant({ width: 1000, height: 800 }), + // Flat like a mobile phone in landscape orientation + windowSize$: constant({ width: 700, height: 400 }), }, }); - // When showHeader is true and headerStyle is AppBar, - // hideSettingsButton is true in the footer, but the settings - // button is rendered in the AppBar via useAppBarSecondaryButton. - const settingsBtns = getAllByRole("button", { name: "Settings" }); - expect(settingsBtns.length).toBe(1); - expect(settingsBtns[0]).toHaveAttribute( - "data-testid", - "settings-app-bar", - ); - expect(settingsBtns[0]).toBeVisible(); + expect(queryByRole("button", { name: "Settings" })).toBe(null); }); }); + describe("audioOutputSwitcher", () => { it("is visible and can be clicked", async () => { const user = userEvent.setup();