From 627dae65808fbafbd2532ec288cea8c3f5c9bff6 Mon Sep 17 00:00:00 2001 From: Marliana Lara Date: Thu, 23 Jan 2020 12:12:15 -0500 Subject: [PATCH] Add delete button to organization details --- .../OrganizationDetail/OrganizationDetail.jsx | 46 +++++++++++- .../OrganizationDetail.test.jsx | 73 ++++++++++++++++++- 2 files changed, 115 insertions(+), 4 deletions(-) diff --git a/awx/ui_next/src/screens/Organization/OrganizationDetail/OrganizationDetail.jsx b/awx/ui_next/src/screens/Organization/OrganizationDetail/OrganizationDetail.jsx index 8895b26094..6811431627 100644 --- a/awx/ui_next/src/screens/Organization/OrganizationDetail/OrganizationDetail.jsx +++ b/awx/ui_next/src/screens/Organization/OrganizationDetail/OrganizationDetail.jsx @@ -1,5 +1,5 @@ import React, { useEffect, useState } from 'react'; -import { Link, useRouteMatch } from 'react-router-dom'; +import { Link, useHistory, useRouteMatch } from 'react-router-dom'; import { withI18n } from '@lingui/react'; import { t } from '@lingui/macro'; import { Button } from '@patternfly/react-core'; @@ -7,8 +7,11 @@ import { OrganizationsAPI } from '@api'; import { DetailList, Detail, UserDateDetail } from '@components/DetailList'; import { CardBody, CardActionsRow } from '@components/Card'; import { ChipGroup, Chip } from '@components/Chip'; +import AlertModal from '@components/AlertModal'; import ContentError from '@components/ContentError'; import ContentLoading from '@components/ContentLoading'; +import DeleteButton from '@components/DeleteButton'; +import ErrorDetail from '@components/ErrorDetail'; function OrganizationDetail({ i18n, organization }) { const { @@ -24,8 +27,10 @@ function OrganizationDetail({ i18n, organization }) { summary_fields, } = organization; const [contentError, setContentError] = useState(null); + const [deletionError, setDeletionError] = useState(null); const [hasContentLoading, setHasContentLoading] = useState(true); const [instanceGroups, setInstanceGroups] = useState([]); + const history = useHistory(); useEffect(() => { (async () => { @@ -44,6 +49,17 @@ function OrganizationDetail({ i18n, organization }) { })(); }, [id]); + const handleDelete = async () => { + setHasContentLoading(true); + try { + await OrganizationsAPI.destroy(id); + history.push(`/organizations`); + } catch (error) { + setDeletionError(error); + } + setHasContentLoading(false); + }; + if (hasContentLoading) { return ; } @@ -94,11 +110,37 @@ function OrganizationDetail({ i18n, organization }) { {summary_fields.user_capabilities.edit && ( - )} + {summary_fields.user_capabilities && + summary_fields.user_capabilities.delete && ( + + {i18n._(t`Delete`)} + + )} + {/* Update delete modal to show dependencies https://github.com/ansible/awx/issues/5546 */} + {deletionError && ( + setDeletionError(null)} + > + {i18n._(t`Failed to delete organization.`)} + + + )} ); } diff --git a/awx/ui_next/src/screens/Organization/OrganizationDetail/OrganizationDetail.test.jsx b/awx/ui_next/src/screens/Organization/OrganizationDetail/OrganizationDetail.test.jsx index b3593fd952..39448e2434 100644 --- a/awx/ui_next/src/screens/Organization/OrganizationDetail/OrganizationDetail.test.jsx +++ b/awx/ui_next/src/screens/Organization/OrganizationDetail/OrganizationDetail.test.jsx @@ -19,6 +19,7 @@ describe('', () => { summary_fields: { user_capabilities: { edit: true, + delete: true, }, }, }; @@ -98,7 +99,7 @@ describe('', () => { }); const editButton = await waitForElement( wrapper, - 'OrganizationDetail Button' + 'OrganizationDetail Button[aria-label="Edit"]' ); expect(editButton.text()).toEqual('Edit'); expect(editButton.prop('to')).toBe('/organizations/undefined/edit'); @@ -115,6 +116,74 @@ describe('', () => { ); }); await waitForElement(wrapper, 'OrganizationDetail'); - expect(wrapper.find('OrganizationDetail Button').length).toBe(0); + expect( + wrapper.find('OrganizationDetail Button[aria-label="Edit"]').length + ).toBe(0); + }); + + test('expected api calls are made for delete', async () => { + OrganizationsAPI.readInstanceGroups.mockResolvedValue({ data: {} }); + + let wrapper; + await act(async () => { + wrapper = mountWithContexts( + + ); + }); + await waitForElement( + wrapper, + 'OrganizationDetail Button[aria-label="Delete"]' + ); + await act(async () => { + wrapper.find('DeleteButton').invoke('onConfirm')(); + }); + expect(OrganizationsAPI.destroy).toHaveBeenCalledTimes(1); + }); + + test('should show content error for failed instance group fetch', async () => { + OrganizationsAPI.readInstanceGroups.mockImplementationOnce(() => + Promise.reject(new Error()) + ); + + let wrapper; + await act(async () => { + wrapper = mountWithContexts( + + ); + }); + await waitForElement(wrapper, 'ContentError', el => el.length === 1); + }); + + test('Error dialog shown for failed deletion', async () => { + OrganizationsAPI.destroy.mockImplementationOnce(() => + Promise.reject(new Error()) + ); + + let wrapper; + await act(async () => { + wrapper = mountWithContexts( + + ); + }); + await waitForElement( + wrapper, + 'OrganizationDetail Button[aria-label="Delete"]' + ); + await act(async () => { + wrapper.find('DeleteButton').invoke('onConfirm')(); + }); + await waitForElement( + wrapper, + 'Modal[title="Error!"]', + el => el.length === 1 + ); + await act(async () => { + wrapper.find('Modal[title="Error!"]').invoke('onClose')(); + }); + await waitForElement( + wrapper, + 'Modal[title="Error!"]', + el => el.length === 0 + ); }); });