mirror of
https://github.com/ansible/awx.git
synced 2024-11-01 08:21:15 +03:00
Merge pull request #87 from ansible/lookup-form-component
Lookup form component
This commit is contained in:
commit
3060abab1d
@ -126,4 +126,16 @@ describe('APIClient (api.js)', () => {
|
|||||||
|
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('getInstanceGroups calls expected http method', async (done) => {
|
||||||
|
const createPromise = () => Promise.resolve();
|
||||||
|
const mockHttp = ({ get: jest.fn(createPromise) });
|
||||||
|
|
||||||
|
const api = new APIClient(mockHttp);
|
||||||
|
await api.getInstanceGroups();
|
||||||
|
|
||||||
|
expect(mockHttp.get).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
78
__tests__/components/Lookup.test.jsx
Normal file
78
__tests__/components/Lookup.test.jsx
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { mount } from 'enzyme';
|
||||||
|
import Lookup from '../../src/components/Lookup';
|
||||||
|
import { I18nProvider } from '@lingui/react';
|
||||||
|
|
||||||
|
const mockData = [{ name: 'foo', id: 0, isChecked: false }];
|
||||||
|
describe('<Lookup />', () => {
|
||||||
|
test('initially renders succesfully', () => {
|
||||||
|
mount(
|
||||||
|
<Lookup
|
||||||
|
lookup_header="Foo Bar"
|
||||||
|
lookupChange={() => { }}
|
||||||
|
data={mockData}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
test('calls "onLookup" when search icon is clicked', () => {
|
||||||
|
const spy = jest.spyOn(Lookup.prototype, 'onLookup');
|
||||||
|
const wrapper = mount(
|
||||||
|
<I18nProvider>
|
||||||
|
<Lookup
|
||||||
|
lookup_header="Foo Bar"
|
||||||
|
lookupChange={() => { }}
|
||||||
|
data={mockData}
|
||||||
|
/>
|
||||||
|
</I18nProvider>
|
||||||
|
);
|
||||||
|
expect(spy).not.toHaveBeenCalled();
|
||||||
|
wrapper.find('#search').simulate('click');
|
||||||
|
expect(spy).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
test('calls "onChecked" when a user changes a checkbox', () => {
|
||||||
|
const spy = jest.spyOn(Lookup.prototype, 'onChecked');
|
||||||
|
const wrapper = mount(
|
||||||
|
<I18nProvider>
|
||||||
|
<Lookup
|
||||||
|
lookup_header="Foo Bar"
|
||||||
|
lookupChange={() => { }}
|
||||||
|
data={mockData}
|
||||||
|
/>
|
||||||
|
</I18nProvider>
|
||||||
|
);
|
||||||
|
wrapper.find('#search').simulate('click');
|
||||||
|
wrapper.find('input[type="checkbox"]').simulate('change');
|
||||||
|
expect(spy).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
test('calls "onRemove" when remove icon is clicked', () => {
|
||||||
|
const spy = jest.spyOn(Lookup.prototype, 'onRemove');
|
||||||
|
const mockData = [{ name: 'foo', id: 0, isChecked: false }, { name: 'bar', id: 1, isChecked: true }];
|
||||||
|
const wrapper = mount(
|
||||||
|
<I18nProvider>
|
||||||
|
<Lookup
|
||||||
|
lookup_header="Foo Bar"
|
||||||
|
lookupChange={() => { }}
|
||||||
|
data={mockData}
|
||||||
|
/>
|
||||||
|
</I18nProvider>
|
||||||
|
);
|
||||||
|
wrapper.find('.awx-c-icon--remove').simulate('click');
|
||||||
|
expect(spy).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
test('"wrapTags" method properly handles data', () => {
|
||||||
|
const spy = jest.spyOn(Lookup.prototype, 'wrapTags');
|
||||||
|
const mockData = [{ name: 'foo', id: 0, isChecked: false }, { name: 'bar', id: 1, isChecked: false }];
|
||||||
|
const wrapper = mount(
|
||||||
|
<I18nProvider>
|
||||||
|
<Lookup
|
||||||
|
lookup_header="Foo Bar"
|
||||||
|
lookupChange={() => { }}
|
||||||
|
data={mockData}
|
||||||
|
/>
|
||||||
|
</I18nProvider>
|
||||||
|
);
|
||||||
|
expect(spy).toHaveBeenCalled();
|
||||||
|
const pill = wrapper.find('span.awx-c-tag--pill');
|
||||||
|
expect(pill).toHaveLength(0);
|
||||||
|
});
|
||||||
|
});
|
@ -1,28 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { mount } from 'enzyme';
|
import { mount } from 'enzyme';
|
||||||
import { MemoryRouter } from 'react-router-dom';
|
import { MemoryRouter } from 'react-router-dom';
|
||||||
|
import OrganizationAdd from '../../../../src/pages/Organizations/screens/OrganizationAdd'
|
||||||
let OrganizationAdd;
|
|
||||||
const getAppWithConfigContext = (context = {
|
|
||||||
custom_virtualenvs: ['foo', 'bar']
|
|
||||||
}) => {
|
|
||||||
|
|
||||||
// Mock the ConfigContext module being used in our OrganizationAdd component
|
|
||||||
jest.doMock('../../../../src/context', () => {
|
|
||||||
return {
|
|
||||||
ConfigContext: {
|
|
||||||
Consumer: (props) => props.children(context)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Return the updated OrganizationAdd module with mocked context
|
|
||||||
return require('../../../../src/pages/Organizations/screens/OrganizationAdd').default;
|
|
||||||
};
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
OrganizationAdd = getAppWithConfigContext();
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('<OrganizationAdd />', () => {
|
describe('<OrganizationAdd />', () => {
|
||||||
test('initially renders succesfully', () => {
|
test('initially renders succesfully', () => {
|
||||||
@ -78,4 +57,48 @@ describe('<OrganizationAdd />', () => {
|
|||||||
wrapper.find('button.at-C-CancelButton').prop('onClick')();
|
wrapper.find('button.at-C-CancelButton').prop('onClick')();
|
||||||
expect(spy).toBeCalled();
|
expect(spy).toBeCalled();
|
||||||
});
|
});
|
||||||
|
test('API response data is formatted properly', () => {
|
||||||
|
const mockData = { data: { results: [{ name: 'test instance', id: 1 }] } };
|
||||||
|
const promise = Promise.resolve(mockData);
|
||||||
|
|
||||||
|
return promise.then(({ data }) => {
|
||||||
|
const expected = [{ id: 1, name: 'test instance', isChecked: false }];
|
||||||
|
const results = OrganizationAdd.WrappedComponent.prototype.format(data);
|
||||||
|
expect(results).toEqual(expected);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
test('API response is formatted properly', (done) => {
|
||||||
|
const spy = jest.spyOn(OrganizationAdd.WrappedComponent.prototype, 'format');
|
||||||
|
const mockedResp = {data: {id: 1, name: 'foo bar'} };
|
||||||
|
const api = { getInstanceGroups: jest.fn().mockResolvedValue(mockedResp) };
|
||||||
|
mount(
|
||||||
|
<MemoryRouter>
|
||||||
|
<OrganizationAdd api={api} />
|
||||||
|
</MemoryRouter>
|
||||||
|
);
|
||||||
|
|
||||||
|
setImmediate(() => {
|
||||||
|
expect(spy).toHaveBeenCalled();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Successful form submission triggers redirect', (done) => {
|
||||||
|
const onSuccess = jest.spyOn(OrganizationAdd.WrappedComponent.prototype, 'onSuccess');
|
||||||
|
const resetForm = jest.spyOn(OrganizationAdd.WrappedComponent.prototype, 'resetForm');
|
||||||
|
const mockedResp = {data: {id: 1, related: {instance_groups: '/bar'}}};
|
||||||
|
const api = { createOrganization: jest.fn().mockResolvedValue(mockedResp), createInstanceGroups: jest.fn().mockResolvedValue('done') };
|
||||||
|
const wrapper = mount(
|
||||||
|
<MemoryRouter>
|
||||||
|
<OrganizationAdd api={api} />
|
||||||
|
</MemoryRouter>
|
||||||
|
);
|
||||||
|
wrapper.find('input#add-org-form-name').simulate('change', { target: { value: 'foo' } });
|
||||||
|
wrapper.find('button.at-C-SubmitButton').prop('onClick')();
|
||||||
|
setImmediate(() => {
|
||||||
|
expect(onSuccess).toHaveBeenCalled();
|
||||||
|
expect(resetForm).toHaveBeenCalled();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -4,6 +4,7 @@ const API_LOGOUT = `${API_ROOT}logout/`;
|
|||||||
const API_V2 = `${API_ROOT}v2/`;
|
const API_V2 = `${API_ROOT}v2/`;
|
||||||
const API_CONFIG = `${API_V2}config/`;
|
const API_CONFIG = `${API_V2}config/`;
|
||||||
const API_ORGANIZATIONS = `${API_V2}organizations/`;
|
const API_ORGANIZATIONS = `${API_V2}organizations/`;
|
||||||
|
const API_INSTANCE_GROUPS = `${API_V2}instance_groups/`;
|
||||||
|
|
||||||
const LOGIN_CONTENT_TYPE = 'application/x-www-form-urlencoded';
|
const LOGIN_CONTENT_TYPE = 'application/x-www-form-urlencoded';
|
||||||
|
|
||||||
@ -68,6 +69,14 @@ class APIClient {
|
|||||||
|
|
||||||
return this.http.get(endpoint);
|
return this.http.get(endpoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getInstanceGroups () {
|
||||||
|
return this.http.get(API_INSTANCE_GROUPS);
|
||||||
|
}
|
||||||
|
|
||||||
|
createInstanceGroups (url, id) {
|
||||||
|
return this.http.post(url, { id });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default APIClient;
|
export default APIClient;
|
||||||
|
60
src/app.scss
60
src/app.scss
@ -119,7 +119,30 @@
|
|||||||
--pf-c-about-modal-box--MaxWidth: 63rem;
|
--pf-c-about-modal-box--MaxWidth: 63rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.pf-c-list {
|
||||||
|
li {
|
||||||
|
list-style-type: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.awx-lookup {
|
||||||
|
min-height: 36px;
|
||||||
|
}
|
||||||
|
.pf-c-input-group__text {
|
||||||
|
&:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.awx-c-tag--pill {
|
||||||
|
color: white;
|
||||||
|
background-color: rgb(0, 123, 186);
|
||||||
|
border-radius: 3px;
|
||||||
|
margin: 1px 2px;
|
||||||
|
padding: 0 10px;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
//
|
//
|
||||||
// layout styles
|
// layout styles
|
||||||
//
|
//
|
||||||
@ -128,3 +151,40 @@
|
|||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
}
|
}
|
||||||
|
//
|
||||||
|
// list styles
|
||||||
|
//
|
||||||
|
.awx-c-list {
|
||||||
|
border-top: 1px solid #d7d7d7;
|
||||||
|
border-bottom: 1px solid #d7d7d7;
|
||||||
|
}
|
||||||
|
//
|
||||||
|
// pf modal overrides
|
||||||
|
//
|
||||||
|
.awx-c-modal {
|
||||||
|
width: 550px;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.awx-c-icon--remove {
|
||||||
|
padding-left: 10px;
|
||||||
|
&:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.pf-c-modal-box__footer {
|
||||||
|
--pf-c-modal-box__footer--PaddingTop: 0;
|
||||||
|
--pf-c-modal-box__footer--PaddingBottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pf-c-modal-box__header {
|
||||||
|
--pf-c-modal-box__header--PaddingTop: 10px;
|
||||||
|
--pf-c-modal-box__header--PaddingRight: 0;
|
||||||
|
--pf-c-modal-box__header--PaddingBottom: 0;
|
||||||
|
--pf-c-modal-box__header--PaddingLeft: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pf-c-modal-box__body {
|
||||||
|
--pf-c-modal-box__body--PaddingLeft: 20px;
|
||||||
|
--pf-c-modal-box__body--PaddingRight: 20px;
|
||||||
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { Component } from 'react';
|
import React from 'react';
|
||||||
import { I18n } from '@lingui/react';
|
import { I18n } from '@lingui/react';
|
||||||
import { Trans, t } from '@lingui/macro';
|
import { Trans, t } from '@lingui/macro';
|
||||||
import {
|
import {
|
||||||
@ -12,7 +12,7 @@ import heroImg from '@patternfly/patternfly-next/assets/images/pfbg_992.jpg';
|
|||||||
import brandImg from '../../images/tower-logo-white.svg';
|
import brandImg from '../../images/tower-logo-white.svg';
|
||||||
import logoImg from '../../images/tower-logo-login.svg';
|
import logoImg from '../../images/tower-logo-login.svg';
|
||||||
|
|
||||||
class About extends Component {
|
class About extends React.Component {
|
||||||
static createSpeechBubble (version) {
|
static createSpeechBubble (version) {
|
||||||
let text = `Tower ${version}`;
|
let text = `Tower ${version}`;
|
||||||
let top = '';
|
let top = '';
|
||||||
|
34
src/components/ListItem/CheckboxListItem.jsx
Normal file
34
src/components/ListItem/CheckboxListItem.jsx
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { I18n } from '@lingui/react';
|
||||||
|
import { t } from '@lingui/macro';
|
||||||
|
import {
|
||||||
|
Checkbox,
|
||||||
|
} from '@patternfly/react-core';
|
||||||
|
|
||||||
|
export default ({
|
||||||
|
itemId,
|
||||||
|
name,
|
||||||
|
isSelected,
|
||||||
|
onSelect,
|
||||||
|
}) => (
|
||||||
|
<li key={itemId} className="pf-c-data-list__item" aria-labelledby="check-action-item1">
|
||||||
|
<div className="pf-c-data-list__check">
|
||||||
|
<I18n>
|
||||||
|
{({ i18n }) => (
|
||||||
|
<Checkbox
|
||||||
|
checked={isSelected}
|
||||||
|
onChange={onSelect}
|
||||||
|
aria-label={i18n._(t`selected ${itemId}`)}
|
||||||
|
id={`selectd-${itemId}`}
|
||||||
|
value={itemId}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</I18n>
|
||||||
|
</div>
|
||||||
|
<div className="pf-c-data-list__cell">
|
||||||
|
<label htmlFor={`selectd-${itemId}`} className="check-action-item">
|
||||||
|
<b>{name}</b>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
);
|
3
src/components/ListItem/index.js
Normal file
3
src/components/ListItem/index.js
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import CheckboxListItem from './CheckboxListItem';
|
||||||
|
|
||||||
|
export default CheckboxListItem;
|
91
src/components/Lookup/Lookup.jsx
Normal file
91
src/components/Lookup/Lookup.jsx
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import { SearchIcon } from '@patternfly/react-icons';
|
||||||
|
import {
|
||||||
|
Modal,
|
||||||
|
Button,
|
||||||
|
ActionGroup,
|
||||||
|
Toolbar,
|
||||||
|
ToolbarGroup,
|
||||||
|
} from '@patternfly/react-core';
|
||||||
|
|
||||||
|
import CheckboxListItem from '../ListItem'
|
||||||
|
|
||||||
|
class Lookup extends React.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
isModalOpen: false,
|
||||||
|
}
|
||||||
|
this.handleModalToggle = this.handleModalToggle.bind(this);
|
||||||
|
this.onLookup = this.onLookup.bind(this);
|
||||||
|
this.onChecked = this.onChecked.bind(this);
|
||||||
|
this.wrapTags = this.wrapTags.bind(this);
|
||||||
|
this.onRemove = this.onRemove.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleModalToggle() {
|
||||||
|
this.setState((prevState, _) => ({
|
||||||
|
isModalOpen: !prevState.isModalOpen,
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
onLookup() {
|
||||||
|
this.handleModalToggle();
|
||||||
|
}
|
||||||
|
|
||||||
|
onChecked(_, evt) {
|
||||||
|
this.props.lookupChange(evt.target.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
onRemove(evt) {
|
||||||
|
this.props.lookupChange(evt.target.id);
|
||||||
|
}
|
||||||
|
wrapTags(tags) {
|
||||||
|
return tags.filter(tag => tag.isChecked).map((tag, index) => {
|
||||||
|
return (
|
||||||
|
<span className="awx-c-tag--pill" key={index}>{tag.name}<span className="awx-c-icon--remove" id={tag.id} onClick={this.onRemove}>x</span></span>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { isModalOpen } = this.state;
|
||||||
|
const { data } = this.props;
|
||||||
|
return (
|
||||||
|
<div className="pf-c-input-group awx-lookup">
|
||||||
|
<span className="pf-c-input-group__text" aria-label="search" id="search" onClick={this.onLookup}><SearchIcon /></span>
|
||||||
|
<div className="pf-c-form-control">{this.wrapTags(this.props.data)}</div>
|
||||||
|
<Modal
|
||||||
|
className="awx-c-modal"
|
||||||
|
title={`Select ${this.props.lookupHeader}`}
|
||||||
|
isOpen={isModalOpen}
|
||||||
|
onClose={this.handleModalToggle}
|
||||||
|
>
|
||||||
|
<ul className="pf-c-data-list awx-c-list">
|
||||||
|
{data.map(i =>
|
||||||
|
<CheckboxListItem
|
||||||
|
key={i.id}
|
||||||
|
itemId={i.id}
|
||||||
|
name={i.name}
|
||||||
|
isSelected={i.isChecked}
|
||||||
|
onSelect={this.onChecked}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</ul>
|
||||||
|
<ActionGroup className="at-align-right">
|
||||||
|
<Toolbar>
|
||||||
|
<ToolbarGroup>
|
||||||
|
<Button className="at-C-SubmitButton" variant="primary" onClick={this.handleModalToggle} >Select</Button>
|
||||||
|
</ToolbarGroup>
|
||||||
|
<ToolbarGroup>
|
||||||
|
<Button className="at-C-CancelButton" variant="secondary" onClick={this.handleModalToggle}>Cancel</Button>
|
||||||
|
</ToolbarGroup>
|
||||||
|
</Toolbar>
|
||||||
|
</ActionGroup>
|
||||||
|
</Modal>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export default Lookup;
|
3
src/components/Lookup/index.js
Normal file
3
src/components/Lookup/index.js
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import Lookup from './Lookup';
|
||||||
|
|
||||||
|
export default Lookup;
|
@ -19,24 +19,28 @@ import {
|
|||||||
} from '@patternfly/react-core';
|
} from '@patternfly/react-core';
|
||||||
|
|
||||||
import { ConfigContext } from '../../../context';
|
import { ConfigContext } from '../../../context';
|
||||||
|
import Lookup from '../../../components/Lookup';
|
||||||
import AnsibleSelect from '../../../components/AnsibleSelect'
|
import AnsibleSelect from '../../../components/AnsibleSelect'
|
||||||
const { light } = PageSectionVariants;
|
const { light } = PageSectionVariants;
|
||||||
|
|
||||||
class OrganizationAdd extends React.Component {
|
class OrganizationAdd extends React.Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.handleChange = this.handleChange.bind(this);
|
this.handleChange = this.handleChange.bind(this);
|
||||||
this.onSelectChange = this.onSelectChange.bind(this);
|
this.onSelectChange = this.onSelectChange.bind(this);
|
||||||
|
this.onLookupChange = this.onLookupChange.bind(this);
|
||||||
this.onSubmit = this.onSubmit.bind(this);
|
this.onSubmit = this.onSubmit.bind(this);
|
||||||
this.resetForm = this.resetForm.bind(this);
|
this.resetForm = this.resetForm.bind(this);
|
||||||
|
this.onSuccess = this.onSuccess.bind(this);
|
||||||
this.onCancel = this.onCancel.bind(this);
|
this.onCancel = this.onCancel.bind(this);
|
||||||
|
this.format = this.format.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
name: '',
|
name: '',
|
||||||
description: '',
|
description: '',
|
||||||
instanceGroups: '',
|
results: [],
|
||||||
|
instance_groups: [],
|
||||||
custom_virtualenv: '',
|
custom_virtualenv: '',
|
||||||
error: '',
|
error: '',
|
||||||
};
|
};
|
||||||
@ -45,12 +49,23 @@ class OrganizationAdd extends React.Component {
|
|||||||
this.setState({ custom_virtualenv: value });
|
this.setState({ custom_virtualenv: value });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
onLookupChange(id, _) {
|
||||||
|
let selected = { ...this.state.results }
|
||||||
|
const index = id - 1;
|
||||||
|
selected[index].isChecked = !selected[index].isChecked;
|
||||||
|
this.setState({ selected })
|
||||||
|
}
|
||||||
|
|
||||||
resetForm() {
|
resetForm() {
|
||||||
this.setState({
|
this.setState({
|
||||||
...this.state,
|
|
||||||
name: '',
|
name: '',
|
||||||
description: ''
|
description: '',
|
||||||
|
});
|
||||||
|
let reset = [];
|
||||||
|
this.state.results.map((result) => {
|
||||||
|
reset.push({ id: result.id, name: result.name, isChecked: false });
|
||||||
})
|
})
|
||||||
|
this.setState({ results: reset });
|
||||||
}
|
}
|
||||||
|
|
||||||
handleChange(_, evt) {
|
handleChange(_, evt) {
|
||||||
@ -60,16 +75,57 @@ class OrganizationAdd extends React.Component {
|
|||||||
async onSubmit() {
|
async onSubmit() {
|
||||||
const { api } = this.props;
|
const { api } = this.props;
|
||||||
const data = Object.assign({}, { ...this.state });
|
const data = Object.assign({}, { ...this.state });
|
||||||
await api.createOrganization(data);
|
try {
|
||||||
|
const { data: response } = await api.createOrganization(data);
|
||||||
|
const url = response.related.instance_groups;
|
||||||
|
const selected = this.state.results.filter(group => group.isChecked);
|
||||||
|
try {
|
||||||
|
if (selected.length > 0) {
|
||||||
|
selected.forEach( async (select) => {
|
||||||
|
await api.createInstanceGroups(url, select.id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
this.setState({ createInstanceGroupsError: err })
|
||||||
|
} finally {
|
||||||
this.resetForm();
|
this.resetForm();
|
||||||
|
this.onSuccess(response.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
this.setState({ onSubmitError: err })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onCancel() {
|
onCancel() {
|
||||||
this.props.history.push('/organizations');
|
this.props.history.push('/organizations');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onSuccess(id) {
|
||||||
|
this.props.history.push(`/organizations/${id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
format(data) {
|
||||||
|
let results = [];
|
||||||
|
data.results.map((result) => {
|
||||||
|
results.push({ id: result.id, name: result.name, isChecked: false });
|
||||||
|
});
|
||||||
|
return results;
|
||||||
|
};
|
||||||
|
|
||||||
|
async componentDidMount() {
|
||||||
|
const { api } = this.props;
|
||||||
|
try {
|
||||||
|
const { data } = await api.getInstanceGroups();
|
||||||
|
const results = this.format(data);
|
||||||
|
this.setState({ results });
|
||||||
|
} catch (error) {
|
||||||
|
this.setState({ getInstanceGroupsError: error })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { name } = this.state;
|
const { name, results } = this.state;
|
||||||
const enabled = name.length > 0; // TODO: add better form validation
|
const enabled = name.length > 0; // TODO: add better form validation
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -106,13 +162,11 @@ class OrganizationAdd extends React.Component {
|
|||||||
onChange={this.handleChange}
|
onChange={this.handleChange}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
{/* LOOKUP MODAL PLACEHOLDER */}
|
|
||||||
<FormGroup label="Instance Groups" fieldId="simple-form-instance-groups">
|
<FormGroup label="Instance Groups" fieldId="simple-form-instance-groups">
|
||||||
<TextInput
|
<Lookup
|
||||||
id="add-org-form-instance-groups"
|
lookupHeader="Instance Groups"
|
||||||
name="instance-groups"
|
lookupChange={this.onLookupChange}
|
||||||
value={this.state.instanceGroups}
|
data={results}
|
||||||
onChange={this.handleChange}
|
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
<ConfigContext.Consumer>
|
<ConfigContext.Consumer>
|
||||||
|
Loading…
Reference in New Issue
Block a user