Skip to content

Commit afcc1f2

Browse files
committed
beforeStepChange
1 parent 0fc9422 commit afcc1f2

File tree

9 files changed

+194
-159
lines changed

9 files changed

+194
-159
lines changed

README.md

+4-4
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,10 @@ Using [npm](https://www.npmjs.com/):
3939

4040
## Documentation
4141

42-
| Component | Description |
43-
| ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
44-
| `<Steps />` | Wrapper component for Step components. Step components must be wrapped in `<Steps />` component. |
45-
| `<Step />` | This is the component you create for each `step` in your application. The `title` prop takes a title for the step, which is provided back in `props` in the step component. The `component` prop takes the component that you would like to show in that step. |
42+
| Component | Description |
43+
| ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
44+
| `<Steps />` | Wrapper component for Step components. Step components must be wrapped in `<Steps />` component. |
45+
| `<Step />` | This is the component you create for each `step` in your application. <br />`title` prop takes a title for the step, which is provided back in `props` in the step component. <br />`component` prop takes the component that you would like to show in that step. <br />`beforeStepChange` prop takes a callback function to run right before the current step is about to change. |
4646

4747
<br />
4848

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "react-step-builder",
33
"description": "Unopinionated multi step interface builder.",
44
"author": "Samet Mutevelli <[email protected]> (https://sametmutevelli.com)",
5-
"version": "1.1.14",
5+
"version": "1.1.15",
66
"private": false,
77
"devDependencies": {
88
"@babel/cli": "^7.8.4",

src/App.js

+4-152
Original file line numberDiff line numberDiff line change
@@ -1,157 +1,9 @@
11
import React from "react";
22
import { Steps, Step } from "./lib";
3-
4-
const Step1 = (props) => {
5-
return (
6-
<div className="step">
7-
First Name:{" "}
8-
<input
9-
name="firstname"
10-
data-testid="firstname"
11-
value={props.getState("firstname")}
12-
onChange={props.handleChange}
13-
/>
14-
Last Name:{" "}
15-
<input
16-
name="lastname"
17-
data-testid="lastname"
18-
value={props.getState("lastname")}
19-
onChange={props.handleChange}
20-
/>
21-
Over 18?{" "}
22-
<input
23-
type="checkbox"
24-
name="over18"
25-
data-testid="checkbox"
26-
checked={props.getState("over18")}
27-
onChange={props.handleChange}
28-
/>
29-
<span>
30-
<button
31-
data-testid="green"
32-
onClick={() => props.setState("color", "green")}
33-
>
34-
Green
35-
</button>
36-
<button
37-
data-testid="blue"
38-
onClick={() => props.setState("color", "blue")}
39-
>
40-
Blue
41-
</button>
42-
</span>
43-
{
44-
<button disabled={props.step.isFirst()} onClick={props.prev}>
45-
Previous
46-
</button>
47-
}
48-
{props.step.hasNext() && <button onClick={props.next}>Next</button>}
49-
<button data-testid="jump3" onClick={() => props.jump(3)}>
50-
Jump to 3. Step
51-
</button>
52-
<button data-testid="jump5" onClick={() => props.jump(5)}>
53-
Jump to 5. Step
54-
</button>
55-
</div>
56-
);
57-
};
58-
59-
const Step2 = (props) => {
60-
return (
61-
<div className="step">
62-
Email:{" "}
63-
<input
64-
name="email"
65-
data-testid="email"
66-
value={props.getState("email")}
67-
onChange={props.handleChange}
68-
/>
69-
Password:{" "}
70-
<input
71-
name="password"
72-
data-testid="password"
73-
value={props.getState("password")}
74-
onChange={props.handleChange}
75-
/>
76-
{props.step.hasPrev() && <button onClick={props.prev}>Previous</button>}
77-
{props.step.hasNext() && <button onClick={props.next}>Next</button>}
78-
</div>
79-
);
80-
};
81-
82-
const Step3 = (props) => {
83-
return (
84-
<div className="step">
85-
Address:{" "}
86-
<input
87-
name="address"
88-
data-testid="address"
89-
value={props.getState("address")}
90-
onChange={props.handleChange}
91-
/>
92-
Phone:{" "}
93-
<input
94-
name="phone"
95-
data-testid="phone"
96-
value={props.getState("phone")}
97-
onChange={props.handleChange}
98-
/>
99-
{props.step.hasPrev() && <button onClick={props.prev}>Previous</button>}
100-
{props.step.hasNext() && <button onClick={props.next}>Next</button>}
101-
</div>
102-
);
103-
};
104-
105-
const Step4 = (props) => {
106-
return (
107-
<div className="step">
108-
<p>
109-
<strong>Name:</strong>
110-
<span data-testid="final_name">
111-
{props.getState("firstname")} {props.getState("lastname")}
112-
</span>
113-
</p>
114-
<p>
115-
<strong>Email:</strong>
116-
<span data-testid="final_email">{props.getState("email")}</span>
117-
</p>
118-
<p>
119-
<strong>Password:</strong>
120-
<span data-testid="final_pass">{props.getState("password")}</span>
121-
</p>
122-
<p>
123-
<strong>Address:</strong>
124-
<span data-testid="final_address">{props.getState("address")}</span>
125-
</p>
126-
<p>
127-
<strong>Phone:</strong>
128-
<span data-testid="final_phone">{props.getState("phone")}</span>
129-
</p>
130-
<p>
131-
<strong>Color Choice:</strong>
132-
<span data-testid="final_color">{props.getState("color")}</span>
133-
</p>
134-
{props.step.hasPrev() && <button onClick={props.prev}>Previous</button>}
135-
{
136-
<button
137-
data-testid="last-next"
138-
onClick={props.next}
139-
disabled={props.step.isLast()}
140-
>
141-
Next
142-
</button>
143-
}
144-
<div>
145-
{props.allSteps().map(({ order, title }) => (
146-
<p key={order}>
147-
<span data-testid={`order ${order}`}>{order}</span>{" "}
148-
<span data-testid={`title ${order}`}>{title}</span>
149-
</p>
150-
))}
151-
</div>
152-
</div>
153-
);
154-
};
3+
import Step1 from "./stepComponents/Step1";
4+
import Step2 from "./stepComponents/Step2";
5+
import Step3 from "./stepComponents/Step3";
6+
import Step4 from "./stepComponents/Step4";
1557

1568
const App = () => {
1579
return (

src/lib/index.js

+9-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useState } from "react";
1+
import React, { useEffect, useState } from "react";
22
import { StepBuilder } from "./StepBuilder";
33

44
var StepContext = React.createContext();
@@ -79,10 +79,17 @@ export function useStepState(INITIAL_VALUE) {
7979
export function Step(props) {
8080
var Component = props.component,
8181
current = props.current,
82-
step = props.step;
82+
step = props.step,
83+
beforeStepChange = props.beforeStepChange;
8384

8485
var context = React.useContext(StepContext);
8586

87+
useEffect(() => {
88+
return () => {
89+
if (current === step.order && beforeStepChange) beforeStepChange();
90+
};
91+
}, [current, step.order, beforeStepChange]);
92+
8693
if (current === step.order) {
8794
return <Component {...context} current={current} step={step} />;
8895
}

src/stepComponents/Step1.js

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import React from "react";
2+
3+
export default (props) => {
4+
return (
5+
<div className="step">
6+
First Name:{" "}
7+
<input
8+
name="firstname"
9+
data-testid="firstname"
10+
value={props.getState("firstname")}
11+
onChange={props.handleChange}
12+
/>
13+
Last Name:{" "}
14+
<input
15+
name="lastname"
16+
data-testid="lastname"
17+
value={props.getState("lastname")}
18+
onChange={props.handleChange}
19+
/>
20+
Over 18?{" "}
21+
<input
22+
type="checkbox"
23+
name="over18"
24+
data-testid="checkbox"
25+
checked={props.getState("over18")}
26+
onChange={props.handleChange}
27+
/>
28+
<span>
29+
<button
30+
data-testid="green"
31+
onClick={() => props.setState("color", "green")}
32+
>
33+
Green
34+
</button>
35+
<button
36+
data-testid="blue"
37+
onClick={() => props.setState("color", "blue")}
38+
>
39+
Blue
40+
</button>
41+
</span>
42+
{
43+
<button disabled={props.step.isFirst()} onClick={props.prev}>
44+
Previous
45+
</button>
46+
}
47+
{props.step.hasNext() && <button onClick={props.next}>Next</button>}
48+
<button data-testid="jump3" onClick={() => props.jump(3)}>
49+
Jump to 3. Step
50+
</button>
51+
<button data-testid="jump5" onClick={() => props.jump(5)}>
52+
Jump to 5. Step
53+
</button>
54+
</div>
55+
);
56+
};

src/stepComponents/Step2.js

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import React from "react";
2+
3+
export default (props) => {
4+
return (
5+
<div className="step">
6+
Email:{" "}
7+
<input
8+
name="email"
9+
data-testid="email"
10+
value={props.getState("email")}
11+
onChange={props.handleChange}
12+
/>
13+
Password:{" "}
14+
<input
15+
name="password"
16+
data-testid="password"
17+
value={props.getState("password")}
18+
onChange={props.handleChange}
19+
/>
20+
{props.step.hasPrev() && <button onClick={props.prev}>Previous</button>}
21+
{props.step.hasNext() && <button onClick={props.next}>Next</button>}
22+
</div>
23+
);
24+
};

src/stepComponents/Step3.js

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import React from "react";
2+
3+
export default (props) => {
4+
return (
5+
<div className="step">
6+
Address:{" "}
7+
<input
8+
name="address"
9+
data-testid="address"
10+
value={props.getState("address")}
11+
onChange={props.handleChange}
12+
/>
13+
Phone:{" "}
14+
<input
15+
name="phone"
16+
data-testid="phone"
17+
value={props.getState("phone")}
18+
onChange={props.handleChange}
19+
/>
20+
{props.step.hasPrev() && <button onClick={props.prev}>Previous</button>}
21+
{props.step.hasNext() && <button onClick={props.next}>Next</button>}
22+
</div>
23+
);
24+
};

src/stepComponents/Step4.js

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import React from "react";
2+
3+
export default (props) => {
4+
return (
5+
<div className="step">
6+
<p>
7+
<strong>Name:</strong>
8+
<span data-testid="final_name">
9+
{props.getState("firstname")} {props.getState("lastname")}
10+
</span>
11+
</p>
12+
<p>
13+
<strong>Email:</strong>
14+
<span data-testid="final_email">{props.getState("email")}</span>
15+
</p>
16+
<p>
17+
<strong>Password:</strong>
18+
<span data-testid="final_pass">{props.getState("password")}</span>
19+
</p>
20+
<p>
21+
<strong>Address:</strong>
22+
<span data-testid="final_address">{props.getState("address")}</span>
23+
</p>
24+
<p>
25+
<strong>Phone:</strong>
26+
<span data-testid="final_phone">{props.getState("phone")}</span>
27+
</p>
28+
<p>
29+
<strong>Color Choice:</strong>
30+
<span data-testid="final_color">{props.getState("color")}</span>
31+
</p>
32+
{props.step.hasPrev() && <button onClick={props.prev}>Previous</button>}
33+
{
34+
<button
35+
data-testid="last-next"
36+
onClick={props.next}
37+
disabled={props.step.isLast()}
38+
>
39+
Next
40+
</button>
41+
}
42+
<div>
43+
{props.allSteps().map(({ order, title }) => (
44+
<p key={order}>
45+
<span data-testid={`order ${order}`}>{order}</span>{" "}
46+
<span data-testid={`title ${order}`}>{title}</span>
47+
</p>
48+
))}
49+
</div>
50+
</div>
51+
);
52+
};

tests/Steps.test.js

+20
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,26 @@ import React from "react";
22
import { render, fireEvent, screen } from "@testing-library/react";
33
import "@testing-library/jest-dom";
44
import App from "../src/App";
5+
import Step1 from "../src/stepComponents/Step1";
6+
import { Step, Steps } from "../src/lib";
7+
import Step2 from "../src/stepComponents/Step2";
8+
9+
const beforeStepChange = jest.fn(() => console.log("beforeStepChange"));
10+
11+
test("beforeStepChange is called before step change", () => {
12+
render(
13+
<Steps>
14+
<Step component={Step1} beforeStepChange={beforeStepChange} />
15+
<Step component={Step2} />
16+
</Steps>,
17+
);
18+
19+
let next_button = screen.getByText(/next/i);
20+
21+
fireEvent.click(next_button);
22+
23+
expect(beforeStepChange).toHaveBeenCalled();
24+
});
525

626
test("Rendered components: Steps, Step", () => {
727
render(<App />);

0 commit comments

Comments
 (0)