diff --git a/x-pack/plugins/spaces/public/management/components/confirm_delete_modal/__snapshots__/confirm_delete_modal.test.tsx.snap b/x-pack/plugins/spaces/public/management/components/confirm_delete_modal/__snapshots__/confirm_delete_modal.test.tsx.snap deleted file mode 100644 index 5bf93a1021c05..0000000000000 --- a/x-pack/plugins/spaces/public/management/components/confirm_delete_modal/__snapshots__/confirm_delete_modal.test.tsx.snap +++ /dev/null @@ -1,93 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`ConfirmDeleteModal renders as expected 1`] = ` - - - - - - - - -

- - - , - } - } - /> -

- - - -
-
- - - - - - - - -
-`; diff --git a/x-pack/plugins/spaces/public/management/components/confirm_delete_modal/confirm_delete_modal.scss b/x-pack/plugins/spaces/public/management/components/confirm_delete_modal/confirm_delete_modal.scss deleted file mode 100644 index 887495a231485..0000000000000 --- a/x-pack/plugins/spaces/public/management/components/confirm_delete_modal/confirm_delete_modal.scss +++ /dev/null @@ -1,3 +0,0 @@ -.spcConfirmDeleteModal { - max-width: $euiFormMaxWidth + ($euiSizeL * 2); -} diff --git a/x-pack/plugins/spaces/public/management/components/confirm_delete_modal/confirm_delete_modal.test.tsx b/x-pack/plugins/spaces/public/management/components/confirm_delete_modal/confirm_delete_modal.test.tsx index 36d282a1cd653..2fe85998fcfb2 100644 --- a/x-pack/plugins/spaces/public/management/components/confirm_delete_modal/confirm_delete_modal.test.tsx +++ b/x-pack/plugins/spaces/public/management/components/confirm_delete_modal/confirm_delete_modal.test.tsx @@ -6,10 +6,10 @@ */ import React from 'react'; +import { act } from 'react-dom/test-utils'; import { mountWithIntl, shallowWithIntl } from '@kbn/test/jest'; -import type { SpacesManager } from '../../../spaces_manager'; import { spacesManagerMock } from '../../../spaces_manager/mocks'; import { ConfirmDeleteModal } from './confirm_delete_modal'; @@ -22,25 +22,53 @@ describe('ConfirmDeleteModal', () => { }; const spacesManager = spacesManagerMock.create(); - spacesManager.getActiveSpace.mockResolvedValue(space); - const onCancel = jest.fn(); - const onConfirm = jest.fn(); expect( shallowWithIntl( - + ) - ).toMatchSnapshot(); + ).toMatchInlineSnapshot(` + + +

+ + + , + } + } + /> +

+

+ +

+
+
+ `); }); - it(`requires the space name to be typed before confirming`, () => { + it('deletes the space when confirmed', async () => { const space = { id: 'my-space', name: 'My Space', @@ -48,34 +76,23 @@ describe('ConfirmDeleteModal', () => { }; const spacesManager = spacesManagerMock.create(); - spacesManager.getActiveSpace.mockResolvedValue(space); - const onCancel = jest.fn(); - const onConfirm = jest.fn(); + const onSuccess = jest.fn(); const wrapper = mountWithIntl( - ); - const input = wrapper.find('input'); - expect(input).toHaveLength(1); - - input.simulate('change', { target: { value: 'My Invalid Space Name ' } }); - - const confirmButton = wrapper.find('button[data-test-subj="confirmModalConfirmButton"]'); - confirmButton.simulate('click'); - - expect(onConfirm).not.toHaveBeenCalled(); - - input.simulate('change', { target: { value: 'My Space' } }); - confirmButton.simulate('click'); + await act(async () => { + wrapper.find('EuiButton[data-test-subj="confirmModalConfirmButton"]').simulate('click'); + await spacesManager.deleteSpace.mock.results[0]; + }); - expect(onConfirm).toHaveBeenCalledTimes(1); + expect(spacesManager.deleteSpace).toHaveBeenLastCalledWith(space); }); }); diff --git a/x-pack/plugins/spaces/public/management/components/confirm_delete_modal/confirm_delete_modal.tsx b/x-pack/plugins/spaces/public/management/components/confirm_delete_modal/confirm_delete_modal.tsx index 100b5b6493e30..7cbe3ab7a71dc 100644 --- a/x-pack/plugins/spaces/public/management/components/confirm_delete_modal/confirm_delete_modal.tsx +++ b/x-pack/plugins/spaces/public/management/components/confirm_delete_modal/confirm_delete_modal.tsx @@ -5,224 +5,127 @@ * 2.0. */ -import './confirm_delete_modal.scss'; - -import type { CommonProps, EuiModalProps } from '@elastic/eui'; -import { - EuiButton, - EuiButtonEmpty, - EuiCallOut, - EuiFieldText, - EuiFormRow, - EuiModal, - EuiModalBody, - EuiModalFooter, - EuiModalHeader, - EuiModalHeaderTitle, - EuiSpacer, - EuiText, -} from '@elastic/eui'; -import type { ChangeEvent } from 'react'; -import React, { Component } from 'react'; - -import type { InjectedIntl } from '@kbn/i18n/react'; -import { FormattedMessage, injectI18n } from '@kbn/i18n/react'; +import { EuiCallOut, EuiConfirmModal, EuiSpacer, EuiText } from '@elastic/eui'; +import type { FunctionComponent } from 'react'; +import React from 'react'; +import useAsync from 'react-use/lib/useAsync'; +import useAsyncFn from 'react-use/lib/useAsyncFn'; + +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; import type { Space } from 'src/plugins/spaces_oss/common'; +import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; import type { SpacesManager } from '../../../spaces_manager'; interface Props { space: Space; spacesManager: SpacesManager; - onCancel: () => void; - onConfirm: () => void; - intl: InjectedIntl; -} - -interface State { - confirmSpaceName: string; - error: boolean | null; - deleteInProgress: boolean; - isDeletingCurrentSpace: boolean; + onCancel(): void; + onSuccess?(): void; } -class ConfirmDeleteModalUI extends Component { - public state = { - confirmSpaceName: '', - error: null, - deleteInProgress: false, - isDeletingCurrentSpace: false, - }; - - public componentDidMount() { - isCurrentSpace(this.props.space, this.props.spacesManager).then((result) => { - this.setState({ - isDeletingCurrentSpace: result, - }); - }); - } - - public render() { - const { space, onCancel, intl } = this.props; - const { isDeletingCurrentSpace } = this.state; - - let warning = null; - if (isDeletingCurrentSpace) { - const name = ( - - ({space.name}) - - ); - warning = ( - <> - - - - - - - +export const ConfirmDeleteModal: FunctionComponent = ({ + space, + onSuccess, + onCancel, + spacesManager, +}) => { + const { services } = useKibana(); + + const { value: isCurrentSpace } = useAsync( + async () => space.id === (await spacesManager.getActiveSpace()).id, + [space.id] + ); + + const [state, deleteSpace] = useAsyncFn(async () => { + try { + await spacesManager.deleteSpace(space); + services.notifications!.toasts.addSuccess( + i18n.translate('xpack.spaces.management.confirmDeleteModal.successMessage', { + defaultMessage: "Deleted space '{name}'", + values: { name: space.name }, + }) ); - } - - // This is largely the same as the built-in EuiConfirmModal component, but we needed the ability - // to disable the buttons since this could be a long-running operation - - const modalProps: Omit & CommonProps = { - onClose: onCancel, - className: 'spcConfirmDeleteModal', - initialFocus: 'input[name="confirmDeleteSpaceInput"]', - }; - - return ( - - - - - - - - -

- - - - ), - }} - /> -

- - - - - - {warning} -
-
- - - - - - - - - -
- ); - } - - private onSpaceNameChange = (e: ChangeEvent) => { - if (typeof this.state.error === 'boolean') { - this.setState({ - confirmSpaceName: e.target.value, - error: e.target.value !== this.props.space.name, - }); - } else { - this.setState({ - confirmSpaceName: e.target.value, - }); - } - }; - - private onConfirm = async () => { - if (this.state.confirmSpaceName === this.props.space.name) { - const needsRedirect = this.state.isDeletingCurrentSpace; - const spacesManager = this.props.spacesManager; - - this.setState({ - deleteInProgress: true, - }); - - await this.props.onConfirm(); - - this.setState({ - deleteInProgress: false, - }); - - if (needsRedirect) { + if (isCurrentSpace) { spacesManager.redirectToSpaceSelector(); + } else { + onSuccess?.(); } - } else { - this.setState({ - error: true, + } catch (error) { + services.notifications!.toasts.addDanger({ + title: i18n.translate('xpack.spaces.management.confirmDeleteModal.errorMessage', { + defaultMessage: "Could not delete space '{name}'", + values: { name: space.name }, + }), + text: (error as any).body?.message || error.message, }); } - }; -} - -async function isCurrentSpace(space: Space, spacesManager: SpacesManager) { - return space.id === (await spacesManager.getActiveSpace()).id; -} - -export const ConfirmDeleteModal = injectI18n(ConfirmDeleteModalUI); + }); + + return ( + + {isCurrentSpace && ( + <> + + + + + + )} + +

+ + + + ), + }} + /> +

+

+ +

+
+
+ ); +}; diff --git a/x-pack/plugins/spaces/public/management/edit_space/delete_spaces_button.tsx b/x-pack/plugins/spaces/public/management/edit_space/delete_spaces_button.tsx index d03b878cb19ab..92b68426d172e 100644 --- a/x-pack/plugins/spaces/public/management/edit_space/delete_spaces_button.tsx +++ b/x-pack/plugins/spaces/public/management/edit_space/delete_spaces_button.tsx @@ -95,44 +95,13 @@ export class DeleteSpacesButton extends Component { showConfirmDeleteModal: false, }); }} - onConfirm={this.deleteSpaces} + onSuccess={() => { + this.setState({ + showConfirmDeleteModal: false, + }); + this.props.onDelete?.(); + }} /> ); }; - - public deleteSpaces = async () => { - const { spacesManager, space } = this.props; - - this.setState({ - showConfirmDeleteModal: false, - }); - - try { - await spacesManager.deleteSpace(space); - } catch (error) { - const { message: errorMessage = '' } = error.data || error.body || {}; - - this.props.notifications.toasts.addDanger( - i18n.translate('xpack.spaces.management.deleteSpacesButton.deleteSpaceErrorTitle', { - defaultMessage: 'Error deleting space: {errorMessage}', - values: { errorMessage }, - }) - ); - return; - } - - const message = i18n.translate( - 'xpack.spaces.management.deleteSpacesButton.spaceSuccessfullyDeletedNotificationMessage', - { - defaultMessage: 'Deleted {spaceName} space.', - values: { spaceName: space.name }, - } - ); - - this.props.notifications.toasts.addSuccess(message); - - if (this.props.onDelete) { - this.props.onDelete(); - } - }; } diff --git a/x-pack/plugins/spaces/public/management/spaces_grid/__snapshots__/spaces_grid_pages.test.tsx.snap b/x-pack/plugins/spaces/public/management/spaces_grid/__snapshots__/spaces_grid_page.test.tsx.snap similarity index 100% rename from x-pack/plugins/spaces/public/management/spaces_grid/__snapshots__/spaces_grid_pages.test.tsx.snap rename to x-pack/plugins/spaces/public/management/spaces_grid/__snapshots__/spaces_grid_page.test.tsx.snap diff --git a/x-pack/plugins/spaces/public/management/spaces_grid/spaces_grid_pages.test.tsx b/x-pack/plugins/spaces/public/management/spaces_grid/spaces_grid_page.test.tsx similarity index 100% rename from x-pack/plugins/spaces/public/management/spaces_grid/spaces_grid_pages.test.tsx rename to x-pack/plugins/spaces/public/management/spaces_grid/spaces_grid_page.test.tsx diff --git a/x-pack/plugins/spaces/public/management/spaces_grid/spaces_grid_page.tsx b/x-pack/plugins/spaces/public/management/spaces_grid/spaces_grid_page.tsx index ac57a566e2a00..a4f797e441ab5 100644 --- a/x-pack/plugins/spaces/public/management/spaces_grid/spaces_grid_page.tsx +++ b/x-pack/plugins/spaces/public/management/spaces_grid/spaces_grid_page.tsx @@ -180,53 +180,16 @@ export class SpacesGridPage extends Component { showConfirmDeleteModal: false, }); }} - onConfirm={this.deleteSpace} + onSuccess={() => { + this.setState({ + showConfirmDeleteModal: false, + }); + this.loadGrid(); + }} /> ); }; - public deleteSpace = async () => { - const { spacesManager } = this.props; - - const space = this.state.selectedSpace; - - if (!space) { - return; - } - - this.setState({ - showConfirmDeleteModal: false, - }); - - try { - await spacesManager.deleteSpace(space); - } catch (error) { - const { message: errorMessage = '' } = error.data || error.body || {}; - - this.props.notifications.toasts.addDanger( - i18n.translate('xpack.spaces.management.spacesGridPage.errorDeletingSpaceErrorMessage', { - defaultMessage: 'Error deleting space: {errorMessage}', - values: { - errorMessage, - }, - }) - ); - return; - } - - this.loadGrid(); - - const message = i18n.translate( - 'xpack.spaces.management.spacesGridPage.spaceSuccessfullyDeletedNotificationMessage', - { - defaultMessage: 'Deleted "{spaceName}" space.', - values: { spaceName: space.name }, - } - ); - - this.props.notifications.toasts.addSuccess(message); - }; - public loadGrid = async () => { const { spacesManager, getFeatures, notifications } = this.props; diff --git a/x-pack/plugins/spaces/public/management/spaces_management_app.test.tsx b/x-pack/plugins/spaces/public/management/spaces_management_app.test.tsx index 76467bd838a10..23db78f6b8065 100644 --- a/x-pack/plugins/spaces/public/management/spaces_management_app.test.tsx +++ b/x-pack/plugins/spaces/public/management/spaces_management_app.test.tsx @@ -87,6 +87,7 @@ describe('spacesManagementApp', () => { > Spaces Page: {"capabilities":{"catalogue":{},"management":{},"navLinks":{}},"notifications":{"toasts":{}},"spacesManager":{"onActiveSpaceChange$":{"_isScalar":false}},"history":{"action":"PUSH","length":1,"location":{"pathname":"/","search":"","hash":""}}} + `); @@ -113,6 +114,7 @@ describe('spacesManagementApp', () => { > Spaces Edit Page: {"capabilities":{"catalogue":{},"management":{},"navLinks":{}},"notifications":{"toasts":{}},"spacesManager":{"onActiveSpaceChange$":{"_isScalar":false}},"history":{"action":"PUSH","length":1,"location":{"pathname":"/create","search":"","hash":""}}} + `); @@ -145,6 +147,7 @@ describe('spacesManagementApp', () => { > Spaces Edit Page: {"capabilities":{"catalogue":{},"management":{},"navLinks":{}},"notifications":{"toasts":{}},"spacesManager":{"onActiveSpaceChange$":{"_isScalar":false}},"spaceId":"some-space","history":{"action":"PUSH","length":1,"location":{"pathname":"/edit/some-space","search":"","hash":""}}} + `); diff --git a/x-pack/plugins/spaces/public/management/spaces_management_app.tsx b/x-pack/plugins/spaces/public/management/spaces_management_app.tsx index da0f9157f310d..24b626c7c00e8 100644 --- a/x-pack/plugins/spaces/public/management/spaces_management_app.tsx +++ b/x-pack/plugins/spaces/public/management/spaces_management_app.tsx @@ -14,7 +14,10 @@ import type { StartServicesAccessor } from 'src/core/public'; import type { RegisterManagementAppArgs } from 'src/plugins/management/public'; import type { Space } from 'src/plugins/spaces_oss/common'; -import { RedirectAppLinks } from '../../../../../src/plugins/kibana_react/public'; +import { + KibanaContextProvider, + RedirectAppLinks, +} from '../../../../../src/plugins/kibana_react/public'; import type { PluginsStart } from '../plugin'; import type { SpacesManager } from '../spaces_manager'; @@ -36,22 +39,23 @@ export const spacesManagementApp = Object.freeze({ title, async mount({ element, setBreadcrumbs, history }) { - const [startServices, { SpacesGridPage }, { ManageSpacePage }] = await Promise.all([ + const [ + [coreStart, { features }], + { SpacesGridPage }, + { ManageSpacePage }, + ] = await Promise.all([ getStartServices(), import('./spaces_grid'), import('./edit_space'), ]); - const [ - { notifications, i18n: i18nStart, application, chrome }, - { features }, - ] = startServices; const spacesBreadcrumbs = [ { text: title, href: `/`, }, ]; + const { notifications, i18n: i18nStart, application, chrome } = coreStart; chrome.docTitle.change(title); @@ -119,23 +123,25 @@ export const spacesManagementApp = Object.freeze({ }; render( - - - - - - - - - - - - - - - - - , + + + + + + + + + + + + + + + + + + {' '} + , element );