From 055480fa4e6fa33ded026d44b48c7d395d65954e Mon Sep 17 00:00:00 2001 From: Yulong Ruan Date: Fri, 22 Sep 2023 11:11:56 +0800 Subject: [PATCH 01/14] add util function to filter workspace feature by wildcard Signed-off-by: Yulong Ruan --- src/plugins/workspace/public/utils.test.ts | 93 ++++++++++++++++++++++ src/plugins/workspace/public/utils.ts | 56 +++++++++++++ 2 files changed, 149 insertions(+) create mode 100644 src/plugins/workspace/public/utils.test.ts create mode 100644 src/plugins/workspace/public/utils.ts diff --git a/src/plugins/workspace/public/utils.test.ts b/src/plugins/workspace/public/utils.test.ts new file mode 100644 index 000000000000..510a775cd745 --- /dev/null +++ b/src/plugins/workspace/public/utils.test.ts @@ -0,0 +1,93 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { featureMatchesConfig } from './utils'; + +describe('workspace utils: featureMatchesConfig', () => { + it('feature configured with `*` should match any features', () => { + const match = featureMatchesConfig(['*']); + expect(match({ id: 'dev_tools', category: { id: 'management', label: 'Management' } })).toBe( + true + ); + expect( + match({ id: 'discover', category: { id: 'opensearchDashboards', label: 'Library' } }) + ).toBe(true); + }); + + it('should NOT match the config if feature id not matches', () => { + const match = featureMatchesConfig(['discover', 'dashboards', 'visualize']); + expect(match({ id: 'dev_tools', category: { id: 'management', label: 'Management' } })).toBe( + false + ); + }); + + it('should match the config if feature id matches', () => { + const match = featureMatchesConfig(['discover', 'dashboards', 'visualize']); + expect( + match({ id: 'discover', category: { id: 'opensearchDashboards', label: 'Library' } }) + ).toBe(true); + }); + + it('should match the config if feature category matches', () => { + const match = featureMatchesConfig(['discover', 'dashboards', '@management', 'visualize']); + expect(match({ id: 'dev_tools', category: { id: 'management', label: 'Management' } })).toBe( + true + ); + }); + + it('should match any features but not the excluded feature id', () => { + const match = featureMatchesConfig(['*', '!discover']); + expect(match({ id: 'dev_tools', category: { id: 'management', label: 'Management' } })).toBe( + true + ); + expect( + match({ id: 'discover', category: { id: 'opensearchDashboards', label: 'Library' } }) + ).toBe(false); + }); + + it('should match any features but not the excluded feature category', () => { + const match = featureMatchesConfig(['*', '!@management']); + expect(match({ id: 'dev_tools', category: { id: 'management', label: 'Management' } })).toBe( + false + ); + expect(match({ id: 'integrations', category: { id: 'management', label: 'Management' } })).toBe( + false + ); + expect( + match({ id: 'discover', category: { id: 'opensearchDashboards', label: 'Library' } }) + ).toBe(true); + }); + + it('should NOT match the excluded feature category', () => { + const match = featureMatchesConfig(['!@management']); + expect(match({ id: 'dev_tools', category: { id: 'management', label: 'Management' } })).toBe( + false + ); + expect(match({ id: 'integrations', category: { id: 'management', label: 'Management' } })).toBe( + false + ); + }); + + it('should match features of a category but NOT the excluded feature', () => { + const match = featureMatchesConfig(['@management', '!dev_tools']); + expect(match({ id: 'dev_tools', category: { id: 'management', label: 'Management' } })).toBe( + false + ); + expect(match({ id: 'integrations', category: { id: 'management', label: 'Management' } })).toBe( + true + ); + }); + + it('a config presents later in the config array should override the previous config', () => { + // though `dev_tools` is excluded, but this config will override by '@management' as dev_tools has category 'management' + const match = featureMatchesConfig(['!dev_tools', '@management']); + expect(match({ id: 'dev_tools', category: { id: 'management', label: 'Management' } })).toBe( + true + ); + expect(match({ id: 'integrations', category: { id: 'management', label: 'Management' } })).toBe( + true + ); + }); +}); diff --git a/src/plugins/workspace/public/utils.ts b/src/plugins/workspace/public/utils.ts new file mode 100644 index 000000000000..f7c59dbfc53c --- /dev/null +++ b/src/plugins/workspace/public/utils.ts @@ -0,0 +1,56 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { AppCategory } from '../../../core/public'; + +/** + * Given a list of feature config, check if a feature matches config + * Rules: + * 1. `*` matches any feature + * 2. config starts with `@` matches category, for example, @management matches any feature of `management` category + * 3. to match a specific feature, just use the feature id, such as `discover` + * 4. to exclude feature or category, use `!@management` or `!discover` + * 5. the order of featureConfig array matters, from left to right, the later config override the previous config, + * for example, ['!@management', '*'] matches any feature because '*' overrides the previous setting: '!@management' + */ +export const featureMatchesConfig = (featureConfigs: string[]) => ({ + id, + category, +}: { + id: string; + category?: AppCategory; +}) => { + let matched = false; + + for (const featureConfig of featureConfigs) { + // '*' matches any feature + if (featureConfig === '*') { + matched = true; + } + + // The config starts with `@` matches a category + if (category && featureConfig === `@${category.id}`) { + matched = true; + } + + // The config matches a feature id + if (featureConfig === id) { + matched = true; + } + + // If a config starts with `!`, such feature or category will be excluded + if (featureConfig.startsWith('!')) { + if (category && featureConfig === `!@${category.id}`) { + matched = false; + } + + if (featureConfig === `!${id}`) { + matched = false; + } + } + } + + return matched; +}; From 93eec7199046251d6fa6ed2abe28305669e39495 Mon Sep 17 00:00:00 2001 From: yuye-aws Date: Fri, 22 Sep 2023 17:41:59 +0800 Subject: [PATCH 02/14] resolve conflict Signed-off-by: yuye-aws --- src/core/public/chrome/chrome_service.mock.ts | 2 + src/core/public/chrome/chrome_service.tsx | 2 +- src/core/public/chrome/nav_links/nav_link.ts | 9 +- .../chrome/nav_links/nav_links_service.ts | 39 +- .../collapsible_nav.test.tsx.snap | 2310 ++--- .../header/__snapshots__/header.test.tsx.snap | 545 +- .../chrome/ui/header/collapsible_nav.test.tsx | 4 +- .../chrome/ui/header/collapsible_nav.tsx | 194 +- src/core/public/chrome/ui/header/nav_link.tsx | 56 +- src/core/public/index.ts | 2 +- .../workspace/workspaces_service.mock.ts | 7 +- .../public/workspace/workspaces_service.ts | 17 +- src/core/types/workspace.ts | 4 + src/core/utils/default_app_categories.ts | 10 +- .../dashboard_listing.test.tsx.snap | 8213 ++++++++++++++++- .../dashboard_top_nav.test.tsx.snap | 12 + src/plugins/dev_tools/public/plugin.ts | 2 +- .../objects_table/saved_objects_table.tsx | 2 +- src/plugins/workspace/public/plugin.ts | 59 +- 19 files changed, 9983 insertions(+), 1506 deletions(-) diff --git a/src/core/public/chrome/chrome_service.mock.ts b/src/core/public/chrome/chrome_service.mock.ts index 14b516ff95bc..b6f130270b8b 100644 --- a/src/core/public/chrome/chrome_service.mock.ts +++ b/src/core/public/chrome/chrome_service.mock.ts @@ -38,6 +38,8 @@ const createStartContractMock = () => { getHeaderComponent: jest.fn(), navLinks: { getNavLinks$: jest.fn(), + getFilteredNavLinks$: jest.fn(), + setFilteredNavLinks: jest.fn(), has: jest.fn(), get: jest.fn(), getAll: jest.fn(), diff --git a/src/core/public/chrome/chrome_service.tsx b/src/core/public/chrome/chrome_service.tsx index d094d86360ef..05904063cc93 100644 --- a/src/core/public/chrome/chrome_service.tsx +++ b/src/core/public/chrome/chrome_service.tsx @@ -250,7 +250,7 @@ export class ChromeService { homeHref={http.basePath.prepend('/app/home')} isVisible$={this.isVisible$} opensearchDashboardsVersion={injectedMetadata.getOpenSearchDashboardsVersion()} - navLinks$={navLinks.getNavLinks$()} + navLinks$={navLinks.getFilteredNavLinks$()} recentlyAccessed$={recentlyAccessed.get$()} navControlsLeft$={navControls.getLeft$()} navControlsCenter$={navControls.getCenter$()} diff --git a/src/core/public/chrome/nav_links/nav_link.ts b/src/core/public/chrome/nav_links/nav_link.ts index cddd45234514..19e2fd2eddab 100644 --- a/src/core/public/chrome/nav_links/nav_link.ts +++ b/src/core/public/chrome/nav_links/nav_link.ts @@ -93,8 +93,10 @@ export interface ChromeNavLink { * Disables a link from being clickable. * * @internalRemarks - * This is only used by the ML and Graph plugins currently. They use this field + * This is used by the ML and Graph plugins. They use this field * to disable the nav link when the license is expired. + * This is also used by recently visited category in left menu + * to disable "No recently visited items". */ readonly disabled?: boolean; @@ -102,6 +104,11 @@ export interface ChromeNavLink { * Hides a link from the navigation. */ readonly hidden?: boolean; + + /** + * Links can be navigated through url. + */ + readonly externalLink?: boolean; } /** @public */ diff --git a/src/core/public/chrome/nav_links/nav_links_service.ts b/src/core/public/chrome/nav_links/nav_links_service.ts index 93c138eac62c..ddbef0beed6d 100644 --- a/src/core/public/chrome/nav_links/nav_links_service.ts +++ b/src/core/public/chrome/nav_links/nav_links_service.ts @@ -53,6 +53,16 @@ export interface ChromeNavLinks { */ getNavLinks$(): Observable>>; + /** + * Set an observable for a sorted list of filtered navlinks. + */ + getFilteredNavLinks$(): Observable>>; + + /** + * Set filtered navlinks. + */ + setFilteredNavLinks(filteredNavLinks: ReadonlyMap): void; + /** * Get the state of a navlink at this point in time. * @param id @@ -116,6 +126,9 @@ type LinksUpdater = (navLinks: Map) => Map | undefined>( + undefined + ); public start({ application, http }: StartDeps): ChromeNavLinks { const appLinks$ = application.applications$.pipe( @@ -140,8 +153,8 @@ export class NavLinksService { return linkUpdaters.reduce((links, updater) => updater(links), appLinks); }) ) - .subscribe((navlinks) => { - navLinks$.next(navlinks); + .subscribe((navLinks) => { + navLinks$.next(navLinks); }); const forceAppSwitcherNavigation$ = new BehaviorSubject(false); @@ -151,6 +164,21 @@ export class NavLinksService { return navLinks$.pipe(map(sortNavLinks), takeUntil(this.stop$)); }, + setFilteredNavLinks: (filteredNavLinks: ReadonlyMap) => { + this.filteredNavLinks$.next(filteredNavLinks); + }, + + getFilteredNavLinks$: () => { + return combineLatest([navLinks$, this.filteredNavLinks$]).pipe( + map(([navLinks, filteredNavLinks]) => + filteredNavLinks === undefined + ? sortNavLinks(navLinks) + : sortChromeNavLinks(filteredNavLinks) + ), + takeUntil(this.stop$) + ); + }, + get(id: string) { const link = navLinks$.value.get(id); return link && link.properties; @@ -215,3 +243,10 @@ function sortNavLinks(navLinks: ReadonlyMap) { 'order' ); } + +function sortChromeNavLinks(chromeNavLinks: ReadonlyMap) { + return sortBy( + [...chromeNavLinks.values()].map((link) => link as Readonly), + 'order' + ); +} diff --git a/src/core/public/chrome/ui/header/__snapshots__/collapsible_nav.test.tsx.snap b/src/core/public/chrome/ui/header/__snapshots__/collapsible_nav.test.tsx.snap index f0cd8afddfa3..66c3463b2790 100644 --- a/src/core/public/chrome/ui/header/__snapshots__/collapsible_nav.test.tsx.snap +++ b/src/core/public/chrome/ui/header/__snapshots__/collapsible_nav.test.tsx.snap @@ -242,7 +242,7 @@ exports[`CollapsibleNav renders links grouped by category 1`] = ` "euiIconType": "managementApp", "id": "management", "label": "Management", - "order": 5000, + "order": 6000, }, "data-test-subj": "monitoring", "href": "monitoring", @@ -566,255 +566,269 @@ exports[`CollapsibleNav renders links grouped by category 1`] = ` className="euiHorizontalRule euiHorizontalRule--full" /> - - - - -

- Recently viewed -

-
-
- - } - className="euiCollapsibleNavGroup euiCollapsibleNavGroup--light euiCollapsibleNavGroup--withHeading" - data-test-subj="collapsibleNavGroup-recentlyViewed" - id="mockId" - initialIsOpen={true} - isLoading={false} - isLoadingMessage={false} - onToggle={[Function]} - paddingSize="none" +
-
-
- -
-
- -
-
+ + +
+
+ -
- +
- - + + + recent 2 + + + + + + +
+
-
+
- -
-
-
-
- -
-
- -
+
+ +
- - - - -

- Recently viewed -

-
-
- - } - className="euiCollapsibleNavGroup euiCollapsibleNavGroup--light euiCollapsibleNavGroup--withHeading" - data-test-subj="collapsibleNavGroup-recentlyViewed" - id="mockId" - initialIsOpen={true} - isLoading={false} - isLoadingMessage={false} - onToggle={[Function]} - paddingSize="none" +
-
-
- -
-
- -
-
+ + +
+
+ -
- +
- -
-

- No recently viewed items -

-
-
+ +
  • + +
  • +
    + +
    - +
    -
    +
    -
    -
    -
    - - - -
    -
    - -
    +
    + + - - - - -

    - Recently viewed -

    -
    -
    - - } - className="euiCollapsibleNavGroup euiCollapsibleNavGroup--light euiCollapsibleNavGroup--withHeading" - data-test-subj="collapsibleNavGroup-recentlyViewed" - id="mockId" - initialIsOpen={true} - isLoading={false} - isLoadingMessage={false} - onToggle={[Function]} - paddingSize="none" +
    -
    -
    - -
    -
    - -
    -
    + + +
    +
    + -
    - +
    - - + + + recent + + + + + + +
    +
    -
    +
    - -
    -
    -
    -
    - -
    -
    - -
    +
    + + - - - - -

    - Recently viewed -

    -
    -
    - - } - className="euiCollapsibleNavGroup euiCollapsibleNavGroup--light euiCollapsibleNavGroup--withHeading" - data-test-subj="collapsibleNavGroup-recentlyViewed" - id="mockId" - initialIsOpen={true} - isLoading={false} - isLoadingMessage={false} - onToggle={[Function]} - paddingSize="none" +
    -
    -
    + + + + + +

    + Recently Visited +

    +
    +
    + + } + className="euiCollapsibleNavGroup euiCollapsibleNavGroup--withHeading" + data-test-opensearch-logo="clock" + data-test-subj="collapsibleNavGroup-recentlyVisited" + id="mockId" + initialIsOpen={true} + isLoading={false} + isLoadingMessage={false} + onToggle={[Function]} + paddingSize="none" > - +
    +
    - -
    - -
    - -

    - Recently viewed -

    -
    -
    -
    -
    -
    - - -
    -
    - -
    -
    -
    - +
    - - + + + recent + + + + + + +
    +
    -
    +
    - -
    -
    -
    -
    - -
    -
    - -
    +
    + +
    - - - - -

    - Recently viewed -

    -
    -
    - - } - className="euiCollapsibleNavGroup euiCollapsibleNavGroup--light euiCollapsibleNavGroup--withHeading" - data-test-subj="collapsibleNavGroup-recentlyViewed" - id="mockId" - initialIsOpen={true} - isLoading={false} - isLoadingMessage={false} - onToggle={[Function]} - paddingSize="none" +
    -
    -
    - -
    -
    - -
    -
    + + +
    +
    + -
    - +
    - - + + + recent + + + + + + +
    +
    -
    +
    - -
    -
    -
    -
    - -
    -
    - -
    +
    + +
    - - - - -

    - Recently viewed -

    -
    -
    - - } - className="euiCollapsibleNavGroup euiCollapsibleNavGroup--light euiCollapsibleNavGroup--withHeading" - data-test-subj="collapsibleNavGroup-recentlyViewed" - id="mockId" - initialIsOpen={true} - isLoading={false} - isLoadingMessage={false} - onToggle={[Function]} - paddingSize="none" +
    -
    -
    - -
    -
    - -
    -
    + + +
    +
    + -
    - +
    - - + + + recent + + + + + + +
    +
    -
    +
    - -
    -
    -
    -
    - -
    -
    - -
    +
    + +
    -
    -
    - -
      - -
    • - -
    • -
      -
    -
    -
    + + + + + +
    +
    +
    -
    -
    -
    - -
    -
    - - - - -

    - Recently viewed -

    -
    -
    - - } - className="euiCollapsibleNavGroup euiCollapsibleNavGroup--light euiCollapsibleNavGroup--withHeading" - data-test-subj="collapsibleNavGroup-recentlyViewed" - id="mockId" - initialIsOpen={true} - isLoading={false} - isLoadingMessage={false} - onToggle={[Function]} - paddingSize="none" - > -
    + +
    +
    + -
    - -
    -
    - -
    -
    + + +
    + -
    +
    - -
    - -
    -
    - -
    -
    - -
    +
    + +
    { ); expectShownNavLinksCount(component, 3); clickGroup(component, 'opensearchDashboards'); - clickGroup(component, 'recentlyViewed'); + clickGroup(component, 'recentlyVisited'); expectShownNavLinksCount(component, 1); component.setProps({ isNavOpen: false }); expectNavIsClosed(component); @@ -205,7 +205,7 @@ describe('CollapsibleNav', () => { }, }); - component.find('[data-test-subj="collapsibleNavGroup-recentlyViewed"] a').simulate('click'); + component.find('[data-test-subj="collapsibleNavGroup-recentlyVisited"] a').simulate('click'); expect(onClose.callCount).toEqual(1); expectNavIsClosed(component); component.setProps({ isNavOpen: true }); diff --git a/src/core/public/chrome/ui/header/collapsible_nav.tsx b/src/core/public/chrome/ui/header/collapsible_nav.tsx index 8b178200114a..abdb170b3aad 100644 --- a/src/core/public/chrome/ui/header/collapsible_nav.tsx +++ b/src/core/public/chrome/ui/header/collapsible_nav.tsx @@ -37,7 +37,6 @@ import { EuiListGroup, EuiListGroupItem, EuiShowFor, - EuiText, } from '@elastic/eui'; import { i18n } from '@osd/i18n'; import { groupBy, sortBy } from 'lodash'; @@ -46,13 +45,18 @@ import useObservable from 'react-use/lib/useObservable'; import * as Rx from 'rxjs'; import { ChromeNavLink, ChromeRecentlyAccessedHistoryItem } from '../..'; import { AppCategory } from '../../../../types'; -import { InternalApplicationStart } from '../../../application/types'; +import { InternalApplicationStart } from '../../../application'; import { HttpStart } from '../../../http'; import { OnIsLockedUpdate } from './'; -import { createEuiListItem, createRecentNavLink, isModifiedOrPrevented } from './nav_link'; -import type { Logos } from '../../../../common/types'; +import type { Logos } from '../../../../common'; +import { + createEuiListItem, + createRecentChromeNavLink, + emptyRecentlyVisited, + CollapsibleNavLink, +} from './nav_link'; -function getAllCategories(allCategorizedLinks: Record) { +function getAllCategories(allCategorizedLinks: Record) { const allCategories = {} as Record; for (const [key, value] of Object.entries(allCategorizedLinks)) { @@ -63,7 +67,7 @@ function getAllCategories(allCategorizedLinks: Record) } function getOrderedCategories( - mainCategories: Record, + mainCategories: Record, categoryDictionary: ReturnType ) { return sortBy( @@ -72,6 +76,30 @@ function getOrderedCategories( ); } +function getMergedNavLinks( + orderedCategories: string[], + uncategorizedLinks: CollapsibleNavLink[], + categoryDictionary: ReturnType +): Array { + const uncategorizedLinksWithOrder = sortBy( + uncategorizedLinks.filter((link) => link.order !== null), + 'order' + ); + const uncategorizedLinksWithoutOrder = uncategorizedLinks.filter((link) => link.order === null); + const orderedCategoryWithOrder = orderedCategories + .filter((categoryName) => categoryDictionary[categoryName]?.order !== null) + .map((categoryName) => ({ categoryName, order: categoryDictionary[categoryName]?.order })); + const orderedCategoryWithoutOrder = orderedCategories.filter( + (categoryName) => categoryDictionary[categoryName]?.order === null + ); + const mergedNavLinks = sortBy( + [...uncategorizedLinksWithOrder, ...orderedCategoryWithOrder], + 'order' + ).map((navLink) => ('categoryName' in navLink ? navLink.categoryName : navLink)); + // if order is not defined , categorized links will be placed before uncategorized links + return [...mergedNavLinks, ...orderedCategoryWithoutOrder, ...uncategorizedLinksWithoutOrder]; +} + function getCategoryLocalStorageKey(id: string) { return `core.navGroup.${id}`; } @@ -121,13 +149,27 @@ export function CollapsibleNav({ const navLinks = useObservable(observables.navLinks$, []).filter((link) => !link.hidden); const recentlyAccessed = useObservable(observables.recentlyAccessed$, []); const customNavLink = useObservable(observables.customNavLink$, undefined); + const allNavLinks: CollapsibleNavLink[] = [...navLinks]; + if (recentlyAccessed.length) { + allNavLinks.push( + ...recentlyAccessed.map((link) => createRecentChromeNavLink(link, navLinks, basePath)) + ); + } else { + allNavLinks.push(emptyRecentlyVisited); + } const appId = useObservable(observables.appId$, ''); const lockRef = useRef(null); - const groupedNavLinks = groupBy(navLinks, (link) => link?.category?.id); - const { undefined: unknowns = [], ...allCategorizedLinks } = groupedNavLinks; + const groupedNavLinks = groupBy(allNavLinks, (link) => link?.category?.id); + const { undefined: uncategorizedLinks = [], ...allCategorizedLinks } = groupedNavLinks; const categoryDictionary = getAllCategories(allCategorizedLinks); const orderedCategories = getOrderedCategories(allCategorizedLinks, categoryDictionary); - const readyForEUI = (link: ChromeNavLink, needsIcon: boolean = false) => { + const mergedNavLinks = getMergedNavLinks( + orderedCategories, + uncategorizedLinks, + categoryDictionary + ); + + const readyForEUI = (link: CollapsibleNavLink, needsIcon: boolean = false) => { return createEuiListItem({ link, appId, @@ -181,103 +223,51 @@ export function CollapsibleNav({ )} - {/* Recently viewed */} - setIsCategoryOpen('recentlyViewed', isCategoryOpen, storage)} - data-test-subj="collapsibleNavGroup-recentlyViewed" - > - {recentlyAccessed.length > 0 ? ( - { - // TODO #64541 - // Can remove icon from recent links completely - const { iconType, onClick, ...hydratedLink } = createRecentNavLink( - link, - navLinks, - basePath, - navigateToUrl - ); - - return { - ...hydratedLink, - 'data-test-subj': 'collapsibleNavAppLink--recent', - onClick: (event) => { - if (!isModifiedOrPrevented(event)) { - closeNav(); - onClick(event); - } - }, - }; - })} - maxWidth="none" - color="subdued" - gutterSize="none" - size="s" - className="osdCollapsibleNav__recentsListGroup" - /> - ) : ( - -

    - {i18n.translate('core.ui.EmptyRecentlyViewed', { - defaultMessage: 'No recently viewed items', - })} -

    -
    - )} -
    - - - - {/* OpenSearchDashboards, Observability, Security, and Management sections */} - {orderedCategories.map((categoryName) => { - const category = categoryDictionary[categoryName]!; - const opensearchLinkLogo = - category.id === 'opensearchDashboards' ? logos.Mark.url : category.euiIconType; + {/* merged NavLinks */} + {mergedNavLinks.map((item, i) => { + if (typeof item === 'string') { + const category = categoryDictionary[item]!; + const opensearchLinkLogo = + category.id === 'opensearchDashboards' ? logos.Mark.url : category.euiIconType; - return ( - setIsCategoryOpen(category.id, isCategoryOpen, storage)} - data-test-subj={`collapsibleNavGroup-${category.id}`} - data-test-opensearch-logo={opensearchLinkLogo} - > - readyForEUI(link))} - maxWidth="none" - color="subdued" - gutterSize="none" - size="s" - /> - - ); + return ( + + setIsCategoryOpen(category.id, isCategoryOpen, storage) + } + data-test-subj={`collapsibleNavGroup-${category.id}`} + data-test-opensearch-logo={opensearchLinkLogo} + > + readyForEUI(link))} + maxWidth="none" + color="subdued" + gutterSize="none" + size="s" + /> + + ); + } else { + return ( + + + + + + ); + } })} - {/* Things with no category (largely for custom plugins) */} - {unknowns.map((link, i) => ( - - - - - - ))} - {/* Docking button only for larger screens that can support it*/} diff --git a/src/core/public/chrome/ui/header/nav_link.tsx b/src/core/public/chrome/ui/header/nav_link.tsx index 11ff0b472bd0..0b0636036bbb 100644 --- a/src/core/public/chrome/ui/header/nav_link.tsx +++ b/src/core/public/chrome/ui/header/nav_link.tsx @@ -31,16 +31,16 @@ import { EuiIcon } from '@elastic/eui'; import { i18n } from '@osd/i18n'; import React from 'react'; -import { ChromeNavLink, ChromeRecentlyAccessedHistoryItem, CoreStart } from '../../..'; +import { AppCategory, ChromeNavLink, ChromeRecentlyAccessedHistoryItem, CoreStart } from '../../..'; import { HttpStart } from '../../../http'; -import { InternalApplicationStart } from '../../../application/types'; import { relativeToAbsolute } from '../../nav_links/to_nav_link'; export const isModifiedOrPrevented = (event: React.MouseEvent) => event.metaKey || event.altKey || event.ctrlKey || event.shiftKey || event.defaultPrevented; +export type CollapsibleNavLink = ChromeNavLink | RecentNavLink; interface Props { - link: ChromeNavLink; + link: ChromeNavLink | RecentNavLink; appId?: string; basePath?: HttpStart['basePath']; dataTestSubj: string; @@ -60,9 +60,10 @@ export function createEuiListItem({ onClick = () => {}, navigateToApp, dataTestSubj, - externalLink = false, + externalLink, }: Props) { const { href, id, title, disabled, euiIconType, icon, tooltip } = link; + externalLink = externalLink || link.externalLink; return { label: tooltip ?? title, @@ -93,14 +94,16 @@ export function createEuiListItem({ }; } -export interface RecentNavLink { - href: string; - label: string; - title: string; - 'aria-label': string; - iconType?: string; - onClick: React.MouseEventHandler; -} +export type RecentNavLink = Omit; + +const recentlyVisitedCategory: AppCategory = { + id: 'recentlyVisited', + label: i18n.translate('core.ui.recentlyVisited.label', { + defaultMessage: 'Recently Visited', + }), + order: 0, + euiIconType: 'clock', +}; /** * Add saved object type info to recently links @@ -112,11 +115,10 @@ export interface RecentNavLink { * @param navLinks * @param basePath */ -export function createRecentNavLink( +export function createRecentChromeNavLink( recentLink: ChromeRecentlyAccessedHistoryItem, navLinks: ChromeNavLink[], - basePath: HttpStart['basePath'], - navigateToUrl: InternalApplicationStart['navigateToUrl'] + basePath: HttpStart['basePath'] ): RecentNavLink { const { link, label } = recentLink; const href = relativeToAbsolute(basePath.prepend(link)); @@ -135,16 +137,20 @@ export function createRecentNavLink( return { href, - label, + id: recentLink.id, + externalLink: true, + category: recentlyVisitedCategory, title: titleAndAriaLabel, - 'aria-label': titleAndAriaLabel, - iconType: navLink?.euiIconType, - /* Use href and onClick to support "open in new tab" and SPA navigation in the same link */ - onClick(event: React.MouseEvent) { - if (event.button === 0 && !isModifiedOrPrevented(event)) { - event.preventDefault(); - navigateToUrl(href); - } - }, }; } + +// As emptyRecentlyVisited is disabled, values for id, href and baseUrl does not affect +export const emptyRecentlyVisited: RecentNavLink = { + id: '', + href: '', + disabled: true, + category: recentlyVisitedCategory, + title: i18n.translate('core.ui.EmptyRecentlyVisited', { + defaultMessage: 'No recently visited items', + }), +}; diff --git a/src/core/public/index.ts b/src/core/public/index.ts index ecdb578890dd..c4d41281bcbd 100644 --- a/src/core/public/index.ts +++ b/src/core/public/index.ts @@ -103,7 +103,7 @@ export { StringValidation, StringValidationRegex, StringValidationRegexString, - WorkspaceAttribute, + WorkspaceObject, } from '../types'; export { diff --git a/src/core/public/workspace/workspaces_service.mock.ts b/src/core/public/workspace/workspaces_service.mock.ts index ae56c035eb3a..ab8bda09730a 100644 --- a/src/core/public/workspace/workspaces_service.mock.ts +++ b/src/core/public/workspace/workspaces_service.mock.ts @@ -5,13 +5,12 @@ import { BehaviorSubject } from 'rxjs'; import type { PublicMethodsOf } from '@osd/utility-types'; - import { WorkspacesService } from './workspaces_service'; -import { WorkspaceAttribute } from '..'; +import { WorkspaceObject } from '..'; const currentWorkspaceId$ = new BehaviorSubject(''); -const workspaceList$ = new BehaviorSubject([]); -const currentWorkspace$ = new BehaviorSubject(null); +const workspaceList$ = new BehaviorSubject([]); +const currentWorkspace$ = new BehaviorSubject(null); const initialized$ = new BehaviorSubject(false); const createWorkspacesSetupContractMock = () => ({ diff --git a/src/core/public/workspace/workspaces_service.ts b/src/core/public/workspace/workspaces_service.ts index a7c62a76bec2..6c2e31c087d7 100644 --- a/src/core/public/workspace/workspaces_service.ts +++ b/src/core/public/workspace/workspaces_service.ts @@ -5,10 +5,19 @@ import { BehaviorSubject, combineLatest } from 'rxjs'; import { isEqual } from 'lodash'; - -import { CoreService, WorkspaceAttribute } from '../../types'; - -type WorkspaceObject = WorkspaceAttribute & { readonly?: boolean }; +import { CoreService, WorkspaceObject } from '../../types'; +import { InternalApplicationStart } from '../application'; +import { HttpStart } from '../http'; + +type WorkspaceMenuRenderFn = ({ + basePath, + getUrlForApp, + observables, +}: { + getUrlForApp: InternalApplicationStart['getUrlForApp']; + basePath: HttpStart['basePath']; + observables: WorkspaceObservables; +}) => JSX.Element | null; interface WorkspaceObservables { /** diff --git a/src/core/types/workspace.ts b/src/core/types/workspace.ts index e99744183cac..d66a93fcc61d 100644 --- a/src/core/types/workspace.ts +++ b/src/core/types/workspace.ts @@ -13,3 +13,7 @@ export interface WorkspaceAttribute { defaultVISTheme?: string; reserved?: boolean; } + +export interface WorkspaceObject extends WorkspaceAttribute { + readonly?: boolean; +} diff --git a/src/core/utils/default_app_categories.ts b/src/core/utils/default_app_categories.ts index 3c0920624e1b..e6e53f9101ed 100644 --- a/src/core/utils/default_app_categories.ts +++ b/src/core/utils/default_app_categories.ts @@ -65,12 +65,20 @@ export const DEFAULT_APP_CATEGORIES: Record = Object.freeze order: 4000, euiIconType: 'logoSecurity', }, + openSearchFeatures: { + id: 'openSearchFeatures', + label: i18n.translate('core.ui.openSearchFeaturesNavList.label', { + defaultMessage: 'OpenSearch Features', + }), + order: 5000, + euiIconType: 'folderClosed', + }, management: { id: 'management', label: i18n.translate('core.ui.managementNavList.label', { defaultMessage: 'Management', }), - order: 5000, + order: 6000, euiIconType: 'managementApp', }, }); diff --git a/src/plugins/dashboard/public/application/components/dashboard_listing/__snapshots__/dashboard_listing.test.tsx.snap b/src/plugins/dashboard/public/application/components/dashboard_listing/__snapshots__/dashboard_listing.test.tsx.snap index 71e393cf25cc..4e8599073779 100644 --- a/src/plugins/dashboard/public/application/components/dashboard_listing/__snapshots__/dashboard_listing.test.tsx.snap +++ b/src/plugins/dashboard/public/application/components/dashboard_listing/__snapshots__/dashboard_listing.test.tsx.snap @@ -223,9 +223,11 @@ exports[`dashboard listing hideWriteControls 1`] = ` "enableForcedAppSwitcherNavigation": [MockFunction], "get": [MockFunction], "getAll": [MockFunction], + "getFilteredNavLinks$": [MockFunction], "getForceAppSwitcherNavigation$": [MockFunction], "getNavLinks$": [MockFunction], "has": [MockFunction], + "setFilteredNavLinks": [MockFunction], "showOnly": [MockFunction], "update": [MockFunction], }, @@ -1117,11 +1119,1481 @@ exports[`dashboard listing hideWriteControls 1`] = ` data-test-subj="dashboardLandingPage" >
    + > + + +
    +
    + +
    + +
    + +

    + Dashboards +

    +
    +
    +
    +
    +
    + +
    + + + } + pagination={ + Object { + "initialPageIndex": 0, + "initialPageSize": 10, + "pageSizeOptions": Array [ + 10, + 20, + 50, + ], + } + } + responsive={true} + search={ + Object { + "box": Object { + "incremental": true, + }, + "defaultQuery": "", + "onChange": [Function], + "toolsLeft": undefined, + } + } + sorting={true} + tableLayout="fixed" + > +
    + + +
    + +
    + + + +
    +
    + + + + +
    + + + + + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    + + + } + onChange={[Function]} + pagination={ + Object { + "hidePerPageOptions": undefined, + "pageIndex": 0, + "pageSize": 10, + "pageSizeOptions": Array [ + 10, + 20, + 50, + ], + "totalItemCount": 2, + } + } + responsive={true} + sorting={ + Object { + "allowNeutralSort": true, + "sort": undefined, + } + } + tableLayout="fixed" + > +
    +
    + +
    + +
    + +
    + + +
    + +
    + + + + } + closePopover={[Function]} + display="inlineBlock" + hasArrow={true} + isOpen={false} + ownFocus={true} + panelPaddingSize="none" + > +
    +
    + + + +
    +
    +
    +
    +
    +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + + + + +
    +
    + Title +
    +
    + + + +
    +
    +
    + Type +
    +
    + + dashboardSavedObjects + +
    +
    +
    + Description +
    +
    + + dashboard0 desc + +
    +
    +
    + Last updated +
    +
    +
    +
    + Title +
    +
    + + + +
    +
    +
    + Type +
    +
    + + dashboardSavedObjects + +
    +
    +
    + Description +
    +
    + + dashboard1 desc + +
    +
    +
    + Last updated +
    +
    +
    +
    +
    + +
    + +
    + + + +
    + +
    + + + : + 10 + + } + closePopover={[Function]} + display="inlineBlock" + hasArrow={true} + isOpen={false} + ownFocus={true} + panelPaddingSize="none" + > +
    +
    + + + +
    +
    +
    +
    +
    + +
    + + + +
    +
    +
    +
    +
    +
    + +
    + +
    + +
    +
    + + +
    @@ -1356,9 +2828,11 @@ exports[`dashboard listing render table listing with initial filters from URL 1` "enableForcedAppSwitcherNavigation": [MockFunction], "get": [MockFunction], "getAll": [MockFunction], + "getFilteredNavLinks$": [MockFunction], "getForceAppSwitcherNavigation$": [MockFunction], "getNavLinks$": [MockFunction], "has": [MockFunction], + "setFilteredNavLinks": [MockFunction], "showOnly": [MockFunction], "update": [MockFunction], }, @@ -2311,11 +3785,2151 @@ exports[`dashboard listing render table listing with initial filters from URL 1` data-test-subj="dashboardLandingPage" >
    + > + + +
    +
    + +
    + +
    + +

    + Dashboards +

    +
    +
    +
    + + +
    + + + + + +
    +
    +
    +
    +
    + +
    + + + } + pagination={ + Object { + "initialPageIndex": 0, + "initialPageSize": 10, + "pageSizeOptions": Array [ + 10, + 20, + 50, + ], + } + } + responsive={true} + search={ + Object { + "box": Object { + "incremental": true, + }, + "defaultQuery": "dashboard", + "onChange": [Function], + "toolsLeft": undefined, + } + } + selection={ + Object { + "onSelectionChange": [Function], + } + } + sorting={true} + tableLayout="fixed" + > +
    + + +
    + +
    + + + +
    +
    + + + + +
    + + + + + +
    +
    + + + + + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    + + + } + onChange={[Function]} + pagination={ + Object { + "hidePerPageOptions": undefined, + "pageIndex": 0, + "pageSize": 10, + "pageSizeOptions": Array [ + 10, + 20, + 50, + ], + "totalItemCount": 2, + } + } + responsive={true} + selection={ + Object { + "onSelectionChange": [Function], + } + } + sorting={ + Object { + "allowNeutralSort": true, + "sort": undefined, + } + } + tableLayout="fixed" + > +
    +
    + +
    + +
    + +
    + + +
    + +
    + +
    + + +
    + + +
    + +
    + + + + } + closePopover={[Function]} + display="inlineBlock" + hasArrow={true} + isOpen={false} + ownFocus={true} + panelPaddingSize="none" + > +
    +
    + + + +
    +
    +
    +
    +
    +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    +
    + + +
    + +
    +
    + + +
    +
    + + + + + + + + + + + + + + Actions + + + + + +
    +
    + + +
    + +
    +
    + + +
    +
    +
    + Title +
    +
    + + + +
    +
    +
    + Type +
    +
    + + dashboardSavedObjects + +
    +
    +
    + Description +
    +
    + + dashboard0 desc + +
    +
    +
    + Last updated +
    +
    +
    +
    + + + + + + + + + + Edit + + + + + + +
    +
    +
    + + +
    + +
    +
    + + +
    +
    +
    + Title +
    +
    + + + +
    +
    +
    + Type +
    +
    + + dashboardSavedObjects + +
    +
    +
    + Description +
    +
    + + dashboard1 desc + +
    +
    +
    + Last updated +
    +
    +
    +
    + + + + + + + + + + Edit + + + + + + +
    +
    +
    +
    + +
    + +
    + + + +
    + +
    + + + : + 10 + + } + closePopover={[Function]} + display="inlineBlock" + hasArrow={true} + isOpen={false} + ownFocus={true} + panelPaddingSize="none" + > +
    +
    + + + +
    +
    +
    +
    +
    + +
    + + + +
    +
    +
    +
    +
    +
    + +
    + +
    + +
    +
    + + +
    @@ -2550,9 +6164,11 @@ exports[`dashboard listing renders call to action when no dashboards exist 1`] = "enableForcedAppSwitcherNavigation": [MockFunction], "get": [MockFunction], "getAll": [MockFunction], + "getFilteredNavLinks$": [MockFunction], "getForceAppSwitcherNavigation$": [MockFunction], "getNavLinks$": [MockFunction], "has": [MockFunction], + "setFilteredNavLinks": [MockFunction], "showOnly": [MockFunction], "update": [MockFunction], }, @@ -3505,11 +7121,274 @@ exports[`dashboard listing renders call to action when no dashboards exist 1`] = data-test-subj="dashboardLandingPage" >
    + > + + +
    + + + + } + body={ + +

    + +

    +

    + + + , + } + } + /> +

    +
    + } + iconType="dashboardApp" + title={ +

    + +

    + } + > +
    + + + + +
    + + +

    + + Create your first dashboard + +

    +
    + + + +
    + + +
    +

    + + You can combine data views from any OpenSearch Dashboards app into one dashboard and see everything in one place. + +

    +

    + + + , + } + } + > + New to OpenSearch Dashboards? + + + + to take a test drive. + +

    +
    +
    + + + +
    + + + + + + +
    + +
    + + +
    @@ -3744,9 +7623,11 @@ exports[`dashboard listing renders table rows 1`] = ` "enableForcedAppSwitcherNavigation": [MockFunction], "get": [MockFunction], "getAll": [MockFunction], + "getFilteredNavLinks$": [MockFunction], "getForceAppSwitcherNavigation$": [MockFunction], "getNavLinks$": [MockFunction], "has": [MockFunction], + "setFilteredNavLinks": [MockFunction], "showOnly": [MockFunction], "update": [MockFunction], }, @@ -4699,11 +8580,2111 @@ exports[`dashboard listing renders table rows 1`] = ` data-test-subj="dashboardLandingPage" >
    + > + + +
    +
    + +
    + +
    + +

    + Dashboards +

    +
    +
    +
    + + +
    + + + + + +
    +
    +
    +
    +
    + +
    + + + } + pagination={ + Object { + "initialPageIndex": 0, + "initialPageSize": 10, + "pageSizeOptions": Array [ + 10, + 20, + 50, + ], + } + } + responsive={true} + search={ + Object { + "box": Object { + "incremental": true, + }, + "defaultQuery": "", + "onChange": [Function], + "toolsLeft": undefined, + } + } + selection={ + Object { + "onSelectionChange": [Function], + } + } + sorting={true} + tableLayout="fixed" + > +
    + + +
    + +
    + + + +
    +
    + + + + +
    + + + + + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    + + + } + onChange={[Function]} + pagination={ + Object { + "hidePerPageOptions": undefined, + "pageIndex": 0, + "pageSize": 10, + "pageSizeOptions": Array [ + 10, + 20, + 50, + ], + "totalItemCount": 2, + } + } + responsive={true} + selection={ + Object { + "onSelectionChange": [Function], + } + } + sorting={ + Object { + "allowNeutralSort": true, + "sort": undefined, + } + } + tableLayout="fixed" + > +
    +
    + +
    + +
    + +
    + + +
    + +
    + +
    + + +
    + + +
    + +
    + + + + } + closePopover={[Function]} + display="inlineBlock" + hasArrow={true} + isOpen={false} + ownFocus={true} + panelPaddingSize="none" + > +
    +
    + + + +
    +
    +
    +
    +
    +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    +
    + + +
    + +
    +
    + + +
    +
    + + + + + + + + + + + + + + Actions + + + + + +
    +
    + + +
    + +
    +
    + + +
    +
    +
    + Title +
    +
    + + + +
    +
    +
    + Type +
    +
    + + dashboardSavedObjects + +
    +
    +
    + Description +
    +
    + + dashboard0 desc + +
    +
    +
    + Last updated +
    +
    +
    +
    + + + + + + + + + + Edit + + + + + + +
    +
    +
    + + +
    + +
    +
    + + +
    +
    +
    + Title +
    +
    + + + +
    +
    +
    + Type +
    +
    + + dashboardSavedObjects + +
    +
    +
    + Description +
    +
    + + dashboard1 desc + +
    +
    +
    + Last updated +
    +
    +
    +
    + + + + + + + + + + Edit + + + + + + +
    +
    +
    +
    + +
    + +
    + + + +
    + +
    + + + : + 10 + + } + closePopover={[Function]} + display="inlineBlock" + hasArrow={true} + isOpen={false} + ownFocus={true} + panelPaddingSize="none" + > +
    +
    + + + +
    +
    +
    +
    +
    + +
    + + + +
    +
    +
    +
    +
    +
    + +
    + +
    + +
    +
    + + +
    @@ -4938,9 +10919,11 @@ exports[`dashboard listing renders warning when listingLimit is exceeded 1`] = ` "enableForcedAppSwitcherNavigation": [MockFunction], "get": [MockFunction], "getAll": [MockFunction], + "getFilteredNavLinks$": [MockFunction], "getForceAppSwitcherNavigation$": [MockFunction], "getNavLinks$": [MockFunction], "has": [MockFunction], + "setFilteredNavLinks": [MockFunction], "showOnly": [MockFunction], "update": [MockFunction], }, @@ -5893,11 +11876,2231 @@ exports[`dashboard listing renders warning when listingLimit is exceeded 1`] = ` data-test-subj="dashboardLandingPage" >
    + > + + +
    +
    + +
    + +
    + +

    + Dashboards +

    +
    +
    +
    + + +
    + + + + + +
    +
    +
    +
    +
    + +
    + + + } + > +
    +
    + + + + Listing limit exceeded + + +
    + +
    + +
    +

    + + + , + "entityNamePlural": "dashboards", + "listingLimitText": + listingLimit + , + "listingLimitValue": 1, + "totalItems": 2, + } + } + > + You have 2 dashboards, but your + + listingLimit + + setting prevents the table below from displaying more than 1. You can change this setting under + + + + Advanced Settings + + + + . + +

    +
    +
    +
    +
    +
    +
    + +
    + + + } + pagination={ + Object { + "initialPageIndex": 0, + "initialPageSize": 10, + "pageSizeOptions": Array [ + 10, + 20, + 50, + ], + } + } + responsive={true} + search={ + Object { + "box": Object { + "incremental": true, + }, + "defaultQuery": "", + "onChange": [Function], + "toolsLeft": undefined, + } + } + selection={ + Object { + "onSelectionChange": [Function], + } + } + sorting={true} + tableLayout="fixed" + > +
    + + +
    + +
    + + + +
    +
    + + + + +
    + + + + + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    + + + } + onChange={[Function]} + pagination={ + Object { + "hidePerPageOptions": undefined, + "pageIndex": 0, + "pageSize": 10, + "pageSizeOptions": Array [ + 10, + 20, + 50, + ], + "totalItemCount": 2, + } + } + responsive={true} + selection={ + Object { + "onSelectionChange": [Function], + } + } + sorting={ + Object { + "allowNeutralSort": true, + "sort": undefined, + } + } + tableLayout="fixed" + > +
    +
    + +
    + +
    + +
    + + +
    + +
    + +
    + + +
    + + +
    + +
    + + + + } + closePopover={[Function]} + display="inlineBlock" + hasArrow={true} + isOpen={false} + ownFocus={true} + panelPaddingSize="none" + > +
    +
    + + + +
    +
    +
    +
    +
    +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    +
    + + +
    + +
    +
    + + +
    +
    + + + + + + + + + + + + + + Actions + + + + + +
    +
    + + +
    + +
    +
    + + +
    +
    +
    + Title +
    +
    + + + +
    +
    +
    + Type +
    +
    + + dashboardSavedObjects + +
    +
    +
    + Description +
    +
    + + dashboard0 desc + +
    +
    +
    + Last updated +
    +
    +
    +
    + + + + + + + + + + Edit + + + + + + +
    +
    +
    + + +
    + +
    +
    + + +
    +
    +
    + Title +
    +
    + + + +
    +
    +
    + Type +
    +
    + + dashboardSavedObjects + +
    +
    +
    + Description +
    +
    + + dashboard1 desc + +
    +
    +
    + Last updated +
    +
    +
    +
    + + + + + + + + + + Edit + + + + + + +
    +
    +
    +
    + +
    + +
    + + + +
    + +
    + + + : + 10 + + } + closePopover={[Function]} + display="inlineBlock" + hasArrow={true} + isOpen={false} + ownFocus={true} + panelPaddingSize="none" + > +
    +
    + + + +
    +
    +
    +
    +
    + +
    + + + +
    +
    +
    +
    +
    +
    + +
    + +
    + +
    +
    + + +
    diff --git a/src/plugins/dashboard/public/application/components/dashboard_top_nav/__snapshots__/dashboard_top_nav.test.tsx.snap b/src/plugins/dashboard/public/application/components/dashboard_top_nav/__snapshots__/dashboard_top_nav.test.tsx.snap index 8720101629cb..03bb33e5c30f 100644 --- a/src/plugins/dashboard/public/application/components/dashboard_top_nav/__snapshots__/dashboard_top_nav.test.tsx.snap +++ b/src/plugins/dashboard/public/application/components/dashboard_top_nav/__snapshots__/dashboard_top_nav.test.tsx.snap @@ -211,9 +211,11 @@ exports[`Dashboard top nav render in embed mode 1`] = ` "enableForcedAppSwitcherNavigation": [MockFunction], "get": [MockFunction], "getAll": [MockFunction], + "getFilteredNavLinks$": [MockFunction], "getForceAppSwitcherNavigation$": [MockFunction], "getNavLinks$": [MockFunction], "has": [MockFunction], + "setFilteredNavLinks": [MockFunction], "showOnly": [MockFunction], "update": [MockFunction], }, @@ -1169,9 +1171,11 @@ exports[`Dashboard top nav render in embed mode, and force hide filter bar 1`] = "enableForcedAppSwitcherNavigation": [MockFunction], "get": [MockFunction], "getAll": [MockFunction], + "getFilteredNavLinks$": [MockFunction], "getForceAppSwitcherNavigation$": [MockFunction], "getNavLinks$": [MockFunction], "has": [MockFunction], + "setFilteredNavLinks": [MockFunction], "showOnly": [MockFunction], "update": [MockFunction], }, @@ -2127,9 +2131,11 @@ exports[`Dashboard top nav render in embed mode, components can be forced show b "enableForcedAppSwitcherNavigation": [MockFunction], "get": [MockFunction], "getAll": [MockFunction], + "getFilteredNavLinks$": [MockFunction], "getForceAppSwitcherNavigation$": [MockFunction], "getNavLinks$": [MockFunction], "has": [MockFunction], + "setFilteredNavLinks": [MockFunction], "showOnly": [MockFunction], "update": [MockFunction], }, @@ -3085,9 +3091,11 @@ exports[`Dashboard top nav render in full screen mode with appended URL param bu "enableForcedAppSwitcherNavigation": [MockFunction], "get": [MockFunction], "getAll": [MockFunction], + "getFilteredNavLinks$": [MockFunction], "getForceAppSwitcherNavigation$": [MockFunction], "getNavLinks$": [MockFunction], "has": [MockFunction], + "setFilteredNavLinks": [MockFunction], "showOnly": [MockFunction], "update": [MockFunction], }, @@ -4043,9 +4051,11 @@ exports[`Dashboard top nav render in full screen mode, no componenets should be "enableForcedAppSwitcherNavigation": [MockFunction], "get": [MockFunction], "getAll": [MockFunction], + "getFilteredNavLinks$": [MockFunction], "getForceAppSwitcherNavigation$": [MockFunction], "getNavLinks$": [MockFunction], "has": [MockFunction], + "setFilteredNavLinks": [MockFunction], "showOnly": [MockFunction], "update": [MockFunction], }, @@ -5001,9 +5011,11 @@ exports[`Dashboard top nav render with all components 1`] = ` "enableForcedAppSwitcherNavigation": [MockFunction], "get": [MockFunction], "getAll": [MockFunction], + "getFilteredNavLinks$": [MockFunction], "getForceAppSwitcherNavigation$": [MockFunction], "getNavLinks$": [MockFunction], "has": [MockFunction], + "setFilteredNavLinks": [MockFunction], "showOnly": [MockFunction], "update": [MockFunction], }, diff --git a/src/plugins/dev_tools/public/plugin.ts b/src/plugins/dev_tools/public/plugin.ts index bb0b6ee1d981..e22f12b9234a 100644 --- a/src/plugins/dev_tools/public/plugin.ts +++ b/src/plugins/dev_tools/public/plugin.ts @@ -85,7 +85,7 @@ export class DevToolsPlugin implements Plugin { icon: '/ui/logos/opensearch_mark.svg', /* the order of dev tools, it shows as last item of management section */ order: 9070, - category: DEFAULT_APP_CATEGORIES.management, + category: DEFAULT_APP_CATEGORIES.openSearchFeatures, mount: async (params: AppMountParameters) => { const { element, history } = params; element.classList.add('devAppWrapper'); diff --git a/src/plugins/saved_objects_management/public/management_section/objects_table/saved_objects_table.tsx b/src/plugins/saved_objects_management/public/management_section/objects_table/saved_objects_table.tsx index ac5d0854cacc..faf6c133ddc0 100644 --- a/src/plugins/saved_objects_management/public/management_section/objects_table/saved_objects_table.tsx +++ b/src/plugins/saved_objects_management/public/management_section/objects_table/saved_objects_table.tsx @@ -871,7 +871,7 @@ export class SavedObjectsTable extends Component { private getWorkspaceIdFromURL(): string | null { return getWorkspaceIdFromUrl(window.location.href); } + + private filterByWorkspace(workspace: WorkspaceObject | null, allNavLinks: ChromeNavLink[]) { + if (!workspace) return allNavLinks; + const features = workspace.features ?? ['*']; + return allNavLinks.filter(featureMatchesConfig(features)); + } + + private filterNavLinks(core: CoreStart) { + const navLinksService = core.chrome.navLinks; + const chromeNavLinks$ = navLinksService.getNavLinks$(); + const currentWorkspace$ = core.workspaces.currentWorkspace$; + combineLatest([ + chromeNavLinks$.pipe(map(this.changeCategoryNameByWorkspaceFeatureFlag)), + currentWorkspace$, + ]).subscribe(([chromeNavLinks, currentWorkspace]) => { + const filteredNavLinks = new Map(); + chromeNavLinks = this.filterByWorkspace(currentWorkspace, chromeNavLinks); + chromeNavLinks.forEach((chromeNavLink) => { + filteredNavLinks.set(chromeNavLink.id, chromeNavLink); + }); + navLinksService.setFilteredNavLinks(filteredNavLinks); + }); + } + + /** + * The category "Opensearch Dashboards" needs to be renamed as "Library" + * when workspace feature flag is on, we need to do it here and generate + * a new item without polluting the original ChromeNavLink. + */ + private changeCategoryNameByWorkspaceFeatureFlag(chromeLinks: ChromeNavLink[]): ChromeNavLink[] { + return chromeLinks.map((item) => { + if (item.category?.id === DEFAULT_APP_CATEGORIES.opensearchDashboards.id) { + return { + ...item, + category: { + ...item.category, + label: i18n.translate('core.ui.libraryNavList.label', { + defaultMessage: 'Library', + }), + }, + }; + } + return item; + }); + } + public async setup(core: CoreSetup) { const workspaceClient = new WorkspaceClient(core.http, core.workspaces); await workspaceClient.init(); - /** * Retrieve workspace id from url */ @@ -82,7 +130,10 @@ export class WorkspacePlugin implements Plugin<{}, {}> { return {}; } - public start() { + public start(core: CoreStart) { + if (core) { + this.filterNavLinks(core); + } return {}; } From a123701f3e5b6da5d29f06ed81f3d93d100c315a Mon Sep 17 00:00:00 2001 From: yuye-aws Date: Fri, 22 Sep 2023 14:55:07 +0800 Subject: [PATCH 03/14] update tests and snapshots Signed-off-by: yuye-aws --- .../nav_links/nav_links_service.test.ts | 141 +- .../collapsible_nav.test.tsx.snap | 8156 ++++++++-------- .../header/__snapshots__/header.test.tsx.snap | 788 +- .../chrome/ui/header/collapsible_nav.test.tsx | 2 +- .../chrome/ui/header/collapsible_nav.tsx | 157 +- .../dashboard_listing.test.tsx.snap | 8203 +---------------- 6 files changed, 4651 insertions(+), 12796 deletions(-) diff --git a/src/core/public/chrome/nav_links/nav_links_service.test.ts b/src/core/public/chrome/nav_links/nav_links_service.test.ts index 3fe2b57676e0..f7a4d26af2ef 100644 --- a/src/core/public/chrome/nav_links/nav_links_service.test.ts +++ b/src/core/public/chrome/nav_links/nav_links_service.test.ts @@ -32,18 +32,12 @@ import { NavLinksService } from './nav_links_service'; import { take, map, takeLast } from 'rxjs/operators'; import { App } from '../../application'; import { BehaviorSubject } from 'rxjs'; +import { ChromeNavLink } from 'opensearch-dashboards/public'; const availableApps = new Map([ ['app1', { id: 'app1', order: 0, title: 'App 1', icon: 'app1' }], - [ - 'app2', - { - id: 'app2', - order: -10, - title: 'App 2', - euiIconType: 'canvasApp', - }, - ], + ['app2', { id: 'app2', order: -10, title: 'App 2', euiIconType: 'canvasApp' }], + ['app3', { id: 'app3', order: 10, title: 'App 3', icon: 'app3' }], ['chromelessApp', { id: 'chromelessApp', order: 20, title: 'Chromless App', chromeless: true }], ]); @@ -79,7 +73,7 @@ describe('NavLinksService', () => { ).not.toContain('chromelessApp'); }); - it('sorts navlinks by `order` property', async () => { + it('sorts navLinks by `order` property', async () => { expect( await start .getNavLinks$() @@ -88,7 +82,7 @@ describe('NavLinksService', () => { map((links) => links.map((l) => l.id)) ) .toPromise() - ).toEqual(['app2', 'app1']); + ).toEqual(['app2', 'app1', 'app3']); }); it('emits multiple values', async () => { @@ -99,8 +93,8 @@ describe('NavLinksService', () => { service.stop(); expect(emittedLinks).toEqual([ - ['app2', 'app1'], - ['app2', 'app1'], + ['app2', 'app1', 'app3'], + ['app2', 'app1', 'app3'], ]); }); @@ -111,6 +105,121 @@ describe('NavLinksService', () => { }); }); + describe('#getFilteredNavLinks$() when non null', () => { + // set filtered nav links, nav link with order smaller than 0 will be filtered + beforeEach(() => { + const filteredNavLinks = new Map(); + start.getNavLinks$().subscribe((links) => + links.forEach((link) => { + if (link.order !== undefined && link.order >= 0) { + filteredNavLinks.set(link.id, link); + } + }) + ); + start.setFilteredNavLinks(filteredNavLinks); + }); + + it('does not include `app2` applications', async () => { + expect( + await start + .getFilteredNavLinks$() + .pipe( + take(1), + map((links) => links.map((l) => l.id)) + ) + .toPromise() + ).not.toContain('app2'); + }); + + it('sorts navLinks by `order` property', async () => { + expect( + await start + .getFilteredNavLinks$() + .pipe( + take(1), + map((links) => links.map((l) => l.id)) + ) + .toPromise() + ).toEqual(['app1', 'app3']); + }); + + it('emits multiple values', async () => { + const navLinkIds$ = start.getFilteredNavLinks$().pipe(map((links) => links.map((l) => l.id))); + const emittedLinks: string[][] = []; + navLinkIds$.subscribe((r) => emittedLinks.push(r)); + start.update('app1', { href: '/foo' }); + + service.stop(); + expect(emittedLinks).toEqual([ + ['app1', 'app3'], + ['app1', 'app3'], + ]); + }); + + it('completes when service is stopped', async () => { + const last$ = start.getFilteredNavLinks$().pipe(takeLast(1)).toPromise(); + service.stop(); + await expect(last$).resolves.toBeInstanceOf(Array); + }); + }); + + describe('#getFilteredNavLinks$() when null', () => { + it('does not include `chromeless` applications', async () => { + expect( + await start + .getFilteredNavLinks$() + .pipe( + take(1), + map((links) => links.map((l) => l.id)) + ) + .toPromise() + ).not.toContain('chromelessApp'); + }); + + it('include `app2` applications', async () => { + expect( + await start + .getFilteredNavLinks$() + .pipe( + take(1), + map((links) => links.map((l) => l.id)) + ) + .toPromise() + ).toContain('app2'); + }); + + it('sorts navLinks by `order` property', async () => { + expect( + await start + .getFilteredNavLinks$() + .pipe( + take(1), + map((links) => links.map((l) => l.id)) + ) + .toPromise() + ).toEqual(['app2', 'app1', 'app3']); + }); + + it('emits multiple values', async () => { + const navLinkIds$ = start.getFilteredNavLinks$().pipe(map((links) => links.map((l) => l.id))); + const emittedLinks: string[][] = []; + navLinkIds$.subscribe((r) => emittedLinks.push(r)); + start.update('app1', { href: '/foo' }); + + service.stop(); + expect(emittedLinks).toEqual([ + ['app2', 'app1', 'app3'], + ['app2', 'app1', 'app3'], + ]); + }); + + it('completes when service is stopped', async () => { + const last$ = start.getFilteredNavLinks$().pipe(takeLast(1)).toPromise(); + service.stop(); + await expect(last$).resolves.toBeInstanceOf(Array); + }); + }); + describe('#get()', () => { it('returns link if exists', () => { expect(start.get('app2')!.title).toEqual('App 2'); @@ -123,7 +232,7 @@ describe('NavLinksService', () => { describe('#getAll()', () => { it('returns a sorted array of navlinks', () => { - expect(start.getAll().map((l) => l.id)).toEqual(['app2', 'app1']); + expect(start.getAll().map((l) => l.id)).toEqual(['app2', 'app1', 'app3']); }); }); @@ -148,7 +257,7 @@ describe('NavLinksService', () => { map((links) => links.map((l) => l.id)) ) .toPromise() - ).toEqual(['app2', 'app1']); + ).toEqual(['app2', 'app1', 'app3']); }); it('does nothing on chromeless applications', async () => { @@ -161,7 +270,7 @@ describe('NavLinksService', () => { map((links) => links.map((l) => l.id)) ) .toPromise() - ).toEqual(['app2', 'app1']); + ).toEqual(['app2', 'app1', 'app3']); }); it('removes all other links', async () => { diff --git a/src/core/public/chrome/ui/header/__snapshots__/collapsible_nav.test.tsx.snap b/src/core/public/chrome/ui/header/__snapshots__/collapsible_nav.test.tsx.snap index 66c3463b2790..6db9ceb59863 100644 --- a/src/core/public/chrome/ui/header/__snapshots__/collapsible_nav.test.tsx.snap +++ b/src/core/public/chrome/ui/header/__snapshots__/collapsible_nav.test.tsx.snap @@ -122,7 +122,7 @@ exports[`CollapsibleNav renders links grouped by category 1`] = ` } } homeHref="/" - id="collapsibe-nav" + id="collapsible-nav" isLocked={false} isNavOpen={true} logos={ @@ -422,7 +422,7 @@ exports[`CollapsibleNav renders links grouped by category 1`] = ` - -
    + + + + + +

    + Recently Visited +

    +
    +
    + + } + className="euiCollapsibleNavGroup euiCollapsibleNavGroup--withHeading" + data-test-opensearch-logo="clock" + data-test-subj="collapsibleNavGroup-recentlyVisited" + id="mockId" + initialIsOpen={true} + isLoading={false} + isLoadingMessage={false} + onToggle={[Function]} + paddingSize="none" > - - - - - - - -

    - Recently Visited -

    -
    -
    - - } - className="euiCollapsibleNavGroup euiCollapsibleNavGroup--withHeading" - data-test-opensearch-logo="clock" - data-test-subj="collapsibleNavGroup-recentlyVisited" - id="mockId" - initialIsOpen={true} - isLoading={false} - isLoadingMessage={false} - onToggle={[Function]} - paddingSize="none" +
    -
    -
    - -
    -
    - +
    + + + +
    +
    + +
    +
    -
    -
    + -
    - - - -
    -
    + recent 2 + + + + + +
    - +
    -
    - - - +
    +
    + +
    + + + + + + + +

    + OpenSearch Dashboards +

    +
    +
    + + } + className="euiCollapsibleNavGroup euiCollapsibleNavGroup--withHeading" + data-test-opensearch-logo="/test/ui/logos/opensearch_mark_on_light.svg" + data-test-subj="collapsibleNavGroup-opensearchDashboards" + id="mockId" + initialIsOpen={true} + isLoading={false} + isLoadingMessage={false} + onToggle={[Function]} + paddingSize="none" + > +
    - - - - - - -

    - OpenSearch Dashboards -

    -
    -
    - - } - className="euiCollapsibleNavGroup euiCollapsibleNavGroup--withHeading" - data-test-opensearch-logo="/test/ui/logos/opensearch_mark_on_light.svg" - data-test-subj="collapsibleNavGroup-opensearchDashboards" - id="mockId" - initialIsOpen={true} - isLoading={false} - isLoadingMessage={false} - onToggle={[Function]} - paddingSize="none" +
    -
    -
    - -
    -
    - +
    + + + +
    +
    + +
    +
    -
    -
    + -
    - - - -
    -
    + dashboard + + + + + +
    - +
    -
    - - - +
    +
    +
    +
    + + + + + + + +

    + Observability +

    +
    +
    + + } + className="euiCollapsibleNavGroup euiCollapsibleNavGroup--withHeading" + data-test-opensearch-logo="logoObservability" + data-test-subj="collapsibleNavGroup-observability" + id="mockId" + initialIsOpen={true} + isLoading={false} + isLoadingMessage={false} + onToggle={[Function]} + paddingSize="none" + > +
    - - - - - - -

    - Observability -

    -
    -
    - - } - className="euiCollapsibleNavGroup euiCollapsibleNavGroup--withHeading" - data-test-opensearch-logo="logoObservability" - data-test-subj="collapsibleNavGroup-observability" - id="mockId" - initialIsOpen={true} - isLoading={false} - isLoadingMessage={false} - onToggle={[Function]} - paddingSize="none" +
    -
    -
    - -
    -
    - +
    + + + +
    +
    + +
    +
    -
    -
    + -
    - - - -
    -
    + logs + + + + + +
    - +
    -
    - - - +
    +
    +
    +
    + + + + + + + +

    + Security +

    +
    +
    + + } + className="euiCollapsibleNavGroup euiCollapsibleNavGroup--withHeading" + data-test-opensearch-logo="logoSecurity" + data-test-subj="collapsibleNavGroup-securitySolution" + id="mockId" + initialIsOpen={true} + isLoading={false} + isLoadingMessage={false} + onToggle={[Function]} + paddingSize="none" + > +
    - - - - - - -

    - Security -

    -
    -
    - - } - className="euiCollapsibleNavGroup euiCollapsibleNavGroup--withHeading" - data-test-opensearch-logo="logoSecurity" - data-test-subj="collapsibleNavGroup-securitySolution" - id="mockId" - initialIsOpen={true} - isLoading={false} - isLoadingMessage={false} - onToggle={[Function]} - paddingSize="none" +
    -
    -
    - -
    -
    - +
    + + + +
    +
    + +
    +
    -
    -
    + -
    - - - -
    -
    + siem + + + + + +
    - +
    -
    - - - - +
    +
    +
    +
    + + + + + + + - - - - - -

    - Management -

    -
    -
    - - } - className="euiCollapsibleNavGroup euiCollapsibleNavGroup--withHeading" - data-test-opensearch-logo="managementApp" - data-test-subj="collapsibleNavGroup-management" - id="mockId" - initialIsOpen={true} - isLoading={false} - isLoadingMessage={false} - onToggle={[Function]} - paddingSize="none" + Management + +
    +
    + + } + className="euiCollapsibleNavGroup euiCollapsibleNavGroup--withHeading" + data-test-opensearch-logo="managementApp" + data-test-subj="collapsibleNavGroup-management" + id="mockId" + initialIsOpen={true} + isLoading={false} + isLoadingMessage={false} + onToggle={[Function]} + paddingSize="none" + > +
    +
    -
    -
    - -
    -
    - +
    + + + +
    +
    + +
    +
    -
    -
    + -
    - - - -
    -
    + monitoring + + + + + +
    - +
    -
    - - - +
    +
    +
    +
    + +
    +
    -
    -
    - -
      - -
    • - + +
    • +
      +
    +
    +
    +
    + + + +
    +
    + +
      + + - canvas + Dock navigation - - - -
    -
    -
    -
    -
    - - -
    -
    - , + } + } + color="subdued" + data-test-subj="collapsible-nav-lock" + iconType="lockOpen" + label="Dock navigation" + onClick={[Function]} + size="xs" > -
      - - - - Dock navigation - - , - } - } - color="subdued" + className="euiListGroupItem__button" data-test-subj="collapsible-nav-lock" - iconType="lockOpen" - label="Dock navigation" + disabled={false} onClick={[Function]} - size="xs" + type="button" > -
    • - -
    • -
      -
    -
    -
    -
    -
    -
    -
    - + + + + Dock navigation + + + + + + +
    + +
    + @@ -2081,7 +2073,7 @@ exports[`CollapsibleNav renders the default nav 1`] = ` } } homeHref="/" - id="collapsibe-nav" + id="collapsible-nav" isLocked={false} isNavOpen={false} logos={ @@ -2262,7 +2254,7 @@ exports[`CollapsibleNav renders the default nav 1`] = ` - -
    + + + + + +

    + Recently Visited +

    +
    +
    + + } + className="euiCollapsibleNavGroup euiCollapsibleNavGroup--withHeading" + data-test-opensearch-logo="clock" + data-test-subj="collapsibleNavGroup-recentlyVisited" + id="mockId" + initialIsOpen={true} + isLoading={false} + isLoadingMessage={false} + onToggle={[Function]} + paddingSize="none" > - - - - - - - -

    - Recently Visited -

    -
    -
    - - } - className="euiCollapsibleNavGroup euiCollapsibleNavGroup--withHeading" - data-test-opensearch-logo="clock" - data-test-subj="collapsibleNavGroup-recentlyVisited" - id="mockId" - initialIsOpen={true} - isLoading={false} - isLoadingMessage={false} - onToggle={[Function]} - paddingSize="none" +
    -
    -
    - -
    -
    - +
    + + + +
    +
    + +
    +
    -
    -
    + -
    - -
      - -
    • - -
    • -
      -
    -
    -
    -
    + No recently visited items + + + + + +
    - +
    -
    - - - +
    +
    + + + + +
    - -
    + -
    - + + + Undock navigation + + , + } + } + color="subdued" + data-test-subj="collapsible-nav-lock" + iconType="lock" + label="Undock navigation" + onClick={[Function]} + size="xs" > -
      - - - - Undock navigation - - , - } - } - color="subdued" + className="euiListGroupItem__button" data-test-subj="collapsible-nav-lock" - iconType="lock" - label="Undock navigation" + disabled={false} onClick={[Function]} - size="xs" + type="button" > -
    • - -
    • -
      -
    -
    -
    -
    -
    - -
    -
    + + + + Undock navigation + + + + + + + + + +
    @@ -3350,7 +3334,7 @@ exports[`CollapsibleNav with custom branding renders the nav bar in dark mode 1` } } homeHref="/" - id="collapsibe-nav" + id="collapsible-nav" isLocked={false} isNavOpen={true} logos={ @@ -3566,7 +3550,7 @@ exports[`CollapsibleNav with custom branding renders the nav bar in dark mode 1` - -
    + + + + + +

    + Recently Visited +

    +
    +
    + + } + className="euiCollapsibleNavGroup euiCollapsibleNavGroup--withHeading" + data-test-opensearch-logo="clock" + data-test-subj="collapsibleNavGroup-recentlyVisited" + id="mockId" + initialIsOpen={true} + isLoading={false} + isLoadingMessage={false} + onToggle={[Function]} + paddingSize="none" > - - - - - - - -

    - Recently Visited -

    -
    -
    - - } - className="euiCollapsibleNavGroup euiCollapsibleNavGroup--withHeading" - data-test-opensearch-logo="clock" - data-test-subj="collapsibleNavGroup-recentlyVisited" - id="mockId" - initialIsOpen={true} - isLoading={false} - isLoadingMessage={false} - onToggle={[Function]} - paddingSize="none" +
    -
    -
    - -
    -
    - +
    + + + +
    +
    + +
    +
    -
    -
    + -
    - - - -
    -
    + recent + + + + + +
    - +
    -
    - - - +
    +
    + + + + + + + + + +

    + OpenSearch Dashboards +

    +
    +
    + + } + className="euiCollapsibleNavGroup euiCollapsibleNavGroup--withHeading" + data-test-opensearch-logo="/custom/branded/mark-darkmode.svg" + data-test-subj="collapsibleNavGroup-opensearchDashboards" + id="mockId" + initialIsOpen={true} + isLoading={false} + isLoadingMessage={false} + onToggle={[Function]} + paddingSize="none" + > +
    - - - - - - -

    - OpenSearch Dashboards -

    -
    -
    - - } - className="euiCollapsibleNavGroup euiCollapsibleNavGroup--withHeading" - data-test-opensearch-logo="/custom/branded/mark-darkmode.svg" - data-test-subj="collapsibleNavGroup-opensearchDashboards" - id="mockId" - initialIsOpen={true} - isLoading={false} - isLoadingMessage={false} - onToggle={[Function]} - paddingSize="none" +
    -
    -
    - -
    -
    - +
    + + + +
    +
    + +
    +
    -
    -
    + -
    - - - -
    -
    + discover + + + + + +
    - +
    -
    - - - +
    +
    +
    +
    + + + + + + + +

    + Observability +

    +
    +
    + + } + className="euiCollapsibleNavGroup euiCollapsibleNavGroup--withHeading" + data-test-opensearch-logo="logoObservability" + data-test-subj="collapsibleNavGroup-observability" + id="mockId" + initialIsOpen={true} + isLoading={false} + isLoadingMessage={false} + onToggle={[Function]} + paddingSize="none" + > +
    - - - - - - -

    - Observability -

    -
    -
    - - } - className="euiCollapsibleNavGroup euiCollapsibleNavGroup--withHeading" - data-test-opensearch-logo="logoObservability" - data-test-subj="collapsibleNavGroup-observability" - id="mockId" - initialIsOpen={true} - isLoading={false} - isLoadingMessage={false} - onToggle={[Function]} - paddingSize="none" +
    -
    -
    - -
    -
    - +
    + + + +
    +
    + +
    +
    -
    -
    + -
    - - - -
    -
    + discover + + + + + +
    - +
    -
    - - - +
    +
    +
    +
    + + +
    - -
    + -
    - + + + Dock navigation + + , + } + } + color="subdued" + data-test-subj="collapsible-nav-lock" + iconType="lockOpen" + label="Dock navigation" + onClick={[Function]} + size="xs" > -
      - - - - Dock navigation - - , - } - } - color="subdued" + className="euiListGroupItem__button" data-test-subj="collapsible-nav-lock" - iconType="lockOpen" - label="Dock navigation" + disabled={false} onClick={[Function]} - size="xs" + type="button" > -
    • - -
    • -
      -
    -
    -
    -
    -
    - -
    -
    + + + + Dock navigation + + + + + + + + + +
    @@ -4483,7 +4459,7 @@ exports[`CollapsibleNav with custom branding renders the nav bar in default mode } } homeHref="/" - id="collapsibe-nav" + id="collapsible-nav" isLocked={false} isNavOpen={true} logos={ @@ -4699,7 +4675,7 @@ exports[`CollapsibleNav with custom branding renders the nav bar in default mode - -
    + + + + + +

    + Recently Visited +

    +
    +
    + + } + className="euiCollapsibleNavGroup euiCollapsibleNavGroup--withHeading" + data-test-opensearch-logo="clock" + data-test-subj="collapsibleNavGroup-recentlyVisited" + id="mockId" + initialIsOpen={true} + isLoading={false} + isLoadingMessage={false} + onToggle={[Function]} + paddingSize="none" > - - - - - - - -

    - Recently Visited -

    -
    -
    - - } - className="euiCollapsibleNavGroup euiCollapsibleNavGroup--withHeading" - data-test-opensearch-logo="clock" - data-test-subj="collapsibleNavGroup-recentlyVisited" - id="mockId" - initialIsOpen={true} - isLoading={false} - isLoadingMessage={false} - onToggle={[Function]} - paddingSize="none" +
    -
    -
    - -
    -
    - +
    + + + +
    +
    + +
    +
    -
    -
    + -
    - - - -
    -
    + recent + + + + + +
    - +
    -
    - - - +
    +
    + + + + + + + + + +

    + OpenSearch Dashboards +

    +
    +
    + + } + className="euiCollapsibleNavGroup euiCollapsibleNavGroup--withHeading" + data-test-opensearch-logo="/custom/branded/mark.svg" + data-test-subj="collapsibleNavGroup-opensearchDashboards" + id="mockId" + initialIsOpen={true} + isLoading={false} + isLoadingMessage={false} + onToggle={[Function]} + paddingSize="none" + > +
    - - - - - - -

    - OpenSearch Dashboards -

    -
    -
    - - } - className="euiCollapsibleNavGroup euiCollapsibleNavGroup--withHeading" - data-test-opensearch-logo="/custom/branded/mark.svg" - data-test-subj="collapsibleNavGroup-opensearchDashboards" - id="mockId" - initialIsOpen={true} - isLoading={false} - isLoadingMessage={false} - onToggle={[Function]} - paddingSize="none" +
    -
    -
    - -
    -
    - +
    + + + +
    +
    + +
    +
    -
    -
    + -
    - - - -
    -
    + discover + + + + + +
    - +
    -
    - - - +
    +
    +
    +
    + + + + + + + +

    + Observability +

    +
    +
    + + } + className="euiCollapsibleNavGroup euiCollapsibleNavGroup--withHeading" + data-test-opensearch-logo="logoObservability" + data-test-subj="collapsibleNavGroup-observability" + id="mockId" + initialIsOpen={true} + isLoading={false} + isLoadingMessage={false} + onToggle={[Function]} + paddingSize="none" + > +
    - - - - - - -

    - Observability -

    -
    -
    - - } - className="euiCollapsibleNavGroup euiCollapsibleNavGroup--withHeading" - data-test-opensearch-logo="logoObservability" - data-test-subj="collapsibleNavGroup-observability" - id="mockId" - initialIsOpen={true} - isLoading={false} - isLoadingMessage={false} - onToggle={[Function]} - paddingSize="none" +
    -
    -
    - -
    -
    - +
    + + + +
    +
    + +
    +
    -
    -
    + -
    - - - -
    -
    + discover + + + + + +
    - +
    -
    - - - +
    +
    +
    +
    + + +
    - -
    + -
    - + + + Dock navigation + + , + } + } + color="subdued" + data-test-subj="collapsible-nav-lock" + iconType="lockOpen" + label="Dock navigation" + onClick={[Function]} + size="xs" > -
      - - - - Dock navigation - - , - } - } - color="subdued" + className="euiListGroupItem__button" data-test-subj="collapsible-nav-lock" - iconType="lockOpen" - label="Dock navigation" + disabled={false} onClick={[Function]} - size="xs" + type="button" > -
    • - -
    • -
      -
    -
    -
    -
    -
    - -
    -
    + + + + Dock navigation + + + + + + + + + +
    @@ -5609,7 +5577,7 @@ exports[`CollapsibleNav without custom branding renders the nav bar in dark mode } } homeHref="/" - id="collapsibe-nav" + id="collapsible-nav" isLocked={false} isNavOpen={true} logos={ @@ -5825,7 +5793,7 @@ exports[`CollapsibleNav without custom branding renders the nav bar in dark mode - -
    + + + + + +

    + Recently Visited +

    +
    +
    + + } + className="euiCollapsibleNavGroup euiCollapsibleNavGroup--withHeading" + data-test-opensearch-logo="clock" + data-test-subj="collapsibleNavGroup-recentlyVisited" + id="mockId" + initialIsOpen={true} + isLoading={false} + isLoadingMessage={false} + onToggle={[Function]} + paddingSize="none" > - - - - - - - -

    - Recently Visited -

    -
    -
    - - } - className="euiCollapsibleNavGroup euiCollapsibleNavGroup--withHeading" - data-test-opensearch-logo="clock" - data-test-subj="collapsibleNavGroup-recentlyVisited" - id="mockId" - initialIsOpen={true} - isLoading={false} - isLoadingMessage={false} - onToggle={[Function]} - paddingSize="none" +
    -
    -
    - -
    -
    - +
    + + + +
    +
    + +
    +
    -
    -
    + -
    - - - -
    -
    + recent + + + + + +
    - +
    -
    - - - +
    +
    + + + + + + + + + +

    + OpenSearch Dashboards +

    +
    +
    + + } + className="euiCollapsibleNavGroup euiCollapsibleNavGroup--withHeading" + data-test-opensearch-logo="/test/ui/logos/opensearch_mark_on_dark.svg" + data-test-subj="collapsibleNavGroup-opensearchDashboards" + id="mockId" + initialIsOpen={true} + isLoading={false} + isLoadingMessage={false} + onToggle={[Function]} + paddingSize="none" + > +
    - - - - - - -

    - OpenSearch Dashboards -

    -
    -
    - - } - className="euiCollapsibleNavGroup euiCollapsibleNavGroup--withHeading" - data-test-opensearch-logo="/test/ui/logos/opensearch_mark_on_dark.svg" - data-test-subj="collapsibleNavGroup-opensearchDashboards" - id="mockId" - initialIsOpen={true} - isLoading={false} - isLoadingMessage={false} - onToggle={[Function]} - paddingSize="none" +
    -
    -
    - -
    -
    - +
    + + + +
    +
    + +
    +
    -
    -
    + -
    - - - -
    -
    + discover + + + + + +
    - +
    -
    - - - +
    +
    +
    +
    + + + + + + + +

    + Observability +

    +
    +
    + + } + className="euiCollapsibleNavGroup euiCollapsibleNavGroup--withHeading" + data-test-opensearch-logo="logoObservability" + data-test-subj="collapsibleNavGroup-observability" + id="mockId" + initialIsOpen={true} + isLoading={false} + isLoadingMessage={false} + onToggle={[Function]} + paddingSize="none" + > +
    - - - - - - -

    - Observability -

    -
    -
    - - } - className="euiCollapsibleNavGroup euiCollapsibleNavGroup--withHeading" - data-test-opensearch-logo="logoObservability" - data-test-subj="collapsibleNavGroup-observability" - id="mockId" - initialIsOpen={true} - isLoading={false} - isLoadingMessage={false} - onToggle={[Function]} - paddingSize="none" +
    -
    -
    - -
    -
    - +
    + + + +
    +
    + +
    +
    -
    -
    + -
    - - - -
    -
    + discover + + + + + +
    - +
    -
    - - - +
    +
    +
    +
    + + +
    - -
    + -
    - + + + Dock navigation + + , + } + } + color="subdued" + data-test-subj="collapsible-nav-lock" + iconType="lockOpen" + label="Dock navigation" + onClick={[Function]} + size="xs" > -
      - - - - Dock navigation - - , - } - } - color="subdued" + className="euiListGroupItem__button" data-test-subj="collapsible-nav-lock" - iconType="lockOpen" - label="Dock navigation" + disabled={false} onClick={[Function]} - size="xs" + type="button" > -
    • - -
    • -
      -
    -
    -
    -
    -
    - -
    -
    + + + + Dock navigation + + + + + + + + + +
    @@ -6731,7 +6691,7 @@ exports[`CollapsibleNav without custom branding renders the nav bar in default m } } homeHref="/" - id="collapsibe-nav" + id="collapsible-nav" isLocked={false} isNavOpen={true} logos={ @@ -6947,7 +6907,7 @@ exports[`CollapsibleNav without custom branding renders the nav bar in default m - -
    + + + + + +

    + Recently Visited +

    +
    +
    + + } + className="euiCollapsibleNavGroup euiCollapsibleNavGroup--withHeading" + data-test-opensearch-logo="clock" + data-test-subj="collapsibleNavGroup-recentlyVisited" + id="mockId" + initialIsOpen={true} + isLoading={false} + isLoadingMessage={false} + onToggle={[Function]} + paddingSize="none" > - - - - - - - -

    - Recently Visited -

    -
    -
    - - } - className="euiCollapsibleNavGroup euiCollapsibleNavGroup--withHeading" - data-test-opensearch-logo="clock" - data-test-subj="collapsibleNavGroup-recentlyVisited" - id="mockId" - initialIsOpen={true} - isLoading={false} - isLoadingMessage={false} - onToggle={[Function]} - paddingSize="none" +
    -
    -
    - -
    -
    - +
    + + + +
    +
    + +
    +
    -
    -
    + -
    - - - -
    -
    + recent + + + + + +
    - +
    -
    - - - +
    +
    + + + + + + + + + +

    + OpenSearch Dashboards +

    +
    +
    + + } + className="euiCollapsibleNavGroup euiCollapsibleNavGroup--withHeading" + data-test-opensearch-logo="/test/ui/logos/opensearch_mark_on_light.svg" + data-test-subj="collapsibleNavGroup-opensearchDashboards" + id="mockId" + initialIsOpen={true} + isLoading={false} + isLoadingMessage={false} + onToggle={[Function]} + paddingSize="none" + > +
    - - - - - - -

    - OpenSearch Dashboards -

    -
    -
    - - } - className="euiCollapsibleNavGroup euiCollapsibleNavGroup--withHeading" - data-test-opensearch-logo="/test/ui/logos/opensearch_mark_on_light.svg" - data-test-subj="collapsibleNavGroup-opensearchDashboards" - id="mockId" - initialIsOpen={true} - isLoading={false} - isLoadingMessage={false} - onToggle={[Function]} - paddingSize="none" +
    -
    -
    - -
    -
    - +
    + + + +
    +
    + +
    +
    -
    -
    + -
    - - - -
    -
    + discover + + + + + +
    - +
    -
    - - - +
    +
    +
    +
    + + + + + + + +

    + Observability +

    +
    +
    + + } + className="euiCollapsibleNavGroup euiCollapsibleNavGroup--withHeading" + data-test-opensearch-logo="logoObservability" + data-test-subj="collapsibleNavGroup-observability" + id="mockId" + initialIsOpen={true} + isLoading={false} + isLoadingMessage={false} + onToggle={[Function]} + paddingSize="none" + > +
    - - - - - - -

    - Observability -

    -
    -
    - - } - className="euiCollapsibleNavGroup euiCollapsibleNavGroup--withHeading" - data-test-opensearch-logo="logoObservability" - data-test-subj="collapsibleNavGroup-observability" - id="mockId" - initialIsOpen={true} - isLoading={false} - isLoadingMessage={false} - onToggle={[Function]} - paddingSize="none" +
    -
    -
    - -
    -
    - +
    + + + +
    +
    + +
    +
    -
    -
    + -
    - - - -
    -
    + discover + + + + + +
    - +
    -
    - - - +
    +
    +
    +
    + + +
    - -
    + -
    - + + + Dock navigation + + , + } + } + color="subdued" + data-test-subj="collapsible-nav-lock" + iconType="lockOpen" + label="Dock navigation" + onClick={[Function]} + size="xs" > -
      - - - - Dock navigation - - , - } - } - color="subdued" + className="euiListGroupItem__button" data-test-subj="collapsible-nav-lock" - iconType="lockOpen" - label="Dock navigation" + disabled={false} onClick={[Function]} - size="xs" + type="button" > -
    • - -
    • -
      -
    -
    -
    -
    -
    - -
    -
    + + + + Dock navigation + + + + + + + + + +
    diff --git a/src/core/public/chrome/ui/header/__snapshots__/header.test.tsx.snap b/src/core/public/chrome/ui/header/__snapshots__/header.test.tsx.snap index 082af589eb62..a974afb64369 100644 --- a/src/core/public/chrome/ui/header/__snapshots__/header.test.tsx.snap +++ b/src/core/public/chrome/ui/header/__snapshots__/header.test.tsx.snap @@ -6228,482 +6228,474 @@ exports[`Header handles visibility and lock changes 1`] = ` role={null} >
    -
    - -
    -
    - -
      - -
    • - -
    • -
      -
    -
    -
    -
    -
    + Manage cloud deployment + + + + + + +
    -
    - -
    -
    - + +
    + +
    +
    + + + + + + + +

    + Recently Visited +

    +
    +
    + + } + className="euiCollapsibleNavGroup euiCollapsibleNavGroup--withHeading" + data-test-opensearch-logo="clock" + data-test-subj="collapsibleNavGroup-recentlyVisited" + id="mockId" + initialIsOpen={true} + isLoading={false} + isLoadingMessage={false} + onToggle={[Function]} + paddingSize="none" + > +
    - - - - - - -

    - Recently Visited -

    -
    -
    - - } - className="euiCollapsibleNavGroup euiCollapsibleNavGroup--withHeading" - data-test-opensearch-logo="clock" - data-test-subj="collapsibleNavGroup-recentlyVisited" - id="mockId" - initialIsOpen={true} - isLoading={false} - isLoadingMessage={false} - onToggle={[Function]} - paddingSize="none" +
    -
    -
    - -
    -
    - +
    + + + +
    +
    + +
    +
    -
    - + dashboard, type: opensearchDashboards + + + + + +
    - +
    -
    - - - +
    +
    +
    +
    + +
    +
    -
    -
    - -
      - -
    • - +
    • +
      +
    +
    +
    +
    + + + +
    +
    + +
      + + - opensearchDashboards + Undock navigation - - - -
    -
    -
    -
    -
    - - -
    -
    - , + } + } + color="subdued" + data-test-subj="collapsible-nav-lock" + iconType="lock" + label="Undock navigation" + onClick={[Function]} + size="xs" > -
      - - - - Undock navigation - - , - } - } - color="subdued" + className="euiListGroupItem__button" data-test-subj="collapsible-nav-lock" - iconType="lock" - label="Undock navigation" + disabled={false} onClick={[Function]} - size="xs" + type="button" > -
    • - -
    • -
      -
    -
    -
    -
    -
    -
    -
    - + + + + Undock navigation + + + + + + +
    + +
    + diff --git a/src/core/public/chrome/ui/header/collapsible_nav.test.tsx b/src/core/public/chrome/ui/header/collapsible_nav.test.tsx index e15a3fdfc629..af8b28b25611 100644 --- a/src/core/public/chrome/ui/header/collapsible_nav.test.tsx +++ b/src/core/public/chrome/ui/header/collapsible_nav.test.tsx @@ -81,7 +81,7 @@ function mockProps(branding = {}) { return { appId$: new BehaviorSubject('test'), basePath: mockBasePath, - id: 'collapsibe-nav', + id: 'collapsible-nav', isLocked: false, isNavOpen: false, homeHref: '/', diff --git a/src/core/public/chrome/ui/header/collapsible_nav.tsx b/src/core/public/chrome/ui/header/collapsible_nav.tsx index abdb170b3aad..680b57a5be7c 100644 --- a/src/core/public/chrome/ui/header/collapsible_nav.tsx +++ b/src/core/public/chrome/ui/header/collapsible_nav.tsx @@ -223,90 +223,85 @@ export function CollapsibleNav({ )} - - {/* merged NavLinks */} - {mergedNavLinks.map((item, i) => { - if (typeof item === 'string') { - const category = categoryDictionary[item]!; - const opensearchLinkLogo = - category.id === 'opensearchDashboards' ? logos.Mark.url : category.euiIconType; + {mergedNavLinks.map((item, i) => { + if (typeof item === 'string') { + const category = categoryDictionary[item]!; + const opensearchLinkLogo = + category.id === 'opensearchDashboards' ? logos.Mark.url : category.euiIconType; - return ( - - setIsCategoryOpen(category.id, isCategoryOpen, storage) - } - data-test-subj={`collapsibleNavGroup-${category.id}`} - data-test-opensearch-logo={opensearchLinkLogo} - > - readyForEUI(link))} - maxWidth="none" - color="subdued" - gutterSize="none" - size="s" - /> - - ); - } else { - return ( - - - - - - ); - } - })} - - {/* Docking button only for larger screens that can support it*/} - - - - setIsCategoryOpen(category.id, isCategoryOpen, storage)} + data-test-subj={`collapsibleNavGroup-${category.id}`} + data-test-opensearch-logo={opensearchLinkLogo} + > + readyForEUI(link))} + maxWidth="none" color="subdued" - label={ - isLocked - ? i18n.translate('core.ui.primaryNavSection.undockLabel', { - defaultMessage: 'Undock navigation', - }) - : i18n.translate('core.ui.primaryNavSection.dockLabel', { - defaultMessage: 'Dock navigation', - }) - } - aria-label={ - isLocked - ? i18n.translate('core.ui.primaryNavSection.undockAriaLabel', { - defaultMessage: 'Undock primary navigation', - }) - : i18n.translate('core.ui.primaryNavSection.dockAriaLabel', { - defaultMessage: 'Dock primary navigation', - }) - } - onClick={() => { - onIsLockedUpdate(!isLocked); - if (lockRef.current) { - lockRef.current.focus(); - } - }} - iconType={isLocked ? 'lock' : 'lockOpen'} + gutterSize="none" + size="s" /> - - - - + + ); + } else { + return ( + + + + + + ); + } + })} + + {/* Docking button only for larger screens that can support it*/} + + + + { + onIsLockedUpdate(!isLocked); + if (lockRef.current) { + lockRef.current.focus(); + } + }} + iconType={isLocked ? 'lock' : 'lockOpen'} + /> + + + ); } diff --git a/src/plugins/dashboard/public/application/components/dashboard_listing/__snapshots__/dashboard_listing.test.tsx.snap b/src/plugins/dashboard/public/application/components/dashboard_listing/__snapshots__/dashboard_listing.test.tsx.snap index 4e8599073779..109ebe3b2cea 100644 --- a/src/plugins/dashboard/public/application/components/dashboard_listing/__snapshots__/dashboard_listing.test.tsx.snap +++ b/src/plugins/dashboard/public/application/components/dashboard_listing/__snapshots__/dashboard_listing.test.tsx.snap @@ -1119,1481 +1119,11 @@ exports[`dashboard listing hideWriteControls 1`] = ` data-test-subj="dashboardLandingPage" >
    - - -
    -
    - -
    - -
    - -

    - Dashboards -

    -
    -
    -
    -
    -
    - -
    - - - } - pagination={ - Object { - "initialPageIndex": 0, - "initialPageSize": 10, - "pageSizeOptions": Array [ - 10, - 20, - 50, - ], - } - } - responsive={true} - search={ - Object { - "box": Object { - "incremental": true, - }, - "defaultQuery": "", - "onChange": [Function], - "toolsLeft": undefined, - } - } - sorting={true} - tableLayout="fixed" - > -
    - - -
    - -
    - - - -
    -
    - - - - -
    - - - - - -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    - -
    - - - } - onChange={[Function]} - pagination={ - Object { - "hidePerPageOptions": undefined, - "pageIndex": 0, - "pageSize": 10, - "pageSizeOptions": Array [ - 10, - 20, - 50, - ], - "totalItemCount": 2, - } - } - responsive={true} - sorting={ - Object { - "allowNeutralSort": true, - "sort": undefined, - } - } - tableLayout="fixed" - > -
    -
    - -
    - -
    - -
    - - -
    - -
    - - - - } - closePopover={[Function]} - display="inlineBlock" - hasArrow={true} - isOpen={false} - ownFocus={true} - panelPaddingSize="none" - > -
    -
    - - - -
    -
    -
    -
    -
    -
    -
    -
    - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    - -
    - - - - - - - -
    -
    - Title -
    -
    - - - -
    -
    -
    - Type -
    -
    - - dashboardSavedObjects - -
    -
    -
    - Description -
    -
    - - dashboard0 desc - -
    -
    -
    - Last updated -
    -
    -
    -
    - Title -
    -
    - - - -
    -
    -
    - Type -
    -
    - - dashboardSavedObjects - -
    -
    -
    - Description -
    -
    - - dashboard1 desc - -
    -
    -
    - Last updated -
    -
    -
    -
    -
    - -
    - -
    - - - -
    - -
    - - - : - 10 - - } - closePopover={[Function]} - display="inlineBlock" - hasArrow={true} - isOpen={false} - ownFocus={true} - panelPaddingSize="none" - > -
    -
    - - - -
    -
    -
    -
    -
    - -
    - - - -
    -
    -
    -
    -
    -
    - -
    - -
    - -
    -
    - - -
    + />
    @@ -3785,2151 +2315,11 @@ exports[`dashboard listing render table listing with initial filters from URL 1` data-test-subj="dashboardLandingPage" >
    - - -
    -
    - -
    - -
    - -

    - Dashboards -

    -
    -
    -
    - - -
    - - - - - -
    -
    -
    -
    -
    - -
    - - - } - pagination={ - Object { - "initialPageIndex": 0, - "initialPageSize": 10, - "pageSizeOptions": Array [ - 10, - 20, - 50, - ], - } - } - responsive={true} - search={ - Object { - "box": Object { - "incremental": true, - }, - "defaultQuery": "dashboard", - "onChange": [Function], - "toolsLeft": undefined, - } - } - selection={ - Object { - "onSelectionChange": [Function], - } - } - sorting={true} - tableLayout="fixed" - > -
    - - -
    - -
    - - - -
    -
    - - - - -
    - - - - - -
    -
    - - - - - -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    - -
    - - - } - onChange={[Function]} - pagination={ - Object { - "hidePerPageOptions": undefined, - "pageIndex": 0, - "pageSize": 10, - "pageSizeOptions": Array [ - 10, - 20, - 50, - ], - "totalItemCount": 2, - } - } - responsive={true} - selection={ - Object { - "onSelectionChange": [Function], - } - } - sorting={ - Object { - "allowNeutralSort": true, - "sort": undefined, - } - } - tableLayout="fixed" - > -
    -
    - -
    - -
    - -
    - - -
    - -
    - -
    - - -
    - - -
    - -
    - - - - } - closePopover={[Function]} - display="inlineBlock" - hasArrow={true} - isOpen={false} - ownFocus={true} - panelPaddingSize="none" - > -
    -
    - - - -
    -
    -
    -
    -
    -
    -
    -
    - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    - -
    -
    - - -
    - -
    -
    - - -
    -
    - - - - - - - - - - - - - - Actions - - - - - -
    -
    - - -
    - -
    -
    - - -
    -
    -
    - Title -
    -
    - - - -
    -
    -
    - Type -
    -
    - - dashboardSavedObjects - -
    -
    -
    - Description -
    -
    - - dashboard0 desc - -
    -
    -
    - Last updated -
    -
    -
    -
    - - - - - - - - - - Edit - - - - - - -
    -
    -
    - - -
    - -
    -
    - - -
    -
    -
    - Title -
    -
    - - - -
    -
    -
    - Type -
    -
    - - dashboardSavedObjects - -
    -
    -
    - Description -
    -
    - - dashboard1 desc - -
    -
    -
    - Last updated -
    -
    -
    -
    - - - - - - - - - - Edit - - - - - - -
    -
    -
    -
    - -
    - -
    - - - -
    - -
    - - - : - 10 - - } - closePopover={[Function]} - display="inlineBlock" - hasArrow={true} - isOpen={false} - ownFocus={true} - panelPaddingSize="none" - > -
    -
    - - - -
    -
    -
    -
    -
    - -
    - - - -
    -
    -
    -
    -
    -
    - -
    - -
    - -
    -
    - - -
    + />
    @@ -7121,274 +3511,11 @@ exports[`dashboard listing renders call to action when no dashboards exist 1`] = data-test-subj="dashboardLandingPage" >
    - - -
    - - - - } - body={ - -

    - -

    -

    - - - , - } - } - /> -

    -
    - } - iconType="dashboardApp" - title={ -

    - -

    - } - > -
    - - - - -
    - - -

    - - Create your first dashboard - -

    -
    - - - -
    - - -
    -

    - - You can combine data views from any OpenSearch Dashboards app into one dashboard and see everything in one place. - -

    -

    - - - , - } - } - > - New to OpenSearch Dashboards? - - - - to take a test drive. - -

    -
    -
    - - - -
    - - - - - - -
    - -
    - - -
    + />
    @@ -8580,2111 +4707,11 @@ exports[`dashboard listing renders table rows 1`] = ` data-test-subj="dashboardLandingPage" >
    - - -
    -
    - -
    - -
    - -

    - Dashboards -

    -
    -
    -
    - - -
    - - - - - -
    -
    -
    -
    -
    - -
    - - - } - pagination={ - Object { - "initialPageIndex": 0, - "initialPageSize": 10, - "pageSizeOptions": Array [ - 10, - 20, - 50, - ], - } - } - responsive={true} - search={ - Object { - "box": Object { - "incremental": true, - }, - "defaultQuery": "", - "onChange": [Function], - "toolsLeft": undefined, - } - } - selection={ - Object { - "onSelectionChange": [Function], - } - } - sorting={true} - tableLayout="fixed" - > -
    - - -
    - -
    - - - -
    -
    - - - - -
    - - - - - -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    - -
    - - - } - onChange={[Function]} - pagination={ - Object { - "hidePerPageOptions": undefined, - "pageIndex": 0, - "pageSize": 10, - "pageSizeOptions": Array [ - 10, - 20, - 50, - ], - "totalItemCount": 2, - } - } - responsive={true} - selection={ - Object { - "onSelectionChange": [Function], - } - } - sorting={ - Object { - "allowNeutralSort": true, - "sort": undefined, - } - } - tableLayout="fixed" - > -
    -
    - -
    - -
    - -
    - - -
    - -
    - -
    - - -
    - - -
    - -
    - - - - } - closePopover={[Function]} - display="inlineBlock" - hasArrow={true} - isOpen={false} - ownFocus={true} - panelPaddingSize="none" - > -
    -
    - - - -
    -
    -
    -
    -
    -
    -
    -
    - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    - -
    -
    - - -
    - -
    -
    - - -
    -
    - - - - - - - - - - - - - - Actions - - - - - -
    -
    - - -
    - -
    -
    - - -
    -
    -
    - Title -
    -
    - - - -
    -
    -
    - Type -
    -
    - - dashboardSavedObjects - -
    -
    -
    - Description -
    -
    - - dashboard0 desc - -
    -
    -
    - Last updated -
    -
    -
    -
    - - - - - - - - - - Edit - - - - - - -
    -
    -
    - - -
    - -
    -
    - - -
    -
    -
    - Title -
    -
    - - - -
    -
    -
    - Type -
    -
    - - dashboardSavedObjects - -
    -
    -
    - Description -
    -
    - - dashboard1 desc - -
    -
    -
    - Last updated -
    -
    -
    -
    - - - - - - - - - - Edit - - - - - - -
    -
    -
    -
    - -
    - -
    - - - -
    - -
    - - - : - 10 - - } - closePopover={[Function]} - display="inlineBlock" - hasArrow={true} - isOpen={false} - ownFocus={true} - panelPaddingSize="none" - > -
    -
    - - - -
    -
    -
    -
    -
    - -
    - - - -
    -
    -
    -
    -
    -
    - -
    - -
    - -
    -
    - - -
    + />
    @@ -11876,2231 +5903,11 @@ exports[`dashboard listing renders warning when listingLimit is exceeded 1`] = ` data-test-subj="dashboardLandingPage" >
    - - -
    -
    - -
    - -
    - -

    - Dashboards -

    -
    -
    -
    - - -
    - - - - - -
    -
    -
    -
    -
    - -
    - - - } - > -
    -
    - - - - Listing limit exceeded - - -
    - -
    - -
    -

    - - - , - "entityNamePlural": "dashboards", - "listingLimitText": - listingLimit - , - "listingLimitValue": 1, - "totalItems": 2, - } - } - > - You have 2 dashboards, but your - - listingLimit - - setting prevents the table below from displaying more than 1. You can change this setting under - - - - Advanced Settings - - - - . - -

    -
    -
    -
    -
    -
    -
    - -
    - - - } - pagination={ - Object { - "initialPageIndex": 0, - "initialPageSize": 10, - "pageSizeOptions": Array [ - 10, - 20, - 50, - ], - } - } - responsive={true} - search={ - Object { - "box": Object { - "incremental": true, - }, - "defaultQuery": "", - "onChange": [Function], - "toolsLeft": undefined, - } - } - selection={ - Object { - "onSelectionChange": [Function], - } - } - sorting={true} - tableLayout="fixed" - > -
    - - -
    - -
    - - - -
    -
    - - - - -
    - - - - - -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    - -
    - - - } - onChange={[Function]} - pagination={ - Object { - "hidePerPageOptions": undefined, - "pageIndex": 0, - "pageSize": 10, - "pageSizeOptions": Array [ - 10, - 20, - 50, - ], - "totalItemCount": 2, - } - } - responsive={true} - selection={ - Object { - "onSelectionChange": [Function], - } - } - sorting={ - Object { - "allowNeutralSort": true, - "sort": undefined, - } - } - tableLayout="fixed" - > -
    -
    - -
    - -
    - -
    - - -
    - -
    - -
    - - -
    - - -
    - -
    - - - - } - closePopover={[Function]} - display="inlineBlock" - hasArrow={true} - isOpen={false} - ownFocus={true} - panelPaddingSize="none" - > -
    -
    - - - -
    -
    -
    -
    -
    -
    -
    -
    - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    - -
    -
    - - -
    - -
    -
    - - -
    -
    - - - - - - - - - - - - - - Actions - - - - - -
    -
    - - -
    - -
    -
    - - -
    -
    -
    - Title -
    -
    - - - -
    -
    -
    - Type -
    -
    - - dashboardSavedObjects - -
    -
    -
    - Description -
    -
    - - dashboard0 desc - -
    -
    -
    - Last updated -
    -
    -
    -
    - - - - - - - - - - Edit - - - - - - -
    -
    -
    - - -
    - -
    -
    - - -
    -
    -
    - Title -
    -
    - - - -
    -
    -
    - Type -
    -
    - - dashboardSavedObjects - -
    -
    -
    - Description -
    -
    - - dashboard1 desc - -
    -
    -
    - Last updated -
    -
    -
    -
    - - - - - - - - - - Edit - - - - - - -
    -
    -
    -
    - -
    - -
    - - - -
    - -
    - - - : - 10 - - } - closePopover={[Function]} - display="inlineBlock" - hasArrow={true} - isOpen={false} - ownFocus={true} - panelPaddingSize="none" - > -
    -
    - - - -
    -
    -
    -
    -
    - -
    - - - -
    -
    -
    -
    -
    -
    - -
    - -
    - -
    -
    - - -
    + />
    From b82ab2a7fbfdd71af55170271b99bf442fe98236 Mon Sep 17 00:00:00 2001 From: yuye-aws Date: Fri, 22 Sep 2023 16:16:22 +0800 Subject: [PATCH 04/14] small adjustment to left menu Signed-off-by: yuye-aws --- .../collapsible_nav.test.tsx.snap | 8122 +++++++++-------- .../header/__snapshots__/header.test.tsx.snap | 620 +- .../chrome/ui/header/collapsible_nav.tsx | 156 +- 3 files changed, 4479 insertions(+), 4419 deletions(-) diff --git a/src/core/public/chrome/ui/header/__snapshots__/collapsible_nav.test.tsx.snap b/src/core/public/chrome/ui/header/__snapshots__/collapsible_nav.test.tsx.snap index 6db9ceb59863..f3fa72c923ff 100644 --- a/src/core/public/chrome/ui/header/__snapshots__/collapsible_nav.test.tsx.snap +++ b/src/core/public/chrome/ui/header/__snapshots__/collapsible_nav.test.tsx.snap @@ -566,1393 +566,1401 @@ exports[`CollapsibleNav renders links grouped by category 1`] = ` className="euiHorizontalRule euiHorizontalRule--full" /> - - - - - - - -

    - Recently Visited -

    -
    -
    - - } - className="euiCollapsibleNavGroup euiCollapsibleNavGroup--withHeading" - data-test-opensearch-logo="clock" - data-test-subj="collapsibleNavGroup-recentlyVisited" - id="mockId" - initialIsOpen={true} - isLoading={false} - isLoadingMessage={false} - onToggle={[Function]} - paddingSize="none" +
    -
    -
    - -
    -
    - -
    -
    + + +
    +
    + -
    - +
    - - + + + recent 2 + + + + + + +
    +
    -
    +
    - -
    -
    -
    -
    - - - - - - - -

    - OpenSearch Dashboards -

    -
    -
    - - } - className="euiCollapsibleNavGroup euiCollapsibleNavGroup--withHeading" - data-test-opensearch-logo="/test/ui/logos/opensearch_mark_on_light.svg" - data-test-subj="collapsibleNavGroup-opensearchDashboards" - id="mockId" - initialIsOpen={true} - isLoading={false} - isLoadingMessage={false} - onToggle={[Function]} - paddingSize="none" - > -
    + + + -
    - -
    -
    - -
    -
    + + +
    +
    + -
    - +
    - - + + + dashboard + + + + + + +
    +
    -
    +
    - -
    - -
    -
    - - - - - - - -

    - Observability -

    -
    -
    - - } - className="euiCollapsibleNavGroup euiCollapsibleNavGroup--withHeading" - data-test-opensearch-logo="logoObservability" - data-test-subj="collapsibleNavGroup-observability" - id="mockId" - initialIsOpen={true} - isLoading={false} - isLoadingMessage={false} - onToggle={[Function]} - paddingSize="none" - > -
    + + + -
    - -
    -
    - -
    -
    + + +
    +
    + -
    - +
    - - + + + logs + + + + + + +
    +
    -
    +
    - -
    - -
    -
    - - - - - - - -

    - Security -

    -
    -
    - - } - className="euiCollapsibleNavGroup euiCollapsibleNavGroup--withHeading" - data-test-opensearch-logo="logoSecurity" - data-test-subj="collapsibleNavGroup-securitySolution" - id="mockId" - initialIsOpen={true} - isLoading={false} - isLoadingMessage={false} - onToggle={[Function]} - paddingSize="none" - > -
    + + + -
    - -
    -
    - -
    -
    + + +
    +
    + -
    - +
    - - + + + siem + + + + + + +
    +
    -
    +
    - -
    - -
    -
    - - - - - - - -

    - Management -

    -
    -
    - - } - className="euiCollapsibleNavGroup euiCollapsibleNavGroup--withHeading" - data-test-opensearch-logo="managementApp" - data-test-subj="collapsibleNavGroup-management" - id="mockId" - initialIsOpen={true} - isLoading={false} - isLoadingMessage={false} - onToggle={[Function]} - paddingSize="none" - > -
    + + + -
    - -
    -
    - -
    -
    + + +
    +
    + -
    - +
    - - + + + monitoring + + + + + + +
    +
    -
    +
    - -
    - -
    -
    - -
    -
    + + + - - - -
    -
    -
    - - -
    -
    - -
      - - - Dock navigation + canvas - , - } - } - color="subdued" - data-test-subj="collapsible-nav-lock" - iconType="lockOpen" - label="Dock navigation" - onClick={[Function]} - size="xs" + + + +
    +
    +
    +
    +
    + + +
    +
    + -
  • - , + } + } + color="subdued" data-test-subj="collapsible-nav-lock" - disabled={false} + iconType="lockOpen" + label="Dock navigation" onClick={[Function]} - type="button" + size="xs" > - - - - - Dock navigation - - -
  • - - -
    -
    -
    -
    -
    + + + + + + + + +
    + + @@ -2893,315 +2901,323 @@ exports[`CollapsibleNav renders the default nav 3`] = ` data-test-subj="collapsibleNav" role={null} > - - - - - - - -

    - Recently Visited -

    -
    -
    - - } - className="euiCollapsibleNavGroup euiCollapsibleNavGroup--withHeading" - data-test-opensearch-logo="clock" - data-test-subj="collapsibleNavGroup-recentlyVisited" - id="mockId" - initialIsOpen={true} - isLoading={false} - isLoadingMessage={false} - onToggle={[Function]} - paddingSize="none" +
    -
    -
    - -
    -
    - -
    -
    + + +
    +
    + -
    - +
    -
      - -
    • - -
    • -
      -
    - + + + + + +
    +
    -
    +
    - -
    -
    -
    -
    - - -
    + + + -
    - +
    -
      - - - - Undock navigation - - , - } - } - color="subdued" - data-test-subj="collapsible-nav-lock" - iconType="lock" - label="Undock navigation" - onClick={[Function]} - size="xs" + -
    • - , + } + } + color="subdued" data-test-subj="collapsible-nav-lock" - disabled={false} + iconType="lock" + label="Undock navigation" onClick={[Function]} - type="button" + size="xs" > - - - - - Undock navigation - - -
    • -
      -
    - -
    -
    - -
    + + + + + +
    + +
    +
    + + @@ -3584,750 +3600,758 @@ exports[`CollapsibleNav with custom branding renders the nav bar in dark mode 1` onClick={[Function]} type="button" /> - - - - - - - -

    - Recently Visited -

    -
    -
    - - } - className="euiCollapsibleNavGroup euiCollapsibleNavGroup--withHeading" - data-test-opensearch-logo="clock" - data-test-subj="collapsibleNavGroup-recentlyVisited" - id="mockId" - initialIsOpen={true} - isLoading={false} - isLoadingMessage={false} - onToggle={[Function]} - paddingSize="none" +
    -
    -
    - -
    -
    - -
    -
    + + +
    +
    + -
    - +
    - - -
    -
    -
    - -
    -
    - - - - - - - - - -

    - OpenSearch Dashboards -

    -
    -
    - - } - className="euiCollapsibleNavGroup euiCollapsibleNavGroup--withHeading" - data-test-opensearch-logo="/custom/branded/mark-darkmode.svg" - data-test-subj="collapsibleNavGroup-opensearchDashboards" - id="mockId" - initialIsOpen={true} - isLoading={false} - isLoadingMessage={false} - onToggle={[Function]} - paddingSize="none" - > -
    +
  • + + + recent + + +
  • + + + +
    +
    +
    + + + +
    +
    + -
    - -
    -
    - -
    -
    + + +
    +
    + -
    - +
    - - + + + discover + + + + + + +
    +
    -
    +
    - - - - -
    - - - - - - - -

    - Observability -

    -
    -
    - - } - className="euiCollapsibleNavGroup euiCollapsibleNavGroup--withHeading" - data-test-opensearch-logo="logoObservability" - data-test-subj="collapsibleNavGroup-observability" - id="mockId" - initialIsOpen={true} - isLoading={false} - isLoadingMessage={false} - onToggle={[Function]} - paddingSize="none" - > -
    + + + -
    - -
    -
    - -
    -
    + + +
    +
    + -
    - +
    - - + + + discover + + + + + + +
    +
    -
    +
    - -
    - -
    -
    - - -
    + + + -
    - +
    -
      - - - - Dock navigation - - , - } - } - color="subdued" - data-test-subj="collapsible-nav-lock" - iconType="lockOpen" - label="Dock navigation" - onClick={[Function]} - size="xs" + -
    • - , + } + } + color="subdued" data-test-subj="collapsible-nav-lock" - disabled={false} + iconType="lockOpen" + label="Dock navigation" onClick={[Function]} - type="button" + size="xs" > - - - - - Dock navigation - - -
    • -
      -
    - -
    -
    - -
    + + + + + +
    + +
    +
    + + @@ -4709,750 +4733,758 @@ exports[`CollapsibleNav with custom branding renders the nav bar in default mode onClick={[Function]} type="button" /> - - - - - - - -

    - Recently Visited -

    -
    -
    - - } - className="euiCollapsibleNavGroup euiCollapsibleNavGroup--withHeading" - data-test-opensearch-logo="clock" - data-test-subj="collapsibleNavGroup-recentlyVisited" - id="mockId" - initialIsOpen={true} - isLoading={false} - isLoadingMessage={false} - onToggle={[Function]} - paddingSize="none" +
    -
    -
    - -
    -
    - -
    -
    + + +
    +
    + -
    - +
    - - + + + recent + + + + + + +
    +
    -
    +
    - -
    -
    -
    -
    - - - - - - - -

    - OpenSearch Dashboards -

    -
    -
    - - } - className="euiCollapsibleNavGroup euiCollapsibleNavGroup--withHeading" - data-test-opensearch-logo="/custom/branded/mark.svg" - data-test-subj="collapsibleNavGroup-opensearchDashboards" - id="mockId" - initialIsOpen={true} - isLoading={false} - isLoadingMessage={false} - onToggle={[Function]} - paddingSize="none" - > -
    + + + -
    - -
    -
    - -
    -
    + + +
    +
    + -
    - - - + + + discover + + + + + + +
    +
    -
    +
    - - - -
    -
    - - - - - - - -

    - Observability -

    -
    -
    - - } - className="euiCollapsibleNavGroup euiCollapsibleNavGroup--withHeading" - data-test-opensearch-logo="logoObservability" - data-test-subj="collapsibleNavGroup-observability" - id="mockId" - initialIsOpen={true} - isLoading={false} - isLoadingMessage={false} - onToggle={[Function]} - paddingSize="none" - > -
    + + + -
    - -
    -
    - -
    -
    + + +
    +
    + -
    - +
    - - + + + discover + + + + + + +
    +
    -
    +
    - -
    - -
    -
    - - -
    + + + -
    - +
    -
      - - - - Dock navigation - - , - } - } - color="subdued" - data-test-subj="collapsible-nav-lock" - iconType="lockOpen" - label="Dock navigation" - onClick={[Function]} - size="xs" + -
    • - , + } + } + color="subdued" data-test-subj="collapsible-nav-lock" - disabled={false} + iconType="lockOpen" + label="Dock navigation" onClick={[Function]} - type="button" + size="xs" > - - - - - Dock navigation - - -
    • -
      -
    - -
    -
    - -
    + + + + + +
    + +
    +
    + + @@ -5827,750 +5859,758 @@ exports[`CollapsibleNav without custom branding renders the nav bar in dark mode onClick={[Function]} type="button" /> - - - - - - - -

    - Recently Visited -

    -
    -
    - - } - className="euiCollapsibleNavGroup euiCollapsibleNavGroup--withHeading" - data-test-opensearch-logo="clock" - data-test-subj="collapsibleNavGroup-recentlyVisited" - id="mockId" - initialIsOpen={true} - isLoading={false} - isLoadingMessage={false} - onToggle={[Function]} - paddingSize="none" +
    -
    -
    - -
    -
    - -
    -
    + + +
    +
    + -
    - +
    - - + + + recent + + + + + + +
    +
    -
    +
    - -
    -
    -
    -
    - - - - - - - -

    - OpenSearch Dashboards -

    -
    -
    - - } - className="euiCollapsibleNavGroup euiCollapsibleNavGroup--withHeading" - data-test-opensearch-logo="/test/ui/logos/opensearch_mark_on_dark.svg" - data-test-subj="collapsibleNavGroup-opensearchDashboards" - id="mockId" - initialIsOpen={true} - isLoading={false} - isLoadingMessage={false} - onToggle={[Function]} - paddingSize="none" - > -
    + + + -
    - -
    -
    - -
    -
    + + +
    +
    + -
    - +
    - - + + + discover + + + + + + +
    +
    -
    +
    - -
    - -
    -
    - - - - - - - -

    - Observability -

    -
    -
    - - } - className="euiCollapsibleNavGroup euiCollapsibleNavGroup--withHeading" - data-test-opensearch-logo="logoObservability" - data-test-subj="collapsibleNavGroup-observability" - id="mockId" - initialIsOpen={true} - isLoading={false} - isLoadingMessage={false} - onToggle={[Function]} - paddingSize="none" - > -
    + + + -
    - -
    -
    - -
    -
    + + +
    + + +
    + +

    + Observability +

    +
    +
    +
    +
    + + + +
    +
    + -
    - +
    - - + + + discover + + + + + + +
    +
    -
    + - - - -
    -
    - - -
    + + + -
    - +
    -
      - - - - Dock navigation - - , - } - } - color="subdued" - data-test-subj="collapsible-nav-lock" - iconType="lockOpen" - label="Dock navigation" - onClick={[Function]} - size="xs" + -
    • - , + } + } + color="subdued" data-test-subj="collapsible-nav-lock" - disabled={false} + iconType="lockOpen" + label="Dock navigation" onClick={[Function]} - type="button" + size="xs" > - - - - - Dock navigation - - -
    • -
      -
    - -
    -
    - -
    + + + + + +
    + +
    +
    + + @@ -6941,750 +6981,758 @@ exports[`CollapsibleNav without custom branding renders the nav bar in default m onClick={[Function]} type="button" /> - - - - - - - -

    - Recently Visited -

    -
    -
    - - } - className="euiCollapsibleNavGroup euiCollapsibleNavGroup--withHeading" - data-test-opensearch-logo="clock" - data-test-subj="collapsibleNavGroup-recentlyVisited" - id="mockId" - initialIsOpen={true} - isLoading={false} - isLoadingMessage={false} - onToggle={[Function]} - paddingSize="none" +
    -
    -
    - -
    -
    - -
    -
    + + +
    +
    + -
    - +
    - - + + + recent + + + + + + +
    +
    -
    +
    - -
    -
    -
    -
    - - - - - - - -

    - OpenSearch Dashboards -

    -
    -
    - - } - className="euiCollapsibleNavGroup euiCollapsibleNavGroup--withHeading" - data-test-opensearch-logo="/test/ui/logos/opensearch_mark_on_light.svg" - data-test-subj="collapsibleNavGroup-opensearchDashboards" - id="mockId" - initialIsOpen={true} - isLoading={false} - isLoadingMessage={false} - onToggle={[Function]} - paddingSize="none" - > -
    + + + -
    - -
    -
    - -
    -
    + + +
    +
    + -
    - +
    - - + + + discover + + + + + + +
    +
    -
    +
    - -
    - -
    -
    - - - - - - - -

    - Observability -

    -
    -
    - - } - className="euiCollapsibleNavGroup euiCollapsibleNavGroup--withHeading" - data-test-opensearch-logo="logoObservability" - data-test-subj="collapsibleNavGroup-observability" - id="mockId" - initialIsOpen={true} - isLoading={false} - isLoadingMessage={false} - onToggle={[Function]} - paddingSize="none" - > -
    + + + -
    - -
    -
    - -
    -
    + + +
    +
    + -
    - +
    - - + + + discover + + + + + + +
    +
    -
    +
    - -
    - -
    -
    - - -
    + + + -
    - +
    -
      - - - - Dock navigation - - , - } - } - color="subdued" - data-test-subj="collapsible-nav-lock" - iconType="lockOpen" - label="Dock navigation" - onClick={[Function]} - size="xs" + -
    • - , + } + } + color="subdued" data-test-subj="collapsible-nav-lock" - disabled={false} + iconType="lockOpen" + label="Dock navigation" onClick={[Function]} - type="button" + size="xs" > - - - - - Dock navigation - - -
    • -
      -
    - -
    -
    - -
    + + + + + +
    + +
    +
    + + diff --git a/src/core/public/chrome/ui/header/__snapshots__/header.test.tsx.snap b/src/core/public/chrome/ui/header/__snapshots__/header.test.tsx.snap index a974afb64369..d80c32b45075 100644 --- a/src/core/public/chrome/ui/header/__snapshots__/header.test.tsx.snap +++ b/src/core/public/chrome/ui/header/__snapshots__/header.test.tsx.snap @@ -6337,365 +6337,373 @@ exports[`Header handles visibility and lock changes 1`] = ` className="euiHorizontalRule euiHorizontalRule--full" /> - - - - - - - -

    - Recently Visited -

    -
    -
    - - } - className="euiCollapsibleNavGroup euiCollapsibleNavGroup--withHeading" - data-test-opensearch-logo="clock" - data-test-subj="collapsibleNavGroup-recentlyVisited" - id="mockId" - initialIsOpen={true} - isLoading={false} - isLoadingMessage={false} - onToggle={[Function]} - paddingSize="none" +
    -
    -
    - -
    - - -
    -
    -
    -
    - -
    -
    + + + - -
      - -
    • - -
    • -
      -
    -
    -
    -
    -
    - - -
    -
    - -
      - - - Undock navigation + opensearchDashboards - , - } - } - color="subdued" - data-test-subj="collapsible-nav-lock" - iconType="lock" - label="Undock navigation" - onClick={[Function]} - size="xs" + + + +
    +
    +
    +
    +
    + + +
    +
    + -
  • - , + } + } + color="subdued" data-test-subj="collapsible-nav-lock" - disabled={false} + iconType="lock" + label="Undock navigation" onClick={[Function]} - type="button" + size="xs" > - - - - - Undock navigation - - -
  • - - -
    -
    -
    -
    -
    + + + + + + + + +
    + + diff --git a/src/core/public/chrome/ui/header/collapsible_nav.tsx b/src/core/public/chrome/ui/header/collapsible_nav.tsx index 680b57a5be7c..94d1213e221c 100644 --- a/src/core/public/chrome/ui/header/collapsible_nav.tsx +++ b/src/core/public/chrome/ui/header/collapsible_nav.tsx @@ -223,85 +223,89 @@ export function CollapsibleNav({ )} - {mergedNavLinks.map((item, i) => { - if (typeof item === 'string') { - const category = categoryDictionary[item]!; - const opensearchLinkLogo = - category.id === 'opensearchDashboards' ? logos.Mark.url : category.euiIconType; + + {mergedNavLinks.map((item, i) => { + if (typeof item === 'string') { + const category = categoryDictionary[item]!; + const opensearchLinkLogo = + category.id === 'opensearchDashboards' ? logos.Mark.url : category.euiIconType; - return ( - setIsCategoryOpen(category.id, isCategoryOpen, storage)} - data-test-subj={`collapsibleNavGroup-${category.id}`} - data-test-opensearch-logo={opensearchLinkLogo} - > - readyForEUI(link))} - maxWidth="none" - color="subdued" - gutterSize="none" - size="s" - /> - - ); - } else { - return ( - - - - - - ); - } - })} + return ( + + setIsCategoryOpen(category.id, isCategoryOpen, storage) + } + data-test-subj={`collapsibleNavGroup-${category.id}`} + data-test-opensearch-logo={opensearchLinkLogo} + > + readyForEUI(link))} + maxWidth="none" + color="subdued" + gutterSize="none" + size="s" + /> + + ); + } else { + return ( + + + + + + ); + } + })} - {/* Docking button only for larger screens that can support it*/} - - - - { - onIsLockedUpdate(!isLocked); - if (lockRef.current) { - lockRef.current.focus(); + {/* Docking button only for larger screens that can support it*/} + + + + - - - + onClick={() => { + onIsLockedUpdate(!isLocked); + if (lockRef.current) { + lockRef.current.focus(); + } + }} + iconType={isLocked ? 'lock' : 'lockOpen'} + /> + + + + ); } From f6b6450d326eaa480ec2f0045aa20ef12e8189ba Mon Sep 17 00:00:00 2001 From: yuye-aws Date: Thu, 28 Sep 2023 10:21:00 +0800 Subject: [PATCH 05/14] resolve git conflict Signed-off-by: yuye-aws --- src/core/public/workspace/workspaces_service.ts | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/core/public/workspace/workspaces_service.ts b/src/core/public/workspace/workspaces_service.ts index 6c2e31c087d7..9f5c97340f24 100644 --- a/src/core/public/workspace/workspaces_service.ts +++ b/src/core/public/workspace/workspaces_service.ts @@ -6,18 +6,6 @@ import { BehaviorSubject, combineLatest } from 'rxjs'; import { isEqual } from 'lodash'; import { CoreService, WorkspaceObject } from '../../types'; -import { InternalApplicationStart } from '../application'; -import { HttpStart } from '../http'; - -type WorkspaceMenuRenderFn = ({ - basePath, - getUrlForApp, - observables, -}: { - getUrlForApp: InternalApplicationStart['getUrlForApp']; - basePath: HttpStart['basePath']; - observables: WorkspaceObservables; -}) => JSX.Element | null; interface WorkspaceObservables { /** From 67c6ea8ea9c4a861545def44f04818750403b2eb Mon Sep 17 00:00:00 2001 From: yuye-aws Date: Sun, 8 Oct 2023 17:20:41 +0800 Subject: [PATCH 06/14] rename nav link service function Signed-off-by: yuye-aws --- src/core/public/chrome/chrome_service.mock.ts | 4 +- src/core/public/chrome/chrome_service.tsx | 2 +- .../nav_links/nav_links_service.test.ts | 36 ++++++++--------- .../chrome/nav_links/nav_links_service.ts | 40 +++++++++---------- .../chrome/ui/header/collapsible_nav.tsx | 5 ++- src/plugins/workspace/public/plugin.ts | 16 ++++---- 6 files changed, 52 insertions(+), 51 deletions(-) diff --git a/src/core/public/chrome/chrome_service.mock.ts b/src/core/public/chrome/chrome_service.mock.ts index b6f130270b8b..f5b9d31bbb38 100644 --- a/src/core/public/chrome/chrome_service.mock.ts +++ b/src/core/public/chrome/chrome_service.mock.ts @@ -37,9 +37,9 @@ const createStartContractMock = () => { const startContract: DeeplyMockedKeys = { getHeaderComponent: jest.fn(), navLinks: { + setNavLinks: jest.fn(), getNavLinks$: jest.fn(), - getFilteredNavLinks$: jest.fn(), - setFilteredNavLinks: jest.fn(), + getAllNavLinks$: jest.fn(), has: jest.fn(), get: jest.fn(), getAll: jest.fn(), diff --git a/src/core/public/chrome/chrome_service.tsx b/src/core/public/chrome/chrome_service.tsx index 05904063cc93..d094d86360ef 100644 --- a/src/core/public/chrome/chrome_service.tsx +++ b/src/core/public/chrome/chrome_service.tsx @@ -250,7 +250,7 @@ export class ChromeService { homeHref={http.basePath.prepend('/app/home')} isVisible$={this.isVisible$} opensearchDashboardsVersion={injectedMetadata.getOpenSearchDashboardsVersion()} - navLinks$={navLinks.getFilteredNavLinks$()} + navLinks$={navLinks.getNavLinks$()} recentlyAccessed$={recentlyAccessed.get$()} navControlsLeft$={navControls.getLeft$()} navControlsCenter$={navControls.getCenter$()} diff --git a/src/core/public/chrome/nav_links/nav_links_service.test.ts b/src/core/public/chrome/nav_links/nav_links_service.test.ts index f7a4d26af2ef..d4cfb2630496 100644 --- a/src/core/public/chrome/nav_links/nav_links_service.test.ts +++ b/src/core/public/chrome/nav_links/nav_links_service.test.ts @@ -60,11 +60,11 @@ describe('NavLinksService', () => { start = service.start({ application: mockAppService, http: mockHttp }); }); - describe('#getNavLinks$()', () => { + describe('#getAllNavLinks$()', () => { it('does not include `chromeless` applications', async () => { expect( await start - .getNavLinks$() + .getAllNavLinks$() .pipe( take(1), map((links) => links.map((l) => l.id)) @@ -76,7 +76,7 @@ describe('NavLinksService', () => { it('sorts navLinks by `order` property', async () => { expect( await start - .getNavLinks$() + .getAllNavLinks$() .pipe( take(1), map((links) => links.map((l) => l.id)) @@ -86,7 +86,7 @@ describe('NavLinksService', () => { }); it('emits multiple values', async () => { - const navLinkIds$ = start.getNavLinks$().pipe(map((links) => links.map((l) => l.id))); + const navLinkIds$ = start.getAllNavLinks$().pipe(map((links) => links.map((l) => l.id))); const emittedLinks: string[][] = []; navLinkIds$.subscribe((r) => emittedLinks.push(r)); start.update('app1', { href: '/foo' }); @@ -99,30 +99,30 @@ describe('NavLinksService', () => { }); it('completes when service is stopped', async () => { - const last$ = start.getNavLinks$().pipe(takeLast(1)).toPromise(); + const last$ = start.getAllNavLinks$().pipe(takeLast(1)).toPromise(); service.stop(); await expect(last$).resolves.toBeInstanceOf(Array); }); }); - describe('#getFilteredNavLinks$() when non null', () => { + describe('#getNavLinks$() when non null', () => { // set filtered nav links, nav link with order smaller than 0 will be filtered beforeEach(() => { const filteredNavLinks = new Map(); - start.getNavLinks$().subscribe((links) => + start.getAllNavLinks$().subscribe((links) => links.forEach((link) => { if (link.order !== undefined && link.order >= 0) { filteredNavLinks.set(link.id, link); } }) ); - start.setFilteredNavLinks(filteredNavLinks); + start.setNavLinks(filteredNavLinks); }); it('does not include `app2` applications', async () => { expect( await start - .getFilteredNavLinks$() + .getNavLinks$() .pipe( take(1), map((links) => links.map((l) => l.id)) @@ -134,7 +134,7 @@ describe('NavLinksService', () => { it('sorts navLinks by `order` property', async () => { expect( await start - .getFilteredNavLinks$() + .getNavLinks$() .pipe( take(1), map((links) => links.map((l) => l.id)) @@ -144,7 +144,7 @@ describe('NavLinksService', () => { }); it('emits multiple values', async () => { - const navLinkIds$ = start.getFilteredNavLinks$().pipe(map((links) => links.map((l) => l.id))); + const navLinkIds$ = start.getNavLinks$().pipe(map((links) => links.map((l) => l.id))); const emittedLinks: string[][] = []; navLinkIds$.subscribe((r) => emittedLinks.push(r)); start.update('app1', { href: '/foo' }); @@ -157,17 +157,17 @@ describe('NavLinksService', () => { }); it('completes when service is stopped', async () => { - const last$ = start.getFilteredNavLinks$().pipe(takeLast(1)).toPromise(); + const last$ = start.getNavLinks$().pipe(takeLast(1)).toPromise(); service.stop(); await expect(last$).resolves.toBeInstanceOf(Array); }); }); - describe('#getFilteredNavLinks$() when null', () => { + describe('#getNavLinks$() when null', () => { it('does not include `chromeless` applications', async () => { expect( await start - .getFilteredNavLinks$() + .getNavLinks$() .pipe( take(1), map((links) => links.map((l) => l.id)) @@ -179,7 +179,7 @@ describe('NavLinksService', () => { it('include `app2` applications', async () => { expect( await start - .getFilteredNavLinks$() + .getNavLinks$() .pipe( take(1), map((links) => links.map((l) => l.id)) @@ -191,7 +191,7 @@ describe('NavLinksService', () => { it('sorts navLinks by `order` property', async () => { expect( await start - .getFilteredNavLinks$() + .getNavLinks$() .pipe( take(1), map((links) => links.map((l) => l.id)) @@ -201,7 +201,7 @@ describe('NavLinksService', () => { }); it('emits multiple values', async () => { - const navLinkIds$ = start.getFilteredNavLinks$().pipe(map((links) => links.map((l) => l.id))); + const navLinkIds$ = start.getNavLinks$().pipe(map((links) => links.map((l) => l.id))); const emittedLinks: string[][] = []; navLinkIds$.subscribe((r) => emittedLinks.push(r)); start.update('app1', { href: '/foo' }); @@ -214,7 +214,7 @@ describe('NavLinksService', () => { }); it('completes when service is stopped', async () => { - const last$ = start.getFilteredNavLinks$().pipe(takeLast(1)).toPromise(); + const last$ = start.getNavLinks$().pipe(takeLast(1)).toPromise(); service.stop(); await expect(last$).resolves.toBeInstanceOf(Array); }); diff --git a/src/core/public/chrome/nav_links/nav_links_service.ts b/src/core/public/chrome/nav_links/nav_links_service.ts index ddbef0beed6d..f3e3b4c97ad0 100644 --- a/src/core/public/chrome/nav_links/nav_links_service.ts +++ b/src/core/public/chrome/nav_links/nav_links_service.ts @@ -54,14 +54,14 @@ export interface ChromeNavLinks { getNavLinks$(): Observable>>; /** - * Set an observable for a sorted list of filtered navlinks. + * Get an observable for a sorted list of all navlinks. */ - getFilteredNavLinks$(): Observable>>; + getAllNavLinks$(): Observable>>; /** * Set filtered navlinks. */ - setFilteredNavLinks(filteredNavLinks: ReadonlyMap): void; + setNavLinks(navLinks: ReadonlyMap): void; /** * Get the state of a navlink at this point in time. @@ -126,7 +126,7 @@ type LinksUpdater = (navLinks: Map) => Map | undefined>( + private navLinks$ = new BehaviorSubject | undefined>( undefined ); @@ -145,7 +145,7 @@ export class NavLinksService { // manual link modifications to be able to re-apply then after every // availableApps$ changes. const linkUpdaters$ = new BehaviorSubject([]); - const navLinks$ = new BehaviorSubject>(new Map()); + const allNavLinks$ = new BehaviorSubject>(new Map()); combineLatest([appLinks$, linkUpdaters$]) .pipe( @@ -154,42 +154,40 @@ export class NavLinksService { }) ) .subscribe((navLinks) => { - navLinks$.next(navLinks); + allNavLinks$.next(navLinks); }); const forceAppSwitcherNavigation$ = new BehaviorSubject(false); return { getNavLinks$: () => { - return navLinks$.pipe(map(sortNavLinks), takeUntil(this.stop$)); + return combineLatest([allNavLinks$, this.navLinks$]).pipe( + map(([allNavLinks, navLinks]) => + navLinks === undefined ? sortNavLinks(allNavLinks) : sortChromeNavLinks(navLinks) + ), + takeUntil(this.stop$) + ); }, - setFilteredNavLinks: (filteredNavLinks: ReadonlyMap) => { - this.filteredNavLinks$.next(filteredNavLinks); + setNavLinks: (filteredNavLinks: ReadonlyMap) => { + this.navLinks$.next(filteredNavLinks); }, - getFilteredNavLinks$: () => { - return combineLatest([navLinks$, this.filteredNavLinks$]).pipe( - map(([navLinks, filteredNavLinks]) => - filteredNavLinks === undefined - ? sortNavLinks(navLinks) - : sortChromeNavLinks(filteredNavLinks) - ), - takeUntil(this.stop$) - ); + getAllNavLinks$: () => { + return allNavLinks$.pipe(map(sortNavLinks), takeUntil(this.stop$)); }, get(id: string) { - const link = navLinks$.value.get(id); + const link = allNavLinks$.value.get(id); return link && link.properties; }, getAll() { - return sortNavLinks(navLinks$.value); + return sortNavLinks(allNavLinks$.value); }, has(id: string) { - return navLinks$.value.has(id); + return allNavLinks$.value.has(id); }, showOnly(id: string) { diff --git a/src/core/public/chrome/ui/header/collapsible_nav.tsx b/src/core/public/chrome/ui/header/collapsible_nav.tsx index 94d1213e221c..5ab4e45a9fd9 100644 --- a/src/core/public/chrome/ui/header/collapsible_nav.tsx +++ b/src/core/public/chrome/ui/header/collapsible_nav.tsx @@ -43,6 +43,7 @@ import { groupBy, sortBy } from 'lodash'; import React, { Fragment, useRef } from 'react'; import useObservable from 'react-use/lib/useObservable'; import * as Rx from 'rxjs'; +import { DEFAULT_APP_CATEGORIES } from 'src/core/utils'; import { ChromeNavLink, ChromeRecentlyAccessedHistoryItem } from '../..'; import { AppCategory } from '../../../../types'; import { InternalApplicationStart } from '../../../application'; @@ -228,7 +229,9 @@ export function CollapsibleNav({ if (typeof item === 'string') { const category = categoryDictionary[item]!; const opensearchLinkLogo = - category.id === 'opensearchDashboards' ? logos.Mark.url : category.euiIconType; + category.id === DEFAULT_APP_CATEGORIES.opensearchDashboards.id + ? logos.Mark.url + : category.euiIconType; return ( { private filterNavLinks(core: CoreStart) { const navLinksService = core.chrome.navLinks; - const chromeNavLinks$ = navLinksService.getNavLinks$(); + const allNavLinks$ = navLinksService.getAllNavLinks$(); const currentWorkspace$ = core.workspaces.currentWorkspace$; combineLatest([ - chromeNavLinks$.pipe(map(this.changeCategoryNameByWorkspaceFeatureFlag)), + allNavLinks$.pipe(map(this.changeCategoryNameByWorkspaceFeatureFlag)), currentWorkspace$, - ]).subscribe(([chromeNavLinks, currentWorkspace]) => { - const filteredNavLinks = new Map(); - chromeNavLinks = this.filterByWorkspace(currentWorkspace, chromeNavLinks); - chromeNavLinks.forEach((chromeNavLink) => { - filteredNavLinks.set(chromeNavLink.id, chromeNavLink); + ]).subscribe(([allNavLinks, currentWorkspace]) => { + const filteredNavLinks = this.filterByWorkspace(currentWorkspace, allNavLinks); + const navLinks = new Map(); + filteredNavLinks.forEach((chromeNavLink) => { + navLinks.set(chromeNavLink.id, chromeNavLink); }); - navLinksService.setFilteredNavLinks(filteredNavLinks); + navLinksService.setNavLinks(navLinks); }); } From ec57a734c5c6a94a928527fbfa57bf884a6894fe Mon Sep 17 00:00:00 2001 From: yuye-aws Date: Mon, 9 Oct 2023 09:10:42 +0800 Subject: [PATCH 07/14] unit test for workspace plugin.ts Signed-off-by: yuye-aws --- .../chrome/ui/header/collapsible_nav.tsx | 2 +- src/plugins/workspace/public/plugin.test.ts | 28 +++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/core/public/chrome/ui/header/collapsible_nav.tsx b/src/core/public/chrome/ui/header/collapsible_nav.tsx index 5ab4e45a9fd9..a34d068c080b 100644 --- a/src/core/public/chrome/ui/header/collapsible_nav.tsx +++ b/src/core/public/chrome/ui/header/collapsible_nav.tsx @@ -43,7 +43,7 @@ import { groupBy, sortBy } from 'lodash'; import React, { Fragment, useRef } from 'react'; import useObservable from 'react-use/lib/useObservable'; import * as Rx from 'rxjs'; -import { DEFAULT_APP_CATEGORIES } from 'src/core/utils'; +import { DEFAULT_APP_CATEGORIES } from '../../../../utils'; import { ChromeNavLink, ChromeRecentlyAccessedHistoryItem } from '../..'; import { AppCategory } from '../../../../types'; import { InternalApplicationStart } from '../../../application'; diff --git a/src/plugins/workspace/public/plugin.test.ts b/src/plugins/workspace/public/plugin.test.ts index 370f60caab52..4b01e674b445 100644 --- a/src/plugins/workspace/public/plugin.test.ts +++ b/src/plugins/workspace/public/plugin.test.ts @@ -3,7 +3,9 @@ * SPDX-License-Identifier: Apache-2.0 */ +import { of } from 'rxjs'; import { waitFor } from '@testing-library/dom'; +import { ChromeNavLink } from 'opensearch-dashboards/public'; import { workspaceClientMock, WorkspaceClientMock } from './workspace_client.mock'; import { applicationServiceMock, chromeServiceMock, coreMock } from '../../../core/public/mocks'; import { WorkspacePlugin } from './plugin'; @@ -112,4 +114,30 @@ describe('Workspace plugin', () => { expect(applicationStartMock.navigateToApp).toBeCalledWith(WORKSPACE_OVERVIEW_APP_ID); windowSpy.mockRestore(); }); + + it('#start filter nav links according to workspace feature', () => { + const workspacePlugin = new WorkspacePlugin(); + const coreStart = coreMock.createStart(); + const navLinksService = coreStart.chrome.navLinks; + const devToolsNavLink = { + id: 'dev_tools', + category: { id: 'management', label: 'Management' }, + }; + const discoverNavLink = { + id: 'discover', + category: { id: 'opensearchDashboards', label: 'Library' }, + }; + const workspace = { + id: 'test', + name: 'test', + features: ['dev_tools'], + }; + const allNavLinks = of([devToolsNavLink, discoverNavLink] as ChromeNavLink[]); + const filteredNavLinksMap = new Map(); + filteredNavLinksMap.set(devToolsNavLink.id, devToolsNavLink as ChromeNavLink); + navLinksService.getAllNavLinks$.mockReturnValue(allNavLinks); + coreStart.workspaces.currentWorkspace$.next(workspace); + workspacePlugin.start(coreStart); + expect(navLinksService.setNavLinks).toHaveBeenCalledWith(filteredNavLinksMap); + }); }); From 64105bd03495810448c5de286038feef5a1b5acd Mon Sep 17 00:00:00 2001 From: yuye-aws Date: Mon, 9 Oct 2023 10:07:30 +0800 Subject: [PATCH 08/14] update snapshots Signed-off-by: yuye-aws --- .../dashboard_listing.test.tsx.snap | 20 ++++++++-------- .../dashboard_top_nav.test.tsx.snap | 24 +++++++++---------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/plugins/dashboard/public/application/components/dashboard_listing/__snapshots__/dashboard_listing.test.tsx.snap b/src/plugins/dashboard/public/application/components/dashboard_listing/__snapshots__/dashboard_listing.test.tsx.snap index 109ebe3b2cea..4cefbb32793b 100644 --- a/src/plugins/dashboard/public/application/components/dashboard_listing/__snapshots__/dashboard_listing.test.tsx.snap +++ b/src/plugins/dashboard/public/application/components/dashboard_listing/__snapshots__/dashboard_listing.test.tsx.snap @@ -223,11 +223,11 @@ exports[`dashboard listing hideWriteControls 1`] = ` "enableForcedAppSwitcherNavigation": [MockFunction], "get": [MockFunction], "getAll": [MockFunction], - "getFilteredNavLinks$": [MockFunction], + "getAllNavLinks$": [MockFunction], "getForceAppSwitcherNavigation$": [MockFunction], "getNavLinks$": [MockFunction], "has": [MockFunction], - "setFilteredNavLinks": [MockFunction], + "setNavLinks": [MockFunction], "showOnly": [MockFunction], "update": [MockFunction], }, @@ -1358,11 +1358,11 @@ exports[`dashboard listing render table listing with initial filters from URL 1` "enableForcedAppSwitcherNavigation": [MockFunction], "get": [MockFunction], "getAll": [MockFunction], - "getFilteredNavLinks$": [MockFunction], + "getAllNavLinks$": [MockFunction], "getForceAppSwitcherNavigation$": [MockFunction], "getNavLinks$": [MockFunction], "has": [MockFunction], - "setFilteredNavLinks": [MockFunction], + "setNavLinks": [MockFunction], "showOnly": [MockFunction], "update": [MockFunction], }, @@ -2554,11 +2554,11 @@ exports[`dashboard listing renders call to action when no dashboards exist 1`] = "enableForcedAppSwitcherNavigation": [MockFunction], "get": [MockFunction], "getAll": [MockFunction], - "getFilteredNavLinks$": [MockFunction], + "getAllNavLinks$": [MockFunction], "getForceAppSwitcherNavigation$": [MockFunction], "getNavLinks$": [MockFunction], "has": [MockFunction], - "setFilteredNavLinks": [MockFunction], + "setNavLinks": [MockFunction], "showOnly": [MockFunction], "update": [MockFunction], }, @@ -3750,11 +3750,11 @@ exports[`dashboard listing renders table rows 1`] = ` "enableForcedAppSwitcherNavigation": [MockFunction], "get": [MockFunction], "getAll": [MockFunction], - "getFilteredNavLinks$": [MockFunction], + "getAllNavLinks$": [MockFunction], "getForceAppSwitcherNavigation$": [MockFunction], "getNavLinks$": [MockFunction], "has": [MockFunction], - "setFilteredNavLinks": [MockFunction], + "setNavLinks": [MockFunction], "showOnly": [MockFunction], "update": [MockFunction], }, @@ -4946,11 +4946,11 @@ exports[`dashboard listing renders warning when listingLimit is exceeded 1`] = ` "enableForcedAppSwitcherNavigation": [MockFunction], "get": [MockFunction], "getAll": [MockFunction], - "getFilteredNavLinks$": [MockFunction], + "getAllNavLinks$": [MockFunction], "getForceAppSwitcherNavigation$": [MockFunction], "getNavLinks$": [MockFunction], "has": [MockFunction], - "setFilteredNavLinks": [MockFunction], + "setNavLinks": [MockFunction], "showOnly": [MockFunction], "update": [MockFunction], }, diff --git a/src/plugins/dashboard/public/application/components/dashboard_top_nav/__snapshots__/dashboard_top_nav.test.tsx.snap b/src/plugins/dashboard/public/application/components/dashboard_top_nav/__snapshots__/dashboard_top_nav.test.tsx.snap index 03bb33e5c30f..e382867ae35f 100644 --- a/src/plugins/dashboard/public/application/components/dashboard_top_nav/__snapshots__/dashboard_top_nav.test.tsx.snap +++ b/src/plugins/dashboard/public/application/components/dashboard_top_nav/__snapshots__/dashboard_top_nav.test.tsx.snap @@ -211,11 +211,11 @@ exports[`Dashboard top nav render in embed mode 1`] = ` "enableForcedAppSwitcherNavigation": [MockFunction], "get": [MockFunction], "getAll": [MockFunction], - "getFilteredNavLinks$": [MockFunction], + "getAllNavLinks$": [MockFunction], "getForceAppSwitcherNavigation$": [MockFunction], "getNavLinks$": [MockFunction], "has": [MockFunction], - "setFilteredNavLinks": [MockFunction], + "setNavLinks": [MockFunction], "showOnly": [MockFunction], "update": [MockFunction], }, @@ -1171,11 +1171,11 @@ exports[`Dashboard top nav render in embed mode, and force hide filter bar 1`] = "enableForcedAppSwitcherNavigation": [MockFunction], "get": [MockFunction], "getAll": [MockFunction], - "getFilteredNavLinks$": [MockFunction], + "getAllNavLinks$": [MockFunction], "getForceAppSwitcherNavigation$": [MockFunction], "getNavLinks$": [MockFunction], "has": [MockFunction], - "setFilteredNavLinks": [MockFunction], + "setNavLinks": [MockFunction], "showOnly": [MockFunction], "update": [MockFunction], }, @@ -2131,11 +2131,11 @@ exports[`Dashboard top nav render in embed mode, components can be forced show b "enableForcedAppSwitcherNavigation": [MockFunction], "get": [MockFunction], "getAll": [MockFunction], - "getFilteredNavLinks$": [MockFunction], + "getAllNavLinks$": [MockFunction], "getForceAppSwitcherNavigation$": [MockFunction], "getNavLinks$": [MockFunction], "has": [MockFunction], - "setFilteredNavLinks": [MockFunction], + "setNavLinks": [MockFunction], "showOnly": [MockFunction], "update": [MockFunction], }, @@ -3091,11 +3091,11 @@ exports[`Dashboard top nav render in full screen mode with appended URL param bu "enableForcedAppSwitcherNavigation": [MockFunction], "get": [MockFunction], "getAll": [MockFunction], - "getFilteredNavLinks$": [MockFunction], + "getAllNavLinks$": [MockFunction], "getForceAppSwitcherNavigation$": [MockFunction], "getNavLinks$": [MockFunction], "has": [MockFunction], - "setFilteredNavLinks": [MockFunction], + "setNavLinks": [MockFunction], "showOnly": [MockFunction], "update": [MockFunction], }, @@ -4051,11 +4051,11 @@ exports[`Dashboard top nav render in full screen mode, no componenets should be "enableForcedAppSwitcherNavigation": [MockFunction], "get": [MockFunction], "getAll": [MockFunction], - "getFilteredNavLinks$": [MockFunction], + "getAllNavLinks$": [MockFunction], "getForceAppSwitcherNavigation$": [MockFunction], "getNavLinks$": [MockFunction], "has": [MockFunction], - "setFilteredNavLinks": [MockFunction], + "setNavLinks": [MockFunction], "showOnly": [MockFunction], "update": [MockFunction], }, @@ -5011,11 +5011,11 @@ exports[`Dashboard top nav render with all components 1`] = ` "enableForcedAppSwitcherNavigation": [MockFunction], "get": [MockFunction], "getAll": [MockFunction], - "getFilteredNavLinks$": [MockFunction], + "getAllNavLinks$": [MockFunction], "getForceAppSwitcherNavigation$": [MockFunction], "getNavLinks$": [MockFunction], "has": [MockFunction], - "setFilteredNavLinks": [MockFunction], + "setNavLinks": [MockFunction], "showOnly": [MockFunction], "update": [MockFunction], }, From 222cb68011c8345a84dcbc1600d2d11ab6466926 Mon Sep 17 00:00:00 2001 From: yuye-aws Date: Mon, 9 Oct 2023 10:33:00 +0800 Subject: [PATCH 09/14] optimize code Signed-off-by: yuye-aws --- src/core/public/chrome/nav_links/nav_links_service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/public/chrome/nav_links/nav_links_service.ts b/src/core/public/chrome/nav_links/nav_links_service.ts index f3e3b4c97ad0..1edfee7db739 100644 --- a/src/core/public/chrome/nav_links/nav_links_service.ts +++ b/src/core/public/chrome/nav_links/nav_links_service.ts @@ -59,7 +59,7 @@ export interface ChromeNavLinks { getAllNavLinks$(): Observable>>; /** - * Set filtered navlinks. + * Set navlinks. */ setNavLinks(navLinks: ReadonlyMap): void; From 811484d035fcb1bf81c37f0ae10f5ab6fad02b53 Mon Sep 17 00:00:00 2001 From: yuye-aws Date: Tue, 10 Oct 2023 15:45:37 +0800 Subject: [PATCH 10/14] optimize code Signed-off-by: yuye-aws --- src/core/public/chrome/ui/header/collapsible_nav.tsx | 6 ++++-- src/core/public/chrome/ui/header/nav_link.tsx | 4 +--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/core/public/chrome/ui/header/collapsible_nav.tsx b/src/core/public/chrome/ui/header/collapsible_nav.tsx index a34d068c080b..58f52a5810a4 100644 --- a/src/core/public/chrome/ui/header/collapsible_nav.tsx +++ b/src/core/public/chrome/ui/header/collapsible_nav.tsx @@ -148,8 +148,11 @@ export function CollapsibleNav({ ...observables }: Props) { const navLinks = useObservable(observables.navLinks$, []).filter((link) => !link.hidden); + let customNavLink = useObservable(observables.customNavLink$, undefined); + if (customNavLink) { + customNavLink = { ...customNavLink, externalLink: true }; + } const recentlyAccessed = useObservable(observables.recentlyAccessed$, []); - const customNavLink = useObservable(observables.customNavLink$, undefined); const allNavLinks: CollapsibleNavLink[] = [...navLinks]; if (recentlyAccessed.length) { allNavLinks.push( @@ -209,7 +212,6 @@ export function CollapsibleNav({ navigateToApp, dataTestSubj: 'collapsibleNavCustomNavLink', onClick: closeNav, - externalLink: true, }), ]} maxWidth="none" diff --git a/src/core/public/chrome/ui/header/nav_link.tsx b/src/core/public/chrome/ui/header/nav_link.tsx index 0b0636036bbb..c28ad9088127 100644 --- a/src/core/public/chrome/ui/header/nav_link.tsx +++ b/src/core/public/chrome/ui/header/nav_link.tsx @@ -60,10 +60,8 @@ export function createEuiListItem({ onClick = () => {}, navigateToApp, dataTestSubj, - externalLink, }: Props) { - const { href, id, title, disabled, euiIconType, icon, tooltip } = link; - externalLink = externalLink || link.externalLink; + const { href, id, title, disabled, euiIconType, icon, tooltip, externalLink } = link; return { label: tooltip ?? title, From 71b41347507115c63d4176cd40fe54cb2fc4dec3 Mon Sep 17 00:00:00 2001 From: yuye-aws Date: Tue, 10 Oct 2023 15:57:20 +0800 Subject: [PATCH 11/14] optimize code Signed-off-by: yuye-aws --- src/core/public/chrome/ui/header/nav_link.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/public/chrome/ui/header/nav_link.tsx b/src/core/public/chrome/ui/header/nav_link.tsx index c28ad9088127..72dfff09f9f3 100644 --- a/src/core/public/chrome/ui/header/nav_link.tsx +++ b/src/core/public/chrome/ui/header/nav_link.tsx @@ -40,7 +40,7 @@ export const isModifiedOrPrevented = (event: React.MouseEvent Date: Tue, 17 Oct 2023 16:42:27 +0800 Subject: [PATCH 12/14] optimize code Signed-off-by: yuye-aws --- .../chrome/nav_links/nav_links_service.ts | 29 ++++----- .../chrome/ui/header/collapsible_nav.tsx | 59 ++++++++----------- 2 files changed, 35 insertions(+), 53 deletions(-) diff --git a/src/core/public/chrome/nav_links/nav_links_service.ts b/src/core/public/chrome/nav_links/nav_links_service.ts index 1edfee7db739..2b339f964704 100644 --- a/src/core/public/chrome/nav_links/nav_links_service.ts +++ b/src/core/public/chrome/nav_links/nav_links_service.ts @@ -126,9 +126,6 @@ type LinksUpdater = (navLinks: Map) => Map | undefined>( - undefined - ); public start({ application, http }: StartDeps): ChromeNavLinks { const appLinks$ = application.applications$.pipe( @@ -145,6 +142,9 @@ export class NavLinksService { // manual link modifications to be able to re-apply then after every // availableApps$ changes. const linkUpdaters$ = new BehaviorSubject([]); + const displayedNavLinks$ = new BehaviorSubject | undefined>( + undefined + ); const allNavLinks$ = new BehaviorSubject>(new Map()); combineLatest([appLinks$, linkUpdaters$]) @@ -161,20 +161,20 @@ export class NavLinksService { return { getNavLinks$: () => { - return combineLatest([allNavLinks$, this.navLinks$]).pipe( + return combineLatest([allNavLinks$, displayedNavLinks$]).pipe( map(([allNavLinks, navLinks]) => - navLinks === undefined ? sortNavLinks(allNavLinks) : sortChromeNavLinks(navLinks) + navLinks === undefined ? sortLinks(allNavLinks) : sortLinks(navLinks) ), takeUntil(this.stop$) ); }, - setNavLinks: (filteredNavLinks: ReadonlyMap) => { - this.navLinks$.next(filteredNavLinks); + setNavLinks: (navLinks: ReadonlyMap) => { + displayedNavLinks$.next(navLinks); }, getAllNavLinks$: () => { - return allNavLinks$.pipe(map(sortNavLinks), takeUntil(this.stop$)); + return allNavLinks$.pipe(map(sortLinks), takeUntil(this.stop$)); }, get(id: string) { @@ -183,7 +183,7 @@ export class NavLinksService { }, getAll() { - return sortNavLinks(allNavLinks$.value); + return sortLinks(allNavLinks$.value); }, has(id: string) { @@ -235,16 +235,9 @@ export class NavLinksService { } } -function sortNavLinks(navLinks: ReadonlyMap) { - return sortBy( - [...navLinks.values()].map((link) => link.properties), - 'order' - ); -} - -function sortChromeNavLinks(chromeNavLinks: ReadonlyMap) { +function sortLinks(links: ReadonlyMap) { return sortBy( - [...chromeNavLinks.values()].map((link) => link as Readonly), + [...links.values()].map((link) => ('properties' in link ? link.properties : link)), 'order' ); } diff --git a/src/core/public/chrome/ui/header/collapsible_nav.tsx b/src/core/public/chrome/ui/header/collapsible_nav.tsx index 58f52a5810a4..27a69f210bde 100644 --- a/src/core/public/chrome/ui/header/collapsible_nav.tsx +++ b/src/core/public/chrome/ui/header/collapsible_nav.tsx @@ -67,38 +67,28 @@ function getAllCategories(allCategorizedLinks: Record, - categoryDictionary: ReturnType -) { - return sortBy( - Object.keys(mainCategories), - (categoryName) => categoryDictionary[categoryName]?.order - ); -} - -function getMergedNavLinks( - orderedCategories: string[], +function getSortedLinksAndCategories( uncategorizedLinks: CollapsibleNavLink[], categoryDictionary: ReturnType -): Array { - const uncategorizedLinksWithOrder = sortBy( - uncategorizedLinks.filter((link) => link.order !== null), - 'order' - ); +): Array { + // uncategorized links and categories are ranked according the order + // if order is not defined, categories will be placed above uncategorized links + const categories = Object.values(categoryDictionary).filter( + (category) => category !== undefined + ) as AppCategory[]; + const uncategorizedLinksWithOrder = uncategorizedLinks.filter((link) => link.order !== null); const uncategorizedLinksWithoutOrder = uncategorizedLinks.filter((link) => link.order === null); - const orderedCategoryWithOrder = orderedCategories - .filter((categoryName) => categoryDictionary[categoryName]?.order !== null) - .map((categoryName) => ({ categoryName, order: categoryDictionary[categoryName]?.order })); - const orderedCategoryWithoutOrder = orderedCategories.filter( - (categoryName) => categoryDictionary[categoryName]?.order === null - ); - const mergedNavLinks = sortBy( - [...uncategorizedLinksWithOrder, ...orderedCategoryWithOrder], + const categoriesWithOrder = categories.filter((category) => category.order !== null); + const categoriesWithoutOrder = categories.filter((category) => category.order === null); + const sortedLinksAndCategories = sortBy( + [...uncategorizedLinksWithOrder, ...categoriesWithOrder], 'order' - ).map((navLink) => ('categoryName' in navLink ? navLink.categoryName : navLink)); - // if order is not defined , categorized links will be placed before uncategorized links - return [...mergedNavLinks, ...orderedCategoryWithoutOrder, ...uncategorizedLinksWithoutOrder]; + ); + return [ + ...sortedLinksAndCategories, + ...categoriesWithoutOrder, + ...uncategorizedLinksWithoutOrder, + ]; } function getCategoryLocalStorageKey(id: string) { @@ -166,9 +156,7 @@ export function CollapsibleNav({ const groupedNavLinks = groupBy(allNavLinks, (link) => link?.category?.id); const { undefined: uncategorizedLinks = [], ...allCategorizedLinks } = groupedNavLinks; const categoryDictionary = getAllCategories(allCategorizedLinks); - const orderedCategories = getOrderedCategories(allCategorizedLinks, categoryDictionary); - const mergedNavLinks = getMergedNavLinks( - orderedCategories, + const sortedLinksAndCategories = getSortedLinksAndCategories( uncategorizedLinks, categoryDictionary ); @@ -227,9 +215,10 @@ export function CollapsibleNav({ )} - {mergedNavLinks.map((item, i) => { - if (typeof item === 'string') { - const category = categoryDictionary[item]!; + {sortedLinksAndCategories.map((item, i) => { + if (!('title' in item)) { + // CollapsibleNavLink has title property, while AppCategory does not have + const category = item; const opensearchLinkLogo = category.id === DEFAULT_APP_CATEGORIES.opensearchDashboards.id ? logos.Mark.url @@ -253,7 +242,7 @@ export function CollapsibleNav({ defaultMessage: 'Primary navigation links, {category}', values: { category: category.label }, })} - listItems={allCategorizedLinks[item].map((link) => readyForEUI(link))} + listItems={allCategorizedLinks[item.id].map((link) => readyForEUI(link))} maxWidth="none" color="subdued" gutterSize="none" From dc3042133cade0ad8bd0d367b950e1338d6a750c Mon Sep 17 00:00:00 2001 From: yuye-aws Date: Tue, 17 Oct 2023 17:01:25 +0800 Subject: [PATCH 13/14] optimize code Signed-off-by: yuye-aws --- src/core/public/chrome/nav_links/nav_links_service.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/public/chrome/nav_links/nav_links_service.ts b/src/core/public/chrome/nav_links/nav_links_service.ts index 2b339f964704..d4c899a57be8 100644 --- a/src/core/public/chrome/nav_links/nav_links_service.ts +++ b/src/core/public/chrome/nav_links/nav_links_service.ts @@ -162,8 +162,8 @@ export class NavLinksService { return { getNavLinks$: () => { return combineLatest([allNavLinks$, displayedNavLinks$]).pipe( - map(([allNavLinks, navLinks]) => - navLinks === undefined ? sortLinks(allNavLinks) : sortLinks(navLinks) + map(([allNavLinks, displayedNavLinks]) => + displayedNavLinks === undefined ? sortLinks(allNavLinks) : sortLinks(displayedNavLinks) ), takeUntil(this.stop$) ); From 1e1e12a1e9b5f6a75f111a613c52791e30e60df4 Mon Sep 17 00:00:00 2001 From: yuye-aws Date: Tue, 17 Oct 2023 17:02:44 +0800 Subject: [PATCH 14/14] optimize code Signed-off-by: yuye-aws --- src/core/public/chrome/ui/header/collapsible_nav.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/public/chrome/ui/header/collapsible_nav.tsx b/src/core/public/chrome/ui/header/collapsible_nav.tsx index 27a69f210bde..26c1ae985a90 100644 --- a/src/core/public/chrome/ui/header/collapsible_nav.tsx +++ b/src/core/public/chrome/ui/header/collapsible_nav.tsx @@ -216,8 +216,8 @@ export function CollapsibleNav({ {sortedLinksAndCategories.map((item, i) => { - if (!('title' in item)) { - // CollapsibleNavLink has title property, while AppCategory does not have + if (!('href' in item)) { + // CollapsibleNavLink has href property, while AppCategory does not have const category = item; const opensearchLinkLogo = category.id === DEFAULT_APP_CATEGORIES.opensearchDashboards.id