Skip to content

Commit

Permalink
Update PipelineResources page to use Table batch actions
Browse files Browse the repository at this point in the history
Move the delete functionality for the PipelineResources page
to Carbon DataTable batch action which appears in the table
header instead of using overflow menus on each individual row.
  • Loading branch information
AlanGreene committed Feb 19, 2020
1 parent 35364d9 commit 14a877c
Show file tree
Hide file tree
Showing 6 changed files with 100 additions and 71 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,15 @@ import { injectIntl } from 'react-intl';
import { Link } from 'react-router-dom';
import { urls } from '@tektoncd/dashboard-utils';

import { FormattedDate, RunDropdown, Table } from '..';
import { FormattedDate, Table } from '..';

const PipelineResources = ({
batchActionButtons,
createPipelineResourcesURL = urls.pipelineResources.byName,
createPipelineResourceDisplayName = ({ pipelineResourceMetadata }) =>
pipelineResourceMetadata.name,
intl,
loading,
pipelineResourceActions,
pipelineResources,
selectedNamespace,
toolbarButtons
Expand Down Expand Up @@ -57,10 +57,6 @@ const PipelineResources = ({
id: 'dashboard.tableHeader.createdTime',
defaultMessage: 'Created'
})
},
{
key: 'actions',
header: ''
}
];

Expand Down Expand Up @@ -94,18 +90,13 @@ const PipelineResources = ({
date={pipelineResource.metadata.creationTimestamp}
relative
/>
),
actions: (
<RunDropdown
items={pipelineResourceActions}
resource={pipelineResource}
/>
)
};
});

