Skip to content

Commit

Permalink
Add right sidebar to workspace create form
Browse files Browse the repository at this point in the history
Signed-off-by: Lin Wang <[email protected]>
  • Loading branch information
wanglam committed Aug 19, 2024
1 parent 389ad1b commit cf4e2ed
Show file tree
Hide file tree
Showing 52 changed files with 902 additions and 327 deletions.
3 changes: 1 addition & 2 deletions src/plugins/workspace/public/application.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

import React from 'react';
import ReactDOM from 'react-dom';
import { HashRouter as Router, Route, Switch, Redirect } from 'react-router-dom';
import { HashRouter as Router, Route, Switch } from 'react-router-dom';
import { AppMountParameters, ScopedHistory } from '../../../core/public';
import { OpenSearchDashboardsContextProvider } from '../../opensearch_dashboards_react/public';
import { WorkspaceFatalError } from './components/workspace_fatal_error';
Expand All @@ -15,7 +15,6 @@ import { Services } from './types';
import { WorkspaceCreatorProps } from './components/workspace_creator/workspace_creator';
import { WorkspaceDetailApp } from './components/workspace_detail_app';
import { WorkspaceDetailProps } from './components/workspace_detail/workspace_detail';
import { DetailTab } from './components/workspace_form/constants';

export const renderCreatorApp = (
{ element }: AppMountParameters,
Expand Down
8 changes: 8 additions & 0 deletions src/plugins/workspace/public/components/forms/fields/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

export { WorkspaceColorField } from './workspace_color_field';
export { WorkspaceNameField } from './workspace_name_field';
export { WorkspaceDescriptionField } from './workspace_description_field';
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import React from 'react';
import {
EuiColorPicker,
EuiColorPickerProps,
EuiCompressedFormRow,
EuiSpacer,
EuiText,
} from '@elastic/eui';
import { i18n } from '@osd/i18n';

interface WorkspaceColorFieldProps extends Pick<EuiColorPickerProps, 'onChange'> {
value?: string;
error?: string;
}

export const WorkspaceColorField = ({ value, error, onChange }: WorkspaceColorFieldProps) => {
return (
<EuiCompressedFormRow
label={i18n.translate('workspace.form.workspaceDetails.color.label', {
defaultMessage: 'Workspace icon color',
})}
isInvalid={!!error}
error={error}
>
<>
<EuiText size="xs" color="subdued">
{i18n.translate('workspace.form.workspaceDetails.color.description', {
defaultMessage: 'Select a background color for the icon representing this workspace.',
})}
</EuiText>
<EuiSpacer size={'s'} />
<EuiColorPicker
color={value}
onChange={onChange}
data-test-subj="workspaceForm-workspaceDetails-colorPicker"
/>
</>
</EuiCompressedFormRow>
);
};
27 changes: 27 additions & 0 deletions src/plugins/workspace/public/components/forms/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

export { WorkspaceDetailForm } from './workspace_detail_form';
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, WorkspaceColorField } from './fields';

export { WorkspaceFormSubmitData, WorkspaceBaseFormProps, WorkspaceFormDataState } from './types';
export {
WorkspaceOperationType,
DetailTab,
DetailTabTitles,
WorkspacePermissionItemType,
} from './constants';
export {
convertPermissionsToPermissionSettings,
convertPermissionSettingsToPermissions,
} from './utils';

