diff --git a/__tests__/components/DataListToolbar.test.jsx b/__tests__/components/DataListToolbar.test.jsx index 6567e21a05..50e2a6f61b 100644 --- a/__tests__/components/DataListToolbar.test.jsx +++ b/__tests__/components/DataListToolbar.test.jsx @@ -236,7 +236,7 @@ describe('', () => { ([1, 2, 3, 4])} + selected={() => [1, 2, 3, 4]} sortedColumnKey="name" sortOrder="ascending" columns={columns} diff --git a/__tests__/pages/Organizations/screens/OrganizationsList.test.jsx b/__tests__/pages/Organizations/screens/OrganizationsList.test.jsx index 6b7f49646d..6b62d9343f 100644 --- a/__tests__/pages/Organizations/screens/OrganizationsList.test.jsx +++ b/__tests__/pages/Organizations/screens/OrganizationsList.test.jsx @@ -4,6 +4,45 @@ import { MemoryRouter } from 'react-router-dom'; import { I18nProvider } from '@lingui/react'; 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('', () => { test('initially renders succesfully', () => { mount( @@ -17,4 +56,81 @@ describe('', () => { ); }); + + test.only('Modal closes when close button is clicked.', async (done) => { + const handleClearOrgsToDelete = jest.fn(); + const wrapper = mount( + + + + + + ); + 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( + + + + + + ); + 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(); + }); + }); }); diff --git a/src/app.scss b/src/app.scss index 6a6ef32afe..b8606fd856 100644 --- a/src/app.scss +++ b/src/app.scss @@ -282,6 +282,13 @@ .orgListAlert-actionBtn{ margin:0 10px; } +.orgListDetete-progressBar{ + padding-right: 32px; +} +.orgListDelete-progressBar-noShow{ + display: none; + padding-right: 32px; +} .awx-c-form-action-group { float: right; diff --git a/src/pages/Organizations/components/OrganizationListItem.jsx b/src/pages/Organizations/components/OrganizationListItem.jsx index 55eea410d9..4a8309ba11 100644 --- a/src/pages/Organizations/components/OrganizationListItem.jsx +++ b/src/pages/Organizations/components/OrganizationListItem.jsx @@ -4,11 +4,7 @@ import { Trans, t } from '@lingui/macro'; import { Badge, Checkbox, - Button, } from '@patternfly/react-core'; -import { - TrashAltIcon, -} from '@patternfly/react-icons'; import { Link } from 'react-router-dom'; diff --git a/src/pages/Organizations/screens/OrganizationsList.jsx b/src/pages/Organizations/screens/OrganizationsList.jsx index 9da3b1f85e..0ed9ce0d3d 100644 --- a/src/pages/Organizations/screens/OrganizationsList.jsx +++ b/src/pages/Organizations/screens/OrganizationsList.jsx @@ -16,8 +16,11 @@ import { PageSection, PageSectionVariants, Title, - Button + Button, + Progress, + ProgressVariant } from '@patternfly/react-core'; + import { CubesIcon } from '@patternfly/react-icons'; import DataListToolbar from '../../../components/DataListToolbar'; import OrganizationListItem from '../components/OrganizationListItem'; @@ -57,7 +60,10 @@ class OrganizationsList extends Component { results: [], selected: [], isModalOpen: false, - orgsToDelete: [] + orgsToDelete: [], + orgsDeleted: [], + deleteSuccess: false, + }; this.onSearch = this.onSearch.bind(this); @@ -137,7 +143,12 @@ class OrganizationsList extends Component { } handleClearOrgsToDelete () { - this.setState(({ isModalOpen }) => ({ isModalOpen: !isModalOpen, orgsToDelete: [] })); + this.setState({ + isModalOpen: false, + orgsDeleted: [], + deleteSuccess: false, + orgsToDelete: [] + }); this.onSelectAll(); } @@ -163,20 +174,24 @@ class OrganizationsList extends Component { } async handleOrgDelete (event) { - const { orgsToDelete } = this.state; + const { orgsToDelete, orgsDeleted } = this.state; const { api } = this.props; - try { - const deleteOrgsApiCalls = []; + this.setState({ deleteStarted: true }); - orgsToDelete.forEach((org) => { - deleteOrgsApiCalls.push(api.destroyOrganization(org.id)); - }); - await Promise.all(deleteOrgsApiCalls); - } finally { - this.handleClearOrgsToDelete(); - const queryParams = this.getQueryParams(); - this.fetchOrganizations(queryParams); - } + orgsToDelete.forEach(async (org) => { + try { + const res = await api.destroyOrganization(org.id); + this.setState({ + orgsDeleted: orgsDeleted.concat(res) + }); + } catch { + this.setState({ deleteSuccess: false }); + } finally { + this.setState({ deleteSuccess: true }); + const queryParams = this.getQueryParams(); + this.fetchOrganizations(queryParams); + } + }); event.preventDefault(); } @@ -245,9 +260,12 @@ class OrganizationsList extends Component { const { count, error, + deleteSuccess, + deleteStarted, loading, noInitialResults, orgsToDelete, + orgsDeleted, page, pageCount, page_size, @@ -266,7 +284,6 @@ class OrganizationsList extends Component { { isModalOpen && ( {orgsToDelete.map((org) => ( - - {org.name} + + + {org.name} +
-
+ ))} +
+ +

- - - - +
+ {orgsDeleted.length + ? + : ( + + + + + )} +
)} {noInitialResults && (