Add alert for org. delete.

This commit is contained in:
Alex Corey
2019-03-25 11:30:40 -04:00
parent cc0fd6beb6
commit de55ec1688
5 changed files with 178 additions and 28 deletions

View File

@@ -236,7 +236,7 @@ describe('<DataListToolbar />', () => {
<I18nProvider> <I18nProvider>
<DataListToolbar <DataListToolbar
isAllSelected={false} isAllSelected={false}
selected={() => ([1, 2, 3, 4])} selected={() => [1, 2, 3, 4]}
sortedColumnKey="name" sortedColumnKey="name"
sortOrder="ascending" sortOrder="ascending"
columns={columns} columns={columns}

View File

@@ -4,6 +4,45 @@ import { MemoryRouter } from 'react-router-dom';
import { I18nProvider } from '@lingui/react'; import { I18nProvider } from '@lingui/react';
import OrganizationsList from '../../../../src/pages/Organizations/screens/OrganizationsList'; import OrganizationsList from '../../../../src/pages/Organizations/screens/OrganizationsList';
const mockAPIOrgsList = {
data: {
results: [{
name: 'Organization 0',
id: 1,
summary_fields: {
related_field_counts: {
teams: 3,
users: 4
}
},
},
{
name: 'Organization 1',
id: 1,
summary_fields: {
related_field_counts: {
teams: 2,
users: 5
}
},
},
{
name: 'Organization 2',
id: 2,
summary_fields: {
related_field_counts: {
teams: 5,
users: 6
}
},
}]
},
isModalOpen: false,
warningTitle: 'title',
warningMsg: 'message'
};
describe('<OrganizationsList />', () => { describe('<OrganizationsList />', () => {
test('initially renders succesfully', () => { test('initially renders succesfully', () => {
mount( mount(
@@ -17,4 +56,81 @@ describe('<OrganizationsList />', () => {
</MemoryRouter> </MemoryRouter>
); );
}); });
test.only('Modal closes when close button is clicked.', async (done) => {
const handleClearOrgsToDelete = jest.fn();
const wrapper = mount(
<MemoryRouter initialEntries={['/organizations']} initialIndex={0}>
<I18nProvider>
<OrganizationsList
match={{ path: '/organizations', url: '/organizations' }}
location={{ search: '', pathname: '/organizations' }}
getItems={({ data: { orgsToDelete: [{ name: 'Organization 1', id: 1 }] } })}
handleClearOrgsToDelete={handleClearOrgsToDelete()}
/>
</I18nProvider>
</MemoryRouter>
);
wrapper.find({ type: 'checkbox' }).simulate('click');
wrapper.find('button[aria-label="Delete"]').simulate('click');
wrapper.find('DataListToolbar').prop('onOpenDeleteModal')();
expect(wrapper.find('OrganizationsList').state().isModalOpen).toEqual(true);
setImmediate(() => {
wrapper.update();
wrapper.setState({
selected: mockAPIOrgsList.data.results.map((result) => result.id),
orgsToDelete: mockAPIOrgsList.data.results.map((result) => result),
isModalOpen: true,
});
wrapper.find('button[aria-label="Close"]').simulate('click');
expect(handleClearOrgsToDelete).toBeCalled();
const list = wrapper.find('OrganizationsList');
expect(list.state().isModalOpen).toBe(false);
done();
});
});
test.only('Orgs to delete length is 0 when all orgs are selected and Delete button is called.', async (done) => {
const handleClearOrgsToDelete = jest.fn();
const handleOrgDelete = jest.fn();
const fetchOrganizations = jest.fn();
const wrapper = mount(
<MemoryRouter initialEntries={['/organizations']} initialIndex={0}>
<I18nProvider>
<OrganizationsList
match={{ path: '/organizations', url: '/organizations' }}
location={{ search: '', pathname: '/organizations' }}
getItems={({ data: { orgsToDelete: [{ name: 'Organization 1', id: 1 }] } })}
handleClearOrgsToDelete={handleClearOrgsToDelete()}
handleOrgDelete={handleOrgDelete()}
fetchOrganizations={fetchOrganizations()}
/>
</I18nProvider>
</MemoryRouter>
);
wrapper.find({ type: 'checkbox' }).simulate('click');
wrapper.find('button[aria-label="Delete"]').simulate('click');
wrapper.find('DataListToolbar').prop('onOpenDeleteModal')();
expect(wrapper.find('OrganizationsList').state().isModalOpen).toEqual(true);
setImmediate(() => {
wrapper.update();
wrapper.setState({
selected: mockAPIOrgsList.data.results.map((result) => result.id),
orgsToDelete: mockAPIOrgsList.data.results.map((result) => result),
isModalOpen: true,
});
wrapper.update();
const list = wrapper.find('OrganizationsList');
wrapper.find('button[aria-label="confirm-delete"]').simulate('click');
expect(list.state().orgsToDelete.length).toEqual(list.state().orgsDeleted.length);
expect(fetchOrganizations).toHaveBeenCalled();
expect(list.state().results).toHaveLength(0);
done();
});
});
}); });

View File

@@ -282,6 +282,13 @@
.orgListAlert-actionBtn{ .orgListAlert-actionBtn{
margin:0 10px; margin:0 10px;
} }
.orgListDetete-progressBar{
padding-right: 32px;
}
.orgListDelete-progressBar-noShow{
display: none;
padding-right: 32px;
}
.awx-c-form-action-group { .awx-c-form-action-group {
float: right; float: right;

View File

@@ -4,11 +4,7 @@ import { Trans, t } from '@lingui/macro';
import { import {
Badge, Badge,
Checkbox, Checkbox,
Button,
} from '@patternfly/react-core'; } from '@patternfly/react-core';
import {
TrashAltIcon,
} from '@patternfly/react-icons';
import { import {
Link Link
} from 'react-router-dom'; } from 'react-router-dom';

View File

@@ -16,8 +16,11 @@ import {
PageSection, PageSection,
PageSectionVariants, PageSectionVariants,
Title, Title,
Button Button,
Progress,
ProgressVariant
} from '@patternfly/react-core'; } from '@patternfly/react-core';
import { CubesIcon } from '@patternfly/react-icons'; import { CubesIcon } from '@patternfly/react-icons';
import DataListToolbar from '../../../components/DataListToolbar'; import DataListToolbar from '../../../components/DataListToolbar';
import OrganizationListItem from '../components/OrganizationListItem'; import OrganizationListItem from '../components/OrganizationListItem';
@@ -57,7 +60,10 @@ class OrganizationsList extends Component {
results: [], results: [],
selected: [], selected: [],
isModalOpen: false, isModalOpen: false,
orgsToDelete: [] orgsToDelete: [],
orgsDeleted: [],
deleteSuccess: false,
}; };
this.onSearch = this.onSearch.bind(this); this.onSearch = this.onSearch.bind(this);
@@ -137,7 +143,12 @@ class OrganizationsList extends Component {
} }
handleClearOrgsToDelete () { handleClearOrgsToDelete () {
this.setState(({ isModalOpen }) => ({ isModalOpen: !isModalOpen, orgsToDelete: [] })); this.setState({
isModalOpen: false,
orgsDeleted: [],
deleteSuccess: false,
orgsToDelete: []
});
this.onSelectAll(); this.onSelectAll();
} }
@@ -163,20 +174,24 @@ class OrganizationsList extends Component {
} }
async handleOrgDelete (event) { async handleOrgDelete (event) {
const { orgsToDelete } = this.state; const { orgsToDelete, orgsDeleted } = this.state;
const { api } = this.props; const { api } = this.props;
try { this.setState({ deleteStarted: true });
const deleteOrgsApiCalls = [];
orgsToDelete.forEach((org) => { orgsToDelete.forEach(async (org) => {
deleteOrgsApiCalls.push(api.destroyOrganization(org.id)); try {
}); const res = await api.destroyOrganization(org.id);
await Promise.all(deleteOrgsApiCalls); this.setState({
} finally { orgsDeleted: orgsDeleted.concat(res)
this.handleClearOrgsToDelete(); });
const queryParams = this.getQueryParams(); } catch {
this.fetchOrganizations(queryParams); this.setState({ deleteSuccess: false });
} } finally {
this.setState({ deleteSuccess: true });
const queryParams = this.getQueryParams();
this.fetchOrganizations(queryParams);
}
});
event.preventDefault(); event.preventDefault();
} }
@@ -245,9 +260,12 @@ class OrganizationsList extends Component {
const { const {
count, count,
error, error,
deleteSuccess,
deleteStarted,
loading, loading,
noInitialResults, noInitialResults,
orgsToDelete, orgsToDelete,
orgsDeleted,
page, page,
pageCount, pageCount,
page_size, page_size,
@@ -266,7 +284,6 @@ class OrganizationsList extends Component {
{ isModalOpen && ( { isModalOpen && (
<Modal <Modal
className="orgListAlert" className="orgListAlert"
width="50%"
title={warningTitle} title={warningTitle}
isOpen={isModalOpen} isOpen={isModalOpen}
style={{ width: '1000px' }} style={{ width: '1000px' }}
@@ -276,16 +293,30 @@ class OrganizationsList extends Component {
{warningMsg} {warningMsg}
<br /> <br />
{orgsToDelete.map((org) => ( {orgsToDelete.map((org) => (
<strong key={org.id}> <span key={org.id}>
{org.name} <strong>
{org.name}
</strong>
<br /> <br />
</strong> </span>
))} ))}
<div className={deleteStarted ? 'orgListDetete-progressBar' : 'orgListDelete-progressBar-noShow'}>
<Progress
value={deleteSuccess ? 100 : 67}
variant={deleteStarted ? ProgressVariant.success : ProgressVariant.danger}
/>
</div>
<br /> <br />
<span className="awx-c-form-action-group"> <div className="awx-c-form-action-group">
<Button className="orgListAlert-actionBtn" keys="cancel" variant="secondary" aria-label="cancel-delete" onClick={this.handleClearOrgsToDelete}>Cancel</Button> {orgsDeleted.length
<Button className="orgListAlert-actionBtn" keys="cancel" variant="danger" aria-label="confirm-delete" onClick={this.handleOrgDelete}>Delete</Button> ? <Button className="orgListAlert-actionBtn" keys="cancel" variant="primary" aria-label="close-delete" onClick={this.handleClearOrgsToDelete}>Close</Button>
</span> : (
<span>
<Button className="orgListAlert-actionBtn" keys="cancel" variant="secondary" aria-label="cancel-delete" onClick={this.handleClearOrgsToDelete}>Cancel</Button>
<Button className="orgListAlert-actionBtn" keys="cancel" variant="danger" aria-label="confirm-delete" onClick={this.handleOrgDelete}>Delete</Button>
</span>
)}
</div>
</Modal> </Modal>
)} )}
{noInitialResults && ( {noInitialResults && (