Merge pull request #95 from jlmitch5/fixLinting

Fix linting
This commit is contained in:
John Mitchell
2019-01-25 16:16:55 -05:00
committed by GitHub
27 changed files with 255 additions and 237 deletions

9
.eslintignore Normal file
View File

@@ -0,0 +1,9 @@
jest.*.js
webpack.*.js
etc
coverage
build
node_modules
dist
images

View File

@@ -7,8 +7,6 @@ import { asyncFlush } from '../jest.setup';
import App from '../src/App'; import App from '../src/App';
const DEFAULT_ACTIVE_GROUP = 'views_group';
describe('<App />', () => { describe('<App />', () => {
test('expected content is rendered', () => { test('expected content is rendered', () => {
const appWrapper = mount( const appWrapper = mount(
@@ -60,13 +58,13 @@ describe('<App />', () => {
const ansible_version = '111'; const ansible_version = '111';
const version = '222'; const version = '222';
const getConfig = jest.fn(() => Promise.resolve({ data: { ansible_version, version} })); const getConfig = jest.fn(() => Promise.resolve({ data: { ansible_version, version } }));
const api = { getConfig }; const api = { getConfig };
const wrapper = mount( const wrapper = mount(
<MemoryRouter> <MemoryRouter>
<I18nProvider> <I18nProvider>
<App api={api}/> <App api={api} />
</I18nProvider> </I18nProvider>
</MemoryRouter> </MemoryRouter>
); );

View File

@@ -105,7 +105,7 @@ describe('APIClient (api.js)', () => {
const mockHttp = ({ post: jest.fn(createPromise) }); const mockHttp = ({ post: jest.fn(createPromise) });
const api = new APIClient(mockHttp); const api = new APIClient(mockHttp);
const data = { name: 'test '}; const data = { name: 'test ' };
await api.createOrganization(data); await api.createOrganization(data);
expect(mockHttp.post).toHaveBeenCalledTimes(1); expect(mockHttp.post).toHaveBeenCalledTimes(1);

View File

@@ -2,8 +2,8 @@ import React from 'react';
import { mount } from 'enzyme'; import { mount } from 'enzyme';
import AnsibleSelect from '../../src/components/AnsibleSelect'; import AnsibleSelect from '../../src/components/AnsibleSelect';
const label = "test select" const label = 'test select';
const mockData = ["/venv/baz/", "/venv/ansible/"]; const mockData = ['/venv/baz/', '/venv/ansible/'];
describe('<AnsibleSelect />', () => { describe('<AnsibleSelect />', () => {
test('initially renders succesfully', async () => { test('initially renders succesfully', async () => {
mount( mount(

View File

@@ -1,14 +1,13 @@
import React from 'react'; import React from 'react';
import { mount } from 'enzyme'; import { mount } from 'enzyme';
import { I18nProvider } from '@lingui/react';
import Background from '../../src/components/Background'; import Background from '../../src/components/Background';
describe('Background', () => { describe('Background', () => {
test('renders the expected content', () => { test('renders the expected content', () => {
const wrapper = mount(<Background><div id="test"/></Background>); const wrapper = mount(<Background><div id="test" /></Background>);
expect(wrapper).toHaveLength(1); expect(wrapper).toHaveLength(1);
expect(wrapper.find('BackgroundImage')).toHaveLength(1); expect(wrapper.find('BackgroundImage')).toHaveLength(1);
expect(wrapper.find('#test')).toHaveLength(1); expect(wrapper.find('#test')).toHaveLength(1);
}); });
}); });

View File

@@ -29,7 +29,7 @@ describe('<DataListToolbar />', () => {
<I18nProvider> <I18nProvider>
<DataListToolbar <DataListToolbar
isAllSelected={false} isAllSelected={false}
showExpandCollapse={true} showExpandCollapse
sortedColumnKey="name" sortedColumnKey="name"
sortOrder="ascending" sortOrder="ascending"
columns={columns} columns={columns}
@@ -94,10 +94,10 @@ describe('<DataListToolbar />', () => {
sortDropdownToggle.at(1).simulate('click'); sortDropdownToggle.at(1).simulate('click');
sortDropdownToggle.at(0).simulate('click'); sortDropdownToggle.at(0).simulate('click');
toolbar.update(); toolbar.update();
const sortDropdownItems = toolbar.find(sortDropdownItemsSelector); const sortDropdownItems = toolbar.find(sortDropdownItemsSelector);
expect(sortDropdownItems.length).toBe(2); expect(sortDropdownItems.length).toBe(2);
const mockedSortEvent = { target: { innerText: 'Bar' } }; const mockedSortEvent = { target: { innerText: 'Bar' } };
sortDropdownItems.at(0).simulate('click', mockedSortEvent); sortDropdownItems.at(0).simulate('click', mockedSortEvent);
toolbar = mount( toolbar = mount(
@@ -114,28 +114,28 @@ describe('<DataListToolbar />', () => {
</I18nProvider> </I18nProvider>
); );
toolbar.update(); toolbar.update();
const sortDropdownToggleDescending = toolbar.find(sortDropdownToggleSelector); const sortDropdownToggleDescending = toolbar.find(sortDropdownToggleSelector);
expect(sortDropdownToggleDescending.length).toBe(2); expect(sortDropdownToggleDescending.length).toBe(2);
sortDropdownToggleDescending.at(1).simulate('click'); sortDropdownToggleDescending.at(1).simulate('click');
sortDropdownToggleDescending.at(0).simulate('click'); sortDropdownToggleDescending.at(0).simulate('click');
toolbar.update(); toolbar.update();
const sortDropdownItemsDescending = toolbar.find(sortDropdownItemsSelector); const sortDropdownItemsDescending = toolbar.find(sortDropdownItemsSelector);
expect(sortDropdownItemsDescending.length).toBe(2); expect(sortDropdownItemsDescending.length).toBe(2);
const mockedSortEventDescending = { target: { innerText: 'Bar' } }; const mockedSortEventDescending = { target: { innerText: 'Bar' } };
sortDropdownItems.at(0).simulate('click', mockedSortEventDescending); sortDropdownItems.at(0).simulate('click', mockedSortEventDescending);
toolbar.update(); toolbar.update();
const searchDropdownToggle = toolbar.find(searchDropdownToggleSelector); const searchDropdownToggle = toolbar.find(searchDropdownToggleSelector);
expect(searchDropdownToggle.length).toBe(1); expect(searchDropdownToggle.length).toBe(1);
searchDropdownToggle.at(0).simulate('click'); searchDropdownToggle.at(0).simulate('click');
toolbar.update(); toolbar.update();
const searchDropdownItems = toolbar.find(searchDropdownItemsSelector); const searchDropdownItems = toolbar.find(searchDropdownItemsSelector);
expect(searchDropdownItems.length).toBe(3); expect(searchDropdownItems.length).toBe(3);
const mockedSearchEvent = { target: { innerText: 'Bar' } }; const mockedSearchEvent = { target: { innerText: 'Bar' } };
searchDropdownItems.at(0).simulate('click', mockedSearchEvent); searchDropdownItems.at(0).simulate('click', mockedSearchEvent);
}); });

View File

@@ -1,9 +1,9 @@
import React from 'react'; import React from 'react';
import { mount } from 'enzyme'; import { mount } from 'enzyme';
import Lookup from '../../src/components/Lookup';
import { I18nProvider } from '@lingui/react'; import { I18nProvider } from '@lingui/react';
import Lookup from '../../src/components/Lookup';
const mockData = [{ name: 'foo', id: 0, isChecked: false }]; let mockData = [{ name: 'foo', id: 0, isChecked: false }];
describe('<Lookup />', () => { describe('<Lookup />', () => {
test('initially renders succesfully', () => { test('initially renders succesfully', () => {
mount( mount(
@@ -26,7 +26,9 @@ describe('<Lookup />', () => {
</I18nProvider> </I18nProvider>
); );
expect(spy).not.toHaveBeenCalled(); expect(spy).not.toHaveBeenCalled();
wrapper.find('#search').simulate('click'); debugger;
const searchItem = wrapper.find('.pf-c-input-group__text#search');
searchItem.first().simulate('click');
expect(spy).toHaveBeenCalled(); expect(spy).toHaveBeenCalled();
}); });
test('calls "onChecked" when a user changes a checkbox', () => { test('calls "onChecked" when a user changes a checkbox', () => {
@@ -40,13 +42,15 @@ describe('<Lookup />', () => {
/> />
</I18nProvider> </I18nProvider>
); );
wrapper.find('#search').simulate('click'); debugger;
const searchItem = wrapper.find('.pf-c-input-group__text#search');
searchItem.first().simulate('click');
wrapper.find('input[type="checkbox"]').simulate('change'); wrapper.find('input[type="checkbox"]').simulate('change');
expect(spy).toHaveBeenCalled(); expect(spy).toHaveBeenCalled();
}); });
test('calls "onRemove" when remove icon is clicked', () => { test('calls "onRemove" when remove icon is clicked', () => {
const spy = jest.spyOn(Lookup.prototype, 'onRemove'); const spy = jest.spyOn(Lookup.prototype, 'onRemove');
const mockData = [{ name: 'foo', id: 0, isChecked: false }, { name: 'bar', id: 1, isChecked: true }]; mockData = [{ name: 'foo', id: 0, isChecked: false }, { name: 'bar', id: 1, isChecked: true }];
const wrapper = mount( const wrapper = mount(
<I18nProvider> <I18nProvider>
<Lookup <Lookup
@@ -56,12 +60,13 @@ describe('<Lookup />', () => {
/> />
</I18nProvider> </I18nProvider>
); );
wrapper.find('.awx-c-icon--remove').simulate('click'); const removeIcon = wrapper.find('.awx-c-icon--remove').first();
removeIcon.simulate('click');
expect(spy).toHaveBeenCalled(); expect(spy).toHaveBeenCalled();
}); });
test('"wrapTags" method properly handles data', () => { test('"wrapTags" method properly handles data', () => {
const spy = jest.spyOn(Lookup.prototype, 'wrapTags'); const spy = jest.spyOn(Lookup.prototype, 'wrapTags');
const mockData = [{ name: 'foo', id: 0, isChecked: false }, { name: 'bar', id: 1, isChecked: false }]; mockData = [{ name: 'foo', id: 0, isChecked: false }, { name: 'bar', id: 1, isChecked: false }];
const wrapper = mount( const wrapper = mount(
<I18nProvider> <I18nProvider>
<Lookup <Lookup

View File

@@ -10,7 +10,7 @@ describe('PageHeaderToolbar', () => {
const pageUserDropdownSelector = 'Dropdown UserIcon'; const pageUserDropdownSelector = 'Dropdown UserIcon';
test('expected content is rendered on initialization', () => { test('expected content is rendered on initialization', () => {
const wrapper = mount(<I18nProvider><PageHeaderToolbar/></I18nProvider>); const wrapper = mount(<I18nProvider><PageHeaderToolbar /></I18nProvider>);
expect(wrapper.find(pageHelpDropdownSelector)).toHaveLength(1); expect(wrapper.find(pageHelpDropdownSelector)).toHaveLength(1);
expect(wrapper.find(pageUserDropdownSelector)).toHaveLength(1); expect(wrapper.find(pageUserDropdownSelector)).toHaveLength(1);

View File

@@ -162,7 +162,7 @@ describe('<Pagination />', () => {
pageSizeOptions={[5, 10, 25, 50]} pageSizeOptions={[5, 10, 25, 50]}
onSetPage={onSetPage} onSetPage={onSetPage}
/> />
</I18nProvider> </I18nProvider>
); );
}); });
}); });

View File

@@ -57,7 +57,6 @@ describe('<TowerLogo />', () => {
}); });
test('handles mouse over and out state.hover changes', () => { test('handles mouse over and out state.hover changes', () => {
const onLogoClick = jest.fn();
logoWrapper = mount( logoWrapper = mount(
<MemoryRouter> <MemoryRouter>
<I18nProvider> <I18nProvider>

View File

@@ -2,7 +2,7 @@ import { mount } from 'enzyme';
import { main, getLanguage } from '../src/index'; import { main, getLanguage } from '../src/index';
const render = template => mount(template); const render = template => mount(template);
const data = { custom_logo: 'foo', custom_login_info: '' } const data = { custom_logo: 'foo', custom_login_info: '' };
describe('index.jsx', () => { describe('index.jsx', () => {
test('login loads when unauthenticated', async (done) => { test('login loads when unauthenticated', async (done) => {

View File

@@ -1,7 +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' import OrganizationAdd from '../../../../src/pages/Organizations/screens/OrganizationAdd';
describe('<OrganizationAdd />', () => { describe('<OrganizationAdd />', () => {
test('initially renders succesfully', () => { test('initially renders succesfully', () => {
@@ -57,28 +57,19 @@ 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) => { test('API response is formatted properly', (done) => {
const spy = jest.spyOn(OrganizationAdd.WrappedComponent.prototype, 'format'); const mockedResp = { data: { results: [{ name: 'test instance', id: 1 }] } };
const mockedResp = {data: {id: 1, name: 'foo bar'} };
const api = { getInstanceGroups: jest.fn().mockResolvedValue(mockedResp) }; const api = { getInstanceGroups: jest.fn().mockResolvedValue(mockedResp) };
mount( const wrapper = mount(
<MemoryRouter> <MemoryRouter>
<OrganizationAdd api={api} /> <OrganizationAdd api={api} />
</MemoryRouter> </MemoryRouter>
); );
setImmediate(() => { setImmediate(() => {
expect(spy).toHaveBeenCalled(); const orgAddElem = wrapper.find('OrganizationAdd');
expect([{ id: 1, isChecked: false, name: 'test instance' }]).toEqual(orgAddElem.state().results);
done(); done();
}); });
}); });
@@ -86,7 +77,7 @@ describe('<OrganizationAdd />', () => {
test('Successful form submission triggers redirect', (done) => { test('Successful form submission triggers redirect', (done) => {
const onSuccess = jest.spyOn(OrganizationAdd.WrappedComponent.prototype, 'onSuccess'); const onSuccess = jest.spyOn(OrganizationAdd.WrappedComponent.prototype, 'onSuccess');
const resetForm = jest.spyOn(OrganizationAdd.WrappedComponent.prototype, 'resetForm'); const resetForm = jest.spyOn(OrganizationAdd.WrappedComponent.prototype, 'resetForm');
const mockedResp = {data: {id: 1, related: {instance_groups: '/bar'}}}; const mockedResp = { data: { id: 1, related: { instance_groups: '/bar' } } };
const api = { createOrganization: jest.fn().mockResolvedValue(mockedResp), createInstanceGroups: jest.fn().mockResolvedValue('done') }; const api = { createOrganization: jest.fn().mockResolvedValue(mockedResp), createInstanceGroups: jest.fn().mockResolvedValue('done') };
const wrapper = mount( const wrapper = mount(
<MemoryRouter> <MemoryRouter>

View File

@@ -8,11 +8,11 @@ describe('qs (qs.js)', () => {
[{ order_by: 'name', page: 1, page_size: 5 }, 'order_by=name&page=1&page_size=5'], [{ order_by: 'name', page: 1, page_size: 5 }, 'order_by=name&page=1&page_size=5'],
[{ '-order_by': 'name', page: '1', page_size: 5 }, '-order_by=name&page=1&page_size=5'], [{ '-order_by': 'name', page: '1', page_size: 5 }, '-order_by=name&page=1&page_size=5'],
] ]
.forEach(([params, expectedQueryString]) => { .forEach(([params, expectedQueryString]) => {
const actualQueryString = encodeQueryString(params); const actualQueryString = encodeQueryString(params);
expect(actualQueryString).toEqual(expectedQueryString); expect(actualQueryString).toEqual(expectedQueryString);
}); });
}); });
test('parseQueryString returns the expected queryParams', () => { test('parseQueryString returns the expected queryParams', () => {
@@ -20,10 +20,10 @@ describe('qs (qs.js)', () => {
['order_by=name&page=1&page_size=5', ['page', 'page_size'], { order_by: 'name', page: 1, page_size: 5 }], ['order_by=name&page=1&page_size=5', ['page', 'page_size'], { order_by: 'name', page: 1, page_size: 5 }],
['order_by=name&page=1&page_size=5', ['page_size'], { order_by: 'name', page: '1', page_size: 5 }], ['order_by=name&page=1&page_size=5', ['page_size'], { order_by: 'name', page: '1', page_size: 5 }],
] ]
.forEach(([queryString, integerFields, expectedQueryParams]) => { .forEach(([queryString, integerFields, expectedQueryParams]) => {
const actualQueryParams = parseQueryString(queryString, integerFields); const actualQueryParams = parseQueryString(queryString, integerFields);
expect(actualQueryParams).toEqual(expectedQueryParams) expect(actualQueryParams).toEqual(expectedQueryParams);
}); });
}); });
}); });

View File

@@ -6,7 +6,7 @@
"scripts": { "scripts": {
"start": "webpack-dev-server --config ./webpack.config.js --mode development", "start": "webpack-dev-server --config ./webpack.config.js --mode development",
"test": "jest --watch --coverage", "test": "jest --watch --coverage",
"lint": "./node_modules/eslint/bin/eslint.js src/**/*.js src/**/*.jsx", "lint": "eslint --ext .js --ext .jsx .",
"add-locale": "lingui add-locale", "add-locale": "lingui add-locale",
"extract-strings": "lingui extract", "extract-strings": "lingui extract",
"compile-strings": "lingui compile" "compile-strings": "lingui compile"

View File

@@ -35,28 +35,17 @@ class App extends Component {
this.onAboutModalClose = this.onAboutModalClose.bind(this); this.onAboutModalClose = this.onAboutModalClose.bind(this);
this.onAboutModalOpen = this.onAboutModalOpen.bind(this); this.onAboutModalOpen = this.onAboutModalOpen.bind(this);
this.onNavToggle = this.onNavToggle.bind(this); this.onNavToggle = this.onNavToggle.bind(this);
}; }
componentDidMount () { componentDidMount () {
this.fetchConfig(); this.fetchConfig();
} }
async fetchConfig () {
const { api } = this.props;
try {
const { data: { ansible_version, custom_virtualenvs, version } } = await api.getConfig();
this.setState({ ansible_version, custom_virtualenvs, version });
} catch (err) {
this.setState({ ansible_version: null, custom_virtualenvs: null, version: null });
}
}
async onLogout () { async onLogout () {
const { api } = this.props; const { api } = this.props;
await api.logout(); await api.logout();
window.location.replace('/#/login') window.location.replace('/#/login');
} }
onAboutModalOpen () { onAboutModalOpen () {
@@ -71,6 +60,17 @@ class App extends Component {
this.setState(({ isNavOpen }) => ({ isNavOpen: !isNavOpen })); this.setState(({ isNavOpen }) => ({ isNavOpen: !isNavOpen }));
} }
async fetchConfig () {
const { api } = this.props;
try {
const { data: { ansible_version, custom_virtualenvs, version } } = await api.getConfig();
this.setState({ ansible_version, custom_virtualenvs, version });
} catch (err) {
this.setState({ ansible_version: null, custom_virtualenvs: null, version: null });
}
}
render () { render () {
const { const {
ansible_version, ansible_version,
@@ -100,17 +100,17 @@ class App extends Component {
<PageHeader <PageHeader
showNavToggle showNavToggle
onNavToggle={this.onNavToggle} onNavToggle={this.onNavToggle}
logo={<TowerLogo linkTo="/"/>} logo={<TowerLogo linkTo="/" />}
toolbar={ toolbar={(
<PageHeaderToolbar <PageHeaderToolbar
isAboutDisabled={!version} isAboutDisabled={!version}
onAboutClick={this.onAboutModalOpen} onAboutClick={this.onAboutModalOpen}
onLogoutClick={this.onLogout} onLogoutClick={this.onLogout}
/> />
} )}
/> />
)} )}
sidebar={ sidebar={(
<PageSidebar <PageSidebar
isNavOpen={isNavOpen} isNavOpen={isNavOpen}
nav={( nav={(
@@ -128,7 +128,7 @@ class App extends Component {
</Nav> </Nav>
)} )}
/> />
} )}
> >
<ConfigContext.Provider value={config}> <ConfigContext.Provider value={config}>
{render && render({ routeGroups })} {render && render({ routeGroups })}

View File

@@ -7,7 +7,7 @@ import {
} from '@patternfly/react-core'; } from '@patternfly/react-core';
class AnsibleSelect extends React.Component { class AnsibleSelect extends React.Component {
constructor(props) { constructor (props) {
super(props); super(props);
this.onSelectChange = this.onSelectChange.bind(this); this.onSelectChange = this.onSelectChange.bind(this);
} }
@@ -16,34 +16,39 @@ class AnsibleSelect extends React.Component {
count: 1, count: 1,
} }
static getDerivedStateFromProps(nexProps, _) { static getDerivedStateFromProps (nexProps) {
if (nexProps.data) { if (nexProps.data) {
return { return {
count: nexProps.data.length, count: nexProps.data.length,
} };
} }
return null; return null;
} }
onSelectChange(val, _) {
this.props.selectChange(val); onSelectChange (val) {
const { selectChange } = this.props;
selectChange(val);
} }
render() { render () {
const { count } = this.state; const { count } = this.state;
const { labelName, selected, data } = this.props;
let elem;
if (count > 1) { if (count > 1) {
return ( elem = (
<FormGroup label={this.props.labelName} fieldId="ansible-select"> <FormGroup label={labelName} fieldId="ansible-select">
<Select value={this.props.selected} onChange={this.onSelectChange} aria-label="Select Input"> <Select value={selected} onChange={this.onSelectChange} aria-label="Select Input">
{this.props.data.map((datum, index) => ( {data.map((datum) => (
<SelectOption isDisabled={datum.disabled} key={index} value={datum} label={datum} /> <SelectOption isDisabled={datum.disabled} key={datum} value={datum} label={datum} />
))} ))}
</Select> </Select>
</FormGroup> </FormGroup>
) );
} } else {
else { elem = null;
return null;
} }
return elem;
} }
} }

View File

@@ -53,10 +53,6 @@ class DataListToolbar extends React.Component {
this.onSort = this.onSort.bind(this); this.onSort = this.onSort.bind(this);
} }
handleSearchInputChange (searchValue) {
this.setState({ searchValue });
}
onSortDropdownToggle (isSortDropdownOpen) { onSortDropdownToggle (isSortDropdownOpen) {
this.setState({ isSortDropdownOpen }); this.setState({ isSortDropdownOpen });
} }
@@ -97,6 +93,10 @@ class DataListToolbar extends React.Component {
onSort(sortedColumnKey, newSortOrder); onSort(sortedColumnKey, newSortOrder);
} }
handleSearchInputChange (searchValue) {
this.setState({ searchValue });
}
render () { render () {
const { up } = DropdownPosition; const { up } = DropdownPosition;
const { const {
@@ -223,7 +223,7 @@ class DataListToolbar extends React.Component {
variant="plain" variant="plain"
aria-label={i18n._(t`Sort`)} aria-label={i18n._(t`Sort`)}
> >
<SortIcon/> <SortIcon />
</Button> </Button>
</ToolbarItem> </ToolbarItem>
</ToolbarGroup> </ToolbarGroup>

View File

@@ -11,24 +11,24 @@ export default ({
isSelected, isSelected,
onSelect, onSelect,
}) => ( }) => (
<li key={itemId} className="pf-c-data-list__item" aria-labelledby="check-action-item1"> <li key={itemId} className="pf-c-data-list__item" aria-labelledby="check-action-item1">
<div className="pf-c-data-list__check"> <div className="pf-c-data-list__check">
<I18n> <I18n>
{({ i18n }) => ( {({ i18n }) => (
<Checkbox <Checkbox
checked={isSelected} checked={isSelected}
onChange={onSelect} onChange={onSelect}
aria-label={i18n._(t`selected ${itemId}`)} aria-label={i18n._(t`selected ${itemId}`)}
id={`selectd-${itemId}`} id={`selectd-${itemId}`}
value={itemId} value={itemId}
/> />
)} )}
</I18n> </I18n>
</div> </div>
<div className="pf-c-data-list__cell"> <div className="pf-c-data-list__cell">
<label htmlFor={`selectd-${itemId}`} className="check-action-item"> <label htmlFor={`selectd-${itemId}`} className="check-action-item">
<b>{name}</b> <b>{name}</b>
</label> </label>
</div> </div>
</li> </li>
); );

View File

@@ -9,14 +9,14 @@ import {
ToolbarGroup, ToolbarGroup,
} from '@patternfly/react-core'; } from '@patternfly/react-core';
import CheckboxListItem from '../ListItem' import CheckboxListItem from '../ListItem';
class Lookup extends React.Component { class Lookup extends React.Component {
constructor(props) { constructor (props) {
super(props); super(props);
this.state = { this.state = {
isModalOpen: false, isModalOpen: false,
} };
this.handleModalToggle = this.handleModalToggle.bind(this); this.handleModalToggle = this.handleModalToggle.bind(this);
this.onLookup = this.onLookup.bind(this); this.onLookup = this.onLookup.bind(this);
this.onChecked = this.onChecked.bind(this); this.onChecked = this.onChecked.bind(this);
@@ -24,46 +24,54 @@ class Lookup extends React.Component {
this.onRemove = this.onRemove.bind(this); this.onRemove = this.onRemove.bind(this);
} }
handleModalToggle() { onLookup () {
this.setState((prevState, _) => ({
isModalOpen: !prevState.isModalOpen,
}));
};
onLookup() {
this.handleModalToggle(); this.handleModalToggle();
} }
onChecked(_, evt) { onChecked (_, evt) {
this.props.lookupChange(evt.target.value); const { lookupChange } = 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() { onRemove (evt) {
const { lookupChange } = this.props;
lookupChange(evt.target.id);
}
handleModalToggle () {
this.setState((prevState) => ({
isModalOpen: !prevState.isModalOpen,
}));
}
wrapTags (tags) {
return tags.filter(tag => tag.isChecked).map((tag) => (
<span className="awx-c-tag--pill" key={tag.id}>
{tag.name}
<Button className="awx-c-icon--remove" id={tag.id} onClick={this.onRemove}>
x
</Button>
</span>
));
}
render () {
const { isModalOpen } = this.state; const { isModalOpen } = this.state;
const { data } = this.props; const { data, lookupHeader } = this.props;
return ( return (
<div className="pf-c-input-group awx-lookup"> <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> <Button className="pf-c-input-group__text" aria-label="search" id="search" onClick={this.onLookup}>
<div className="pf-c-form-control">{this.wrapTags(this.props.data)}</div> <SearchIcon />
</Button>
<div className="pf-c-form-control">{this.wrapTags(data)}</div>
<Modal <Modal
className="awx-c-modal" className="awx-c-modal"
title={`Select ${this.props.lookupHeader}`} title={`Select ${lookupHeader}`}
isOpen={isModalOpen} isOpen={isModalOpen}
onClose={this.handleModalToggle} onClose={this.handleModalToggle}
> >
<ul className="pf-c-data-list awx-c-list"> <ul className="pf-c-data-list awx-c-list">
{data.map(i => {data.map(i => (
<CheckboxListItem <CheckboxListItem
key={i.id} key={i.id}
itemId={i.id} itemId={i.id}
@@ -71,12 +79,12 @@ class Lookup extends React.Component {
isSelected={i.isChecked} isSelected={i.isChecked}
onSelect={this.onChecked} onSelect={this.onChecked}
/> />
)} ))}
</ul> </ul>
<ActionGroup className="at-align-right"> <ActionGroup className="at-align-right">
<Toolbar> <Toolbar>
<ToolbarGroup> <ToolbarGroup>
<Button className="at-C-SubmitButton" variant="primary" onClick={this.handleModalToggle} >Select</Button> <Button className="at-C-SubmitButton" variant="primary" onClick={this.handleModalToggle}>Select</Button>
</ToolbarGroup> </ToolbarGroup>
<ToolbarGroup> <ToolbarGroup>
<Button className="at-C-CancelButton" variant="secondary" onClick={this.handleModalToggle}>Cancel</Button> <Button className="at-C-CancelButton" variant="secondary" onClick={this.handleModalToggle}>Cancel</Button>
@@ -85,7 +93,7 @@ class Lookup extends React.Component {
</ActionGroup> </ActionGroup>
</Modal> </Modal>
</div> </div>
) );
} }
} }
export default Lookup; export default Lookup;

View File

@@ -49,6 +49,7 @@ class Pagination extends Component {
event.preventDefault(); event.preventDefault();
// eslint-disable-next-line no-bitwise
const isPositiveInteger = value >>> 0 === parseFloat(value) && parseInt(value, 10) > 0; const isPositiveInteger = value >>> 0 === parseFloat(value) && parseInt(value, 10) > 0;
const isValid = isPositiveInteger && parseInt(value, 10) <= pageCount; const isValid = isPositiveInteger && parseInt(value, 10) <= pageCount;
@@ -73,7 +74,7 @@ class Pagination extends Component {
} }
onNext () { onNext () {
const { onSetPage, page, pageCount, page_size } = this.props; const { onSetPage, page, page_size } = this.props;
const nextPage = page + 1; const nextPage = page + 1;
onSetPage(nextPage, page_size); onSetPage(nextPage, page_size);
@@ -82,7 +83,7 @@ class Pagination extends Component {
onLast () { onLast () {
const { onSetPage, pageCount, page_size } = this.props; const { onSetPage, pageCount, page_size } = this.props;
onSetPage(pageCount, page_size) onSetPage(pageCount, page_size);
} }
onTogglePageSize (isOpen) { onTogglePageSize (isOpen) {
@@ -160,7 +161,7 @@ class Pagination extends Component {
<LevelItem> <LevelItem>
<Split gutter="md" className="pf-u-display-flex pf-u-align-items-center"> <Split gutter="md" className="pf-u-display-flex pf-u-align-items-center">
<SplitItem> <SplitItem>
<Trans>{itemMin} - {itemMax} of {count}</Trans> <Trans>{`${itemMin} - ${itemMax} of ${count}`}</Trans>
</SplitItem> </SplitItem>
<SplitItem> <SplitItem>
<div className="pf-c-input-group"> <div className="pf-c-input-group">
@@ -187,7 +188,8 @@ class Pagination extends Component {
<SplitItem isMain> <SplitItem isMain>
<form onSubmit={this.onSubmit}> <form onSubmit={this.onSubmit}>
<Trans> <Trans>
Page <TextInput {'Page '}
<TextInput
isDisabled={pageCount === 1} isDisabled={pageCount === 1}
aria-label={i18n._(t`Page Number`)} aria-label={i18n._(t`Page Number`)}
style={{ style={{
@@ -201,7 +203,9 @@ class Pagination extends Component {
value={value} value={value}
type="text" type="text"
onChange={this.onPageChange} onChange={this.onPageChange}
/> of {pageCount} />
{' of '}
{pageCount}
</Trans> </Trans>
</form> </form>
</SplitItem> </SplitItem>

View File

@@ -3,7 +3,6 @@ import { Link } from 'react-router-dom';
import './tabs.scss'; import './tabs.scss';
const Tab = ({ location, match, tab, currentTab, children, breadcrumb }) => { const Tab = ({ location, match, tab, currentTab, children, breadcrumb }) => {
const tabClasses = () => { const tabClasses = () => {
let classes = 'pf-c-tabs__item'; let classes = 'pf-c-tabs__item';
@@ -28,13 +27,14 @@ const Tab = ({ location, match, tab, currentTab, children, breadcrumb }) => {
return ( return (
<li className={tabClasses()}> <li className={tabClasses()}>
<Link <Link
className={'pf-c-tabs__button'} className="pf-c-tabs__button"
to={{ pathname: `${match.url}`, search: tabParams(), state: { breadcrumb } }} to={{ pathname: `${match.url}`, search: tabParams(), state: { breadcrumb } }}
replace={tab === currentTab}> replace={tab === currentTab}
>
{children} {children}
</Link> </Link>
</li> </li>
) );
} };
export default Tab; export default Tab;

View File

@@ -1,7 +1,6 @@
import React from 'react'; import React from 'react';
import './tabs.scss'; import './tabs.scss';
const Tabs = ({ children, labelText }) => ( const Tabs = ({ children, labelText }) => (
<div className="pf-c-tabs" aria-label={labelText}> <div className="pf-c-tabs" aria-label={labelText}>
<ul className="pf-c-tabs__list"> <ul className="pf-c-tabs__list">

View File

@@ -1,3 +1,4 @@
import React from "react"; import React from 'react';
// eslint-disable-next-line import/prefer-default-export
export const ConfigContext = React.createContext({}); export const ConfigContext = React.createContext({});

View File

@@ -65,7 +65,7 @@ export function getLanguage (nav) {
const languageWithoutRegionCode = language.toLowerCase().split(/[_-]+/)[0]; const languageWithoutRegionCode = language.toLowerCase().split(/[_-]+/)[0];
return languageWithoutRegionCode; return languageWithoutRegionCode;
}; }
// //
// Function Main // Function Main
@@ -269,7 +269,7 @@ export async function main (render, api) {
/> />
)} )}
/> />
)) ))
)} )}
/> />
)} )}
@@ -280,7 +280,8 @@ export async function main (render, api) {
)} )}
</I18n> </I18n>
</I18nProvider> </I18nProvider>
</HashRouter>, el); </HashRouter>, el
}; );
}
main(ReactDOM.render, new APIClient(http)); main(ReactDOM.render, new APIClient(http));

View File

@@ -70,7 +70,6 @@ const OrganizationBreadcrumb = ({ parentObj, organization, currentTab, location
</Fragment> </Fragment>
); );
} else { } else {
breadcrumb = ( breadcrumb = (
<Fragment> <Fragment>
{generateCrumb(true)} {generateCrumb(true)}

View File

@@ -8,18 +8,15 @@ import {
Link Link
} from 'react-router-dom'; } from 'react-router-dom';
const OrganizationEdit = ({ match, parentBreadcrumbObj, organization }) => { const OrganizationEdit = ({ match, parentBreadcrumbObj, organization }) => (
<Card className="at-c-orgPane">
return ( <CardBody>
<Card className="at-c-orgPane"> <Trans>edit view </Trans>
<CardBody> <Link to={{ pathname: `/organizations/${match.params.id}`, state: { breadcrumb: parentBreadcrumbObj, organization } }}>
<Trans>edit view </Trans> <Trans>save/cancel and go back to view</Trans>
<Link to={{ pathname: `/organizations/${match.params.id}`, state: { breadcrumb: parentBreadcrumbObj, organization } }}> </Link>
<Trans>save/cancel and go back to view</Trans> </CardBody>
</Link> </Card>
</CardBody> );
</Card>
);
};
export default OrganizationEdit; export default OrganizationEdit;

View File

@@ -20,10 +20,21 @@ import {
import { ConfigContext } from '../../../context'; import { ConfigContext } from '../../../context';
import Lookup from '../../../components/Lookup'; import Lookup from '../../../components/Lookup';
import AnsibleSelect from '../../../components/AnsibleSelect' import AnsibleSelect from '../../../components/AnsibleSelect';
const { light } = PageSectionVariants; const { light } = PageSectionVariants;
const format = (data) => {
const results = data.results.map((result) => ({
id: result.id,
name: result.name,
isChecked: false
}));
return results;
};
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);
@@ -33,7 +44,6 @@ class OrganizationAdd extends React.Component {
this.resetForm = this.resetForm.bind(this); this.resetForm = this.resetForm.bind(this);
this.onSuccess = this.onSuccess.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 = {
@@ -45,87 +55,80 @@ class OrganizationAdd extends React.Component {
error: '', error: '',
}; };
onSelectChange(value, _) { async componentDidMount () {
this.setState({ custom_virtualenv: value }); const { api } = this.props;
}; try {
const { data } = await api.getInstanceGroups();
const results = format(data);
this.setState({ results });
} catch (error) {
this.setState({ getInstanceGroupsError: error });
}
}
onLookupChange(id, _) { onSelectChange (value) {
let selected = { ...this.state.results } this.setState({ custom_virtualenv: value });
}
onLookupChange (id) {
const { results } = this.state;
const selected = { ...results };
const index = id - 1; const index = id - 1;
selected[index].isChecked = !selected[index].isChecked; selected[index].isChecked = !selected[index].isChecked;
this.setState({ selected }) this.setState({ selected });
} }
resetForm() { async onSubmit () {
this.setState({
name: '',
description: '',
});
let reset = [];
this.state.results.map((result) => {
reset.push({ id: result.id, name: result.name, isChecked: false });
})
this.setState({ results: reset });
}
handleChange(_, evt) {
this.setState({ [evt.target.name]: evt.target.value });
}
async onSubmit() {
const { api } = this.props; const { api } = this.props;
const data = Object.assign({}, { ...this.state }); const data = Object.assign({}, { ...this.state });
const { results } = this.state;
try { try {
const { data: response } = await api.createOrganization(data); const { data: response } = await api.createOrganization(data);
const url = response.related.instance_groups; const url = response.related.instance_groups;
const selected = this.state.results.filter(group => group.isChecked); const selected = results.filter(group => group.isChecked);
try { try {
if (selected.length > 0) { if (selected.length > 0) {
selected.forEach( async (select) => { selected.forEach(async (select) => {
await api.createInstanceGroups(url, select.id); await api.createInstanceGroups(url, select.id);
}); });
} }
} catch (err) { } catch (err) {
this.setState({ createInstanceGroupsError: err }) this.setState({ createInstanceGroupsError: err });
} finally { } finally {
this.resetForm(); this.resetForm();
this.onSuccess(response.id); this.onSuccess(response.id);
} }
} } catch (err) {
catch (err) { this.setState({ onSubmitError: err });
this.setState({ onSubmitError: err })
} }
} }
onCancel() { onCancel () {
this.props.history.push('/organizations'); const { history } = this.props;
history.push('/organizations');
} }
onSuccess(id) { onSuccess (id) {
this.props.history.push(`/organizations/${id}`); const { history } = this.props;
history.push(`/organizations/${id}`);
} }
format(data) { handleChange (_, evt) {
let results = []; this.setState({ [evt.target.name]: evt.target.value });
data.results.map((result) => { }
results.push({ id: result.id, name: result.name, isChecked: false });
resetForm () {
this.setState({
name: '',
description: '',
}); });
return results; const { results } = this.state;
}; const reset = results.map((result) => ({ id: result.id, name: result.name, isChecked: false }));
this.setState({ results: reset });
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, results } = this.state; const { name, results, description, custom_virtualenv } = this.state;
const enabled = name.length > 0; // TODO: add better form validation const enabled = name.length > 0; // TODO: add better form validation
return ( return (
@@ -150,7 +153,7 @@ class OrganizationAdd extends React.Component {
type="text" type="text"
id="add-org-form-name" id="add-org-form-name"
name="name" name="name"
value={this.state.name} value={name}
onChange={this.handleChange} onChange={this.handleChange}
/> />
</FormGroup> </FormGroup>
@@ -158,7 +161,7 @@ class OrganizationAdd extends React.Component {
<TextInput <TextInput
id="add-org-form-description" id="add-org-form-description"
name="description" name="description"
value={this.state.description} value={description}
onChange={this.handleChange} onChange={this.handleChange}
/> />
</FormGroup> </FormGroup>
@@ -170,14 +173,14 @@ class OrganizationAdd extends React.Component {
/> />
</FormGroup> </FormGroup>
<ConfigContext.Consumer> <ConfigContext.Consumer>
{({ custom_virtualenvs }) => {({ custom_virtualenvs }) => (
<AnsibleSelect <AnsibleSelect
labelName="Ansible Environment" labelName="Ansible Environment"
selected={this.state.custom_virtualenv} selected={custom_virtualenv}
selectChange={this.onSelectChange} selectChange={this.onSelectChange}
data={custom_virtualenvs} data={custom_virtualenvs}
/> />
} )}
</ConfigContext.Consumer> </ConfigContext.Consumer>
</Gallery> </Gallery>
<ActionGroup className="at-align-right"> <ActionGroup className="at-align-right">
@@ -200,7 +203,7 @@ class OrganizationAdd extends React.Component {
} }
OrganizationAdd.contextTypes = { OrganizationAdd.contextTypes = {
custom_virtualenvs: PropTypes.array, custom_virtualenvs: PropTypes.arrayOf(PropTypes.string)
}; };
export default withRouter(OrganizationAdd); export default withRouter(OrganizationAdd);