[Core] Remove usage of deprecated React rendering utilities (#180973)

## Summary

Partially addresses https://github.com/elastic/kibana-team/issues/805

Follows https://github.com/elastic/kibana/pull/180331

These changes come up from searching in the code and finding where
certain kinds of deprecated AppEx-SharedUX modules are imported.
**Reviewers: Please interact with critical paths through the UI
components touched in this PR, ESPECIALLY in terms of testing dark mode
and i18n.**

This focuses on code within Kibana Core.

<img width="1196" alt="image"
src="7f8d3707-94f0-4746-8dd5-dd858ce027f9">

Note: this also makes inclusion of `i18n` and `analytics` dependencies
consistent. Analytics is an optional dependency for the SharedUX
modules, which wrap `KibanaErrorBoundaryProvider` and is designed to
capture telemetry about errors that are caught in the error boundary.
### Checklist

Delete any items that are not applicable to this PR.

- [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
- [ ] This renders correctly on smaller devices using a responsive
layout. (You can test this [in your
browser](https://www.browserstack.com/guide/responsive-testing-on-local-server))
- [ ] This was checked for [cross-browser
compatibility](https://www.elastic.co/support/matrix#matrix_browsers)

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Tim Sullivan 2024-04-18 16:36:08 -07:00 committed by GitHub
parent 2d2649f13c
commit e951c37322
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
45 changed files with 279 additions and 182 deletions

View file

@ -13,7 +13,7 @@ import { i18n } from '@kbn/i18n';
import { I18nProvider } from '@kbn/i18n-react'; import { I18nProvider } from '@kbn/i18n-react';
import { EuiPageTemplate } from '@elastic/eui'; import { EuiPageTemplate } from '@elastic/eui';
import { CoreThemeProvider } from '@kbn/core-theme-browser-internal'; import { KibanaThemeProvider } from '@kbn/react-kibana-context-theme';
import type { IBasePath } from '@kbn/core-http-browser'; import type { IBasePath } from '@kbn/core-http-browser';
import type { AppMountParameters } from '@kbn/core-application-browser'; import type { AppMountParameters } from '@kbn/core-application-browser';
import { UrlOverflowUi } from './url_overflow_ui'; import { UrlOverflowUi } from './url_overflow_ui';
@ -77,9 +77,9 @@ interface Deps {
export const renderApp = ({ element, history, theme$ }: AppMountParameters, { basePath }: Deps) => { export const renderApp = ({ element, history, theme$ }: AppMountParameters, { basePath }: Deps) => {
ReactDOM.render( ReactDOM.render(
<I18nProvider> <I18nProvider>
<CoreThemeProvider theme$={theme$}> <KibanaThemeProvider theme={{ theme$ }}>
<ErrorApp history={history} basePath={basePath} /> <ErrorApp history={history} basePath={basePath} />
</CoreThemeProvider> </KibanaThemeProvider>
</I18nProvider>, </I18nProvider>,
element element
); );

View file

@ -9,7 +9,7 @@
import React from 'react'; import React from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import { I18nProvider } from '@kbn/i18n-react'; import { I18nProvider } from '@kbn/i18n-react';
import { CoreThemeProvider } from '@kbn/core-theme-browser-internal'; import { KibanaThemeProvider } from '@kbn/react-kibana-context-theme';
import type { InternalHttpSetup } from '@kbn/core-http-browser-internal'; import type { InternalHttpSetup } from '@kbn/core-http-browser-internal';
import type { NotificationsSetup } from '@kbn/core-notifications-browser'; import type { NotificationsSetup } from '@kbn/core-notifications-browser';
import type { AppMountParameters } from '@kbn/core-application-browser'; import type { AppMountParameters } from '@kbn/core-application-browser';
@ -26,9 +26,9 @@ export const renderApp = (
) => { ) => {
ReactDOM.render( ReactDOM.render(
<I18nProvider> <I18nProvider>
<CoreThemeProvider theme$={theme$}> <KibanaThemeProvider theme={{ theme$ }}>
<StatusApp http={http} notifications={notifications} /> <StatusApp http={http} notifications={notifications} />
</CoreThemeProvider> </KibanaThemeProvider>
</I18nProvider>, </I18nProvider>,
element element
); );

View file

@ -23,7 +23,6 @@
"@kbn/core-notifications-browser", "@kbn/core-notifications-browser",
"@kbn/core-application-browser", "@kbn/core-application-browser",
"@kbn/core-application-browser-internal", "@kbn/core-application-browser-internal",
"@kbn/core-theme-browser-internal",
"@kbn/core-mount-utils-browser-internal", "@kbn/core-mount-utils-browser-internal",
"@kbn/core-status-common-internal", "@kbn/core-status-common-internal",
"@kbn/core-http-browser-internal", "@kbn/core-http-browser-internal",
@ -35,6 +34,7 @@
"@kbn/core-status-common", "@kbn/core-status-common",
"@kbn/core-doc-links-browser-mocks", "@kbn/core-doc-links-browser-mocks",
"@kbn/test-jest-helpers", "@kbn/test-jest-helpers",
"@kbn/react-kibana-context-theme",
], ],
"exclude": [ "exclude": [
"target/**/*", "target/**/*",

View file

@ -17,8 +17,8 @@ import {
EuiModalFooter, EuiModalFooter,
EuiModalHeader, EuiModalHeader,
EuiModalHeaderTitle, EuiModalHeaderTitle,
EuiSpacer,
} from '@elastic/eui'; } from '@elastic/eui';
import { EuiSpacer } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n-react'; import { FormattedMessage } from '@kbn/i18n-react';
import type { AnalyticsServiceStart } from '@kbn/core-analytics-browser'; import type { AnalyticsServiceStart } from '@kbn/core-analytics-browser';
import type { I18nStart } from '@kbn/core-i18n-browser'; import type { I18nStart } from '@kbn/core-i18n-browser';
@ -26,14 +26,17 @@ import type { OverlayStart } from '@kbn/core-overlays-browser';
import { ThemeServiceStart } from '@kbn/core-theme-browser'; import { ThemeServiceStart } from '@kbn/core-theme-browser';
import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render'; import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render';
interface ErrorToastProps { interface StartServices {
analytics: AnalyticsServiceStart;
i18n: I18nStart;
theme: ThemeServiceStart;
}
interface ErrorToastProps extends StartServices {
title: string; title: string;
error: Error; error: Error;
toastMessage: string; toastMessage: string;
openModal: OverlayStart['openModal']; openModal: OverlayStart['openModal'];
analytics: AnalyticsServiceStart;
i18n: I18nStart;
theme: ThemeServiceStart;
} }
interface RequestError extends Error { interface RequestError extends Error {
@ -57,9 +60,7 @@ export function showErrorDialog({
title, title,
error, error,
openModal, openModal,
analytics, ...startServices
i18n,
theme,
}: Pick<ErrorToastProps, 'error' | 'title' | 'openModal' | 'analytics' | 'i18n' | 'theme'>) { }: Pick<ErrorToastProps, 'error' | 'title' | 'openModal' | 'analytics' | 'i18n' | 'theme'>) {
let text = ''; let text = '';
@ -74,7 +75,7 @@ export function showErrorDialog({
const modal = openModal( const modal = openModal(
mount( mount(
<KibanaRenderContextProvider analytics={analytics} i18n={i18n} theme={theme}> <KibanaRenderContextProvider {...startServices}>
<EuiModalHeader> <EuiModalHeader>
<EuiModalHeaderTitle>{title}</EuiModalHeaderTitle> <EuiModalHeaderTitle>{title}</EuiModalHeaderTitle>
</EuiModalHeader> </EuiModalHeader>
@ -107,9 +108,7 @@ export function ErrorToast({
error, error,
toastMessage, toastMessage,
openModal, openModal,
analytics, ...startServices
i18n,
theme,
}: ErrorToastProps) { }: ErrorToastProps) {
return ( return (
<React.Fragment> <React.Fragment>
@ -119,7 +118,7 @@ export function ErrorToast({
size="s" size="s"
color="danger" color="danger"
data-test-subj="errorToastBtn" data-test-subj="errorToastBtn"
onClick={() => showErrorDialog({ title, error, openModal, analytics, i18n, theme })} onClick={() => showErrorDialog({ title, error, openModal, ...startServices })}
> >
<FormattedMessage <FormattedMessage
id="core.toasts.errorToast.seeFullError" id="core.toasts.errorToast.seeFullError"

View file

@ -8,14 +8,18 @@
import { InternalOverlayBannersStart, OverlayBannersService } from './banners_service'; import { InternalOverlayBannersStart, OverlayBannersService } from './banners_service';
import { take } from 'rxjs'; import { take } from 'rxjs';
import { analyticsServiceMock } from '@kbn/core-analytics-browser-mocks';
import { i18nServiceMock } from '@kbn/core-i18n-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 { uiSettingsServiceMock } from '@kbn/core-ui-settings-browser-mocks';
describe('OverlayBannersService', () => { describe('OverlayBannersService', () => {
let service: InternalOverlayBannersStart; let service: InternalOverlayBannersStart;
beforeEach(() => { beforeEach(() => {
service = new OverlayBannersService().start({ service = new OverlayBannersService().start({
analytics: analyticsServiceMock.createAnalyticsServiceStart(),
i18n: i18nServiceMock.createStartContract(), i18n: i18nServiceMock.createStartContract(),
theme: themeServiceMock.createStartContract(),
uiSettings: uiSettingsServiceMock.createStartContract(), uiSettings: uiSettingsServiceMock.createStartContract(),
}); });
}); });

View file

@ -10,7 +10,9 @@ import React from 'react';
import { BehaviorSubject, type Observable } from 'rxjs'; import { BehaviorSubject, type Observable } from 'rxjs';
import { map } from 'rxjs'; import { map } from 'rxjs';
import type { AnalyticsServiceStart } from '@kbn/core-analytics-browser';
import type { I18nStart } from '@kbn/core-i18n-browser'; import type { I18nStart } from '@kbn/core-i18n-browser';
import type { ThemeServiceStart } from '@kbn/core-theme-browser';
import type { IUiSettingsClient } from '@kbn/core-ui-settings-browser'; import type { IUiSettingsClient } from '@kbn/core-ui-settings-browser';
import type { MountPoint } from '@kbn/core-mount-utils-browser'; import type { MountPoint } from '@kbn/core-mount-utils-browser';
import type { OverlayBannersStart } from '@kbn/core-overlays-browser'; import type { OverlayBannersStart } from '@kbn/core-overlays-browser';
@ -18,8 +20,13 @@ import { PriorityMap } from './priority_map';
import { BannersList } from './banners_list'; import { BannersList } from './banners_list';
import { UserBannerService } from './user_banner_service'; import { UserBannerService } from './user_banner_service';
interface StartDeps { interface StartServices {
analytics: AnalyticsServiceStart;
i18n: I18nStart; i18n: I18nStart;
theme: ThemeServiceStart;
}
interface StartDeps extends StartServices {
uiSettings: IUiSettingsClient; uiSettings: IUiSettingsClient;
} }
@ -39,7 +46,7 @@ export interface OverlayBanner {
export class OverlayBannersService { export class OverlayBannersService {
private readonly userBanner = new UserBannerService(); private readonly userBanner = new UserBannerService();
public start({ i18n, uiSettings }: StartDeps): InternalOverlayBannersStart { public start({ uiSettings, ...startServices }: StartDeps): InternalOverlayBannersStart {
let uniqueId = 0; let uniqueId = 0;
const genId = () => `${uniqueId++}`; const genId = () => `${uniqueId++}`;
const banners$ = new BehaviorSubject(new PriorityMap<string, OverlayBanner>()); const banners$ = new BehaviorSubject(new PriorityMap<string, OverlayBanner>());
@ -79,7 +86,7 @@ export class OverlayBannersService {
}, },
}; };
this.userBanner.start({ banners: service, i18n, uiSettings }); this.userBanner.start({ banners: service, uiSettings, ...startServices });
return service; return service;
} }

View file

@ -9,7 +9,9 @@
import { uiSettingsServiceMock } from '@kbn/core-ui-settings-browser-mocks'; import { uiSettingsServiceMock } from '@kbn/core-ui-settings-browser-mocks';
import { UserBannerService } from './user_banner_service'; import { UserBannerService } from './user_banner_service';
import { overlayBannersServiceMock } from './banners_service.test.mocks'; import { overlayBannersServiceMock } from './banners_service.test.mocks';
import { analyticsServiceMock } from '@kbn/core-analytics-browser-mocks';
import { i18nServiceMock } from '@kbn/core-i18n-browser-mocks'; import { i18nServiceMock } from '@kbn/core-i18n-browser-mocks';
import { themeServiceMock } from '@kbn/core-theme-browser-mocks';
import { Subject } from 'rxjs'; import { Subject } from 'rxjs';
describe('OverlayBannersService', () => { describe('OverlayBannersService', () => {
@ -33,7 +35,9 @@ describe('OverlayBannersService', () => {
service = new UserBannerService(); service = new UserBannerService();
service.start({ service.start({
banners, banners,
analytics: analyticsServiceMock.createAnalyticsServiceStart(),
i18n: i18nServiceMock.createStartContract(), i18n: i18nServiceMock.createStartContract(),
theme: themeServiceMock.createStartContract(),
uiSettings, uiSettings,
}); });
}; };

View file

@ -14,13 +14,21 @@ import { Subscription } from 'rxjs';
import { FormattedMessage } from '@kbn/i18n-react'; import { FormattedMessage } from '@kbn/i18n-react';
import { EuiCallOut, EuiButton, EuiLoadingSpinner } from '@elastic/eui'; 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 { I18nStart } from '@kbn/core-i18n-browser'; import type { I18nStart } from '@kbn/core-i18n-browser';
import type { IUiSettingsClient } from '@kbn/core-ui-settings-browser'; import type { IUiSettingsClient } from '@kbn/core-ui-settings-browser';
import type { OverlayBannersStart } from '@kbn/core-overlays-browser'; import type { OverlayBannersStart } from '@kbn/core-overlays-browser';
import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render';
interface StartDeps { interface StartServices {
banners: OverlayBannersStart; analytics: AnalyticsServiceStart;
i18n: I18nStart; i18n: I18nStart;
theme: ThemeServiceStart;
}
interface StartDeps extends StartServices {
banners: OverlayBannersStart;
uiSettings: IUiSettingsClient; uiSettings: IUiSettingsClient;
} }
@ -33,7 +41,7 @@ const ReactMarkdownLazy = React.lazy(() => import('react-markdown'));
export class UserBannerService { export class UserBannerService {
private settingsSubscription?: Subscription; private settingsSubscription?: Subscription;
public start({ banners, i18n, uiSettings }: StartDeps) { public start({ banners, uiSettings, ...startServices }: StartDeps) {
let id: string | undefined; let id: string | undefined;
let timeout: any; let timeout: any;
@ -55,7 +63,7 @@ export class UserBannerService {
id, id,
(el) => { (el) => {
ReactDOM.render( ReactDOM.render(
<i18n.Context> <KibanaRenderContextProvider {...startServices}>
<EuiCallOut <EuiCallOut
title={ title={
<FormattedMessage <FormattedMessage
@ -82,7 +90,7 @@ export class UserBannerService {
/> />
</EuiButton> </EuiButton>
</EuiCallOut> </EuiCallOut>
</i18n.Context>, </KibanaRenderContextProvider>,
el el
); );

View file

@ -39,7 +39,7 @@ export class OverlayService {
targetDomElement: flyoutElement, targetDomElement: flyoutElement,
}); });
const banners = this.bannersService.start({ i18n, uiSettings }); const banners = this.bannersService.start({ uiSettings, analytics, i18n, theme });
const modalElement = document.createElement('div'); const modalElement = document.createElement('div');
targetDomElement.appendChild(modalElement); targetDomElement.appendChild(modalElement);

View file

@ -19,14 +19,17 @@ import type { ThemeServiceStart } from '@kbn/core-theme-browser';
import { KibanaRootContextProvider } from '@kbn/react-kibana-context-root'; import { KibanaRootContextProvider } from '@kbn/react-kibana-context-root';
import { AppWrapper } from './app_containers'; import { AppWrapper } from './app_containers';
export interface StartDeps { interface StartServices {
analytics: AnalyticsServiceStart; analytics: AnalyticsServiceStart;
i18n: I18nStart;
theme: ThemeServiceStart;
}
export interface StartDeps extends StartServices {
application: InternalApplicationStart; application: InternalApplicationStart;
chrome: InternalChromeStart; chrome: InternalChromeStart;
overlays: OverlayStart; overlays: OverlayStart;
targetDomElement: HTMLDivElement; targetDomElement: HTMLDivElement;
theme: ThemeServiceStart;
i18n: I18nStart;
} }
/** /**
@ -38,7 +41,7 @@ export interface StartDeps {
* @internal * @internal
*/ */
export class RenderingService { export class RenderingService {
start({ analytics, application, chrome, overlays, theme, i18n, targetDomElement }: StartDeps) { start({ application, chrome, overlays, targetDomElement, ...startServices }: StartDeps) {
const chromeHeader = chrome.getHeaderComponent(); const chromeHeader = chrome.getHeaderComponent();
const appComponent = application.getComponent(); const appComponent = application.getComponent();
const bannerComponent = overlays.banners.getComponent(); const bannerComponent = overlays.banners.getComponent();
@ -53,12 +56,7 @@ export class RenderingService {
}); });
ReactDOM.render( ReactDOM.render(
<KibanaRootContextProvider <KibanaRootContextProvider {...startServices} globalStyles={true}>
analytics={analytics}
i18n={i18n}
theme={theme}
globalStyles={true}
>
<> <>
{/* Fixed headers */} {/* Fixed headers */}
{chromeHeader} {chromeHeader}

View file

@ -15,8 +15,6 @@
"kbn_references": [ "kbn_references": [
"@kbn/core-application-common", "@kbn/core-application-common",
"@kbn/core-application-browser-internal", "@kbn/core-application-browser-internal",
"@kbn/core-theme-browser",
"@kbn/core-i18n-browser",
"@kbn/core-overlays-browser", "@kbn/core-overlays-browser",
"@kbn/core-chrome-browser-internal", "@kbn/core-chrome-browser-internal",
"@kbn/core-application-browser-mocks", "@kbn/core-application-browser-mocks",
@ -25,8 +23,10 @@
"@kbn/core-theme-browser-mocks", "@kbn/core-theme-browser-mocks",
"@kbn/core-i18n-browser-mocks", "@kbn/core-i18n-browser-mocks",
"@kbn/react-kibana-context-root", "@kbn/react-kibana-context-root",
"@kbn/core-analytics-browser",
"@kbn/core-analytics-browser-mocks", "@kbn/core-analytics-browser-mocks",
"@kbn/core-analytics-browser",
"@kbn/core-i18n-browser",
"@kbn/core-theme-browser"
], ],
"exclude": [ "exclude": [
"target/**/*", "target/**/*",

View file

@ -9,8 +9,6 @@
"requiredPlugins": [ "requiredPlugins": [
"screenshotMode" "screenshotMode"
], ],
"requiredBundles": [ "requiredBundles": []
"kibanaReact"
]
} }
} }

View file

@ -11,15 +11,8 @@ import { catchError, takeUntil } from 'rxjs';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import React from 'react'; import React from 'react';
import moment from 'moment'; import moment from 'moment';
import { I18nProvider } from '@kbn/i18n-react'; import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from '@kbn/core/public';
import { import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render';
PluginInitializerContext,
CoreSetup,
CoreStart,
CoreTheme,
Plugin,
} from '@kbn/core/public';
import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public';
import { NewsfeedPluginBrowserConfig, NewsfeedPluginStartDependencies } from './types'; import { NewsfeedPluginBrowserConfig, NewsfeedPluginStartDependencies } from './types';
import { NewsfeedNavButton } from './components/newsfeed_header_nav_button'; import { NewsfeedNavButton } from './components/newsfeed_header_nav_button';
import { getApi, NewsfeedApi, NewsfeedApiEndpoint } from './lib/api'; import { getApi, NewsfeedApi, NewsfeedApiEndpoint } from './lib/api';
@ -45,7 +38,7 @@ export class NewsfeedPublicPlugin
}); });
} }
public setup(core: CoreSetup) { public setup(_core: CoreSetup) {
return {}; return {};
} }
@ -55,8 +48,7 @@ export class NewsfeedPublicPlugin
const api = this.createNewsfeedApi(this.config, NewsfeedApiEndpoint.KIBANA, isScreenshotMode); const api = this.createNewsfeedApi(this.config, NewsfeedApiEndpoint.KIBANA, isScreenshotMode);
core.chrome.navControls.registerRight({ core.chrome.navControls.registerRight({
order: 1000, order: 1000,
mount: (target) => mount: (target) => this.mount(api, target, core),
this.mount(api, target, core.theme.theme$, core.customBranding.hasCustomBranding$),
}); });
return { return {
@ -92,18 +84,12 @@ export class NewsfeedPublicPlugin
}; };
} }
private mount( private mount(api: NewsfeedApi, targetDomElement: HTMLElement, core: CoreStart) {
api: NewsfeedApi, const hasCustomBranding$ = core.customBranding.hasCustomBranding$;
targetDomElement: HTMLElement,
theme$: Rx.Observable<CoreTheme>,
hasCustomBranding$: Rx.Observable<boolean>
) {
ReactDOM.render( ReactDOM.render(
<KibanaThemeProvider theme$={theme$}> <KibanaRenderContextProvider {...core}>
<I18nProvider> <NewsfeedNavButton newsfeedApi={api} hasCustomBranding$={hasCustomBranding$} />
<NewsfeedNavButton newsfeedApi={api} hasCustomBranding$={hasCustomBranding$} /> </KibanaRenderContextProvider>,
</I18nProvider>
</KibanaThemeProvider>,
targetDomElement targetDomElement
); );
return () => ReactDOM.unmountComponentAtNode(targetDomElement); return () => ReactDOM.unmountComponentAtNode(targetDomElement);

View file

@ -6,12 +6,12 @@
"include": ["public/**/*", "server/**/*", "common/*", "../../../typings/**/*"], "include": ["public/**/*", "server/**/*", "common/*", "../../../typings/**/*"],
"kbn_references": [ "kbn_references": [
"@kbn/core", "@kbn/core",
"@kbn/kibana-react-plugin",
"@kbn/screenshot-mode-plugin", "@kbn/screenshot-mode-plugin",
"@kbn/i18n-react", "@kbn/i18n-react",
"@kbn/i18n", "@kbn/i18n",
"@kbn/utility-types", "@kbn/utility-types",
"@kbn/config-schema", "@kbn/config-schema",
"@kbn/react-kibana-context-render",
], ],
"exclude": [ "exclude": [
"target/**/*", "target/**/*",

View file

@ -11,8 +11,7 @@
"dataViews" "dataViews"
], ],
"requiredBundles": [ "requiredBundles": [
"kibanaUtils", "kibanaUtils"
"kibanaReact"
] ]
} }
} }

View file

@ -13,5 +13,6 @@ export function setStartServices(core: CoreStart) {
coreStart = core; coreStart = core;
} }
export const getAnalytics = () => coreStart.analytics;
export const getI18n = () => coreStart.i18n; export const getI18n = () => coreStart.i18n;
export const getTheme = () => coreStart.theme; export const getTheme = () => coreStart.theme;

View file

@ -57,6 +57,7 @@ export class SavedObjectsPublicPlugin
chrome: core.chrome, chrome: core.chrome,
overlays: core.overlays, overlays: core.overlays,
}, },
core,
this.decoratorRegistry this.decoratorRegistry
), ),
}; };

View file

@ -9,8 +9,8 @@
import React from 'react'; import React from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render';
import { getI18n, getTheme } from '../kibana_services'; import { getAnalytics, getI18n, getTheme } from '../kibana_services';
/** /**
* Represents the result of trying to persist the saved object. * Represents the result of trying to persist the saved object.
@ -59,9 +59,9 @@ export function showSaveModal(
const I18nContext = getI18n().Context; const I18nContext = getI18n().Context;
ReactDOM.render( ReactDOM.render(
<KibanaThemeProvider theme$={getTheme().theme$}> <KibanaRenderContextProvider analytics={getAnalytics()} i18n={getI18n()} theme={getTheme()}>
<I18nContext>{Wrapper ? <Wrapper>{element}</Wrapper> : element}</I18nContext> <I18nContext>{Wrapper ? <Wrapper>{element}</Wrapper> : element}</I18nContext>
</KibanaThemeProvider>, </KibanaRenderContextProvider>,
container container
); );
} }

View file

@ -17,6 +17,7 @@ import {
SavedObjectConfig, SavedObjectConfig,
SavedObjectKibanaServices, SavedObjectKibanaServices,
SavedObjectSaveOpts, SavedObjectSaveOpts,
StartServices,
} from '../../types'; } from '../../types';
import { applyESResp } from './apply_es_resp'; import { applyESResp } from './apply_es_resp';
import { saveSavedObject } from './save_saved_object'; import { saveSavedObject } from './save_saved_object';
@ -37,6 +38,7 @@ export function buildSavedObject(
savedObject: SavedObject, savedObject: SavedObject,
config: SavedObjectConfig, config: SavedObjectConfig,
services: SavedObjectKibanaServices, services: SavedObjectKibanaServices,
startServices: StartServices,
decorators: SavedObjectDecorator[] = [] decorators: SavedObjectDecorator[] = []
) { ) {
applyDecorators(savedObject, config, decorators); applyDecorators(savedObject, config, decorators);
@ -110,7 +112,7 @@ export function buildSavedObject(
savedObject.save = async (opts: SavedObjectSaveOpts) => { savedObject.save = async (opts: SavedObjectSaveOpts) => {
try { try {
const result = await saveSavedObject(savedObject, config, opts, services); const result = await saveSavedObject(savedObject, config, opts, services, startServices);
return Promise.resolve(result); return Promise.resolve(result);
} catch (e) { } catch (e) {
return Promise.reject(e); return Promise.reject(e);

View file

@ -6,7 +6,7 @@
* Side Public License, v 1. * Side Public License, v 1.
*/ */
import { SavedObject, SavedObjectKibanaServices } from '../../types'; import { SavedObject, SavedObjectKibanaServices, StartServices } from '../../types';
import { findObjectByTitle } from './find_object_by_title'; import { findObjectByTitle } from './find_object_by_title';
import { SAVE_DUPLICATE_REJECTED } from '../../constants'; import { SAVE_DUPLICATE_REJECTED } from '../../constants';
import { displayDuplicateTitleConfirmModal } from './display_duplicate_title_confirm_modal'; import { displayDuplicateTitleConfirmModal } from './display_duplicate_title_confirm_modal';
@ -19,6 +19,7 @@ import { displayDuplicateTitleConfirmModal } from './display_duplicate_title_con
* @param isTitleDuplicateConfirmed * @param isTitleDuplicateConfirmed
* @param onTitleDuplicate * @param onTitleDuplicate
* @param services * @param services
* @param startServices
*/ */
export async function checkForDuplicateTitle( export async function checkForDuplicateTitle(
savedObject: Pick< savedObject: Pick<
@ -27,7 +28,8 @@ export async function checkForDuplicateTitle(
>, >,
isTitleDuplicateConfirmed: boolean, isTitleDuplicateConfirmed: boolean,
onTitleDuplicate: (() => void) | undefined, onTitleDuplicate: (() => void) | undefined,
services: Pick<SavedObjectKibanaServices, 'savedObjectsClient' | 'overlays'> services: Pick<SavedObjectKibanaServices, 'savedObjectsClient' | 'overlays'>,
startServices: StartServices
): Promise<true> { ): Promise<true> {
const { savedObjectsClient, overlays } = services; const { savedObjectsClient, overlays } = services;
// Don't check for duplicates if user has already confirmed save with duplicate title // Don't check for duplicates if user has already confirmed save with duplicate title
@ -58,5 +60,5 @@ export async function checkForDuplicateTitle(
// TODO: make onTitleDuplicate a required prop and remove UI components from this class // TODO: make onTitleDuplicate a required prop and remove UI components from this class
// Need to leave here until all users pass onTitleDuplicate. // Need to leave here until all users pass onTitleDuplicate.
return displayDuplicateTitleConfirmModal(savedObject, overlays); return displayDuplicateTitleConfirmModal(savedObject, overlays, startServices);
} }

View file

@ -7,16 +7,19 @@
*/ */
import React from 'react'; import React from 'react';
import { OverlayStart } from '@kbn/core/public'; import { CoreStart, OverlayStart } from '@kbn/core/public';
import { i18n } from '@kbn/i18n'; import { i18n } from '@kbn/i18n';
import { EuiConfirmModal } from '@elastic/eui'; import { EuiConfirmModal } from '@elastic/eui';
import { toMountPoint } from '@kbn/kibana-react-plugin/public'; import { toMountPoint } from '@kbn/react-kibana-mount';
type StartServices = Pick<CoreStart, 'analytics' | 'i18n' | 'theme'>;
export function confirmModalPromise( export function confirmModalPromise(
message = '', message = '',
title = '', title = '',
confirmBtnText = '', confirmBtnText = '',
overlays: OverlayStart overlays: OverlayStart,
startServices: StartServices
): Promise<true> { ): Promise<true> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const cancelButtonText = i18n.translate('savedObjects.confirmModal.cancelButtonLabel', { const cancelButtonText = i18n.translate('savedObjects.confirmModal.cancelButtonLabel', {
@ -39,7 +42,8 @@ export function confirmModalPromise(
title={title} title={title}
> >
{message} {message}
</EuiConfirmModal> </EuiConfirmModal>,
startServices
) )
); );
}); });

View file

@ -9,7 +9,7 @@
import { get } from 'lodash'; import { get } from 'lodash';
import { i18n } from '@kbn/i18n'; import { i18n } from '@kbn/i18n';
import { SavedObjectAttributes } from '@kbn/core/public'; import { SavedObjectAttributes } from '@kbn/core/public';
import { SavedObject, SavedObjectKibanaServices } from '../../types'; import { SavedObject, SavedObjectKibanaServices, StartServices } from '../../types';
import { OVERWRITE_REJECTED } from '../../constants'; import { OVERWRITE_REJECTED } from '../../constants';
import { confirmModalPromise } from './confirm_modal_promise'; import { confirmModalPromise } from './confirm_modal_promise';
@ -33,7 +33,8 @@ export async function createSource(
savedObject: SavedObject, savedObject: SavedObject,
esType: string, esType: string,
options = {}, options = {},
services: SavedObjectKibanaServices services: SavedObjectKibanaServices,
startServices: StartServices
) { ) {
const { savedObjectsClient, overlays } = services; const { savedObjectsClient, overlays } = services;
try { try {
@ -57,7 +58,7 @@ export async function createSource(
defaultMessage: 'Overwrite', defaultMessage: 'Overwrite',
}); });
return confirmModalPromise(confirmMessage, title, confirmButtonText, overlays) return confirmModalPromise(confirmMessage, title, confirmButtonText, overlays, startServices)
.then(() => .then(() =>
savedObjectsClient.create( savedObjectsClient.create(
esType, esType,

View file

@ -10,11 +10,12 @@ import { i18n } from '@kbn/i18n';
import { OverlayStart } from '@kbn/core/public'; import { OverlayStart } from '@kbn/core/public';
import { SAVE_DUPLICATE_REJECTED } from '../../constants'; import { SAVE_DUPLICATE_REJECTED } from '../../constants';
import { confirmModalPromise } from './confirm_modal_promise'; import { confirmModalPromise } from './confirm_modal_promise';
import { SavedObject } from '../../types'; import { SavedObject, StartServices } from '../../types';
export function displayDuplicateTitleConfirmModal( export function displayDuplicateTitleConfirmModal(
savedObject: Pick<SavedObject, 'title' | 'getDisplayName'>, savedObject: Pick<SavedObject, 'title' | 'getDisplayName'>,
overlays: OverlayStart overlays: OverlayStart,
startServices: StartServices
): Promise<true> { ): Promise<true> {
const confirmMessage = i18n.translate( const confirmMessage = i18n.translate(
'savedObjects.confirmModal.saveDuplicateConfirmationMessage', 'savedObjects.confirmModal.saveDuplicateConfirmationMessage',
@ -29,7 +30,7 @@ export function displayDuplicateTitleConfirmModal(
values: { name: savedObject.getDisplayName() }, values: { name: savedObject.getDisplayName() },
}); });
try { try {
return confirmModalPromise(confirmMessage, '', confirmButtonText, overlays); return confirmModalPromise(confirmMessage, '', confirmButtonText, overlays, startServices);
} catch (_) { } catch (_) {
return Promise.reject(new Error(SAVE_DUPLICATE_REJECTED)); return Promise.reject(new Error(SAVE_DUPLICATE_REJECTED));
} }

View file

@ -11,6 +11,7 @@ import {
SavedObjectConfig, SavedObjectConfig,
SavedObjectKibanaServices, SavedObjectKibanaServices,
SavedObjectSaveOpts, SavedObjectSaveOpts,
StartServices,
} from '../../types'; } from '../../types';
import { OVERWRITE_REJECTED, SAVE_DUPLICATE_REJECTED } from '../../constants'; import { OVERWRITE_REJECTED, SAVE_DUPLICATE_REJECTED } from '../../constants';
import { createSource } from './create_source'; import { createSource } from './create_source';
@ -38,6 +39,7 @@ export function isErrorNonFatal(error: { message: string }) {
* @property {func} [options.onTitleDuplicate] - function called if duplicate title exists. * @property {func} [options.onTitleDuplicate] - function called if duplicate title exists.
* When not provided, confirm modal will be displayed asking user to confirm or cancel save. * When not provided, confirm modal will be displayed asking user to confirm or cancel save.
* @param {SavedObjectKibanaServices} [services] * @param {SavedObjectKibanaServices} [services]
* @param {StartServices} [startServices]
* @return {Promise} * @return {Promise}
* @resolved {String} - The id of the doc * @resolved {String} - The id of the doc
*/ */
@ -49,7 +51,8 @@ export async function saveSavedObject(
isTitleDuplicateConfirmed = false, isTitleDuplicateConfirmed = false,
onTitleDuplicate, onTitleDuplicate,
}: SavedObjectSaveOpts = {}, }: SavedObjectSaveOpts = {},
services: SavedObjectKibanaServices services: SavedObjectKibanaServices,
startServices: StartServices
): Promise<string> { ): Promise<string> {
const { savedObjectsClient, chrome } = services; const { savedObjectsClient, chrome } = services;
@ -79,7 +82,8 @@ export async function saveSavedObject(
savedObject, savedObject,
isTitleDuplicateConfirmed, isTitleDuplicateConfirmed,
onTitleDuplicate, onTitleDuplicate,
services services,
startServices
); );
savedObject.isSaving = true; savedObject.isSaving = true;
const resp = confirmOverwrite const resp = confirmOverwrite
@ -88,7 +92,8 @@ export async function saveSavedObject(
savedObject, savedObject,
esType, esType,
savedObject.creationOpts({ references }), savedObject.creationOpts({ references }),
services services,
startServices
) )
: await savedObjectsClient.create( : await savedObjectsClient.create(
esType, esType,

View file

@ -8,6 +8,7 @@
import { SavedObjectAttributes, SavedObjectsCreateOptions, OverlayStart } from '@kbn/core/public'; import { SavedObjectAttributes, SavedObjectsCreateOptions, OverlayStart } from '@kbn/core/public';
import { SavedObjectsClientContract } from '@kbn/core/public'; import { SavedObjectsClientContract } from '@kbn/core/public';
import { analyticsServiceMock, i18nServiceMock, themeServiceMock } from '@kbn/core/public/mocks';
import { saveWithConfirmation } from './save_with_confirmation'; import { saveWithConfirmation } from './save_with_confirmation';
import * as deps from './confirm_modal_promise'; import * as deps from './confirm_modal_promise';
import { OVERWRITE_REJECTED } from '../../constants'; import { OVERWRITE_REJECTED } from '../../constants';
@ -22,6 +23,11 @@ describe('saveWithConfirmation', () => {
title: 'test title', title: 'test title',
displayName: 'test display name', displayName: 'test display name',
}; };
const startServices = {
analytics: analyticsServiceMock.createAnalyticsServiceStart(),
i18n: i18nServiceMock.createStartContract(),
theme: themeServiceMock.createStartContract(),
};
beforeEach(() => { beforeEach(() => {
savedObjectsClient.create = jest.fn(); savedObjectsClient.create = jest.fn();
@ -29,7 +35,13 @@ describe('saveWithConfirmation', () => {
}); });
test('should call create of savedObjectsClient', async () => { test('should call create of savedObjectsClient', async () => {
await saveWithConfirmation(source, savedObject, options, { savedObjectsClient, overlays }); await saveWithConfirmation(
source,
savedObject,
options,
{ savedObjectsClient, overlays },
startServices
);
expect(savedObjectsClient.create).toHaveBeenCalledWith( expect(savedObjectsClient.create).toHaveBeenCalledWith(
savedObject.getEsType(), savedObject.getEsType(),
source, source,
@ -44,12 +56,23 @@ describe('saveWithConfirmation', () => {
opt && opt.overwrite ? Promise.resolve({} as any) : Promise.reject({ res: { status: 409 } }) opt && opt.overwrite ? Promise.resolve({} as any) : Promise.reject({ res: { status: 409 } })
); );
await saveWithConfirmation(source, savedObject, options, { savedObjectsClient, overlays }); await saveWithConfirmation(
source,
savedObject,
options,
{ savedObjectsClient, overlays },
startServices
);
expect(deps.confirmModalPromise).toHaveBeenCalledWith( expect(deps.confirmModalPromise).toHaveBeenCalledWith(
expect.any(String), expect.any(String),
expect.any(String), expect.any(String),
expect.any(String), expect.any(String),
overlays overlays,
expect.objectContaining({
analytics: expect.any(Object),
i18n: expect.any(Object),
theme: expect.any(Object),
})
); );
}); });
@ -60,7 +83,13 @@ describe('saveWithConfirmation', () => {
opt && opt.overwrite ? Promise.resolve({} as any) : Promise.reject({ res: { status: 409 } }) opt && opt.overwrite ? Promise.resolve({} as any) : Promise.reject({ res: { status: 409 } })
); );
await saveWithConfirmation(source, savedObject, options, { savedObjectsClient, overlays }); await saveWithConfirmation(
source,
savedObject,
options,
{ savedObjectsClient, overlays },
startServices
);
expect(savedObjectsClient.create).toHaveBeenLastCalledWith(savedObject.getEsType(), source, { expect(savedObjectsClient.create).toHaveBeenLastCalledWith(savedObject.getEsType(), source, {
overwrite: true, overwrite: true,
...options, ...options,
@ -73,10 +102,16 @@ describe('saveWithConfirmation', () => {
expect.assertions(1); expect.assertions(1);
await expect( await expect(
saveWithConfirmation(source, savedObject, options, { saveWithConfirmation(
savedObjectsClient, source,
overlays, savedObject,
}) options,
{
savedObjectsClient,
overlays,
},
startServices
)
).rejects.toThrow(OVERWRITE_REJECTED); ).rejects.toThrow(OVERWRITE_REJECTED);
}); });
}); });

View file

@ -15,6 +15,7 @@ import {
SavedObjectsClientContract, SavedObjectsClientContract,
} from '@kbn/core/public'; } from '@kbn/core/public';
import { OVERWRITE_REJECTED } from '../../constants'; import { OVERWRITE_REJECTED } from '../../constants';
import type { StartServices } from '../../types';
import { confirmModalPromise } from './confirm_modal_promise'; import { confirmModalPromise } from './confirm_modal_promise';
/** /**
@ -38,7 +39,8 @@ export async function saveWithConfirmation(
displayName: string; displayName: string;
}, },
options: SavedObjectsCreateOptions, options: SavedObjectsCreateOptions,
services: { savedObjectsClient: SavedObjectsClientContract; overlays: OverlayStart } services: { savedObjectsClient: SavedObjectsClientContract; overlays: OverlayStart },
startServices: StartServices
) { ) {
const { savedObjectsClient, overlays } = services; const { savedObjectsClient, overlays } = services;
try { try {
@ -62,7 +64,7 @@ export async function saveWithConfirmation(
defaultMessage: 'Overwrite', defaultMessage: 'Overwrite',
}); });
return confirmModalPromise(confirmMessage, title, confirmButtonText, overlays) return confirmModalPromise(confirmMessage, title, confirmButtonText, overlays, startServices)
.then(() => .then(() =>
savedObjectsClient.create(savedObject.getEsType(), source, { savedObjectsClient.create(savedObject.getEsType(), source, {
overwrite: true, overwrite: true,

View file

@ -15,7 +15,12 @@ import {
} from '../types'; } from '../types';
import { SavedObjectDecorator } from './decorators'; import { SavedObjectDecorator } from './decorators';
import { coreMock } from '@kbn/core/public/mocks'; import {
analyticsServiceMock,
coreMock,
i18nServiceMock,
themeServiceMock,
} from '@kbn/core/public/mocks';
import { dataPluginMock, createSearchSourceMock } from '@kbn/data-plugin/public/mocks'; import { dataPluginMock, createSearchSourceMock } from '@kbn/data-plugin/public/mocks';
import { createStubIndexPattern } from '@kbn/data-plugin/common/stubs'; import { createStubIndexPattern } from '@kbn/data-plugin/common/stubs';
import { SavedObjectAttributes, SimpleSavedObject } from '@kbn/core/public'; import { SavedObjectAttributes, SimpleSavedObject } from '@kbn/core/public';
@ -27,6 +32,11 @@ describe('Saved Object', () => {
const dataStartMock = dataPluginMock.createStartContract(); const dataStartMock = dataPluginMock.createStartContract();
const saveOptionsMock = {} as SavedObjectSaveOpts; const saveOptionsMock = {} as SavedObjectSaveOpts;
const savedObjectsClientStub = startMock.savedObjects.client; const savedObjectsClientStub = startMock.savedObjects.client;
const startServices = {
analytics: analyticsServiceMock.createAnalyticsServiceStart(),
i18n: i18nServiceMock.createStartContract(),
theme: themeServiceMock.createStartContract(),
};
let decoratorRegistry: ReturnType<typeof savedObjectsDecoratorRegistryMock.create>; let decoratorRegistry: ReturnType<typeof savedObjectsDecoratorRegistryMock.create>;
let SavedObjectClass: new (config: SavedObjectConfig) => SavedObject; let SavedObjectClass: new (config: SavedObjectConfig) => SavedObject;
@ -104,6 +114,7 @@ describe('Saved Object', () => {
}, },
}, },
} as unknown as SavedObjectKibanaServices, } as unknown as SavedObjectKibanaServices,
startServices,
decoratorRegistry decoratorRegistry
); );
}; };
@ -660,6 +671,7 @@ describe('Saved Object', () => {
...dataStartMock.search, ...dataStartMock.search,
}, },
} as unknown as SavedObjectKibanaServices, } as unknown as SavedObjectKibanaServices,
startServices,
decoratorRegistry decoratorRegistry
); );
const savedObject = new SavedObjectClass({ type: 'dashboard', searchSource: true }); const savedObject = new SavedObjectClass({ type: 'dashboard', searchSource: true });

View file

@ -16,12 +16,13 @@
* This class seems to interface with ES primarily through the es Angular * This class seems to interface with ES primarily through the es Angular
* service and the saved object api. * service and the saved object api.
*/ */
import { SavedObject, SavedObjectConfig, SavedObjectKibanaServices } from '../types'; import { SavedObject, SavedObjectConfig, SavedObjectKibanaServices, StartServices } from '../types';
import { ISavedObjectDecoratorRegistry } from './decorators'; import { ISavedObjectDecoratorRegistry } from './decorators';
import { buildSavedObject } from './helpers/build_saved_object'; import { buildSavedObject } from './helpers/build_saved_object';
export function createSavedObjectClass( export function createSavedObjectClass(
services: SavedObjectKibanaServices, services: SavedObjectKibanaServices,
startServices: StartServices,
decoratorRegistry: ISavedObjectDecoratorRegistry decoratorRegistry: ISavedObjectDecoratorRegistry
) { ) {
/** /**
@ -35,7 +36,13 @@ export function createSavedObjectClass(
constructor(config: SavedObjectConfig = {}) { constructor(config: SavedObjectConfig = {}) {
// @ts-ignore // @ts-ignore
const self: SavedObject = this; const self: SavedObject = this;
buildSavedObject(self, config, services, decoratorRegistry.getOrderedDecorators(services)); buildSavedObject(
self,
config,
services,
startServices,
decoratorRegistry.getOrderedDecorators(services)
);
} }
} }

View file

@ -8,6 +8,7 @@
import { import {
ChromeStart, ChromeStart,
CoreStart,
OverlayStart, OverlayStart,
SavedObjectsClientContract, SavedObjectsClientContract,
SavedObjectAttributes, SavedObjectAttributes,
@ -68,6 +69,8 @@ export interface SavedObjectKibanaServices {
overlays: OverlayStart; overlays: OverlayStart;
} }
export type StartServices = Pick<CoreStart, 'analytics' | 'i18n' | 'theme'>;
export interface SavedObjectAttributesAndRefs { export interface SavedObjectAttributesAndRefs {
attributes: SavedObjectAttributes; attributes: SavedObjectAttributes;
references: SavedObjectReference[]; references: SavedObjectReference[];

View file

@ -8,12 +8,13 @@
"@kbn/core", "@kbn/core",
"@kbn/data-plugin", "@kbn/data-plugin",
"@kbn/kibana-utils-plugin", "@kbn/kibana-utils-plugin",
"@kbn/kibana-react-plugin",
"@kbn/i18n", "@kbn/i18n",
"@kbn/data-views-plugin", "@kbn/data-views-plugin",
"@kbn/i18n-react", "@kbn/i18n-react",
"@kbn/utility-types", "@kbn/utility-types",
"@kbn/ui-theme", "@kbn/ui-theme",
"@kbn/react-kibana-context-render",
"@kbn/react-kibana-mount",
], ],
"exclude": [ "exclude": [
"target/**/*", "target/**/*",

View file

@ -9,12 +9,11 @@
import React, { lazy, Suspense } from 'react'; import React, { lazy, Suspense } from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import { Router, Routes, Route } from '@kbn/shared-ux-router'; import { Router, Routes, Route } from '@kbn/shared-ux-router';
import { I18nProvider } from '@kbn/i18n-react';
import { i18n } from '@kbn/i18n'; import { i18n } from '@kbn/i18n';
import { EuiLoadingSpinner } from '@elastic/eui'; import { EuiLoadingSpinner } from '@elastic/eui';
import { CoreSetup } from '@kbn/core/public'; import { CoreSetup } from '@kbn/core/public';
import { wrapWithTheme } from '@kbn/kibana-react-plugin/public';
import { ManagementAppMountParams } from '@kbn/management-plugin/public'; import { ManagementAppMountParams } from '@kbn/management-plugin/public';
import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render';
import type { SavedObjectManagementTypeInfo } from '../../common/types'; import type { SavedObjectManagementTypeInfo } from '../../common/types';
import { StartDependencies, SavedObjectsManagementPluginStart } from '../plugin'; import { StartDependencies, SavedObjectsManagementPluginStart } from '../plugin';
import { getAllowedTypes } from '../lib'; import { getAllowedTypes } from '../lib';
@ -37,7 +36,6 @@ export const mountManagementSection = async ({ core, mountParams }: MountParams)
await core.getStartServices(); await core.getStartServices();
const { capabilities } = coreStart.application; const { capabilities } = coreStart.application;
const { element, history, setBreadcrumbs } = mountParams; const { element, history, setBreadcrumbs } = mountParams;
const { theme$ } = core.theme;
if (!allowedObjectTypes) { if (!allowedObjectTypes) {
allowedObjectTypes = await getAllowedTypes(coreStart.http); allowedObjectTypes = await getAllowedTypes(coreStart.http);
@ -56,43 +54,40 @@ export const mountManagementSection = async ({ core, mountParams }: MountParams)
}; };
ReactDOM.render( ReactDOM.render(
wrapWithTheme( <KibanaRenderContextProvider {...coreStart}>
<I18nProvider> <Router history={history}>
<Router history={history}> <Routes>
<Routes> <Route path={'/:type/:id'} exact={true}>
<Route path={'/:type/:id'} exact={true}> <RedirectToHomeIfUnauthorized>
<RedirectToHomeIfUnauthorized> <Suspense fallback={<EuiLoadingSpinner />}>
<Suspense fallback={<EuiLoadingSpinner />}> <SavedObjectsEditionPage
<SavedObjectsEditionPage coreStart={coreStart}
coreStart={coreStart} setBreadcrumbs={setBreadcrumbs}
setBreadcrumbs={setBreadcrumbs} history={history}
history={history} />
/> </Suspense>
</Suspense> </RedirectToHomeIfUnauthorized>
</RedirectToHomeIfUnauthorized> </Route>
</Route> <Route path={'/'} exact={false}>
<Route path={'/'} exact={false}> <RedirectToHomeIfUnauthorized>
<RedirectToHomeIfUnauthorized> <Suspense fallback={<EuiLoadingSpinner />}>
<Suspense fallback={<EuiLoadingSpinner />}> <SavedObjectsTablePage
<SavedObjectsTablePage coreStart={coreStart}
coreStart={coreStart} taggingApi={savedObjectsTaggingOss?.getTaggingApi()}
taggingApi={savedObjectsTaggingOss?.getTaggingApi()} spacesApi={spacesApi}
spacesApi={spacesApi} dataStart={data}
dataStart={data} dataViewsApi={dataViews}
dataViewsApi={dataViews} actionRegistry={pluginStart.actions}
actionRegistry={pluginStart.actions} columnRegistry={pluginStart.columns}
columnRegistry={pluginStart.columns} allowedTypes={allowedObjectTypes}
allowedTypes={allowedObjectTypes} setBreadcrumbs={setBreadcrumbs}
setBreadcrumbs={setBreadcrumbs} />
/> </Suspense>
</Suspense> </RedirectToHomeIfUnauthorized>
</RedirectToHomeIfUnauthorized> </Route>
</Route> </Routes>
</Routes> </Router>
</Router> </KibanaRenderContextProvider>,
</I18nProvider>,
theme$
),
element element
); );

View file

@ -31,6 +31,7 @@
"@kbn/core-saved-objects-api-server", "@kbn/core-saved-objects-api-server",
"@kbn/shared-ux-link-redirect-app", "@kbn/shared-ux-link-redirect-app",
"@kbn/code-editor", "@kbn/code-editor",
"@kbn/react-kibana-context-render",
], ],
"exclude": [ "exclude": [
"target/**/*", "target/**/*",

View file

@ -17,8 +17,7 @@
"security" "security"
], ],
"requiredBundles": [ "requiredBundles": [
"kibanaUtils", "kibanaUtils"
"kibanaReact"
], ],
"extraPublicDirs": [ "extraPublicDirs": [
"common/constants" "common/constants"

View file

@ -8,7 +8,9 @@
import { import {
overlayServiceMock, overlayServiceMock,
analyticsServiceMock,
httpServiceMock, httpServiceMock,
i18nServiceMock,
notificationServiceMock, notificationServiceMock,
themeServiceMock, themeServiceMock,
} from '@kbn/core/public/mocks'; } from '@kbn/core/public/mocks';
@ -76,6 +78,8 @@ export function mockTelemetryNotifications({
return new TelemetryNotifications({ return new TelemetryNotifications({
http: httpServiceMock.createSetupContract(), http: httpServiceMock.createSetupContract(),
overlays: overlayServiceMock.createStartContract(), overlays: overlayServiceMock.createStartContract(),
analytics: analyticsServiceMock.createAnalyticsServiceStart(),
i18n: i18nServiceMock.createStartContract(),
theme: themeServiceMock.createStartContract(), theme: themeServiceMock.createStartContract(),
telemetryService, telemetryService,
telemetryConstants: mockTelemetryConstants(), telemetryConstants: mockTelemetryConstants(),

View file

@ -229,7 +229,7 @@ export class TelemetryPlugin
} }
public start( public start(
{ analytics, http, overlays, theme, application, docLinks }: CoreStart, { analytics, http, overlays, application, docLinks, ...startServices }: CoreStart,
{ screenshotMode }: TelemetryPluginStartDependencies { screenshotMode }: TelemetryPluginStartDependencies
): TelemetryPluginStart { ): TelemetryPluginStart {
if (!this.telemetryService) { if (!this.telemetryService) {
@ -243,9 +243,10 @@ export class TelemetryPlugin
const telemetryNotifications = new TelemetryNotifications({ const telemetryNotifications = new TelemetryNotifications({
http, http,
overlays, overlays,
theme,
telemetryService: this.telemetryService, telemetryService: this.telemetryService,
telemetryConstants, telemetryConstants,
analytics,
...startServices,
}); });
this.telemetryNotifications = telemetryNotifications; this.telemetryNotifications = telemetryNotifications;

View file

@ -7,7 +7,13 @@
*/ */
import { renderOptInStatusNoticeBanner } from './render_opt_in_status_notice_banner'; import { renderOptInStatusNoticeBanner } from './render_opt_in_status_notice_banner';
import { overlayServiceMock, httpServiceMock, themeServiceMock } from '@kbn/core/public/mocks'; import {
analyticsServiceMock,
httpServiceMock,
i18nServiceMock,
overlayServiceMock,
themeServiceMock,
} from '@kbn/core/public/mocks';
import { mockTelemetryConstants, mockTelemetryService } from '../../mocks'; import { mockTelemetryConstants, mockTelemetryService } from '../../mocks';
describe('renderOptInStatusNoticeBanner', () => { describe('renderOptInStatusNoticeBanner', () => {
@ -15,6 +21,8 @@ describe('renderOptInStatusNoticeBanner', () => {
const bannerID = 'brucer-wayne'; const bannerID = 'brucer-wayne';
const overlays = overlayServiceMock.createStartContract(); const overlays = overlayServiceMock.createStartContract();
const mockHttp = httpServiceMock.createStartContract(); const mockHttp = httpServiceMock.createStartContract();
const analytics = analyticsServiceMock.createAnalyticsServiceStart();
const i18n = i18nServiceMock.createStartContract();
const theme = themeServiceMock.createStartContract(); const theme = themeServiceMock.createStartContract();
const telemetryConstants = mockTelemetryConstants(); const telemetryConstants = mockTelemetryConstants();
const telemetryService = mockTelemetryService(); const telemetryService = mockTelemetryService();
@ -24,6 +32,8 @@ describe('renderOptInStatusNoticeBanner', () => {
http: mockHttp, http: mockHttp,
onSeen: jest.fn(), onSeen: jest.fn(),
overlays, overlays,
analytics,
i18n,
theme, theme,
telemetryConstants, telemetryConstants,
telemetryService, telemetryService,

View file

@ -7,16 +7,15 @@
*/ */
import React from 'react'; import React from 'react';
import type { HttpStart, OverlayStart, ThemeServiceStart } from '@kbn/core/public'; import type { CoreStart, HttpStart, OverlayStart } from '@kbn/core/public';
import { toMountPoint } from '@kbn/kibana-react-plugin/public'; import { toMountPoint } from '@kbn/react-kibana-mount';
import { withSuspense } from '@kbn/shared-ux-utility'; import { withSuspense } from '@kbn/shared-ux-utility';
import { TelemetryService } from '..'; import { TelemetryService } from '..';
import type { TelemetryConstants } from '../..'; import type { TelemetryConstants } from '../..';
interface RenderBannerConfig { interface RenderBannerConfig extends Pick<CoreStart, 'analytics' | 'i18n' | 'theme'> {
http: HttpStart; http: HttpStart;
overlays: OverlayStart; overlays: OverlayStart;
theme: ThemeServiceStart;
onSeen: () => void; onSeen: () => void;
telemetryConstants: TelemetryConstants; telemetryConstants: TelemetryConstants;
telemetryService: TelemetryService; telemetryService: TelemetryService;
@ -26,9 +25,9 @@ export function renderOptInStatusNoticeBanner({
onSeen, onSeen,
overlays, overlays,
http, http,
theme,
telemetryConstants, telemetryConstants,
telemetryService, telemetryService,
...startServices
}: RenderBannerConfig) { }: RenderBannerConfig) {
const OptedInNoticeBannerLazy = withSuspense( const OptedInNoticeBannerLazy = withSuspense(
React.lazy(() => React.lazy(() =>
@ -47,7 +46,7 @@ export function renderOptInStatusNoticeBanner({
telemetryConstants={telemetryConstants} telemetryConstants={telemetryConstants}
telemetryService={telemetryService} telemetryService={telemetryService}
/>, />,
{ theme$: theme.theme$ } startServices
); );
const bannerId = overlays.banners.add(mount, 10000); const bannerId = overlays.banners.add(mount, 10000);

View file

@ -6,15 +6,15 @@
* Side Public License, v 1. * Side Public License, v 1.
*/ */
import type { HttpStart, OverlayStart, ThemeServiceStart } from '@kbn/core/public'; import type { CoreStart, HttpStart, OverlayStart } from '@kbn/core/public';
import type { TelemetryService } from '../telemetry_service'; import type { TelemetryService } from '../telemetry_service';
import type { TelemetryConstants } from '../..'; import type { TelemetryConstants } from '../..';
import { renderOptInStatusNoticeBanner } from './render_opt_in_status_notice_banner'; import { renderOptInStatusNoticeBanner } from './render_opt_in_status_notice_banner';
interface TelemetryNotificationsConstructor { interface TelemetryNotificationsConstructor
extends Pick<CoreStart, 'analytics' | 'i18n' | 'theme'> {
http: HttpStart; http: HttpStart;
overlays: OverlayStart; overlays: OverlayStart;
theme: ThemeServiceStart;
telemetryService: TelemetryService; telemetryService: TelemetryService;
telemetryConstants: TelemetryConstants; telemetryConstants: TelemetryConstants;
} }
@ -25,7 +25,7 @@ interface TelemetryNotificationsConstructor {
export class TelemetryNotifications { export class TelemetryNotifications {
private readonly http: HttpStart; private readonly http: HttpStart;
private readonly overlays: OverlayStart; private readonly overlays: OverlayStart;
private readonly theme: ThemeServiceStart; private readonly startServices: Pick<CoreStart, 'analytics' | 'i18n' | 'theme'>;
private readonly telemetryConstants: TelemetryConstants; private readonly telemetryConstants: TelemetryConstants;
private readonly telemetryService: TelemetryService; private readonly telemetryService: TelemetryService;
private optInStatusNoticeBannerId?: string; private optInStatusNoticeBannerId?: string;
@ -33,14 +33,14 @@ export class TelemetryNotifications {
constructor({ constructor({
http, http,
overlays, overlays,
theme,
telemetryService, telemetryService,
telemetryConstants, telemetryConstants,
...startServices
}: TelemetryNotificationsConstructor) { }: TelemetryNotificationsConstructor) {
this.telemetryService = telemetryService; this.telemetryService = telemetryService;
this.http = http; this.http = http;
this.overlays = overlays; this.overlays = overlays;
this.theme = theme; this.startServices = startServices;
this.telemetryConstants = telemetryConstants; this.telemetryConstants = telemetryConstants;
} }
@ -61,9 +61,9 @@ export class TelemetryNotifications {
http: this.http, http: this.http,
onSeen: this.setOptInStatusNoticeSeen, onSeen: this.setOptInStatusNoticeSeen,
overlays: this.overlays, overlays: this.overlays,
theme: this.theme,
telemetryConstants: this.telemetryConstants, telemetryConstants: this.telemetryConstants,
telemetryService: this.telemetryService, telemetryService: this.telemetryService,
...this.startServices,
}); });
this.optInStatusNoticeBannerId = bannerId; this.optInStatusNoticeBannerId = bannerId;

View file

@ -15,7 +15,6 @@
"kbn_references": [ "kbn_references": [
"@kbn/core", "@kbn/core",
"@kbn/home-plugin", "@kbn/home-plugin",
"@kbn/kibana-react-plugin",
"@kbn/kibana-utils-plugin", "@kbn/kibana-utils-plugin",
"@kbn/screenshot-mode-plugin", "@kbn/screenshot-mode-plugin",
"@kbn/telemetry-collection-manager-plugin", "@kbn/telemetry-collection-manager-plugin",
@ -36,6 +35,7 @@
"@kbn/core-http-browser", "@kbn/core-http-browser",
"@kbn/core-http-server", "@kbn/core-http-server",
"@kbn/analytics-collection-utils", "@kbn/analytics-collection-utils",
"@kbn/react-kibana-mount",
"@kbn/core-node-server", "@kbn/core-node-server",
], ],
"exclude": [ "exclude": [

View file

@ -57,7 +57,7 @@ export class CloudChatPlugin implements Plugin<void, void, CloudChatSetupDeps, C
); );
} }
public start(core: CoreStart, { cloud }: CloudChatStartDeps) { public start(core: CoreStart) {
const CloudChatContextProvider: FC = ({ children }) => { const CloudChatContextProvider: FC = ({ children }) => {
// There's a risk that the request for chat config will take too much time to complete, and the provider // There's a risk that the request for chat config will take too much time to complete, and the provider
// will maintain a stale value. To avoid this, we'll use an Observable. // will maintain a stale value. To avoid this, we'll use an Observable.
@ -67,7 +67,7 @@ export class CloudChatPlugin implements Plugin<void, void, CloudChatSetupDeps, C
function ConnectedChat(props: { chatVariant: ChatVariant }) { function ConnectedChat(props: { chatVariant: ChatVariant }) {
return ( return (
<CloudChatContextProvider> <CloudChatContextProvider>
<KibanaRenderContextProvider theme={core.theme} i18n={core.i18n}> <KibanaRenderContextProvider {...core}>
<ChatExperimentSwitcher <ChatExperimentSwitcher
location$={core.application.currentLocation$} location$={core.application.currentLocation$}
variant={props.chatVariant} variant={props.chatVariant}

View file

@ -10,8 +10,6 @@
"xpack", "xpack",
"licensing" "licensing"
], ],
"requiredBundles": [ "requiredBundles": []
"kibanaReact"
]
} }
} }

View file

@ -7,9 +7,10 @@
import React from 'react'; import React from 'react';
import { EuiCallOut } from '@elastic/eui'; import { EuiCallOut } from '@elastic/eui';
import { CoreStart } from '@kbn/core/public';
import { FormattedMessage } from '@kbn/i18n-react'; import { FormattedMessage } from '@kbn/i18n-react';
import { toMountPoint } from '@kbn/kibana-react-plugin/public'; import { toMountPoint } from '@kbn/react-kibana-mount';
interface Props { interface Props {
type: string; type: string;
@ -46,5 +47,7 @@ const ExpiredBanner: React.FunctionComponent<Props> = (props) => (
</EuiCallOut> </EuiCallOut>
); );
export const mountExpiredBanner = (props: Props) => type MountProps = Props & Pick<CoreStart, 'analytics' | 'i18n' | 'theme'>;
toMountPoint(<ExpiredBanner type={props.type!} uploadUrl={props.uploadUrl} />);
export const mountExpiredBanner = ({ type, uploadUrl, ...startServices }: MountProps) =>
toMountPoint(<ExpiredBanner type={type!} uploadUrl={uploadUrl} />, startServices);

View file

@ -406,10 +406,15 @@ describe('licensing plugin', () => {
expect(coreStart.overlays.banners.add).toHaveBeenCalledTimes(1); expect(coreStart.overlays.banners.add).toHaveBeenCalledTimes(1);
await refresh(); await refresh();
expect(coreStart.overlays.banners.add).toHaveBeenCalledTimes(1); expect(coreStart.overlays.banners.add).toHaveBeenCalledTimes(1);
expect(mountExpiredBannerMock).toHaveBeenCalledWith({ expect(mountExpiredBannerMock).toHaveBeenCalledWith(
type: 'gold', expect.objectContaining({
uploadUrl: '/app/management/stack/license_management/upload_license', type: 'gold',
}); uploadUrl: '/app/management/stack/license_management/upload_license',
analytics: expect.any(Object),
i18n: expect.any(Object),
theme: expect.any(Object),
})
);
}); });
}); });

View file

@ -46,7 +46,7 @@ export class LicensingPlugin implements Plugin<LicensingPluginSetup, LicensingPl
private featureUsage = new FeatureUsageService(); private featureUsage = new FeatureUsageService();
constructor( constructor(
context: PluginInitializerContext, _context: PluginInitializerContext,
private readonly storage: Storage = sessionStorage private readonly storage: Storage = sessionStorage
) {} ) {}
@ -169,13 +169,15 @@ export class LicensingPlugin implements Plugin<LicensingPluginSetup, LicensingPl
}; };
private showExpiredBanner(license: ILicense) { private showExpiredBanner(license: ILicense) {
const uploadUrl = this.coreStart!.http.basePath.prepend( const coreStart = this.coreStart!;
const uploadUrl = coreStart.http.basePath.prepend(
'/app/management/stack/license_management/upload_license' '/app/management/stack/license_management/upload_license'
); );
this.coreStart!.overlays.banners.add( coreStart.overlays.banners.add(
mountExpiredBanner({ mountExpiredBanner({
type: license.type!, type: license.type!,
uploadUrl, uploadUrl,
...coreStart,
}) })
); );
} }

View file

@ -7,14 +7,14 @@
"include": ["public/**/*", "server/**/*", "common/**/*"], "include": ["public/**/*", "server/**/*", "common/**/*"],
"kbn_references": [ "kbn_references": [
"@kbn/core", "@kbn/core",
"@kbn/kibana-react-plugin",
"@kbn/i18n-react", "@kbn/i18n-react",
"@kbn/utility-types", "@kbn/utility-types",
"@kbn/config-schema", "@kbn/config-schema",
"@kbn/std", "@kbn/std",
"@kbn/i18n", "@kbn/i18n",
"@kbn/analytics-client", "@kbn/analytics-client",
"@kbn/logging-mocks" "@kbn/logging-mocks",
"@kbn/react-kibana-mount"
], ],
"exclude": ["target/**/*"] "exclude": ["target/**/*"]
} }