Merge pull request #112 from mabashian/88-sort-filter-ig-modal

Adds sorting to IG lookup on org form
This commit is contained in:
Michael Abashian
2019-02-20 15:53:47 -05:00
committed by GitHub
13 changed files with 243 additions and 108 deletions

View File

@@ -162,6 +162,7 @@ describe('<DataListToolbar />', () => {
onSearch={onSearch} onSearch={onSearch}
onSort={onSort} onSort={onSort}
onSelectAll={onSelectAll} onSelectAll={onSelectAll}
showDelete
/> />
</I18nProvider> </I18nProvider>
); );

View File

@@ -3,7 +3,10 @@ import { mount } from 'enzyme';
import { I18nProvider } from '@lingui/react'; import { I18nProvider } from '@lingui/react';
import Lookup from '../../src/components/Lookup'; import Lookup from '../../src/components/Lookup';
let mockData = [{ name: 'foo', id: 1 }]; let mockData = [{ name: 'foo', id: 1, isChecked: false }];
const mockColumns = [
{ name: 'Name', key: 'name', isSortable: true }
];
describe('<Lookup />', () => { describe('<Lookup />', () => {
test('initially renders succesfully', () => { test('initially renders succesfully', () => {
mount( mount(
@@ -14,6 +17,8 @@ describe('<Lookup />', () => {
value={mockData} value={mockData}
onLookupSave={() => { }} onLookupSave={() => { }}
getItems={() => { }} getItems={() => { }}
columns={mockColumns}
sortedColumnKey="name"
/> />
</I18nProvider> </I18nProvider>
); );
@@ -27,6 +32,8 @@ describe('<Lookup />', () => {
value={mockData} value={mockData}
onLookupSave={() => { }} onLookupSave={() => { }}
getItems={() => ({ data: { results: [{ name: 'test instance', id: 1 }] } })} getItems={() => ({ data: { results: [{ name: 'test instance', id: 1 }] } })}
columns={mockColumns}
sortedColumnKey="name"
/> />
</I18nProvider> </I18nProvider>
).find('Lookup'); ).find('Lookup');
@@ -47,6 +54,8 @@ describe('<Lookup />', () => {
value={mockSelected} value={mockSelected}
onLookupSave={() => { }} onLookupSave={() => { }}
getItems={() => { }} getItems={() => { }}
columns={mockColumns}
sortedColumnKey="name"
/> />
</I18nProvider> </I18nProvider>
).find('Lookup'); ).find('Lookup');
@@ -72,6 +81,8 @@ describe('<Lookup />', () => {
value={mockSelected} value={mockSelected}
onLookupSave={() => { }} onLookupSave={() => { }}
getItems={() => ({ data: { results: [{ name: 'test instance', id: 1 }] } })} getItems={() => ({ data: { results: [{ name: 'test instance', id: 1 }] } })}
columns={mockColumns}
sortedColumnKey="name"
/> />
</I18nProvider> </I18nProvider>
); );
@@ -94,6 +105,8 @@ describe('<Lookup />', () => {
value={mockData} value={mockData}
onLookupSave={() => { }} onLookupSave={() => { }}
getItems={() => { }} getItems={() => { }}
columns={mockColumns}
sortedColumnKey="name"
/> />
</I18nProvider> </I18nProvider>
); );
@@ -112,6 +125,8 @@ describe('<Lookup />', () => {
value={mockData} value={mockData}
selected={[]} selected={[]}
getItems={() => { }} getItems={() => { }}
columns={mockColumns}
sortedColumnKey="name"
/> />
</I18nProvider> </I18nProvider>
); );
@@ -129,6 +144,8 @@ describe('<Lookup />', () => {
value={mockData} value={mockData}
selected={[]} selected={[]}
getItems={() => { }} getItems={() => { }}
columns={mockColumns}
sortedColumnKey="name"
/> />
</I18nProvider> </I18nProvider>
).find('Lookup'); ).find('Lookup');
@@ -174,4 +191,42 @@ describe('<Lookup />', () => {
name: 'foo' name: 'foo'
}], 'fooBar'); }], 'fooBar');
}); });
test('onSort sets state and calls getData ', () => {
const spy = jest.spyOn(Lookup.prototype, 'getData');
const wrapper = mount(
<I18nProvider>
<Lookup
lookup_header="Foo Bar"
onLookupSave={() => { }}
data={mockData}
selected={[]}
columns={mockColumns}
sortedColumnKey="name"
/>
</I18nProvider>
).find('Lookup');
wrapper.instance().onSort('id', 'descending');
expect(wrapper.state('sortedColumnKey')).toEqual('id');
expect(wrapper.state('sortOrder')).toEqual('descending');
expect(spy).toHaveBeenCalled();
});
test('onSetPage sets state and calls getData ', () => {
const spy = jest.spyOn(Lookup.prototype, 'getData');
const wrapper = mount(
<I18nProvider>
<Lookup
lookup_header="Foo Bar"
onLookupSave={() => { }}
data={mockData}
selected={[]}
columns={mockColumns}
sortedColumnKey="name"
/>
</I18nProvider>
).find('Lookup');
wrapper.instance().onSetPage(2, 10);
expect(wrapper.state('page')).toEqual(2);
expect(wrapper.state('page_size')).toEqual(10);
expect(spy).toHaveBeenCalled();
});
}); });

