Skip to content

Commit

Permalink
[Reporting] Disable Delete button after click (elastic#173707)
Browse files Browse the repository at this point in the history
## Summary

This PR takes care of an issue in Stack Management Reporting where the
Delete button must be disabled after click to avoid accidental
double-clicking

### Release note
Fixed a bug in Stack Management Reporting where the Delete button was
not disabled after click.

### Checklist

Delete any items that are not applicable to this PR.

- [x] Any text added follows [EUI's writing
guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses
sentence case text and includes [i18n
support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)
- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios

---------

Co-authored-by: Kibana Machine <[email protected]>
  • Loading branch information
tsullivan and kibanamachine authored Dec 27, 2023
1 parent 121fb3a commit 4f9dc10
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 9 deletions.
3 changes: 0 additions & 3 deletions x-pack/plugins/reporting/public/management/__test__/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,5 @@
* 2.0.
*/

export { mockJobs } from '../../../common/test';

export { setup } from './report_listing.test.helpers';

export type { TestBed, TestDependencies } from './report_listing.test.helpers';
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import React from 'react';
import * as Rx from 'rxjs';
import {
fireEvent,
render,
findByTestId as findItByTestId,
findByText as findItByText,
} from '@testing-library/react';

import { mockJobs } from '../../../common/test';
import type { Job } from '../../lib/job';
import { ReportDeleteButton } from './report_delete_button';

describe('ReportDeleteButton', () => {
it('renders prompt modal for single selected report', async () => {
const deletePerformed$ = new Rx.Subject<void>();
const performDelete = jest.fn().mockResolvedValue(Rx.firstValueFrom(deletePerformed$));
const jobs = [mockJobs[0].payload] as Job[];

const { findByTestId } = render(
<ReportDeleteButton jobsToDelete={jobs} performDelete={performDelete} />
);
const getDeleteReportButton = () => findByTestId('deleteReportButton');

expect(await getDeleteReportButton()).not.toBeDisabled();
fireEvent.click(await getDeleteReportButton());
expect(await getDeleteReportButton()).toBeDisabled();

const modalElem = await findByTestId('deleteReportConfirm');
expect(
await findItByText(modalElem, 'Delete the "My Canvas Workpad" report?')
).toBeInTheDocument();
const getConfirmButton = () => findItByTestId(modalElem, 'confirmModalConfirmButton');

expect(await getConfirmButton()).not.toBeDisabled();
fireEvent.click(await getConfirmButton());
expect(await getConfirmButton()).toBeDisabled();

deletePerformed$.next();

expect(await getConfirmButton()).not.toBeDisabled();
});

it('renders prompt modal for multiple selected reports', async () => {
const deletePerformed$ = new Rx.Subject<void>();
const performDelete = jest.fn().mockResolvedValue(Rx.firstValueFrom(deletePerformed$));
const jobs = [mockJobs[0].payload, mockJobs[1].payload] as Job[];

const { findByTestId } = render(
<ReportDeleteButton jobsToDelete={jobs} performDelete={performDelete} />
);
const getDeleteReportButton = () => findByTestId('deleteReportButton');

expect(await getDeleteReportButton()).not.toBeDisabled();
fireEvent.click(await getDeleteReportButton());
expect(await getDeleteReportButton()).toBeDisabled();

const modalElem = await findByTestId('deleteReportConfirm');
expect(await findItByText(modalElem, 'Delete the 2 selected reports?')).toBeInTheDocument();
const getConfirmButton = () => findItByTestId(modalElem, 'confirmModalConfirmButton');

expect(await getConfirmButton()).not.toBeDisabled();
fireEvent.click(await getConfirmButton());
expect(await getConfirmButton()).toBeDisabled();

deletePerformed$.next();

expect(await getConfirmButton()).not.toBeDisabled();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,23 @@ import { EuiButton, EuiConfirmModal } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import React, { Fragment, PureComponent } from 'react';
import { Job } from '../../lib/job';
import { ListingProps } from '..';

type DeleteFn = () => Promise<void>;
type Props = { jobsToDelete: Job[]; performDelete: DeleteFn } & ListingProps;

interface Props {
jobsToDelete: Job[];
performDelete: DeleteFn;
}

interface State {
isDeleting: boolean;
showConfirm: boolean;
}

export class ReportDeleteButton extends PureComponent<Props, State> {
constructor(props: Props) {
super(props);
this.state = { showConfirm: false };
this.state = { isDeleting: false, showConfirm: false };
}

private hideConfirm() {
Expand All @@ -31,13 +36,27 @@ export class ReportDeleteButton extends PureComponent<Props, State> {
this.setState({ showConfirm: true });
}

private performDelete() {
this.setState({ isDeleting: true });
this.props
.performDelete()
.then(() => {
this.setState({ isDeleting: false });
})
.catch((err) => {
// eslint-disable-next-line no-console
console.error(err);
this.setState({ isDeleting: false });
});
}

private renderConfirm() {
const { jobsToDelete } = this.props;

const title =
jobsToDelete.length > 1
? i18n.translate('xpack.reporting.listing.table.deleteNumConfirmTitle', {
defaultMessage: `Delete {num} reports?`,
defaultMessage: `Delete the {num} selected reports?`,
values: { num: jobsToDelete.length },
})
: i18n.translate('xpack.reporting.listing.table.deleteConfirmTitle', {
Expand All @@ -58,11 +77,13 @@ export class ReportDeleteButton extends PureComponent<Props, State> {
<EuiConfirmModal
title={title}
onCancel={() => this.hideConfirm()}
onConfirm={() => this.props.performDelete()}
onConfirm={() => this.performDelete()}
confirmButtonText={confirmButtonText}
confirmButtonDisabled={this.state.isDeleting}
cancelButtonText={cancelButtonText}
defaultFocusedButton="confirm"
buttonColor="danger"
data-test-subj="deleteReportConfirm"
>
{message}
</EuiConfirmModal>
Expand All @@ -80,6 +101,7 @@ export class ReportDeleteButton extends PureComponent<Props, State> {
iconType="trash"
color={'danger'}
data-test-subj="deleteReportButton"
disabled={this.state.showConfirm}
>
{i18n.translate('xpack.reporting.listing.table.deleteReportButton', {
defaultMessage: `Delete {num, plural, one {report} other {reports} }`,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ import type { ILicense } from '@kbn/licensing-plugin/public';
import { IlmPolicyMigrationStatus } from '@kbn/reporting-common/types';

import { ListingProps as Props } from '.';
import { mockJobs } from '../../common/test';
import { Job } from '../lib/job';
import { TestBed, TestDependencies, mockJobs, setup } from './__test__';
import { TestBed, TestDependencies, setup } from './__test__';
import { mockConfig } from './__test__/report_listing.test.helpers';

describe('ReportListing', () => {
Expand Down

0 comments on commit 4f9dc10

Please sign in to comment.