[Discover] Remove usage of deprecated React rendering utilities (#181623)

## Summary

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

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 **Discover**. Due to related type-safety
needs there are also some minor changes in the **Data** plugin.

<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>
Co-authored-by: Julia Rechkunova <julia.rechkunova@gmail.com>
This commit is contained in:
Tim Sullivan 2024-04-26 11:48:06 -07:00 committed by GitHub
parent a05355713e
commit bc87224b41
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
42 changed files with 468 additions and 584 deletions

View file

@ -16,8 +16,6 @@
"share", "share",
"unifiedSearch" "unifiedSearch"
], ],
"requiredBundles": [ "requiredBundles": []
"kibanaReact"
]
} }
} }

View file

@ -39,7 +39,7 @@ const LINKS: ExampleLink[] = [
]; ];
export const renderApp = ( export const renderApp = (
{ notifications, savedObjects, http, application }: CoreStart, { notifications, savedObjects, http, application, ...startServices }: CoreStart,
{ data, navigation, unifiedSearch }: AppPluginStartDependencies, { data, navigation, unifiedSearch }: AppPluginStartDependencies,
{ element, history }: AppMountParameters { element, history }: AppMountParameters
) => { ) => {
@ -60,6 +60,7 @@ export const renderApp = (
data={data} data={data}
http={http} http={http}
unifiedSearch={unifiedSearch} unifiedSearch={unifiedSearch}
{...startServices}
/> />
</Route> </Route>
<Route path={LINKS[1].path}> <Route path={LINKS[1].path}>

View file

@ -36,7 +36,7 @@ import type { DataView, DataViewField } from '@kbn/data-views-plugin/public';
import { i18n } from '@kbn/i18n'; import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n-react'; import { FormattedMessage } from '@kbn/i18n-react';
import { RequestAdapter } from '@kbn/inspector-plugin/common'; import { RequestAdapter } from '@kbn/inspector-plugin/common';
import { toMountPoint } from '@kbn/kibana-react-plugin/public'; import { toMountPoint } from '@kbn/react-kibana-mount';
import { NavigationPublicPluginStart } from '@kbn/navigation-plugin/public'; import { NavigationPublicPluginStart } from '@kbn/navigation-plugin/public';
import { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public'; import { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public';
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
@ -44,9 +44,8 @@ import { lastValueFrom } from 'rxjs';
import { PLUGIN_ID, PLUGIN_NAME, SERVER_SEARCH_ROUTE_PATH } from '../../common'; import { PLUGIN_ID, PLUGIN_NAME, SERVER_SEARCH_ROUTE_PATH } from '../../common';
import { IMyStrategyResponse } from '../../common/types'; import { IMyStrategyResponse } from '../../common/types';
interface SearchExamplesAppDeps { interface SearchExamplesAppDeps
notifications: CoreStart['notifications']; extends Pick<CoreStart, 'notifications' | 'http' | 'analytics' | 'i18n' | 'theme'> {
http: CoreStart['http'];
navigation: NavigationPublicPluginStart; navigation: NavigationPublicPluginStart;
data: DataPublicPluginStart; data: DataPublicPluginStart;
unifiedSearch: UnifiedSearchPublicPluginStart; unifiedSearch: UnifiedSearchPublicPluginStart;
@ -86,6 +85,7 @@ export const SearchExamplesApp = ({
navigation, navigation,
data, data,
unifiedSearch, unifiedSearch,
...startServices
}: SearchExamplesAppDeps) => { }: SearchExamplesAppDeps) => {
const { IndexPatternSelect } = unifiedSearch.ui; const { IndexPatternSelect } = unifiedSearch.ui;
const [getCool, setGetCool] = useState<boolean>(false); const [getCool, setGetCool] = useState<boolean>(false);
@ -234,7 +234,7 @@ export const SearchExamplesApp = ({
notifications.toasts.addSuccess( notifications.toasts.addSuccess(
{ {
title: 'Query result', title: 'Query result',
text: toMountPoint(message), text: toMountPoint(message, startServices),
}, },
{ {
toastLifeTimeMs: 300000, toastLifeTimeMs: 300000,
@ -243,7 +243,7 @@ export const SearchExamplesApp = ({
if (res.warning) { if (res.warning) {
notifications.toasts.addWarning({ notifications.toasts.addWarning({
title: 'Warning', title: 'Warning',
text: toMountPoint(res.warning), text: toMountPoint(res.warning, startServices),
}); });
} }
} }
@ -337,7 +337,7 @@ export const SearchExamplesApp = ({
notifications.toasts.addSuccess( notifications.toasts.addSuccess(
{ {
title: 'Query result', title: 'Query result',
text: toMountPoint(message), text: toMountPoint(message, startServices),
}, },
{ {
toastLifeTimeMs: 300000, toastLifeTimeMs: 300000,

View file

@ -20,7 +20,6 @@
"@kbn/data-views-plugin", "@kbn/data-views-plugin",
"@kbn/inspector-plugin", "@kbn/inspector-plugin",
"@kbn/kibana-utils-plugin", "@kbn/kibana-utils-plugin",
"@kbn/kibana-react-plugin",
"@kbn/navigation-plugin", "@kbn/navigation-plugin",
"@kbn/share-plugin", "@kbn/share-plugin",
"@kbn/developer-examples-plugin", "@kbn/developer-examples-plugin",
@ -34,5 +33,6 @@
"@kbn/shared-ux-router", "@kbn/shared-ux-router",
"@kbn/search-response-warnings", "@kbn/search-response-warnings",
"@kbn/shared-ux-link-redirect-app", "@kbn/shared-ux-link-redirect-app",
"@kbn/react-kibana-mount",
] ]
} }

View file

@ -7,17 +7,16 @@
*/ */
import { estypes } from '@elastic/elasticsearch'; import { estypes } from '@elastic/elasticsearch';
import type { ThemeServiceStart } from '@kbn/core/public'; import { coreMock } from '@kbn/core/public/mocks';
import type { I18nStart } from '@kbn/core-i18n-browser';
import { notificationServiceMock } from '@kbn/core-notifications-browser-mocks';
import type { Start as InspectorStart, RequestAdapter } from '@kbn/inspector-plugin/public'; import type { Start as InspectorStart, RequestAdapter } from '@kbn/inspector-plugin/public';
import { handleWarnings } from './handle_warnings'; import { handleWarnings } from './handle_warnings';
describe('handleWarnings', () => { describe('handleWarnings', () => {
const notifications = notificationServiceMock.createStartContract(); const coreStart = coreMock.createStart();
const addWarningSpy = jest.spyOn(coreStart.notifications.toasts, 'addWarning');
beforeEach(() => { beforeEach(() => {
notifications.toasts.addWarning.mockClear(); addWarningSpy.mockClear();
}); });
it('should not show notifications when there are no warnings', () => { it('should not show notifications when there are no warnings', () => {
@ -34,14 +33,12 @@ describe('handleWarnings', () => {
}, },
} as estypes.SearchResponse, } as estypes.SearchResponse,
services: { services: {
i18n: {} as unknown as I18nStart,
inspector: {} as unknown as InspectorStart, inspector: {} as unknown as InspectorStart,
notifications, ...coreStart,
theme: {} as unknown as ThemeServiceStart,
}, },
}); });
expect(notifications.toasts.addWarning).toBeCalledTimes(0); expect(addWarningSpy).toBeCalledTimes(0);
}); });
it('should show notifications for warnings when there is no callback', () => { it('should show notifications for warnings when there is no callback', () => {
@ -57,14 +54,12 @@ describe('handleWarnings', () => {
hits: { hits: [] }, hits: { hits: [] },
} as estypes.SearchResponse, } as estypes.SearchResponse,
services: { services: {
i18n: {} as unknown as I18nStart,
inspector: {} as unknown as InspectorStart, inspector: {} as unknown as InspectorStart,
notifications, ...coreStart,
theme: {} as unknown as ThemeServiceStart,
}, },
}); });
expect(notifications.toasts.addWarning).toBeCalledTimes(1); expect(addWarningSpy).toBeCalledTimes(1);
}); });
it('should show notifications for warnings not handled by callback', () => { it('should show notifications for warnings not handled by callback', () => {
@ -82,15 +77,13 @@ describe('handleWarnings', () => {
hits: { hits: [] }, hits: { hits: [] },
} as estypes.SearchResponse, } as estypes.SearchResponse,
services: { services: {
i18n: {} as unknown as I18nStart,
inspector: {} as unknown as InspectorStart, inspector: {} as unknown as InspectorStart,
notifications, ...coreStart,
theme: {} as unknown as ThemeServiceStart,
}, },
}); });
expect(callbackMock).toBeCalledTimes(1); expect(callbackMock).toBeCalledTimes(1);
expect(notifications.toasts.addWarning).toBeCalledTimes(1); expect(addWarningSpy).toBeCalledTimes(1);
}); });
it('should not show notifications for warnings handled by callback', () => { it('should not show notifications for warnings handled by callback', () => {
@ -108,14 +101,12 @@ describe('handleWarnings', () => {
hits: { hits: [] }, hits: { hits: [] },
} as estypes.SearchResponse, } as estypes.SearchResponse,
services: { services: {
i18n: {} as unknown as I18nStart,
inspector: {} as unknown as InspectorStart, inspector: {} as unknown as InspectorStart,
notifications, ...coreStart,
theme: {} as unknown as ThemeServiceStart,
}, },
}); });
expect(callbackMock).toBeCalledTimes(1); expect(callbackMock).toBeCalledTimes(1);
expect(notifications.toasts.addWarning).toBeCalledTimes(0); expect(addWarningSpy).toBeCalledTimes(0);
}); });
}); });

