-
Notifications
You must be signed in to change notification settings - Fork 58
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add stepper and step components
- Loading branch information
1 parent
b03c168
commit 85c995d
Showing
11 changed files
with
868 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
@import "vanilla-framework"; | ||
|
||
.step-number { | ||
border: 0.08rem solid black; | ||
border-radius: 1rem; | ||
height: 1.4rem; | ||
line-height: 1.3; | ||
margin-left: $sph--small; | ||
margin-right: 0.1rem; | ||
margin-top: 0.1rem; | ||
text-align: center; | ||
width: 1.4rem; | ||
} | ||
|
||
.step-number-disabled { | ||
border: 0.08rem solid #757575; | ||
color: #757575; | ||
} | ||
|
||
.step-content { | ||
display: flex; | ||
flex: 1; | ||
flex-direction: column; | ||
margin-left: $sph--small; | ||
} | ||
|
||
.step-enabled:hover { | ||
cursor: pointer; | ||
text-decoration: underline; | ||
} | ||
|
||
.step-disabled { | ||
color: #757575; | ||
pointer-events: none; | ||
} | ||
|
||
.step-status-icon { | ||
height: 1.6rem; | ||
margin-left: 0.4rem; | ||
width: 1.6rem; | ||
} | ||
|
||
.step-selected { | ||
background-color: var(--vf-color-background-alt); | ||
} | ||
|
||
.step-optional-content { | ||
font-size: 12px; | ||
max-width: 10rem; | ||
} | ||
|
||
.stepper-horizontal { | ||
display: flex; | ||
|
||
.p-inline-list__item { | ||
margin: 0; | ||
} | ||
|
||
.step { | ||
border-top: 0.2rem solid var(--vf-color-border-default); | ||
display: flex; | ||
height: 100%; | ||
padding: 0.4rem $spv--medium; | ||
width: fit-content; | ||
} | ||
|
||
.step-status-icon { | ||
margin-left: 0; | ||
} | ||
|
||
.step-number { | ||
margin-left: 0; | ||
} | ||
|
||
.step-content { | ||
max-width: 10rem; | ||
} | ||
|
||
.progress-line { | ||
border-top: 0.2rem solid black; | ||
} | ||
|
||
:first-child .step { | ||
padding-left: 0; | ||
} | ||
} | ||
|
||
.stepper-vertical { | ||
.p-list__item { | ||
padding-bottom: 0; | ||
padding-top: 0; | ||
} | ||
|
||
.step { | ||
border-left: 0.2rem solid var(--vf-color-border-default); | ||
display: flex; | ||
padding: $spv--medium 0; | ||
padding-right: 0.5rem; | ||
width: fit-content; | ||
} | ||
|
||
.progress-line { | ||
border-left: 0.2rem solid black; | ||
} | ||
|
||
:first-child .step { | ||
padding-top: 0; | ||
} | ||
|
||
:last-child .step { | ||
padding-bottom: 0; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import React from "react"; | ||
import { Meta, StoryObj } from "@storybook/react"; | ||
import Step from "./Step"; | ||
import Stepper from "../Stepper"; | ||
|
||
const meta: Meta<typeof Step> = { | ||
component: Step, | ||
render: (args) => ( | ||
<Stepper variant="horizontal" steps={[<Step key="step" {...args} />]} /> | ||
), | ||
tags: ["autodocs"], | ||
}; | ||
|
||
export default meta; | ||
|
||
type Story = StoryObj<typeof Step>; | ||
|
||
export const Default: Story = { | ||
name: "Default", | ||
|
||
args: { | ||
title: "Step 1", | ||
index: 1, | ||
enabled: false, | ||
hasProgressLine: false, | ||
iconName: "number", | ||
handleClick: () => {}, | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
import React from "react"; | ||
import { render, screen } from "@testing-library/react"; | ||
import userEvent from "@testing-library/user-event"; | ||
import Step from "./Step"; | ||
import type { Props } from "./Step"; | ||
|
||
describe("Step component", () => { | ||
const props: Props = { | ||
hasProgressLine: true, | ||
index: 1, | ||
title: "Title", | ||
enabled: true, | ||
iconName: "number", | ||
handleClick: jest.fn(), | ||
}; | ||
|
||
it("renders the step with the required props", () => { | ||
render(<Step {...props} />); | ||
expect(screen.getByText("Title")).toBeInTheDocument(); | ||
expect(screen.getByText("1")).toBeInTheDocument(); | ||
expect(document.querySelector(".progress-line")).toBeInTheDocument(); | ||
}); | ||
|
||
it("can display an icon", () => { | ||
render(<Step {...props} iconName="success" />); | ||
expect(document.querySelector(".p-icon--success")).toBeInTheDocument(); | ||
}); | ||
|
||
it("can remove the progress line", () => { | ||
render(<Step {...props} hasProgressLine={false} />); | ||
expect(document.querySelector(".progress-line")).toBeNull(); | ||
}); | ||
|
||
it("can disable the step", () => { | ||
render(<Step {...props} enabled={false} />); | ||
expect(screen.getByText("Title")).toHaveClass("step-disabled"); | ||
}); | ||
|
||
it("can select the step", () => { | ||
render(<Step {...props} selected={true} />); | ||
expect(document.querySelector(".step-selected")).toBeInTheDocument(); | ||
}); | ||
|
||
it("can call handleClick when clicked", async () => { | ||
render(<Step {...props} />); | ||
await userEvent.click(screen.getByText("Title")); | ||
expect(props.handleClick).toHaveBeenCalled(); | ||
}); | ||
|
||
it("can display optional label", () => { | ||
render(<Step {...props} label="Optional label" />); | ||
|
||
expect(screen.getByText("Optional label")).toBeInTheDocument(); | ||
}); | ||
|
||
it("can display optional link", () => { | ||
const linkProps = { | ||
href: "/test-link", | ||
children: "Link", | ||
}; | ||
render(<Step {...props} linkProps={linkProps} />); | ||
const linkElement = screen.getByRole("link", { name: "Link" }); | ||
expect(linkElement).toBeInTheDocument(); | ||
expect(linkElement).toHaveAttribute("href", "/test-link"); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
import classNames from "classnames"; | ||
import React from "react"; | ||
import Icon from "components/Icon"; | ||
import Link, { LinkProps } from "components/Link"; | ||
import { ClassName } from "types"; | ||
import "./Step.scss"; | ||
|
||
export type Props = { | ||
/** | ||
* Whether the step has a darkened progress line. | ||
*/ | ||
hasProgressLine: boolean; | ||
/** | ||
* Index of the step. | ||
*/ | ||
index: number; | ||
/** | ||
* Title of the step. | ||
*/ | ||
title: string; | ||
/** | ||
* Optional label for the step. | ||
*/ | ||
label?: string; | ||
/** | ||
* Optional props to configure the `Link` component. | ||
*/ | ||
linkProps?: LinkProps; | ||
/** | ||
* Whether the step is clickable. If set to false, the step is not clickable and the text is muted with a light-dark colour. | ||
*/ | ||
enabled: boolean; | ||
/** | ||
* Optional value to highlight the selected step. | ||
*/ | ||
selected?: boolean; | ||
/** | ||
* Icon to display in the step. Specify "number" if the index should be displayed. | ||
*/ | ||
iconName: string; | ||
/** | ||
* Optional class(es) to pass to the Icon component. | ||
*/ | ||
iconClassName?: ClassName; | ||
/** | ||
* Function that is called when the step is clicked. | ||
*/ | ||
handleClick: () => void; | ||
}; | ||
|
||
const Step = ({ | ||
hasProgressLine, | ||
index, | ||
title, | ||
label, | ||
linkProps, | ||
enabled, | ||
selected = false, | ||
iconName, | ||
iconClassName, | ||
handleClick, | ||
...props | ||
}: Props): JSX.Element => { | ||
const stepStatusClass = enabled ? "step-enabled" : "step-disabled"; | ||
|
||
return ( | ||
<div | ||
className={classNames("step", { | ||
"progress-line": hasProgressLine, | ||
"step-selected": selected, | ||
})} | ||
{...props} | ||
> | ||
{iconName === "number" ? ( | ||
<span | ||
className={classNames("step-number", { | ||
"step-number-disabled": !enabled, | ||
})} | ||
> | ||
{index} | ||
</span> | ||
) : ( | ||
<Icon | ||
name={iconName} | ||
className={classNames("step-status-icon", iconClassName)} | ||
/> | ||
)} | ||
<div className="step-content"> | ||
<span className={classNames(stepStatusClass)} onClick={handleClick}> | ||
{title} | ||
</span> | ||
{label && ( | ||
<span | ||
className={classNames( | ||
"step-optional-content", | ||
"u-no-margin--bottom", | ||
{ | ||
"step-disabled": !enabled, | ||
}, | ||
)} | ||
> | ||
{label} | ||
</span> | ||
)} | ||
{linkProps && ( | ||
<Link | ||
className="p-text--small u-no-margin--bottom step-optional-content" | ||
{...linkProps} | ||
> | ||
{linkProps.children} | ||
</Link> | ||
)} | ||
</div> | ||
</div> | ||
); | ||
}; | ||
|
||
export default Step; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export { default } from "./Step"; | ||
export type { Props as StepProps } from "./Step"; |
Oops, something went wrong.