Skip to content

Commit

Permalink
[Workspace] Add use case enabled in chrome service(#7072)
Browse files Browse the repository at this point in the history
Signed-off-by: SuZhou-Joe <[email protected]>
  • Loading branch information
SuZhou-Joe committed Jun 24, 2024
1 parent 66f3d3f commit c9ac524
Show file tree
Hide file tree
Showing 9 changed files with 122 additions and 12 deletions.
2 changes: 2 additions & 0 deletions changelogs/fragments/7072.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
feat:
- Add use case enabled in chrome service ([#7072](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/7072))
2 changes: 2 additions & 0 deletions src/core/public/chrome/chrome_service.mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ const createSetupContractMock = () => {
registerCollapsibleNavHeader: jest.fn(),
addNavLinksToGroup: jest.fn(),
getNavGroupsMap$: getGroupsMapMock,
getUseCaseEnabled: jest.fn(),
};
};

Expand Down Expand Up @@ -91,6 +92,7 @@ const createStartContractMock = () => {
getCustomNavLink$: jest.fn(),
setCustomNavLink: jest.fn(),
getNavGroupsMap$: jest.fn(),
getUseCaseEnabled: jest.fn(),
};
startContract.navLinks.getAll.mockReturnValue([]);
startContract.getIsVisible$.mockReturnValue(new BehaviorSubject(false));
Expand Down
48 changes: 43 additions & 5 deletions src/core/public/chrome/chrome_service.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ async function start({
}: { options?: any; cspConfigMock?: any; startDeps?: ReturnType<typeof defaultStartDeps> } = {}) {
const service = new ChromeService(options);

service.setup({ uiSettings: startDeps.uiSettings });

if (cspConfigMock) {
startDeps.injectedMetadata.getCspConfig.mockReturnValue(cspConfigMock);
}
Expand Down Expand Up @@ -139,8 +141,9 @@ describe('setup', () => {
const customHeaderMock = React.createElement('TestCustomNavHeader');
const renderMock = jest.fn().mockReturnValue(customHeaderMock);
const chrome = new ChromeService({ browserSupportsCsp: true });
const uiSettings = uiSettingsServiceMock.createSetupContract();

const chromeSetup = chrome.setup();
const chromeSetup = chrome.setup({ uiSettings });
chromeSetup.registerCollapsibleNavHeader(renderMock);

const chromeStart = await chrome.start(defaultStartDeps());
Expand All @@ -155,8 +158,9 @@ describe('setup', () => {
const customHeaderMock = React.createElement('TestCustomNavHeader');
const renderMock = jest.fn().mockReturnValue(customHeaderMock);
const chrome = new ChromeService({ browserSupportsCsp: true });
const uiSettings = uiSettingsServiceMock.createSetupContract();

const chromeSetup = chrome.setup();
const chromeSetup = chrome.setup({ uiSettings });
// call 1st time
chromeSetup.registerCollapsibleNavHeader(renderMock);
// call 2nd time
Expand All @@ -170,9 +174,10 @@ describe('setup', () => {
it('should be able to `addNavLinksToGroup`', async () => {
const warnMock = jest.fn();
jest.spyOn(console, 'warn').mockImplementation(warnMock);
const uiSettings = uiSettingsServiceMock.createSetupContract();
const chrome = new ChromeService({ browserSupportsCsp: true });

const chromeSetup = chrome.setup();
const chromeSetup = chrome.setup({ uiSettings });

chromeSetup.addNavLinksToGroup(mockedGroupFoo, [mockedGroupFoo, mockedGroupBar]);
chromeSetup.addNavLinksToGroup(mockedGroupBar, [mockedGroupBar]);
Expand All @@ -187,8 +192,9 @@ describe('setup', () => {
const warnMock = jest.fn();
jest.spyOn(console, 'warn').mockImplementation(warnMock);
const chrome = new ChromeService({ browserSupportsCsp: true });
const uiSettings = uiSettingsServiceMock.createSetupContract();

const chromeSetup = chrome.setup();
const chromeSetup = chrome.setup({ uiSettings });

chromeSetup.addNavLinksToGroup(mockedGroupFoo, [mockedNavLinkFoo, mockedGroupFoo]);
chromeSetup.addNavLinksToGroup(mockedGroupBar, [mockedGroupBar]);
Expand All @@ -200,6 +206,14 @@ describe('setup', () => {
`[ChromeService] Navlink of ${mockedGroupFoo.id} has already been registered in group ${mockedGroupFoo.id}`
);
});
it('should return useCaseEnabled from ui settings', () => {
const chrome = new ChromeService({ browserSupportsCsp: true });
const uiSettings = uiSettingsServiceMock.createSetupContract();
uiSettings.get$.mockImplementation(() => new Rx.BehaviorSubject(true));

const chromeSetup = chrome.setup({ uiSettings });
expect(chromeSetup.getUseCaseEnabled()).toBe(true);
});
});

describe('start', () => {
Expand Down Expand Up @@ -545,7 +559,7 @@ describe('start', () => {
const startDeps = defaultStartDeps([]);
const chrome = new ChromeService({ browserSupportsCsp: true });

const chromeSetup = chrome.setup();
const chromeSetup = chrome.setup({ uiSettings: startDeps.uiSettings });

chromeSetup.addNavLinksToGroup(mockedGroupFoo, [mockedNavLinkFoo]);
chromeSetup.addNavLinksToGroup(mockedGroupBar, [mockedNavLinkBar]);
Expand All @@ -559,6 +573,15 @@ describe('start', () => {
expect(groupsMap[mockedGroupBar.id].navLinks.length).toEqual(1);
});
});

it('should return useCaseEnabled from ui settings', async () => {
const uiSettings = uiSettingsServiceMock.createSetupContract();
uiSettings.get$.mockImplementation(() => new Rx.BehaviorSubject(true));
const startDeps = defaultStartDeps();
const { chrome } = await start({ startDeps: { ...startDeps, uiSettings } });

expect(chrome.getUseCaseEnabled()).toBe(true);
});
});

describe('stop', () => {
Expand Down Expand Up @@ -590,4 +613,19 @@ describe('stop', () => {
).toPromise()
).resolves.toBe(undefined);
});

it('should not update useCaseEnabled after stopped', async () => {
const uiSettings = uiSettingsServiceMock.createSetupContract();
const useCaseEnabled$ = new Rx.BehaviorSubject(true);
uiSettings.get$.mockImplementation(() => useCaseEnabled$);
const startDeps = defaultStartDeps();
const { chrome, service } = await start({ startDeps: { ...startDeps, uiSettings } });

useCaseEnabled$.next(false);
expect(chrome.getUseCaseEnabled()).toBe(false);

service.stop();
useCaseEnabled$.next(true);
expect(chrome.getUseCaseEnabled()).toBe(false);
});
});
37 changes: 35 additions & 2 deletions src/core/public/chrome/chrome_service.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,15 @@
import { EuiBreadcrumb, IconType } from '@elastic/eui';
import React from 'react';
import { FormattedMessage } from '@osd/i18n/react';
import { BehaviorSubject, combineLatest, merge, Observable, of, ReplaySubject } from 'rxjs';
import {
BehaviorSubject,
combineLatest,
merge,
Observable,
of,
ReplaySubject,
Subscription,
} from 'rxjs';
import { flatMap, map, takeUntil } from 'rxjs/operators';
import { EuiLink } from '@elastic/eui';
import { mountReactNode } from '../utils/mount';
Expand Down Expand Up @@ -90,6 +98,10 @@ interface ConstructorParams {
browserSupportsCsp: boolean;
}

export interface SetupDeps {
uiSettings: IUiSettingsClient;
}

export interface StartDeps {
application: InternalApplicationStart;
docLinks: DocLinksStart;
Expand Down Expand Up @@ -117,6 +129,8 @@ export class ChromeService {
private readonly docTitle = new DocTitleService();
private readonly navGroupsMap$ = new BehaviorSubject<Record<string, NavGroupItemInMap>>({});
private collapsibleNavHeaderRender?: CollapsibleNavHeaderRender;
private useCaseEnabled: boolean = false;
private useCaseEnabledUiSettingsSubscription: Subscription | undefined;

constructor(private readonly params: ConstructorParams) {}

Expand Down Expand Up @@ -179,7 +193,12 @@ export class ChromeService {
return currentGroupsMap;
}

public setup(): ChromeSetup {
public setup({ uiSettings }: SetupDeps) {
this.useCaseEnabledUiSettingsSubscription = uiSettings
.get$('useCaseEnabled', false)
.subscribe((value) => {
this.useCaseEnabled = value;
});
return {
registerCollapsibleNavHeader: (render: CollapsibleNavHeaderRender) => {
if (this.collapsibleNavHeaderRender) {
Expand All @@ -202,6 +221,7 @@ export class ChromeService {
this.navGroupsMap$.next(navGroupsMapAfterAdd);
},
getNavGroupsMap$: () => this.navGroupsMap$.pipe(takeUntil(this.stop$)),
getUseCaseEnabled: () => this.useCaseEnabled,
};
}

Expand Down Expand Up @@ -385,12 +405,14 @@ export class ChromeService {
},

getNavGroupsMap$: () => this.navGroupsMap$.pipe(takeUntil(this.stop$)),
getUseCaseEnabled: () => this.useCaseEnabled,
};
}

public stop() {
this.navLinks.stop();
this.stop$.next();
this.useCaseEnabledUiSettingsSubscription?.unsubscribe();
}
}

Expand All @@ -408,6 +430,10 @@ export interface ChromeSetup {
registerCollapsibleNavHeader: (render: CollapsibleNavHeaderRender) => void;
addNavLinksToGroup: (group: ChromeNavGroup, navLinks: ChromeRegistrationNavLink[]) => void;
getNavGroupsMap$: () => Observable<Record<string, NavGroupItemInMap>>;
/**
* Get a boolean value to indicates whether use case is enabled
*/
getUseCaseEnabled: () => boolean;
}

/**
Expand Down Expand Up @@ -536,6 +562,10 @@ export interface ChromeStart {
getIsNavDrawerLocked$(): Observable<boolean>;

getNavGroupsMap$: ChromeSetup['getNavGroupsMap$'];
/**
* Get a boolean value to indicates whether use case is enabled
*/
getUseCaseEnabled: () => boolean;
}

/** @internal */
Expand All @@ -545,6 +575,9 @@ export interface InternalChromeStart extends ChromeStart {
* @internal
*/
getHeaderComponent(): JSX.Element;
/**
* Get a boolean value to indicates whether use case is enabled
*/
}

/** @public */
Expand Down
2 changes: 1 addition & 1 deletion src/core/public/core_system.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ export class CoreSystem {
});
const application = this.application.setup({ context, http });
this.coreApp.setup({ application, http, injectedMetadata, notifications });
const chrome = this.chrome.setup();
const chrome = this.chrome.setup({ uiSettings });

const core: InternalCoreSetup = {
application,
Expand Down
20 changes: 16 additions & 4 deletions src/core/server/ui_settings/settings/navigation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,22 @@ describe('navigation settings', () => {
expect(() => validate('modern')).not.toThrow();
expect(() => validate('legacy')).not.toThrow();
expect(() => validate('invalid')).toThrowErrorMatchingInlineSnapshot(`
"types that failed validation:
- [0]: expected value to equal [modern]
- [1]: expected value to equal [legacy]"
`);
"types that failed validation:
- [0]: expected value to equal [modern]
- [1]: expected value to equal [legacy]"
`);
});
});

describe('useCaseEnabled', () => {
const validate = getValidationFn(navigationSettings.useCaseEnabled);

it('should only accept valid values', () => {
expect(() => validate(false)).not.toThrow();
expect(() => validate(true)).not.toThrow();
expect(() => validate('invalid')).toThrowErrorMatchingInlineSnapshot(
`"expected value of type [boolean] but got [string]"`
);
});
});
});
12 changes: 12 additions & 0 deletions src/core/server/ui_settings/settings/navigation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,5 +80,17 @@ export const getNavigationSettings = (): Record<string, UiSettingsParams> => {
category: ['appearance'],
schema: schema.oneOf([schema.literal('modern'), schema.literal('legacy')]),
},
useCaseEnabled: {
name: i18n.translate('core.ui_settings.params.useCaseEnabledName', {
defaultMessage: 'Enable use case in navigation',
}),
value: false,
description: i18n.translate('core.ui_settings.params.useCaseEnabledDesc', {
defaultMessage: 'Used to control whether navigation items are grouped into use cases.',
}),
category: ['appearance'],
requiresPageReload: true,
schema: schema.boolean(),
},
};
};

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit c9ac524

Please sign in to comment.