Skip to content

Commit

Permalink
Before and After components added to config object
Browse files Browse the repository at this point in the history
  • Loading branch information
sametweb committed Mar 14, 2021
1 parent efae024 commit cc8fb30
Show file tree
Hide file tree
Showing 6 changed files with 155 additions and 50 deletions.
36 changes: 32 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
[![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)
<br/><br/>

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

<br/>

Expand Down Expand Up @@ -42,7 +42,7 @@ Example:

# Config Object

`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:
`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:

```javascript
const Navigation = (props) => {
Expand All @@ -54,7 +54,17 @@ const Navigation = (props) => {
);
}

const Before = (props) => {
return <span>This component will be rendered before the Step components in every step</span>
}

const After = (props) => {
return <span>This component will be rendered after the Step components in every step</span>
}

const config = {
before: Before, // a React component with special props provided automatically
after: After, // a React component with special props provided automatically
navigation: {
component: Navigation, // a React component with special props provided automatically
location: "before" // or after
Expand Down Expand Up @@ -127,6 +137,14 @@ The React component that is passed to each `Step` wrapper component will be inje

## Config Object

### `before`

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)).

### `after`

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)).

### `navigation`

| Property | Type | Description |
Expand Down Expand Up @@ -170,9 +188,9 @@ const Step1 = (props: StepComponentProps) => {
export default Step1;
```

## Example Navigation Component
## Example Navigation, Before, and After Components

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:
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:

```javascript
const Navigation = (props: NavigationComponentProps) => {
Expand All @@ -183,4 +201,14 @@ const Navigation = (props: NavigationComponentProps) => {
</div>
);
};

const Before = (props: NavigationComponentProps) => {
return (
<span>This component will be rendered before the Step components</span>
);
};

const After = (props: NavigationComponentProps) => {
return <span>This component will be rendered after the Step components</span>;
};
```
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "react-step-builder",
"description": "Unopinionated multi step interface builder.",
"author": "Samet Mutevelli <[email protected]> (https://sametmutevelli.com)",
"version": "2.0.6",
"version": "2.0.7",
"private": false,
"main": "src/dist/index",
"types": "src/dist/index.d.ts",
Expand Down
26 changes: 16 additions & 10 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import React from "react";
import { Steps, Step, NavigationComponentProps } from "./lib-ts/index";
import {
Steps,
Step,
NavigationComponentProps,
StepsConfig,
} from "./lib-ts/index";
import Step1 from "./stepComponents/Step1";
import Step2 from "./stepComponents/Step2";
import Step3 from "./stepComponents/Step3";
Expand All @@ -20,17 +25,18 @@ export const Navigation = (props: NavigationComponentProps) => {
};

const App = () => {
const config: StepsConfig = {
before: (props) => <Navigation {...props} test="test" />,
after: Navigation,
navigation: {
component: Navigation,
location: "before",
},
};
return (
<div className="steps_wrapper">
<h1>React Step Builder v2.0.6</h1>
<Steps
config={{
navigation: {
component: Navigation,
location: "before",
},
}}
>
<h1>React Step Builder v2.0.7</h1>
<Steps config={config}>
<Step title="Hello" component={Step1} deneme={"deneme"} />
<Step component={Step2} />
<Step title="Contact Info" component={Step3} />
Expand Down
32 changes: 26 additions & 6 deletions src/lib-ts/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,18 @@ interface State {
[key: string]: InputValue | CheckboxValue;
}

export type StepsConfig = {
before?: (props: any) => JSX.Element;
after?: (props: any) => JSX.Element;
navigation?: {
component: (props: any) => JSX.Element;
location?: "before" | "after";
};
};

type StepsProps = {
children: ReactElement<StepProps> | ReactElement<StepProps>[];
config?: {
navigation?: {
component: (props: any) => JSX.Element;
location?: "before" | "after";
};
};
config?: StepsConfig;
};

type BeforeStepChange = () => any;
Expand Down Expand Up @@ -139,6 +143,20 @@ export function Steps({ children, config }: StepsProps) {
}
};

const BeforeComponent = (context: NavigationComponentProps) => {
if (config?.before) {
const Before = config.before;
return <Before {...context} />;
}
};

const AfterComponent = (context: NavigationComponentProps) => {
if (config?.after) {
const After = config.after;
return <After {...context} />;
}
};

const allSteps: AllSteps = childSteps.map((child, order) => {
return {
title:
Expand Down Expand Up @@ -227,6 +245,7 @@ export function Steps({ children, config }: StepsProps) {

return (
<StepsContext.Provider value={context}>
{config?.before && BeforeComponent(context)}
{config?.navigation?.location === "before" &&
NavigationComponent(context)}
{React.Children.map(children, (child, order) => (
Expand All @@ -235,6 +254,7 @@ export function Steps({ children, config }: StepsProps) {
</StepContext.Provider>
))}
{config?.navigation?.location === "after" && NavigationComponent(context)}
{config?.after && AfterComponent(context)}
</StepsContext.Provider>
);
}
Expand Down
60 changes: 59 additions & 1 deletion src/tests/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,17 @@ const Step2 = () => <></>;
const Step3 = () => <></>;
const Step4 = () => <></>;
const Navigation = () => <></>;
const Before = () => <></>;
const After = () => <></>;

const testRenderer = TestRenderer.create(
<Steps config={{ navigation: { component: Navigation, location: "before" } }}>
<Steps
config={{
before: Before,
after: After,
navigation: { component: Navigation, location: "before" },
}}
>
<Step component={Step1} title="My first step" beforeStepChange={mockFn} />
<Step component={Step2} />
<Step component={Step3} />
Expand Down Expand Up @@ -200,3 +208,53 @@ describe("global navigation", () => {
);
});
});

describe("before and after components", () => {
it("renders components", () => {
const before = testInstance.findByType(Before).props;
const after = testInstance.findByType(After).props;
expect(before.size).toBe(4);
expect(before.current).toBe(2);
expect(Number(before.progress.toFixed(2))).toBe(0.33);
expect(after.size).toBe(4);
expect(after.current).toBe(2);
expect(Number(after.progress.toFixed(2))).toBe(0.33);
});

it("prev/next works in before/after components correctly", () => {
let before = testInstance.findByType(Before).props;
let after = testInstance.findByType(After).props;

act(() => before.next());

before = testInstance.findByType(Before).props;
after = testInstance.findByType(After).props;

expect(before.current).toBe(3);
expect(after.current).toBe(3);

act(() => after.next());

before = testInstance.findByType(Before).props;
after = testInstance.findByType(After).props;

expect(before.current).toBe(4);
expect(after.current).toBe(4);

act(() => before.prev());

before = testInstance.findByType(Before).props;
after = testInstance.findByType(After).props;

expect(before.current).toBe(3);
expect(after.current).toBe(3);

act(() => after.prev());

before = testInstance.findByType(Before).props;
after = testInstance.findByType(After).props;

expect(before.current).toBe(2);
expect(after.current).toBe(2);
});
});
49 changes: 21 additions & 28 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,30 +1,23 @@
{
"compilerOptions": {
"target": "es6",
"module": "esnext",
"jsx": "react",
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"outDir": "src/lib",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"allowSyntheticDefaultImports": true,
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true
},
"include": [
"src/lib-ts/**/*.ts",
"src/lib-ts/**/*.tsx"
]
"compilerOptions": {
"target": "es6",
"module": "esnext",
"jsx": "react",
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"outDir": "src/lib",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"allowSyntheticDefaultImports": true,
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": false
},
"include": ["src/lib-ts/**/*.ts", "src/lib-ts/**/*.tsx"]
}

0 comments on commit cc8fb30

Please sign in to comment.