Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace service form #4985

Merged
merged 4 commits into from Nov 30, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view

This file was deleted.

52 changes: 52 additions & 0 deletions app/javascript/components/service-form/index.jsx
@@ -0,0 +1,52 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Grid } from 'patternfly-react';
import createSchema from './schema';
import MiqFormRenderer from '../../forms/data-driven-form';
import { http } from '../../http_api';
import { cleanVirtualDom } from '../../miq-component/helpers';

class ServiceForm extends Component {
constructor(props) {
super(props);
this.state = {
schema: createSchema(props.maxNameLen, props.maxDescLen),
};
}
componentDidMount() {
cleanVirtualDom();
miqSparkleOn();
http.get(`/service/service_form_fields/${this.props.serviceFormId}`)
.then(data => this.setState({ initialValues: { ...data } }, miqSparkleOff()));
}
render() {
const { serviceFormId } = this.props;
const cancelUrl = `/service/service_edit/${serviceFormId}?button=cancel`;
const submitUrl = `/service/service_edit/${serviceFormId}?button=save`;
return (
<Grid fluid style={{ paddingTop: 20 }}>
<MiqFormRenderer
initialValues={this.state.initialValues}
schema={this.state.schema}
onSubmit={values => miqAjaxButton(submitUrl, values)}
onCancel={() => miqAjaxButton(cancelUrl)}
onReset={() => add_flash(__('All changes have been reset'), 'warn')}
canReset
buttonsLabels={{
submitLabel: __('Save'),
resetLabel: __('Reset'),
cancelLabel: __('Cancel'),
}}
/>
</Grid>
);
}
}

ServiceForm.propTypes = {
maxNameLen: PropTypes.number.isRequired,
maxDescLen: PropTypes.number.isRequired,
serviceFormId: PropTypes.number.isRequired,
};

export default ServiceForm;
27 changes: 27 additions & 0 deletions app/javascript/components/service-form/schema.js
@@ -0,0 +1,27 @@
function createSchema(maxNameLen, maxDescLen) {
return {
fields: [{
component: 'text-field',
name: 'name',
maxLength: maxNameLen,
label: __('Name'),
validateOnMount: true,
autoFocus: true,
validate: [{
type: 'required-validator',
}],
}, {
component: 'text-field',
name: 'description',
maxLength: maxDescLen,
label: __('Description'),
validateOnMount: true,
autoFocus: true,
validate: [{
type: 'required-validator',
}],
}],
};
}

export default createSchema;
1 change: 0 additions & 1 deletion app/javascript/forms/data-driven-form.jsx
Expand Up @@ -10,5 +10,4 @@ const MiqFormRenderer = props => (
{...props}
/>
);

export default MiqFormRenderer;
2 changes: 2 additions & 0 deletions app/javascript/packs/component-definitions-common.js
Expand Up @@ -7,6 +7,7 @@ import VmSnapshotFormComponent from '../components/vm-snapshot-form-component';
import FormButtonsRedux from '../forms/form-buttons-redux';
import MiqAboutModal from '../components/miq-about-modal';
import CloudTennantForm from '../components/cloud-tenant-form/cloud-tenant-form';
import ServiceForm from '../components/service-form';

