1
0
mirror of https://github.com/ansible/awx.git synced 2024-10-31 23:51:09 +03:00

Merge pull request #53 from ansible/add-org-new

Basic Add Organization View
This commit is contained in:
kialam 2018-12-17 17:14:05 -05:00 committed by GitHub
commit 46e9fcfda7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 268 additions and 18 deletions

View File

@ -0,0 +1,19 @@
import React from 'react';
import { mount } from 'enzyme';
import AnsibleSelect from '../../src/components/AnsibleSelect';
const mockData = ['foo', 'bar'];
describe('<AnsibleSelect />', () => {
test('initially renders succesfully', async() => {
const wrapper = mount(<AnsibleSelect selected="foo" data={mockData} selectChange={() => {}} />);
wrapper.setState({ isHidden: false });
});
test('calls "onSelectChange" on dropdown select change', () => {
const spy = jest.spyOn(AnsibleSelect.prototype, 'onSelectChange');
const wrapper = mount(<AnsibleSelect selected="foo" data={mockData} selectChange={() => {}} />);
wrapper.setState({ isHidden: false });
expect(spy).not.toHaveBeenCalled();
wrapper.find('select').simulate('change');
expect(spy).toHaveBeenCalled();
});
});

View File

@ -1,14 +1,60 @@
import React from 'react';
import { mount } from 'enzyme';
import OrganizationAdd from '../../../../src/pages/Organizations/views/Organization.add';
import { MemoryRouter } from 'react-router-dom';
describe('<OrganizationAdd />', () => {
test('initially renders succesfully', () => {
mount(
<OrganizationAdd
match={{ path: '/organizations/add', url: '/organizations/add' }}
location={{ search: '', pathname: '/organizations/add' }}
/>
<MemoryRouter>
<OrganizationAdd
match={{ path: '/organizations/add', url: '/organizations/add' }}
location={{ search: '', pathname: '/organizations/add' }}
/>
</MemoryRouter>
);
});
test('calls "handleChange" when input values change', () => {
const spy = jest.spyOn(OrganizationAdd.WrappedComponent.prototype, 'handleChange');
const wrapper = mount(
<MemoryRouter>
<OrganizationAdd
match={{ path: '/organizations/add', url: '/organizations/add' }}
location={{ search: '', pathname: '/organizations/add' }}
/>
</MemoryRouter>
);
expect(spy).not.toHaveBeenCalled();
wrapper.find('input#add-org-form-name').simulate('change', { target: { value: 'foo' } });
wrapper.find('input#add-org-form-description').simulate('change', { target: { value: 'bar' } });
expect(spy).toHaveBeenCalledTimes(2);
});
test('calls "onSubmit" when Save button is clicked', () => {
const spy = jest.spyOn(OrganizationAdd.WrappedComponent.prototype, 'onSubmit');
const wrapper = mount(
<MemoryRouter>
<OrganizationAdd
match={{ path: '/organizations/add', url: '/organizations/add' }}
location={{ search: '', pathname: '/organizations/add' }}
/>
</MemoryRouter>
);
expect(spy).not.toHaveBeenCalled();
wrapper.find('button.at-C-SubmitButton').prop('onClick')();
expect(spy).toBeCalled();
});
test('calls "onCancel" when Cancel button is clicked', () => {
const spy = jest.spyOn(OrganizationAdd.WrappedComponent.prototype, 'onCancel');
const wrapper = mount(
<MemoryRouter>
<OrganizationAdd
match={{ path: '/organizations/add', url: '/organizations/add' }}
location={{ search: '', pathname: '/organizations/add' }}
/>
</MemoryRouter>
);
expect(spy).not.toHaveBeenCalled();
wrapper.find('button.at-C-CancelButton').prop('onClick')();
expect(spy).toBeCalled();
});
});

View File

@ -45,6 +45,8 @@ class APIClient {
}
get = (endpoint, params = {}) => this.http.get(endpoint, { params });
post = (endpoint, data) => this.http.post(endpoint, data);
}
export default new APIClient();

View File

@ -118,3 +118,13 @@
--pf-c-about-modal-box--MaxHeight: 40rem;
--pf-c-about-modal-box--MaxWidth: 63rem;
}
//
// layout styles
//
.at-align-right {
display: flex;
flex-direction: row;
justify-content: flex-end;
}

View File

@ -0,0 +1,36 @@
import React from 'react';
import {
FormGroup,
Select,
SelectOption,
} from '@patternfly/react-core';
class AnsibleSelect extends React.Component {
constructor(props) {
super(props);
this.onSelectChange = this.onSelectChange.bind(this);
}
onSelectChange(val, _) {
this.props.selectChange(val);
}
render() {
const { hidden } = this.props;
if (hidden) {
return null;
} else {
return (
<FormGroup label={this.props.labelName} fieldId="ansible-select">
<Select value={this.props.selected} onChange={this.onSelectChange} aria-label="Select Input">
{this.props.data.map((env, index) => (
<SelectOption isDisabled={env.disabled} key={index} value={env} label={env} />
))}
</Select>
</FormGroup>
);
}
}
}
export default AnsibleSelect;