View File

@@ -0,0 +1,11 @@
import React from 'react';
import { mount } from 'enzyme';
import VerticalSeparator from '../../src/components/VerticalSeparator';
describe('VerticalSeparator', () => {
test('renders the expected content', () => {
const wrapper = mount(<VerticalSeparator />);
expect(wrapper).toHaveLength(1);
});
});

View File

@@ -1 +1 @@
/* eslint-disable */module.exports={languageData:{"plurals":function(n,ord){var s=String(n).split("."),v0=!s[1],t0=Number(s[0])==n,n10=t0&&s[0].slice(-1),n100=t0&&s[0].slice(-2);if(ord)return n10==1&&n100!=11?"one":n10==2&&n100!=12?"two":n10==3&&n100!=13?"few":"other";return n==1&&v0?"one":"other"}},messages:{"> add":"> add","> edit":"> edit","About":"About","AboutModal Logo":"AboutModal Logo","Access":"Access","Add":"Add","Administration":"Administration","Admins":"Admins","Ansible Version":"Ansible Version","Applications":"Applications","Authentication":"Authentication","Authentication Settings":"Authentication Settings","Brand Image":"Brand Image","Collapse":"Collapse","Copyright 2018 Red Hat, Inc.":"Copyright 2018 Red Hat, Inc.","Created":"Created","Credential Types":"Credential Types","Credentials":"Credentials","Dashboard":"Dashboard","Delete":"Delete","Edit":"Edit","Expand":"Expand","First":"First","Help":"Help","Instance Groups":"Instance Groups","Integrations":"Integrations","Invalid username or password. Please try again.":"Invalid username or password. Please try again.","Inventories":"Inventories","Inventory Scripts":"Inventory Scripts","Jobs":"Jobs","Jobs Settings":"Jobs Settings","Last":"Last","License":"License","Logout":"Logout","Management Jobs":"Management Jobs","Modified":"Modified","My View":"Foo","Name":"Name","Next":"Next","Notification Templates":"Notification Templates","Notifications":"Notifications","Organization Add":"Organization Add","Organization detail tabs":"Organization detail tabs","Organizations":"Organizations","Organizations List":"Organizations List","Page <0/> of {pageCount}":function(a){return["Page <0/> of ",a("pageCount")]},"Page Number":"Page Number","Password":"Password","Per Page":"Per Page","Portal Mode":"Portal Mode","Previous":"Previous","Primary Navigation":"Primary Navigation","Projects":"Projects","Resources":"Resources","Schedules":"Schedules","Search":"Search","Search text input":"Search text input","Select all":"Select all","Settings":"Settings","Sort":"Sort","System":"System","System Settings":"System Settings","Teams":"Teams","Templates":"Templates","Tower Brand Image":"Tower Brand Image","User Details":"User Details","User Interface":"User Interface","User Interface Settings":"User Interface Settings","Username":"Username","Users":"Users","Views":"Views","Welcome to Ansible Tower! Please Sign In.":"Welcome to Ansible Tower! Please Sign In.","add {currentTab}":function(a){return["add ",a("currentTab")]},"adding {currentTab}":function(a){return["adding ",a("currentTab")]},"confirm removal of {currentTab}/cancel and go back to {currentTab} view.":function(a){return["confirm removal of ",a("currentTab"),"/cancel and go back to ",a("currentTab")," view."]},"delete {currentTab}":function(a){return["delete ",a("currentTab")]},"deleting {currentTab} association with orgs":function(a){return["deleting ",a("currentTab")," association with orgs"]},"edit view":"edit view","save/cancel and go back to view":"save/cancel and go back to view","save/cancel and go back to {currentTab} view":function(a){return["save/cancel and go back to ",a("currentTab")," view"]},"select organization {itemId}":function(a){return["select organization ",a("itemId")]},"{0}":function(a){return[a("0")]},"{currentTab} detail view":function(a){return[a("currentTab")," detail view"]},"{itemMin} - {itemMax} of {count}":function(a){return[a("itemMin")," - ",a("itemMax")," of ",a("count")]}}}; /* eslint-disable */module.exports={languageData:{"plurals":function(n,ord){var s=String(n).split("."),v0=!s[1],t0=Number(s[0])==n,n10=t0&&s[0].slice(-1),n100=t0&&s[0].slice(-2);if(ord)return n10==1&&n100!=11?"one":n10==2&&n100!=12?"two":n10==3&&n100!=13?"few":"other";return n==1&&v0?"one":"other"}},messages:{"> add":"> add","> edit":"> edit","About":"About","AboutModal Logo":"AboutModal Logo","Access":"Access","Add":"Add","Administration":"Administration","Admins":"Admins","Ansible Version":"Ansible Version","Applications":"Applications","Authentication":"Authentication","Authentication Settings":"Authentication Settings","Brand Image":"Brand Image","Collapse":"Collapse","Copyright 2018 Red Hat, Inc.":"Copyright 2018 Red Hat, Inc.","Created":"Created","Credential Types":"Credential Types","Credentials":"Credentials","Dashboard":"Dashboard","Delete":"Delete","Edit":"Edit","Expand":"Expand","First":"First","Help":"Help","Instance Groups":"Instance Groups","Integrations":"Integrations","Invalid username or password. Please try again.":"Invalid username or password. Please try again.","Inventories":"Inventories","Inventory Scripts":"Inventory Scripts","Jobs":"Jobs","Jobs Settings":"Jobs Settings","Last":"Last","License":"License","Logout":"Logout","Management Jobs":"Management Jobs","Modified":"Modified","My View":"My View","Name":"Name","Next":"Next","Notification Templates":"Notification Templates","Notifications":"Notifications","Organization Add":"Organization Add","Organization detail tabs":"Organization detail tabs","Organizations":"Organizations","Organizations List":"Organizations List","Page <0/> of {pageCount}":function(a){return["Page <0/> of ",a("pageCount")]},"Page Number":"Page Number","Password":"Password","Per Page":"Per Page","Portal Mode":"Portal Mode","Previous":"Previous","Primary Navigation":"Primary Navigation","Projects":"Projects","Resources":"Resources","Schedules":"Schedules","Search":"Search","Search text input":"Search text input","Select all":"Select all","Settings":"Settings","Sort":"Sort","System":"System","System Settings":"System Settings","Teams":"Teams","Templates":"Templates","Tower Brand Image":"Tower Brand Image","User Details":"User Details","User Interface":"User Interface","User Interface Settings":"User Interface Settings","Username":"Username","Users":"Users","Views":"Views","Welcome to Ansible Tower! Please Sign In.":"Welcome to Ansible Tower! Please Sign In.","add {currentTab}":function(a){return["add ",a("currentTab")]},"adding {currentTab}":function(a){return["adding ",a("currentTab")]},"confirm removal of {currentTab}/cancel and go back to {currentTab} view.":function(a){return["confirm removal of ",a("currentTab"),"/cancel and go back to ",a("currentTab")," view."]},"delete {currentTab}":function(a){return["delete ",a("currentTab")]},"deleting {currentTab} association with orgs":function(a){return["deleting ",a("currentTab")," association with orgs"]},"edit view":"edit view","save/cancel and go back to view":"save/cancel and go back to view","save/cancel and go back to {currentTab} view":function(a){return["save/cancel and go back to ",a("currentTab")," view"]},"select organization {itemId}":function(a){return["select organization ",a("itemId")]},"{0}":function(a){return[a("0")]},"{currentTab} detail view":function(a){return[a("currentTab")," detail view"]},"{itemMin} - {itemMax} of {count}":function(a){return[a("itemMin")," - ",a("itemMax")," of ",a("count")]}}};

