mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[EuiProvider] Fix AppEx-SharedUX code (#183872)
## Summary
Fixes needed for getting CI to pass when EUI throws an error if
attempting to render a component without the EuiProvider in the render
tree.
## Detailed description
In https://github.com/elastic/kibana/pull/180819, I will deliver a
change that will cause EUI components to throw an error if the
EuiProvider context is missing. This PR comes in as part of the final
work to get all functional tests passing in an environment where EUI
will throw the error. The tied to the ["Fix 'dark mode' inconsistencies
in Kibana" Epic](https://github.com/elastic/kibana-team/issues/805) has
so far been in preparation for this.
**Reviewers: Please interact with critical paths through the UI
components touched in this PR, ESPECIALLY in terms of testing dark mode
and i18n.**
<img width="1107" alt="image"
src="c0d2ce08
-ac35-45a7-8192-0b2256fceb0e">
### Checklist
Delete any items that are not applicable to this PR.
- [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
- [ ] This renders correctly on smaller devices using a responsive
layout. (You can test this [in your
browser](https://www.browserstack.com/guide/responsive-testing-on-local-server))
- [ ] This was checked for [cross-browser
compatibility](https://www.elastic.co/support/matrix#matrix_browsers)
---------
Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
c17e957724
commit
7d7f9de609
20 changed files with 311 additions and 217 deletions
|
@ -10,6 +10,7 @@ import * as React from 'react';
|
|||
import { render, unmountComponentAtNode } from 'react-dom';
|
||||
import { CoreSetup, CoreStart, AppMountParameters } from '@kbn/core/public';
|
||||
import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public';
|
||||
import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render';
|
||||
import { BfetchExplorerStartPlugins, ExplorerService } from './plugin';
|
||||
import { App } from './containers/app';
|
||||
|
||||
|
@ -26,9 +27,11 @@ export const mount =
|
|||
const [core, plugins] = await coreSetup.getStartServices();
|
||||
const deps: BfetchDeps = { appBasePath, core, plugins, explorer };
|
||||
const reactElement = (
|
||||
<KibanaContextProvider services={deps}>
|
||||
<App />
|
||||
</KibanaContextProvider>
|
||||
<KibanaRenderContextProvider {...core}>
|
||||
<KibanaContextProvider services={deps}>
|
||||
<App />
|
||||
</KibanaContextProvider>
|
||||
</KibanaRenderContextProvider>
|
||||
);
|
||||
render(reactElement, element);
|
||||
return () => unmountComponentAtNode(element);
|
||||
|
|
|
@ -19,5 +19,6 @@
|
|||
"@kbn/bfetch-plugin",
|
||||
"@kbn/kibana-react-plugin",
|
||||
"@kbn/shared-ux-router",
|
||||
"@kbn/react-kibana-context-render",
|
||||
]
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { Redirect } from 'react-router-dom';
|
||||
import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render';
|
||||
import { Router, Routes, Route } from '@kbn/shared-ux-router';
|
||||
import { RedirectAppLinks } from '@kbn/shared-ux-link-redirect-app';
|
||||
import { EuiPageTemplate, EuiSideNav } from '@elastic/eui';
|
||||
|
@ -24,65 +25,67 @@ export const renderApp = (
|
|||
{ element, history }: AppMountParameters
|
||||
) => {
|
||||
ReactDOM.render(
|
||||
<Router history={history}>
|
||||
<RedirectAppLinks coreStart={core}>
|
||||
<EuiPageTemplate offset={0}>
|
||||
<EuiPageTemplate.Sidebar>
|
||||
<EuiSideNav
|
||||
items={[
|
||||
{
|
||||
id: 'Examples',
|
||||
name: 'Examples',
|
||||
items: [
|
||||
{
|
||||
id: 'todos',
|
||||
name: 'Todo app',
|
||||
'data-test-subj': 'todosExample',
|
||||
href: '/app/contentManagementExamples/todos',
|
||||
},
|
||||
{
|
||||
id: 'msearch',
|
||||
name: 'MSearch',
|
||||
'data-test-subj': 'msearchExample',
|
||||
href: '/app/contentManagementExamples/msearch',
|
||||
},
|
||||
{
|
||||
id: 'finder',
|
||||
name: 'Finder',
|
||||
'data-test-subj': 'finderExample',
|
||||
href: '/app/contentManagementExamples/finder',
|
||||
},
|
||||
],
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</EuiPageTemplate.Sidebar>
|
||||
<KibanaRenderContextProvider {...core}>
|
||||
<Router history={history}>
|
||||
<RedirectAppLinks coreStart={core}>
|
||||
<EuiPageTemplate offset={0}>
|
||||
<EuiPageTemplate.Sidebar>
|
||||
<EuiSideNav
|
||||
items={[
|
||||
{
|
||||
id: 'Examples',
|
||||
name: 'Examples',
|
||||
items: [
|
||||
{
|
||||
id: 'todos',
|
||||
name: 'Todo app',
|
||||
'data-test-subj': 'todosExample',
|
||||
href: '/app/contentManagementExamples/todos',
|
||||
},
|
||||
{
|
||||
id: 'msearch',
|
||||
name: 'MSearch',
|
||||
'data-test-subj': 'msearchExample',
|
||||
href: '/app/contentManagementExamples/msearch',
|
||||
},
|
||||
{
|
||||
id: 'finder',
|
||||
name: 'Finder',
|
||||
'data-test-subj': 'finderExample',
|
||||
href: '/app/contentManagementExamples/finder',
|
||||
},
|
||||
],
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</EuiPageTemplate.Sidebar>
|
||||
|
||||
<EuiPageTemplate.Section>
|
||||
<Routes>
|
||||
<Redirect from="/" to="/todos" exact />
|
||||
<Route path="/todos">
|
||||
<TodoApp contentClient={contentManagement.client} />
|
||||
</Route>
|
||||
<Route path="/msearch">
|
||||
<MSearchApp
|
||||
contentClient={contentManagement.client}
|
||||
core={core}
|
||||
savedObjectsTagging={savedObjectsTaggingOss}
|
||||
/>
|
||||
</Route>
|
||||
<Route path="/finder">
|
||||
<FinderApp
|
||||
contentClient={contentManagement.client}
|
||||
core={core}
|
||||
savedObjectsTagging={savedObjectsTaggingOss}
|
||||
/>
|
||||
</Route>
|
||||
</Routes>
|
||||
</EuiPageTemplate.Section>
|
||||
</EuiPageTemplate>
|
||||
</RedirectAppLinks>
|
||||
</Router>,
|
||||
<EuiPageTemplate.Section>
|
||||
<Routes>
|
||||
<Redirect from="/" to="/todos" exact />
|
||||
<Route path="/todos">
|
||||
<TodoApp contentClient={contentManagement.client} />
|
||||
</Route>
|
||||
<Route path="/msearch">
|
||||
<MSearchApp
|
||||
contentClient={contentManagement.client}
|
||||
core={core}
|
||||
savedObjectsTagging={savedObjectsTaggingOss}
|
||||
/>
|
||||
</Route>
|
||||
<Route path="/finder">
|
||||
<FinderApp
|
||||
contentClient={contentManagement.client}
|
||||
core={core}
|
||||
savedObjectsTagging={savedObjectsTaggingOss}
|
||||
/>
|
||||
</Route>
|
||||
</Routes>
|
||||
</EuiPageTemplate.Section>
|
||||
</EuiPageTemplate>
|
||||
</RedirectAppLinks>
|
||||
</Router>
|
||||
</KibanaRenderContextProvider>,
|
||||
element
|
||||
);
|
||||
|
||||
|
|
|
@ -29,5 +29,6 @@
|
|||
"@kbn/shared-ux-router",
|
||||
"@kbn/saved-objects-finder-plugin",
|
||||
"@kbn/content-management-table-list-view-common",
|
||||
"@kbn/react-kibana-context-render",
|
||||
]
|
||||
}
|
||||
|
|
|
@ -22,16 +22,29 @@ import {
|
|||
EuiLink,
|
||||
EuiButtonIcon,
|
||||
} from '@elastic/eui';
|
||||
import { AppMountParameters } from '@kbn/core/public';
|
||||
import {
|
||||
AnalyticsServiceStart,
|
||||
AppMountParameters,
|
||||
I18nStart,
|
||||
ThemeServiceStart,
|
||||
} from '@kbn/core/public';
|
||||
import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render';
|
||||
import { ExampleDefinition } from './types';
|
||||
|
||||
interface StartServices {
|
||||
analytics: Pick<AnalyticsServiceStart, 'reportEvent'>;
|
||||
i18n: I18nStart;
|
||||
theme: Pick<ThemeServiceStart, 'theme$'>;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
startServices: StartServices;
|
||||
examples: ExampleDefinition[];
|
||||
navigateToApp: (appId: string) => void;
|
||||
getUrlForApp: (appId: string) => string;
|
||||
}
|
||||
|
||||
function DeveloperExamples({ examples, navigateToApp, getUrlForApp }: Props) {
|
||||
function DeveloperExamples({ startServices, examples, navigateToApp, getUrlForApp }: Props) {
|
||||
const [search, setSearch] = useState<string>('');
|
||||
|
||||
const lcSearch = search.toLowerCase();
|
||||
|
@ -44,7 +57,7 @@ function DeveloperExamples({ examples, navigateToApp, getUrlForApp }: Props) {
|
|||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<KibanaRenderContextProvider {...startServices}>
|
||||
<EuiPageTemplate.Header>
|
||||
<EuiFlexGroup justifyContent={'spaceBetween'}>
|
||||
<EuiFlexItem>
|
||||
|
@ -103,7 +116,7 @@ function DeveloperExamples({ examples, navigateToApp, getUrlForApp }: Props) {
|
|||
))}
|
||||
</EuiFlexGroup>
|
||||
</EuiPageTemplate.Section>
|
||||
</>
|
||||
</KibanaRenderContextProvider>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -27,8 +27,10 @@ export class DeveloperExamplesPlugin implements Plugin<DeveloperExamplesSetup, v
|
|||
async mount(params: AppMountParameters) {
|
||||
const { renderApp } = await import('./app');
|
||||
const [coreStart] = await core.getStartServices();
|
||||
const { analytics, i18n, theme } = coreStart;
|
||||
return renderApp(
|
||||
{
|
||||
startServices: { analytics, i18n, theme },
|
||||
examples,
|
||||
navigateToApp: (appId: string) => coreStart.application.navigateToApp(appId),
|
||||
getUrlForApp: (appId: string) => coreStart.application.getUrlForApp(appId),
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
"target/**/*",
|
||||
],
|
||||
"kbn_references": [
|
||||
"@kbn/core"
|
||||
"@kbn/core",
|
||||
"@kbn/react-kibana-context-render"
|
||||
]
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
import { EuiButton } from '@elastic/eui';
|
||||
import { EuiButton, EuiProvider } from '@elastic/eui';
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
|
@ -74,23 +74,27 @@ export class ErrorBoundaryExamplePlugin implements Plugin<void, void, SetupDeps>
|
|||
id: 'errorBoundaryExample',
|
||||
title: 'Error Boundary Example',
|
||||
async mount({ element }: AppMountParameters) {
|
||||
// Using the "EuiProvider" here rather than KibanaRenderContextProvider, because KibanaRenderContextProvider
|
||||
// wraps KibanaErrorBoundaryProvider and KibanaErrorBoundary and we want to test it directly, not a wrapper.
|
||||
ReactDOM.render(
|
||||
<KibanaErrorBoundaryProvider analytics={core.analytics}>
|
||||
<KibanaErrorBoundary>
|
||||
<KibanaPageTemplate>
|
||||
<KibanaPageTemplate.Header
|
||||
pageTitle="KibanaErrorBoundary example"
|
||||
data-test-subj="errorBoundaryExampleHeader"
|
||||
/>
|
||||
<KibanaPageTemplate.Section grow={false}>
|
||||
<FatalComponent />
|
||||
</KibanaPageTemplate.Section>
|
||||
<KibanaPageTemplate.Section>
|
||||
<RecoverableComponent />
|
||||
</KibanaPageTemplate.Section>
|
||||
</KibanaPageTemplate>
|
||||
</KibanaErrorBoundary>
|
||||
</KibanaErrorBoundaryProvider>,
|
||||
<EuiProvider>
|
||||
<KibanaErrorBoundaryProvider analytics={core.analytics}>
|
||||
<KibanaErrorBoundary>
|
||||
<KibanaPageTemplate>
|
||||
<KibanaPageTemplate.Header
|
||||
pageTitle="KibanaErrorBoundary example"
|
||||
data-test-subj="errorBoundaryExampleHeader"
|
||||
/>
|
||||
<KibanaPageTemplate.Section grow={false}>
|
||||
<FatalComponent />
|
||||
</KibanaPageTemplate.Section>
|
||||
<KibanaPageTemplate.Section>
|
||||
<RecoverableComponent />
|
||||
</KibanaPageTemplate.Section>
|
||||
</KibanaPageTemplate>
|
||||
</KibanaErrorBoundary>
|
||||
</KibanaErrorBoundaryProvider>
|
||||
</EuiProvider>,
|
||||
element
|
||||
);
|
||||
return () => ReactDOM.unmountComponentAtNode(element);
|
||||
|
|
|
@ -42,6 +42,7 @@ export class StateContainersExamplesPlugin implements Plugin {
|
|||
const { renderApp, History } = await import('./todo/app');
|
||||
const [coreStart] = await core.getStartServices();
|
||||
return renderApp(
|
||||
coreStart,
|
||||
params,
|
||||
{
|
||||
appTitle: examples.stateContainersExampleBrowserHistory.title,
|
||||
|
@ -59,6 +60,7 @@ export class StateContainersExamplesPlugin implements Plugin {
|
|||
const { renderApp, History } = await import('./todo/app');
|
||||
const [coreStart] = await core.getStartServices();
|
||||
return renderApp(
|
||||
coreStart,
|
||||
params,
|
||||
{
|
||||
appTitle: examples.stateContainersExampleHashHistory.title,
|
||||
|
|
|
@ -6,9 +6,11 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { AppMountParameters, CoreStart } from '@kbn/core/public';
|
||||
import ReactDOM from 'react-dom';
|
||||
import React from 'react';
|
||||
|
||||
import { AppMountParameters, CoreStart } from '@kbn/core/public';
|
||||
import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render';
|
||||
import { createHashHistory } from 'history';
|
||||
import { TodoAppPage } from './todo';
|
||||
import { StateContainersExamplesPage, ExampleLink } from '../common/example_page';
|
||||
|
@ -29,15 +31,18 @@ export interface Deps {
|
|||
}
|
||||
|
||||
export const renderApp = (
|
||||
core: CoreStart,
|
||||
{ appBasePath, element, history: platformHistory }: AppMountParameters,
|
||||
{ appTitle, historyType }: AppOptions,
|
||||
{ navigateToApp, exampleLinks }: Deps
|
||||
) => {
|
||||
const history = historyType === History.Browser ? platformHistory : createHashHistory();
|
||||
ReactDOM.render(
|
||||
<StateContainersExamplesPage navigateToApp={navigateToApp} exampleLinks={exampleLinks}>
|
||||
<TodoAppPage history={history} appTitle={appTitle} appBasePath={appBasePath} />
|
||||
</StateContainersExamplesPage>,
|
||||
<KibanaRenderContextProvider {...core}>
|
||||
<StateContainersExamplesPage navigateToApp={navigateToApp} exampleLinks={exampleLinks}>
|
||||
<TodoAppPage history={history} appTitle={appTitle} appBasePath={appBasePath} />
|
||||
</StateContainersExamplesPage>
|
||||
</KibanaRenderContextProvider>,
|
||||
element
|
||||
);
|
||||
|
||||
|
|
|
@ -23,5 +23,6 @@
|
|||
"@kbn/developer-examples-plugin",
|
||||
"@kbn/es-query",
|
||||
"@kbn/shared-ux-router",
|
||||
"@kbn/react-kibana-context-render",
|
||||
]
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ import {
|
|||
EuiPageHeader,
|
||||
} from '@elastic/eui';
|
||||
import { AppMountParameters, CoreStart } from '@kbn/core/public';
|
||||
import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render';
|
||||
import { UiActionsStart } from '@kbn/ui-actions-plugin/public';
|
||||
import { TriggerContextExample } from './trigger_context_example';
|
||||
import { ContextMenuExamples } from './context_menu_examples';
|
||||
|
@ -31,30 +32,35 @@ interface Props {
|
|||
|
||||
const ActionsExplorer = ({ uiActionsStartService, core }: Props) => {
|
||||
return (
|
||||
<EuiPage>
|
||||
<EuiPageBody>
|
||||
<EuiPageSection>
|
||||
<EuiPageHeader pageTitle="Actions and Triggers" />
|
||||
</EuiPageSection>
|
||||
<EuiPageTemplate.Section>
|
||||
<KibanaRenderContextProvider {...core}>
|
||||
<EuiPage>
|
||||
<EuiPageBody>
|
||||
<EuiPageSection>
|
||||
<Overview />
|
||||
|
||||
<EuiSpacer />
|
||||
|
||||
<HelloWorldExample uiActionsStartService={uiActionsStartService} startServices={core} />
|
||||
|
||||
<EuiSpacer />
|
||||
|
||||
<TriggerContextExample uiActionsApi={uiActionsStartService} />
|
||||
|
||||
<EuiSpacer />
|
||||
|
||||
<ContextMenuExamples />
|
||||
<EuiPageHeader pageTitle="Actions and Triggers" />
|
||||
</EuiPageSection>
|
||||
</EuiPageTemplate.Section>
|
||||
</EuiPageBody>
|
||||
</EuiPage>
|
||||
<EuiPageTemplate.Section>
|
||||
<EuiPageSection>
|
||||
<Overview />
|
||||
|
||||
<EuiSpacer />
|
||||
|
||||
<HelloWorldExample
|
||||
uiActionsStartService={uiActionsStartService}
|
||||
startServices={core}
|
||||
/>
|
||||
|
||||
<EuiSpacer />
|
||||
|
||||
<TriggerContextExample uiActionsApi={uiActionsStartService} />
|
||||
|
||||
<EuiSpacer />
|
||||
|
||||
<ContextMenuExamples />
|
||||
</EuiPageSection>
|
||||
</EuiPageTemplate.Section>
|
||||
</EuiPageBody>
|
||||
</EuiPage>
|
||||
</KibanaRenderContextProvider>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -18,5 +18,6 @@
|
|||
"@kbn/ui-actions-examples-plugin",
|
||||
"@kbn/developer-examples-plugin",
|
||||
"@kbn/react-kibana-mount",
|
||||
"@kbn/react-kibana-context-render",
|
||||
]
|
||||
}
|
||||
|
|
|
@ -21,7 +21,10 @@ import { notificationServiceMock } from '@kbn/core-notifications-browser-mocks';
|
|||
import { uiSettingsServiceMock } from '@kbn/core-ui-settings-browser-mocks';
|
||||
import { customBrandingServiceMock } from '@kbn/core-custom-branding-browser-mocks';
|
||||
import { analyticsServiceMock } from '@kbn/core-analytics-browser-mocks';
|
||||
import { i18nServiceMock } from '@kbn/core-i18n-browser-mocks';
|
||||
import { themeServiceMock } from '@kbn/core-theme-browser-mocks';
|
||||
import { getAppInfo } from '@kbn/core-application-browser-internal';
|
||||
import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render';
|
||||
import { findTestSubject } from '@kbn/test-jest-helpers';
|
||||
import { ChromeService } from './chrome_service';
|
||||
|
||||
|
@ -48,6 +51,9 @@ Object.defineProperty(window, 'localStorage', {
|
|||
|
||||
function defaultStartDeps(availableApps?: App[], currentAppId?: string) {
|
||||
const deps = {
|
||||
analytics: analyticsServiceMock.createAnalyticsServiceStart(),
|
||||
i18n: i18nServiceMock.createStartContract(),
|
||||
theme: themeServiceMock.createStartContract(),
|
||||
application: applicationServiceMock.createInternalStartContract(currentAppId),
|
||||
docLinks: docLinksServiceMock.createStartContract(),
|
||||
http: httpServiceMock.createStartContract(),
|
||||
|
@ -94,7 +100,7 @@ async function start({
|
|||
startDeps.injectedMetadata.getCspConfig.mockReturnValue(cspConfigMock);
|
||||
}
|
||||
|
||||
await service.setup({ analytics: analyticsServiceMock.createAnalyticsServiceSetup() });
|
||||
service.setup({ analytics: analyticsServiceMock.createAnalyticsServiceSetup() });
|
||||
const chromeStart = await service.start(startDeps);
|
||||
|
||||
return {
|
||||
|
@ -118,7 +124,7 @@ describe('setup', () => {
|
|||
it('calls registerAnalyticsContextProvider with the correct parameters', async () => {
|
||||
const service = new ChromeService(defaultStartTestOptions({}));
|
||||
const analytics = analyticsServiceMock.createAnalyticsServiceSetup();
|
||||
await service.setup({ analytics });
|
||||
service.setup({ analytics });
|
||||
|
||||
expect(registerAnalyticsContextProviderMock).toHaveBeenCalledTimes(1);
|
||||
expect(registerAnalyticsContextProviderMock).toHaveBeenCalledWith(
|
||||
|
@ -206,7 +212,7 @@ describe('start', () => {
|
|||
});
|
||||
|
||||
it('renders the custom project side navigation', async () => {
|
||||
const { chrome } = await start({
|
||||
const { chrome, startDeps } = await start({
|
||||
startDeps: defaultStartDeps([{ id: 'foo', title: 'Foo' } as App], 'foo'),
|
||||
});
|
||||
|
||||
|
@ -216,7 +222,11 @@ describe('start', () => {
|
|||
chrome.setChromeStyle('project');
|
||||
chrome.project.setSideNavComponent(MyNav);
|
||||
|
||||
const component = mount(chrome.getHeaderComponent());
|
||||
const component = mount(
|
||||
<KibanaRenderContextProvider {...startDeps}>
|
||||
{chrome.getHeaderComponent()}
|
||||
</KibanaRenderContextProvider>
|
||||
);
|
||||
|
||||
const projectHeader = findTestSubject(component, 'kibanaProjectHeader');
|
||||
expect(projectHeader.length).toBe(1);
|
||||
|
@ -229,11 +239,15 @@ describe('start', () => {
|
|||
});
|
||||
|
||||
it('renders chromeless header', async () => {
|
||||
const { chrome } = await start({ startDeps: defaultStartDeps() });
|
||||
const { chrome, startDeps } = await start({ startDeps: defaultStartDeps() });
|
||||
|
||||
chrome.setIsVisible(false);
|
||||
|
||||
const component = mount(chrome.getHeaderComponent());
|
||||
const component = mount(
|
||||
<KibanaRenderContextProvider {...startDeps}>
|
||||
{chrome.getHeaderComponent()}
|
||||
</KibanaRenderContextProvider>
|
||||
);
|
||||
|
||||
const chromeless = findTestSubject(component, 'kibanaHeaderChromeless');
|
||||
expect(chromeless.length).toBe(1);
|
||||
|
@ -418,7 +432,7 @@ describe('start', () => {
|
|||
const promise = chrome.getBreadcrumbsAppendExtension$().pipe(toArray()).toPromise();
|
||||
|
||||
chrome.setBreadcrumbsAppendExtension({
|
||||
content: (element) => () => {},
|
||||
content: () => () => {},
|
||||
});
|
||||
service.stop();
|
||||
|
||||
|
|
|
@ -52,6 +52,9 @@
|
|||
"@kbn/core-base-browser-mocks",
|
||||
"@kbn/logging",
|
||||
"@kbn/logging-mocks",
|
||||
"@kbn/core-i18n-browser-mocks",
|
||||
"@kbn/core-theme-browser-mocks",
|
||||
"@kbn/react-kibana-context-render",
|
||||
],
|
||||
"exclude": [
|
||||
"target/**/*",
|
||||
|
|
|
@ -10,30 +10,37 @@ import React from 'react';
|
|||
import { EuiFlexGroup, EuiFlexItem, EuiBadge, EuiFlyoutBody } from '@elastic/eui';
|
||||
import { CoreStart } from '@kbn/core/public';
|
||||
import { toMountPoint } from '@kbn/react-kibana-mount';
|
||||
import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render';
|
||||
import { ActionDefinition } from '../../actions';
|
||||
|
||||
const MenuItem: React.FC = () => {
|
||||
return (
|
||||
<EuiFlexGroup alignItems="center">
|
||||
<EuiFlexItem>Hello world!</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiBadge color={'danger'}>{'secret'}</EuiBadge>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
type StartServices = Pick<CoreStart, 'analytics' | 'i18n' | 'theme'>;
|
||||
|
||||
const getMenuItem = (core: StartServices) => {
|
||||
return () => {
|
||||
return (
|
||||
<KibanaRenderContextProvider {...core}>
|
||||
<EuiFlexGroup alignItems="center">
|
||||
<EuiFlexItem>Hello world!</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiBadge color={'danger'}>{'secret'}</EuiBadge>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</KibanaRenderContextProvider>
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
export const ACTION_HELLO_WORLD = 'ACTION_HELLO_WORLD';
|
||||
|
||||
export function createHelloWorldAction(
|
||||
coreStart: Pick<CoreStart, 'overlays' | 'analytics' | 'i18n' | 'theme'>
|
||||
coreStart: StartServices & Pick<CoreStart, 'overlays'>
|
||||
): ActionDefinition {
|
||||
const { overlays, ...startServices } = coreStart;
|
||||
return {
|
||||
id: ACTION_HELLO_WORLD,
|
||||
type: ACTION_HELLO_WORLD,
|
||||
getIconType: () => 'lock',
|
||||
MenuItem,
|
||||
MenuItem: getMenuItem(startServices),
|
||||
execute: async () => {
|
||||
overlays.openFlyout(
|
||||
toMountPoint(
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import React, { useCallback, useContext, useState } from 'react';
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import {
|
||||
EuiButton,
|
||||
EuiCallOut,
|
||||
|
@ -24,11 +24,12 @@ import {
|
|||
EuiTextArea,
|
||||
EuiTitle,
|
||||
} from '@elastic/eui';
|
||||
import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render';
|
||||
import { API_ENDPOINT, ScreenshottingExpressionResponse } from '../../common';
|
||||
import { HttpContext } from './http_context';
|
||||
import { useAppContext } from './http_context';
|
||||
|
||||
export function App() {
|
||||
const http = useContext(HttpContext);
|
||||
const { http, ...startServices } = useAppContext();
|
||||
const [expression, setExpression] = useState<string>();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [response, setResponse] = useState<ScreenshottingExpressionResponse>();
|
||||
|
@ -37,7 +38,7 @@ export function App() {
|
|||
try {
|
||||
setLoading(true);
|
||||
setResponse(
|
||||
await http?.get<ScreenshottingExpressionResponse>(API_ENDPOINT, {
|
||||
await http.get<ScreenshottingExpressionResponse>(API_ENDPOINT, {
|
||||
query: { expression },
|
||||
})
|
||||
);
|
||||
|
@ -50,85 +51,87 @@ export function App() {
|
|||
}, []);
|
||||
|
||||
return (
|
||||
<EuiPage>
|
||||
<EuiPageBody style={{ maxWidth: 1200, margin: '0 auto' }}>
|
||||
<EuiPageHeader>
|
||||
<EuiPageHeaderSection>
|
||||
<EuiTitle size="l">
|
||||
<h1>Screenshotting Demo</h1>
|
||||
</EuiTitle>
|
||||
</EuiPageHeaderSection>
|
||||
</EuiPageHeader>
|
||||
<EuiPageSection>
|
||||
<EuiText>
|
||||
<p>This example captures a screenshot of an expression provided below.</p>
|
||||
</EuiText>
|
||||
<EuiSpacer size={'m'} />
|
||||
<EuiTextArea
|
||||
placeholder="Expression to render"
|
||||
fullWidth
|
||||
onChange={handleChange}
|
||||
data-test-subj="expression"
|
||||
/>
|
||||
<EuiSpacer size={'m'} />
|
||||
<EuiButton
|
||||
iconType="play"
|
||||
onClick={handleClick}
|
||||
isDisabled={!expression}
|
||||
isLoading={loading}
|
||||
data-test-subj="run"
|
||||
>
|
||||
Run
|
||||
</EuiButton>
|
||||
{!!response && <EuiHorizontalRule />}
|
||||
{response?.errors && (
|
||||
<>
|
||||
<EuiCallOut
|
||||
title="Sorry, there was an error"
|
||||
color="danger"
|
||||
iconType="warning"
|
||||
data-test-subj="error"
|
||||
>
|
||||
<p>{response.errors.join('\n')}</p>
|
||||
</EuiCallOut>
|
||||
<EuiSpacer size={'m'} />
|
||||
</>
|
||||
)}
|
||||
<EuiFlexGroup justifyContent="spaceBetween">
|
||||
<EuiFlexItem grow={false}>
|
||||
{response?.image && (
|
||||
<EuiImage
|
||||
src={`data:image/png;base64,${response.image}`}
|
||||
alt="Screenshot"
|
||||
size="xl"
|
||||
allowFullScreen
|
||||
hasShadow
|
||||
data-test-subj="image"
|
||||
/>
|
||||
)}
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
{response?.metrics && (
|
||||
<>
|
||||
<EuiStat
|
||||
title={`${response.metrics.cpuInPercentage ?? 'N/A'}%`}
|
||||
description="CPU"
|
||||
titleColor="primary"
|
||||
data-test-subj="cpu"
|
||||
<KibanaRenderContextProvider {...startServices}>
|
||||
<EuiPage>
|
||||
<EuiPageBody style={{ maxWidth: 1200, margin: '0 auto' }}>
|
||||
<EuiPageHeader>
|
||||
<EuiPageHeaderSection>
|
||||
<EuiTitle size="l">
|
||||
<h1>Screenshotting Demo</h1>
|
||||
</EuiTitle>
|
||||
</EuiPageHeaderSection>
|
||||
</EuiPageHeader>
|
||||
<EuiPageSection>
|
||||
<EuiText>
|
||||
<p>This example captures a screenshot of an expression provided below.</p>
|
||||
</EuiText>
|
||||
<EuiSpacer size={'m'} />
|
||||
<EuiTextArea
|
||||
placeholder="Expression to render"
|
||||
fullWidth
|
||||
onChange={handleChange}
|
||||
data-test-subj="expression"
|
||||
/>
|
||||
<EuiSpacer size={'m'} />
|
||||
<EuiButton
|
||||
iconType="play"
|
||||
onClick={handleClick}
|
||||
isDisabled={!expression}
|
||||
isLoading={loading}
|
||||
data-test-subj="run"
|
||||
>
|
||||
Run
|
||||
</EuiButton>
|
||||
{!!response && <EuiHorizontalRule />}
|
||||
{response?.errors && (
|
||||
<>
|
||||
<EuiCallOut
|
||||
title="Sorry, there was an error"
|
||||
color="danger"
|
||||
iconType="warning"
|
||||
data-test-subj="error"
|
||||
>
|
||||
<p>{response.errors.join('\n')}</p>
|
||||
</EuiCallOut>
|
||||
<EuiSpacer size={'m'} />
|
||||
</>
|
||||
)}
|
||||
<EuiFlexGroup justifyContent="spaceBetween">
|
||||
<EuiFlexItem grow={false}>
|
||||
{response?.image && (
|
||||
<EuiImage
|
||||
src={`data:image/png;base64,${response.image}`}
|
||||
alt="Screenshot"
|
||||
size="xl"
|
||||
allowFullScreen
|
||||
hasShadow
|
||||
data-test-subj="image"
|
||||
/>
|
||||
<EuiSpacer size={'m'} />
|
||||
<EuiStat
|
||||
title={`${response.metrics.memoryInMegabytes ?? 'N/A'} MB`}
|
||||
description="Memory"
|
||||
titleColor="primary"
|
||||
data-test-subj="memory"
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiPageSection>
|
||||
</EuiPageBody>
|
||||
</EuiPage>
|
||||
)}
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
{response?.metrics && (
|
||||
<>
|
||||
<EuiStat
|
||||
title={`${response.metrics.cpuInPercentage ?? 'N/A'}%`}
|
||||
description="CPU"
|
||||
titleColor="primary"
|
||||
data-test-subj="cpu"
|
||||
/>
|
||||
<EuiSpacer size={'m'} />
|
||||
<EuiStat
|
||||
title={`${response.metrics.memoryInMegabytes ?? 'N/A'} MB`}
|
||||
description="Memory"
|
||||
titleColor="primary"
|
||||
data-test-subj="memory"
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiPageSection>
|
||||
</EuiPageBody>
|
||||
</EuiPage>
|
||||
</KibanaRenderContextProvider>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -5,7 +5,29 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { createContext } from 'react';
|
||||
import type { HttpStart } from '@kbn/core/public';
|
||||
import { createContext, useContext } from 'react';
|
||||
import type {
|
||||
AnalyticsServiceStart,
|
||||
HttpStart,
|
||||
I18nStart,
|
||||
ThemeServiceStart,
|
||||
} from '@kbn/core/public';
|
||||
|
||||
export const HttpContext = createContext<HttpStart | undefined>(undefined);
|
||||
export interface StartServices {
|
||||
http: HttpStart;
|
||||
analytics: Pick<AnalyticsServiceStart, 'reportEvent'>;
|
||||
i18n: I18nStart;
|
||||
theme: Pick<ThemeServiceStart, 'theme$'>;
|
||||
}
|
||||
|
||||
export const AppContext = createContext<StartServices | undefined>(undefined);
|
||||
|
||||
export const useAppContext = () => {
|
||||
const context = useContext(AppContext);
|
||||
|
||||
if (!context) {
|
||||
throw new Error('App Context Error!');
|
||||
}
|
||||
|
||||
return context;
|
||||
};
|
||||
|
|
|
@ -10,7 +10,7 @@ import ReactDOM from 'react-dom';
|
|||
import type { AppMountParameters, CoreSetup, Plugin } from '@kbn/core/public';
|
||||
import type { DeveloperExamplesSetup } from '@kbn/developer-examples-plugin/public';
|
||||
|
||||
import { App, HttpContext } from './app';
|
||||
import { App, AppContext } from './app';
|
||||
|
||||
interface SetupDeps {
|
||||
developerExamples: DeveloperExamplesSetup;
|
||||
|
@ -26,12 +26,13 @@ export class ScreenshottingExamplePlugin implements Plugin<void, void> {
|
|||
title: APPLICATION_NAME,
|
||||
visibleIn: [],
|
||||
mount: async ({ element }: AppMountParameters) => {
|
||||
const [{ http }] = await getStartServices();
|
||||
const [{ http, analytics, i18n, theme }] = await getStartServices();
|
||||
const startServices = { analytics, http, i18n, theme };
|
||||
|
||||
ReactDOM.render(
|
||||
<HttpContext.Provider value={http}>
|
||||
<AppContext.Provider value={startServices}>
|
||||
<App />
|
||||
</HttpContext.Provider>,
|
||||
</AppContext.Provider>,
|
||||
element
|
||||
);
|
||||
return () => ReactDOM.unmountComponentAtNode(element);
|
||||
|
|
|
@ -19,5 +19,6 @@
|
|||
"@kbn/developer-examples-plugin",
|
||||
"@kbn/screenshotting-plugin",
|
||||
"@kbn/config-schema",
|
||||
"@kbn/react-kibana-context-render",
|
||||
]
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue