Skip to content

Commit cc8fb30

Browse files
committed
Before and After components added to config object
1 parent efae024 commit cc8fb30

File tree

6 files changed

+155
-50
lines changed

6 files changed

+155
-50
lines changed

README.md

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
[![Build Status](https://travis-ci.com/sametweb/react-step-builder.svg?branch=master)](https://travis-ci.com/sametweb/react-step-builder) [![Coverage Status](https://coveralls.io/repos/github/sametweb/react-step-builder/badge.svg?branch=master)](https://coveralls.io/github/sametweb/react-step-builder?branch=master) [![Test Coverage](https://api.codeclimate.com/v1/badges/f0c62e4a8e4826eec6c9/test_coverage)](https://codeclimate.com/github/sametweb/react-step-builder/test_coverage) [![Maintainability](https://api.codeclimate.com/v1/badges/f0c62e4a8e4826eec6c9/maintainability)](https://codeclimate.com/github/sametweb/react-step-builder/maintainability) [![Total NPM Download](https://img.shields.io/npm/dt/react-step-builder.svg)](https://www.npmjs.com/package/react-step-builder)
44
<br/><br/>
55

6-
## A headless, type-safe, UI
6+
## A headless, type-safe multi-step UI builder.
77

88
<br/>
99

@@ -42,7 +42,7 @@ Example:
4242

4343
# Config Object
4444

45-
`Steps` component accepts an optional `config` object for configuring the common navigation component. The `Navigation` component provided in the config object will be rendered along with every step component. Here is an example:
45+
`Steps` component accepts an optional `config` object for configuring the common navigation component or components that you'd like render before or after the Step components. These components are rendered along with every step component. Here is an example:
4646

4747
```javascript
4848
const Navigation = (props) => {
@@ -54,7 +54,17 @@ const Navigation = (props) => {
5454
);
5555
}
5656

57+
const Before = (props) => {
58+
return <span>This component will be rendered before the Step components in every step</span>
59+
}
60+
61+
const After = (props) => {
62+
return <span>This component will be rendered after the Step components in every step</span>
63+
}
64+
5765
const config = {
66+
before: Before, // a React component with special props provided automatically
67+
after: After, // a React component with special props provided automatically
5868
navigation: {
5969
component: Navigation, // a React component with special props provided automatically
6070
location: "before" // or after
@@ -127,6 +137,14 @@ The React component that is passed to each `Step` wrapper component will be inje
127137

128138
## Config Object
129139

140+
### `before`
141+
142+
It accepts a function that returns some JSX.Element. This component's `props` object is automatically populated with the `Steps` component's state (see: [NavigationComponentProps](#component-type-of-example-navigation-component)).
143+
144+
### `after`
145+
146+
It accepts a function that returns some JSX.Element. This component's `props` object is automatically populated with the `Steps` component's state (see: [NavigationComponentProps](#component-type-of-example-navigation-component)).
147+
130148
### `navigation`
131149

132150
| Property | Type | Description |
@@ -170,9 +188,9 @@ const Step1 = (props: StepComponentProps) => {
170188
export default Step1;
171189
```
172190

173-
## Example Navigation Component
191+
## Example Navigation, Before, and After Components
174192

175-
If you'd like to add a persistent navigation component to be shown on every step, you may utilize `NavigationComponentProps` type for your custom `Navigation` component. Here is an example:
193+
If you'd like to add a persistent components to be shown on before or after every step, you may utilize `NavigationComponentProps` type for your custom `Navigation`, `Before`, or `After` components. Here is an example:
176194

177195
```javascript
178196
const Navigation = (props: NavigationComponentProps) => {
@@ -183,4 +201,14 @@ const Navigation = (props: NavigationComponentProps) => {
183201
</div>
184202
);
185203
};
204+
205+
const Before = (props: NavigationComponentProps) => {
206+
return (
207+
<span>This component will be rendered before the Step components</span>
208+
);
209+
};
210+
211+
const After = (props: NavigationComponentProps) => {
212+
return <span>This component will be rendered after the Step components</span>;
213+
};
186214
```

package.json

Lines changed: 1 addition & 1 deletion
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 <mutevellisamet@gmail.com> (https://sametmutevelli.com)",
5-
"version": "2.0.6",
5+
"version": "2.0.7",
66
"private": false,
77
"main": "src/dist/index",
88
"types": "src/dist/index.d.ts",

src/App.tsx

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
import React from "react";
2-
import { Steps, Step, NavigationComponentProps } from "./lib-ts/index";
2+
import {
3+
Steps,
4+
Step,
5+
NavigationComponentProps,
6+
StepsConfig,
7+
} from "./lib-ts/index";
38
import Step1 from "./stepComponents/Step1";
49
import Step2 from "./stepComponents/Step2";
510
import Step3 from "./stepComponents/Step3";
@@ -20,17 +25,18 @@ export const Navigation = (props: NavigationComponentProps) => {
2025
};
2126

2227
const App = () => {
28+
const config: StepsConfig = {
29+
before: (props) => <Navigation {...props} test="test" />,
30+
after: Navigation,
31+
navigation: {
32+
component: Navigation,
33+
location: "before",
34+
},
35+
};
2336
return (
2437
<div className="steps_wrapper">
25-
<h1>React Step Builder v2.0.6</h1>
26-
<Steps
27-
config={{
28-
navigation: {
29-
component: Navigation,
30-
location: "before",
31-
},
32-
}}
33-
>
38+
<h1>React Step Builder v2.0.7</h1>
39+
<Steps config={config}>
3440
<Step title="Hello" component={Step1} deneme={"deneme"} />
3541
<Step component={Step2} />
3642
<Step title="Contact Info" component={Step3} />

src/lib-ts/index.tsx

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,18 @@ interface State {
2222
[key: string]: InputValue | CheckboxValue;
2323
}
2424

25+
export type StepsConfig = {
26+
before?: (props: any) => JSX.Element;
27+
after?: (props: any) => JSX.Element;
28+
navigation?: {
29+
component: (props: any) => JSX.Element;
30+
location?: "before" | "after";
31+
};
32+
};
33+
2534
type StepsProps = {
2635
children: ReactElement<StepProps> | ReactElement<StepProps>[];
27-
config?: {
28-
navigation?: {
29-
component: (props: any) => JSX.Element;
30-
location?: "before" | "after";
31-
};
32-
};
36+
config?: StepsConfig;
3337
};
3438

3539
type BeforeStepChange = () => any;
@@ -139,6 +143,20 @@ export function Steps({ children, config }: StepsProps) {
139143
}
140144
};
141145

146+
const BeforeComponent = (context: NavigationComponentProps) => {
147+
if (config?.before) {
148+
const Before = config.before;
149+
return <Before {...context} />;
150+
}
151+
};
152+
153+
const AfterComponent = (context: NavigationComponentProps) => {
154+
if (config?.after) {
155+
const After = config.after;
156+
return <After {...context} />;
157+
}
158+
};
159+
142160
const allSteps: AllSteps = childSteps.map((child, order) => {
143161
return {
144162
title:
@@ -227,6 +245,7 @@ export function Steps({ children, config }: StepsProps) {
227245

228246
return (
229247
<StepsContext.Provider value={context}>
248+
{config?.before && BeforeComponent(context)}
230249
{config?.navigation?.location === "before" &&
231250
NavigationComponent(context)}
232251
{React.Children.map(children, (child, order) => (
@@ -235,6 +254,7 @@ export function Steps({ children, config }: StepsProps) {
235254
</StepContext.Provider>
236255
))}
237256
{config?.navigation?.location === "after" && NavigationComponent(context)}
257+
{config?.after && AfterComponent(context)}
238258
</StepsContext.Provider>
239259
);
240260
}

src/tests/index.test.js

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,17 @@ const Step2 = () => <></>;
1010
const Step3 = () => <></>;
1111
const Step4 = () => <></>;
1212
const Navigation = () => <></>;
13+
const Before = () => <></>;
14+
const After = () => <></>;
1315

1416
const testRenderer = TestRenderer.create(
15-
<Steps config={{ navigation: { component: Navigation, location: "before" } }}>
17+
<Steps
18+
config={{
19+
before: Before,
20+
after: After,
21+
navigation: { component: Navigation, location: "before" },
22+
}}
23+
>
1624
<Step component={Step1} title="My first step" beforeStepChange={mockFn} />
1725
<Step component={Step2} />
1826
<Step component={Step3} />
@@ -200,3 +208,53 @@ describe("global navigation", () => {
200208
);
201209
});
202210
});
211+
212+
describe("before and after components", () => {
213+
it("renders components", () => {
214+
const before = testInstance.findByType(Before).props;
215+
const after = testInstance.findByType(After).props;
216+
expect(before.size).toBe(4);
217+
expect(before.current).toBe(2);
218+
expect(Number(before.progress.toFixed(2))).toBe(0.33);
219+
expect(after.size).toBe(4);
220+
expect(after.current).toBe(2);
221+
expect(Number(after.progress.toFixed(2))).toBe(0.33);
222+
});
223+
224+
it("prev/next works in before/after components correctly", () => {
225+
let before = testInstance.findByType(Before).props;
226+
let after = testInstance.findByType(After).props;
227+
228+
act(() => before.next());
229+
230+
before = testInstance.findByType(Before).props;
231+
after = testInstance.findByType(After).props;
232+
233+
expect(before.current).toBe(3);
234+
expect(after.current).toBe(3);
235+
236+
act(() => after.next());
237+
238+
before = testInstance.findByType(Before).props;
239+
after = testInstance.findByType(After).props;
240+
241+
expect(before.current).toBe(4);
242+
expect(after.current).toBe(4);
243+
244+
act(() => before.prev());
245+
246+
before = testInstance.findByType(Before).props;
247+
after = testInstance.findByType(After).props;
248+
249+
expect(before.current).toBe(3);
250+
expect(after.current).toBe(3);
251+
252+
act(() => after.prev());
253+
254+
before = testInstance.findByType(Before).props;
255+
after = testInstance.findByType(After).props;
256+
257+
expect(before.current).toBe(2);
258+
expect(after.current).toBe(2);
259+
});
260+
});

tsconfig.json

Lines changed: 21 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,23 @@
11
{
2-
"compilerOptions": {
3-
"target": "es6",
4-
"module": "esnext",
5-
"jsx": "react",
6-
"declaration": true,
7-
"declarationMap": true,
8-
"sourceMap": true,
9-
"outDir": "src/lib",
10-
"strict": true,
11-
"esModuleInterop": true,
12-
"skipLibCheck": true,
13-
"forceConsistentCasingInFileNames": true,
14-
"lib": [
15-
"dom",
16-
"dom.iterable",
17-
"esnext"
18-
],
19-
"allowJs": true,
20-
"allowSyntheticDefaultImports": true,
21-
"moduleResolution": "node",
22-
"resolveJsonModule": true,
23-
"isolatedModules": true,
24-
"noEmit": true
25-
},
26-
"include": [
27-
"src/lib-ts/**/*.ts",
28-
"src/lib-ts/**/*.tsx"
29-
]
2+
"compilerOptions": {
3+
"target": "es6",
4+
"module": "esnext",
5+
"jsx": "react",
6+
"declaration": true,
7+
"declarationMap": true,
8+
"sourceMap": true,
9+
"outDir": "src/lib",
10+
"strict": true,
11+
"esModuleInterop": true,
12+
"skipLibCheck": true,
13+
"forceConsistentCasingInFileNames": true,
14+
"lib": ["dom", "dom.iterable", "esnext"],
15+
"allowJs": true,
16+
"allowSyntheticDefaultImports": true,
17+
"moduleResolution": "node",
18+
"resolveJsonModule": true,
19+
"isolatedModules": true,
20+
"noEmit": false
21+
},
22+
"include": ["src/lib-ts/**/*.ts", "src/lib-ts/**/*.tsx"]
3023
}

0 commit comments

Comments
 (0)