From bc617bd3fa6add04c086a0dae3eed3b4ec447e99 Mon Sep 17 00:00:00 2001
From: Hailong Cui
Date: Wed, 7 Aug 2024 16:41:37 +0800
Subject: [PATCH] navigates to use case overview page
Signed-off-by: Hailong Cui
click on workspace name navigates to use case overview page
Signed-off-by: Hailong Cui
Changeset file for PR #7748 created/updated
support customized width
Signed-off-by: Hailong Cui
style adjustment
Signed-off-by: Hailong Cui
update test snapshot
Signed-off-by: Hailong Cui
---
changelogs/fragments/7748.yml | 2 +
.../workspace_list_card.test.tsx.snap | 87 +++++----
.../service_card/workspace_list_card.test.tsx | 52 +++++-
.../service_card/workspace_list_card.tsx | 168 ++++++++++++------
.../public/components/utils/workspace.ts | 24 ++-
.../workspace_detail_panel.tsx | 7 +-
src/plugins/workspace/public/plugin.ts | 1 +
7 files changed, 240 insertions(+), 101 deletions(-)
create mode 100644 changelogs/fragments/7748.yml
diff --git a/changelogs/fragments/7748.yml b/changelogs/fragments/7748.yml
new file mode 100644
index 000000000000..a62a59be3178
--- /dev/null
+++ b/changelogs/fragments/7748.yml
@@ -0,0 +1,2 @@
+feat:
+- [Workspace]Fix click on workspace name not navigates to use case overview page ([#7748](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/7748))
\ No newline at end of file
diff --git a/src/plugins/workspace/public/components/service_card/__snapshots__/workspace_list_card.test.tsx.snap b/src/plugins/workspace/public/components/service_card/__snapshots__/workspace_list_card.test.tsx.snap
index 0607b29799a9..36a442328839 100644
--- a/src/plugins/workspace/public/components/service_card/__snapshots__/workspace_list_card.test.tsx.snap
+++ b/src/plugins/workspace/public/components/service_card/__snapshots__/workspace_list_card.test.tsx.snap
@@ -16,37 +16,48 @@ exports[`workspace list card render normally should show workspace list card cor
class="euiFlexGroup euiFlexGroup--gutterExtraSmall euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow euiFlexGroup--responsive"
>
-
- Workspaces
-
-
-
-
-
+
+ Workspaces
+
+
+
+
+
+
+
+
-
+
@@ -116,20 +139,6 @@ exports[`workspace list card render normally should show workspace list card cor
-
diff --git a/src/plugins/workspace/public/components/service_card/workspace_list_card.test.tsx b/src/plugins/workspace/public/components/service_card/workspace_list_card.test.tsx
index 10efd2b6b591..0cb5b10f1233 100644
--- a/src/plugins/workspace/public/components/service_card/workspace_list_card.test.tsx
+++ b/src/plugins/workspace/public/components/service_card/workspace_list_card.test.tsx
@@ -8,9 +8,32 @@ import { coreMock } from '../../../../../core/public/mocks';
import { fireEvent, render, waitFor } from '@testing-library/react';
import { WorkspaceListCard } from './workspace_list_card';
import { recentWorkspaceManager } from '../../recent_workspace_manager';
+import { BehaviorSubject } from 'rxjs';
+import { NavGroupItemInMap } from 'opensearch-dashboards/public';
describe('workspace list card render normally', () => {
- const coreStart = coreMock.createStart();
+ const navGroupMap: Record = {
+ group1: {
+ id: 'group1',
+ title: 'title',
+ description: 'desc',
+ navLinks: [
+ {
+ id: 'link1',
+ },
+ ],
+ },
+ };
+ const coreStart = {
+ ...coreMock.createStart(),
+ chrome: {
+ ...coreMock.createStart().chrome,
+ navGroup: {
+ ...coreMock.createStart().chrome.navGroup,
+ getNavGroupsMap$: () => new BehaviorSubject(navGroupMap),
+ },
+ },
+ };
beforeAll(() => {
const workspaceList = [
@@ -18,6 +41,7 @@ describe('workspace list card render normally', () => {
id: 'ws-1',
name: 'foo',
lastUpdatedTime: new Date().toISOString(),
+ features: ['use-case-group1'],
},
{
id: 'ws-2',
@@ -38,7 +62,11 @@ describe('workspace list card render normally', () => {
expect(getByTestId('workspace_filter')).toHaveDisplayValue('Recently viewed');
// empty statue for recently viewed
- expect(getByText('Workspaces you have recently viewed will appear here.')).toBeInTheDocument();
+ expect(
+ getByText(
+ 'Contact your administrator to create a workspace or to be added to an existing one.'
+ )
+ ).toBeInTheDocument();
});
it('should show default filter as recently viewed', () => {
@@ -64,6 +92,21 @@ describe('workspace list card render normally', () => {
expect(getByText('bar')).toBeInTheDocument();
});
+ it('should navigate to workspace use case overview page when click on workspace name', () => {
+ const { getByTestId, getByText } = render();
+ const filterSelector = getByTestId('workspace_filter');
+ fireEvent.change(filterSelector, { target: { value: 'updated' } });
+ expect(getByTestId('workspace_filter')).toHaveDisplayValue('Recently updated');
+
+ // workspace list
+ expect(getByText('foo')).toBeInTheDocument();
+
+ fireEvent.click(getByText('foo'));
+ expect(coreStart.application.getUrlForApp).toHaveBeenLastCalledWith('link1', {
+ absolute: true,
+ });
+ });
+
it('should render create workspace button when is dashboard admin and navigate to create new workspace page when clicking on plus button', () => {
coreStart.application.capabilities = {
...coreStart.application.capabilities,
@@ -79,7 +122,10 @@ describe('workspace list card render normally', () => {
});
it('should navigate to workspace list page when click on View all button', () => {
- const { getByText } = render();
+ const { getByText, getByTestId } = render();
+ const filterSelector = getByTestId('workspace_filter');
+ fireEvent.change(filterSelector, { target: { value: 'updated' } });
+ expect(getByTestId('workspace_filter')).toHaveDisplayValue('Recently updated');
const mockButton = getByText('View all');
fireEvent.click(mockButton);
expect(coreStart.application.navigateToApp).toHaveBeenCalledWith('workspace_list');
diff --git a/src/plugins/workspace/public/components/service_card/workspace_list_card.tsx b/src/plugins/workspace/public/components/service_card/workspace_list_card.tsx
index 009f6519ce8f..e143d5d9f9fb 100644
--- a/src/plugins/workspace/public/components/service_card/workspace_list_card.tsx
+++ b/src/plugins/workspace/public/components/service_card/workspace_list_card.tsx
@@ -10,27 +10,28 @@ import {
EuiDescriptionList,
EuiIcon,
EuiFlexItem,
- EuiSelect,
- EuiButtonIcon,
EuiText,
EuiSpacer,
EuiPanel,
EuiTitle,
EuiToolTip,
EuiEmptyPrompt,
+ EuiSmallButton,
+ EuiCompressedSelect,
} from '@elastic/eui';
import { i18n } from '@osd/i18n';
import moment from 'moment';
import { orderBy } from 'lodash';
+import { useObservable } from 'react-use';
import { CoreStart, WorkspaceObject } from '../../../../../core/public';
-import { navigateToWorkspaceDetail } from '../utils/workspace';
import { WORKSPACE_CREATE_APP_ID, WORKSPACE_LIST_APP_ID } from '../../../common/constants';
import { recentWorkspaceManager } from '../../recent_workspace_manager';
+import { getFirstUseCaseOfFeatureConfigs } from '../../utils';
+import { navigateToAppWithinWorkspace } from '../utils/workspace';
const WORKSPACE_LIST_CARD_DESCRIPTION = i18n.translate('workspace.list.card.description', {
- defaultMessage:
- 'Workspaces are dedicated environments for organizing and collaborating on your data, dashboards, and analytics workflows. Each Workspace acts as a self-contained space with its own set of saved objects and access controls.',
+ defaultMessage: 'Dedicated environments for organizing, sharing and collaborating.',
});
const MAX_ITEM_IN_LIST = 5;
@@ -42,6 +43,7 @@ export interface WorkspaceListCardProps {
export const WorkspaceListCard = (props: WorkspaceListCardProps) => {
const [availableWorkspaces, setAvailableWorkspaces] = useState([]);
const [filter, setFilter] = useState('viewed');
+ const navGroups = useObservable(props.core.chrome.navGroup.getNavGroupsMap$());
useEffect(() => {
const workspaceSub = props.core.workspaces.workspaceList$.subscribe((list) => {
@@ -75,10 +77,14 @@ export const WorkspaceListCard = (props: WorkspaceListCardProps) => {
return [];
}, [filter, availableWorkspaces]);
- const handleSwitchWorkspace = (id: string) => {
+ const handleSwitchWorkspace = (workspaceId: string) => {
const { application, http } = props.core;
- if (application && http) {
- navigateToWorkspaceDetail({ application, http }, id);
+ const workspaceObj = availableWorkspaces.find((item) => item.id === workspaceId);
+ const useCase = getFirstUseCaseOfFeatureConfigs(workspaceObj?.features || []);
+ if (useCase && navGroups) {
+ // should be workspace use case overview page
+ const appId = navGroups[useCase].navLinks?.[0].id;
+ navigateToAppWithinWorkspace({ application, http }, workspaceId, appId);
}
};
@@ -86,6 +92,65 @@ export const WorkspaceListCard = (props: WorkspaceListCardProps) => {
const isDashboardAdmin = application.capabilities.dashboards?.isDashboardAdmin;
+ const createWorkspace = i18n.translate('workspace.list.card.createWorkspace', {
+ defaultMessage: 'Create workspace',
+ });
+
+ const workspaceAvailable = workspaceList && workspaceList.length > 0;
+
+ const createWorkspaceButton = (
+ {
+ application.navigateToApp(WORKSPACE_CREATE_APP_ID);
+ }}
+ >
+ {createWorkspace}
+
+ );
+
+ let emptyStateBody: JSX.Element = (
+
+ {i18n.translate('workspace.list.card.empty.readOnly', {
+ defaultMessage:
+ 'Contact your administrator to create a workspace or to be added to an existing one.',
+ })}
+
+ );
+
+ if (isDashboardAdmin) {
+ emptyStateBody = (
+ <>
+
+ {i18n.translate('workspace.list.card.empty', {
+ defaultMessage: 'Create a workspace to get started.',
+ })}
+
+
+
+
+ {
+ application.navigateToApp(WORKSPACE_LIST_APP_ID);
+ }}
+ >
+
+ {i18n.translate('workspace.list.card.manageWorkspaces', {
+ defaultMessage: 'Manage workspaces',
+ })}
+
+
+
+ {createWorkspaceButton}
+
+ >
+ );
+ }
+
return (
{
>
-
-
- Workspaces
-
-
-
-
-
-
+
+
+
+
+ Workspaces
+
+
+
+
+
+
+
+
+
-
-
+ {
@@ -129,19 +199,10 @@ export const WorkspaceListCard = (props: WorkspaceListCardProps) => {
]}
/>
- {isDashboardAdmin && (
+ {isDashboardAdmin && workspaceAvailable && (
-
- {
- application.navigateToApp(WORKSPACE_CREATE_APP_ID);
- }}
- />
+
+ {createWorkspaceButton}
)}
@@ -149,17 +210,16 @@ export const WorkspaceListCard = (props: WorkspaceListCardProps) => {
- {workspaceList && workspaceList.length === 0 ? (
+ {!workspaceList || workspaceList.length === 0 ? (
No Workspaces found}
- body={i18n.translate('workspace.list.card.empty', {
- values: {
- filter,
- },
- defaultMessage: 'Workspaces you have recently {filter} will appear here.',
- })}
+ title={
+
+ No Workspaces available
+
+ }
+ body={emptyStateBody}
/>
) : (
{
)}
-
- {
- application.navigateToApp(WORKSPACE_LIST_APP_ID);
- }}
- >
-
- {i18n.translate('workspace.list.card.view_all', {
- defaultMessage: 'View all',
- })}
-
-
-
+ {workspaceAvailable && (
+
+ {
+ application.navigateToApp(WORKSPACE_LIST_APP_ID);
+ }}
+ >
+
+ {i18n.translate('workspace.list.card.view_all', {
+ defaultMessage: 'View all',
+ })}
+
+
+
+ )}
);
diff --git a/src/plugins/workspace/public/components/utils/workspace.ts b/src/plugins/workspace/public/components/utils/workspace.ts
index 3acd26570f59..e9565a07b857 100644
--- a/src/plugins/workspace/public/components/utils/workspace.ts
+++ b/src/plugins/workspace/public/components/utils/workspace.ts
@@ -12,19 +12,35 @@ type Core = Pick;
export const navigateToWorkspaceDetail = (
{ application, http }: Core,
- id: string,
+ workspaceId: string,
tabId: string = DetailTab.Details
+) => {
+ navigateToAppWithinWorkspace(
+ { application, http },
+ workspaceId,
+ WORKSPACE_DETAIL_APP_ID,
+ `/?tab=${tabId}`
+ );
+};
+
+export const navigateToAppWithinWorkspace = (
+ { application, http }: Core,
+ workspaceId: string,
+ appId: string,
+ hash?: string
) => {
const newUrl = formatUrlWithWorkspaceId(
- application.getUrlForApp(WORKSPACE_DETAIL_APP_ID, {
+ application.getUrlForApp(appId, {
absolute: true,
}),
- id,
+ workspaceId,
http.basePath
);
if (newUrl) {
const url = new URL(newUrl);
- url.hash = `/?tab=${tabId}`;
+ if (hash) {
+ url.hash = hash;
+ }
application.navigateToUrl(url.toString());
}
};
diff --git a/src/plugins/workspace/public/components/workspace_detail/workspace_detail_panel.tsx b/src/plugins/workspace/public/components/workspace_detail/workspace_detail_panel.tsx
index 1f426743ac17..6ada5490c379 100644
--- a/src/plugins/workspace/public/components/workspace_detail/workspace_detail_panel.tsx
+++ b/src/plugins/workspace/public/components/workspace_detail/workspace_detail_panel.tsx
@@ -45,8 +45,11 @@ const overview = i18n.translate('workspace.detail.overview', {
});
function getOwners(currentWorkspace: WorkspaceAttributeWithPermission) {
- const { groups = [], users = [] } = currentWorkspace.permissions!.write;
- return [...groups, ...users];
+ if (currentWorkspace.permissions) {
+ const { groups = [], users = [] } = currentWorkspace.permissions.write;
+ return [...groups, ...users];
+ }
+ return [];
}
interface WorkspaceDetailPanelProps {
diff --git a/src/plugins/workspace/public/plugin.ts b/src/plugins/workspace/public/plugin.ts
index 0086b9bedaae..5358edef6aa1 100644
--- a/src/plugins/workspace/public/plugin.ts
+++ b/src/plugins/workspace/public/plugin.ts
@@ -503,6 +503,7 @@ export class WorkspacePlugin
id: 'workspace_list',
kind: 'custom',
order: 0,
+ width: 16,
render: () => React.createElement(WorkspaceListCard, { core }),
}),
getTargetArea: () => HOME_CONTENT_AREAS.SERVICE_CARDS,