Skip to content

Commit b8cc9da

Browse files
authored
Merge pull request #1059 from rvsia/addFormAlerts
Add form alerts and add implementation for PF4
2 parents 9b9c524 + 6567210 commit b8cc9da

File tree

10 files changed

+161
-8
lines changed

10 files changed

+161
-8
lines changed

packages/common/src/form-template/form-template.js

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,8 @@ const FormTemplate = ({
121121
descriptionProps,
122122
buttonGroupProps,
123123
buttonsProps,
124+
alertProps,
125+
BeforeError,
124126
...rest
125127
}) => {
126128
const {
@@ -137,6 +139,14 @@ const FormTemplate = ({
137139
{description && <Description {...descriptionProps}>{description}</Description>}
138140
</Header>
139141
)}
142+
{BeforeError && (
143+
<FormSpy subscription={{ submitError: true, error: true }}>
144+
{() => {
145+
const state = getState();
146+
return <BeforeError formError={state.error || state.submitError} formSpyProps={state} alertProps={alertProps} />;
147+
}}
148+
</FormSpy>
149+
)}
140150
{formFields}
141151
{showFormControls && (
142152
<FormSpy>
@@ -174,7 +184,9 @@ FormTemplate.propTypes = {
174184
titleProps: PropTypes.object,
175185
descriptionProps: PropTypes.object,
176186
buttonGroupProps: PropTypes.object,
177-
buttonsProps: PropTypes.object
187+
buttonsProps: PropTypes.object,
188+
BeforeError: PropTypes.elementType,
189+
alertProps: PropTypes.object
178190
};
179191

180192
FormTemplate.defaultProps = {

packages/pf4-component-mapper/src/form-template/form-template.js

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
33

44
import FormTemplate from '@data-driven-forms/common/form-template';
55

6-
import { Button as PF4Button, ActionGroup, Form, TextContent, Text, TextVariants } from '@patternfly/react-core';
6+
import { Button as PF4Button, ActionGroup, Form, TextContent, Text, TextVariants, Alert } from '@patternfly/react-core';
77

88
export const Button = ({ label, bsStyle, children, disabled, buttonType, ...props }) => (
99
<PF4Button variant={buttonType === 'cancel' ? 'link' : bsStyle || 'secondary'} isDisabled={disabled} {...props}>
@@ -50,8 +50,39 @@ Description.propTypes = {
5050
children: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node])
5151
};
5252

53+
export const FormError = ({ formError, alertProps }) => {
54+
if (typeof formError === 'object' && formError.title) {
55+
const { title, description, ...props } = formError;
56+
57+
return (
58+
<Alert variant="danger" isInline title={title} {...props} {...alertProps}>
59+
{description}
60+
</Alert>
61+
);
62+
}
63+
64+
if (typeof formError === 'string') {
65+
return <Alert variant="danger" isInline title={formError} {...alertProps} />;
66+
}
67+
68+
return null;
69+
};
70+
71+
FormError.propTypes = {
72+
formError: PropTypes.any,
73+
alertProps: PropTypes.object
74+
};
75+
5376
const PF4FormTemplate = (props) => (
54-
<FormTemplate FormWrapper={Form} Button={Button} ButtonGroup={ButtonGroup} Title={Title} Description={Description} {...props} />
77+
<FormTemplate
78+
BeforeError={FormError}
79+
FormWrapper={Form}
80+
Button={Button}
81+
ButtonGroup={ButtonGroup}
82+
Title={Title}
83+
Description={Description}
84+
{...props}
85+
/>
5586
);
5687

5788
export default PF4FormTemplate;

packages/pf4-component-mapper/src/tests/form-template-common.test.js

Lines changed: 97 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,27 @@
11
import React from 'react';
2-
import { FormRenderer, Form, FormSpy } from '@data-driven-forms/react-form-renderer';
2+
import { act } from 'react-dom/test-utils';
3+
import { FormRenderer, Form, FormSpy, FormError } from '@data-driven-forms/react-form-renderer';
34
import { mount } from 'enzyme';
5+
import { Alert } from '@patternfly/react-core';
6+
47
import FormTemplate, { Title, Description, Button } from '../form-template';
58
import RenderWithProvider from '../../../../__mocks__/with-provider';
9+
import componentMapper from '../component-mapper';
610

711
describe('FormTemplate PF4 Common', () => {
812
let initialProps;
913
let ContextWrapper;
1014
let formOptions;
1115

1216
beforeEach(() => {
13-
formOptions = { onSubmit: jest.fn(), onReset: jest.fn(), onCancel: jest.fn(), canReset: true, pristine: true };
17+
formOptions = {
18+
onSubmit: jest.fn(),
19+
onReset: jest.fn(),
20+
onCancel: jest.fn(),
21+
canReset: true,
22+
pristine: true,
23+
getState: jest.fn().mockImplementation(() => ({}))
24+
};
1425
ContextWrapper = ({ children, ...props }) => (
1526
<RenderWithProvider value={{ formOptions }}>
1627
<Form onSubmit={jest.fn()}>{() => children}</Form>
@@ -37,7 +48,7 @@ describe('FormTemplate PF4 Common', () => {
3748
expect(wrapper.find(Title)).toHaveLength(1);
3849
expect(wrapper.find(Description)).toHaveLength(0);
3950
expect(wrapper.find(Button)).toHaveLength(2);
40-
expect(wrapper.find(FormSpy)).toHaveLength(1);
51+
expect(wrapper.find(FormSpy)).toHaveLength(2);
4152
});
4253

4354
it('should hide buttons', () => {
@@ -48,7 +59,7 @@ describe('FormTemplate PF4 Common', () => {
4859
);
4960

5061
expect(wrapper.find(Button)).toHaveLength(0);
51-
expect(wrapper.find(FormSpy)).toHaveLength(0);
62+
expect(wrapper.find(FormSpy)).toHaveLength(1);
5263
});
5364

5465
it('should render description', () => {
@@ -158,4 +169,86 @@ describe('FormTemplate PF4 Common', () => {
158169

159170
expect(onCancel).toHaveBeenCalledWith(expectedValues);
160171
});
172+
173+
it('show form alert message', async () => {
174+
const wrapper = mount(
175+
<FormRenderer
176+
schema={{
177+
fields: [
178+
{
179+
component: 'text-field',
180+
name: 'field'
181+
}
182+
]
183+
}}
184+
validate={({ field }) => {
185+
if (field) {
186+
return { [FormError]: 'some error title' };
187+
}
188+
}}
189+
onSubmit={jest.fn()}
190+
FormTemplate={FormTemplate}
191+
componentMapper={componentMapper}
192+
/>
193+
);
194+
195+
expect(wrapper.find(Alert)).toHaveLength(0);
196+
197+
await act(async () => {
198+
wrapper
199+
.find('input')
200+
.first()
201+
.instance().value = 'cats';
202+
wrapper
203+
.find('input')
204+
.first()
205+
.simulate('change');
206+
});
207+
wrapper.update();
208+
209+
expect(wrapper.find(Alert)).toHaveLength(1);
210+
expect(wrapper.find(Alert).props().title).toEqual('some error title');
211+
expect(wrapper.find(Alert).text()).toEqual('Danger alert:some error title');
212+
});
213+
214+
it('show form alert message as object', async () => {
215+
const wrapper = mount(
216+
<FormRenderer
217+
schema={{
218+
fields: [
219+
{
220+
component: 'text-field',
221+
name: 'field'
222+
}
223+
]
224+
}}
225+
validate={({ field }) => {
226+
if (field) {
227+
return { [FormError]: { title: 'some error title', description: 'some description' } };
228+
}
229+
}}
230+
onSubmit={jest.fn()}
231+
FormTemplate={FormTemplate}
232+
componentMapper={componentMapper}
233+
/>
234+
);
235+
236+
expect(wrapper.find(Alert)).toHaveLength(0);
237+
238+
await act(async () => {
239+
wrapper
240+
.find('input')
241+
.first()
242+
.instance().value = 'cats';
243+
wrapper
244+
.find('input')
245+
.first()
246+
.simulate('change');
247+
});
248+
wrapper.update();
249+
250+
expect(wrapper.find(Alert)).toHaveLength(1);
251+
expect(wrapper.find(Alert).props().title).toEqual('some error title');
252+
expect(wrapper.find(Alert).text()).toEqual('Danger alert:some error titlesome description');
253+
});
161254
});

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,7 @@ describe('<WizardSTepButtons', () => {
328328
});
329329
});
330330

331-
it.only('conditional submit step', () => {
331+
it('conditional submit step', () => {
332332
const submit = jest.fn();
333333
const schema = {
334334
fields: [
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export const FormError: 'FINAL_FORM/form-error'
2+
3+
export default FormError;
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { FORM_ERROR } from 'final-form';
2+
3+
export default FORM_ERROR;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { default } from './form-error';
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { default } from './form-error';

packages/react-form-renderer/src/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export { default as FieldProvider } from './field-provider';
88
export { default as FormRenderer } from './form-renderer';
99
export { default as FormSpy } from './form-spy';
1010
export { default as Form } from './form';
11+
export { default as FormError } from './form-error';
1112
export { default as parseCondition } from './parse-condition';
1213
export { default as RendererContext } from './renderer-context';
1314
export { default as schemaErrors } from './schema-errors';

packages/react-renderer-demo/src/pages/components/form-template.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,14 @@ import { FormTemplate } from '@data-driven-forms/pf4-component-mapper'
5656

5757
### Props
5858

59+
#### alertProps
60+
61+
*object*
62+
63+
Props passed to `FormError` alert component.
64+
65+
---
66+
5967
#### buttonClassName
6068

6169
*string*

0 commit comments

Comments
 (0)