From b1a87efa69af0942914e0339761e0728206a9752 Mon Sep 17 00:00:00 2001 From: Lin Wang Date: Thu, 22 Aug 2024 17:12:43 +0800 Subject: [PATCH] Add submitting lock for workspace create page Signed-off-by: Lin Wang --- .../workspace_create_action_panel.test.tsx | 16 ++++++++++ .../workspace_create_action_panel.tsx | 6 +++- .../workspace_creator.test.tsx | 29 +++++++++++++++++++ .../workspace_creator/workspace_creator.tsx | 12 ++++++-- .../workspace_creator_form.test.tsx | 1 + .../workspace_creator_form.tsx | 7 ++++- 6 files changed, 67 insertions(+), 4 deletions(-) diff --git a/src/plugins/workspace/public/components/workspace_creator/workspace_create_action_panel.test.tsx b/src/plugins/workspace/public/components/workspace_creator/workspace_create_action_panel.test.tsx index 6f1dbc58bf9e..3fd6e78ac7d1 100644 --- a/src/plugins/workspace/public/components/workspace_creator/workspace_create_action_panel.test.tsx +++ b/src/plugins/workspace/public/components/workspace_creator/workspace_create_action_panel.test.tsx @@ -27,6 +27,7 @@ describe('WorkspaceCreateActionPanel', () => { formId={formId} formData={{ name: longName, description: formData.description }} application={mockApplication} + isSubmitting={false} /> ); const createButton = screen.getByText('Create workspace'); @@ -40,6 +41,7 @@ describe('WorkspaceCreateActionPanel', () => { formId={formId} formData={{ name: formData.name, description: longDescription }} application={mockApplication} + isSubmitting={false} /> ); const createButton = screen.getByText('Create workspace'); @@ -52,9 +54,23 @@ describe('WorkspaceCreateActionPanel', () => { formId={formId} formData={formData} application={mockApplication} + isSubmitting={false} /> ); const createButton = screen.getByText('Create workspace'); expect(createButton.closest('button')).not.toBeDisabled(); }); + + it('should disable the "Create Workspace" and "Cancel" button when submitting', () => { + render( + + ); + expect(screen.getByText('Create workspace').closest('button')).toBeDisabled(); + expect(screen.getByText('Cancel').closest('button')).toBeDisabled(); + }); }); diff --git a/src/plugins/workspace/public/components/workspace_creator/workspace_create_action_panel.tsx b/src/plugins/workspace/public/components/workspace_creator/workspace_create_action_panel.tsx index 7fb6e8d512cc..782196e2bbc7 100644 --- a/src/plugins/workspace/public/components/workspace_creator/workspace_create_action_panel.tsx +++ b/src/plugins/workspace/public/components/workspace_creator/workspace_create_action_panel.tsx @@ -17,12 +17,14 @@ interface WorkspaceCreateActionPanelProps { formId: string; formData: Pick; application: ApplicationStart; + isSubmitting: boolean; } export const WorkspaceCreateActionPanel = ({ formId, formData, application, + isSubmitting, }: WorkspaceCreateActionPanelProps) => { const [isCancelModalVisible, setIsCancelModalVisible] = useState(false); const closeCancelModal = useCallback(() => setIsCancelModalVisible(false), []); @@ -38,6 +40,7 @@ export const WorkspaceCreateActionPanel = ({ {i18n.translate('workspace.form.right.sidebar.buttons.cancelText', { defaultMessage: 'Cancel', @@ -50,7 +53,8 @@ export const WorkspaceCreateActionPanel = ({ form={formId} data-test-subj="workspaceForm-bottomBar-createButton" fill - disabled={createButtonDisabled} + disabled={createButtonDisabled || isSubmitting} + isLoading={isSubmitting} > {i18n.translate('workspace.form.right.sidebar.buttons.createWorkspaceText', { defaultMessage: 'Create workspace', diff --git a/src/plugins/workspace/public/components/workspace_creator/workspace_creator.test.tsx b/src/plugins/workspace/public/components/workspace_creator/workspace_creator.test.tsx index 760c4060de58..083c1d61c4de 100644 --- a/src/plugins/workspace/public/components/workspace_creator/workspace_creator.test.tsx +++ b/src/plugins/workspace/public/components/workspace_creator/workspace_creator.test.tsx @@ -344,4 +344,33 @@ describe('WorkspaceCreator', () => { }); expect(notificationToastsAddDanger).not.toHaveBeenCalled(); }); + + it('should not create workspace API when submitting', async () => { + workspaceClientCreate.mockImplementationOnce( + () => + new Promise((resolve) => { + setTimeout(resolve, 100); + }) + ); + const { getByTestId } = render(); + // Ensure workspace create form rendered + await waitFor(() => { + expect(getByTestId('workspaceForm-bottomBar-createButton')).toBeInTheDocument(); + }); + const nameInput = getByTestId('workspaceForm-workspaceDetails-nameInputText'); + fireEvent.input(nameInput, { + target: { value: 'test workspace name' }, + }); + fireEvent.click(getByTestId('workspaceUseCase-observability')); + fireEvent.click(getByTestId('workspaceForm-bottomBar-createButton')); + expect(workspaceClientCreate).toHaveBeenCalledTimes(1); + + fireEvent.click(getByTestId('workspaceForm-bottomBar-createButton')); + expect(workspaceClientCreate).toHaveBeenCalledTimes(1); + + await waitFor(() => { + fireEvent.click(getByTestId('workspaceForm-bottomBar-createButton')); + expect(workspaceClientCreate).toHaveBeenCalledTimes(2); + }); + }); }); diff --git a/src/plugins/workspace/public/components/workspace_creator/workspace_creator.tsx b/src/plugins/workspace/public/components/workspace_creator/workspace_creator.tsx index 8f99ba4731ee..9a96137c143f 100644 --- a/src/plugins/workspace/public/components/workspace_creator/workspace_creator.tsx +++ b/src/plugins/workspace/public/components/workspace_creator/workspace_creator.tsx @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import React, { useCallback } from 'react'; +import React, { useCallback, useState } from 'react'; import { EuiPage, EuiPageBody, EuiPageContent, euiPaletteColorBlind } from '@elastic/eui'; import { i18n } from '@osd/i18n'; import { BehaviorSubject } from 'rxjs'; @@ -43,6 +43,7 @@ export const WorkspaceCreator = (props: WorkspaceCreatorProps) => { dataSourceManagement?: DataSourceManagementPluginSetup; navigationUI: NavigationPublicPluginStart['ui']; }>(); + const [isFormSubmitting, setIsFormSubmitting] = useState(false); const isPermissionEnabled = application?.capabilities.workspaces.permissionEnabled; const { isOnlyAllowEssential, availableUseCases } = useFormAvailableUseCases({ @@ -65,6 +66,10 @@ export const WorkspaceCreator = (props: WorkspaceCreatorProps) => { const handleWorkspaceFormSubmit = useCallback( async (data: WorkspaceFormSubmitData) => { let result; + if (isFormSubmitting) { + return; + } + setIsFormSubmitting(true); try { const { permissionSettings, selectedDataSources, ...attributes } = data; const selectedDataSourceIds = (selectedDataSources ?? []).map((ds: DataSource) => { @@ -105,9 +110,11 @@ export const WorkspaceCreator = (props: WorkspaceCreatorProps) => { text: error instanceof Error ? error.message : JSON.stringify(error), }); return; + } finally { + setIsFormSubmitting(false); } }, - [notifications?.toasts, http, application, workspaceClient] + [notifications?.toasts, http, application, workspaceClient, isFormSubmitting] ); const isFormReadyToRender = @@ -146,6 +153,7 @@ export const WorkspaceCreator = (props: WorkspaceCreatorProps) => { dataSourceManagement={dataSourceManagement} availableUseCases={availableUseCases} defaultValues={defaultWorkspaceFormValues} + isSubmitting={isFormSubmitting} /> )} diff --git a/src/plugins/workspace/public/components/workspace_creator/workspace_creator_form.test.tsx b/src/plugins/workspace/public/components/workspace_creator/workspace_creator_form.test.tsx index 81251685fb3e..fbbf4443c9ec 100644 --- a/src/plugins/workspace/public/components/workspace_creator/workspace_creator_form.test.tsx +++ b/src/plugins/workspace/public/components/workspace_creator/workspace_creator_form.test.tsx @@ -38,6 +38,7 @@ const setup = ( return render( { +interface WorkspaceCreatorFormProps extends WorkspaceFormProps { + isSubmitting: boolean; +} + +export const WorkspaceCreatorForm = (props: WorkspaceCreatorFormProps) => { const { application, savedObjects, @@ -217,6 +221,7 @@ export const WorkspaceCreatorForm = (props: WorkspaceFormProps) => { formData={formData} formId={formId} application={application} + isSubmitting={props.isSubmitting} />