Skip to content

Commit

Permalink
Introduce the redesign page and applications headers behind a switch (#…
Browse files Browse the repository at this point in the history
…7637) (#7692)

* Bump OUI to 1.9.0



* Introduce the redesigned page header



Update UX of breadcrumbs, menu toggle, and the new contribution points



Add renderElement option in HeaderControls



Update application mocks and rearrange header layout



Break and restyle breadcrumb



Implement header updates



* Introduce HeaderVariant



* Organize Header's layout



* Fix header control spacing



* Conditionally append breadcrumb to recent popover
Fix mock for recent items




* Update top nav render and add app header
Use ScreenTitle instead of appname from topnav menu



* Compress QueryStringInput appearance



* Update header for applications



* Eliminate colors from the borders of grouped action menu items



* Update TopNavControl*Data type to controlType for consistency



* Add tests for chrome Header



* Update Breadcrumbs tests



* Add tests for HeaderControlsContainer



* Add tests for TopNavControls and TopNavControlItem



* Updated tests for TopNavMenu and TopNavMenuItem



* Fix `uiSettingsServiceMock` missing `start`



* Add the `target` property to TopNavControlItem



* Update Navigation mock and start contract



* Add createGetterSetter mock in dashboards app state



* Add tests for setting and unsetting header variant



* Add tests for setting header controls



* Re-skin DataSource selection's trigger button



* Conditionally change where theme management menu item shows up



* Conditionally change where the help menu items shows up



* Make IndexPatternTable page conditionally use the new page header



* Make Discover conditionally use the new application header



* Make Dashboards conditionally use the new application header



* Add changelog fragment




* Add tracking issue for empty label for DataSourceMenuPopoverButton



* Use EUI aliases in CSS variables



* Remove TopNavMenuLink



* Make sure OuiHeader doesn't contribute to the background

Also:
* Remove unused code



* Better border hiding for DSM popover button



* Make popover button overflow later



---------






(cherry picked from commit 265a176)

Signed-off-by: Miki <[email protected]>
Signed-off-by: Shenoy Pratik <[email protected]>
Co-authored-by: Zhongnan Su <[email protected]>
Co-authored-by: Shenoy Pratik <[email protected]>
  • Loading branch information
3 people authored Aug 13, 2024
1 parent 3fad78c commit 9320b9e
Show file tree
Hide file tree
Showing 103 changed files with 15,047 additions and 4,538 deletions.
2 changes: 2 additions & 0 deletions changelogs/fragments/7637.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
feat:
- Introduce the redesign page and applications headers behind a switch ([#7637](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/7637))
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@
"dependencies": {
"@aws-crypto/client-node": "^3.1.1",
"@elastic/datemath": "5.0.3",
"@elastic/eui": "npm:@opensearch-project/oui@1.8.1",
"@elastic/eui": "npm:@opensearch-project/oui@1.9.0",
"@elastic/good": "^9.0.1-kibana3",
"@elastic/numeral": "npm:@amoo-miki/[email protected]",
"@elastic/request-crypto": "2.0.0",
Expand Down
2 changes: 1 addition & 1 deletion packages/osd-ui-framework/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"enzyme-adapter-react-16": "^1.9.1"
},
"devDependencies": {
"@elastic/eui": "npm:@opensearch-project/oui@1.8.1",
"@elastic/eui": "npm:@opensearch-project/oui@1.9.0",
"@osd/babel-preset": "1.0.0",
"@osd/optimizer": "1.0.0",
"comment-stripper": "^0.0.4",
Expand Down
2 changes: 1 addition & 1 deletion packages/osd-ui-shared-deps/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
},
"dependencies": {
"@elastic/charts": "31.1.0",
"@elastic/eui": "npm:@opensearch-project/oui@1.8.1",
"@elastic/eui": "npm:@opensearch-project/oui@1.9.0",
"@elastic/numeral": "npm:@amoo-miki/[email protected]",
"@opensearch/datemath": "5.0.3",
"@osd/i18n": "1.0.0",
Expand Down

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

18 changes: 18 additions & 0 deletions src/core/public/application/application_service.mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,12 @@ const createStartContractMock = (): jest.Mocked<ApplicationStart> => {
navigateToUrl: jest.fn(),
getUrlForApp: jest.fn(),
registerMountContext: jest.fn(),
setAppLeftControls: jest.fn(),
setAppCenterControls: jest.fn(),
setAppRightControls: jest.fn(),
setAppBadgeControls: jest.fn(),
setAppDescriptionControls: jest.fn(),
setAppBottomControls: jest.fn(),
};
};

Expand Down Expand Up @@ -98,6 +104,18 @@ const createInternalStartContractMock = (): jest.Mocked<InternalApplicationStart
capabilities: capabilitiesServiceMock.createStartContract().capabilities,
currentAppId$: currentAppId$.asObservable(),
currentActionMenu$: new BehaviorSubject<MountPoint | undefined>(undefined),
currentLeftControls$: new BehaviorSubject<MountPoint | undefined>(undefined),
currentCenterControls$: new BehaviorSubject<MountPoint | undefined>(undefined),
currentRightControls$: new BehaviorSubject<MountPoint | undefined>(undefined),
currentBadgeControls$: new BehaviorSubject<MountPoint | undefined>(undefined),
currentDescriptionControls$: new BehaviorSubject<MountPoint | undefined>(undefined),
currentBottomControls$: new BehaviorSubject<MountPoint | undefined>(undefined),
setAppLeftControls: jest.fn(),
setAppCenterControls: jest.fn(),
setAppRightControls: jest.fn(),
setAppBadgeControls: jest.fn(),
setAppDescriptionControls: jest.fn(),
setAppBottomControls: jest.fn(),
getComponent: jest.fn(),
getUrlForApp: jest.fn(),
navigateToApp: jest.fn().mockImplementation((appId) => currentAppId$.next(appId)),
Expand Down
32 changes: 31 additions & 1 deletion src/core/public/application/application_service.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import {
} from './application_service.test.mocks';

import { createElement } from 'react';
import { BehaviorSubject, Subject } from 'rxjs';
import { BehaviorSubject, Subject, Observable } from 'rxjs';
import { bufferCount, take, takeUntil } from 'rxjs/operators';
import { shallow, mount } from 'enzyme';

Expand All @@ -51,7 +51,9 @@ import {
AppStatus,
AppUpdater,
WorkspaceAvailability,
InternalApplicationStart,
} from './types';
import { MountPoint } from '../types';
import { act } from 'react-dom/test-utils';
import { workspacesServiceMock } from '../mocks';

Expand Down Expand Up @@ -937,6 +939,34 @@ describe('#start()', () => {
expect(setupDeps.redirectTo).not.toHaveBeenCalled();
});
});

describe('AppControls', () => {
test.each(['Left', 'Center', 'Right', 'Badge', 'Description', 'Bottom'])(
'records the App%sControls',
async (container) => {
const { register } = service.setup(setupDeps);

register(Symbol(), createApp({ id: `app${container}` }));
const appStart = await service.start(startDeps);
const setControls = appStart[
`setApp${container}Controls` as keyof InternalApplicationStart
] as (mount: MountPoint | undefined) => void;
const currentControls$ = appStart[
`current${container}Controls$` as keyof InternalApplicationStart
] as Observable<MountPoint | undefined>;

const oldMountPoint = jest.fn();
const expectedMountPoint = jest.fn();

await appStart.navigateToApp(`app${container}`);
setControls(oldMountPoint);
setControls(expectedMountPoint);

const mountPoint = await currentControls$.pipe(take(1)).toPromise();
expect(mountPoint).toBe(expectedMountPoint);
}
);
});
});

