diff --git a/changelogs/fragments/7802.yml b/changelogs/fragments/7802.yml new file mode 100644 index 000000000000..2937e55931ae --- /dev/null +++ b/changelogs/fragments/7802.yml @@ -0,0 +1,2 @@ +feat: +- Add home icon in left bottom ([#7802](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/7802)) \ No newline at end of file diff --git a/src/core/public/chrome/ui/header/__snapshots__/collapsible_nav_group_enabled.test.tsx.snap b/src/core/public/chrome/ui/header/__snapshots__/collapsible_nav_group_enabled.test.tsx.snap index 95e501650f08..7f896674faac 100644 --- a/src/core/public/chrome/ui/header/__snapshots__/collapsible_nav_group_enabled.test.tsx.snap +++ b/src/core/public/chrome/ui/header/__snapshots__/collapsible_nav_group_enabled.test.tsx.snap @@ -236,7 +236,7 @@ exports[` should render correctly 1`] = ` class="euiHorizontalRule euiHorizontalRule--full" />
@@ -260,7 +260,7 @@ exports[` should render correctly 2`] = ` class="euiHorizontalRule euiHorizontalRule--full" />
@@ -333,7 +333,7 @@ exports[` should show all use case by default and class="euiHorizontalRule euiHorizontalRule--full" />
@@ -406,7 +406,7 @@ exports[` should show all use case when current na class="euiHorizontalRule euiHorizontalRule--full" />
diff --git a/src/core/public/chrome/ui/header/collapsible_nav_group_enabled.scss b/src/core/public/chrome/ui/header/collapsible_nav_group_enabled.scss index 39978cc2eff6..978ed743a24b 100644 --- a/src/core/public/chrome/ui/header/collapsible_nav_group_enabled.scss +++ b/src/core/public/chrome/ui/header/collapsible_nav_group_enabled.scss @@ -62,6 +62,7 @@ .bottom-container { padding: 0 $euiSize; display: flex; + -ms-overflow-style: -ms-autohiding-scrollbar; &.bottom-container-collapsed { flex-direction: column; diff --git a/src/core/public/chrome/ui/header/collapsible_nav_group_enabled.tsx b/src/core/public/chrome/ui/header/collapsible_nav_group_enabled.tsx index af67974ecb9d..d196e760e43b 100644 --- a/src/core/public/chrome/ui/header/collapsible_nav_group_enabled.tsx +++ b/src/core/public/chrome/ui/header/collapsible_nav_group_enabled.tsx @@ -435,6 +435,7 @@ export function CollapsibleNavGroupEnabled({
', () => { + it('should call chrome.navGroup.setCurrentNavGroup and application.navigateToApp methods from core service when click', () => { + const coreStartMock = coreMock.createStart(); + const { container } = render(); + const component = container.children[0]; + fireEvent.click(component); + expect(coreStartMock.application.navigateToApp).toBeCalledWith('foo'); + }); +}); diff --git a/src/plugins/home/public/application/components/home_icon.tsx b/src/plugins/home/public/application/components/home_icon.tsx new file mode 100644 index 000000000000..705b89df9bab --- /dev/null +++ b/src/plugins/home/public/application/components/home_icon.tsx @@ -0,0 +1,20 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import React from 'react'; +import { EuiButtonIcon } from '@elastic/eui'; +import { CoreStart } from 'opensearch-dashboards/public'; + +export function HomeIcon({ core, appId }: { core: CoreStart; appId: string }) { + return ( + { + core.application.navigateToApp(appId); + }} + /> + ); +} diff --git a/src/plugins/home/public/plugin.ts b/src/plugins/home/public/plugin.ts index fe1099a8e635..cf499c19ddb9 100644 --- a/src/plugins/home/public/plugin.ts +++ b/src/plugins/home/public/plugin.ts @@ -37,6 +37,7 @@ import { } from 'opensearch-dashboards/public'; import { i18n } from '@osd/i18n'; import { first } from 'rxjs/operators'; +import React from 'react'; import { Branding } from 'src/core/types'; import { @@ -70,6 +71,8 @@ import { ContentManagementPluginStart, } from '../../content_management/public'; import { initHome, setupHome } from './application/home_render'; +import { toMountPoint } from '../../opensearch_dashboards_react/public'; +import { HomeIcon } from './application/components/home_icon'; export interface HomePluginStartDependencies { data: DataPublicPluginStart; @@ -151,9 +154,7 @@ export class HomePublicPlugin core.application.register({ id: PLUGIN_ID, title: 'Home', - navLinkStatus: core.chrome.navGroup.getNavGroupEnabled() - ? undefined - : AppNavLinkStatus.hidden, + navLinkStatus: AppNavLinkStatus.hidden, mount: async (params: AppMountParameters) => { const [coreStart] = await core.getStartServices(); setCommonService(); @@ -166,13 +167,6 @@ export class HomePublicPlugin workspaceAvailability: WorkspaceAvailability.outsideWorkspace, }); - core.chrome.navGroup.addNavLinksToGroup(DEFAULT_NAV_GROUPS.all, [ - { - id: PLUGIN_ID, - title: 'Home', - }, - ]); - // Register import sample data as a standalone app so that it is available inside workspace. core.application.register({ id: IMPORT_SAMPLE_DATA_APP_ID, @@ -255,6 +249,18 @@ export class HomePublicPlugin }); } + if (core.chrome.navGroup.getNavGroupEnabled()) { + core.chrome.navControls.registerLeftBottom({ + order: 0, + mount: toMountPoint( + React.createElement(HomeIcon, { + core, + appId: PLUGIN_ID, + }) + ), + }); + } + return { featureCatalogue: this.featuresCatalogueRegistry, getSavedHomepageLoader: () => this.sectionTypeService.getSavedHomepageLoader(), diff --git a/src/plugins/saved_objects_management/public/plugin.ts b/src/plugins/saved_objects_management/public/plugin.ts index 2f69c1587c65..104507f028e2 100644 --- a/src/plugins/saved_objects_management/public/plugin.ts +++ b/src/plugins/saved_objects_management/public/plugin.ts @@ -198,7 +198,7 @@ export class SavedObjectsManagementPlugin core.chrome.navGroup.addNavLinksToGroup(DEFAULT_NAV_GROUPS.settingsAndSetup, [ { id: APP_ID, - order: 300, + order: 400, }, ]); diff --git a/src/plugins/visualize/public/plugin.ts b/src/plugins/visualize/public/plugin.ts index 311fc20ed85d..835bfb306796 100644 --- a/src/plugins/visualize/public/plugin.ts +++ b/src/plugins/visualize/public/plugin.ts @@ -228,11 +228,16 @@ export class VisualizePlugin }, }); + const titleInLeftNav = i18n.translate('visualize.leftNav.visualizeTitle', { + defaultMessage: 'Visualizations', + }); + core.chrome.navGroup.addNavLinksToGroup(DEFAULT_NAV_GROUPS.observability, [ { id: visualizeAppId, category: DEFAULT_APP_CATEGORIES.visualizeAndReport, order: 200, + title: titleInLeftNav, }, ]); core.chrome.navGroup.addNavLinksToGroup(DEFAULT_NAV_GROUPS['security-analytics'], [ @@ -240,13 +245,15 @@ export class VisualizePlugin id: visualizeAppId, category: DEFAULT_APP_CATEGORIES.visualizeAndReport, order: 200, + title: titleInLeftNav, }, ]); core.chrome.navGroup.addNavLinksToGroup(DEFAULT_NAV_GROUPS.essentials, [ { id: visualizeAppId, - category: DEFAULT_APP_CATEGORIES.visualizeAndReport, - order: 200, + category: undefined, + order: 400, + title: titleInLeftNav, }, ]); core.chrome.navGroup.addNavLinksToGroup(DEFAULT_NAV_GROUPS.search, [ @@ -254,6 +261,7 @@ export class VisualizePlugin id: visualizeAppId, category: DEFAULT_APP_CATEGORIES.analyzeSearch, order: 400, + title: titleInLeftNav, }, ]); core.chrome.navGroup.addNavLinksToGroup(DEFAULT_NAV_GROUPS.all, [ @@ -261,6 +269,7 @@ export class VisualizePlugin id: visualizeAppId, category: undefined, order: 400, + title: titleInLeftNav, }, ]); diff --git a/src/plugins/workspace/public/components/workspace_menu/workspace_menu.tsx b/src/plugins/workspace/public/components/workspace_menu/workspace_menu.tsx index c2e8e39331d4..2ef12e3f12bb 100644 --- a/src/plugins/workspace/public/components/workspace_menu/workspace_menu.tsx +++ b/src/plugins/workspace/public/components/workspace_menu/workspace_menu.tsx @@ -108,7 +108,7 @@ export const WorkspaceMenu = ({ coreStart, registeredUseCases$ }: Props) => { }; const currentWorkspaceButton = currentWorkspace ? ( - + { expect.arrayContaining([ { id: 'workspace_list', - order: 150, - title: 'Workspace settings', + order: 350, + title: 'Workspaces', }, ]) ); }); - it('#setup should register workspace detail with a visible application and register to all nav group', async () => { + it('#setup should register workspace detail', async () => { const setupMock = coreMock.createSetup(); setupMock.chrome.navGroup.getNavGroupEnabled.mockReturnValue(true); const workspacePlugin = new WorkspacePlugin(); @@ -189,20 +189,8 @@ describe('Workspace plugin', () => { expect(setupMock.application.register).toHaveBeenCalledWith( expect.objectContaining({ id: 'workspace_detail', - navLinkStatus: AppNavLinkStatus.hidden, }) ); - - expect(setupMock.chrome.navGroup.addNavLinksToGroup).toHaveBeenCalledWith( - DEFAULT_NAV_GROUPS.all, - expect.arrayContaining([ - { - id: 'workspace_detail', - title: 'Overview', - order: 100, - }, - ]) - ); }); it('#setup should register workspace initial with a visible application', async () => { diff --git a/src/plugins/workspace/public/plugin.ts b/src/plugins/workspace/public/plugin.ts index 4731e9d205b7..1617744fa9ff 100644 --- a/src/plugins/workspace/public/plugin.ts +++ b/src/plugins/workspace/public/plugin.ts @@ -113,19 +113,7 @@ export class WorkspacePlugin this.registeredUseCases$, ]).subscribe(([currentWorkspace, registeredUseCases]) => { if (currentWorkspace) { - const isAllUseCase = - getFirstUseCaseOfFeatureConfigs(currentWorkspace.features || []) === ALL_USE_CASE_ID; this.appUpdater$.next((app) => { - // When in all workspace, the home should be replaced by workspace detail page - if (app.id === 'home' && isAllUseCase) { - return { navLinkStatus: AppNavLinkStatus.hidden }; - } - - // show the overview page in all use case - if (app.id === WORKSPACE_DETAIL_APP_ID && isAllUseCase) { - return { navLinkStatus: AppNavLinkStatus.visible }; - } - if (isAppAccessibleInWorkspace(app, currentWorkspace, registeredUseCases)) { return; } @@ -356,7 +344,6 @@ export class WorkspacePlugin title: i18n.translate('workspace.settings.workspaceDetail', { defaultMessage: 'Workspace Detail', }), - navLinkStatus: AppNavLinkStatus.hidden, async mount(params: AppMountParameters) { const { renderDetailApp } = await import('./application'); return mountWorkspaceApp(params, renderDetailApp); @@ -425,16 +412,6 @@ export class WorkspacePlugin workspaceAvailability: WorkspaceAvailability.outsideWorkspace, }); - core.chrome.navGroup.addNavLinksToGroup(DEFAULT_NAV_GROUPS.all, [ - { - id: WORKSPACE_DETAIL_APP_ID, - order: 100, - title: i18n.translate('workspace.nav.workspaceDetail.title', { - defaultMessage: 'Overview', - }), - }, - ]); - /** * register workspace column into saved objects table */ @@ -446,9 +423,9 @@ export class WorkspacePlugin core.chrome.navGroup.addNavLinksToGroup(DEFAULT_NAV_GROUPS.settingsAndSetup, [ { id: WORKSPACE_LIST_APP_ID, - order: 150, - title: i18n.translate('workspace.settings.workspaceSettings', { - defaultMessage: 'Workspace settings', + order: 350, + title: i18n.translate('workspace.settings.workspaces', { + defaultMessage: 'Workspaces', }), }, ]); diff --git a/src/plugins/workspace/public/services/use_case_service.test.ts b/src/plugins/workspace/public/services/use_case_service.test.ts index 00938fd7d60d..17f977474f07 100644 --- a/src/plugins/workspace/public/services/use_case_service.test.ts +++ b/src/plugins/workspace/public/services/use_case_service.test.ts @@ -5,14 +5,17 @@ import { BehaviorSubject } from 'rxjs'; import { first } from 'rxjs/operators'; -import { chromeServiceMock } from '../../../../core/public/mocks'; +import { chromeServiceMock, coreMock } from '../../../../core/public/mocks'; import { ALL_USE_CASE_ID, + DEFAULT_APP_CATEGORIES, DEFAULT_NAV_GROUPS, NavGroupItemInMap, NavGroupType, } from '../../../../core/public'; import { UseCaseService } from './use_case_service'; +import { waitFor } from '@testing-library/dom'; +import { WORKSPACE_DETAIL_APP_ID } from '../../common/constants'; const mockNavGroupsMap = { system: { @@ -61,6 +64,54 @@ const setupUseCaseStart = (options?: { navGroupEnabled?: boolean }) => { }; describe('UseCaseService', () => { + describe('#setup', () => { + it('should add manage workspace category to current use case', async () => { + const useCaseService = new UseCaseService(); + const coreSetup = coreMock.createSetup(); + const navGroupMap$ = new BehaviorSubject>({}); + const coreStartMock = coreMock.createStart(); + coreSetup.getStartServices.mockResolvedValue([coreStartMock, {}, {}]); + coreStartMock.chrome.navGroup.getNavGroupsMap$.mockReturnValue(navGroupMap$); + useCaseService.setup(coreSetup); + const navGroupInfo = { + ...DEFAULT_NAV_GROUPS.all, + navLinks: [], + }; + navGroupMap$.next({ + [ALL_USE_CASE_ID]: navGroupInfo, + }); + coreSetup.workspaces.currentWorkspace$.next({ + id: ALL_USE_CASE_ID, + name: ALL_USE_CASE_ID, + features: [`use-case-${ALL_USE_CASE_ID}`], + }); + await waitFor(() => { + expect(coreSetup.chrome.navGroup.addNavLinksToGroup).toBeCalledWith(navGroupInfo, [ + { + id: 'dataSources_core', + category: DEFAULT_APP_CATEGORIES.manageWorkspace, + order: 100, + }, + { + id: 'indexPatterns', + category: DEFAULT_APP_CATEGORIES.manageWorkspace, + order: 200, + }, + { + id: 'objects', + category: DEFAULT_APP_CATEGORIES.manageWorkspace, + order: 300, + }, + { + id: WORKSPACE_DETAIL_APP_ID, + category: DEFAULT_APP_CATEGORIES.manageWorkspace, + order: 400, + title: 'Workspace settings', + }, + ]); + }); + }); + }); describe('#start', () => { it('should return built in use cases when nav group disabled', async () => { const { useCaseStart } = setupUseCaseStart({ diff --git a/src/plugins/workspace/public/services/use_case_service.ts b/src/plugins/workspace/public/services/use_case_service.ts index 59764a256480..7807c4af3881 100644 --- a/src/plugins/workspace/public/services/use_case_service.ts +++ b/src/plugins/workspace/public/services/use_case_service.ts @@ -5,6 +5,7 @@ import { combineLatest, Observable, Subscription } from 'rxjs'; import { distinctUntilChanged, map } from 'rxjs/operators'; +import { i18n } from '@osd/i18n'; import { ChromeStart, @@ -15,7 +16,7 @@ import { DEFAULT_NAV_GROUPS, ALL_USE_CASE_ID, } from '../../../../core/public'; -import { WORKSPACE_USE_CASES } from '../../common/constants'; +import { WORKSPACE_DETAIL_APP_ID, WORKSPACE_USE_CASES } from '../../common/constants'; import { convertNavGroupToWorkspaceUseCase, getFirstUseCaseOfFeatureConfigs, @@ -78,6 +79,14 @@ export class UseCaseService { category: DEFAULT_APP_CATEGORIES.manageWorkspace, order: 300, }, + { + id: WORKSPACE_DETAIL_APP_ID, + category: DEFAULT_APP_CATEGORIES.manageWorkspace, + order: 400, + title: i18n.translate('workspace.settings.workspaceSettings', { + defaultMessage: 'Workspace settings', + }), + }, ]); } });