[8.9] Changing where CodeEditor fields get useDarkMode value (#159638) (#161707)

# Backport

This will backport the following commits from `main` to `8.9`:
- [Changing where CodeEditor fields get useDarkMode value
(#159638)](https://github.com/elastic/kibana/pull/159638)

<!--- Backport version: 8.9.7 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT
[{"author":{"name":"Kurt","email":"kc13greiner@users.noreply.github.com"},"sourceCommit":{"committedDate":"2023-07-11T18:02:42Z","message":"Changing
where CodeEditor fields get useDarkMode value (#159638)\n\n##
Summary\r\n\r\nCloses
https://github.com/elastic/kibana/issues/157203\r\nCloses
https://github.com/elastic/kibana/issues/158051\r\nCloses
https://github.com/elastic/kibana/issues/158049\r\n\r\nWith the addition
of Per User Dark Mode, components can no longer rely\r\non `uiSettings`
as the source of truth for theme.\r\n\r\nCodeEditor fields used to call
`uiSettings` to determine if Dark Mode\r\nwas enabled, which had been
provided in each callers Kibana React\r\nContext.\r\n\r\nThe new source
of truth for theme is the `CoreStart
>\r\nThemeServiceStart`.\r\n\r\nCurrently, not all callers of CodeEditor
provide the `theme` service in\r\ntheir Kibana Context in a similar way
and some callers don't provide it\r\nat all.\r\n\r\nThis PR updates
CodeEditor to get theme values from the Kibana Context\r\nusing a new
`useKibanaTheme` react hook.\r\n\r\nIt also attempts audit the callers
of CodeEditor to see if their Kibana\r\nContext contains the theme
service at the top level (Where I could add\r\ntheme to a caller's
Context without major changes, I did. Some cases\r\nwill require
CodeOwner guidance).\r\n\r\nThe new `useKibanaTheme` react hook will
throw a TypeError if theme\r\nisn't found in the top level of the Kibana
Context, this will help with\r\ntesting as the component will not
render. I will remove this after\r\ntesting so as not to introduce
breaking changes.\r\n\r\n## Testing\r\n\r\nPlease review files for which
you are CODEOWNER.\r\n\r\nI've attempted to tag all usages of
`CodeEditor`/`CodeEditorFIeld` with\r\na TODO comment with one of the
following scenarios:\r\n\r\n1) a note where theme was provided
already/where I made changes to\r\nprovide it in the appropriate
context\r\n2) I've asked for CODEOWNER guidance\r\n\r\nFor scenario 1,
please pull and test that CodeEditor locally:\r\n\r\n1. Enable Dark Mode
from Edit User Profiles by clicking on the Profile\r\nIcon on the top
right, and updating your profile.\r\n2. Navigate to the CodeEditors in
the plugins you own. If they render\r\nand display in Dark Mode - add a
green check to the table below - and\r\nyou're done!\r\n3. If it is not
rendering, please help me figure out where the theme\r\nservice should
be provided in the context.\r\n\r\nFor scenario 2, we will need to
figure out where to make changes so your\r\ncontext is providing theme.
Some of the more complex usages may need to\r\naddressed in separate
issues.\r\n\r\n\r\n## Tracking\r\n| Team | Plugin | Theme in Context ? |
Verified Working |\r\n| - | - | - | - |\r\n| apm-ui | apm | APM
Storybook broken | ? |\r\n| kibana-presentation | presentation_util |
Yes. | Yes |\r\n| response-ops | trigger_actions_ui | Yes | Yes |\r\n|
response-ops | stack_alerts | Yes | Yes |\r\n| kibana-security |
security | Yes | Yes |\r\n| security-defend-workflows | osquery | Yes |
Yes |\r\n| kibana-app-services | examples/expression_explorer | Yes |
Yes |\r\n| ml-ui | transform | Yes | Yes |\r\n| ml-ui | ml | Yes | Yes
|\r\n| uptime | synthetics | Yes | Yes |\r\n| kibana-gis | maps | Yes |
Yes |\r\n| kibana-gis | file_upload | Yes | Yes |\r\n|
platform-deployment-management | watcher | Yes | [AG] Yes |\r\n|
platform-deployment-management | snapshot_restore | Yes | [AG] Yes
|\r\n| platform-deployment-management | runtime_fields | Yes | [AG] Yes
|\r\n| platform-deployment-management | painless_lab | Yes | [AG] Yes
|\r\n| platform-deployment-management | ingest_pipelines | Yes | [AG]
Yes |\r\n| platform-deployment-management | index_management | Yes |
[AG] Yes |\r\n| platform-deployment-management | grokdebugger | Yes |
[AG] Yes |\r\n| platform-deployment-management | es_ui_shared | Yes |
[AG] Yes |\r\n| fleet | fleet | Yes | Yes |\r\n|
enterprise-search-frontend | enterprise_search | Yes | [AG] Yes |\r\n|
kibana-cloud-security-posture | cloud-security-posture | Yes | yes
|\r\n| sec-cloudnative-integrations | cloud_defend | Yes | Yes |\r\n|
kibana-visualizations/kibana-data-discovery | data | Yes | Yes |\r\n|
kibana-visualizations | examples/testing_embedded_lens | Yes | Yes
|\r\n| kibana-visualizations | vis_types | Yes | Yes |\r\n|
kibana-visualizations | vis_default_editor | Yes | Yes |\r\n|
kibana-visualizations | unified_search | Yes | Yes |\r\n|
kibana-visualizations | packages/kbn-text-based-editor | Yes | Yes
|\r\n| kibana-visualizatons | lens | Yes | Yes|\r\n| kibana-core |
saved_objects_management | Yes | Yes |\r\n| kibana-presentation |
inspector | Yes | Yes |\r\n| kibana-presentation | canvas | Yes | Yes
|\r\n| kibana-data-discovery | discover | Yes | Yes |\r\n|
kibana-data-discovery | data_view_management | Yes | Yes |\r\n|
kibana-data-discovery | data_view_field_editor | Yes | Yes |\r\n|
appex-sharedux | advanced_settings | Yes | Yes |\r\n|
enterprise-search-frontend | serverless_search | Yes | [AG] Yes |\r\n| -
| - | - | - |\r\n\r\n## Unit tests\r\n\r\nCurrently, many tests are
failing since they are probably not providing\r\n`theme` in the context.
Once CODEOWNERs have weighed in on CodeEditors\r\nusages that require
discussion, I will update the accompanying tests.\r\n\r\n## Release
note\r\n- Fixes theming of
CodeEditors\r\n\r\n---------\r\n\r\nCo-authored-by: kibanamachine
<42973632+kibanamachine@users.noreply.github.com>\r\nCo-authored-by:
Alison Goryachev <alisonmllr20@gmail.com>\r\nCo-authored-by: Dima
Arnautov <dmitrii.arnautov@elastic.co>\r\nCo-authored-by: Dima Arnautov
<arnautov.dima@gmail.com>","sha":"323b0477e35cb3e49cc01b56b23969fb72c7111e","branchLabelMapping":{"^v8.10.0$":"main","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:fix","Team:APM","Team:Security","Team:uptime","Feature:Drilldowns","Team:Fleet","Feature:Security/User
Profile","backport:prev-minor","v8.10.0"],"number":159638,"url":"https://github.com/elastic/kibana/pull/159638","mergeCommit":{"message":"Changing
where CodeEditor fields get useDarkMode value (#159638)\n\n##
Summary\r\n\r\nCloses
https://github.com/elastic/kibana/issues/157203\r\nCloses
https://github.com/elastic/kibana/issues/158051\r\nCloses
https://github.com/elastic/kibana/issues/158049\r\n\r\nWith the addition
of Per User Dark Mode, components can no longer rely\r\non `uiSettings`
as the source of truth for theme.\r\n\r\nCodeEditor fields used to call
`uiSettings` to determine if Dark Mode\r\nwas enabled, which had been
provided in each callers Kibana React\r\nContext.\r\n\r\nThe new source
of truth for theme is the `CoreStart
>\r\nThemeServiceStart`.\r\n\r\nCurrently, not all callers of CodeEditor
provide the `theme` service in\r\ntheir Kibana Context in a similar way
and some callers don't provide it\r\nat all.\r\n\r\nThis PR updates
CodeEditor to get theme values from the Kibana Context\r\nusing a new
`useKibanaTheme` react hook.\r\n\r\nIt also attempts audit the callers
of CodeEditor to see if their Kibana\r\nContext contains the theme
service at the top level (Where I could add\r\ntheme to a caller's
Context without major changes, I did. Some cases\r\nwill require
CodeOwner guidance).\r\n\r\nThe new `useKibanaTheme` react hook will
throw a TypeError if theme\r\nisn't found in the top level of the Kibana
Context, this will help with\r\ntesting as the component will not
render. I will remove this after\r\ntesting so as not to introduce
breaking changes.\r\n\r\n## Testing\r\n\r\nPlease review files for which
you are CODEOWNER.\r\n\r\nI've attempted to tag all usages of
`CodeEditor`/`CodeEditorFIeld` with\r\na TODO comment with one of the
following scenarios:\r\n\r\n1) a note where theme was provided
already/where I made changes to\r\nprovide it in the appropriate
context\r\n2) I've asked for CODEOWNER guidance\r\n\r\nFor scenario 1,
please pull and test that CodeEditor locally:\r\n\r\n1. Enable Dark Mode
from Edit User Profiles by clicking on the Profile\r\nIcon on the top
right, and updating your profile.\r\n2. Navigate to the CodeEditors in
the plugins you own. If they render\r\nand display in Dark Mode - add a
green check to the table below - and\r\nyou're done!\r\n3. If it is not
rendering, please help me figure out where the theme\r\nservice should
be provided in the context.\r\n\r\nFor scenario 2, we will need to
figure out where to make changes so your\r\ncontext is providing theme.
Some of the more complex usages may need to\r\naddressed in separate
issues.\r\n\r\n\r\n## Tracking\r\n| Team | Plugin | Theme in Context ? |
Verified Working |\r\n| - | - | - | - |\r\n| apm-ui | apm | APM
Storybook broken | ? |\r\n| kibana-presentation | presentation_util |
Yes. | Yes |\r\n| response-ops | trigger_actions_ui | Yes | Yes |\r\n|
response-ops | stack_alerts | Yes | Yes |\r\n| kibana-security |
security | Yes | Yes |\r\n| security-defend-workflows | osquery | Yes |
Yes |\r\n| kibana-app-services | examples/expression_explorer | Yes |
Yes |\r\n| ml-ui | transform | Yes | Yes |\r\n| ml-ui | ml | Yes | Yes
|\r\n| uptime | synthetics | Yes | Yes |\r\n| kibana-gis | maps | Yes |
Yes |\r\n| kibana-gis | file_upload | Yes | Yes |\r\n|
platform-deployment-management | watcher | Yes | [AG] Yes |\r\n|
platform-deployment-management | snapshot_restore | Yes | [AG] Yes
|\r\n| platform-deployment-management | runtime_fields | Yes | [AG] Yes
|\r\n| platform-deployment-management | painless_lab | Yes | [AG] Yes
|\r\n| platform-deployment-management | ingest_pipelines | Yes | [AG]
Yes |\r\n| platform-deployment-management | index_management | Yes |
[AG] Yes |\r\n| platform-deployment-management | grokdebugger | Yes |
[AG] Yes |\r\n| platform-deployment-management | es_ui_shared | Yes |
[AG] Yes |\r\n| fleet | fleet | Yes | Yes |\r\n|
enterprise-search-frontend | enterprise_search | Yes | [AG] Yes |\r\n|
kibana-cloud-security-posture | cloud-security-posture | Yes | yes
|\r\n| sec-cloudnative-integrations | cloud_defend | Yes | Yes |\r\n|
kibana-visualizations/kibana-data-discovery | data | Yes | Yes |\r\n|
kibana-visualizations | examples/testing_embedded_lens | Yes | Yes
|\r\n| kibana-visualizations | vis_types | Yes | Yes |\r\n|
kibana-visualizations | vis_default_editor | Yes | Yes |\r\n|
kibana-visualizations | unified_search | Yes | Yes |\r\n|
kibana-visualizations | packages/kbn-text-based-editor | Yes | Yes
|\r\n| kibana-visualizatons | lens | Yes | Yes|\r\n| kibana-core |
saved_objects_management | Yes | Yes |\r\n| kibana-presentation |
inspector | Yes | Yes |\r\n| kibana-presentation | canvas | Yes | Yes
|\r\n| kibana-data-discovery | discover | Yes | Yes |\r\n|
kibana-data-discovery | data_view_management | Yes | Yes |\r\n|
kibana-data-discovery | data_view_field_editor | Yes | Yes |\r\n|
appex-sharedux | advanced_settings | Yes | Yes |\r\n|
enterprise-search-frontend | serverless_search | Yes | [AG] Yes |\r\n| -
| - | - | - |\r\n\r\n## Unit tests\r\n\r\nCurrently, many tests are
failing since they are probably not providing\r\n`theme` in the context.
Once CODEOWNERs have weighed in on CodeEditors\r\nusages that require
discussion, I will update the accompanying tests.\r\n\r\n## Release
note\r\n- Fixes theming of
CodeEditors\r\n\r\n---------\r\n\r\nCo-authored-by: kibanamachine
<42973632+kibanamachine@users.noreply.github.com>\r\nCo-authored-by:
Alison Goryachev <alisonmllr20@gmail.com>\r\nCo-authored-by: Dima
Arnautov <dmitrii.arnautov@elastic.co>\r\nCo-authored-by: Dima Arnautov
<arnautov.dima@gmail.com>","sha":"323b0477e35cb3e49cc01b56b23969fb72c7111e"}},"sourceBranch":"main","suggestedTargetBranches":[],"targetPullRequestStates":[{"branch":"main","label":"v8.10.0","labelRegex":"^v8.10.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/159638","number":159638,"mergeCommit":{"message":"Changing
where CodeEditor fields get useDarkMode value (#159638)\n\n##
Summary\r\n\r\nCloses
https://github.com/elastic/kibana/issues/157203\r\nCloses
https://github.com/elastic/kibana/issues/158051\r\nCloses
https://github.com/elastic/kibana/issues/158049\r\n\r\nWith the addition
of Per User Dark Mode, components can no longer rely\r\non `uiSettings`
as the source of truth for theme.\r\n\r\nCodeEditor fields used to call
`uiSettings` to determine if Dark Mode\r\nwas enabled, which had been
provided in each callers Kibana React\r\nContext.\r\n\r\nThe new source
of truth for theme is the `CoreStart
>\r\nThemeServiceStart`.\r\n\r\nCurrently, not all callers of CodeEditor
provide the `theme` service in\r\ntheir Kibana Context in a similar way
and some callers don't provide it\r\nat all.\r\n\r\nThis PR updates
CodeEditor to get theme values from the Kibana Context\r\nusing a new
`useKibanaTheme` react hook.\r\n\r\nIt also attempts audit the callers
of CodeEditor to see if their Kibana\r\nContext contains the theme
service at the top level (Where I could add\r\ntheme to a caller's
Context without major changes, I did. Some cases\r\nwill require
CodeOwner guidance).\r\n\r\nThe new `useKibanaTheme` react hook will
throw a TypeError if theme\r\nisn't found in the top level of the Kibana
Context, this will help with\r\ntesting as the component will not
render. I will remove this after\r\ntesting so as not to introduce
breaking changes.\r\n\r\n## Testing\r\n\r\nPlease review files for which
you are CODEOWNER.\r\n\r\nI've attempted to tag all usages of
`CodeEditor`/`CodeEditorFIeld` with\r\na TODO comment with one of the
following scenarios:\r\n\r\n1) a note where theme was provided
already/where I made changes to\r\nprovide it in the appropriate
context\r\n2) I've asked for CODEOWNER guidance\r\n\r\nFor scenario 1,
please pull and test that CodeEditor locally:\r\n\r\n1. Enable Dark Mode
from Edit User Profiles by clicking on the Profile\r\nIcon on the top
right, and updating your profile.\r\n2. Navigate to the CodeEditors in
the plugins you own. If they render\r\nand display in Dark Mode - add a
green check to the table below - and\r\nyou're done!\r\n3. If it is not
rendering, please help me figure out where the theme\r\nservice should
be provided in the context.\r\n\r\nFor scenario 2, we will need to
figure out where to make changes so your\r\ncontext is providing theme.
Some of the more complex usages may need to\r\naddressed in separate
issues.\r\n\r\n\r\n## Tracking\r\n| Team | Plugin | Theme in Context ? |
Verified Working |\r\n| - | - | - | - |\r\n| apm-ui | apm | APM
Storybook broken | ? |\r\n| kibana-presentation | presentation_util |
Yes. | Yes |\r\n| response-ops | trigger_actions_ui | Yes | Yes |\r\n|
response-ops | stack_alerts | Yes | Yes |\r\n| kibana-security |
security | Yes | Yes |\r\n| security-defend-workflows | osquery | Yes |
Yes |\r\n| kibana-app-services | examples/expression_explorer | Yes |
Yes |\r\n| ml-ui | transform | Yes | Yes |\r\n| ml-ui | ml | Yes | Yes
|\r\n| uptime | synthetics | Yes | Yes |\r\n| kibana-gis | maps | Yes |
Yes |\r\n| kibana-gis | file_upload | Yes | Yes |\r\n|
platform-deployment-management | watcher | Yes | [AG] Yes |\r\n|
platform-deployment-management | snapshot_restore | Yes | [AG] Yes
|\r\n| platform-deployment-management | runtime_fields | Yes | [AG] Yes
|\r\n| platform-deployment-management | painless_lab | Yes | [AG] Yes
|\r\n| platform-deployment-management | ingest_pipelines | Yes | [AG]
Yes |\r\n| platform-deployment-management | index_management | Yes |
[AG] Yes |\r\n| platform-deployment-management | grokdebugger | Yes |
[AG] Yes |\r\n| platform-deployment-management | es_ui_shared | Yes |
[AG] Yes |\r\n| fleet | fleet | Yes | Yes |\r\n|
enterprise-search-frontend | enterprise_search | Yes | [AG] Yes |\r\n|
kibana-cloud-security-posture | cloud-security-posture | Yes | yes
|\r\n| sec-cloudnative-integrations | cloud_defend | Yes | Yes |\r\n|
kibana-visualizations/kibana-data-discovery | data | Yes | Yes |\r\n|
kibana-visualizations | examples/testing_embedded_lens | Yes | Yes
|\r\n| kibana-visualizations | vis_types | Yes | Yes |\r\n|
kibana-visualizations | vis_default_editor | Yes | Yes |\r\n|
kibana-visualizations | unified_search | Yes | Yes |\r\n|
kibana-visualizations | packages/kbn-text-based-editor | Yes | Yes
|\r\n| kibana-visualizatons | lens | Yes | Yes|\r\n| kibana-core |
saved_objects_management | Yes | Yes |\r\n| kibana-presentation |
inspector | Yes | Yes |\r\n| kibana-presentation | canvas | Yes | Yes
|\r\n| kibana-data-discovery | discover | Yes | Yes |\r\n|
kibana-data-discovery | data_view_management | Yes | Yes |\r\n|
kibana-data-discovery | data_view_field_editor | Yes | Yes |\r\n|
appex-sharedux | advanced_settings | Yes | Yes |\r\n|
enterprise-search-frontend | serverless_search | Yes | [AG] Yes |\r\n| -
| - | - | - |\r\n\r\n## Unit tests\r\n\r\nCurrently, many tests are
failing since they are probably not providing\r\n`theme` in the context.
Once CODEOWNERs have weighed in on CodeEditors\r\nusages that require
discussion, I will update the accompanying tests.\r\n\r\n## Release
note\r\n- Fixes theming of
CodeEditors\r\n\r\n---------\r\n\r\nCo-authored-by: kibanamachine
<42973632+kibanamachine@users.noreply.github.com>\r\nCo-authored-by:
Alison Goryachev <alisonmllr20@gmail.com>\r\nCo-authored-by: Dima
Arnautov <dmitrii.arnautov@elastic.co>\r\nCo-authored-by: Dima Arnautov
<arnautov.dima@gmail.com>","sha":"323b0477e35cb3e49cc01b56b23969fb72c7111e"}}]}]
BACKPORT-->

Co-authored-by: Kurt <kc13greiner@users.noreply.github.com>
This commit is contained in:
Kibana Machine 2023-07-11 17:14:37 -04:00 committed by GitHub
parent c1d0010b77
commit 6faee304fe
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

@ -88,7 +88,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

@ -493,7 +493,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,
}}
>
<EuiPageTemplate fullHeight template="empty">
<EuiFlexGroup

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} />