describe('#stop()', () => {
Expand Down
142 changes: 142 additions & 0 deletions src/core/public/application/application_service.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import { RecursiveReadonly } from '@osd/utility-types';
import { MountPoint } from '../types';
import { HttpSetup, HttpStart } from '../http';
import { OverlayStart } from '../overlays';
import { HeaderControlsContainer } from '../chrome/constants';
import { ContextSetup, IContextContainer } from '../context';
import { PluginOpaqueId } from '../plugins';
import { AppRouter } from './ui';
Expand Down Expand Up @@ -104,6 +105,12 @@ interface AppUpdaterWrapper {
interface AppInternalState {
leaveHandler?: AppLeaveHandler;
actionMenu?: MountPoint;
leftControls?: MountPoint;
centerControls?: MountPoint;
rightControls?: MountPoint;
badgeControls?: MountPoint;
descriptionControls?: MountPoint;
bottomControls?: MountPoint;
}

/**
Expand All @@ -117,6 +124,15 @@ export class ApplicationService {
private readonly appInternalStates = new Map<string, AppInternalState>();
private currentAppId$ = new BehaviorSubject<string | undefined>(undefined);
private currentActionMenu$ = new BehaviorSubject<MountPoint | undefined>(undefined);

// HeaderControls
private currentLeftControls$ = new BehaviorSubject<MountPoint | undefined>(undefined);
private currentCenterControls$ = new BehaviorSubject<MountPoint | undefined>(undefined);
private currentRightControls$ = new BehaviorSubject<MountPoint | undefined>(undefined);
private currentBadgeControls$ = new BehaviorSubject<MountPoint | undefined>(undefined);
private currentDescriptionControls$ = new BehaviorSubject<MountPoint | undefined>(undefined);
private currentBottomControls$ = new BehaviorSubject<MountPoint | undefined>(undefined);

private readonly statusUpdaters$ = new BehaviorSubject<Map<symbol, AppUpdaterWrapper>>(new Map());
private readonly subscriptions: Subscription[] = [];
private stop$ = new Subject();
Expand Down Expand Up @@ -291,6 +307,15 @@ export class ApplicationService {

this.currentAppId$.subscribe(() => this.refreshCurrentActionMenu());

this.currentAppId$.subscribe(() => this.refreshCurrentControls(HeaderControlsContainer.LEFT));
this.currentAppId$.subscribe(() => this.refreshCurrentControls(HeaderControlsContainer.CENTER));
this.currentAppId$.subscribe(() => this.refreshCurrentControls(HeaderControlsContainer.RIGHT));
this.currentAppId$.subscribe(() => this.refreshCurrentControls(HeaderControlsContainer.BADGE));
this.currentAppId$.subscribe(() =>
this.refreshCurrentControls(HeaderControlsContainer.DESCRIPTION)
);
this.currentAppId$.subscribe(() => this.refreshCurrentControls(HeaderControlsContainer.BOTTOM));

return {
applications$: applications$.pipe(
map((apps) => new Map([...apps.entries()].map(([id, app]) => [id, getAppInfo(app)]))),
Expand All @@ -306,6 +331,46 @@ export class ApplicationService {
distinctUntilChanged(),
takeUntil(this.stop$)
),

// HeaderControls
currentLeftControls$: this.currentLeftControls$.pipe(
distinctUntilChanged(),
takeUntil(this.stop$)
),
currentCenterControls$: this.currentCenterControls$.pipe(
distinctUntilChanged(),
takeUntil(this.stop$)
),
currentRightControls$: this.currentRightControls$.pipe(
distinctUntilChanged(),
takeUntil(this.stop$)
),
currentBadgeControls$: this.currentBadgeControls$.pipe(
distinctUntilChanged(),
takeUntil(this.stop$)
),
currentDescriptionControls$: this.currentDescriptionControls$.pipe(
distinctUntilChanged(),
takeUntil(this.stop$)
),
currentBottomControls$: this.currentBottomControls$.pipe(
distinctUntilChanged(),
takeUntil(this.stop$)
),

setAppLeftControls: (mount: MountPoint | undefined) =>
this.setAppLeftControls(this.currentAppId$.value, mount),
setAppCenterControls: (mount: MountPoint | undefined) =>
this.setAppCenterControls(this.currentAppId$.value, mount),
setAppRightControls: (mount: MountPoint | undefined) =>
this.setAppRightControls(this.currentAppId$.value, mount),
setAppBadgeControls: (mount: MountPoint | undefined) =>
this.setAppBadgeControls(this.currentAppId$.value, mount),
setAppDescriptionControls: (mount: MountPoint | undefined) =>
this.setAppDescriptionControls(this.currentAppId$.value, mount),
setAppBottomControls: (mount: MountPoint | undefined) =>
this.setAppBottomControls(this.currentAppId$.value, mount),

history: this.history!,
registerMountContext: this.mountContext.registerContext,
getUrlForApp: (
Expand Down Expand Up @@ -339,6 +404,12 @@ export class ApplicationService {
appStatuses$={applicationStatuses$}
setAppLeaveHandler={this.setAppLeaveHandler}
setAppActionMenu={this.setAppActionMenu}
setAppLeftControls={this.setAppLeftControls}
setAppCenterControls={this.setAppCenterControls}
setAppRightControls={this.setAppRightControls}
setAppBadgeControls={this.setAppBadgeControls}
setAppDescriptionControls={this.setAppDescriptionControls}
setAppBottomControls={this.setAppBottomControls}
setIsMounting={(isMounting) => httpLoadingCount$.next(isMounting ? 1 : 0)}
/>
);
Expand Down Expand Up @@ -367,6 +438,71 @@ export class ApplicationService {
this.currentActionMenu$.next(currentActionMenu);
};

private setAppLeftControls = (appPath: string | undefined, mount: MountPoint | undefined) =>
this.setAppControls(appPath, mount, HeaderControlsContainer.LEFT);

private setAppCenterControls = (appPath: string | undefined, mount: MountPoint | undefined) =>
this.setAppControls(appPath, mount, HeaderControlsContainer.CENTER);

private setAppRightControls = (appPath: string | undefined, mount: MountPoint | undefined) =>
this.setAppControls(appPath, mount, HeaderControlsContainer.RIGHT);

private setAppBadgeControls = (appPath: string | undefined, mount: MountPoint | undefined) =>
this.setAppControls(appPath, mount, HeaderControlsContainer.BADGE);

private setAppDescriptionControls = (
appPath: string | undefined,
mount: MountPoint | undefined
) => this.setAppControls(appPath, mount, HeaderControlsContainer.DESCRIPTION);

private setAppBottomControls = (appPath: string | undefined, mount: MountPoint | undefined) =>
this.setAppControls(appPath, mount, HeaderControlsContainer.BOTTOM);

private setAppControls = (
appPath: string | undefined,
mount: MountPoint | undefined,
container: HeaderControlsContainer
) => {
if (!appPath) return;

this.appInternalStates.set(appPath, {
...(this.appInternalStates.get(appPath) ?? {}),
[`${container}Controls`]: mount,
});

this.refreshCurrentControls(container);
};

private refreshCurrentControls = (container: HeaderControlsContainer) => {
const appId = this.currentAppId$.getValue();
switch (container) {
case HeaderControlsContainer.LEFT:
return this.currentLeftControls$.next(
appId ? this.appInternalStates.get(appId)?.leftControls : undefined
);
case HeaderControlsContainer.CENTER:
return this.currentCenterControls$.next(
appId ? this.appInternalStates.get(appId)?.centerControls : undefined
);
case HeaderControlsContainer.RIGHT:
return this.currentRightControls$.next(
appId ? this.appInternalStates.get(appId)?.rightControls : undefined
);
case HeaderControlsContainer.BADGE:
return this.currentBadgeControls$.next(
appId ? this.appInternalStates.get(appId)?.badgeControls : undefined
);
case HeaderControlsContainer.DESCRIPTION:
return this.currentDescriptionControls$.next(
appId ? this.appInternalStates.get(appId)?.descriptionControls : undefined
);
case HeaderControlsContainer.BOTTOM:
return this.currentBottomControls$.next(
appId ? this.appInternalStates.get(appId)?.bottomControls : undefined
);
}
};

private async shouldNavigate(overlays: OverlayStart): Promise<boolean> {
const currentAppId = this.currentAppId$.value;
if (currentAppId === undefined) {
Expand Down Expand Up @@ -402,6 +538,12 @@ export class ApplicationService {
this.stop$.next();
this.currentAppId$.complete();
this.currentActionMenu$.complete();
this.currentLeftControls$.complete();
this.currentCenterControls$.complete();
this.currentRightControls$.complete();
this.currentBadgeControls$.complete();
this.currentDescriptionControls$.complete();
this.currentBottomControls$.complete();
this.statusUpdaters$.complete();
this.subscriptions.forEach((sub) => sub.unsubscribe());
window.removeEventListener('beforeunload', this.onBeforeUnload);
Expand Down
6 changes: 6 additions & 0 deletions src/core/public/application/integration_tests/router.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,12 @@ describe('AppRouter', () => {
appStatuses$={mountersToAppStatus$()}
setAppLeaveHandler={noop}
setAppActionMenu={noop}
setAppLeftControls={noop}
setAppCenterControls={noop}
setAppRightControls={noop}
setAppBadgeControls={noop}
setAppDescriptionControls={noop}
setAppBottomControls={noop}
setIsMounting={noop}
/>
);
Expand Down
Loading

0 comments on commit 9320b9e

Please sign in to comment.