[Chrome Workspace] Fix banner (#216143)

> Addresses #216317 

## Summary

As discussed, just for completion, this PR fixes banner (in a same hacky
way as other chrome elements)

![Screenshot 2025-03-27 at 12 04
16](https://github.com/user-attachments/assets/6de3a169-48e0-49a1-9a0c-677726526eb7)
This commit is contained in:
Anton Dosov 2025-03-28 17:08:09 +01:00 committed by Clint Andrew Hall
parent 1aa122f3ab
commit f825c1923b
5 changed files with 42 additions and 1 deletions

View file

@ -543,6 +543,7 @@ export class ChromeService {
recentlyAccessed,
http: { basePath: http.basePath, getLoadingCount$: http.getLoadingCount$ },
customBranding: { customBranding$ },
banner$: headerBanner$.asObservable(),
});
return {

View file

@ -21,6 +21,7 @@ import {
type WorkspaceTool,
type WorkspaceStart,
WORKSPACE_KNOWN_TOOLS,
ChromeUserBanner,
} from '@kbn/core-chrome-browser';
import React from 'react';
import { Provider, type ProviderProps } from 'react-redux';
@ -33,10 +34,13 @@ import {
setHomeHref,
setIconType,
createStore,
setHasBanner,
} from '@kbn/core-workspace-state';
import { ApplicationStart } from '@kbn/core-application-browser';
import { RecentlyAccessed } from '@kbn/recently-accessed';
import { css } from '@emotion/css';
import { ProjectNavigationService } from '../project_navigation';
import { HeaderExtension } from '../ui/header/header_extension';
const isKnownTool = (toolId: string): toolId is WorkspaceKnownTool => {
return WORKSPACE_KNOWN_TOOLS.includes(toolId as WorkspaceKnownTool);
@ -74,6 +78,7 @@ export interface WorkspaceServiceStartDeps {
projectNavigation: ReturnType<ProjectNavigationService['start']>;
isVisible$: Observable<boolean>;
recentlyAccessed: RecentlyAccessed;
banner$: Observable<ChromeUserBanner | undefined>;
}
/** @internal */
@ -83,6 +88,7 @@ export class WorkspaceService {
private isVisible$: Subscription | null = null;
private homeHref$: Subscription | null = null;
private iconType$: Subscription | null = null;
private banner$: Subscription | null = null;
private search: JSX.Element | null = null;
public start({
@ -91,6 +97,7 @@ export class WorkspaceService {
customBranding,
projectNavigation,
isVisible$,
banner$,
}: WorkspaceServiceStartDeps): WorkspaceStart {
const store = createStore();
const tools$ = new BehaviorSubject<ReadonlySet<WorkspaceTool>>(new Set());
@ -132,6 +139,10 @@ export class WorkspaceService {
tools$.next(new Set([...tools$.value.values(), tool]));
};
this.banner$ = banner$.subscribe((banner) => {
store.dispatch(setHasBanner(Boolean(banner)));
});
return {
getStateProvider:
() =>
@ -141,6 +152,22 @@ export class WorkspaceService {
header: {
getBreadcrumbs$: () => breadcrumbs$,
},
banner: {
getBanner$: () =>
banner$.pipe(
map((banner) => {
// TODO: This is a hack to make the banner a ReactNode
return banner
? React.createElement(HeaderExtension, {
extension: banner.content,
containerClassName: css`
height: 100%;
`,
})
: null;
})
),
},
toolbox: {
registerTool,
registerSearchControl: (control) => {
@ -171,5 +198,6 @@ export class WorkspaceService {
this.isVisible$?.unsubscribe();
this.homeHref$?.unsubscribe();
this.iconType$?.unsubscribe();
this.banner$?.unsubscribe();
}
}

View file

@ -16,6 +16,10 @@ export interface WorkspaceHeaderService {
getBreadcrumbs$: () => Observable<EuiBreadcrumb[]>;
}
export interface WorkspaceBannerService {
getBanner$: () => Observable<ReactNode | undefined>;
}
export interface WorkspaceToolboxService {
getTools$: () => Observable<Array<Readonly<WorkspaceTool>>>;
getTool$: (toolId: string | null) => Observable<WorkspaceTool | undefined>;
@ -27,6 +31,7 @@ export interface WorkspaceToolboxService {
export interface WorkspaceService {
isEnabled: () => boolean;
header: WorkspaceHeaderService;
banner: WorkspaceBannerService;
toolbox: WorkspaceToolboxService;
getStateProvider: () => ({ children }: { children: ReactNode }) => JSX.Element;
}

View file

@ -8,14 +8,19 @@
*/
import { css } from '@emotion/react';
import { SerializedStyles } from '@emotion/serialize';
import { UseEuiTheme } from '@elastic/eui';
const root = css`
type EmotionFn = (theme: UseEuiTheme) => SerializedStyles;
const root: EmotionFn = ({ euiTheme: { border } }) => css`
position: sticky;
overflow: hidden;
grid-area: banner;
height: var(--kbnWorkspace--banner-height, 0);
width: var(--kbnWorkspace--banner-width, 100vw);
top: var(--kbnWorkspace--banner-top, 0);
outline: ${border.thin};
`;
export const styles = {

View file

@ -46,6 +46,7 @@ const Component = ({
const actionMenu = <KibanaActionMenuMount {...{ currentActionMenu$ }} />;
const sideNav = <KibanaSideNavigation {...{ getActiveNodes$, getProjectSideNavComponent$ }} />;
const logo = <WorkspaceHeaderLogo />;
const banner = useDistinctObservable(workspace.banner.getBanner$(), null);
const searchButton = search ? (
<EuiFlexItem key="search" grow={false}>
@ -57,6 +58,7 @@ const Component = ({
<WorkspaceProvider {...{ tools }}>
<Workspace>
{{
banner: banner ? <Workspace.Banner>{banner}</Workspace.Banner> : undefined,
header: (
<Workspace.Header {...{ breadcrumbs, logo }}>
<EuiFlexGroup justifyContent="center" alignItems="center" gutterSize="s">