1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-06 00:13:28 +00:00

[PM-26821] Improve macOS fullscreen ux (#16838)

* Improve popout window UX for fullscreen macOS

Adds special handling for popout windows when the sender is in fullscreen mode on macOS. The sender window moves from fullscreen to maximized before opening the popout, and the new window is focused after creation to improve user experience.

* Add tests for fullscreen popout behavior on mac

added happy path and skip path tests

* Move popout window check before fullscreen logic

* Refactor openPopout tests for platform-specific fullscreen handling

* run prettier

---------

Co-authored-by: Addison Beck <github@addisonbeck.com>
This commit is contained in:
Szymon
2025-11-05 02:54:20 +01:00
committed by GitHub
parent 8e8092c828
commit 05ca57d538
2 changed files with 86 additions and 1 deletions

View File

@@ -140,6 +140,11 @@ describe("BrowserPopupUtils", () => {
describe("openPopout", () => {
beforeEach(() => {
jest.spyOn(BrowserApi, "getPlatformInfo").mockResolvedValueOnce({
os: "linux",
arch: "x86-64",
nacl_arch: "x86-64",
});
jest.spyOn(BrowserApi, "getWindow").mockResolvedValueOnce({
id: 1,
left: 100,
@@ -150,6 +155,8 @@ describe("BrowserPopupUtils", () => {
width: 380,
});
jest.spyOn(BrowserApi, "createWindow").mockImplementation();
jest.spyOn(BrowserApi, "updateWindowProperties").mockImplementation();
jest.spyOn(BrowserApi, "getPlatformInfo").mockImplementation();
});
it("creates a window with the default window options", async () => {
@@ -267,6 +274,63 @@ describe("BrowserPopupUtils", () => {
url: `chrome-extension://id/${url}?uilocation=popout&singleActionPopout=123`,
});
});
it("exits fullscreen and focuses popout window if the current window is fullscreen and platform is mac", async () => {
const url = "popup/index.html";
jest.spyOn(BrowserPopupUtils as any, "isSingleActionPopoutOpen").mockResolvedValueOnce(false);
jest.spyOn(BrowserApi, "getPlatformInfo").mockReset().mockResolvedValueOnce({
os: "mac",
arch: "x86-64",
nacl_arch: "x86-64",
});
jest.spyOn(BrowserApi, "getWindow").mockReset().mockResolvedValueOnce({
id: 1,
left: 100,
top: 100,
focused: false,
alwaysOnTop: false,
incognito: false,
width: 380,
state: "fullscreen",
});
jest
.spyOn(BrowserApi, "createWindow")
.mockResolvedValueOnce({ id: 2 } as chrome.windows.Window);
await BrowserPopupUtils.openPopout(url, { senderWindowId: 1 });
expect(BrowserApi.updateWindowProperties).toHaveBeenCalledWith(1, {
state: "maximized",
});
expect(BrowserApi.updateWindowProperties).toHaveBeenCalledWith(2, {
focused: true,
});
});
it("doesnt exit fullscreen if the platform is not mac", async () => {
const url = "popup/index.html";
jest.spyOn(BrowserPopupUtils as any, "isSingleActionPopoutOpen").mockResolvedValueOnce(false);
jest.spyOn(BrowserApi, "getPlatformInfo").mockReset().mockResolvedValueOnce({
os: "win",
arch: "x86-64",
nacl_arch: "x86-64",
});
jest.spyOn(BrowserApi, "getWindow").mockResolvedValueOnce({
id: 1,
left: 100,
top: 100,
focused: false,
alwaysOnTop: false,
incognito: false,
width: 380,
state: "fullscreen",
});
await BrowserPopupUtils.openPopout(url);
expect(BrowserApi.updateWindowProperties).not.toHaveBeenCalledWith(1, {
state: "maximized",
});
});
});
describe("openCurrentPagePopout", () => {

View File

@@ -168,8 +168,29 @@ export default class BrowserPopupUtils {
) {
return;
}
const platform = await BrowserApi.getPlatformInfo();
const isMacOS = platform.os === "mac";
const isFullscreen = senderWindow.state === "fullscreen";
const isFullscreenAndMacOS = isFullscreen && isMacOS;
//macOS specific handling for improved UX when sender in fullscreen aka green button;
if (isFullscreenAndMacOS) {
await BrowserApi.updateWindowProperties(senderWindow.id, {
state: "maximized",
});
return await BrowserApi.createWindow(popoutWindowOptions);
//wait for macOS animation to finish
await new Promise((resolve) => setTimeout(resolve, 1000));
}
const newWindow = await BrowserApi.createWindow(popoutWindowOptions);
if (isFullscreenAndMacOS) {
await BrowserApi.updateWindowProperties(newWindow.id, {
focused: true,
});
}
return newWindow;
}
/**