Skip to content

Commit 06598c7

Browse files
committed
fix(mui): add stepper to wizard
1 parent ebb777e commit 06598c7

File tree

4 files changed

+215
-113
lines changed

4 files changed

+215
-113
lines changed

packages/mui-component-mapper/src/files/wizard.js

Lines changed: 77 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,37 @@
1-
import React, { cloneElement } from 'react';
1+
import React from 'react';
22
import PropTypes from 'prop-types';
3-
import WizardStep from './wizard/wizard-step';
4-
import { Grid, Typography } from '@material-ui/core';
5-
import Wizard, { wizardProps } from '@data-driven-forms/common/src/wizard/wizard';
3+
import clsx from 'clsx';
4+
5+
import { Grid } from '@material-ui/core';
6+
import { makeStyles } from '@material-ui/core/styles';
7+
8+
import Wizard from '@data-driven-forms/common/src/wizard/wizard';
9+
import WizardNav from './wizard/wizard-nav';
10+
import WizardStepButtons from './wizard/step-buttons';
11+
12+
const useStyles = makeStyles(() => ({
13+
wizardBody: {
14+
padding: 24,
15+
margin: 0
16+
}
17+
}));
18+
19+
const WizardInternal = ({
20+
currentStep,
21+
formOptions,
22+
activeStepIndex,
23+
prevSteps,
24+
handleNext,
25+
handlePrev,
26+
buttonLabels,
27+
stepsInfo,
28+
ButtonContainerProps,
29+
StepperProps,
30+
WizardBodyProps,
31+
WizardProps
32+
}) => {
33+
const classes = useStyles();
634

7-
const WizardInternal = ({ title, description, currentStep, formOptions, prevSteps, handleNext, handlePrev, buttonLabels }) => {
835
const buttonLabelsFinal = {
936
next: 'Continue',
1037
submit: 'Submit',
@@ -13,21 +40,20 @@ const WizardInternal = ({ title, description, currentStep, formOptions, prevStep
1340
...buttonLabels
1441
};
1542

16-
const step = <WizardStep {...currentStep} formOptions={formOptions} buttonLabels={buttonLabelsFinal} />;
17-
1843
return (
19-
<Grid container spacing={6}>
20-
<Grid item xs={12}>
21-
<Typography component="h3">{title}</Typography>
22-
<Typography paragraph>{description}</Typography>
23-
<Typography component="h5">{`Step ${prevSteps.length + 1}`}</Typography>
24-
</Grid>
25-
<Grid item xs={12}>
26-
{cloneElement(step, {
27-
handleNext,
28-
handlePrev,
29-
disableBack: prevSteps.length === 0
30-
})}
44+
<Grid container spacing={3} {...WizardProps}>
45+
{stepsInfo && <WizardNav StepperProps={StepperProps} stepsInfo={stepsInfo} activeStepIndex={activeStepIndex} />}
46+
<Grid container spacing={2} {...WizardBodyProps} className={clsx(classes.wizardBody, WizardBodyProps.className)}>
47+
{currentStep.fields.map((item) => formOptions.renderForm([item], formOptions))}
48+
<WizardStepButtons
49+
{...currentStep}
50+
formOptions={formOptions}
51+
buttonLabels={buttonLabelsFinal}
52+
handleNext={handleNext}
53+
handlePrev={handlePrev}
54+
disableBack={prevSteps.length === 0}
55+
ButtonContainerProps={ButtonContainerProps}
56+
/>
3157
</Grid>
3258
</Grid>
3359
);
@@ -36,7 +62,38 @@ const WizardInternal = ({ title, description, currentStep, formOptions, prevStep
3662
WizardInternal.propTypes = {
3763
title: PropTypes.node,
3864
description: PropTypes.node,
39-
...wizardProps
65+
currentStep: PropTypes.object,
66+
handlePrev: PropTypes.func,
67+
onKeyDown: PropTypes.func,
68+
jumpToStep: PropTypes.func,
69+
setPrevSteps: PropTypes.func,
70+
handleNext: PropTypes.func,
71+
navSchema: PropTypes.array,
72+
activeStepIndex: PropTypes.number,
73+
maxStepIndex: PropTypes.number,
74+
formOptions: PropTypes.shape({
75+
onCancel: PropTypes.func,
76+
renderForm: PropTypes.func
77+
}),
78+
prevSteps: PropTypes.array,
79+
buttonLabels: PropTypes.object,
80+
stepsInfo: PropTypes.arrayOf(
81+
PropTypes.shape({
82+
title: PropTypes.node,
83+
label: PropTypes.node,
84+
key: PropTypes.string,
85+
LabelProps: PropTypes.object,
86+
StepProps: PropTypes.object
87+
})
88+
),
89+
ButtonContainerProps: PropTypes.object,
90+
StepperProps: PropTypes.object,
91+
WizardBodyProps: PropTypes.object,
92+
WizardProps: PropTypes.object
93+
};
94+
95+
WizardInternal.defaultProps = {
96+
WizardBodyProps: {}
4097
};
4198

4299
const MuiWizard = (props) => <Wizard Wizard={WizardInternal} {...props} />;
Lines changed: 92 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,83 +1,96 @@
11
import React from 'react';
22
import PropTypes from 'prop-types';
3-
import { Button } from '@material-ui/core';
4-
import NavigateNextIcon from '@material-ui/icons/NavigateNext';
5-
import NavigateBackIcon from '@material-ui/icons/NavigateBefore';
6-
import Send from '@material-ui/icons/Send';
7-
import { useFieldApi } from '@data-driven-forms/react-form-renderer';
3+
import clsx from 'clsx';
84

9-
const SimpleNext = ({ next, valid, handleNext, submit, buttonLabels }) => (
10-
<Button variant="contained" type="button" color="primary" onClick={() => (valid ? handleNext(next) : submit())}>
11-
{buttonLabels.next}
12-
<NavigateNextIcon />
5+
import selectNext from '@data-driven-forms/common/src/wizard/select-next';
6+
import { FormSpy } from '@data-driven-forms/react-form-renderer';
7+
import { Button, Grid } from '@material-ui/core';
8+
import { makeStyles } from '@material-ui/core/styles';
9+
10+
const NextButton = ({ nextStep, valid, handleNext, nextLabel, getState, handleSubmit, submitLabel }) => (
11+
<Button
12+
variant="contained"
13+
color="primary"
14+
disabled={!valid || getState().validating || getState().submitting}
15+
onClick={() => (nextStep ? handleNext(selectNext(nextStep, getState)) : handleSubmit())}
16+
>
17+
{nextStep ? nextLabel : submitLabel}
1318
</Button>
1419
);
1520

16-
SimpleNext.propTypes = {
17-
next: PropTypes.string,
21+
NextButton.propTypes = {
22+
nextStep: PropTypes.oneOfType([PropTypes.string, PropTypes.func, PropTypes.object]),
23+
handleSubmit: PropTypes.func.isRequired,
24+
submitLabel: PropTypes.node.isRequired,
1825
valid: PropTypes.bool,
1926
handleNext: PropTypes.func.isRequired,
20-
submit: PropTypes.func.isRequired,
21-
buttonLabels: PropTypes.shape({
22-
next: PropTypes.node
23-
})
24-
};
25-
26-
const ConditionalNext = ({ nextStep, ...rest }) => {
27-
const {
28-
input: { value }
29-
} = useFieldApi({ name: nextStep.when, subscription: { value: true } });
30-
return <SimpleNext next={nextStep.stepMapper[value]} {...rest} />;
27+
nextLabel: PropTypes.node.isRequired,
28+
getState: PropTypes.func.isRequired
3129
};
3230

33-
ConditionalNext.propTypes = {
34-
nextStep: PropTypes.shape({
35-
when: PropTypes.string.isRequired,
36-
stepMapper: PropTypes.object.isRequired
37-
}).isRequired
38-
};
31+
const useStyles = makeStyles(() => ({
32+
wizardBody: {
33+
padding: 24,
34+
margin: 0
35+
},
36+
buttons: {
37+
display: 'flex',
38+
justifyContent: 'flex-end'
39+
},
40+
button: {
41+
marginRight: 16
42+
},
43+
buttonsContainer: {
44+
marginTop: 36
45+
}
46+
}));
3947

40-
const submitButton = (handleSubmit, label) => (
41-
<Button type="button" variant="contained" color="primary" onClick={handleSubmit}>
42-
{label} <Send />
43-
</Button>
44-
);
48+
const WizardStepButtons = ({ buttons: Buttons, ...props }) => {
49+
const classes = useStyles();
4550

46-
const renderNextButton = ({ nextStep, handleSubmit, ...rest }) =>
47-
!nextStep ? (
48-
submitButton(handleSubmit, rest.buttonLabels.submit)
49-
) : typeof nextStep === 'object' ? (
50-
<ConditionalNext nextStep={nextStep} {...rest} />
51-
) : (
52-
<SimpleNext next={nextStep} {...rest} />
53-
);
51+
if (Buttons) {
52+
return <Buttons classes={classes} {...props} />;
53+
}
5454

55-
const WizardStepButtons = ({ formOptions, disableBack, handlePrev, nextStep, handleNext, buttonLabels }) => (
56-
<div>
57-
{formOptions.onCancel && (
58-
<Button type="button" variant="contained" color="secondary" onClick={formOptions.onCancel}>
59-
{buttonLabels.cancel}
60-
</Button>
61-
)}
55+
const {
56+
disableBack,
57+
handlePrev,
58+
nextStep,
59+
handleNext,
60+
buttonLabels: { cancel, submit, back, next },
61+
formOptions,
62+
ButtonContainerProps
63+
} = props;
6264

63-
<Button type="button" variant="contained" disabled={disableBack} onClick={handlePrev}>
64-
<NavigateBackIcon />
65-
{buttonLabels.back}
66-
</Button>
67-
{renderNextButton({
68-
...formOptions,
69-
handleNext,
70-
nextStep,
71-
buttonLabels
72-
})}
73-
</div>
74-
);
65+
return (
66+
<Grid
67+
container
68+
direction="row"
69+
justify="space-evenly"
70+
{...ButtonContainerProps}
71+
className={clsx(classes.buttonsContainer, ButtonContainerProps.className)}
72+
>
73+
<FormSpy subscription={{ valid: true, validating: true, submitting: true }}>
74+
{() => (
75+
<React.Fragment>
76+
<Grid item md={2} xs={2}>
77+
<Button onClick={formOptions.onCancel}>{cancel}</Button>
78+
</Grid>
79+
<Grid item md={10} xs={10} className={classes.buttons}>
80+
<Button disabled={disableBack} onClick={handlePrev} className={classes.button}>
81+
{back}
82+
</Button>
83+
<NextButton {...formOptions} handleNext={handleNext} nextStep={nextStep} nextLabel={next} submitLabel={submit} />
84+
</Grid>
85+
</React.Fragment>
86+
)}
87+
</FormSpy>
88+
</Grid>
89+
);
90+
};
7591

7692
WizardStepButtons.propTypes = {
77-
formOptions: PropTypes.shape({
78-
onCancel: PropTypes.func,
79-
handleSubmit: PropTypes.func.isRequired
80-
}).isRequired,
93+
ButtonContainerProps: PropTypes.object,
8194
disableBack: PropTypes.bool,
8295
handlePrev: PropTypes.func.isRequired,
8396
handleNext: PropTypes.func.isRequired,
@@ -86,14 +99,24 @@ WizardStepButtons.propTypes = {
8699
PropTypes.shape({
87100
when: PropTypes.string.isRequired,
88101
stepMapper: PropTypes.object.isRequired
89-
})
102+
}),
103+
PropTypes.func
90104
]),
91105
buttonLabels: PropTypes.shape({
92-
submit: PropTypes.node,
93-
cancel: PropTypes.node,
94-
back: PropTypes.node,
95-
next: PropTypes.node
106+
submit: PropTypes.node.isRequired,
107+
cancel: PropTypes.node.isRequired,
108+
back: PropTypes.node.isRequired,
109+
next: PropTypes.node.isRequired
110+
}).isRequired,
111+
buttons: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),
112+
formOptions: PropTypes.shape({
113+
getState: PropTypes.func.isRequired,
114+
onCancel: PropTypes.func.isRequired
96115
})
97116
};
98117

118+
WizardStepButtons.defaultProps = {
119+
ButtonContainerProps: {}
120+
};
121+
99122
export default WizardStepButtons;
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import React from 'react';
2+
import PropTypes from 'prop-types';
3+
import clsx from 'clsx';
4+
5+
import { makeStyles } from '@material-ui/core/styles';
6+
import { Stepper, Step, StepLabel } from '@material-ui/core';
7+
8+
const useStyles = makeStyles(() => ({
9+
stepper: {
10+
width: '100%'
11+
}
12+
}));
13+
14+
const WizardNav = ({ StepperProps, stepsInfo, activeStepIndex }) => {
15+
const classes = useStyles();
16+
17+
return (
18+
<Stepper {...StepperProps} className={clsx(classes.stepper, StepperProps.className)} activeStep={activeStepIndex}>
19+
{stepsInfo.map(({ title, label, LabelProps, StepProps }, idx) => (
20+
<Step {...StepProps} key={idx}>
21+
<StepLabel {...LabelProps}>{title || label}</StepLabel>
22+
</Step>
23+
))}
24+
</Stepper>
25+
);
26+
};
27+
28+
WizardNav.propTypes = {
29+
StepperProps: PropTypes.object,
30+
stepsInfo: PropTypes.arrayOf(
31+
PropTypes.shape({
32+
title: PropTypes.node,
33+
label: PropTypes.node,
34+
key: PropTypes.string,
35+
LabelProps: PropTypes.object,
36+
StepProps: PropTypes.object
37+
})
38+
),
39+
activeStepIndex: PropTypes.number
40+
};
41+
42+
WizardNav.defaultProps = {
43+
StepperProps: {}
44+
};
45+
46+
export default WizardNav;

packages/mui-component-mapper/src/files/wizard/wizard-step.js

Lines changed: 0 additions & 24 deletions
This file was deleted.

0 commit comments

Comments
 (0)