Skip to content

Commit d359d80

Browse files
committed
Transforms haml forms to react for Automate class
1 parent 61578ea commit d359d80

File tree

9 files changed

+476
-35
lines changed

9 files changed

+476
-35
lines changed

app/controllers/miq_ae_class_controller.rb

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,9 @@ def replace_right_cell(options = {})
341341
:serialize => @sb[:active_tab] == 'methods',
342342
}
343343
])
344+
if @hide_bottom_bar
345+
presenter.hide(:paging_div, :form_buttons_div)
346+
end
344347
else
345348
# incase it was hidden for summary screen, and incase there were no records on show_list
346349
presenter.hide(:paging_div, :form_buttons_div)
@@ -459,6 +462,7 @@ def edit_class
459462
@ae_class = find_record_with_rbac(MiqAeClass, params[:id])
460463
end
461464
set_form_vars
465+
@hide_bottom_bar = true
462466
# have to get name and set node info, to load multiple tabs correctly
463467
# rec_name = get_rec_name(@ae_class)
464468
# get_node_info("aec-#{@ae_class.id}")
@@ -468,6 +472,24 @@ def edit_class
468472
replace_right_cell
469473
end
470474

475+
def edit_class_record
476+
assert_privileges("miq_ae_class_edit")
477+
unless params[:id]
478+
obj = find_checked_items
479+
@_params[:id] = obj[0]
480+
end
481+
@hide_bottom_bar = true
482+
483+
class_rec = MiqAeClass.find(params[:id])
484+
485+
render :json => {
486+
:fqname => class_rec.fqname,
487+
:name => class_rec.name,
488+
:display_name => class_rec.display_name,
489+
:description => class_rec.description
490+
}
491+
end
492+
471493
def edit_fields
472494
assert_privileges("miq_ae_field_edit")
473495
if params[:pressed] == "miq_ae_item_edit" # came from Namespace details screen
@@ -1291,6 +1313,7 @@ def new
12911313
assert_privileges("miq_ae_class_new")
12921314
@ae_class = MiqAeClass.new
12931315
set_form_vars
1316+
@hide_bottom_bar = true
12941317
@in_a_form = true
12951318
replace_right_cell
12961319
end
@@ -1841,8 +1864,49 @@ def ae_method_operations
18411864
end
18421865
end
18431866

1867+
def class_update
1868+
assert_privileges(params[:id].present? ? 'miq_ae_class_edit' : 'miq_ae_class_new')
1869+
@hide_bottom_bar = true
1870+
class_update_create
1871+
end
1872+
18441873
private
18451874

