Preparation for High Contrast Mode, Core/SharedUX domains (#202606)

## Summary

**Reviewers: Please test the code paths affected by this PR. See the
"Risks" section below.**

Part of work for enabling "high contrast mode" in Kibana. See
https://github.com/elastic/kibana/issues/176219.

**Background:**
Kibana will soon have a user profile setting to allow users to enable
"high contrast mode." This setting will activate a flag with
`<EuiProvider>` that causes EUI components to render with higher
contrast visual elements. Consumer plugins and packages need to be
updated selected places where `<EuiProvider>` is wrapped, to pass the
`UserProfileService` service dependency from the CoreStart contract.

**NOTE:** **EUI currently does not yet support the high-contrast mode
flag**, but support for that is expected to come in around 2 weeks.
These first PRs are simply preparing the code by wiring up the
`UserProvideService`.

### Checklist

Check the PR satisfies following conditions. 

Reviewers should verify this PR satisfies this list as well.

- [X] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
- [X] The PR description includes the appropriate Release Notes section,
and the correct `release_note:*` label is applied per the
[guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)

### Risks

Does this PR introduce any risks? For example, consider risks like hard
to test bugs, performance regression, potential of data loss.

Describe the risk, its severity, and mitigation for each identified
risk. Invite stakeholders and evaluate how to proceed before merging.

- [ ] [medium/high] The implementor of this change did not manually test
the affected code paths and relied on type-checking and functional tests
to drive the changes. Code owners for this PR need to manually test the
affected code paths.
- [ ] [medium] The `UserProfileService` dependency comes from the
CoreStart contract. If acquiring the service causes synchronous code to
become asynchronous, check for race conditions or errors in rendering
React components. Code owners for this PR need to manually test the
affected code paths.

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Tim Sullivan 2024-12-05 08:26:41 -07:00 committed by GitHub
parent 3579425d77
commit 6178e8295d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
95 changed files with 514 additions and 164 deletions

View file

@ -28,6 +28,7 @@ import {
AppMountParameters,
I18nStart,
ThemeServiceStart,
UserProfileService,
} from '@kbn/core/public';
import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render';
import { ExampleDefinition } from './types';
@ -36,6 +37,7 @@ interface StartServices {
analytics: Pick<AnalyticsServiceStart, 'reportEvent'>;
i18n: I18nStart;
theme: Pick<ThemeServiceStart, 'theme$'>;
userProfile: UserProfileService;
}
interface Props {

View file

@ -28,10 +28,10 @@ export class DeveloperExamplesPlugin implements Plugin<DeveloperExamplesSetup, v
async mount(params: AppMountParameters) {
const { renderApp } = await import('./app');
const [coreStart] = await core.getStartServices();
const { analytics, i18n, theme } = coreStart;
const { analytics, i18n, theme, userProfile } = coreStart;
return renderApp(
{
startServices: { analytics, i18n, theme },
startServices: { analytics, i18n, theme, userProfile },
examples,
navigateToApp: (appId: string) => coreStart.application.navigateToApp(appId),
getUrlForApp: (appId: string) => coreStart.application.getUrlForApp(appId),

View file

@ -12,6 +12,7 @@ import type {
CoreStart,
I18nStart,
ThemeServiceStart,
UserProfileService,
} from '@kbn/core/public';
import type { IHttpFetchError } from '@kbn/core-http-browser';
import {
@ -25,6 +26,7 @@ interface StartServices {
analytics: Pick<AnalyticsServiceStart, 'reportEvent'>;
i18n: I18nStart;
theme: Pick<ThemeServiceStart, 'theme$'>;
userProfile: UserProfileService;
}
export interface Services {
@ -37,8 +39,8 @@ export interface Services {
}
export function getServices(core: CoreStart): Services {
const { analytics, i18n, theme } = core;
const startServices = { analytics, i18n, theme };
const { analytics, i18n, theme, userProfile } = core;
const startServices = { analytics, i18n, theme, userProfile };
return {
startServices,

View file

@ -14,7 +14,7 @@ import { toMountPoint } from '@kbn/react-kibana-mount';
export const ACTION_HELLO_WORLD = 'ACTION_HELLO_WORLD';
type StartServices = Pick<CoreStart, 'overlays' | 'analytics' | 'i18n' | 'theme'>;
type StartServices = Pick<CoreStart, 'overlays' | 'analytics' | 'i18n' | 'theme' | 'userProfile'>;
export const createHelloWorldActionDefinition = (
getStartServices: () => Promise<StartServices>

View file

@ -19,7 +19,7 @@ const DYNAMIC_ACTION_ID = `${ACTION_HELLO_WORLD}-Waldo`;
interface Props {
uiActionsStartService: UiActionsStart;
startServices: Pick<CoreStart, 'overlays' | 'analytics' | 'i18n' | 'theme'>;
startServices: Pick<CoreStart, 'overlays' | 'analytics' | 'i18n' | 'theme' | 'userProfile'>;
}
export const HelloWorldExample = ({ uiActionsStartService, startServices }: Props) => {

View file

@ -21,6 +21,7 @@ export interface ConnectionDetailsGlobalDependencies {
http: CoreStart['http'];
application: CoreStart['application'];
overlays: CoreStart['overlays'];
userProfile: CoreStart['userProfile'];
};
plugins: {
cloud?: CloudStart;

View file

@ -21,6 +21,7 @@ export interface OpenConnectionDetailsParams {
i18n: CoreStart['i18n'];
analytics?: CoreStart['analytics'];
theme: CoreStart['theme'];
userProfile: CoreStart['userProfile'];
};
};
}

View file

@ -20,6 +20,7 @@ import type { I18nStart } from '@kbn/core-i18n-browser';
import type { MountPoint, OverlayRef } from '@kbn/core-mount-utils-browser';
import type { OverlayFlyoutOpenOptions } from '@kbn/core-overlays-browser';
import type { ThemeServiceStart } from '@kbn/core-theme-browser';
import type { UserProfileService } from '@kbn/core-user-profile-browser';
import { toMountPoint } from '@kbn/react-kibana-mount';
type NotifyFn = (title: JSX.Element, text?: string) => void;
@ -68,6 +69,7 @@ interface ContentEditorStartServices {
analytics: Pick<AnalyticsServiceStart, 'reportEvent'>;
i18n: I18nStart;
theme: Pick<ThemeServiceStart, 'theme$'>;
userProfile: UserProfileService;
}
/**

View file

@ -30,6 +30,7 @@
"@kbn/test-jest-helpers",
"@kbn/react-kibana-mount",
"@kbn/content-management-user-profiles",
"@kbn/core-user-profile-browser",
],
"exclude": [
"target/**/*"

View file

@ -24,7 +24,7 @@ import type { I18nStart } from '@kbn/core-i18n-browser';
import type { MountPoint, OverlayRef } from '@kbn/core-mount-utils-browser';
import type { OverlayFlyoutOpenOptions } from '@kbn/core-overlays-browser';
import type { ThemeServiceStart } from '@kbn/core-theme-browser';
import type { UserProfileServiceStart } from '@kbn/core-user-profile-browser';
import type { UserProfileService, UserProfileServiceStart } from '@kbn/core-user-profile-browser';
import type { FormattedRelative } from '@kbn/i18n-react';
import { toMountPoint } from '@kbn/react-kibana-mount';
import { RedirectAppLinksKibanaProvider } from '@kbn/shared-ux-link-redirect-app';
@ -100,6 +100,7 @@ interface TableListViewStartServices {
analytics: Pick<AnalyticsServiceStart, 'reportEvent'>;
i18n: I18nStart;
theme: Pick<ThemeServiceStart, 'theme$'>;
userProfile: UserProfileService;
}
/**

View file

@ -22,6 +22,7 @@ import type {
import type { AnalyticsServiceStart } from '@kbn/core-analytics-browser';
import type { I18nStart } from '@kbn/core-i18n-browser';
import type { ThemeServiceStart } from '@kbn/core-theme-browser';
import type { UserProfileService } from '@kbn/core-user-profile-browser';
import { renderApp as renderStatusApp } from './status';
import {
renderApp as renderErrorApp,
@ -45,6 +46,7 @@ export interface CoreAppsServiceStartDeps {
analytics: AnalyticsServiceStart;
i18n: I18nStart;
theme: ThemeServiceStart;
userProfile: UserProfileService;
}
export class CoreAppsService {
@ -86,9 +88,7 @@ export class CoreAppsService {
http,
notifications,
uiSettings,
analytics,
i18n,
theme,
...startDeps
}: CoreAppsServiceStartDeps) {
if (!application.history) {
return;
@ -101,7 +101,7 @@ export class CoreAppsService {
uiSettings,
});
setupPublicBaseUrlConfigWarning({ docLinks, http, notifications, analytics, i18n, theme });
setupPublicBaseUrlConfigWarning({ docLinks, http, notifications, ...startDeps });
}
public stop() {

View file

@ -13,6 +13,7 @@ import { httpServiceMock } from '@kbn/core-http-browser-mocks';
import { i18nServiceMock } from '@kbn/core-i18n-browser-mocks';
import { notificationServiceMock } from '@kbn/core-notifications-browser-mocks';
import { themeServiceMock } from '@kbn/core-theme-browser-mocks';
import { userProfileServiceMock } from '@kbn/core-user-profile-browser-mocks';
import { setupPublicBaseUrlConfigWarning } from './public_base_url';
@ -22,12 +23,8 @@ describe('publicBaseUrl warning', () => {
const i18nStart = i18nServiceMock.createStartContract();
const analytics = analyticsServiceMock.createAnalyticsServiceStart();
const theme = themeServiceMock.createStartContract();
const startServices = {
notifications,
analytics,
i18n: i18nStart,
theme,
};
const userProfile = userProfileServiceMock.createStart();
const startServices = { notifications, analytics, i18n: i18nStart, theme, userProfile };
const addWarningToastSpy = jest.spyOn(notifications.toasts, 'addWarning');
beforeEach(() => {

View file

@ -16,6 +16,7 @@ import type { DocLinksStart } from '@kbn/core-doc-links-browser';
import type { AnalyticsServiceStart } from '@kbn/core-analytics-browser';
import type { I18nStart } from '@kbn/core-i18n-browser';
import type { ThemeServiceStart } from '@kbn/core-theme-browser';
import type { UserProfileService } from '@kbn/core-user-profile-browser';
import type { InternalHttpStart } from '@kbn/core-http-browser-internal';
import type { NotificationsStart } from '@kbn/core-notifications-browser';
import { mountReactNode } from '@kbn/core-mount-utils-browser-internal';
@ -35,6 +36,7 @@ interface Deps {
analytics: AnalyticsServiceStart;
i18n: I18nStart;
theme: ThemeServiceStart;
userProfile: UserProfileService;
}
export const setupPublicBaseUrlConfigWarning = ({

View file

@ -40,7 +40,9 @@
"@kbn/react-kibana-context-render",
"@kbn/core-analytics-browser-mocks",
"@kbn/core-i18n-browser-mocks",
"@kbn/core-theme-browser-mocks"
"@kbn/core-theme-browser-mocks",
"@kbn/core-user-profile-browser",
"@kbn/core-user-profile-browser-mocks"
],
"exclude": [
"target/**/*"

View file

@ -24,6 +24,7 @@ import { customBrandingServiceMock } from '@kbn/core-custom-branding-browser-moc
import { analyticsServiceMock } from '@kbn/core-analytics-browser-mocks';
import { i18nServiceMock } from '@kbn/core-i18n-browser-mocks';
import { themeServiceMock } from '@kbn/core-theme-browser-mocks';
import { userProfileServiceMock } from '@kbn/core-user-profile-browser-mocks';
import { getAppInfo } from '@kbn/core-application-browser-internal';
import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render';
import { findTestSubject } from '@kbn/test-jest-helpers';
@ -55,6 +56,7 @@ function defaultStartDeps(availableApps?: App[], currentAppId?: string) {
analytics: analyticsServiceMock.createAnalyticsServiceStart(),
i18n: i18nServiceMock.createStartContract(),
theme: themeServiceMock.createStartContract(),
userProfile: userProfileServiceMock.createStart(),
application: applicationServiceMock.createInternalStartContract(currentAppId),
docLinks: docLinksServiceMock.createStartContract(),
http: httpServiceMock.createStartContract(),

View file

@ -55,6 +55,7 @@
"@kbn/core-theme-browser-mocks",
"@kbn/react-kibana-context-render",
"@kbn/recently-accessed",
"@kbn/core-user-profile-browser-mocks",
],
"exclude": [
"target/**/*",

View file

@ -12,6 +12,7 @@ import { i18n } from '@kbn/i18n';
import { Subscription } from 'rxjs';
import type { AnalyticsServiceStart, AnalyticsServiceSetup } from '@kbn/core-analytics-browser';
import type { ThemeServiceStart } from '@kbn/core-theme-browser';
import type { UserProfileService } from '@kbn/core-user-profile-browser';
import type { I18nStart } from '@kbn/core-i18n-browser';
import type { IUiSettingsClient } from '@kbn/core-ui-settings-browser';
import type { OverlayStart } from '@kbn/core-overlays-browser';
@ -29,6 +30,7 @@ export interface StartDeps {
i18n: I18nStart;
overlays: OverlayStart;
theme: ThemeServiceStart;
userProfile: UserProfileService;
analytics: AnalyticsServiceStart;
targetDomElement: HTMLElement;
}
@ -62,36 +64,26 @@ export class NotificationsService {
return notificationSetup;
}
public start({
analytics,
i18n: i18nDep,
overlays,
theme,
targetDomElement,
}: StartDeps): NotificationsStart {
public start({ overlays, targetDomElement, ...startDeps }: StartDeps): NotificationsStart {
this.targetDomElement = targetDomElement;
const toastsContainer = document.createElement('div');
targetDomElement.appendChild(toastsContainer);
const eventReporter = new EventReporter({ analytics });
const eventReporter = new EventReporter({ analytics: startDeps.analytics });
return {
toasts: this.toasts.start({
eventReporter,
i18n: i18nDep,
overlays,
analytics,
theme,
targetDomElement: toastsContainer,
...startDeps,
}),
showErrorDialog: ({ title, error }) =>
showErrorDialog({
title,
error,
openModal: overlays.openModal,
analytics,
i18n: i18nDep,
theme,
...startDeps,
}),
};
}

View file

@ -29,6 +29,16 @@ exports[`renders matching snapshot 1`] = `
},
}
}
userProfile={
Object {
"bulkGet": [MockFunction],
"getCurrent": [MockFunction],
"getUserProfile$": [MockFunction],
"partialUpdate": [MockFunction],
"suggest": [MockFunction],
"update": [MockFunction],
}
}
>
<p
data-test-subj="errorToastMessage"

View file

@ -31,6 +31,16 @@ Array [
},
}
}
userProfile={
Object {
"bulkGet": [MockFunction],
"getCurrent": [MockFunction],
"getUserProfile$": [MockFunction],
"partialUpdate": [MockFunction],
"suggest": [MockFunction],
"update": [MockFunction],
}
}
>
<GlobalToastList
dismissToast={[Function]}

View file

@ -14,6 +14,7 @@ import { mountWithIntl } from '@kbn/test-jest-helpers';
import { analyticsServiceMock } from '@kbn/core-analytics-browser-mocks';
import { ErrorToast } from './error_toast';
import { themeServiceMock } from '@kbn/core-theme-browser-mocks';
import { userProfileServiceMock } from '@kbn/core-user-profile-browser-mocks';
import { i18nServiceMock } from '@kbn/core-i18n-browser-mocks';
interface ErrorToastProps {
@ -25,6 +26,7 @@ interface ErrorToastProps {
let openModal: jest.Mock;
const mockAnalytics = analyticsServiceMock.createAnalyticsServiceStart();
const mockTheme = themeServiceMock.createStartContract();
const mockUserProfile = userProfileServiceMock.createStart();
const mockI18n = i18nServiceMock.createStartContract();
beforeEach(() => (openModal = jest.fn()));
@ -39,6 +41,7 @@ function render(props: ErrorToastProps = {}) {
analytics={mockAnalytics}
i18n={mockI18n}
theme={mockTheme}
userProfile={mockUserProfile}
/>
);
}

View file

@ -25,11 +25,13 @@ import type { AnalyticsServiceStart } from '@kbn/core-analytics-browser';
import type { I18nStart } from '@kbn/core-i18n-browser';
import type { OverlayStart } from '@kbn/core-overlays-browser';
import { ThemeServiceStart } from '@kbn/core-theme-browser';
import type { UserProfileService } from '@kbn/core-user-profile-browser';
import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render';
interface StartServices {
analytics: AnalyticsServiceStart;
i18n: I18nStart;
userProfile: UserProfileService;
theme: ThemeServiceStart;
}
@ -62,7 +64,10 @@ export function showErrorDialog({
error,
openModal,
...startServices
}: Pick<ErrorToastProps, 'error' | 'title' | 'openModal' | 'analytics' | 'i18n' | 'theme'>) {
}: Pick<
ErrorToastProps,
'error' | 'title' | 'openModal' | 'analytics' | 'i18n' | 'userProfile' | 'theme'
>) {
let text = '';
if (isRequestError(error)) {

View file

@ -12,8 +12,10 @@ import { firstValueFrom } from 'rxjs';
import { ToastsApi } from './toasts_api';
import { uiSettingsServiceMock } from '@kbn/core-ui-settings-browser-mocks';
import { analyticsServiceMock } from '@kbn/core-analytics-browser-mocks';
import { i18nServiceMock } from '@kbn/core-i18n-browser-mocks';
import { themeServiceMock } from '@kbn/core-theme-browser-mocks';
import { userProfileServiceMock } from '@kbn/core-user-profile-browser-mocks';
async function getCurrentToasts(toasts: ToastsApi) {
return await firstValueFrom(toasts.get$());
@ -45,8 +47,10 @@ function toastDeps() {
function startDeps() {
return {
overlays: {} as any,
analytics: analyticsServiceMock.createAnalyticsServiceStart(),
i18n: i18nServiceMock.createStartContract(),
theme: themeServiceMock.createStartContract(),
userProfile: userProfileServiceMock.createStart(),
};
}

View file

@ -24,7 +24,8 @@ import type {
ToastInputFields,
ToastOptions,
} from '@kbn/core-notifications-browser';
import { ThemeServiceStart } from '@kbn/core-theme-browser';
import type { ThemeServiceStart } from '@kbn/core-theme-browser';
import type { UserProfileService } from '@kbn/core-user-profile-browser';
import { ErrorToast } from './error_toast';
const normalizeToast = (toastOrTitle: ToastInput): ToastInputFields => {
@ -36,6 +37,14 @@ const normalizeToast = (toastOrTitle: ToastInput): ToastInputFields => {
return omitBy(toastOrTitle, isUndefined);
};
interface StartDeps {
analytics: AnalyticsServiceStart;
overlays: OverlayStart;
i18n: I18nStart;
theme: ThemeServiceStart;
userProfile: UserProfileService;
}
/**
* Methods for adding and removing global toast messages.
* @public
@ -45,28 +54,15 @@ export class ToastsApi implements IToasts {
private idCounter = 0;
private uiSettings: IUiSettingsClient;
private overlays?: OverlayStart;
private analytics?: AnalyticsServiceStart;
private i18n?: I18nStart;
private theme?: ThemeServiceStart;
private startDeps?: StartDeps;
constructor(deps: { uiSettings: IUiSettingsClient }) {
this.uiSettings = deps.uiSettings;
}
/** @internal */
public start({
overlays,
i18n,
theme,
}: {
overlays: OverlayStart;
i18n: I18nStart;
theme: ThemeServiceStart;
}) {
this.overlays = overlays;
this.i18n = i18n;
this.theme = theme;
public start(startDeps: StartDeps) {
this.startDeps = startDeps;
}
/** Observable of the toast messages to show to the user. */
@ -190,9 +186,7 @@ export class ToastsApi implements IToasts {
error={error}
title={options.title}
toastMessage={message}
analytics={this.analytics!}
i18n={this.i18n!}
theme={this.theme!}
{...this.startDeps!}
/>
),
...options,
@ -202,12 +196,13 @@ export class ToastsApi implements IToasts {
private openModal(
...args: Parameters<OverlayStart['openModal']>
): ReturnType<OverlayStart['openModal']> {
if (!this.overlays) {
const { overlays } = this.startDeps ?? {};
if (!overlays) {
// This case should never happen because no rendering should be occurring
// before the ToastService is started.
throw new Error(`Modal opened before ToastService was started.`);
}
return this.overlays.openModal(...args);
return overlays.openModal(...args);
}
}

View file

@ -13,6 +13,7 @@ import { ToastsService } from './toasts_service';
import { ToastsApi } from './toasts_api';
import { overlayServiceMock } from '@kbn/core-overlays-browser-mocks';
import { themeServiceMock } from '@kbn/core-theme-browser-mocks';
import { userProfileServiceMock } from '@kbn/core-user-profile-browser-mocks';
import { uiSettingsServiceMock } from '@kbn/core-ui-settings-browser-mocks';
import { analyticsServiceMock } from '@kbn/core-analytics-browser-mocks';
import { EventReporter } from './telemetry';
@ -25,6 +26,7 @@ const mockI18n: any = {
const mockOverlays = overlayServiceMock.createStartContract();
const mockTheme = themeServiceMock.createStartContract();
const mockUserProfile = userProfileServiceMock.createStart();
const mockAnalytics = analyticsServiceMock.createAnalyticsServiceStart();
const eventReporter = new EventReporter({ analytics: mockAnalytics });
@ -51,6 +53,7 @@ describe('#start()', () => {
analytics: mockAnalytics,
i18n: mockI18n,
theme: mockTheme,
userProfile: mockUserProfile,
targetDomElement,
overlays: mockOverlays,
eventReporter,
@ -70,6 +73,7 @@ describe('#start()', () => {
analytics: mockAnalytics,
i18n: mockI18n,
theme: mockTheme,
userProfile: mockUserProfile,
targetDomElement,
overlays: mockOverlays,
eventReporter,
@ -89,6 +93,7 @@ describe('#stop()', () => {
analytics: mockAnalytics,
i18n: mockI18n,
theme: mockTheme,
userProfile: mockUserProfile,
targetDomElement,
overlays: mockOverlays,
eventReporter,
@ -115,6 +120,7 @@ describe('#stop()', () => {
analytics: mockAnalytics,
i18n: mockI18n,
theme: mockTheme,
userProfile: mockUserProfile,
targetDomElement,
overlays: mockOverlays,
eventReporter,

View file

@ -12,6 +12,7 @@ import { render, unmountComponentAtNode } from 'react-dom';
import type { AnalyticsServiceStart } from '@kbn/core-analytics-browser';
import type { ThemeServiceStart } from '@kbn/core-theme-browser';
import type { UserProfileService } from '@kbn/core-user-profile-browser';
import type { I18nStart } from '@kbn/core-i18n-browser';
import type { IUiSettingsClient } from '@kbn/core-ui-settings-browser';
import type { OverlayStart } from '@kbn/core-overlays-browser';
@ -29,6 +30,7 @@ interface StartDeps {
i18n: I18nStart;
overlays: OverlayStart;
theme: ThemeServiceStart;
userProfile: UserProfileService;
eventReporter: EventReporter;
targetDomElement: HTMLElement;
}
@ -42,12 +44,12 @@ export class ToastsService {
return this.api!;
}
public start({ eventReporter, analytics, i18n, overlays, theme, targetDomElement }: StartDeps) {
this.api!.start({ overlays, i18n, theme });
public start({ eventReporter, overlays, targetDomElement, ...startDeps }: StartDeps) {
this.api!.start({ overlays, ...startDeps });
this.targetDomElement = targetDomElement;
render(
<KibanaRenderContextProvider analytics={analytics} i18n={i18n} theme={theme}>
<KibanaRenderContextProvider {...startDeps}>
<GlobalToastList
dismissToast={(toastId: string) => this.api!.remove(toastId)}
toasts$={this.api!.get$()}

View file

@ -30,7 +30,9 @@
"@kbn/core-mount-utils-browser",
"@kbn/react-kibana-context-render",
"@kbn/core-analytics-browser",
"@kbn/core-analytics-browser-mocks"
"@kbn/core-analytics-browser-mocks",
"@kbn/core-user-profile-browser",
"@kbn/core-user-profile-browser-mocks"
],
"exclude": [
"target/**/*",

View file

@ -13,6 +13,7 @@ import { analyticsServiceMock } from '@kbn/core-analytics-browser-mocks';
import { i18nServiceMock } from '@kbn/core-i18n-browser-mocks';
import { themeServiceMock } from '@kbn/core-theme-browser-mocks';
import { uiSettingsServiceMock } from '@kbn/core-ui-settings-browser-mocks';
import { userProfileServiceMock } from '@kbn/core-user-profile-browser-mocks';
describe('OverlayBannersService', () => {
let service: InternalOverlayBannersStart;
@ -22,6 +23,7 @@ describe('OverlayBannersService', () => {
i18n: i18nServiceMock.createStartContract(),
theme: themeServiceMock.createStartContract(),
uiSettings: uiSettingsServiceMock.createStartContract(),
userProfile: userProfileServiceMock.createStart(),
});
});

View file

@ -14,6 +14,7 @@ import { map } from 'rxjs';
import type { AnalyticsServiceStart } from '@kbn/core-analytics-browser';
import type { I18nStart } from '@kbn/core-i18n-browser';
import type { ThemeServiceStart } from '@kbn/core-theme-browser';
import type { UserProfileService } from '@kbn/core-user-profile-browser';
import type { IUiSettingsClient } from '@kbn/core-ui-settings-browser';
import type { MountPoint } from '@kbn/core-mount-utils-browser';
import type { OverlayBannersStart } from '@kbn/core-overlays-browser';
@ -25,6 +26,7 @@ interface StartServices {
analytics: AnalyticsServiceStart;
i18n: I18nStart;
theme: ThemeServiceStart;
userProfile: UserProfileService;
}
interface StartDeps extends StartServices {

View file

@ -13,6 +13,7 @@ import { overlayBannersServiceMock } from './banners_service.test.mocks';
import { analyticsServiceMock } from '@kbn/core-analytics-browser-mocks';
import { i18nServiceMock } from '@kbn/core-i18n-browser-mocks';
import { themeServiceMock } from '@kbn/core-theme-browser-mocks';
import { userProfileServiceMock } from '@kbn/core-user-profile-browser-mocks';
import { Subject } from 'rxjs';
describe('OverlayBannersService', () => {
@ -20,6 +21,7 @@ describe('OverlayBannersService', () => {
let service: UserBannerService;
let uiSettings: ReturnType<typeof uiSettingsServiceMock.createStartContract>;
let banners: ReturnType<typeof overlayBannersServiceMock.createStartContract>;
let userProfile: ReturnType<typeof userProfileServiceMock.createStart>;
const startService = (content?: string) => {
bannerContent = content;
@ -40,6 +42,7 @@ describe('OverlayBannersService', () => {
i18n: i18nServiceMock.createStartContract(),
theme: themeServiceMock.createStartContract(),
uiSettings,
userProfile,
});
};

View file

@ -17,6 +17,7 @@ import { EuiCallOut, EuiButton, EuiLoadingSpinner } from '@elastic/eui';
import type { AnalyticsServiceStart } from '@kbn/core-analytics-browser';
import type { ThemeServiceStart } from '@kbn/core-theme-browser';
import type { UserProfileService } from '@kbn/core-user-profile-browser';
import type { I18nStart } from '@kbn/core-i18n-browser';
import type { IUiSettingsClient } from '@kbn/core-ui-settings-browser';
import type { OverlayBannersStart } from '@kbn/core-overlays-browser';
@ -26,6 +27,7 @@ interface StartServices {
analytics: AnalyticsServiceStart;
i18n: I18nStart;
theme: ThemeServiceStart;
userProfile: UserProfileService;
}
interface StartDeps extends StartServices {

View file

@ -39,6 +39,16 @@ Array [
},
}
}
userProfile={
Object {
"bulkGet": [MockFunction],
"getCurrent": [MockFunction],
"getUserProfile$": [MockFunction],
"partialUpdate": [MockFunction],
"suggest": [MockFunction],
"update": [MockFunction],
}
}
>
<EuiFlyout
onClose={[Function]}
@ -156,6 +166,16 @@ Array [
},
}
}
userProfile={
Object {
"bulkGet": [MockFunction],
"getCurrent": [MockFunction],
"getUserProfile$": [MockFunction],
"partialUpdate": [MockFunction],
"suggest": [MockFunction],
"update": [MockFunction],
}
}
>
<EuiFlyout
onClose={[Function]}
@ -266,6 +286,16 @@ Array [
},
}
}
userProfile={
Object {
"bulkGet": [MockFunction],
"getCurrent": [MockFunction],
"getUserProfile$": [MockFunction],
"partialUpdate": [MockFunction],
"suggest": [MockFunction],
"update": [MockFunction],
}
}
>
<EuiFlyout
onClose={[Function]}

View file

@ -13,6 +13,7 @@ import { mount } from 'enzyme';
import { analyticsServiceMock } from '@kbn/core-analytics-browser-mocks';
import { i18nServiceMock } from '@kbn/core-i18n-browser-mocks';
import { themeServiceMock } from '@kbn/core-theme-browser-mocks';
import { userProfileServiceMock } from '@kbn/core-user-profile-browser-mocks';
import { FlyoutService } from './flyout_service';
import type { OverlayRef } from '@kbn/core-mount-utils-browser';
import type { OverlayFlyoutStart } from '@kbn/core-overlays-browser';
@ -20,6 +21,7 @@ import type { OverlayFlyoutStart } from '@kbn/core-overlays-browser';
const analyticsMock = analyticsServiceMock.createAnalyticsServiceStart();
const i18nMock = i18nServiceMock.createStartContract();
const themeMock = themeServiceMock.createStartContract();
const userProfileMock = userProfileServiceMock.createStart();
beforeEach(() => {
mockReactDomRender.mockClear();
@ -39,6 +41,7 @@ const getServiceStart = () => {
analytics: analyticsMock,
i18n: i18nMock,
theme: themeMock,
userProfile: userProfileMock,
targetDomElement: document.createElement('div'),
});
};

View file

@ -15,6 +15,7 @@ import { render, unmountComponentAtNode } from 'react-dom';
import { Subject } from 'rxjs';
import type { AnalyticsServiceStart } from '@kbn/core-analytics-browser';
import type { ThemeServiceStart } from '@kbn/core-theme-browser';
import type { UserProfileService } from '@kbn/core-user-profile-browser';
import type { I18nStart } from '@kbn/core-i18n-browser';
import type { MountPoint, OverlayRef } from '@kbn/core-mount-utils-browser';
import { MountWrapper } from '@kbn/core-mount-utils-browser-internal';
@ -65,6 +66,7 @@ interface StartDeps {
analytics: AnalyticsServiceStart;
i18n: I18nStart;
theme: ThemeServiceStart;
userProfile: UserProfileService;
targetDomElement: Element;
}
@ -73,7 +75,13 @@ export class FlyoutService {
private activeFlyout: FlyoutRef | null = null;
private targetDomElement: Element | null = null;
public start({ analytics, i18n, theme, targetDomElement }: StartDeps): OverlayFlyoutStart {
public start({
analytics,
i18n,
theme,
userProfile,
targetDomElement,
}: StartDeps): OverlayFlyoutStart {
this.targetDomElement = targetDomElement;
return {
@ -121,7 +129,12 @@ export class FlyoutService {
};
render(
<KibanaRenderContextProvider analytics={analytics} i18n={i18n} theme={theme}>
<KibanaRenderContextProvider
analytics={analytics}
i18n={i18n}
theme={theme}
userProfile={userProfile}
>
{getWrapper(<MountWrapper mount={mount} className="kbnOverlayMountWrapper" />)}
</KibanaRenderContextProvider>,
this.targetDomElement

View file

@ -108,6 +108,16 @@ Array [
},
}
}
userProfile={
Object {
"bulkGet": [MockFunction],
"getCurrent": [MockFunction],
"getUserProfile$": [MockFunction],
"partialUpdate": [MockFunction],
"suggest": [MockFunction],
"update": [MockFunction],
}
}
>
<EuiConfirmModal
cancelButtonText="Cancel"
@ -376,6 +386,16 @@ Array [
},
}
}
userProfile={
Object {
"bulkGet": [MockFunction],
"getCurrent": [MockFunction],
"getUserProfile$": [MockFunction],
"partialUpdate": [MockFunction],
"suggest": [MockFunction],
"update": [MockFunction],
}
}
>
<EuiConfirmModal
cancelButtonText="Cancel"
@ -701,6 +721,16 @@ Array [
},
}
}
userProfile={
Object {
"bulkGet": [MockFunction],
"getCurrent": [MockFunction],
"getUserProfile$": [MockFunction],
"partialUpdate": [MockFunction],
"suggest": [MockFunction],
"update": [MockFunction],
}
}
>
<EuiConfirmModal
cancelButtonText="Cancel"
@ -945,6 +975,16 @@ Array [
},
}
}
userProfile={
Object {
"bulkGet": [MockFunction],
"getCurrent": [MockFunction],
"getUserProfile$": [MockFunction],
"partialUpdate": [MockFunction],
"suggest": [MockFunction],
"update": [MockFunction],
}
}
>
<EuiConfirmModal
cancelButtonText="Cancel"
@ -1194,6 +1234,16 @@ Array [
},
}
}
userProfile={
Object {
"bulkGet": [MockFunction],
"getCurrent": [MockFunction],
"getUserProfile$": [MockFunction],
"partialUpdate": [MockFunction],
"suggest": [MockFunction],
"update": [MockFunction],
}
}
>
<EuiModal
onClose={[Function]}
@ -1438,6 +1488,16 @@ Array [
},
}
}
userProfile={
Object {
"bulkGet": [MockFunction],
"getCurrent": [MockFunction],
"getUserProfile$": [MockFunction],
"partialUpdate": [MockFunction],
"suggest": [MockFunction],
"update": [MockFunction],
}
}
>
<EuiConfirmModal
cancelButtonText="Cancel"
@ -1484,6 +1544,16 @@ Array [
},
}
}
userProfile={
Object {
"bulkGet": [MockFunction],
"getCurrent": [MockFunction],
"getUserProfile$": [MockFunction],
"partialUpdate": [MockFunction],
"suggest": [MockFunction],
"update": [MockFunction],
}
}
>
<EuiModal
onClose={[Function]}
@ -1632,6 +1702,16 @@ Array [
},
}
}
userProfile={
Object {
"bulkGet": [MockFunction],
"getCurrent": [MockFunction],
"getUserProfile$": [MockFunction],
"partialUpdate": [MockFunction],
"suggest": [MockFunction],
"update": [MockFunction],
}
}
>
<EuiConfirmModal
cancelButtonText="Cancel"
@ -1742,6 +1822,16 @@ Array [
},
}
}
userProfile={
Object {
"bulkGet": [MockFunction],
"getCurrent": [MockFunction],
"getUserProfile$": [MockFunction],
"partialUpdate": [MockFunction],
"suggest": [MockFunction],
"update": [MockFunction],
}
}
>
<EuiConfirmModal
cancelButtonText="Cancel"
@ -1857,6 +1947,16 @@ Array [
},
}
}
userProfile={
Object {
"bulkGet": [MockFunction],
"getCurrent": [MockFunction],
"getUserProfile$": [MockFunction],
"partialUpdate": [MockFunction],
"suggest": [MockFunction],
"update": [MockFunction],
}
}
>
<EuiModal
onClose={[Function]}
@ -1967,6 +2067,16 @@ Array [
},
}
}
userProfile={
Object {
"bulkGet": [MockFunction],
"getCurrent": [MockFunction],
"getUserProfile$": [MockFunction],
"partialUpdate": [MockFunction],
"suggest": [MockFunction],
"update": [MockFunction],
}
}
>
<EuiModal
onClose={[Function]}

View file

@ -14,6 +14,7 @@ import { mount } from 'enzyme';
import { analyticsServiceMock } from '@kbn/core-analytics-browser-mocks';
import { i18nServiceMock } from '@kbn/core-i18n-browser-mocks';
import { themeServiceMock } from '@kbn/core-theme-browser-mocks';
import { userProfileServiceMock } from '@kbn/core-user-profile-browser-mocks';
import { ModalService } from './modal_service';
import type { OverlayModalStart } from '@kbn/core-overlays-browser';
import { mountReactNode } from '@kbn/core-mount-utils-browser-internal';
@ -22,6 +23,7 @@ import type { OverlayRef } from '@kbn/core-mount-utils-browser';
const analyticsMock = analyticsServiceMock.createAnalyticsServiceStart();
const i18nMock = i18nServiceMock.createStartContract();
const themeMock = themeServiceMock.createStartContract();
const userProfileMock = userProfileServiceMock.createStart();
beforeEach(() => {
mockReactDomRender.mockClear();
@ -34,6 +36,7 @@ const getServiceStart = () => {
analytics: analyticsMock,
i18n: i18nMock,
theme: themeMock,
userProfile: userProfileMock,
targetDomElement: document.createElement('div'),
});
};

View file

@ -16,6 +16,7 @@ import { render, unmountComponentAtNode } from 'react-dom';
import { Subject } from 'rxjs';
import type { AnalyticsServiceStart } from '@kbn/core-analytics-browser';
import type { ThemeServiceStart } from '@kbn/core-theme-browser';
import type { UserProfileService } from '@kbn/core-user-profile-browser';
import type { I18nStart } from '@kbn/core-i18n-browser';
import type { MountPoint, OverlayRef } from '@kbn/core-mount-utils-browser';
import { MountWrapper } from '@kbn/core-mount-utils-browser-internal';
@ -58,6 +59,7 @@ class ModalRef implements OverlayRef {
interface StartDeps {
i18n: I18nStart;
theme: ThemeServiceStart;
userProfile: UserProfileService;
analytics: AnalyticsServiceStart;
targetDomElement: Element;
}
@ -67,7 +69,7 @@ export class ModalService {
private activeModal: ModalRef | null = null;
private targetDomElement: Element | null = null;
public start({ analytics, i18n, theme, targetDomElement }: StartDeps): OverlayModalStart {
public start({ targetDomElement, ...startDeps }: StartDeps): OverlayModalStart {
this.targetDomElement = targetDomElement;
return {
@ -90,7 +92,7 @@ export class ModalService {
this.activeModal = modal;
render(
<KibanaRenderContextProvider analytics={analytics} i18n={i18n} theme={theme}>
<KibanaRenderContextProvider {...startDeps}>
<EuiModal {...options} onClose={() => modal.close()}>
<MountWrapper mount={mount} className="kbnOverlayMountWrapper" />
</EuiModal>
@ -150,7 +152,7 @@ export class ModalService {
};
render(
<KibanaRenderContextProvider analytics={analytics} i18n={i18n} theme={theme}>
<KibanaRenderContextProvider {...startDeps}>
<EuiConfirmModal {...props} />
</KibanaRenderContextProvider>,
targetDomElement

View file

@ -9,6 +9,7 @@
import type { AnalyticsServiceStart } from '@kbn/core-analytics-browser';
import type { ThemeServiceStart } from '@kbn/core-theme-browser';
import type { UserProfileService } from '@kbn/core-user-profile-browser';
import type { I18nStart } from '@kbn/core-i18n-browser';
import type { IUiSettingsClient } from '@kbn/core-ui-settings-browser';
import type { OverlayStart } from '@kbn/core-overlays-browser';
@ -17,10 +18,11 @@ import { FlyoutService } from './flyout';
import { ModalService } from './modal';
interface StartDeps {
targetDomElement: HTMLElement;
analytics: AnalyticsServiceStart;
i18n: I18nStart;
theme: ThemeServiceStart;
targetDomElement: HTMLElement;
userProfile: UserProfileService;
uiSettings: IUiSettingsClient;
}
@ -30,25 +32,21 @@ export class OverlayService {
private modalService = new ModalService();
private flyoutService = new FlyoutService();
public start({ analytics, i18n, targetDomElement, uiSettings, theme }: StartDeps): OverlayStart {
public start({ targetDomElement, ...startDeps }: StartDeps): OverlayStart {
const flyoutElement = document.createElement('div');
targetDomElement.appendChild(flyoutElement);
const flyouts = this.flyoutService.start({
analytics,
i18n,
theme,
targetDomElement: flyoutElement,
...startDeps,
});
const banners = this.bannersService.start({ uiSettings, analytics, i18n, theme });
const banners = this.bannersService.start(startDeps);
const modalElement = document.createElement('div');
targetDomElement.appendChild(modalElement);
const modals = this.modalService.start({
analytics,
i18n,
theme,
targetDomElement: modalElement,
...startDeps,
});
return {

View file

@ -27,6 +27,8 @@
"@kbn/react-kibana-context-render",
"@kbn/core-analytics-browser-mocks",
"@kbn/core-analytics-browser",
"@kbn/core-user-profile-browser-mocks",
"@kbn/core-user-profile-browser",
],
"exclude": [
"target/**/*",

View file

@ -15,6 +15,7 @@ import { analyticsServiceMock } from '@kbn/core-analytics-browser-mocks';
import { applicationServiceMock } from '@kbn/core-application-browser-mocks';
import { chromeServiceMock } from '@kbn/core-chrome-browser-mocks';
import { overlayServiceMock } from '@kbn/core-overlays-browser-mocks';
import { userProfileServiceMock } from '@kbn/core-user-profile-browser-mocks';
import { themeServiceMock } from '@kbn/core-theme-browser-mocks';
import { i18nServiceMock } from '@kbn/core-i18n-browser-mocks';
import { RenderingService } from './rendering_service';
@ -26,6 +27,7 @@ describe('RenderingService#start', () => {
let overlays: ReturnType<typeof overlayServiceMock.createStartContract>;
let i18n: ReturnType<typeof i18nServiceMock.createStartContract>;
let theme: ReturnType<typeof themeServiceMock.createStartContract>;
let userProfile: ReturnType<typeof userProfileServiceMock.createStart>;
let targetDomElement: HTMLDivElement;
let rendering: RenderingService;
@ -41,8 +43,8 @@ describe('RenderingService#start', () => {
overlays = overlayServiceMock.createStartContract();
overlays.banners.getComponent.mockReturnValue(<div>I&apos;m a banner!</div>);
userProfile = userProfileServiceMock.createStart();
theme = themeServiceMock.createStartContract();
i18n = i18nServiceMock.createStartContract();
targetDomElement = document.createElement('div');
@ -58,6 +60,7 @@ describe('RenderingService#start', () => {
overlays,
i18n,
theme,
userProfile,
targetDomElement,
});
};

View file

@ -17,6 +17,7 @@ import type { InternalChromeStart } from '@kbn/core-chrome-browser-internal';
import type { I18nStart } from '@kbn/core-i18n-browser';
import type { OverlayStart } from '@kbn/core-overlays-browser';
import type { ThemeServiceStart } from '@kbn/core-theme-browser';
import type { UserProfileService } from '@kbn/core-user-profile-browser';
import { KibanaRootContextProvider } from '@kbn/react-kibana-context-root';
import { APP_FIXED_VIEWPORT_ID } from '@kbn/core-rendering-browser';
import { AppWrapper } from './app_containers';
@ -25,6 +26,7 @@ interface StartServices {
analytics: AnalyticsServiceStart;
i18n: I18nStart;
theme: ThemeServiceStart;
userProfile: UserProfileService;
}
export interface StartDeps extends StartServices {

View file

@ -27,7 +27,9 @@
"@kbn/core-analytics-browser",
"@kbn/core-i18n-browser",
"@kbn/core-theme-browser",
"@kbn/core-rendering-browser"
"@kbn/core-rendering-browser",
"@kbn/core-user-profile-browser",
"@kbn/core-user-profile-browser-mocks"
],
"exclude": [
"target/**/*",

View file

@ -469,6 +469,7 @@ describe('#start()', () => {
i18n: expect.any(Object),
overlays: expect.any(Object),
theme: expect.any(Object),
userProfile: expect.any(Object),
targetDomElement: expect.any(HTMLElement),
analytics: expect.any(Object),
});
@ -494,6 +495,7 @@ describe('#start()', () => {
overlays: expect.any(Object),
i18n: expect.any(Object),
theme: expect.any(Object),
userProfile: expect.any(Object),
targetDomElement: expect.any(HTMLElement),
});
});

View file

@ -319,6 +319,7 @@ export class CoreSystem {
analytics,
theme,
uiSettings,
userProfile,
targetDomElement: overlayTargetDomElement,
});
const notifications = this.notifications.start({
@ -326,6 +327,7 @@ export class CoreSystem {
i18n,
overlays,
theme,
userProfile,
targetDomElement: notificationsTargetDomElement,
});
const customBranding = this.customBranding.start();
@ -360,6 +362,7 @@ export class CoreSystem {
analytics,
i18n,
theme,
userProfile,
});
const featureFlags = await this.featureFlags.start();
@ -404,6 +407,7 @@ export class CoreSystem {
overlays,
theme,
targetDomElement: coreUiTargetDomElement,
userProfile,
});
performance.mark(KBN_LOAD_MARKS, {

View file

@ -14,14 +14,20 @@ import { of, BehaviorSubject } from 'rxjs';
import { useEuiTheme } from '@elastic/eui';
import type { UseEuiTheme } from '@elastic/eui';
import { mountWithIntl } from '@kbn/test-jest-helpers';
import type { UserProfileService } from '@kbn/core-user-profile-browser';
import { userProfileServiceMock } from '@kbn/core-user-profile-browser-mocks';
import type { CoreTheme } from '@kbn/core-theme-browser';
import { CoreThemeProvider } from './core_theme_provider';
describe('CoreThemeProvider', () => {
let euiTheme: UseEuiTheme | undefined;
let userProfile: UserProfileService;
beforeEach(() => {
euiTheme = undefined;
userProfile = userProfileServiceMock.createStart();
});
const flushPromises = async () => {
@ -53,7 +59,7 @@ describe('CoreThemeProvider', () => {
const coreTheme: CoreTheme = { darkMode: true, name: 'amsterdam' };
const wrapper = mountWithIntl(
<CoreThemeProvider theme$={of(coreTheme)}>
<CoreThemeProvider theme$={of(coreTheme)} userProfile={userProfile}>
<InnerComponent />
</CoreThemeProvider>
);
@ -67,7 +73,7 @@ describe('CoreThemeProvider', () => {
const coreTheme$ = new BehaviorSubject<CoreTheme>({ darkMode: true, name: 'amsterdam' });
const wrapper = mountWithIntl(
<CoreThemeProvider theme$={coreTheme$}>
<CoreThemeProvider theme$={coreTheme$} userProfile={userProfile}>
<InnerComponent />
</CoreThemeProvider>
);

View file

@ -10,10 +10,12 @@
import React, { type FC, type PropsWithChildren } from 'react';
import { CoreTheme } from '@kbn/core-theme-browser/src/types';
import { KibanaThemeProvider } from '@kbn/react-kibana-context-theme';
import type { UserProfileService } from '@kbn/core-user-profile-browser';
import { Observable } from 'rxjs';
interface CoreThemeProviderProps {
theme$: Observable<CoreTheme>;
userProfile: UserProfileService;
globalStyles?: boolean;
}
@ -24,8 +26,11 @@ interface CoreThemeProviderProps {
*/
export const CoreThemeProvider: FC<PropsWithChildren<CoreThemeProviderProps>> = ({
theme$,
userProfile,
globalStyles,
children,
}) => (
<KibanaThemeProvider {...{ theme: { theme$ }, globalStyles }}>{children}</KibanaThemeProvider>
<KibanaThemeProvider {...{ theme: { theme$ }, userProfile, globalStyles }}>
{children}
</KibanaThemeProvider>
);

View file

@ -20,6 +20,8 @@
"@kbn/react-kibana-context-theme",
"@kbn/core-injected-metadata-common-internal",
"@kbn/ui-theme",
"@kbn/core-user-profile-browser",
"@kbn/core-user-profile-browser-mocks",
],
"exclude": [
"target/**/*",

View file

@ -7,6 +7,7 @@
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import { of } from 'rxjs';
import type {
UserProfileServiceSetup,
UserProfileServiceStart,
@ -26,7 +27,7 @@ const createSetupMock = () => {
const createStartMock = () => {
const mock: jest.Mocked<UserProfileServiceStart> = {
getUserProfile$: jest.fn(),
getUserProfile$: jest.fn().mockReturnValue(of(null)),
getCurrent: jest.fn(),
bulkGet: jest.fn(),
suggest: jest.fn(),
@ -47,7 +48,7 @@ const createInternalSetupMock = () => {
const createInternalStartMock = () => {
const mock: jest.Mocked<InternalUserProfileServiceStart> = {
getUserProfile$: jest.fn(),
getUserProfile$: jest.fn().mockReturnValue(of(null)),
getCurrent: jest.fn(),
bulkGet: jest.fn(),
suggest: jest.fn(),

View file

@ -15,7 +15,6 @@ import {
CoreStart,
I18nStart,
NotificationsSetup,
ThemeServiceSetup,
} from '@kbn/core/public';
import { DataPublicPluginStart, SerializedSearchSourceFields } from '@kbn/data-plugin/public';
import {
@ -59,6 +58,7 @@ type StartServices = [
| 'analytics'
| 'i18n'
| 'theme'
| 'userProfile'
// used extensively in Reporting share panel action
| 'application'
| 'uiSettings'
@ -101,14 +101,12 @@ export class ReportingCsvPanelAction implements ActionDefinition<EmbeddableApiCo
private readonly i18nStrings: ReturnType<typeof getI18nStrings>;
private readonly notifications: NotificationsSetup;
private readonly apiClient: ReportingAPIClient;
private readonly theme: ThemeServiceSetup;
private readonly startServices$: Params['startServices$'];
private readonly startServices$: Observable<StartServices>;
constructor({ core, apiClient, startServices$ }: Params) {
this.isDownloading = false;
this.apiClient = apiClient;
this.notifications = core.notifications;
this.theme = core.theme;
this.startServices$ = startServices$;
this.i18nStrings = getI18nStrings(apiClient);
}
@ -148,7 +146,8 @@ export class ReportingCsvPanelAction implements ActionDefinition<EmbeddableApiCo
};
private executeGenerate = async (params: ExecutionParams) => {
const { searchSource, columns, title, analytics, i18nStart } = params;
const [startServices] = await firstValueFrom(this.startServices$);
const { searchSource, columns, title } = params;
const csvJobParams = this.apiClient.getDecoratedJobParams<JobAppParamsCSV>({
searchSource,
columns,
@ -162,11 +161,7 @@ export class ReportingCsvPanelAction implements ActionDefinition<EmbeddableApiCo
if (job) {
this.notifications.toasts.addSuccess({
title: this.i18nStrings.generate.toasts.success.title,
text: toMountPoint(this.i18nStrings.generate.toasts.success.body, {
analytics,
i18n: i18nStart,
theme: this.theme,
}),
text: toMountPoint(this.i18nStrings.generate.toasts.success.body, startServices),
'data-test-subj': 'csvReportStarted',
});
}

View file

@ -21,6 +21,7 @@ export type StartServices = [
| 'analytics'
| 'i18n'
| 'theme'
| 'userProfile'
// used extensively in Reporting share context menus and modal
| 'notifications'
>,

View file

@ -7,7 +7,7 @@
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import { Subject } from 'rxjs';
import { of, Subject } from 'rxjs';
import React, { useEffect } from 'react';
import { action } from '@storybook/addon-actions';
import type { DecoratorFn } from '@storybook/react';
@ -22,6 +22,7 @@ import { KibanaRootContextProvider } from '@kbn/react-kibana-context-root';
import { i18n } from '@kbn/i18n';
const theme$ = new BehaviorSubject<CoreTheme>({ darkMode: false, name: 'amsterdam' });
const userProfile = { getUserProfile$: () => of(null) };
const i18nStart: I18nStart = {
Context: ({ children }) => <I18nProvider>{children}</I18nProvider>,
@ -47,7 +48,7 @@ const KibanaContextDecorator: DecoratorFn = (storyFn, { globals }) => {
}, [colorMode]);
return (
<KibanaRootContextProvider {...{ theme: { theme$ }, analytics, i18n: i18nStart }}>
<KibanaRootContextProvider {...{ theme: { theme$ }, userProfile, analytics, i18n: i18nStart }}>
{storyFn()}
</KibanaRootContextProvider>
);

View file

@ -7,24 +7,28 @@
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import { useEuiTheme } from '@elastic/eui';
import type { ReactWrapper } from 'enzyme';
import type { FC } from 'react';
import React, { useEffect } from 'react';
import { act } from 'react-dom/test-utils';
import { BehaviorSubject, of } from 'rxjs';
import { useEuiTheme } from '@elastic/eui';
import type { UserProfileService } from '@kbn/core-user-profile-browser';
import { userProfileServiceMock } from '@kbn/core-user-profile-browser-mocks';
import type { KibanaTheme } from '@kbn/react-kibana-context-common';
import { mountWithIntl } from '@kbn/test-jest-helpers';
import type { KibanaTheme } from '@kbn/react-kibana-context-common';
import { KibanaEuiProvider } from './eui_provider';
describe('KibanaEuiProvider', () => {
let euiTheme: ReturnType<typeof useEuiTheme> | undefined;
let userProfile: UserProfileService;
let consoleWarnMock: jest.SpyInstance;
beforeEach(() => {
euiTheme = undefined;
userProfile = userProfileServiceMock.createStart();
consoleWarnMock = jest.spyOn(global.console, 'warn').mockImplementation(() => {});
});
@ -57,7 +61,11 @@ describe('KibanaEuiProvider', () => {
const coreTheme: KibanaTheme = { darkMode: true, name: 'amsterdam' };
const wrapper = mountWithIntl(
<KibanaEuiProvider theme={{ theme$: of(coreTheme) }} modify={{ breakpoint: { xxl: 1600 } }}>
<KibanaEuiProvider
theme={{ theme$: of(coreTheme) }}
modify={{ breakpoint: { xxl: 1600 } }}
userProfile={userProfile}
>
<InnerComponent />
</KibanaEuiProvider>
);
@ -73,7 +81,7 @@ describe('KibanaEuiProvider', () => {
const coreTheme$ = new BehaviorSubject<KibanaTheme>({ darkMode: true, name: 'amsterdam' });
const wrapper = mountWithIntl(
<KibanaEuiProvider theme={{ theme$: coreTheme$ }}>
<KibanaEuiProvider theme={{ theme$: coreTheme$ }} userProfile={userProfile}>
<InnerComponent />
</KibanaEuiProvider>
);

View file

@ -19,13 +19,15 @@ import {
getThemeConfigByName,
DEFAULT_THEME_CONFIG,
} from '@kbn/react-kibana-context-common';
import { ThemeServiceStart } from '@kbn/react-kibana-context-common';
import type { UserProfileService } from '@kbn/core-user-profile-browser';
import type { ThemeServiceStart } from '@kbn/react-kibana-context-common';
/**
* Props for the KibanaEuiProvider.
*/
export interface KibanaEuiProviderProps extends Pick<EuiProviderProps<{}>, 'modify' | 'colorMode'> {
theme: ThemeServiceStart;
userProfile?: Pick<UserProfileService, 'getUserProfile$'>; // TODO: use this to access a "high contrast mode" flag from user settings. Pass the flag to EuiProvider, when it is supported in EUI.
globalStyles?: boolean;
}
@ -87,7 +89,14 @@ export const KibanaEuiProvider: FC<PropsWithChildren<KibanaEuiProviderProps>> =
return (
<EuiProvider
{...{ cache, modify, colorMode, globalStyles, utilityClasses: globalStyles, theme }}
{...{
cache,
modify,
colorMode,
globalStyles,
utilityClasses: globalStyles,
theme,
}}
>
{children}
</EuiProvider>

View file

@ -18,18 +18,22 @@ import type { KibanaTheme } from '@kbn/react-kibana-context-common';
import type { AnalyticsServiceStart } from '@kbn/core-analytics-browser';
import { analyticsServiceMock } from '@kbn/core-analytics-browser-mocks';
import { i18nServiceMock } from '@kbn/core-i18n-browser-mocks';
import { KibanaRootContextProvider } from './root_provider';
import { I18nStart } from '@kbn/core-i18n-browser';
import type { UserProfileService } from '@kbn/core-user-profile-browser';
import { userProfileServiceMock } from '@kbn/core-user-profile-browser-mocks';
import { KibanaRootContextProvider } from './root_provider';
describe('KibanaRootContextProvider', () => {
let euiTheme: UseEuiTheme | undefined;
let i18nMock: I18nStart;
let analytics: AnalyticsServiceStart;
let userProfile: UserProfileService;
beforeEach(() => {
euiTheme = undefined;
analytics = analyticsServiceMock.createAnalyticsServiceStart();
i18nMock = i18nServiceMock.createStartContract();
userProfile = userProfileServiceMock.createStart();
});
const flushPromises = async () => {
@ -64,6 +68,7 @@ describe('KibanaRootContextProvider', () => {
<KibanaRootContextProvider
analytics={analytics}
i18n={i18nMock}
userProfile={userProfile}
theme={{ theme$: of(coreTheme) }}
>
<InnerComponent />
@ -82,6 +87,7 @@ describe('KibanaRootContextProvider', () => {
<KibanaRootContextProvider
analytics={analytics}
i18n={i18nMock}
userProfile={userProfile}
theme={{ theme$: coreTheme$ }}
>
<InnerComponent />

View file

@ -23,5 +23,7 @@
"@kbn/core-base-common",
"@kbn/core-analytics-browser",
"@kbn/core-analytics-browser-mocks",
"@kbn/core-user-profile-browser",
"@kbn/core-user-profile-browser-mocks",
]
}

View file

@ -16,14 +16,19 @@ import { BehaviorSubject } from 'rxjs';
import { mountWithIntl } from '@kbn/test-jest-helpers';
import type { UserProfileService } from '@kbn/core-user-profile-browser';
import { userProfileServiceMock } from '@kbn/core-user-profile-browser-mocks';
import type { KibanaTheme } from '@kbn/react-kibana-context-common';
import { KibanaThemeProvider } from './theme_provider';
describe('KibanaThemeProvider', () => {
let euiTheme: ReturnType<typeof useEuiTheme> | undefined;
let userProfile: UserProfileService;
beforeEach(() => {
euiTheme = undefined;
userProfile = userProfileServiceMock.createStart();
});
const flushPromises = async () => {
@ -55,7 +60,7 @@ describe('KibanaThemeProvider', () => {
const coreTheme$ = new BehaviorSubject<KibanaTheme>({ darkMode: true, name: 'amsterdam' });
const wrapper = mountWithIntl(
<KibanaThemeProvider theme={{ theme$: coreTheme$ }}>
<KibanaThemeProvider theme={{ theme$: coreTheme$ }} userProfile={userProfile}>
<InnerComponent />
</KibanaThemeProvider>
);
@ -72,7 +77,7 @@ describe('KibanaThemeProvider', () => {
});
const wrapper = mountWithIntl(
<KibanaThemeProvider theme={{ theme$: coreTheme$ }}>
<KibanaThemeProvider theme={{ theme$: coreTheme$ }} userProfile={userProfile}>
<InnerComponent />
</KibanaThemeProvider>
);

View file

@ -17,6 +17,7 @@ import { useIsNestedEuiProvider } from '@elastic/eui/lib/components/provider/nes
// @ts-expect-error EUI exports this component internally, but Kibana isn't picking it up its types
import { emitEuiProviderWarning } from '@elastic/eui/lib/services/theme/warning';
import type { UserProfileService } from '@kbn/core-user-profile-browser';
import { KibanaEuiProvider } from '@kbn/react-kibana-context-root';
import {
@ -40,6 +41,8 @@ interface EuiProps<T = {}> extends Omit<EuiThemeProviderProps<T>, 'theme' | 'col
export interface KibanaThemeProviderProps extends EuiProps {
/** The `ThemeServiceStart` API. */
theme: ThemeServiceStart;
/** The `UserProfileService` start API. */
userProfile?: Pick<UserProfileService, 'getUserProfile$'>;
}
/**
@ -70,12 +73,17 @@ const KibanaThemeProviderOnly = ({
* TODO: clintandrewhall - We can remove this and revert to only exporting the above component
* once all out-of-band renders are using `KibanaRenderContextProvider`.
*/
const KibanaThemeProviderCheck = ({ theme, children, ...props }: KibanaThemeProviderProps) => {
const KibanaThemeProviderCheck = ({
theme,
userProfile,
children,
...props
}: KibanaThemeProviderProps) => {
const hasEuiProvider = useIsNestedEuiProvider();
if (hasEuiProvider) {
return (
<KibanaThemeProviderOnly theme={theme} {...props}>
<KibanaThemeProviderOnly theme={theme} userProfile={userProfile} {...props}>
{children}
</KibanaThemeProviderOnly>
);
@ -84,7 +92,7 @@ const KibanaThemeProviderCheck = ({ theme, children, ...props }: KibanaThemeProv
'KibanaThemeProvider requires a parent KibanaRenderContextProvider. Check your React tree and ensure that they are wrapped in a KibanaRenderContextProvider.'
);
return (
<KibanaEuiProvider theme={theme} globalStyles={false}>
<KibanaEuiProvider theme={theme} userProfile={userProfile} globalStyles={false}>
{children}
</KibanaEuiProvider>
);

View file

@ -19,5 +19,7 @@
"@kbn/test-jest-helpers",
"@kbn/react-kibana-context-common",
"@kbn/react-kibana-context-root",
"@kbn/core-user-profile-browser",
"@kbn/core-user-profile-browser-mocks",
]
}

View file

@ -8,6 +8,7 @@
*/
import { ThemeServiceStart } from '@kbn/react-kibana-context-common';
import type { UserProfileService } from '@kbn/core-user-profile-browser';
import React from 'react';
import { KibanaThemeProvider } from './theme_provider';
@ -17,6 +18,8 @@ import { KibanaThemeProvider } from './theme_provider';
* @param node The node to wrap.
* @param theme The `ThemeServiceStart` API.
*/
export const wrapWithTheme = (node: React.ReactNode, theme: ThemeServiceStart) => (
<KibanaThemeProvider {...{ theme }}>{node}</KibanaThemeProvider>
);
export const wrapWithTheme = (
node: React.ReactNode,
theme: ThemeServiceStart,
userProfile?: UserProfileService
) => <KibanaThemeProvider {...{ theme, userProfile }}>{node}</KibanaThemeProvider>;

View file

@ -15,12 +15,14 @@ import type { UseEuiTheme } from '@elastic/eui';
import type { CoreTheme } from '@kbn/core/public';
import { toMountPoint } from './to_mount_point';
import { analyticsServiceMock } from '@kbn/core-analytics-browser-mocks';
import { userProfileServiceMock } from '@kbn/core-user-profile-browser-mocks';
import { i18nServiceMock } from '@kbn/core-i18n-browser-mocks';
describe('toMountPoint', () => {
let euiTheme: UseEuiTheme;
const i18n = i18nServiceMock.createStartContract();
const analytics = analyticsServiceMock.createAnalyticsServiceStart();
const userProfile = userProfileServiceMock.createStart();
const InnerComponent: FC = () => {
const theme = useEuiTheme();
@ -42,7 +44,7 @@ describe('toMountPoint', () => {
it('exposes the euiTheme when `theme$` is provided', async () => {
const theme = { theme$: of<CoreTheme>({ darkMode: true, name: 'amsterdam' }) };
const mount = toMountPoint(<InnerComponent />, { theme, i18n, analytics });
const mount = toMountPoint(<InnerComponent />, { theme, i18n, analytics, userProfile });
const targetEl = document.createElement('div');
mount(targetEl);
@ -55,7 +57,12 @@ describe('toMountPoint', () => {
it('propagates changes of the theme$ observable', async () => {
const theme$ = new BehaviorSubject<CoreTheme>({ darkMode: true, name: 'amsterdam' });
const mount = toMountPoint(<InnerComponent />, { theme: { theme$ }, i18n, analytics });
const mount = toMountPoint(<InnerComponent />, {
theme: { theme$ },
i18n,
analytics,
userProfile,
});
const targetEl = document.createElement('div');
mount(targetEl);

View file

@ -17,7 +17,7 @@ import {
export type ToMountPointParams = Pick<
KibanaRenderContextProviderProps,
'analytics' | 'i18n' | 'theme'
'analytics' | 'i18n' | 'theme' | 'userProfile'
>;
/**

View file

@ -21,5 +21,6 @@
"@kbn/core-i18n-browser-mocks",
"@kbn/react-kibana-context-render",
"@kbn/core-analytics-browser-mocks",
"@kbn/core-user-profile-browser-mocks",
]
}

View file

@ -44,7 +44,7 @@ export const renderApp = async (
);
render(
<KibanaRenderContextProvider i18n={coreStart.i18n} theme={coreStart.theme}>
<KibanaRenderContextProvider {...coreStart}>
<RedirectAppLinks
coreStart={{
application: coreStart.application,

View file

@ -13,19 +13,32 @@ import {
KibanaThemeProviderProps as KbnThemeProviderProps,
wrapWithTheme as kbnWrapWithTheme,
} from '@kbn/react-kibana-context-theme';
import type { UserProfileService } from '@kbn/core-user-profile-browser';
/** @deprecated Use `KibanaThemeProviderProps` from `@kbn/react-kibana-context-theme` */
export type KibanaThemeProviderProps = Pick<KbnThemeProviderProps, 'children' | 'modify'> &
export type KibanaThemeProviderProps = Pick<
KbnThemeProviderProps,
'children' | 'modify' | 'userProfile'
> &
KbnThemeProviderProps['theme'];
/** @deprecated Use `KibanaThemeProvider` from `@kbn/react-kibana-context-theme` */
export const KibanaThemeProvider = ({ children, theme$, modify }: KibanaThemeProviderProps) => (
<KbnThemeProvider theme={{ theme$ }} {...modify}>
export const KibanaThemeProvider = ({
children,
theme$,
userProfile,
modify,
}: KibanaThemeProviderProps) => (
<KbnThemeProvider theme={{ theme$ }} {...modify} userProfile={userProfile}>
{children}
</KbnThemeProvider>
);
type Theme = KbnThemeProviderProps['theme']['theme$'];
export const wrapWithTheme = (node: React.ReactNode, theme$: Theme) =>
kbnWrapWithTheme(node, { theme$ });
/** @deprecated Use `wrapWithTheme` from `@kbn/react-kibana-context-theme` */
export const wrapWithTheme = (
node: React.ReactNode,
theme$: Theme,
userProfile?: UserProfileService
) => kbnWrapWithTheme(node, { theme$ }, userProfile);

View file

@ -15,6 +15,7 @@ import type { MountPoint } from '@kbn/core/public';
import type { AnalyticsServiceStart } from '@kbn/core-analytics-browser';
import type { I18nStart } from '@kbn/core-i18n-browser';
import type { CoreTheme, ThemeServiceStart } from '@kbn/core-theme-browser';
import type { UserProfileService } from '@kbn/core-user-profile-browser';
import { defaultTheme } from '@kbn/react-kibana-context-common';
import { toMountPoint as _toMountPoint } from '@kbn/react-kibana-mount';
@ -40,6 +41,7 @@ const i18n: I18nStart = {
export interface ToMountPointOptions {
analytics?: AnalyticsServiceStart;
theme$?: Observable<CoreTheme>;
userProfile?: UserProfileService;
}
/**
@ -47,8 +49,8 @@ export interface ToMountPointOptions {
*/
export const toMountPoint = (
node: React.ReactNode,
{ analytics, theme$ }: ToMountPointOptions = {}
{ analytics, theme$, userProfile }: ToMountPointOptions = {}
): MountPoint => {
const theme = theme$ ? { theme$ } : themeStart;
return _toMountPoint(node, { analytics, theme, i18n });
return _toMountPoint(node, { analytics, theme, i18n, userProfile });
};

View file

@ -25,6 +25,7 @@
"@kbn/code-editor",
"@kbn/core-analytics-browser",
"@kbn/shared-ux-link-redirect-app",
"@kbn/core-user-profile-browser",
],
"exclude": [
"target/**/*",

View file

@ -15,6 +15,7 @@ import ReactDOM from 'react-dom';
import { ApplicationStart, HttpStart, ToastsSetup } from '@kbn/core/public';
import type { ThemeServiceStart } from '@kbn/core/public';
import type { UserProfileService } from '@kbn/core-user-profile-browser';
import { SavedObjectNotFound } from '..';
import { KibanaThemeProvider } from '../theme';
@ -49,6 +50,7 @@ export function redirectWhenMissing({
toastNotifications,
onBeforeRedirect,
theme,
userProfile,
}: {
history: History;
navigateToApp: ApplicationStart['navigateToApp'];
@ -67,6 +69,7 @@ export function redirectWhenMissing({
*/
onBeforeRedirect?: (error: SavedObjectNotFound) => void;
theme: ThemeServiceStart;
userProfile?: UserProfileService;
}) {
let localMappingObject: Mapping;
@ -98,7 +101,7 @@ export function redirectWhenMissing({
}),
text: (element: HTMLElement) => {
ReactDOM.render(
<KibanaThemeProvider theme$={theme.theme$}>
<KibanaThemeProvider theme$={theme.theme$} userProfile={userProfile}>
<ErrorRenderer>{error.message}</ErrorRenderer>
</KibanaThemeProvider>,
element

View file

@ -14,6 +14,7 @@ import React, { useEffect } from 'react';
import { act } from 'react-dom/test-utils';
import { BehaviorSubject, of } from 'rxjs';
import { userProfileServiceMock } from '@kbn/core-user-profile-browser-mocks';
import { mountWithIntl } from '@kbn/test-jest-helpers';
import type { CoreTheme } from '@kbn/core/public';
@ -21,6 +22,7 @@ import { KibanaThemeProvider } from './kibana_theme_provider';
describe('KibanaThemeProvider', () => {
let euiTheme: ReturnType<typeof useEuiTheme> | undefined;
const userProfile = userProfileServiceMock.createStart();
beforeEach(() => {
euiTheme = undefined;
@ -55,7 +57,7 @@ describe('KibanaThemeProvider', () => {
const coreTheme: CoreTheme = { darkMode: true, name: 'amsterdam' };
const wrapper = mountWithIntl(
<KibanaThemeProvider theme$={of(coreTheme)}>
<KibanaThemeProvider theme$={of(coreTheme)} userProfile={userProfile}>
<InnerComponent />
</KibanaThemeProvider>
);
@ -69,7 +71,7 @@ describe('KibanaThemeProvider', () => {
const coreTheme$ = new BehaviorSubject<CoreTheme>({ darkMode: true, name: 'amsterdam' });
const wrapper = mountWithIntl(
<KibanaThemeProvider theme$={coreTheme$}>
<KibanaThemeProvider theme$={coreTheme$} userProfile={userProfile}>
<InnerComponent />
</KibanaThemeProvider>
);

View file

@ -12,10 +12,12 @@ import { Observable } from 'rxjs';
import { EuiProviderProps } from '@elastic/eui';
import { CoreTheme } from '@kbn/core-theme-browser';
import type { UserProfileService } from '@kbn/core-user-profile-browser';
import { KibanaThemeProvider as KbnThemeProvider } from '@kbn/react-kibana-context-theme';
export interface KibanaThemeProviderProps {
theme$: Observable<CoreTheme>;
userProfile?: UserProfileService;
modify?: EuiProviderProps<{}>['modify'];
children: React.ReactNode;
}
@ -23,6 +25,11 @@ export interface KibanaThemeProviderProps {
/** @deprecated use `KibanaThemeProvider` from `@kbn/react-kibana-context-theme */
export const KibanaThemeProvider: FC<PropsWithChildren<KibanaThemeProviderProps>> = ({
theme$,
userProfile,
modify,
children,
}) => <KbnThemeProvider {...{ theme: { theme$ }, modify }}>{children}</KbnThemeProvider>;
}) => (
<KbnThemeProvider {...{ theme: { theme$ }, modify }} userProfile={userProfile}>
{children}
</KbnThemeProvider>
);

View file

@ -23,6 +23,8 @@
"@kbn/core-notifications-browser-mocks",
"@kbn/react-kibana-context-theme",
"@kbn/core-theme-browser",
"@kbn/core-user-profile-browser",
"@kbn/core-user-profile-browser-mocks",
],
"exclude": [
"target/**/*",

View file

@ -17,3 +17,4 @@ export function setStartServices(core: CoreStart) {
export const getAnalytics = () => coreStart.analytics;
export const getI18n = () => coreStart.i18n;
export const getTheme = () => coreStart.theme;
export const getUserProfile = () => coreStart.userProfile;

View file

@ -10,7 +10,7 @@
import React, { FC, PropsWithChildren } from 'react';
import { toMountPoint } from '@kbn/react-kibana-mount';
import { getAnalytics, getI18n, getTheme } from '../kibana_services';
import { getAnalytics, getI18n, getTheme, getUserProfile } from '../kibana_services';
/**
* Represents the result of trying to persist the saved object.
@ -68,7 +68,7 @@ export function showSaveModal(
children: augmentedElement,
});
}),
{ analytics: getAnalytics(), theme: getTheme(), i18n: getI18n() }
{ analytics: getAnalytics(), theme: getTheme(), i18n: getI18n(), userProfile: getUserProfile() }
);
unmount = mount(document.createElement('div'));

View file

@ -54,7 +54,7 @@ export interface SavedObjectCreationOpts {
overwrite?: boolean;
}
export type StartServices = Pick<CoreStart, 'analytics' | 'i18n' | 'theme'>;
export type StartServices = Pick<CoreStart, 'analytics' | 'i18n' | 'theme' | 'userProfile'>;
export interface SavedObjectAttributesAndRefs {
attributes: SavedObjectAttributes;

View file

@ -10,7 +10,7 @@
import React from 'react';
import ReactDOM from 'react-dom';
import { toMountPoint } from '@kbn/react-kibana-mount';
import { CoreStart, ThemeServiceStart, ToastsSetup } from '@kbn/core/public';
import { CoreStart, ThemeServiceStart, ToastsSetup, UserProfileService } from '@kbn/core/public';
import { ShowShareMenuOptions } from '../types';
import { ShareMenuRegistryStart } from './share_menu_registry';
import { AnonymousAccessServiceContract } from '../../common/anonymous_access';
@ -49,10 +49,9 @@ export class ShareMenuManager {
menuItems,
urlService,
anonymousAccess,
theme: core.theme,
i18n: core.i18n,
toasts: core.notifications.toasts,
publicAPIEnabled: !disableEmbed,
...core,
});
},
};
@ -75,28 +74,28 @@ export class ShareMenuManager {
shareableUrl,
shareableUrlLocatorParams,
embedUrlParamExtensions,
theme,
showPublicUrlSwitch,
urlService,
anonymousAccess,
snapshotShareWarning,
onClose,
disabledShareUrl,
i18n,
isDirty,
toasts,
delegatedShareUrlHandler,
publicAPIEnabled,
...startServices
}: ShowShareMenuOptions & {
anchorElement: HTMLElement;
menuItems: ShareMenuItemV2[];
urlService: BrowserUrlService;
anonymousAccess: AnonymousAccessServiceContract | undefined;
theme: ThemeServiceStart;
onClose: () => void;
i18n: CoreStart['i18n'];
isDirty: boolean;
toasts: ToastsSetup;
userProfile: UserProfileService;
theme: ThemeServiceStart;
i18n: CoreStart['i18n'];
}) {
if (this.isOpen) {
onClose();
@ -135,11 +134,10 @@ export class ShareMenuManager {
onClose();
unmount();
},
theme,
i18n,
...startServices,
}}
/>,
{ i18n, theme }
startServices
);
const openModal = () => {

View file

@ -14,6 +14,7 @@ import { EuiPageTemplate } from '@elastic/eui';
import type { CustomBrandingSetup } from '@kbn/core-custom-branding-browser';
import type { ChromeDocTitle, ThemeServiceSetup } from '@kbn/core/public';
import { KibanaThemeProvider } from '@kbn/react-kibana-context-theme';
import type { UserProfileService } from '@kbn/core-user-profile-browser';
import type { RedirectManager } from '../redirect_manager';
import { RedirectEmptyPrompt } from './empty_prompt';
@ -25,6 +26,7 @@ export interface PageProps {
customBranding: CustomBrandingSetup;
manager: Pick<RedirectManager, 'error$'>;
theme: ThemeServiceSetup;
userProfile: UserProfileService;
}
export const Page: React.FC<PageProps> = ({
@ -32,14 +34,14 @@ export const Page: React.FC<PageProps> = ({
homeHref,
customBranding,
docTitle,
theme,
...startServices
}) => {
const error = useObservable(manager.error$);
const hasCustomBranding = useObservable(customBranding.hasCustomBranding$);
if (error) {
return (
<KibanaThemeProvider theme={theme}>
<KibanaThemeProvider {...startServices}>
<EuiPageTemplate>
<RedirectEmptyPrompt docTitle={docTitle} error={error} homeHref={homeHref} />
</EuiPageTemplate>
@ -48,7 +50,7 @@ export const Page: React.FC<PageProps> = ({
}
return (
<KibanaThemeProvider theme={theme}>
<KibanaThemeProvider {...startServices}>
<EuiPageTemplate>
<Spinner showPlainSpinner={Boolean(hasCustomBranding)} />
</EuiPageTemplate>

View file

@ -39,13 +39,14 @@ export class RedirectManager {
mount: async (params) => {
const { render } = await import('./render');
const [start] = await core.getStartServices();
const { chrome, uiSettings } = start;
const { chrome, uiSettings, userProfile } = start;
const unmount = render(params.element, {
manager: this,
customBranding,
docTitle: chrome.docTitle,
theme,
userProfile,
homeHref: getHomeHref(http, uiSettings),
});

View file

@ -25,6 +25,7 @@
"@kbn/core-theme-browser-mocks",
"@kbn/core-i18n-browser-mocks",
"@kbn/core-notifications-browser-mocks",
"@kbn/core-user-profile-browser",
],
"exclude": [
"target/**/*",

View file

@ -15,6 +15,7 @@ import {
notificationServiceMock,
themeServiceMock,
} from '@kbn/core/public/mocks';
import { userProfileServiceMock } from '@kbn/core-user-profile-browser-mocks';
import type { TelemetryConstants } from '.';
import type { TelemetryPluginStart, TelemetryPluginSetup, TelemetryPluginConfig } from './plugin';
import { TelemetryService, TelemetryNotifications } from './services';
@ -82,6 +83,7 @@ export function mockTelemetryNotifications({
analytics: analyticsServiceMock.createAnalyticsServiceStart(),
i18n: i18nServiceMock.createStartContract(),
theme: themeServiceMock.createStartContract(),
userProfile: userProfileServiceMock.createStart(),
telemetryService,
telemetryConstants: mockTelemetryConstants(),
});

View file

@ -15,6 +15,7 @@ import {
overlayServiceMock,
themeServiceMock,
} from '@kbn/core/public/mocks';
import { userProfileServiceMock } from '@kbn/core-user-profile-browser-mocks';
import { mockTelemetryConstants, mockTelemetryService } from '../../mocks';
describe('renderOptInStatusNoticeBanner', () => {
@ -25,6 +26,7 @@ describe('renderOptInStatusNoticeBanner', () => {
const analytics = analyticsServiceMock.createAnalyticsServiceStart();
const i18n = i18nServiceMock.createStartContract();
const theme = themeServiceMock.createStartContract();
const userProfile = userProfileServiceMock.createStart();
const telemetryConstants = mockTelemetryConstants();
const telemetryService = mockTelemetryService();
overlays.banners.add.mockReturnValue(bannerID);
@ -36,6 +38,7 @@ describe('renderOptInStatusNoticeBanner', () => {
analytics,
i18n,
theme,
userProfile,
telemetryConstants,
telemetryService,
});

View file

@ -14,7 +14,8 @@ import { withSuspense } from '@kbn/shared-ux-utility';
import { TelemetryService } from '..';
import type { TelemetryConstants } from '../..';
interface RenderBannerConfig extends Pick<CoreStart, 'analytics' | 'i18n' | 'theme'> {
interface RenderBannerConfig
extends Pick<CoreStart, 'analytics' | 'i18n' | 'theme' | 'userProfile'> {
http: HttpStart;
overlays: OverlayStart;
onSeen: () => void;

View file

@ -12,8 +12,9 @@ import type { TelemetryService } from '../telemetry_service';
import type { TelemetryConstants } from '../..';
import { renderOptInStatusNoticeBanner } from './render_opt_in_status_notice_banner';
interface TelemetryNotificationsConstructor
extends Pick<CoreStart, 'analytics' | 'i18n' | 'theme'> {
type StartServices = Pick<CoreStart, 'analytics' | 'i18n' | 'theme' | 'userProfile'>;
interface TelemetryNotificationsConstructor extends StartServices {
http: HttpStart;
overlays: OverlayStart;
telemetryService: TelemetryService;
@ -26,7 +27,7 @@ interface TelemetryNotificationsConstructor
export class TelemetryNotifications {
private readonly http: HttpStart;
private readonly overlays: OverlayStart;
private readonly startServices: Pick<CoreStart, 'analytics' | 'i18n' | 'theme'>;
private readonly startServices: StartServices;
private readonly telemetryConstants: TelemetryConstants;
private readonly telemetryService: TelemetryService;
private optInStatusNoticeBannerId?: string;

View file

@ -35,6 +35,7 @@
"@kbn/react-kibana-mount",
"@kbn/core-node-server",
"@kbn/security-plugin-types-server",
"@kbn/core-user-profile-browser-mocks",
],
"exclude": [
"target/**/*",

View file

@ -13,7 +13,7 @@ import { EuiContextMenu, EuiContextMenuPanelDescriptor, EuiPopover } from '@elas
import { EventEmitter } from 'events';
import ReactDOM from 'react-dom';
import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render';
import { getAnalytics, getI18n, getTheme } from '../services';
import { getAnalytics, getI18n, getTheme, getUserProfile } from '../services';
let activeSession: ContextMenuSession | null = null;
@ -171,7 +171,12 @@ export function openContextMenu(
};
ReactDOM.render(
<KibanaRenderContextProvider analytics={getAnalytics()} i18n={getI18n()} theme={getTheme()}>
<KibanaRenderContextProvider
analytics={getAnalytics()}
i18n={getI18n()}
theme={getTheme()}
userProfile={getUserProfile()}
>
<EuiPopover
className="embPanel__optionsMenuPopover"
// @ts-expect-error @types/react@18 upgrade - Type 'HTMLElement' is not assignable to type 'NonNullable<ReactNode> | undefined'

View file

@ -16,7 +16,7 @@ import {
addPanelMenuTrigger,
} from '@kbn/ui-actions-browser/src/triggers';
import { UiActionsService } from './service';
import { setAnalytics, setI18n, setNotifications, setTheme } from './services';
import { setAnalytics, setI18n, setNotifications, setTheme, setUserProfile } from './services';
export type UiActionsPublicSetup = Pick<
UiActionsService,
@ -62,6 +62,7 @@ export class UiActionsPlugin
setI18n(core.i18n);
setNotifications(core.notifications);
setTheme(core.theme);
setUserProfile(core.userProfile);
return this.service;
}

View file

@ -7,7 +7,13 @@
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import { AnalyticsServiceStart, CoreStart, I18nStart, ThemeServiceSetup } from '@kbn/core/public';
import {
AnalyticsServiceStart,
CoreStart,
I18nStart,
ThemeServiceSetup,
UserProfileService,
} from '@kbn/core/public';
import { createGetterSetter } from '@kbn/kibana-utils-plugin/public';
export const [getAnalytics, setAnalytics] = createGetterSetter<AnalyticsServiceStart>('Analytics');
@ -15,3 +21,5 @@ export const [getI18n, setI18n] = createGetterSetter<I18nStart>('I18n');
export const [getNotifications, setNotifications] =
createGetterSetter<CoreStart['notifications']>('Notifications');
export const [getTheme, setTheme] = createGetterSetter<ThemeServiceSetup>('Theme');
export const [getUserProfile, setUserProfile] =
createGetterSetter<UserProfileService>('UserProfile');

View file

@ -14,7 +14,7 @@ import { toMountPoint } from '@kbn/react-kibana-mount';
import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render';
import { ActionDefinition } from '../../actions';
type StartServices = Pick<CoreStart, 'analytics' | 'i18n' | 'theme'>;
type StartServices = Pick<CoreStart, 'analytics' | 'i18n' | 'theme' | 'userProfile'>;
const getMenuItem = (core: StartServices) => {
return () => {

View file

@ -18,6 +18,7 @@ import {
EuiTitle,
} from '@elastic/eui';
import type { AnalyticsServiceStart } from '@kbn/core-analytics-browser';
import type { UserProfileService } from '@kbn/core-user-profile-browser';
import type { I18nStart } from '@kbn/core-i18n-browser';
import type { ThemeServiceStart } from '@kbn/core-theme-browser';
import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render';
@ -26,6 +27,7 @@ interface StartServices {
analytics: Pick<AnalyticsServiceStart, 'reportEvent'>;
i18n: I18nStart;
theme: Pick<ThemeServiceStart, 'theme$'>;
userProfile: UserProfileService;
}
import { AppMountParameters } from '@kbn/core/public';

View file

@ -17,6 +17,7 @@
"@kbn/core-analytics-browser",
"@kbn/core-i18n-browser",
"@kbn/core-theme-browser",
"@kbn/react-kibana-context-render"
"@kbn/react-kibana-context-render",
"@kbn/core-user-profile-browser"
]
}

View file

@ -11,6 +11,7 @@ import type {
HttpStart,
I18nStart,
ThemeServiceStart,
UserProfileService,
} from '@kbn/core/public';
export interface StartServices {
@ -18,6 +19,7 @@ export interface StartServices {
analytics: Pick<AnalyticsServiceStart, 'reportEvent'>;
i18n: I18nStart;
theme: Pick<ThemeServiceStart, 'theme$'>;
userProfile: UserProfileService;
}
export const AppContext = createContext<StartServices | undefined>(undefined);

View file

@ -26,8 +26,8 @@ export class ScreenshottingExamplePlugin implements Plugin<void, void> {
title: APPLICATION_NAME,
visibleIn: [],
mount: async ({ element }: AppMountParameters) => {
const [{ http, analytics, i18n, theme }] = await getStartServices();
const startServices = { analytics, http, i18n, theme };
const [{ http, analytics, i18n, theme, userProfile }] = await getStartServices();
const startServices = { analytics, http, i18n, theme, userProfile };
ReactDOM.render(
<AppContext.Provider value={startServices}>

View file

@ -50,14 +50,14 @@ export class GlobalSearchBarPlugin implements Plugin<{}, {}, {}, GlobalSearchBar
private getNavControl(deps: { core: CoreStart } & GlobalSearchBarPluginStartDeps) {
const { core, globalSearch, savedObjectsTagging, usageCollection } = deps;
const { application, http, theme, i18n } = core;
const { application, http } = core;
const reportEvent = new EventReporter({ analytics: core.analytics, usageCollection });
const navControl: ChromeNavControl = {
order: 1000,
mount: (container) => {
ReactDOM.render(
<KibanaRenderContextProvider theme={theme} i18n={i18n}>
<KibanaRenderContextProvider {...core}>
<SearchBar
globalSearch={{ ...globalSearch, searchCharLimit: this.config.input_max_limit }}
navigateToUrl={application.navigateToUrl}

View file

@ -47,7 +47,7 @@ const ExpiredBanner: React.FunctionComponent<Props> = (props) => (
</EuiCallOut>
);
type MountProps = Props & Pick<CoreStart, 'analytics' | 'i18n' | 'theme'>;
type MountProps = Props & Pick<CoreStart, 'analytics' | 'i18n' | 'theme' | 'userProfile'>;
export const mountExpiredBanner = ({ type, uploadUrl, ...startServices }: MountProps) =>
toMountPoint(<ExpiredBanner type={type!} uploadUrl={uploadUrl} />, startServices);

View file

@ -110,15 +110,16 @@ export class ReportingPublicPlugin
} = setupDeps;
const startServices$: Observable<StartServices> = from(getStartServices()).pipe(
map(([services, ...rest]) => {
map(([start, ...rest]) => {
return [
{
application: services.application,
analytics: services.analytics,
i18n: services.i18n,
theme: services.theme,
notifications: services.notifications,
uiSettings: services.uiSettings,
application: start.application,
analytics: start.analytics,
i18n: start.i18n,
theme: start.theme,
userProfile: start.userProfile,
notifications: start.notifications,
uiSettings: start.uiSettings,
},
...rest,
];

View file

@ -20,6 +20,7 @@ export type StartServices = [
| 'analytics'
| 'i18n'
| 'theme'
| 'userProfile'
// used extensively in Reporting plugin
| 'application'
| 'notifications'

View file

@ -12,5 +12,5 @@ export type SavedObjectTaggingPluginStart = SavedObjectsTaggingApi;
export type StartServices = Pick<
CoreStart,
'overlays' | 'notifications' | 'analytics' | 'i18n' | 'theme'
'overlays' | 'notifications' | 'analytics' | 'i18n' | 'theme' | 'userProfile'
>;

View file

@ -83,7 +83,7 @@ export class ServerlessPlugin
core.chrome.navControls.registerRight({
order: 1,
mount: toMountPoint(
<KibanaRenderContextProvider i18n={core.i18n} theme={core.theme}>
<KibanaRenderContextProvider {...core}>
<EuiButton
href="https://ela.st/serverless-feedback"
size={'s'}
@ -143,7 +143,7 @@ export class ServerlessPlugin
currentProjectType: ProjectType
) {
ReactDOM.render(
<KibanaRenderContextProvider i18n={coreStart.i18n} theme={coreStart.theme}>
<KibanaRenderContextProvider {...coreStart}>
<ProjectSwitcherKibanaProvider {...{ coreStart, projectChangeAPIUrl }}>
<ProjectSwitcher {...{ currentProjectType }} />
</ProjectSwitcherKibanaProvider>