View File

@@ -158,10 +158,13 @@
.awx-c-modal.pf-c-modal-box { .awx-c-modal.pf-c-modal-box {
margin: 0; margin: 0;
padding: 20px; width: 600px;
width: 550px;
.pf-c-button:not(:last-child) { .pf-c-modal-box__body {
overflow: visible;
}
.pf-c-modal-box__footer > .pf-c-button:not(:last-child) {
margin-right: 20px; margin-right: 20px;
} }
} }
@@ -233,7 +236,6 @@
} }
.awx-c-list { .awx-c-list {
border-top: 1px solid #d7d7d7;
border-bottom: 1px solid #d7d7d7; border-bottom: 1px solid #d7d7d7;
} }

View File

@@ -31,6 +31,11 @@ import {
} from 'react-router-dom'; } from 'react-router-dom';
import Tooltip from '../Tooltip'; import Tooltip from '../Tooltip';
import VerticalSeparator from '../VerticalSeparator';
const flexGrowStyling = {
flexGrow: '1'
};
class DataListToolbar extends React.Component { class DataListToolbar extends React.Component {
constructor (props) { constructor (props) {
@@ -108,10 +113,10 @@ class DataListToolbar extends React.Component {
addUrl, addUrl,
showExpandCollapse, showExpandCollapse,
showDelete, showDelete,
showSelectAll showSelectAll,
isLookup
} = this.props; } = this.props;
const { const {
// isActionDropdownOpen,
isSearchDropdownOpen, isSearchDropdownOpen,
isSortDropdownOpen, isSortDropdownOpen,
searchKey, searchKey,
@@ -150,8 +155,8 @@ class DataListToolbar extends React.Component {
{({ i18n }) => ( {({ i18n }) => (
<div className="awx-toolbar"> <div className="awx-toolbar">
<Level> <Level>
<LevelItem> <LevelItem style={{ display: 'flex', flexBasis: '700px' }}>
<Toolbar style={{ marginLeft: '20px' }}> <Toolbar style={{ marginLeft: isLookup ? '0px' : '20px', flexGrow: '1' }}>
{ showSelectAll && ( { showSelectAll && (
<ToolbarGroup> <ToolbarGroup>
<ToolbarItem> <ToolbarItem>
@@ -162,10 +167,11 @@ class DataListToolbar extends React.Component {
id="select-all" id="select-all"
/> />
</ToolbarItem> </ToolbarItem>
<VerticalSeparator />
</ToolbarGroup> </ToolbarGroup>
)} )}
<ToolbarGroup> <ToolbarGroup style={flexGrowStyling}>
<ToolbarItem> <ToolbarItem style={flexGrowStyling}>
<div className="pf-c-input-group"> <div className="pf-c-input-group">
<Dropdown <Dropdown
className="searchKeyDropdown" className="searchKeyDropdown"
@@ -187,6 +193,7 @@ class DataListToolbar extends React.Component {
aria-label={i18n._(t`Search text input`)} aria-label={i18n._(t`Search text input`)}
value={searchValue} value={searchValue}
onChange={this.handleSearchInputChange} onChange={this.handleSearchInputChange}
style={{ height: '30px' }}
/> />
<Button <Button
variant="tertiary" variant="tertiary"
@@ -197,26 +204,29 @@ class DataListToolbar extends React.Component {
</Button> </Button>
</div> </div>
</ToolbarItem> </ToolbarItem>
<VerticalSeparator />
</ToolbarGroup> </ToolbarGroup>
<ToolbarGroup <ToolbarGroup
className="sortDropdownGroup" className="sortDropdownGroup"
> >
<ToolbarItem> { sortDropdownItems.length > 1 && (
<Dropdown <ToolbarItem>
onToggle={this.onSortDropdownToggle} <Dropdown
onSelect={this.onSortDropdownSelect} onToggle={this.onSortDropdownToggle}
direction={up} onSelect={this.onSortDropdownSelect}
isOpen={isSortDropdownOpen} direction={up}
toggle={( isOpen={isSortDropdownOpen}
<DropdownToggle toggle={(
onToggle={this.onSortDropdownToggle} <DropdownToggle
> onToggle={this.onSortDropdownToggle}
{sortedColumnName} >
</DropdownToggle> {sortedColumnName}
)} </DropdownToggle>
dropdownItems={sortDropdownItems} )}
/> dropdownItems={sortDropdownItems}
</ToolbarItem> />
</ToolbarItem>
)}
<ToolbarItem> <ToolbarItem>
<Button <Button
onClick={this.onSort} onClick={this.onSort}
@@ -226,6 +236,9 @@ class DataListToolbar extends React.Component {
<SortIcon /> <SortIcon />
</Button> </Button>
</ToolbarItem> </ToolbarItem>
{ (showExpandCollapse || showDelete || addUrl) && (
<VerticalSeparator />
)}
</ToolbarGroup> </ToolbarGroup>
{showExpandCollapse && ( {showExpandCollapse && (
<ToolbarGroup> <ToolbarGroup>
@@ -245,6 +258,9 @@ class DataListToolbar extends React.Component {
<EqualsIcon /> <EqualsIcon />
</Button> </Button>
</ToolbarItem> </ToolbarItem>
{ (showDelete || addUrl) && (
<VerticalSeparator />
)}
</ToolbarGroup> </ToolbarGroup>
)} )}
</Toolbar> </Toolbar>

View File

@@ -28,16 +28,6 @@
--pf-l-toolbar__group--MarginLeft: 0px; --pf-l-toolbar__group--MarginLeft: 0px;
} }
.awx-toolbar .pf-l-toolbar__group:after {
content: "";
background-color: #d7d7d7;
width: 1px;
height: 30px;
display: block;
margin-left: 20px;
margin-right: 20px;
}
.awx-toolbar button.pf-c-button { .awx-toolbar button.pf-c-button {
height: 30px; height: 30px;
padding: 0px; padding: 0px;
@@ -47,12 +37,6 @@
min-height: 0px; min-height: 0px;
height: 30px; height: 30px;
input {
height: 30px;
padding: 0 10px;
width: 300px;
}
.pf-m-tertiary { .pf-m-tertiary {
width: 34px; width: 34px;
padding: 0px; padding: 0px;

View File

@@ -13,6 +13,7 @@ import { I18n } from '@lingui/react';
import { Trans, t } from '@lingui/macro'; import { Trans, t } from '@lingui/macro';
import CheckboxListItem from '../ListItem'; import CheckboxListItem from '../ListItem';
import DataListToolbar from '../DataListToolbar';
import SelectedList from '../SelectedList'; import SelectedList from '../SelectedList';
import Pagination from '../Pagination'; import Pagination from '../Pagination';
@@ -34,7 +35,9 @@ class Lookup extends React.Component {
count: 0, count: 0,
page: 1, page: 1,
page_size: 5, page_size: 5,
error: null error: null,
sortOrder: 'ascending',
sortedColumnKey: props.sortedColumnKey
}; };
this.onSetPage = this.onSetPage.bind(this); this.onSetPage = this.onSetPage.bind(this);
this.handleModalToggle = this.handleModalToggle.bind(this); this.handleModalToggle = this.handleModalToggle.bind(this);
@@ -42,6 +45,8 @@ class Lookup extends React.Component {
this.toggleSelected = this.toggleSelected.bind(this); this.toggleSelected = this.toggleSelected.bind(this);
this.saveModal = this.saveModal.bind(this); this.saveModal = this.saveModal.bind(this);
this.getData = this.getData.bind(this); this.getData = this.getData.bind(this);
this.onSearch = this.onSearch.bind(this);
this.onSort = this.onSort.bind(this);
} }
componentDidMount () { componentDidMount () {
@@ -49,18 +54,35 @@ class Lookup extends React.Component {
this.getData({ page_size, page }); this.getData({ page_size, page });
} }
async getData (queryParams) { onSearch () {
const { sortedColumnKey, sortOrder } = this.state;
this.onSort(sortedColumnKey, sortOrder);
}
onSort (sortedColumnKey, sortOrder) {
this.setState({ page: 1, sortedColumnKey, sortOrder }, this.getData);
}
async getData () {
const { getItems } = this.props; const { getItems } = this.props;
const { page } = queryParams; const { page, page_size, sortedColumnKey, sortOrder } = this.state;
this.setState({ error: false }); this.setState({ error: false });
const queryParams = {
page,
page_size
};
if (sortedColumnKey) {
queryParams.order_by = sortOrder === 'descending' ? `-${sortedColumnKey}` : sortedColumnKey;
}
try { try {
const { data } = await getItems(queryParams); const { data } = await getItems(queryParams);
const { results, count } = data; const { results, count } = data;
const stateToUpdate = { const stateToUpdate = {
page,
results, results,
count count
}; };
@@ -74,7 +96,7 @@ class Lookup extends React.Component {
onSetPage = async (pageNumber, pageSize) => { onSetPage = async (pageNumber, pageSize) => {
const page = parseInt(pageNumber, 10); const page = parseInt(pageNumber, 10);
const page_size = parseInt(pageSize, 10); const page_size = parseInt(pageSize, 10);
this.getData({ page_size, page }); this.setState({ page, page_size }, this.getData);
}; };
toggleSelected (row) { toggleSelected (row) {
@@ -124,8 +146,18 @@ class Lookup extends React.Component {
} }
render () { render () {
const { isModalOpen, lookupSelectedItems, error, results, count, page, page_size } = this.state; const {
const { lookupHeader, value } = this.props; isModalOpen,
lookupSelectedItems,
error,
results,
count,
page,
page_size,
sortedColumnKey,
sortOrder
} = this.state;
const { lookupHeader = 'items', value, columns } = this.props;
return ( return (
<I18n> <I18n>
@@ -157,6 +189,14 @@ class Lookup extends React.Component {
</EmptyState> </EmptyState>
) : ( ) : (
<Fragment> <Fragment>
<DataListToolbar
sortedColumnKey={sortedColumnKey}
sortOrder={sortOrder}
columns={columns}
onSearch={this.onSearch}
onSort={this.onSort}
isLookup
/>
<ul className="pf-c-data-list awx-c-list"> <ul className="pf-c-data-list awx-c-list">
{results.map(i => ( {results.map(i => (
<CheckboxListItem <CheckboxListItem

View File

@@ -4,6 +4,8 @@ import {
Chip Chip
} from '@patternfly/react-core'; } from '@patternfly/react-core';
import VerticalSeparator from '../VerticalSeparator';
const selectedRowStyling = { const selectedRowStyling = {
paddingTop: '15px', paddingTop: '15px',
paddingBottom: '5px', paddingBottom: '5px',
@@ -41,6 +43,7 @@ class SelectedList extends Component {
<div className="pf-l-split__item" style={selectedLabelStyling}> <div className="pf-l-split__item" style={selectedLabelStyling}>
{label} {label}
</div> </div>
<VerticalSeparator />
<div className="pf-l-split__item"> <div className="pf-l-split__item">
<div className="pf-c-chip-group"> <div className="pf-c-chip-group">
{selected {selected

View File

@@ -15,15 +15,6 @@
white-space: nowrap; white-space: nowrap;
height: 30px; height: 30px;
} }
.pf-l-split__item:not(:last-child):after {
content: "";
background-color: var(--awx-selectedList--BorderColor);
width: 1px;
height: 30px;
display: block;
margin-left: 20px;
margin-right: 20px;
}
.pf-c-chip { .pf-c-chip {
margin-right: 10px; margin-right: 10px;
margin-bottom: 10px; margin-bottom: 10px;

View File

@@ -0,0 +1,16 @@
import React from 'react';
const VerticalSeparator = () => (
<span style={{
content: '',
backgroundColor: '#d7d7d7',
width: '1px',
height: '30px',
display: 'block',
marginLeft: '20px',
marginRight: '20px'
}}
/>
);
export default VerticalSeparator;

View File

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

View File

@@ -1,6 +1,8 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { withRouter } from 'react-router-dom'; import { withRouter } from 'react-router-dom';
import { I18n, i18nMark } from '@lingui/react';
import { t } from '@lingui/macro';
import { import {
PageSection, PageSection,
Form, Form,
@@ -98,60 +100,71 @@ class OrganizationAdd extends React.Component {
error error
} = this.state; } = this.state;
const enabled = name.length > 0; // TODO: add better form validation const enabled = name.length > 0; // TODO: add better form validation
const instanceGroupsLookupColumns = [
{ name: i18nMark('Name'), key: 'name', isSortable: true },
{ name: i18nMark('Modified'), key: 'modified', isSortable: false, isNumeric: true },
{ name: i18nMark('Created'), key: 'created', isSortable: false, isNumeric: true }
];
return ( return (
<PageSection> <PageSection>
<Card> <Card>
<CardBody> <CardBody>
<Form autoComplete="off"> <Form autoComplete="off">
<Gallery gutter="md"> <I18n>
<FormGroup {({ i18n }) => (
label="Name" <Gallery gutter="md">
isRequired <FormGroup
fieldId="add-org-form-name" label={i18n._(t`Name`)}
> isRequired
<TextInput fieldId="add-org-form-name"
isRequired >
id="add-org-form-name" <TextInput
name="name" isRequired
value={name} id="add-org-form-name"
onChange={this.onFieldChange} name="name"
/> value={name}
</FormGroup> onChange={this.onFieldChange}
<FormGroup label="Description" fieldId="add-org-form-description"> />
<TextInput </FormGroup>
id="add-org-form-description" <FormGroup label={i18n._(t`Description`)} fieldId="add-org-form-description">
name="description" <TextInput
value={description} id="add-org-form-description"
onChange={this.onFieldChange} name="description"
/> value={description}
</FormGroup> onChange={this.onFieldChange}
<FormGroup label="Instance Groups" fieldId="add-org-form-instance-groups"> />
<Lookup </FormGroup>
lookupHeader="Instance Groups" <FormGroup label={i18n._(t`Instance Groups`)} fieldId="add-org-form-instance-groups">
name="instanceGroups" <Lookup
value={instanceGroups} lookupHeader={i18n._(t`Instance Groups`)}
onLookupSave={this.onLookupSave} name="instanceGroups"
getItems={this.getInstanceGroups} value={instanceGroups}
/> onLookupSave={this.onLookupSave}
</FormGroup> getItems={this.getInstanceGroups}
<ConfigContext.Consumer> columns={instanceGroupsLookupColumns}
{({ custom_virtualenvs }) => ( sortedColumnKey="name"
custom_virtualenvs && custom_virtualenvs.length > 1 && ( />
<FormGroup label="Ansible Environment" fieldId="add-org-custom-virtualenv"> </FormGroup>
<AnsibleSelect <ConfigContext.Consumer>
label="Ansible Environment" {({ custom_virtualenvs }) => (
name="custom_virtualenv" custom_virtualenvs && custom_virtualenvs.length > 1 && (
value={custom_virtualenv} <FormGroup label={i18n._(t`Ansible Environment`)} fieldId="add-org-custom-virtualenv">
onChange={this.onFieldChange} <AnsibleSelect
data={custom_virtualenvs} label={i18n._(t`Ansible Environment`)}
defaultSelected={defaultEnv} name="custom_virtualenv"
/> value={custom_virtualenv}
</FormGroup> onChange={this.onFieldChange}
) data={custom_virtualenvs}
)} defaultSelected={defaultEnv}
</ConfigContext.Consumer> />
</Gallery> </FormGroup>
)
)}
</ConfigContext.Consumer>
</Gallery>
)}
</I18n>
<FormActionGroup <FormActionGroup
onSubmit={this.onSubmit} onSubmit={this.onSubmit}
submitDisabled={!enabled} submitDisabled={!enabled}