diff --git a/changelogs/fragments/8286.yml b/changelogs/fragments/8286.yml
new file mode 100644
index 000000000000..fce477aa351c
--- /dev/null
+++ b/changelogs/fragments/8286.yml
@@ -0,0 +1,2 @@
+feat:
+- [navigation] remember state when expand / collapse left nav ([#8286](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/8286))
\ No newline at end of file
diff --git a/src/core/public/chrome/ui/header/collapsible_nav_group_enabled.test.tsx b/src/core/public/chrome/ui/header/collapsible_nav_group_enabled.test.tsx
index 418dca694e21..e332cb2dac59 100644
--- a/src/core/public/chrome/ui/header/collapsible_nav_group_enabled.test.tsx
+++ b/src/core/public/chrome/ui/header/collapsible_nav_group_enabled.test.tsx
@@ -69,7 +69,6 @@ describe('', () => {
appId$: new BehaviorSubject('test'),
basePath: mockBasePath,
id: 'collapsibe-nav',
- isLocked: false,
isNavOpen: false,
currentWorkspace$: new BehaviorSubject({ id: 'test', name: 'test' }),
navLinks$: new BehaviorSubject([
@@ -94,7 +93,6 @@ describe('', () => {
...(props?.navLinks || []),
]),
storage: new StubBrowserStorage(),
- onIsLockedUpdate: () => {},
closeNav: () => {},
navigateToApp: () => Promise.resolve(),
navigateToUrl: () => Promise.resolve(),
diff --git a/src/core/public/chrome/ui/header/collapsible_nav_group_enabled.tsx b/src/core/public/chrome/ui/header/collapsible_nav_group_enabled.tsx
index d08e2a413d42..c1c9d518a219 100644
--- a/src/core/public/chrome/ui/header/collapsible_nav_group_enabled.tsx
+++ b/src/core/public/chrome/ui/header/collapsible_nav_group_enabled.tsx
@@ -23,7 +23,6 @@ import { ChromeNavControl, ChromeNavLink } from '../..';
import { AppCategory, NavGroupType } from '../../../../types';
import { InternalApplicationStart } from '../../../application/types';
import { HttpStart } from '../../../http';
-import { OnIsLockedUpdate } from './';
import { createEuiListItem } from './nav_link';
import type { Logos } from '../../../../common/types';
import {
@@ -42,11 +41,9 @@ export interface CollapsibleNavGroupEnabledProps {
collapsibleNavHeaderRender?: () => JSX.Element | null;
basePath: HttpStart['basePath'];
id: string;
- isLocked: boolean;
isNavOpen: boolean;
navLinks$: Rx.Observable;
storage?: Storage;
- onIsLockedUpdate: OnIsLockedUpdate;
closeNav: () => void;
navigateToApp: InternalApplicationStart['navigateToApp'];
navigateToUrl: InternalApplicationStart['navigateToUrl'];
@@ -80,10 +77,8 @@ enum NavWidth {
export function CollapsibleNavGroupEnabled({
basePath,
id,
- isLocked,
isNavOpen,
storage = window.localStorage,
- onIsLockedUpdate,
closeNav,
navigateToApp,
navigateToUrl,
diff --git a/src/core/public/chrome/ui/header/header.test.tsx b/src/core/public/chrome/ui/header/header.test.tsx
index c9fc84ef362c..f6a10aefc74c 100644
--- a/src/core/public/chrome/ui/header/header.test.tsx
+++ b/src/core/public/chrome/ui/header/header.test.tsx
@@ -257,4 +257,19 @@ describe('Header', () => {
expect(component.find('[data-test-subj="headerRightControl"]').exists()).toBeFalsy();
expect(component).toMatchSnapshot();
});
+
+ it('should remember the collapse state when new nav is enabled', () => {
+ const branding = {
+ useExpandedHeader: false,
+ };
+ const props = {
+ ...mockProps(),
+ branding,
+ useUpdatedHeader: true,
+ onIsLockedUpdate: jest.fn(),
+ };
+ const component = mountWithIntl();
+ component.find(EuiHeaderSectionItemButton).first().simulate('click');
+ expect(props.onIsLockedUpdate).toBeCalledWith(true);
+ });
});
diff --git a/src/core/public/chrome/ui/header/header.tsx b/src/core/public/chrome/ui/header/header.tsx
index e92ac628b1a9..784eb0dfc464 100644
--- a/src/core/public/chrome/ui/header/header.tsx
+++ b/src/core/public/chrome/ui/header/header.tsx
@@ -45,7 +45,7 @@ import {
} from '@elastic/eui';
import { i18n } from '@osd/i18n';
import classnames from 'classnames';
-import React, { createRef, useMemo, useState } from 'react';
+import React, { createRef, useCallback, useMemo, useState } from 'react';
import useObservable from 'react-use/lib/useObservable';
import { Observable } from 'rxjs';
import { LoadingIndicator } from '../';
@@ -153,7 +153,7 @@ export function Header({
const isVisible = useObservable(observables.isVisible$, false);
const headerVariant = useObservable(observables.headerVariant$, HeaderVariant.PAGE);
const isLocked = useObservable(observables.isLocked$, false);
- const [isNavOpen, setIsNavOpen] = useState(false);
+ const [isNavOpenState, setIsNavOpenState] = useState(false);
const sidecarConfig = useObservable(observables.sidecarConfig$, undefined);
const breadcrumbs = useObservable(observables.breadcrumbs$, []);
@@ -177,6 +177,22 @@ export function Header({
return getOsdSidecarPaddingStyle(sidecarConfig);
}, [sidecarConfig]);
+ const isNavOpen = useUpdatedHeader ? isLocked : isNavOpenState;
+
+ const setIsNavOpen = useCallback(
+ (value) => {
+ /**
+ * When use updated header, we will regard the lock state as source of truth
+ */
+ if (useUpdatedHeader) {
+ onIsLockedUpdate(value);
+ } else {
+ setIsNavOpenState(value);
+ }
+ },
+ [setIsNavOpenState, onIsLockedUpdate, useUpdatedHeader]
+ );
+
if (!isVisible) {
return ;
}
@@ -616,13 +632,11 @@ export function Header({
appId$={application.currentAppId$}
collapsibleNavHeaderRender={collapsibleNavHeaderRender}
id={navId}
- isLocked={isLocked}
navLinks$={observables.navLinks$}
isNavOpen={isNavOpen}
basePath={basePath}
navigateToApp={application.navigateToApp}
navigateToUrl={application.navigateToUrl}
- onIsLockedUpdate={onIsLockedUpdate}
closeNav={() => {
setIsNavOpen(false);
if (toggleCollapsibleNavRef.current) {