Skip to content

Commit 9eb63aa

Browse files
committed
feat(pf4wizard): Allow custom buttons for steps
1 parent 243b306 commit 9eb63aa

File tree

6 files changed

+186
-66
lines changed

6 files changed

+186
-66
lines changed

packages/pf4-component-mapper/demo/demo-schemas/wizard-schema.js

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,32 @@
1+
import React, { useState } from 'react';
12
import { componentTypes, validatorTypes } from '@data-driven-forms/react-form-renderer';
3+
import { Button } from '@patternfly/react-core';
4+
5+
const ValidateButtons = ({ disableBack, handlePrev, buttonLabels: { back, cancel }, formOptions, renderNextButton }) => {
6+
const [ state, setState ] = useState('init');
7+
8+
const setValidating = () => {
9+
setState('validating');
10+
setTimeout(() => setState('done'), 2000);
11+
};
12+
13+
return (
14+
<React.Fragment>
15+
{ state === 'init' ? <Button
16+
variant="primary"
17+
type="button"
18+
isDisabled={ !formOptions.valid }
19+
onClick={ setValidating }
20+
>
21+
Validate
22+
</Button> :
23+
state === 'validating' ? <Button type="button" variant="primary" isDisabled={ true } onClick={ () => {} }>Validating...</Button> :
24+
renderNextButton() }
25+
<Button type="button" variant="secondary" isDisabled={ disableBack } onClick={ handlePrev }>{ back }</Button>
26+
<Button type="button" variant="link" onClick={ formOptions.onCancel }>{ cancel }</Button>
27+
</React.Fragment>
28+
);
29+
};
230

