Add delete button to team details

This commit is contained in:
Marliana Lara 2020-01-23 17:53:08 -05:00
parent 145476c7d9
commit 2fae523fd4
No known key found for this signature in database
GPG Key ID: 38C73B40DFA809EE
2 changed files with 111 additions and 22 deletions

View File

@ -1,17 +1,40 @@
import React from 'react';
import { Link, useParams } from 'react-router-dom';
import React, { useState } from 'react';
import { Link, useHistory, useParams } from 'react-router-dom';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import { Button } from '@patternfly/react-core';
import AlertModal from '@components/AlertModal';
import { CardBody, CardActionsRow } from '@components/Card';
import ContentLoading from '@components/ContentLoading';
import DeleteButton from '@components/DeleteButton';
import { DetailList, Detail } from '@components/DetailList';
import ErrorDetail from '@components/ErrorDetail';
import { formatDateString } from '@util/dates';
import { TeamsAPI } from '@api';
function TeamDetail({ team, i18n }) {
const { name, description, created, modified, summary_fields } = team;
const [deletionError, setDeletionError] = useState(null);
const [hasContentLoading, setHasContentLoading] = useState(false);
const history = useHistory();
const { id } = useParams();
const handleDelete = async () => {
setHasContentLoading(true);
try {
await TeamsAPI.destroy(id);
history.push(`/teams`);
} catch (error) {
setDeletionError(error);
}
setHasContentLoading(false);
};
if (hasContentLoading) {
return <ContentLoading />;
}
return (
<CardBody>
<DetailList>
@ -36,12 +59,38 @@ function TeamDetail({ team, i18n }) {
/>
</DetailList>
<CardActionsRow>
{summary_fields.user_capabilities.edit && (
<Button component={Link} to={`/teams/${id}/edit`}>
{i18n._(t`Edit`)}
</Button>
)}
{summary_fields.user_capabilities &&
summary_fields.user_capabilities.edit && (
<Button
aria-label={i18n._(t`Edit`)}
component={Link}
to={`/teams/${id}/edit`}
>
{i18n._(t`Edit`)}
</Button>
)}
{summary_fields.user_capabilities &&
summary_fields.user_capabilities.delete && (
<DeleteButton
name={name}
modalTitle={i18n._(t`Delete Team`)}
onConfirm={handleDelete}
>
{i18n._(t`Delete`)}
</DeleteButton>
)}
</CardActionsRow>
{deletionError && (
<AlertModal
isOpen={deletionError}
variant="danger"
title={i18n._(t`Error!`)}
onClose={() => setDeletionError(null)}
>
{i18n._(t`Failed to delete team.`)}
<ErrorDetail error={deletionError} />
</AlertModal>
)}
</CardBody>
);
}

View File

@ -1,12 +1,13 @@
import React from 'react';
import { act } from 'react-dom/test-utils';
import { mountWithContexts, waitForElement } from '@testUtils/enzymeHelpers';
import TeamDetail from './TeamDetail';
import { TeamsAPI } from '@api';
jest.mock('@api');
describe('<TeamDetail />', () => {
let wrapper;
const mockTeam = {
name: 'Foo',
description: 'Bar',
@ -19,15 +20,25 @@ describe('<TeamDetail />', () => {
},
user_capabilities: {
edit: true,
delete: true,
},
},
};
test('initially renders succesfully', () => {
mountWithContexts(<TeamDetail team={mockTeam} />);
beforeEach(async () => {
wrapper = mountWithContexts(<TeamDetail team={mockTeam} />);
await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
});
test('should render Details', async done => {
const wrapper = mountWithContexts(<TeamDetail team={mockTeam} />);
afterEach(() => {
wrapper.unmount();
});
test('initially renders succesfully', async () => {
await waitForElement(wrapper, 'TeamDetail');
});
test('should render Details', async () => {
const testParams = [
{ label: 'Name', value: 'Foo' },
{ label: 'Description', value: 'Bar' },
@ -42,23 +53,52 @@ describe('<TeamDetail />', () => {
expect(detail.find('dt').text()).toBe(label);
expect(detail.find('dd').text()).toBe(value);
}
done();
});
test('should show edit button for users with edit permission', async done => {
const wrapper = mountWithContexts(<TeamDetail team={mockTeam} />);
const editButton = await waitForElement(wrapper, 'TeamDetail Button');
test('should show edit button for users with edit permission', async () => {
const editButton = await waitForElement(
wrapper,
'TeamDetail Button[aria-label="Edit"]'
);
expect(editButton.text()).toEqual('Edit');
expect(editButton.prop('to')).toBe('/teams/undefined/edit');
done();
});
test('should hide edit button for users without edit permission', async done => {
test('should hide edit button for users without edit permission', async () => {
const readOnlyTeam = { ...mockTeam };
readOnlyTeam.summary_fields.user_capabilities.edit = false;
const wrapper = mountWithContexts(<TeamDetail team={readOnlyTeam} />);
wrapper = mountWithContexts(<TeamDetail team={readOnlyTeam} />);
await waitForElement(wrapper, 'TeamDetail');
expect(wrapper.find('TeamDetail Button').length).toBe(0);
done();
expect(wrapper.find('TeamDetail Button[aria-label="Edit"]').length).toBe(0);
});
test('expected api call is made for delete', async () => {
await waitForElement(wrapper, 'TeamDetail Button[aria-label="Delete"]');
await act(async () => {
wrapper.find('DeleteButton').invoke('onConfirm')();
});
expect(TeamsAPI.destroy).toHaveBeenCalledTimes(1);
});
test('Error dialog shown for failed deletion', async () => {
TeamsAPI.destroy.mockImplementationOnce(() => Promise.reject(new Error()));
wrapper = mountWithContexts(<TeamDetail team={mockTeam} />);
await waitForElement(wrapper, 'TeamDetail 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
);
});
});