From 76d7a8bbf7af1ec220e4b67f71e482bbc804a8d6 Mon Sep 17 00:00:00 2001 From: Lin Wang Date: Fri, 30 Aug 2024 10:46:40 +0800 Subject: [PATCH] [Workspace]Add right sidebar to workspace create form (#7750) * Move workspace form inside workspace creator Signed-off-by: Lin Wang * Remove enter details panel Signed-off-by: Lin Wang * Rename workspace_form to workspace_creator_form Signed-off-by: Lin Wang * Clarify workspace form types Signed-off-by: Lin Wang * Add right sidebar to workspace creator Signed-off-by: Lin Wang * Add submitting lock for workspace create page Signed-off-by: Lin Wang * Changeset file for PR #7750 created/updated * Fix form submitting lock not been covered Signed-off-by: Lin Wang --------- Signed-off-by: Lin Wang Co-authored-by: opensearch-changeset-bot[bot] <154024398+opensearch-changeset-bot[bot]@users.noreply.github.com> --- changelogs/fragments/7750.yml | 2 + .../workspace_faq_panel.test.tsx.snap | 66 +++++ .../components/workspace_creator/utils.ts | 19 ++ .../workspace_create_action_panel.test.tsx | 16 ++ .../workspace_create_action_panel.tsx | 25 +- .../workspace_creator.test.tsx | 25 ++ .../workspace_creator/workspace_creator.tsx | 20 +- .../workspace_creator_form.scss | 31 +++ .../workspace_creator_form.test.tsx} | 11 +- .../workspace_creator_form.tsx | 236 ++++++++++++++++++ .../workspace_faq_panel.test.tsx | 15 ++ .../workspace_creator/workspace_faq_panel.tsx | 72 ++++++ .../workspace_form_summary_panel.test.tsx | 182 ++++++++++++++ .../workspace_form_summary_panel.tsx | 193 ++++++++++++++ .../components/workspace_form/fields/index.ts | 7 + .../public/components/workspace_form/index.ts | 24 +- .../public/components/workspace_form/types.ts | 27 +- .../workspace_form/use_workspace_form.test.ts | 4 +- .../workspace_form/use_workspace_form.ts | 11 +- .../public/components/workspace_form/utils.ts | 16 +- .../workspace_form/workspace_detail_form.tsx | 7 +- .../workspace_enter_details_panel.tsx | 72 ------ .../workspace_form/workspace_form.tsx | 152 ----------- .../workspace_form/workspace_use_case.tsx | 1 - 24 files changed, 952 insertions(+), 282 deletions(-) create mode 100644 changelogs/fragments/7750.yml create mode 100644 src/plugins/workspace/public/components/workspace_creator/__snapshots__/workspace_faq_panel.test.tsx.snap create mode 100644 src/plugins/workspace/public/components/workspace_creator/utils.ts rename src/plugins/workspace/public/components/{workspace_form => workspace_creator}/workspace_create_action_panel.test.tsx (80%) rename src/plugins/workspace/public/components/{workspace_form => workspace_creator}/workspace_create_action_panel.tsx (71%) create mode 100644 src/plugins/workspace/public/components/workspace_creator/workspace_creator_form.scss rename src/plugins/workspace/public/components/{workspace_form/workspace_form.test.tsx => workspace_creator/workspace_creator_form.test.tsx} (89%) create mode 100644 src/plugins/workspace/public/components/workspace_creator/workspace_creator_form.tsx create mode 100644 src/plugins/workspace/public/components/workspace_creator/workspace_faq_panel.test.tsx create mode 100644 src/plugins/workspace/public/components/workspace_creator/workspace_faq_panel.tsx create mode 100644 src/plugins/workspace/public/components/workspace_creator/workspace_form_summary_panel.test.tsx create mode 100644 src/plugins/workspace/public/components/workspace_creator/workspace_form_summary_panel.tsx create mode 100644 src/plugins/workspace/public/components/workspace_form/fields/index.ts delete mode 100644 src/plugins/workspace/public/components/workspace_form/workspace_enter_details_panel.tsx delete mode 100644 src/plugins/workspace/public/components/workspace_form/workspace_form.tsx diff --git a/changelogs/fragments/7750.yml b/changelogs/fragments/7750.yml new file mode 100644 index 000000000000..a9ec74c2cdc9 --- /dev/null +++ b/changelogs/fragments/7750.yml @@ -0,0 +1,2 @@ +feat: +- [Workspace]Add right sidebar to workspace create form ([#7750](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/7750)) \ No newline at end of file diff --git a/src/plugins/workspace/public/components/workspace_creator/__snapshots__/workspace_faq_panel.test.tsx.snap b/src/plugins/workspace/public/components/workspace_creator/__snapshots__/workspace_faq_panel.test.tsx.snap new file mode 100644 index 000000000000..9fe60a2ab6e1 --- /dev/null +++ b/src/plugins/workspace/public/components/workspace_creator/__snapshots__/workspace_faq_panel.test.tsx.snap @@ -0,0 +1,66 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`WorkspaceFaqPanel renders correctly 1`] = ` +
+
+
+

+ FAQs +

+
+
+

+ Can I change the workspace use case later? +

+

+ You can only change to the All use case after workspace creation. +

+
+
+
+
+

+ Why can’t I find the data sources I want to attached to the workspace? +

+

+ Available data sources to all workspaces here are configured by OpenSearch admin. Contact OpenSearch admin within your organization to add the requested data source. +

+
+
+
+
+

+ Do the added team members automatically gain access to the attached data sources? +

+

+ No. Adding team members will only grant them access to the created workspace. To grant access to the attached data sources, contact the data source admin within your organization. +

+
+
+
+
+
+`; diff --git a/src/plugins/workspace/public/components/workspace_creator/utils.ts b/src/plugins/workspace/public/components/workspace_creator/utils.ts new file mode 100644 index 000000000000..9be647c1a30b --- /dev/null +++ b/src/plugins/workspace/public/components/workspace_creator/utils.ts @@ -0,0 +1,19 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +export const RIGHT_SIDEBAR_SCROLL_KEY = 'data-right-sidebar-scroll'; + +export enum RightSidebarScrollField { + Name = 'name', + Description = 'description', + Color = 'color', + UseCase = 'useCase', + DataSource = 'dataSource', + Member = 'member', +} + +export const generateRightSidebarScrollProps = (key: RightSidebarScrollField) => { + return { [RIGHT_SIDEBAR_SCROLL_KEY]: key }; +}; diff --git a/src/plugins/workspace/public/components/workspace_form/workspace_create_action_panel.test.tsx b/src/plugins/workspace/public/components/workspace_creator/workspace_create_action_panel.test.tsx similarity index 80% rename from src/plugins/workspace/public/components/workspace_form/workspace_create_action_panel.test.tsx rename to src/plugins/workspace/public/components/workspace_creator/workspace_create_action_panel.test.tsx index 6f1dbc58bf9e..3fd6e78ac7d1 100644 --- a/src/plugins/workspace/public/components/workspace_form/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_form/workspace_create_action_panel.tsx b/src/plugins/workspace/public/components/workspace_creator/workspace_create_action_panel.tsx similarity index 71% rename from src/plugins/workspace/public/components/workspace_form/workspace_create_action_panel.tsx rename to src/plugins/workspace/public/components/workspace_creator/workspace_create_action_panel.tsx index 0b914c0a7658..5f59cbb9587b 100644 --- a/src/plugins/workspace/public/components/workspace_form/workspace_create_action_panel.tsx +++ b/src/plugins/workspace/public/components/workspace_creator/workspace_create_action_panel.tsx @@ -3,12 +3,11 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { EuiSmallButton, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { EuiSmallButton, EuiFlexGroup, EuiFlexItem, EuiSmallButtonEmpty } from '@elastic/eui'; import { i18n } from '@osd/i18n'; import React, { useState, useCallback } from 'react'; import type { ApplicationStart } from 'opensearch-dashboards/public'; -import type { WorkspaceFormData } from './types'; -import { WorkspaceCancelModal } from './workspace_cancel_modal'; +import { WorkspaceFormDataState, WorkspaceCancelModal } from '../workspace_form'; import { MAX_WORKSPACE_DESCRIPTION_LENGTH, MAX_WORKSPACE_NAME_LENGTH, @@ -16,14 +15,16 @@ import { interface WorkspaceCreateActionPanelProps { formId: string; - formData: Partial>; + formData: Pick; application: ApplicationStart; + isSubmitting: boolean; } export const WorkspaceCreateActionPanel = ({ formId, formData, application, + isSubmitting, }: WorkspaceCreateActionPanelProps) => { const [isCancelModalVisible, setIsCancelModalVisible] = useState(false); const closeCancelModal = useCallback(() => setIsCancelModalVisible(false), []); @@ -34,26 +35,28 @@ export const WorkspaceCreateActionPanel = ({ return ( <> - + - - {i18n.translate('workspace.form.bottomBar.cancel', { + {i18n.translate('workspace.form.right.sidebar.buttons.cancelText', { defaultMessage: 'Cancel', })} - + - {i18n.translate('workspace.form.bottomBar.createWorkspace', { + {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..9200ea7cfa07 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,29 @@ 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(); + }); + fireEvent.click(getByTestId('workspaceForm-bottomBar-createButton')); + expect(workspaceClientCreate).toHaveBeenCalledTimes(1); + + // Since create button was been disabled, fire form submit event by form directly + fireEvent.submit(getByTestId('workspaceCreatorForm')); + 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 a7a2b247914a..3ba8b7850753 100644 --- a/src/plugins/workspace/public/components/workspace_creator/workspace_creator.tsx +++ b/src/plugins/workspace/public/components/workspace_creator/workspace_creator.tsx @@ -3,13 +3,13 @@ * 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'; import { useOpenSearchDashboards } from '../../../../opensearch_dashboards_react/public'; -import { WorkspaceForm, WorkspaceFormSubmitData, WorkspaceOperationType } from '../workspace_form'; +import { WorkspaceFormSubmitData, WorkspaceOperationType } from '../workspace_form'; import { WORKSPACE_DETAIL_APP_ID } from '../../../common/constants'; import { formatUrlWithWorkspaceId } from '../../../../../core/public/utils'; import { WorkspaceClient } from '../../workspace_client'; @@ -17,10 +17,10 @@ import { convertPermissionSettingsToPermissions } from '../workspace_form'; import { DataSource } from '../../../common/types'; import { DataSourceManagementPluginSetup } from '../../../../../plugins/data_source_management/public'; import { WorkspaceUseCase } from '../../types'; -import { WorkspaceFormData } from '../workspace_form/types'; import { getUseCaseFeatureConfig } from '../../utils'; import { useFormAvailableUseCases } from '../workspace_form/use_form_available_use_cases'; import { NavigationPublicPluginStart } from '../../../../../plugins/navigation/public'; +import { WorkspaceCreatorForm } from './workspace_creator_form'; export interface WorkspaceCreatorProps { registeredUseCases$: BehaviorSubject; @@ -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({ @@ -52,7 +53,7 @@ export const WorkspaceCreator = (props: WorkspaceCreatorProps) => { }); const defaultSelectedUseCase = availableUseCases?.[0]; - const defaultWorkspaceFormValues: Partial = { + const defaultWorkspaceFormValues: Partial = { color: euiPaletteColorBlind()[0], ...(defaultSelectedUseCase ? { @@ -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 = @@ -137,7 +144,7 @@ export const WorkspaceCreator = (props: WorkspaceCreatorProps) => { hasShadow={false} > {isFormReadyToRender && ( - { dataSourceManagement={dataSourceManagement} availableUseCases={availableUseCases} defaultValues={defaultWorkspaceFormValues} + isSubmitting={isFormSubmitting} /> )} diff --git a/src/plugins/workspace/public/components/workspace_creator/workspace_creator_form.scss b/src/plugins/workspace/public/components/workspace_creator/workspace_creator_form.scss new file mode 100644 index 000000000000..010406b8797a --- /dev/null +++ b/src/plugins/workspace/public/components/workspace_creator/workspace_creator_form.scss @@ -0,0 +1,31 @@ +$workspaceCreateRightSideBarTopOffset: 116px; +$workspaceCreateRightSideBarBottomOffset: 100px; + +.workspaceCreateRightSidebar { + position: sticky; + top: $workspaceCreateRightSideBarTopOffset; + max-height: calc(100vh - $workspaceCreateRightSideBarTopOffset - $workspaceCreateRightSideBarBottomOffset); + overflow: hidden; + display: flex; + flex-direction: column; + width: 280px; + + @include ouiBreakpoint("xs","s") { + position: static; + width: 100%; + } +} + +.workspaceCreateRightSideBarContentWrapper { + overflow-y: scroll; + + @include ouiBreakpoint("xs","s") { + overflow: visible; + } +} + +.workspaceCreateRightSideBarActionsWrapper { + padding: $ouiSizeM; + border-radius: $ouiSizeM; + background: $ouiColorEmptyShade; +} diff --git a/src/plugins/workspace/public/components/workspace_form/workspace_form.test.tsx b/src/plugins/workspace/public/components/workspace_creator/workspace_creator_form.test.tsx similarity index 89% rename from src/plugins/workspace/public/components/workspace_form/workspace_form.test.tsx rename to src/plugins/workspace/public/components/workspace_creator/workspace_creator_form.test.tsx index 66279a68a1b6..95ca5bf948c5 100644 --- a/src/plugins/workspace/public/components/workspace_form/workspace_form.test.tsx +++ b/src/plugins/workspace/public/components/workspace_creator/workspace_creator_form.test.tsx @@ -8,8 +8,8 @@ import { fireEvent, render } from '@testing-library/react'; import { coreMock } from '../../../../../core/public/mocks'; import { DataSourceManagementPluginSetup } from '../../../../../plugins/data_source_management/public'; import { createMockedRegisteredUseCases } from '../../mocks'; -import { WorkspaceOperationType } from './constants'; -import { WorkspaceForm } from './workspace_form'; +import { WorkspaceOperationType } from '../workspace_form'; +import { WorkspaceCreatorForm } from './workspace_creator_form'; const mockCoreStart = coreMock.createStart(); @@ -37,7 +37,8 @@ const setup = ( }; return render( - { it('should enable data source panel for dashboard admin and when data source is enabled', () => { const { getByText } = setup(true, mockDataSourceManagementSetup); - expect(getByText('Associate data source')).toBeInTheDocument(); + expect(getByText('Associate data sources')).toBeInTheDocument(); }); it('should not display data source panel for non dashboard admin', () => { const { queryByText } = setup(false, mockDataSourceManagementSetup); - expect(queryByText('Associate data source')).not.toBeInTheDocument(); + expect(queryByText('Associate data sources')).not.toBeInTheDocument(); }); it('should not display data source panel when data source is disabled', () => { diff --git a/src/plugins/workspace/public/components/workspace_creator/workspace_creator_form.tsx b/src/plugins/workspace/public/components/workspace_creator/workspace_creator_form.tsx new file mode 100644 index 000000000000..9ab0a35e722b --- /dev/null +++ b/src/plugins/workspace/public/components/workspace_creator/workspace_creator_form.tsx @@ -0,0 +1,236 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import React, { useCallback, useRef } from 'react'; +import { + EuiSpacer, + EuiTitle, + EuiForm, + EuiText, + EuiCompressedFormRow, + EuiColorPicker, + EuiFlexItem, + EuiFlexGroup, +} from '@elastic/eui'; +import { i18n } from '@osd/i18n'; +import { + useWorkspaceForm, + WorkspacePermissionSettingPanel, + WorkspaceUseCase, + WorkspaceFormErrorCallout, + SelectDataSourcePanel, + usersAndPermissionsCreatePageTitle, + WorkspaceFormProps, + WorkspaceNameField, + WorkspaceDescriptionField, +} from '../workspace_form'; + +import { WorkspaceCreateActionPanel } from './workspace_create_action_panel'; +import { WorkspaceFaqPanel } from './workspace_faq_panel'; +import { WorkspaceFormSummaryPanel } from './workspace_form_summary_panel'; +import { generateRightSidebarScrollProps, RightSidebarScrollField } from './utils'; + +import './workspace_creator_form.scss'; + +interface WorkspaceCreatorFormProps extends WorkspaceFormProps { + isSubmitting: boolean; +} + +export const WorkspaceCreatorForm = (props: WorkspaceCreatorFormProps) => { + const { + application, + savedObjects, + defaultValues, + permissionEnabled, + dataSourceManagement: isDataSourceEnabled, + availableUseCases, + } = props; + const { + formId, + formData, + formErrors, + numberOfErrors, + setName, + setDescription, + handleFormSubmit, + handleColorChange, + handleUseCaseChange: handleUseCaseChangeInHook, + setPermissionSettings, + setSelectedDataSources, + } = useWorkspaceForm(props); + const nameManualChangedRef = useRef(false); + + const disabledUserOrGroupInputIdsRef = useRef( + defaultValues?.permissionSettings?.map((item) => item.id) ?? [] + ); + const isDashboardAdmin = application?.capabilities?.dashboards?.isDashboardAdmin ?? false; + const handleNameInputChange = useCallback( + (newName) => { + setName(newName); + nameManualChangedRef.current = true; + }, + [setName] + ); + const handleUseCaseChange = useCallback( + (newUseCase) => { + handleUseCaseChangeInHook(newUseCase); + const useCase = availableUseCases.find((item) => newUseCase === item.id); + if (!nameManualChangedRef.current && useCase) { + setName(useCase.title); + } + }, + [handleUseCaseChangeInHook, availableUseCases, setName] + ); + + return ( + + + + {numberOfErrors > 0 && ( + <> + + + + )} + +

+ {i18n.translate('workspace.creator.form.customizeTitle', { + defaultMessage: 'Customize the workspace', + })} +

+
+
+ +
+ +
+ + +
+ + + +
+ + {i18n.translate('workspace.form.workspaceDetails.color.description', { + defaultMessage: + 'Select a background color for the icon representing this workspace.', + })} + + + +
+
+ + {/* SelectDataSourcePanel is only visible for dashboard admin and when data source is enabled*/} + {isDashboardAdmin && isDataSourceEnabled && ( + <> + +

+ {i18n.translate('workspace.creator.form.associateDataSourceTitle', { + defaultMessage: 'Associate data sources', + })} +

+
+ + {i18n.translate('workspace.creator.form.associateDataSourceDescription', { + defaultMessage: + 'Add data sources that will be available in the workspace. If a selected OpenSearch connection has embedded Direct Query connection, they will also be available in the workspace.', + })} + + + + + + )} + {permissionEnabled && ( + <> + +

{usersAndPermissionsCreatePageTitle}

+
+ + {i18n.translate('workspace.creator.form.usersAndPermissionsDescription', { + defaultMessage: + 'You will be added as an owner to the workspace. Select additional users and user groups as workspace collaborators with different access levels.', + })} + + + + + )} + + + +
+
+ + + +
+ +
+ +
+
+
+ + ); +}; diff --git a/src/plugins/workspace/public/components/workspace_creator/workspace_faq_panel.test.tsx b/src/plugins/workspace/public/components/workspace_creator/workspace_faq_panel.test.tsx new file mode 100644 index 000000000000..e8d737b32242 --- /dev/null +++ b/src/plugins/workspace/public/components/workspace_creator/workspace_faq_panel.test.tsx @@ -0,0 +1,15 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import React from 'react'; +import { render } from '@testing-library/react'; +import { WorkspaceFaqPanel } from './workspace_faq_panel'; + +describe('WorkspaceFaqPanel', () => { + it('renders correctly', () => { + const tree = render(); + expect(tree.container).toMatchSnapshot(); + }); +}); diff --git a/src/plugins/workspace/public/components/workspace_creator/workspace_faq_panel.tsx b/src/plugins/workspace/public/components/workspace_creator/workspace_faq_panel.tsx new file mode 100644 index 000000000000..218e9a7618cf --- /dev/null +++ b/src/plugins/workspace/public/components/workspace_creator/workspace_faq_panel.tsx @@ -0,0 +1,72 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import React from 'react'; +import { EuiCard, EuiSpacer, EuiText } from '@elastic/eui'; +import { i18n } from '@osd/i18n'; + +const WorkspaceFaqItem = ({ question, answer }: { question: string; answer: string }) => { + return ( + +

{question}

+

{answer}

+
+ ); +}; + +const FAQs = [ + { + question: i18n.translate('workspace.form.faq.panel.question1', { + defaultMessage: 'Can I change the workspace use case later?', + }), + answer: i18n.translate('workspace.form.faq.panel.answer1', { + defaultMessage: 'You can only change to the All use case after workspace creation.', + }), + }, + { + question: i18n.translate('workspace.form.faq.panel.question2', { + defaultMessage: 'Why can’t I find the data sources I want to attached to the workspace? ', + }), + answer: i18n.translate('workspace.form.faq.panel.answer2', { + defaultMessage: + 'Available data sources to all workspaces here are configured by OpenSearch admin. Contact OpenSearch admin within your organization to add the requested data source. ', + }), + }, + { + question: i18n.translate('workspace.form.faq.panel.question3', { + defaultMessage: + 'Do the added team members automatically gain access to the attached data sources?', + }), + answer: i18n.translate('workspace.form.faq.panel.answer3', { + defaultMessage: + 'No. Adding team members will only grant them access to the created workspace. To grant access to the attached data sources, contact the data source admin within your organization.', + }), + }, +]; + +export const WorkspaceFaqPanel = () => { + return ( + + {FAQs.map(({ question, answer }, index) => ( + + + {index !== FAQs.length - 1 && ( + <> + + + + )} + + ))} + + ); +}; diff --git a/src/plugins/workspace/public/components/workspace_creator/workspace_form_summary_panel.test.tsx b/src/plugins/workspace/public/components/workspace_creator/workspace_form_summary_panel.test.tsx new file mode 100644 index 000000000000..4db2d9365ab9 --- /dev/null +++ b/src/plugins/workspace/public/components/workspace_creator/workspace_form_summary_panel.test.tsx @@ -0,0 +1,182 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import React from 'react'; +import { render, screen, fireEvent } from '@testing-library/react'; +import { + WorkspaceFormSummaryPanel, + ExpandableTextList, + FieldSummaryItem, +} from './workspace_form_summary_panel'; +import { RightSidebarScrollField } from './utils'; +import { WorkspacePermissionItemType } from '../workspace_form'; + +describe('WorkspaceFormSummaryPanel', () => { + const formData = { + features: [], + useCase: 'useCase1', + name: 'Test Workspace', + description: 'This is a test workspace', + color: '#000000', + selectedDataSources: [ + { id: 'data-source-1', title: 'Data Source 1' }, + { id: 'data-source-2', title: 'Data Source 2' }, + { id: 'data-source-3', title: 'Data Source 3' }, + ], + permissionSettings: [ + { id: 1, type: WorkspacePermissionItemType.User, userId: 'user1' }, + { id: 2, type: WorkspacePermissionItemType.Group, group: 'group1' }, + { id: 3, type: WorkspacePermissionItemType.User, userId: 'user2' }, + ], + }; + + const availableUseCases = [ + { + id: 'useCase1', + title: 'Use Case 1', + description: 'This is Use Case 1', + features: [], + }, + { + id: 'useCase2', + title: 'Use Case 2', + description: 'This is Use Case 2', + features: [], + }, + ]; + + it('renders summary panel with correct data', () => { + render( + + ); + + expect(screen.getByText('Summary')).toBeInTheDocument(); + expect(screen.getByText('Use Case 1')).toBeInTheDocument(); + expect(screen.getByText('This is Use Case 1')).toBeInTheDocument(); + expect(screen.getByText('Test Workspace')).toBeInTheDocument(); + expect(screen.getByText('This is a test workspace')).toBeInTheDocument(); + expect(screen.getByText('#000000')).toBeInTheDocument(); + expect(screen.getByText('Data Source 1')).toBeInTheDocument(); + expect(screen.getByText('user1')).toBeInTheDocument(); + expect(screen.getByText('group1')).toBeInTheDocument(); + expect(screen.queryByText('user2')).toBeNull(); + }); + + it('renders placeholders for empty form data', () => { + render( + + ); + + expect(screen.getByText('Summary')).toBeInTheDocument(); + + // Use case placeholder + const useCasePlaceholder = screen.getByTestId('workspaceFormRightSideBarSummary-useCase-Value'); + expect(useCasePlaceholder).toHaveTextContent('—'); + + // Name placeholder + const namePlaceholder = screen.getByTestId('workspaceFormRightSideBarSummary-name-Value'); + expect(namePlaceholder).toHaveTextContent('—'); + + // Description placeholder + const descriptionPlaceholder = screen.getByTestId( + 'workspaceFormRightSideBarSummary-description-Value' + ); + expect(descriptionPlaceholder).toHaveTextContent('—'); + + // Color placeholder + const colorPlaceholder = screen.getByTestId('workspaceFormRightSideBarSummary-color-Value'); + expect(colorPlaceholder).toHaveTextContent('—'); + + // Data sources placeholder + const dataSourcesPlaceholder = screen.getByTestId( + 'workspaceFormRightSideBarSummary-dataSource-Value' + ); + expect(dataSourcesPlaceholder).toHaveTextContent('—'); + + // Permissions placeholder + const permissionsPlaceholder = screen.getByTestId( + 'workspaceFormRightSideBarSummary-member-Value' + ); + expect(permissionsPlaceholder).toHaveTextContent('—'); + }); +}); + +describe('ExpandableTextList', () => { + it('renders all texts when expanded', () => { + const texts = ['Text 1', 'Text 2', 'Text 3', 'Text 4']; + render(); + + expect(screen.getByText('Text 1')).toBeInTheDocument(); + expect(screen.getByText('Text 2')).toBeInTheDocument(); + expect(screen.queryByText('Text 3')).not.toBeInTheDocument(); + expect(screen.queryByText('Text 4')).not.toBeInTheDocument(); + + fireEvent.click(screen.getByText('Show all')); + + expect(screen.getByText('Text 3')).toBeInTheDocument(); + expect(screen.getByText('Text 4')).toBeInTheDocument(); + }); + it('should not show "Show all" button when all texts can be displayed', () => { + const texts = ['Text 1', 'Text 2']; + render(); + + expect(screen.getByText('Text 1')).toBeInTheDocument(); + expect(screen.getByText('Text 2')).toBeInTheDocument(); + expect(screen.queryByText('Show all')).not.toBeInTheDocument(); + }); +}); + +describe('FieldSummaryItem', () => { + it('renders title and content correctly', () => { + render( + Content for Name + ); + + expect(screen.getByText('Name')).toBeInTheDocument(); + expect(screen.getByText('Content for Name')).toBeInTheDocument(); + }); + + it('renders placeholder when no content is provided', () => { + render(); + + expect(screen.getByText('Name')).toBeInTheDocument(); + expect(screen.getByText('—')).toBeInTheDocument(); + }); + + it('scrolls to the corresponding field when title is clicked', () => { + const originScrollIntoView = window.HTMLElement.prototype.scrollIntoView; + const scrollIntoViewMock = jest.fn(); + window.HTMLElement.prototype.scrollIntoView = scrollIntoViewMock; + + render( +
+
+ Content for Name +
+ ); + + fireEvent.click(screen.getByText('Name')); + + expect(scrollIntoViewMock).toHaveBeenCalledWith({ + behavior: 'smooth', + block: 'center', + }); + window.HTMLElement.prototype.scrollIntoView = originScrollIntoView; + }); +}); diff --git a/src/plugins/workspace/public/components/workspace_creator/workspace_form_summary_panel.tsx b/src/plugins/workspace/public/components/workspace_creator/workspace_form_summary_panel.tsx new file mode 100644 index 000000000000..a16cad76ede7 --- /dev/null +++ b/src/plugins/workspace/public/components/workspace_creator/workspace_form_summary_panel.tsx @@ -0,0 +1,193 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import React, { useCallback, useState } from 'react'; +import { + EuiCard, + EuiFlexGroup, + EuiFlexItem, + EuiIcon, + EuiSpacer, + EuiText, + EuiTextColor, + EuiLink, +} from '@elastic/eui'; +import { i18n } from '@osd/i18n'; +import { WorkspaceFormDataState } from '../forms'; +import { WorkspaceUseCase } from '../../types'; +import { RightSidebarScrollField, RIGHT_SIDEBAR_SCROLL_KEY } from './utils'; + +const SCROLL_FIELDS = { + [RightSidebarScrollField.UseCase]: i18n.translate('workspace.form.summary.panel.useCase.title', { + defaultMessage: 'Use case', + }), + [RightSidebarScrollField.Name]: i18n.translate('workspace.form.summary.panel.name.title', { + defaultMessage: 'Name', + }), + [RightSidebarScrollField.Description]: i18n.translate( + 'workspace.form.summary.panel.description.title', + { + defaultMessage: 'Description', + } + ), + [RightSidebarScrollField.Color]: i18n.translate('workspace.form.summary.panel.color.title', { + defaultMessage: 'Accent color', + }), + [RightSidebarScrollField.DataSource]: i18n.translate( + 'workspace.form.summary.panel.dataSources.title', + { + defaultMessage: 'Data sources', + } + ), + [RightSidebarScrollField.Member]: i18n.translate('workspace.form.summary.panel.members.title', { + defaultMessage: 'Members', + }), +}; + +export const FieldSummaryItem = ({ + field, + children, + bottomGap = true, +}: React.PropsWithChildren<{ + field: RightSidebarScrollField; + bottomGap?: boolean; +}>) => { + const handleTitleClick = useCallback(() => { + const element = document.querySelector( + `.workspaceCreateFormContainer [${RIGHT_SIDEBAR_SCROLL_KEY}="${field}"]` + ); + + element?.scrollIntoView({ behavior: 'smooth', block: 'center' }); + }, [field]); + + return ( + <> + +
+ + {SCROLL_FIELDS[field]} + +
+
+ + + {!!children ? children : } + + {bottomGap && ( + <> + + + + )} + + ); +}; + +export const ExpandableTextList = ({ + texts, + collapseDisplayCount, +}: { + texts: string[]; + collapseDisplayCount: number; +}) => { + const [isExpanded, setIsExpanded] = useState(false); + const uniqueTexts = Array.from(new Set(texts)); + const displayedTexts = isExpanded ? uniqueTexts : uniqueTexts.slice(0, collapseDisplayCount); + return ( + <> + {displayedTexts.map((text) => ( + + {text} + + ))} + {uniqueTexts.length > collapseDisplayCount && ( + { + setIsExpanded((flag) => !flag); + }} + > + {isExpanded + ? i18n.translate('workspace.form.summary.members.showLess', { + defaultMessage: 'Show less', + }) + : i18n.translate('workspace.form.summary.members.showAll', { + defaultMessage: 'Show all', + })} + + )} + + ); +}; + +interface WorkspaceFormSummaryPanelProps { + formData: WorkspaceFormDataState; + availableUseCases: WorkspaceUseCase[]; + permissionEnabled?: boolean; +} + +export const WorkspaceFormSummaryPanel = ({ + formData, + availableUseCases, + permissionEnabled, +}: WorkspaceFormSummaryPanelProps) => { + const useCase = availableUseCases.find((item) => item.id === formData.useCase); + const userAndGroups = formData.permissionSettings.flatMap((setting) => { + if ('userId' in setting && !!setting.userId) { + return [setting.userId]; + } + if ('group' in setting && !!setting.group) { + return [setting.group]; + } + return []; + }); + + return ( + + + {useCase && ( + <> + {useCase.title} + {useCase.description} + + )} + + {formData.name} + + {formData.description?.trim()} + + + {formData.color && ( + + + + + + {formData.color} + + + )} + + + {formData.selectedDataSources.length > 0 && ( + title)} + collapseDisplayCount={2} + /> + )} + + {permissionEnabled && ( + + {userAndGroups.length > 0 && ( + + )} + + )} + + ); +}; diff --git a/src/plugins/workspace/public/components/workspace_form/fields/index.ts b/src/plugins/workspace/public/components/workspace_form/fields/index.ts new file mode 100644 index 000000000000..f9217e910213 --- /dev/null +++ b/src/plugins/workspace/public/components/workspace_form/fields/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +export { WorkspaceDescriptionField } from './workspace_description_field'; +export { WorkspaceNameField } from './workspace_name_field'; diff --git a/src/plugins/workspace/public/components/workspace_form/index.ts b/src/plugins/workspace/public/components/workspace_form/index.ts index 42164ca530e2..3c493bf4aa3c 100644 --- a/src/plugins/workspace/public/components/workspace_form/index.ts +++ b/src/plugins/workspace/public/components/workspace_form/index.ts @@ -3,12 +3,30 @@ * SPDX-License-Identifier: Apache-2.0 */ -export { WorkspaceForm } from './workspace_form'; export { WorkspaceDetailForm } from './workspace_detail_form'; -export { WorkspaceFormSubmitData } from './types'; -export { WorkspaceOperationType } from './constants'; +export { SelectDataSourcePanel } from './select_data_source_panel'; +export { WorkspaceFormErrorCallout } from './workspace_form_error_callout'; +export { WorkspaceUseCase } from './workspace_use_case'; +export { WorkspacePermissionSettingPanel } from './workspace_permission_setting_panel'; +export { WorkspaceCancelModal } from './workspace_cancel_modal'; +export { WorkspaceNameField, WorkspaceDescriptionField } from './fields'; + +export { WorkspaceFormSubmitData, WorkspaceFormProps, WorkspaceFormDataState } from './types'; +export { + WorkspaceOperationType, + DetailTab, + DetailTabTitles, + WorkspacePermissionItemType, + usersAndPermissionsCreatePageTitle, + selectDataSourceTitle, + workspaceDetailsTitle, + workspaceUseCaseTitle, +} from './constants'; export { convertPermissionsToPermissionSettings, convertPermissionSettingsToPermissions, } from './utils'; + export { WorkspaceFormProvider, useWorkspaceFormContext } from './workspace_form_context'; +export { useWorkspaceForm } from './use_workspace_form'; +export { useFormAvailableUseCases } from './use_form_available_use_cases'; diff --git a/src/plugins/workspace/public/components/workspace_form/types.ts b/src/plugins/workspace/public/components/workspace_form/types.ts index 5ec929e17b0c..2625047c2b69 100644 --- a/src/plugins/workspace/public/components/workspace_form/types.ts +++ b/src/plugins/workspace/public/components/workspace_form/types.ts @@ -31,17 +31,12 @@ export type WorkspacePermissionSetting = export interface WorkspaceFormSubmitData { name: string; description?: string; - features?: string[]; + features: string[]; color?: string; permissionSettings?: WorkspacePermissionSetting[]; selectedDataSources?: DataSource[]; } -export interface WorkspaceFormData extends WorkspaceFormSubmitData { - id: string; - reserved?: boolean; -} - export enum WorkspaceFormErrorCode { InvalidWorkspaceName, WorkspaceNameMissing, @@ -65,7 +60,7 @@ export interface WorkspaceFormError { export type WorkspaceFormErrors = { [key in keyof Omit< - WorkspaceFormData, + WorkspaceFormSubmitData, 'permissionSettings' | 'description' | 'selectedDataSources' >]?: WorkspaceFormError; } & { @@ -80,20 +75,24 @@ export interface WorkspaceFormProps { application: ApplicationStart; savedObjects: SavedObjectsStart; onSubmit?: (formData: WorkspaceFormSubmitData) => void; - defaultValues?: Partial; + defaultValues?: Partial; operationType: WorkspaceOperationType; permissionEnabled?: boolean; - detailTab?: DetailTab; dataSourceManagement?: DataSourceManagementPluginSetup; availableUseCases: WorkspaceUseCase[]; - detailTitle?: string; -} - -export interface WorkspaceDetailedFormProps extends WorkspaceFormProps { - defaultValues?: WorkspaceFormData; } export interface AvailableUseCaseItem extends Pick { disabled?: boolean; } + +export interface WorkspaceFormDataState + extends Omit { + name: string; + useCase: string | undefined; + selectedDataSources: DataSource[]; + permissionSettings: Array< + Pick & Partial + >; +} diff --git a/src/plugins/workspace/public/components/workspace_form/use_workspace_form.test.ts b/src/plugins/workspace/public/components/workspace_form/use_workspace_form.test.ts index 6e25c81d4440..078ce9af3e45 100644 --- a/src/plugins/workspace/public/components/workspace_form/use_workspace_form.test.ts +++ b/src/plugins/workspace/public/components/workspace_form/use_workspace_form.test.ts @@ -8,10 +8,10 @@ import { renderHook, act } from '@testing-library/react-hooks'; import { applicationServiceMock } from '../../../../../core/public/mocks'; import { WorkspacePermissionMode } from '../../../common/constants'; import { WorkspaceOperationType, WorkspacePermissionItemType } from './constants'; -import { WorkspaceFormData, WorkspaceFormErrorCode } from './types'; +import { WorkspaceFormSubmitData, WorkspaceFormErrorCode } from './types'; import { useWorkspaceForm } from './use_workspace_form'; -const setup = (defaultValues?: WorkspaceFormData, permissionEnabled = false) => { +const setup = (defaultValues?: WorkspaceFormSubmitData, permissionEnabled = false) => { const onSubmitMock = jest.fn(); const renderResult = renderHook(useWorkspaceForm, { initialProps: { diff --git a/src/plugins/workspace/public/components/workspace_form/use_workspace_form.ts b/src/plugins/workspace/public/components/workspace_form/use_workspace_form.ts index f627df1f3aad..55f5c5af1607 100644 --- a/src/plugins/workspace/public/components/workspace_form/use_workspace_form.ts +++ b/src/plugins/workspace/public/components/workspace_form/use_workspace_form.ts @@ -13,7 +13,12 @@ import { isUseCaseFeatureConfig, } from '../../utils'; import { DataSource } from '../../../common/types'; -import { WorkspaceFormProps, WorkspaceFormErrors, WorkspacePermissionSetting } from './types'; +import { + WorkspaceFormProps, + WorkspaceFormErrors, + WorkspacePermissionSetting, + WorkspaceFormDataState, +} from './types'; import { generatePermissionSettingsState, getNumberOfChanges, @@ -46,7 +51,7 @@ export const useWorkspaceForm = ({ featureConfigs, ]); const [permissionSettings, setPermissionSettings] = useState< - Array & Partial> + WorkspaceFormDataState['permissionSettings'] >(initialPermissionSettingsRef.current); const [selectedDataSources, setSelectedDataSources] = useState( @@ -58,7 +63,7 @@ export const useWorkspaceForm = ({ const [formErrors, setFormErrors] = useState({}); const numberOfErrors = useMemo(() => getNumberOfErrors(formErrors), [formErrors]); const formIdRef = useRef(); - const getFormData = () => ({ + const getFormData = (): WorkspaceFormDataState => ({ name, description, features: featureConfigs, diff --git a/src/plugins/workspace/public/components/workspace_form/utils.ts b/src/plugins/workspace/public/components/workspace_form/utils.ts index 04c1f3772600..bd45cf6a901f 100644 --- a/src/plugins/workspace/public/components/workspace_form/utils.ts +++ b/src/plugins/workspace/public/components/workspace_form/utils.ts @@ -16,7 +16,7 @@ import { } from './constants'; import { - WorkspaceFormData, + WorkspaceFormDataState, WorkspaceFormError, WorkspaceFormErrorCode, WorkspaceFormErrors, @@ -295,11 +295,7 @@ export const isSelectedDataSourcesDuplicated = ( ) => selectedDataSources.some((ds) => ds.id === row.id); export const validateWorkspaceForm = ( - formData: Omit, 'permissionSettings'> & { - permissionSettings?: Array< - Pick & Partial - >; - }, + formData: Partial, isPermissionEnabled: boolean ) => { const formErrors: WorkspaceFormErrors = {}; @@ -452,12 +448,8 @@ const isSamePermissionSetting = (a: PermissionSettingLike, b: PermissionSettingL }; export const getNumberOfChanges = ( - newFormData: Partial> & { - permissionSettings?: Array< - Pick & Partial - >; - }, - initialFormData: Partial> + newFormData: Partial, + initialFormData: Partial ) => { let count = 0; if (newFormData.name !== initialFormData.name) { diff --git a/src/plugins/workspace/public/components/workspace_form/workspace_detail_form.tsx b/src/plugins/workspace/public/components/workspace_form/workspace_detail_form.tsx index bc3ce92067ff..8b037a99ea63 100644 --- a/src/plugins/workspace/public/components/workspace_form/workspace_detail_form.tsx +++ b/src/plugins/workspace/public/components/workspace_form/workspace_detail_form.tsx @@ -50,7 +50,12 @@ const FormGroup = ({ title, children, describe }: FormGroupProps) => ( ); -export const WorkspaceDetailForm = (props: WorkspaceFormProps) => { +interface WorkspaceDetailedFormProps extends WorkspaceFormProps { + detailTab?: DetailTab; + detailTitle?: string; +} + +export const WorkspaceDetailForm = (props: WorkspaceDetailedFormProps) => { const { detailTab, detailTitle, defaultValues, availableUseCases } = props; const { formId, diff --git a/src/plugins/workspace/public/components/workspace_form/workspace_enter_details_panel.tsx b/src/plugins/workspace/public/components/workspace_form/workspace_enter_details_panel.tsx deleted file mode 100644 index 0bd172e6c47a..000000000000 --- a/src/plugins/workspace/public/components/workspace_form/workspace_enter_details_panel.tsx +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -import { EuiColorPicker, EuiCompressedFormRow, EuiSpacer, EuiText } from '@elastic/eui'; -import { i18n } from '@osd/i18n'; -import React from 'react'; -import { EuiColorPickerOutput } from '@elastic/eui/src/components/color_picker/color_picker'; -import { WorkspaceFormErrors } from './types'; -import { WorkspaceNameField } from './fields/workspace_name_field'; -import { WorkspaceDescriptionField } from './fields/workspace_description_field'; - -export interface EnterDetailsPanelProps { - formErrors: WorkspaceFormErrors; - name?: string; - description?: string; - color?: string; - readOnly: boolean; - onNameChange: (newValue: string) => void; - onDescriptionChange: (newValue: string) => void; - handleColorChange: (text: string, output: EuiColorPickerOutput) => void; -} - -export const EnterDetailsPanel = ({ - formErrors, - name, - description, - color, - readOnly, - onNameChange, - onDescriptionChange, - handleColorChange, -}: EnterDetailsPanelProps) => { - return ( - <> - - - -
- - {i18n.translate('workspace.form.workspaceDetails.color.description', { - defaultMessage: 'Select a background color for the icon representing this workspace.', - })} - - - -
-
- - ); -}; diff --git a/src/plugins/workspace/public/components/workspace_form/workspace_form.tsx b/src/plugins/workspace/public/components/workspace_form/workspace_form.tsx deleted file mode 100644 index f21a800a8357..000000000000 --- a/src/plugins/workspace/public/components/workspace_form/workspace_form.tsx +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -import React, { useCallback, useRef } from 'react'; -import { EuiPanel, EuiSpacer, EuiTitle, EuiForm, EuiText } from '@elastic/eui'; -import { i18n } from '@osd/i18n'; -import { WorkspaceFormProps } from './types'; -import { useWorkspaceForm } from './use_workspace_form'; -import { WorkspacePermissionSettingPanel } from './workspace_permission_setting_panel'; -import { WorkspaceUseCase } from './workspace_use_case'; -import { WorkspaceFormErrorCallout } from './workspace_form_error_callout'; -import { WorkspaceCreateActionPanel } from './workspace_create_action_panel'; -import { SelectDataSourcePanel } from './select_data_source_panel'; -import { EnterDetailsPanel } from './workspace_enter_details_panel'; -import { - selectDataSourceTitle, - usersAndPermissionsCreatePageTitle, - workspaceDetailsTitle, - workspaceUseCaseTitle, -} from './constants'; - -export const WorkspaceForm = (props: WorkspaceFormProps) => { - const { - application, - savedObjects, - defaultValues, - permissionEnabled, - dataSourceManagement: isDataSourceEnabled, - availableUseCases, - } = props; - const { - formId, - formData, - formErrors, - numberOfErrors, - setName, - setDescription, - handleFormSubmit, - handleColorChange, - handleUseCaseChange: handleUseCaseChangeInHook, - setPermissionSettings, - setSelectedDataSources, - } = useWorkspaceForm(props); - const nameManualChangedRef = useRef(false); - - const disabledUserOrGroupInputIdsRef = useRef( - defaultValues?.permissionSettings?.map((item) => item.id) ?? [] - ); - const isDashboardAdmin = application?.capabilities?.dashboards?.isDashboardAdmin ?? false; - const handleNameInputChange = useCallback( - (newName) => { - setName(newName); - nameManualChangedRef.current = true; - }, - [setName] - ); - const handleUseCaseChange = useCallback( - (newUseCase) => { - handleUseCaseChangeInHook(newUseCase); - const useCase = availableUseCases.find((item) => newUseCase === item.id); - if (!nameManualChangedRef.current && useCase) { - setName(useCase.title); - } - }, - [handleUseCaseChangeInHook, availableUseCases, setName] - ); - - return ( - - {numberOfErrors > 0 && ( - <> - - - - )} - - - -

{workspaceDetailsTitle}

-
- - -
- - - -

{workspaceUseCaseTitle}

-
- - -
- - {permissionEnabled && ( - - -

{usersAndPermissionsCreatePageTitle}

-
- - - {i18n.translate('workspace.form.usersAndPermissions.description', { - defaultMessage: - 'You will be added as an owner to the workspace. Select additional users and user groups as workspace collaborators with different access levels.', - })} - - - -
- )} - - - {/* SelectDataSourcePanel is only visible for dashboard admin and when data source is enabled*/} - {isDashboardAdmin && isDataSourceEnabled && ( - - -

{selectDataSourceTitle}

-
- -
- )} - - -
- ); -}; diff --git a/src/plugins/workspace/public/components/workspace_form/workspace_use_case.tsx b/src/plugins/workspace/public/components/workspace_form/workspace_use_case.tsx index 24d1e7c80e64..5eebdc8fa369 100644 --- a/src/plugins/workspace/public/components/workspace_form/workspace_use_case.tsx +++ b/src/plugins/workspace/public/components/workspace_form/workspace_use_case.tsx @@ -140,7 +140,6 @@ export const WorkspaceUseCase = ({ })} isInvalid={!!formErrors.features} error={formErrors.features?.message} - fullWidth > {availableUseCases.map(({ id, title, description, features, disabled }) => (