diff --git a/.github/workflows/add-untriaged.yml b/.github/workflows/add-untriaged.yml index 95b110e1011..2c5daad407b 100644 --- a/.github/workflows/add-untriaged.yml +++ b/.github/workflows/add-untriaged.yml @@ -17,30 +17,58 @@ jobs: const repoOwner = context.repo.owner; const repoName = context.repo.repo; let isCollaborator = false; + + console.log(`Checking collaborator status for ${issueAuthor} in ${repoOwner}/${repoName}`); + try { - // Attempt to fetch user's permission level - await github.rest.repos.getCollaboratorPermissionLevel({ + const { data: permissionLevel } = await github.rest.repos.getCollaboratorPermissionLevel({ owner: repoOwner, repo: repoName, username: issueAuthor, }); - - // If no error is thrown, the user is a collaborator - isCollaborator = true; + + console.log(`Permission level for ${issueAuthor}: ${permissionLevel.permission}`); + + if (permissionLevel.permission !== 'none') { + isCollaborator = true; + console.log(`${issueAuthor} is a collaborator with ${permissionLevel.permission} permission.`); + } else { + console.log(`${issueAuthor} has 'none' permission, considered as non-collaborator.`); + } } catch (error) { - // Error thrown indicates the user is not a collaborator, - // or does not have explicit permission set. - console.log(`${issueAuthor} is not a collaborator.`); + if (error.status === 404) { + console.log(`${issueAuthor} is not a collaborator (404 error).`); + } else { + console.error(`Error checking collaborator status: ${error.message}`); + core.setFailed(`Error checking collaborator status: ${error.message}`); + return; + } } + core.setOutput('is_collaborator', isCollaborator.toString()); + console.log(`Set output is_collaborator to: ${isCollaborator.toString()}`); + + - name: Debug outputs + run: | + echo "Is collaborator: ${{ steps.check-collaborator.outputs.is_collaborator }}" + echo "Issue author: ${{ github.event.issue.user.login }}" + echo "Repo: ${{ github.repository }}" + - name: Apply label if not a collaborator if: steps.check-collaborator.outputs.is_collaborator == 'false' uses: actions/github-script@v6 with: script: | - github.rest.issues.addLabels({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo, - labels: ['untriaged'] - }) + console.log('Attempting to add "untriaged" label'); + try { + await github.rest.issues.addLabels({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + labels: ['untriaged'] + }); + console.log('Successfully added "untriaged" label'); + } catch (error) { + console.error(`Error adding label: ${error.message}`); + core.setFailed(`Error adding label: ${error.message}`); + } diff --git a/.github/workflows/build_and_test_workflow.yml b/.github/workflows/build_and_test_workflow.yml index 6eff022974c..728722e952d 100644 --- a/.github/workflows/build_and_test_workflow.yml +++ b/.github/workflows/build_and_test_workflow.yml @@ -11,6 +11,7 @@ on: - '**/*.md' - 'docs/**' - '.lycheeignore' + - 'CODEOWNERS' - 'changelogs/fragments/**' pull_request: branches: ['**'] @@ -18,6 +19,7 @@ on: - '**/*.md' - 'docs/**' - '.lycheeignore' + - 'CODEOWNERS' - 'changelogs/fragments/**' env: diff --git a/changelogs/fragments/7133.yml b/changelogs/fragments/7133.yml new file mode 100644 index 00000000000..e1755da0683 --- /dev/null +++ b/changelogs/fragments/7133.yml @@ -0,0 +1,2 @@ +feat: +- [Workspace] Refactor workspace form UI ([#7133](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/7133)) \ No newline at end of file diff --git a/changelogs/fragments/7166.yml b/changelogs/fragments/7166.yml new file mode 100644 index 00000000000..937c8545c0b --- /dev/null +++ b/changelogs/fragments/7166.yml @@ -0,0 +1,2 @@ +feat: +- 1. Add current nav group into chrome service 2. Prepend current nav group into breadcrumb ([#7166](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/7166)) \ No newline at end of file diff --git a/changelogs/fragments/7192.yml b/changelogs/fragments/7192.yml new file mode 100644 index 00000000000..4076bf2d162 --- /dev/null +++ b/changelogs/fragments/7192.yml @@ -0,0 +1,2 @@ +refactor: +- [Look&Feel] Refactor to use semantic headers for page, modal & flyout ([#7192](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/7192)) \ No newline at end of file diff --git a/changelogs/fragments/7195.yml b/changelogs/fragments/7195.yml new file mode 100644 index 00000000000..4aa060e47dc --- /dev/null +++ b/changelogs/fragments/7195.yml @@ -0,0 +1,2 @@ +refactor: +- [Look&Feel] Consistency of Plus Icons ([#7195](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/7195)) \ No newline at end of file diff --git a/changelogs/fragments/7197.yml b/changelogs/fragments/7197.yml new file mode 100644 index 00000000000..1810544b0b5 --- /dev/null +++ b/changelogs/fragments/7197.yml @@ -0,0 +1,2 @@ +chore: +- Skip running tests for updates in CODEOWNERS ([#7197](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/7197)) \ No newline at end of file diff --git a/changelogs/fragments/7200.yml b/changelogs/fragments/7200.yml new file mode 100644 index 00000000000..e4602a49b52 --- /dev/null +++ b/changelogs/fragments/7200.yml @@ -0,0 +1,2 @@ +refactor: +- [Look&Feel] Update Popover Padding Size ([#7200](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/7200)) \ No newline at end of file diff --git a/changelogs/fragments/7211.yml b/changelogs/fragments/7211.yml new file mode 100644 index 00000000000..504ddde9a66 --- /dev/null +++ b/changelogs/fragments/7211.yml @@ -0,0 +1,2 @@ +feat: +- Address styling of non-primary buttons by making secondary/empty ([#7211](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/7211)) \ No newline at end of file diff --git a/changelogs/fragments/7235.yml b/changelogs/fragments/7235.yml new file mode 100644 index 00000000000..e1ef6b249df --- /dev/null +++ b/changelogs/fragments/7235.yml @@ -0,0 +1,2 @@ +feat: +- Add all use case ([#7235](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/7235)) \ No newline at end of file diff --git a/src/core/public/application/ui/app_not_found_screen.tsx b/src/core/public/application/ui/app_not_found_screen.tsx index ac2b2f80b9c..9575846095c 100644 --- a/src/core/public/application/ui/app_not_found_screen.tsx +++ b/src/core/public/application/ui/app_not_found_screen.tsx @@ -28,7 +28,7 @@ * under the License. */ -import { EuiEmptyPrompt, EuiPage, EuiPageBody, EuiPageContent } from '@elastic/eui'; +import { EuiEmptyPrompt, EuiPage, EuiPageBody, EuiPageContent, EuiText } from '@elastic/eui'; import React from 'react'; import { FormattedMessage } from '@osd/i18n/react'; @@ -48,12 +48,14 @@ export const AppNotFound = () => ( } body={ -

- -

+ +

+ +

+
} /> diff --git a/src/core/public/chrome/chrome_service.mock.ts b/src/core/public/chrome/chrome_service.mock.ts index d81ec2340ff..dd595c31f45 100644 --- a/src/core/public/chrome/chrome_service.mock.ts +++ b/src/core/public/chrome/chrome_service.mock.ts @@ -78,6 +78,8 @@ const createStartContractMock = () => { navGroup: { getNavGroupsMap$: jest.fn(() => new BehaviorSubject({})), getNavGroupEnabled: jest.fn(), + getCurrentNavGroup$: jest.fn(() => new BehaviorSubject(undefined)), + setCurrentNavGroup: jest.fn(), }, setAppTitle: jest.fn(), setIsVisible: jest.fn(), diff --git a/src/core/public/chrome/chrome_service.tsx b/src/core/public/chrome/chrome_service.tsx index ccabcc0ceb8..c6e600d39fd 100644 --- a/src/core/public/chrome/chrome_service.tsx +++ b/src/core/public/chrome/chrome_service.tsx @@ -200,7 +200,7 @@ export class ChromeService { const navLinks = this.navLinks.start({ application, http }); const recentlyAccessed = await this.recentlyAccessed.start({ http, workspaces }); const docTitle = this.docTitle.start({ document: window.document }); - const navGroup = await this.navGroup.start({ navLinks }); + const navGroup = await this.navGroup.start({ navLinks, application }); // erase chrome fields from a previous app while switching to a next app application.currentAppId$.subscribe(() => { @@ -301,6 +301,8 @@ export class ChromeService { survey={injectedMetadata.getSurvey()} collapsibleNavHeaderRender={this.collapsibleNavHeaderRender} sidecarConfig$={sidecarConfig$} + navGroupEnabled={navGroup.getNavGroupEnabled()} + currentNavgroup$={navGroup.getCurrentNavGroup$()} /> ), diff --git a/src/core/public/chrome/nav_group/nav_group_service.test.ts b/src/core/public/chrome/nav_group/nav_group_service.test.ts index 64997b4e3bd..bc18483178f 100644 --- a/src/core/public/chrome/nav_group/nav_group_service.test.ts +++ b/src/core/public/chrome/nav_group/nav_group_service.test.ts @@ -5,7 +5,11 @@ import * as Rx from 'rxjs'; import { first } from 'rxjs/operators'; -import { ChromeNavGroupService, ChromeRegistrationNavLink } from './nav_group_service'; +import { + ChromeNavGroupService, + ChromeRegistrationNavLink, + CURRENT_NAV_GROUP_ID, +} from './nav_group_service'; import { uiSettingsServiceMock } from '../../ui_settings/ui_settings_service.mock'; import { NavLinksService } from '../nav_links'; import { applicationServiceMock, httpServiceMock } from '../../mocks'; @@ -82,6 +86,31 @@ mockedGetNavLinks.mockReturnValue( ]) ); +interface LooseObject { + [key: string]: any; +} + +// Mock sessionStorage +const sessionStorageMock = (() => { + let store = {} as LooseObject; + return { + getItem(key: string) { + return store[key] || null; + }, + setItem(key: string, value: string) { + store[key] = value.toString(); + }, + removeItem(key: string) { + delete store[key]; + }, + clear() { + store = {}; + }, + }; +})(); + +Object.defineProperty(window, 'sessionStorage', { value: sessionStorageMock }); + describe('ChromeNavGroupService#setup()', () => { it('should be able to `addNavLinksToGroup`', async () => { const warnMock = jest.fn(); @@ -94,6 +123,7 @@ describe('ChromeNavGroupService#setup()', () => { chromeNavGroupServiceSetup.addNavLinksToGroup(mockedGroupBar, [mockedGroupBar]); const chromeNavGroupServiceStart = await chromeNavGroupService.start({ navLinks: mockedNavLinkService, + application: mockedApplicationService, }); const groupsMap = await chromeNavGroupServiceStart.getNavGroupsMap$().pipe(first()).toPromise(); expect(groupsMap[mockedGroupFoo.id].navLinks.length).toEqual(2); @@ -116,6 +146,7 @@ describe('ChromeNavGroupService#setup()', () => { chromeNavGroupServiceSetup.addNavLinksToGroup(mockedGroupBar, [mockedGroupBar]); const chromeNavGroupServiceStart = await chromeNavGroupService.start({ navLinks: mockedNavLinkService, + application: mockedApplicationService, }); const groupsMap = await chromeNavGroupServiceStart.getNavGroupsMap$().pipe(first()).toPromise(); expect(groupsMap[mockedGroupFoo.id].navLinks.length).toEqual(1); @@ -172,7 +203,10 @@ describe('ChromeNavGroupService#start()', () => { ]); chromeNavGroupServiceSetup.addNavLinksToGroup(mockedGroupBar, [mockedNavLinkBar]); - const chromeStart = await chromeNavGroupService.start({ navLinks: mockedNavLinkService }); + const chromeStart = await chromeNavGroupService.start({ + navLinks: mockedNavLinkService, + application: mockedApplicationService, + }); const groupsMap = await chromeStart.getNavGroupsMap$().pipe(first()).toPromise(); @@ -196,6 +230,7 @@ describe('ChromeNavGroupService#start()', () => { chromeNavGroupService.setup({ uiSettings }); const chromeNavGroupServiceStart = await chromeNavGroupService.start({ navLinks: mockedNavLinkService, + application: mockedApplicationService, }); expect(chromeNavGroupServiceStart.getNavGroupEnabled()).toBe(true); @@ -210,6 +245,7 @@ describe('ChromeNavGroupService#start()', () => { chromeNavGroupService.setup({ uiSettings }); const chromeNavGroupServiceStart = await chromeNavGroupService.start({ navLinks: mockedNavLinkService, + application: mockedApplicationService, }); navGroupEnabled$.next(false); @@ -219,6 +255,109 @@ describe('ChromeNavGroupService#start()', () => { navGroupEnabled$.next(true); expect(chromeNavGroupServiceStart.getNavGroupEnabled()).toBe(false); }); + + it('should able to set current nav group', async () => { + const uiSettings = uiSettingsServiceMock.createSetupContract(); + const navGroupEnabled$ = new Rx.BehaviorSubject(true); + uiSettings.get$.mockImplementation(() => navGroupEnabled$); + + const chromeNavGroupService = new ChromeNavGroupService(); + const chromeNavGroupServiceSetup = chromeNavGroupService.setup({ uiSettings }); + + chromeNavGroupServiceSetup.addNavLinksToGroup( + { + id: 'foo', + title: 'foo title', + description: 'foo description', + }, + [mockedNavLinkFoo] + ); + + const chromeNavGroupServiceStart = await chromeNavGroupService.start({ + navLinks: mockedNavLinkService, + application: mockedApplicationService, + }); + + // set an existing nav group id + chromeNavGroupServiceStart.setCurrentNavGroup('foo'); + + expect(sessionStorageMock.getItem(CURRENT_NAV_GROUP_ID)).toEqual('foo'); + + let currentNavGroup = await chromeNavGroupServiceStart + .getCurrentNavGroup$() + .pipe(first()) + .toPromise(); + + expect(currentNavGroup?.id).toEqual('foo'); + expect(currentNavGroup?.title).toEqual('foo title'); + + // set a invalid nav group id + chromeNavGroupServiceStart.setCurrentNavGroup('bar'); + currentNavGroup = await chromeNavGroupServiceStart + .getCurrentNavGroup$() + .pipe(first()) + .toPromise(); + + expect(sessionStorageMock.getItem(CURRENT_NAV_GROUP_ID)).toBeFalsy(); + expect(currentNavGroup).toBeUndefined(); + + // reset current nav group + chromeNavGroupServiceStart.setCurrentNavGroup(undefined); + currentNavGroup = await chromeNavGroupServiceStart + .getCurrentNavGroup$() + .pipe(first()) + .toPromise(); + + expect(sessionStorageMock.getItem(CURRENT_NAV_GROUP_ID)).toBeFalsy(); + expect(currentNavGroup).toBeUndefined(); + }); + + it('should reset current nav group if app not belongs to any nav group', async () => { + const uiSettings = uiSettingsServiceMock.createSetupContract(); + const navGroupEnabled$ = new Rx.BehaviorSubject(true); + uiSettings.get$.mockImplementation(() => navGroupEnabled$); + + const chromeNavGroupService = new ChromeNavGroupService(); + const chromeNavGroupServiceSetup = chromeNavGroupService.setup({ uiSettings }); + + chromeNavGroupServiceSetup.addNavLinksToGroup( + { + id: 'foo', + title: 'foo title', + description: 'foo description', + }, + [{ id: 'foo-app1' }] + ); + + const chromeNavGroupServiceStart = await chromeNavGroupService.start({ + navLinks: mockedNavLinkService, + application: mockedApplicationService, + }); + + // set an existing nav group id + chromeNavGroupServiceStart.setCurrentNavGroup('foo'); + + expect(sessionStorageMock.getItem(CURRENT_NAV_GROUP_ID)).toEqual('foo'); + + let currentNavGroup = await chromeNavGroupServiceStart + .getCurrentNavGroup$() + .pipe(first()) + .toPromise(); + + expect(currentNavGroup?.id).toEqual('foo'); + + // navigate to app don't belongs to any nav group + mockedApplicationService.navigateToApp('bar-app'); + + currentNavGroup = await chromeNavGroupServiceStart + .getCurrentNavGroup$() + .pipe(first()) + .toPromise(); + + // verify current nav group been reset + expect(currentNavGroup).toBeFalsy(); + expect(sessionStorageMock.getItem(CURRENT_NAV_GROUP_ID)).toBeFalsy(); + }); }); describe('nav group updater', () => { @@ -233,7 +372,10 @@ describe('nav group updater', () => { id: 'foo', }, ]); - const navGroupStart = await navGroup.start({ navLinks: mockedNavLinkService }); + const navGroupStart = await navGroup.start({ + navLinks: mockedNavLinkService, + application: mockedApplicationService, + }); expect(await navGroupStart.getNavGroupsMap$().pipe(first()).toPromise()).toEqual({ dataAdministration: expect.not.objectContaining({ @@ -267,7 +409,10 @@ describe('nav group updater', () => { status: 2, })); const unregister = navGroupSetup.registerNavGroupUpdater(appUpdater$); - const navGroupStart = await navGroup.start({ navLinks: mockedNavLinkService }); + const navGroupStart = await navGroup.start({ + navLinks: mockedNavLinkService, + application: mockedApplicationService, + }); expect(await navGroupStart.getNavGroupsMap$().pipe(first()).toPromise()).toEqual({ dataAdministration: expect.objectContaining({ status: 2, diff --git a/src/core/public/chrome/nav_group/nav_group_service.ts b/src/core/public/chrome/nav_group/nav_group_service.ts index fa52b726219..bde7d0d9111 100644 --- a/src/core/public/chrome/nav_group/nav_group_service.ts +++ b/src/core/public/chrome/nav_group/nav_group_service.ts @@ -13,6 +13,10 @@ import { getOrderedLinksOrCategories, } from '../utils'; import { ChromeNavLinks } from '../nav_links'; +import { InternalApplicationStart } from '../../application'; +import { NavGroupStatus } from '../../../../core/types'; + +export const CURRENT_NAV_GROUP_ID = 'core.chrome.currentNavGroupId'; /** @public */ export interface ChromeRegistrationNavLink { @@ -25,6 +29,11 @@ export interface ChromeRegistrationNavLink { * link with parentNavLinkId field will be displayed as nested items in navigation. */ parentNavLinkId?: string; + + /** + * If the nav link should be shown in 'all' nav group + */ + showInAllNavGroup?: boolean; } export type NavGroupItemInMap = ChromeNavGroup & { @@ -45,6 +54,16 @@ export interface ChromeNavGroupServiceSetupContract { export interface ChromeNavGroupServiceStartContract { getNavGroupsMap$: () => Observable>; getNavGroupEnabled: ChromeNavGroupServiceSetupContract['getNavGroupEnabled']; + /** + * Get an observable of the current selected nav group + */ + getCurrentNavGroup$: () => Observable; + + /** + * Set current selected nav group + * @param navGroupId The id of the nav group to be set as current + */ + setCurrentNavGroup: (navGroupId: string | undefined) => void; } /** @internal */ @@ -55,6 +74,8 @@ export class ChromeNavGroupService { private navGroupEnabled: boolean = false; private navGroupEnabledUiSettingsSubscription: Subscription | undefined; private navGroupUpdaters$$ = new BehaviorSubject>>([]); + private currentNavGroup$ = new BehaviorSubject(undefined); + private addNavLinkToGroup( currentGroupsMap: Record, navGroup: ChromeNavGroup, @@ -81,6 +102,18 @@ export class ChromeNavGroupService { return currentGroupsMap; } + + private sortNavGroupNavLinks( + navGroup: NavGroupItemInMap, + allVaildNavLinks: Array> + ) { + return flattenLinksOrCategories( + getOrderedLinksOrCategories( + fulfillRegistrationLinksToChromeNavLinks(navGroup.navLinks, allVaildNavLinks) + ) + ); + } + private getSortedNavGroupsMap$() { return combineLatest([this.getUpdatedNavGroupsMap$(), this.navLinks$]) .pipe(takeUntil(this.stop$)) @@ -88,12 +121,9 @@ export class ChromeNavGroupService { map(([navGroupsMap, navLinks]) => { return Object.keys(navGroupsMap).reduce((sortedNavGroupsMap, navGroupId) => { const navGroup = navGroupsMap[navGroupId]; - const sortedNavLinks = getOrderedLinksOrCategories( - fulfillRegistrationLinksToChromeNavLinks(navGroup.navLinks, navLinks) - ); sortedNavGroupsMap[navGroupId] = { ...navGroup, - navLinks: flattenLinksOrCategories(sortedNavLinks), + navLinks: this.sortNavGroupNavLinks(navGroup, navLinks), }; return sortedNavGroupsMap; }, {} as Record); @@ -159,15 +189,63 @@ export class ChromeNavGroupService { } async start({ navLinks, + application, }: { navLinks: ChromeNavLinks; + application: InternalApplicationStart; }): Promise { this.navLinks$ = navLinks.getNavLinks$(); + + const currentNavGroupId = sessionStorage.getItem(CURRENT_NAV_GROUP_ID); + this.currentNavGroup$ = new BehaviorSubject( + currentNavGroupId ? this.navGroupsMap$.getValue()[currentNavGroupId] : undefined + ); + + const setCurrentNavGroup = (navGroupId: string | undefined) => { + const navGroup = navGroupId ? this.navGroupsMap$.getValue()[navGroupId] : undefined; + if (navGroup && navGroup.status !== NavGroupStatus.Hidden) { + this.currentNavGroup$.next(navGroup); + sessionStorage.setItem(CURRENT_NAV_GROUP_ID, navGroup.id); + } else { + this.currentNavGroup$.next(undefined); + sessionStorage.removeItem(CURRENT_NAV_GROUP_ID); + } + }; + + // erase current nav group when switch app don't belongs to any nav group + application.currentAppId$.subscribe((appId) => { + const navGroupMap = this.navGroupsMap$.getValue(); + const appIdsWithNavGroup = Object.values(navGroupMap).flatMap(({ navLinks: links }) => + links.map(({ id }) => id) + ); + + if (appId && !appIdsWithNavGroup.includes(appId)) { + setCurrentNavGroup(undefined); + } + }); + + const currentNavGroupSorted$ = combineLatest([ + this.getSortedNavGroupsMap$(), + this.currentNavGroup$, + ]) + .pipe(takeUntil(this.stop$)) + .pipe( + map(([navGroupsMapSorted, currentNavGroup]) => { + if (currentNavGroup) { + return navGroupsMapSorted[currentNavGroup.id]; + } + }) + ); + return { getNavGroupsMap$: () => this.getSortedNavGroupsMap$(), getNavGroupEnabled: () => this.navGroupEnabled, + + getCurrentNavGroup$: () => currentNavGroupSorted$, + setCurrentNavGroup, }; } + async stop() { this.stop$.next(); this.navGroupEnabledUiSettingsSubscription?.unsubscribe(); 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 b1bcadd8919..ec32ff17cf9 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 @@ -304,6 +304,55 @@ exports[`Header handles visibility and lock changes 1`] = ` "thrownError": null, } } + currentNavgroup$={ + BehaviorSubject { + "_isScalar": false, + "_value": undefined, + "closed": false, + "hasError": false, + "isStopped": false, + "observers": Array [ + Subscriber { + "_parentOrParents": null, + "_subscriptions": Array [ + SubjectSubscription { + "_parentOrParents": [Circular], + "_subscriptions": null, + "closed": false, + "subject": [Circular], + "subscriber": [Circular], + }, + ], + "closed": false, + "destination": SafeSubscriber { + "_complete": undefined, + "_context": [Circular], + "_error": undefined, + "_next": [Function], + "_parentOrParents": null, + "_parentSubscriber": [Circular], + "_subscriptions": null, + "closed": false, + "destination": Object { + "closed": true, + "complete": [Function], + "error": [Function], + "next": [Function], + }, + "isStopped": false, + "syncErrorThrowable": false, + "syncErrorThrown": false, + "syncErrorValue": null, + }, + "isStopped": false, + "syncErrorThrowable": true, + "syncErrorThrown": false, + "syncErrorValue": null, + }, + ], + "thrownError": null, + } + } customNavLink$={ BehaviorSubject { "_isScalar": false, @@ -1739,6 +1788,7 @@ exports[`Header handles visibility and lock changes 1`] = ` "thrownError": null, } } + navGroupEnabled={false} navLinks$={ BehaviorSubject { "_isScalar": false, @@ -3941,6 +3991,57 @@ exports[`Header handles visibility and lock changes 1`] = ` "thrownError": null, } } + currentNavgroup$={ + BehaviorSubject { + "_isScalar": false, + "_value": undefined, + "closed": false, + "hasError": false, + "isStopped": false, + "observers": Array [ + Subscriber { + "_parentOrParents": null, + "_subscriptions": Array [ + SubjectSubscription { + "_parentOrParents": [Circular], + "_subscriptions": null, + "closed": false, + "subject": [Circular], + "subscriber": [Circular], + }, + ], + "closed": false, + "destination": SafeSubscriber { + "_complete": undefined, + "_context": [Circular], + "_error": undefined, + "_next": [Function], + "_parentOrParents": null, + "_parentSubscriber": [Circular], + "_subscriptions": null, + "closed": false, + "destination": Object { + "closed": true, + "complete": [Function], + "error": [Function], + "next": [Function], + }, + "isStopped": false, + "syncErrorThrowable": false, + "syncErrorThrown": false, + "syncErrorValue": null, + }, + "isStopped": false, + "syncErrorThrowable": true, + "syncErrorThrown": false, + "syncErrorValue": null, + }, + ], + "thrownError": null, + } + } + navGroupEnabled={false} + navigateToApp={[MockFunction]} >
test @@ -11320,7 +11522,7 @@ exports[`Header renders condensed header 1`] = ` id="headerHelpMenu" isOpen={false} ownFocus={true} - panelPaddingSize="m" + panelPaddingSize="s" repositionOnScroll={true} >
+ + + + + +
, +
+ + + + + +
, +
+ + + First + + +
, +
+ + + Second + + +
, +] +`; + +exports[`HeaderBreadcrumbs prepend current nav group into existing breadcrumbs when nav group is enabled 2`] = ` +Array [ +
+ + + + + +
, +
+ + + + + +
, +] +`; + exports[`HeaderBreadcrumbs renders updates to the breadcrumbs$ observable 1`] = `