mirror of
https://github.com/ansible/awx.git
synced 2026-01-18 05:01:19 -03:30
Add Organization Details view and test
This commit is contained in:
parent
a2007b8e0c
commit
8846e1427e
@ -5,16 +5,114 @@ import { I18nProvider } from '@lingui/react';
|
||||
import OrganizationDetail from '../../../../../src/pages/Organizations/screens/Organization/OrganizationDetail';
|
||||
|
||||
describe('<OrganizationDetail />', () => {
|
||||
const mockDetails = {
|
||||
name: 'Foo',
|
||||
description: 'Bar',
|
||||
custom_virtualenv: 'Fizz',
|
||||
created: 'Bat',
|
||||
modified: 'Boo'
|
||||
};
|
||||
|
||||
test('initially renders succesfully', () => {
|
||||
mount(
|
||||
<I18nProvider>
|
||||
<MemoryRouter initialEntries={['/organizations/1']} initialIndex={0}>
|
||||
<OrganizationDetail
|
||||
match={{ url: '/organizations/1' }}
|
||||
organization={{ name: 'Default' }}
|
||||
match={{ params: { id: '1' } }}
|
||||
organization={mockDetails}
|
||||
/>
|
||||
</MemoryRouter>
|
||||
</I18nProvider>
|
||||
);
|
||||
});
|
||||
|
||||
test('should request instance groups from api', () => {
|
||||
const getOrganizationInstanceGroups = jest.fn();
|
||||
mount(
|
||||
<I18nProvider>
|
||||
<MemoryRouter initialEntries={['/organizations/1']} initialIndex={0}>
|
||||
<OrganizationDetail
|
||||
match={{ params: { id: '1' } }}
|
||||
api={{
|
||||
getOrganizationInstanceGroups
|
||||
}}
|
||||
organization={mockDetails}
|
||||
/>
|
||||
</MemoryRouter>
|
||||
</I18nProvider>
|
||||
).find('OrganizationDetail');
|
||||
|
||||
expect(getOrganizationInstanceGroups).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test('should handle setting instance groups to state', async () => {
|
||||
const mockInstanceGroups = [
|
||||
{ name: 'One', id: 1 },
|
||||
{ name: 'Two', id: 2 }
|
||||
];
|
||||
const getOrganizationInstanceGroups = jest.fn(() => (
|
||||
Promise.resolve({ data: { results: mockInstanceGroups } })
|
||||
));
|
||||
const wrapper = mount(
|
||||
<I18nProvider>
|
||||
<MemoryRouter initialEntries={['/organizations/1']} initialIndex={0}>
|
||||
<OrganizationDetail
|
||||
match={{
|
||||
path: '/organizations/:id',
|
||||
url: '/organizations/1',
|
||||
params: { id: '1' }
|
||||
}}
|
||||
organization={mockDetails}
|
||||
api={{
|
||||
getOrganizationInstanceGroups
|
||||
}}
|
||||
/>
|
||||
</MemoryRouter>
|
||||
</I18nProvider>
|
||||
).find('OrganizationDetail');
|
||||
|
||||
await getOrganizationInstanceGroups();
|
||||
expect(wrapper.state().instanceGroups).toEqual(mockInstanceGroups);
|
||||
});
|
||||
|
||||
test('should render Details', async () => {
|
||||
const wrapper = mount(
|
||||
<I18nProvider>
|
||||
<MemoryRouter initialEntries={['/organizations/1']} initialIndex={0}>
|
||||
<OrganizationDetail
|
||||
match={{
|
||||
path: '/organizations/:id',
|
||||
url: '/organizations/1',
|
||||
params: { id: '1' }
|
||||
}}
|
||||
organization={mockDetails}
|
||||
/>
|
||||
</MemoryRouter>
|
||||
</I18nProvider>
|
||||
);
|
||||
|
||||
const detailWrapper = wrapper.find('Detail');
|
||||
expect(detailWrapper.length).toBe(5);
|
||||
|
||||
const nameDetail = detailWrapper.findWhere(node => node.props().label === 'Name');
|
||||
const descriptionDetail = detailWrapper.findWhere(node => node.props().label === 'Description');
|
||||
const custom_virtualenvDetail = detailWrapper.findWhere(node => node.props().label === 'Ansible Environment');
|
||||
const createdDetail = detailWrapper.findWhere(node => node.props().label === 'Created');
|
||||
const modifiedDetail = detailWrapper.findWhere(node => node.props().label === 'Last Modified');
|
||||
|
||||
expect(nameDetail.find('h6').text()).toBe('Name');
|
||||
expect(nameDetail.find('p').text()).toBe('Foo');
|
||||
|
||||
expect(descriptionDetail.find('h6').text()).toBe('Description');
|
||||
expect(descriptionDetail.find('p').text()).toBe('Bar');
|
||||
|
||||
expect(custom_virtualenvDetail.find('h6').text()).toBe('Ansible Environment');
|
||||
expect(custom_virtualenvDetail.find('p').text()).toBe('Fizz');
|
||||
|
||||
expect(createdDetail.find('h6').text()).toBe('Created');
|
||||
expect(createdDetail.find('p').text()).toBe('Bat');
|
||||
|
||||
expect(modifiedDetail.find('h6').text()).toBe('Last Modified');
|
||||
expect(modifiedDetail.find('p').text()).toBe('Boo');
|
||||
});
|
||||
});
|
||||
|
||||
@ -70,6 +70,12 @@ class APIClient {
|
||||
return this.http.get(endpoint);
|
||||
}
|
||||
|
||||
getOrganizationInstanceGroups (id, params = {}) {
|
||||
const endpoint = `${API_ORGANIZATIONS}${id}/instance_groups/`;
|
||||
|
||||
return this.http.get(endpoint, { params });
|
||||
}
|
||||
|
||||
getOrganizationNotifications (id, params = {}) {
|
||||
const endpoint = `${API_ORGANIZATIONS}${id}/notification_templates/`;
|
||||
|
||||
|
||||
13
src/components/BasicChip/BasicChip.jsx
Normal file
13
src/components/BasicChip/BasicChip.jsx
Normal file
@ -0,0 +1,13 @@
|
||||
import React from 'react';
|
||||
import { Chip } from '@patternfly/react-core';
|
||||
import './basicChip.scss';
|
||||
|
||||
const BasicChip = ({ text }) => (
|
||||
<Chip
|
||||
className="awx-c-chip--basic"
|
||||
>
|
||||
{text}
|
||||
</Chip>
|
||||
);
|
||||
|
||||
export default BasicChip;
|
||||
10
src/components/BasicChip/basicChip.scss
Normal file
10
src/components/BasicChip/basicChip.scss
Normal file
@ -0,0 +1,10 @@
|
||||
.awx-c-chip--basic {
|
||||
padding: 6px 8px;
|
||||
height: 30px;
|
||||
margin-right: 10px;
|
||||
margin-bottom: 10px;
|
||||
|
||||
.pf-c-button {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,8 @@
|
||||
.pf-c-card__header {
|
||||
--pf-c-card__header--PaddingBottom: 0;
|
||||
--pf-c-card__header--PaddingX: 0;
|
||||
--pf-c-card__header--PaddingRight: 0;
|
||||
--pf-c-card__header--PaddingLeft: 0;
|
||||
--pf-c-card__header--PaddingTop: 0;
|
||||
}
|
||||
|
||||
|
||||
@ -127,16 +127,18 @@ class Organization extends Component {
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
path="/organizations/:id/details"
|
||||
render={() => (
|
||||
<OrganizationDetail
|
||||
organization={organization}
|
||||
match={match}
|
||||
location={location}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
{organization && (
|
||||
<Route
|
||||
path="/organizations/:id/details"
|
||||
render={() => (
|
||||
<OrganizationDetail
|
||||
api={api}
|
||||
match={match}
|
||||
organization={organization}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
<Route
|
||||
path="/organizations/:id/access"
|
||||
render={() => <CardBody><h1><Trans>Access</Trans></h1></CardBody>}
|
||||
|
||||
@ -1,15 +1,144 @@
|
||||
import React from 'react';
|
||||
import { withRouter, Link } from 'react-router-dom';
|
||||
import { Trans } from '@lingui/macro';
|
||||
import { CardBody } from '@patternfly/react-core';
|
||||
import React, { Component } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { I18n } from '@lingui/react';
|
||||
import { Trans, t } from '@lingui/macro';
|
||||
import {
|
||||
CardBody,
|
||||
Button,
|
||||
Text,
|
||||
TextContent,
|
||||
TextVariants,
|
||||
} from '@patternfly/react-core';
|
||||
import BasicChip from '../../../../components/BasicChip/BasicChip';
|
||||
|
||||
const OrganizationDetail = ({ match, organization }) => (
|
||||
<CardBody>
|
||||
<h1><Trans>{`${organization && organization.name} Detail View`}</Trans></h1>
|
||||
<Link to={`/organizations/${match.params.id}/edit`}>
|
||||
<Trans>Edit Details</Trans>
|
||||
</Link>
|
||||
</CardBody>
|
||||
);
|
||||
const detailWrapperStyle = {
|
||||
display: 'flex'
|
||||
};
|
||||
|
||||
export default withRouter(OrganizationDetail);
|
||||
const detailLabelStyle = {
|
||||
minWidth: '150px',
|
||||
marginRight: '20px',
|
||||
textAlign: 'right'
|
||||
};
|
||||
|
||||
const Detail = ({ label, value }) => {
|
||||
let detail = null;
|
||||
if (value) {
|
||||
detail = (
|
||||
<TextContent style={detailWrapperStyle}>
|
||||
<Text component={TextVariants.h6} style={detailLabelStyle}>{ label }</Text>
|
||||
<Text component={TextVariants.p}>{ value }</Text>
|
||||
</TextContent>
|
||||
);
|
||||
}
|
||||
return detail;
|
||||
};
|
||||
|
||||
class OrganizationDetail extends Component {
|
||||
constructor (props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
instanceGroups: [],
|
||||
error: false
|
||||
};
|
||||
|
||||
this.loadInstanceGroups = this.loadInstanceGroups.bind(this);
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
this.loadInstanceGroups();
|
||||
}
|
||||
|
||||
async loadInstanceGroups () {
|
||||
const {
|
||||
api,
|
||||
match
|
||||
} = this.props;
|
||||
try {
|
||||
const {
|
||||
data
|
||||
} = await api.getOrganizationInstanceGroups(match.params.id);
|
||||
this.setState({
|
||||
instanceGroups: [...data.results]
|
||||
});
|
||||
} catch (err) {
|
||||
this.setState({ error: true });
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
const {
|
||||
error,
|
||||
instanceGroups,
|
||||
} = this.state;
|
||||
|
||||
const {
|
||||
organization: {
|
||||
name,
|
||||
description,
|
||||
custom_virtualenv,
|
||||
created,
|
||||
modified
|
||||
},
|
||||
match
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<I18n>
|
||||
{({ i18n }) => (
|
||||
<CardBody className="pf-u-pt-lg">
|
||||
<div className="pf-l-grid pf-m-gutter pf-m-all-12-col-on-md pf-m-all-6-col-on-lg pf-m-all-4-col-on-xl">
|
||||
<Detail
|
||||
label={i18n._(t`Name`)}
|
||||
value={name}
|
||||
/>
|
||||
<Detail
|
||||
label={i18n._(t`Description`)}
|
||||
value={description}
|
||||
/>
|
||||
{(instanceGroups && instanceGroups.length > 0) && (
|
||||
<TextContent style={detailWrapperStyle}>
|
||||
<Text
|
||||
component={TextVariants.h6}
|
||||
style={detailLabelStyle}
|
||||
>
|
||||
<Trans>Instance Groups</Trans>
|
||||
</Text>
|
||||
<div>
|
||||
{instanceGroups.map(instanceGroup => (
|
||||
<BasicChip
|
||||
key={instanceGroup.id}
|
||||
text={instanceGroup.name}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</TextContent>
|
||||
)}
|
||||
<Detail
|
||||
label={i18n._(t`Ansible Environment`)}
|
||||
value={custom_virtualenv}
|
||||
/>
|
||||
<Detail
|
||||
label={i18n._(t`Created`)}
|
||||
value={created}
|
||||
/>
|
||||
<Detail
|
||||
label={i18n._(t`Last Modified`)}
|
||||
value={modified}
|
||||
/>
|
||||
</div>
|
||||
<div className="pf-u-display-flex pf-u-flex-direction-row-reverse pf-u-ml-auto pf-u-mt-md">
|
||||
<Link to={`/organizations/${match.params.id}/edit`}>
|
||||
<Button><Trans>Edit</Trans></Button>
|
||||
</Link>
|
||||
</div>
|
||||
{error ? 'error!' : ''}
|
||||
</CardBody>
|
||||
)}
|
||||
</I18n>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default OrganizationDetail;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user