[Theme] fix theme$ usage when used with useObservable (#220141)

## Summary

When using `theme$` with
[`useObservable`](ad33f76dff/src/useObservable.ts (L10-L21))
passing custom default `theme`, the resulting `theme` can be wrong.

Take for example this code...

```ts
import useObservable from 'react-use/lib/useObservable';

const theme = useObservable<CoreTheme>(kibanaTheme.theme$, {
  darkMode: false,
  name: 'amsterdam',
});
```

In such case `kibanaTheme.theme$` has the correct value but the
`useObservable` returns the default/initial value immediately, so the
default is always applied then updated, requiring 2 renderings just to
update to the correct theme.

The simplest approach to fix this is just to pass the
`kibanaTheme.getTheme()` as the default when using with `useObservable`.

```ts
const theme = useObservable<CoreTheme>(kibanaTheme.theme$, kibanaTheme.getTheme());
```

---

Ideally, in the future we have a commonly shared way to access the theme
in react via a `useKibanaTheme` hook or a better/more consistent API for
`useEuiTheme`.

### Checklist

- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
- [x] The PR description includes the appropriate Release Notes section,
and the correct `release_note:*` label is applied per the
[guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: Edgar Santos <edgar.santos@elastic.co>
This commit is contained in:
Nick Partridge 2025-05-22 11:54:15 -07:00 committed by GitHub
parent fdf3ef7ffc
commit 12b7429afe
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
81 changed files with 212 additions and 376 deletions

View file

@ -25,7 +25,6 @@ const buildTableContext = (dataView: DataView, rows: DataTableRecord[]): DataTab
getRowByIndex: jest.fn((index) => rows[index]), getRowByIndex: jest.fn((index) => rows[index]),
onFilter: jest.fn(), onFilter: jest.fn(),
dataView, dataView,
isDarkMode: false,
selectedDocsState: buildSelectedDocsState([]), selectedDocsState: buildSelectedDocsState([]),
pageIndex: 0, pageIndex: 0,
pageSize: 10, pageSize: 10,

View file

@ -10,8 +10,6 @@
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import classnames from 'classnames'; import classnames from 'classnames';
import { FormattedMessage } from '@kbn/i18n-react'; import { FormattedMessage } from '@kbn/i18n-react';
import { of } from 'rxjs';
import useObservable from 'react-use/lib/useObservable';
import './data_table.scss'; import './data_table.scss';
import type { Storage } from '@kbn/kibana-utils-plugin/public'; import type { Storage } from '@kbn/kibana-utils-plugin/public';
import { import {
@ -99,7 +97,6 @@ import {
import { useSorting } from '../hooks/use_sorting'; import { useSorting } from '../hooks/use_sorting';
const CONTROL_COLUMN_IDS_DEFAULT = [SELECT_ROW, OPEN_DETAILS]; const CONTROL_COLUMN_IDS_DEFAULT = [SELECT_ROW, OPEN_DETAILS];
const THEME_DEFAULT = { darkMode: false };
const VIRTUALIZATION_OPTIONS: EuiDataGridProps['virtualizationOptions'] = { const VIRTUALIZATION_OPTIONS: EuiDataGridProps['virtualizationOptions'] = {
// Allowing some additional rows to be rendered outside // Allowing some additional rows to be rendered outside
// the view minimizes pop-in when scrolling quickly // the view minimizes pop-in when scrolling quickly
@ -520,7 +517,6 @@ export const UnifiedDataTable = ({
}: UnifiedDataTableProps) => { }: UnifiedDataTableProps) => {
const { fieldFormats, toastNotifications, dataViewFieldEditor, uiSettings, storage, data } = const { fieldFormats, toastNotifications, dataViewFieldEditor, uiSettings, storage, data } =
services; services;
const { darkMode } = useObservable(services.theme?.theme$ ?? of(THEME_DEFAULT), THEME_DEFAULT);
const dataGridRef = useRef<EuiDataGridRefProps>(null); const dataGridRef = useRef<EuiDataGridRefProps>(null);
const [isFilterActive, setIsFilterActive] = useState(false); const [isFilterActive, setIsFilterActive] = useState(false);
const [isCompareActive, setIsCompareActive] = useState(false); const [isCompareActive, setIsCompareActive] = useState(false);
@ -686,7 +682,6 @@ export const UnifiedDataTable = ({
getRowByIndex: (index: number) => displayedRows[index], getRowByIndex: (index: number) => displayedRows[index],
onFilter, onFilter,
dataView, dataView,
isDarkMode: darkMode,
selectedDocsState, selectedDocsState,
valueToStringConverter, valueToStringConverter,
componentsTourSteps, componentsTourSteps,
@ -696,7 +691,6 @@ export const UnifiedDataTable = ({
}), }),
[ [
componentsTourSteps, componentsTourSteps,
darkMode,
dataView, dataView,
isPlainRecord, isPlainRecord,
isPaginationEnabled, isPaginationEnabled,

View file

@ -20,7 +20,6 @@ export interface DataTableContext {
getRowByIndex: (index: number) => DataTableRecord | undefined; getRowByIndex: (index: number) => DataTableRecord | undefined;
onFilter?: DocViewFilterFn; onFilter?: DocViewFilterFn;
dataView: DataView; dataView: DataView;
isDarkMode: boolean;
selectedDocsState: UseSelectedDocsState; selectedDocsState: UseSelectedDocsState;
valueToStringConverter: ValueToStringConverter; valueToStringConverter: ValueToStringConverter;
componentsTourSteps?: Record<string, string>; componentsTourSteps?: Record<string, string>;

View file

@ -8,7 +8,6 @@
*/ */
import React from 'react'; import React from 'react';
import { of } from 'rxjs';
import { shallow } from 'enzyme'; import { shallow } from 'enzyme';
import { findTestSubject } from '@elastic/eui/lib/test'; import { findTestSubject } from '@elastic/eui/lib/test';
import { mountWithIntl } from '@kbn/test-jest-helpers'; import { mountWithIntl } from '@kbn/test-jest-helpers';
@ -51,9 +50,6 @@ const mockServices = {
fieldFormats: { fieldFormats: {
getDefaultInstance: jest.fn(() => ({ convert: (value: unknown) => (value ? value : '-') })), getDefaultInstance: jest.fn(() => ({ convert: (value: unknown) => (value ? value : '-') })),
}, },
theme: {
theme$: of({ darkMode: false }),
},
}; };
const rowsSource: EsHitRecord[] = [ const rowsSource: EsHitRecord[] = [

View file

@ -7,4 +7,4 @@
* License v3.0 only", or the "Server Side Public License, v 1". * License v3.0 only", or the "Server Side Public License, v 1".
*/ */
export { useDarkMode } from './use_dark_mode'; export { useKibanaIsDarkMode } from './use_kibana_is_dark_mode';

View file

@ -0,0 +1,22 @@
/*
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import { useEuiTheme, COLOR_MODES_STANDARD } from '@elastic/eui';
/**
* A **temporary** hook to simplify getting `isDarkMode` from eui context
*
* TODO: Replace with hook directly from eui
* See https://github.com/elastic/eui/issues/8693
*/
export const useKibanaIsDarkMode = (): boolean => {
const { colorMode } = useEuiTheme();
return colorMode === COLOR_MODES_STANDARD.dark;
};

View file

@ -9,6 +9,7 @@
export { KibanaThemeProvider, type KibanaThemeProviderProps } from './theme_provider'; export { KibanaThemeProvider, type KibanaThemeProviderProps } from './theme_provider';
export { wrapWithTheme } from './with_theme'; export { wrapWithTheme } from './with_theme';
export { useKibanaIsDarkMode } from './hooks';
// Re-exporting from @kbn/react-kibana-context-common for convenience to consumers. // Re-exporting from @kbn/react-kibana-context-common for convenience to consumers.
export { defaultTheme, type KibanaTheme } from '@kbn/react-kibana-context-common'; export { defaultTheme, type KibanaTheme } from '@kbn/react-kibana-context-common';

View file

@ -10,7 +10,6 @@
import { snakeCase } from 'lodash'; import { snakeCase } from 'lodash';
import React, { FC, useState, useEffect, useMemo } from 'react'; import React, { FC, useState, useEffect, useMemo } from 'react';
import { css } from '@emotion/react'; import { css } from '@emotion/react';
import useObservable from 'react-use/lib/useObservable';
import { import {
EuiCard, EuiCard,
EuiFlexGroup, EuiFlexGroup,
@ -36,6 +35,7 @@ import {
RedirectAppLinksContainer as RedirectAppLinks, RedirectAppLinksContainer as RedirectAppLinks,
RedirectAppLinksKibanaProvider, RedirectAppLinksKibanaProvider,
} from '@kbn/shared-ux-link-redirect-app'; } from '@kbn/shared-ux-link-redirect-app';
import { useKibanaIsDarkMode } from '@kbn/react-kibana-context-theme';
import { FetchResult } from '@kbn/newsfeed-plugin/public'; import { FetchResult } from '@kbn/newsfeed-plugin/public';
import { import {
FeatureCatalogueEntry, FeatureCatalogueEntry,
@ -66,20 +66,11 @@ export const Overview: FC<Props> = ({ newsFetchResult, solutions, features }) =>
const [hasDataView, setHasDataView] = useState(false); const [hasDataView, setHasDataView] = useState(false);
const [isLoading, setIsLoading] = useState(true); const [isLoading, setIsLoading] = useState(true);
const { services } = useKibana<CoreStart & AppPluginStartDependencies>(); const { services } = useKibana<CoreStart & AppPluginStartDependencies>();
const { const { http, docLinks, dataViews, share, application, chrome, dataViewEditor, customBranding } =
http, services;
docLinks,
dataViews,
share,
application,
chrome,
dataViewEditor,
customBranding,
theme,
} = services;
const addBasePath = http.basePath.prepend; const addBasePath = http.basePath.prepend;
const currentTheme = useObservable(theme.theme$, { darkMode: false, name: 'amsterdam' });
const { euiTheme } = useEuiTheme(); const { euiTheme } = useEuiTheme();
const isDarkMode = useKibanaIsDarkMode();
const minBreakpointM = useEuiMinBreakpoint('m'); const minBreakpointM = useEuiMinBreakpoint('m');
// Home does not have a locator implemented, so hard-code it here. // Home does not have a locator implemented, so hard-code it here.
@ -152,9 +143,7 @@ export const Overview: FC<Props> = ({ newsFetchResult, solutions, features }) =>
trackUiMetric(METRIC_TYPE.CLICK, `app_card_${appId}`); trackUiMetric(METRIC_TYPE.CLICK, `app_card_${appId}`);
}} }}
image={addBasePath( image={addBasePath(
`/plugins/${PLUGIN_ID}/assets/kibana_${appId}_${ `/plugins/${PLUGIN_ID}/assets/kibana_${appId}_${isDarkMode ? 'dark' : 'light'}.svg`
currentTheme.darkMode ? 'dark' : 'light'
}.svg`
)} )}
title={app.title} title={app.title}
titleElement="h3" titleElement="h3"

View file

@ -31,6 +31,7 @@
"@kbn/react-kibana-context-render", "@kbn/react-kibana-context-render",
"@kbn/core-application-browser-mocks", "@kbn/core-application-browser-mocks",
"@kbn/core-http-browser-mocks", "@kbn/core-http-browser-mocks",
"@kbn/react-kibana-context-theme",
], ],
"exclude": [ "exclude": [
"target/**/*" "target/**/*"

View file

@ -43,6 +43,7 @@ import { i18n } from '@kbn/i18n';
import { DatatableColumn } from '@kbn/expressions-plugin/public'; import { DatatableColumn } from '@kbn/expressions-plugin/public';
import { IconChartHeatmap } from '@kbn/chart-icons'; import { IconChartHeatmap } from '@kbn/chart-icons';
import { getOverridesFor } from '@kbn/chart-expressions-common'; import { getOverridesFor } from '@kbn/chart-expressions-common';
import { useKibanaIsDarkMode } from '@kbn/react-kibana-context-theme';
import type { HeatmapRenderProps, FilterEvent, BrushEvent } from '../../common'; import type { HeatmapRenderProps, FilterEvent, BrushEvent } from '../../common';
import { import {
applyPaletteParams, applyPaletteParams,
@ -156,7 +157,7 @@ export const HeatmapComponent: FC<HeatmapRenderProps> = memo(
overrides, overrides,
}) => { }) => {
const chartRef = useRef<Chart>(null); const chartRef = useRef<Chart>(null);
const isDarkTheme = chartsThemeService.useDarkMode(); const isDarkTheme = useKibanaIsDarkMode();
// legacy heatmap legend is handled by the uiState // legacy heatmap legend is handled by the uiState
const [showLegend, setShowLegend] = useState<boolean>(() => { const [showLegend, setShowLegend] = useState<boolean>(() => {
const bwcLegendStateDefault = args.legend.isVisible ?? true; const bwcLegendStateDefault = args.legend.isVisible ?? true;

View file

@ -31,6 +31,7 @@
"@kbn/react-kibana-context-render", "@kbn/react-kibana-context-render",
"@kbn/transpose-utils", "@kbn/transpose-utils",
"@kbn/ebt-tools", "@kbn/ebt-tools",
"@kbn/react-kibana-context-theme",
], ],
"exclude": [ "exclude": [
"target/**/*", "target/**/*",

View file

@ -43,6 +43,7 @@ import type { FieldFormat } from '@kbn/field-formats-plugin/common';
import { getOverridesFor } from '@kbn/chart-expressions-common'; import { getOverridesFor } from '@kbn/chart-expressions-common';
import { useKbnPalettes } from '@kbn/palettes'; import { useKbnPalettes } from '@kbn/palettes';
import { useAppFixedViewport } from '@kbn/core-rendering-browser'; import { useAppFixedViewport } from '@kbn/core-rendering-browser';
import { useKibanaIsDarkMode } from '@kbn/react-kibana-context-theme';
import { consolidateMetricColumns } from '../../common/utils'; import { consolidateMetricColumns } from '../../common/utils';
import { DEFAULT_PERCENT_DECIMALS } from '../../common/constants'; import { DEFAULT_PERCENT_DECIMALS } from '../../common/constants';
import { import {
@ -303,7 +304,7 @@ const PartitionVisComponent = (props: PartitionVisComponentProps) => {
}, },
}); });
const isDarkMode = props.chartsThemeService.useDarkMode(); const isDarkMode = useKibanaIsDarkMode();
const layers = useMemo( const layers = useMemo(
() => () =>
getLayers( getLayers(

View file

@ -33,6 +33,7 @@
"@kbn/core-rendering-browser", "@kbn/core-rendering-browser",
"@kbn/palettes", "@kbn/palettes",
"@kbn/ebt-tools", "@kbn/ebt-tools",
"@kbn/react-kibana-context-theme",
], ],
"exclude": [ "exclude": [
"target/**/*", "target/**/*",

View file

@ -27,6 +27,7 @@ import {
extractVisualizationType, extractVisualizationType,
} from '@kbn/chart-expressions-common'; } from '@kbn/chart-expressions-common';
import { useKibanaIsDarkMode } from '@kbn/react-kibana-context-theme';
import { ExpressionTagcloudRendererDependencies } from '../plugin'; import { ExpressionTagcloudRendererDependencies } from '../plugin';
import { TagcloudRendererConfig } from '../../common/types'; import { TagcloudRendererConfig } from '../../common/types';
import { EXPRESSION_NAME } from '../../common'; import { EXPRESSION_NAME } from '../../common';
@ -98,12 +99,6 @@ export const tagcloudRenderer: (
handlers.event(chartSizeEvent); handlers.event(chartSizeEvent);
const palettesRegistry = await plugins.charts.palettes.getPalettes(); const palettesRegistry = await plugins.charts.palettes.getPalettes();
let isDarkMode = false;
plugins.charts.theme.darkModeEnabled$
.subscribe((val) => {
isDarkMode = val.darkMode;
})
.unsubscribe();
performanceTracker.mark(PERFORMANCE_TRACKER_MARKS.RENDER_START); performanceTracker.mark(PERFORMANCE_TRACKER_MARKS.RENDER_START);
@ -125,7 +120,7 @@ export const tagcloudRenderer: (
fireEvent={handlers.event} fireEvent={handlers.event}
syncColors={config.syncColors} syncColors={config.syncColors}
overrides={config.overrides} overrides={config.overrides}
isDarkMode={isDarkMode} isDarkMode={useKibanaIsDarkMode()}
/> />
</VisualizationContainer> </VisualizationContainer>
)} )}

View file

@ -31,6 +31,7 @@
"@kbn/palettes", "@kbn/palettes",
"@kbn/charts-theme", "@kbn/charts-theme",
"@kbn/ebt-tools", "@kbn/ebt-tools",
"@kbn/react-kibana-context-theme",
], ],
"exclude": [ "exclude": [
"target/**/*", "target/**/*",

View file

@ -58,6 +58,7 @@ import {
import { PersistedState } from '@kbn/visualizations-plugin/public'; import { PersistedState } from '@kbn/visualizations-plugin/public';
import { getOverridesFor, ChartSizeSpec } from '@kbn/chart-expressions-common'; import { getOverridesFor, ChartSizeSpec } from '@kbn/chart-expressions-common';
import { useAppFixedViewport } from '@kbn/core-rendering-browser'; import { useAppFixedViewport } from '@kbn/core-rendering-browser';
import { useKibanaIsDarkMode } from '@kbn/react-kibana-context-theme';
import { AlertRuleFromVisUIActionData } from '@kbn/alerts-ui-shared'; import { AlertRuleFromVisUIActionData } from '@kbn/alerts-ui-shared';
import type { import type {
FilterEvent, FilterEvent,
@ -237,7 +238,7 @@ export function XYChart({
const chartRef = useRef<Chart>(null); const chartRef = useRef<Chart>(null);
const chartBaseTheme = chartsThemeService.useChartsBaseTheme(); const chartBaseTheme = chartsThemeService.useChartsBaseTheme();
const darkMode = chartsThemeService.useDarkMode(); const darkMode = useKibanaIsDarkMode();
const palettes = useKbnPalettes(); const palettes = useKbnPalettes();
const appFixedViewport = useAppFixedViewport(); const appFixedViewport = useAppFixedViewport();
const filteredLayers = getFilteredLayers(layers); const filteredLayers = getFilteredLayers(layers);

View file

@ -41,6 +41,7 @@
"@kbn/alerts-ui-shared", "@kbn/alerts-ui-shared",
"@kbn/ui-actions-browser", "@kbn/ui-actions-browser",
"@kbn/ebt-tools", "@kbn/ebt-tools",
"@kbn/react-kibana-context-theme",
], ],
"exclude": [ "exclude": [
"target/**/*", "target/**/*",

View file

@ -23,7 +23,11 @@ export class ThemeService {
/** An observable of the current charts base theme */ /** An observable of the current charts base theme */
public chartsBaseTheme$ = this._chartsBaseTheme$.asObservable(); public chartsBaseTheme$ = this._chartsBaseTheme$.asObservable();
/** An observable boolean for dark mode of kibana */ /**
* An observable boolean for dark mode of kibana
*
* @deprecated use `useKibanaIsDarkMode`
*/
public get darkModeEnabled$(): Observable<CoreTheme> { public get darkModeEnabled$(): Observable<CoreTheme> {
if (!this.theme$) { if (!this.theme$) {
throw new Error('ThemeService not initialized'); throw new Error('ThemeService not initialized');
@ -32,7 +36,11 @@ export class ThemeService {
return this.theme$; return this.theme$;
} }
/** A React hook for consuming the dark mode value */ /**
* A React hook for consuming the dark mode value
*
* @deprecated use `useKibanaIsDarkMode`
*/
public useDarkMode = (): boolean => { public useDarkMode = (): boolean => {
const [value, update] = useState(false); const [value, update] = useState(false);

View file

@ -8,7 +8,6 @@
*/ */
import React, { useMemo, useState } from 'react'; import React, { useMemo, useState } from 'react';
import useObservable from 'react-use/lib/useObservable';
import { i18n } from '@kbn/i18n'; import { i18n } from '@kbn/i18n';
import { import {
EuiButton, EuiButton,
@ -20,6 +19,7 @@ import {
EuiText, EuiText,
} from '@elastic/eui'; } from '@elastic/eui';
import { useStateFromPublishingSubject } from '@kbn/presentation-publishing'; import { useStateFromPublishingSubject } from '@kbn/presentation-publishing';
import { useKibanaIsDarkMode } from '@kbn/react-kibana-context-theme';
import useMountedState from 'react-use/lib/useMountedState'; import useMountedState from 'react-use/lib/useMountedState';
import { useDashboardApi } from '../../../dashboard_api/use_dashboard_api'; import { useDashboardApi } from '../../../dashboard_api/use_dashboard_api';
@ -36,7 +36,7 @@ export function DashboardEmptyScreen() {
const isMounted = useMountedState(); const isMounted = useMountedState();
const dashboardApi = useDashboardApi(); const dashboardApi = useDashboardApi();
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const isDarkTheme = useObservable(coreServices.theme.theme$)?.darkMode; const isDarkTheme = useKibanaIsDarkMode();
const viewMode = useStateFromPublishingSubject(dashboardApi.viewMode$); const viewMode = useStateFromPublishingSubject(dashboardApi.viewMode$);
const isEditMode = useMemo(() => { const isEditMode = useMemo(() => {
return viewMode === 'edit'; return viewMode === 'edit';

View file

@ -84,6 +84,7 @@
"@kbn/ui-actions-browser", "@kbn/ui-actions-browser",
"@kbn/esql-types", "@kbn/esql-types",
"@kbn/saved-objects-tagging-plugin", "@kbn/saved-objects-tagging-plugin",
"@kbn/react-kibana-context-theme",
"@kbn/std" "@kbn/std"
], ],
"exclude": ["target/**/*"] "exclude": ["target/**/*"]

View file

@ -8,17 +8,16 @@
*/ */
import React, { useState, useEffect, useCallback } from 'react'; import React, { useState, useEffect, useCallback } from 'react';
import useObservable from 'react-use/lib/useObservable';
import type { Observable } from 'rxjs';
import { useEuiTheme } from '@elastic/eui'; import { useEuiTheme } from '@elastic/eui';
import { i18n } from '@kbn/i18n'; import { i18n } from '@kbn/i18n';
import { ApplicationStart, CoreTheme, NotificationsStart } from '@kbn/core/public'; import { ApplicationStart, NotificationsStart } from '@kbn/core/public';
import type { GuideState, GuideStep as GuideStepStatus } from '@kbn/guided-onboarding'; import type { GuideState, GuideStep as GuideStepStatus } from '@kbn/guided-onboarding';
import type { GuideId, GuideConfig, StepConfig } from '@kbn/guided-onboarding'; import type { GuideId, GuideConfig, StepConfig } from '@kbn/guided-onboarding';
import { useKibanaIsDarkMode } from '@kbn/react-kibana-context-theme';
import type { GuidedOnboardingApi } from '../types'; import type { GuidedOnboardingApi } from '../types';
import type { PluginState } from '../../common'; import type { PluginState } from '../../common';
@ -33,7 +32,6 @@ interface GuidePanelProps {
api: GuidedOnboardingApi; api: GuidedOnboardingApi;
application: ApplicationStart; application: ApplicationStart;
notifications: NotificationsStart; notifications: NotificationsStart;
theme$: Observable<CoreTheme>;
} }
const getProgress = (state?: GuideState): number => { const getProgress = (state?: GuideState): number => {
@ -48,15 +46,16 @@ const getProgress = (state?: GuideState): number => {
return 0; return 0;
}; };
export const GuidePanel = ({ api, application, notifications, theme$ }: GuidePanelProps) => { export const GuidePanel = ({ api, application, notifications }: GuidePanelProps) => {
const euiThemeContext = useEuiTheme(); const euiThemeContext = useEuiTheme();
const euiTheme = euiThemeContext.euiTheme; const euiTheme = euiThemeContext.euiTheme;
const isDarkTheme = useKibanaIsDarkMode();
const [isGuideOpen, setIsGuideOpen] = useState(false); const [isGuideOpen, setIsGuideOpen] = useState(false);
const [isQuitGuideModalOpen, setIsQuitGuideModalOpen] = useState(false); const [isQuitGuideModalOpen, setIsQuitGuideModalOpen] = useState(false);
const [pluginState, setPluginState] = useState<PluginState | undefined>(undefined); const [pluginState, setPluginState] = useState<PluginState | undefined>(undefined);
const [guideConfig, setGuideConfig] = useState<GuideConfig | undefined>(undefined); const [guideConfig, setGuideConfig] = useState<GuideConfig | undefined>(undefined);
const [isLoading, setIsLoading] = useState<boolean>(false); const [isLoading, setIsLoading] = useState<boolean>(false);
const { darkMode: isDarkTheme } = useObservable(theme$, { darkMode: false, name: 'amsterdam' });
const styles = getGuidePanelStyles({ euiThemeContext, isDarkTheme }); const styles = getGuidePanelStyles({ euiThemeContext, isDarkTheme });

View file

@ -86,15 +86,9 @@ export class GuidedOnboardingPlugin
application: ApplicationStart; application: ApplicationStart;
notifications: NotificationsStart; notifications: NotificationsStart;
}) { }) {
const { theme } = startServices;
ReactDOM.render( ReactDOM.render(
<KibanaRenderContextProvider {...startServices}> <KibanaRenderContextProvider {...startServices}>
<GuidePanel <GuidePanel api={api} application={application} notifications={notifications} />
api={api}
application={application}
notifications={notifications}
theme$={theme.theme$}
/>
</KibanaRenderContextProvider>, </KibanaRenderContextProvider>,
targetDomElement targetDomElement
); );

View file

@ -17,6 +17,7 @@
"@kbn/config-schema", "@kbn/config-schema",
"@kbn/features-plugin", "@kbn/features-plugin",
"@kbn/react-kibana-context-render", "@kbn/react-kibana-context-render",
"@kbn/react-kibana-context-theme",
], ],
"exclude": [ "exclude": [
"target/**/*", "target/**/*",

View file

@ -17,7 +17,7 @@ import React from 'react';
import { EuiCard, EuiButton, EuiButtonEmpty, UseEuiTheme } from '@elastic/eui'; import { EuiCard, EuiButton, EuiButtonEmpty, UseEuiTheme } from '@elastic/eui';
import { css } from '@emotion/react'; import { css } from '@emotion/react';
import { FormattedMessage } from '@kbn/i18n-react'; import { FormattedMessage } from '@kbn/i18n-react';
import { getServices } from '../../kibana_services'; import { useKibanaIsDarkMode } from '@kbn/react-kibana-context-theme';
interface Props { interface Props {
urlBasePath: string; urlBasePath: string;
@ -26,8 +26,9 @@ interface Props {
} }
export function SampleDataCard({ urlBasePath, onDecline, onConfirm }: Props) { export function SampleDataCard({ urlBasePath, onDecline, onConfirm }: Props) {
const IS_DARK_THEME = getServices().theme.getTheme().darkMode; const isDarkMode = useKibanaIsDarkMode();
const cardGraphicFile = !IS_DARK_THEME
const cardGraphicFile = !isDarkMode
? 'illustration_integrations_lightmode.png' ? 'illustration_integrations_lightmode.png'
: 'illustration_integrations_darkmode.png'; : 'illustration_integrations_darkmode.png';
const cardGraphicURL = `${urlBasePath}/plugins/home/assets/common/${cardGraphicFile}`; const cardGraphicURL = `${urlBasePath}/plugins/home/assets/common/${cardGraphicFile}`;

View file

@ -36,6 +36,7 @@
"@kbn/react-kibana-context-render", "@kbn/react-kibana-context-render",
"@kbn/core-http-browser", "@kbn/core-http-browser",
"@kbn/deeplinks-observability", "@kbn/deeplinks-observability",
"@kbn/react-kibana-context-theme",
], ],
"exclude": [ "exclude": [
"target/**/*", "target/**/*",

View file

@ -1,82 +0,0 @@
/*
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { act } from 'react-dom/test-utils';
import { useDarkMode } from './use_dark_mode';
import { createKibanaReactContext } from '../context';
import { KibanaServices } from '../context/types';
import { BehaviorSubject } from 'rxjs';
import { CoreTheme } from '@kbn/core/public';
import { coreMock } from '@kbn/core/public/mocks';
describe('useDarkMode', () => {
let container: HTMLDivElement | null;
beforeEach(() => {
container = document.createElement('div');
document.body.appendChild(container);
});
afterEach(() => {
document.body.removeChild(container!);
container = null;
});
const TestConsumer: React.FC = () => {
const darkMode = useDarkMode();
return <div>{String(darkMode)}</div>;
};
const mock = (): [KibanaServices, BehaviorSubject<CoreTheme>] => {
const core = coreMock.createStart();
const subject = new BehaviorSubject<CoreTheme>({ darkMode: false, name: 'amsterdam' });
core.theme.theme$ = subject.asObservable();
return [core, subject];
};
test('returns the value from the theme', () => {
const [core] = mock();
const { Provider } = createKibanaReactContext(core);
ReactDOM.render(
<Provider>
<TestConsumer />
</Provider>,
container
);
const div = container!.querySelector('div');
expect(div!.textContent).toBe('false');
});
test('value changes if the theme changes', () => {
const [core, subject] = mock();
const { Provider } = createKibanaReactContext(core);
ReactDOM.render(
<Provider>
<TestConsumer />
</Provider>,
container
);
let div = container!.querySelector('div');
expect(div!.textContent).toBe('false');
act(() => {
subject.next({ darkMode: true, name: 'amsterdam' });
});
div = container!.querySelector('div');
expect(div!.textContent).toBe('true');
});
});

View file

@ -1,27 +0,0 @@
/*
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import useObservable from 'react-use/lib/useObservable';
import { useKibana } from '../context';
export const useDarkMode = (defaultValue?: boolean): boolean => {
const {
services: { theme },
} = useKibana();
if (!theme) {
if (defaultValue !== undefined) {
return defaultValue;
}
throw new TypeError('theme service not available in kibana-react context.');
}
const currentTheme = useObservable(theme.theme$, theme.getTheme());
return currentTheme.darkMode;
};

View file

@ -31,8 +31,6 @@ export {
useGlobalUiSetting$, useGlobalUiSetting$,
} from './ui_settings'; } from './ui_settings';
export { useDarkMode } from './dark_mode';
export { useExecutionContext } from './use_execution_context'; export { useExecutionContext } from './use_execution_context';
export { reactRouterNavigate, reactRouterOnClickHandler } from './react_router_navigate'; export { reactRouterNavigate, reactRouterOnClickHandler } from './react_router_navigate';

View file

@ -8,7 +8,7 @@
*/ */
import React from 'react'; import React from 'react';
import { Observable } from 'rxjs'; import { of, type Observable } from 'rxjs';
import { I18nProvider } from '@kbn/i18n-react'; import { I18nProvider } from '@kbn/i18n-react';
import type { MountPoint } from '@kbn/core/public'; import type { MountPoint } from '@kbn/core/public';
@ -24,7 +24,7 @@ import { toMountPoint as _toMountPoint } from '@kbn/react-kibana-mount';
// dark mode is applied correctly. This code is for compatibility purposes, // dark mode is applied correctly. This code is for compatibility purposes,
// and will be removed when the deprecated usages are removed. // and will be removed when the deprecated usages are removed.
const themeStart: ThemeServiceStart = { const themeStart: ThemeServiceStart = {
theme$: new Observable((subscriber) => subscriber.next(defaultTheme)), theme$: of(defaultTheme),
getTheme: () => defaultTheme, getTheme: () => defaultTheme,
}; };

View file

@ -14,7 +14,6 @@ import { Router, Routes, Route } from '@kbn/shared-ux-router';
import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render'; import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render';
import { FormattedMessage } from '@kbn/i18n-react'; import { FormattedMessage } from '@kbn/i18n-react';
import { i18n } from '@kbn/i18n'; import { i18n } from '@kbn/i18n';
import useObservable from 'react-use/lib/useObservable';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools'; import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
import { css } from '@emotion/css'; import { css } from '@emotion/css';
@ -23,11 +22,15 @@ import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public';
import { RedirectAppLinks } from '@kbn/shared-ux-link-redirect-app'; import { RedirectAppLinks } from '@kbn/shared-ux-link-redirect-app';
import { EuiThemeProvider } from '@kbn/kibana-react-plugin/common'; import { EuiThemeProvider } from '@kbn/kibana-react-plugin/common';
import { useKibanaIsDarkMode } from '@kbn/react-kibana-context-theme';
import type { FleetConfigType, FleetStartServices } from '../../plugin'; import type { FleetConfigType, FleetStartServices } from '../../plugin';
import { PackageInstallProvider } from '../integrations/hooks'; import { PackageInstallProvider } from '../integrations/hooks';
import { SpaceSettingsContextProvider } from '../../hooks/use_space_settings_context'; import { SpaceSettingsContextProvider } from '../../hooks/use_space_settings_context';
import { ErrorLayout, PermissionsError } from '../../layouts/error';
import { type FleetStatusProviderProps, useAuthz, useFleetStatus, useFlyoutContext } from './hooks'; import { type FleetStatusProviderProps, useAuthz, useFleetStatus, useFlyoutContext } from './hooks';
import { import {
@ -60,7 +63,6 @@ import { EnrollmentTokenListPage } from './sections/agents/enrollment_token_list
import { UninstallTokenListPage } from './sections/agents/uninstall_token_list_page'; import { UninstallTokenListPage } from './sections/agents/uninstall_token_list_page';
import { SettingsApp } from './sections/settings'; import { SettingsApp } from './sections/settings';
import { DebugPage } from './sections/debug'; import { DebugPage } from './sections/debug';
import { ErrorLayout, PermissionsError } from '../../layouts/error';
const FEEDBACK_URL = 'https://ela.st/fleet-feedback'; const FEEDBACK_URL = 'https://ela.st/fleet-feedback';
@ -199,8 +201,7 @@ export const FleetAppContext: React.FC<{
fleetStatus, fleetStatus,
}) => { }) => {
const XXL_BREAKPOINT = 1600; const XXL_BREAKPOINT = 1600;
const darkModeObservable = useObservable(startServices.theme.theme$); const isDarkMode = useKibanaIsDarkMode();
const isDarkMode = darkModeObservable && darkModeObservable.darkMode;
return ( return (
<KibanaRenderContextProvider <KibanaRenderContextProvider

View file

@ -28,7 +28,7 @@ import type { Pagination } from '../../../../hooks';
import { useAgentVersion } from '../../../../hooks'; import { useAgentVersion } from '../../../../hooks';
import { useLink, useAuthz } from '../../../../hooks'; import { useLink, useAuthz } from '../../../../hooks';
import { AgentPolicySummaryLine } from '../../../../components'; import { AgentPolicySummaryLine } from '../../../../../../components';
import { Tags } from '../../components/tags'; import { Tags } from '../../components/tags';
import type { AgentMetrics } from '../../../../../../../common/types'; import type { AgentMetrics } from '../../../../../../../common/types';
import { formatAgentCPU, formatAgentMemory } from '../../services/agent_metrics'; import { formatAgentCPU, formatAgentMemory } from '../../services/agent_metrics';

View file

@ -17,7 +17,7 @@ import { sendGetAgents, sendGetAgentStatus } from '../../../hooks';
import { AgentListPage } from '.'; import { AgentListPage } from '.';
jest.mock('../../../../integrations/hooks/use_confirm_force_install', () => ({ jest.mock('../../../../integrations/hooks/use_confirm_force_install', () => ({
useConfirmForceInstall: () => <>confirmForceInstall</>, useConfirmForceInstall: jest.fn(),
})); }));
jest.mock('./hooks/use_missing_encryption_key_callout', () => ({ jest.mock('./hooks/use_missing_encryption_key_callout', () => ({
@ -195,6 +195,8 @@ describe('agent_list_page', () => {
totalInactive: 0, totalInactive: 0,
}, },
}); });
jest.useFakeTimers({ legacyFakeTimers: true });
({ utils } = renderAgentList()); ({ utils } = renderAgentList());
await waitFor(() => { await waitFor(() => {
@ -212,6 +214,10 @@ describe('agent_list_page', () => {
utils.getByText('All agents selected'); utils.getByText('All agents selected');
}); });
afterEach(() => {
jest.useRealTimers();
});
it('should not set selection mode when agent selection changed automatically', async () => { it('should not set selection mode when agent selection changed automatically', async () => {
act(() => { act(() => {
jest.runOnlyPendingTimers(); jest.runOnlyPendingTimers();

View file

@ -13,12 +13,13 @@ import { Redirect, useRouteMatch } from 'react-router-dom';
import { Router, Routes, Route } from '@kbn/shared-ux-router'; import { Router, Routes, Route } from '@kbn/shared-ux-router';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools'; import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
import useObservable from 'react-use/lib/useObservable';
import { EuiThemeProvider } from '@kbn/kibana-react-plugin/common'; import { EuiThemeProvider } from '@kbn/kibana-react-plugin/common';
import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public';
import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render'; import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render';
import { RedirectAppLinks } from '@kbn/shared-ux-link-redirect-app'; import { RedirectAppLinks } from '@kbn/shared-ux-link-redirect-app';
import { useKibanaIsDarkMode } from '@kbn/react-kibana-context-theme';
import type { FleetConfigType, FleetStartServices } from '../../plugin'; import type { FleetConfigType, FleetStartServices } from '../../plugin';
import { import {
@ -33,6 +34,8 @@ import { SpaceSettingsContextProvider } from '../../hooks/use_space_settings_con
import { FleetServerFlyout } from '../fleet/components'; import { FleetServerFlyout } from '../fleet/components';
import { ErrorLayout, PermissionsError } from '../../layouts/error';
import { AgentPolicyContextProvider, useFlyoutContext } from './hooks'; import { AgentPolicyContextProvider, useFlyoutContext } from './hooks';
import { FLEET_ROUTING_PATHS, INTEGRATIONS_ROUTING_PATHS, pagePathGetters } from './constants'; import { FLEET_ROUTING_PATHS, INTEGRATIONS_ROUTING_PATHS, pagePathGetters } from './constants';
@ -40,12 +43,11 @@ import type { UIExtensionsStorage } from './types';
import { EPMApp } from './sections/epm'; import { EPMApp } from './sections/epm';
import { ErrorLayout, PermissionsError } from '../../layouts/error';
import { PackageInstallProvider, UIExtensionsContext, FlyoutContextProvider } from './hooks'; import { PackageInstallProvider, UIExtensionsContext, FlyoutContextProvider } from './hooks';
import { IntegrationsHeader } from './components/header'; import { IntegrationsHeader } from './components/header';
import { AgentEnrollmentFlyout } from './components'; import { AgentEnrollmentFlyout } from './components';
import { ReadOnlyContextProvider } from './hooks/use_read_only_context'; import { ReadOnlyContextProvider } from './hooks/use_read_only_context';
const queryClient = new QueryClient(); const queryClient = new QueryClient();
const EmptyContext = () => <></>; const EmptyContext = () => <></>;
@ -78,8 +80,7 @@ export const IntegrationsAppContext: React.FC<{
fleetStatus, fleetStatus,
}) => { }) => {
const XXL_BREAKPOINT = 1600; const XXL_BREAKPOINT = 1600;
const darkModeObservable = useObservable(startServices.theme.theme$); const isDarkMode = useKibanaIsDarkMode();
const isDarkMode = darkModeObservable && darkModeObservable.darkMode;
const CloudContext = startServices.cloud?.CloudContextProvider || EmptyContext; const CloudContext = startServices.cloud?.CloudContextProvider || EmptyContext;

View file

@ -7,10 +7,12 @@
import React from 'react'; import React from 'react';
import { COLOR_MODES_STANDARD, EuiCallOut, EuiSpacer, EuiToolTip, useEuiTheme } from '@elastic/eui'; import { EuiCallOut, EuiSpacer, EuiToolTip, useEuiTheme } from '@elastic/eui';
import { i18n } from '@kbn/i18n'; import { i18n } from '@kbn/i18n';
import { css } from '@emotion/css'; import { css } from '@emotion/css';
import { useKibanaIsDarkMode } from '@kbn/react-kibana-context-theme';
import { installationStatuses } from '../../../../../../common/constants'; import { installationStatuses } from '../../../../../../common/constants';
import type { EpmPackageInstallStatus } from '../../../../../../common/types'; import type { EpmPackageInstallStatus } from '../../../../../../common/types';
@ -90,8 +92,8 @@ const getCalloutText = ({
const useInstallationStatusStyles = (): InstallationStatusStylesProps & const useInstallationStatusStyles = (): InstallationStatusStylesProps &
CompressedInstallationStylesProps => { CompressedInstallationStylesProps => {
const { euiTheme, colorMode } = useEuiTheme(); const { euiTheme } = useEuiTheme();
const isDarkMode = colorMode === COLOR_MODES_STANDARD.dark; const isDarkMode = useKibanaIsDarkMode();
return React.useMemo( return React.useMemo(
() => ({ () => ({

View file

@ -107,6 +107,7 @@
"@kbn/zod-helpers", "@kbn/zod-helpers",
"@kbn/react-kibana-mount", "@kbn/react-kibana-mount",
"@kbn/react-kibana-context-render", "@kbn/react-kibana-context-render",
"@kbn/react-kibana-context-theme",
"@kbn/fields-metadata-plugin", "@kbn/fields-metadata-plugin",
"@kbn/test-jest-helpers", "@kbn/test-jest-helpers",
"@kbn/core-saved-objects-utils-server", "@kbn/core-saved-objects-utils-server",

View file

@ -6,10 +6,10 @@
*/ */
import React, { useState, useEffect, useMemo, useCallback, useRef } from 'react'; import React, { useState, useEffect, useMemo, useCallback, useRef } from 'react';
import useObservable from 'react-use/lib/useObservable';
import classNames from 'classnames'; import classNames from 'classnames';
import { FormattedMessage } from '@kbn/i18n-react'; import { FormattedMessage } from '@kbn/i18n-react';
import { toExpression } from '@kbn/interpreter'; import { toExpression } from '@kbn/interpreter';
import { useKibanaIsDarkMode } from '@kbn/react-kibana-context-theme';
import type { KibanaExecutionContext } from '@kbn/core-execution-context-common'; import type { KibanaExecutionContext } from '@kbn/core-execution-context-common';
import { i18n } from '@kbn/i18n'; import { i18n } from '@kbn/i18n';
import { import {
@ -497,10 +497,7 @@ export const InnerWorkspacePanel = React.memo(function InnerWorkspacePanel({
} }
}, [suggestionForDraggedField, dispatchLens]); }, [suggestionForDraggedField, dispatchLens]);
const IS_DARK_THEME: boolean = useObservable(core.theme.theme$, { const isDarkMode = useKibanaIsDarkMode();
darkMode: false,
name: 'amsterdam',
}).darkMode;
const renderDragDropPrompt = () => { const renderDragDropPrompt = () => {
if (chartSizeSpec) { if (chartSizeSpec) {
@ -580,7 +577,7 @@ export const InnerWorkspacePanel = React.memo(function InnerWorkspacePanel({
<img <img
aria-hidden={true} aria-hidden={true}
css={promptIllustrationStyle} css={promptIllustrationStyle}
src={IS_DARK_THEME ? applyChangesIllustrationDark : applyChangesIllustrationLight} src={isDarkMode ? applyChangesIllustrationDark : applyChangesIllustrationLight}
alt={applyChangesString} alt={applyChangesString}
/> />
<h2> <h2>

View file

@ -690,7 +690,7 @@ describe('DatatableComponent', () => {
renderDatatableComponent(); renderDatatableComponent();
expect(getCellColorFn).toBeCalledTimes(3); // 3 initial renders of table expect(getCellColorFn).toBeCalledTimes(2); // 2 initial renders of table
}); });
test('caches getCellColorFn by columnId with transpose columns', () => { test('caches getCellColorFn by columnId with transpose columns', () => {
@ -717,7 +717,7 @@ describe('DatatableComponent', () => {
}, },
}); });
expect(getCellColorFn).toBeCalledTimes(3); // 3 initial renders of table expect(getCellColorFn).toBeCalledTimes(2); // 2 initial renders of table
}); });
}); });

View file

@ -29,13 +29,12 @@ import {
import { CustomPaletteState, EmptyPlaceholder } from '@kbn/charts-plugin/public'; import { CustomPaletteState, EmptyPlaceholder } from '@kbn/charts-plugin/public';
import { ClickTriggerEvent } from '@kbn/charts-plugin/public'; import { ClickTriggerEvent } from '@kbn/charts-plugin/public';
import { IconChartDatatable } from '@kbn/chart-icons'; import { IconChartDatatable } from '@kbn/chart-icons';
import useObservable from 'react-use/lib/useObservable';
import { getOriginalId } from '@kbn/transpose-utils'; import { getOriginalId } from '@kbn/transpose-utils';
import { CoreTheme } from '@kbn/core/public'; import { useKbnPalettes } from '@kbn/palettes';
import { getKbnPalettes } from '@kbn/palettes';
import type { IFieldFormat } from '@kbn/field-formats-plugin/common'; import type { IFieldFormat } from '@kbn/field-formats-plugin/common';
import { getColorCategories, getLegacyColorCategories } from '@kbn/chart-expressions-common'; import { getColorCategories, getLegacyColorCategories } from '@kbn/chart-expressions-common';
import { css } from '@emotion/react'; import { css } from '@emotion/react';
import { useKibanaIsDarkMode } from '@kbn/react-kibana-context-theme/hooks';
import type { LensTableRowContextMenuEvent } from '../../../types'; import type { LensTableRowContextMenuEvent } from '../../../types';
import { RowHeightMode } from '../../../../common/types'; import { RowHeightMode } from '../../../../common/types';
import { LensGridDirection } from '../../../../common/expressions'; import { LensGridDirection } from '../../../../common/expressions';
@ -82,11 +81,8 @@ export const DatatableComponent = (props: DatatableRenderProps) => {
const dataGridRef = useRef<EuiDataGridRefProps>(null); const dataGridRef = useRef<EuiDataGridRefProps>(null);
const isInteractive = props.interactive; const isInteractive = props.interactive;
const theme = useObservable<CoreTheme>(props.theme.theme$, { const isDarkMode = useKibanaIsDarkMode();
darkMode: false, const palettes = useKbnPalettes();
name: 'amsterdam',
});
const palettes = getKbnPalettes(theme);
const [columnConfig, setColumnConfig] = useState({ const [columnConfig, setColumnConfig] = useState({
columns: props.args.columns, columns: props.args.columns,
@ -423,7 +419,7 @@ export const DatatableComponent = (props: DatatableRenderProps) => {
palettes, palettes,
data, data,
colorByTerms, colorByTerms,
theme.darkMode, isDarkMode,
syncColors, syncColors,
palette, palette,
colorMapping colorMapping
@ -437,14 +433,14 @@ export const DatatableComponent = (props: DatatableRenderProps) => {
formatters, formatters,
columnConfig, columnConfig,
DataContext, DataContext,
theme.darkMode, isDarkMode,
getCellColor, getCellColor,
props.args.fitRowToContent props.args.fitRowToContent
); );
}, [ }, [
formatters, formatters,
columnConfig, columnConfig,
theme.darkMode, isDarkMode,
props.args.fitRowToContent, props.args.fitRowToContent,
props.paletteService, props.paletteService,
palettes, palettes,

View file

@ -9,7 +9,7 @@ import React from 'react';
import { Ast } from '@kbn/interpreter'; import { Ast } from '@kbn/interpreter';
import { i18n } from '@kbn/i18n'; import { i18n } from '@kbn/i18n';
import { CoreTheme, ThemeServiceStart } from '@kbn/core/public'; import { ThemeServiceStart } from '@kbn/core/public';
import { import {
PaletteRegistry, PaletteRegistry,
CUSTOM_PALETTE, CUSTOM_PALETTE,
@ -23,9 +23,9 @@ import { IconChartDatatable } from '@kbn/chart-icons';
import { getOriginalId } from '@kbn/transpose-utils'; import { getOriginalId } from '@kbn/transpose-utils';
import { LayerTypes } from '@kbn/expression-xy-plugin/public'; import { LayerTypes } from '@kbn/expression-xy-plugin/public';
import { buildExpression, buildExpressionFunction } from '@kbn/expressions-plugin/common'; import { buildExpression, buildExpressionFunction } from '@kbn/expressions-plugin/common';
import useObservable from 'react-use/lib/useObservable';
import { getSortingCriteria } from '@kbn/sort-predicates'; import { getSortingCriteria } from '@kbn/sort-predicates';
import { getKbnPalettes } from '@kbn/palettes'; import { getKbnPalettes, useKbnPalettes } from '@kbn/palettes';
import { useKibanaIsDarkMode } from '@kbn/react-kibana-context-theme';
import type { FormBasedPersistedState } from '../../datasources/form_based/types'; import type { FormBasedPersistedState } from '../../datasources/form_based/types';
import type { import type {
SuggestionRequest, SuggestionRequest,
@ -493,16 +493,13 @@ export const getDatatableVisualization = ({
}; };
}, },
DimensionEditorComponent(props) { DimensionEditorComponent(props) {
const theme = useObservable<CoreTheme>(kibanaTheme.theme$, { const isDarkMode = useKibanaIsDarkMode();
darkMode: false, const palettes = useKbnPalettes();
name: 'amsterdam',
});
const palettes = getKbnPalettes(theme);
return ( return (
<TableDimensionEditor <TableDimensionEditor
{...props} {...props}
isDarkMode={theme.darkMode} isDarkMode={isDarkMode}
palettes={palettes} palettes={palettes}
paletteService={paletteService} paletteService={paletteService}
formatFactory={formatFactory} formatFactory={formatFactory}

View file

@ -505,10 +505,7 @@ export const getPieVisualization = ({
}; };
}, },
DimensionEditorComponent(props) { DimensionEditorComponent(props) {
const theme = useObservable<CoreTheme>(kibanaTheme.theme$, { const theme = useObservable<CoreTheme>(kibanaTheme.theme$, kibanaTheme.getTheme());
darkMode: false,
name: 'amsterdam',
});
const palettes = getKbnPalettes(theme); const palettes = getKbnPalettes(theme);
return ( return (
<DimensionEditor <DimensionEditor

View file

@ -305,10 +305,7 @@ export const getTagcloudVisualization = ({
}, },
DimensionEditorComponent(props) { DimensionEditorComponent(props) {
const theme = useObservable<CoreTheme>(kibanaTheme.theme$, { const theme = useObservable<CoreTheme>(kibanaTheme.theme$, kibanaTheme.getTheme());
darkMode: false,
name: 'amsterdam',
});
const palettes = getKbnPalettes(theme); const palettes = getKbnPalettes(theme);
if (props.groupId === TAG_GROUP_ID) { if (props.groupId === TAG_GROUP_ID) {

View file

@ -754,10 +754,7 @@ export const getXyVisualization = ({
paletteService, paletteService,
}; };
const theme = useObservable<CoreTheme>(kibanaTheme.theme$, { const theme = useObservable<CoreTheme>(kibanaTheme.theme$, kibanaTheme.getTheme());
darkMode: false,
name: 'amsterdam',
});
const palettes = getKbnPalettes(theme); const palettes = getKbnPalettes(theme);
const layer = props.state.layers.find((l) => l.layerId === props.layerId)!; const layer = props.state.layers.find((l) => l.layerId === props.layerId)!;
const dimensionEditor = isReferenceLayer(layer) ? ( const dimensionEditor = isReferenceLayer(layer) ? (

View file

@ -120,6 +120,7 @@
"@kbn/core-capabilities-common", "@kbn/core-capabilities-common",
"@kbn/presentation-panel-plugin", "@kbn/presentation-panel-plugin",
"@kbn/esql-types", "@kbn/esql-types",
"@kbn/react-kibana-context-theme",
"@kbn/fields-metadata-plugin", "@kbn/fields-metadata-plugin",
"@kbn/response-ops-rule-form", "@kbn/response-ops-rule-form",
"@kbn/alerts-ui-shared", "@kbn/alerts-ui-shared",

View file

@ -41,7 +41,8 @@ import type { KibanaFeature } from '@kbn/features-plugin/common';
import type { FeaturesPluginStart } from '@kbn/features-plugin/public'; import type { FeaturesPluginStart } from '@kbn/features-plugin/public';
import { i18n } from '@kbn/i18n'; import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n-react'; import { FormattedMessage } from '@kbn/i18n-react';
import { reactRouterNavigate, useDarkMode } from '@kbn/kibana-react-plugin/public'; import { reactRouterNavigate } from '@kbn/kibana-react-plugin/public';
import { useKibanaIsDarkMode } from '@kbn/react-kibana-context-theme';
import { toMountPoint } from '@kbn/react-kibana-mount'; import { toMountPoint } from '@kbn/react-kibana-mount';
import type { Cluster } from '@kbn/remote-clusters-plugin/public'; import type { Cluster } from '@kbn/remote-clusters-plugin/public';
import { REMOTE_CLUSTERS_PATH } from '@kbn/remote-clusters-plugin/public'; import { REMOTE_CLUSTERS_PATH } from '@kbn/remote-clusters-plugin/public';
@ -339,7 +340,7 @@ export const EditRolePage: FunctionComponent<Props> = ({
cloudOrgUrl, cloudOrgUrl,
...startServices ...startServices
}) => { }) => {
const isDarkMode = useDarkMode(); const isDarkMode = useKibanaIsDarkMode();
if (!dataViews) { if (!dataViews) {
// The dataViews plugin is technically marked as an optional dependency because we don't need to pull it in for Anonymous pages (such // The dataViews plugin is technically marked as an optional dependency because we don't need to pull it in for Anonymous pages (such

View file

@ -92,6 +92,7 @@
"@kbn/core-elasticsearch-server", "@kbn/core-elasticsearch-server",
"@kbn/core-http-server-utils", "@kbn/core-http-server-utils",
"@kbn/core-user-profile-browser-mocks", "@kbn/core-user-profile-browser-mocks",
"@kbn/react-kibana-context-theme",
], ],
"exclude": [ "exclude": [
"target/**/*", "target/**/*",

View file

@ -5,16 +5,17 @@
* 2.0. * 2.0.
*/ */
import React from 'react'; import React from 'react';
import { EuiEmptyPrompt, EuiImage, useEuiTheme } from '@elastic/eui'; import { EuiEmptyPrompt, EuiImage } from '@elastic/eui';
import { i18n } from '@kbn/i18n'; import { i18n } from '@kbn/i18n';
import { dashboardsDark, dashboardsLight } from '@kbn/shared-svg'; import { dashboardsDark, dashboardsLight } from '@kbn/shared-svg';
import { useKibanaIsDarkMode } from '@kbn/react-kibana-context-theme';
interface Props { interface Props {
actions: React.ReactNode; actions: React.ReactNode;
} }
export function EmptyDashboards({ actions }: Props) { export function EmptyDashboards({ actions }: Props) {
const { colorMode } = useEuiTheme(); const isDarkMode = useKibanaIsDarkMode();
return ( return (
<> <>
@ -22,11 +23,7 @@ export function EmptyDashboards({ actions }: Props) {
hasShadow={false} hasShadow={false}
hasBorder={false} hasBorder={false}
icon={ icon={
<EuiImage <EuiImage size="fullWidth" src={isDarkMode ? dashboardsDark : dashboardsLight} alt="" />
size="fullWidth"
src={colorMode === 'DARK' ? dashboardsDark : dashboardsLight}
alt=""
/>
} }
title={ title={
<h2> <h2>

View file

@ -5,13 +5,14 @@
* 2.0. * 2.0.
*/ */
import { EuiFlexGroup, EuiFlexItem, EuiSkeletonText, useEuiTheme } from '@elastic/eui'; import { EuiFlexGroup, EuiFlexItem, EuiSkeletonText } from '@elastic/eui';
import type { CloudProvider } from '@kbn/custom-icons'; import type { CloudProvider } from '@kbn/custom-icons';
import { getAgentIcon, getCloudProviderIcon } from '@kbn/custom-icons'; import { getAgentIcon, getCloudProviderIcon } from '@kbn/custom-icons';
import { i18n } from '@kbn/i18n'; import { i18n } from '@kbn/i18n';
import { get } from 'lodash'; import { get } from 'lodash';
import React from 'react'; import React from 'react';
import { useHistory } from 'react-router-dom'; import { useHistory } from 'react-router-dom';
import { useKibanaIsDarkMode } from '@kbn/react-kibana-context-theme';
import { import {
CLOUD_AVAILABILITY_ZONE, CLOUD_AVAILABILITY_ZONE,
CLOUD_INSTANCE_ID, CLOUD_INSTANCE_ID,
@ -89,7 +90,7 @@ const cloudDetailsKeys = [
]; ];
export function InstanceDetails({ serviceName, serviceNodeName, kuery }: Props) { export function InstanceDetails({ serviceName, serviceNodeName, kuery }: Props) {
const { colorMode } = useEuiTheme(); const isDarkMode = useKibanaIsDarkMode();
const history = useHistory(); const history = useHistory();
const { data, status } = useInstanceDetailsFetcher({ const { data, status } = useInstanceDetailsFetcher({
@ -133,7 +134,6 @@ export function InstanceDetails({ serviceName, serviceNodeName, kuery }: Props)
const containerType = data.kubernetes?.pod?.name ? 'Kubernetes' : 'Docker'; const containerType = data.kubernetes?.pod?.name ? 'Kubernetes' : 'Docker';
const isDarkMode = colorMode === 'DARK';
return ( return (
<EuiFlexGroup direction="column" responsive={false}> <EuiFlexGroup direction="column" responsive={false}>
<EuiFlexItem> <EuiFlexItem>

View file

@ -31,6 +31,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiIcon, EuiSpacer, useEuiTheme } from '@ela
import { i18n } from '@kbn/i18n'; import { i18n } from '@kbn/i18n';
import type { ReactElement } from 'react'; import type { ReactElement } from 'react';
import React from 'react'; import React from 'react';
import { useKibanaIsDarkMode } from '@kbn/react-kibana-context-theme';
import { useHistory } from 'react-router-dom'; import { useHistory } from 'react-router-dom';
import { useChartThemes } from '@kbn/observability-shared-plugin/public'; import { useChartThemes } from '@kbn/observability-shared-plugin/public';
import { isExpectedBoundsComparison } from '../time_comparison/get_comparison_options'; import { isExpectedBoundsComparison } from '../time_comparison/get_comparison_options';
@ -77,7 +78,8 @@ export function TimeseriesChart({
}: TimeseriesChartProps) { }: TimeseriesChartProps) {
const history = useHistory(); const history = useHistory();
const { chartRef, updatePointerEvent } = useChartPointerEventContext(); const { chartRef, updatePointerEvent } = useChartPointerEventContext();
const { euiTheme, colorMode } = useEuiTheme(); const { euiTheme } = useEuiTheme();
const isDarkMode = useKibanaIsDarkMode();
const chartThemes = useChartThemes(); const chartThemes = useChartThemes();
const anomalyChartTimeseries = getChartAnomalyTimeseries({ const anomalyChartTimeseries = getChartAnomalyTimeseries({
anomalyTimeseries, anomalyTimeseries,
@ -117,7 +119,6 @@ export function TimeseriesChart({
} }
: undefined; : undefined;
const isDarkMode = colorMode === 'DARK';
const endZoneColor = isDarkMode ? euiTheme.colors.lightShade : euiTheme.colors.darkShade; const endZoneColor = isDarkMode ? euiTheme.colors.lightShade : euiTheme.colors.darkShade;
const endZoneRectAnnotationStyle: Partial<RectAnnotationStyle> = { const endZoneRectAnnotationStyle: Partial<RectAnnotationStyle> = {
stroke: endZoneColor, stroke: endZoneColor,
@ -152,7 +153,7 @@ export function TimeseriesChart({
alignItems="center" alignItems="center"
responsive={false} responsive={false}
gutterSize="xs" gutterSize="xs"
style={{ fontWeight: 'normal' }} css={{ fontWeight: 'normal' }}
> >
<EuiFlexItem grow={false}> <EuiFlexItem grow={false}>
<EuiIcon type="iInCircle" /> <EuiIcon type="iInCircle" />

View file

@ -5,8 +5,9 @@
* 2.0. * 2.0.
*/ */
import { EuiFlexGroup, EuiFlexItem, EuiLoadingSpinner, useEuiTheme } from '@elastic/eui'; import { EuiFlexGroup, EuiFlexItem, EuiLoadingSpinner } from '@elastic/eui';
import { i18n } from '@kbn/i18n'; import { i18n } from '@kbn/i18n';
import { useKibanaIsDarkMode } from '@kbn/react-kibana-context-theme';
import type { CloudProvider } from '@kbn/custom-icons'; import type { CloudProvider } from '@kbn/custom-icons';
import { getAgentIcon, getCloudProviderIcon, getServerlessIcon } from '@kbn/custom-icons'; import { getAgentIcon, getCloudProviderIcon, getServerlessIcon } from '@kbn/custom-icons';
import type { ReactChild } from 'react'; import type { ReactChild } from 'react';
@ -75,11 +76,9 @@ export interface PopoverItem {
} }
export function ServiceIcons({ start, end, serviceName, environment }: Props) { export function ServiceIcons({ start, end, serviceName, environment }: Props) {
const isDarkMode = useKibanaIsDarkMode();
const [selectedIconPopover, setSelectedIconPopover] = useState<Icons | null>(); const [selectedIconPopover, setSelectedIconPopover] = useState<Icons | null>();
const { colorMode } = useEuiTheme();
const isDarkMode = colorMode === 'DARK';
const { data: icons, status: iconsFetchStatus } = useFetcher( const { data: icons, status: iconsFetchStatus } = useFetcher(
(callApmApi) => { (callApmApi) => {
if (serviceName && start && end) { if (serviceName && start && end) {

View file

@ -15,6 +15,7 @@ import type { Storage } from '@kbn/kibana-utils-plugin/public';
import { NavigationWarningPromptProvider } from '@kbn/observability-shared-plugin/public'; import { NavigationWarningPromptProvider } from '@kbn/observability-shared-plugin/public';
import type { TriggersAndActionsUIPublicPluginStart } from '@kbn/triggers-actions-ui-plugin/public'; import type { TriggersAndActionsUIPublicPluginStart } from '@kbn/triggers-actions-ui-plugin/public';
import { RedirectAppLinks } from '@kbn/shared-ux-link-redirect-app'; import { RedirectAppLinks } from '@kbn/shared-ux-link-redirect-app';
import { useKibanaIsDarkMode } from '@kbn/react-kibana-context-theme/hooks';
import { import {
type KibanaEnvContext, type KibanaEnvContext,
useKibanaContextForPluginProvider, useKibanaContextForPluginProvider,
@ -23,7 +24,6 @@ import {
import type { InfraClientStartDeps, InfraClientStartExports } from '../types'; import type { InfraClientStartDeps, InfraClientStartExports } from '../types';
import { HeaderActionMenuProvider } from '../containers/header_action_menu_provider'; import { HeaderActionMenuProvider } from '../containers/header_action_menu_provider';
import { TriggersActionsProvider } from '../containers/triggers_actions_context'; import { TriggersActionsProvider } from '../containers/triggers_actions_context';
import { useIsDarkMode } from '../hooks/use_is_dark_mode';
export const CommonInfraProviders: FC< export const CommonInfraProviders: FC<
PropsWithChildren<{ PropsWithChildren<{
@ -34,7 +34,7 @@ export const CommonInfraProviders: FC<
theme$: AppMountParameters['theme$']; theme$: AppMountParameters['theme$'];
}> }>
> = ({ children, triggersActionsUI, setHeaderActionMenu, appName, storage, theme$ }) => { > = ({ children, triggersActionsUI, setHeaderActionMenu, appName, storage, theme$ }) => {
const darkMode = useIsDarkMode(); const darkMode = useKibanaIsDarkMode();
return ( return (
<TriggersActionsProvider triggersActionsUI={triggersActionsUI}> <TriggersActionsProvider triggersActionsUI={triggersActionsUI}>

View file

@ -6,7 +6,7 @@
*/ */
import type { FC, PropsWithChildren } from 'react'; import type { FC, PropsWithChildren } from 'react';
import React, { useEffect, useMemo, useState } from 'react'; import React, { useMemo } from 'react';
import { merge } from 'rxjs'; import { merge } from 'rxjs';
import { i18n } from '@kbn/i18n'; import { i18n } from '@kbn/i18n';
import { EuiCallOut, EuiLink, useEuiTheme } from '@elastic/eui'; import { EuiCallOut, EuiLink, useEuiTheme } from '@elastic/eui';
@ -21,6 +21,7 @@ import {
import { initializeUnsavedChanges } from '@kbn/presentation-containers'; import { initializeUnsavedChanges } from '@kbn/presentation-containers';
import { LogStream } from '@kbn/logs-shared-plugin/public'; import { LogStream } from '@kbn/logs-shared-plugin/public';
import type { AppMountParameters, CoreStart } from '@kbn/core/public'; import type { AppMountParameters, CoreStart } from '@kbn/core/public';
import { useKibanaIsDarkMode } from '@kbn/react-kibana-context-theme';
import { EuiThemeProvider } from '@kbn/kibana-react-plugin/common'; import { EuiThemeProvider } from '@kbn/kibana-react-plugin/common';
import type { Query } from '@kbn/es-query'; import type { Query } from '@kbn/es-query';
import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render'; import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render';
@ -73,6 +74,7 @@ export function getLogStreamEmbeddableFactory(services: Services) {
return { return {
api, api,
Component: () => { Component: () => {
const darkMode = useKibanaIsDarkMode();
const { filters, query, timeRange } = useFetchContext(api); const { filters, query, timeRange } = useFetchContext(api);
const { startTimestamp, endTimestamp } = useMemo(() => { const { startTimestamp, endTimestamp } = useMemo(() => {
return { return {
@ -81,14 +83,6 @@ export function getLogStreamEmbeddableFactory(services: Services) {
}; };
}, [timeRange]); }, [timeRange]);
const [darkMode, setDarkMode] = useState(false);
useEffect(() => {
const subscription = services.coreStart.theme.theme$.subscribe((theme) => {
setDarkMode(theme.darkMode);
});
return () => subscription.unsubscribe();
}, []);
return !startTimestamp || !endTimestamp ? null : ( return !startTimestamp || !endTimestamp ? null : (
<LogStreamEmbeddableProviders <LogStreamEmbeddableProviders
core={services.coreStart} core={services.coreStart}

View file

@ -1,20 +0,0 @@
/*
* 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; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import type { CoreTheme } from '@kbn/core/public';
import useObservable from 'react-use/lib/useObservable';
import { of } from 'rxjs';
import { useKibanaContextForPlugin } from './use_kibana';
const themeDefault: CoreTheme = { darkMode: false, name: 'amsterdam' };
export const useIsDarkMode = () => {
const { services } = useKibanaContextForPlugin();
const { darkMode } = useObservable(services.theme?.theme$ ?? of(themeDefault), themeDefault);
return darkMode;
};

View file

@ -17,7 +17,7 @@ import {
import styled from 'styled-components'; import styled from 'styled-components';
import { FormattedMessage } from '@kbn/i18n-react'; import { FormattedMessage } from '@kbn/i18n-react';
import { euiLightVars, euiDarkVars } from '@kbn/ui-theme'; import { euiLightVars, euiDarkVars } from '@kbn/ui-theme';
import { useDarkMode } from '@kbn/kibana-react-plugin/public'; import { useKibanaIsDarkMode } from '@kbn/react-kibana-context-theme';
import { getCoreVitalTooltipMessage, Thresholds } from './core_vital_item'; import { getCoreVitalTooltipMessage, Thresholds } from './core_vital_item';
import { import {
LEGEND_NEEDS_IMPROVEMENT_LABEL, LEGEND_NEEDS_IMPROVEMENT_LABEL,
@ -50,7 +50,7 @@ interface Props {
} }
export function PaletteLegends({ ranks, title, onItemHover, thresholds, isCls }: Props) { export function PaletteLegends({ ranks, title, onItemHover, thresholds, isCls }: Props) {
const darkMode = useDarkMode(false); const darkMode = useKibanaIsDarkMode();
const palette = euiPaletteForStatus(3); const palette = euiPaletteForStatus(3);
const labels = [LEGEND_GOOD_LABEL, LEGEND_NEEDS_IMPROVEMENT_LABEL, LEGEND_POOR_LABEL]; const labels = [LEGEND_GOOD_LABEL, LEGEND_NEEDS_IMPROVEMENT_LABEL, LEGEND_POOR_LABEL];

View file

@ -17,13 +17,14 @@ import type { AppMountParameters } from '@kbn/core/public';
import { EuiThemeProvider } from '@kbn/kibana-react-plugin/common'; import { EuiThemeProvider } from '@kbn/kibana-react-plugin/common';
import { CellActionsProvider } from '@kbn/cell-actions'; import { CellActionsProvider } from '@kbn/cell-actions';
import { NavigationProvider } from '@kbn/security-solution-navigation'; import { NavigationProvider } from '@kbn/security-solution-navigation';
import { useKibanaIsDarkMode } from '@kbn/react-kibana-context-theme';
import { UpsellingProvider } from '../common/components/upselling_provider'; import { UpsellingProvider } from '../common/components/upselling_provider';
import { ManageUserInfo } from '../detections/components/user_info'; import { ManageUserInfo } from '../detections/components/user_info';
import { APP_NAME } from '../../common/constants'; import { APP_NAME } from '../../common/constants';
import { ErrorToastDispatcher } from '../common/components/error_toast_dispatcher'; import { ErrorToastDispatcher } from '../common/components/error_toast_dispatcher';
import { MlCapabilitiesProvider } from '../common/components/ml/permissions/ml_capabilities_provider'; import { MlCapabilitiesProvider } from '../common/components/ml/permissions/ml_capabilities_provider';
import { GlobalToaster, ManageGlobalToaster } from '../common/components/toasters'; import { GlobalToaster, ManageGlobalToaster } from '../common/components/toasters';
import { KibanaContextProvider, useKibana, useDarkMode } from '../common/lib/kibana'; import { KibanaContextProvider, useKibana } from '../common/lib/kibana';
import type { State } from '../common/store'; import type { State } from '../common/store';
import type { StartServices } from '../types'; import type { StartServices } from '../types';
import { PageRouter } from './routes'; import { PageRouter } from './routes';
@ -47,7 +48,7 @@ const StartAppComponent: FC<StartAppComponent> = ({ children, history, store, th
upselling, upselling,
} = services; } = services;
const darkMode = useDarkMode(); const darkMode = useKibanaIsDarkMode();
return ( return (
<KibanaRenderContextProvider {...services}> <KibanaRenderContextProvider {...services}>

View file

@ -19,7 +19,7 @@ import type { GenerationInterval } from '@kbn/elastic-assistant-common';
import React, { useCallback, useEffect, useMemo, useState } from 'react'; import React, { useCallback, useEffect, useMemo, useState } from 'react';
import moment from 'moment'; import moment from 'moment';
import { useKibana } from '../../../../common/lib/kibana'; import { useKibanaIsDarkMode } from '@kbn/react-kibana-context-theme';
import { InfoPopoverBody } from '../info_popover_body'; import { InfoPopoverBody } from '../info_popover_body';
import { getTimerPrefix } from './last_times_popover/helpers'; import { getTimerPrefix } from './last_times_popover/helpers';
import * as i18n from '../translations'; import * as i18n from '../translations';
@ -42,8 +42,7 @@ const CountdownComponent: React.FC<Props> = ({
}) => { }) => {
// theming: // theming:
const { euiTheme } = useEuiTheme(); const { euiTheme } = useEuiTheme();
const { theme } = useKibana().services; const isDarkMode = useKibanaIsDarkMode();
const isDarkMode = useMemo(() => theme.getTheme().darkMode === true, [theme]);
// popover state: // popover state:
const [isPopoverOpen, setIsPopoverOpen] = useState(false); const [isPopoverOpen, setIsPopoverOpen] = useState(false);

View file

@ -9,10 +9,10 @@ import { EuiFlexGroup, EuiFlexItem, EuiBadge, EuiText, useEuiTheme } from '@elas
import { css } from '@emotion/react'; import { css } from '@emotion/react';
import type { GenerationInterval } from '@kbn/elastic-assistant-common'; import type { GenerationInterval } from '@kbn/elastic-assistant-common';
import moment from 'moment'; import moment from 'moment';
import React, { useMemo } from 'react'; import React from 'react';
import { useKibanaIsDarkMode } from '@kbn/react-kibana-context-theme';
import { PreferenceFormattedDate } from '../../../../../../common/components/formatted_date'; import { PreferenceFormattedDate } from '../../../../../../common/components/formatted_date';
import { useKibana } from '../../../../../../common/lib/kibana';
import { MAX_SECONDS_BADGE_WIDTH } from '../helpers'; import { MAX_SECONDS_BADGE_WIDTH } from '../helpers';
import * as i18n from '../translations'; import * as i18n from '../translations';
@ -22,8 +22,7 @@ interface Props {
const GenerationTimingComponent: React.FC<Props> = ({ interval }) => { const GenerationTimingComponent: React.FC<Props> = ({ interval }) => {
const { euiTheme } = useEuiTheme(); const { euiTheme } = useEuiTheme();
const { theme } = useKibana().services; const isDarkMode = useKibanaIsDarkMode();
const isDarkMode = useMemo(() => theme.getTheme().darkMode === true, [theme]);
return ( return (
<EuiFlexGroup alignItems="center" data-test-subj="generationTiming" gutterSize="none"> <EuiFlexGroup alignItems="center" data-test-subj="generationTiming" gutterSize="none">

View file

@ -7,10 +7,10 @@
import { EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiText, useEuiTheme } from '@elastic/eui'; import { EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiText, useEuiTheme } from '@elastic/eui';
import { css } from '@emotion/react'; import { css } from '@emotion/react';
import React, { useMemo } from 'react'; import React from 'react';
import type { GenerationInterval } from '@kbn/elastic-assistant-common'; import type { GenerationInterval } from '@kbn/elastic-assistant-common';
import { useKibanaIsDarkMode } from '@kbn/react-kibana-context-theme';
import { useKibana } from '../../../../../common/lib/kibana';
import { GenerationTiming } from './generation_timing'; import { GenerationTiming } from './generation_timing';
import { useKibanaFeatureFlags } from '../../../use_kibana_feature_flags'; import { useKibanaFeatureFlags } from '../../../use_kibana_feature_flags';
import * as i18n from './translations'; import * as i18n from './translations';
@ -25,8 +25,7 @@ const LastTimesPopoverComponent: React.FC<Props> = ({
successfulGenerations, successfulGenerations,
}) => { }) => {
const { euiTheme } = useEuiTheme(); const { euiTheme } = useEuiTheme();
const { theme } = useKibana().services; const isDarkMode = useKibanaIsDarkMode();
const isDarkMode = useMemo(() => theme.getTheme().darkMode === true, [theme]);
const { attackDiscoveryAlertsEnabled } = useKibanaFeatureFlags(); const { attackDiscoveryAlertsEnabled } = useKibanaFeatureFlags();
const calculatedBy = attackDiscoveryAlertsEnabled const calculatedBy = attackDiscoveryAlertsEnabled

View file

@ -18,7 +18,7 @@ import { css } from '@emotion/react';
import React, { useCallback, useMemo, useState } from 'react'; import React, { useCallback, useMemo, useState } from 'react';
import type { GenerationInterval } from '@kbn/elastic-assistant-common'; import type { GenerationInterval } from '@kbn/elastic-assistant-common';
import { useKibana } from '../../../common/lib/kibana'; import { useKibanaIsDarkMode } from '@kbn/react-kibana-context-theme';
import { Countdown } from './countdown'; import { Countdown } from './countdown';
import { LoadingMessages } from './loading_messages'; import { LoadingMessages } from './loading_messages';
import * as i18n from './translations'; import * as i18n from './translations';
@ -69,7 +69,7 @@ const LoadingCalloutComponent: React.FC<Props> = ({
successfulGenerations, successfulGenerations,
}) => { }) => {
const { euiTheme } = useEuiTheme(); const { euiTheme } = useEuiTheme();
const { theme } = useKibana().services; const isDarkMode = useKibanaIsDarkMode();
const { attackDiscoveryAlertsEnabled } = useKibanaFeatureFlags(); const { attackDiscoveryAlertsEnabled } = useKibanaFeatureFlags();
const isTerminalState = useMemo(() => getIsTerminalState(status), [status]); const isTerminalState = useMemo(() => getIsTerminalState(status), [status]);
@ -122,8 +122,6 @@ const LoadingCalloutComponent: React.FC<Props> = ({
] ]
); );
const isDarkMode = useMemo(() => theme.getTheme().darkMode === true, [theme]);
const backgroundColor = useMemo(() => { const backgroundColor = useMemo(() => {
const defaultBackgroundColor = isDarkMode ? BACKGROUND_COLOR_DARK : BACKGROUND_COLOR_LIGHT; const defaultBackgroundColor = isDarkMode ? BACKGROUND_COLOR_DARK : BACKGROUND_COLOR_LIGHT;
const successBackgroundColor = euiTheme.colors.backgroundBaseSuccess; const successBackgroundColor = euiTheme.colors.backgroundBaseSuccess;

View file

@ -8,8 +8,8 @@
import { EuiBadge, EuiFlexGroup, EuiFlexItem, EuiPopoverTitle, EuiText } from '@elastic/eui'; import { EuiBadge, EuiFlexGroup, EuiFlexItem, EuiPopoverTitle, EuiText } from '@elastic/eui';
import { css } from '@emotion/react'; import { css } from '@emotion/react';
import type { GenerationInterval } from '@kbn/elastic-assistant-common'; import type { GenerationInterval } from '@kbn/elastic-assistant-common';
import { useKibanaIsDarkMode } from '@kbn/react-kibana-context-theme';
import React, { useMemo } from 'react'; import React, { useMemo } from 'react';
import { useKibana } from '../../../../common/lib/kibana';
import { LastTimesPopover } from '../countdown/last_times_popover'; import { LastTimesPopover } from '../countdown/last_times_popover';
import { import {
@ -33,8 +33,7 @@ const InfoPopoverBodyComponent: React.FC<Props> = ({
connectorIntervals, connectorIntervals,
successfulGenerations, successfulGenerations,
}) => { }) => {
const { theme } = useKibana().services; const isDarkMode = useKibanaIsDarkMode();
const isDarkMode = useMemo(() => theme.getTheme().darkMode === true, [theme]);
const { attackDiscoveryAlertsEnabled } = useKibanaFeatureFlags(); const { attackDiscoveryAlertsEnabled } = useKibanaFeatureFlags();
const averageIntervalSeconds = useMemo(() => { const averageIntervalSeconds = useMemo(() => {
if (attackDiscoveryAlertsEnabled) { if (attackDiscoveryAlertsEnabled) {

View file

@ -12,7 +12,8 @@ import { DEFAULT_ATTACK_DISCOVERY_MAX_ALERTS } from '@kbn/elastic-assistant';
import React, { useMemo } from 'react'; import React, { useMemo } from 'react';
import { getAttackDiscoveryLoadingMessage } from '@kbn/elastic-assistant-common'; import { getAttackDiscoveryLoadingMessage } from '@kbn/elastic-assistant-common';
import { useDateFormat, useKibana } from '../../../../common/lib/kibana'; import { useKibanaIsDarkMode } from '@kbn/react-kibana-context-theme';
import { useDateFormat } from '../../../../common/lib/kibana';
import { getCanceledResultMessage } from './get_canceled_result_message'; import { getCanceledResultMessage } from './get_canceled_result_message';
import { getFormattedDate } from './get_formatted_time'; import { getFormattedDate } from './get_formatted_time';
import { getIsTerminalState } from '../get_is_terminal_state'; import { getIsTerminalState } from '../get_is_terminal_state';
@ -49,7 +50,7 @@ const LoadingMessagesComponent: React.FC<Props> = ({
start, start,
status, status,
}) => { }) => {
const { theme } = useKibana().services; const isDarkMode = useKibanaIsDarkMode();
const dateFormat = useDateFormat(); const dateFormat = useDateFormat();
const formattedStart = useMemo( const formattedStart = useMemo(
@ -76,8 +77,6 @@ const LoadingMessagesComponent: React.FC<Props> = ({
localStorageAttackDiscoveryMaxAlerts, localStorageAttackDiscoveryMaxAlerts,
}); });
const isDarkMode = theme.getTheme().darkMode === true;
const isTerminalState = useMemo(() => getIsTerminalState(status), [status]); const isTerminalState = useMemo(() => getIsTerminalState(status), [status]);
const progressMessage = useMemo( const progressMessage = useMemo(

View file

@ -6,7 +6,8 @@
*/ */
import React, { type PropsWithChildren } from 'react'; import React, { type PropsWithChildren } from 'react';
import { css, type CSSInterpolation } from '@emotion/css'; import { css, type CSSInterpolation } from '@emotion/css';
import { EuiText, useEuiTheme, COLOR_MODES_STANDARD, type EuiTextProps } from '@elastic/eui'; import { EuiText, useEuiTheme, type EuiTextProps } from '@elastic/eui';
import { useKibanaIsDarkMode } from '@kbn/react-kibana-context-theme';
export interface PanelTextProps extends PropsWithChildren<EuiTextProps> { export interface PanelTextProps extends PropsWithChildren<EuiTextProps> {
subdued?: true; subdued?: true;
@ -15,8 +16,8 @@ export interface PanelTextProps extends PropsWithChildren<EuiTextProps> {
} }
export const PanelText = React.memo<PanelTextProps>( export const PanelText = React.memo<PanelTextProps>(
({ children, subdued, semiBold, cursive, ...props }) => { ({ children, subdued, semiBold, cursive, ...props }) => {
const { euiTheme, colorMode } = useEuiTheme(); const { euiTheme } = useEuiTheme();
const isDarkMode = colorMode === COLOR_MODES_STANDARD.dark; const isDarkMode = useKibanaIsDarkMode();
let color; let color;
if (subdued && !isDarkMode) { if (subdued && !isDarkMode) {

View file

@ -85,9 +85,6 @@ export const useKibana = jest.fn().mockReturnValue({
}); });
export const useUiSetting = jest.fn(createUseUiSettingMock()); export const useUiSetting = jest.fn(createUseUiSettingMock());
export const useUiSetting$ = jest.fn(createUseUiSetting$Mock()); export const useUiSetting$ = jest.fn(createUseUiSetting$Mock());
export const useDarkMode = jest
.fn()
.mockImplementation((defaultValue?: boolean) => defaultValue ?? false);
export const useHttp = jest.fn().mockReturnValue(createStartServicesMock().http); export const useHttp = jest.fn().mockReturnValue(createStartServicesMock().http);
export const useTimeZone = jest.fn(); export const useTimeZone = jest.fn();
export const useDateFormat = jest.fn().mockReturnValue('MMM D, YYYY @ HH:mm:ss.SSS'); export const useDateFormat = jest.fn().mockReturnValue('MMM D, YYYY @ HH:mm:ss.SSS');

View file

@ -10,7 +10,6 @@ import {
useKibana, useKibana,
useUiSetting, useUiSetting,
useUiSetting$, useUiSetting$,
useDarkMode,
withKibana, withKibana,
} from '@kbn/kibana-react-plugin/public'; } from '@kbn/kibana-react-plugin/public';
import type { ApmBase } from '@elastic/apm-rum'; import type { ApmBase } from '@elastic/apm-rum';
@ -24,6 +23,5 @@ export {
useTypedKibana as useKibana, useTypedKibana as useKibana,
useUiSetting, useUiSetting,
useUiSetting$, useUiSetting$,
useDarkMode,
withKibana, withKibana,
}; };

View file

@ -1,15 +0,0 @@
/*
* 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; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { euiLightVars as lightTheme, euiDarkVars as darkTheme } from '@kbn/ui-theme';
import { useDarkMode } from '../kibana';
export const useEuiTheme = () => {
const darkMode = useDarkMode();
return darkMode ? darkTheme : lightTheme;
};

View file

@ -10,7 +10,7 @@ import React, { memo, useMemo } from 'react';
import { Provider } from 'react-redux'; import { Provider } from 'react-redux';
import { Router } from '@kbn/shared-ux-router'; import { Router } from '@kbn/shared-ux-router';
import type { History } from 'history'; import type { History } from 'history';
import useObservable from 'react-use/lib/useObservable'; import { useKibanaIsDarkMode } from '@kbn/react-kibana-context-theme';
import type { Store } from 'redux'; import type { Store } from 'redux';
import { EuiThemeProvider } from '@kbn/kibana-react-plugin/common'; import { EuiThemeProvider } from '@kbn/kibana-react-plugin/common';
import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render'; import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render';
@ -37,9 +37,7 @@ export const AppRootProvider = memo<{
queryClient: QueryClient; queryClient: QueryClient;
children: ReactNode | ReactNode[]; children: ReactNode | ReactNode[];
}>(({ store, history, coreStart, depsStart, queryClient, startServices, children }) => { }>(({ store, history, coreStart, depsStart, queryClient, startServices, children }) => {
const { theme: themeStart } = coreStart; const isDarkMode = useKibanaIsDarkMode();
const theme = useObservable(themeStart.theme$, themeStart.getTheme());
const isDarkMode = theme.darkMode;
const services = useMemo(() => { const services = useMemo(() => {
return { return {
...depsStart, ...depsStart,

View file

@ -5,11 +5,10 @@
* 2.0. * 2.0.
*/ */
import { EuiAvatar, EuiIcon } from '@elastic/eui'; import { EuiAvatar, EuiIcon, useEuiTheme } from '@elastic/eui';
import React, { memo } from 'react'; import React, { memo } from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
import { useEuiTheme } from '../../../../common/lib/theme/use_eui_theme';
import type { RuleStatusType } from '../../../common/types'; import type { RuleStatusType } from '../../../common/types';
export interface RuleStatusIconProps { export interface RuleStatusIconProps {
@ -27,12 +26,16 @@ const RuleStatusIconStyled = styled.div`
`; `;
const RuleStatusIconComponent: React.FC<RuleStatusIconProps> = ({ name, type }) => { const RuleStatusIconComponent: React.FC<RuleStatusIconProps> = ({ name, type }) => {
const theme = useEuiTheme(); const { euiTheme } = useEuiTheme();
const color = type === 'passive' ? theme.euiColorLightestShade : theme.euiColorPrimary;
const color =
type === 'passive' ? euiTheme.colors.backgroundBaseDisabled : euiTheme.colors.primary;
return ( return (
<RuleStatusIconStyled> <RuleStatusIconStyled>
<EuiAvatar color={color} name={type === 'valid' ? '' : name} size="l" aria-label={name} /> <EuiAvatar color={color} name={type === 'valid' ? '' : name} size="l" aria-label={name} />
{type === 'valid' ? <EuiIcon type="check" color={theme.euiColorEmptyShade} size="l" /> : null} {type === 'valid' ? (
<EuiIcon type="check" color={euiTheme.colors.backgroundBasePlain} size="l" />
) : null}
</RuleStatusIconStyled> </RuleStatusIconStyled>
); );
}; };

View file

@ -8,8 +8,8 @@
import { euiDarkVars as darkTheme, euiLightVars as lightTheme } from '@kbn/ui-theme'; import { euiDarkVars as darkTheme, euiLightVars as lightTheme } from '@kbn/ui-theme';
import React from 'react'; import React from 'react';
import { useKibanaIsDarkMode } from '@kbn/react-kibana-context-theme';
import type { DescriptionList } from '../../../../../common/utility_types'; import type { DescriptionList } from '../../../../../common/utility_types';
import { useDarkMode } from '../../../../common/lib/kibana';
import type { import type {
FlowTargetSourceDest, FlowTargetSourceDest,
NetworkDetailsStrategyResponse, NetworkDetailsStrategyResponse,
@ -79,7 +79,7 @@ export const IpOverview = React.memo<IpOverviewProps>(
}) => { }) => {
const capabilities = useMlCapabilities(); const capabilities = useMlCapabilities();
const userPermissions = hasMlUserPermissions(capabilities); const userPermissions = hasMlUserPermissions(capabilities);
const darkMode = useDarkMode(); const darkMode = useKibanaIsDarkMode();
const typeData = data[flowTarget]; const typeData = data[flowTarget];
const column: DescriptionList[] = [ const column: DescriptionList[] = [
{ {

View file

@ -6,12 +6,12 @@
*/ */
import React, { type PropsWithChildren } from 'react'; import React, { type PropsWithChildren } from 'react';
import { EuiText, useEuiTheme, COLOR_MODES_STANDARD, type EuiTextProps } from '@elastic/eui'; import { EuiText, type EuiTextProps } from '@elastic/eui';
import { useKibanaIsDarkMode } from '@kbn/react-kibana-context-theme';
export type CardSubduedTextProps = PropsWithChildren<EuiTextProps>; export type CardSubduedTextProps = PropsWithChildren<EuiTextProps>;
export const CardSubduedText = React.memo<CardSubduedTextProps>(({ children, ...props }) => { export const CardSubduedText = React.memo<CardSubduedTextProps>(({ children, ...props }) => {
const { colorMode } = useEuiTheme(); const isDarkMode = useKibanaIsDarkMode();
const isDarkMode = colorMode === COLOR_MODES_STANDARD.dark;
return ( return (
<EuiText {...props} color={isDarkMode ? 'text' : 'subdued'}> <EuiText {...props} color={isDarkMode ? 'text' : 'subdued'}>
{children} {children}

View file

@ -5,15 +5,16 @@
* 2.0. * 2.0.
*/ */
import { COLOR_MODES_STANDARD, useEuiTheme } from '@elastic/eui'; import { useKibanaIsDarkMode } from '@kbn/react-kibana-context-theme';
import { useEuiTheme } from '@elastic/eui';
import { css } from '@emotion/css'; import { css } from '@emotion/css';
export const HEIGHT_ANIMATION_DURATION = 250; export const HEIGHT_ANIMATION_DURATION = 250;
export const useCardPanelStyles = () => { export const useCardPanelStyles = () => {
const { euiTheme, colorMode } = useEuiTheme(); const { euiTheme } = useEuiTheme();
const isDarkMode = useKibanaIsDarkMode();
const successBackgroundColor = euiTheme.colors.backgroundBaseSuccess; const successBackgroundColor = euiTheme.colors.backgroundBaseSuccess;
const isDarkMode = colorMode === COLOR_MODES_STANDARD.dark;
const darkModeStyles = useDarkPanelStyles(isDarkMode); const darkModeStyles = useDarkPanelStyles(isDarkMode);
return css` return css`

View file

@ -13,9 +13,9 @@ import type { OnboardingCardId } from '../../constants';
import { TestProviders } from '../../../common/mock/test_providers'; import { TestProviders } from '../../../common/mock/test_providers';
const mockUseDarkMode = jest.fn(() => false); const mockUseDarkMode = jest.fn(() => false);
jest.mock('@kbn/kibana-react-plugin/public', () => ({ jest.mock('@kbn/react-kibana-context-theme', () => ({
...jest.requireActual('@kbn/kibana-react-plugin/public'), ...jest.requireActual('@kbn/react-kibana-context-theme'),
useDarkMode: () => mockUseDarkMode(), useKibanaIsDarkMode: () => mockUseDarkMode(),
})); }));
jest.mock('@elastic/eui', () => ({ jest.mock('@elastic/eui', () => ({

View file

@ -17,7 +17,7 @@ import {
EuiTitle, EuiTitle,
} from '@elastic/eui'; } from '@elastic/eui';
import classnames from 'classnames'; import classnames from 'classnames';
import { useDarkMode } from '@kbn/kibana-react-plugin/public'; import { useKibanaIsDarkMode } from '@kbn/react-kibana-context-theme';
import type { OnboardingCardId } from '../../constants'; import type { OnboardingCardId } from '../../constants';
import type { CheckCompleteResult, CardBadge } from '../../types'; import type { CheckCompleteResult, CardBadge } from '../../types';
import { CARD_COMPLETE_BADGE, EXPAND_CARD_BUTTON_LABEL } from './translations'; import { CARD_COMPLETE_BADGE, EXPAND_CARD_BUTTON_LABEL } from './translations';
@ -55,7 +55,7 @@ export const OnboardingCardPanel = React.memo<PropsWithChildren<OnboardingCardPa
'onboardingCardPanel-expanded': isExpanded, 'onboardingCardPanel-expanded': isExpanded,
'onboardingCardPanel-completed': isComplete, 'onboardingCardPanel-completed': isComplete,
}); });
const isDarkMode = useDarkMode(); const isDarkMode = useKibanaIsDarkMode();
const iconType = useMemo( const iconType = useMemo(
() => (iconDark && isDarkMode ? iconDark : icon), () => (iconDark && isDarkMode ? iconDark : icon),
[isDarkMode, iconDark, icon] [isDarkMode, iconDark, icon]

View file

@ -5,12 +5,14 @@
* 2.0. * 2.0.
*/ */
import { COLOR_MODES_STANDARD, useEuiTheme } from '@elastic/eui'; import { useEuiTheme } from '@elastic/eui';
import { css } from '@emotion/css'; import { css } from '@emotion/css';
import { useKibanaIsDarkMode } from '@kbn/react-kibana-context-theme';
export const useCardStyles = () => { export const useCardStyles = () => {
const { euiTheme, colorMode } = useEuiTheme(); const { euiTheme } = useEuiTheme();
const isDarkMode = colorMode === COLOR_MODES_STANDARD.dark; const isDarkMode = useKibanaIsDarkMode();
return css` return css`
min-width: 315px; min-width: 315px;

View file

@ -6,16 +6,9 @@
*/ */
import React, { useMemo } from 'react'; import React, { useMemo } from 'react';
import { import { EuiFlexGroup, EuiFlexItem, EuiImage, EuiSpacer, EuiText, EuiTitle } from '@elastic/eui';
COLOR_MODES_STANDARD,
EuiFlexGroup, import { useKibanaIsDarkMode } from '@kbn/react-kibana-context-theme';
EuiFlexItem,
EuiImage,
EuiSpacer,
EuiText,
EuiTitle,
useEuiTheme,
} from '@elastic/eui';
import { useCurrentUser } from '../../../common/lib/kibana/hooks'; import { useCurrentUser } from '../../../common/lib/kibana/hooks';
import { OnboardingHeaderTopicSelector } from './onboarding_header_topic_selector'; import { OnboardingHeaderTopicSelector } from './onboarding_header_topic_selector';
import { useOnboardingHeaderStyles } from './onboarding_header.styles'; import { useOnboardingHeaderStyles } from './onboarding_header.styles';
@ -30,8 +23,7 @@ import { useKibana } from '../../../common/lib/kibana';
export const OnboardingHeader = React.memo(() => { export const OnboardingHeader = React.memo(() => {
const currentUser = useCurrentUser(); const currentUser = useCurrentUser();
const { colorMode } = useEuiTheme(); const isDarkMode = useKibanaIsDarkMode();
const isDarkMode = colorMode === COLOR_MODES_STANDARD.dark;
const styles = useOnboardingHeaderStyles(); const styles = useOnboardingHeaderStyles();

View file

@ -10,6 +10,7 @@ import { euiDarkVars as darkTheme, euiLightVars as lightTheme } from '@kbn/ui-th
import { getOr } from 'lodash/fp'; import { getOr } from 'lodash/fp';
import React, { useCallback, useMemo } from 'react'; import React, { useCallback, useMemo } from 'react';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { useKibanaIsDarkMode } from '@kbn/react-kibana-context-theme';
import { RiskScoreHeaderTitle } from '../../../entity_analytics/components/risk_score_header_title'; import { RiskScoreHeaderTitle } from '../../../entity_analytics/components/risk_score_header_title';
import { useGlobalTime } from '../../../common/containers/use_global_time'; import { useGlobalTime } from '../../../common/containers/use_global_time';
import { useQueryInspector } from '../../../common/components/page/manage_query'; import { useQueryInspector } from '../../../common/components/page/manage_query';
@ -18,7 +19,6 @@ import { useRiskScore } from '../../../entity_analytics/api/hooks/use_risk_score
import { buildHostNamesFilter, type HostItem } from '../../../../common/search_strategy'; import { buildHostNamesFilter, type HostItem } from '../../../../common/search_strategy';
import { EntityType } from '../../../../common/entity_analytics/types'; import { EntityType } from '../../../../common/entity_analytics/types';
import type { DescriptionList } from '../../../../common/utility_types'; import type { DescriptionList } from '../../../../common/utility_types';
import { useDarkMode } from '../../../common/lib/kibana';
import { getEmptyTagValue } from '../../../common/components/empty_value'; import { getEmptyTagValue } from '../../../common/components/empty_value';
import { hostIdRenderer } from '../../../timelines/components/field_renderers/field_renderers'; import { hostIdRenderer } from '../../../timelines/components/field_renderers/field_renderers';
import { DefaultFieldRenderer } from '../../../timelines/components/field_renderers/default_renderer'; import { DefaultFieldRenderer } from '../../../timelines/components/field_renderers/default_renderer';
@ -85,7 +85,7 @@ export const HostOverview = React.memo<HostSummaryProps>(
}) => { }) => {
const capabilities = useMlCapabilities(); const capabilities = useMlCapabilities();
const userPermissions = hasMlUserPermissions(capabilities); const userPermissions = hasMlUserPermissions(capabilities);
const darkMode = useDarkMode(); const darkMode = useKibanaIsDarkMode();
const filterQuery = useMemo( const filterQuery = useMemo(
() => (hostName ? buildHostNamesFilter([hostName]) : undefined), () => (hostName ? buildHostNamesFilter([hostName]) : undefined),
[hostName] [hostName]

View file

@ -10,6 +10,7 @@ import { euiDarkVars as darkTheme, euiLightVars as lightTheme } from '@kbn/ui-th
import { getOr } from 'lodash/fp'; import { getOr } from 'lodash/fp';
import React, { useCallback, useMemo } from 'react'; import React, { useCallback, useMemo } from 'react';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { useKibanaIsDarkMode } from '@kbn/react-kibana-context-theme';
import { buildUserNamesFilter } from '../../../../common/search_strategy'; import { buildUserNamesFilter } from '../../../../common/search_strategy';
import { RiskScoreHeaderTitle } from '../../../entity_analytics/components/risk_score_header_title'; import { RiskScoreHeaderTitle } from '../../../entity_analytics/components/risk_score_header_title';
import { useGlobalTime } from '../../../common/containers/use_global_time'; import { useGlobalTime } from '../../../common/containers/use_global_time';
@ -18,7 +19,6 @@ import { useQueryInspector } from '../../../common/components/page/manage_query'
import { useRiskScore } from '../../../entity_analytics/api/hooks/use_risk_score'; import { useRiskScore } from '../../../entity_analytics/api/hooks/use_risk_score';
import { EntityType } from '../../../../common/entity_analytics/types'; import { EntityType } from '../../../../common/entity_analytics/types';
import type { DescriptionList } from '../../../../common/utility_types'; import type { DescriptionList } from '../../../../common/utility_types';
import { useDarkMode } from '../../../common/lib/kibana';
import { getEmptyTagValue } from '../../../common/components/empty_value'; import { getEmptyTagValue } from '../../../common/components/empty_value';
import { DefaultFieldRenderer } from '../../../timelines/components/field_renderers/default_renderer'; import { DefaultFieldRenderer } from '../../../timelines/components/field_renderers/default_renderer';
import { import {
@ -86,7 +86,7 @@ export const UserOverview = React.memo<UserSummaryProps>(
}) => { }) => {
const capabilities = useMlCapabilities(); const capabilities = useMlCapabilities();
const userPermissions = hasMlUserPermissions(capabilities); const userPermissions = hasMlUserPermissions(capabilities);
const darkMode = useDarkMode(); const darkMode = useKibanaIsDarkMode();
const filterQuery = useMemo( const filterQuery = useMemo(
() => (userName ? buildUserNamesFilter([userName]) : undefined), () => (userName ? buildUserNamesFilter([userName]) : undefined),
[userName] [userName]

View file

@ -8,7 +8,7 @@
import React, { memo } from 'react'; import React, { memo } from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
import { i18n } from '@kbn/i18n'; import { i18n } from '@kbn/i18n';
import { useDarkMode } from '@kbn/kibana-react-plugin/public'; import { useKibanaIsDarkMode } from '@kbn/react-kibana-context-theme';
import { useSymbolIDs } from './use_symbol_ids'; import { useSymbolIDs } from './use_symbol_ids';
import { usePaintServerIDs } from './use_paint_server_ids'; import { usePaintServerIDs } from './use_paint_server_ids';
@ -435,7 +435,7 @@ const SymbolsAndShapes = memo(({ id, isDarkMode }: { id: string; isDarkMode: boo
*/ */
// eslint-disable-next-line react/display-name // eslint-disable-next-line react/display-name
export const SymbolDefinitions = memo(({ id }: { id: string }) => { export const SymbolDefinitions = memo(({ id }: { id: string }) => {
const isDarkMode = useDarkMode(); const isDarkMode = useKibanaIsDarkMode();
return ( return (
<HiddenSVG> <HiddenSVG>
<defs> <defs>

View file

@ -5,13 +5,13 @@
* 2.0. * 2.0.
*/ */
import React from 'react'; import React from 'react';
import { useDarkMode } from '@kbn/kibana-react-plugin/public';
import { EuiIcon, type EuiIconProps } from '@elastic/eui'; import { EuiIcon, type EuiIconProps } from '@elastic/eui';
import { useKibanaIsDarkMode } from '@kbn/react-kibana-context-theme';
import SiemMigrationsIconSVG from './siem_migrations.svg'; import SiemMigrationsIconSVG from './siem_migrations.svg';
import SiemMigrationsIconDarkSVG from './siem_migrations_dark.svg'; import SiemMigrationsIconDarkSVG from './siem_migrations_dark.svg';
export const SiemMigrationsIcon = React.memo<Omit<EuiIconProps, 'type'>>((props) => { export const SiemMigrationsIcon = React.memo<Omit<EuiIconProps, 'type'>>((props) => {
const isDark = useDarkMode(); const isDark = useKibanaIsDarkMode();
if (isDark) { if (isDark) {
return <EuiIcon type={SiemMigrationsIconDarkSVG} {...props} />; return <EuiIcon type={SiemMigrationsIconDarkSVG} {...props} />;
} }

View file

@ -22,9 +22,9 @@ import {
EuiBadge, EuiBadge,
type EuiBasicTableColumn, type EuiBasicTableColumn,
useEuiTheme, useEuiTheme,
COLOR_MODES_STANDARD,
} from '@elastic/eui'; } from '@elastic/eui';
import { Chart, BarSeries, Settings, ScaleType } from '@elastic/charts'; import { Chart, BarSeries, Settings, ScaleType } from '@elastic/charts';
import { useKibanaIsDarkMode } from '@kbn/react-kibana-context-theme';
import { SecurityPageName } from '@kbn/security-solution-navigation'; import { SecurityPageName } from '@kbn/security-solution-navigation';
import { AssistantIcon } from '@kbn/ai-assistant-icon'; import { AssistantIcon } from '@kbn/ai-assistant-icon';
import { useElasticChartsTheme } from '@kbn/charts-theme'; import { useElasticChartsTheme } from '@kbn/charts-theme';
@ -52,8 +52,8 @@ const headerStyle = css`
`; `;
const useCompleteBadgeStyles = () => { const useCompleteBadgeStyles = () => {
const { euiTheme, colorMode } = useEuiTheme(); const { euiTheme } = useEuiTheme();
const isDarkMode = colorMode === COLOR_MODES_STANDARD.dark; const isDarkMode = useKibanaIsDarkMode();
return css` return css`
background-color: ${isDarkMode background-color: ${isDarkMode
? euiTheme.colors.success ? euiTheme.colors.success

View file

@ -249,5 +249,6 @@
"@kbn/actions-types", "@kbn/actions-types",
"@kbn/triggers-actions-ui-types", "@kbn/triggers-actions-ui-types",
"@kbn/unified-histogram", "@kbn/unified-histogram",
"@kbn/react-kibana-context-theme",
] ]
} }

View file

@ -8,7 +8,7 @@
import { EuiDataGridCellValueElementProps } from '@elastic/eui'; import { EuiDataGridCellValueElementProps } from '@elastic/eui';
import React, { useContext, useEffect } from 'react'; import React, { useContext, useEffect } from 'react';
import { euiDarkVars as themeDark, euiLightVars as themeLight } from '@kbn/ui-theme'; import { euiDarkVars as themeDark, euiLightVars as themeLight } from '@kbn/ui-theme';
import { useDarkMode } from '@kbn/kibana-react-plugin/public'; import { useKibanaIsDarkMode } from '@kbn/react-kibana-context-theme';
import { useStyles } from './styles'; import { useStyles } from './styles';
import { Indicator } from '../../../../../common/types/indicator'; import { Indicator } from '../../../../../common/types/indicator';
import { IndicatorFieldValue } from '../common/field_value'; import { IndicatorFieldValue } from '../common/field_value';
@ -25,7 +25,7 @@ export const cellRendererFactory = (from: number) => {
throw new Error('this can only be used inside indicators table'); throw new Error('this can only be used inside indicators table');
} }
const darkMode = useDarkMode(); const darkMode = useKibanaIsDarkMode();
const { indicators, expanded } = indicatorsTableContext; const { indicators, expanded } = indicatorsTableContext;

View file

@ -35,7 +35,8 @@
"@kbn/core-ui-settings-browser", "@kbn/core-ui-settings-browser",
"@kbn/search-types", "@kbn/search-types",
"@kbn/response-ops-alerts-fields-browser", "@kbn/response-ops-alerts-fields-browser",
"@kbn/charts-theme" "@kbn/charts-theme",
"@kbn/react-kibana-context-theme"
], ],
"exclude": ["target/**/*"] "exclude": ["target/**/*"]
} }