View file

@ -9,7 +9,11 @@
import React from 'react'; import React from 'react';
import { EuiButtonEmpty, EuiText } from '@elastic/eui'; import { EuiButtonEmpty, EuiText } from '@elastic/eui';
import { estypes } from '@elastic/elasticsearch'; import { estypes } from '@elastic/elasticsearch';
import type { NotificationsStart, ThemeServiceStart } from '@kbn/core/public'; import type {
AnalyticsServiceStart,
NotificationsStart,
ThemeServiceStart,
} from '@kbn/core/public';
import { toMountPoint } from '@kbn/react-kibana-mount'; import { toMountPoint } from '@kbn/react-kibana-mount';
import type { I18nStart } from '@kbn/core-i18n-browser'; import type { I18nStart } from '@kbn/core-i18n-browser';
import type { Start as InspectorStart, RequestAdapter } from '@kbn/inspector-plugin/public'; import type { Start as InspectorStart, RequestAdapter } from '@kbn/inspector-plugin/public';
@ -26,6 +30,7 @@ import {
} from './components/search_response_warnings/i18n_utils'; } from './components/search_response_warnings/i18n_utils';
interface Services { interface Services {
analytics: AnalyticsServiceStart;
i18n: I18nStart; i18n: I18nStart;
inspector: InspectorStart; inspector: InspectorStart;
notifications: NotificationsStart; notifications: NotificationsStart;
@ -94,7 +99,7 @@ export function handleWarnings({
{viewDetailsLabel} {viewDetailsLabel}
</EuiButtonEmpty> </EuiButtonEmpty>
</>, </>,
{ theme: services.theme, i18n: services.i18n } services
), ),
}); });
} }

View file

@ -10,7 +10,6 @@
"@kbn/core", "@kbn/core",
"@kbn/react-kibana-mount", "@kbn/react-kibana-mount",
"@kbn/core-i18n-browser", "@kbn/core-i18n-browser",
"@kbn/core-notifications-browser-mocks",
], ],
"exclude": ["target/**/*"] "exclude": ["target/**/*"]
} }

View file

@ -21,7 +21,7 @@ import { RequestAdapter } from '@kbn/inspector-plugin/common/adapters/request';
import { DataViewsContract } from '@kbn/data-views-plugin/common'; import { DataViewsContract } from '@kbn/data-views-plugin/common';
import { ExpressionsSetup } from '@kbn/expressions-plugin/public'; import { ExpressionsSetup } from '@kbn/expressions-plugin/public';
import { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; import { FieldFormatsStart } from '@kbn/field-formats-plugin/public';
import { toMountPoint } from '@kbn/kibana-react-plugin/public'; import { toMountPoint } from '@kbn/react-kibana-mount';
import { Storage } from '@kbn/kibana-utils-plugin/public'; import { Storage } from '@kbn/kibana-utils-plugin/public';
import { ManagementSetup } from '@kbn/management-plugin/public'; import { ManagementSetup } from '@kbn/management-plugin/public';
import { ScreenshotModePluginStart } from '@kbn/screenshot-mode-plugin/public'; import { ScreenshotModePluginStart } from '@kbn/screenshot-mode-plugin/public';
@ -226,7 +226,16 @@ export class SearchService implements Plugin<ISearchSetup, ISearchStart> {
} }
public start( public start(
{ http, theme, uiSettings, chrome, application, notifications, i18n: i18nStart }: CoreStart, {
analytics,
http,
theme,
uiSettings,
chrome,
application,
notifications,
i18n: i18nStart,
}: CoreStart,
{ {
fieldFormats, fieldFormats,
indexPatterns, indexPatterns,
@ -245,6 +254,7 @@ export class SearchService implements Plugin<ISearchSetup, ISearchStart> {
const aggs = this.aggsService.start({ fieldFormats, indexPatterns }); const aggs = this.aggsService.start({ fieldFormats, indexPatterns });
const warningsServices = { const warningsServices = {
analytics,
i18n: i18nStart, i18n: i18nStart,
inspector, inspector,
notifications, notifications,
@ -303,7 +313,7 @@ export class SearchService implements Plugin<ISearchSetup, ISearchStart> {
tourDisabled: screenshotMode.isScreenshotMode(), tourDisabled: screenshotMode.isScreenshotMode(),
}) })
), ),
{ theme$: theme.theme$ } { analytics, i18n: i18nStart, theme }
), ),
}); });
} }

View file

