Skip to content

Commit

Permalink
refactor: register workspace dropdown menu to left menu (#90)
Browse files Browse the repository at this point in the history
- register workspace dropdown menu to left menu from workspace plugin
- rename PUBLIC_WORKSPACE and MANAGEMENT_WORKSPACE

---------

Signed-off-by: Yulong Ruan <ruanyl@amazon.com>
  • Loading branch information
ruanyl authored Aug 21, 2023
1 parent 7fd4139 commit 64bcc8b
Show file tree
Hide file tree
Showing 26 changed files with 403 additions and 285 deletions.
4 changes: 0 additions & 4 deletions src/core/public/chrome/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,3 @@
export const OPENSEARCH_DASHBOARDS_ASK_OPENSEARCH_LINK = 'https://forum.opensearch.org/';
export const GITHUB_CREATE_ISSUE_LINK =
'https://github.com/opensearch-project/OpenSearch-Dashboards/issues/new/choose';

export const WORKSPACE_CREATE_APP_ID = 'workspace_create';
export const WORKSPACE_LIST_APP_ID = 'workspace_list';
export const WORKSPACE_OVERVIEW_APP_ID = 'workspace_overview';
6 changes: 1 addition & 5 deletions src/core/public/chrome/ui/header/collapsible_nav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -233,11 +233,7 @@ export function CollapsibleNav({
outsideClickCloses={false}
>
<EuiFlexItem className="eui-yScroll">
<CollapsibleNavHeader
getUrlForApp={getUrlForApp}
workspaces={workspaces}
basePath={basePath}
/>
<CollapsibleNavHeader workspaces={workspaces} />

{/* merged NavLinks */}
{mergedNavLinks.map((item, i) => {
Expand Down
207 changes: 6 additions & 201 deletions src/core/public/chrome/ui/header/collapsible_nav_header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,68 +3,23 @@
* SPDX-License-Identifier: Apache-2.0
*/
import { i18n } from '@osd/i18n';
import React, { useState } from 'react';
import React from 'react';
import useObservable from 'react-use/lib/useObservable';
import {
EuiContextMenu,
EuiPopover,
EuiIcon,
EuiFlexGroup,
EuiFlexItem,
EuiText,
EuiCollapsibleNavGroup,
} from '@elastic/eui';
import {
HttpStart,
WorkspaceStart,
WorkspaceAttribute,
MANAGEMENT_WORKSPACE,
} from '../../../../public';
import { InternalApplicationStart } from '../../../application';
import { formatUrlWithWorkspaceId } from '../../../utils';
import {
WORKSPACE_CREATE_APP_ID,
WORKSPACE_LIST_APP_ID,
WORKSPACE_OVERVIEW_APP_ID,
} from '../../constants';
import { EuiIcon, EuiFlexGroup, EuiFlexItem, EuiText, EuiCollapsibleNavGroup } from '@elastic/eui';
import { WorkspaceStart } from '../../../../public';

interface Props {
workspaces: WorkspaceStart;
basePath: HttpStart['basePath'];
getUrlForApp: InternalApplicationStart['getUrlForApp'];
}

function getFilteredWorkspaceList(
workspaceList: WorkspaceAttribute[],
currentWorkspace: WorkspaceAttribute | null
): WorkspaceAttribute[] {
// list top5 workspaces except management workspace, place current workspace at the top
return [
...(currentWorkspace ? [currentWorkspace] : []),
...workspaceList.filter(
(workspace) => workspace.id !== MANAGEMENT_WORKSPACE && workspace.id !== currentWorkspace?.id
),
].slice(0, 5);
}

export function CollapsibleNavHeader({ workspaces, getUrlForApp, basePath }: Props) {
export function CollapsibleNavHeader({ workspaces }: Props) {
const workspaceEnabled = useObservable(workspaces.workspaceEnabled$, false);
const workspaceList = useObservable(workspaces.workspaceList$, []);
const currentWorkspace = useObservable(workspaces.currentWorkspace$, null);
const filteredWorkspaceList = getFilteredWorkspaceList(workspaceList, currentWorkspace);
const defaultHeaderName = i18n.translate(
'core.ui.primaryNav.workspacePickerMenu.defaultHeaderName',
{
defaultMessage: 'OpenSearch Analytics',
}
);
const managementWorkspaceName =
workspaceList.find((workspace) => workspace.id === MANAGEMENT_WORKSPACE)?.name ??
i18n.translate('core.ui.primaryNav.workspacePickerMenu.managementWorkspaceName', {
defaultMessage: 'Management',
});
const currentWorkspaceName = currentWorkspace?.name ?? defaultHeaderName;
const [isPopoverOpen, setPopover] = useState(false);

if (!workspaceEnabled) {
return (
Expand All @@ -81,157 +36,7 @@ export function CollapsibleNavHeader({ workspaces, getUrlForApp, basePath }: Pro
</EuiFlexGroup>
</EuiCollapsibleNavGroup>
);
} else {
return workspaces.renderWorkspaceMenu();
}
const onButtonClick = () => {
setPopover(!isPopoverOpen);
};

const closePopover = () => {
setPopover(false);
};

const workspaceToItem = (workspace: WorkspaceAttribute, index: number) => {
const href = formatUrlWithWorkspaceId(
getUrlForApp(WORKSPACE_OVERVIEW_APP_ID, {
absolute: false,
}),
workspace.id,
basePath
);
const name =
currentWorkspace !== null && index === 0 ? (
<EuiText>
<strong> {workspace.name} </strong>
</EuiText>
) : (
workspace.name
);
return {
href,
name,
key: index.toString(),
icon: <EuiIcon type="stopFilled" color={workspace.color ?? 'primary'} />,
};
};

const getWorkspaceListItems = () => {
const workspaceListItems = filteredWorkspaceList.map((workspace, index) =>
workspaceToItem(workspace, index)
);
const length = workspaceListItems.length;
workspaceListItems.push({
icon: <EuiIcon type="plus" />,
name: i18n.translate('core.ui.primaryNav.workspaceContextMenu.createWorkspace', {
defaultMessage: 'Create workspace',
}),
key: length.toString(),
href: formatUrlWithWorkspaceId(
getUrlForApp(WORKSPACE_CREATE_APP_ID, {
absolute: false,
}),
currentWorkspace?.id ?? '',
basePath
),
});
workspaceListItems.push({
icon: <EuiIcon type="folderClosed" />,
name: i18n.translate('core.ui.primaryNav.workspaceContextMenu.allWorkspace', {
defaultMessage: 'All workspaces',
}),
key: (length + 1).toString(),
href: formatUrlWithWorkspaceId(
getUrlForApp(WORKSPACE_LIST_APP_ID, {
absolute: false,
}),
currentWorkspace?.id ?? '',
basePath
),
});
return workspaceListItems;
};

const currentWorkspaceButton = (
<EuiCollapsibleNavGroup>
<EuiFlexGroup>
<EuiFlexItem>
<EuiIcon type="logoOpenSearch" size="l" />
</EuiFlexItem>
<EuiFlexItem>
<EuiText>
<strong> {currentWorkspaceName} </strong>
</EuiText>
</EuiFlexItem>
<EuiFlexItem>
<EuiIcon type="arrowDown" onClick={onButtonClick} />
</EuiFlexItem>
</EuiFlexGroup>
</EuiCollapsibleNavGroup>
);

const currentWorkspaceTitle = (
<EuiFlexGroup>
<EuiFlexItem>
<EuiIcon type="logoOpenSearch" size="l" />
</EuiFlexItem>
<EuiFlexItem>
<EuiText>
<strong> {currentWorkspaceName} </strong>
</EuiText>
</EuiFlexItem>
<EuiFlexItem>
<EuiIcon type="cross" onClick={closePopover} />
</EuiFlexItem>
</EuiFlexGroup>
);

const panels = [
{
id: 0,
title: currentWorkspaceTitle,
items: [
{
name: (
<EuiText>
<strong>
{i18n.translate('core.ui.primaryNav.workspacePickerMenu.workspaceList', {
defaultMessage: 'Workspaces',
})}
</strong>
</EuiText>
),
icon: 'folderClosed',
panel: 1,
},
{
name: managementWorkspaceName,
icon: 'managementApp',
href: formatUrlWithWorkspaceId(
getUrlForApp(WORKSPACE_OVERVIEW_APP_ID, {
absolute: false,
}),
MANAGEMENT_WORKSPACE,
basePath
),
},
],
},
{
id: 1,
title: 'Workspaces',
items: getWorkspaceListItems(),
},
];

return (
<EuiPopover
id="contextMenuExample"
button={currentWorkspaceButton}
isOpen={isPopoverOpen}
closePopover={closePopover}
panelPaddingSize="none"
anchorPosition="downLeft"
>
<EuiContextMenu initialPanelId={0} panels={panels} />
</EuiPopover>
);
}
2 changes: 1 addition & 1 deletion src/core/public/core_system.ts
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ export class CoreSystem {
targetDomElement: notificationsTargetDomElement,
});
const application = await this.application.start({ http, overlays });
const workspaces = this.workspaces.start();
const workspaces = this.workspaces.start({ application, http });
const chrome = await this.chrome.start({
application,
docLinks,
Expand Down
10 changes: 8 additions & 2 deletions src/core/public/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -348,8 +348,14 @@ export {

export { __osdBootstrap__ } from './osd_bootstrap';

export { WorkspaceStart, WorkspaceService, WorkspaceAttribute } from './workspace';
export {
WorkspaceStart,
WorkspaceSetup,
WorkspaceService,
WorkspaceAttribute,
WorkspaceObservables,
} from './workspace';

export { WorkspacePermissionMode, PUBLIC_WORKSPACE, MANAGEMENT_WORKSPACE } from '../utils';
export { WorkspacePermissionMode, PUBLIC_WORKSPACE_ID, MANAGEMENT_WORKSPACE_ID } from '../utils';

export { getWorkspaceIdFromUrl, WORKSPACE_TYPE } from './utils';
4 changes: 2 additions & 2 deletions src/core/public/saved_objects/saved_objects_client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ import {

import { SimpleSavedObject } from './simple_saved_object';
import { HttpFetchOptions, HttpSetup } from '../http';
import { PUBLIC_WORKSPACE } from '../../utils';
import { PUBLIC_WORKSPACE_ID } from '../../utils';

type SavedObjectsFindOptions = Omit<
SavedObjectFindOptionsServer,
Expand Down Expand Up @@ -382,7 +382,7 @@ export class SavedObjectsClient {
if (options.hasOwnProperty('workspaces')) {
finalWorkspaces = options.workspaces;
} else if (typeof currentWorkspaceId === 'string') {
finalWorkspaces = Array.from(new Set([PUBLIC_WORKSPACE, currentWorkspaceId]));
finalWorkspaces = Array.from(new Set([PUBLIC_WORKSPACE_ID, currentWorkspaceId]));
}

const renamedQuery = renameKeys<SavedObjectsFindOptions, any>(renameMap, {
Expand Down
4 changes: 2 additions & 2 deletions src/core/public/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,5 @@
export { shareWeakReplay } from './share_weak_replay';
export { Sha256 } from './crypto';
export { MountWrapper, mountReactNode } from './mount';
export { getWorkspaceIdFromUrl, WORKSPACE_TYPE, formatUrlWithWorkspaceId } from './workspace';
export { WORKSPACE_PATH_PREFIX, PUBLIC_WORKSPACE, MANAGEMENT_WORKSPACE } from '../../utils';
export { getWorkspaceIdFromUrl, WORKSPACE_TYPE } from './workspace';
export { WORKSPACE_PATH_PREFIX, PUBLIC_WORKSPACE_ID, MANAGEMENT_WORKSPACE_ID } from '../../utils';
27 changes: 0 additions & 27 deletions src/core/public/utils/workspace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@
* SPDX-License-Identifier: Apache-2.0
*/

import { IBasePath } from '../http';
import { WORKSPACE_PATH_PREFIX } from '../../utils';

export const getWorkspaceIdFromUrl = (url: string): string => {
const regexp = /\/w\/([^\/]*)/;
const urlObject = new URL(url);
Expand All @@ -17,28 +14,4 @@ export const getWorkspaceIdFromUrl = (url: string): string => {
return '';
};

export const formatUrlWithWorkspaceId = (
url: string,
workspaceId: string,
basePath?: IBasePath
) => {
const newUrl = new URL(url, window.location.href);
/**
* Patch workspace id into path
*/
newUrl.pathname = basePath?.remove(newUrl.pathname) || '';
if (workspaceId) {
newUrl.pathname = `${WORKSPACE_PATH_PREFIX}/${workspaceId}${newUrl.pathname}`;
} else {
newUrl.pathname = newUrl.pathname.replace(/^\/w\/([^\/]*)/, '');
}

newUrl.pathname =
basePath?.prepend(newUrl.pathname, {
withoutWorkspace: true,
}) || '';

return newUrl.toString();
};

export const WORKSPACE_TYPE = 'workspace';
7 changes: 6 additions & 1 deletion src/core/public/workspace/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,10 @@
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/
export { WorkspaceStart, WorkspaceService, WorkspaceSetup } from './workspaces_service';
export {
WorkspaceStart,
WorkspaceService,
WorkspaceSetup,
WorkspaceObservables,
} from './workspaces_service';
export type { WorkspaceAttribute } from './workspaces_service';
11 changes: 9 additions & 2 deletions src/core/public/workspace/workspaces_service.mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,18 @@ const createWorkspacesSetupContractMock = () => ({
workspaceList$,
currentWorkspace$,
workspaceEnabled$,
registerWorkspaceMenuRender: jest.fn(),
});

const createWorkspacesStartContractMock = createWorkspacesSetupContractMock;
const createWorkspacesStartContractMock = () => ({
currentWorkspaceId$,
workspaceList$,
currentWorkspace$,
workspaceEnabled$,
renderWorkspaceMenu: jest.fn(),
});

export const workspacesServiceMock = {
createSetupContractMock: createWorkspacesStartContractMock,
createSetupContractMock: createWorkspacesSetupContractMock,
createStartContract: createWorkspacesStartContractMock,
};
Loading

0 comments on commit 64bcc8b

Please sign in to comment.