mirror of
https://github.com/bitwarden/browser.git
synced 2025-03-23 15:29:29 +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:
parent
a569dd9ad6
commit
85a5aea897
apps/web/src/app/vault/components/browser-extension-prompt
libs/vault/src/icons
@ -13,12 +13,27 @@ import { BrowserExtensionPromptComponent } from "./browser-extension-prompt.comp
|
|||||||
|
|
||||||
describe("BrowserExtensionPromptComponent", () => {
|
describe("BrowserExtensionPromptComponent", () => {
|
||||||
let fixture: ComponentFixture<BrowserExtensionPromptComponent>;
|
let fixture: ComponentFixture<BrowserExtensionPromptComponent>;
|
||||||
|
let component: BrowserExtensionPromptComponent;
|
||||||
const start = jest.fn();
|
const start = jest.fn();
|
||||||
const pageState$ = new BehaviorSubject(BrowserPromptState.Loading);
|
const pageState$ = new BehaviorSubject(BrowserPromptState.Loading);
|
||||||
|
const setAttribute = jest.fn();
|
||||||
|
const getAttribute = jest.fn().mockReturnValue("width=1010");
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
start.mockClear();
|
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({
|
await TestBed.configureTestingModule({
|
||||||
providers: [
|
providers: [
|
||||||
@ -34,9 +49,14 @@ describe("BrowserExtensionPromptComponent", () => {
|
|||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
|
|
||||||
fixture = TestBed.createComponent(BrowserExtensionPromptComponent);
|
fixture = TestBed.createComponent(BrowserExtensionPromptComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
jest.restoreAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
it("calls start on initialization", () => {
|
it("calls start on initialization", () => {
|
||||||
expect(start).toHaveBeenCalledTimes(1);
|
expect(start).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
@ -87,6 +107,33 @@ describe("BrowserExtensionPromptComponent", () => {
|
|||||||
const mobileText = fixture.debugElement.query(By.css("p")).nativeElement;
|
const mobileText = fixture.debugElement.query(By.css("p")).nativeElement;
|
||||||
expect(mobileText.textContent.trim()).toBe("reopenLinkOnDesktop");
|
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", () => {
|
describe("manual error state", () => {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { CommonModule } from "@angular/common";
|
import { CommonModule, DOCUMENT } from "@angular/common";
|
||||||
import { Component, OnInit } from "@angular/core";
|
import { Component, Inject, OnDestroy, OnInit } from "@angular/core";
|
||||||
|
|
||||||
import { ButtonComponent, IconModule } from "@bitwarden/components";
|
import { ButtonComponent, IconModule } from "@bitwarden/components";
|
||||||
import { I18nPipe } from "@bitwarden/ui-common";
|
import { I18nPipe } from "@bitwarden/ui-common";
|
||||||
@ -16,7 +16,7 @@ import {
|
|||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [CommonModule, I18nPipe, ButtonComponent, IconModule],
|
imports: [CommonModule, I18nPipe, ButtonComponent, IconModule],
|
||||||
})
|
})
|
||||||
export class BrowserExtensionPromptComponent implements OnInit {
|
export class BrowserExtensionPromptComponent implements OnInit, OnDestroy {
|
||||||
/** Current state of the prompt page */
|
/** Current state of the prompt page */
|
||||||
protected pageState$ = this.browserExtensionPromptService.pageState$;
|
protected pageState$ = this.browserExtensionPromptService.pageState$;
|
||||||
|
|
||||||
@ -25,10 +25,39 @@ export class BrowserExtensionPromptComponent implements OnInit {
|
|||||||
|
|
||||||
protected BitwardenIcon = VaultIcons.BitwardenIcon;
|
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 {
|
ngOnInit(): void {
|
||||||
this.browserExtensionPromptService.start();
|
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 {
|
openExtension(): void {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { svgIcon } from "@bitwarden/components";
|
import { svgIcon } from "@bitwarden/components";
|
||||||
|
|
||||||
export const BrowserExtensionIcon = svgIcon`
|
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-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-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" />
|
<rect class="tw-fill-art-primary" x="97.97" y="25.65" width="5.7" height="5.7" rx="2" />
|
||||||
|
Loading…
Reference in New Issue
Block a user