/**
* Add component definitions to this file.
Expand All @@ -24,3 +25,4 @@ ManageIQ.component.addReact('VmSnapshotFormComponent', VmSnapshotFormComponent);
ManageIQ.component.addReact('FormButtonsRedux', FormButtonsRedux);
ManageIQ.component.addReact('MiqAboutModal', MiqAboutModal);
ManageIQ.component.addReact('CloudTennantForm', CloudTennantForm);
ManageIQ.component.addReact('ServiceForm', ServiceForm);
94 changes: 94 additions & 0 deletions app/javascript/spec/service-form/service-form.spec.js
@@ -0,0 +1,94 @@
import React from 'react';
import { mount } from 'enzyme';
import fetchMock from 'fetch-mock';
import FormRender from '@data-driven-forms/react-form-renderer';
import ServiceForm from '../../components/service-form';

describe('Cloud tenant form component', () => {
Copy link
Contributor

@himdel himdel Nov 30, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cloud tenant != Service

EDIT: fixed in #5018

let initialProps;
let submitSpy;
let flashSpy;

beforeEach(() => {
initialProps = {
maxNameLen: 1,
Copy link
Contributor

@himdel himdel Nov 30, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Submit can't ever be undisabled when validation fails, right?

This needs to be at least "foo".length

EDIT: fixed in #5018

maxDescLen: 2,
serviceFormId: 3,
};
submitSpy = jest.spyOn(window, 'miqAjaxButton');
flashSpy = jest.spyOn(window, 'add_flash');
});

afterEach(() => {
fetchMock.reset();
fetchMock.restore();
submitSpy.mockRestore();
});

it('should request data after mount and set to state', (done) => {
fetchMock
.getOnce('/service/service_form_fields/3', {
foo: 'bar',
});
const wrapper = mount(<ServiceForm {...initialProps} />);
expect(fetchMock.lastUrl()).toEqual('/service/service_form_fields/3');
setImmediate(() => {
wrapper.update();
expect(wrapper.state().initialValues).toEqual({ foo: 'bar' });
done();
});
});

it('should call cancel action', (done) => {
fetchMock
.getOnce('/service/service_form_fields/3', {
name: 'foo',
description: 'bar',
});
const wrapper = mount(<ServiceForm {...initialProps} />);

setImmediate(() => {
wrapper.find('button').last().simulate('click');
expect(submitSpy).toHaveBeenCalledWith('/service/service_edit/3?button=cancel');
done();
});
});

it('should enable reset button and call reset callback', (done) => {
fetchMock
.getOnce('/service/service_form_fields/3', {});
const wrapper = mount(<ServiceForm {...initialProps} />);

setImmediate(() => {
// reset should be disabled
wrapper.find('button').at(1).simulate('click');
expect(flashSpy).not.toHaveBeenCalled();
// change value of some input to enable reset button
wrapper.find('input').first().simulate('change', {
target: {
value: 'foo',
},
});
wrapper.find('button').at(1).simulate('click');
expect(flashSpy).toHaveBeenCalledWith(expect.any(String), 'warn');
done();
});
});

it('should enable submit button and call submit callback', (done) => {
fetchMock
.getOnce('/service/service_form_fields/3', {});
const wrapper = mount(<ServiceForm {...initialProps} />);

setImmediate(() => {
// reset should be disabled
wrapper.find('button').at(0).simulate('click');
// change form state to enable reset button
wrapper.find(FormRender).childAt(0).instance().form.change('name', 'foo');
wrapper.find(FormRender).childAt(0).instance().form.change('description', 'bar');
wrapper.find('button').at(0).simulate('click');
Copy link
Contributor

@himdel himdel Nov 30, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's still disabled, MiqFormRenderer is using disableSubmit

(probably fixed in #5018)

expect(submitSpy).toHaveBeenCalledWith('/service/service_edit/3?button=save', { name: 'foo', description: 'bar' });
done();
});
});
});
45 changes: 2 additions & 43 deletions app/views/service/_service_form.html.haml
@@ -1,43 +1,2 @@
- @angular_form = true

%form.form-horizontal#form_div{"name" => "angularForm",
"ng-controller" => "serviceFormController as vm",
"ng-show" => "vm.afterGet",
"model" => "vm.serviceModel",
"model-copy" => "vm.modelCopy",
"miq-form" => true,
"form-changed" => true}
= render :partial => "layouts/flash_msg"
%div
%div
.form-group{"ng-class" => "{'has-error': angularForm.name.$invalid}"}
%div
%div
.form-group{"ng-class" => "{'has-error': angularForm.name.$invalid}"}
%label.col-md-2.control-label{"for" => "name"}
= _("Name")
.col-md-8
%input.form-control{"type" => "text",
"id" => "name",
"name" => "name",
"ng-model" => "vm.serviceModel.name",
"maxlength" => "#{ViewHelper::MAX_NAME_LEN}",
"required" => true,
"auto-focus" => ""}
%span.help-block{"ng-show" => "angularForm.name.$error.miqrequired"}
= _("Required")
.form-group{"ng-class" => "{'has-error': angularForm.description.$invalid}"}
%label.col-md-2.control-label{"for" => "description"}
= _("Description")
.col-md-8
%input.form-control{"type" => "text",
"id" => "description",
"name" => "description",
"ng-model" => "vm.serviceModel.description",
"maxlength" => "#{ViewHelper::MAX_DESC_LEN}",
"required" => true}
= render :partial => "layouts/angular/generic_form_buttons"

:javascript
ManageIQ.angular.app.value('serviceFormId', '#{@service.id}');
miq_bootstrap('#form_div');
= render :partial => "layouts/flash_msg"
= react('ServiceForm', { :maxNameLen => ViewHelper::MAX_NAME_LEN, :maxDescLen => ViewHelper::MAX_DESC_LEN, :serviceFormId => @service.id})
2 changes: 1 addition & 1 deletion package.json
Expand Up @@ -43,7 +43,6 @@
"bootstrap-switch": "~3.3.4",
"codemirror": "~5.19.0",
"connected-react-router": "^4.3.0",
"enzyme-to-json": "^3.3.4",
"eonasdan-bootstrap-datetimepicker": "~4.17.47",
"es6-shim": "~0.35.3",
"graphiql": "^0.11.11",
Expand Down Expand Up @@ -112,6 +111,7 @@
"enhanced-resolve": "~4.0.0",
"enzyme": "^3.3.0",
"enzyme-adapter-react-16": "^1.1.1",
"enzyme-to-json": "^3.3.4",
"eslint": "^4.14.0",
"eslint-config-airbnb": "^16.0.0",
"eslint-config-angular": "~0.5.0",
Expand Down