diff --git a/src/frontend/src/components/authenticateBox.ts b/src/frontend/src/components/authenticateBox.ts index 0eb1748979..9a2fb9ba1e 100644 --- a/src/frontend/src/components/authenticateBox.ts +++ b/src/frontend/src/components/authenticateBox.ts @@ -34,8 +34,21 @@ import { withLoader } from "./loader"; import { mainWindow } from "./mainWindow"; import { promptUserNumber } from "./promptUserNumber"; +import { getDapps } from "$src/flows/dappsExplorer/dapps"; +import { dappsHeader } from "$src/flows/dappsExplorer/teaser"; +import { shuffleArray } from "$src/utils/utils"; + import copyJson from "./authenticateBox.json"; +/** dapps visual used in sidebar */ +const dappsVisual = (): TemplateResult => { + const dapps = shuffleArray(getDapps()); + return dappsHeader({ + dapps, + clickable: false, + }); +}; + /** Template used for rendering specific authentication screens. See `authnPages` below * for meaning of "firstTime", "useExisting" and "pick". */ export type AuthnTemplates = { @@ -321,11 +334,68 @@ export const authenticate = async ( }; // Wrap the template with header & footer and render the page +// we are including the logo because of SVG ID conflicts, instead of using the +// logo from the main window const page = (slot: TemplateResult) => { const template = mainWindow({ slot: html`

Internet Identity

${slot}`, + slotSidebar: html``, }); const container = document.getElementById("pageContent") as HTMLElement; render(template, container); diff --git a/src/frontend/src/components/icons.ts b/src/frontend/src/components/icons.ts index 89535ba281..5e83bfc555 100644 --- a/src/frontend/src/components/icons.ts +++ b/src/frontend/src/components/icons.ts @@ -94,7 +94,6 @@ export const icLogo = html`

Internet Identity

- `; export const closeIcon = html` diff --git a/src/frontend/src/components/mainWindow.ts b/src/frontend/src/components/mainWindow.ts index adb2ecb4eb..64cbc97bf8 100644 --- a/src/frontend/src/components/mainWindow.ts +++ b/src/frontend/src/components/mainWindow.ts @@ -16,6 +16,7 @@ import { icLogo } from "./icons"; */ export const mainWindow = ({ slot, + slotSidebar, id, showFooter = true, showLogo = true, @@ -23,6 +24,7 @@ export const mainWindow = ({ additionalContainerClasses = [], }: { slot: TemplateResult; + slotSidebar?: TemplateResult; id?: string; showFooter?: boolean; showLogo?: boolean; @@ -37,16 +39,37 @@ export const mainWindow = ({ if (additionalContainerClasses.length > 0) { containerClasses.push(...additionalContainerClasses); } + + const wrapClasses = ["l-wrap"]; + + if (slotSidebar !== undefined) { + wrapClasses.push("l-wrap--sidebar"); + } + return html` -
- ${showLogo ? html`` : ""} -
-
${slot}
+
+ ${slotSidebar !== undefined ? slotSidebar : ""} +
+ ${showLogo + ? // when there is a sidebar, the logo is hidden on desktop + // because it typically comes inside the sidebar + html`` + : ""} +
+
${slot}
+
+ ${showFooter ? footer : ""}
- ${showFooter ? footer : ""} + `; }; diff --git a/src/frontend/src/styles/main.css b/src/frontend/src/styles/main.css index 15065f2a8a..49395542ba 100644 --- a/src/frontend/src/styles/main.css +++ b/src/frontend/src/styles/main.css @@ -135,6 +135,8 @@ --vs-bezel: calc(var(--vs-gutter) * 2); /* 4 */ --vs-line: 1px; /* 5 */ + --vs-gutter-relative: 5vmax; + --vs-line-height: 1.4; --vs-border-radius: 0.8rem; @@ -166,6 +168,9 @@ --rc-background: var(--rc-light); --rc-background-transparent: var(--rc-light-transparent); + --rc-sidebar: var(--vc-brand-purple); + --rc-onSidebar: var(--vc-snow); + --rc-footer: var(--rc-dark); --rc-onFooter: var(--rc-light); @@ -239,6 +244,9 @@ --rg-brand-bruised: var(--vc-brand-blue) 50%, var(--vc-brand-purple) 90%; /* reference tokens: sizes */ + --rs-bezel-layout: var(--vs-gutter-relative); + --rs-bezel-sidebar: calc(var(--vs-gutter-relative) * 0.5); + --rs-inline-grid-gap: var(--vs-inline); --rs-inline-icon-gap: var(--vs-inline); @@ -602,33 +610,63 @@ a:hover, width: 100%; } -#pageContent, .l-wrap { position: relative; display: flex; flex-direction: column; align-items: center; min-height: 100vh; - padding: 5vmax; + padding: var(--rs-bezel-layout); } @media (max-width: 512px) { - #pageContent, .l-wrap { padding: var(--rs-card-bezel); } } +.l-wrap--sidebar { + --sidebar-width: calc(30rem + var(--rs-bezel-sidebar) * 2); + padding-left: var(--sidebar-width); +} + +.l-sidebar { + position: fixed; + left: 0; + top: 0; + bottom: 0; + display: flex; + flex-direction: column; + width: calc(30rem + var(--rs-bezel-sidebar) * 2); + background: var(--rc-sidebar); + color: var(--rc-onSidebar); + padding: var(--rs-bezel-sidebar); +} + +.l-sidebar__main { + flex: 1; + display: flex; + flex-direction: column; + gap: 2rem; +} + +.l-sidebar__decoration { + position: relative; + margin: 0 calc(var(--rs-bezel-sidebar) * -1) + calc(var(--rs-bezel-sidebar) * -1); +} + .l-container { position: relative; font-size: 1.6rem; - min-width: 40rem; - max-width: 40rem; + width: 40rem; /* centers the container and adds a bit of space to make sure the footer does not stick to it */ margin: 0 auto 2rem; } .l-container--wide { + width: auto; + min-width: 40rem; max-width: 60rem; } @@ -645,6 +683,16 @@ a:hover, right: 0; } +.l-wrap--sidebar .l-footer { + left: var(--sidebar-width); +} + +@media (max-width: 512px) { + .l-wrap--sidebar .l-footer { + left: 0; + } +} + /** * Title with counter and Actions */ @@ -1147,6 +1195,10 @@ by all browsers (FF is missing) */ gap: 1em; } +.c-logo--sidebar { + padding: var(--rs-logo-stack--top) 0 var(--rs-logo-stack--bottom); +} + .c-logo svg { display: block; width: 5.5rem; @@ -2364,7 +2416,6 @@ a.c-action-list__item { /** responsiveness */ @media (max-width: 512px) { - #pageContent, .l-wrap { justify-content: flex-start; padding: 0; @@ -2383,6 +2434,18 @@ a.c-action-list__item { display: none !important; } +@media (max-width: 512px) { + .is-hidden--mobile { + display: none !important; + } +} + +@media (min-width: 513px) { + .is-hidden--desktop { + display: none !important; + } +} + .is-visible { display: block !important; } diff --git a/src/frontend/src/test-e2e/addDevice.test.ts b/src/frontend/src/test-e2e/addDevice.test.ts index c816e4557d..71748d9746 100644 --- a/src/frontend/src/test-e2e/addDevice.test.ts +++ b/src/frontend/src/test-e2e/addDevice.test.ts @@ -49,10 +49,9 @@ test("Add device", async () => { const addRemoteDeviceInstructionsView = new AddRemoteDeviceInstructionsView( browser ); + await addRemoteDeviceInstructionsView.waitForDisplay(); await addRemoteDeviceInstructionsView.addFIDODevice(); - await browser.pause(10_000); - // success page const addDeviceSuccessView = new AddDeviceSuccessView(browser); await addDeviceSuccessView.waitForDisplay(); diff --git a/src/frontend/src/test-e2e/views.ts b/src/frontend/src/test-e2e/views.ts index 2174b0a63f..247fc5752c 100644 --- a/src/frontend/src/test-e2e/views.ts +++ b/src/frontend/src/test-e2e/views.ts @@ -366,10 +366,16 @@ export class AddRemoteDeviceInstructionsView extends View { } async cancel(): Promise { + await this.browser.execute( + "window.scrollTo(0, document.body.scrollHeight)" + ); await this.browser.$("#cancelAddRemoteDevice").click(); } async addFIDODevice(): Promise { + await this.browser.execute( + "window.scrollTo(0, document.body.scrollHeight)" + ); await this.browser.$('[data-action="use-fido"]').click(); } @@ -408,7 +414,10 @@ export class AddDeviceSuccessView extends View { private readonly SELECTOR = "[data-action='next']"; async waitForDisplay(): Promise { - await this.browser.$(this.SELECTOR).waitForDisplayed({ timeout: 5_000 }); + await this.browser.execute( + "window.scrollTo(0, document.body.scrollHeight)" + ); + await this.browser.$(this.SELECTOR).waitForDisplayed({ timeout: 15_000 }); } async continue(): Promise {