diff --git a/web-wallet/CHANGELOG.md b/web-wallet/CHANGELOG.md index f7f7b5cf00..c6cab6622f 100644 --- a/web-wallet/CHANGELOG.md +++ b/web-wallet/CHANGELOG.md @@ -17,10 +17,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Update anchor colors to ensure better accessibility [#1765] - Update Transactions list design [#1922] - Update Buttons to match the design system [#1606] +- Update `Stepper` component to new design [#2071] ### Fixed - Fix Receive tab content overflows [#1901] +- Add missing "Soehne Mono" and its `@font-face` definition [#2071] ## [0.5.0] - 2024-03-27 @@ -232,6 +234,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 [#1922]: https://github.com/dusk-network/rusk/issues/1922 [#2026]: https://github.com/dusk-network/rusk/issues/2026 [#2000]: https://github.com/dusk-network/rusk/issues/2000 +[#2071]: https://github.com/dusk-network/rusk/issues/2071 diff --git a/web-wallet/src/lib/dusk/components/Stepper/Stepper.svelte b/web-wallet/src/lib/dusk/components/Stepper/Stepper.svelte index 33804cf27a..779e4996e6 100644 --- a/web-wallet/src/lib/dusk/components/Stepper/Stepper.svelte +++ b/web-wallet/src/lib/dusk/components/Stepper/Stepper.svelte @@ -1,43 +1,98 @@ + + -
-
-
-
- - {#if steps >= 2} -
- {#each Array(steps).keys() as currentStep (currentStep)} -
= 2} +
+ {#if Array.isArray(steps)} + {#each steps as currentStep, idx (currentStep)} + {@const id = `step-${randomUUID()}`} + + class:dusk-stepper__step--processed={idx <= activeStep} + aria-current={idx === activeStep ? "step" : undefined} + aria-labelledby={id} + > + {#if currentStep.iconPath} + + {:else} + {showStepNumbers ? idx + 1 : ""} + {/if} + + {currentStep.label} {/each} -
- {/if} -
+ {:else} + {#each Array(steps).keys() as idx (idx)} + {showStepNumbers ? idx + 1 : ""} + {/each} + {/if} +
+{/if} diff --git a/web-wallet/src/lib/dusk/components/Wizard/WizardStep.svelte b/web-wallet/src/lib/dusk/components/Wizard/WizardStep.svelte index 444fbaf5e9..1183692daa 100644 --- a/web-wallet/src/lib/dusk/components/Wizard/WizardStep.svelte +++ b/web-wallet/src/lib/dusk/components/Wizard/WizardStep.svelte @@ -90,7 +90,7 @@ {#if step === currentStep} {#if showStepper} - + {/if} diff --git a/web-wallet/src/lib/dusk/components/__tests__/Stepper.spec.js b/web-wallet/src/lib/dusk/components/__tests__/Stepper.spec.js index 9f531ae969..4630d1063d 100644 --- a/web-wallet/src/lib/dusk/components/__tests__/Stepper.spec.js +++ b/web-wallet/src/lib/dusk/components/__tests__/Stepper.spec.js @@ -1,39 +1,91 @@ -import { afterEach, describe, expect, it } from "vitest"; +import { afterAll, afterEach, describe, expect, it, vi } from "vitest"; import { cleanup, render } from "@testing-library/svelte"; +import { mdiCheckDecagramOutline } from "@mdi/js"; import { Stepper } from ".."; +vi.mock("$lib/dusk/string", async (importOriginal) => { + /** @type {typeof import("$lib/dusk/string")} */ + const original = await importOriginal(); + + return { + ...original, + randomUUID: () => "some-generated-id", + }; +}); + describe("Stepper", () => { + const baseProps = { + activeStep: 2, + steps: [ + { label: "foo" }, + { label: "bar" }, + { label: "baz" }, + { label: "qux" }, + { iconPath: mdiCheckDecagramOutline, label: "quux" }, + ], + }; + const baseOptions = { + props: baseProps, + target: document.body, + }; + afterEach(cleanup); - it("renders the Stepper component with two steps", () => { - const { container } = render(Stepper, { - props: { activeStep: 0, steps: 2 }, - }); + afterAll(() => { + vi.doUnmock("$lib/dusk/string"); + }); + + it("should render the `Stepper` component accepting an array of `StepperStep` objects as steps", async () => { + const { container, rerender } = render(Stepper, baseOptions); + + expect(container.firstChild).toMatchSnapshot(); + + await rerender({ ...baseProps, activeStep: 3 }); expect(container.firstChild).toMatchSnapshot(); }); - it("renders the Stepper component with a completed step", () => { - const { container } = render(Stepper, { - props: { activeStep: 1, steps: 2 }, - }); + it("should render the `Stepper` component accepting a number as the amount of steps", async () => { + const props = { ...baseProps, steps: 5 }; + const { container, rerender } = render(Stepper, { ...baseOptions, props }); + + expect(container.firstChild).toMatchSnapshot(); + + await rerender({ ...baseProps, activeStep: 3 }); + + expect(container.firstChild).toMatchSnapshot(); + }); + + it("should pass additional class names and attributes to the rendered element", () => { + const props = { + ...baseProps, + className: "foo bar", + id: "some-id", + }; + const { container } = render(Stepper, { ...baseOptions, props }); expect(container.firstChild).toMatchSnapshot(); }); - it("renders the Stepper component with five steps", () => { - const { container } = render(Stepper, { - props: { activeStep: 0, steps: 5 }, - }); + it("should add the proper class name for the desired variant", () => { + const props = { + ...baseProps, + + /** @type {StepperVariant} */ + variant: "secondary", + }; + const { container } = render(Stepper, { ...baseOptions, props }); expect(container.firstChild).toMatchSnapshot(); }); - it("renders the Stepper component with five steps, with the third one being active, and the first two – completed", () => { - const { container } = render(Stepper, { - props: { activeStep: 3, steps: 5 }, - }); + it("should allow to hide the step numbers", () => { + const props = { + ...baseProps, + showStepNumbers: false, + }; + const { container } = render(Stepper, { ...baseOptions, props }); expect(container.firstChild).toMatchSnapshot(); }); diff --git a/web-wallet/src/lib/dusk/components/__tests__/__snapshots__/Stepper.spec.js.snap b/web-wallet/src/lib/dusk/components/__tests__/__snapshots__/Stepper.spec.js.snap index aeb0ef98a3..e0ac9f7bad 100644 --- a/web-wallet/src/lib/dusk/components/__tests__/__snapshots__/Stepper.spec.js.snap +++ b/web-wallet/src/lib/dusk/components/__tests__/__snapshots__/Stepper.spec.js.snap @@ -1,155 +1,541 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`Stepper > renders the Stepper component with a completed step 1`] = ` +exports[`Stepper > should add the proper class name for the desired variant 1`] = `
-
-
-
- -
-
-
-
+ + 1 + + + + foo + + + 2 + + + + bar + + + 3 + + + + baz + + + 4 + + + + qux + + + + + + + + + + quux + + +
+`; + +exports[`Stepper > should allow to hide the step numbers 1`] = ` +
+ + + + + + foo + + + + + + + bar + + + + + + + baz + + + + + + + qux + + + + + + + + + + quux + + +
+`; + +exports[`Stepper > should pass additional class names and attributes to the rendered element 1`] = ` +
+ + 1 + + + + foo + + + 2 + + + + bar + + + 3 + + + + baz + + + 4 + + + + qux + + + + + + + + + + quux + +
`; -exports[`Stepper > renders the Stepper component with five steps 1`] = ` +exports[`Stepper > should render the \`Stepper\` component accepting a number as the amount of steps 1`] = `
-
-
-
- -
-
-
-
-
-
-
+ + 1 + + + 2 + + + 3 + + + 4 + + + 5 + +
`; -exports[`Stepper > renders the Stepper component with five steps, with the third one being active, and the first two – completed 1`] = ` +exports[`Stepper > should render the \`Stepper\` component accepting a number as the amount of steps 2`] = `
-
-
-
- -
-
-
-
-
-
-
+ + 1 + + + + foo + + + 2 + + + + bar + + + 3 + + + + baz + + + 4 + + + + qux + + + + + + + + + + quux + +
`; -exports[`Stepper > renders the Stepper component with two steps 1`] = ` +exports[`Stepper > should render the \`Stepper\` component accepting an array of \`StepperStep\` objects as steps 1`] = `
-
-
-
- -
-
-
-
+ + 1 + + + + foo + + + 2 + + + + bar + + + 3 + + + + baz + + + 4 + + + + qux + + + + + + + + + + quux + + +
+`; + +exports[`Stepper > should render the \`Stepper\` component accepting an array of \`StepperStep\` objects as steps 2`] = ` +
+ + 1 + + + + foo + + + 2 + + + + bar + + + 3 + + + + baz + + + 4 + + + + qux + + + + + + + + + + quux + +
`; diff --git a/web-wallet/src/lib/dusk/components/dusk.components.d.ts b/web-wallet/src/lib/dusk/components/dusk.components.d.ts index 6ef80fece2..0412d875e6 100644 --- a/web-wallet/src/lib/dusk/components/dusk.components.d.ts +++ b/web-wallet/src/lib/dusk/components/dusk.components.d.ts @@ -34,6 +34,13 @@ type SelectOption = { value: string; }; +type StepperStep = { + iconPath?: string; + label: string; +}; + +type StepperVariant = "primary" | "secondary"; + type SuspenseErrorVariant = "alert" | "details"; type TabItem = { diff --git a/web-wallet/src/routes/(welcome)/setup/create/__tests__/__snapshots__/page.spec.js.snap b/web-wallet/src/routes/(welcome)/setup/create/__tests__/__snapshots__/page.spec.js.snap index 7a9f01b3f1..95d257c130 100644 --- a/web-wallet/src/routes/(welcome)/setup/create/__tests__/__snapshots__/page.spec.js.snap +++ b/web-wallet/src/routes/(welcome)/setup/create/__tests__/__snapshots__/page.spec.js.snap @@ -19,53 +19,43 @@ exports[`Create > correctly renders the Mnemonic Preview page 1`] = `
-
-
-
- -
+ -
-
-
-
-
-
-
+ 2 + + + 3 + + + 4 + + + 5 + + + 6 + +
+
correctly renders the Mnemonic Verification page 1`] = `
-
-
-
- -
+ -
-
-
-
-
-
-
+ 2 + + + 3 + + + 4 + + + 5 + + + 6 + +
+
doesn't let the user proceed if they have entered mismatching
-
-
-
- -
+ -
-
-
-
-
-
-
+ 2 + + + 3 + + + 4 + + + 5 + + + 6 + +
+
ensures that the Undo button on the Mnemonic Validate step wor
-
-
-
- -
+ -
-
-
-
-
-
-
+ 2 + + + 3 + + + 4 + + + 5 + + + 6 + +
+
ensures the All Done step renders as expected 1`] = `
-
-
-
- -
+ -
-
-
-
-
-
-
+ 2 + + + 3 + + + 4 + + + 5 + + + 6 + +
+
ensures the Password step renders as expected 1`] = `
-
-
-
- -
+ -
-
-
-
-
-
-
+ 2 + + + 3 + + + 4 + + + 5 + + + 6 + +
+
ensures the Password step renders as expected 2`] = `
-
-
-
- -
+ -
-
-
-
-
-
-
+ 2 + + + 3 + + + 4 + + + 5 + + + 6 + +
+
ensures the Swap To Native Dusk step renders as expected 1`] =
-
-
-
- -
+ -
-
-
-
-
-
-
+ 2 + + + 3 + + + 4 + + + 5 + + + 6 + +
+