View File

@ -0,0 +1,3 @@
import AnsibleSelect from './AnsibleSelect';
export default AnsibleSelect;

View File

@ -1,24 +1,158 @@
import React, { Fragment } from 'react';
import { withRouter } from 'react-router-dom';
import { Trans } from '@lingui/macro';
import {
PageSection,
PageSectionVariants,
Title,
Form,
FormGroup,
TextInput,
ActionGroup,
Toolbar,
ToolbarGroup,
Button,
Gallery,
Card,
CardBody,
} from '@patternfly/react-core';
const { light, medium } = PageSectionVariants;
import { API_ORGANIZATIONS, API_CONFIG } from '../../../endpoints';
import api from '../../../api';
import AnsibleSelect from '../../../components/AnsibleSelect'
const { light } = PageSectionVariants;
const OrganizationView = () => (
<Fragment>
<PageSection variant={light} className="pf-m-condensed">
<Title size="2xl">
<Trans>Organization Add</Trans>
</Title>
</PageSection>
<PageSection variant={medium}>
This is the add view
</PageSection>
</Fragment>
);
class OrganizationAdd extends React.Component {
constructor(props) {
super(props);
export default OrganizationView;
this.handleChange = this.handleChange.bind(this);
this.onSelectChange = this.onSelectChange.bind(this);
this.onSubmit = this.onSubmit.bind(this);
this.resetForm = this.resetForm.bind(this);
this.onCancel = this.onCancel.bind(this);
}
state = {
name: '',
description: '',
instanceGroups: '',
custom_virtualenv: '',
custom_virtualenvs: [],
hideAnsibleSelect: true,
error:'',
};
onSelectChange(value, _) {
this.setState({ custom_virtualenv: value });
};
resetForm() {
this.setState({
...this.state,
name: '',
description: ''
})
}
handleChange(_, evt) {
this.setState({ [evt.target.name]: evt.target.value });
}
async onSubmit() {
const data = Object.assign({}, { ...this.state });
await api.post(API_ORGANIZATIONS, data);
this.resetForm();
}
onCancel() {
this.props.history.push('/organizations');
}
async componentDidMount() {
try {
const { data } = await api.get(API_CONFIG);
this.setState({ custom_virtualenvs: [...data.custom_virtualenvs] });
if (this.state.custom_virtualenvs.length > 1) {
// Show dropdown if we have more than one ansible environment
this.setState({ hideAnsibleSelect: !this.state.hideAnsibleSelect });
}
} catch (error) {
this.setState({ error })
}
}
render() {
const { name } = this.state;
const enabled = name.length > 0; // TODO: add better form validation
return (
<Fragment>
<PageSection variant={light} className="pf-m-condensed">
<Title size="2xl">
<Trans>Organization Add</Trans>
</Title>
</PageSection>
<PageSection>
<Card>
<CardBody>
<Form autoComplete="off">
<Gallery gutter="md">
<FormGroup
label="Name"
isRequired
fieldId="add-org-form-name"
>
<TextInput
isRequired
type="text"
id="add-org-form-name"
name="name"
value={this.state.name}
onChange={this.handleChange}
/>
</FormGroup>
<FormGroup label="Description" fieldId="add-org-form-description">
<TextInput
id="add-org-form-description"
name="description"
value={this.state.description}
onChange={this.handleChange}
/>
</FormGroup>
{/* LOOKUP MODAL PLACEHOLDER */}
<FormGroup label="Instance Groups" fieldId="simple-form-instance-groups">
<TextInput
id="add-org-form-instance-groups"
name="instance-groups"
value={this.state.instanceGroups}
onChange={this.handleChange}
/>
</FormGroup>
<AnsibleSelect
labelName="Ansible Environment"
selected={this.state.custom_virtualenv}
selectChange={this.onSelectChange}
data={this.state.custom_virtualenvs}
hidden={this.state.hideAnsibleSelect}
/>
</Gallery>
<ActionGroup className="at-align-right">
<Toolbar>
<ToolbarGroup>
<Button className="at-C-SubmitButton" variant="primary" onClick={this.onSubmit} isDisabled={!enabled}>Save</Button>
</ToolbarGroup>
<ToolbarGroup>
<Button className="at-C-CancelButton" variant="secondary" onClick={this.onCancel}>Cancel</Button>
</ToolbarGroup>
</Toolbar>
</ActionGroup>
</Form>
</CardBody>
</Card>
</PageSection>
</Fragment>
);
}
}
export default withRouter(OrganizationAdd);