Rebase and incorporates feedback

This commit is contained in:
mabashian
2019-04-22 17:15:32 -04:00
parent 9880f1e124
commit e8d73babaf
9 changed files with 161 additions and 174 deletions

View File

@@ -1,6 +1,6 @@
import React from 'react'; import React from 'react';
import { mount } from 'enzyme'; import { shallow } from 'enzyme';
import { I18nProvider } from '@lingui/react'; import { mountWithContexts } from '../enzymeHelpers';
import AddResourceRole from '../../src/components/AddRole/AddResourceRole'; import AddResourceRole from '../../src/components/AddRole/AddResourceRole';
describe('<SelectResourceStep />', () => { describe('<SelectResourceStep />', () => {
@@ -30,28 +30,24 @@ describe('<SelectResourceStep />', () => {
} }
}; };
test('initially renders without crashing', () => { test('initially renders without crashing', () => {
mount( shallow(
<I18nProvider> <AddResourceRole
<AddResourceRole api={api}
api={api} onClose={() => {}}
onClose={jest.fn()} onSave={() => {}}
onSave={jest.fn()} roles={roles}
roles={roles} />
/>
</I18nProvider>
); );
}); });
test('handleRoleCheckboxClick properly updates state', () => { test('handleRoleCheckboxClick properly updates state', () => {
const wrapper = mount( const wrapper = shallow(
<I18nProvider> <AddResourceRole
<AddResourceRole api={api}
api={api} onClose={() => {}}
onClose={jest.fn()} onSave={() => {}}
onSave={jest.fn()} roles={roles}
roles={roles} />
/> );
</I18nProvider>
).find('AddResourceRole');
wrapper.setState({ wrapper.setState({
selectedRoleRows: [ selectedRoleRows: [
{ {
@@ -79,16 +75,14 @@ describe('<SelectResourceStep />', () => {
}]); }]);
}); });
test('handleResourceCheckboxClick properly updates state', () => { test('handleResourceCheckboxClick properly updates state', () => {
const wrapper = mount( const wrapper = shallow(
<I18nProvider> <AddResourceRole
<AddResourceRole api={api}
api={api} onClose={() => {}}
onClose={jest.fn()} onSave={() => {}}
onSave={jest.fn()} roles={roles}
roles={roles} />
/> );
</I18nProvider>
).find('AddResourceRole');
wrapper.setState({ wrapper.setState({
selectedResourceRows: [ selectedResourceRows: [
{ {
@@ -113,15 +107,13 @@ describe('<SelectResourceStep />', () => {
}); });
test('clicking user/team cards updates state', () => { test('clicking user/team cards updates state', () => {
const spy = jest.spyOn(AddResourceRole.prototype, 'handleResourceSelect'); const spy = jest.spyOn(AddResourceRole.prototype, 'handleResourceSelect');
const wrapper = mount( const wrapper = mountWithContexts(
<I18nProvider> <AddResourceRole
<AddResourceRole onClose={() => {}}
onClose={jest.fn()} onSave={() => {}}
onSave={jest.fn()} api={api}
api={api} roles={roles}
roles={roles} />
/>
</I18nProvider>
).find('AddResourceRole'); ).find('AddResourceRole');
const selectableCardWrapper = wrapper.find('SelectableCard'); const selectableCardWrapper = wrapper.find('SelectableCard');
expect(selectableCardWrapper.length).toBe(2); expect(selectableCardWrapper.length).toBe(2);
@@ -133,16 +125,14 @@ describe('<SelectResourceStep />', () => {
expect(wrapper.state('selectedResource')).toBe('teams'); expect(wrapper.state('selectedResource')).toBe('teams');
}); });
test('readUsers and readTeams call out to corresponding api functions', () => { test('readUsers and readTeams call out to corresponding api functions', () => {
const wrapper = mount( const wrapper = shallow(
<I18nProvider> <AddResourceRole
<AddResourceRole api={api}
api={api} onClose={() => {}}
onClose={jest.fn()} onSave={() => {}}
onSave={jest.fn()} roles={roles}
roles={roles} />
/> );
</I18nProvider>
).find('AddResourceRole');
wrapper.instance().readUsers({ wrapper.instance().readUsers({
foo: 'bar' foo: 'bar'
}); });
@@ -158,16 +148,14 @@ describe('<SelectResourceStep />', () => {
}); });
}); });
test('handleResourceSelect clears out selected lists and sets selectedResource', () => { test('handleResourceSelect clears out selected lists and sets selectedResource', () => {
const wrapper = mount( const wrapper = shallow(
<I18nProvider> <AddResourceRole
<AddResourceRole api={api}
api={api} onClose={() => {}}
onClose={jest.fn()} onSave={() => {}}
onSave={jest.fn()} roles={roles}
roles={roles} />
/> );
</I18nProvider>
).find('AddResourceRole');
wrapper.setState({ wrapper.setState({
selectedResource: 'teams', selectedResource: 'teams',
selectedResourceRows: [ selectedResourceRows: [
@@ -199,15 +187,13 @@ describe('<SelectResourceStep />', () => {
}); });
test('handleWizardSave makes correct api calls, calls onSave when done', async () => { test('handleWizardSave makes correct api calls, calls onSave when done', async () => {
const handleSave = jest.fn(); const handleSave = jest.fn();
const wrapper = mount( const wrapper = mountWithContexts(
<I18nProvider> <AddResourceRole
<AddResourceRole api={api}
api={api} onClose={() => {}}
onClose={jest.fn()} onSave={handleSave}
onSave={handleSave} roles={roles}
roles={roles} />
/>
</I18nProvider>
).find('AddResourceRole'); ).find('AddResourceRole');
wrapper.setState({ wrapper.setState({
selectedResource: 'users', selectedResource: 'users',

View File

@@ -1,17 +1,16 @@
import React from 'react'; import React from 'react';
import { mount } from 'enzyme'; import { shallow } from 'enzyme';
import CheckboxCard from '../../src/components/AddRole/CheckboxCard'; import CheckboxCard from '../../src/components/AddRole/CheckboxCard';
describe('<CheckboxCard />', () => { describe('<CheckboxCard />', () => {
let wrapper; let wrapper;
test('initially renders without crashing', () => { test('initially renders without crashing', () => {
wrapper = mount( wrapper = shallow(
<CheckboxCard <CheckboxCard
name="Foobar" name="Foobar"
itemId={5} itemId={5}
/> />
); );
expect(wrapper.length).toBe(1); expect(wrapper.length).toBe(1);
wrapper.unmount();
}); });
}); });

View File

@@ -1,6 +1,6 @@
import React from 'react'; import React from 'react';
import { mount } from 'enzyme'; import { shallow } from 'enzyme';
import { I18nProvider } from '@lingui/react'; import { mountWithContexts } from '../enzymeHelpers';
import SelectResourceStep from '../../src/components/AddRole/SelectResourceStep'; import SelectResourceStep from '../../src/components/AddRole/SelectResourceStep';
describe('<SelectResourceStep />', () => { describe('<SelectResourceStep />', () => {
@@ -11,16 +11,14 @@ describe('<SelectResourceStep />', () => {
jest.restoreAllMocks(); jest.restoreAllMocks();
}); });
test('initially renders without crashing', () => { test('initially renders without crashing', () => {
mount( shallow(
<I18nProvider> <SelectResourceStep
<SelectResourceStep columns={columns}
columns={columns} displayKey="username"
displayKey="username" onRowClick={() => {}}
onRowClick={jest.fn()} onSearch={() => {}}
onSearch={jest.fn()} sortedColumnKey="username"
sortedColumnKey="username" />
/>
</I18nProvider>
); );
}); });
test('fetches resources on mount', async () => { test('fetches resources on mount', async () => {
@@ -33,16 +31,14 @@ describe('<SelectResourceStep />', () => {
] ]
} }
}); });
mount( mountWithContexts(
<I18nProvider> <SelectResourceStep
<SelectResourceStep columns={columns}
columns={columns} displayKey="username"
displayKey="username" onRowClick={() => {}}
onRowClick={jest.fn()} onSearch={handleSearch}
onSearch={handleSearch} sortedColumnKey="username"
sortedColumnKey="username" />
/>
</I18nProvider>
); );
expect(handleSearch).toHaveBeenCalledWith({ expect(handleSearch).toHaveBeenCalledWith({
order_by: 'username', order_by: 'username',
@@ -66,17 +62,15 @@ describe('<SelectResourceStep />', () => {
] ]
} }
}); });
const wrapper = await mount( const wrapper = await mountWithContexts(
<I18nProvider> <SelectResourceStep
<SelectResourceStep columns={columns}
columns={columns} displayKey="username"
displayKey="username" onRowClick={() => {}}
onRowClick={jest.fn()} onSearch={handleSearch}
onSearch={handleSearch} selectedResourceRows={selectedResourceRows}
selectedResourceRows={selectedResourceRows} sortedColumnKey="username"
sortedColumnKey="username" />
/>
</I18nProvider>
).find('SelectResourceStep'); ).find('SelectResourceStep');
await wrapper.instance().readResourceList({ await wrapper.instance().readResourceList({
page: 1, page: 1,
@@ -93,17 +87,15 @@ describe('<SelectResourceStep />', () => {
}); });
test('handleSetPage calls readResourceList with correct params', () => { test('handleSetPage calls readResourceList with correct params', () => {
const spy = jest.spyOn(SelectResourceStep.prototype, 'readResourceList'); const spy = jest.spyOn(SelectResourceStep.prototype, 'readResourceList');
const wrapper = mount( const wrapper = mountWithContexts(
<I18nProvider> <SelectResourceStep
<SelectResourceStep columns={columns}
columns={columns} displayKey="username"
displayKey="username" onRowClick={() => {}}
onRowClick={jest.fn()} onSearch={() => {}}
onSearch={jest.fn()} selectedResourceRows={[]}
selectedResourceRows={[]} sortedColumnKey="username"
sortedColumnKey="username" />
/>
</I18nProvider>
).find('SelectResourceStep'); ).find('SelectResourceStep');
wrapper.setState({ sortOrder: 'descending' }); wrapper.setState({ sortOrder: 'descending' });
wrapper.instance().handleSetPage(2); wrapper.instance().handleSetPage(2);
@@ -114,17 +106,15 @@ describe('<SelectResourceStep />', () => {
}); });
test('handleSort calls readResourceList with correct params', () => { test('handleSort calls readResourceList with correct params', () => {
const spy = jest.spyOn(SelectResourceStep.prototype, 'readResourceList'); const spy = jest.spyOn(SelectResourceStep.prototype, 'readResourceList');
const wrapper = mount( const wrapper = mountWithContexts(
<I18nProvider> <SelectResourceStep
<SelectResourceStep columns={columns}
columns={columns} displayKey="username"
displayKey="username" onRowClick={() => {}}
onRowClick={jest.fn()} onSearch={() => {}}
onSearch={jest.fn()} selectedResourceRows={[]}
selectedResourceRows={[]} sortedColumnKey="username"
sortedColumnKey="username" />
/>
</I18nProvider>
).find('SelectResourceStep'); ).find('SelectResourceStep');
wrapper.instance().handleSort('username', 'descending'); wrapper.instance().handleSort('username', 'descending');
expect(spy).toHaveBeenCalledWith({ page: 1, page_size: 5, order_by: '-username' }); expect(spy).toHaveBeenCalledWith({ page: 1, page_size: 5, order_by: '-username' });
@@ -133,17 +123,15 @@ describe('<SelectResourceStep />', () => {
}); });
test('clicking on row fires callback with correct params', () => { test('clicking on row fires callback with correct params', () => {
const handleRowClick = jest.fn(); const handleRowClick = jest.fn();
const wrapper = mount( const wrapper = mountWithContexts(
<I18nProvider> <SelectResourceStep
<SelectResourceStep columns={columns}
columns={columns} displayKey="username"
displayKey="username" onRowClick={handleRowClick}
onRowClick={handleRowClick} onSearch={() => {}}
onSearch={jest.fn()} selectedResourceRows={[]}
selectedResourceRows={[]} sortedColumnKey="username"
sortedColumnKey="username" />
/>
</I18nProvider>
); );
const selectResourceStepWrapper = wrapper.find('SelectResourceStep'); const selectResourceStepWrapper = wrapper.find('SelectResourceStep');
selectResourceStepWrapper.setState({ selectResourceStepWrapper.setState({

View File

@@ -1,5 +1,5 @@
import React from 'react'; import React from 'react';
import { mount } from 'enzyme'; import { mount, shallow } from 'enzyme';
import SelectRoleStep from '../../src/components/AddRole/SelectRoleStep'; import SelectRoleStep from '../../src/components/AddRole/SelectRoleStep';
describe('<SelectRoleStep />', () => { describe('<SelectRoleStep />', () => {
@@ -30,7 +30,7 @@ describe('<SelectRoleStep />', () => {
} }
]; ];
test('initially renders without crashing', () => { test('initially renders without crashing', () => {
wrapper = mount( wrapper = shallow(
<SelectRoleStep <SelectRoleStep
roles={roles} roles={roles}
selectedResourceRows={selectedResourceRows} selectedResourceRows={selectedResourceRows}

View File

@@ -1,12 +1,12 @@
import React from 'react'; import React from 'react';
import { mount } from 'enzyme'; import { shallow } from 'enzyme';
import SelectableCard from '../../src/components/AddRole/SelectableCard'; import SelectableCard from '../../src/components/AddRole/SelectableCard';
describe('<SelectableCard />', () => { describe('<SelectableCard />', () => {
let wrapper; let wrapper;
const onClick = jest.fn(); const onClick = jest.fn();
test('initially renders without crashing when not selected', () => { test('initially renders without crashing when not selected', () => {
wrapper = mount( wrapper = shallow(
<SelectableCard <SelectableCard
onClick={onClick} onClick={onClick}
/> />
@@ -15,7 +15,7 @@ describe('<SelectableCard />', () => {
wrapper.unmount(); wrapper.unmount();
}); });
test('initially renders without crashing when selected', () => { test('initially renders without crashing when selected', () => {
wrapper = mount( wrapper = shallow(
<SelectableCard <SelectableCard
isSelected isSelected
onClick={onClick} onClick={onClick}

View File

@@ -142,6 +142,19 @@ class AddResourceRole extends React.Component {
{ name: i18nMark('Name'), key: 'name', isSortable: true } { name: i18nMark('Name'), key: 'name', isSortable: true }
]; ];
let wizardTitle = '';
switch (selectedResource) {
case 'users':
wizardTitle = i18nMark('Add User Roles');
break;
case 'teams':
wizardTitle = i18nMark('Add Team Roles');
break;
default:
wizardTitle = i18nMark('Add Roles');
}
const steps = [ const steps = [
{ {
name: i18nMark('Select Users Or Teams'), name: i18nMark('Select Users Or Teams'),
@@ -179,10 +192,9 @@ class AddResourceRole extends React.Component {
emptyListTitle={i18n._(t`No Users Found`)} emptyListTitle={i18n._(t`No Users Found`)}
onRowClick={this.handleResourceCheckboxClick} onRowClick={this.handleResourceCheckboxClick}
onSearch={this.readUsers} onSearch={this.readUsers}
selectedLabel={i18n._(t`Selected Users`)} selectedLabel={i18n._(t`Selected`)}
selectedResourceRows={selectedResourceRows} selectedResourceRows={selectedResourceRows}
sortedColumnKey="username" sortedColumnKey="username"
title={i18n._(t`Users`)}
/> />
)} )}
{selectedResource === 'teams' && ( {selectedResource === 'teams' && (
@@ -192,9 +204,8 @@ class AddResourceRole extends React.Component {
emptyListTitle={i18n._(t`No Teams Found`)} emptyListTitle={i18n._(t`No Teams Found`)}
onRowClick={this.handleResourceCheckboxClick} onRowClick={this.handleResourceCheckboxClick}
onSearch={this.readTeams} onSearch={this.readTeams}
selectedLabel={i18n._(t`Selected Teams`)} selectedLabel={i18n._(t`Selected`)}
selectedResourceRows={selectedResourceRows} selectedResourceRows={selectedResourceRows}
title={i18n._(t`Teams`)}
/> />
)} )}
</Fragment> </Fragment>
@@ -212,7 +223,7 @@ class AddResourceRole extends React.Component {
onRolesClick={this.handleRoleCheckboxClick} onRolesClick={this.handleRoleCheckboxClick}
roles={roles} roles={roles}
selectedListKey={selectedResource === 'users' ? 'username' : 'name'} selectedListKey={selectedResource === 'users' ? 'username' : 'name'}
selectedListLabel={selectedResource === 'users' ? i18n._(t`Selected Users`) : i18n._(t`Selected Teams`)} selectedListLabel={i18n._(t`Selected`)}
selectedResourceRows={selectedResourceRows} selectedResourceRows={selectedResourceRows}
selectedRoleRows={selectedRoleRows} selectedRoleRows={selectedRoleRows}
/> />
@@ -234,7 +245,7 @@ class AddResourceRole extends React.Component {
onClose={onClose} onClose={onClose}
onSave={this.handleWizardSave} onSave={this.handleWizardSave}
steps={steps} steps={steps}
title={i18n._(t`Add Roles`)} title={wizardTitle}
/> />
)} )}
</I18n> </I18n>

View File

@@ -17,6 +17,14 @@ import DataListToolbar from '../DataListToolbar';
import Pagination from '../Pagination'; import Pagination from '../Pagination';
import SelectedList from '../SelectedList'; import SelectedList from '../SelectedList';
const paginationStyling = {
paddingLeft: '0',
justifyContent: 'flex-end',
borderRight: '1px solid #ebebeb',
borderBottom: '1px solid #ebebeb',
borderTop: '0'
};
class SelectResourceStep extends React.Component { class SelectResourceStep extends React.Component {
constructor (props) { constructor (props) {
super(props); super(props);
@@ -119,8 +127,7 @@ class SelectResourceStep extends React.Component {
emptyListTitle, emptyListTitle,
onRowClick, onRowClick,
selectedLabel, selectedLabel,
selectedResourceRows, selectedResourceRows
title
} = this.props; } = this.props;
return ( return (
@@ -138,9 +145,15 @@ class SelectResourceStep extends React.Component {
</EmptyState> </EmptyState>
) : ( ) : (
<Fragment> <Fragment>
<Title size="lg"> {selectedResourceRows.length > 0 && (
{title} <SelectedList
</Title> displayKey={displayKey}
label={selectedLabel}
onRemove={onRowClick}
selected={selectedResourceRows}
showOverflowAfter={5}
/>
)}
<DataListToolbar <DataListToolbar
columns={columns} columns={columns}
noLeftMargin noLeftMargin
@@ -168,19 +181,11 @@ class SelectResourceStep extends React.Component {
pageSizeOptions={null} pageSizeOptions={null}
page_size={page_size} page_size={page_size}
showPageSizeOptions={false} showPageSizeOptions={false}
style={paginationStyling}
/> />
</Fragment> </Fragment>
)} )}
</Fragment> </Fragment>
{selectedResourceRows.length > 0 && (
<SelectedList
displayKey={displayKey}
label={selectedLabel}
onRemove={onRowClick}
selected={selectedResourceRows}
showOverflowAfter={5}
/>
)}
{ error ? <div>error</div> : '' } { error ? <div>error</div> : '' }
</Fragment> </Fragment>
); );
@@ -196,8 +201,7 @@ SelectResourceStep.propTypes = {
onSearch: PropTypes.func.isRequired, onSearch: PropTypes.func.isRequired,
selectedLabel: PropTypes.string, selectedLabel: PropTypes.string,
selectedResourceRows: PropTypes.arrayOf(PropTypes.object), selectedResourceRows: PropTypes.arrayOf(PropTypes.object),
sortedColumnKey: PropTypes.string, sortedColumnKey: PropTypes.string
title: PropTypes.string
}; };
SelectResourceStep.defaultProps = { SelectResourceStep.defaultProps = {
@@ -207,8 +211,7 @@ SelectResourceStep.defaultProps = {
onRowClick: () => {}, onRowClick: () => {},
selectedLabel: i18nMark('Selected Items'), selectedLabel: i18nMark('Selected Items'),
selectedResourceRows: [], selectedResourceRows: [],
sortedColumnKey: 'name', sortedColumnKey: 'name'
title: ''
}; };
export default SelectResourceStep; export default SelectResourceStep;

View File

@@ -8,7 +8,7 @@
.awx-selectableCard__indicator { .awx-selectableCard__indicator {
display: flex; display: flex;
flex: 0 0 10px; flex: 0 0 5px;
} }
.awx-selectableCard__label { .awx-selectableCard__label {

View File

@@ -45,6 +45,7 @@ class SelectedList extends Component {
isReadOnly isReadOnly
} = this.props; } = this.props;
const { showOverflow } = this.state; const { showOverflow } = this.state;
const visibleItems = selected.slice(0, showOverflow ? selected.length : showOverflowAfter);
return ( return (
<div className="awx-selectedList"> <div className="awx-selectedList">
<div className="pf-l-split" style={selectedRowStyling}> <div className="pf-l-split" style={selectedRowStyling}>
@@ -56,20 +57,19 @@ class SelectedList extends Component {
<div className="pf-c-chip-group"> <div className="pf-c-chip-group">
{isReadOnly ? ( {isReadOnly ? (
<Fragment> <Fragment>
{selected {visibleItems
.slice(0, showOverflow ? selected.length : showOverflowAfter)
.map(selectedItem => ( .map(selectedItem => (
<BasicChip <BasicChip
key={selectedItem.id} key={selectedItem.id}
text={selectedItem[displayKey]} >
/> {selectedItem[displayKey]}
</BasicChip>
)) ))
} }
</Fragment> </Fragment>
) : ( ) : (
<Fragment> <Fragment>
{selected {visibleItems
.slice(0, showOverflow ? selected.length : showOverflowAfter)
.map(selectedItem => ( .map(selectedItem => (
<Chip <Chip
key={selectedItem.id} key={selectedItem.id}