1875+
def class_update_create
1876+
case params[:button]
1877+
when "add", "save"
1878+
class_rec = params[:id].blank? ? MiqAeClass.new : MiqAeClass.find(params[:id]) # Get new or existing record
1879+
add_flash(_("Name is required"), :error) if params[:name].blank?
1880+
class_rec.name = params[:name]
1881+
class_rec.display_name = params[:display_name]
1882+
class_rec.description = params[:description]
1883+
class_rec.namespace_id = x_node.split('-')[1] if params[:id].blank?
1884+
begin
1885+
class_rec.save!
1886+
rescue StandardError
1887+
class_rec.errors.each do |error|
1888+
add_flash("#{error.attribute.to_s.capitalize} #{error.message}", :error)
1889+
end
1890+
@changed = true
1891+
javascript_flash
1892+
else
1893+
edit_hash = {}
1894+
edit_hash[:new] = {:name => params[:name],
1895+
:display_name => params[:display_name], :description => params[:description]}
1896+
edit_hash[:current] = if params[:old_data]
1897+
{:name => params[:old_data][:name],
1898+
:display_name => params[:old_data][:display_name],
1899+
:description => params[:old_data][:description]}
1900+
else
1901+
{:name => nil, :display_name => nil, :description => nil}
1902+
end
1903+
AuditEvent.success(build_saved_audit(class_rec, edit_hash))
1904+
@edit = session[:edit] = nil # clean out the saved info
1905+
replace_right_cell(:nodetype => x_node, :replace_trees => [:ae])
1906+
end
1907+
end
1908+
end
1909+
18461910
def get_template_class(location)
18471911
if location == "ansible_workflow_template"
18481912
ManageIQ::Providers::ExternalAutomationManager::ConfigurationWorkflow
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { componentTypes, validatorTypes } from '@@ddf';
2+
3+
const createSchema = (fqname) => ({
4+
fields: [
5+
{
6+
component: componentTypes.PLAIN_TEXT,
7+
name: 'fqname',
8+
label: `${__('Fully Qualified Name')}:\t ${fqname}`,
9+
},
10+
{
11+
component: componentTypes.TEXT_FIELD,
12+
id: 'name',
13+
name: 'name',
14+
label: __('Name'),
15+
maxLength: 128,
16+
validate: [{ type: validatorTypes.REQUIRED }],
17+
isRequired: true,
18+
},
19+
{
20+
component: componentTypes.TEXT_FIELD,
21+
id: 'display_name',
22+
name: 'display_name',
23+
label: __('Display Name'),
24+
maxLength: 128,
25+
},
26+
{
27+
component: componentTypes.TEXT_FIELD,
28+
id: 'description',
29+
name: 'description',
30+
label: __('Description'),
31+
maxLength: 255,
32+
},
33+
],
34+
});
35+
36+
export default createSchema;
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
import React, { useState, useEffect } from 'react';
2+
import { FormSpy } from '@data-driven-forms/react-form-renderer';
3+
import { Button } from 'carbon-components-react';
4+
import MiqFormRenderer, { useFormApi } from '@@ddf';
5+
import PropTypes from 'prop-types';
6+
import createSchema from './class-form.schema';
7+
import miqRedirectBack from '../../helpers/miq-redirect-back';
8+
9+
const MiqAeClass = ({ classRecord, fqname }) => {
10+
const [data, setData] = useState({
11+
isLoading: true,
12+
initialValues: undefined,
13+
});
14+
15+
const isEdit = !!(classRecord && classRecord.id);
16+
17+
useEffect(() => {
18+
if (isEdit) {
19+
http.get(`/miq_ae_class/edit_class_record/${classRecord.id}/`).then((recordValues) => {
20+
if (recordValues) {
21+
setData({ ...data, isLoading: false, initialValues: recordValues });
22+
}
23+
});
24+
} else {
25+
const initialValues = {
26+
fqname,
27+
name: classRecord && classRecord.name,
28+
display_name: classRecord && classRecord.display_name,
29+
description: classRecord && classRecord.description,
30+
};
31+
setData({ ...data, isLoading: false, initialValues });
32+
}
33+
}, [classRecord]);
34+
35+
const onSubmit = (values) => {
36+
miqSparkleOn();
37+
38+
const params = {
39+
action: isEdit ? 'edit' : 'create',
40+
name: values.name,
41+
display_name: values.display_name,
42+
description: values.description,
43+
old_data: data.initialValues,
44+
button: classRecord.id ? 'save' : 'add',
45+
};
46+
47+
const request = isEdit
48+
? http.post(`/miq_ae_class/class_update/${classRecord.id}`, params)
49+
: http.post(`/miq_ae_class/class_update/`, params);
50+
51+
request
52+
.then(() => {
53+
const confirmation = isEdit ? __(`Class "${values.name}" was saved`) : __(`Class "${values.name}" was added`);
54+
miqRedirectBack(sprintf(confirmation, values.name), 'success', '/miq_ae_class/explorer');
55+
})
56+
.catch(miqSparkleOff);
57+
};
58+
59+
const onCancel = () => {
60+
const confirmation = classRecord.id ? __(`Edit of Class "${classRecord.name}" cancelled by the user`)
61+
: __(`Add of new Class was cancelled by the user`);
62+
const message = sprintf(
63+
confirmation
64+
);
65+
miqRedirectBack(message, 'warning', '/miq_ae_class/explorer');
66+
};
67+
68+
return (!data.isLoading
69+
? (
70+
<div className="dialog-provision-form">
71+
<MiqFormRenderer
72+
schema={createSchema(fqname)}
73+
initialValues={data.initialValues}
74+
onSubmit={onSubmit}
75+
onCancel={onCancel}
76+
canReset={!!classRecord.id}
77+
validate={() => {}}
78+
FormTemplate={(props) => <FormTemplate {...props} recId={classRecord.id} />}
79+
/>
80+
</div>
81+
) : null
82+
);
83+
};
84+
85+
const FormTemplate = ({
86+
formFields, recId,
87+
}) => {
88+
const {
89+
handleSubmit, onReset, onCancel, getState,
90+
} = useFormApi();
91+
const { valid, pristine } = getState();
92+
const submitLabel = !!recId ? __('Save') : __('Add');
93+
return (
94+
<form onSubmit={handleSubmit}>
95+
{formFields}
96+
<FormSpy>
97+
{() => (
98+
<div className="custom-button-wrapper">
99+
{ !recId
100+
? (
101+
<Button
102+
disabled={!valid}
103+
kind="primary"
104+
className="btnRight"
105+
type="submit"
106+
variant="contained"
107+
>
108+
{submitLabel}
109+
</Button>
110+
) : (
111+
<Button
112+
disabled={!valid || pristine}
113+
kind="primary"
114+
className="btnRight"
115+
type="submit"
116+
variant="contained"
117+
>
118+
{submitLabel}
119+
</Button>
120+
)}
121+
{!!recId
122+
? (
123+
<Button
124+
disabled={pristine}
125+
kind="secondary"
126+
className="btnRight"
127+
variant="contained"
128+
onClick={onReset}
129+
type="button"
130+
>
131+
{ __('Reset')}
132+
</Button>
133+
) : null}
134+
135+
<Button variant="contained" type="button" onClick={onCancel} kind="secondary">
136+
{ __('Cancel')}
137+
</Button>
138+
</div>
139+
)}
140+
</FormSpy>
141+
</form>
142+
);
143+
};
144+
145+
MiqAeClass.propTypes = {
146+
classRecord: PropTypes.shape({
147+
id: PropTypes.number,
148+
name: PropTypes.string,
149+
display_name: PropTypes.string,
150+
description: PropTypes.string,
151+
}),
152+
fqname: PropTypes.string.isRequired,
153+
};
154+
155+
MiqAeClass.defaultProps = {
156+
classRecord: undefined,
157+
};
158+
159+
FormTemplate.propTypes = {
160+
formFields: PropTypes.arrayOf(
161+
PropTypes.shape({ id: PropTypes.number }),
162+
PropTypes.shape({ name: PropTypes.string }),
163+
PropTypes.shape({ display_name: PropTypes.string }),
164+
PropTypes.shape({ description: PropTypes.string }),
165+
),
166+
recId: PropTypes.number,
167+
};
168+
169+
FormTemplate.defaultProps = {
170+
formFields: undefined,
171+
recId: undefined,
172+
};
173+
174+
export default MiqAeClass;