The ERC20 token swap functionality is currently disabled and will be provided @@ -2227,53 +2147,43 @@ exports[`Create > lets the user proceed if they have entered a matching Mnemonic

-
-
-
- -
+ -
-
-
-
-
-
-
+ 2 + + + 3 + + + 4 + + + 5 + + + 6 + +
+
should render the \`Securely store your seed phrase!\` agreeme
-
-
-
- -
+ -
-
-
-
-
-
-
+ 2 + + + 3 + + + 4 + + + 5 + + + 6 + +
+
should render the Mnemonic Authenticate step after accepting
-
-
-
- -
+ -
-
-
-
+ 2 + + + 3 + +
+
+ -
- - +
+
+ + +
+
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+ +
+ +
+ + diff --git a/web-wallet/src/style/dusk-components/stepper.css b/web-wallet/src/style/dusk-components/stepper.css index 9318fe4146..8b98cd84ba 100644 --- a/web-wallet/src/style/dusk-components/stepper.css +++ b/web-wallet/src/style/dusk-components/stepper.css @@ -1,38 +1,76 @@ .dusk-stepper { + --step-size: 1.5em; + position: relative; width: 100%; + display: grid; + row-gap: var(--default-gap); + grid-template-columns: repeat(var(--columns), 1fr); + justify-content: space-between; } -.dusk-stepper__progress-bar { +.dusk-stepper::before, +.dusk-stepper::after { + --left-offset: calc(50% / var(--columns)); + + content: ""; + display: block; position: absolute; - top: 50%; + top: calc(var(--step-size) / 2); + left: var(--left-offset); transform: translateY(-50%); - width: 100%; - height: var(--progress-bar-height); - background-color: var(--progress-bg-color); + height: var(--stepper-bar-height); border-radius: var(--control-border-radius-size); - overflow: hidden; } -.dusk-stepper__progress-filler { - height: 100%; - background-color: var(--progress-filler-color); +.dusk-stepper::before { + width: calc(100% - var(--left-offset) * 2); + background-color: var(--stepper-rail-color); } -.dusk-stepper__steps { - display: flex; - justify-content: space-between; - width: 100%; +.dusk-stepper--variant--secondary::before { + background-color: var(--stepper-rail-color-variant-secondary); +} + +.dusk-stepper::after { + width: var(--progress-width); + background-color: var(--stepper-filler-color); } .dusk-stepper__step { - width: 0.625em; - height: 0.625em; - background-color: var(--progress-bg-color); + grid-row: 1; + display: inline-flex; + align-items: center; + justify-content: center; + width: var(--step-size); + height: var(--step-size); + font-family: var(--mono-font-family); + line-height: 1; + background-color: var(--stepper-rail-color); + color: var(--stepper-on-rail-color); border-radius: 50%; z-index: 1; + margin: 0 auto; +} + +.dusk-stepper--variant--secondary + .dusk-stepper__step:not(.dusk-stepper__step--processed) { + background-color: var(--stepper-rail-color-variant-secondary); + color: var(--stepper-on-rail-variant-secondary-color); } .dusk-stepper__step--processed { - background-color: var(--progress-filler-color); + background-color: var(--stepper-filler-color); + color: var(--stepper-on-filler-color); +} + +.dusk-stepper__step-label { + grid-row: 2; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + font-family: var(--mono-font-family); + font-size: 0.5em; + text-transform: uppercase; + text-align: center; } diff --git a/web-wallet/src/style/dusk/language.css b/web-wallet/src/style/dusk/language.css index 00c49de720..0e094d6e0e 100644 --- a/web-wallet/src/style/dusk/language.css +++ b/web-wallet/src/style/dusk/language.css @@ -84,12 +84,22 @@ --anchor-color-hover: var(--secondary-color-variant-light); --anchor-color-active: var(--secondary-color-variant-light); - /* Progress-related components (Stepper, Progress Bar, etc */ + /* Progress bars */ --progress-bar-height: 0.3125em; --progress-bg-color: var(--primary-color); --progress-filler-color: var(--success-color); + /* Steppers */ + + --stepper-bar-height: 0.3125em; + --stepper-rail-color: var(--background-color); + --stepper-rail-color-variant-secondary: var(--primary-color); + --stepper-filler-color: var(--success-color); + --stepper-on-filler-color: var(--on-success-color); + --stepper-on-rail-color: var(--on-background-color); + --stepper-on-rail-variant-secondary-color: var(--on-primary-color); + /* Checkbox control */ --checkbox-control-size: 1.2em; diff --git a/web-wallet/src/style/main.css b/web-wallet/src/style/main.css index ff620c4135..3e99a04f9c 100644 --- a/web-wallet/src/style/main.css +++ b/web-wallet/src/style/main.css @@ -32,17 +32,33 @@ @font-face { font-family: "Soehne"; font-style: normal; + font-display: swap; font-weight: 400; src: url("/fonts/soehne-buch.woff2") format("woff2"); - font-display: swap; } @font-face { font-family: "Soehne"; font-style: normal; + font-display: swap; font-weight: 500; src: url("/fonts/soehne-kraftig.woff2") format("woff2"); +} + +@font-face { + font-family: "Soehne Mono"; + font-style: normal; font-display: swap; + font-weight: 400; + src: url("/fonts/soehne-mono-buch.woff2") format("woff2"); +} + +@font-face { + font-family: "Soehne Mono"; + font-style: normal; + font-display: swap; + font-weight: 500; + src: url("/fonts/soehne-mono-kraftig.woff2") format("woff2"); } * { diff --git a/web-wallet/static/fonts/soehne-mono-buch.woff2 b/web-wallet/static/fonts/soehne-mono-buch.woff2 new file mode 100644 index 0000000000..4fc6eb90b5 Binary files /dev/null and b/web-wallet/static/fonts/soehne-mono-buch.woff2 differ diff --git a/web-wallet/static/fonts/soehne-mono-kraftig.woff2 b/web-wallet/static/fonts/soehne-mono-kraftig.woff2 new file mode 100644 index 0000000000..f17feb5289 Binary files /dev/null and b/web-wallet/static/fonts/soehne-mono-kraftig.woff2 differ