diff --git a/src/plugins/dashboard/public/application/embeddable/dashboard_container.test.tsx b/src/plugins/dashboard/public/application/embeddable/dashboard_container.test.tsx index d5eef0c05129d..5f50cfd842b67 100644 --- a/src/plugins/dashboard/public/application/embeddable/dashboard_container.test.tsx +++ b/src/plugins/dashboard/public/application/embeddable/dashboard_container.test.tsx @@ -41,6 +41,7 @@ import { uiActionsPluginMock } from '../../../../ui_actions/public/mocks'; import { getStubPluginServices } from '../../../../presentation_util/public'; const presentationUtil = getStubPluginServices(); +const theme = coreMock.createStart().theme; const options: DashboardContainerServices = { // TODO: clean up use of any @@ -55,7 +56,7 @@ const options: DashboardContainerServices = { uiActions: {} as any, uiSettings: uiSettingsServiceMock.createStartContract(), http: coreMock.createStart().http, - theme: coreMock.createStart().theme, + theme, presentationUtil, }; @@ -251,6 +252,7 @@ test('DashboardContainer in edit mode shows edit mode actions', async () => { overlays={{} as any} inspector={inspector} SavedObjectFinder={() => null} + theme={theme} /> diff --git a/src/plugins/dashboard/public/application/listing/__snapshots__/dashboard_listing.test.tsx.snap b/src/plugins/dashboard/public/application/listing/__snapshots__/dashboard_listing.test.tsx.snap index 2f383adb3f5c3..598254ad2173f 100644 --- a/src/plugins/dashboard/public/application/listing/__snapshots__/dashboard_listing.test.tsx.snap +++ b/src/plugins/dashboard/public/application/listing/__snapshots__/dashboard_listing.test.tsx.snap @@ -96,6 +96,14 @@ exports[`after fetch When given a title that matches multiple dashboards, filter ] } tableListTitle="Dashboards" + theme={ + Object { + "theme$": Observable { + "_isScalar": false, + "_subscribe": [Function], + }, + } + } toastNotifications={ Object { "add": [MockFunction], @@ -208,6 +216,14 @@ exports[`after fetch initialFilter 1`] = ` ] } tableListTitle="Dashboards" + theme={ + Object { + "theme$": Observable { + "_isScalar": false, + "_subscribe": [Function], + }, + } + } toastNotifications={ Object { "add": [MockFunction], @@ -319,6 +335,14 @@ exports[`after fetch renders all table rows 1`] = ` ] } tableListTitle="Dashboards" + theme={ + Object { + "theme$": Observable { + "_isScalar": false, + "_subscribe": [Function], + }, + } + } toastNotifications={ Object { "add": [MockFunction], @@ -430,6 +454,14 @@ exports[`after fetch renders call to action when no dashboards exist 1`] = ` ] } tableListTitle="Dashboards" + theme={ + Object { + "theme$": Observable { + "_isScalar": false, + "_subscribe": [Function], + }, + } + } toastNotifications={ Object { "add": [MockFunction], @@ -552,6 +584,14 @@ exports[`after fetch renders call to action with continue when no dashboards exi ] } tableListTitle="Dashboards" + theme={ + Object { + "theme$": Observable { + "_isScalar": false, + "_subscribe": [Function], + }, + } + } toastNotifications={ Object { "add": [MockFunction], @@ -663,6 +703,14 @@ exports[`after fetch renders warning when listingLimit is exceeded 1`] = ` ] } tableListTitle="Dashboards" + theme={ + Object { + "theme$": Observable { + "_isScalar": false, + "_subscribe": [Function], + }, + } + } toastNotifications={ Object { "add": [MockFunction], @@ -744,6 +792,14 @@ exports[`after fetch showWriteControls 1`] = ` ] } tableListTitle="Dashboards" + theme={ + Object { + "theme$": Observable { + "_isScalar": false, + "_subscribe": [Function], + }, + } + } toastNotifications={ Object { "add": [MockFunction], diff --git a/src/plugins/dashboard/public/application/listing/dashboard_listing.tsx b/src/plugins/dashboard/public/application/listing/dashboard_listing.tsx index deb8671edb97d..5b53fc47e06a4 100644 --- a/src/plugins/dashboard/public/application/listing/dashboard_listing.tsx +++ b/src/plugins/dashboard/public/application/listing/dashboard_listing.tsx @@ -297,6 +297,7 @@ export const DashboardListing = ({ listingLimit, tableColumns, }} + theme={core.theme} > { return { @@ -22,6 +23,8 @@ jest.mock('@kbn/i18n', () => { }; }); +const theme = themeServiceMock.createStartContract(); + describe('handleResponse', () => { const notifications = notificationServiceMock.createStartContract(); @@ -37,7 +40,7 @@ describe('handleResponse', () => { timed_out: true, }, } as IKibanaSearchResponse; - const result = handleResponse(request, response); + const result = handleResponse(request, response, theme); expect(result).toBe(response); expect(notifications.toasts.addWarning).toBeCalled(); expect((notifications.toasts.addWarning as jest.Mock).mock.calls[0][0].title).toMatch( @@ -57,7 +60,7 @@ describe('handleResponse', () => { }, }, } as IKibanaSearchResponse; - const result = handleResponse(request, response); + const result = handleResponse(request, response, theme); expect(result).toBe(response); expect(notifications.toasts.addWarning).toBeCalled(); expect((notifications.toasts.addWarning as jest.Mock).mock.calls[0][0].title).toMatch( @@ -70,7 +73,7 @@ describe('handleResponse', () => { const response = { rawResponse: {}, } as IKibanaSearchResponse; - const result = handleResponse(request, response); + const result = handleResponse(request, response, theme); expect(result).toBe(response); }); }); diff --git a/src/plugins/data/public/search/fetch/handle_response.tsx b/src/plugins/data/public/search/fetch/handle_response.tsx index 10b2f69a2a320..618efcb702ec4 100644 --- a/src/plugins/data/public/search/fetch/handle_response.tsx +++ b/src/plugins/data/public/search/fetch/handle_response.tsx @@ -11,11 +11,16 @@ import { i18n } from '@kbn/i18n'; import { EuiSpacer } from '@elastic/eui'; import { IKibanaSearchResponse } from 'src/plugins/data/common'; import { ShardFailureOpenModalButton } from '../../ui/shard_failure_modal'; +import { ThemeServiceStart } from '../../../../../core/public'; import { toMountPoint } from '../../../../kibana_react/public'; import { getNotifications } from '../../services'; import type { SearchRequest } from '..'; -export function handleResponse(request: SearchRequest, response: IKibanaSearchResponse) { +export function handleResponse( + request: SearchRequest, + response: IKibanaSearchResponse, + theme: ThemeServiceStart +) { const { rawResponse } = response; if (rawResponse.timed_out) { @@ -45,8 +50,14 @@ export function handleResponse(request: SearchRequest, response: IKibanaSearchRe <> {description} - - + + , + { theme$: theme.theme$ } ); getNotifications().toasts.addWarning({ title, text }); diff --git a/src/plugins/data/public/search/search_interceptor/search_interceptor.test.ts b/src/plugins/data/public/search/search_interceptor/search_interceptor.test.ts index 142fa94c96162..968dd870489fe 100644 --- a/src/plugins/data/public/search/search_interceptor/search_interceptor.test.ts +++ b/src/plugins/data/public/search/search_interceptor/search_interceptor.test.ts @@ -8,7 +8,7 @@ import type { MockedKeys } from '@kbn/utility-types/jest'; import { CoreSetup, CoreStart } from '../../../../../core/public'; -import { coreMock } from '../../../../../core/public/mocks'; +import { coreMock, themeServiceMock } from '../../../../../core/public/mocks'; import { IEsSearchRequest } from '../../../common/search'; import { SearchInterceptor } from './search_interceptor'; import { AbortError } from '../../../../kibana_utils/public'; @@ -120,6 +120,7 @@ describe('SearchInterceptor', () => { uiSettings: mockCoreSetup.uiSettings, http: mockCoreSetup.http, session: sessionService, + theme: themeServiceMock.createSetupContract(), }); }); diff --git a/src/plugins/data/public/search/search_interceptor/search_interceptor.ts b/src/plugins/data/public/search/search_interceptor/search_interceptor.ts index 9e968c9bae8a0..8c7bfe68fd54b 100644 --- a/src/plugins/data/public/search/search_interceptor/search_interceptor.ts +++ b/src/plugins/data/public/search/search_interceptor/search_interceptor.ts @@ -21,7 +21,7 @@ import { tap, } from 'rxjs/operators'; import { PublicMethodsOf } from '@kbn/utility-types'; -import { CoreSetup, CoreStart, ToastsSetup } from 'kibana/public'; +import { CoreSetup, CoreStart, ThemeServiceSetup, ToastsSetup } from 'kibana/public'; import { i18n } from '@kbn/i18n'; import { BatchedFunc, BfetchPublicSetup } from 'src/plugins/bfetch/public'; import { @@ -60,6 +60,7 @@ export interface SearchInterceptorDeps { toasts: ToastsSetup; usageCollector?: SearchUsageCollector; session: ISessionService; + theme: ThemeServiceSetup; } const MAX_CACHE_ITEMS = 50; @@ -377,7 +378,7 @@ export class SearchInterceptor { private showTimeoutErrorToast = (e: SearchTimeoutError, sessionId?: string) => { this.deps.toasts.addDanger({ title: 'Timed out', - text: toMountPoint(e.getErrorMessage(this.application)), + text: toMountPoint(e.getErrorMessage(this.application), { theme$: this.deps.theme.theme$ }), }); }; @@ -392,7 +393,9 @@ export class SearchInterceptor { this.deps.toasts.addWarning( { title: 'Your search session is still running', - text: toMountPoint(SearchSessionIncompleteWarning(this.docLinks)), + text: toMountPoint(SearchSessionIncompleteWarning(this.docLinks), { + theme$: this.deps.theme.theme$, + }), }, { toastLifeTimeMs: 60000, @@ -423,14 +426,14 @@ export class SearchInterceptor { title: i18n.translate('data.search.esErrorTitle', { defaultMessage: 'Cannot retrieve search results', }), - text: toMountPoint(e.getErrorMessage(this.application)), + text: toMountPoint(e.getErrorMessage(this.application), { theme$: this.deps.theme.theme$ }), }); } else if (e.constructor.name === 'HttpFetchError') { this.deps.toasts.addDanger({ title: i18n.translate('data.search.httpErrorTitle', { defaultMessage: 'Cannot retrieve your data', }), - text: toMountPoint(getHttpError(e.message)), + text: toMountPoint(getHttpError(e.message), { theme$: this.deps.theme.theme$ }), }); } else { this.deps.toasts.addError(e, { diff --git a/src/plugins/data/public/search/search_service.ts b/src/plugins/data/public/search/search_service.ts index 76aae8582287d..311a863a74933 100644 --- a/src/plugins/data/public/search/search_service.ts +++ b/src/plugins/data/public/search/search_service.ts @@ -46,7 +46,7 @@ import { esRawResponse, } from '../../common/search'; import { AggsService, AggsStartDependencies } from './aggs'; -import { IndexPatternsContract } from '..'; +import { IKibanaSearchResponse, IndexPatternsContract, SearchRequest } from '..'; import { ISearchInterceptor, SearchInterceptor } from './search_interceptor'; import { SearchUsageCollector, createUsageCollector } from './collectors'; import { UsageCollectionSetup } from '../../../usage_collection/public'; @@ -88,7 +88,7 @@ export class SearchService implements Plugin { constructor(private initializerContext: PluginInitializerContext) {} public setup( - { http, getStartServices, notifications, uiSettings }: CoreSetup, + { http, getStartServices, notifications, uiSettings, theme }: CoreSetup, { bfetch, expressions, usageCollection, nowProvider }: SearchServiceSetupDependencies ): ISearchSetup { this.usageCollector = createUsageCollector(getStartServices, usageCollection); @@ -112,6 +112,7 @@ export class SearchService implements Plugin { startServices: getStartServices(), usageCollector: this.usageCollector!, session: this.sessionService, + theme, }); expressions.registerFunction( @@ -173,7 +174,7 @@ export class SearchService implements Plugin { } public start( - { http, uiSettings }: CoreStart, + { http, theme, uiSettings }: CoreStart, { fieldFormats, indexPatterns }: SearchServiceStartDependencies ): ISearchStart { const search = ((request, options = {}) => { @@ -186,7 +187,8 @@ export class SearchService implements Plugin { const searchSourceDependencies: SearchSourceDependencies = { getConfig: uiSettings.get.bind(uiSettings), search, - onResponse: handleResponse, + onResponse: (request: SearchRequest, response: IKibanaSearchResponse) => + handleResponse(request, response, theme), }; return { diff --git a/src/plugins/data/public/services.ts b/src/plugins/data/public/services.ts index c1a0ae1ac1b53..5c52a1e695359 100644 --- a/src/plugins/data/public/services.ts +++ b/src/plugins/data/public/services.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { NotificationsStart, CoreStart } from 'src/core/public'; +import { NotificationsStart, CoreStart, ThemeServiceStart } from 'src/core/public'; import { createGetterSetter } from '../../kibana_utils/public'; import { IndexPatternsContract } from './data_views'; import { DataPublicPluginStart } from './types'; @@ -24,3 +24,5 @@ export const [getIndexPatterns, setIndexPatterns] = export const [getSearchService, setSearchService] = createGetterSetter('Search'); + +export const [getTheme, setTheme] = createGetterSetter('Theme'); diff --git a/src/plugins/data/public/ui/query_string_input/query_bar_top_row.tsx b/src/plugins/data/public/ui/query_string_input/query_bar_top_row.tsx index bb5e61bdb1946..e5da2bb9f089d 100644 --- a/src/plugins/data/public/ui/query_string_input/query_bar_top_row.tsx +++ b/src/plugins/data/public/ui/query_string_input/query_bar_top_row.tsx @@ -24,7 +24,6 @@ import { EuiSuperUpdateButton, OnRefreshProps, } from '@elastic/eui'; - import { IDataPluginServices, IIndexPattern, TimeRange, TimeHistoryContract, Query } from '../..'; import { useKibana, withKibana } from '../../../../kibana_react/public'; import QueryStringInputUI from './query_string_input'; diff --git a/src/plugins/data/public/ui/query_string_input/query_string_input.tsx b/src/plugins/data/public/ui/query_string_input/query_string_input.tsx index a0b214d1be8c7..6464f02dd7cb7 100644 --- a/src/plugins/data/public/ui/query_string_input/query_string_input.tsx +++ b/src/plugins/data/public/ui/query_string_input/query_string_input.tsx @@ -40,6 +40,7 @@ import type { SuggestionsListSize } from '../typeahead/suggestions_component'; import { SuggestionsComponent } from '..'; import { getFieldSubtypeNested, KIBANA_USER_QUERY_LANGUAGE_KEY } from '../../../common'; import { onRaf } from '../utils'; +import { getTheme } from '../../services'; export interface QueryStringInputProps { indexPatterns: Array; @@ -487,7 +488,8 @@ export default class QueryStringInputUI extends PureComponent { - + , + { theme$: getTheme().theme$ } ), }); } diff --git a/src/plugins/data/public/ui/shard_failure_modal/shard_failure_open_modal_button.test.tsx b/src/plugins/data/public/ui/shard_failure_modal/shard_failure_open_modal_button.test.tsx index 28822cbd71ca7..b8289bc23cf01 100644 --- a/src/plugins/data/public/ui/shard_failure_modal/shard_failure_open_modal_button.test.tsx +++ b/src/plugins/data/public/ui/shard_failure_modal/shard_failure_open_modal_button.test.tsx @@ -8,18 +8,22 @@ import { openModal } from './shard_failure_open_modal_button.test.mocks'; import React from 'react'; +import { themeServiceMock } from 'src/core/public/mocks'; import { mountWithIntl } from '@kbn/test/jest'; import ShardFailureOpenModalButton from './shard_failure_open_modal_button'; import { shardFailureRequest } from './__mocks__/shard_failure_request'; import { shardFailureResponse } from './__mocks__/shard_failure_response'; import { findTestSubject } from '@elastic/eui/lib/test'; +const theme = themeServiceMock.createStartContract(); + describe('ShardFailureOpenModalButton', () => { it('triggers the openModal function when "Show details" button is clicked', () => { const component = mountWithIntl( ); diff --git a/src/plugins/data/public/ui/shard_failure_modal/shard_failure_open_modal_button.tsx b/src/plugins/data/public/ui/shard_failure_modal/shard_failure_open_modal_button.tsx index 32ebd83aa47f0..585268824fb93 100644 --- a/src/plugins/data/public/ui/shard_failure_modal/shard_failure_open_modal_button.tsx +++ b/src/plugins/data/public/ui/shard_failure_modal/shard_failure_open_modal_button.tsx @@ -12,6 +12,7 @@ import { EuiButton, EuiTextAlign } from '@elastic/eui'; import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { getOverlays } from '../../services'; +import { ThemeServiceStart } from '../../../../../core/public'; import { toMountPoint } from '../../../../kibana_react/public'; import { ShardFailureModal } from './shard_failure_modal'; import { ShardFailureRequest } from './shard_failure_types'; @@ -20,6 +21,7 @@ import { ShardFailureRequest } from './shard_failure_types'; export interface ShardFailureOpenModalButtonProps { request: ShardFailureRequest; response: estypes.SearchResponse; + theme: ThemeServiceStart; title: string; } @@ -28,6 +30,7 @@ export interface ShardFailureOpenModalButtonProps { export default function ShardFailureOpenModalButton({ request, response, + theme, title, }: ShardFailureOpenModalButtonProps) { function onClick() { @@ -38,7 +41,8 @@ export default function ShardFailureOpenModalButton({ response={response} title={title} onClose={() => modal.close()} - /> + />, + { theme$: theme.theme$ } ), { className: 'shardFailureModal', diff --git a/src/plugins/data_view_editor/public/open_editor.tsx b/src/plugins/data_view_editor/public/open_editor.tsx index 98843d6d1698a..fcf0fad5a32b0 100644 --- a/src/plugins/data_view_editor/public/open_editor.tsx +++ b/src/plugins/data_view_editor/public/open_editor.tsx @@ -79,7 +79,8 @@ export const getEditorOpener = requireTimestampField={requireTimestampField} /> - + , + { theme$: core.theme.theme$ } ), { hideCloseButton: true, diff --git a/src/plugins/data_view_field_editor/public/open_delete_modal.tsx b/src/plugins/data_view_field_editor/public/open_delete_modal.tsx index 84e3885ddb605..f44367d16d08d 100644 --- a/src/plugins/data_view_field_editor/public/open_delete_modal.tsx +++ b/src/plugins/data_view_field_editor/public/open_delete_modal.tsx @@ -75,7 +75,8 @@ export const getFieldDeleteModalOpener = fieldsToDelete={fieldsToDelete} closeModal={closeModal} confirmDelete={onConfirmDelete} - /> + />, + { theme$: core.theme.theme$ } ) ); diff --git a/src/plugins/data_view_field_editor/public/open_editor.tsx b/src/plugins/data_view_field_editor/public/open_editor.tsx index 277d7f5c549ae..c66e8183b9ab6 100644 --- a/src/plugins/data_view_field_editor/public/open_editor.tsx +++ b/src/plugins/data_view_field_editor/public/open_editor.tsx @@ -128,7 +128,8 @@ export const getFieldEditorOpener = fieldFormats={fieldFormats} uiSettings={uiSettings} /> - + , + { theme$: core.theme.theme$ } ), { className: euiFlyoutClassname, diff --git a/src/plugins/data_view_management/public/components/edit_index_pattern/indexed_fields_table/components/table/table.test.tsx b/src/plugins/data_view_management/public/components/edit_index_pattern/indexed_fields_table/components/table/table.test.tsx index dd78b00f9775e..f85f7bb254826 100644 --- a/src/plugins/data_view_management/public/components/edit_index_pattern/indexed_fields_table/components/table/table.test.tsx +++ b/src/plugins/data_view_management/public/components/edit_index_pattern/indexed_fields_table/components/table/table.test.tsx @@ -11,7 +11,9 @@ import { shallow } from 'enzyme'; import { IndexPattern } from 'src/plugins/data/public'; import { IndexedFieldItem } from '../../types'; import { Table, renderFieldName, getConflictModalContent } from './table'; -import { overlayServiceMock } from 'src/core/public/mocks'; +import { overlayServiceMock, themeServiceMock } from 'src/core/public/mocks'; + +const theme = themeServiceMock.createStartContract(); const indexPattern = { timeFieldName: 'timestamp', @@ -89,6 +91,7 @@ const renderTable = ( editField={editField} deleteField={() => {}} openModal={overlayServiceMock.createStartContract().openModal} + theme={theme} /> ); diff --git a/src/plugins/data_view_management/public/components/edit_index_pattern/indexed_fields_table/components/table/table.tsx b/src/plugins/data_view_management/public/components/edit_index_pattern/indexed_fields_table/components/table/table.tsx index 6a82d0380629c..7e915e3c930a5 100644 --- a/src/plugins/data_view_management/public/components/edit_index_pattern/indexed_fields_table/components/table/table.tsx +++ b/src/plugins/data_view_management/public/components/edit_index_pattern/indexed_fields_table/components/table/table.tsx @@ -7,7 +7,7 @@ */ import React, { PureComponent } from 'react'; -import { OverlayModalStart } from 'src/core/public'; +import { OverlayModalStart, ThemeServiceStart } from 'src/core/public'; import { EuiIcon, @@ -179,6 +179,7 @@ interface IndexedFieldProps { editField: (field: IndexedFieldItem) => void; deleteField: (fieldName: string) => void; openModal: OverlayModalStart['open']; + theme: ThemeServiceStart; } const getItems = (conflictDescriptions: IndexedFieldItem['conflictDescriptions']) => { @@ -311,7 +312,8 @@ export const getConflictModalContent = ({ const getConflictBtn = ( fieldName: string, conflictDescriptions: IndexedFieldItem['conflictDescriptions'], - openModal: IndexedFieldProps['openModal'] + openModal: IndexedFieldProps['openModal'], + theme: ThemeServiceStart ) => { const onClick = () => { const overlayRef = openModal( @@ -322,7 +324,8 @@ const getConflictBtn = ( }, fieldName, conflictDescriptions, - }) + }), + { theme$: theme.theme$ } ) ); }; @@ -355,7 +358,12 @@ export class Table extends PureComponent { {type === 'conflict' && conflictDescription ? '' : type} {field.conflictDescriptions - ? getConflictBtn(field.name, field.conflictDescriptions, this.props.openModal) + ? getConflictBtn( + field.name, + field.conflictDescriptions, + this.props.openModal, + this.props.theme + ) : ''} ); diff --git a/src/plugins/data_view_management/public/components/edit_index_pattern/indexed_fields_table/indexed_fields_table.tsx b/src/plugins/data_view_management/public/components/edit_index_pattern/indexed_fields_table/indexed_fields_table.tsx index a72c87655fd63..29b8d82a99704 100644 --- a/src/plugins/data_view_management/public/components/edit_index_pattern/indexed_fields_table/indexed_fields_table.tsx +++ b/src/plugins/data_view_management/public/components/edit_index_pattern/indexed_fields_table/indexed_fields_table.tsx @@ -8,7 +8,7 @@ import React, { Component } from 'react'; import { createSelector } from 'reselect'; -import { OverlayStart } from 'src/core/public'; +import { OverlayStart, ThemeServiceStart } from 'src/core/public'; import { IndexPatternField, IndexPattern } from '../../../../../../plugins/data/public'; import { useKibana } from '../../../../../../plugins/kibana_react/public'; import { Table } from './components/table'; @@ -28,6 +28,7 @@ interface IndexedFieldsTableProps { fieldWildcardMatcher: (filters: any[]) => (val: any) => boolean; userEditPermission: boolean; openModal: OverlayStart['openModal']; + theme: ThemeServiceStart; } interface IndexedFieldsTableState { @@ -129,6 +130,7 @@ class IndexedFields extends Component this.props.helpers.editField(field.name)} deleteField={(fieldName) => this.props.helpers.deleteField(fieldName)} openModal={this.props.openModal} + theme={this.props.theme} /> ); diff --git a/src/plugins/data_view_management/public/components/edit_index_pattern/tabs/tabs.tsx b/src/plugins/data_view_management/public/components/edit_index_pattern/tabs/tabs.tsx index b5940fa8d1bb0..58b064fa79893 100644 --- a/src/plugins/data_view_management/public/components/edit_index_pattern/tabs/tabs.tsx +++ b/src/plugins/data_view_management/public/components/edit_index_pattern/tabs/tabs.tsx @@ -80,7 +80,7 @@ export function Tabs({ location, refreshFields, }: TabsProps) { - const { application, uiSettings, docLinks, dataViewFieldEditor, overlays } = + const { application, uiSettings, docLinks, dataViewFieldEditor, overlays, theme } = useKibana().services; const [fieldFilter, setFieldFilter] = useState(''); const [indexedFieldTypeFilter, setIndexedFieldTypeFilter] = useState(''); @@ -236,6 +236,7 @@ export function Tabs({ getFieldInfo, }} openModal={overlays.openModal} + theme={theme} /> )} @@ -295,6 +296,7 @@ export function Tabs({ DeleteRuntimeFieldProvider, refreshFields, overlays, + theme, ] ); diff --git a/src/plugins/data_view_management/public/management_app/mount_management_section.tsx b/src/plugins/data_view_management/public/management_app/mount_management_section.tsx index 6e0e7ffc9091d..4bc0a204f68a1 100644 --- a/src/plugins/data_view_management/public/management_app/mount_management_section.tsx +++ b/src/plugins/data_view_management/public/management_app/mount_management_section.tsx @@ -14,7 +14,7 @@ import { i18n } from '@kbn/i18n'; import { I18nProvider } from '@kbn/i18n-react'; import { StartServicesAccessor } from 'src/core/public'; -import { KibanaContextProvider } from '../../../kibana_react/public'; +import { KibanaContextProvider, KibanaThemeProvider } from '../../../kibana_react/public'; import { ManagementAppMountParams } from '../../../management/public'; import { IndexPatternTableWithRouter, @@ -39,7 +39,7 @@ export async function mountManagementSection( params: ManagementAppMountParams ) { const [ - { chrome, application, uiSettings, notifications, overlays, http, docLinks }, + { chrome, application, uiSettings, notifications, overlays, http, docLinks, theme }, { data, dataViewFieldEditor, dataViewEditor }, indexPatternManagementStart, ] = await getStartServices(); @@ -67,25 +67,27 @@ export async function mountManagementSection( ReactDOM.render( - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + , params.element ); diff --git a/src/plugins/data_views/public/data_views/redirect_no_index_pattern.tsx b/src/plugins/data_views/public/data_views/redirect_no_index_pattern.tsx index 086cd92a92d82..fc7b8c9eb42b6 100644 --- a/src/plugins/data_views/public/data_views/redirect_no_index_pattern.tsx +++ b/src/plugins/data_views/public/data_views/redirect_no_index_pattern.tsx @@ -18,7 +18,8 @@ export const onRedirectNoIndexPattern = ( capabilities: CoreStart['application']['capabilities'], navigateToApp: CoreStart['application']['navigateToApp'], - overlays: CoreStart['overlays'] + overlays: CoreStart['overlays'], + theme: CoreStart['theme'] ) => () => { const canManageIndexPatterns = capabilities.management.kibana.indexPatterns; @@ -38,7 +39,9 @@ export const onRedirectNoIndexPattern = // give them a friendly info message instead of a terse error message bannerId = overlays.banners.replace( bannerId, - toMountPoint() + toMountPoint(, { + theme$: theme.theme$, + }) ); // hide the message after the user has had a chance to acknowledge it -- so it doesn't permanently stick around diff --git a/src/plugins/data_views/public/plugin.ts b/src/plugins/data_views/public/plugin.ts index 4a00ea91a47bd..bf092d3fae177 100644 --- a/src/plugins/data_views/public/plugin.ts +++ b/src/plugins/data_views/public/plugin.ts @@ -45,7 +45,7 @@ export class DataViewsPublicPlugin core: CoreStart, { fieldFormats }: DataViewsPublicStartDependencies ): DataViewsPublicPluginStart { - const { uiSettings, http, notifications, savedObjects, overlays, application } = core; + const { uiSettings, http, notifications, savedObjects, theme, overlays, application } = core; return new DataViewsService({ uiSettings: new UiSettingsPublicToCommon(uiSettings), @@ -59,7 +59,8 @@ export class DataViewsPublicPlugin onRedirectNoIndexPattern: onRedirectNoIndexPattern( application.capabilities, application.navigateToApp, - overlays + overlays, + theme ), getCanSave: () => Promise.resolve(application.capabilities.indexPatterns.save === true), }); diff --git a/src/plugins/discover/public/application/main/discover_main_route.tsx b/src/plugins/discover/public/application/main/discover_main_route.tsx index b2576a3b5d582..dd1d036b811a2 100644 --- a/src/plugins/discover/public/application/main/discover_main_route.tsx +++ b/src/plugins/discover/public/application/main/discover_main_route.tsx @@ -122,6 +122,7 @@ export function DiscoverMainRoute({ services, history }: DiscoverMainProps) { onBeforeRedirect() { getUrlTracker().setTrackedUrl('/'); }, + theme: core.theme, })(e); } } @@ -139,6 +140,7 @@ export function DiscoverMainRoute({ services, history }: DiscoverMainProps) { id, services, toastNotifications, + core.theme, ]); useEffect(() => { diff --git a/src/plugins/embeddable/public/lib/embeddables/error_embeddable.tsx b/src/plugins/embeddable/public/lib/embeddables/error_embeddable.tsx index f4c650507add9..70c30d314fc82 100644 --- a/src/plugins/embeddable/public/lib/embeddables/error_embeddable.tsx +++ b/src/plugins/embeddable/public/lib/embeddables/error_embeddable.tsx @@ -9,10 +9,11 @@ import { EuiText, EuiIcon, EuiSpacer } from '@elastic/eui'; import React from 'react'; import ReactDOM from 'react-dom'; -import { Markdown } from '../../../../kibana_react/public'; +import { KibanaThemeProvider, Markdown } from '../../../../kibana_react/public'; import { Embeddable } from './embeddable'; import { EmbeddableInput, EmbeddableOutput, IEmbeddable } from './i_embeddable'; import { IContainer } from '../containers'; +import { getTheme } from '../../services'; export const ERROR_EMBEDDABLE_TYPE = 'error'; @@ -37,8 +38,13 @@ export class ErrorEmbeddable extends Embeddable @@ -49,9 +55,16 @@ export class ErrorEmbeddable extends Embeddable - , - dom + ); + const content = + theme && theme.theme$ ? ( + {node} + ) : ( + node + ); + + ReactDOM.render(content, dom); } public destroy() { diff --git a/src/plugins/embeddable/public/lib/panel/embeddable_panel.test.tsx b/src/plugins/embeddable/public/lib/panel/embeddable_panel.test.tsx index 78bd337b21e52..8d313030556c6 100644 --- a/src/plugins/embeddable/public/lib/panel/embeddable_panel.test.tsx +++ b/src/plugins/embeddable/public/lib/panel/embeddable_panel.test.tsx @@ -31,7 +31,7 @@ import { import { inspectorPluginMock } from '../../../../inspector/public/mocks'; import { EuiBadge } from '@elastic/eui'; import { embeddablePluginMock } from '../../mocks'; -import { applicationServiceMock } from '../../../../../core/public/mocks'; +import { applicationServiceMock, themeServiceMock } from '../../../../../core/public/mocks'; const actionRegistry = new Map(); const triggerRegistry = new Map(); @@ -44,6 +44,7 @@ const trigger: Trigger = { }; const embeddableFactory = new ContactCardEmbeddableFactory((() => null) as any, {} as any); const applicationMock = applicationServiceMock.createStartContract(); +const theme = themeServiceMock.createStartContract(); actionRegistry.set(editModeAction.id, editModeAction); triggerRegistry.set(trigger.id, trigger); @@ -152,6 +153,7 @@ test('HelloWorldContainer in view mode hides edit mode actions', async () => { overlays={{} as any} inspector={inspector} SavedObjectFinder={() => null} + theme={theme} /> ); @@ -191,6 +193,7 @@ const renderInEditModeAndOpenContextMenu = async ( application={applicationMock} inspector={inspector} SavedObjectFinder={() => null} + theme={theme} /> ); @@ -298,6 +301,7 @@ test('HelloWorldContainer in edit mode shows edit mode actions', async () => { application={applicationMock} inspector={inspector} SavedObjectFinder={() => null} + theme={theme} /> ); @@ -360,6 +364,7 @@ test('Panel title customize link does not exist in view mode', async () => { application={applicationMock} inspector={inspector} SavedObjectFinder={() => null} + theme={theme} /> ); @@ -395,6 +400,7 @@ test('Runs customize panel action on title click when in edit mode', async () => application={applicationMock} inspector={inspector} SavedObjectFinder={() => null} + theme={theme} /> ); @@ -443,6 +449,7 @@ test('Updates when hidePanelTitles is toggled', async () => { application={applicationMock} inspector={inspector} SavedObjectFinder={() => null} + theme={theme} /> ); @@ -497,6 +504,7 @@ test('Check when hide header option is false', async () => { inspector={inspector} SavedObjectFinder={() => null} hideHeader={false} + theme={theme} /> ); @@ -535,6 +543,7 @@ test('Check when hide header option is true', async () => { inspector={inspector} SavedObjectFinder={() => null} hideHeader={true} + theme={theme} /> ); @@ -567,6 +576,7 @@ test('Should work in minimal way rendering only the inspector action', async () getActions={() => Promise.resolve([])} inspector={inspector} hideHeader={false} + theme={theme} /> ); diff --git a/src/plugins/embeddable/public/lib/panel/embeddable_panel.tsx b/src/plugins/embeddable/public/lib/panel/embeddable_panel.tsx index 6748e9f3b1d08..2e501984dfa76 100644 --- a/src/plugins/embeddable/public/lib/panel/embeddable_panel.tsx +++ b/src/plugins/embeddable/public/lib/panel/embeddable_panel.tsx @@ -12,7 +12,7 @@ import React from 'react'; import { Subscription } from 'rxjs'; import deepEqual from 'fast-deep-equal'; import { buildContextMenuForActions, UiActionsService, Action } from '../ui_actions'; -import { CoreStart, OverlayStart } from '../../../../../core/public'; +import { CoreStart, OverlayStart, ThemeServiceStart } from '../../../../../core/public'; import { toMountPoint } from '../../../../kibana_react/public'; import { UsageCollectionStart } from '../../../../usage_collection/public'; @@ -83,6 +83,7 @@ interface Props { showBadges?: boolean; showNotifications?: boolean; containerContext?: EmbeddableContainerContext; + theme: ThemeServiceStart; } interface State { @@ -347,8 +348,7 @@ export class EmbeddablePanel extends React.Component { ) { return actions; } - - const createGetUserData = (overlays: OverlayStart) => + const createGetUserData = (overlays: OverlayStart, theme: ThemeServiceStart) => async function getUserData(context: { embeddable: IEmbeddable }) { return new Promise<{ title: string | undefined; hideTitle?: boolean }>((resolve) => { const session = overlays.openModal( @@ -360,7 +360,8 @@ export class EmbeddablePanel extends React.Component { resolve({ title, hideTitle }); }} cancel={() => session.close()} - /> + />, + { theme$: theme.theme$ } ), { 'data-test-subj': 'customizePanel', @@ -373,13 +374,16 @@ export class EmbeddablePanel extends React.Component { // registry. return { ...actions, - customizePanelTitle: new CustomizePanelTitleAction(createGetUserData(this.props.overlays)), + customizePanelTitle: new CustomizePanelTitleAction( + createGetUserData(this.props.overlays, this.props.theme) + ), addPanel: new AddPanelAction( this.props.getEmbeddableFactory, this.props.getAllEmbeddableFactories, this.props.overlays, this.props.notifications, this.props.SavedObjectFinder, + this.props.theme, this.props.reportUiCounter ), removePanel: new RemovePanelAction(), diff --git a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_action.test.tsx b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_action.test.tsx index 224cb80478769..fe6a9ea3c22b3 100644 --- a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_action.test.tsx +++ b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_action.test.tsx @@ -16,7 +16,7 @@ import { } from '../../../../test_samples/embeddables/filterable_embeddable'; import { FilterableEmbeddableFactory } from '../../../../test_samples/embeddables/filterable_embeddable_factory'; import { FilterableContainer } from '../../../../test_samples/embeddables/filterable_container'; -import { coreMock } from '../../../../../../../../core/public/mocks'; +import { coreMock, themeServiceMock } from '../../../../../../../../core/public/mocks'; import { ContactCardEmbeddable } from '../../../../test_samples'; import { EmbeddableStart } from '../../../../../plugin'; import { embeddablePluginMock } from '../../../../../mocks'; @@ -25,6 +25,7 @@ import { defaultTrigger } from '../../../../../../../ui_actions/public/triggers' const { setup, doStart } = embeddablePluginMock.createInstance(); setup.registerEmbeddableFactory(FILTERABLE_EMBEDDABLE, new FilterableEmbeddableFactory()); const getFactory = doStart().getEmbeddableFactory; +const theme = themeServiceMock.createStartContract(); let container: FilterableContainer; let embeddable: FilterableEmbeddable; @@ -37,7 +38,8 @@ beforeEach(async () => { () => [] as any, start.overlays, start.notifications, - () => null + () => null, + theme ); const derivedFilter: MockFilter = { @@ -72,7 +74,8 @@ test('Is not compatible when container is in view mode', async () => { () => [] as any, start.overlays, start.notifications, - () => null + () => null, + theme ); container.updateInput({ viewMode: ViewMode.VIEW }); expect( diff --git a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_action.ts b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_action.ts index 49be1c3ce0123..d766c509782a0 100644 --- a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_action.ts +++ b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_action.ts @@ -8,7 +8,7 @@ import { i18n } from '@kbn/i18n'; import { Action, ActionExecutionContext } from 'src/plugins/ui_actions/public'; -import { NotificationsStart, OverlayStart } from 'src/core/public'; +import { NotificationsStart, OverlayStart, ThemeServiceStart } from 'src/core/public'; import { EmbeddableStart } from 'src/plugins/embeddable/public/plugin'; import { ViewMode } from '../../../../types'; import { openAddPanelFlyout } from './open_add_panel_flyout'; @@ -31,6 +31,7 @@ export class AddPanelAction implements Action { private readonly overlays: OverlayStart, private readonly notifications: NotificationsStart, private readonly SavedObjectFinder: React.ComponentType, + private readonly theme: ThemeServiceStart, private readonly reportUiCounter?: UsageCollectionStart['reportUiCounter'] ) {} @@ -63,6 +64,7 @@ export class AddPanelAction implements Action { notifications: this.notifications, SavedObjectFinder: this.SavedObjectFinder, reportUiCounter: this.reportUiCounter, + theme: this.theme, }); } } diff --git a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/open_add_panel_flyout.tsx b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/open_add_panel_flyout.tsx index fe54b3d134aa0..00c6f99abda09 100644 --- a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/open_add_panel_flyout.tsx +++ b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/open_add_panel_flyout.tsx @@ -7,7 +7,7 @@ */ import React from 'react'; -import { NotificationsStart, OverlayRef, OverlayStart } from 'src/core/public'; +import { NotificationsStart, OverlayRef, OverlayStart, ThemeServiceStart } from 'src/core/public'; import { EmbeddableStart } from '../../../../../plugin'; import { toMountPoint } from '../../../../../../../kibana_react/public'; import { IContainer } from '../../../../containers'; @@ -23,6 +23,7 @@ export function openAddPanelFlyout(options: { SavedObjectFinder: React.ComponentType; showCreateNewMenu?: boolean; reportUiCounter?: UsageCollectionStart['reportUiCounter']; + theme: ThemeServiceStart; }): OverlayRef { const { embeddable, @@ -33,6 +34,7 @@ export function openAddPanelFlyout(options: { SavedObjectFinder, showCreateNewMenu, reportUiCounter, + theme, } = options; const flyoutSession = overlays.openFlyout( toMountPoint( @@ -49,7 +51,8 @@ export function openAddPanelFlyout(options: { reportUiCounter={reportUiCounter} SavedObjectFinder={SavedObjectFinder} showCreateNewMenu={showCreateNewMenu} - /> + />, + { theme$: theme.theme$ } ), { 'data-test-subj': 'dashboardAddPanel', diff --git a/src/plugins/embeddable/public/mocks.tsx b/src/plugins/embeddable/public/mocks.tsx index 94eb5e5cc6a02..44d2b395a48c3 100644 --- a/src/plugins/embeddable/public/mocks.tsx +++ b/src/plugins/embeddable/public/mocks.tsx @@ -20,7 +20,7 @@ import { ReferenceOrValueEmbeddable, } from '.'; import { EmbeddablePublicPlugin } from './plugin'; -import { coreMock } from '../../../core/public/mocks'; +import { coreMock, themeServiceMock } from '../../../core/public/mocks'; import { UiActionsService } from './lib/ui_actions'; import { CoreStart } from '../../../core/public'; import { Start as InspectorStart } from '../../inspector/public'; @@ -43,6 +43,8 @@ interface CreateEmbeddablePanelMockArgs { SavedObjectFinder: React.ComponentType; } +const theme = themeServiceMock.createStartContract(); + export const createEmbeddablePanelMock = ({ getActions, getEmbeddableFactory, @@ -64,6 +66,7 @@ export const createEmbeddablePanelMock = ({ overlays={overlays || ({} as any)} inspector={inspector || ({} as any)} SavedObjectFinder={SavedObjectFinder || (() => null)} + theme={theme} /> ); }; diff --git a/src/plugins/embeddable/public/plugin.tsx b/src/plugins/embeddable/public/plugin.tsx index 465c5d741d5a9..041207f2f2380 100644 --- a/src/plugins/embeddable/public/plugin.tsx +++ b/src/plugins/embeddable/public/plugin.tsx @@ -52,6 +52,7 @@ import { getTelemetryFunction, } from '../common/lib'; import { getAllMigrations } from '../common/lib/get_all_migrations'; +import { setTheme } from './services'; export interface EmbeddableSetupDependencies { uiActions: UiActionsSetup; @@ -119,6 +120,7 @@ export class EmbeddablePublicPlugin implements Plugin ); diff --git a/src/plugins/embeddable/public/services.ts b/src/plugins/embeddable/public/services.ts new file mode 100644 index 0000000000000..96088e086a771 --- /dev/null +++ b/src/plugins/embeddable/public/services.ts @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { ThemeServiceSetup } from 'src/core/public'; +import { createGetterSetter } from '../../kibana_utils/public'; + +export const [getTheme, setTheme] = createGetterSetter('Theme'); diff --git a/src/plugins/inspector/public/plugin.tsx b/src/plugins/inspector/public/plugin.tsx index e561a9719b3fb..14a141f7c2ec1 100644 --- a/src/plugins/inspector/public/plugin.tsx +++ b/src/plugins/inspector/public/plugin.tsx @@ -106,7 +106,8 @@ export class InspectorPublicPlugin implements Plugin { uiSettings: core.uiSettings, share: startDeps.share, }} - /> + />, + { theme$: core.theme.theme$ } ), { 'data-test-subj': 'inspectorPanel', diff --git a/src/plugins/kibana_react/public/notifications/create_notifications.tsx b/src/plugins/kibana_react/public/notifications/create_notifications.tsx index 2e59e611fc421..8eb16a5580ab3 100644 --- a/src/plugins/kibana_react/public/notifications/create_notifications.tsx +++ b/src/plugins/kibana_react/public/notifications/create_notifications.tsx @@ -24,8 +24,8 @@ export const createNotifications = (services: KibanaServices): KibanaReactNotifi throw new TypeError('Could not show notification as notifications service is not available.'); } services.notifications!.toasts.add({ - title: toMountPoint(title), - text: toMountPoint(<>{body || null}), + title: toMountPoint(title, { theme$: services.theme?.theme$ }), + text: toMountPoint(<>{body || null}, { theme$: services.theme?.theme$ }), color, iconType, toastLifeTimeMs, diff --git a/src/plugins/kibana_react/public/overlays/create_react_overlays.tsx b/src/plugins/kibana_react/public/overlays/create_react_overlays.tsx index 3274699e4bd69..4349e39d04fd5 100644 --- a/src/plugins/kibana_react/public/overlays/create_react_overlays.tsx +++ b/src/plugins/kibana_react/public/overlays/create_react_overlays.tsx @@ -20,12 +20,18 @@ export const createReactOverlays = (services: KibanaServices): KibanaReactOverla const openFlyout: KibanaReactOverlays['openFlyout'] = (node, options?) => { checkCoreService(); - return services.overlays!.openFlyout(toMountPoint(<>{node}), options); + return services.overlays!.openFlyout( + toMountPoint(<>{node}, { theme$: services.theme?.theme$ }), + options + ); }; const openModal: KibanaReactOverlays['openModal'] = (node, options?) => { checkCoreService(); - return services.overlays!.openModal(toMountPoint(<>{node}), options); + return services.overlays!.openModal( + toMountPoint(<>{node}, { theme$: services.theme?.theme$ }), + options + ); }; const overlays: KibanaReactOverlays = { diff --git a/src/plugins/kibana_react/public/table_list_view/table_list_view.test.tsx b/src/plugins/kibana_react/public/table_list_view/table_list_view.test.tsx index 3663f156c69cb..bdc5ca30216bc 100644 --- a/src/plugins/kibana_react/public/table_list_view/table_list_view.test.tsx +++ b/src/plugins/kibana_react/public/table_list_view/table_list_view.test.tsx @@ -10,6 +10,7 @@ import { EuiEmptyPrompt } from '@elastic/eui'; import { shallowWithIntl } from '@kbn/test/jest'; import { ToastsStart } from 'kibana/public'; import React from 'react'; +import { themeServiceMock } from '../../../../../src/core/public/mocks'; import { TableListView } from './table_list_view'; const requiredProps = { @@ -24,6 +25,7 @@ const requiredProps = { tableCaption: 'test caption', toastNotifications: {} as ToastsStart, findItems: jest.fn(() => Promise.resolve({ total: 0, hits: [] })), + theme: themeServiceMock.createStartContract(), }; describe('TableListView', () => { diff --git a/src/plugins/kibana_react/public/table_list_view/table_list_view.tsx b/src/plugins/kibana_react/public/table_list_view/table_list_view.tsx index 65c62543538d0..dd023d522dbb6 100644 --- a/src/plugins/kibana_react/public/table_list_view/table_list_view.tsx +++ b/src/plugins/kibana_react/public/table_list_view/table_list_view.tsx @@ -20,7 +20,7 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; -import { HttpFetchError, ToastsStart } from 'kibana/public'; +import { ThemeServiceStart, HttpFetchError, ToastsStart } from 'kibana/public'; import { debounce, keyBy, sortBy, uniq } from 'lodash'; import React from 'react'; import { KibanaPageTemplate } from '../page_template'; @@ -57,6 +57,7 @@ export interface TableListViewProps { */ tableCaption: string; searchFilters?: SearchFilterConfig[]; + theme: ThemeServiceStart; } export interface TableListViewState { @@ -177,7 +178,8 @@ class TableListView extends React.Component< id="kibana-react.tableListView.listing.unableToDeleteDangerMessage" defaultMessage="Unable to delete {entityName}(s)" values={{ entityName: this.props.entityName }} - /> + />, + { theme$: this.props.theme.theme$ } ), text: `${error}`, }); diff --git a/src/plugins/kibana_react/public/theme/kibana_theme_provider.tsx b/src/plugins/kibana_react/public/theme/kibana_theme_provider.tsx index 65d640f34a2ca..56ca7642f1cde 100644 --- a/src/plugins/kibana_react/public/theme/kibana_theme_provider.tsx +++ b/src/plugins/kibana_react/public/theme/kibana_theme_provider.tsx @@ -21,8 +21,9 @@ const defaultTheme: CoreTheme = { darkMode: false, }; -// IMPORTANT: This code has been copied to the `interactive_setup` plugin, any changes here should be applied there too. -// That copy and this comment can be removed once https://github.com/elastic/kibana/issues/119204 is implemented. +/* IMPORTANT: This code has been copied to the `interactive_setup` plugin, any changes here should be applied there too. +That copy and this comment can be removed once https://github.com/elastic/kibana/issues/119204 is implemented.*/ +// IMPORTANT: This code has been copied to the `kibana_utils` plugin, to avoid cyclical dependency, any changes here should be applied there too. export const KibanaThemeProvider: FC = ({ theme$, children }) => { const theme = useObservable(theme$, defaultTheme); diff --git a/src/plugins/kibana_react/public/theme/utils.ts b/src/plugins/kibana_react/public/theme/utils.ts index 161f3a5e36b76..fe8092949d431 100644 --- a/src/plugins/kibana_react/public/theme/utils.ts +++ b/src/plugins/kibana_react/public/theme/utils.ts @@ -10,8 +10,9 @@ import { COLOR_MODES_STANDARD } from '@elastic/eui'; import type { EuiThemeColorModeStandard } from '@elastic/eui'; import type { CoreTheme } from '../../../../core/public'; -// IMPORTANT: This code has been copied to the `interactive_setup` plugin, any changes here should be applied there too. -// That copy and this comment can be removed once https://github.com/elastic/kibana/issues/119204 is implemented. +/* IMPORTANT: This code has been copied to the `interactive_setup` plugin, any changes here should be applied there too. +That copy and this comment can be removed once https://github.com/elastic/kibana/issues/119204 is implemented.*/ +// IMPORTANT: This code has been copied to the `kibana_utils` plugin, to avoid cyclical dependency, any changes here should be applied there too. export const getColorMode = (theme: CoreTheme): EuiThemeColorModeStandard => { return theme.darkMode ? COLOR_MODES_STANDARD.dark : COLOR_MODES_STANDARD.light; diff --git a/src/plugins/kibana_utils/public/history/redirect_when_missing.tsx b/src/plugins/kibana_utils/public/history/redirect_when_missing.tsx index c64ac35e6f83f..6913c94a6bb5f 100644 --- a/src/plugins/kibana_utils/public/history/redirect_when_missing.tsx +++ b/src/plugins/kibana_utils/public/history/redirect_when_missing.tsx @@ -13,7 +13,9 @@ import { EuiLoadingSpinner } from '@elastic/eui'; import ReactDOM from 'react-dom'; import { ApplicationStart, HttpStart, ToastsSetup } from 'kibana/public'; +import type { ThemeServiceStart } from '../../../../core/public'; import { SavedObjectNotFound } from '..'; +import { KibanaThemeProvider } from '../theme'; const ReactMarkdown = React.lazy(() => import('react-markdown')); const ErrorRenderer = (props: { children: string }) => ( @@ -45,6 +47,7 @@ export function redirectWhenMissing({ mapping, toastNotifications, onBeforeRedirect, + theme, }: { history: History; navigateToApp: ApplicationStart['navigateToApp']; @@ -62,6 +65,7 @@ export function redirectWhenMissing({ * Optional callback invoked directly before a redirect is triggered */ onBeforeRedirect?: (error: SavedObjectNotFound) => void; + theme: ThemeServiceStart; }) { let localMappingObject: Mapping; @@ -92,7 +96,12 @@ export function redirectWhenMissing({ defaultMessage: 'Saved object is missing', }), text: (element: HTMLElement) => { - ReactDOM.render({error.message}, element); + ReactDOM.render( + + {error.message} + , + element + ); return () => ReactDOM.unmountComponentAtNode(element); }, }); diff --git a/src/plugins/kibana_utils/public/theme/index.ts b/src/plugins/kibana_utils/public/theme/index.ts new file mode 100644 index 0000000000000..165c5ef9195c2 --- /dev/null +++ b/src/plugins/kibana_utils/public/theme/index.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export { KibanaThemeProvider } from './kibana_theme_provider'; diff --git a/src/plugins/kibana_utils/public/theme/kibana_theme_provider.test.tsx b/src/plugins/kibana_utils/public/theme/kibana_theme_provider.test.tsx new file mode 100644 index 0000000000000..21059bd4a8236 --- /dev/null +++ b/src/plugins/kibana_utils/public/theme/kibana_theme_provider.test.tsx @@ -0,0 +1,88 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { useEuiTheme } from '@elastic/eui'; +import type { ReactWrapper } from 'enzyme'; +import type { FC } from 'react'; +import React, { useEffect } from 'react'; +import { act } from 'react-dom/test-utils'; +import { BehaviorSubject, of } from 'rxjs'; + +import { mountWithIntl } from '@kbn/test/jest'; +import type { CoreTheme } from 'src/core/public'; + +import { KibanaThemeProvider } from './kibana_theme_provider'; + +describe('KibanaThemeProvider', () => { + let euiTheme: ReturnType | undefined; + + beforeEach(() => { + euiTheme = undefined; + }); + + const flushPromises = async () => { + await new Promise(async (resolve, reject) => { + try { + setImmediate(() => resolve()); + } catch (error) { + reject(error); + } + }); + }; + + const InnerComponent: FC = () => { + const theme = useEuiTheme(); + useEffect(() => { + euiTheme = theme; + }, [theme]); + return
foo
; + }; + + const refresh = async (wrapper: ReactWrapper) => { + await act(async () => { + await flushPromises(); + wrapper.update(); + }); + }; + + it('exposes the EUI theme provider', async () => { + const coreTheme: CoreTheme = { darkMode: true }; + + const wrapper = mountWithIntl( + + + + ); + + await refresh(wrapper); + + expect(euiTheme!.colorMode).toEqual('DARK'); + }); + + it('propagates changes of the coreTheme observable', async () => { + const coreTheme$ = new BehaviorSubject({ darkMode: true }); + + const wrapper = mountWithIntl( + + + + ); + + await refresh(wrapper); + + expect(euiTheme!.colorMode).toEqual('DARK'); + + await act(async () => { + coreTheme$.next({ darkMode: false }); + }); + + await refresh(wrapper); + + expect(euiTheme!.colorMode).toEqual('LIGHT'); + }); +}); diff --git a/src/plugins/kibana_utils/public/theme/kibana_theme_provider.tsx b/src/plugins/kibana_utils/public/theme/kibana_theme_provider.tsx new file mode 100644 index 0000000000000..7c7963eff984b --- /dev/null +++ b/src/plugins/kibana_utils/public/theme/kibana_theme_provider.tsx @@ -0,0 +1,33 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { EuiThemeProvider } from '@elastic/eui'; +import type { FC } from 'react'; +import React, { useMemo } from 'react'; +import useObservable from 'react-use/lib/useObservable'; +import type { Observable } from 'rxjs'; + +import type { CoreTheme } from '../../../../core/public'; +import { getColorMode } from './utils'; + +interface KibanaThemeProviderProps { + theme$: Observable; +} + +const defaultTheme: CoreTheme = { + darkMode: false, +}; + +/** + * Copied from the `kibana_react` plugin, to avoid cyclical dependency + */ +export const KibanaThemeProvider: FC = ({ theme$, children }) => { + const theme = useObservable(theme$, defaultTheme); + const colorMode = useMemo(() => getColorMode(theme), [theme]); + return {children}; +}; diff --git a/src/plugins/kibana_utils/public/theme/utils.test.ts b/src/plugins/kibana_utils/public/theme/utils.test.ts new file mode 100644 index 0000000000000..57b37f4fb2f62 --- /dev/null +++ b/src/plugins/kibana_utils/public/theme/utils.test.ts @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { getColorMode } from './utils'; + +describe('getColorMode', () => { + it('returns the correct `colorMode` when `darkMode` is enabled', () => { + expect(getColorMode({ darkMode: true })).toEqual('DARK'); + }); + + it('returns the correct `colorMode` when `darkMode` is disabled', () => { + expect(getColorMode({ darkMode: false })).toEqual('LIGHT'); + }); +}); diff --git a/src/plugins/kibana_utils/public/theme/utils.ts b/src/plugins/kibana_utils/public/theme/utils.ts new file mode 100644 index 0000000000000..887e4fe61fbe1 --- /dev/null +++ b/src/plugins/kibana_utils/public/theme/utils.ts @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { COLOR_MODES_STANDARD } from '@elastic/eui'; +import type { EuiThemeColorModeStandard } from '@elastic/eui'; + +import type { CoreTheme } from '../../../../core/public'; + +/** + * Copied from the `kibana_react` plugin, to avoid cyclical dependency + */ +export const getColorMode = (theme: CoreTheme): EuiThemeColorModeStandard => { + return theme.darkMode ? COLOR_MODES_STANDARD.dark : COLOR_MODES_STANDARD.light; +}; diff --git a/src/plugins/kibana_utils/tsconfig.json b/src/plugins/kibana_utils/tsconfig.json index 0538b145a5d62..0fba68be6aa57 100644 --- a/src/plugins/kibana_utils/tsconfig.json +++ b/src/plugins/kibana_utils/tsconfig.json @@ -14,7 +14,5 @@ "index.ts", "../../../typings/**/*" ], - "references": [ - { "path": "../../core/tsconfig.json" } - ] + "references": [{ "path": "../../core/tsconfig.json" }] } diff --git a/src/plugins/share/kibana.json b/src/plugins/share/kibana.json index 2e34da1da0287..08365d895f4b4 100644 --- a/src/plugins/share/kibana.json +++ b/src/plugins/share/kibana.json @@ -8,6 +8,6 @@ "githubTeam": "kibana-app-services" }, "description": "Adds URL Service and sharing capabilities to Kibana", - "requiredBundles": ["kibanaUtils"], + "requiredBundles": ["kibanaReact", "kibanaUtils"], "optionalPlugins": [] } diff --git a/src/plugins/share/public/services/share_menu_manager.tsx b/src/plugins/share/public/services/share_menu_manager.tsx index 52f000512aa07..237e71009d205 100644 --- a/src/plugins/share/public/services/share_menu_manager.tsx +++ b/src/plugins/share/public/services/share_menu_manager.tsx @@ -11,7 +11,8 @@ import ReactDOM from 'react-dom'; import { I18nProvider } from '@kbn/i18n-react'; import { EuiWrappingPopover } from '@elastic/eui'; -import { CoreStart, HttpStart } from 'kibana/public'; +import { CoreStart, HttpStart, ThemeServiceStart } from 'kibana/public'; +import { KibanaThemeProvider } from '../../../kibana_react/public'; import { ShareContextMenu } from '../components/share_context_menu'; import { ShareMenuItem, ShowShareMenuOptions } from '../types'; import { ShareMenuRegistryStart } from './share_menu_registry'; @@ -42,6 +43,7 @@ export class ShareMenuManager { post: core.http.post, basePath: core.http.basePath.get(), anonymousAccess, + theme: core.theme, }); }, }; @@ -65,12 +67,14 @@ export class ShareMenuManager { basePath, embedUrlParamExtensions, anonymousAccess, + theme, showPublicUrlSwitch, }: ShowShareMenuOptions & { menuItems: ShareMenuItem[]; post: HttpStart['post']; basePath: string; anonymousAccess: AnonymousAccessServiceContract | undefined; + theme: ThemeServiceStart; }) { if (this.isOpen) { this.onClose(); @@ -82,30 +86,32 @@ export class ShareMenuManager { document.body.appendChild(this.container); const element = ( - - - + + + + + ); ReactDOM.render(element, this.container); diff --git a/src/plugins/share/public/url_service/redirect/components/page.tsx b/src/plugins/share/public/url_service/redirect/components/page.tsx index 805213b73fdd0..f6aa4d62767c5 100644 --- a/src/plugins/share/public/url_service/redirect/components/page.tsx +++ b/src/plugins/share/public/url_service/redirect/components/page.tsx @@ -9,38 +9,45 @@ import * as React from 'react'; import useObservable from 'react-use/lib/useObservable'; import { EuiPageTemplate } from '@elastic/eui'; +import { ThemeServiceSetup } from 'kibana/public'; import { Error } from './error'; import { RedirectManager } from '../redirect_manager'; import { Spinner } from './spinner'; +import { KibanaThemeProvider } from '../../../../../kibana_react/public'; export interface PageProps { manager: Pick; + theme: ThemeServiceSetup; } -export const Page: React.FC = ({ manager }) => { +export const Page: React.FC = ({ manager, theme }) => { const error = useObservable(manager.error$); if (error) { return ( + + + + + + ); + } + + return ( + - + - ); - } - - return ( - - - + ); }; diff --git a/src/plugins/share/public/url_service/redirect/redirect_manager.ts b/src/plugins/share/public/url_service/redirect/redirect_manager.ts index e6f524347e48c..9d7357eab310c 100644 --- a/src/plugins/share/public/url_service/redirect/redirect_manager.ts +++ b/src/plugins/share/public/url_service/redirect/redirect_manager.ts @@ -29,7 +29,7 @@ export class RedirectManager { chromeless: true, mount: async (params) => { const { render } = await import('./render'); - const unmount = render(params.element, { manager: this }); + const unmount = render(params.element, { manager: this, theme: core.theme }); this.onMount(params.history.location.search); return () => { unmount(); diff --git a/src/plugins/share/tsconfig.json b/src/plugins/share/tsconfig.json index 1f9c438f03fc4..2633d840895d6 100644 --- a/src/plugins/share/tsconfig.json +++ b/src/plugins/share/tsconfig.json @@ -9,6 +9,7 @@ "include": ["common/**/*", "public/**/*", "server/**/*"], "references": [ { "path": "../../core/tsconfig.json" }, - { "path": "../kibana_utils/tsconfig.json" }, + { "path": "../kibana_react/tsconfig.json" }, + { "path": "../kibana_utils/tsconfig.json" } ] } diff --git a/src/plugins/ui_actions/public/context_menu/open_context_menu.tsx b/src/plugins/ui_actions/public/context_menu/open_context_menu.tsx index 91cb8099e8b3c..04449d7b656bc 100644 --- a/src/plugins/ui_actions/public/context_menu/open_context_menu.tsx +++ b/src/plugins/ui_actions/public/context_menu/open_context_menu.tsx @@ -11,6 +11,8 @@ import React from 'react'; import { EuiContextMenu, EuiContextMenuPanelDescriptor, EuiPopover } from '@elastic/eui'; import { EventEmitter } from 'events'; import ReactDOM from 'react-dom'; +import { KibanaThemeProvider } from '../../../kibana_react/public'; +import { getTheme } from '../services'; let activeSession: ContextMenuSession | null = null; @@ -168,20 +170,22 @@ export function openContextMenu( }; ReactDOM.render( - - - , + + + + + , container ); diff --git a/src/plugins/ui_actions/public/plugin.ts b/src/plugins/ui_actions/public/plugin.ts index ea6a7e42815cb..2a2ad100a53d3 100644 --- a/src/plugins/ui_actions/public/plugin.ts +++ b/src/plugins/ui_actions/public/plugin.ts @@ -10,6 +10,7 @@ import { CoreStart, CoreSetup, Plugin, PluginInitializerContext } from 'src/core import { PublicMethodsOf } from '@kbn/utility-types'; import { UiActionsService } from './service'; import { rowClickTrigger, visualizeFieldTrigger, visualizeGeoFieldTrigger } from './triggers'; +import { setTheme } from './services'; export type UiActionsSetup = Pick< UiActionsService, @@ -29,6 +30,7 @@ export class UiActionsPlugin implements Plugin { constructor(initializerContext: PluginInitializerContext) {} public setup(core: CoreSetup): UiActionsSetup { + setTheme(core.theme); this.service.registerTrigger(rowClickTrigger); this.service.registerTrigger(visualizeFieldTrigger); this.service.registerTrigger(visualizeGeoFieldTrigger); diff --git a/src/plugins/ui_actions/public/services.ts b/src/plugins/ui_actions/public/services.ts new file mode 100644 index 0000000000000..96088e086a771 --- /dev/null +++ b/src/plugins/ui_actions/public/services.ts @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { ThemeServiceSetup } from 'src/core/public'; +import { createGetterSetter } from '../../kibana_utils/public'; + +export const [getTheme, setTheme] = createGetterSetter('Theme'); diff --git a/src/plugins/visualizations/public/visualize_app/components/visualize_listing.tsx b/src/plugins/visualizations/public/visualize_app/components/visualize_listing.tsx index 24f0a871a12f7..cf219b1cda117 100644 --- a/src/plugins/visualizations/public/visualize_app/components/visualize_listing.tsx +++ b/src/plugins/visualizations/public/visualize_app/components/visualize_listing.tsx @@ -42,6 +42,7 @@ export const VisualizeListing = () => { visualizeCapabilities, dashboardCapabilities, kbnUrlStateStorage, + theme, }, } = useKibana(); const { pathname } = useLocation(); @@ -201,6 +202,7 @@ export const VisualizeListing = () => { })} toastNotifications={toastNotifications} searchFilters={searchFilters} + theme={theme} > {dashboardCapabilities.createNew && ( <> diff --git a/src/plugins/visualizations/public/visualize_app/types.ts b/src/plugins/visualizations/public/visualize_app/types.ts index cca4d9a48d104..a414dd2e61762 100644 --- a/src/plugins/visualizations/public/visualize_app/types.ts +++ b/src/plugins/visualizations/public/visualize_app/types.ts @@ -17,6 +17,7 @@ import type { ToastsStart, ScopedHistory, AppMountParameters, + ThemeServiceStart, } from 'kibana/public'; import type { @@ -105,6 +106,7 @@ export interface VisualizeServices extends CoreStart { usageCollection?: UsageCollectionStart; getKibanaVersion: () => string; spaces?: SpacesPluginStart; + theme: ThemeServiceStart; visEditorsRegistry: VisEditorsRegistry; } diff --git a/src/plugins/visualizations/public/visualize_app/utils/utils.ts b/src/plugins/visualizations/public/visualize_app/utils/utils.ts index a99b756fe8714..b3257f03354a6 100644 --- a/src/plugins/visualizations/public/visualize_app/utils/utils.ts +++ b/src/plugins/visualizations/public/visualize_app/utils/utils.ts @@ -92,5 +92,6 @@ export const redirectToSavedObjectPage = ( onBeforeRedirect() { setActiveUrl(VisualizeConstants.LANDING_PAGE_PATH); }, + theme: services.theme, })(error); }; diff --git a/x-pack/examples/reporting_example/kibana.json b/x-pack/examples/reporting_example/kibana.json index 94780f1df0b36..489b2bcd9f506 100644 --- a/x-pack/examples/reporting_example/kibana.json +++ b/x-pack/examples/reporting_example/kibana.json @@ -10,6 +10,13 @@ }, "description": "Example integration code for applications to feature reports.", "optionalPlugins": [], - "requiredPlugins": ["reporting", "developerExamples", "navigation", "screenshotMode", "share"], + "requiredPlugins": [ + "reporting", + "developerExamples", + "kibanaReact", + "navigation", + "screenshotMode", + "share" + ], "requiredBundles": ["screenshotting"] } diff --git a/x-pack/examples/reporting_example/public/application.tsx b/x-pack/examples/reporting_example/public/application.tsx index 3e1afd7c517a2..9b044ac801773 100644 --- a/x-pack/examples/reporting_example/public/application.tsx +++ b/x-pack/examples/reporting_example/public/application.tsx @@ -9,6 +9,7 @@ import React from 'react'; import ReactDOM from 'react-dom'; import { Router, Route, Switch } from 'react-router-dom'; import { AppMountParameters, CoreStart } from '../../../../src/core/public'; +import { KibanaThemeProvider } from '../../../../../kibana/src/plugins/kibana_react/public'; import { CaptureTest } from './containers/capture_test'; import { Main } from './containers/main'; import { ApplicationContextProvider } from './application_context'; @@ -23,12 +24,14 @@ export const renderApp = ( ) => { ReactDOM.render( - - - } /> -
} /> - - + + + + } /> +
} /> + + + , element ); diff --git a/x-pack/examples/reporting_example/tsconfig.json b/x-pack/examples/reporting_example/tsconfig.json index 4c4016911e0c5..1b097d8e52868 100644 --- a/x-pack/examples/reporting_example/tsconfig.json +++ b/x-pack/examples/reporting_example/tsconfig.json @@ -9,15 +9,15 @@ "public/**/*.tsx", "server/**/*.ts", "common/**/*.ts", - "../../../typings/**/*", + "../../../typings/**/*" ], "exclude": [], "references": [ { "path": "../../../src/core/tsconfig.json" }, + { "path": "../../../src/plugins/kibana_react/tsconfig.json" }, { "path": "../../../src/plugins/navigation/tsconfig.json" }, { "path": "../../../src/plugins/screenshot_mode/tsconfig.json" }, { "path": "../../../examples/developer_examples/tsconfig.json" }, - { "path": "../../plugins/reporting/tsconfig.json" }, + { "path": "../../plugins/reporting/tsconfig.json" } ] } - diff --git a/x-pack/plugins/data_enhanced/public/plugin.ts b/x-pack/plugins/data_enhanced/public/plugin.ts index 6ec645c932e05..ee76cce9b9d2b 100644 --- a/x-pack/plugins/data_enhanced/public/plugin.ts +++ b/x-pack/plugins/data_enhanced/public/plugin.ts @@ -81,7 +81,8 @@ export class DataEnhancedPlugin usageCollector: this.usageCollector, tourDisabled: plugins.screenshotMode.isScreenshotMode(), }) - ) + ), + { theme$: core.theme.theme$ } ), }); } diff --git a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/actions/delete_button.tsx b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/actions/delete_button.tsx index 3d1a3052e720b..127a63b647a24 100644 --- a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/actions/delete_button.tsx +++ b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/actions/delete_button.tsx @@ -74,7 +74,8 @@ export const createDeleteActionDescriptor = ( onClick: async () => { const ref = core.overlays.openModal( toMountPoint( - ref?.close()} searchSession={uiSession} api={api} /> + ref?.close()} searchSession={uiSession} api={api} />, + { theme$: core.theme.theme$ } ) ); await ref.onClose; diff --git a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/actions/extend_button.tsx b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/actions/extend_button.tsx index 6989caeca359e..d8b5e9de16688 100644 --- a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/actions/extend_button.tsx +++ b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/actions/extend_button.tsx @@ -81,7 +81,8 @@ export const createExtendActionDescriptor = ( onClick: async () => { const ref = core.overlays.openModal( toMountPoint( - ref?.close()} searchSession={uiSession} api={api} /> + ref?.close()} searchSession={uiSession} api={api} />, + { theme$: core.theme.theme$ } ) ); await ref.onClose; diff --git a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/actions/inspect_button.tsx b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/actions/inspect_button.tsx index 23c010e0fbc67..2b917c28c4b3b 100644 --- a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/actions/inspect_button.tsx +++ b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/actions/inspect_button.tsx @@ -97,7 +97,7 @@ export const createInspectActionDescriptor = ( ), onClick: async () => { const flyout = ; - const overlay = core.overlays.openFlyout(toMountPoint(flyout)); + const overlay = core.overlays.openFlyout(toMountPoint(flyout, { theme$: core.theme.theme$ })); await overlay.onClose; }, }); diff --git a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/actions/rename_button.tsx b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/actions/rename_button.tsx index beb773e057cb9..d663d0da5cad7 100644 --- a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/actions/rename_button.tsx +++ b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/actions/rename_button.tsx @@ -113,7 +113,8 @@ export const createRenameActionDescriptor = ( onClick: async () => { const ref = core.overlays.openModal( toMountPoint( - ref?.close()} api={api} searchSession={uiSession} /> + ref?.close()} api={api} searchSession={uiSession} />, + { theme$: core.theme.theme$ } ) ); await ref.onClose; diff --git a/x-pack/plugins/graph/public/apps/listing_route.tsx b/x-pack/plugins/graph/public/apps/listing_route.tsx index 4ed0789f33fdf..dc70d84155bf9 100644 --- a/x-pack/plugins/graph/public/apps/listing_route.tsx +++ b/x-pack/plugins/graph/public/apps/listing_route.tsx @@ -102,6 +102,7 @@ export function ListingRoute({ tableListTitle={i18n.translate('xpack.graph.listing.graphsTitle', { defaultMessage: 'Graphs', })} + theme={coreStart.theme} /> ); diff --git a/x-pack/plugins/lens/public/embeddable/embeddable_component.tsx b/x-pack/plugins/lens/public/embeddable/embeddable_component.tsx index 7f65e50bf4429..e501138648b14 100644 --- a/x-pack/plugins/lens/public/embeddable/embeddable_component.tsx +++ b/x-pack/plugins/lens/public/embeddable/embeddable_component.tsx @@ -6,7 +6,7 @@ */ import React, { FC, useEffect } from 'react'; -import type { CoreStart } from 'kibana/public'; +import type { CoreStart, ThemeServiceStart } from 'kibana/public'; import type { UiActionsStart } from 'src/plugins/ui_actions/public'; import type { Start as InspectorStartContract } from 'src/plugins/inspector/public'; import { EuiLoadingChart } from '@elastic/eui'; @@ -68,6 +68,7 @@ export function getEmbeddableComponent(core: CoreStart, plugins: PluginsStartDep const input = { ...props }; const [embeddable, loading, error] = useEmbeddableFactory({ factory, input }); const hasActions = props.withActions === true; + const theme = core.theme; if (loading) { return ; @@ -81,6 +82,7 @@ export function getEmbeddableComponent(core: CoreStart, plugins: PluginsStartDep inspector={inspector} actionPredicate={() => hasActions} input={input} + theme={theme} /> ); } @@ -95,6 +97,7 @@ interface EmbeddablePanelWrapperProps { inspector: PluginsStartDependencies['inspector']; actionPredicate: (id: string) => boolean; input: EmbeddableComponentProps; + theme: ThemeServiceStart; } const EmbeddablePanelWrapper: FC = ({ @@ -103,6 +106,7 @@ const EmbeddablePanelWrapper: FC = ({ actionPredicate, inspector, input, + theme, }) => { useEffect(() => { embeddable.updateInput(input); @@ -118,6 +122,7 @@ const EmbeddablePanelWrapper: FC = ({ showShadow={false} showBadges={false} showNotifications={false} + theme={theme} /> ); }; diff --git a/x-pack/plugins/maps/public/kibana_services.ts b/x-pack/plugins/maps/public/kibana_services.ts index ddc2851f595b0..027981de32295 100644 --- a/x-pack/plugins/maps/public/kibana_services.ts +++ b/x-pack/plugins/maps/public/kibana_services.ts @@ -54,6 +54,7 @@ export const getSavedObjectsTagging = () => pluginsStart.savedObjectsTagging; export const getPresentationUtilContext = () => pluginsStart.presentationUtil.ContextProvider; export const getSecurityService = () => pluginsStart.security; export const getSpacesApi = () => pluginsStart.spaces; +export const getTheme = () => coreStart.theme; // xpack.maps.* kibana.yml settings from this plugin let mapAppConfig: MapsConfigType; diff --git a/x-pack/plugins/maps/public/routes/list_page/maps_list_view.tsx b/x-pack/plugins/maps/public/routes/list_page/maps_list_view.tsx index 7dc8c9c88d4ca..571cba64a06c4 100644 --- a/x-pack/plugins/maps/public/routes/list_page/maps_list_view.tsx +++ b/x-pack/plugins/maps/public/routes/list_page/maps_list_view.tsx @@ -21,6 +21,7 @@ import { getSavedObjectsClient, getSavedObjectsTagging, getSavedObjects, + getTheme, } from '../../kibana_services'; import { getAppTitle } from '../../../common/i18n_getters'; import { MapSavedObjectAttributes } from '../../../common/map_saved_object_type'; @@ -148,6 +149,7 @@ export function MapsListView() { tableListTitle={getAppTitle()} toastNotifications={getToasts()} searchFilters={searchFilters} + theme={getTheme()} /> ); } diff --git a/x-pack/plugins/reporting/public/lib/stream_handler.test.ts b/x-pack/plugins/reporting/public/lib/stream_handler.test.ts index 1bb8c3229407d..78742af7fe879 100644 --- a/x-pack/plugins/reporting/public/lib/stream_handler.test.ts +++ b/x-pack/plugins/reporting/public/lib/stream_handler.test.ts @@ -7,7 +7,7 @@ import sinon, { stub } from 'sinon'; import { NotificationsStart } from 'src/core/public'; -import { coreMock } from '../../../../../src/core/public/mocks'; +import { coreMock, themeServiceMock } from '../../../../../src/core/public/mocks'; import { JobSummary, ReportApiJSON } from '../../common/types'; import { Job } from './job'; import { ReportingAPIClient } from './reporting_api_client'; @@ -46,19 +46,21 @@ const notificationsMock = { }, } as unknown as NotificationsStart; +const theme = themeServiceMock.createStartContract(); + describe('stream handler', () => { afterEach(() => { sinon.reset(); }); it('constructs', () => { - const sh = new ReportingNotifierStreamHandler(notificationsMock, jobQueueClientMock); + const sh = new ReportingNotifierStreamHandler(notificationsMock, jobQueueClientMock, theme); expect(sh).not.toBe(null); }); describe('findChangedStatusJobs', () => { it('finds no changed status jobs from empty', (done) => { - const sh = new ReportingNotifierStreamHandler(notificationsMock, jobQueueClientMock); + const sh = new ReportingNotifierStreamHandler(notificationsMock, jobQueueClientMock, theme); const findJobs = sh.findChangedStatusJobs([]); findJobs.subscribe((data) => { expect(data).toEqual({ completed: [], failed: [] }); @@ -67,7 +69,7 @@ describe('stream handler', () => { }); it('finds changed status jobs', (done) => { - const sh = new ReportingNotifierStreamHandler(notificationsMock, jobQueueClientMock); + const sh = new ReportingNotifierStreamHandler(notificationsMock, jobQueueClientMock, theme); const findJobs = sh.findChangedStatusJobs([ 'job-source-mock1', 'job-source-mock2', @@ -83,7 +85,7 @@ describe('stream handler', () => { describe('showNotifications', () => { it('show success', (done) => { - const sh = new ReportingNotifierStreamHandler(notificationsMock, jobQueueClientMock); + const sh = new ReportingNotifierStreamHandler(notificationsMock, jobQueueClientMock, theme); sh.showNotifications({ completed: [ { @@ -104,7 +106,7 @@ describe('stream handler', () => { }); it('show max length warning', (done) => { - const sh = new ReportingNotifierStreamHandler(notificationsMock, jobQueueClientMock); + const sh = new ReportingNotifierStreamHandler(notificationsMock, jobQueueClientMock, theme); sh.showNotifications({ completed: [ { @@ -126,7 +128,7 @@ describe('stream handler', () => { }); it('show csv formulas warning', (done) => { - const sh = new ReportingNotifierStreamHandler(notificationsMock, jobQueueClientMock); + const sh = new ReportingNotifierStreamHandler(notificationsMock, jobQueueClientMock, theme); sh.showNotifications({ completed: [ { @@ -148,7 +150,7 @@ describe('stream handler', () => { }); it('show failed job toast', (done) => { - const sh = new ReportingNotifierStreamHandler(notificationsMock, jobQueueClientMock); + const sh = new ReportingNotifierStreamHandler(notificationsMock, jobQueueClientMock, theme); sh.showNotifications({ completed: [], failed: [ @@ -169,7 +171,7 @@ describe('stream handler', () => { }); it('show multiple toast', (done) => { - const sh = new ReportingNotifierStreamHandler(notificationsMock, jobQueueClientMock); + const sh = new ReportingNotifierStreamHandler(notificationsMock, jobQueueClientMock, theme); sh.showNotifications({ completed: [ { diff --git a/x-pack/plugins/reporting/public/lib/stream_handler.ts b/x-pack/plugins/reporting/public/lib/stream_handler.ts index 304b4fb73374d..27e220221156e 100644 --- a/x-pack/plugins/reporting/public/lib/stream_handler.ts +++ b/x-pack/plugins/reporting/public/lib/stream_handler.ts @@ -8,7 +8,7 @@ import { i18n } from '@kbn/i18n'; import * as Rx from 'rxjs'; import { catchError, map } from 'rxjs/operators'; -import { NotificationsSetup } from 'src/core/public'; +import { NotificationsSetup, ThemeServiceStart } from 'src/core/public'; import { JOB_COMPLETION_NOTIFICATIONS_SESSION_KEY, JOB_STATUSES } from '../../common/constants'; import { JobId, JobSummary, JobSummarySet } from '../../common/types'; import { @@ -37,7 +37,11 @@ function getReportStatus(src: Job): JobSummary { } export class ReportingNotifierStreamHandler { - constructor(private notifications: NotificationsSetup, private apiClient: ReportingAPIClient) {} + constructor( + private notifications: NotificationsSetup, + private apiClient: ReportingAPIClient, + private theme: ThemeServiceStart + ) {} /* * Use Kibana Toast API to show our messages @@ -54,7 +58,8 @@ export class ReportingNotifierStreamHandler { getWarningFormulasToast( job, this.apiClient.getManagementLink, - this.apiClient.getDownloadLink + this.apiClient.getDownloadLink, + this.theme ) ); } else if (job.maxSizeReached) { @@ -62,12 +67,18 @@ export class ReportingNotifierStreamHandler { getWarningMaxSizeToast( job, this.apiClient.getManagementLink, - this.apiClient.getDownloadLink + this.apiClient.getDownloadLink, + this.theme ) ); } else { this.notifications.toasts.addSuccess( - getSuccessToast(job, this.apiClient.getManagementLink, this.apiClient.getDownloadLink) + getSuccessToast( + job, + this.apiClient.getManagementLink, + this.apiClient.getDownloadLink, + this.theme + ) ); } } @@ -76,7 +87,7 @@ export class ReportingNotifierStreamHandler { for (const job of failedJobs) { const errorText = await this.apiClient.getError(job.id); this.notifications.toasts.addDanger( - getFailureToast(errorText, job, this.apiClient.getManagementLink) + getFailureToast(errorText, job, this.apiClient.getManagementLink, this.theme) ); } return { completed: completedJobs, failed: failedJobs }; @@ -120,7 +131,8 @@ export class ReportingNotifierStreamHandler { i18n.translate('xpack.reporting.publicNotifier.httpErrorMessage', { defaultMessage: 'Could not check Reporting job status!', }), - err + err, + this.theme ) ); // prettier-ignore window.console.error(err); diff --git a/x-pack/plugins/reporting/public/notifier/general_error.tsx b/x-pack/plugins/reporting/public/notifier/general_error.tsx index 141b7b49444b0..66fff4d00ceeb 100644 --- a/x-pack/plugins/reporting/public/notifier/general_error.tsx +++ b/x-pack/plugins/reporting/public/notifier/general_error.tsx @@ -8,10 +8,14 @@ import React, { Fragment } from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; import { EuiCallOut, EuiSpacer } from '@elastic/eui'; -import { ToastInput } from 'src/core/public'; +import { ThemeServiceStart, ToastInput } from 'src/core/public'; import { toMountPoint } from '../../../../../src/plugins/kibana_react/public'; -export const getGeneralErrorToast = (errorText: string, err: Error): ToastInput => ({ +export const getGeneralErrorToast = ( + errorText: string, + err: Error, + theme: ThemeServiceStart +): ToastInput => ({ text: toMountPoint( @@ -24,7 +28,8 @@ export const getGeneralErrorToast = (errorText: string, err: Error): ToastInput id="xpack.reporting.publicNotifier.error.tryRefresh" defaultMessage="Try refreshing the page." /> - + , + { theme$: theme.theme$ } ), iconType: undefined, }); diff --git a/x-pack/plugins/reporting/public/notifier/job_failure.tsx b/x-pack/plugins/reporting/public/notifier/job_failure.tsx index a9e7b78c7e12f..87fbc72d29ab8 100644 --- a/x-pack/plugins/reporting/public/notifier/job_failure.tsx +++ b/x-pack/plugins/reporting/public/notifier/job_failure.tsx @@ -9,14 +9,15 @@ import { EuiCallOut, EuiSpacer } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import React, { Fragment } from 'react'; -import { ToastInput } from 'src/core/public'; +import { ThemeServiceStart, ToastInput } from 'src/core/public'; import { toMountPoint } from '../../../../../src/plugins/kibana_react/public'; import { JobSummary, ManagementLinkFn } from '../../common/types'; export const getFailureToast = ( errorText: string, job: JobSummary, - getManagmenetLink: ManagementLinkFn + getManagmenetLink: ManagementLinkFn, + theme: ThemeServiceStart ): ToastInput => { return { title: toMountPoint( @@ -24,7 +25,8 @@ export const getFailureToast = ( id="xpack.reporting.publicNotifier.error.couldNotCreateReportTitle" defaultMessage="Could not create report for {reportObjectType} '{reportObjectTitle}'." values={{ reportObjectType: job.jobtype, reportObjectTitle: job.title }} - /> + />, + { theme$: theme.theme$ } ), text: toMountPoint( @@ -58,7 +60,8 @@ export const getFailureToast = ( }} />

-
+ , + { theme$: theme.theme$ } ), iconType: undefined, 'data-test-subj': 'completeReportFailure', diff --git a/x-pack/plugins/reporting/public/notifier/job_success.tsx b/x-pack/plugins/reporting/public/notifier/job_success.tsx index c1de9a7625858..f949c27f6fedb 100644 --- a/x-pack/plugins/reporting/public/notifier/job_success.tsx +++ b/x-pack/plugins/reporting/public/notifier/job_success.tsx @@ -7,7 +7,7 @@ import { FormattedMessage } from '@kbn/i18n-react'; import React, { Fragment } from 'react'; -import { ToastInput } from 'src/core/public'; +import { ThemeServiceStart, ToastInput } from 'src/core/public'; import { toMountPoint } from '../../../../../src/plugins/kibana_react/public'; import { JobId, JobSummary } from '../../common/types'; import { DownloadButton } from './job_download_button'; @@ -16,14 +16,16 @@ import { ReportLink } from './report_link'; export const getSuccessToast = ( job: JobSummary, getReportLink: () => string, - getDownloadLink: (jobId: JobId) => string + getDownloadLink: (jobId: JobId) => string, + theme: ThemeServiceStart ): ToastInput => ({ title: toMountPoint( + />, + { theme$: theme.theme$ } ), color: 'success', text: toMountPoint( @@ -32,7 +34,8 @@ export const getSuccessToast = (

- + , + { theme$: theme.theme$ } ), 'data-test-subj': 'completeReportSuccess', }); diff --git a/x-pack/plugins/reporting/public/notifier/job_warning_formulas.tsx b/x-pack/plugins/reporting/public/notifier/job_warning_formulas.tsx index c835203813b86..08c87a40a829a 100644 --- a/x-pack/plugins/reporting/public/notifier/job_warning_formulas.tsx +++ b/x-pack/plugins/reporting/public/notifier/job_warning_formulas.tsx @@ -7,7 +7,7 @@ import { FormattedMessage } from '@kbn/i18n-react'; import React, { Fragment } from 'react'; -import { ToastInput } from 'src/core/public'; +import { ThemeServiceStart, ToastInput } from 'src/core/public'; import { toMountPoint } from '../../../../../src/plugins/kibana_react/public'; import { JobId, JobSummary } from '../../common/types'; import { DownloadButton } from './job_download_button'; @@ -16,14 +16,16 @@ import { ReportLink } from './report_link'; export const getWarningFormulasToast = ( job: JobSummary, getReportLink: () => string, - getDownloadLink: (jobId: JobId) => string + getDownloadLink: (jobId: JobId) => string, + theme: ThemeServiceStart ): ToastInput => ({ title: toMountPoint( + />, + { theme$: theme.theme$ } ), text: toMountPoint( @@ -37,7 +39,8 @@ export const getWarningFormulasToast = (

-
+ , + { theme$: theme.theme$ } ), 'data-test-subj': 'completeReportCsvFormulasWarning', }); diff --git a/x-pack/plugins/reporting/public/notifier/job_warning_max_size.tsx b/x-pack/plugins/reporting/public/notifier/job_warning_max_size.tsx index f7cc8e2219df9..629ac44adeae8 100644 --- a/x-pack/plugins/reporting/public/notifier/job_warning_max_size.tsx +++ b/x-pack/plugins/reporting/public/notifier/job_warning_max_size.tsx @@ -7,7 +7,7 @@ import { FormattedMessage } from '@kbn/i18n-react'; import React, { Fragment } from 'react'; -import { ToastInput } from 'src/core/public'; +import { ThemeServiceStart, ToastInput } from 'src/core/public'; import { toMountPoint } from '../../../../../src/plugins/kibana_react/public'; import { JobId, JobSummary } from '../../common/types'; import { DownloadButton } from './job_download_button'; @@ -16,14 +16,16 @@ import { ReportLink } from './report_link'; export const getWarningMaxSizeToast = ( job: JobSummary, getReportLink: () => string, - getDownloadLink: (jobId: JobId) => string + getDownloadLink: (jobId: JobId) => string, + theme: ThemeServiceStart ): ToastInput => ({ title: toMountPoint( + />, + { theme$: theme.theme$ } ), text: toMountPoint( @@ -37,7 +39,8 @@ export const getWarningMaxSizeToast = (

-
+ , + { theme$: theme.theme$ } ), 'data-test-subj': 'completeReportMaxSizeWarning', }); diff --git a/x-pack/plugins/reporting/public/plugin.ts b/x-pack/plugins/reporting/public/plugin.ts index b1f9b63e66cbe..77c8489bb8992 100644 --- a/x-pack/plugins/reporting/public/plugin.ts +++ b/x-pack/plugins/reporting/public/plugin.ts @@ -17,6 +17,7 @@ import { NotificationsSetup, Plugin, PluginInitializerContext, + ThemeServiceStart, } from 'src/core/public'; import type { ScreenshottingSetup } from '../../screenshotting/public'; import { CONTEXT_MENU_TRIGGER } from '../../../../src/plugins/embeddable/public'; @@ -56,13 +57,18 @@ function getStored(): JobId[] { return sessionValue ? JSON.parse(sessionValue) : []; } -function handleError(notifications: NotificationsSetup, err: Error): Rx.Observable { +function handleError( + notifications: NotificationsSetup, + err: Error, + theme: ThemeServiceStart +): Rx.Observable { notifications.toasts.addDanger( getGeneralErrorToast( i18n.translate('xpack.reporting.publicNotifier.pollingErrorMessage', { defaultMessage: 'Reporting notifier error!', }), - err + err, + theme ) ); window.console.error(err); @@ -235,6 +241,7 @@ export class ReportingPublicPlugin startServices$, uiSettings, usesUiCapabilities, + theme: core.theme, }) ); @@ -246,6 +253,7 @@ export class ReportingPublicPlugin startServices$, uiSettings, usesUiCapabilities, + theme: core.theme, }) ); @@ -255,7 +263,7 @@ export class ReportingPublicPlugin public start(core: CoreStart) { const { notifications } = core; const apiClient = this.getApiClient(core.http, core.uiSettings); - const streamHandler = new StreamHandler(notifications, apiClient); + const streamHandler = new StreamHandler(notifications, apiClient, core.theme); const interval = durationToNumber(this.config.poll.jobsRefresh.interval); Rx.timer(0, interval) .pipe( @@ -264,7 +272,7 @@ export class ReportingPublicPlugin filter((storedJobs) => storedJobs.length > 0), // stop the pipeline here if there are none pending mergeMap((storedJobs) => streamHandler.findChangedStatusJobs(storedJobs)), // look up the latest status of all pending jobs on the server mergeMap(({ completed, failed }) => streamHandler.showNotifications({ completed, failed })), - catchError((err) => handleError(notifications, err)) + catchError((err) => handleError(notifications, err, core.theme)) ) .subscribe(); diff --git a/x-pack/plugins/reporting/public/share_context_menu/index.ts b/x-pack/plugins/reporting/public/share_context_menu/index.ts index 321a5a29281af..6a5dbf970e0b4 100644 --- a/x-pack/plugins/reporting/public/share_context_menu/index.ts +++ b/x-pack/plugins/reporting/public/share_context_menu/index.ts @@ -6,7 +6,7 @@ */ import * as Rx from 'rxjs'; -import type { IUiSettingsClient, ToastsSetup } from 'src/core/public'; +import type { IUiSettingsClient, ThemeServiceSetup, ToastsSetup } from 'src/core/public'; import { CoreStart } from 'src/core/public'; import type { LayoutParams } from '../../../screenshotting/common'; import type { LicensingPluginSetup } from '../../../licensing/public'; @@ -19,6 +19,7 @@ export interface ExportPanelShareOpts { license$: LicensingPluginSetup['license$']; // FIXME: 'license$' is deprecated startServices$: Rx.Observable<[CoreStart, object, unknown]>; usesUiCapabilities: boolean; + theme: ThemeServiceSetup; } export interface ReportingSharingData { diff --git a/x-pack/plugins/reporting/public/share_context_menu/register_csv_reporting.tsx b/x-pack/plugins/reporting/public/share_context_menu/register_csv_reporting.tsx index 8859d01e4fe9a..b264c96361122 100644 --- a/x-pack/plugins/reporting/public/share_context_menu/register_csv_reporting.tsx +++ b/x-pack/plugins/reporting/public/share_context_menu/register_csv_reporting.tsx @@ -21,6 +21,7 @@ export const ReportingCsvShareProvider = ({ license$, startServices$, usesUiCapabilities, + theme, }: ExportPanelShareOpts) => { let licenseToolTipContent = ''; let licenseHasCsvReporting = false; @@ -96,6 +97,7 @@ export const ReportingCsvShareProvider = ({ objectId={objectId} getJobParams={getJobParams} onClose={onClose} + theme={theme} /> ), }, diff --git a/x-pack/plugins/reporting/public/share_context_menu/register_pdf_png_reporting.tsx b/x-pack/plugins/reporting/public/share_context_menu/register_pdf_png_reporting.tsx index 610781f3b6ea0..3cc8cbacc7921 100644 --- a/x-pack/plugins/reporting/public/share_context_menu/register_pdf_png_reporting.tsx +++ b/x-pack/plugins/reporting/public/share_context_menu/register_pdf_png_reporting.tsx @@ -63,6 +63,7 @@ export const reportingScreenshotShareProvider = ({ license$, startServices$, usesUiCapabilities, + theme, }: ExportPanelShareOpts) => { let licenseToolTipContent = ''; let licenseDisabled = true; @@ -156,6 +157,7 @@ export const reportingScreenshotShareProvider = ({ getJobParams={getJobParams(apiClient, jobProviderOptions, pngReportType)} isDirty={isDirty} onClose={onClose} + theme={theme} /> ), }, @@ -191,6 +193,7 @@ export const reportingScreenshotShareProvider = ({ getJobParams={getJobParams(apiClient, jobProviderOptions, pdfReportType)} isDirty={isDirty} onClose={onClose} + theme={theme} /> ), }, diff --git a/x-pack/plugins/reporting/public/share_context_menu/reporting_panel_content/reporting_panel_content.test.tsx b/x-pack/plugins/reporting/public/share_context_menu/reporting_panel_content/reporting_panel_content.test.tsx index e9dd584e51f82..ef3e9940238c1 100644 --- a/x-pack/plugins/reporting/public/share_context_menu/reporting_panel_content/reporting_panel_content.test.tsx +++ b/x-pack/plugins/reporting/public/share_context_menu/reporting_panel_content/reporting_panel_content.test.tsx @@ -10,6 +10,7 @@ import { mountWithIntl } from '@kbn/test/jest'; import { httpServiceMock, notificationServiceMock, + themeServiceMock, uiSettingsServiceMock, } from 'src/core/public/mocks'; import { ReportingAPIClient } from '../../lib/reporting_api_client'; @@ -21,6 +22,8 @@ jest.mock('./constants', () => ({ })); import * as constants from './constants'; +const theme = themeServiceMock.createSetupContract(); + describe('ReportingPanelContent', () => { const props: Partial = { layoutId: 'super_cool_layout_id_X', @@ -58,6 +61,7 @@ describe('ReportingPanelContent', () => { apiClient={apiClient} toasts={toasts} uiSettings={uiSettings} + theme={theme} {...props} {...newProps} /> diff --git a/x-pack/plugins/reporting/public/share_context_menu/reporting_panel_content/reporting_panel_content.tsx b/x-pack/plugins/reporting/public/share_context_menu/reporting_panel_content/reporting_panel_content.tsx index 73ccbc2b13d75..e1fa1198cf1f8 100644 --- a/x-pack/plugins/reporting/public/share_context_menu/reporting_panel_content/reporting_panel_content.tsx +++ b/x-pack/plugins/reporting/public/share_context_menu/reporting_panel_content/reporting_panel_content.tsx @@ -18,7 +18,7 @@ import { import { i18n } from '@kbn/i18n'; import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n-react'; import React, { Component, ReactElement } from 'react'; -import { IUiSettingsClient, ToastsSetup } from 'src/core/public'; +import { IUiSettingsClient, ThemeServiceSetup, ToastsSetup } from 'src/core/public'; import url from 'url'; import { toMountPoint } from '../../../../../../src/plugins/kibana_react/public'; import { @@ -46,6 +46,7 @@ export interface ReportingPanelProps { options?: ReactElement | null; isDirty?: boolean; onClose?: () => void; + theme: ThemeServiceSetup; } export type Props = ReportingPanelProps & { intl: InjectedIntl }; @@ -291,7 +292,8 @@ class ReportingPanelContentUi extends Component { ), }} - /> + />, + { theme$: this.props.theme.theme$ } ), 'data-test-subj': 'queueReportSuccess', }); diff --git a/x-pack/plugins/reporting/public/share_context_menu/screen_capture_panel_content.test.tsx b/x-pack/plugins/reporting/public/share_context_menu/screen_capture_panel_content.test.tsx index 7a2fa52d010e3..ebf741c79bd86 100644 --- a/x-pack/plugins/reporting/public/share_context_menu/screen_capture_panel_content.test.tsx +++ b/x-pack/plugins/reporting/public/share_context_menu/screen_capture_panel_content.test.tsx @@ -8,7 +8,7 @@ import { mount } from 'enzyme'; import React from 'react'; import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; -import { coreMock } from 'src/core/public/mocks'; +import { coreMock, themeServiceMock } from 'src/core/public/mocks'; import { ReportingAPIClient } from '../lib/reporting_api_client'; import { ScreenCapturePanelContent } from './screen_capture_panel_content'; @@ -27,6 +27,8 @@ const getJobParamsDefault = () => ({ browserTimezone: 'America/New_York', }); +const theme = themeServiceMock.createSetupContract(); + test('ScreenCapturePanelContent renders the default view properly', () => { const component = mount( @@ -37,6 +39,7 @@ test('ScreenCapturePanelContent renders the default view properly', () => { uiSettings={uiSettings} toasts={coreSetup.notifications.toasts} getJobParams={getJobParamsDefault} + theme={theme} /> ); @@ -56,6 +59,7 @@ test('ScreenCapturePanelContent properly renders a view with "canvas" layout opt uiSettings={uiSettings} toasts={coreSetup.notifications.toasts} getJobParams={getJobParamsDefault} + theme={theme} /> ); @@ -75,6 +79,7 @@ test('ScreenCapturePanelContent allows POST URL to be copied when objectId is pr toasts={coreSetup.notifications.toasts} getJobParams={getJobParamsDefault} objectId={'1234-5'} + theme={theme} /> ); @@ -93,6 +98,7 @@ test('ScreenCapturePanelContent does not allow POST URL to be copied when object uiSettings={uiSettings} toasts={coreSetup.notifications.toasts} getJobParams={getJobParamsDefault} + theme={theme} /> ); @@ -111,6 +117,7 @@ test('ScreenCapturePanelContent properly renders a view with "print" layout opti uiSettings={uiSettings} toasts={coreSetup.notifications.toasts} getJobParams={getJobParamsDefault} + theme={theme} /> ); @@ -130,6 +137,7 @@ test('ScreenCapturePanelContent decorated job params are visible in the POST URL uiSettings={uiSettings} toasts={coreSetup.notifications.toasts} getJobParams={getJobParamsDefault} + theme={theme} /> ); diff --git a/x-pack/plugins/reporting/public/shared/get_shared_components.tsx b/x-pack/plugins/reporting/public/shared/get_shared_components.tsx index b08036e8b1c80..0906bf85c9538 100644 --- a/x-pack/plugins/reporting/public/shared/get_shared_components.tsx +++ b/x-pack/plugins/reporting/public/shared/get_shared_components.tsx @@ -35,6 +35,7 @@ export function getSharedComponents(core: CoreSetup, apiClient: ReportingAPIClie apiClient={apiClient} toasts={core.notifications.toasts} uiSettings={core.uiSettings} + theme={core.theme} {...props} /> ); @@ -48,6 +49,7 @@ export function getSharedComponents(core: CoreSetup, apiClient: ReportingAPIClie apiClient={apiClient} toasts={core.notifications.toasts} uiSettings={core.uiSettings} + theme={core.theme} {...props} /> ); @@ -61,6 +63,7 @@ export function getSharedComponents(core: CoreSetup, apiClient: ReportingAPIClie apiClient={apiClient} toasts={core.notifications.toasts} uiSettings={core.uiSettings} + theme={core.theme} {...props} /> ); diff --git a/x-pack/plugins/reporting/tsconfig.json b/x-pack/plugins/reporting/tsconfig.json index 66d528cd83a22..24db825856627 100644 --- a/x-pack/plugins/reporting/tsconfig.json +++ b/x-pack/plugins/reporting/tsconfig.json @@ -6,18 +6,14 @@ "declaration": true, "declarationMap": true }, - "include": [ - "common/**/*", - "public/**/*", - "server/**/*", - "../../../typings/**/*" - ], + "include": ["common/**/*", "public/**/*", "server/**/*", "../../../typings/**/*"], "references": [ { "path": "../../../src/core/tsconfig.json" }, - { "path": "../../../src/plugins/data/tsconfig.json"}, + { "path": "../../../src/plugins/data/tsconfig.json" }, { "path": "../../../src/plugins/discover/tsconfig.json" }, { "path": "../../../src/plugins/embeddable/tsconfig.json" }, { "path": "../../../src/plugins/kibana_react/tsconfig.json" }, + { "path": "../../../src/plugins/kibana_utils/tsconfig.json" }, { "path": "../../../src/plugins/management/tsconfig.json" }, { "path": "../../../src/plugins/screenshot_mode/tsconfig.json" }, { "path": "../../../src/plugins/share/tsconfig.json" }, @@ -29,6 +25,6 @@ { "path": "../licensing/tsconfig.json" }, { "path": "../screenshotting/tsconfig.json" }, { "path": "../security/tsconfig.json" }, - { "path": "../spaces/tsconfig.json" }, + { "path": "../spaces/tsconfig.json" } ] } diff --git a/x-pack/plugins/runtime_fields/README.md b/x-pack/plugins/runtime_fields/README.md index eb7b31e6e1154..9c0e0e03f2fe5 100644 --- a/x-pack/plugins/runtime_fields/README.md +++ b/x-pack/plugins/runtime_fields/README.md @@ -72,7 +72,7 @@ interface RuntimeField { type: RuntimeType; // 'long' | 'boolean' ... script: { source: string; - } + }; } ``` @@ -103,8 +103,8 @@ interface Context { The runtime field editor is also exported as static React component that you can import into your components. The editor is exported in 2 flavours: -* As the content of a `` (it contains a flyout header and footer) -* As a standalone component that you can inline anywhere +- As the content of a `` (it contains a flyout header and footer) +- As a standalone component that you can inline anywhere **Note:** The runtime field editor uses the `` that has a dependency on the `Provider` from the `"kibana_react"` plugin. If your app is not already wrapped by this provider you will need to add it at least around the runtime field editor. You can see an example in the ["Using the core.overlays.openFlyout()"](#using-the-coreoverlaysopenflyout) example below. @@ -118,7 +118,7 @@ import { RuntimeFieldEditorFlyoutContent, RuntimeField } from '../runtime_fields const MyComponent = () => { const { docLinksStart } = useCoreContext(); // access the core start service const [isFlyoutVisilbe, setIsFlyoutVisible] = useState(false); - + const saveRuntimeField = useCallback((field: RuntimeField) => { // Do something with the field }, []); @@ -139,7 +139,7 @@ const MyComponent = () => { )} - ) + ) } ``` @@ -157,11 +157,11 @@ import { RuntimeFieldEditorFlyoutContent, RuntimeField } from '../runtime_fields const MyComponent = () => { // Access the core start service - const { docLinksStart, overlays, uiSettings } = useCoreContext(); + const { docLinksStart, theme, overlays, uiSettings } = useCoreContext(); const flyoutEditor = useRef(null); const { openFlyout } = overlays; - + const saveRuntimeField = useCallback((field: RuntimeField) => { // Do something with the field }, []); @@ -179,7 +179,8 @@ const MyComponent = () => { defaultValue={defaultRuntimeField} ctx={/*optional context object -- see section above*/} /> - + , + { theme$: theme.theme$ } ) ); }, [openFlyout, saveRuntimeField, uiSettings]); @@ -188,7 +189,7 @@ const MyComponent = () => { <> Create field - ) + ) } ``` @@ -208,7 +209,7 @@ const MyComponent = () => { }); const { submit, isValid: isFormValid, isSubmitted } = runtimeFieldFormState; - + const saveRuntimeField = useCallback(async () => { const { isValid, data } = await submit(); if (isValid) { @@ -233,6 +234,6 @@ const MyComponent = () => { Save field - ) + ) } -``` \ No newline at end of file +``` diff --git a/x-pack/plugins/runtime_fields/public/load_editor.tsx b/x-pack/plugins/runtime_fields/public/load_editor.tsx index 0cea90f33a54d..6aec33b90466f 100644 --- a/x-pack/plugins/runtime_fields/public/load_editor.tsx +++ b/x-pack/plugins/runtime_fields/public/load_editor.tsx @@ -22,7 +22,7 @@ export const getRuntimeFieldEditorLoader = (coreSetup: CoreSetup) => async (): Promise => { const { RuntimeFieldEditorFlyoutContent } = await import('./components'); const [core] = await coreSetup.getStartServices(); - const { uiSettings, overlays, docLinks } = core; + const { uiSettings, theme, overlays, docLinks } = core; const { Provider: KibanaReactContextProvider } = createKibanaReactContext({ uiSettings }); let overlayRef: OverlayRef | null = null; @@ -50,7 +50,8 @@ export const getRuntimeFieldEditorLoader = defaultValue={defaultValue} ctx={ctx} /> - + , + { theme$: theme.theme$ } ) ); diff --git a/x-pack/plugins/runtime_fields/public/plugin.test.ts b/x-pack/plugins/runtime_fields/public/plugin.test.ts index 0f72d99ec5d4f..fc36eecc12f0a 100644 --- a/x-pack/plugins/runtime_fields/public/plugin.test.ts +++ b/x-pack/plugins/runtime_fields/public/plugin.test.ts @@ -6,7 +6,7 @@ */ import { CoreSetup } from 'src/core/public'; -import { coreMock } from 'src/core/public/mocks'; +import { coreMock, themeServiceMock } from 'src/core/public/mocks'; jest.mock('../../../../src/plugins/kibana_react/public', () => { const original = jest.requireActual('../../../../src/plugins/kibana_react/public'); @@ -52,6 +52,7 @@ describe('RuntimeFieldsPlugin', () => { openFlyout, }, uiSettings: {}, + theme: themeServiceMock.createStartContract(), }; coreSetup.getStartServices = async () => [mockCore] as any; const setupApi = await plugin.setup(coreSetup, {});