331
export const wizardSchema = {
432
fields: [{
@@ -48,10 +76,15 @@ export const wizardSchema = {
4876
stepKey: 'aws',
4977
substepOf: 'Summary',
5078
nextStep: 'summary',
79+
buttons: ValidateButtons,
5180
fields: [{
5281
component: componentTypes.TEXT_FIELD,
5382
name: 'aws-field',
5483
label: 'Aws field part',
84+
validate: [{
85+
type: validatorTypes.REQUIRED,
86+
}],
87+
isRequired: true,
5588
}],
5689
}, {
5790
stepKey: 'google',

packages/pf4-component-mapper/src/form-fields/wizard/step-buttons.js

Lines changed: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -48,16 +48,17 @@ ConditionalNext.propTypes = {
4848
FieldProvider: PropTypes.func.isRequired,
4949
};
5050

51-
const submitButton = (handleSubmit, submitLabel) => <Button type="button" variant="primary" onClick={ handleSubmit }>{ submitLabel }</Button>;
51+
const SubmitButton = ({ handleSubmit, submitLabel }) => <Button type="button" variant="primary" onClick={ handleSubmit }>{ submitLabel }</Button>;
5252

5353
const renderNextButton = ({ nextStep, handleSubmit, submitLabel, ...rest }) =>
5454
!nextStep
55-
? submitButton(handleSubmit, submitLabel)
55+
? <SubmitButton handleSubmit={ handleSubmit } submitLabel={ submitLabel } />
5656
: typeof nextStep === 'object'
5757
? <ConditionalNext nextStep={ nextStep } { ...rest }/>
5858
: <SimpleNext next={ nextStep } { ...rest } />;
5959

6060
const WizardStepButtons = ({
61+
buttons: Buttons,
6162
formOptions,
6263
disableBack,
6364
handlePrev,
@@ -72,16 +73,40 @@ const WizardStepButtons = ({
7273
next,
7374
}}) =>
7475
<footer className={ `pf-c-wizard__footer ${buttonsClassName ? buttonsClassName : ''}` }>
75-
{ renderNextButton({
76-
...formOptions,
77-
handleNext,
78-
nextStep,
79-
FieldProvider,
80-
nextLabel: next,
81-
submitLabel: submit,
82-
}) }
83-
<Button type="button" variant="secondary" isDisabled={ disableBack } onClick={ handlePrev }>{ back }</Button>
84-
<Button type="button" variant="link" onClick={ formOptions.onCancel }>{ cancel }</Button>
76+
{ Buttons ? <Buttons
77+
ConditionalNext={ ConditionalNext }
78+
SubmitButton={ SubmitButton }
79+
SimpleNext={ SimpleNext }
80+
formOptions={ formOptions }
81+
disableBack={ disableBack }
82+
handlePrev={ handlePrev }
83+
nextStep={ nextStep }
84+
FieldProvider={ FieldProvider }
85+
handleNext={ handleNext }
86+
buttonsClassName={ buttonsClassName }
87+
buttonLabels={{ cancel, submit, back, next }}
88+
renderNextButton={ args => renderNextButton({
89+
...formOptions,
90+
handleNext,
91+
nextStep,
92+
FieldProvider,
93+
nextLabel: next,
94+
submitLabel: submit,
95+
...args,
96+
}) }
97+
/>
98+
: <React.Fragment>
99+
{ renderNextButton({
100+
...formOptions,
101+
handleNext,
102+
nextStep,
103+
FieldProvider,
104+
nextLabel: next,
105+
submitLabel: submit,
106+
}) }
107+
<Button type="button" variant="secondary" isDisabled={ disableBack } onClick={ handlePrev }>{ back }</Button>
108+
<Button type="button" variant="link" onClick={ formOptions.onCancel }>{ cancel }</Button>
109+
</React.Fragment> }
85110
</footer>;
86111

87112
WizardStepButtons.propTypes = {
@@ -107,6 +132,7 @@ WizardStepButtons.propTypes = {
107132
next: PropTypes.string.isRequired,
108133
}).isRequired,
109134
buttonsClassName: PropTypes.string,
135+
buttons: PropTypes.oneOfType([ PropTypes.node, PropTypes.func ]),
110136
};
111137

112138
export default WizardStepButtons;

packages/pf4-component-mapper/src/tests/wizard/__snapshots__/step-buttons.test.js.snap

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,10 @@ exports[`<WizardSTepButtons should add custom className to toolbar 1`] = `
44
<footer
55
className="pf-c-wizard__footer foo-class"
66
>
7-
<Button
8-
onClick={[MockFunction]}
9-
type="button"
10-
variant="primary"
11-
>
12-
Submit
13-
</Button>
7+
<SubmitButton
8+
handleSubmit={[MockFunction]}
9+
submitLabel="Submit"
10+
/>
1411
<Button
1512
onClick={[MockFunction]}
1613
type="button"
@@ -32,13 +29,10 @@ exports[`<WizardSTepButtons should render correctly 1`] = `
3229
<footer
3330
className="pf-c-wizard__footer "
3431
>
35-
<Button
36-
onClick={[MockFunction]}
37-
type="button"
38-
variant="primary"
39-
>
40-
Submit
41-
</Button>
32+
<SubmitButton
33+
handleSubmit={[MockFunction]}
34+
submitLabel="Submit"
35+
/>
4236
<Button
4337
onClick={[MockFunction]}
4438
type="button"

packages/pf4-component-mapper/src/tests/wizard/__snapshots__/wizard.test.js.snap

Lines changed: 54 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -240,23 +240,28 @@ exports[`<Wizard /> should render correctly and unmount 1`] = `
240240
<footer
241241
className="pf-c-wizard__footer "
242242
>
243-
<Button
244-
onClick={[Function]}
245-
type="button"
246-
variant="primary"
243+
<SubmitButton
244+
handleSubmit={[Function]}
245+
submitLabel="Submit"
247246
>
248-
<button
249-
aria-disabled={null}
250-
aria-label={null}
251-
className="pf-c-button pf-m-primary"
252-
disabled={false}
247+
<Button
253248
onClick={[Function]}
254-
tabIndex={null}
255249
type="button"
250+
variant="primary"
256251
>
257-
Submit
258-
</button>
259-
</Button>
252+
<button
253+
aria-disabled={null}
254+
aria-label={null}
255+
className="pf-c-button pf-m-primary"
256+
disabled={false}
257+
onClick={[Function]}
258+
tabIndex={null}
259+
type="button"
260+
>
261+
Submit
262+
</button>
263+
</Button>
264+
</SubmitButton>
260265
<Button
261266
isDisabled={true}
262267
onClick={[Function]}
@@ -773,23 +778,28 @@ exports[`<Wizard /> should render correctly in modal and unmount 1`] = `
773778
<footer
774779
className="pf-c-wizard__footer "
775780
>
776-
<Button
777-
onClick={[Function]}
778-
type="button"
779-
variant="primary"
781+
<SubmitButton
782+
handleSubmit={[Function]}
783+
submitLabel="Submit"
780784
>
781-
<button
782-
aria-disabled={null}
783-
aria-label={null}
784-
className="pf-c-button pf-m-primary"
785-
disabled={false}
785+
<Button
786786
onClick={[Function]}
787-
tabIndex={null}
788787
type="button"
788+
variant="primary"
789789
>
790-
Submit
791-
</button>
792-
</Button>
790+
<button
791+
aria-disabled={null}
792+
aria-label={null}
793+
className="pf-c-button pf-m-primary"
794+
disabled={false}
795+
onClick={[Function]}
796+
tabIndex={null}
797+
type="button"
798+
>
799+
Submit
800+
</button>
801+
</Button>
802+
</SubmitButton>
793803
<Button
794804
isDisabled={true}
795805
onClick={[Function]}
@@ -1118,23 +1128,28 @@ exports[`<Wizard /> should render correctly with custom title and description 1`
11181128
<footer
11191129
className="pf-c-wizard__footer "
11201130
>
1121-
<Button
1122-
onClick={[Function]}
1123-
type="button"
1124-
variant="primary"
1131+
<SubmitButton
1132+
handleSubmit={[Function]}
1133+
submitLabel="Submit"
11251134
>
1126-
<button
1127-
aria-disabled={null}
1128-
aria-label={null}
1129-
className="pf-c-button pf-m-primary"
1130-
disabled={false}
1135+
<Button
11311136
onClick={[Function]}
1132-
tabIndex={null}
11331137
type="button"
1138+
variant="primary"
11341139
>
1135-
Submit
1136-
</button>
1137-
</Button>
1140+
<button
1141+
aria-disabled={null}
1142+
aria-label={null}
1143+
className="pf-c-button pf-m-primary"
1144+
disabled={false}
1145+
onClick={[Function]}
1146+
tabIndex={null}
1147+
type="button"
1148+
>
1149+
Submit
1150+
</button>
1151+
</Button>
1152+
</SubmitButton>
11381153
<Button
11391154
isDisabled={true}
11401155
onClick={[Function]}

packages/pf4-component-mapper/src/tests/wizard/wizard.test.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,22 @@ describe('<Wizard />', () => {
124124
expect(toJSon(wrapper)).toMatchSnapshot();
125125
});
126126

127+
it('should render correctly with custom buttons', () => {
128+
const Buttons = () => <div>Hello</div>;
129+
130+
const wrapper = mount(<Wizard { ...initialProps } fields={ [{
131+
title: 'foo-step',
132+
stepKey: '1',
133+
name: 'foo',
134+
buttons: Buttons,
135+
fields: [{
136+
name: 'foo-field',
137+
}],
138+
nextStep: '2',
139+
}] }/>);
140+
expect(wrapper.find(Buttons).length).toEqual(1);
141+
});
142+
127143
it('should call submit function', () => {
128144
const onSubmit = jest.fn();
129145
const wrapper = mount(<Wizard { ...initialProps } formOptions={{ ...initialProps.formOptions, onSubmit }} />);

packages/react-renderer-demo/src/docs-components/pf4-wizard.md

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ You can rewrite only selection of them, e.g.
4343
| nextStep | object/stepKey of next step | See below |
4444
| fields | array | As usual |
4545
| substep | string | Substep title (steps are grouped by this title) |
46-
| title | string | Step title
46+
| title | string | Step title |
47+
| buttons | node, func | Custom buttons component
4748

4849
- nextStep can be stepKey of the next step
4950
- or you can branch the way by using of object:
@@ -59,6 +60,41 @@ nextStep: {
5960
},
6061
```
6162

63+
#### Buttons
64+
65+
Each step can implement its own buttons.
66+
67+
```jsx
68+
const Buttons = () => <div>Hello</div>;
69+
70+
[{
71+
title: 'foo-step',
72+
stepKey: '1',
73+
name: 'foo',
74+
buttons: Buttons,
75+
fields: [{
76+
name: 'foo-field',
77+
}],
78+
}]
79+
```
80+
81+
The components receives these props:
82+
83+
|Props|Decription|
84+
| --- | -------- |
85+
|ConditionalNext|Conditional next button|
86+
|SubmitButton|Default submit button.|
87+
|SimpleNext|Default next button.|
88+
|formOptions|formOptions|
89+
|disableBack|If it's first step, disable back is true.|
90+
|handlePrev|Function to handle back button.|
91+
|nextStep|Next step field from the schema.|
92+
|FieldProvider|FieldProvider|
93+
|handleNext|Function to handle next click.|
94+
|buttonsClassName|Classname of buttons.|
95+
|buttonLabels|Object with labels.|
96+
|renderNextButton|Function which completely handle the next/submit button.|
97+
6298

6399
### How to do substeps
64100

0 commit comments

Comments
 (0)