From e5b279f318eac64660a5f4b157e891339f05ea61 Mon Sep 17 00:00:00 2001 From: Joe Portner <5295965+jportner@users.noreply.github.com> Date: Tue, 23 Nov 2021 12:03:46 -0500 Subject: [PATCH] Use `KibanaThemeProvider` for security/spaces apps (#119124) --- .../public/cluster_address_form.test.tsx | 8 +- .../cluster_configuration_form.test.tsx | 10 ++- .../public/enrollment_token_form.test.tsx | 8 +- .../interactive_setup/public/plugin.tsx | 22 +++-- .../interactive_setup/public/theme/index.ts | 9 ++ .../theme/kibana_theme_provider.test.tsx | 88 +++++++++++++++++++ .../public/theme/kibana_theme_provider.tsx | 33 +++++++ .../public/theme/utils.test.ts | 19 ++++ .../interactive_setup/public/theme/utils.ts | 19 ++++ .../public/verification_code_form.test.tsx | 8 +- .../public/theme/kibana_theme_provider.tsx | 3 + .../kibana_react/public/theme/utils.ts | 3 + .../account_management_app.test.ts | 22 +++-- .../account_management_app.ts | 16 ++-- .../account_management_page.tsx | 9 +- .../access_agreement_app.test.ts | 22 +++-- .../access_agreement/access_agreement_app.ts | 16 ++-- .../access_agreement_page.tsx | 15 +++- .../logged_out/logged_out_app.test.ts | 17 ++-- .../logged_out/logged_out_app.ts | 8 +- .../logged_out/logged_out_page.tsx | 13 ++- .../authentication/login/login_app.test.ts | 24 ++--- .../public/authentication/login/login_app.ts | 18 ++-- .../authentication/login/login_page.tsx | 19 +++- .../overwritten_session_app.test.ts | 18 ++-- .../overwritten_session_app.ts | 14 +-- .../overwritten_session_page.tsx | 9 +- .../api_keys_grid/api_keys_grid_page.test.tsx | 11 +-- .../api_keys/api_keys_management_app.tsx | 25 ++++-- .../role_mappings_management_app.tsx | 57 ++++++------ .../management/roles/roles_management_app.tsx | 51 ++++++----- .../users/edit_user/create_user_page.test.tsx | 8 +- .../users/edit_user/edit_user_page.test.tsx | 11 +-- .../management/users/users_management_app.tsx | 21 +++-- .../nav_control/nav_control_service.tsx | 8 +- .../management/spaces_management_app.tsx | 35 ++++---- .../spaces/public/nav_control/nav_control.tsx | 22 +++-- .../public/space_selector/space_selector.tsx | 17 ++-- .../space_selector/space_selector_app.tsx | 14 +-- 39 files changed, 532 insertions(+), 218 deletions(-) create mode 100644 src/plugins/interactive_setup/public/theme/index.ts create mode 100644 src/plugins/interactive_setup/public/theme/kibana_theme_provider.test.tsx create mode 100644 src/plugins/interactive_setup/public/theme/kibana_theme_provider.tsx create mode 100644 src/plugins/interactive_setup/public/theme/utils.test.ts create mode 100644 src/plugins/interactive_setup/public/theme/utils.ts diff --git a/src/plugins/interactive_setup/public/cluster_address_form.test.tsx b/src/plugins/interactive_setup/public/cluster_address_form.test.tsx index 5c770fbfbfefe..098012bd36310 100644 --- a/src/plugins/interactive_setup/public/cluster_address_form.test.tsx +++ b/src/plugins/interactive_setup/public/cluster_address_form.test.tsx @@ -9,7 +9,7 @@ import { fireEvent, render, waitFor } from '@testing-library/react'; import React from 'react'; -import { coreMock } from 'src/core/public/mocks'; +import { coreMock, themeServiceMock } from 'src/core/public/mocks'; import { ClusterAddressForm } from './cluster_address_form'; import { Providers } from './plugin'; @@ -21,6 +21,8 @@ jest.mock('@elastic/eui/lib/services/accessibility/html_id_generator', () => ({ describe('ClusterAddressForm', () => { jest.setTimeout(20_000); + const theme$ = themeServiceMock.createTheme$(); + it('calls enrollment API when submitting form', async () => { const coreStart = coreMock.createStart(); coreStart.http.post.mockResolvedValue({}); @@ -28,7 +30,7 @@ describe('ClusterAddressForm', () => { const onSuccess = jest.fn(); const { findByRole, findByLabelText } = render( - + ); @@ -52,7 +54,7 @@ describe('ClusterAddressForm', () => { const onSuccess = jest.fn(); const { findAllByText, findByRole, findByLabelText } = render( - + ); diff --git a/src/plugins/interactive_setup/public/cluster_configuration_form.test.tsx b/src/plugins/interactive_setup/public/cluster_configuration_form.test.tsx index 7e20deb9251ec..21c8d193fbec1 100644 --- a/src/plugins/interactive_setup/public/cluster_configuration_form.test.tsx +++ b/src/plugins/interactive_setup/public/cluster_configuration_form.test.tsx @@ -9,7 +9,7 @@ import { fireEvent, render, waitFor } from '@testing-library/react'; import React from 'react'; -import { coreMock } from 'src/core/public/mocks'; +import { coreMock, themeServiceMock } from 'src/core/public/mocks'; import { ClusterConfigurationForm } from './cluster_configuration_form'; import { Providers } from './plugin'; @@ -21,6 +21,8 @@ jest.mock('@elastic/eui/lib/services/accessibility/html_id_generator', () => ({ describe('ClusterConfigurationForm', () => { jest.setTimeout(20_000); + const theme$ = themeServiceMock.createTheme$(); + it('calls enrollment API for https addresses when submitting form', async () => { const coreStart = coreMock.createStart(); coreStart.http.post.mockResolvedValue({}); @@ -28,7 +30,7 @@ describe('ClusterConfigurationForm', () => { const onSuccess = jest.fn(); const { findByRole, findByLabelText } = render( - + { const onSuccess = jest.fn(); const { findByRole } = render( - + { const onSuccess = jest.fn(); const { findAllByText, findByRole, findByLabelText } = render( - + { jest.setTimeout(20_000); + const theme$ = themeServiceMock.createTheme$(); + it('calls enrollment API when submitting form', async () => { const coreStart = coreMock.createStart(); coreStart.http.post.mockResolvedValue({}); @@ -36,7 +38,7 @@ describe('EnrollmentTokenForm', () => { const onSuccess = jest.fn(); const { findByRole, findByLabelText } = render( - + ); @@ -62,7 +64,7 @@ describe('EnrollmentTokenForm', () => { const onSuccess = jest.fn(); const { findAllByText, findByRole, findByLabelText } = render( - + ); diff --git a/src/plugins/interactive_setup/public/plugin.tsx b/src/plugins/interactive_setup/public/plugin.tsx index dd2f4c14a5f77..d3fc53e8bc690 100644 --- a/src/plugins/interactive_setup/public/plugin.tsx +++ b/src/plugins/interactive_setup/public/plugin.tsx @@ -9,11 +9,13 @@ import type { FunctionComponent } from 'react'; import React from 'react'; import ReactDOM from 'react-dom'; +import type { Observable } from 'rxjs'; import { I18nProvider } from '@kbn/i18n/react'; -import type { CoreSetup, CoreStart, Plugin } from 'src/core/public'; +import type { CoreSetup, CoreStart, CoreTheme, Plugin } from 'src/core/public'; import { App } from './app'; +import { KibanaThemeProvider } from './theme'; // TODO: replace this with the one exported from `kibana_react` after https://github.com/elastic/kibana/issues/119204 is implemented. import { KibanaProvider } from './use_kibana'; import { VerificationProvider } from './use_verification'; @@ -24,7 +26,7 @@ export class InteractiveSetupPlugin implements Plugin { title: 'Configure Elastic to get started', appRoute: '/', chromeless: true, - mount: async (params) => { + mount: async ({ element, theme$ }) => { const url = new URL(window.location.href); const defaultCode = url.searchParams.get('code') || undefined; const onSuccess = () => { @@ -34,12 +36,12 @@ export class InteractiveSetupPlugin implements Plugin { const [services] = await core.getStartServices(); ReactDOM.render( - + , - params.element + element ); - return () => ReactDOM.unmountComponentAtNode(params.element); + return () => ReactDOM.unmountComponentAtNode(element); }, }); } @@ -49,17 +51,21 @@ export class InteractiveSetupPlugin implements Plugin { export interface ProvidersProps { services: CoreStart; + theme$: Observable; defaultCode?: string; } export const Providers: FunctionComponent = ({ defaultCode, services, + theme$, children, }) => ( - - {children} - + + + {children} + + ); diff --git a/src/plugins/interactive_setup/public/theme/index.ts b/src/plugins/interactive_setup/public/theme/index.ts new file mode 100644 index 0000000000000..165c5ef9195c2 --- /dev/null +++ b/src/plugins/interactive_setup/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/interactive_setup/public/theme/kibana_theme_provider.test.tsx b/src/plugins/interactive_setup/public/theme/kibana_theme_provider.test.tsx new file mode 100644 index 0000000000000..21059bd4a8236 --- /dev/null +++ b/src/plugins/interactive_setup/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/interactive_setup/public/theme/kibana_theme_provider.tsx b/src/plugins/interactive_setup/public/theme/kibana_theme_provider.tsx new file mode 100644 index 0000000000000..b1de6bd143049 --- /dev/null +++ b/src/plugins/interactive_setup/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, remove once https://github.com/elastic/kibana/issues/119204 is implemented. + */ +export const KibanaThemeProvider: FC = ({ theme$, children }) => { + const theme = useObservable(theme$, defaultTheme); + const colorMode = useMemo(() => getColorMode(theme), [theme]); + return {children}; +}; diff --git a/src/plugins/interactive_setup/public/theme/utils.test.ts b/src/plugins/interactive_setup/public/theme/utils.test.ts new file mode 100644 index 0000000000000..57b37f4fb2f62 --- /dev/null +++ b/src/plugins/interactive_setup/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/interactive_setup/public/theme/utils.ts b/src/plugins/interactive_setup/public/theme/utils.ts new file mode 100644 index 0000000000000..55730974afc16 --- /dev/null +++ b/src/plugins/interactive_setup/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 type { EuiThemeColorMode } from '@elastic/eui/src/services/theme/types'; + +import type { CoreTheme } from '../../../../core/public'; + +/** + * Copied from the `kibana_react` plugin, remove once https://github.com/elastic/kibana/issues/119204 is implemented. + */ +export const getColorMode = (theme: CoreTheme): EuiThemeColorMode => { + // COLOR_MODES_STANDARD is not exported from eui + return theme.darkMode ? 'DARK' : 'LIGHT'; +}; diff --git a/src/plugins/interactive_setup/public/verification_code_form.test.tsx b/src/plugins/interactive_setup/public/verification_code_form.test.tsx index 9deb39e8e91a5..4848a2838f9d9 100644 --- a/src/plugins/interactive_setup/public/verification_code_form.test.tsx +++ b/src/plugins/interactive_setup/public/verification_code_form.test.tsx @@ -9,7 +9,7 @@ import { fireEvent, render, waitFor } from '@testing-library/react'; import React from 'react'; -import { coreMock } from 'src/core/public/mocks'; +import { coreMock, themeServiceMock } from 'src/core/public/mocks'; import { Providers } from './plugin'; import { VerificationCodeForm } from './verification_code_form'; @@ -21,6 +21,8 @@ jest.mock('@elastic/eui/lib/services/accessibility/html_id_generator', () => ({ describe('VerificationCodeForm', () => { jest.setTimeout(20_000); + const theme$ = themeServiceMock.createTheme$(); + it('calls enrollment API when submitting form', async () => { const coreStart = coreMock.createStart(); coreStart.http.post.mockResolvedValue({}); @@ -28,7 +30,7 @@ describe('VerificationCodeForm', () => { const onSuccess = jest.fn(); const { findByRole, findByLabelText } = render( - + ); @@ -65,7 +67,7 @@ describe('VerificationCodeForm', () => { const onSuccess = jest.fn(); const { findAllByText, findByRole, findByLabelText } = render( - + ); 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 bd5d8c2ea8453..65d640f34a2ca 100644 --- a/src/plugins/kibana_react/public/theme/kibana_theme_provider.tsx +++ b/src/plugins/kibana_react/public/theme/kibana_theme_provider.tsx @@ -21,6 +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. + export const KibanaThemeProvider: FC = ({ theme$, children }) => { const theme = useObservable(theme$, defaultTheme); const colorMode = useMemo(() => getColorMode(theme), [theme]); diff --git a/src/plugins/kibana_react/public/theme/utils.ts b/src/plugins/kibana_react/public/theme/utils.ts index e85bc78333255..b3d2f8bc6bb30 100644 --- a/src/plugins/kibana_react/public/theme/utils.ts +++ b/src/plugins/kibana_react/public/theme/utils.ts @@ -9,6 +9,9 @@ import type { EuiThemeColorMode } from '@elastic/eui/src/services/theme/types'; 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. + export const getColorMode = (theme: CoreTheme): EuiThemeColorMode => { // COLOR_MODES_STANDARD is not exported from eui return theme.darkMode ? 'DARK' : 'LIGHT'; diff --git a/x-pack/plugins/security/public/account_management/account_management_app.test.ts b/x-pack/plugins/security/public/account_management/account_management_app.test.ts index d3a4c36ba6b93..11b188a1d1370 100644 --- a/x-pack/plugins/security/public/account_management/account_management_app.test.ts +++ b/x-pack/plugins/security/public/account_management/account_management_app.test.ts @@ -43,7 +43,6 @@ describe('accountManagementApp', () => { coreSetupMock.getStartServices.mockResolvedValue([coreStartMock, {}, {}]); const authcMock = securityMock.createSetup().authc; - const containerMock = document.createElement('div'); accountManagementApp.create({ application: coreSetupMock.application, @@ -52,14 +51,15 @@ describe('accountManagementApp', () => { }); const [[{ mount }]] = coreSetupMock.application.register.mock.calls; - await (mount as AppMount)({ - element: containerMock, + const appMountParams = { + element: document.createElement('div'), appBasePath: '', onAppLeave: jest.fn(), setHeaderActionMenu: jest.fn(), history: scopedHistoryMock.create(), theme$: themeServiceMock.createTheme$(), - }); + }; + await (mount as AppMount)(appMountParams); expect(coreStartMock.chrome.setBreadcrumbs).toHaveBeenCalledTimes(1); expect(coreStartMock.chrome.setBreadcrumbs).toHaveBeenCalledWith([ @@ -68,10 +68,14 @@ describe('accountManagementApp', () => { const mockRenderApp = jest.requireMock('./account_management_page').renderAccountManagementPage; expect(mockRenderApp).toHaveBeenCalledTimes(1); - expect(mockRenderApp).toHaveBeenCalledWith(coreStartMock.i18n, containerMock, { - userAPIClient: expect.any(UserAPIClient), - authc: authcMock, - notifications: coreStartMock.notifications, - }); + expect(mockRenderApp).toHaveBeenCalledWith( + coreStartMock.i18n, + { element: appMountParams.element, theme$: appMountParams.theme$ }, + { + userAPIClient: expect.any(UserAPIClient), + authc: authcMock, + notifications: coreStartMock.notifications, + } + ); }); }); diff --git a/x-pack/plugins/security/public/account_management/account_management_app.ts b/x-pack/plugins/security/public/account_management/account_management_app.ts index 69a5155fa51a7..d95b86194f54f 100644 --- a/x-pack/plugins/security/public/account_management/account_management_app.ts +++ b/x-pack/plugins/security/public/account_management/account_management_app.ts @@ -28,18 +28,22 @@ export const accountManagementApp = Object.freeze({ title, navLinkStatus: AppNavLinkStatus.hidden, appRoute: '/security/account', - async mount({ element }: AppMountParameters) { + async mount({ element, theme$ }: AppMountParameters) { const [[coreStart], { renderAccountManagementPage }, { UserAPIClient }] = await Promise.all( [getStartServices(), import('./account_management_page'), import('../management')] ); coreStart.chrome.setBreadcrumbs([{ text: title }]); - return renderAccountManagementPage(coreStart.i18n, element, { - authc, - notifications: coreStart.notifications, - userAPIClient: new UserAPIClient(coreStart.http), - }); + return renderAccountManagementPage( + coreStart.i18n, + { element, theme$ }, + { + authc, + notifications: coreStart.notifications, + userAPIClient: new UserAPIClient(coreStart.http), + } + ); }, }); }, diff --git a/x-pack/plugins/security/public/account_management/account_management_page.tsx b/x-pack/plugins/security/public/account_management/account_management_page.tsx index 60f48c01a6ff7..2e63ff052c9cc 100644 --- a/x-pack/plugins/security/public/account_management/account_management_page.tsx +++ b/x-pack/plugins/security/public/account_management/account_management_page.tsx @@ -11,8 +11,9 @@ import ReactDOM from 'react-dom'; import { FormattedMessage } from '@kbn/i18n/react'; import type { PublicMethodsOf } from '@kbn/utility-types'; -import type { CoreStart, NotificationsStart } from 'src/core/public'; +import type { AppMountParameters, CoreStart, NotificationsStart } from 'src/core/public'; +import { KibanaThemeProvider } from '../../../../../src/plugins/kibana_react/public'; import type { AuthenticatedUser } from '../../common/model'; import { getUserDisplayName } from '../../common/model'; import type { AuthenticationServiceSetup } from '../authentication'; @@ -67,12 +68,14 @@ export const AccountManagementPage = ({ userAPIClient, authc, notifications }: P export function renderAccountManagementPage( i18nStart: CoreStart['i18n'], - element: Element, + { element, theme$ }: Pick, props: Props ) { ReactDOM.render( - + + + , element ); diff --git a/x-pack/plugins/security/public/authentication/access_agreement/access_agreement_app.test.ts b/x-pack/plugins/security/public/authentication/access_agreement/access_agreement_app.test.ts index f192f298009f7..e76a4ca20b59c 100644 --- a/x-pack/plugins/security/public/authentication/access_agreement/access_agreement_app.test.ts +++ b/x-pack/plugins/security/public/authentication/access_agreement/access_agreement_app.test.ts @@ -37,7 +37,6 @@ describe('accessAgreementApp', () => { const coreSetupMock = coreMock.createSetup(); const coreStartMock = coreMock.createStart(); coreSetupMock.getStartServices.mockResolvedValue([coreStartMock, {}, {}]); - const containerMock = document.createElement('div'); accessAgreementApp.create({ application: coreSetupMock.application, @@ -45,21 +44,26 @@ describe('accessAgreementApp', () => { }); const [[{ mount }]] = coreSetupMock.application.register.mock.calls; - await (mount as AppMount)({ - element: containerMock, + const appMountParams = { + element: document.createElement('div'), appBasePath: '', onAppLeave: jest.fn(), setHeaderActionMenu: jest.fn(), history: scopedHistoryMock.create(), theme$: themeServiceMock.createTheme$(), - }); + }; + await (mount as AppMount)(appMountParams); const mockRenderApp = jest.requireMock('./access_agreement_page').renderAccessAgreementPage; expect(mockRenderApp).toHaveBeenCalledTimes(1); - expect(mockRenderApp).toHaveBeenCalledWith(coreStartMock.i18n, containerMock, { - http: coreStartMock.http, - notifications: coreStartMock.notifications, - fatalErrors: coreStartMock.fatalErrors, - }); + expect(mockRenderApp).toHaveBeenCalledWith( + coreStartMock.i18n, + { element: appMountParams.element, theme$: appMountParams.theme$ }, + { + http: coreStartMock.http, + notifications: coreStartMock.notifications, + fatalErrors: coreStartMock.fatalErrors, + } + ); }); }); diff --git a/x-pack/plugins/security/public/authentication/access_agreement/access_agreement_app.ts b/x-pack/plugins/security/public/authentication/access_agreement/access_agreement_app.ts index 27da278b8fa89..44fd5048cc60b 100644 --- a/x-pack/plugins/security/public/authentication/access_agreement/access_agreement_app.ts +++ b/x-pack/plugins/security/public/authentication/access_agreement/access_agreement_app.ts @@ -23,16 +23,20 @@ export const accessAgreementApp = Object.freeze({ }), chromeless: true, appRoute: '/security/access_agreement', - async mount({ element }: AppMountParameters) { + async mount({ element, theme$ }: AppMountParameters) { const [[coreStart], { renderAccessAgreementPage }] = await Promise.all([ getStartServices(), import('./access_agreement_page'), ]); - return renderAccessAgreementPage(coreStart.i18n, element, { - http: coreStart.http, - notifications: coreStart.notifications, - fatalErrors: coreStart.fatalErrors, - }); + return renderAccessAgreementPage( + coreStart.i18n, + { element, theme$ }, + { + http: coreStart.http, + notifications: coreStart.notifications, + fatalErrors: coreStart.fatalErrors, + } + ); }, }); }, diff --git a/x-pack/plugins/security/public/authentication/access_agreement/access_agreement_page.tsx b/x-pack/plugins/security/public/authentication/access_agreement/access_agreement_page.tsx index 78f36bb460f47..cfc8349103551 100644 --- a/x-pack/plugins/security/public/authentication/access_agreement/access_agreement_page.tsx +++ b/x-pack/plugins/security/public/authentication/access_agreement/access_agreement_page.tsx @@ -23,8 +23,15 @@ import ReactMarkdown from 'react-markdown'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import type { CoreStart, FatalErrorsStart, HttpStart, NotificationsStart } from 'src/core/public'; +import type { + AppMountParameters, + CoreStart, + FatalErrorsStart, + HttpStart, + NotificationsStart, +} from 'src/core/public'; +import { KibanaThemeProvider } from '../../../../../../src/plugins/kibana_react/public'; import { parseNext } from '../../../common/parse_next'; import { AuthenticationStatePage } from '../components'; @@ -122,12 +129,14 @@ export function AccessAgreementPage({ http, fatalErrors, notifications }: Props) export function renderAccessAgreementPage( i18nStart: CoreStart['i18n'], - element: Element, + { element, theme$ }: Pick, props: Props ) { ReactDOM.render( - + + + , element ); diff --git a/x-pack/plugins/security/public/authentication/logged_out/logged_out_app.test.ts b/x-pack/plugins/security/public/authentication/logged_out/logged_out_app.test.ts index a88195c6fe8a6..fff6c9e69c5c0 100644 --- a/x-pack/plugins/security/public/authentication/logged_out/logged_out_app.test.ts +++ b/x-pack/plugins/security/public/authentication/logged_out/logged_out_app.test.ts @@ -38,24 +38,25 @@ describe('loggedOutApp', () => { const coreStartMock = coreMock.createStart(); coreSetupMock.getStartServices.mockResolvedValue([coreStartMock, {}, {}]); - const containerMock = document.createElement('div'); - loggedOutApp.create(coreSetupMock); const [[{ mount }]] = coreSetupMock.application.register.mock.calls; - await (mount as AppMount)({ - element: containerMock, + const appMountParams = { + element: document.createElement('div'), appBasePath: '', onAppLeave: jest.fn(), setHeaderActionMenu: jest.fn(), history: scopedHistoryMock.create(), theme$: themeServiceMock.createTheme$(), - }); + }; + await (mount as AppMount)(appMountParams); const mockRenderApp = jest.requireMock('./logged_out_page').renderLoggedOutPage; expect(mockRenderApp).toHaveBeenCalledTimes(1); - expect(mockRenderApp).toHaveBeenCalledWith(coreStartMock.i18n, containerMock, { - basePath: coreStartMock.http.basePath, - }); + expect(mockRenderApp).toHaveBeenCalledWith( + coreStartMock.i18n, + { element: appMountParams.element, theme$: appMountParams.theme$ }, + { basePath: coreStartMock.http.basePath } + ); }); }); diff --git a/x-pack/plugins/security/public/authentication/logged_out/logged_out_app.ts b/x-pack/plugins/security/public/authentication/logged_out/logged_out_app.ts index 962524de6d1f0..b84bf9b21f2fc 100644 --- a/x-pack/plugins/security/public/authentication/logged_out/logged_out_app.ts +++ b/x-pack/plugins/security/public/authentication/logged_out/logged_out_app.ts @@ -28,12 +28,16 @@ export const loggedOutApp = Object.freeze({ title: i18n.translate('xpack.security.loggedOutAppTitle', { defaultMessage: 'Logged out' }), chromeless: true, appRoute: '/security/logged_out', - async mount({ element }: AppMountParameters) { + async mount({ element, theme$ }: AppMountParameters) { const [[coreStart], { renderLoggedOutPage }] = await Promise.all([ getStartServices(), import('./logged_out_page'), ]); - return renderLoggedOutPage(coreStart.i18n, element, { basePath: coreStart.http.basePath }); + return renderLoggedOutPage( + coreStart.i18n, + { element, theme$ }, + { basePath: coreStart.http.basePath } + ); }, }); }, diff --git a/x-pack/plugins/security/public/authentication/logged_out/logged_out_page.tsx b/x-pack/plugins/security/public/authentication/logged_out/logged_out_page.tsx index 477a41e248858..1e897c26400eb 100644 --- a/x-pack/plugins/security/public/authentication/logged_out/logged_out_page.tsx +++ b/x-pack/plugins/security/public/authentication/logged_out/logged_out_page.tsx @@ -10,8 +10,9 @@ import React from 'react'; import ReactDOM from 'react-dom'; import { FormattedMessage } from '@kbn/i18n/react'; -import type { CoreStart, IBasePath } from 'src/core/public'; +import type { AppMountParameters, CoreStart, IBasePath } from 'src/core/public'; +import { KibanaThemeProvider } from '../../../../../../src/plugins/kibana_react/public'; import { parseNext } from '../../../common/parse_next'; import { AuthenticationStatePage } from '../components'; @@ -36,10 +37,16 @@ export function LoggedOutPage({ basePath }: Props) { ); } -export function renderLoggedOutPage(i18nStart: CoreStart['i18n'], element: Element, props: Props) { +export function renderLoggedOutPage( + i18nStart: CoreStart['i18n'], + { element, theme$ }: Pick, + props: Props +) { ReactDOM.render( - + + + , element ); diff --git a/x-pack/plugins/security/public/authentication/login/login_app.test.ts b/x-pack/plugins/security/public/authentication/login/login_app.test.ts index 5406494067d0e..bf121c02f4c96 100644 --- a/x-pack/plugins/security/public/authentication/login/login_app.test.ts +++ b/x-pack/plugins/security/public/authentication/login/login_app.test.ts @@ -40,7 +40,6 @@ describe('loginApp', () => { const coreSetupMock = coreMock.createSetup(); const coreStartMock = coreMock.createStart(); coreSetupMock.getStartServices.mockResolvedValue([coreStartMock, {}, {}]); - const containerMock = document.createElement('div'); loginApp.create({ ...coreSetupMock, @@ -48,22 +47,27 @@ describe('loginApp', () => { }); const [[{ mount }]] = coreSetupMock.application.register.mock.calls; - await (mount as AppMount)({ - element: containerMock, + const appMountParams = { + element: document.createElement('div'), appBasePath: '', onAppLeave: jest.fn(), setHeaderActionMenu: jest.fn(), history: scopedHistoryMock.create(), theme$: themeServiceMock.createTheme$(), - }); + }; + await (mount as AppMount)(appMountParams); const mockRenderApp = jest.requireMock('./login_page').renderLoginPage; expect(mockRenderApp).toHaveBeenCalledTimes(1); - expect(mockRenderApp).toHaveBeenCalledWith(coreStartMock.i18n, containerMock, { - http: coreStartMock.http, - notifications: coreStartMock.notifications, - fatalErrors: coreStartMock.fatalErrors, - loginAssistanceMessage: 'some-message', - }); + expect(mockRenderApp).toHaveBeenCalledWith( + coreStartMock.i18n, + { element: appMountParams.element, theme$: appMountParams.theme$ }, + { + http: coreStartMock.http, + notifications: coreStartMock.notifications, + fatalErrors: coreStartMock.fatalErrors, + loginAssistanceMessage: 'some-message', + } + ); }); }); diff --git a/x-pack/plugins/security/public/authentication/login/login_app.ts b/x-pack/plugins/security/public/authentication/login/login_app.ts index 21937e937ccf1..62007e9c56e5e 100644 --- a/x-pack/plugins/security/public/authentication/login/login_app.ts +++ b/x-pack/plugins/security/public/authentication/login/login_app.ts @@ -31,17 +31,21 @@ export const loginApp = Object.freeze({ title: i18n.translate('xpack.security.loginAppTitle', { defaultMessage: 'Login' }), chromeless: true, appRoute: '/login', - async mount({ element }: AppMountParameters) { + async mount({ element, theme$ }: AppMountParameters) { const [[coreStart], { renderLoginPage }] = await Promise.all([ getStartServices(), import('./login_page'), ]); - return renderLoginPage(coreStart.i18n, element, { - http: coreStart.http, - notifications: coreStart.notifications, - fatalErrors: coreStart.fatalErrors, - loginAssistanceMessage: config.loginAssistanceMessage, - }); + return renderLoginPage( + coreStart.i18n, + { element, theme$ }, + { + http: coreStart.http, + notifications: coreStart.notifications, + fatalErrors: coreStart.fatalErrors, + loginAssistanceMessage: config.loginAssistanceMessage, + } + ); }, }); }, diff --git a/x-pack/plugins/security/public/authentication/login/login_page.tsx b/x-pack/plugins/security/public/authentication/login/login_page.tsx index e22c38b956e8d..0f32bd5cbbd41 100644 --- a/x-pack/plugins/security/public/authentication/login/login_page.tsx +++ b/x-pack/plugins/security/public/authentication/login/login_page.tsx @@ -15,8 +15,15 @@ import { BehaviorSubject } from 'rxjs'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import type { CoreStart, FatalErrorsStart, HttpStart, NotificationsStart } from 'src/core/public'; +import type { + AppMountParameters, + CoreStart, + FatalErrorsStart, + HttpStart, + NotificationsStart, +} from 'src/core/public'; +import { KibanaThemeProvider } from '../../../../../../src/plugins/kibana_react/public'; import { AUTH_PROVIDER_HINT_QUERY_STRING_PARAMETER, LOGOUT_REASON_QUERY_STRING_PARAMETER, @@ -251,10 +258,16 @@ export class LoginPage extends Component { }; } -export function renderLoginPage(i18nStart: CoreStart['i18n'], element: Element, props: Props) { +export function renderLoginPage( + i18nStart: CoreStart['i18n'], + { element, theme$ }: Pick, + props: Props +) { ReactDOM.render( - + + + , element ); diff --git a/x-pack/plugins/security/public/authentication/overwritten_session/overwritten_session_app.test.ts b/x-pack/plugins/security/public/authentication/overwritten_session/overwritten_session_app.test.ts index 95497bdc1fb54..6fb40181e1b77 100644 --- a/x-pack/plugins/security/public/authentication/overwritten_session/overwritten_session_app.test.ts +++ b/x-pack/plugins/security/public/authentication/overwritten_session/overwritten_session_app.test.ts @@ -41,8 +41,6 @@ describe('overwrittenSessionApp', () => { coreSetupMock.getStartServices.mockResolvedValue([coreStartMock, {}, {}]); const authcMock = securityMock.createSetup().authc; - const containerMock = document.createElement('div'); - overwrittenSessionApp.create({ application: coreSetupMock.application, getStartServices: coreSetupMock.getStartServices, @@ -50,22 +48,24 @@ describe('overwrittenSessionApp', () => { }); const [[{ mount }]] = coreSetupMock.application.register.mock.calls; - await (mount as AppMount)({ - element: containerMock, + const appMountParams = { + element: document.createElement('div'), appBasePath: '', onAppLeave: jest.fn(), setHeaderActionMenu: jest.fn(), history: scopedHistoryMock.create(), theme$: themeServiceMock.createTheme$(), - }); + }; + await (mount as AppMount)(appMountParams); const mockRenderApp = jest.requireMock( './overwritten_session_page' ).renderOverwrittenSessionPage; expect(mockRenderApp).toHaveBeenCalledTimes(1); - expect(mockRenderApp).toHaveBeenCalledWith(coreStartMock.i18n, containerMock, { - authc: authcMock, - basePath: coreStartMock.http.basePath, - }); + expect(mockRenderApp).toHaveBeenCalledWith( + coreStartMock.i18n, + { element: appMountParams.element, theme$: appMountParams.theme$ }, + { authc: authcMock, basePath: coreStartMock.http.basePath } + ); }); }); diff --git a/x-pack/plugins/security/public/authentication/overwritten_session/overwritten_session_app.ts b/x-pack/plugins/security/public/authentication/overwritten_session/overwritten_session_app.ts index 715d732cd47e7..d03f300f67608 100644 --- a/x-pack/plugins/security/public/authentication/overwritten_session/overwritten_session_app.ts +++ b/x-pack/plugins/security/public/authentication/overwritten_session/overwritten_session_app.ts @@ -26,15 +26,19 @@ export const overwrittenSessionApp = Object.freeze({ }), chromeless: true, appRoute: '/security/overwritten_session', - async mount({ element }: AppMountParameters) { + async mount({ element, theme$ }: AppMountParameters) { const [[coreStart], { renderOverwrittenSessionPage }] = await Promise.all([ getStartServices(), import('./overwritten_session_page'), ]); - return renderOverwrittenSessionPage(coreStart.i18n, element, { - authc, - basePath: coreStart.http.basePath, - }); + return renderOverwrittenSessionPage( + coreStart.i18n, + { element, theme$ }, + { + authc, + basePath: coreStart.http.basePath, + } + ); }, }); }, diff --git a/x-pack/plugins/security/public/authentication/overwritten_session/overwritten_session_page.tsx b/x-pack/plugins/security/public/authentication/overwritten_session/overwritten_session_page.tsx index 4d9d62bb7c65f..72eb8da0271a3 100644 --- a/x-pack/plugins/security/public/authentication/overwritten_session/overwritten_session_page.tsx +++ b/x-pack/plugins/security/public/authentication/overwritten_session/overwritten_session_page.tsx @@ -10,8 +10,9 @@ import React, { useEffect, useState } from 'react'; import ReactDOM from 'react-dom'; import { FormattedMessage } from '@kbn/i18n/react'; -import type { CoreStart, IBasePath } from 'src/core/public'; +import type { AppMountParameters, CoreStart, IBasePath } from 'src/core/public'; +import { KibanaThemeProvider } from '../../../../../../src/plugins/kibana_react/public'; import { parseNext } from '../../../common/parse_next'; import type { AuthenticationServiceSetup } from '../authentication_service'; import { AuthenticationStatePage } from '../components'; @@ -53,12 +54,14 @@ export function OverwrittenSessionPage({ authc, basePath }: Props) { export function renderOverwrittenSessionPage( i18nStart: CoreStart['i18n'], - element: Element, + { element, theme$ }: Pick, props: Props ) { ReactDOM.render( - + + + , element ); diff --git a/x-pack/plugins/security/public/management/api_keys/api_keys_grid/api_keys_grid_page.test.tsx b/x-pack/plugins/security/public/management/api_keys/api_keys_grid/api_keys_grid_page.test.tsx index 6e3de061fd191..1e944e1f31353 100644 --- a/x-pack/plugins/security/public/management/api_keys/api_keys_grid/api_keys_grid_page.test.tsx +++ b/x-pack/plugins/security/public/management/api_keys/api_keys_grid/api_keys_grid_page.test.tsx @@ -9,7 +9,7 @@ import { render } from '@testing-library/react'; import { createMemoryHistory } from 'history'; import React from 'react'; -import { coreMock } from '../../../../../../../src/core/public/mocks'; +import { coreMock, themeServiceMock } from '../../../../../../../src/core/public/mocks'; import { mockAuthenticatedUser } from '../../../../common/model/authenticated_user.mock'; import { securityMock } from '../../../mocks'; import { Providers } from '../api_keys_management_app'; @@ -33,6 +33,7 @@ describe('APIKeysGridPage', () => { const consoleWarnMock = jest.spyOn(console, 'error').mockImplementation(); const coreStart = coreMock.createStart(); + const theme$ = themeServiceMock.createTheme$(); const apiClientMock = apiKeysAPIClientMock.create(); const { authc } = securityMock.createSetup(); @@ -85,7 +86,7 @@ describe('APIKeysGridPage', () => { const history = createMemoryHistory({ initialEntries: ['/'] }); const { findByText } = render( - + { }); const { findByText } = render( - + { }); const { findByText } = render( - + { const history = createMemoryHistory({ initialEntries: ['/'] }); const { findByText } = render( - + ; history: History; authc: AuthenticationServiceSetup; onChange?: BreadcrumbsChangeHandler; @@ -88,6 +98,7 @@ export interface ProvidersProps { export const Providers: FunctionComponent = ({ services, + theme$, history, authc, onChange, @@ -96,9 +107,11 @@ export const Providers: FunctionComponent = ({ - - {children} - + + + {children} + + diff --git a/x-pack/plugins/security/public/management/role_mappings/role_mappings_management_app.tsx b/x-pack/plugins/security/public/management/role_mappings/role_mappings_management_app.tsx index 41e6a9562612d..9367e7cd447f2 100644 --- a/x-pack/plugins/security/public/management/role_mappings/role_mappings_management_app.tsx +++ b/x-pack/plugins/security/public/management/role_mappings/role_mappings_management_app.tsx @@ -13,7 +13,10 @@ import { i18n } from '@kbn/i18n'; import type { StartServicesAccessor } from 'src/core/public'; import type { RegisterManagementAppArgs } from 'src/plugins/management/public'; -import { KibanaContextProvider } from '../../../../../../src/plugins/kibana_react/public'; +import { + KibanaContextProvider, + KibanaThemeProvider, +} from '../../../../../../src/plugins/kibana_react/public'; import { Breadcrumb, BreadcrumbsProvider, @@ -37,7 +40,7 @@ export const roleMappingsManagementApp = Object.freeze({ id: this.id, order: 40, title, - async mount({ element, setBreadcrumbs, history }) { + async mount({ element, theme$, setBreadcrumbs, history }) { const [ [core], { RoleMappingsGridPage }, @@ -90,30 +93,32 @@ export const roleMappingsManagementApp = Object.freeze({ render( - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + , element diff --git a/x-pack/plugins/security/public/management/roles/roles_management_app.tsx b/x-pack/plugins/security/public/management/roles/roles_management_app.tsx index fcd037a861ed0..fb68fa7857668 100644 --- a/x-pack/plugins/security/public/management/roles/roles_management_app.tsx +++ b/x-pack/plugins/security/public/management/roles/roles_management_app.tsx @@ -13,7 +13,10 @@ import { i18n } from '@kbn/i18n'; import type { FatalErrorsSetup, StartServicesAccessor } from 'src/core/public'; import type { RegisterManagementAppArgs } from 'src/plugins/management/public'; -import { KibanaContextProvider } from '../../../../../../src/plugins/kibana_react/public'; +import { + KibanaContextProvider, + KibanaThemeProvider, +} from '../../../../../../src/plugins/kibana_react/public'; import type { SecurityLicense } from '../../../common/licensing'; import { Breadcrumb, @@ -39,7 +42,7 @@ export const rolesManagementApp = Object.freeze({ id: this.id, order: 20, title, - async mount({ element, setBreadcrumbs, history }) { + async mount({ element, theme$, setBreadcrumbs, history }) { const [ [startServices, { data, features, spaces }], { RolesGridPage }, @@ -116,27 +119,29 @@ export const rolesManagementApp = Object.freeze({ render( - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + , element diff --git a/x-pack/plugins/security/public/management/users/edit_user/create_user_page.test.tsx b/x-pack/plugins/security/public/management/users/edit_user/create_user_page.test.tsx index c3b9304be37f7..5ae0a3c85cd0a 100644 --- a/x-pack/plugins/security/public/management/users/edit_user/create_user_page.test.tsx +++ b/x-pack/plugins/security/public/management/users/edit_user/create_user_page.test.tsx @@ -9,7 +9,7 @@ import { fireEvent, render, waitFor, within } from '@testing-library/react'; import { createMemoryHistory } from 'history'; import React from 'react'; -import { coreMock } from 'src/core/public/mocks'; +import { coreMock, themeServiceMock } from 'src/core/public/mocks'; import { securityMock } from '../../../mocks'; import { Providers } from '../users_management_app'; @@ -22,6 +22,8 @@ jest.mock('@elastic/eui/lib/services/accessibility/html_id_generator', () => ({ describe('CreateUserPage', () => { jest.setTimeout(15_000); + const theme$ = themeServiceMock.createTheme$(); + it('creates user when submitting form and redirects back', async () => { const coreStart = coreMock.createStart(); const history = createMemoryHistory({ initialEntries: ['/create'] }); @@ -29,7 +31,7 @@ describe('CreateUserPage', () => { coreStart.http.post.mockResolvedValue({}); const { findByRole, findByLabelText } = render( - + ); @@ -72,7 +74,7 @@ describe('CreateUserPage', () => { ]); const { findAllByText, findByRole, findByLabelText } = render( - + ); diff --git a/x-pack/plugins/security/public/management/users/edit_user/edit_user_page.test.tsx b/x-pack/plugins/security/public/management/users/edit_user/edit_user_page.test.tsx index 66a73d9c6aa87..3e0970b66563b 100644 --- a/x-pack/plugins/security/public/management/users/edit_user/edit_user_page.test.tsx +++ b/x-pack/plugins/security/public/management/users/edit_user/edit_user_page.test.tsx @@ -9,7 +9,7 @@ import { fireEvent, render } from '@testing-library/react'; import { createMemoryHistory } from 'history'; import React from 'react'; -import { coreMock } from 'src/core/public/mocks'; +import { coreMock, themeServiceMock } from 'src/core/public/mocks'; import { securityMock } from '../../../mocks'; import { Providers } from '../users_management_app'; @@ -25,6 +25,7 @@ const userMock = { describe('EditUserPage', () => { const coreStart = coreMock.createStart(); + const theme$ = themeServiceMock.createTheme$(); let history = createMemoryHistory({ initialEntries: ['/edit/jdoe'] }); const authc = securityMock.createSetup().authc; @@ -46,7 +47,7 @@ describe('EditUserPage', () => { coreStart.http.get.mockResolvedValueOnce([]); const { findByText } = render( - + ); @@ -66,7 +67,7 @@ describe('EditUserPage', () => { coreStart.http.get.mockResolvedValueOnce([]); const { findByRole, findByText } = render( - + ); @@ -87,7 +88,7 @@ describe('EditUserPage', () => { coreStart.http.get.mockResolvedValueOnce([]); const { findByRole, findByText } = render( - + ); @@ -117,7 +118,7 @@ describe('EditUserPage', () => { ]); const { findByText } = render( - + ); diff --git a/x-pack/plugins/security/public/management/users/users_management_app.tsx b/x-pack/plugins/security/public/management/users/users_management_app.tsx index 7957599da7f57..43b217c186719 100644 --- a/x-pack/plugins/security/public/management/users/users_management_app.tsx +++ b/x-pack/plugins/security/public/management/users/users_management_app.tsx @@ -11,13 +11,17 @@ import React from 'react'; import { render, unmountComponentAtNode } from 'react-dom'; import type { RouteComponentProps } from 'react-router-dom'; import { Redirect, Route, Router, Switch } from 'react-router-dom'; +import type { Observable } from 'rxjs'; import { i18n } from '@kbn/i18n'; import { I18nProvider } from '@kbn/i18n/react'; -import type { CoreStart, StartServicesAccessor } from 'src/core/public'; +import type { CoreStart, CoreTheme, StartServicesAccessor } from 'src/core/public'; import type { RegisterManagementAppArgs } from 'src/plugins/management/public'; -import { KibanaContextProvider } from '../../../../../../src/plugins/kibana_react/public'; +import { + KibanaContextProvider, + KibanaThemeProvider, +} from '../../../../../../src/plugins/kibana_react/public'; import type { AuthenticationServiceSetup } from '../../authentication'; import type { BreadcrumbsChangeHandler } from '../../components/breadcrumb'; import { @@ -48,7 +52,7 @@ export const usersManagementApp = Object.freeze({ id: this.id, order: 10, title, - async mount({ element, setBreadcrumbs, history }) { + async mount({ element, theme$, setBreadcrumbs, history }) { const [ [coreStart], { UsersGridPage }, @@ -66,6 +70,7 @@ export const usersManagementApp = Object.freeze({ render( ; history: History; authc: AuthenticationServiceSetup; onChange?: BreadcrumbsChangeHandler; @@ -135,6 +141,7 @@ export interface ProvidersProps { export const Providers: FunctionComponent = ({ services, + theme$, history, authc, onChange, @@ -143,9 +150,11 @@ export const Providers: FunctionComponent = ({ - - {children} - + + + {children} + + diff --git a/x-pack/plugins/security/public/nav_control/nav_control_service.tsx b/x-pack/plugins/security/public/nav_control/nav_control_service.tsx index c1be6999c6472..0db60c83f55d7 100644 --- a/x-pack/plugins/security/public/nav_control/nav_control_service.tsx +++ b/x-pack/plugins/security/public/nav_control/nav_control_service.tsx @@ -14,6 +14,7 @@ import { map, takeUntil } from 'rxjs/operators'; import type { CoreStart } from 'src/core/public'; +import { KibanaThemeProvider } from '../../../../../src/plugins/kibana_react/public'; import type { SecurityLicense } from '../../common/licensing'; import type { AuthenticationServiceSetup } from '../authentication'; import type { UserMenuLink } from './nav_control_component'; @@ -110,8 +111,9 @@ export class SecurityNavControlService { } private registerSecurityNavControl( - core: Pick + core: Pick ) { + const { theme$ } = core.theme; const currentUserPromise = this.authc.getCurrentUser(); core.chrome.navControls.registerRight({ order: 2000, @@ -126,7 +128,9 @@ export class SecurityNavControlService { }; ReactDOM.render( - + + + , el ); diff --git a/x-pack/plugins/spaces/public/management/spaces_management_app.tsx b/x-pack/plugins/spaces/public/management/spaces_management_app.tsx index 8ea947a33037d..30a724ba504b3 100644 --- a/x-pack/plugins/spaces/public/management/spaces_management_app.tsx +++ b/x-pack/plugins/spaces/public/management/spaces_management_app.tsx @@ -16,6 +16,7 @@ import type { RegisterManagementAppArgs } from 'src/plugins/management/public'; import { APP_WRAPPER_CLASS } from '../../../../../src/core/public'; import { KibanaContextProvider, + KibanaThemeProvider, RedirectAppLinks, } from '../../../../../src/plugins/kibana_react/public'; import type { Space } from '../../common'; @@ -39,7 +40,7 @@ export const spacesManagementApp = Object.freeze({ order: 2, title, - async mount({ element, setBreadcrumbs, history }) { + async mount({ element, theme$, setBreadcrumbs, history }) { const [[coreStart, { features }], { SpacesGridPage }, { ManageSpacePage }] = await Promise.all([getStartServices(), import('./spaces_grid'), import('./edit_space')]); @@ -114,21 +115,23 @@ export const spacesManagementApp = Object.freeze({ render( - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + , element diff --git a/x-pack/plugins/spaces/public/nav_control/nav_control.tsx b/x-pack/plugins/spaces/public/nav_control/nav_control.tsx index aa4d9e0bef704..1afb6c9244f35 100644 --- a/x-pack/plugins/spaces/public/nav_control/nav_control.tsx +++ b/x-pack/plugins/spaces/public/nav_control/nav_control.tsx @@ -11,10 +11,12 @@ import ReactDOM from 'react-dom'; import type { CoreStart } from 'src/core/public'; +import { KibanaThemeProvider } from '../../../../../src/plugins/kibana_react/public'; import type { SpacesManager } from '../spaces_manager'; export function initSpacesNavControl(spacesManager: SpacesManager, core: CoreStart) { const I18nContext = core.i18n.Context; + const { theme$ } = core.theme; core.chrome.navControls.registerLeft({ order: 1000, mount(targetDomElement: HTMLElement) { @@ -30,15 +32,17 @@ export function initSpacesNavControl(spacesManager: SpacesManager, core: CoreSta ReactDOM.render( - }> - - + + }> + + + , targetDomElement ); diff --git a/x-pack/plugins/spaces/public/space_selector/space_selector.tsx b/x-pack/plugins/spaces/public/space_selector/space_selector.tsx index 00ad39bf0027f..283653efc6626 100644 --- a/x-pack/plugins/spaces/public/space_selector/space_selector.tsx +++ b/x-pack/plugins/spaces/public/space_selector/space_selector.tsx @@ -27,8 +27,9 @@ import ReactDOM from 'react-dom'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import type { CoreStart } from 'src/core/public'; +import type { AppMountParameters, CoreStart } from 'src/core/public'; +import { KibanaThemeProvider } from '../../../../../src/plugins/kibana_react/public'; import type { Space } from '../../common'; import { SPACE_SEARCH_COUNT_THRESHOLD } from '../../common/constants'; import type { SpacesManager } from '../spaces_manager'; @@ -213,12 +214,18 @@ export class SpaceSelector extends Component { }; } -export const renderSpaceSelectorApp = (i18nStart: CoreStart['i18n'], el: Element, props: Props) => { +export const renderSpaceSelectorApp = ( + i18nStart: CoreStart['i18n'], + { element, theme$ }: Pick, + props: Props +) => { ReactDOM.render( - + + + , - el + element ); - return () => ReactDOM.unmountComponentAtNode(el); + return () => ReactDOM.unmountComponentAtNode(element); }; diff --git a/x-pack/plugins/spaces/public/space_selector/space_selector_app.tsx b/x-pack/plugins/spaces/public/space_selector/space_selector_app.tsx index 542a4be50d57c..d6935e065815d 100644 --- a/x-pack/plugins/spaces/public/space_selector/space_selector_app.tsx +++ b/x-pack/plugins/spaces/public/space_selector/space_selector_app.tsx @@ -26,15 +26,19 @@ export const spaceSelectorApp = Object.freeze({ }), chromeless: true, appRoute: '/spaces/space_selector', - mount: async (params: AppMountParameters) => { + mount: async ({ element, theme$ }: AppMountParameters) => { const [[coreStart], { renderSpaceSelectorApp }] = await Promise.all([ getStartServices(), import('./space_selector'), ]); - return renderSpaceSelectorApp(coreStart.i18n, params.element, { - spacesManager, - serverBasePath: coreStart.http.basePath.serverBasePath, - }); + return renderSpaceSelectorApp( + coreStart.i18n, + { element, theme$ }, + { + spacesManager, + serverBasePath: coreStart.http.basePath.serverBasePath, + } + ); }, }); },