@ -54,7 +54,8 @@
"@kbn/es-types", "@kbn/es-types",
"@kbn/code-editor", "@kbn/code-editor",
"@kbn/core-test-helpers-model-versions", "@kbn/core-test-helpers-model-versions",
"@kbn/esql-utils" "@kbn/esql-utils",
"@kbn/react-kibana-mount"
], ],
"exclude": [ "exclude": [
"target/**/*", "target/**/*",

View file

@ -80,7 +80,7 @@ export const getEditorOpener =
showManagementLink={Boolean(editData && editData.isPersisted())} showManagementLink={Boolean(editData && editData.isPersisted())}
/> />
</KibanaReactContextProvider>, </KibanaReactContextProvider>,
{ theme: core.theme, i18n: core.i18n } core
), ),
{ {
hideCloseButton: true, hideCloseButton: true,

View file

@ -7,8 +7,8 @@
*/ */
import React from 'react'; import React from 'react';
jest.mock('@kbn/kibana-react-plugin/public', () => { jest.mock('@kbn/react-kibana-mount', () => {
const original = jest.requireActual('@kbn/kibana-react-plugin/public'); const original = jest.requireActual('@kbn/react-kibana-mount');
return { return {
...original, ...original,
@ -43,7 +43,7 @@ describe('DataViewEditorPlugin', () => {
}); });
test('should expose a handler to open the data view field editor', async () => { test('should expose a handler to open the data view field editor', async () => {
const startApi = await plugin.start(coreStart, pluginStart); const startApi = plugin.start(coreStart, pluginStart);
expect(startApi.openEditor).toBeDefined(); expect(startApi.openEditor).toBeDefined();
}); });
@ -58,13 +58,13 @@ describe('DataViewEditorPlugin', () => {
openFlyout, openFlyout,
}, },
}; };
const { openEditor } = await plugin.start(coreStartMocked, pluginStart); const { openEditor } = plugin.start(coreStartMocked, pluginStart);
openEditor({ onSave: onSaveSpy }); openEditor({ onSave: onSaveSpy });
expect(openFlyout).toHaveBeenCalled(); expect(openFlyout).toHaveBeenCalled();
const [[{ __reactMount__ }]] = openFlyout.mock.calls; const [[__reactMount__]] = openFlyout.mock.calls;
expect(__reactMount__.props.children.type).toBe(DataViewEditorLazy); expect(__reactMount__.props.children.type).toBe(DataViewEditorLazy);
// We force call the "onSave" prop from the <RuntimeFieldEditorFlyoutContent /> component // We force call the "onSave" prop from the <RuntimeFieldEditorFlyoutContent /> component
@ -77,7 +77,7 @@ describe('DataViewEditorPlugin', () => {
}); });
test('should return a handler to close the flyout', async () => { test('should return a handler to close the flyout', async () => {
const { openEditor } = await plugin.start(coreStart, pluginStart); const { openEditor } = plugin.start(coreStart, pluginStart);
const closeEditorHandler = openEditor({ onSave: noop }); const closeEditorHandler = openEditor({ onSave: noop });
expect(typeof closeEditorHandler).toBe('function'); expect(typeof closeEditorHandler).toBe('function');

View file

@ -13,7 +13,9 @@ export type {
} from '@kbn/data-plugin/public'; } from '@kbn/data-plugin/public';
export type { DataView, DataViewField, DataViewSpec } from '@kbn/data-views-plugin/public'; export type { DataView, DataViewField, DataViewSpec } from '@kbn/data-views-plugin/public';
export { createKibanaReactContext, toMountPoint, useKibana } from '@kbn/kibana-react-plugin/public'; export { createKibanaReactContext, useKibana } from '@kbn/kibana-react-plugin/public';
export { toMountPoint } from '@kbn/react-kibana-mount';
export { CodeEditor } from '@kbn/code-editor'; export { CodeEditor } from '@kbn/code-editor';

View file

@ -114,7 +114,7 @@ export const getFieldDeleteModalOpener =
closeModal={closeModal} closeModal={closeModal}
confirmDelete={onConfirmDelete} confirmDelete={onConfirmDelete}
/>, />,
{ theme$: core.theme.theme$ } core
) )
); );

View file

@ -200,7 +200,7 @@ export const getFieldEditorOpener =
uiSettings={uiSettings} uiSettings={uiSettings}
/> />
</KibanaReactContextProvider>, </KibanaReactContextProvider>,
{ theme: core.theme, i18n: core.i18n } core
), ),
{ {
className: euiFlyoutClassname, className: euiFlyoutClassname,

View file

@ -8,8 +8,8 @@
import React from 'react'; import React from 'react';
import { registerTestBed } from '@kbn/test-jest-helpers'; import { registerTestBed } from '@kbn/test-jest-helpers';
jest.mock('@kbn/kibana-react-plugin/public', () => { jest.mock('@kbn/react-kibana-mount', () => {
const original = jest.requireActual('@kbn/kibana-react-plugin/public'); const original = jest.requireActual('@kbn/react-kibana-mount');
return { return {
...original, ...original,
@ -46,7 +46,7 @@ describe('DataViewFieldEditorPlugin', () => {
}); });
test('should expose a handler to open the indexpattern field editor', async () => { test('should expose a handler to open the indexpattern field editor', async () => {
const startApi = await plugin.start(coreStart, pluginStart); const startApi = plugin.start(coreStart, pluginStart);
expect(startApi.openEditor).toBeDefined(); expect(startApi.openEditor).toBeDefined();
}); });
@ -61,13 +61,13 @@ describe('DataViewFieldEditorPlugin', () => {
openFlyout, openFlyout,
}, },
}; };
const { openEditor } = await plugin.start(coreStartMocked, pluginStart); const { openEditor } = plugin.start(coreStartMocked, pluginStart);
openEditor({ onSave: onSaveSpy, ctx: { dataView: {} as any } }); openEditor({ onSave: onSaveSpy, ctx: { dataView: {} as any } });
expect(openFlyout).toHaveBeenCalled(); expect(openFlyout).toHaveBeenCalled();
const [[{ __reactMount__ }]] = openFlyout.mock.calls; const [[__reactMount__]] = openFlyout.mock.calls;
expect(__reactMount__.props.children.type).toBe(FieldEditorLoader); expect(__reactMount__.props.children.type).toBe(FieldEditorLoader);
// We force call the "onSave" prop from the <RuntimeFieldEditorFlyoutContent /> component // We force call the "onSave" prop from the <RuntimeFieldEditorFlyoutContent /> component
@ -80,14 +80,14 @@ describe('DataViewFieldEditorPlugin', () => {
}); });
test('should return a handler to close the flyout', async () => { test('should return a handler to close the flyout', async () => {
const { openEditor } = await plugin.start(coreStart, pluginStart); const { openEditor } = plugin.start(coreStart, pluginStart);
const closeEditorHandler = openEditor({ onSave: noop, ctx: { dataView: {} as any } }); const closeEditorHandler = openEditor({ onSave: noop, ctx: { dataView: {} as any } });
expect(typeof closeEditorHandler).toBe('function'); expect(typeof closeEditorHandler).toBe('function');
}); });
test('should expose a handler to open field deletion modal', async () => { test('should expose a handler to open field deletion modal', async () => {
const startApi = await plugin.start(coreStart, pluginStart); const startApi = plugin.start(coreStart, pluginStart);
expect(startApi.openDeleteModal).toBeDefined(); expect(startApi.openDeleteModal).toBeDefined();
}); });
@ -111,7 +111,7 @@ describe('DataViewFieldEditorPlugin', () => {
updateSavedObject: jest.fn(), updateSavedObject: jest.fn(),
}, },
}; };
const { openDeleteModal } = await plugin.start(coreStartMocked, pluginStartMocked); const { openDeleteModal } = plugin.start(coreStartMocked, pluginStartMocked);
const indexPatternMock = { const indexPatternMock = {
removeRuntimeField: removeFieldSpy, removeRuntimeField: removeFieldSpy,
@ -145,14 +145,14 @@ describe('DataViewFieldEditorPlugin', () => {
}); });
test('should return a handler to close the modal', async () => { test('should return a handler to close the modal', async () => {
const { openDeleteModal } = await plugin.start(coreStart, pluginStart); const { openDeleteModal } = plugin.start(coreStart, pluginStart);
const closeModal = openDeleteModal({ fieldName: ['a'], ctx: { dataView: {} as any } }); const closeModal = openDeleteModal({ fieldName: ['a'], ctx: { dataView: {} as any } });
expect(typeof closeModal).toBe('function'); expect(typeof closeModal).toBe('function');
}); });
test('should expose a render props component to delete runtime fields', async () => { test('should expose a render props component to delete runtime fields', async () => {
const { DeleteRuntimeFieldProvider } = await plugin.start(coreStart, pluginStart); const { DeleteRuntimeFieldProvider } = plugin.start(coreStart, pluginStart);
const TestComponent = ({ callback }: { callback: (...args: any[]) => void }) => { const TestComponent = ({ callback }: { callback: (...args: any[]) => void }) => {
return ( return (

View file

@ -27,7 +27,9 @@ export type {
} from '@kbn/data-views-plugin/common'; } from '@kbn/data-views-plugin/common';
export { KBN_FIELD_TYPES, ES_FIELD_TYPES } from '@kbn/data-plugin/common'; export { KBN_FIELD_TYPES, ES_FIELD_TYPES } from '@kbn/data-plugin/common';
export { createKibanaReactContext, toMountPoint } from '@kbn/kibana-react-plugin/public'; export { createKibanaReactContext } from '@kbn/kibana-react-plugin/public';
export { toMountPoint } from '@kbn/react-kibana-mount';
export { CodeEditor } from '@kbn/code-editor'; export { CodeEditor } from '@kbn/code-editor';

View file

@ -80,6 +80,7 @@ export const EditIndexPattern = withRouter(
IndexPatternEditor, IndexPatternEditor,
savedObjectsManagement, savedObjectsManagement,
application, application,
...startServices
} = useKibana<IndexPatternManagmentContext>().services; } = useKibana<IndexPatternManagmentContext>().services;
const [fields, setFields] = useState<DataViewField[]>(indexPattern.getNonScriptedFields()); const [fields, setFields] = useState<DataViewField[]>(indexPattern.getNonScriptedFields());
const [compositeRuntimeFields, setCompositeRuntimeFields] = useState< const [compositeRuntimeFields, setCompositeRuntimeFields] = useState<
@ -167,6 +168,7 @@ export const EditIndexPattern = withRouter(
onDelete: () => { onDelete: () => {
history.push(''); history.push('');
}, },
startServices,
}); });
const isRollup = const isRollup =

View file

@ -1,70 +1,8 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP // Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`IndexedFieldsTable IndexedFieldsTable with rollup index pattern should render normally 1`] = ` exports[`IndexedFieldsTable IndexedFieldsTable with rollup index pattern should render normally 1`] = `
<div>
<Table
deleteField={[Function]}
editField={[Function]}
indexPattern={
Object { Object {
"getFormatterForFieldNoDefault": [Function], "items": Array [
"getNonScriptedFields": [Function],
"type": "rollup",
"typeMeta": Object {
"aggs": Object {
"avg": Object {
"amount": Object {
"agg": "avg",
},
},
"date_histogram": Object {
"timestamp": Object {
"agg": "date_histogram",
"delay": "30s",
"fixed_interval": "30s",
"time_zone": "UTC",
},
},
"histogram": Object {
"amount": Object {
"agg": "histogram",
"interval": 5,
},
},
"max": Object {
"amount": Object {
"agg": "max",
},
},
"min": Object {
"amount": Object {
"agg": "min",
},
},
"sum": Object {
"amount": Object {
"agg": "sum",
},
},
"terms": Object {
"Elastic": Object {
"agg": "terms",
},
},
"value_count": Object {
"amount": Object {
"agg": "value_count",
},
},
},
"params": Object {
"rollup-index": "rollup",
},
},
}
}
items={
Array [
Object { Object {
"displayName": "Elastic", "displayName": "Elastic",
"esTypes": Array [ "esTypes": Array [
@ -167,27 +105,13 @@ exports[`IndexedFieldsTable IndexedFieldsTable with rollup index pattern should
}, },
"type": "long", "type": "long",
}, },
] ],
} }
openModal={[Function]}
theme={Object {}}
/>
</div>
`; `;
exports[`IndexedFieldsTable should filter based on the query bar 1`] = ` exports[`IndexedFieldsTable should filter based on the query bar 1`] = `
<div>
<Table
deleteField={[Function]}
editField={[Function]}
indexPattern={
Object { Object {
"getFormatterForFieldNoDefault": [Function], "items": Array [
"getNonScriptedFields": [Function],
}
}
items={
Array [
Object { Object {
"displayName": "Elastic", "displayName": "Elastic",
"esTypes": Array [ "esTypes": Array [
@ -205,27 +129,13 @@ exports[`IndexedFieldsTable should filter based on the query bar 1`] = `
"searchable": true, "searchable": true,
"type": "keyword", "type": "keyword",
}, },
] ],
} }
openModal={[Function]}
theme={Object {}}
/>
</div>
`; `;
exports[`IndexedFieldsTable should filter based on the schema filter 1`] = ` exports[`IndexedFieldsTable should filter based on the schema filter 1`] = `
<div>
<Table
deleteField={[Function]}
editField={[Function]}
indexPattern={
Object { Object {
"getFormatterForFieldNoDefault": [Function], "items": Array [
"getNonScriptedFields": [Function],
}
}
items={
Array [
Object { Object {
"displayName": "runtime", "displayName": "runtime",
"esTypes": Array [ "esTypes": Array [
@ -248,27 +158,13 @@ exports[`IndexedFieldsTable should filter based on the schema filter 1`] = `
}, },
"type": "long", "type": "long",
}, },
] ],
} }
openModal={[Function]}
theme={Object {}}
/>
</div>
`; `;
exports[`IndexedFieldsTable should filter based on the type filter 1`] = ` exports[`IndexedFieldsTable should filter based on the type filter 1`] = `
<div>
<Table
deleteField={[Function]}
editField={[Function]}
indexPattern={
Object { Object {
"getFormatterForFieldNoDefault": [Function], "items": Array [
"getNonScriptedFields": [Function],
}
}
items={
Array [
Object { Object {
"displayName": "timestamp", "displayName": "timestamp",
"esTypes": Array [ "esTypes": Array [
@ -285,27 +181,13 @@ exports[`IndexedFieldsTable should filter based on the type filter 1`] = `
"name": "timestamp", "name": "timestamp",
"type": "date", "type": "date",
}, },
] ],
} }
openModal={[Function]}
theme={Object {}}
/>
</div>
`; `;
exports[`IndexedFieldsTable should render normally 1`] = ` exports[`IndexedFieldsTable should render normally 1`] = `
<div>
<Table
deleteField={[Function]}
editField={[Function]}
indexPattern={
Object { Object {
"getFormatterForFieldNoDefault": [Function], "items": Array [
"getNonScriptedFields": [Function],
}
}
items={
Array [
Object { Object {
"displayName": "Elastic", "displayName": "Elastic",
"esTypes": Array [ "esTypes": Array [
@ -394,10 +276,6 @@ exports[`IndexedFieldsTable should render normally 1`] = `
}, },
"type": "long", "type": "long",
}, },
] ],
} }
openModal={[Function]}
theme={Object {}}
/>
</div>
`; `;

View file

@ -11,9 +11,9 @@ import { shallow } from 'enzyme';
import { DataView } from '@kbn/data-views-plugin/public'; import { DataView } from '@kbn/data-views-plugin/public';
import { IndexedFieldItem } from '../../types'; import { IndexedFieldItem } from '../../types';
import { Table, renderFieldName, getConflictModalContent, showDelete } from './table'; import { Table, renderFieldName, getConflictModalContent, showDelete } from './table';
import { overlayServiceMock, themeServiceMock } from '@kbn/core/public/mocks'; import { coreMock, overlayServiceMock } from '@kbn/core/public/mocks';
const theme = themeServiceMock.createStartContract(); const coreStart = coreMock.createStart();
const indexPattern = { const indexPattern = {
timeFieldName: 'timestamp', timeFieldName: 'timestamp',
@ -91,7 +91,7 @@ const renderTable = (
editField={editField} editField={editField}
deleteField={() => {}} deleteField={() => {}}
openModal={overlayServiceMock.createStartContract().openModal} openModal={overlayServiceMock.createStartContract().openModal}
theme={theme} startServices={coreStart}
/> />
); );

View file

@ -7,7 +7,7 @@
*/ */
import React, { PureComponent } from 'react'; import React, { PureComponent } from 'react';
import { OverlayModalStart, ThemeServiceStart } from '@kbn/core/public'; import { OverlayModalStart } from '@kbn/core/public';
import { FieldDescription } from '@kbn/field-utils'; import { FieldDescription } from '@kbn/field-utils';
import { import {
EuiIcon, EuiIcon,
@ -29,9 +29,10 @@ import {
import { i18n } from '@kbn/i18n'; import { i18n } from '@kbn/i18n';
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';
import { DataView } from '@kbn/data-views-plugin/public'; import { DataView } from '@kbn/data-views-plugin/public';
import { StartServices } from '../../../../../types';
import { IndexedFieldItem } from '../../types'; import { IndexedFieldItem } from '../../types';
export const showDelete = (field: IndexedFieldItem) => export const showDelete = (field: IndexedFieldItem) =>
@ -205,7 +206,7 @@ interface IndexedFieldProps {
editField: (field: IndexedFieldItem) => void; editField: (field: IndexedFieldItem) => void;
deleteField: (fieldName: string[]) => void; deleteField: (fieldName: string[]) => void;
openModal: OverlayModalStart['open']; openModal: OverlayModalStart['open'];
theme: ThemeServiceStart; startServices: StartServices;
} }
const getItems = (conflictDescriptions: IndexedFieldItem['conflictDescriptions']) => { const getItems = (conflictDescriptions: IndexedFieldItem['conflictDescriptions']) => {
@ -343,7 +344,7 @@ const getConflictBtn = (
fieldName: string, fieldName: string,
conflictDescriptions: IndexedFieldItem['conflictDescriptions'], conflictDescriptions: IndexedFieldItem['conflictDescriptions'],
openModal: IndexedFieldProps['openModal'], openModal: IndexedFieldProps['openModal'],
theme: ThemeServiceStart startServices: StartServices
) => { ) => {
const onClick = () => { const onClick = () => {
const overlayRef = openModal( const overlayRef = openModal(
@ -355,7 +356,7 @@ const getConflictBtn = (
fieldName, fieldName,
conflictDescriptions, conflictDescriptions,
}), }),
{ theme$: theme.theme$ } startServices
) )
); );
}; };
@ -393,7 +394,7 @@ export class Table extends PureComponent<IndexedFieldProps> {
field.name, field.name,
field.conflictDescriptions, field.conflictDescriptions,
this.props.openModal, this.props.openModal,
this.props.theme this.props.startServices
) )
: ''} : ''}
</span> </span>
@ -414,7 +415,7 @@ export class Table extends PureComponent<IndexedFieldProps> {
name: nameHeader, name: nameHeader,
dataType: 'string', dataType: 'string',
sortable: true, sortable: true,
render: (value: string, field: IndexedFieldItem) => { render: (_value: string, field: IndexedFieldItem) => {
return renderFieldName(field, indexPattern.timeFieldName); return renderFieldName(field, indexPattern.timeFieldName);
}, },
width: '38%', width: '38%',

View file

@ -8,6 +8,7 @@
import React from 'react'; import React from 'react';
import { shallow, ShallowWrapper } from 'enzyme'; import { shallow, ShallowWrapper } from 'enzyme';
import { coreMock } from '@kbn/core/public/mocks';
import { DataViewField, DataView, DataViewType } from '@kbn/data-views-plugin/public'; import { DataViewField, DataView, DataViewType } from '@kbn/data-views-plugin/public';
import { IndexedFieldsTable } from './indexed_fields_table'; import { IndexedFieldsTable } from './indexed_fields_table';
import { getFieldInfo } from '../../utils'; import { getFieldInfo } from '../../utils';
@ -28,8 +29,8 @@ jest.mock('./components/table', () => ({
})); }));
const helpers = { const helpers = {
editField: (fieldName: string) => {}, editField: (_fieldName: string) => {},
deleteField: (fieldName: string[]) => {}, deleteField: (_fieldName: string[]) => {},
// getFieldInfo handles non rollups as well // getFieldInfo handles non rollups as well
getFieldInfo, getFieldInfo,
}; };
@ -99,6 +100,8 @@ const fields = [
}, },
].map(mockFieldToIndexPatternField); ].map(mockFieldToIndexPatternField);
const startServices = coreMock.createStart();
const mockedServices = { const mockedServices = {
userEditPermission: false, userEditPermission: false,
openModal: () => ({ onClose: new Promise<void>(() => {}), close: async () => {} }), openModal: () => ({ onClose: new Promise<void>(() => {}), close: async () => {} }),
@ -119,6 +122,7 @@ describe('IndexedFieldsTable', () => {
schemaFieldTypeFilter={[]} schemaFieldTypeFilter={[]}
fieldFilter="" fieldFilter=""
compositeRuntimeFields={{}} compositeRuntimeFields={{}}
startServices={startServices}
{...mockedServices} {...mockedServices}
/> />
); );
@ -126,7 +130,7 @@ describe('IndexedFieldsTable', () => {
await new Promise((resolve) => process.nextTick(resolve)); await new Promise((resolve) => process.nextTick(resolve));
component.update(); component.update();
expect(component).toMatchSnapshot(); expect({ items: component.props().children.props.items }).toMatchSnapshot();
}); });
test('should filter based on the query bar', async () => { test('should filter based on the query bar', async () => {
@ -142,6 +146,7 @@ describe('IndexedFieldsTable', () => {
schemaFieldTypeFilter={[]} schemaFieldTypeFilter={[]}
fieldFilter="" fieldFilter=""
compositeRuntimeFields={{}} compositeRuntimeFields={{}}
startServices={startServices}
{...mockedServices} {...mockedServices}
/> />
); );
@ -150,7 +155,7 @@ describe('IndexedFieldsTable', () => {
component.setProps({ fieldFilter: 'Elast' }); component.setProps({ fieldFilter: 'Elast' });
component.update(); component.update();
expect(component).toMatchSnapshot(); expect({ items: component.props().children.props.items }).toMatchSnapshot();
}); });
test('should filter based on the type filter', async () => { test('should filter based on the type filter', async () => {
@ -166,6 +171,7 @@ describe('IndexedFieldsTable', () => {
schemaFieldTypeFilter={[]} schemaFieldTypeFilter={[]}
fieldFilter="" fieldFilter=""
compositeRuntimeFields={{}} compositeRuntimeFields={{}}
startServices={startServices}
{...mockedServices} {...mockedServices}
/> />
); );
@ -174,7 +180,7 @@ describe('IndexedFieldsTable', () => {
component.setProps({ indexedFieldTypeFilter: ['date'] }); component.setProps({ indexedFieldTypeFilter: ['date'] });
component.update(); component.update();
expect(component).toMatchSnapshot(); expect({ items: component.props().children.props.items }).toMatchSnapshot();
}); });
test('should filter based on the schema filter', async () => { test('should filter based on the schema filter', async () => {
@ -190,6 +196,7 @@ describe('IndexedFieldsTable', () => {
schemaFieldTypeFilter={[]} schemaFieldTypeFilter={[]}
fieldFilter="" fieldFilter=""
compositeRuntimeFields={{}} compositeRuntimeFields={{}}
startServices={startServices}
{...mockedServices} {...mockedServices}
/> />
); );
@ -198,7 +205,7 @@ describe('IndexedFieldsTable', () => {
component.setProps({ schemaFieldTypeFilter: ['runtime'] }); component.setProps({ schemaFieldTypeFilter: ['runtime'] });
component.update(); component.update();
expect(component).toMatchSnapshot(); expect({ items: component.props().children.props.items }).toMatchSnapshot();
}); });
describe('IndexedFieldsTable with rollup index pattern', () => { describe('IndexedFieldsTable with rollup index pattern', () => {
@ -215,6 +222,7 @@ describe('IndexedFieldsTable', () => {
schemaFieldTypeFilter={[]} schemaFieldTypeFilter={[]}
fieldFilter="" fieldFilter=""
compositeRuntimeFields={{}} compositeRuntimeFields={{}}
startServices={startServices}
{...mockedServices} {...mockedServices}
/> />
); );
@ -222,7 +230,7 @@ describe('IndexedFieldsTable', () => {
await new Promise((resolve) => process.nextTick(resolve)); await new Promise((resolve) => process.nextTick(resolve));
component.update(); component.update();
expect(component).toMatchSnapshot(); expect({ items: component.props().children.props.items }).toMatchSnapshot();
}); });
}); });
}); });

View file

@ -8,10 +8,11 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import { createSelector } from 'reselect'; import { createSelector } from 'reselect';
import { OverlayStart, ThemeServiceStart } from '@kbn/core/public'; import { OverlayStart } from '@kbn/core/public';
import { DataViewField, DataView, RuntimeField } from '@kbn/data-views-plugin/public'; import { DataViewField, DataView, RuntimeField } from '@kbn/data-views-plugin/public';
import { Table } from './components/table'; import { Table } from './components/table';
import { IndexedFieldItem } from './types'; import { IndexedFieldItem } from './types';
import { StartServices } from '../../../types';
interface IndexedFieldsTableProps { interface IndexedFieldsTableProps {
fields: DataViewField[]; fields: DataViewField[];
@ -27,8 +28,8 @@ interface IndexedFieldsTableProps {
fieldWildcardMatcher: (filters: string[] | undefined) => (val: string) => boolean; fieldWildcardMatcher: (filters: string[] | undefined) => (val: string) => boolean;
userEditPermission: boolean; userEditPermission: boolean;
openModal: OverlayStart['openModal']; openModal: OverlayStart['openModal'];
theme: ThemeServiceStart;
compositeRuntimeFields: Record<string, RuntimeField>; compositeRuntimeFields: Record<string, RuntimeField>;
startServices: StartServices;
} }
interface IndexedFieldsTableState { interface IndexedFieldsTableState {
@ -196,7 +197,7 @@ export class IndexedFieldsTable extends Component<
editField={(field) => this.props.helpers.editField(field.name)} editField={(field) => this.props.helpers.editField(field.name)}
deleteField={(fieldNames) => this.props.helpers.deleteField(fieldNames)} deleteField={(fieldNames) => this.props.helpers.deleteField(fieldNames)}
openModal={this.props.openModal} openModal={this.props.openModal}
theme={this.props.theme} startServices={this.props.startServices}
/> />
</div> </div>
); );

View file

@ -10,8 +10,9 @@ import { i18n } from '@kbn/i18n';
import type { IUiSettingsClient, OverlayStart } from '@kbn/core/public'; import type { IUiSettingsClient, OverlayStart } from '@kbn/core/public';
import { asyncForEach } from '@kbn/std'; import { asyncForEach } from '@kbn/std';
import { EuiConfirmModalProps } from '@elastic/eui'; import { EuiConfirmModalProps } from '@elastic/eui';
import { toMountPoint } from '@kbn/kibana-react-plugin/public'; import { toMountPoint } from '@kbn/react-kibana-mount';
import { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; import { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public';
import type { StartServices } from '../../types';
const confirmModalOptionsDelete = { const confirmModalOptionsDelete = {
confirmButtonText: i18n.translate('indexPatternManagement.editIndexPattern.deleteButton', { confirmButtonText: i18n.translate('indexPatternManagement.editIndexPattern.deleteButton', {
@ -36,12 +37,15 @@ interface RemoveDataViewDeps {
uiSettings: IUiSettingsClient; uiSettings: IUiSettingsClient;
overlays: OverlayStart; overlays: OverlayStart;
onDelete: () => void; onDelete: () => void;
startServices: StartServices;
} }
export const removeDataView = export const removeDataView =
({ dataViews, overlays, onDelete }: RemoveDataViewDeps) => ({ dataViews, overlays, onDelete, startServices }: RemoveDataViewDeps) =>
(dataViewArray: RemoveDataViewProps[], msg: JSX.Element) => { (dataViewArray: RemoveDataViewProps[], msg: JSX.Element) => {
overlays.openConfirm(toMountPoint(msg), confirmModalOptionsDelete).then(async (isConfirmed) => { overlays
.openConfirm(toMountPoint(msg, startServices), confirmModalOptionsDelete)
.then(async (isConfirmed) => {
if (isConfirmed) { if (isConfirmed) {
await asyncForEach(dataViewArray, async ({ id }) => dataViews.delete(id)); await asyncForEach(dataViewArray, async ({ id }) => dataViews.delete(id));
onDelete(); onDelete();

View file

@ -178,11 +178,11 @@ export const Tabs: React.FC<TabsProps> = ({
docLinks, docLinks,
dataViewFieldEditor, dataViewFieldEditor,
overlays, overlays,
theme,
dataViews, dataViews,
http, http,
application, application,
savedObjectsManagement, savedObjectsManagement,
...startServices
} = useKibana<IndexPatternManagmentContext>().services; } = useKibana<IndexPatternManagmentContext>().services;
const [fieldFilter, setFieldFilter] = useState<string>(''); const [fieldFilter, setFieldFilter] = useState<string>('');
const [syncingStateFunc, setSyncingStateFunc] = useState<{ const [syncingStateFunc, setSyncingStateFunc] = useState<{
@ -377,7 +377,7 @@ export const Tabs: React.FC<TabsProps> = ({
isOpen={isIndexedFilterOpen} isOpen={isIndexedFilterOpen}
closePopover={() => setIsIndexedFilterOpen(false)} closePopover={() => setIsIndexedFilterOpen(false)}
> >
{indexedFieldTypes.map((item, index) => { {indexedFieldTypes.map((item) => {
const isSelected = filteredIndexedFieldTypeFilter.includes(item.value); const isSelected = filteredIndexedFieldTypeFilter.includes(item.value);
return ( return (
<EuiFilterSelectItem <EuiFilterSelectItem
@ -582,8 +582,8 @@ export const Tabs: React.FC<TabsProps> = ({
getFieldInfo, getFieldInfo,
}} }}
openModal={overlays.openModal} openModal={overlays.openModal}
theme={theme}
userEditPermission={dataViews.getCanSaveSync()} userEditPermission={dataViews.getCanSaveSync()}
startServices={startServices}
/> />
)} )}
</DeleteRuntimeFieldProvider> </DeleteRuntimeFieldProvider>
@ -661,7 +661,7 @@ export const Tabs: React.FC<TabsProps> = ({
DeleteRuntimeFieldProvider, DeleteRuntimeFieldProvider,
refreshFields, refreshFields,
overlays, overlays,
theme, startServices,
dataViews, dataViews,
compositeRuntimeFields, compositeRuntimeFields,
http, http,

View file

@ -89,6 +89,7 @@ export const IndexPatternTable = ({
overlays, overlays,
docLinks, docLinks,
noDataPage, noDataPage,
...startServices
} = useKibana<IndexPatternManagmentContext>().services; } = useKibana<IndexPatternManagmentContext>().services;
const [query, setQuery] = useState(''); const [query, setQuery] = useState('');
const [showCreateDialog, setShowCreateDialog] = useState<boolean>(showCreateDialogProp); const [showCreateDialog, setShowCreateDialog] = useState<boolean>(showCreateDialogProp);
@ -128,6 +129,7 @@ export const IndexPatternTable = ({
setSelectedItems([]); setSelectedItems([]);
dataViewController.loadDataViews(); dataViewController.loadDataViews();
}, },
startServices,
}); });
if (selectedItems.length === 0) { if (selectedItems.length === 0) {
return; return;
@ -183,6 +185,7 @@ export const IndexPatternTable = ({
uiSettings, uiSettings,
overlays, overlays,
onDelete: () => dataViewController.loadDataViews(), onDelete: () => dataViewController.loadDataViews(),
startServices,
}); });
const alertColumn = { const alertColumn = {
@ -218,7 +221,7 @@ export const IndexPatternTable = ({
defaultMessage: 'Name', defaultMessage: 'Name',
}), }),
width: spaces ? '70%' : '90%', width: spaces ? '70%' : '90%',
render: (name: string, dataView: IndexPatternTableItem) => ( render: (_name: string, dataView: IndexPatternTableItem) => (
<div> <div>
<EuiLink <EuiLink
{...reactRouterNavigate(history, `patterns/${dataView.id}`)} {...reactRouterNavigate(history, `patterns/${dataView.id}`)}
@ -261,7 +264,7 @@ export const IndexPatternTable = ({
defaultMessage: 'Spaces', defaultMessage: 'Spaces',
}), }),
width: '20%', width: '20%',
render: (name: string, dataView: IndexPatternTableItem) => { render: (_name: string, dataView: IndexPatternTableItem) => {
return spaces ? ( return spaces ? (
<SpacesList <SpacesList
spacesApi={spaces} spacesApi={spaces}

View file

@ -53,8 +53,7 @@ export async function mountManagementSection(
overlays, overlays,
http, http,
docLinks, docLinks,
theme, ...startServices
i18n: coreI18n,
}, },
{ {
data, data,
@ -93,9 +92,9 @@ export async function mountManagementSection(
IndexPatternEditor: dataViewEditor.IndexPatternEditorComponent, IndexPatternEditor: dataViewEditor.IndexPatternEditorComponent,
fieldFormats, fieldFormats,
spaces: spaces?.hasOnlyDefaultSpace ? undefined : spaces, spaces: spaces?.hasOnlyDefaultSpace ? undefined : spaces,
theme,
savedObjectsManagement, savedObjectsManagement,
noDataPage, noDataPage,
...startServices,
}; };
const editPath = '/dataView/:id/field/:fieldName'; const editPath = '/dataView/:id/field/:fieldName';
@ -103,7 +102,7 @@ export async function mountManagementSection(
const createEditPath = dataViews.scriptedFieldsEnabled ? [editPath, createPath] : [editPath]; const createEditPath = dataViews.scriptedFieldsEnabled ? [editPath, createPath] : [editPath];
ReactDOM.render( ReactDOM.render(
<KibanaRenderContextProvider theme={theme} i18n={coreI18n}> <KibanaRenderContextProvider {...startServices}>
<KibanaContextProvider services={deps}> <KibanaContextProvider services={deps}>
<Router history={params.history}> <Router history={params.history}>
<Routes> <Routes>

View file

@ -25,6 +25,9 @@ import {
} from './plugin'; } from './plugin';
import { IndexPatternManagmentContext } from './types'; import { IndexPatternManagmentContext } from './types';
const coreSetup = coreMock.createSetup();
const coreStart = coreMock.createStart();
const createSetupContract = (): IndexPatternManagementSetup => ({}); const createSetupContract = (): IndexPatternManagementSetup => ({});
const createStartContract = (): IndexPatternManagementStart => ({}); const createStartContract = (): IndexPatternManagementStart => ({});
@ -32,7 +35,7 @@ const createStartContract = (): IndexPatternManagementStart => ({});
const createInstance = async () => { const createInstance = async () => {
const plugin = new IndexPatternManagementPlugin({} as PluginInitializerContext); const plugin = new IndexPatternManagementPlugin({} as PluginInitializerContext);
const setup = plugin.setup(coreMock.createSetup(), { const setup = plugin.setup(coreSetup, {
management: managementPluginMock.createSetupContract(), management: managementPluginMock.createSetupContract(),
urlForwarding: urlForwardingPluginMock.createSetupContract(), urlForwarding: urlForwardingPluginMock.createSetupContract(),
noDataPage: noDataPagePublicMock.createSetup(), noDataPage: noDataPagePublicMock.createSetup(),
@ -59,9 +62,6 @@ const docLinks = {
const createIndexPatternManagmentContext = (): { const createIndexPatternManagmentContext = (): {
[key in keyof IndexPatternManagmentContext]: any; [key in keyof IndexPatternManagmentContext]: any;
} => { } => {
const { application, chrome, uiSettings, notifications, overlays, theme, settings } =
coreMock.createStart();
const { http } = coreMock.createSetup();
const data = dataPluginMock.createStartContract(); const data = dataPluginMock.createStartContract();
const dataViewFieldEditor = indexPatternFieldEditorPluginMock.createStartContract(); const dataViewFieldEditor = indexPatternFieldEditorPluginMock.createStartContract();
const dataViews = dataViewPluginMocks.createStartContract(); const dataViews = dataViewPluginMocks.createStartContract();
@ -69,13 +69,7 @@ const createIndexPatternManagmentContext = (): {
const savedObjectsManagement = savedObjectsManagementPluginMock.createStartContract(); const savedObjectsManagement = savedObjectsManagementPluginMock.createStartContract();
return { return {
application, ...coreStart,
chrome,
uiSettings,
settings,
notifications,
overlays,
http,
docLinks, docLinks,
data, data,
dataViews, dataViews,
@ -88,7 +82,6 @@ const createIndexPatternManagmentContext = (): {
IndexPatternEditor: IndexPatternEditor:
indexPatternEditorPluginMock.createStartContract().IndexPatternEditorComponent, indexPatternEditorPluginMock.createStartContract().IndexPatternEditorComponent,
fieldFormats: fieldFormatsServiceMock.createStartContract(), fieldFormats: fieldFormatsServiceMock.createStartContract(),
theme,
savedObjectsManagement, savedObjectsManagement,
}; };
}; };

View file

@ -8,13 +8,13 @@
import type { import type {
ChromeStart, ChromeStart,
CoreStart,
IUiSettingsClient, IUiSettingsClient,
OverlayStart, OverlayStart,
NotificationsStart, NotificationsStart,
DocLinksStart, DocLinksStart,
HttpSetup, HttpSetup,
ApplicationStart, ApplicationStart,
ThemeServiceStart,
} from '@kbn/core/public'; } from '@kbn/core/public';
import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; import type { DataPublicPluginStart } from '@kbn/data-plugin/public';
import type { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public'; import type { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public';
@ -30,7 +30,9 @@ import type { SettingsStart } from '@kbn/core-ui-settings-browser';
import type { NoDataPagePluginSetup } from '@kbn/no-data-page-plugin/public'; import type { NoDataPagePluginSetup } from '@kbn/no-data-page-plugin/public';
import type { IndexPatternManagementStart } from '.'; import type { IndexPatternManagementStart } from '.';
export interface IndexPatternManagmentContext { export type StartServices = Pick<CoreStart, 'analytics' | 'i18n' | 'theme'>;
export interface IndexPatternManagmentContext extends StartServices {
application: ApplicationStart; application: ApplicationStart;
chrome: ChromeStart; chrome: ChromeStart;
uiSettings: IUiSettingsClient; uiSettings: IUiSettingsClient;
@ -49,7 +51,6 @@ export interface IndexPatternManagmentContext {
IndexPatternEditor: DataViewEditorStart['IndexPatternEditorComponent']; IndexPatternEditor: DataViewEditorStart['IndexPatternEditorComponent'];
fieldFormats: FieldFormatsStart; fieldFormats: FieldFormatsStart;
spaces?: SpacesPluginStart; spaces?: SpacesPluginStart;
theme: ThemeServiceStart;
savedObjectsManagement: SavedObjectsManagementPluginStart; savedObjectsManagement: SavedObjectsManagementPluginStart;
noDataPage?: NoDataPagePluginSetup; noDataPage?: NoDataPagePluginSetup;
} }

View file

@ -43,6 +43,7 @@
"@kbn/shared-ux-page-analytics-no-data", "@kbn/shared-ux-page-analytics-no-data",
"@kbn/core-http-browser", "@kbn/core-http-browser",
"@kbn/code-editor", "@kbn/code-editor",
"@kbn/react-kibana-mount",
], ],
"exclude": [ "exclude": [
"target/**/*", "target/**/*",

View file

@ -105,10 +105,7 @@ export function useContextAppFetch({
setState(createError('anchorStatus', FailureReason.UNKNOWN, error)); setState(createError('anchorStatus', FailureReason.UNKNOWN, error));
toastNotifications.addDanger({ toastNotifications.addDanger({
title: errorTitle, title: errorTitle,
text: toMountPoint(<Markdown readOnly>{error.message}</Markdown>, { text: toMountPoint(<Markdown readOnly>{error.message}</Markdown>, services),
theme: services.core.theme,
i18n: services.core.i18n,
}),
}); });
} }
}, [ }, [
@ -160,10 +157,7 @@ export function useContextAppFetch({
setState(createError(statusKey, FailureReason.UNKNOWN, error)); setState(createError(statusKey, FailureReason.UNKNOWN, error));
toastNotifications.addDanger({ toastNotifications.addDanger({
title: errorTitle, title: errorTitle,
text: toMountPoint(<Markdown readOnly>{error.message}</Markdown>, { text: toMountPoint(<Markdown readOnly>{error.message}</Markdown>, services),
theme: services.core.theme,
i18n: services.core.i18n,
}),
}); });
} }
}, },

View file

@ -48,10 +48,7 @@ export const renderApp = ({
experimentalFeatures={experimentalFeatures} experimentalFeatures={experimentalFeatures}
history={history} history={history}
/>, />,
{ core
theme: core.theme,
i18n: core.i18n,
}
)(element); )(element);
return () => { return () => {

View file

@ -145,7 +145,7 @@ const mountComponent = async ({
stateContainer.searchSessionManager = createSearchSessionMock(session).searchSessionManager; stateContainer.searchSessionManager = createSearchSessionMock(session).searchSessionManager;
const component = mountWithIntl( const component = mountWithIntl(
<KibanaRenderContextProvider theme={services.core.theme} i18n={services.core.i18n}> <KibanaRenderContextProvider {...services.core}>
<KibanaContextProvider services={services}> <KibanaContextProvider services={services}>
<DiscoverMainProvider value={stateContainer}> <DiscoverMainProvider value={stateContainer}>
<DiscoverHistogramLayout {...props} /> <DiscoverHistogramLayout {...props} />

View file

@ -128,7 +128,7 @@ const mountComponent = async ({
}; };
const component = mountWithIntl( const component = mountWithIntl(
<KibanaRenderContextProvider theme={services.core.theme} i18n={services.core.i18n}> <KibanaRenderContextProvider {...services.core}>
<KibanaContextProvider services={services}> <KibanaContextProvider services={services}>
<DiscoverMainProvider value={stateContainer}> <DiscoverMainProvider value={stateContainer}>
<DiscoverMainContent {...props} /> <DiscoverMainContent {...props} />

View file

@ -218,7 +218,7 @@ export function openAlertsPopover({
document.body.appendChild(container); document.body.appendChild(container);
const element = ( const element = (
<KibanaRenderContextProvider theme={services.core.theme} i18n={services.core.i18n}> <KibanaRenderContextProvider {...services.core}>
<KibanaContextProvider services={services}> <KibanaContextProvider services={services}>
<AlertsPopover <AlertsPopover
onClose={closeAlertsPopover} onClose={closeAlertsPopover}

View file

@ -36,7 +36,7 @@ export function showOpenSearchPanel({
document.body.appendChild(container); document.body.appendChild(container);
const element = ( const element = (
<KibanaRenderContextProvider theme={services.core.theme} i18n={services.core.i18n}> <KibanaRenderContextProvider {...services.core}>
<KibanaContextProvider services={services}> <KibanaContextProvider services={services}>
<OpenSearchPanel onClose={onClose} onOpenSavedSearch={onOpenSavedSearch} /> <OpenSearchPanel onClose={onClose} onOpenSavedSearch={onOpenSavedSearch} />
</KibanaContextProvider> </KibanaContextProvider>

View file

@ -43,10 +43,7 @@ export function NotFoundRoute() {
/> />
</p> </p>
</EuiCallOut>, </EuiCallOut>,
{ core
theme: core.theme,
i18n: core.i18n,
}
) )
); );

View file

@ -84,10 +84,7 @@ export const getAlertUtils = (
}); });
toastNotifications.addDanger({ toastNotifications.addDanger({
title: errorTitle, title: errorTitle,
text: toMountPoint(<Markdown readOnly>{error.message}</Markdown>, { text: toMountPoint(<Markdown readOnly>{error.message}</Markdown>, core),
theme: core.theme,
i18n: core.i18n,
}),
}); });
throw new Error(errorTitle); throw new Error(errorTitle);
} }
@ -107,10 +104,7 @@ export const getAlertUtils = (
}); });
toastNotifications.addDanger({ toastNotifications.addDanger({
title: errorTitle, title: errorTitle,
text: toMountPoint(<Markdown markdownContent={error.message} readOnly />, { text: toMountPoint(<Markdown markdownContent={error.message} readOnly />, core),
theme: core.theme,
i18n: core.i18n,
}),
}); });
throw new Error(errorTitle); throw new Error(errorTitle);
} }

View file

@ -14,6 +14,7 @@ import {
CoreStart, CoreStart,
DocLinksStart, DocLinksStart,
ToastsStart, ToastsStart,
I18nStart,
IUiSettingsClient, IUiSettingsClient,
PluginInitializerContext, PluginInitializerContext,
HttpStart, HttpStart,
@ -77,6 +78,7 @@ export interface DiscoverServices {
application: ApplicationStart; application: ApplicationStart;
addBasePath: (path: string) => string; addBasePath: (path: string) => string;
analytics: AnalyticsServiceStart; analytics: AnalyticsServiceStart;
i18n: I18nStart;
capabilities: Capabilities; capabilities: Capabilities;
chrome: ChromeStart; chrome: ChromeStart;
core: CoreStart; core: CoreStart;
@ -160,6 +162,7 @@ export const buildServices = memoize(
data: plugins.data, data: plugins.data,
docLinks: core.docLinks, docLinks: core.docLinks,
embeddable: plugins.embeddable, embeddable: plugins.embeddable,
i18n: core.i18n,
theme: core.theme, theme: core.theme,
fieldFormats: plugins.fieldFormats, fieldFormats: plugins.fieldFormats,
filterManager: plugins.data.query.filterManager, filterManager: plugins.data.query.filterManager,

View file

@ -7,12 +7,10 @@
*/ */
import React from 'react'; import React from 'react';
import { I18nProvider } from '@kbn/i18n-react';
import { DocTableEmbeddable, DocTableEmbeddableProps } from './doc_table_embeddable'; import { DocTableEmbeddable, DocTableEmbeddableProps } from './doc_table_embeddable';
export function DiscoverDocTableEmbeddable(renderProps: DocTableEmbeddableProps) { export function DiscoverDocTableEmbeddable(renderProps: DocTableEmbeddableProps) {
return ( return (
<I18nProvider>
<DocTableEmbeddable <DocTableEmbeddable
columns={renderProps.columns} columns={renderProps.columns}
rows={renderProps.rows} rows={renderProps.rows}
@ -35,6 +33,5 @@ export function DiscoverDocTableEmbeddable(renderProps: DocTableEmbeddableProps)
isPlainRecord={renderProps.isPlainRecord} isPlainRecord={renderProps.isPlainRecord}
interceptedWarnings={renderProps.interceptedWarnings} interceptedWarnings={renderProps.interceptedWarnings}
/> />
</I18nProvider>
); );
} }

View file

@ -651,10 +651,7 @@ export class SavedSearchEmbeddable
Array.isArray(searchProps.columns) Array.isArray(searchProps.columns)
) { ) {
ReactDOM.render( ReactDOM.render(
<KibanaRenderContextProvider <KibanaRenderContextProvider {...searchProps.services.core}>
theme={searchProps.services.core.theme}
i18n={searchProps.services.core.i18n}
>
<KibanaContextProvider services={searchProps.services}> <KibanaContextProvider services={searchProps.services}>
<FieldStatisticsTable <FieldStatisticsTable
dataView={searchProps.dataView} dataView={searchProps.dataView}
@ -694,10 +691,7 @@ export class SavedSearchEmbeddable
const { getTriggerCompatibleActions } = searchProps.services.uiActions; const { getTriggerCompatibleActions } = searchProps.services.uiActions;
ReactDOM.render( ReactDOM.render(
<KibanaRenderContextProvider <KibanaRenderContextProvider {...searchProps.services.core}>
theme={searchProps.services.core.theme}
i18n={searchProps.services.core.i18n}
>
<KibanaContextProvider services={searchProps.services}> <KibanaContextProvider services={searchProps.services}>
<CellActionsProvider getTriggerCompatibleActions={getTriggerCompatibleActions}> <CellActionsProvider getTriggerCompatibleActions={getTriggerCompatibleActions}>
<SavedSearchEmbeddableComponent <SavedSearchEmbeddableComponent

View file

@ -10,15 +10,19 @@ import React from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import { EuiConfirmModal } from '@elastic/eui'; import { EuiConfirmModal } from '@elastic/eui';
import { i18n } from '@kbn/i18n'; import { i18n } from '@kbn/i18n';
import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render';
import { StartRenderServices } from '../plugin';
let isOpenConfirmPanel = false; let isOpenConfirmPanel = false;
export const showConfirmPanel = ({ export const showConfirmPanel = ({
onConfirm, onConfirm,
onCancel, onCancel,
startServices,
}: { }: {
onConfirm: () => void; onConfirm: () => void;
onCancel: () => void; onCancel: () => void;
startServices: StartRenderServices;
}) => { }) => {
if (isOpenConfirmPanel) { if (isOpenConfirmPanel) {
return; return;
@ -34,6 +38,7 @@ export const showConfirmPanel = ({
document.body.appendChild(container); document.body.appendChild(container);
const element = ( const element = (
<KibanaRenderContextProvider {...startServices}>
<EuiConfirmModal <EuiConfirmModal
title={i18n.translate('discover.confirmDataViewSave.title', { title={i18n.translate('discover.confirmDataViewSave.title', {
defaultMessage: 'Save data view', defaultMessage: 'Save data view',
@ -60,6 +65,7 @@ export const showConfirmPanel = ({
})} })}
</p> </p>
</EuiConfirmModal> </EuiConfirmModal>
</KibanaRenderContextProvider>
); );
ReactDOM.render(element, container); ReactDOM.render(element, container);
}; };

View file

@ -198,6 +198,8 @@ export interface DiscoverStartPlugins {
noDataPage?: NoDataPagePluginStart; noDataPage?: NoDataPagePluginStart;
} }
export type StartRenderServices = Pick<CoreStart, 'analytics' | 'i18n' | 'theme'>;
/** /**
* Contains Discover, one of the oldest parts of Kibana * Contains Discover, one of the oldest parts of Kibana
* Discover provides embeddables for Dashboards * Discover provides embeddables for Dashboards