1
0
mirror of https://github.com/bitwarden/browser.git synced 2025-03-12 13:39:14 +01:00

[PM-18859] Mobile Viewports - Extension Prompt (#13703)

* remove min-width on body element for extension prompt page

* reset meta viewport content for extension prompt page

* set max width of svg to avoid any overflow on mobile devices

* use inline display to avoid icon  overflow on mobile devices

* use max width on the icon to fix overflow rather than editing the anon layout
This commit is contained in:
Nick Krantz 2025-03-10 10:33:56 -05:00 committed by GitHub
parent a569dd9ad6
commit 85a5aea897
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 82 additions and 6 deletions

View File

@ -13,12 +13,27 @@ import { BrowserExtensionPromptComponent } from "./browser-extension-prompt.comp
describe("BrowserExtensionPromptComponent", () => {
let fixture: ComponentFixture<BrowserExtensionPromptComponent>;
let component: BrowserExtensionPromptComponent;
const start = jest.fn();
const pageState$ = new BehaviorSubject(BrowserPromptState.Loading);
const setAttribute = jest.fn();
const getAttribute = jest.fn().mockReturnValue("width=1010");
beforeEach(async () => {
start.mockClear();
setAttribute.mockClear();
getAttribute.mockClear();
// Store original querySelector
const originalQuerySelector = document.querySelector.bind(document);
// Mock querySelector while preserving the document context
jest.spyOn(document, "querySelector").mockImplementation(function (selector) {
if (selector === 'meta[name="viewport"]') {
return { setAttribute, getAttribute } as unknown as HTMLMetaElement;
}
return originalQuerySelector.call(document, selector);
});
await TestBed.configureTestingModule({
providers: [
@ -34,9 +49,14 @@ describe("BrowserExtensionPromptComponent", () => {
}).compileComponents();
fixture = TestBed.createComponent(BrowserExtensionPromptComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
afterEach(() => {
jest.restoreAllMocks();
});
it("calls start on initialization", () => {
expect(start).toHaveBeenCalledTimes(1);
});
@ -87,6 +107,33 @@ describe("BrowserExtensionPromptComponent", () => {
const mobileText = fixture.debugElement.query(By.css("p")).nativeElement;
expect(mobileText.textContent.trim()).toBe("reopenLinkOnDesktop");
});
it("sets min-width on the body", () => {
expect(document.body.style.minWidth).toBe("auto");
});
it("stores viewport content", () => {
expect(getAttribute).toHaveBeenCalledWith("content");
expect(component["viewportContent"]).toBe("width=1010");
});
it("sets viewport meta tag to be mobile friendly", () => {
expect(setAttribute).toHaveBeenCalledWith("content", "width=device-width, initial-scale=1.0");
});
describe("on destroy", () => {
beforeEach(() => {
fixture.destroy();
});
it("resets body min-width", () => {
expect(document.body.style.minWidth).toBe("");
});
it("resets viewport meta tag", () => {
expect(setAttribute).toHaveBeenCalledWith("content", "width=1010");
});
});
});
describe("manual error state", () => {

View File

@ -1,5 +1,5 @@
import { CommonModule } from "@angular/common";
import { Component, OnInit } from "@angular/core";
import { CommonModule, DOCUMENT } from "@angular/common";
import { Component, Inject, OnDestroy, OnInit } from "@angular/core";
import { ButtonComponent, IconModule } from "@bitwarden/components";
import { I18nPipe } from "@bitwarden/ui-common";
@ -16,7 +16,7 @@ import {
standalone: true,
imports: [CommonModule, I18nPipe, ButtonComponent, IconModule],
})
export class BrowserExtensionPromptComponent implements OnInit {
export class BrowserExtensionPromptComponent implements OnInit, OnDestroy {
/** Current state of the prompt page */
protected pageState$ = this.browserExtensionPromptService.pageState$;
@ -25,10 +25,39 @@ export class BrowserExtensionPromptComponent implements OnInit {
protected BitwardenIcon = VaultIcons.BitwardenIcon;
constructor(private browserExtensionPromptService: BrowserExtensionPromptService) {}
/** Content of the meta[name="viewport"] element */
private viewportContent: string | null = null;
constructor(
private browserExtensionPromptService: BrowserExtensionPromptService,
@Inject(DOCUMENT) private document: Document,
) {}
ngOnInit(): void {
this.browserExtensionPromptService.start();
// It is not be uncommon for users to hit this page from a mobile device.
// There are global styles and the viewport meta tag that set a min-width
// for the page which cause it to render poorly. Remove them here.
// https://github.com/bitwarden/clients/blob/main/apps/web/src/scss/base.scss#L6
this.document.body.style.minWidth = "auto";
const viewportMeta = this.document.querySelector('meta[name="viewport"]');
// Save the current viewport content to reset it when the component is destroyed
this.viewportContent = viewportMeta?.getAttribute("content") ?? null;
viewportMeta?.setAttribute("content", "width=device-width, initial-scale=1.0");
}
ngOnDestroy(): void {
// Reset the body min-width when the component is destroyed
this.document.body.style.minWidth = "";
if (this.viewportContent !== null) {
this.document
.querySelector('meta[name="viewport"]')
?.setAttribute("content", this.viewportContent);
}
}
openExtension(): void {

View File

@ -1,7 +1,7 @@
import { svgIcon } from "@bitwarden/components";
export const BrowserExtensionIcon = svgIcon`
<svg width="115" height="114" viewBox="0 0 115 114" fill="none" xmlns="http://www.w3.org/2000/svg">
<svg class="tw-max-w-full" width="115" height="114" viewBox="0 0 115 114" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect class="tw-stroke-art-primary" width="107.16" height="70.68" rx="10" transform="matrix(-1 0 0 1 111.08 21.66)" stroke-width="4"/>
<rect class="tw-stroke-art-accent" x="77.88" y="36.91" width="24.79" height="33.34" rx="5" stroke-width="2"/>
<rect class="tw-fill-art-primary" x="97.97" y="25.65" width="5.7" height="5.7" rx="2" />