Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Workspace] Refactor the UI of workspace picker #7045

Merged
Merged
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
e7c8d98
Refactor the UI of workspace picker
yubonluo Jun 17, 2024
1d003d2
Merge branch 'main' of github.com:opensearch-project/OpenSearch-Dashb…
yubonluo Jun 17, 2024
7458eb7
Changeset file for PR #7045 created/updated
opensearch-changeset-bot[bot] Jun 17, 2024
8a4e041
Optimize the code
yubonluo Jun 17, 2024
1792e16
Merge branch '2.16-refactor-workspace-picker' of github.com:yubonluo/…
yubonluo Jun 17, 2024
b9e1b16
Optimize the code
yubonluo Jun 17, 2024
b517c03
Optimize the code
yubonluo Jun 17, 2024
900acf4
optimize the code
yubonluo Jun 21, 2024
dfe6935
support suggested workspace
yubonluo Jun 26, 2024
13888cd
optimize the code
yubonluo Jun 27, 2024
0fc0360
optimize the code
yubonluo Jul 4, 2024
be32db9
optimize the code
yubonluo Jul 8, 2024
04928d8
delete search and use case menu
yubonluo Jul 9, 2024
0ecfe0e
solve the code conflict
yubonluo Jul 9, 2024
0240ce4
optimize the code
yubonluo Jul 9, 2024
11dfb75
delete useless code
yubonluo Jul 9, 2024
720dd58
optimize the code
yubonluo Jul 10, 2024
ff65b87
solve th code conflict
yubonluo Jul 16, 2024
2842bd5
optimize the code
yubonluo Jul 16, 2024
b8fd0e4
add timestamp
yubonluo Jul 18, 2024
9f3df8e
solve the code conflict
yubonluo Jul 18, 2024
4046ef6
optimize the code
yubonluo Jul 18, 2024
8a602b3
optimzie the code
yubonluo Jul 18, 2024
ff44081
optimzie the code
yubonluo Jul 19, 2024
d7011b9
fix unit test error
yubonluo Jul 19, 2024
3f70468
solve the code conflict
yubonluo Jul 19, 2024
576f5b0
optimize the code
yubonluo Jul 19, 2024
0520db1
optimize the code
yubonluo Jul 19, 2024
a21ced9
optimize the code
yubonluo Jul 19, 2024
03ff529
Merge branch 'main' of github.com:opensearch-project/OpenSearch-Dashb…
yubonluo Jul 19, 2024
125b43e
optimize the code
yubonluo Jul 19, 2024
fce6aa6
Replace menuContext with listGroup
yubonluo Jul 19, 2024
3cae370
solve the code conflict
yubonluo Jul 20, 2024
4932cf3
optimize the code
yubonluo Jul 20, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions changelogs/fragments/7045.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
feat:
- [Workspace] Refactor the UI of workspace picker ([#7045](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/7045))
6 changes: 5 additions & 1 deletion src/core/public/chrome/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,11 @@ export {
} from './ui/header/header_help_menu';
export { NavType, RightNavigationButton, RightNavigationButtonProps } from './ui';
export { ChromeNavLink, ChromeNavLinks, ChromeNavLinkUpdateableFields } from './nav_links';
export { ChromeRecentlyAccessed, ChromeRecentlyAccessedHistoryItem } from './recently_accessed';
export {
ChromeRecentlyAccessed,
ChromeRecentlyAccessedHistoryItem,
PersistedLog,
} from './recently_accessed';
export { ChromeNavControl, ChromeNavControls } from './nav_controls';
export { ChromeDocTitle } from './doc_title';
export { RightNavigationOrder } from './constants';
Expand Down
2 changes: 2 additions & 0 deletions src/core/public/chrome/recently_accessed/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,5 @@ export {
ChromeRecentlyAccessedHistoryItem,
RecentlyAccessedService,
} from './recently_accessed_service';

export { PersistedLog } from './persisted_log';
2 changes: 2 additions & 0 deletions src/core/public/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ import {
RightNavigationButtonProps,
ChromeRegistrationNavLink,
ChromeNavGroupUpdater,
PersistedLog,
NavGroupItemInMap,
fulfillRegistrationLinksToChromeNavLinks,
} from './chrome';
Expand Down Expand Up @@ -375,6 +376,7 @@ export {
RightNavigationButtonProps,
ChromeRegistrationNavLink,
ChromeNavGroupUpdater,
PersistedLog,
NavGroupItemInMap,
fulfillRegistrationLinksToChromeNavLinks,
};
Expand Down
2 changes: 2 additions & 0 deletions src/plugins/workspace/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,4 +182,6 @@ export const WORKSPACE_USE_CASES = Object.freeze({
},
});

export const MAX_WORKSPACE_PICKER_NUM = 3;
export const RECENT_WORKSPACES_KEY = 'recentWorkspaces';
export const CURRENT_USER_PLACEHOLDER = '%me%';
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

.custom-title {
text-transform: none !important;
font-size: 15px !important;
}

.text-ellipsis {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
width: 220px;
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,50 @@ import { fireEvent, render, screen, waitFor } from '@testing-library/react';
import { WorkspaceMenu } from './workspace_menu';
import { coreMock } from '../../../../../core/public/mocks';
import { CoreStart } from '../../../../../core/public';
import { BehaviorSubject, of } from 'rxjs';
import { IntlProvider } from 'react-intl';
import { recentWorkspaceManager } from '../../recent_workspace_manager';
import { WORKSPACE_USE_CASES } from '../../../common/constants';

describe('<WorkspaceMenu />', () => {
let coreStartMock: CoreStart;
const navigateToApp = jest.fn();
const registeredUseCases$ = new BehaviorSubject([
WORKSPACE_USE_CASES.observability,
WORKSPACE_USE_CASES['security-analytics'],
WORKSPACE_USE_CASES.analytics,
WORKSPACE_USE_CASES.search,
]);

beforeEach(() => {
coreStartMock = coreMock.createStart();
coreStartMock.application.capabilities = {
navLinks: {},
management: {},
catalogue: {},
savedObjectsManagement: {},
workspaces: { permissionEnabled: true },
dashboards: { isDashboardAdmin: true },
};
coreStartMock.application = {
...coreStartMock.application,
navigateToApp,
};

coreStartMock.workspaces.initialized$.next(true);
jest.spyOn(coreStartMock.application, 'getUrlForApp').mockImplementation((appId: string) => {
return `https://test.com/app/${appId}`;
});
});

const WorkspaceMenuCreatorComponent = () => {
return (
<IntlProvider locale="en">
<WorkspaceMenu coreStart={coreStartMock} registeredUseCases$={registeredUseCases$} />
</IntlProvider>
);
};

afterEach(() => {
jest.clearAllMocks();
jest.restoreAllMocks();
Expand All @@ -32,34 +64,72 @@ describe('<WorkspaceMenu />', () => {
{ id: 'workspace-2', name: 'workspace 2' },
]);

render(<WorkspaceMenu coreStart={coreStartMock} />);
fireEvent.click(screen.getByText(/select a workspace/i));
render(<WorkspaceMenuCreatorComponent />);
const selectButton = screen.getByTestId('workspace-select-button');
fireEvent.click(selectButton);

expect(screen.getByText(/all workspaces/i)).toBeInTheDocument();
expect(screen.getByTestId('context-menu-item-all-workspace-1')).toBeInTheDocument();
expect(screen.getByTestId('context-menu-item-all-workspace-2')).toBeInTheDocument();
});

it('should display a list of recent workspaces in the dropdown', () => {
jest.spyOn(recentWorkspaceManager, 'getRecentWorkspaces').mockReturnValue([
{ id: 'workspace-1', timestamp: 1234567890 },
{ id: 'workspace-2', timestamp: 1234567899 },
]);

coreStartMock.workspaces.workspaceList$.next([
{ id: 'workspace-1', name: 'workspace 1' },
{ id: 'workspace-2', name: 'workspace 2' },
]);

render(<WorkspaceMenuCreatorComponent />);

const selectButton = screen.getByTestId('workspace-select-button');
fireEvent.click(selectButton);

expect(screen.getByText(/workspace 1/i)).toBeInTheDocument();
expect(screen.getByText(/workspace 2/i)).toBeInTheDocument();
expect(screen.getByText(/recent workspaces/i)).toBeInTheDocument();
expect(screen.getByTestId('context-menu-item-recent-workspace-1')).toBeInTheDocument();
expect(screen.getByTestId('context-menu-item-recent-workspace-2')).toBeInTheDocument();
});

it('should display current workspace name', () => {
coreStartMock.workspaces.currentWorkspace$.next({ id: 'workspace-1', name: 'workspace 1' });
render(<WorkspaceMenu coreStart={coreStartMock} />);
expect(screen.getByText(/workspace 1/i)).toBeInTheDocument();
it('should display current workspace name and use case name', () => {
coreStartMock.workspaces.currentWorkspace$.next({
id: 'workspace-1',
name: 'workspace 1',
features: ['use-case-observability'],
});
render(<WorkspaceMenuCreatorComponent />);

fireEvent.click(screen.getByTestId('current-workspace-button'));
expect(screen.getByTestId('context-menu-current-workspace-name')).toBeInTheDocument();
expect(screen.getByTestId('context-menu-current-use-case')).toBeInTheDocument();
expect(screen.getByText('Observability')).toBeInTheDocument();
});

it('should close the workspace dropdown list', async () => {
render(<WorkspaceMenu coreStart={coreStartMock} />);
fireEvent.click(screen.getByText(/select a workspace/i));
render(<WorkspaceMenuCreatorComponent />);

fireEvent.click(screen.getByTestId('workspace-select-button'));

expect(screen.getByLabelText(/close workspace dropdown/i)).toBeInTheDocument();
fireEvent.click(screen.getByLabelText(/close workspace dropdown/i));
expect(screen.getByText(/all workspaces/i)).toBeInTheDocument();
fireEvent.click(screen.getByTestId('workspace-select-button'));
await waitFor(() => {
expect(screen.queryByLabelText(/close workspace dropdown/i)).not.toBeInTheDocument();
expect(screen.queryByText(/all workspaces/i)).not.toBeInTheDocument();
});
});

it('should navigate to the workspace', () => {
jest.spyOn(coreStartMock.chrome.navGroup, 'getNavGroupsMap$').mockImplementation(() => {
return of({
observability: {
navLinks: [{ id: 'dashboard', title: '', description: '' }],
},
});
});
coreStartMock.workspaces.workspaceList$.next([
{ id: 'workspace-1', name: 'workspace 1' },
{ id: 'workspace-2', name: 'workspace 2' },
{ id: 'workspace-1', name: 'workspace 1', features: ['use-case-observability'] },
]);

const originalLocation = window.location;
Expand All @@ -69,52 +139,63 @@ describe('<WorkspaceMenu />', () => {
},
});

render(<WorkspaceMenu coreStart={coreStartMock} />);
fireEvent.click(screen.getByText(/select a workspace/i));
render(<WorkspaceMenuCreatorComponent />);
fireEvent.click(screen.getByTestId('workspace-select-button'));
fireEvent.click(screen.getByText(/workspace 1/i));

expect(window.location.assign).toHaveBeenCalledWith(
'https://test.com/w/workspace-1/app/workspace_detail'
'https://test.com/w/workspace-1/app/discover'
);

Object.defineProperty(window, 'location', {
value: originalLocation,
});
});

it('should navigate to create workspace page', () => {
it('should navigate to workspace management page', () => {
coreStartMock.workspaces.currentWorkspace$.next({
id: 'workspace-1',
name: 'workspace 1',
features: ['use-case-observability'],
});
const originalLocation = window.location;
Object.defineProperty(window, 'location', {
value: {
assign: jest.fn(),
},
});
render(<WorkspaceMenuCreatorComponent />);

render(<WorkspaceMenu coreStart={coreStartMock} />);
fireEvent.click(screen.getByText(/select a workspace/i));
fireEvent.click(screen.getByText(/create workspace/i));
expect(window.location.assign).toHaveBeenCalledWith('https://test.com/app/workspace_create');

fireEvent.click(screen.getByTestId('current-workspace-button'));
const button = screen.getByText(/Manage workspace/i);
fireEvent.click(button);
expect(window.location.assign).toHaveBeenCalledWith(
'https://test.com/w/workspace-1/app/workspace_detail'
);
Object.defineProperty(window, 'location', {
value: originalLocation,
});
});

it('should navigate to workspace list page', () => {
const originalLocation = window.location;
Object.defineProperty(window, 'location', {
value: {
assign: jest.fn(),
},
});
it('should navigate to workspaces management page', () => {
render(<WorkspaceMenuCreatorComponent />);
fireEvent.click(screen.getByTestId('workspace-select-button'));
fireEvent.click(screen.getByText(/manage workspaces/i));
expect(coreStartMock.application.navigateToApp).toHaveBeenCalledWith('workspace_list');
});

render(<WorkspaceMenu coreStart={coreStartMock} />);
fireEvent.click(screen.getByText(/select a workspace/i));
fireEvent.click(screen.getByText(/all workspace/i));
expect(window.location.assign).toHaveBeenCalledWith('https://test.com/app/workspace_list');
it('should navigate to create workspace page', () => {
render(<WorkspaceMenuCreatorComponent />);
fireEvent.click(screen.getByTestId('workspace-select-button'));
fireEvent.click(screen.getByText(/create workspace/i));
expect(coreStartMock.application.navigateToApp).toHaveBeenCalledWith('workspace_create');
});

Object.defineProperty(window, 'location', {
value: originalLocation,
});
it('should navigate to workspace list page', () => {
render(<WorkspaceMenuCreatorComponent />);

fireEvent.click(screen.getByTestId('workspace-select-button'));
fireEvent.click(screen.getByText(/View all/i));
expect(coreStartMock.application.navigateToApp).toHaveBeenCalledWith('workspace_list');
});
});
Loading
Loading