mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
Provide React Context composition; new Kibana Context, consolidate/deprecate.
This commit is contained in:
parent
663066b3f9
commit
815226a926
62 changed files with 539 additions and 497 deletions
2
.github/CODEOWNERS
vendored
2
.github/CODEOWNERS
vendored
|
@ -511,6 +511,8 @@ src/plugins/presentation_util @elastic/kibana-presentation
|
|||
x-pack/plugins/profiling @elastic/profiling-ui
|
||||
x-pack/packages/kbn-random-sampling @elastic/kibana-visualizations
|
||||
packages/kbn-react-field @elastic/kibana-data-discovery
|
||||
packages/react/kibana_context @elastic/appex-sharedux
|
||||
packages/react/kibana_mount @elastic/appex-sharedux
|
||||
x-pack/plugins/remote_clusters @elastic/platform-deployment-management
|
||||
test/plugin_functional/plugins/rendering_plugin @elastic/kibana-core
|
||||
packages/kbn-repo-file-maps @elastic/kibana-operations
|
||||
|
|
|
@ -76,6 +76,7 @@
|
|||
"newsfeed": "src/plugins/newsfeed",
|
||||
"presentationUtil": "src/plugins/presentation_util",
|
||||
"randomSampling": "x-pack/packages/kbn-random-sampling",
|
||||
"reactPackages": "packages/react",
|
||||
"textBasedEditor": "packages/kbn-text-based-editor",
|
||||
"reporting": "packages/kbn-reporting/common",
|
||||
"savedObjects": "src/plugins/saved_objects",
|
||||
|
|
|
@ -519,6 +519,8 @@
|
|||
"@kbn/profiling-plugin": "link:x-pack/plugins/profiling",
|
||||
"@kbn/random-sampling": "link:x-pack/packages/kbn-random-sampling",
|
||||
"@kbn/react-field": "link:packages/kbn-react-field",
|
||||
"@kbn/react-kibana-context": "link:packages/react/kibana_context",
|
||||
"@kbn/react-kibana-mount": "link:packages/react/kibana_mount",
|
||||
"@kbn/remote-clusters-plugin": "link:x-pack/plugins/remote_clusters",
|
||||
"@kbn/rendering-plugin": "link:test/plugin_functional/plugins/rendering_plugin",
|
||||
"@kbn/repo-info": "link:packages/kbn-repo-info",
|
||||
|
|
|
@ -68,6 +68,10 @@ Array [
|
|||
/>
|
||||
</EuiFlyout>
|
||||
</CoreThemeProvider>,
|
||||
"globalStyles": false,
|
||||
"theme$": Observable {
|
||||
"_subscribe": [Function],
|
||||
},
|
||||
},
|
||||
Object {},
|
||||
],
|
||||
|
@ -141,6 +145,10 @@ Array [
|
|||
/>
|
||||
</EuiFlyout>
|
||||
</CoreThemeProvider>,
|
||||
"globalStyles": false,
|
||||
"theme$": Observable {
|
||||
"_subscribe": [Function],
|
||||
},
|
||||
},
|
||||
Object {},
|
||||
],
|
||||
|
|
|
@ -35,6 +35,10 @@ Array [
|
|||
/>
|
||||
</EuiModal>
|
||||
</CoreThemeProvider>,
|
||||
"globalStyles": false,
|
||||
"theme$": Observable {
|
||||
"_subscribe": [Function],
|
||||
},
|
||||
},
|
||||
Object {},
|
||||
],
|
||||
|
@ -194,6 +198,10 @@ Array [
|
|||
/>
|
||||
</EuiModal>
|
||||
</CoreThemeProvider>,
|
||||
"globalStyles": false,
|
||||
"theme$": Observable {
|
||||
"_subscribe": [Function],
|
||||
},
|
||||
},
|
||||
Object {},
|
||||
],
|
||||
|
@ -219,6 +227,10 @@ Array [
|
|||
/>
|
||||
</EuiConfirmModal>
|
||||
</CoreThemeProvider>,
|
||||
"globalStyles": false,
|
||||
"theme$": Observable {
|
||||
"_subscribe": [Function],
|
||||
},
|
||||
},
|
||||
Object {},
|
||||
],
|
||||
|
@ -394,6 +406,10 @@ Array [
|
|||
/>
|
||||
</EuiModal>
|
||||
</CoreThemeProvider>,
|
||||
"globalStyles": false,
|
||||
"theme$": Observable {
|
||||
"_subscribe": [Function],
|
||||
},
|
||||
},
|
||||
Object {},
|
||||
],
|
||||
|
@ -419,6 +435,10 @@ Array [
|
|||
/>
|
||||
</EuiConfirmModal>
|
||||
</CoreThemeProvider>,
|
||||
"globalStyles": false,
|
||||
"theme$": Observable {
|
||||
"_subscribe": [Function],
|
||||
},
|
||||
},
|
||||
Object {},
|
||||
],
|
||||
|
@ -441,6 +461,10 @@ Array [
|
|||
Some message
|
||||
</EuiConfirmModal>
|
||||
</CoreThemeProvider>,
|
||||
"globalStyles": false,
|
||||
"theme$": Observable {
|
||||
"_subscribe": [Function],
|
||||
},
|
||||
},
|
||||
Object {},
|
||||
],
|
||||
|
@ -557,6 +581,10 @@ Array [
|
|||
/>
|
||||
</EuiModal>
|
||||
</CoreThemeProvider>,
|
||||
"globalStyles": false,
|
||||
"theme$": Observable {
|
||||
"_subscribe": [Function],
|
||||
},
|
||||
},
|
||||
Object {},
|
||||
],
|
||||
|
@ -582,6 +610,10 @@ Array [
|
|||
/>
|
||||
</EuiConfirmModal>
|
||||
</CoreThemeProvider>,
|
||||
"globalStyles": false,
|
||||
"theme$": Observable {
|
||||
"_subscribe": [Function],
|
||||
},
|
||||
},
|
||||
Object {},
|
||||
],
|
||||
|
@ -604,6 +636,10 @@ Array [
|
|||
Some message
|
||||
</EuiConfirmModal>
|
||||
</CoreThemeProvider>,
|
||||
"globalStyles": false,
|
||||
"theme$": Observable {
|
||||
"_subscribe": [Function],
|
||||
},
|
||||
},
|
||||
Object {},
|
||||
],
|
||||
|
@ -725,6 +761,10 @@ Array [
|
|||
/>
|
||||
</EuiModal>
|
||||
</CoreThemeProvider>,
|
||||
"globalStyles": false,
|
||||
"theme$": Observable {
|
||||
"_subscribe": [Function],
|
||||
},
|
||||
},
|
||||
Object {},
|
||||
],
|
||||
|
@ -750,6 +790,10 @@ Array [
|
|||
/>
|
||||
</EuiConfirmModal>
|
||||
</CoreThemeProvider>,
|
||||
"globalStyles": false,
|
||||
"theme$": Observable {
|
||||
"_subscribe": [Function],
|
||||
},
|
||||
},
|
||||
Object {},
|
||||
],
|
||||
|
@ -772,6 +816,10 @@ Array [
|
|||
Some message
|
||||
</EuiConfirmModal>
|
||||
</CoreThemeProvider>,
|
||||
"globalStyles": false,
|
||||
"theme$": Observable {
|
||||
"_subscribe": [Function],
|
||||
},
|
||||
},
|
||||
Object {},
|
||||
],
|
||||
|
@ -888,6 +936,10 @@ Array [
|
|||
/>
|
||||
</EuiModal>
|
||||
</CoreThemeProvider>,
|
||||
"globalStyles": false,
|
||||
"theme$": Observable {
|
||||
"_subscribe": [Function],
|
||||
},
|
||||
},
|
||||
Object {},
|
||||
],
|
||||
|
@ -913,6 +965,10 @@ Array [
|
|||
/>
|
||||
</EuiConfirmModal>
|
||||
</CoreThemeProvider>,
|
||||
"globalStyles": false,
|
||||
"theme$": Observable {
|
||||
"_subscribe": [Function],
|
||||
},
|
||||
},
|
||||
Object {},
|
||||
],
|
||||
|
@ -935,6 +991,10 @@ Array [
|
|||
Some message
|
||||
</EuiConfirmModal>
|
||||
</CoreThemeProvider>,
|
||||
"globalStyles": false,
|
||||
"theme$": Observable {
|
||||
"_subscribe": [Function],
|
||||
},
|
||||
},
|
||||
Object {},
|
||||
],
|
||||
|
@ -1118,6 +1178,10 @@ Array [
|
|||
/>
|
||||
</EuiModal>
|
||||
</CoreThemeProvider>,
|
||||
"globalStyles": false,
|
||||
"theme$": Observable {
|
||||
"_subscribe": [Function],
|
||||
},
|
||||
},
|
||||
Object {},
|
||||
],
|
||||
|
@ -1191,6 +1255,10 @@ Array [
|
|||
/>
|
||||
</EuiModal>
|
||||
</CoreThemeProvider>,
|
||||
"globalStyles": false,
|
||||
"theme$": Observable {
|
||||
"_subscribe": [Function],
|
||||
},
|
||||
},
|
||||
Object {},
|
||||
],
|
||||
|
@ -1269,6 +1337,10 @@ Array [
|
|||
/>
|
||||
</EuiModal>
|
||||
</CoreThemeProvider>,
|
||||
"globalStyles": false,
|
||||
"theme$": Observable {
|
||||
"_subscribe": [Function],
|
||||
},
|
||||
},
|
||||
Object {},
|
||||
],
|
||||
|
@ -1342,6 +1414,10 @@ Array [
|
|||
/>
|
||||
</EuiModal>
|
||||
</CoreThemeProvider>,
|
||||
"globalStyles": false,
|
||||
"theme$": Observable {
|
||||
"_subscribe": [Function],
|
||||
},
|
||||
},
|
||||
Object {},
|
||||
],
|
||||
|
|
|
@ -1,19 +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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { convertCoreTheme } from './convert_core_theme';
|
||||
|
||||
describe('convertCoreTheme', () => {
|
||||
it('returns the correct `colorMode` when `darkMode` is enabled', () => {
|
||||
expect(convertCoreTheme({ darkMode: true }).colorMode).toEqual('DARK');
|
||||
});
|
||||
|
||||
it('returns the correct `colorMode` when `darkMode` is disabled', () => {
|
||||
expect(convertCoreTheme({ darkMode: false }).colorMode).toEqual('LIGHT');
|
||||
});
|
||||
});
|
|
@ -1,25 +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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { COLOR_MODES_STANDARD } from '@elastic/eui';
|
||||
import type { EuiThemeSystem, EuiThemeColorModeStandard } from '@elastic/eui';
|
||||
import type { CoreTheme } from '@kbn/core-theme-browser';
|
||||
|
||||
/** @internal */
|
||||
export interface EuiTheme {
|
||||
colorMode: EuiThemeColorModeStandard;
|
||||
euiThemeSystem?: EuiThemeSystem;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export const convertCoreTheme = (coreTheme: CoreTheme): EuiTheme => {
|
||||
const { darkMode } = coreTheme;
|
||||
return {
|
||||
colorMode: darkMode ? COLOR_MODES_STANDARD.dark : COLOR_MODES_STANDARD.light,
|
||||
};
|
||||
};
|
|
@ -6,35 +6,31 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import React, { FC } from 'react';
|
||||
import type { ThemeServiceStart } from '@kbn/core-theme-browser';
|
||||
import React, { FC, useMemo } from 'react';
|
||||
import type { I18nStart } from '@kbn/core-i18n-browser';
|
||||
import { CoreThemeProvider } from './core_theme_provider';
|
||||
import { composeProviders, KibanaThemeProvider } from '@kbn/react-kibana-context';
|
||||
import { ThemeServiceStart } from '@kbn/core-theme-browser/src/types';
|
||||
|
||||
interface CoreContextProviderProps {
|
||||
theme: ThemeServiceStart;
|
||||
i18n: I18nStart;
|
||||
theme: ThemeServiceStart;
|
||||
globalStyles?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* utility component exposing all the context providers required by core when integrating with react
|
||||
* Utility component exposing all the context providers required by core when integrating with React.
|
||||
**/
|
||||
export const CoreContextProvider: FC<CoreContextProviderProps> = ({
|
||||
i18n,
|
||||
theme,
|
||||
children,
|
||||
theme,
|
||||
globalStyles = false,
|
||||
}) => {
|
||||
// `globalStyles` and `utilityClasses` default values are inverted from that of `EuiProvider`.
|
||||
// Default to `false` (does not add EUI global styles) because more instances use that value.
|
||||
// A value of `true` (does add EUI global styles) will have `EuiProvider` use its default value.
|
||||
const includeGlobalStyles = globalStyles === false ? false : undefined;
|
||||
const Provider = useMemo(() => composeProviders([i18n.Context, KibanaThemeProvider]), [i18n]);
|
||||
|
||||
return (
|
||||
<i18n.Context>
|
||||
<CoreThemeProvider theme$={theme.theme$} globalStyles={includeGlobalStyles}>
|
||||
{children}
|
||||
</CoreThemeProvider>
|
||||
</i18n.Context>
|
||||
<Provider theme$={theme.theme$} {...{ globalStyles }}>
|
||||
{children}
|
||||
</Provider>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -6,55 +6,12 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import React, { FC, useMemo } from 'react';
|
||||
import { Observable } from 'rxjs';
|
||||
import useObservable from 'react-use/lib/useObservable';
|
||||
import createCache from '@emotion/cache';
|
||||
import { EuiProvider } from '@elastic/eui';
|
||||
import { EUI_STYLES_GLOBAL } from '@kbn/core-base-common';
|
||||
import type { CoreTheme } from '@kbn/core-theme-browser';
|
||||
import { convertCoreTheme } from './convert_core_theme';
|
||||
|
||||
const defaultTheme: CoreTheme = {
|
||||
darkMode: false,
|
||||
};
|
||||
|
||||
interface CoreThemeProviderProps {
|
||||
theme$: Observable<CoreTheme>;
|
||||
globalStyles?: boolean;
|
||||
}
|
||||
|
||||
const globalCache = createCache({
|
||||
key: 'eui',
|
||||
container: document.querySelector(`meta[name="${EUI_STYLES_GLOBAL}"]`) as HTMLElement,
|
||||
});
|
||||
const emotionCache = createCache({
|
||||
key: 'css',
|
||||
container: document.querySelector(`meta[name="emotion"]`) as HTMLElement,
|
||||
});
|
||||
emotionCache.compat = true;
|
||||
import { KibanaThemeProvider } from '@kbn/react-kibana-context';
|
||||
|
||||
/**
|
||||
* Wrapper around `EuiProvider` converting (and exposing) core's theme to EUI theme.
|
||||
* @internal Only meant to be used within core for internal usages of EUI/React
|
||||
* @deprecated use `KibanaThemeProvider` from `@kbn/react-kibana-context
|
||||
*/
|
||||
export const CoreThemeProvider: FC<CoreThemeProviderProps> = ({
|
||||
theme$,
|
||||
children,
|
||||
globalStyles,
|
||||
}) => {
|
||||
const coreTheme = useObservable(theme$, defaultTheme);
|
||||
const euiTheme = useMemo(() => convertCoreTheme(coreTheme), [coreTheme]);
|
||||
const includeGlobalStyles = globalStyles === false ? false : undefined;
|
||||
return (
|
||||
<EuiProvider
|
||||
globalStyles={includeGlobalStyles}
|
||||
utilityClasses={includeGlobalStyles}
|
||||
colorMode={euiTheme.colorMode}
|
||||
theme={euiTheme.euiThemeSystem}
|
||||
cache={{ default: emotionCache, global: globalCache }}
|
||||
>
|
||||
{children}
|
||||
</EuiProvider>
|
||||
);
|
||||
};
|
||||
export const CoreThemeProvider = KibanaThemeProvider;
|
||||
CoreThemeProvider.displayName = 'CoreThemeProvider';
|
||||
|
|
|
@ -13,12 +13,12 @@
|
|||
"**/*.tsx",
|
||||
],
|
||||
"kbn_references": [
|
||||
"@kbn/core-base-common",
|
||||
"@kbn/core-injected-metadata-browser-internal",
|
||||
"@kbn/core-theme-browser",
|
||||
"@kbn/core-i18n-browser",
|
||||
"@kbn/core-injected-metadata-browser-mocks",
|
||||
"@kbn/test-jest-helpers",
|
||||
"@kbn/react-kibana-context",
|
||||
"@kbn/core-i18n-browser",
|
||||
],
|
||||
"exclude": [
|
||||
"target/**/*",
|
||||
|
|
|
@ -6,34 +6,28 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { EuiProvider } from '@elastic/eui';
|
||||
import createCache from '@emotion/cache';
|
||||
import React, { useEffect } from 'react';
|
||||
import type { DecoratorFn } from '@storybook/react';
|
||||
import { KibanaContextProvider } from '@kbn/react-kibana-context';
|
||||
|
||||
import 'core_styles';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { CoreTheme } from '@kbn/core-theme-browser';
|
||||
|
||||
const theme$ = new BehaviorSubject<CoreTheme>({ darkMode: false });
|
||||
|
||||
/**
|
||||
* Storybook decorator using the EUI provider. Uses the value from
|
||||
* `globals` provided by the Storybook theme switcher.
|
||||
* Storybook decorator using the `KibanaContextProvider`. Uses the value from
|
||||
* `globals` provided by the Storybook theme switcher to set the `colorMode`.
|
||||
*/
|
||||
const EuiProviderDecorator: DecoratorFn = (storyFn, { globals }) => {
|
||||
const KibanaContextDecorator: DecoratorFn = (storyFn, { globals }) => {
|
||||
const colorMode = globals.euiTheme === 'v8.dark' ? 'dark' : 'light';
|
||||
const globalCache = createCache({
|
||||
key: 'eui',
|
||||
container: document.querySelector(`meta[name="eui-global"]`) as HTMLElement,
|
||||
});
|
||||
const emotionCache = createCache({
|
||||
key: 'css',
|
||||
container: document.querySelector(`meta[name="emotion"]`) as HTMLElement,
|
||||
});
|
||||
emotionCache.compat = true;
|
||||
|
||||
return (
|
||||
<EuiProvider colorMode={colorMode} cache={{ default: emotionCache, global: globalCache }}>
|
||||
{storyFn()}
|
||||
</EuiProvider>
|
||||
);
|
||||
useEffect(() => {
|
||||
theme$.next({ darkMode: colorMode === 'dark' });
|
||||
}, [colorMode]);
|
||||
|
||||
return <KibanaContextProvider {...{ theme$ }}>{storyFn()}</KibanaContextProvider>;
|
||||
};
|
||||
|
||||
export const decorators = [EuiProviderDecorator];
|
||||
export const decorators = [KibanaContextDecorator];
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
"@kbn/ui-shared-deps-src",
|
||||
"@kbn/repo-info",
|
||||
"@kbn/dev-cli-runner",
|
||||
"@kbn/react-kibana-context",
|
||||
"@kbn/core-theme-browser",
|
||||
],
|
||||
"exclude": [
|
||||
"target/**/*",
|
||||
|
|
|
@ -76,7 +76,7 @@ module.exports = {
|
|||
|
||||
// A list of paths to snapshot serializer modules Jest should use for snapshot testing
|
||||
snapshotSerializers: [
|
||||
'<rootDir>/src/plugins/kibana_react/public/util/test_helpers/react_mount_serializer.ts',
|
||||
'<rootDir>/packages/react/kibana_mount/test_helpers/react_mount_serializer.ts',
|
||||
'enzyme-to-json/serializer',
|
||||
'<rootDir>/packages/kbn-test/src/jest/setup/emotion.js',
|
||||
],
|
||||
|
|
1
packages/react/README.mdx
Normal file
1
packages/react/README.mdx
Normal file
|
@ -0,0 +1 @@
|
|||
TODO
|
3
packages/react/kibana_context/README.md
Normal file
3
packages/react/kibana_context/README.md
Normal file
|
@ -0,0 +1,3 @@
|
|||
# @kbn/react-kibana-context
|
||||
|
||||
Empty package generated by @kbn/generate
|
15
packages/react/kibana_context/index.ts
Normal file
15
packages/react/kibana_context/index.ts
Normal file
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
export { KibanaThemeProvider, wrapWithTheme } from './theme';
|
||||
export type { KibanaThemeProviderProps } from './theme';
|
||||
|
||||
export { KibanaContextProvider, withKibanaContextProvider } from './provider';
|
||||
export type { KibanaContextProviderProps } from './provider';
|
||||
|
||||
export { composeProviders } from './utils';
|
13
packages/react/kibana_context/jest.config.js
Normal file
13
packages/react/kibana_context/jest.config.js
Normal file
|
@ -0,0 +1,13 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
preset: '@kbn/test',
|
||||
rootDir: '../../..',
|
||||
roots: ['<rootDir>/packages/react/kibana_context'],
|
||||
};
|
5
packages/react/kibana_context/kibana.jsonc
Normal file
5
packages/react/kibana_context/kibana.jsonc
Normal file
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"type": "shared-common",
|
||||
"id": "@kbn/react-kibana-context",
|
||||
"owner": "@elastic/appex-sharedux"
|
||||
}
|
6
packages/react/kibana_context/package.json
Normal file
6
packages/react/kibana_context/package.json
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"name": "@kbn/react-kibana-context",
|
||||
"private": true,
|
||||
"version": "1.0.0",
|
||||
"license": "SSPL-1.0 OR Elastic License 2.0"
|
||||
}
|
10
packages/react/kibana_context/provider/index.tsx
Normal file
10
packages/react/kibana_context/provider/index.tsx
Normal file
|
@ -0,0 +1,10 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
export { KibanaContextProvider, withKibanaContextProvider } from './kibana_provider';
|
||||
export type { KibanaContextProviderProps } from './kibana_provider';
|
20
packages/react/kibana_context/provider/kibana_provider.tsx
Normal file
20
packages/react/kibana_context/provider/kibana_provider.tsx
Normal file
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { I18nProvider } from '@kbn/i18n-react';
|
||||
import { ComponentPropsWithoutRef } from 'react';
|
||||
|
||||
import { KibanaThemeProvider } from '../theme';
|
||||
import { composeProviders } from '../utils';
|
||||
import { ComposeProvidersFn } from '../utils/compose';
|
||||
|
||||
export const KibanaContextProvider = composeProviders([I18nProvider, KibanaThemeProvider]);
|
||||
export type KibanaContextProviderProps = ComponentPropsWithoutRef<typeof KibanaContextProvider>;
|
||||
|
||||
export const withKibanaContextProvider: ComposeProvidersFn = (providers) =>
|
||||
composeProviders([KibanaContextProvider, ...providers]);
|
|
@ -6,7 +6,7 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { getColorMode } from './utils';
|
||||
import { getColorMode } from './color_mode';
|
||||
|
||||
describe('getColorMode', () => {
|
||||
it('returns the correct `colorMode` when `darkMode` is enabled', () => {
|
|
@ -8,12 +8,8 @@
|
|||
|
||||
import { COLOR_MODES_STANDARD } from '@elastic/eui';
|
||||
import type { EuiThemeColorModeStandard } from '@elastic/eui';
|
||||
import type { Theme } from '../types';
|
||||
|
||||
import type { CoreTheme } from '@kbn/core/public';
|
||||
|
||||
/**
|
||||
* Copied from the `kibana_react` plugin, to avoid cyclical dependency
|
||||
*/
|
||||
export const getColorMode = (theme: CoreTheme): EuiThemeColorModeStandard => {
|
||||
export const getColorMode = (theme: Theme): EuiThemeColorModeStandard | undefined => {
|
||||
return theme.darkMode ? COLOR_MODES_STANDARD.dark : COLOR_MODES_STANDARD.light;
|
||||
};
|
11
packages/react/kibana_context/theme/index.ts
Normal file
11
packages/react/kibana_context/theme/index.ts
Normal file
|
@ -0,0 +1,11 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
export { KibanaThemeProvider } from './theme';
|
||||
export type { KibanaThemeProviderProps } from './theme';
|
||||
export { wrapWithTheme } from './with_theme';
|
|
@ -13,10 +13,10 @@ import React, { useEffect } from 'react';
|
|||
import { act } from 'react-dom/test-utils';
|
||||
import { BehaviorSubject, of } from 'rxjs';
|
||||
|
||||
import type { CoreTheme } from '@kbn/core/public';
|
||||
import { mountWithIntl } from '@kbn/test-jest-helpers';
|
||||
|
||||
import { KibanaThemeProvider } from './kibana_theme_provider';
|
||||
import type { Theme } from '../types';
|
||||
import { KibanaThemeProvider } from './theme';
|
||||
|
||||
describe('KibanaThemeProvider', () => {
|
||||
let euiTheme: ReturnType<typeof useEuiTheme> | undefined;
|
||||
|
@ -51,7 +51,7 @@ describe('KibanaThemeProvider', () => {
|
|||
};
|
||||
|
||||
it('exposes the EUI theme provider', async () => {
|
||||
const coreTheme: CoreTheme = { darkMode: true };
|
||||
const coreTheme: Theme = { darkMode: true };
|
||||
|
||||
const wrapper = mountWithIntl(
|
||||
<KibanaThemeProvider theme$={of(coreTheme)}>
|
||||
|
@ -65,7 +65,7 @@ describe('KibanaThemeProvider', () => {
|
|||
});
|
||||
|
||||
it('propagates changes of the coreTheme observable', async () => {
|
||||
const coreTheme$ = new BehaviorSubject<CoreTheme>({ darkMode: true });
|
||||
const coreTheme$ = new BehaviorSubject<Theme>({ darkMode: true });
|
||||
|
||||
const wrapper = mountWithIntl(
|
||||
<KibanaThemeProvider theme$={coreTheme$}>
|
70
packages/react/kibana_context/theme/theme.tsx
Normal file
70
packages/react/kibana_context/theme/theme.tsx
Normal file
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import React, { FC, useMemo } from 'react';
|
||||
import useObservable from 'react-use/lib/useObservable';
|
||||
import { Observable } from 'rxjs';
|
||||
import createCache from '@emotion/cache';
|
||||
|
||||
import { EuiProvider, EuiProviderProps } from '@elastic/eui';
|
||||
|
||||
import { getColorMode } from './color_mode';
|
||||
import type { Theme } from '../types';
|
||||
|
||||
export interface KibanaThemeProviderProps
|
||||
extends Pick<EuiProviderProps<{}>, 'modify' | 'colorMode'> {
|
||||
theme$: Observable<Theme>;
|
||||
globalStyles?: boolean;
|
||||
utilityClasses?: boolean;
|
||||
}
|
||||
|
||||
const defaultTheme: Theme = {
|
||||
darkMode: false,
|
||||
};
|
||||
|
||||
const globalCache = createCache({
|
||||
key: 'eui',
|
||||
container: document.querySelector(`meta[name="eui-global"]`) as HTMLElement,
|
||||
});
|
||||
|
||||
const emotionCache = createCache({
|
||||
key: 'css',
|
||||
container: document.querySelector(`meta[name="emotion"]`) as HTMLElement,
|
||||
});
|
||||
|
||||
emotionCache.compat = true;
|
||||
|
||||
const cache = { default: emotionCache, global: globalCache };
|
||||
|
||||
export const KibanaThemeProvider: FC<KibanaThemeProviderProps> = ({
|
||||
theme$,
|
||||
globalStyles: globalStylesProp,
|
||||
utilityClasses: utilityClassesProp,
|
||||
colorMode: colorModeProp,
|
||||
modify,
|
||||
children,
|
||||
}) => {
|
||||
const theme = useObservable(theme$, defaultTheme);
|
||||
const themeColorMode = useMemo(() => getColorMode(theme), [theme]);
|
||||
|
||||
// In some cases-- like in Storybook or testing-- we want to explicitly override the
|
||||
// colorMode provided by the `theme`.
|
||||
const colorMode = colorModeProp || themeColorMode;
|
||||
|
||||
// This logic was drawn from the Core theme provider, and wasn't present (or even used)
|
||||
// elsewhere. Should be a passive addition to anyone using the older theme provider(s).
|
||||
const globalStyles = globalStylesProp === false ? false : undefined;
|
||||
const utilityClasses =
|
||||
utilityClassesProp === false || globalStylesProp === false ? false : undefined;
|
||||
|
||||
return (
|
||||
<EuiProvider {...{ cache, colorMode, globalStyles, modify, utilityClasses }}>
|
||||
{children}
|
||||
</EuiProvider>
|
||||
);
|
||||
};
|
|
@ -8,12 +8,10 @@
|
|||
|
||||
import React from 'react';
|
||||
import { Observable } from 'rxjs';
|
||||
import type { CoreTheme } from '@kbn/core/public';
|
||||
import { KibanaThemeProvider } from './kibana_theme_provider';
|
||||
|
||||
export const wrapWithTheme = (
|
||||
node: React.ReactNode,
|
||||
theme$: Observable<CoreTheme>
|
||||
): React.ReactElement => {
|
||||
return <KibanaThemeProvider theme$={theme$}>{node}</KibanaThemeProvider>;
|
||||
};
|
||||
import { KibanaThemeProvider } from './theme';
|
||||
import type { Theme } from '../types';
|
||||
|
||||
export const wrapWithTheme = (node: React.ReactNode, theme$: Observable<Theme>) => (
|
||||
<KibanaThemeProvider theme$={theme$}>{node}</KibanaThemeProvider>
|
||||
);
|
22
packages/react/kibana_context/tsconfig.json
Normal file
22
packages/react/kibana_context/tsconfig.json
Normal file
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"extends": "../../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "target/types",
|
||||
"types": [
|
||||
"jest",
|
||||
"node",
|
||||
"react"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
],
|
||||
"exclude": [
|
||||
"target/**/*"
|
||||
],
|
||||
"kbn_references": [
|
||||
"@kbn/i18n-react",
|
||||
"@kbn/test-jest-helpers",
|
||||
]
|
||||
}
|
16
packages/react/kibana_context/types.ts
Normal file
16
packages/react/kibana_context/types.ts
Normal file
|
@ -0,0 +1,16 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
// To avoid a circular dependency with the deprecation of `CoreThemeProvider`,
|
||||
// we need to define the theme type here.
|
||||
//
|
||||
// TODO: clintandrewhall - remove this file once `CoreThemeProvider` is removed
|
||||
export interface Theme {
|
||||
/** is dark mode enabled or not */
|
||||
readonly darkMode: boolean;
|
||||
}
|
38
packages/react/kibana_context/utils/compose.tsx
Normal file
38
packages/react/kibana_context/utils/compose.tsx
Normal file
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import React, { PropsWithChildren, FC, ComponentPropsWithoutRef } from 'react';
|
||||
|
||||
export type UnionToIntersection<U> = (U extends unknown ? (u: U) => void : never) extends (
|
||||
i: infer I
|
||||
) => void
|
||||
? I
|
||||
: never;
|
||||
|
||||
type CombinedProps<P extends Array<FC<any>>> = PropsWithChildren<
|
||||
UnionToIntersection<ComponentPropsWithoutRef<P[number]>>
|
||||
>;
|
||||
|
||||
const compose = <P extends Array<FC<any>>>(providers: P, props: CombinedProps<P>): FC<{}> => {
|
||||
return ({ children }) => (
|
||||
<>
|
||||
{providers.reduceRight((acc, ContextProvider) => {
|
||||
return <ContextProvider {...props}>{acc}</ContextProvider>;
|
||||
}, children)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export const composeProviders = <P extends Array<FC<any>>>(providers: P): FC<CombinedProps<P>> => {
|
||||
return (props: CombinedProps<P>) => {
|
||||
const ContextProvider = compose(providers, props);
|
||||
return <ContextProvider>{props.children}</ContextProvider>;
|
||||
};
|
||||
};
|
||||
|
||||
export type ComposeProvidersFn = typeof composeProviders;
|
|
@ -6,6 +6,4 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import type { UseEuiTheme } from '@elastic/eui';
|
||||
|
||||
export type EuiTheme = UseEuiTheme;
|
||||
export { composeProviders } from './compose';
|
3
packages/react/kibana_mount/README.md
Normal file
3
packages/react/kibana_mount/README.md
Normal file
|
@ -0,0 +1,3 @@
|
|||
# @kbn/react-kibana-mount
|
||||
|
||||
Empty package generated by @kbn/generate
|
|
@ -7,6 +7,7 @@
|
|||
*/
|
||||
|
||||
export { toMountPoint } from './to_mount_point';
|
||||
export type { ToMountPointOptions } from './to_mount_point';
|
||||
export type { ToMountPointParams } from './to_mount_point';
|
||||
export { MountPointPortal } from './mount_point_portal';
|
||||
export type { MountPointPortalProps } from './mount_point_portal';
|
||||
export { useIfMounted } from './utils';
|
13
packages/react/kibana_mount/jest.config.js
Normal file
13
packages/react/kibana_mount/jest.config.js
Normal file
|
@ -0,0 +1,13 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
preset: '@kbn/test',
|
||||
rootDir: '../../..',
|
||||
roots: ['<rootDir>/packages/react/kibana_mount'],
|
||||
};
|
5
packages/react/kibana_mount/kibana.jsonc
Normal file
5
packages/react/kibana_mount/kibana.jsonc
Normal file
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"type": "shared-common",
|
||||
"id": "@kbn/react-kibana-mount",
|
||||
"owner": "@elastic/appex-sharedux"
|
||||
}
|
|
@ -12,7 +12,7 @@ import ReactDOM from 'react-dom';
|
|||
import { MountPoint } from '@kbn/core/public';
|
||||
import { useIfMounted } from './utils';
|
||||
|
||||
interface MountPointPortalProps {
|
||||
export interface MountPointPortalProps {
|
||||
setMountPoint: (mountPoint: MountPoint<HTMLElement>) => void;
|
||||
}
|
||||
|
||||
|
@ -48,7 +48,7 @@ export const MountPointPortal: React.FC<MountPointPortalProps> = ({ children, se
|
|||
el.current = undefined;
|
||||
});
|
||||
};
|
||||
}, [setMountPoint]);
|
||||
}, [setMountPoint, ifMounted]);
|
||||
|
||||
if (shouldRender && el.current) {
|
||||
return ReactDOM.createPortal(
|
||||
|
@ -77,7 +77,7 @@ class MountPointPortalErrorBoundary extends Component<{}, { error?: unknown }> {
|
|||
if (this.state.error) {
|
||||
return (
|
||||
<p>
|
||||
{i18n.translate('kibana-react.mountPointPortal.errorMessage', {
|
||||
{i18n.translate('reactPackages.mountPointPortal.errorMessage', {
|
||||
defaultMessage: 'Error rendering portal content',
|
||||
})}
|
||||
</p>
|
6
packages/react/kibana_mount/package.json
Normal file
6
packages/react/kibana_mount/package.json
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"name": "@kbn/react-kibana-mount",
|
||||
"private": true,
|
||||
"version": "1.0.0",
|
||||
"license": "SSPL-1.0 OR Elastic License 2.0"
|
||||
}
|
|
@ -15,11 +15,7 @@ import type { CoreTheme } from '@kbn/core/public';
|
|||
import { toMountPoint } from './to_mount_point';
|
||||
|
||||
describe('toMountPoint', () => {
|
||||
let euiTheme: UseEuiTheme | undefined;
|
||||
|
||||
beforeEach(() => {
|
||||
euiTheme = undefined;
|
||||
});
|
||||
let euiTheme: UseEuiTheme;
|
||||
|
||||
const InnerComponent: FC = () => {
|
||||
const theme = useEuiTheme();
|
|
@ -9,12 +9,11 @@
|
|||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { Observable } from 'rxjs';
|
||||
import { I18nProvider } from '@kbn/i18n-react';
|
||||
import type { MountPoint, CoreTheme } from '@kbn/core/public';
|
||||
import { KibanaThemeProvider } from '../theme';
|
||||
import { KibanaContextProvider } from '@kbn/react-kibana-context';
|
||||
|
||||
export interface ToMountPointOptions {
|
||||
theme$?: Observable<CoreTheme>;
|
||||
export interface ToMountPointParams {
|
||||
theme$: Observable<CoreTheme>;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -22,18 +21,16 @@ export interface ToMountPointOptions {
|
|||
*
|
||||
* @param node to get a mount point for
|
||||
*/
|
||||
export const toMountPoint = (
|
||||
node: React.ReactNode,
|
||||
{ theme$ }: ToMountPointOptions = {}
|
||||
): MountPoint => {
|
||||
const content = theme$ ? <KibanaThemeProvider theme$={theme$}>{node}</KibanaThemeProvider> : node;
|
||||
export const toMountPoint = (node: React.ReactNode, { theme$ }: ToMountPointParams): MountPoint => {
|
||||
const mount = (element: HTMLElement) => {
|
||||
ReactDOM.render(<I18nProvider>{content}</I18nProvider>, element);
|
||||
ReactDOM.render(<KibanaContextProvider {...{ theme$ }}>{node}</KibanaContextProvider>, element);
|
||||
return () => ReactDOM.unmountComponentAtNode(element);
|
||||
};
|
||||
|
||||
// only used for tests and snapshots serialization
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
mount.__reactMount__ = node;
|
||||
}
|
||||
|
||||
return mount;
|
||||
};
|
23
packages/react/kibana_mount/tsconfig.json
Normal file
23
packages/react/kibana_mount/tsconfig.json
Normal file
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"extends": "../../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "target/types",
|
||||
"types": [
|
||||
"jest",
|
||||
"node",
|
||||
"react"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
],
|
||||
"exclude": [
|
||||
"target/**/*"
|
||||
],
|
||||
"kbn_references": [
|
||||
"@kbn/react-kibana-context",
|
||||
"@kbn/core",
|
||||
"@kbn/i18n",
|
||||
]
|
||||
}
|
|
@ -6,4 +6,11 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
export { KibanaThemeProvider } from './kibana_theme_provider';
|
||||
import { KibanaThemeProvider as ThemeProvider } from '@kbn/react-kibana-context';
|
||||
|
||||
export type { KibanaThemeProviderProps } from '@kbn/react-kibana-context';
|
||||
|
||||
/**
|
||||
* @deprecated use `KibanaThemeProvider` from `@kbn/react-kibana-context
|
||||
*/
|
||||
export const KibanaThemeProvider = ThemeProvider;
|
||||
|
|
|
@ -1,57 +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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import type { EuiProviderProps } from '@elastic/eui';
|
||||
import { EuiProvider } from '@elastic/eui';
|
||||
import createCache from '@emotion/cache';
|
||||
import type { FC } from 'react';
|
||||
import React, { useMemo } from 'react';
|
||||
import useObservable from 'react-use/lib/useObservable';
|
||||
import type { Observable } from 'rxjs';
|
||||
|
||||
import type { CoreTheme } from '@kbn/core/public';
|
||||
|
||||
import { getColorMode } from './utils';
|
||||
|
||||
interface KibanaThemeProviderProps {
|
||||
theme$: Observable<CoreTheme>;
|
||||
modify?: EuiProviderProps<{}>['modify'];
|
||||
}
|
||||
|
||||
const defaultTheme: CoreTheme = {
|
||||
darkMode: false,
|
||||
};
|
||||
|
||||
const globalCache = createCache({
|
||||
key: 'eui',
|
||||
container: document.querySelector(`meta[name="eui-styles"]`) as HTMLElement,
|
||||
});
|
||||
const emotionCache = createCache({
|
||||
key: 'css',
|
||||
container: document.querySelector(`meta[name="emotion"]`) as HTMLElement,
|
||||
});
|
||||
emotionCache.compat = true;
|
||||
|
||||
/**
|
||||
* Copied from the `kibana_react` plugin, remove once https://github.com/elastic/kibana/issues/119204 is implemented.
|
||||
*/
|
||||
export const KibanaThemeProvider: FC<KibanaThemeProviderProps> = ({ theme$, modify, children }) => {
|
||||
const theme = useObservable(theme$, defaultTheme);
|
||||
const colorMode = useMemo(() => getColorMode(theme), [theme]);
|
||||
return (
|
||||
<EuiProvider
|
||||
colorMode={colorMode}
|
||||
cache={{ default: emotionCache, global: globalCache }}
|
||||
globalStyles={false}
|
||||
utilityClasses={false}
|
||||
modify={modify}
|
||||
>
|
||||
{children}
|
||||
</EuiProvider>
|
||||
);
|
||||
};
|
|
@ -1,19 +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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { getColorMode } from './utils';
|
||||
|
||||
describe('getColorMode', () => {
|
||||
it('returns the correct `colorMode` when `darkMode` is enabled', () => {
|
||||
expect(getColorMode({ darkMode: true })).toEqual('DARK');
|
||||
});
|
||||
|
||||
it('returns the correct `colorMode` when `darkMode` is disabled', () => {
|
||||
expect(getColorMode({ darkMode: false })).toEqual('LIGHT');
|
||||
});
|
||||
});
|
|
@ -1,19 +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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { COLOR_MODES_STANDARD } from '@elastic/eui';
|
||||
import type { EuiThemeColorModeStandard } from '@elastic/eui';
|
||||
|
||||
import type { CoreTheme } from '@kbn/core/public';
|
||||
|
||||
/**
|
||||
* Copied from the `kibana_react` plugin, remove once https://github.com/elastic/kibana/issues/119204 is implemented.
|
||||
*/
|
||||
export const getColorMode = (theme: CoreTheme): EuiThemeColorModeStandard => {
|
||||
return theme.darkMode ? COLOR_MODES_STANDARD.dark : COLOR_MODES_STANDARD.light;
|
||||
};
|
|
@ -19,6 +19,7 @@
|
|||
"@kbn/utils",
|
||||
"@kbn/core-logging-server-mocks",
|
||||
"@kbn/core-preboot-server",
|
||||
"@kbn/react-kibana-context",
|
||||
],
|
||||
"exclude": [
|
||||
"target/**/*",
|
||||
|
|
|
@ -6,6 +6,20 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
export { wrapWithTheme } from './wrap_with_theme';
|
||||
export { KibanaThemeProvider } from './kibana_theme_provider';
|
||||
export type { EuiTheme } from './types';
|
||||
import {
|
||||
KibanaThemeProvider as ThemeProvider,
|
||||
wrapWithTheme as withTheme,
|
||||
} from '@kbn/react-kibana-context';
|
||||
|
||||
export type { KibanaThemeProviderProps } from '@kbn/react-kibana-context';
|
||||
export type { UseEuiTheme as EuiTheme } from '@elastic/eui';
|
||||
|
||||
/**
|
||||
* @deprecated use `KibanaThemeProvider` from `@kbn/react-kibana-context
|
||||
*/
|
||||
export const KibanaThemeProvider = ThemeProvider;
|
||||
|
||||
/**
|
||||
* @deprecated use `wrapWithTheme` from `@kbn/react-kibana-context
|
||||
*/
|
||||
export const wrapWithTheme = withTheme;
|
||||
|
|
|
@ -1,86 +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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import React, { FC, useEffect } from 'react';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import type { ReactWrapper } from 'enzyme';
|
||||
import { of, BehaviorSubject } from 'rxjs';
|
||||
import { useEuiTheme } from '@elastic/eui';
|
||||
import type { UseEuiTheme } from '@elastic/eui';
|
||||
import { mountWithIntl } from '@kbn/test-jest-helpers';
|
||||
import type { CoreTheme } from '@kbn/core/public';
|
||||
import { KibanaThemeProvider } from './kibana_theme_provider';
|
||||
|
||||
describe('KibanaThemeProvider', () => {
|
||||
let euiTheme: UseEuiTheme | undefined;
|
||||
|
||||
beforeEach(() => {
|
||||
euiTheme = undefined;
|
||||
});
|
||||
|
||||
const flushPromises = async () => {
|
||||
await new Promise<void>(async (resolve, reject) => {
|
||||
try {
|
||||
setImmediate(() => resolve());
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const InnerComponent: FC = () => {
|
||||
const theme = useEuiTheme();
|
||||
useEffect(() => {
|
||||
euiTheme = theme;
|
||||
}, [theme]);
|
||||
return <div>foo</div>;
|
||||
};
|
||||
|
||||
const refresh = async (wrapper: ReactWrapper<unknown>) => {
|
||||
await act(async () => {
|
||||
await flushPromises();
|
||||
wrapper.update();
|
||||
});
|
||||
};
|
||||
|
||||
it('exposes the EUI theme provider', async () => {
|
||||
const coreTheme: CoreTheme = { darkMode: true };
|
||||
|
||||
const wrapper = mountWithIntl(
|
||||
<KibanaThemeProvider theme$={of(coreTheme)}>
|
||||
<InnerComponent />
|
||||
</KibanaThemeProvider>
|
||||
);
|
||||
|
||||
await refresh(wrapper);
|
||||
|
||||
expect(euiTheme!.colorMode).toEqual('DARK');
|
||||
});
|
||||
|
||||
it('propagates changes of the coreTheme observable', async () => {
|
||||
const coreTheme$ = new BehaviorSubject<CoreTheme>({ darkMode: true });
|
||||
|
||||
const wrapper = mountWithIntl(
|
||||
<KibanaThemeProvider theme$={coreTheme$}>
|
||||
<InnerComponent />
|
||||
</KibanaThemeProvider>
|
||||
);
|
||||
|
||||
await refresh(wrapper);
|
||||
|
||||
expect(euiTheme!.colorMode).toEqual('DARK');
|
||||
|
||||
await act(async () => {
|
||||
coreTheme$.next({ darkMode: false });
|
||||
});
|
||||
|
||||
await refresh(wrapper);
|
||||
|
||||
expect(euiTheme!.colorMode).toEqual('LIGHT');
|
||||
});
|
||||
});
|
|
@ -1,54 +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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import React, { FC, useMemo } from 'react';
|
||||
import useObservable from 'react-use/lib/useObservable';
|
||||
import { Observable } from 'rxjs';
|
||||
import { EuiProvider, EuiProviderProps } from '@elastic/eui';
|
||||
import createCache from '@emotion/cache';
|
||||
import type { CoreTheme } from '@kbn/core/public';
|
||||
import { getColorMode } from './utils';
|
||||
|
||||
interface KibanaThemeProviderProps {
|
||||
theme$: Observable<CoreTheme>;
|
||||
modify?: EuiProviderProps<{}>['modify'];
|
||||
}
|
||||
|
||||
const defaultTheme: CoreTheme = {
|
||||
darkMode: false,
|
||||
};
|
||||
|
||||
const globalCache = createCache({
|
||||
key: 'eui',
|
||||
container: document.querySelector(`meta[name="eui-global"]`) as HTMLElement,
|
||||
});
|
||||
const emotionCache = createCache({
|
||||
key: 'css',
|
||||
container: document.querySelector(`meta[name="emotion"]`) as HTMLElement,
|
||||
});
|
||||
emotionCache.compat = true;
|
||||
|
||||
/* IMPORTANT: This code has been copied to the `interactive_setup` plugin, any changes here should be applied there too.
|
||||
That copy and this comment can be removed once https://github.com/elastic/kibana/issues/119204 is implemented.*/
|
||||
// IMPORTANT: This code has been copied to the `kibana_utils` plugin, to avoid cyclical dependency, any changes here should be applied there too.
|
||||
|
||||
export const KibanaThemeProvider: FC<KibanaThemeProviderProps> = ({ theme$, modify, children }) => {
|
||||
const theme = useObservable(theme$, defaultTheme);
|
||||
const colorMode = useMemo(() => getColorMode(theme), [theme]);
|
||||
return (
|
||||
<EuiProvider
|
||||
colorMode={colorMode}
|
||||
cache={{ default: emotionCache, global: globalCache }}
|
||||
globalStyles={false}
|
||||
utilityClasses={false}
|
||||
modify={modify}
|
||||
>
|
||||
{children}
|
||||
</EuiProvider>
|
||||
);
|
||||
};
|
|
@ -1,19 +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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { getColorMode } from './utils';
|
||||
|
||||
describe('getColorMode', () => {
|
||||
it('returns the correct `colorMode` when `darkMode` is enabled', () => {
|
||||
expect(getColorMode({ darkMode: true })).toEqual('DARK');
|
||||
});
|
||||
|
||||
it('returns the correct `colorMode` when `darkMode` is disabled', () => {
|
||||
expect(getColorMode({ darkMode: false })).toEqual('LIGHT');
|
||||
});
|
||||
});
|
|
@ -1,19 +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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { COLOR_MODES_STANDARD } from '@elastic/eui';
|
||||
import type { EuiThemeColorModeStandard } from '@elastic/eui';
|
||||
import type { CoreTheme } from '@kbn/core/public';
|
||||
|
||||
/* IMPORTANT: This code has been copied to the `interactive_setup` plugin, any changes here should be applied there too.
|
||||
That copy and this comment can be removed once https://github.com/elastic/kibana/issues/119204 is implemented.*/
|
||||
// IMPORTANT: This code has been copied to the `kibana_utils` plugin, to avoid cyclical dependency, any changes here should be applied there too.
|
||||
|
||||
export const getColorMode = (theme: CoreTheme): EuiThemeColorModeStandard => {
|
||||
return theme.darkMode ? COLOR_MODES_STANDARD.dark : COLOR_MODES_STANDARD.light;
|
||||
};
|
61
src/plugins/kibana_react/public/util/index.tsx
Normal file
61
src/plugins/kibana_react/public/util/index.tsx
Normal file
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { Observable } from 'rxjs';
|
||||
import type { MountPoint, CoreTheme } from '@kbn/core/public';
|
||||
|
||||
import {
|
||||
toMountPoint as _toMountPoint,
|
||||
MountPointPortal as _MountPointPortal,
|
||||
useIfMounted as _useIfMounted,
|
||||
} from '@kbn/react-kibana-mount';
|
||||
import { I18nProvider } from '@kbn/i18n-react';
|
||||
|
||||
/**
|
||||
* @deprecated use `ToMountPointParams` from `@kbn/react-kibana-mount`
|
||||
*/
|
||||
export interface ToMountPointOptions {
|
||||
theme$?: Observable<CoreTheme>;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use `toMountPoint` from `@kbn/react-kibana-mount`
|
||||
*/
|
||||
export const toMountPoint = (
|
||||
node: React.ReactNode,
|
||||
{ theme$ }: ToMountPointOptions = {}
|
||||
): MountPoint => {
|
||||
if (theme$) {
|
||||
return _toMountPoint(node, { theme$ });
|
||||
}
|
||||
|
||||
// A `theme` should always be included in order to ensure dark mode
|
||||
// is applied correctly. This code is for compatibility purposes, and
|
||||
// will be removed when the deprecated usages are removed.
|
||||
const mount = (element: HTMLElement) => {
|
||||
ReactDOM.render(<I18nProvider>{node}</I18nProvider>, element);
|
||||
return () => ReactDOM.unmountComponentAtNode(element);
|
||||
};
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
mount.__reactMount__ = node;
|
||||
}
|
||||
return mount;
|
||||
};
|
||||
|
||||
/**
|
||||
* @deprecated use `MountPointPortal` from `@kbn/react-kibana-mount`
|
||||
*/
|
||||
export const MountPointPortal = _MountPointPortal;
|
||||
|
||||
/**
|
||||
* @deprecated use `useIfMounted` from `@kbn/react-kibana-mount`
|
||||
*/
|
||||
export const useIfMounted = _useIfMounted;
|
|
@ -12,6 +12,8 @@
|
|||
"@kbn/test-jest-helpers",
|
||||
"@kbn/i18n",
|
||||
"@kbn/i18n-react",
|
||||
"@kbn/react-kibana-context",
|
||||
"@kbn/react-kibana-mount",
|
||||
],
|
||||
"exclude": [
|
||||
"target/**/*",
|
||||
|
|
|
@ -6,50 +6,7 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { EuiProvider, EuiProviderProps } from '@elastic/eui';
|
||||
import createCache from '@emotion/cache';
|
||||
import type { FC } from 'react';
|
||||
import React, { useMemo } from 'react';
|
||||
import useObservable from 'react-use/lib/useObservable';
|
||||
import type { Observable } from 'rxjs';
|
||||
import { KibanaThemeProvider as _KibanaThemeProvider } from '@kbn/react-kibana-context';
|
||||
|
||||
import type { CoreTheme } from '@kbn/core/public';
|
||||
import { getColorMode } from './utils';
|
||||
|
||||
interface KibanaThemeProviderProps {
|
||||
theme$: Observable<CoreTheme>;
|
||||
modify?: EuiProviderProps<{}>['modify'];
|
||||
}
|
||||
|
||||
const defaultTheme: CoreTheme = {
|
||||
darkMode: false,
|
||||
};
|
||||
|
||||
const globalCache = createCache({
|
||||
key: 'eui',
|
||||
container: document.querySelector(`meta[name="eui-global"]`) as HTMLElement,
|
||||
});
|
||||
const emotionCache = createCache({
|
||||
key: 'css',
|
||||
container: document.querySelector(`meta[name="emotion"]`) as HTMLElement,
|
||||
});
|
||||
emotionCache.compat = true;
|
||||
|
||||
/**
|
||||
* Copied from the `kibana_react` plugin, to avoid cyclical dependency
|
||||
*/
|
||||
export const KibanaThemeProvider: FC<KibanaThemeProviderProps> = ({ theme$, modify, children }) => {
|
||||
const theme = useObservable(theme$, defaultTheme);
|
||||
const colorMode = useMemo(() => getColorMode(theme), [theme]);
|
||||
return (
|
||||
<EuiProvider
|
||||
colorMode={colorMode}
|
||||
cache={{ default: emotionCache, global: globalCache }}
|
||||
globalStyles={false}
|
||||
utilityClasses={false}
|
||||
modify={modify}
|
||||
>
|
||||
{children}
|
||||
</EuiProvider>
|
||||
);
|
||||
};
|
||||
/** @deprecated use `KibanaThemeProvider` from `@kbn/react-kibana-context */
|
||||
export const KibanaThemeProvider = _KibanaThemeProvider;
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
"@kbn/rison",
|
||||
"@kbn/crypto-browser",
|
||||
"@kbn/core-notifications-browser-mocks",
|
||||
"@kbn/react-kibana-context",
|
||||
],
|
||||
"exclude": [
|
||||
"target/**/*",
|
||||
|
|
|
@ -1016,6 +1016,10 @@
|
|||
"@kbn/random-sampling/*": ["x-pack/packages/kbn-random-sampling/*"],
|
||||
"@kbn/react-field": ["packages/kbn-react-field"],
|
||||
"@kbn/react-field/*": ["packages/kbn-react-field/*"],
|
||||
"@kbn/react-kibana-context": ["packages/react/kibana_context"],
|
||||
"@kbn/react-kibana-context/*": ["packages/react/kibana_context/*"],
|
||||
"@kbn/react-kibana-mount": ["packages/react/kibana_mount"],
|
||||
"@kbn/react-kibana-mount/*": ["packages/react/kibana_mount/*"],
|
||||
"@kbn/remote-clusters-plugin": ["x-pack/plugins/remote_clusters"],
|
||||
"@kbn/remote-clusters-plugin/*": ["x-pack/plugins/remote_clusters/*"],
|
||||
"@kbn/rendering-plugin": ["test/plugin_functional/plugins/rendering_plugin"],
|
||||
|
|
|
@ -4738,7 +4738,7 @@
|
|||
"kibana-react.kibanaCodeEditor.ariaLabel": "Éditeur de code",
|
||||
"kibana-react.kibanaCodeEditor.enterKeyLabel": "Entrée",
|
||||
"kibana-react.kibanaCodeEditor.escapeKeyLabel": "Échap",
|
||||
"kibana-react.mountPointPortal.errorMessage": "Erreur lors du rendu du contenu du portail.",
|
||||
"reactPackages.mountPointPortal.errorMessage": "Erreur lors du rendu du contenu du portail.",
|
||||
"kibana-react.noDataPage.cantDecide.link": "Consultez la documentation pour en savoir plus.",
|
||||
"kibana-react.noDataPage.elasticAgentCard.description": "Utilisez Elastic Agent pour collecter de manière simple et unifiée les données de vos machines.",
|
||||
"kibana-react.noDataPage.elasticAgentCard.noPermission.description": "Cette intégration n'est pas encore activée. Votre administrateur possède les autorisations requises pour l'activer.",
|
||||
|
|
|
@ -4739,7 +4739,7 @@
|
|||
"kibana-react.kibanaCodeEditor.ariaLabel": "コードエディター",
|
||||
"kibana-react.kibanaCodeEditor.enterKeyLabel": "Enter",
|
||||
"kibana-react.kibanaCodeEditor.escapeKeyLabel": "Esc",
|
||||
"kibana-react.mountPointPortal.errorMessage": "ポータルコンテンツのレンダリングエラー",
|
||||
"reactPackages.mountPointPortal.errorMessage": "ポータルコンテンツのレンダリングエラー",
|
||||
"kibana-react.noDataPage.cantDecide.link": "詳細については、ドキュメントをご確認ください。",
|
||||
"kibana-react.noDataPage.elasticAgentCard.description": "Elasticエージェントを使用すると、シンプルで統一された方法でコンピューターからデータを収集するできます。",
|
||||
"kibana-react.noDataPage.elasticAgentCard.noPermission.description": "この統合はまだ有効ではありません。管理者にはオンにするために必要なアクセス権があります。",
|
||||
|
|
|
@ -4738,7 +4738,7 @@
|
|||
"kibana-react.kibanaCodeEditor.ariaLabel": "代码编辑器",
|
||||
"kibana-react.kibanaCodeEditor.enterKeyLabel": "Enter",
|
||||
"kibana-react.kibanaCodeEditor.escapeKeyLabel": "Esc",
|
||||
"kibana-react.mountPointPortal.errorMessage": "呈现门户内容时出错",
|
||||
"reactPackages.mountPointPortal.errorMessage": "呈现门户内容时出错",
|
||||
"kibana-react.noDataPage.cantDecide.link": "请参阅我们的文档以了解更多信息。",
|
||||
"kibana-react.noDataPage.elasticAgentCard.description": "使用 Elastic 代理以简单统一的方式从您的计算机中收集数据。",
|
||||
"kibana-react.noDataPage.elasticAgentCard.noPermission.description": "尚未启用此集成。您的管理员具有打开它所需的权限。",
|
||||
|
|
|
@ -4779,6 +4779,14 @@
|
|||
version "0.0.0"
|
||||
uid ""
|
||||
|
||||
"@kbn/react-kibana-context@link:packages/react/kibana_context":
|
||||
version "0.0.0"
|
||||
uid ""
|
||||
|
||||
"@kbn/react-kibana-mount@link:packages/react/kibana_mount":
|
||||
version "0.0.0"
|
||||
uid ""
|
||||
|
||||
"@kbn/remote-clusters-plugin@link:x-pack/plugins/remote_clusters":
|
||||
version "0.0.0"
|
||||
uid ""
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue