Skip to content

Commit

Permalink
Merge pull request #2099 from dusk-network/feature-2071
Browse files Browse the repository at this point in the history
web-wallet: Update `Stepper` component to new design
  • Loading branch information
ascartabelli authored Aug 12, 2024
2 parents db9bf46 + 0186383 commit 8bfe398
Show file tree
Hide file tree
Showing 14 changed files with 1,265 additions and 659 deletions.
3 changes: 3 additions & 0 deletions web-wallet/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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

<!-- VERSIONS -->

Expand Down
107 changes: 81 additions & 26 deletions web-wallet/src/lib/dusk/components/Stepper/Stepper.svelte
Original file line number Diff line number Diff line change
@@ -1,43 +1,98 @@
<svelte:options immutable={true} />

<script>
import { makeClassName, randomUUID } from "$lib/dusk/string";
import { Icon } from "..";
/**
* The number of steps – should be greater or equal to two.
* The current active step.
* The value starts from zero as it refers
* to the `steps` array elements.
* @type {number}
*/
export let activeStep;
/** @type {string | undefined} */
export let className = undefined;
/**
* Whether to show step numbers or not.
* @type {boolean}
*/
export let showStepNumbers = true;
/**
* The number of steps, greater or equal to two,
* if a number is passed.
* An array of `StepperStep` objects otherwise.
*
* @type {StepperStep[] | number}
*/
export let steps;
/** @type {number} */
export let activeStep;
/** @type {StepperVariant} */
export let variant = "primary";
$: classes = makeClassName([
"dusk-stepper",
`dusk-stepper--variant--${variant}`,
className,
]);
$: stepsAmount = Array.isArray(steps) ? steps.length : steps;
/**
* Calculates the progress percentage based on the active step in a stepper.
* The width of the bar connecting the steps, based on
* the active step and on the amount of steps.
* As the steps are in a grid and centered in the containing
* cell, the width doesn't represent the actual progress percentage.
*
* @constant
* @type {string}
*
* @example
* // If there are 5 steps in total and the active step is 2
* // progress will be "width: 50%;"
*
* @param {number} steps - Total number of steps in the stepper.
* @param {number} activeStep - The current active step index (starting from 0).
* With 2 steps, if the active step is 1 the width will be 80%.
* The remaining 20% is the blank space before and after the steps.
*
* If there are 5 steps in total and the active step is 2,
* the width will be 40%.
*/
$: progress = `width: ${(100 / (steps - 1)) * activeStep}%;`;
$: progressWidth = `${(100 * activeStep) / stepsAmount}%`;
</script>

<div class="dusk-stepper" role="tablist">
<div class="dusk-stepper__progress-bar">
<div class="dusk-stepper__progress-filler" style={progress} />
</div>

{#if steps >= 2}
<div class="dusk-stepper__steps">
{#each Array(steps).keys() as currentStep (currentStep)}
<div
{#if stepsAmount >= 2}
<div
class={classes}
style:--columns={stepsAmount}
style:--progress-width={progressWidth}
{...$$restProps}
>
{#if Array.isArray(steps)}
{#each steps as currentStep, idx (currentStep)}
{@const id = `step-${randomUUID()}`}
<span
class="dusk-stepper__step"
class:dusk-stepper__step--processed={currentStep <= activeStep}
aria-selected={currentStep === activeStep}
aria-disabled="true"
/>
class:dusk-stepper__step--processed={idx <= activeStep}
aria-current={idx === activeStep ? "step" : undefined}
aria-labelledby={id}
>
{#if currentStep.iconPath}
<Icon path={currentStep.iconPath} />
{:else}
{showStepNumbers ? idx + 1 : ""}
{/if}
</span>
<span class="dusk-stepper__step-label" {id}>{currentStep.label}</span>
{/each}
</div>
{/if}
</div>
{:else}
{#each Array(steps).keys() as idx (idx)}
<span
class="dusk-stepper__step"
class:dusk-stepper__step--processed={idx <= activeStep}
aria-current={idx === activeStep ? "step" : undefined}
>{showStepNumbers ? idx + 1 : ""}</span
>
{/each}
{/if}
</div>
{/if}
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@
{#if step === currentStep}
<slot name="heading" />
{#if showStepper}
<Stepper steps={stepsCount} activeStep={currentStep} />
<Stepper steps={stepsCount} activeStep={currentStep} variant="secondary" />
{/if}
<slot />
Expand Down
86 changes: 69 additions & 17 deletions web-wallet/src/lib/dusk/components/__tests__/Stepper.spec.js
Original file line number Diff line number Diff line change
@@ -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();
});
Expand Down
Loading

0 comments on commit 8bfe398

Please sign in to comment.