return (
<Table
batchActionButtons={batchActionButtons}
headers={headers}
rows={pipelineResourcesFormatted}
loading={loading}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ limitations under the License.
import React from 'react';
import { fireEvent, waitForElement } from 'react-testing-library';
import { createIntl } from 'react-intl';
import { Delete16 } from '@carbon/icons-react';
import { renderWithIntl, renderWithRouter } from '../../utils/test';
import PipelineResources from './PipelineResources';

Expand Down Expand Up @@ -42,15 +43,17 @@ it('PipelineResources renders headers state', () => {

it('PipelineResources renders correct data', async () => {
const pipelineResourceName = 'pipeline-resource-20190816124708';
const { queryByText, getByTestId, getByText } = renderWithRouter(
const batchDeleteSpy = jest.fn();
const { queryByText, getByLabelText, getByText } = renderWithRouter(
<PipelineResources
intl={intl}
pipelineResources={[
{
metadata: {
name: pipelineResourceName,
namespace: 'default-namespace',
type: 'git'
type: 'git',
uid: '15269df7-0b1e-4a04-a9ea-f47f7da20fa4'
},
spec: {
params: [
Expand All @@ -67,19 +70,19 @@ it('PipelineResources renders correct data', async () => {
}
}
]}
pipelineResourceActions={[
batchActionButtons={[
{
actionText: intl.formatMessage({
id: 'test.actionText',
defaultMessage: 'TestAction'
})
onClick: batchDeleteSpy,
text: 'Delete',
icon: Delete16
}
]}
/>
);
expect(queryByText(pipelineResourceName)).toBeTruthy();
expect(queryByText(/default-namespace/i)).toBeTruthy();
expect(queryByText(/git/i)).toBeTruthy();
fireEvent.click(await waitForElement(() => getByTestId('overflowmenu')));
await waitForElement(() => getByText(/TestAction/i));
fireEvent.click(await waitForElement(() => getByLabelText(/select row/i)));
await waitForElement(() => getByText(/Delete/i));
expect(getByText(/1 item selected/i)).toBeTruthy();
});
2 changes: 0 additions & 2 deletions packages/components/src/components/Table/Table.scss
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

@import '~@tektoncd/dashboard-components/dist/scss/vars';

.tableComponent {
.bx--table-toolbar {
background: transparent;
Expand Down
127 changes: 83 additions & 44 deletions src/containers/PipelineResources/PipelineResources.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,21 @@ import { Link } from 'react-router-dom';
import { connect } from 'react-redux';
import { injectIntl } from 'react-intl';
import isEqual from 'lodash.isequal';
import keyBy from 'lodash.keyby';
import { getErrorMessage, getFilters, urls } from '@tektoncd/dashboard-utils';
import { PipelineResources as PipelineResourcesList } from '@tektoncd/dashboard-components';
import { InlineNotification } from 'carbon-components-react';
import { Add16 as Add } from '@carbon/icons-react';
import {
InlineNotification,
ListItem,
Modal,
UnorderedList
} from 'carbon-components-react';
import { Add16 as Add, Delete16 as Delete } from '@carbon/icons-react';

import { LabelFilter } from '..';
import { fetchPipelineResources } from '../../actions/pipelineResources';
import { deletePipelineResource } from '../../api';
import PipelineResourcesModal from '../PipelineResourcesModal';

import {
getPipelineResources,
getPipelineResourcesErrorMessage,
Expand All @@ -37,7 +42,9 @@ import {
const initialState = {
showCreatePipelineResourceModal: false,
createdPipelineResource: null,
submitError: ''
submitError: '',
isDeleteModalOpen: false,
toBeDeleted: []
};

export /* istanbul ignore next */ class PipelineResources extends Component {
Expand Down Expand Up @@ -80,9 +87,36 @@ export /* istanbul ignore next */ class PipelineResources extends Component {
this.setState({ createdPipelineResource: false });
};

openDeleteModal = (selectedRows, cancelSelection) => {
const pipelineResourcesById = keyBy(
this.props.pipelineResources,
'metadata.uid'
);
const toBeDeleted = selectedRows.map(({ id }) => pipelineResourcesById[id]);

this.setState({ isDeleteModalOpen: true, toBeDeleted, cancelSelection });
};

closeDeleteModal = () => {
this.setState({
isDeleteModalOpen: false,
toBeDeleted: []
});
};

handleDelete = async () => {
const { cancelSelection, toBeDeleted } = this.state;
const deletions = toBeDeleted.map(resource =>
this.deleteResource(resource)
);
this.closeDeleteModal();
await Promise.all(deletions);
cancelSelection();
};

deleteResource = pipelineResource => {
const { name, namespace } = pipelineResource.metadata;
deletePipelineResource({ name, namespace }).catch(error => {
return deletePipelineResource({ name, namespace }).catch(error => {
error.response.text().then(text => {
const statusCode = error.response.status;
let errorMessage = `error code ${statusCode}`;
Expand All @@ -94,43 +128,6 @@ export /* istanbul ignore next */ class PipelineResources extends Component {
});
};

pipelineResourceActions = () => {
const { intl } = this.props;
return [
{
actionText: intl.formatMessage({
id: 'dashboard.actions.deleteButton',
defaultMessage: 'Delete'
}),
action: this.deleteResource,
modalProperties: {
danger: true,
heading: intl.formatMessage({
id: 'dashboard.deletePipelineResource.heading',
defaultMessage: 'Delete PipelineResource'
}),
primaryButtonText: intl.formatMessage({
id: 'dashboard.deletePipelineResource.primaryText',
defaultMessage: 'Delete PipelineResource'
}),
secondaryButtonText: intl.formatMessage({
id: 'dashboard.modal.cancelButton',
defaultMessage: 'Cancel'
}),
body: resource =>
intl.formatMessage(
{
id: 'dashboard.deletePipelineResource.body',
defaultMessage:
'Are you sure you would like to delete PipelineResource {name}?'
},
{ name: resource.metadata.name }
)
}
}
];
};

handleCreatePipelineResourceClick = showCreatePipelineResourceModal => {
if (showCreatePipelineResourceModal) {
this.setState({
Expand Down Expand Up @@ -168,7 +165,7 @@ export /* istanbul ignore next */ class PipelineResources extends Component {
intl
} = this.props;

const pipelineResourceActions = this.pipelineResourceActions();
const { isDeleteModalOpen, toBeDeleted } = this.state;

if (error) {
return (
Expand Down Expand Up @@ -231,9 +228,18 @@ export /* istanbul ignore next */ class PipelineResources extends Component {
namespace={selectedNamespace}
/>
<PipelineResourcesList
batchActionButtons={[
{
onClick: this.openDeleteModal,
text: intl.formatMessage({
id: 'dashboard.actions.deleteButton',
defaultMessage: 'Delete'
}),
icon: Delete
}
]}
loading={loading}
pipelineResources={pipelineResources}
pipelineResourceActions={pipelineResourceActions}
selectedNamespace={selectedNamespace}
toolbarButtons={[
{
Expand All @@ -246,6 +252,39 @@ export /* istanbul ignore next */ class PipelineResources extends Component {
}
]}
/>
<Modal
open={isDeleteModalOpen}
primaryButtonText={intl.formatMessage({
id: 'dashboard.actions.deleteButton',
defaultMessage: 'Delete'
})}
secondaryButtonText={intl.formatMessage({
id: 'dashboard.modal.cancelButton',
defaultMessage: 'Cancel'
})}
modalHeading={intl.formatMessage({
id: 'dashboard.pipelineResources.deleteHeading',
defaultMessage: 'Delete PipelineResources'
})}
onSecondarySubmit={this.closeDeleteModal}
onRequestSubmit={this.handleDelete}
onRequestClose={this.closeDeleteModal}
danger
>
<p>
{intl.formatMessage({
id: 'dashboard.pipelineResources.deleteConfirm',
defaultMessage:
'Are you sure you want to delete these PipelineResources?'
})}
</p>
<UnorderedList nested>
{toBeDeleted.map(pipelineResource => {
const { name, namespace } = pipelineResource.metadata;
return <ListItem key={`${name}:${namespace}`}>{name}</ListItem>;
})}
</UnorderedList>
</Modal>
</>
);
}
Expand Down
1 change: 0 additions & 1 deletion src/containers/Secrets/Secrets.js
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,6 @@ export /* istanbul ignore next */ class Secrets extends Component {
headers={initialHeaders}
rows={secretsFormatted}
handleDisplayModal={this.handleDisplayModalClick}
handleDelete={this.handleDeleteSecretClick}
loading={loading}
selectedNamespace={selectedNamespace}
emptyTextAllNamespaces={intl.formatMessage(
Expand Down
5 changes: 2 additions & 3 deletions src/nls/messages_en.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,6 @@
"dashboard.createPipelineRun.timeoutLabel": "Timeout",
"dashboard.createPipelineRun.validationError": "Please fix the fields with errors, then resubmit",
"dashboard.customResourceDefinition.errorLoading": "Error loading resource",
"dashboard.deletePipelineResource.body": "Are you sure you would like to delete PipelineResource {name}?",
"dashboard.deletePipelineResource.heading": "Delete PipelineResource",
"dashboard.deletePipelineResource.primaryText": "Delete PipelineResource",
"dashboard.deletePipelineRun.body": "Are you sure you would like to delete PipelineRun {name}?",
"dashboard.deletePipelineRun.heading": "Delete PipelineRun",
"dashboard.deleteTaskRun.body": "Are you sure you would like to delete TaskRun {name}?",
Expand Down Expand Up @@ -105,6 +102,8 @@
"dashboard.pipelineResource.secretKey": "Secret Key",
"dashboard.pipelineResource.secretName": "Secret Name",
"dashboard.pipelineResources.createSuccess": "Successfully created PipelineResource",
"dashboard.pipelineResources.deleteConfirm": "Are you sure you want to delete these PipelineResources?",
"dashboard.pipelineResources.deleteHeading": "Delete PipelineResources",
"dashboard.pipelineResources.error": "Error loading PipelineResources",
"dashboard.pipelineResourcesModal.heading": "Create PipelineResource",
"dashboard.pipelineRun.error": "Error loading PipelineRun",
Expand Down

0 comments on commit 14a877c

Please sign in to comment.