app/javascript/packs/component-definitions-common.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@ import WorkflowPayload from '../components/workflows/workflow_payload';
177177
import WorkflowRepositoryForm from '../components/workflow-repository-form';
178178
import XmlHolder from '../components/XmlHolder';
179179
import ZoneForm from '../components/zone-form';
180+
import MiqAeClass from '../components/miq-ae-class';
180181

181182
/**
182183
* Add component definitions to this file.
@@ -363,3 +364,4 @@ ManageIQ.component.addReact('WorkflowPayload', WorkflowPayload);
363364
ManageIQ.component.addReact('WorkflowRepositoryForm', WorkflowRepositoryForm);
364365
ManageIQ.component.addReact('XmlHolder', XmlHolder);
365366
ManageIQ.component.addReact('ZoneForm', ZoneForm);
367+
ManageIQ.component.addReact('MiqAeClass', MiqAeClass);
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`MiqAeClass Form Component should render add class form correctly 1`] = `""`;
4+
5+
exports[`MiqAeClass Form Component should render edit class form correctly 1`] = `""`;
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import React from 'react';
2+
import fetchMock from 'fetch-mock';
3+
import { shallow } from 'enzyme';
4+
import toJson from 'enzyme-to-json';
5+
import MiqAeClass from '../../components/miq-ae-class';
6+
7+
describe('MiqAeClass Form Component', () => {
8+
const classMockData = [
9+
{
10+
href: `/miq_ae_class/edit_class/2/`,
11+
id: 2,
12+
description: 'Configured System Provision',
13+
},
14+
];
15+
16+
const MiqAeClassEditData = {
17+
id: 40,
18+
name: 'test',
19+
display_name: 'test display name',
20+
description: 'test description',
21+
};
22+
23+
const fqName = 'Sample FQ Name';
24+
25+
afterEach(() => {
26+
fetchMock.reset();
27+
fetchMock.restore();
28+
});
29+
30+
it('should render add class form correctly', async() => {
31+
const wrapper = shallow(<MiqAeClass
32+
classRecord={undefined}
33+
fqname={fqName}
34+
/>);
35+
36+
fetchMock.get(`/miq_ae_class/new?&expand=resources/`, classMockData);
37+
38+
await new Promise((resolve) => {
39+
setImmediate(() => {
40+
wrapper.update();
41+
expect(toJson(wrapper)).toMatchSnapshot();
42+
resolve();
43+
});
44+
});
45+
});
46+
47+
it('should render edit class form correctly', async() => {
48+
const wrapper = shallow(<MiqAeClass
49+
classRecord={MiqAeClassEditData}
50+
fqname={fqName}
51+
/>);
52+
53+
fetchMock.get(`/miq_ae_class/edit_class_react/${MiqAeClassEditData.id}?&expand=resources/`, classMockData);
54+
await new Promise((resolve) => {
55+
setImmediate(() => {
56+
wrapper.update();
57+
expect(toJson(wrapper)).toMatchSnapshot();
58+
resolve();
59+
});
60+
});
61+
});
62+
});

0 commit comments

Comments
 (0)