Changing where CodeEditor fields get useDarkMode value (#159638)

## Summary

Closes https://github.com/elastic/kibana/issues/157203
Closes https://github.com/elastic/kibana/issues/158051
Closes https://github.com/elastic/kibana/issues/158049

With the addition of Per User Dark Mode, components can no longer rely
on `uiSettings` as the source of truth for theme.

CodeEditor fields used to call `uiSettings` to determine if Dark Mode
was enabled, which had been provided in each callers Kibana React
Context.

The new source of truth for theme is the `CoreStart >
ThemeServiceStart`.

Currently, not all callers of CodeEditor provide the `theme` service in
their Kibana Context in a similar way and some callers don't provide it
at all.

This PR updates CodeEditor to get theme values from the Kibana Context
using a new `useKibanaTheme` react hook.

It also attempts audit the callers of CodeEditor to see if their Kibana
Context contains the theme service at the top level (Where I could add
theme to a caller's Context without major changes, I did. Some cases
will require CodeOwner guidance).

The new `useKibanaTheme` react hook will throw a TypeError if theme
isn't found in the top level of the Kibana Context, this will help with
testing as the component will not render. I will remove this after
testing so as not to introduce breaking changes.

## Testing

Please review files for which you are CODEOWNER.

I've attempted to tag all usages of `CodeEditor`/`CodeEditorFIeld` with
a TODO comment with one of the following scenarios:

1) a note where theme was provided already/where I made changes to
provide it in the appropriate context
2) I've asked for CODEOWNER guidance

For scenario 1, please pull and test that CodeEditor locally:

1. Enable Dark Mode from Edit User Profiles by clicking on the Profile
Icon on the top right, and updating your profile.
2. Navigate to the CodeEditors in the plugins you own. If they render
and display in Dark Mode - add a green check to the table below - and
you're done!
3. If it is not rendering, please help me figure out where the theme
service should be provided in the context.

For scenario 2, we will need to figure out where to make changes so your
context is providing theme. Some of the more complex usages may need to
addressed in separate issues.


## Tracking
| Team | Plugin | Theme in Context ? | Verified Working |
| - | - | - | - |
| apm-ui | apm | APM Storybook broken | ? |
| kibana-presentation | presentation_util | Yes. | Yes |
| response-ops | trigger_actions_ui | Yes | Yes |
| response-ops | stack_alerts | Yes | Yes |
| kibana-security | security  | Yes | Yes |
| security-defend-workflows | osquery | Yes | Yes |
| kibana-app-services | examples/expression_explorer | Yes | Yes |
| ml-ui | transform | Yes | Yes |
| ml-ui | ml | Yes | Yes |
| uptime | synthetics | Yes | Yes |
| kibana-gis | maps | Yes | Yes |
| kibana-gis | file_upload | Yes | Yes |
| platform-deployment-management | watcher | Yes | [AG] Yes |
| platform-deployment-management | snapshot_restore | Yes | [AG] Yes |
| platform-deployment-management | runtime_fields | Yes | [AG] Yes |
| platform-deployment-management | painless_lab | Yes | [AG] Yes |
| platform-deployment-management | ingest_pipelines | Yes | [AG] Yes |
| platform-deployment-management | index_management | Yes | [AG] Yes |
| platform-deployment-management | grokdebugger | Yes | [AG] Yes |
| platform-deployment-management | es_ui_shared | Yes | [AG] Yes |
| fleet | fleet | Yes | Yes |
| enterprise-search-frontend | enterprise_search | Yes | [AG] Yes |
| kibana-cloud-security-posture | cloud-security-posture | Yes | yes |
| sec-cloudnative-integrations | cloud_defend | Yes | Yes |
| kibana-visualizations/kibana-data-discovery | data | Yes | Yes |
| kibana-visualizations | examples/testing_embedded_lens | Yes | Yes |
| kibana-visualizations | vis_types | Yes | Yes |
| kibana-visualizations | vis_default_editor | Yes | Yes |
| kibana-visualizations | unified_search | Yes | Yes |
| kibana-visualizations | packages/kbn-text-based-editor | Yes | Yes |
| kibana-visualizatons | lens | Yes | Yes|
| kibana-core | saved_objects_management | Yes | Yes |
| kibana-presentation | inspector | Yes | Yes |
| kibana-presentation | canvas | Yes | Yes |
| kibana-data-discovery | discover | Yes | Yes |
| kibana-data-discovery | data_view_management | Yes | Yes |
| kibana-data-discovery | data_view_field_editor | Yes | Yes |
| appex-sharedux | advanced_settings | Yes | Yes |
| enterprise-search-frontend | serverless_search | Yes | [AG] Yes |
| - | - | - | - |