export { WorkspaceFormProvider, useWorkspaceFormContext } from './workspace_form_context';
export { useWorkspaceForm } from './use_workspace_form';
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -65,7 +60,7 @@ export interface WorkspaceFormError {

export type WorkspaceFormErrors = {
[key in keyof Omit<
WorkspaceFormData,
WorkspaceFormSubmitData,
'permissionSettings' | 'description' | 'selectedDataSources'
>]?: WorkspaceFormError;
} & {
Expand All @@ -76,19 +71,29 @@ export type WorkspaceFormErrors = {
selectedDataSources?: { [key: number]: WorkspaceFormError };
};

export interface WorkspaceFormProps {
export interface WorkspaceBaseFormProps {
application: ApplicationStart;
savedObjects: SavedObjectsStart;
onSubmit?: (formData: WorkspaceFormSubmitData) => void;
defaultValues?: Partial<WorkspaceFormData>;
operationType: WorkspaceOperationType;
defaultValues?: Partial<WorkspaceFormSubmitData>;
permissionEnabled?: boolean;
detailTab?: DetailTab;
dataSourceManagement?: DataSourceManagementPluginSetup;
availableUseCases: WorkspaceUseCase[];
}

export interface WorkspaceDetailedFormProps extends WorkspaceBaseFormProps {
operationType: WorkspaceOperationType;
detailTab?: DetailTab;
detailTitle?: string;
defaultValues?: WorkspaceFormSubmitData;
}

export interface WorkspaceDetailedFormProps extends WorkspaceFormProps {
defaultValues?: WorkspaceFormData;
export interface WorkspaceFormDataState
extends Omit<WorkspaceFormSubmitData, 'name' | 'permissionSettings'> {
name: string;
useCase: string | undefined;
selectedDataSources: DataSource[];
permissionSettings: Array<
Pick<WorkspacePermissionSetting, 'id'> & Partial<WorkspacePermissionSetting>
>;
}
Original file line number Diff line number Diff line change
Expand Up @@ -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 { WorkspaceFormErrorCode, WorkspaceFormSubmitData } from './types';
import { useWorkspaceForm } from './use_workspace_form';

const setup = (defaultValues?: WorkspaceFormData, permissionEnabled = false) => {
const setup = (defaultValues?: Partial<WorkspaceFormSubmitData>, permissionEnabled = false) => {
const onSubmitMock = jest.fn();
const renderResult = renderHook(useWorkspaceForm, {
initialProps: {
Expand All @@ -31,7 +31,6 @@ const setup = (defaultValues?: WorkspaceFormData, permissionEnabled = false) =>
describe('useWorkspaceForm', () => {
it('should return invalid workspace name error and not call onSubmit when invalid name', async () => {
const { renderResult, onSubmitMock } = setup({
id: 'foo',
name: '~',
});
expect(renderResult.result.current.formErrors).toEqual({});
Expand All @@ -51,7 +50,6 @@ describe('useWorkspaceForm', () => {
});
it('should return "Use case is required. Select a use case." and not call onSubmit', async () => {
const { renderResult, onSubmitMock } = setup({
id: 'foo',
name: 'test-workspace-name',
});
expect(renderResult.result.current.formErrors).toEqual({});
Expand All @@ -72,7 +70,6 @@ describe('useWorkspaceForm', () => {
it('should return "Add workspace owner." and not call onSubmit', async () => {
const { renderResult, onSubmitMock } = setup(
{
id: 'foo',
name: 'test-workspace-name',
},
true
Expand Down Expand Up @@ -111,7 +108,6 @@ describe('useWorkspaceForm', () => {
});
it('should call onSubmit with workspace name and features', async () => {
const { renderResult, onSubmitMock } = setup({
id: 'foo',
name: 'test-workspace-name',
features: ['use-case-observability'],
});
Expand All @@ -129,7 +125,6 @@ describe('useWorkspaceForm', () => {
});
it('should update selected use case', () => {
const { renderResult } = setup({
id: 'foo',
name: 'test-workspace-name',
features: ['use-case-observability'],
});
Expand All @@ -143,7 +138,6 @@ describe('useWorkspaceForm', () => {

it('should reset workspace form', () => {
const { renderResult } = setup({
id: 'test',
name: 'current-workspace-name',
features: ['use-case-observability'],
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,26 +12,38 @@ import {
getUseCaseFeatureConfig,
isUseCaseFeatureConfig,
} from '../../utils';
import { DataSource } from '../../../common/types';
import { WorkspaceFormProps, WorkspaceFormErrors, WorkspacePermissionSetting } from './types';
import {
WorkspaceFormErrors,
WorkspacePermissionSetting,
WorkspaceBaseFormProps,
WorkspaceFormDataState,
} from './types';
import {
appendDefaultFeatureIds,
generatePermissionSettingsState,
getNumberOfChanges,
getNumberOfErrors,
validateWorkspaceForm,
} from './utils';
import { WorkspacePermissionItemType } from './constants';
import { WorkspaceOperationType, WorkspacePermissionItemType } from './constants';

const workspaceHtmlIdGenerator = htmlIdGenerator();

export interface UseWorkspaceFormOptions
extends Pick<
WorkspaceBaseFormProps,
'application' | 'defaultValues' | 'onSubmit' | 'permissionEnabled'
> {
operationType: WorkspaceOperationType;
}

export const useWorkspaceForm = ({
application,
defaultValues,
operationType,
onSubmit,
permissionEnabled,
}: WorkspaceFormProps) => {
}: UseWorkspaceFormOptions) => {
const applications = useApplications(application);
const [name, setName] = useState(defaultValues?.name ?? '');
const [description, setDescription] = useState(defaultValues?.description);
Expand All @@ -49,10 +61,12 @@ export const useWorkspaceForm = ({
featureConfigs,
]);
const [permissionSettings, setPermissionSettings] = useState<
Array<Pick<WorkspacePermissionSetting, 'id'> & Partial<WorkspacePermissionSetting>>
WorkspaceFormDataState['permissionSettings']
>(initialPermissionSettingsRef.current);

const [selectedDataSources, setSelectedDataSources] = useState<DataSource[]>(
const [selectedDataSources, setSelectedDataSources] = useState<
WorkspaceFormDataState['selectedDataSources']
>(
defaultValues?.selectedDataSources && defaultValues.selectedDataSources.length > 0
? defaultValues.selectedDataSources
: []
Expand All @@ -61,7 +75,7 @@ export const useWorkspaceForm = ({
const [formErrors, setFormErrors] = useState<WorkspaceFormErrors>({});
const numberOfErrors = useMemo(() => getNumberOfErrors(formErrors), [formErrors]);
const formIdRef = useRef<string>();
const getFormData = () => ({
const getFormData = (): WorkspaceFormDataState => ({
name,
description,
features: featureConfigs,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import {
} from './constants';

import {
WorkspaceFormData,
WorkspaceFormDataState,
WorkspaceFormError,
WorkspaceFormErrorCode,
WorkspaceFormErrors,
Expand Down Expand Up @@ -304,11 +304,7 @@ export const isSelectedDataSourcesDuplicated = (
) => selectedDataSources.some((ds) => ds.id === row.id);

export const validateWorkspaceForm = (
formData: Omit<Partial<WorkspaceFormSubmitData>, 'permissionSettings'> & {
permissionSettings?: Array<
Pick<WorkspacePermissionSetting, 'id'> & Partial<WorkspacePermissionSetting>
>;
},
formData: Partial<WorkspaceFormDataState>,
isPermissionEnabled: boolean
) => {
const formErrors: WorkspaceFormErrors = {};
Expand Down Expand Up @@ -461,12 +457,8 @@ const isSamePermissionSetting = (a: PermissionSettingLike, b: PermissionSettingL
};

export const getNumberOfChanges = (
newFormData: Partial<Omit<WorkspaceFormSubmitData, 'permissionSettings'>> & {
permissionSettings?: Array<
Pick<WorkspacePermissionSetting, 'id'> & Partial<WorkspacePermissionSetting>
>;
},
initialFormData: Partial<Omit<WorkspaceFormData, 'id'>>
newFormData: Partial<WorkspaceFormDataState>,
initialFormData: Partial<WorkspaceFormSubmitData>
) => {
let count = 0;
if (newFormData.name !== initialFormData.name) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
} from '@elastic/eui';

import { i18n } from '@osd/i18n';
import { WorkspaceFormProps } from './types';
import { WorkspaceDetailedFormProps } from './types';
import { WorkspacePermissionSettingPanel } from './workspace_permission_setting_panel';
import { DetailTab, usersAndPermissionsTitle } from './constants';
import { WorkspaceFormErrorCallout } from './workspace_form_error_callout';
Expand Down Expand Up @@ -51,7 +51,7 @@ const FormGroup = ({ title, children, describe }: FormGroupProps) => (
</>
);

export const WorkspaceDetailForm = (props: WorkspaceFormProps) => {
export const WorkspaceDetailForm = (props: WorkspaceDetailedFormProps) => {
const { detailTab, detailTitle, defaultValues, availableUseCases } = props;
const {
formId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import {
EuiColorPicker,
EuiCompressedFormRow,
EuiDescribedFormGroup,
EuiCompressedTextArea,
} from '@elastic/eui';
import React, { useEffect, useState } from 'react';
import { useObservable } from 'react-use';
Expand All @@ -17,7 +16,6 @@ import {
detailsColorLabel,
detailsUseCaseLabel,
detailsColorHelpText,
detailsDescriptionPlaceholder,
detailsDescriptionIntroduction,
detailsUseCaseHelpText,
} from './constants';
Expand Down
Loading

0 comments on commit cf4e2ed

Please sign in to comment.