## Unit tests

Currently, many tests are failing since they are probably not providing
`theme` in the context. Once CODEOWNERs have weighed in on CodeEditors
usages that require discussion, I will update the accompanying tests.

## Release note
- Fixes theming of CodeEditors

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: Alison Goryachev <alisonmllr20@gmail.com>
Co-authored-by: Dima Arnautov <dmitrii.arnautov@elastic.co>
Co-authored-by: Dima Arnautov <arnautov.dima@gmail.com>
This commit is contained in:
Kurt 2023-07-11 14:02:42 -04:00 committed by GitHub
parent 4ce8b3f4eb
commit 323b0477e3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
44 changed files with 220 additions and 36 deletions

View file

@ -18,7 +18,7 @@ import {
EuiText,
EuiLink,
} from '@elastic/eui';
import { AppMountParameters, IUiSettingsClient } from '@kbn/core/public';
import { AppMountParameters, IUiSettingsClient, ThemeServiceStart } from '@kbn/core/public';
import { ExpressionsStart } from '@kbn/expressions-plugin/public';
import { Start as InspectorStart } from '@kbn/inspector-plugin/public';
import { UiActionsStart } from '@kbn/ui-actions-plugin/public';
@ -35,12 +35,21 @@ interface Props {
actions: UiActionsStart;
uiSettings: IUiSettingsClient;
settings: SettingsStart;
theme: ThemeServiceStart;
}
const ExpressionsExplorer = ({ expressions, inspector, actions, uiSettings, settings }: Props) => {
const ExpressionsExplorer = ({
expressions,
inspector,
actions,
uiSettings,
settings,
theme,
}: Props) => {
const { Provider: KibanaReactContextProvider } = createKibanaReactContext({
uiSettings,
settings,
theme,
});
return (
<KibanaReactContextProvider>

View file

@ -49,7 +49,7 @@ export class ExpressionsExplorerPlugin implements Plugin<void, void, SetupDeps,
title: 'Expressions Explorer',
navLinkStatus: AppNavLinkStatus.hidden,
async mount(params: AppMountParameters) {
const [, depsStart] = await core.getStartServices();
const [coreStart, depsStart] = await core.getStartServices();
const { renderApp } = await import('./app');
return renderApp(
{
@ -58,6 +58,7 @@ export class ExpressionsExplorerPlugin implements Plugin<void, void, SetupDeps,
actions: depsStart.uiActions,
uiSettings: core.uiSettings,
settings: core.settings,
theme: coreStart.theme,
},
params
);

View file

@ -16,18 +16,23 @@ import {
TextBasedLanguagesEditor,
TextBasedLanguagesEditorProps,
} from './text_based_languages_editor';
import { of } from 'rxjs';
describe('TextBasedLanguagesEditor', () => {
const uiConfig: Record<string, any> = {};
const uiSettings = {
get: (key: string) => uiConfig[key],
} as IUiSettingsClient;
const theme = {
theme$: of({ darkMode: false }),
};
const services = {
uiSettings,
settings: {
client: uiSettings,
},
theme,
};
function renderTextBasedLanguagesEditorComponent(testProps: TextBasedLanguagesEditorProps) {

View file

@ -74,6 +74,7 @@ export class AdvancedSettings extends Component<AdvancedSettingsProps> {
services={{
uiSettings: this.props.settingsService.client,
settings: this.props.settingsService,
theme: { theme$: this.props.theme },
}}
>
<Form

View file

@ -21,6 +21,12 @@ jest.mock('@kbn/kibana-react-plugin/public/ui_settings/use_ui_setting', () => ({
useUiSetting: jest.fn(),
}));
jest.mock('@kbn/kibana-react-plugin/public/theme/use_theme', () => ({
useKibanaTheme: jest.fn(() => {
return { darkMode: false };
}),
}));
const defaults = {
requiresPageReload: false,
readOnly: false,

View file

@ -16,6 +16,7 @@ import { EuiButton, EuiEmptyPrompt, EuiLoadingSpinner } from '@elastic/eui';
import { JsonCodeEditorCommon } from '../../../../components/json_code_editor/json_code_editor_common';
import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public';
import { buildDataTableRecord } from '../../../../utils/build_data_record';
import { of } from 'rxjs';
const mockDataView = {
getComputedFields: () => [],
@ -35,6 +36,9 @@ const services = {
data: {
dataViewService: mockDataViewService,
},
theme: {
theme$: of({ darkMode: false }),
},
};
describe('Source Viewer component', () => {

View file

@ -106,6 +106,7 @@ export class InspectorPublicPlugin implements Plugin<Setup, Start> {
uiSettings: core.uiSettings,
share: startDeps.share,
settings: core.settings,
theme: core.theme,
}}
/>,
{ theme$: core.theme.theme$ }

View file

@ -14,9 +14,10 @@ import { Adapters } from '../../common';
import type { ApplicationStart, HttpSetup, IUiSettingsClient } from '@kbn/core/public';
import { SharePluginStart } from '@kbn/share-plugin/public';
import { sharePluginMock } from '@kbn/share-plugin/public/mocks';
import { applicationServiceMock } from '@kbn/core/public/mocks';
import { applicationServiceMock, themeServiceMock } from '@kbn/core/public/mocks';
import { settingsServiceMock } from '@kbn/core-ui-settings-browser-mocks';
import type { SettingsStart } from '@kbn/core-ui-settings-browser';
import { ThemeServiceStart } from '@kbn/core/public';
describe('InspectorPanel', () => {
let adapters: Adapters;
@ -27,12 +28,14 @@ describe('InspectorPanel', () => {
share: sharePluginMock.createStartContract(),
uiSettings: {},
settings: settingsServiceMock.createStartContract(),
theme: themeServiceMock.createStartContract(),
} as unknown as {
application: ApplicationStart;
http: HttpSetup;
share: SharePluginStart;
uiSettings: IUiSettingsClient;
settings: SettingsStart;
theme: ThemeServiceStart;
};
beforeEach(() => {

View file

@ -18,7 +18,12 @@ import {
EuiFlyoutBody,
EuiLoadingSpinner,
} from '@elastic/eui';
import { ApplicationStart, HttpStart, IUiSettingsClient } from '@kbn/core/public';
import {
ApplicationStart,
HttpStart,
IUiSettingsClient,
ThemeServiceStart,
} from '@kbn/core/public';
import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public';
import { SharePluginStart } from '@kbn/share-plugin/public';
import type { SettingsStart } from '@kbn/core-ui-settings-browser';
@ -48,6 +53,7 @@ interface InspectorPanelProps {
uiSettings: IUiSettingsClient;
share: SharePluginStart;
settings: SettingsStart;
theme: ThemeServiceStart;
};
}

View file

@ -115,8 +115,8 @@ describe('<CodeEditor />', () => {
// Verify our mount callback will be called
expect(editorWillMount.mock.calls.length).toBe(1);
// Verify that both, default and transparent theme will be setup
expect((monaco.editor.defineTheme as jest.Mock).mock.calls.length).toBe(2);
// Verify that both, default and transparent theme will be setup and then redefined as new values come through from the theme$ observable
expect((monaco.editor.defineTheme as jest.Mock).mock.calls.length).toBe(4);
// Verify our language features have been registered
expect((monaco.languages.onLanguage as jest.Mock).mock.calls.length).toBe(1);

View file

@ -357,6 +357,15 @@ export const CodeEditor: React.FC<Props> = ({
]
);
useEffect(() => {
// Register themes when 'useDarkThem' changes
monaco.editor.defineTheme('euiColors', useDarkTheme ? DARK_THEME : LIGHT_THEME);
monaco.editor.defineTheme(
'euiColorsTransparent',
useDarkTheme ? DARK_THEME_TRANSPARENT : LIGHT_THEME_TRANSPARENT
);
}, [useDarkTheme]);
const _editorDidMount = useCallback(
(editor: monaco.editor.IStandaloneCodeEditor, __monaco: unknown) => {
if (__monaco !== monaco) {

View file

@ -9,7 +9,7 @@
import React from 'react';
import { EuiDelayRender, EuiErrorBoundary, EuiSkeletonText } from '@elastic/eui';
import { useUiSetting } from '../ui_settings';
import { useKibanaTheme } from '../theme';
import type { Props } from './code_editor';
export * from './languages/constants';
@ -40,11 +40,11 @@ export type CodeEditorProps = Props;
* @see CodeEditorField to render a code editor in the same style as other EUI form fields.
*/
export const CodeEditor: React.FunctionComponent<Props> = (props) => {
const darkMode = useUiSetting<boolean>('theme:darkMode');
const coreTheme = useKibanaTheme();
return (
<EuiErrorBoundary>
<React.Suspense fallback={<Fallback height={props.height} />}>
<LazyBaseEditor {...props} useDarkTheme={darkMode} />
<LazyBaseEditor {...props} useDarkTheme={coreTheme.darkMode} />
</React.Suspense>
</EuiErrorBoundary>
);
@ -54,11 +54,11 @@ export const CodeEditor: React.FunctionComponent<Props> = (props) => {
* Renders a Monaco code editor in the same style as other EUI form fields.
*/
export const CodeEditorField: React.FunctionComponent<Props> = (props) => {
const darkMode = useUiSetting<boolean>('theme:darkMode');
const coreTheme = useKibanaTheme();
return (
<EuiErrorBoundary>
<React.Suspense fallback={<Fallback height={props.height} />}>
<LazyCodeEditorField {...props} useDarkTheme={darkMode} />
<LazyCodeEditorField {...props} useDarkTheme={coreTheme.darkMode} />
</React.Suspense>
</EuiErrorBoundary>
);

View file

@ -86,7 +86,7 @@ export type { ToMountPointOptions } from './util';
/** @deprecated Use `RedirectAppLinks` from `@kbn/shared-ux-link-redirect-app` */
export { RedirectAppLinks } from './app_links';
export { wrapWithTheme, KibanaThemeProvider } from './theme';
export { wrapWithTheme, KibanaThemeProvider, useKibanaTheme } from './theme';
/** dummy plugin, we just want kibanaReact to have its own bundle */
export function plugin() {

View file

@ -19,6 +19,7 @@ jest.mock('../../../context', () => ({
},
application: { capabilities: { navLinks: { integrations: true } } },
uiSettings: { get: jest.fn() },
theme: { theme$: jest.fn() },
},
}),
}));

View file

@ -8,4 +8,5 @@
export { wrapWithTheme } from './wrap_with_theme';
export { KibanaThemeProvider } from './kibana_theme_provider';
export { useKibanaTheme } from './use_theme';
export type { EuiTheme } from './types';

View file

@ -0,0 +1,58 @@
/*
* 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 React, { FC, useEffect } from 'react';
import { mountWithIntl } from '@kbn/test-jest-helpers';
import type { CoreTheme } from '@kbn/core/public';
import { KibanaContextProvider } from '../context';
import { themeServiceMock } from '@kbn/core/public/mocks';
import { useKibanaTheme } from './use_theme';
import { of } from 'rxjs';
describe('useKibanaTheme', () => {
let resultTheme: CoreTheme | undefined;
beforeEach(() => {
resultTheme = undefined;
});
const InnerComponent: FC = () => {
const theme = useKibanaTheme();
useEffect(() => {
resultTheme = theme;
}, [theme]);
return <div>foo</div>;
};
it('retrieve CoreTheme when theme service is provided in context', async () => {
const expectedCoreTheme: CoreTheme = { darkMode: true };
const themeServiceStart = themeServiceMock.createStartContract();
themeServiceStart.theme$ = of({ darkMode: true });
mountWithIntl(
<KibanaContextProvider services={{ theme: themeServiceStart }}>
<InnerComponent />
</KibanaContextProvider>
);
expect(resultTheme).toEqual(expectedCoreTheme);
});
it('does not throw error when theme service is not provided, default theme applied', async () => {
const expectedCoreTheme: CoreTheme = { darkMode: false };
mountWithIntl(
<KibanaContextProvider>
<InnerComponent />
</KibanaContextProvider>
);
expect(resultTheme).toEqual(expectedCoreTheme);
});
});

View file

@ -0,0 +1,30 @@
/*
* 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 { CoreTheme } from '@kbn/core-theme-browser';
import useObservable from 'react-use/lib/useObservable';
import { of } from 'rxjs';
import { useKibana } from '../context/context';
export const useKibanaTheme = (): CoreTheme => {
const defaultTheme: CoreTheme = { darkMode: false };
const {
services: { theme },
} = useKibana();
let themeObservable;
if (!theme) {
themeObservable = of(defaultTheme);
} else {
themeObservable = theme.theme$;
}
return useObservable(themeObservable, defaultTheme);
};

View file

@ -11,7 +11,7 @@ import ReactDOM from 'react-dom';
import { Observable } from 'rxjs';
import { I18nProvider } from '@kbn/i18n-react';
import type { MountPoint, CoreTheme } from '@kbn/core/public';
import { KibanaThemeProvider } from '../theme';
import { KibanaThemeProvider } from '../theme/kibana_theme_provider';
export interface ToMountPointOptions {
theme$?: Observable<CoreTheme>;

View file

@ -12,6 +12,7 @@
"@kbn/test-jest-helpers",
"@kbn/i18n",
"@kbn/i18n-react",
"@kbn/core-theme-browser",
],
"exclude": [
"target/**/*",

View file

@ -32,6 +32,11 @@ exports[`SavedObjectEdition should render normally 1`] = `
"set": [MockFunction],
},
},
"theme": Object {
"theme$": Observable {
"_subscribe": [Function],
},
},
"uiSettings": Object {
"get": [MockFunction],
"get$": [MockFunction],

View file

@ -20,6 +20,7 @@ import {
uiSettingsServiceMock,
scopedHistoryMock,
docLinksServiceMock,
themeServiceMock,
} from '@kbn/core/public/mocks';
import type { SavedObjectWithMetadata } from '../../types';
@ -42,6 +43,7 @@ describe('SavedObjectEdition', () => {
let applications: ReturnType<typeof applicationServiceMock.createStartContract>;
let docLinks: ReturnType<typeof docLinksServiceMock.createStartContract>;
let settings: ReturnType<typeof settingsServiceMock.createStartContract>;
let theme: ReturnType<typeof themeServiceMock.createStartContract>;
const shallowRender = (overrides: Partial<SavedObjectEditionProps> = {}) => {
return shallowWithI18nProvider(
@ -62,6 +64,7 @@ describe('SavedObjectEdition', () => {
history = scopedHistoryMock.create();
docLinks = docLinksServiceMock.createStartContract();
applications = applicationServiceMock.createStartContract();
theme = themeServiceMock.createStartContract();
applications.capabilities = {
navLinks: {},
management: {},
@ -86,6 +89,7 @@ describe('SavedObjectEdition', () => {
uiSettings,
docLinks: docLinks.links,
settings,
theme,
};
bulkDeleteObjectsMock.mockResolvedValue([{}]);

View file

@ -19,6 +19,7 @@ import {
HttpSetup,
IUiSettingsClient,
DocLinksStart,
ThemeServiceStart,
} from '@kbn/core/public';
import type { SettingsStart } from '@kbn/core-ui-settings-browser';
import { Header, Inspect, NotFoundErrors } from './components';
@ -38,6 +39,7 @@ export interface SavedObjectEditionProps {
uiSettings: IUiSettingsClient;
docLinks: DocLinksStart['links'];
settings: SettingsStart;
theme: ThemeServiceStart;
}
export interface SavedObjectEditionState {
type: string;
@ -94,12 +96,12 @@ export class SavedObjectEdition extends Component<
}
render() {
const { capabilities, notFoundType, http, uiSettings, docLinks, settings } = this.props;
const { capabilities, notFoundType, http, uiSettings, docLinks, settings, theme } = this.props;
const { object } = this.state;
const { delete: canDelete } = capabilities.savedObjectsManagement as Record<string, boolean>;
const canView = this.canViewInApp(capabilities, object);
return (
<KibanaContextProvider services={{ uiSettings, settings }}>
<KibanaContextProvider services={{ uiSettings, settings, theme }}>
<EuiFlexGroup
direction="column"
data-test-subject="savedObjectsEdit"

View file

@ -65,6 +65,7 @@ const SavedObjectsEditionPage = ({
history={history}
docLinks={docLinks}
settings={coreStart.settings}
theme={coreStart.theme}
/>
</RedirectAppLinks>
);

View file

@ -496,7 +496,11 @@ export const App = (props: {
return (
<KibanaContextProvider
services={{ uiSettings: props.core.uiSettings, settings: props.core.settings }}
services={{
uiSettings: props.core.uiSettings,
settings: props.core.settings,
theme: props.core.theme,
}}
>
<EuiPage>
<EuiPageBody style={{ maxWidth: 1200, margin: '0 auto' }}>

View file

@ -105,7 +105,7 @@ export const GenerateMap: Story<{}> = () => {
</Cytoscape>
{json && (
<CodeEditor
<CodeEditor // TODO Unable to find context that provides theme. Need CODEOWNER Input
languageId="json"
value={json}
height="200px"
@ -152,7 +152,7 @@ export const MapFromJSON: Story<{}> = () => {
<EuiForm isInvalid={error !== undefined} error={error}>
<EuiFlexGroup>
<EuiFlexItem>
<CodeEditor
<CodeEditor // TODO Unable to find context that provides theme. Need CODEOWNER Input
languageId="json"
value={json}
options={{ fontFamily: 'monospace' }}

View file

@ -18,6 +18,7 @@ import { of } from '@kbn/kibana-utils-plugin/common';
import { createPoint, rowClickData, TestEmbeddable } from './test/data';
import { ROW_CLICK_TRIGGER } from '@kbn/ui-actions-plugin/public';
import { settingsServiceMock } from '@kbn/core-ui-settings-browser-mocks';
import { themeServiceMock } from '@kbn/core-theme-browser-mocks';
const mockDataPoints = [
{
@ -85,6 +86,9 @@ const createDrilldown = (isExternalUrlValid: boolean = true) => {
getVariablesHelpDocsLink: () => 'http://localhost:5601/docs',
navigateToUrl: mockNavigateToUrl,
settings: settingsServiceMock.createSetupContract(),
theme: () => {
return themeServiceMock.createStartContract();
},
});
return drilldown;
};

View file

@ -6,7 +6,7 @@
*/
import React from 'react';
import { IExternalUrl } from '@kbn/core/public';
import { IExternalUrl, ThemeServiceStart } from '@kbn/core/public';
import {
ChartActionContext,
CONTEXT_MENU_TRIGGER,
@ -52,6 +52,7 @@ interface UrlDrilldownDeps {
getSyntaxHelpDocsLink: () => string;
getVariablesHelpDocsLink: () => string;
settings: SettingsStart;
theme: () => ThemeServiceStart;
}
export type ActionContext = ChartActionContext<EmbeddableWithQueryInput>;
@ -124,6 +125,7 @@ export class UrlDrilldown implements Drilldown<Config, ActionContext, ActionFact
<KibanaContextProvider
services={{
settings: this.deps.settings,
theme: this.deps.theme(),
}}
>
<UrlDrilldownCollectConfig

View file

@ -49,6 +49,7 @@ export class UrlDrilldownPlugin
getVariablesHelpDocsLink: () =>
startServices().core.docLinks.links.dashboard.urlDrilldownVariables,
settings: core.settings,
theme: () => startServices().core.theme,
})
);

View file

@ -18,7 +18,8 @@
"@kbn/std",
"@kbn/image-embeddable-plugin",
"@kbn/core-ui-settings-browser-mocks",
"@kbn/core-ui-settings-browser"
"@kbn/core-ui-settings-browser",
"@kbn/core-theme-browser-mocks"
],
"exclude": [
"target/**/*",

View file

@ -7,6 +7,9 @@ exports[`Should render error when upload fails from elasticsearch request failur
"settings": Object {
"get": [MockFunction],
},
"theme": Object {
"theme$": [MockFunction],
},
"uiSettings": Object {
"get": [MockFunction],
},
@ -104,6 +107,9 @@ exports[`Should render error when upload fails from http request timeout 1`] = `
"settings": Object {
"get": [MockFunction],
},
"theme": Object {
"theme$": [MockFunction],
},
"uiSettings": Object {
"get": [MockFunction],
},
@ -201,6 +207,9 @@ exports[`Should render success 1`] = `
"settings": Object {
"get": [MockFunction],
},
"theme": Object {
"theme$": [MockFunction],
},
"uiSettings": Object {
"get": [MockFunction],
},
@ -361,6 +370,9 @@ exports[`Should render warning when some features failed import 1`] = `
"settings": Object {
"get": [MockFunction],
},
"theme": Object {
"theme$": [MockFunction],
},
"uiSettings": Object {
"get": [MockFunction],
},

View file

@ -38,6 +38,11 @@ jest.mock('../kibana_services', () => ({
get: jest.fn(),
};
},
getTheme: () => {
return {
theme$: jest.fn(),
};
},
}));
test('Should render success', () => {

View file

@ -20,13 +20,14 @@ import {
EuiTitle,
} from '@elastic/eui';
import { CodeEditor, KibanaContextProvider } from '@kbn/kibana-react-plugin/public';
import { getDocLinks, getHttp, getUiSettings, getSettings } from '../kibana_services';
import { getDocLinks, getHttp, getUiSettings, getSettings, getTheme } from '../kibana_services';
import { ImportResults } from '../importer';
import { getPartialImportMessage } from './utils';
const services = {
uiSettings: getUiSettings(),
settings: getSettings(),
theme: getTheme(),
};
interface Props {

View file

@ -21,3 +21,4 @@ export const getHttp = () => coreStart.http;
export const getSavedObjectsClient = () => coreStart.savedObjects.client;
export const getUiSettings = () => coreStart.settings.client;
export const getSettings = () => coreStart.settings;
export const getTheme = () => coreStart.theme;

View file

@ -258,7 +258,7 @@ export const FleetAppContext: React.FC<{
return (
<RedirectAppLinks application={startServices.application}>
<startServices.i18n.Context>
<KibanaContextProvider services={{ ...startServices }}>
<KibanaContextProvider services={{ ...startServices, theme: { theme$ } }}>
<EuiErrorBoundary>
<ConfigContext.Provider value={config}>
<KibanaVersionContext.Provider value={kibanaVersion}>

View file

@ -14,6 +14,7 @@ import {
notificationServiceMock,
docLinksServiceMock,
uiSettingsServiceMock,
themeServiceMock,
executionContextServiceMock,
} from '@kbn/core/public/mocks';
import { GlobalFlyout } from '@kbn/es-ui-shared-plugin/public';
@ -62,6 +63,7 @@ export const kibanaVersion = new SemVer(MAJOR_VERSION);
const { Provider: KibanaReactContextProvider } = createKibanaReactContext({
uiSettings: uiSettingsServiceMock.createSetupContract(),
settings: settingsServiceMock.createStartContract(),
theme: themeServiceMock.createStartContract(),
kibanaVersion: {
get: () => kibanaVersion,
},

View file

@ -35,12 +35,12 @@ export const renderApp = (
return () => undefined;
}
const { i18n, docLinks, notifications, application, executionContext, overlays } = core;
const { i18n, docLinks, notifications, application, executionContext, overlays, theme } = core;
const { Context: I18nContext } = i18n;
const { services, history, setBreadcrumbs, uiSettings, settings, kibanaVersion, theme$ } =
dependencies;
// uiSettings is required by the CodeEditor component used to edit runtime field Painless scripts.
// theme is required by the CodeEditor component used to edit runtime field Painless scripts.
const { Provider: KibanaReactContextProvider } =
createKibanaReactContext<KibanaReactContextServices>({
application,
@ -49,6 +49,7 @@ export const renderApp = (
kibanaVersion: {
get: () => kibanaVersion,
},
theme,
});
const componentTemplateProviderValues = {
@ -96,6 +97,7 @@ interface KibanaReactContextServices {
kibanaVersion: {
get: () => SemVer;
};
theme: CoreStart['theme'];
}
// We override useKibana() from the react plugin to return a typed version for this app

View file

@ -67,7 +67,7 @@ export const renderApp = (
>
<I18nContext>
<KibanaThemeProvider theme$={theme$}>
<KibanaContextProvider services={services}>
<KibanaContextProvider services={{ ...services, theme: { theme$ } }}>
<App />
</KibanaContextProvider>
</KibanaThemeProvider>

View file

@ -20,8 +20,6 @@ interface MlJobEditorProps {
width?: string;
mode?: typeof ML_EDITOR_MODE[keyof typeof ML_EDITOR_MODE];
readOnly?: boolean;
syntaxChecking?: boolean;
theme?: string;
onChange?: EuiCodeEditorProps['onChange'];
'data-test-subj'?: string;
schema?: object;
@ -32,8 +30,6 @@ export const MLJobEditor: FC<MlJobEditorProps> = ({
width = '100%',
mode = ML_EDITOR_MODE.JSON,
readOnly = false,
syntaxChecking = true,
theme = 'textmate',
onChange = () => {},
'data-test-subj': dataTestSubj,
schema,
@ -50,10 +46,7 @@ export const MLJobEditor: FC<MlJobEditorProps> = ({
return (
<CodeEditor
languageId={mode}
options={{
readOnly,
theme,
}}
options={{ readOnly }}
value={value}
width={width}
height={height}

View file

@ -36,6 +36,7 @@ export function renderApp(
const { Provider: KibanaReactContextProvider } = createKibanaReactContext({
uiSettings,
settings,
theme: { theme$ },
});
render(
<I18nContext>

View file

@ -26,6 +26,7 @@ export const getRuntimeFieldEditorLoader =
const { Provider: KibanaReactContextProvider } = createKibanaReactContext({
uiSettings,
settings,
theme,
});
let overlayRef: OverlayRef | null = null;

View file

@ -65,6 +65,7 @@ const appDependencies = {
const kibanaContextDependencies = {
uiSettings: core.uiSettings,
settings: core.settings,
theme: core.theme,
};
export const setupEnvironment = () => {

View file

@ -31,6 +31,7 @@ export const renderApp = (elem: Element, dependencies: AppDependencies) => {
services={{
uiSettings: dependencies.services.uiSettings,
settings: dependencies.services.settings,
theme: dependencies.core.theme,
}}
>
<AppProviders appDependencies={dependencies}>

View file

@ -91,7 +91,7 @@ export const App = ({ deps }: { deps: TriggersAndActionsUiServices }) => {
<I18nProvider>
<EuiThemeProvider darkMode={isDarkMode}>
<KibanaThemeProvider theme$={theme$}>
<KibanaContextProvider services={{ ...deps }}>
<KibanaContextProvider services={{ ...deps, theme: { theme$ } }}>
<Router history={deps.history}>
<QueryClientProvider client={queryClient}>
<AppWithoutRouter sectionsRegex={sectionsRegex} />

View file

@ -29,7 +29,11 @@ export const renderApp = (bootDeps: BootDeps) => {
render(
<I18nContext>
<KibanaContextProvider
services={{ uiSettings: bootDeps.uiSettings, settings: bootDeps.settings }}
services={{
uiSettings: bootDeps.uiSettings,
settings: bootDeps.settings,
theme: bootDeps.theme,
}}
>
<KibanaThemeProvider theme$={theme$}>
<App {...appDeps} />