mirror of
https://github.com/elastic/kibana.git
synced 2025-06-28 11:05:39 -04:00
[Shared UX][packages] Move No Data Page components; model service composition (#133593)
* [Shared UX] Move No Data Page components to packages * Fix type export. * Fix Kibana dependency miss; exported type * Address feedback; add docs * More updates after merge * Move comment Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
cde209b23a
commit
8882a50a34
41 changed files with 923 additions and 693 deletions
|
@ -203,6 +203,7 @@
|
||||||
"@kbn/shared-ux-components": "link:bazel-bin/packages/kbn-shared-ux-components",
|
"@kbn/shared-ux-components": "link:bazel-bin/packages/kbn-shared-ux-components",
|
||||||
"@kbn/shared-ux-link-redirect-app": "link:bazel-bin/packages/shared-ux/link/redirect_app",
|
"@kbn/shared-ux-link-redirect-app": "link:bazel-bin/packages/shared-ux/link/redirect_app",
|
||||||
"@kbn/shared-ux-page-analytics-no-data": "link:bazel-bin/packages/shared-ux/page/analytics_no_data",
|
"@kbn/shared-ux-page-analytics-no-data": "link:bazel-bin/packages/shared-ux/page/analytics_no_data",
|
||||||
|
"@kbn/shared-ux-page-kibana-no-data": "link:bazel-bin/packages/shared-ux/page/kibana_no_data",
|
||||||
"@kbn/shared-ux-prompt-no-data-views": "link:bazel-bin/packages/shared-ux/prompt/no_data_views",
|
"@kbn/shared-ux-prompt-no-data-views": "link:bazel-bin/packages/shared-ux/prompt/no_data_views",
|
||||||
"@kbn/shared-ux-services": "link:bazel-bin/packages/kbn-shared-ux-services",
|
"@kbn/shared-ux-services": "link:bazel-bin/packages/kbn-shared-ux-services",
|
||||||
"@kbn/shared-ux-storybook": "link:bazel-bin/packages/kbn-shared-ux-storybook",
|
"@kbn/shared-ux-storybook": "link:bazel-bin/packages/kbn-shared-ux-storybook",
|
||||||
|
@ -729,6 +730,7 @@
|
||||||
"@types/kbn__shared-ux-components": "link:bazel-bin/packages/kbn-shared-ux-components/npm_module_types",
|
"@types/kbn__shared-ux-components": "link:bazel-bin/packages/kbn-shared-ux-components/npm_module_types",
|
||||||
"@types/kbn__shared-ux-link-redirect-app": "link:bazel-bin/packages/shared-ux/link/redirect_app/npm_module_types",
|
"@types/kbn__shared-ux-link-redirect-app": "link:bazel-bin/packages/shared-ux/link/redirect_app/npm_module_types",
|
||||||
"@types/kbn__shared-ux-page-analytics-no-data": "link:bazel-bin/packages/shared-ux/page/analytics_no_data/npm_module_types",
|
"@types/kbn__shared-ux-page-analytics-no-data": "link:bazel-bin/packages/shared-ux/page/analytics_no_data/npm_module_types",
|
||||||
|
"@types/kbn__shared-ux-page-kibana-no-data": "link:bazel-bin/packages/shared-ux/page/kibana_no_data/npm_module_types",
|
||||||
"@types/kbn__shared-ux-prompt-no-data-views": "link:bazel-bin/packages/shared-ux/prompt/no_data_views/npm_module_types",
|
"@types/kbn__shared-ux-prompt-no-data-views": "link:bazel-bin/packages/shared-ux/prompt/no_data_views/npm_module_types",
|
||||||
"@types/kbn__shared-ux-services": "link:bazel-bin/packages/kbn-shared-ux-services/npm_module_types",
|
"@types/kbn__shared-ux-services": "link:bazel-bin/packages/kbn-shared-ux-services/npm_module_types",
|
||||||
"@types/kbn__shared-ux-storybook": "link:bazel-bin/packages/kbn-shared-ux-storybook/npm_module_types",
|
"@types/kbn__shared-ux-storybook": "link:bazel-bin/packages/kbn-shared-ux-storybook/npm_module_types",
|
||||||
|
|
|
@ -136,6 +136,7 @@ filegroup(
|
||||||
"//packages/shared-ux/button/exit_full_screen:build",
|
"//packages/shared-ux/button/exit_full_screen:build",
|
||||||
"//packages/shared-ux/link/redirect_app:build",
|
"//packages/shared-ux/link/redirect_app:build",
|
||||||
"//packages/shared-ux/page/analytics_no_data:build",
|
"//packages/shared-ux/page/analytics_no_data:build",
|
||||||
|
"//packages/shared-ux/page/kibana_no_data:build",
|
||||||
"//packages/shared-ux/prompt/no_data_views:build",
|
"//packages/shared-ux/prompt/no_data_views:build",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
@ -256,6 +257,7 @@ filegroup(
|
||||||
"//packages/shared-ux/button/exit_full_screen:build_types",
|
"//packages/shared-ux/button/exit_full_screen:build_types",
|
||||||
"//packages/shared-ux/link/redirect_app:build_types",
|
"//packages/shared-ux/link/redirect_app:build_types",
|
||||||
"//packages/shared-ux/page/analytics_no_data:build_types",
|
"//packages/shared-ux/page/analytics_no_data:build_types",
|
||||||
|
"//packages/shared-ux/page/kibana_no_data:build_types",
|
||||||
"//packages/shared-ux/prompt/no_data_views:build_types",
|
"//packages/shared-ux/prompt/no_data_views:build_types",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,23 +0,0 @@
|
||||||
---
|
|
||||||
id: sharedUX/Components/KibanaNoDataPage
|
|
||||||
slug: /shared-ux-components/kibana_no_data_page
|
|
||||||
title: Kibana No Data Page
|
|
||||||
summary: A page to be displayed when there is no data in Elasticsearch, or no data views
|
|
||||||
tags: ['shared-ux', 'component']
|
|
||||||
date: 2022-04-20
|
|
||||||
---
|
|
||||||
|
|
||||||
## Description
|
|
||||||
|
|
||||||
Many plugins display "no data" page, either when there is no data in Elasticsearch, or there haven't been any data views created yet. This component is meant
|
|
||||||
to be used in those scenarios. It displays an appropriate message to the user and facilitate addition of integrations and creation of data views.
|
|
||||||
|
|
||||||
## Component: `KibanaNoDataPage`
|
|
||||||
|
|
||||||
- uses `hasUserDataView` and `hasData` API from `HasData` service in `data_views` plugin to check for existence of data / data views
|
|
||||||
- uses `onDataViewCreated` callback to be called once the data view has been created
|
|
||||||
- receives (noDataConfig)[https://github.com/elastic/kibana/blob/main/packages/kbn-shared-ux-components/src/page_template/no_data_page/types.ts] as configuration for the page in case of no data
|
|
||||||
- needs to be wrapped in `ServicesContext` provided by the start contract of the `shared_ux` plugin to be used
|
|
||||||
|
|
||||||
## EUI Promotion Status
|
|
||||||
This component is not currently considered for promotion to EUI.
|
|
|
@ -1,93 +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 { action } from '@storybook/addon-actions';
|
|
||||||
import React from 'react';
|
|
||||||
import { servicesFactory, DataServiceFactoryConfig } from '@kbn/shared-ux-storybook';
|
|
||||||
import { SharedUxServicesProvider } from '@kbn/shared-ux-services';
|
|
||||||
import mdx from './kibana_no_data_page.mdx';
|
|
||||||
import { NoDataPageProps } from '../page_template';
|
|
||||||
import { KibanaNoDataPage } from './kibana_no_data_page';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
title: 'No Data/Kibana No Data Page',
|
|
||||||
description: 'A component to display when there is no data available',
|
|
||||||
parameters: {
|
|
||||||
docs: {
|
|
||||||
page: mdx,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const noDataConfig = {
|
|
||||||
solution: 'Analytics',
|
|
||||||
logo: 'logoKibana',
|
|
||||||
action: {
|
|
||||||
elasticAgent: {
|
|
||||||
title: 'Add Integrations',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
docsLink: 'http://docs.elastic.dev',
|
|
||||||
};
|
|
||||||
|
|
||||||
type Params = Pick<NoDataPageProps, 'solution' | 'logo'> & DataServiceFactoryConfig;
|
|
||||||
|
|
||||||
export const PureComponent = (params: Params) => {
|
|
||||||
const { solution, logo, hasESData, hasUserDataView } = params;
|
|
||||||
|
|
||||||
const serviceParams = { hasESData, hasUserDataView, hasDataViews: false };
|
|
||||||
const services = servicesFactory({ ...serviceParams, hasESData, hasUserDataView });
|
|
||||||
return (
|
|
||||||
<SharedUxServicesProvider {...services}>
|
|
||||||
<KibanaNoDataPage
|
|
||||||
onDataViewCreated={action('onDataViewCreated')}
|
|
||||||
noDataConfig={{ ...noDataConfig, solution, logo }}
|
|
||||||
/>
|
|
||||||
</SharedUxServicesProvider>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const PureComponentLoadingState = () => {
|
|
||||||
const dataCheck = () => new Promise<boolean>((resolve, reject) => {});
|
|
||||||
const services = {
|
|
||||||
...servicesFactory({ hasESData: false, hasUserDataView: false, hasDataViews: false }),
|
|
||||||
data: {
|
|
||||||
hasESData: dataCheck,
|
|
||||||
hasUserDataView: dataCheck,
|
|
||||||
hasDataView: dataCheck,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
return (
|
|
||||||
<SharedUxServicesProvider {...services}>
|
|
||||||
<KibanaNoDataPage
|
|
||||||
onDataViewCreated={action('onDataViewCreated')}
|
|
||||||
noDataConfig={noDataConfig}
|
|
||||||
/>
|
|
||||||
</SharedUxServicesProvider>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
PureComponent.argTypes = {
|
|
||||||
solution: {
|
|
||||||
control: 'text',
|
|
||||||
defaultValue: 'Observability',
|
|
||||||
},
|
|
||||||
logo: {
|
|
||||||
control: { type: 'radio' },
|
|
||||||
options: ['logoElastic', 'logoKibana', 'logoCloud', undefined],
|
|
||||||
defaultValue: undefined,
|
|
||||||
},
|
|
||||||
hasESData: {
|
|
||||||
control: 'boolean',
|
|
||||||
defaultValue: false,
|
|
||||||
},
|
|
||||||
hasUserDataView: {
|
|
||||||
control: 'boolean',
|
|
||||||
defaultValue: false,
|
|
||||||
},
|
|
||||||
};
|
|
|
@ -6,27 +6,11 @@
|
||||||
* Side Public License, v 1.
|
* Side Public License, v 1.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
|
||||||
import { withSuspense } from '@kbn/shared-ux-utility';
|
|
||||||
|
|
||||||
export { ToolbarButton, IconButtonGroup, AddFromLibraryButton, ToolbarPopover } from './toolbar';
|
export { ToolbarButton, IconButtonGroup, AddFromLibraryButton, ToolbarPopover } from './toolbar';
|
||||||
export { KibanaPageTemplateSolutionNav } from './page_template/solution_nav';
|
export { KibanaPageTemplateSolutionNav } from './page_template/solution_nav';
|
||||||
export type { KibanaPageTemplateProps } from './page_template';
|
|
||||||
export { KibanaPageTemplate } from './page_template';
|
|
||||||
|
|
||||||
/**
|
// TODO: clintandrewhall - NoDataPageProps is a temporary addition until it is split into its own package
|
||||||
* A `KibanaNoDataPage` component, with service hooks. Consumers should use `React.Suspennse` or the
|
export type { KibanaPageTemplateProps, NoDataPageProps } from './page_template';
|
||||||
* `withSuspense` HOC to load this component.
|
|
||||||
*/
|
|
||||||
export const KibanaNoDataPageLazy = React.lazy(() =>
|
|
||||||
import('./empty_state').then(({ KibanaNoDataPage }) => ({
|
|
||||||
default: KibanaNoDataPage,
|
|
||||||
}))
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
// TODO: clintandrewhall - NoDataConfigPage is a temporary addition until it is split into its own package
|
||||||
* A `KibanaNoDataPage` component. The component is wrapped by the `withSuspense` HOC.
|
export { KibanaPageTemplate, NoDataConfigPage } from './page_template';
|
||||||
* This component can be used directly by consumers and will load the `KibanaNoDataPageLazy` lazily with
|
|
||||||
* a predefined fallback and error boundary.
|
|
||||||
*/
|
|
||||||
export const KibanaNoDataPage = withSuspense(KibanaNoDataPageLazy);
|
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
export type { ServiceFactory, SharedUxServices, SharedUxServicesContext } from './types';
|
export type { ServiceFactory, SharedUxServices, SharedUxServicesContext } from './types';
|
||||||
export type {
|
export type {
|
||||||
|
MockServicesFactoryParams,
|
||||||
SharedUxApplicationService,
|
SharedUxApplicationService,
|
||||||
SharedUxDocLinksService,
|
SharedUxDocLinksService,
|
||||||
SharedUxEditorsService,
|
SharedUxEditorsService,
|
||||||
|
|
|
@ -13,6 +13,7 @@ export type { SharedUxHttpService } from './http';
|
||||||
export type { SharedUxUserPermissionsService } from './permissions';
|
export type { SharedUxUserPermissionsService } from './permissions';
|
||||||
export type { SharedUxPlatformService } from './platform';
|
export type { SharedUxPlatformService } from './platform';
|
||||||
export type { SharedUxDataService } from './data';
|
export type { SharedUxDataService } from './data';
|
||||||
|
export type { MockServicesFactoryParams } from './mock';
|
||||||
|
|
||||||
export { mockServicesFactory, mockServiceFactories } from './mock';
|
export { mockServicesFactory, mockServiceFactories } from './mock';
|
||||||
export { stubServicesFactory, stubServiceFactories } from './stub';
|
export { stubServicesFactory, stubServiceFactories } from './stub';
|
||||||
|
|
|
@ -39,11 +39,9 @@ NPM_MODULE_EXTRA_FILES = [
|
||||||
# eg. "@npm//lodash"
|
# eg. "@npm//lodash"
|
||||||
RUNTIME_DEPS = [
|
RUNTIME_DEPS = [
|
||||||
"@npm//react",
|
"@npm//react",
|
||||||
"@npm//rxjs",
|
"@npm//@testing-library",
|
||||||
"//packages/kbn-i18n",
|
"//packages/kbn-i18n",
|
||||||
"//packages/kbn-shared-ux-services",
|
"//packages/shared-ux/page/kibana_no_data",
|
||||||
"//packages/kbn-shared-ux-components",
|
|
||||||
"//packages/kbn-shared-ux-storybook"
|
|
||||||
]
|
]
|
||||||
|
|
||||||
# In this array place dependencies necessary to build the types, which will include the
|
# In this array place dependencies necessary to build the types, which will include the
|
||||||
|
@ -60,9 +58,7 @@ TYPES_DEPS = [
|
||||||
"@npm//@types/jest",
|
"@npm//@types/jest",
|
||||||
"@npm//@types/react",
|
"@npm//@types/react",
|
||||||
"//packages/kbn-i18n:npm_module_types",
|
"//packages/kbn-i18n:npm_module_types",
|
||||||
"//packages/kbn-shared-ux-services:npm_module_types",
|
"//packages/shared-ux/page/kibana_no_data:npm_module_types",
|
||||||
"//packages/kbn-shared-ux-storybook:npm_module_types",
|
|
||||||
"//packages/kbn-shared-ux-components:npm_module_types",
|
|
||||||
"//packages/kbn-ambient-ui-types",
|
"//packages/kbn-ambient-ui-types",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -1,15 +1,22 @@
|
||||||
---
|
---
|
||||||
id: sharedUX/Components/AnalyticsNoDataPage
|
id: sharedUX/Page/AnalyticsNoDataPage
|
||||||
slug: /shared-ux/components/analytics-no-data-page
|
slug: /shared-ux/page/analytics-no-data-page
|
||||||
title: Analytics "No Data" Page
|
title: Analytics "No Data" Page
|
||||||
summary: An entire page that can be displayed when Kibana "has no data", specifically for Analytics.
|
summary: An entire page that can be displayed when Kibana "has no data", specifically for Analytics.
|
||||||
tags: ['shared-ux', 'component']
|
tags: ['shared-ux', 'component']
|
||||||
date: 2021-12-28
|
date: 2021-12-28
|
||||||
---
|
---
|
||||||
|
|
||||||
## Description
|
This is an Analytics-specific version of `KibanaNoDataPage`, which defaults most of the fields appropriate for Analytics solutions.
|
||||||
|
|
||||||
This is an Analytics-specific version of `KibanaNoDataPage`, which defaults most of the fields to give a consistent set of terms for Analytics solutions.
|
## API
|
||||||
|
|
||||||
|
| Export | Description |
|
||||||
|
|---|---|
|
||||||
|
| `AnalyticsNoDataPageProvider` | Provides contextual services to `AnalyticsNoDataPage`. |
|
||||||
|
| `AnalyticsNoDataPageKibanaProvider` | Maps Kibana dependencies to provide contextual services to `AnalyticsNoDataPage`. |
|
||||||
|
| `AnalyticsNoDataPage` | Uses a `Provider` to access contextual services to populate props on the `AnalyticsNoDataPageComponent`. |
|
||||||
|
| `AnalyticsNoDataPageComponent` | The pure component. |
|
||||||
|
|
||||||
## EUI Promotion Status
|
## EUI Promotion Status
|
||||||
|
|
||||||
|
|
|
@ -1,276 +0,0 @@
|
||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
||||||
|
|
||||||
exports[`AnalyticsNoDataPageComponent renders correctly 1`] = `
|
|
||||||
<AnalyticsNoDataPage
|
|
||||||
intl={
|
|
||||||
Object {
|
|
||||||
"defaultFormats": Object {},
|
|
||||||
"defaultLocale": "en",
|
|
||||||
"formatDate": [Function],
|
|
||||||
"formatHTMLMessage": [Function],
|
|
||||||
"formatMessage": [Function],
|
|
||||||
"formatNumber": [Function],
|
|
||||||
"formatPlural": [Function],
|
|
||||||
"formatRelative": [Function],
|
|
||||||
"formatTime": [Function],
|
|
||||||
"formats": Object {
|
|
||||||
"date": Object {
|
|
||||||
"full": Object {
|
|
||||||
"day": "numeric",
|
|
||||||
"month": "long",
|
|
||||||
"weekday": "long",
|
|
||||||
"year": "numeric",
|
|
||||||
},
|
|
||||||
"long": Object {
|
|
||||||
"day": "numeric",
|
|
||||||
"month": "long",
|
|
||||||
"year": "numeric",
|
|
||||||
},
|
|
||||||
"medium": Object {
|
|
||||||
"day": "numeric",
|
|
||||||
"month": "short",
|
|
||||||
"year": "numeric",
|
|
||||||
},
|
|
||||||
"short": Object {
|
|
||||||
"day": "numeric",
|
|
||||||
"month": "numeric",
|
|
||||||
"year": "2-digit",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"number": Object {
|
|
||||||
"currency": Object {
|
|
||||||
"style": "currency",
|
|
||||||
},
|
|
||||||
"percent": Object {
|
|
||||||
"style": "percent",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"relative": Object {
|
|
||||||
"days": Object {
|
|
||||||
"units": "day",
|
|
||||||
},
|
|
||||||
"hours": Object {
|
|
||||||
"units": "hour",
|
|
||||||
},
|
|
||||||
"minutes": Object {
|
|
||||||
"units": "minute",
|
|
||||||
},
|
|
||||||
"months": Object {
|
|
||||||
"units": "month",
|
|
||||||
},
|
|
||||||
"seconds": Object {
|
|
||||||
"units": "second",
|
|
||||||
},
|
|
||||||
"years": Object {
|
|
||||||
"units": "year",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"time": Object {
|
|
||||||
"full": Object {
|
|
||||||
"hour": "numeric",
|
|
||||||
"minute": "numeric",
|
|
||||||
"second": "numeric",
|
|
||||||
"timeZoneName": "short",
|
|
||||||
},
|
|
||||||
"long": Object {
|
|
||||||
"hour": "numeric",
|
|
||||||
"minute": "numeric",
|
|
||||||
"second": "numeric",
|
|
||||||
"timeZoneName": "short",
|
|
||||||
},
|
|
||||||
"medium": Object {
|
|
||||||
"hour": "numeric",
|
|
||||||
"minute": "numeric",
|
|
||||||
"second": "numeric",
|
|
||||||
},
|
|
||||||
"short": Object {
|
|
||||||
"hour": "numeric",
|
|
||||||
"minute": "numeric",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"formatters": Object {
|
|
||||||
"getDateTimeFormat": [Function],
|
|
||||||
"getMessageFormat": [Function],
|
|
||||||
"getNumberFormat": [Function],
|
|
||||||
"getPluralFormat": [Function],
|
|
||||||
"getRelativeFormat": [Function],
|
|
||||||
},
|
|
||||||
"locale": "en",
|
|
||||||
"messages": Object {},
|
|
||||||
"now": [Function],
|
|
||||||
"onError": [Function],
|
|
||||||
"textComponent": Symbol(react.fragment),
|
|
||||||
"timeZone": null,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
kibanaGuideDocLink="http://www.test.com"
|
|
||||||
onDataViewCreated={[MockFunction]}
|
|
||||||
>
|
|
||||||
<ForwardRef
|
|
||||||
noDataConfig={
|
|
||||||
Object {
|
|
||||||
"action": Object {
|
|
||||||
"elasticAgent": Object {
|
|
||||||
"data-test-subj": "kbnOverviewAddIntegrations",
|
|
||||||
"description": "Use Elastic Agent to collect data and build out Analytics solutions.",
|
|
||||||
"title": "Add integrations",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"docsLink": "http://www.test.com",
|
|
||||||
"logo": "logoKibana",
|
|
||||||
"pageTitle": "Welcome to Analytics!",
|
|
||||||
"solution": "Analytics",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onDataViewCreated={[MockFunction]}
|
|
||||||
>
|
|
||||||
<EuiErrorBoundary>
|
|
||||||
<Suspense
|
|
||||||
fallback={<Fallback />}
|
|
||||||
>
|
|
||||||
<Fallback>
|
|
||||||
<div
|
|
||||||
css={
|
|
||||||
Object {
|
|
||||||
"map": undefined,
|
|
||||||
"name": "1q0vk5q",
|
|
||||||
"next": undefined,
|
|
||||||
"styles": "
|
|
||||||
text-align: center;
|
|
||||||
",
|
|
||||||
"toString": [Function],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<EuiLoadingSpinner>
|
|
||||||
<span
|
|
||||||
aria-label="Loading"
|
|
||||||
css="unknown styles"
|
|
||||||
role="progressbar"
|
|
||||||
>
|
|
||||||
<Insertion
|
|
||||||
cache={
|
|
||||||
Object {
|
|
||||||
"insert": [Function],
|
|
||||||
"inserted": Object {
|
|
||||||
"155ohbx-euiLoadingSpinner-m": true,
|
|
||||||
"animation-c24ia8": true,
|
|
||||||
},
|
|
||||||
"key": "css",
|
|
||||||
"nonce": undefined,
|
|
||||||
"registered": Object {},
|
|
||||||
"sheet": StyleSheet {
|
|
||||||
"_alreadyInsertedOrderInsensitiveRule": true,
|
|
||||||
"_insertTag": [Function],
|
|
||||||
"before": null,
|
|
||||||
"container": <head>
|
|
||||||
<style
|
|
||||||
data-emotion="css"
|
|
||||||
data-s=""
|
|
||||||
>
|
|
||||||
|
|
||||||
.css-155ohbx-euiLoadingSpinner-m{-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0;display:inline-block;border-radius:50%;border:2px solid #D3DAE6;border-color:#07C #D3DAE6 #D3DAE6 #D3DAE6;width:16px;height:16px;border-width:calc(1px * 1.5);}/*# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9jb21wb25lbnRzL2xvYWRpbmcvbG9hZGluZ19zcGlubmVyLnN0eWxlcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUE0RFUiLCJmaWxlIjoiLi4vLi4vLi4vc3JjL2NvbXBvbmVudHMvbG9hZGluZy9sb2FkaW5nX3NwaW5uZXIuc3R5bGVzLnRzIiwic291cmNlc0NvbnRlbnQiOlsiLypcbiAqIENvcHlyaWdodCBFbGFzdGljc2VhcmNoIEIuVi4gYW5kL29yIGxpY2Vuc2VkIHRvIEVsYXN0aWNzZWFyY2ggQi5WLiB1bmRlciBvbmVcbiAqIG9yIG1vcmUgY29udHJpYnV0b3IgbGljZW5zZSBhZ3JlZW1lbnRzLiBMaWNlbnNlZCB1bmRlciB0aGUgRWxhc3RpYyBMaWNlbnNlXG4gKiAyLjAgYW5kIHRoZSBTZXJ2ZXIgU2lkZSBQdWJsaWMgTGljZW5zZSwgdiAxOyB5b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdFxuICogaW4gY29tcGxpYW5jZSB3aXRoLCBhdCB5b3VyIGVsZWN0aW9uLCB0aGUgRWxhc3RpYyBMaWNlbnNlIDIuMCBvciB0aGUgU2VydmVyXG4gKiBTaWRlIFB1YmxpYyBMaWNlbnNlLCB2IDEuXG4gKi9cblxuaW1wb3J0IHsgY3NzLCBrZXlmcmFtZXMgfSBmcm9tICdAZW1vdGlvbi9yZWFjdCc7XG5pbXBvcnQgeyBfRXVpVGhlbWVTaXplLCBldWlDYW5BbmltYXRlIH0gZnJvbSAnLi4vLi4vZ2xvYmFsX3N0eWxpbmcnO1xuaW1wb3J0IHsgVXNlRXVpVGhlbWUgfSBmcm9tICcuLi8uLi9zZXJ2aWNlcyc7XG5pbXBvcnQgeyBFdWlMb2FkaW5nU3Bpbm5lclNpemUgfSBmcm9tICcuL2xvYWRpbmdfc3Bpbm5lcic7XG5cbmNvbnN0IF9sb2FkaW5nU3Bpbm5lciA9IGtleWZyYW1lc2BcbiAgZnJvbSB7XG4gICAgdHJhbnNmb3JtOiByb3RhdGUoMGRlZyk7XG4gIH1cblxuICB0byB7XG4gICAgdHJhbnNmb3JtOiByb3RhdGUoMzU5ZGVnKTtcbiAgfVxuYDtcblxuY29uc3Qgc3Bpbm5lclNpemVzOiB7XG4gIFtzaXplIGluIEV1aUxvYWRpbmdTcGlubmVyU2l6ZV06IF9FdWlUaGVtZVNpemU7XG59ID0ge1xuICBzOiAnbScsXG4gIG06ICdiYXNlJyxcbiAgbDogJ2wnLFxuICB4bDogJ3hsJyxcbiAgeHhsOiAneHhsJyxcbn07XG5cbmNvbnN0IHNwaW5uZXJDb2xvcnMgPSAobWFpbjogc3RyaW5nLCBoaWdobGlnaHQ6IHN0cmluZykgPT4ge1xuICByZXR1cm4gYCR7aGlnaGxpZ2h0fSAke21haW59ICR7bWFpbn0gJHttYWlufWA7XG59O1xuXG5leHBvcnQgY29uc3QgZXVpTG9hZGluZ1NwaW5uZXJTdHlsZXMgPSAoeyBldWlUaGVtZSB9OiBVc2VFdWlUaGVtZSkgPT4ge1xuICByZXR1cm4ge1xuICAgIGV1aUxvYWRpbmdTcGlubmVyOiBjc3NgXG4gICAgICBmbGV4LXNocmluazogMDsgLy8gRW5zdXJlcyBpdCBuZXZlciBzY2FsZXMgZG93biBiZWxvdyBpdHMgaW50ZW5kZWQgc2l6ZVxuICAgICAgZGlzcGxheTogaW5saW5lLWJsb2NrO1xuICAgICAgYm9yZGVyLXJhZGl1czogNTAlO1xuICAgICAgYm9yZGVyOiAke2V1aVRoZW1lLmJvcmRlci50aGlja307XG4gICAgICBib3JkZXItY29sb3I6ICR7c3Bpbm5lckNvbG9ycyhcbiAgICAgICAgZXVpVGhlbWUuYm9yZGVyLmNvbG9yLFxuICAgICAgICBldWlUaGVtZS5jb2xvcnMucHJpbWFyeVxuICAgICAgKX07XG5cbiAgICAgICR7ZXVpQ2FuQW5pbWF0ZX0ge1xuICAgICAgICBhbmltYXRpb246ICR7X2xvYWRpbmdTcGlubmVyfSAwLjZzIGluZmluaXRlIGxpbmVhcjtcbiAgICAgIH1cbiAgICBgLFxuXG4gICAgLy8gU2l6ZXNcbiAgICBzOiBjc3NgXG4gICAgICB3aWR0aDogJHtldWlUaGVtZS5zaXplW3NwaW5uZXJTaXplcy5zXX07XG4gICAgICBoZWlnaHQ6ICR7ZXVpVGhlbWUuc2l6ZVtzcGlubmVyU2l6ZXMuc119O1xuICAgICAgYm9yZGVyLXdpZHRoOiBjYWxjKCR7ZXVpVGhlbWUuYm9yZGVyLndpZHRoLnRoaW59ICogMS41KTtcbiAgICBgLFxuICAgIG06IGNzc2BcbiAgICAgIHdpZHRoOiAke2V1aVRoZW1lLnNpemVbc3Bpbm5lclNpemVzLm1dfTtcbiAgICAgIGhlaWdodDogJHtldWlUaGVtZS5zaXplW3NwaW5uZXJTaXplcy5tXX07XG4gICAgICBib3JkZXItd2lkdGg6IGNhbGMoJHtldWlUaGVtZS5ib3JkZXIud2lkdGgudGhpbn0gKiAxLjUpO1xuICAgIGAsXG4gICAgbDogY3NzYFxuICAgICAgd2lkdGg6ICR7ZXVpVGhlbWUuc2l6ZVtzcGlubmVyU2l6ZXMubF19O1xuICAgICAgaGVpZ2h0OiAke2V1aVRoZW1lLnNpemVbc3Bpbm5lclNpemVzLmxdfTtcbiAgICBgLFxuICAgIHhsOiBjc3NgXG4gICAgICB3aWR0aDogJHtldWlUaGVtZS5zaXplW3NwaW5uZXJTaXplcy54bF19O1xuICAgICAgaGVpZ2h0OiAke2V1aVRoZW1lLnNpemVbc3Bpbm5lclNpemVzLnhsXX07XG4gICAgYCxcbiAgICB4eGw6IGNzc2BcbiAgICAgIHdpZHRoOiAke2V1aVRoZW1lLnNpemVbc3Bpbm5lclNpemVzLnh4bF19O1xuICAgICAgaGVpZ2h0OiAke2V1aVRoZW1lLnNpemVbc3Bpbm5lclNpemVzLnh4bF19O1xuICAgIGAsXG4gIH07XG59O1xuIl19 */
|
|
||||||
</style>
|
|
||||||
<style
|
|
||||||
data-emotion="css"
|
|
||||||
data-s=""
|
|
||||||
>
|
|
||||||
|
|
||||||
@media screen and (prefers-reduced-motion: no-preference){.css-155ohbx-euiLoadingSpinner-m{-webkit-animation:animation-c24ia8 0.6s infinite linear;animation:animation-c24ia8 0.6s infinite linear;}}/*# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9jb21wb25lbnRzL2xvYWRpbmcvbG9hZGluZ19zcGlubmVyLnN0eWxlcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUE0RFUiLCJmaWxlIjoiLi4vLi4vLi4vc3JjL2NvbXBvbmVudHMvbG9hZGluZy9sb2FkaW5nX3NwaW5uZXIuc3R5bGVzLnRzIiwic291cmNlc0NvbnRlbnQiOlsiLypcbiAqIENvcHlyaWdodCBFbGFzdGljc2VhcmNoIEIuVi4gYW5kL29yIGxpY2Vuc2VkIHRvIEVsYXN0aWNzZWFyY2ggQi5WLiB1bmRlciBvbmVcbiAqIG9yIG1vcmUgY29udHJpYnV0b3IgbGljZW5zZSBhZ3JlZW1lbnRzLiBMaWNlbnNlZCB1bmRlciB0aGUgRWxhc3RpYyBMaWNlbnNlXG4gKiAyLjAgYW5kIHRoZSBTZXJ2ZXIgU2lkZSBQdWJsaWMgTGljZW5zZSwgdiAxOyB5b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdFxuICogaW4gY29tcGxpYW5jZSB3aXRoLCBhdCB5b3VyIGVsZWN0aW9uLCB0aGUgRWxhc3RpYyBMaWNlbnNlIDIuMCBvciB0aGUgU2VydmVyXG4gKiBTaWRlIFB1YmxpYyBMaWNlbnNlLCB2IDEuXG4gKi9cblxuaW1wb3J0IHsgY3NzLCBrZXlmcmFtZXMgfSBmcm9tICdAZW1vdGlvbi9yZWFjdCc7XG5pbXBvcnQgeyBfRXVpVGhlbWVTaXplLCBldWlDYW5BbmltYXRlIH0gZnJvbSAnLi4vLi4vZ2xvYmFsX3N0eWxpbmcnO1xuaW1wb3J0IHsgVXNlRXVpVGhlbWUgfSBmcm9tICcuLi8uLi9zZXJ2aWNlcyc7XG5pbXBvcnQgeyBFdWlMb2FkaW5nU3Bpbm5lclNpemUgfSBmcm9tICcuL2xvYWRpbmdfc3Bpbm5lcic7XG5cbmNvbnN0IF9sb2FkaW5nU3Bpbm5lciA9IGtleWZyYW1lc2BcbiAgZnJvbSB7XG4gICAgdHJhbnNmb3JtOiByb3RhdGUoMGRlZyk7XG4gIH1cblxuICB0byB7XG4gICAgdHJhbnNmb3JtOiByb3RhdGUoMzU5ZGVnKTtcbiAgfVxuYDtcblxuY29uc3Qgc3Bpbm5lclNpemVzOiB7XG4gIFtzaXplIGluIEV1aUxvYWRpbmdTcGlubmVyU2l6ZV06IF9FdWlUaGVtZVNpemU7XG59ID0ge1xuICBzOiAnbScsXG4gIG06ICdiYXNlJyxcbiAgbDogJ2wnLFxuICB4bDogJ3hsJyxcbiAgeHhsOiAneHhsJyxcbn07XG5cbmNvbnN0IHNwaW5uZXJDb2xvcnMgPSAobWFpbjogc3RyaW5nLCBoaWdobGlnaHQ6IHN0cmluZykgPT4ge1xuICByZXR1cm4gYCR7aGlnaGxpZ2h0fSAke21haW59ICR7bWFpbn0gJHttYWlufWA7XG59O1xuXG5leHBvcnQgY29uc3QgZXVpTG9hZGluZ1NwaW5uZXJTdHlsZXMgPSAoeyBldWlUaGVtZSB9OiBVc2VFdWlUaGVtZSkgPT4ge1xuICByZXR1cm4ge1xuICAgIGV1aUxvYWRpbmdTcGlubmVyOiBjc3NgXG4gICAgICBmbGV4LXNocmluazogMDsgLy8gRW5zdXJlcyBpdCBuZXZlciBzY2FsZXMgZG93biBiZWxvdyBpdHMgaW50ZW5kZWQgc2l6ZVxuICAgICAgZGlzcGxheTogaW5saW5lLWJsb2NrO1xuICAgICAgYm9yZGVyLXJhZGl1czogNTAlO1xuICAgICAgYm9yZGVyOiAke2V1aVRoZW1lLmJvcmRlci50aGlja307XG4gICAgICBib3JkZXItY29sb3I6ICR7c3Bpbm5lckNvbG9ycyhcbiAgICAgICAgZXVpVGhlbWUuYm9yZGVyLmNvbG9yLFxuICAgICAgICBldWlUaGVtZS5jb2xvcnMucHJpbWFyeVxuICAgICAgKX07XG5cbiAgICAgICR7ZXVpQ2FuQW5pbWF0ZX0ge1xuICAgICAgICBhbmltYXRpb246ICR7X2xvYWRpbmdTcGlubmVyfSAwLjZzIGluZmluaXRlIGxpbmVhcjtcbiAgICAgIH1cbiAgICBgLFxuXG4gICAgLy8gU2l6ZXNcbiAgICBzOiBjc3NgXG4gICAgICB3aWR0aDogJHtldWlUaGVtZS5zaXplW3NwaW5uZXJTaXplcy5zXX07XG4gICAgICBoZWlnaHQ6ICR7ZXVpVGhlbWUuc2l6ZVtzcGlubmVyU2l6ZXMuc119O1xuICAgICAgYm9yZGVyLXdpZHRoOiBjYWxjKCR7ZXVpVGhlbWUuYm9yZGVyLndpZHRoLnRoaW59ICogMS41KTtcbiAgICBgLFxuICAgIG06IGNzc2BcbiAgICAgIHdpZHRoOiAke2V1aVRoZW1lLnNpemVbc3Bpbm5lclNpemVzLm1dfTtcbiAgICAgIGhlaWdodDogJHtldWlUaGVtZS5zaXplW3NwaW5uZXJTaXplcy5tXX07XG4gICAgICBib3JkZXItd2lkdGg6IGNhbGMoJHtldWlUaGVtZS5ib3JkZXIud2lkdGgudGhpbn0gKiAxLjUpO1xuICAgIGAsXG4gICAgbDogY3NzYFxuICAgICAgd2lkdGg6ICR7ZXVpVGhlbWUuc2l6ZVtzcGlubmVyU2l6ZXMubF19O1xuICAgICAgaGVpZ2h0OiAke2V1aVRoZW1lLnNpemVbc3Bpbm5lclNpemVzLmxdfTtcbiAgICBgLFxuICAgIHhsOiBjc3NgXG4gICAgICB3aWR0aDogJHtldWlUaGVtZS5zaXplW3NwaW5uZXJTaXplcy54bF19O1xuICAgICAgaGVpZ2h0OiAke2V1aVRoZW1lLnNpemVbc3Bpbm5lclNpemVzLnhsXX07XG4gICAgYCxcbiAgICB4eGw6IGNzc2BcbiAgICAgIHdpZHRoOiAke2V1aVRoZW1lLnNpemVbc3Bpbm5lclNpemVzLnh4bF19O1xuICAgICAgaGVpZ2h0OiAke2V1aVRoZW1lLnNpemVbc3Bpbm5lclNpemVzLnh4bF19O1xuICAgIGAsXG4gIH07XG59O1xuIl19 */
|
|
||||||
</style>
|
|
||||||
<style
|
|
||||||
data-emotion="css"
|
|
||||||
data-s=""
|
|
||||||
>
|
|
||||||
|
|
||||||
@-webkit-keyframes animation-c24ia8{from{-webkit-transform:rotate(0deg);-moz-transform:rotate(0deg);-ms-transform:rotate(0deg);transform:rotate(0deg);}to{-webkit-transform:rotate(359deg);-moz-transform:rotate(359deg);-ms-transform:rotate(359deg);transform:rotate(359deg);}}
|
|
||||||
</style>
|
|
||||||
<style
|
|
||||||
data-emotion="css"
|
|
||||||
data-s=""
|
|
||||||
>
|
|
||||||
|
|
||||||
@keyframes animation-c24ia8{from{-webkit-transform:rotate(0deg);-moz-transform:rotate(0deg);-ms-transform:rotate(0deg);transform:rotate(0deg);}to{-webkit-transform:rotate(359deg);-moz-transform:rotate(359deg);-ms-transform:rotate(359deg);transform:rotate(359deg);}}
|
|
||||||
</style>
|
|
||||||
<style
|
|
||||||
data-styled="active"
|
|
||||||
data-styled-version="5.1.0"
|
|
||||||
/>
|
|
||||||
</head>,
|
|
||||||
"ctr": 4,
|
|
||||||
"insertionPoint": undefined,
|
|
||||||
"isSpeedy": false,
|
|
||||||
"key": "css",
|
|
||||||
"nonce": undefined,
|
|
||||||
"prepend": undefined,
|
|
||||||
"tags": Array [
|
|
||||||
<style
|
|
||||||
data-emotion="css"
|
|
||||||
data-s=""
|
|
||||||
>
|
|
||||||
|
|
||||||
.css-155ohbx-euiLoadingSpinner-m{-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0;display:inline-block;border-radius:50%;border:2px solid #D3DAE6;border-color:#07C #D3DAE6 #D3DAE6 #D3DAE6;width:16px;height:16px;border-width:calc(1px * 1.5);}/*# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9jb21wb25lbnRzL2xvYWRpbmcvbG9hZGluZ19zcGlubmVyLnN0eWxlcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUE0RFUiLCJmaWxlIjoiLi4vLi4vLi4vc3JjL2NvbXBvbmVudHMvbG9hZGluZy9sb2FkaW5nX3NwaW5uZXIuc3R5bGVzLnRzIiwic291cmNlc0NvbnRlbnQiOlsiLypcbiAqIENvcHlyaWdodCBFbGFzdGljc2VhcmNoIEIuVi4gYW5kL29yIGxpY2Vuc2VkIHRvIEVsYXN0aWNzZWFyY2ggQi5WLiB1bmRlciBvbmVcbiAqIG9yIG1vcmUgY29udHJpYnV0b3IgbGljZW5zZSBhZ3JlZW1lbnRzLiBMaWNlbnNlZCB1bmRlciB0aGUgRWxhc3RpYyBMaWNlbnNlXG4gKiAyLjAgYW5kIHRoZSBTZXJ2ZXIgU2lkZSBQdWJsaWMgTGljZW5zZSwgdiAxOyB5b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdFxuICogaW4gY29tcGxpYW5jZSB3aXRoLCBhdCB5b3VyIGVsZWN0aW9uLCB0aGUgRWxhc3RpYyBMaWNlbnNlIDIuMCBvciB0aGUgU2VydmVyXG4gKiBTaWRlIFB1YmxpYyBMaWNlbnNlLCB2IDEuXG4gKi9cblxuaW1wb3J0IHsgY3NzLCBrZXlmcmFtZXMgfSBmcm9tICdAZW1vdGlvbi9yZWFjdCc7XG5pbXBvcnQgeyBfRXVpVGhlbWVTaXplLCBldWlDYW5BbmltYXRlIH0gZnJvbSAnLi4vLi4vZ2xvYmFsX3N0eWxpbmcnO1xuaW1wb3J0IHsgVXNlRXVpVGhlbWUgfSBmcm9tICcuLi8uLi9zZXJ2aWNlcyc7XG5pbXBvcnQgeyBFdWlMb2FkaW5nU3Bpbm5lclNpemUgfSBmcm9tICcuL2xvYWRpbmdfc3Bpbm5lcic7XG5cbmNvbnN0IF9sb2FkaW5nU3Bpbm5lciA9IGtleWZyYW1lc2BcbiAgZnJvbSB7XG4gICAgdHJhbnNmb3JtOiByb3RhdGUoMGRlZyk7XG4gIH1cblxuICB0byB7XG4gICAgdHJhbnNmb3JtOiByb3RhdGUoMzU5ZGVnKTtcbiAgfVxuYDtcblxuY29uc3Qgc3Bpbm5lclNpemVzOiB7XG4gIFtzaXplIGluIEV1aUxvYWRpbmdTcGlubmVyU2l6ZV06IF9FdWlUaGVtZVNpemU7XG59ID0ge1xuICBzOiAnbScsXG4gIG06ICdiYXNlJyxcbiAgbDogJ2wnLFxuICB4bDogJ3hsJyxcbiAgeHhsOiAneHhsJyxcbn07XG5cbmNvbnN0IHNwaW5uZXJDb2xvcnMgPSAobWFpbjogc3RyaW5nLCBoaWdobGlnaHQ6IHN0cmluZykgPT4ge1xuICByZXR1cm4gYCR7aGlnaGxpZ2h0fSAke21haW59ICR7bWFpbn0gJHttYWlufWA7XG59O1xuXG5leHBvcnQgY29uc3QgZXVpTG9hZGluZ1NwaW5uZXJTdHlsZXMgPSAoeyBldWlUaGVtZSB9OiBVc2VFdWlUaGVtZSkgPT4ge1xuICByZXR1cm4ge1xuICAgIGV1aUxvYWRpbmdTcGlubmVyOiBjc3NgXG4gICAgICBmbGV4LXNocmluazogMDsgLy8gRW5zdXJlcyBpdCBuZXZlciBzY2FsZXMgZG93biBiZWxvdyBpdHMgaW50ZW5kZWQgc2l6ZVxuICAgICAgZGlzcGxheTogaW5saW5lLWJsb2NrO1xuICAgICAgYm9yZGVyLXJhZGl1czogNTAlO1xuICAgICAgYm9yZGVyOiAke2V1aVRoZW1lLmJvcmRlci50aGlja307XG4gICAgICBib3JkZXItY29sb3I6ICR7c3Bpbm5lckNvbG9ycyhcbiAgICAgICAgZXVpVGhlbWUuYm9yZGVyLmNvbG9yLFxuICAgICAgICBldWlUaGVtZS5jb2xvcnMucHJpbWFyeVxuICAgICAgKX07XG5cbiAgICAgICR7ZXVpQ2FuQW5pbWF0ZX0ge1xuICAgICAgICBhbmltYXRpb246ICR7X2xvYWRpbmdTcGlubmVyfSAwLjZzIGluZmluaXRlIGxpbmVhcjtcbiAgICAgIH1cbiAgICBgLFxuXG4gICAgLy8gU2l6ZXNcbiAgICBzOiBjc3NgXG4gICAgICB3aWR0aDogJHtldWlUaGVtZS5zaXplW3NwaW5uZXJTaXplcy5zXX07XG4gICAgICBoZWlnaHQ6ICR7ZXVpVGhlbWUuc2l6ZVtzcGlubmVyU2l6ZXMuc119O1xuICAgICAgYm9yZGVyLXdpZHRoOiBjYWxjKCR7ZXVpVGhlbWUuYm9yZGVyLndpZHRoLnRoaW59ICogMS41KTtcbiAgICBgLFxuICAgIG06IGNzc2BcbiAgICAgIHdpZHRoOiAke2V1aVRoZW1lLnNpemVbc3Bpbm5lclNpemVzLm1dfTtcbiAgICAgIGhlaWdodDogJHtldWlUaGVtZS5zaXplW3NwaW5uZXJTaXplcy5tXX07XG4gICAgICBib3JkZXItd2lkdGg6IGNhbGMoJHtldWlUaGVtZS5ib3JkZXIud2lkdGgudGhpbn0gKiAxLjUpO1xuICAgIGAsXG4gICAgbDogY3NzYFxuICAgICAgd2lkdGg6ICR7ZXVpVGhlbWUuc2l6ZVtzcGlubmVyU2l6ZXMubF19O1xuICAgICAgaGVpZ2h0OiAke2V1aVRoZW1lLnNpemVbc3Bpbm5lclNpemVzLmxdfTtcbiAgICBgLFxuICAgIHhsOiBjc3NgXG4gICAgICB3aWR0aDogJHtldWlUaGVtZS5zaXplW3NwaW5uZXJTaXplcy54bF19O1xuICAgICAgaGVpZ2h0OiAke2V1aVRoZW1lLnNpemVbc3Bpbm5lclNpemVzLnhsXX07XG4gICAgYCxcbiAgICB4eGw6IGNzc2BcbiAgICAgIHdpZHRoOiAke2V1aVRoZW1lLnNpemVbc3Bpbm5lclNpemVzLnh4bF19O1xuICAgICAgaGVpZ2h0OiAke2V1aVRoZW1lLnNpemVbc3Bpbm5lclNpemVzLnh4bF19O1xuICAgIGAsXG4gIH07XG59O1xuIl19 */
|
|
||||||
</style>,
|
|
||||||
<style
|
|
||||||
data-emotion="css"
|
|
||||||
data-s=""
|
|
||||||
>
|
|
||||||
|
|
||||||
@media screen and (prefers-reduced-motion: no-preference){.css-155ohbx-euiLoadingSpinner-m{-webkit-animation:animation-c24ia8 0.6s infinite linear;animation:animation-c24ia8 0.6s infinite linear;}}/*# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9jb21wb25lbnRzL2xvYWRpbmcvbG9hZGluZ19zcGlubmVyLnN0eWxlcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUE0RFUiLCJmaWxlIjoiLi4vLi4vLi4vc3JjL2NvbXBvbmVudHMvbG9hZGluZy9sb2FkaW5nX3NwaW5uZXIuc3R5bGVzLnRzIiwic291cmNlc0NvbnRlbnQiOlsiLypcbiAqIENvcHlyaWdodCBFbGFzdGljc2VhcmNoIEIuVi4gYW5kL29yIGxpY2Vuc2VkIHRvIEVsYXN0aWNzZWFyY2ggQi5WLiB1bmRlciBvbmVcbiAqIG9yIG1vcmUgY29udHJpYnV0b3IgbGljZW5zZSBhZ3JlZW1lbnRzLiBMaWNlbnNlZCB1bmRlciB0aGUgRWxhc3RpYyBMaWNlbnNlXG4gKiAyLjAgYW5kIHRoZSBTZXJ2ZXIgU2lkZSBQdWJsaWMgTGljZW5zZSwgdiAxOyB5b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdFxuICogaW4gY29tcGxpYW5jZSB3aXRoLCBhdCB5b3VyIGVsZWN0aW9uLCB0aGUgRWxhc3RpYyBMaWNlbnNlIDIuMCBvciB0aGUgU2VydmVyXG4gKiBTaWRlIFB1YmxpYyBMaWNlbnNlLCB2IDEuXG4gKi9cblxuaW1wb3J0IHsgY3NzLCBrZXlmcmFtZXMgfSBmcm9tICdAZW1vdGlvbi9yZWFjdCc7XG5pbXBvcnQgeyBfRXVpVGhlbWVTaXplLCBldWlDYW5BbmltYXRlIH0gZnJvbSAnLi4vLi4vZ2xvYmFsX3N0eWxpbmcnO1xuaW1wb3J0IHsgVXNlRXVpVGhlbWUgfSBmcm9tICcuLi8uLi9zZXJ2aWNlcyc7XG5pbXBvcnQgeyBFdWlMb2FkaW5nU3Bpbm5lclNpemUgfSBmcm9tICcuL2xvYWRpbmdfc3Bpbm5lcic7XG5cbmNvbnN0IF9sb2FkaW5nU3Bpbm5lciA9IGtleWZyYW1lc2BcbiAgZnJvbSB7XG4gICAgdHJhbnNmb3JtOiByb3RhdGUoMGRlZyk7XG4gIH1cblxuICB0byB7XG4gICAgdHJhbnNmb3JtOiByb3RhdGUoMzU5ZGVnKTtcbiAgfVxuYDtcblxuY29uc3Qgc3Bpbm5lclNpemVzOiB7XG4gIFtzaXplIGluIEV1aUxvYWRpbmdTcGlubmVyU2l6ZV06IF9FdWlUaGVtZVNpemU7XG59ID0ge1xuICBzOiAnbScsXG4gIG06ICdiYXNlJyxcbiAgbDogJ2wnLFxuICB4bDogJ3hsJyxcbiAgeHhsOiAneHhsJyxcbn07XG5cbmNvbnN0IHNwaW5uZXJDb2xvcnMgPSAobWFpbjogc3RyaW5nLCBoaWdobGlnaHQ6IHN0cmluZykgPT4ge1xuICByZXR1cm4gYCR7aGlnaGxpZ2h0fSAke21haW59ICR7bWFpbn0gJHttYWlufWA7XG59O1xuXG5leHBvcnQgY29uc3QgZXVpTG9hZGluZ1NwaW5uZXJTdHlsZXMgPSAoeyBldWlUaGVtZSB9OiBVc2VFdWlUaGVtZSkgPT4ge1xuICByZXR1cm4ge1xuICAgIGV1aUxvYWRpbmdTcGlubmVyOiBjc3NgXG4gICAgICBmbGV4LXNocmluazogMDsgLy8gRW5zdXJlcyBpdCBuZXZlciBzY2FsZXMgZG93biBiZWxvdyBpdHMgaW50ZW5kZWQgc2l6ZVxuICAgICAgZGlzcGxheTogaW5saW5lLWJsb2NrO1xuICAgICAgYm9yZGVyLXJhZGl1czogNTAlO1xuICAgICAgYm9yZGVyOiAke2V1aVRoZW1lLmJvcmRlci50aGlja307XG4gICAgICBib3JkZXItY29sb3I6ICR7c3Bpbm5lckNvbG9ycyhcbiAgICAgICAgZXVpVGhlbWUuYm9yZGVyLmNvbG9yLFxuICAgICAgICBldWlUaGVtZS5jb2xvcnMucHJpbWFyeVxuICAgICAgKX07XG5cbiAgICAgICR7ZXVpQ2FuQW5pbWF0ZX0ge1xuICAgICAgICBhbmltYXRpb246ICR7X2xvYWRpbmdTcGlubmVyfSAwLjZzIGluZmluaXRlIGxpbmVhcjtcbiAgICAgIH1cbiAgICBgLFxuXG4gICAgLy8gU2l6ZXNcbiAgICBzOiBjc3NgXG4gICAgICB3aWR0aDogJHtldWlUaGVtZS5zaXplW3NwaW5uZXJTaXplcy5zXX07XG4gICAgICBoZWlnaHQ6ICR7ZXVpVGhlbWUuc2l6ZVtzcGlubmVyU2l6ZXMuc119O1xuICAgICAgYm9yZGVyLXdpZHRoOiBjYWxjKCR7ZXVpVGhlbWUuYm9yZGVyLndpZHRoLnRoaW59ICogMS41KTtcbiAgICBgLFxuICAgIG06IGNzc2BcbiAgICAgIHdpZHRoOiAke2V1aVRoZW1lLnNpemVbc3Bpbm5lclNpemVzLm1dfTtcbiAgICAgIGhlaWdodDogJHtldWlUaGVtZS5zaXplW3NwaW5uZXJTaXplcy5tXX07XG4gICAgICBib3JkZXItd2lkdGg6IGNhbGMoJHtldWlUaGVtZS5ib3JkZXIud2lkdGgudGhpbn0gKiAxLjUpO1xuICAgIGAsXG4gICAgbDogY3NzYFxuICAgICAgd2lkdGg6ICR7ZXVpVGhlbWUuc2l6ZVtzcGlubmVyU2l6ZXMubF19O1xuICAgICAgaGVpZ2h0OiAke2V1aVRoZW1lLnNpemVbc3Bpbm5lclNpemVzLmxdfTtcbiAgICBgLFxuICAgIHhsOiBjc3NgXG4gICAgICB3aWR0aDogJHtldWlUaGVtZS5zaXplW3NwaW5uZXJTaXplcy54bF19O1xuICAgICAgaGVpZ2h0OiAke2V1aVRoZW1lLnNpemVbc3Bpbm5lclNpemVzLnhsXX07XG4gICAgYCxcbiAgICB4eGw6IGNzc2BcbiAgICAgIHdpZHRoOiAke2V1aVRoZW1lLnNpemVbc3Bpbm5lclNpemVzLnh4bF19O1xuICAgICAgaGVpZ2h0OiAke2V1aVRoZW1lLnNpemVbc3Bpbm5lclNpemVzLnh4bF19O1xuICAgIGAsXG4gIH07XG59O1xuIl19 */
|
|
||||||
</style>,
|
|
||||||
<style
|
|
||||||
data-emotion="css"
|
|
||||||
data-s=""
|
|
||||||
>
|
|
||||||
|
|
||||||
@-webkit-keyframes animation-c24ia8{from{-webkit-transform:rotate(0deg);-moz-transform:rotate(0deg);-ms-transform:rotate(0deg);transform:rotate(0deg);}to{-webkit-transform:rotate(359deg);-moz-transform:rotate(359deg);-ms-transform:rotate(359deg);transform:rotate(359deg);}}
|
|
||||||
</style>,
|
|
||||||
<style
|
|
||||||
data-emotion="css"
|
|
||||||
data-s=""
|
|
||||||
>
|
|
||||||
|
|
||||||
@keyframes animation-c24ia8{from{-webkit-transform:rotate(0deg);-moz-transform:rotate(0deg);-ms-transform:rotate(0deg);transform:rotate(0deg);}to{-webkit-transform:rotate(359deg);-moz-transform:rotate(359deg);-ms-transform:rotate(359deg);transform:rotate(359deg);}}
|
|
||||||
</style>,
|
|
||||||
],
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
isStringTag={true}
|
|
||||||
serialized={
|
|
||||||
Object {
|
|
||||||
"map": "/*# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9jb21wb25lbnRzL2xvYWRpbmcvbG9hZGluZ19zcGlubmVyLnN0eWxlcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUE0RFUiLCJmaWxlIjoiLi4vLi4vLi4vc3JjL2NvbXBvbmVudHMvbG9hZGluZy9sb2FkaW5nX3NwaW5uZXIuc3R5bGVzLnRzIiwic291cmNlc0NvbnRlbnQiOlsiLypcbiAqIENvcHlyaWdodCBFbGFzdGljc2VhcmNoIEIuVi4gYW5kL29yIGxpY2Vuc2VkIHRvIEVsYXN0aWNzZWFyY2ggQi5WLiB1bmRlciBvbmVcbiAqIG9yIG1vcmUgY29udHJpYnV0b3IgbGljZW5zZSBhZ3JlZW1lbnRzLiBMaWNlbnNlZCB1bmRlciB0aGUgRWxhc3RpYyBMaWNlbnNlXG4gKiAyLjAgYW5kIHRoZSBTZXJ2ZXIgU2lkZSBQdWJsaWMgTGljZW5zZSwgdiAxOyB5b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdFxuICogaW4gY29tcGxpYW5jZSB3aXRoLCBhdCB5b3VyIGVsZWN0aW9uLCB0aGUgRWxhc3RpYyBMaWNlbnNlIDIuMCBvciB0aGUgU2VydmVyXG4gKiBTaWRlIFB1YmxpYyBMaWNlbnNlLCB2IDEuXG4gKi9cblxuaW1wb3J0IHsgY3NzLCBrZXlmcmFtZXMgfSBmcm9tICdAZW1vdGlvbi9yZWFjdCc7XG5pbXBvcnQgeyBfRXVpVGhlbWVTaXplLCBldWlDYW5BbmltYXRlIH0gZnJvbSAnLi4vLi4vZ2xvYmFsX3N0eWxpbmcnO1xuaW1wb3J0IHsgVXNlRXVpVGhlbWUgfSBmcm9tICcuLi8uLi9zZXJ2aWNlcyc7XG5pbXBvcnQgeyBFdWlMb2FkaW5nU3Bpbm5lclNpemUgfSBmcm9tICcuL2xvYWRpbmdfc3Bpbm5lcic7XG5cbmNvbnN0IF9sb2FkaW5nU3Bpbm5lciA9IGtleWZyYW1lc2BcbiAgZnJvbSB7XG4gICAgdHJhbnNmb3JtOiByb3RhdGUoMGRlZyk7XG4gIH1cblxuICB0byB7XG4gICAgdHJhbnNmb3JtOiByb3RhdGUoMzU5ZGVnKTtcbiAgfVxuYDtcblxuY29uc3Qgc3Bpbm5lclNpemVzOiB7XG4gIFtzaXplIGluIEV1aUxvYWRpbmdTcGlubmVyU2l6ZV06IF9FdWlUaGVtZVNpemU7XG59ID0ge1xuICBzOiAnbScsXG4gIG06ICdiYXNlJyxcbiAgbDogJ2wnLFxuICB4bDogJ3hsJyxcbiAgeHhsOiAneHhsJyxcbn07XG5cbmNvbnN0IHNwaW5uZXJDb2xvcnMgPSAobWFpbjogc3RyaW5nLCBoaWdobGlnaHQ6IHN0cmluZykgPT4ge1xuICByZXR1cm4gYCR7aGlnaGxpZ2h0fSAke21haW59ICR7bWFpbn0gJHttYWlufWA7XG59O1xuXG5leHBvcnQgY29uc3QgZXVpTG9hZGluZ1NwaW5uZXJTdHlsZXMgPSAoeyBldWlUaGVtZSB9OiBVc2VFdWlUaGVtZSkgPT4ge1xuICByZXR1cm4ge1xuICAgIGV1aUxvYWRpbmdTcGlubmVyOiBjc3NgXG4gICAgICBmbGV4LXNocmluazogMDsgLy8gRW5zdXJlcyBpdCBuZXZlciBzY2FsZXMgZG93biBiZWxvdyBpdHMgaW50ZW5kZWQgc2l6ZVxuICAgICAgZGlzcGxheTogaW5saW5lLWJsb2NrO1xuICAgICAgYm9yZGVyLXJhZGl1czogNTAlO1xuICAgICAgYm9yZGVyOiAke2V1aVRoZW1lLmJvcmRlci50aGlja307XG4gICAgICBib3JkZXItY29sb3I6ICR7c3Bpbm5lckNvbG9ycyhcbiAgICAgICAgZXVpVGhlbWUuYm9yZGVyLmNvbG9yLFxuICAgICAgICBldWlUaGVtZS5jb2xvcnMucHJpbWFyeVxuICAgICAgKX07XG5cbiAgICAgICR7ZXVpQ2FuQW5pbWF0ZX0ge1xuICAgICAgICBhbmltYXRpb246ICR7X2xvYWRpbmdTcGlubmVyfSAwLjZzIGluZmluaXRlIGxpbmVhcjtcbiAgICAgIH1cbiAgICBgLFxuXG4gICAgLy8gU2l6ZXNcbiAgICBzOiBjc3NgXG4gICAgICB3aWR0aDogJHtldWlUaGVtZS5zaXplW3NwaW5uZXJTaXplcy5zXX07XG4gICAgICBoZWlnaHQ6ICR7ZXVpVGhlbWUuc2l6ZVtzcGlubmVyU2l6ZXMuc119O1xuICAgICAgYm9yZGVyLXdpZHRoOiBjYWxjKCR7ZXVpVGhlbWUuYm9yZGVyLndpZHRoLnRoaW59ICogMS41KTtcbiAgICBgLFxuICAgIG06IGNzc2BcbiAgICAgIHdpZHRoOiAke2V1aVRoZW1lLnNpemVbc3Bpbm5lclNpemVzLm1dfTtcbiAgICAgIGhlaWdodDogJHtldWlUaGVtZS5zaXplW3NwaW5uZXJTaXplcy5tXX07XG4gICAgICBib3JkZXItd2lkdGg6IGNhbGMoJHtldWlUaGVtZS5ib3JkZXIud2lkdGgudGhpbn0gKiAxLjUpO1xuICAgIGAsXG4gICAgbDogY3NzYFxuICAgICAgd2lkdGg6ICR7ZXVpVGhlbWUuc2l6ZVtzcGlubmVyU2l6ZXMubF19O1xuICAgICAgaGVpZ2h0OiAke2V1aVRoZW1lLnNpemVbc3Bpbm5lclNpemVzLmxdfTtcbiAgICBgLFxuICAgIHhsOiBjc3NgXG4gICAgICB3aWR0aDogJHtldWlUaGVtZS5zaXplW3NwaW5uZXJTaXplcy54bF19O1xuICAgICAgaGVpZ2h0OiAke2V1aVRoZW1lLnNpemVbc3Bpbm5lclNpemVzLnhsXX07XG4gICAgYCxcbiAgICB4eGw6IGNzc2BcbiAgICAgIHdpZHRoOiAke2V1aVRoZW1lLnNpemVbc3Bpbm5lclNpemVzLnh4bF19O1xuICAgICAgaGVpZ2h0OiAke2V1aVRoZW1lLnNpemVbc3Bpbm5lclNpemVzLnh4bF19O1xuICAgIGAsXG4gIH07XG59O1xuIl19 */",
|
|
||||||
"name": "155ohbx-euiLoadingSpinner-m",
|
|
||||||
"next": Object {
|
|
||||||
"name": "animation-c24ia8",
|
|
||||||
"next": undefined,
|
|
||||||
"styles": "@keyframes animation-c24ia8{
|
|
||||||
from {
|
|
||||||
transform: rotate(0deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
to {
|
|
||||||
transform: rotate(359deg);
|
|
||||||
}
|
|
||||||
}",
|
|
||||||
},
|
|
||||||
"styles": "flex-shrink:0;display:inline-block;border-radius:50%;border:2px solid #D3DAE6;border-color:#07C #D3DAE6 #D3DAE6 #D3DAE6;@media screen and (prefers-reduced-motion: no-preference){animation:animation-c24ia8 0.6s infinite linear;};label:euiLoadingSpinner;;;width:16px;height:16px;border-width:calc(1px * 1.5);;label:m;;;",
|
|
||||||
"toString": [Function],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<span
|
|
||||||
aria-label="Loading"
|
|
||||||
className="euiLoadingSpinner css-155ohbx-euiLoadingSpinner-m"
|
|
||||||
role="progressbar"
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
</EuiLoadingSpinner>
|
|
||||||
</div>
|
|
||||||
</Fallback>
|
|
||||||
</Suspense>
|
|
||||||
</EuiErrorBoundary>
|
|
||||||
</ForwardRef>
|
|
||||||
</AnalyticsNoDataPage>
|
|
||||||
`;
|
|
|
@ -7,21 +7,31 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { act } from 'react-dom/test-utils';
|
||||||
|
|
||||||
import { mountWithIntl } from '@kbn/test-jest-helpers';
|
import { mountWithIntl } from '@kbn/test-jest-helpers';
|
||||||
import { KibanaNoDataPage } from '@kbn/shared-ux-components';
|
import { KibanaNoDataPage } from '@kbn/shared-ux-page-kibana-no-data';
|
||||||
|
|
||||||
import { AnalyticsNoDataPage } from './analytics_no_data_page.component';
|
import { AnalyticsNoDataPage } from './analytics_no_data_page.component';
|
||||||
|
import { AnalyticsNoDataPageProvider } from './services';
|
||||||
|
import { getMockServices } from './mocks';
|
||||||
|
|
||||||
describe('AnalyticsNoDataPageComponent', () => {
|
describe('AnalyticsNoDataPageComponent', () => {
|
||||||
|
const services = getMockServices();
|
||||||
const onDataViewCreated = jest.fn();
|
const onDataViewCreated = jest.fn();
|
||||||
|
|
||||||
it('renders correctly', () => {
|
it('renders correctly', async () => {
|
||||||
const component = mountWithIntl(
|
const component = mountWithIntl(
|
||||||
|
// Include context so composed components will have access to their services.
|
||||||
|
<AnalyticsNoDataPageProvider {...services}>
|
||||||
<AnalyticsNoDataPage
|
<AnalyticsNoDataPage
|
||||||
onDataViewCreated={onDataViewCreated}
|
onDataViewCreated={onDataViewCreated}
|
||||||
kibanaGuideDocLink={'http://www.test.com'}
|
kibanaGuideDocLink={'http://www.test.com'}
|
||||||
/>
|
/>
|
||||||
|
</AnalyticsNoDataPageProvider>
|
||||||
);
|
);
|
||||||
expect(component).toMatchSnapshot();
|
|
||||||
|
await act(() => new Promise(setImmediate));
|
||||||
|
|
||||||
expect(component.find(KibanaNoDataPage).length).toBe(1);
|
expect(component.find(KibanaNoDataPage).length).toBe(1);
|
||||||
|
|
||||||
|
|
|
@ -7,13 +7,15 @@
|
||||||
*/
|
*/
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { i18n } from '@kbn/i18n';
|
import { i18n } from '@kbn/i18n';
|
||||||
import { KibanaNoDataPage } from '@kbn/shared-ux-components';
|
import { KibanaNoDataPage } from '@kbn/shared-ux-page-kibana-no-data';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Props for the pure component.
|
* Props for the pure component.
|
||||||
*/
|
*/
|
||||||
export interface Props {
|
export interface Props {
|
||||||
|
/** A link to documentation. */
|
||||||
kibanaGuideDocLink: string;
|
kibanaGuideDocLink: string;
|
||||||
|
/** Handler for successfully creating a new data view. */
|
||||||
onDataViewCreated: (dataView: unknown) => void;
|
onDataViewCreated: (dataView: unknown) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,14 +8,15 @@
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { action } from '@storybook/addon-actions';
|
import { action } from '@storybook/addon-actions';
|
||||||
import { servicesFactory, DataServiceFactoryConfig } from '@kbn/shared-ux-storybook';
|
|
||||||
|
|
||||||
import { AnalyticsNoDataPage as Component } from './analytics_no_data_page';
|
import { AnalyticsNoDataPage as Component } from './analytics_no_data_page';
|
||||||
import { AnalyticsNoDataPageProvider, Services } from './services';
|
import { AnalyticsNoDataPageProvider } from './services';
|
||||||
import mdx from '../README.mdx';
|
import mdx from '../README.mdx';
|
||||||
|
|
||||||
|
import { Params, getStoryArgTypes, getStoryServices } from './mocks';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
title: 'Analytics No Data Page',
|
title: 'No Data/Analytics',
|
||||||
description: 'An Analytics-specific version of KibanaNoDataPage.',
|
description: 'An Analytics-specific version of KibanaNoDataPage.',
|
||||||
parameters: {
|
parameters: {
|
||||||
docs: {
|
docs: {
|
||||||
|
@ -24,22 +25,27 @@ export default {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
type Params = Pick<DataServiceFactoryConfig, 'hasESData' | 'hasUserDataView'>;
|
|
||||||
|
|
||||||
export const AnalyticsNoDataPage = (params: Params) => {
|
export const AnalyticsNoDataPage = (params: Params) => {
|
||||||
// Workaround to leverage the services package.
|
return (
|
||||||
const { application, data, docLinks, editors, http, permissions, platform } =
|
<AnalyticsNoDataPageProvider {...getStoryServices(params)}>
|
||||||
servicesFactory(params);
|
<Component onDataViewCreated={action('onDataViewCreated')} />
|
||||||
|
</AnalyticsNoDataPageProvider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const services: Services = {
|
AnalyticsNoDataPage.argTypes = {
|
||||||
...application,
|
...getStoryArgTypes(),
|
||||||
...data,
|
};
|
||||||
...docLinks,
|
|
||||||
...editors,
|
export const LoadingState = (params: Params) => {
|
||||||
...http,
|
// Simulate loading with a Promise that doesn't resolve.
|
||||||
...permissions,
|
const dataCheck = () => new Promise<boolean>((_reject, _resolve) => {});
|
||||||
...platform,
|
|
||||||
kibanaGuideDocLink: 'Kibana guide',
|
const services = {
|
||||||
|
...getStoryServices(params),
|
||||||
|
hasESData: dataCheck,
|
||||||
|
hasUserDataView: dataCheck,
|
||||||
|
hasDataView: dataCheck,
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -48,14 +54,3 @@ export const AnalyticsNoDataPage = (params: Params) => {
|
||||||
</AnalyticsNoDataPageProvider>
|
</AnalyticsNoDataPageProvider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
AnalyticsNoDataPage.argTypes = {
|
|
||||||
hasESData: {
|
|
||||||
control: 'boolean',
|
|
||||||
defaultValue: false,
|
|
||||||
},
|
|
||||||
hasUserDataView: {
|
|
||||||
control: 'boolean',
|
|
||||||
defaultValue: false,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
|
@ -7,30 +7,18 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { act } from 'react-dom/test-utils';
|
||||||
import { mountWithIntl } from '@kbn/test-jest-helpers';
|
import { mountWithIntl } from '@kbn/test-jest-helpers';
|
||||||
import { mockServicesFactory } from '@kbn/shared-ux-services';
|
|
||||||
|
|
||||||
import { Services, AnalyticsNoDataPageProvider } from './services';
|
import { AnalyticsNoDataPageProvider } from './services';
|
||||||
import { AnalyticsNoDataPage as Component } from './analytics_no_data_page.component';
|
import { AnalyticsNoDataPage as Component } from './analytics_no_data_page.component';
|
||||||
import { AnalyticsNoDataPage } from './analytics_no_data_page';
|
import { AnalyticsNoDataPage } from './analytics_no_data_page';
|
||||||
|
import { getMockServices } from './mocks';
|
||||||
|
|
||||||
describe('AnalyticsNoDataPage', () => {
|
describe('AnalyticsNoDataPage', () => {
|
||||||
const onDataViewCreated = jest.fn();
|
const onDataViewCreated = jest.fn();
|
||||||
|
|
||||||
// Workaround to leverage the services package.
|
const services = getMockServices();
|
||||||
const { application, data, docLinks, editors, http, permissions, platform } =
|
|
||||||
mockServicesFactory();
|
|
||||||
|
|
||||||
const services: Services = {
|
|
||||||
...application,
|
|
||||||
...data,
|
|
||||||
...docLinks,
|
|
||||||
...editors,
|
|
||||||
...http,
|
|
||||||
...permissions,
|
|
||||||
...platform,
|
|
||||||
kibanaGuideDocLink: 'Kibana guide',
|
|
||||||
};
|
|
||||||
|
|
||||||
afterAll(() => {
|
afterAll(() => {
|
||||||
jest.resetAllMocks();
|
jest.resetAllMocks();
|
||||||
|
@ -43,6 +31,8 @@ describe('AnalyticsNoDataPage', () => {
|
||||||
</AnalyticsNoDataPageProvider>
|
</AnalyticsNoDataPageProvider>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
await act(() => new Promise(setImmediate));
|
||||||
|
|
||||||
expect(component.find(Component).length).toBe(1);
|
expect(component.find(Component).length).toBe(1);
|
||||||
expect(component.find(Component).props().kibanaGuideDocLink).toBe(services.kibanaGuideDocLink);
|
expect(component.find(Component).props().kibanaGuideDocLink).toBe(services.kibanaGuideDocLink);
|
||||||
expect(component.find(Component).props().onDataViewCreated).toBe(onDataViewCreated);
|
expect(component.find(Component).props().onDataViewCreated).toBe(onDataViewCreated);
|
||||||
|
|
|
@ -6,8 +6,6 @@
|
||||||
* Side Public License, v 1.
|
* Side Public License, v 1.
|
||||||
*/
|
*/
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { LegacyServicesProvider, getLegacyServices } from './legacy_services';
|
|
||||||
import { useServices } from './services';
|
import { useServices } from './services';
|
||||||
import { AnalyticsNoDataPage as Component } from './analytics_no_data_page.component';
|
import { AnalyticsNoDataPage as Component } from './analytics_no_data_page.component';
|
||||||
|
|
||||||
|
@ -15,25 +13,24 @@ import { AnalyticsNoDataPage as Component } from './analytics_no_data_page.compo
|
||||||
* Props for the `AnalyticsNoDataPage` component.
|
* Props for the `AnalyticsNoDataPage` component.
|
||||||
*/
|
*/
|
||||||
export interface AnalyticsNoDataPageProps {
|
export interface AnalyticsNoDataPageProps {
|
||||||
|
/** Handler for successfully creating a new data view. */
|
||||||
onDataViewCreated: (dataView: unknown) => void;
|
onDataViewCreated: (dataView: unknown) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An entire page that can be displayed when Kibana "has no data", specifically for Analytics. Uses
|
* An entire page that can be displayed when Kibana "has no data", specifically for Analytics. Uses
|
||||||
* services from a provider to provide props to a pure component.
|
* services from a Provider to supply props to a pure component.
|
||||||
*/
|
*/
|
||||||
export const AnalyticsNoDataPage = ({ onDataViewCreated }: AnalyticsNoDataPageProps) => {
|
export const AnalyticsNoDataPage = ({ onDataViewCreated }: AnalyticsNoDataPageProps) => {
|
||||||
const services = useServices();
|
const services = useServices();
|
||||||
const { kibanaGuideDocLink } = services;
|
const { kibanaGuideDocLink } = services;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<LegacyServicesProvider {...getLegacyServices(services)}>
|
|
||||||
<Component
|
<Component
|
||||||
{...{
|
{...{
|
||||||
onDataViewCreated,
|
onDataViewCreated,
|
||||||
kibanaGuideDocLink,
|
kibanaGuideDocLink,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</LegacyServicesProvider>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -8,3 +8,10 @@
|
||||||
|
|
||||||
export { AnalyticsNoDataPageProvider, AnalyticsNoDataPageKibanaProvider } from './services';
|
export { AnalyticsNoDataPageProvider, AnalyticsNoDataPageKibanaProvider } from './services';
|
||||||
export { AnalyticsNoDataPage } from './analytics_no_data_page';
|
export { AnalyticsNoDataPage } from './analytics_no_data_page';
|
||||||
|
export { AnalyticsNoDataPage as AnalyticsNoDataPageComponent } from './analytics_no_data_page.component';
|
||||||
|
|
||||||
|
export {
|
||||||
|
getMockServices as getAnalyticsNoDataPageMockServices,
|
||||||
|
getStoryArgTypes as getAnalyticsNoDataPageStoryArgTypes,
|
||||||
|
getStoryServices as getAnalyticsNoDataPageStoryServices,
|
||||||
|
} from './mocks';
|
||||||
|
|
49
packages/shared-ux/page/analytics_no_data/src/mocks.ts
Normal file
49
packages/shared-ux/page/analytics_no_data/src/mocks.ts
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
/*
|
||||||
|
* 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 {
|
||||||
|
getKibanaNoDataPageStoryArgTypes,
|
||||||
|
getKibanaNoDataPageStoryMock,
|
||||||
|
getKibanaNoDataPageMockServices,
|
||||||
|
} from '@kbn/shared-ux-page-kibana-no-data';
|
||||||
|
|
||||||
|
import { AnalyticsNoDataPageServices } from './services';
|
||||||
|
|
||||||
|
export type Params = Record<keyof ReturnType<typeof getStoryArgTypes>, any>;
|
||||||
|
|
||||||
|
export const getStoryServices = (params: Params) => {
|
||||||
|
const services: AnalyticsNoDataPageServices = {
|
||||||
|
...getKibanaNoDataPageStoryMock({ ...params, solution: 'Analytics', logo: 'logoKibana' }),
|
||||||
|
kibanaGuideDocLink: 'Kibana guide',
|
||||||
|
};
|
||||||
|
|
||||||
|
return services;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getStoryArgTypes = () => {
|
||||||
|
// While `solution` and `logo` are props on `KibanaNoDataPage`, they are set by
|
||||||
|
// `AnalyticsNoDataPage` internally. So calls to `getStoryArgTypes` for `AnalyticsNoDataPage`
|
||||||
|
// need to remove them for any of its stories, (as they'll have no effect).
|
||||||
|
const { solution, logo, ...args } = getKibanaNoDataPageStoryArgTypes();
|
||||||
|
return {
|
||||||
|
kibanaGuideDocLink: {
|
||||||
|
control: 'text',
|
||||||
|
defaultValue: 'Kibana guide',
|
||||||
|
},
|
||||||
|
...args,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getMockServices = () => {
|
||||||
|
const services: AnalyticsNoDataPageServices = {
|
||||||
|
...getKibanaNoDataPageMockServices(),
|
||||||
|
kibanaGuideDocLink: 'Kibana guide',
|
||||||
|
};
|
||||||
|
|
||||||
|
return services;
|
||||||
|
};
|
|
@ -7,114 +7,60 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { FC, useContext } from 'react';
|
import React, { FC, useContext } from 'react';
|
||||||
import { Observable } from 'rxjs';
|
import {
|
||||||
|
KibanaNoDataPageServices,
|
||||||
|
KibanaNoDataPageKibanaDependencies,
|
||||||
|
KibanaNoDataPageKibanaProvider,
|
||||||
|
KibanaNoDataPageProvider,
|
||||||
|
} from '@kbn/shared-ux-page-kibana-no-data';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO: `DataView` is a class exported by `src/plugins/data_views/public`. Since this service
|
* A list of services that are consumed by this component.
|
||||||
* is contained in this package-- and packages can only depend on other packages and never on
|
|
||||||
* plugins-- we have to set this to `unknown`. If and when `DataView` is exported from a
|
|
||||||
* stateless package, we can remove this.
|
|
||||||
*
|
|
||||||
* @see: https://github.com/elastic/kibana/issues/127695
|
|
||||||
*/
|
*/
|
||||||
type DataView = unknown;
|
interface Services {
|
||||||
|
|
||||||
/**
|
|
||||||
* A subset of the `DataViewEditorOptions` interface relevant to this component.
|
|
||||||
*
|
|
||||||
* @see: src/plugins/data_view_editor/public/types.ts
|
|
||||||
*/
|
|
||||||
interface DataViewEditorOptions {
|
|
||||||
/** Handler to be invoked when the Data View Editor completes a save operation. */
|
|
||||||
onSave: (dataView: DataView) => void;
|
|
||||||
/** If set to false, will skip empty prompt in data view editor. */
|
|
||||||
showEmptyPrompt?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A list of Services that are consumed by this component.
|
|
||||||
*
|
|
||||||
* This list is temporary, a stopgap as we migrate to a package-based architecture, where
|
|
||||||
* services are not collected in a single package. In order to make the transition, this
|
|
||||||
* interface is intentionally "flat".
|
|
||||||
*
|
|
||||||
* Expect this list to dwindle to zero as `@kbn/shared-ux-components` are migrated to their
|
|
||||||
* own packages, (and `@kbn/shared-ux-services` is removed).
|
|
||||||
*/
|
|
||||||
export interface Services {
|
|
||||||
addBasePath: (url: string) => string;
|
|
||||||
canAccessFleet: boolean;
|
|
||||||
canCreateNewDataView: boolean;
|
|
||||||
currentAppId$: Observable<string | undefined>;
|
|
||||||
dataViewsDocLink: string;
|
|
||||||
hasDataView: () => Promise<boolean>;
|
|
||||||
hasESData: () => Promise<boolean>;
|
|
||||||
hasUserDataView: () => Promise<boolean>;
|
|
||||||
kibanaGuideDocLink: string;
|
kibanaGuideDocLink: string;
|
||||||
navigateToUrl: (url: string) => Promise<void>;
|
|
||||||
openDataViewEditor: (options: DataViewEditorOptions) => () => void;
|
|
||||||
setIsFullscreen: (isFullscreen: boolean) => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const AnalyticsNoDataPageContext = React.createContext<Services | null>(null);
|
const Context = React.createContext<Services | null>(null);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Context Provider that provides services to the component.
|
* Services that are consumed by this component and its dependencies.
|
||||||
*/
|
*/
|
||||||
export const AnalyticsNoDataPageProvider: FC<Services> = ({ children, ...services }) => {
|
export type AnalyticsNoDataPageServices = Services & KibanaNoDataPageServices;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Context Provider that provides services to the component and its dependencies.
|
||||||
|
*/
|
||||||
|
export const AnalyticsNoDataPageProvider: FC<AnalyticsNoDataPageServices> = ({
|
||||||
|
children,
|
||||||
|
...services
|
||||||
|
}) => {
|
||||||
|
const { kibanaGuideDocLink } = services;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AnalyticsNoDataPageContext.Provider value={services}>
|
<Context.Provider value={{ kibanaGuideDocLink }}>
|
||||||
{children}
|
<KibanaNoDataPageProvider {...services}>{children}</KibanaNoDataPageProvider>
|
||||||
</AnalyticsNoDataPageContext.Provider>
|
</Context.Provider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
interface KibanaDependencies {
|
||||||
* An interface containing a collection of Kibana plugins and services required to
|
|
||||||
* render this component and its dependencies.
|
|
||||||
*/
|
|
||||||
export interface AnalyticsNoDataPageKibanaDependencies {
|
|
||||||
coreStart: {
|
coreStart: {
|
||||||
application: {
|
|
||||||
capabilities: {
|
|
||||||
navLinks: Record<string, boolean>;
|
|
||||||
};
|
|
||||||
currentAppId$: Observable<string | undefined>;
|
|
||||||
navigateToUrl: (url: string) => Promise<void>;
|
|
||||||
};
|
|
||||||
chrome: {
|
|
||||||
setIsVisible: (isVisible: boolean) => void;
|
|
||||||
};
|
|
||||||
docLinks: {
|
docLinks: {
|
||||||
links: {
|
links: {
|
||||||
indexPatterns: {
|
|
||||||
introduction: string;
|
|
||||||
};
|
|
||||||
kibana: {
|
kibana: {
|
||||||
guide: string;
|
guide: string;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
http: {
|
|
||||||
basePath: {
|
|
||||||
prepend: (url: string) => string;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
dataViews: {
|
|
||||||
hasData: {
|
|
||||||
hasDataView: () => Promise<boolean>;
|
|
||||||
hasESData: () => Promise<boolean>;
|
|
||||||
hasUserDataView: () => Promise<boolean>;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
dataViewEditor: {
|
|
||||||
openEditor: (options: DataViewEditorOptions) => () => void;
|
|
||||||
userPermissions: {
|
|
||||||
editDataView: () => boolean;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* An interface containing a collection of Kibana plugins and services required to
|
||||||
|
* render this component as well as its dependencies.
|
||||||
|
*/
|
||||||
|
export type AnalyticsNoDataPageKibanaDependencies = KibanaDependencies &
|
||||||
|
KibanaNoDataPageKibanaDependencies;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Kibana-specific Provider that maps dependencies to services.
|
* Kibana-specific Provider that maps dependencies to services.
|
||||||
|
@ -123,26 +69,14 @@ export const AnalyticsNoDataPageKibanaProvider: FC<AnalyticsNoDataPageKibanaDepe
|
||||||
children,
|
children,
|
||||||
...dependencies
|
...dependencies
|
||||||
}) => {
|
}) => {
|
||||||
const { coreStart, dataViewEditor, dataViews } = dependencies;
|
|
||||||
const value: Services = {
|
const value: Services = {
|
||||||
addBasePath: coreStart.http.basePath.prepend,
|
kibanaGuideDocLink: dependencies.coreStart.docLinks.links.kibana.guide,
|
||||||
canAccessFleet: coreStart.application.capabilities.navLinks.integrations,
|
|
||||||
canCreateNewDataView: dataViewEditor.userPermissions.editDataView(),
|
|
||||||
currentAppId$: coreStart.application.currentAppId$,
|
|
||||||
dataViewsDocLink: coreStart.docLinks.links.indexPatterns?.introduction,
|
|
||||||
hasDataView: dataViews.hasData.hasDataView,
|
|
||||||
hasESData: dataViews.hasData.hasESData,
|
|
||||||
hasUserDataView: dataViews.hasData.hasUserDataView,
|
|
||||||
kibanaGuideDocLink: coreStart.docLinks.links.kibana.guide,
|
|
||||||
navigateToUrl: coreStart.application.navigateToUrl,
|
|
||||||
openDataViewEditor: dataViewEditor.openEditor,
|
|
||||||
setIsFullscreen: (isVisible: boolean) => coreStart.chrome.setIsVisible(isVisible),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AnalyticsNoDataPageContext.Provider value={value}>
|
<Context.Provider {...{ value }}>
|
||||||
{children}
|
<KibanaNoDataPageKibanaProvider {...dependencies}>{children}</KibanaNoDataPageKibanaProvider>
|
||||||
</AnalyticsNoDataPageContext.Provider>
|
</Context.Provider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -150,11 +84,11 @@ export const AnalyticsNoDataPageKibanaProvider: FC<AnalyticsNoDataPageKibanaDepe
|
||||||
* React hook for accessing pre-wired services.
|
* React hook for accessing pre-wired services.
|
||||||
*/
|
*/
|
||||||
export function useServices() {
|
export function useServices() {
|
||||||
const context = useContext(AnalyticsNoDataPageContext);
|
const context = useContext(Context);
|
||||||
|
|
||||||
if (!context) {
|
if (!context) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
'AnalyticsNoDataPageContext is missing. Ensure your component or React root is wrapped with AnalyticsNoDataPageContext.'
|
'AnalyticsNoDataPage Context is missing. Ensure your component or React root is wrapped with AnalyticsNoDataPageContext.'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
148
packages/shared-ux/page/kibana_no_data/BUILD.bazel
Normal file
148
packages/shared-ux/page/kibana_no_data/BUILD.bazel
Normal file
|
@ -0,0 +1,148 @@
|
||||||
|
load("@npm//@bazel/typescript:index.bzl", "ts_config")
|
||||||
|
load("@build_bazel_rules_nodejs//:index.bzl", "js_library")
|
||||||
|
load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", "ts_project")
|
||||||
|
|
||||||
|
PKG_DIRNAME = "kibana_no_data"
|
||||||
|
PKG_REQUIRE_NAME = "@kbn/shared-ux-page-kibana-no-data"
|
||||||
|
|
||||||
|
SOURCE_FILES = glob(
|
||||||
|
[
|
||||||
|
"src/**/*.ts",
|
||||||
|
"src/**/*.tsx",
|
||||||
|
"src/**/*.mdx",
|
||||||
|
],
|
||||||
|
exclude = [
|
||||||
|
"**/*.test.*",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
SRCS = SOURCE_FILES
|
||||||
|
|
||||||
|
filegroup(
|
||||||
|
name = "srcs",
|
||||||
|
srcs = SRCS,
|
||||||
|
)
|
||||||
|
|
||||||
|
NPM_MODULE_EXTRA_FILES = [
|
||||||
|
"package.json",
|
||||||
|
]
|
||||||
|
|
||||||
|
# In this array place runtime dependencies, including other packages and NPM packages
|
||||||
|
# which must be available for this code to run.
|
||||||
|
#
|
||||||
|
# To reference other packages use:
|
||||||
|
# "//repo/relative/path/to/package"
|
||||||
|
# eg. "//packages/kbn-utils"
|
||||||
|
#
|
||||||
|
# To reference a NPM package use:
|
||||||
|
# "@npm//name-of-package"
|
||||||
|
# eg. "@npm//lodash"
|
||||||
|
RUNTIME_DEPS = [
|
||||||
|
"@npm//@elastic/eui",
|
||||||
|
"@npm//@emotion/css",
|
||||||
|
"@npm//@emotion/react",
|
||||||
|
"@npm//@storybook/addon-actions",
|
||||||
|
"@npm//react",
|
||||||
|
"//packages/kbn-i18n",
|
||||||
|
"//packages/kbn-shared-ux-components",
|
||||||
|
"//packages/kbn-shared-ux-services",
|
||||||
|
"//packages/kbn-shared-ux-storybook",
|
||||||
|
"//packages/shared-ux/prompt/no_data_views",
|
||||||
|
]
|
||||||
|
|
||||||
|
# In this array place dependencies necessary to build the types, which will include the
|
||||||
|
# :npm_module_types target of other packages and packages from NPM, including @types/*
|
||||||
|
# packages.
|
||||||
|
#
|
||||||
|
# To reference the types for another package use:
|
||||||
|
# "//repo/relative/path/to/package:npm_module_types"
|
||||||
|
# eg. "//packages/kbn-utils:npm_module_types"
|
||||||
|
#
|
||||||
|
# References to NPM packages work the same as RUNTIME_DEPS
|
||||||
|
TYPES_DEPS = [
|
||||||
|
"@npm//@elastic/eui",
|
||||||
|
"@npm//@emotion/css",
|
||||||
|
"@npm//@emotion/react",
|
||||||
|
"@npm//@storybook/addon-actions",
|
||||||
|
"@npm//@types/jest",
|
||||||
|
"@npm//@types/node",
|
||||||
|
"@npm//@types/react",
|
||||||
|
"//packages/kbn-ambient-ui-types",
|
||||||
|
"//packages/kbn-i18n:npm_module_types",
|
||||||
|
"//packages/kbn-shared-ux-components:npm_module_types",
|
||||||
|
"//packages/kbn-shared-ux-services:npm_module_types",
|
||||||
|
"//packages/kbn-shared-ux-storybook:npm_module_types",
|
||||||
|
"//packages/shared-ux/prompt/no_data_views:npm_module_types",
|
||||||
|
]
|
||||||
|
|
||||||
|
jsts_transpiler(
|
||||||
|
name = "target_node",
|
||||||
|
srcs = SRCS,
|
||||||
|
build_pkg_name = package_name(),
|
||||||
|
)
|
||||||
|
|
||||||
|
jsts_transpiler(
|
||||||
|
name = "target_web",
|
||||||
|
srcs = SRCS,
|
||||||
|
build_pkg_name = package_name(),
|
||||||
|
web = True,
|
||||||
|
additional_args = [
|
||||||
|
"--copy-files",
|
||||||
|
"--quiet"
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
ts_config(
|
||||||
|
name = "tsconfig",
|
||||||
|
src = "tsconfig.json",
|
||||||
|
deps = [
|
||||||
|
"//:tsconfig.base.json",
|
||||||
|
"//:tsconfig.bazel.json",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
ts_project(
|
||||||
|
name = "tsc_types",
|
||||||
|
args = ['--pretty'],
|
||||||
|
srcs = SRCS,
|
||||||
|
deps = TYPES_DEPS,
|
||||||
|
declaration = True,
|
||||||
|
emit_declaration_only = True,
|
||||||
|
out_dir = "target_types",
|
||||||
|
root_dir = "src",
|
||||||
|
tsconfig = ":tsconfig",
|
||||||
|
)
|
||||||
|
|
||||||
|
js_library(
|
||||||
|
name = PKG_DIRNAME,
|
||||||
|
srcs = NPM_MODULE_EXTRA_FILES,
|
||||||
|
deps = RUNTIME_DEPS + [":target_node", ":target_web"],
|
||||||
|
package_name = PKG_REQUIRE_NAME,
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
)
|
||||||
|
|
||||||
|
pkg_npm(
|
||||||
|
name = "npm_module",
|
||||||
|
deps = [":" + PKG_DIRNAME],
|
||||||
|
)
|
||||||
|
|
||||||
|
filegroup(
|
||||||
|
name = "build",
|
||||||
|
srcs = [":npm_module"],
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
)
|
||||||
|
|
||||||
|
pkg_npm_types(
|
||||||
|
name = "npm_module_types",
|
||||||
|
srcs = SRCS,
|
||||||
|
deps = [":tsc_types"],
|
||||||
|
package_name = PKG_REQUIRE_NAME,
|
||||||
|
tsconfig = ":tsconfig",
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
)
|
||||||
|
|
||||||
|
filegroup(
|
||||||
|
name = "build_types",
|
||||||
|
srcs = [":npm_module_types"],
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
)
|
29
packages/shared-ux/page/kibana_no_data/README.mdx
Normal file
29
packages/shared-ux/page/kibana_no_data/README.mdx
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
---
|
||||||
|
id: sharedUX/Page/KibanaNoDataPage
|
||||||
|
slug: /shared-ux/page/kibana-no-data
|
||||||
|
title: Kibana No Data Page
|
||||||
|
summary: A page to be displayed when there is no data in Elasticsearch, or no data views.
|
||||||
|
tags: ['shared-ux', 'component']
|
||||||
|
date: 2022-04-20
|
||||||
|
---
|
||||||
|
|
||||||
|
Many plugins display a "no data" page, either when there is no data in Elasticsearch, or there haven't been any data views created yet. This component is meant
|
||||||
|
to be used in those scenarios. It displays an appropriate message to the user and facilitate addition of integrations and creation of data views.
|
||||||
|
|
||||||
|
The `KibanaNoDataPage` connected component uses:
|
||||||
|
|
||||||
|
- `hasUserDataView` and `hasData` API from the `HasData` service in the `data_views` plugin to check for existence of data an data views.
|
||||||
|
- `onDataViewCreated` callback once a data view has been created.
|
||||||
|
- (noDataConfig)[https://github.com/elastic/kibana/blob/main/packages/kbn-shared-ux-components/src/page_template/no_data_page/types.ts] as configuration for the page in case of no data.
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
|
| Export | Description |
|
||||||
|
|---|---|
|
||||||
|
| `KibanaNoDataPageProvider` | Provides contextual services to `KibanaNoDataPage`. |
|
||||||
|
| `KibanaNoDataPageKibanaProvider` | Maps Kibana dependencies to provide contextual services to `KibanaNoDataPage`. |
|
||||||
|
| `KibanaNoDataPage` | Uses a `Provider` to access contextual services and render the component. |
|
||||||
|
|
||||||
|
## EUI Promotion Status
|
||||||
|
|
||||||
|
This component is not currently considered for promotion to EUI.
|
|
@ -6,4 +6,8 @@
|
||||||
* Side Public License, v 1.
|
* Side Public License, v 1.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export { KibanaNoDataPage } from './kibana_no_data_page';
|
module.exports = {
|
||||||
|
preset: '@kbn/test',
|
||||||
|
rootDir: '../../../..',
|
||||||
|
roots: ['<rootDir>/packages/shared-ux/page/kibana_no_data'],
|
||||||
|
};
|
8
packages/shared-ux/page/kibana_no_data/package.json
Normal file
8
packages/shared-ux/page/kibana_no_data/package.json
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"name": "@kbn/shared-ux-page-kibana-no-data",
|
||||||
|
"private": true,
|
||||||
|
"version": "1.0.0",
|
||||||
|
"main": "./target_node/index.js",
|
||||||
|
"browser": "./target_web/index.js",
|
||||||
|
"license": "SSPL-1.0 OR Elastic License 2.0"
|
||||||
|
}
|
18
packages/shared-ux/page/kibana_no_data/src/index.ts
Normal file
18
packages/shared-ux/page/kibana_no_data/src/index.ts
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
/*
|
||||||
|
* 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 type { KibanaNoDataPageServices, KibanaNoDataPageKibanaDependencies } from './services';
|
||||||
|
|
||||||
|
export { KibanaNoDataPage } from './kibana_no_data_page';
|
||||||
|
export { KibanaNoDataPageKibanaProvider, KibanaNoDataPageProvider } from './services';
|
||||||
|
|
||||||
|
export {
|
||||||
|
getStoryArgTypes as getKibanaNoDataPageStoryArgTypes,
|
||||||
|
getStoryServices as getKibanaNoDataPageStoryMock,
|
||||||
|
getMockServices as getKibanaNoDataPageMockServices,
|
||||||
|
} from './mocks';
|
|
@ -0,0 +1,72 @@
|
||||||
|
/*
|
||||||
|
* 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 { action } from '@storybook/addon-actions';
|
||||||
|
|
||||||
|
import { KibanaNoDataPage as Component } from './kibana_no_data_page';
|
||||||
|
import { StoryParams, getStoryServices, getStoryArgTypes } from './mocks';
|
||||||
|
|
||||||
|
import { KibanaNoDataPageProvider } from './services';
|
||||||
|
import mdx from '../README.mdx';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'No Data/Kibana',
|
||||||
|
description: 'A component to display when there is no data available',
|
||||||
|
parameters: {
|
||||||
|
docs: {
|
||||||
|
page: mdx,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const noDataConfig = {
|
||||||
|
solution: 'Analytics',
|
||||||
|
logo: 'logoKibana',
|
||||||
|
action: {
|
||||||
|
elasticAgent: {
|
||||||
|
title: 'Add Integrations',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
docsLink: 'http://docs.elastic.dev',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const KibanaNoDataPage = (params: StoryParams) => {
|
||||||
|
const { solution, logo } = params;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<KibanaNoDataPageProvider {...getStoryServices(params)}>
|
||||||
|
<Component
|
||||||
|
onDataViewCreated={action('onDataViewCreated')}
|
||||||
|
noDataConfig={{ ...noDataConfig, solution, logo }}
|
||||||
|
/>
|
||||||
|
</KibanaNoDataPageProvider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
KibanaNoDataPage.argTypes = {
|
||||||
|
...getStoryArgTypes(),
|
||||||
|
};
|
||||||
|
|
||||||
|
export const LoadingState = (params: StoryParams) => {
|
||||||
|
// Simulate loading with a Promise that doesn't resolve.
|
||||||
|
const dataCheck = () => new Promise<boolean>((resolve, reject) => {});
|
||||||
|
|
||||||
|
const services = {
|
||||||
|
...getStoryServices(params),
|
||||||
|
hasESData: dataCheck,
|
||||||
|
hasUserDataView: dataCheck,
|
||||||
|
hasDataView: dataCheck,
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<KibanaNoDataPageProvider {...services}>
|
||||||
|
<Component onDataViewCreated={action('onDataViewCreated')} noDataConfig={noDataConfig} />
|
||||||
|
</KibanaNoDataPageProvider>
|
||||||
|
);
|
||||||
|
};
|
|
@ -11,11 +11,12 @@ import { act } from 'react-dom/test-utils';
|
||||||
|
|
||||||
import { EuiLoadingElastic } from '@elastic/eui';
|
import { EuiLoadingElastic } from '@elastic/eui';
|
||||||
import { mountWithIntl } from '@kbn/test-jest-helpers';
|
import { mountWithIntl } from '@kbn/test-jest-helpers';
|
||||||
import { SharedUxServicesProvider, mockServicesFactory } from '@kbn/shared-ux-services';
|
|
||||||
import { NoDataViewsPrompt } from '@kbn/shared-ux-prompt-no-data-views';
|
import { NoDataViewsPrompt } from '@kbn/shared-ux-prompt-no-data-views';
|
||||||
|
import { NoDataConfigPage } from '@kbn/shared-ux-components';
|
||||||
|
|
||||||
import { KibanaNoDataPage } from './kibana_no_data_page';
|
import { KibanaNoDataPage } from './kibana_no_data_page';
|
||||||
import { NoDataConfigPage } from '../page_template';
|
import { KibanaNoDataPageProvider } from './services';
|
||||||
|
import { getMockServices } from './mocks';
|
||||||
|
|
||||||
describe('Kibana No Data Page', () => {
|
describe('Kibana No Data Page', () => {
|
||||||
const noDataConfig = {
|
const noDataConfig = {
|
||||||
|
@ -29,6 +30,7 @@ describe('Kibana No Data Page', () => {
|
||||||
},
|
},
|
||||||
docsLink: 'http://www.docs.com',
|
docsLink: 'http://www.docs.com',
|
||||||
};
|
};
|
||||||
|
|
||||||
const onDataViewCreated = jest.fn();
|
const onDataViewCreated = jest.fn();
|
||||||
const config = {
|
const config = {
|
||||||
hasESData: false,
|
hasESData: false,
|
||||||
|
@ -41,11 +43,11 @@ describe('Kibana No Data Page', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('renders NoDataConfigPage', async () => {
|
test('renders NoDataConfigPage', async () => {
|
||||||
const services = mockServicesFactory({ config: { ...config, hasESData: false } });
|
const services = getMockServices({ config: { ...config, hasESData: false } });
|
||||||
const component = mountWithIntl(
|
const component = mountWithIntl(
|
||||||
<SharedUxServicesProvider {...services}>
|
<KibanaNoDataPageProvider {...services}>
|
||||||
<KibanaNoDataPage noDataConfig={noDataConfig} onDataViewCreated={onDataViewCreated} />
|
<KibanaNoDataPage {...{ noDataConfig, onDataViewCreated }} />
|
||||||
</SharedUxServicesProvider>
|
</KibanaNoDataPageProvider>
|
||||||
);
|
);
|
||||||
|
|
||||||
await act(() => new Promise(setImmediate));
|
await act(() => new Promise(setImmediate));
|
||||||
|
@ -56,11 +58,11 @@ describe('Kibana No Data Page', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('renders NoDataViews', async () => {
|
test('renders NoDataViews', async () => {
|
||||||
const services = mockServicesFactory({ config: { ...config, hasESData: true } });
|
const services = getMockServices({ config: { ...config, hasESData: true } });
|
||||||
const component = mountWithIntl(
|
const component = mountWithIntl(
|
||||||
<SharedUxServicesProvider {...services}>
|
<KibanaNoDataPageProvider {...services}>
|
||||||
<KibanaNoDataPage noDataConfig={noDataConfig} onDataViewCreated={onDataViewCreated} />
|
<KibanaNoDataPage noDataConfig={noDataConfig} onDataViewCreated={onDataViewCreated} />
|
||||||
</SharedUxServicesProvider>
|
</KibanaNoDataPageProvider>
|
||||||
);
|
);
|
||||||
|
|
||||||
await act(() => new Promise(setImmediate));
|
await act(() => new Promise(setImmediate));
|
||||||
|
@ -71,23 +73,24 @@ describe('Kibana No Data Page', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('renders loading indicator', async () => {
|
test('renders loading indicator', async () => {
|
||||||
const dataCheck = () => new Promise<boolean>((resolve, reject) => {});
|
// Simulate loading with a Promise that doesn't resolve.
|
||||||
|
const dataCheck = () => new Promise<boolean>(() => {});
|
||||||
const services = {
|
const services = {
|
||||||
...mockServicesFactory(),
|
...getMockServices(),
|
||||||
data: {
|
data: {
|
||||||
hasESData: dataCheck,
|
hasESData: dataCheck,
|
||||||
hasUserDataView: dataCheck,
|
hasUserDataView: dataCheck,
|
||||||
hasDataView: dataCheck,
|
hasDataView: dataCheck,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const component = mountWithIntl(
|
const component = mountWithIntl(
|
||||||
<SharedUxServicesProvider {...services}>
|
<KibanaNoDataPageProvider {...services}>
|
||||||
<KibanaNoDataPage noDataConfig={noDataConfig} onDataViewCreated={onDataViewCreated} />
|
<KibanaNoDataPage noDataConfig={noDataConfig} onDataViewCreated={onDataViewCreated} />
|
||||||
</SharedUxServicesProvider>
|
</KibanaNoDataPageProvider>
|
||||||
);
|
);
|
||||||
|
|
||||||
await act(() => new Promise(setImmediate));
|
await act(() => new Promise(setImmediate));
|
||||||
component.update();
|
|
||||||
|
|
||||||
expect(component.find(EuiLoadingElastic).length).toBe(1);
|
expect(component.find(EuiLoadingElastic).length).toBe(1);
|
||||||
expect(component.find(NoDataViewsPrompt).length).toBe(0);
|
expect(component.find(NoDataViewsPrompt).length).toBe(0);
|
|
@ -6,27 +6,30 @@
|
||||||
* Side Public License, v 1.
|
* Side Public License, v 1.
|
||||||
*/
|
*/
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { useData, useDocLinks, useEditors, usePermissions } from '@kbn/shared-ux-services';
|
|
||||||
import {
|
|
||||||
NoDataViewsPrompt,
|
|
||||||
NoDataViewsPromptProvider,
|
|
||||||
NoDataViewsPromptServices,
|
|
||||||
} from '@kbn/shared-ux-prompt-no-data-views';
|
|
||||||
import { EuiLoadingElastic } from '@elastic/eui';
|
import { EuiLoadingElastic } from '@elastic/eui';
|
||||||
import { NoDataConfigPage, NoDataPageProps } from '../page_template';
|
import { NoDataConfigPage, NoDataPageProps } from '@kbn/shared-ux-components';
|
||||||
|
import { NoDataViewsPrompt } from '@kbn/shared-ux-prompt-no-data-views';
|
||||||
|
|
||||||
|
import { useServices } from './services';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Props for `KibanaNoDataPage`.
|
||||||
|
*/
|
||||||
export interface Props {
|
export interface Props {
|
||||||
|
/** Handler for successfully creating a new data view. */
|
||||||
onDataViewCreated: (dataView: unknown) => void;
|
onDataViewCreated: (dataView: unknown) => void;
|
||||||
|
/** `NoDataPage` configuration; see `NoDataPageProps`. */
|
||||||
noDataConfig: NoDataPageProps;
|
noDataConfig: NoDataPageProps;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A page to display when Kibana has no data, prompting a person to add integrations or create a new data view.
|
||||||
|
*/
|
||||||
export const KibanaNoDataPage = ({ onDataViewCreated, noDataConfig }: Props) => {
|
export const KibanaNoDataPage = ({ onDataViewCreated, noDataConfig }: Props) => {
|
||||||
// These hooks are temporary, until this component is moved to a package.
|
// These hooks are temporary, until this component is moved to a package.
|
||||||
const { canCreateNewDataView } = usePermissions();
|
const services = useServices();
|
||||||
const { dataViewsDocLink } = useDocLinks();
|
const { hasESData, hasUserDataView } = services;
|
||||||
const { openDataViewEditor } = useEditors();
|
|
||||||
|
|
||||||
const { hasESData, hasUserDataView } = useData();
|
|
||||||
const [isLoading, setIsLoading] = useState(true);
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
const [dataExists, setDataExists] = useState(false);
|
const [dataExists, setDataExists] = useState(false);
|
||||||
const [hasUserDataViews, setHasUserDataViews] = useState(false);
|
const [hasUserDataViews, setHasUserDataViews] = useState(false);
|
||||||
|
@ -48,26 +51,8 @@ export const KibanaNoDataPage = ({ onDataViewCreated, noDataConfig }: Props) =>
|
||||||
return <EuiLoadingElastic css={{ margin: 'auto' }} size="xxl" />;
|
return <EuiLoadingElastic css={{ margin: 'auto' }} size="xxl" />;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
TODO: clintandrewhall - the use and population of `NoDataViewPromptProvider` here is temporary,
|
|
||||||
until `KibanaNoDataPage` is moved to a package of its own.
|
|
||||||
|
|
||||||
Once `KibanaNoDataPage` is moved to a package, `NoDataViewsPromptProvider` will be *combined*
|
|
||||||
with `KibanaNoDataPageProvider`, creating a single Provider that manages contextual dependencies
|
|
||||||
throughout the React tree from the top-level of composition and consumption.
|
|
||||||
*/
|
|
||||||
if (!hasUserDataViews && dataExists) {
|
if (!hasUserDataViews && dataExists) {
|
||||||
const services: NoDataViewsPromptServices = {
|
return <NoDataViewsPrompt onDataViewCreated={onDataViewCreated} />;
|
||||||
canCreateNewDataView,
|
|
||||||
dataViewsDocLink,
|
|
||||||
openDataViewEditor,
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<NoDataViewsPromptProvider {...services}>
|
|
||||||
<NoDataViewsPrompt onDataViewCreated={onDataViewCreated} />
|
|
||||||
</NoDataViewsPromptProvider>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!dataExists) {
|
if (!dataExists) {
|
|
@ -10,7 +10,7 @@ export { SharedUxServicesProvider as LegacyServicesProvider } from '@kbn/shared-
|
||||||
export type { SharedUxServices as LegacyServices } from '@kbn/shared-ux-services';
|
export type { SharedUxServices as LegacyServices } from '@kbn/shared-ux-services';
|
||||||
|
|
||||||
import { SharedUxServices as LegacyServices } from '@kbn/shared-ux-services';
|
import { SharedUxServices as LegacyServices } from '@kbn/shared-ux-services';
|
||||||
import { Services } from './services';
|
import { KibanaNoDataPageServices } from './services';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This list is temporary, a stop-gap as we migrate to a package-based architecture, where
|
* This list is temporary, a stop-gap as we migrate to a package-based architecture, where
|
||||||
|
@ -20,7 +20,7 @@ import { Services } from './services';
|
||||||
* Expect this list to dwindle to zero as `@kbn/shared-ux-components` are migrated to their
|
* Expect this list to dwindle to zero as `@kbn/shared-ux-components` are migrated to their
|
||||||
* own packages, (and `@kbn/shared-ux-services` is removed).
|
* own packages, (and `@kbn/shared-ux-services` is removed).
|
||||||
*/
|
*/
|
||||||
export const getLegacyServices = (services: Services): LegacyServices => ({
|
export const getLegacyServices = (services: KibanaNoDataPageServices): LegacyServices => ({
|
||||||
application: {
|
application: {
|
||||||
currentAppId$: services.currentAppId$,
|
currentAppId$: services.currentAppId$,
|
||||||
navigateToUrl: services.navigateToUrl,
|
navigateToUrl: services.navigateToUrl,
|
104
packages/shared-ux/page/kibana_no_data/src/mocks.ts
Normal file
104
packages/shared-ux/page/kibana_no_data/src/mocks.ts
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
/*
|
||||||
|
* 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 { servicesFactory } from '@kbn/shared-ux-storybook';
|
||||||
|
import { mockServicesFactory, MockServicesFactoryParams } from '@kbn/shared-ux-services';
|
||||||
|
import {
|
||||||
|
getNoDataViewsPromptStoryArgTypes,
|
||||||
|
getNoDataViewsPromptStorybookServices,
|
||||||
|
getNoDataViewsPromptMockServices,
|
||||||
|
} from '@kbn/shared-ux-prompt-no-data-views';
|
||||||
|
|
||||||
|
import { KibanaNoDataPageServices } from './services';
|
||||||
|
|
||||||
|
// TODO: clintandrewhall - this looks (and is) a bit complicated because the No Data View
|
||||||
|
// dependency has not been converted to its own package yet. As with `AnalyticsNoDataPage`,
|
||||||
|
// this file will be significantly simplified when that happens.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parameters drawn from the Storybook arguments collection that customize a component story.
|
||||||
|
*/
|
||||||
|
export type StoryParams = Record<keyof ReturnType<typeof getStoryArgTypes>, any>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns Storybook-compatible service abstractions for the `KibanaNoDataPage` Provider.
|
||||||
|
*/
|
||||||
|
export const getStoryServices = (params: StoryParams) => {
|
||||||
|
const { canCreateNewDataView, dataViewsDocLink, openDataViewEditor } =
|
||||||
|
getNoDataViewsPromptStorybookServices(params);
|
||||||
|
|
||||||
|
// Workaround to leverage the services package.
|
||||||
|
const { application, data, docLinks, editors, http, permissions, platform } =
|
||||||
|
servicesFactory(params);
|
||||||
|
|
||||||
|
const services: KibanaNoDataPageServices = {
|
||||||
|
...application,
|
||||||
|
...data,
|
||||||
|
...docLinks,
|
||||||
|
...editors,
|
||||||
|
...http,
|
||||||
|
...permissions,
|
||||||
|
...platform,
|
||||||
|
canCreateNewDataView,
|
||||||
|
dataViewsDocLink,
|
||||||
|
openDataViewEditor,
|
||||||
|
};
|
||||||
|
|
||||||
|
return services;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the Storybook arguments for `KibanaNoDataPage`, for its stories for and for
|
||||||
|
* consuming component stories.
|
||||||
|
*/
|
||||||
|
export const getStoryArgTypes = () => ({
|
||||||
|
solution: {
|
||||||
|
control: 'text',
|
||||||
|
defaultValue: 'Observability',
|
||||||
|
},
|
||||||
|
logo: {
|
||||||
|
control: { type: 'radio' },
|
||||||
|
options: ['logoElastic', 'logoKibana', 'logoCloud', undefined],
|
||||||
|
defaultValue: undefined,
|
||||||
|
},
|
||||||
|
hasESData: {
|
||||||
|
control: 'boolean',
|
||||||
|
defaultValue: false,
|
||||||
|
},
|
||||||
|
hasUserDataView: {
|
||||||
|
control: 'boolean',
|
||||||
|
defaultValue: false,
|
||||||
|
},
|
||||||
|
...getNoDataViewsPromptStoryArgTypes(),
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the Jest-compatible service abstractions for the `KibanaNoDataPage` Provider.
|
||||||
|
*/
|
||||||
|
export const getMockServices = (params?: MockServicesFactoryParams) => {
|
||||||
|
const { canCreateNewDataView, dataViewsDocLink, openDataViewEditor } =
|
||||||
|
getNoDataViewsPromptMockServices();
|
||||||
|
|
||||||
|
const { application, data, docLinks, editors, http, permissions, platform } =
|
||||||
|
mockServicesFactory(params);
|
||||||
|
|
||||||
|
const services: KibanaNoDataPageServices = {
|
||||||
|
...application,
|
||||||
|
...data,
|
||||||
|
...docLinks,
|
||||||
|
...editors,
|
||||||
|
...http,
|
||||||
|
...permissions,
|
||||||
|
...platform,
|
||||||
|
canCreateNewDataView,
|
||||||
|
dataViewsDocLink,
|
||||||
|
openDataViewEditor,
|
||||||
|
};
|
||||||
|
|
||||||
|
return services;
|
||||||
|
};
|
181
packages/shared-ux/page/kibana_no_data/src/services.tsx
Normal file
181
packages/shared-ux/page/kibana_no_data/src/services.tsx
Normal file
|
@ -0,0 +1,181 @@
|
||||||
|
/*
|
||||||
|
* 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, useContext } from 'react';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import {
|
||||||
|
NoDataViewsPromptProvider,
|
||||||
|
NoDataViewsPromptKibanaProvider,
|
||||||
|
} from '@kbn/shared-ux-prompt-no-data-views';
|
||||||
|
|
||||||
|
import { LegacyServicesProvider, getLegacyServices } from './legacy_services';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO: `DataView` is a class exported by `src/plugins/data_views/public`. Since this service
|
||||||
|
* is contained in this package-- and packages can only depend on other packages and never on
|
||||||
|
* plugins-- we have to set this to `unknown`. If and when `DataView` is exported from a
|
||||||
|
* stateless package, we can remove this.
|
||||||
|
*
|
||||||
|
* @see: https://github.com/elastic/kibana/issues/127695
|
||||||
|
*/
|
||||||
|
type DataView = unknown;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A subset of the `DataViewEditorOptions` interface relevant to this component.
|
||||||
|
*
|
||||||
|
* @see: src/plugins/data_view_editor/public/types.ts
|
||||||
|
*/
|
||||||
|
interface DataViewEditorOptions {
|
||||||
|
/** Handler to be invoked when the Data View Editor completes a save operation. */
|
||||||
|
onSave: (dataView: DataView) => void;
|
||||||
|
/** If set to false, will skip empty prompt in data view editor. */
|
||||||
|
showEmptyPrompt?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A list of Services that are consumed by this component.
|
||||||
|
*
|
||||||
|
* This list is temporary, a stopgap as we migrate to a package-based architecture, where
|
||||||
|
* services are not collected in a single package. In order to make the transition, this
|
||||||
|
* interface is intentionally "flat".
|
||||||
|
*
|
||||||
|
* Expect this list to dwindle to zero as `@kbn/shared-ux-components` are migrated to their
|
||||||
|
* own packages, (and `@kbn/shared-ux-services` is removed).
|
||||||
|
*/
|
||||||
|
export interface KibanaNoDataPageServices {
|
||||||
|
/** True if the cluster contains data, false otherwise. */
|
||||||
|
hasESData: () => Promise<boolean>;
|
||||||
|
/** True if Kibana instance contains user-created data view, false otherwise. */
|
||||||
|
hasUserDataView: () => Promise<boolean>;
|
||||||
|
|
||||||
|
// Provided to Legacy Services, not relevant to this component. Will be removed.
|
||||||
|
/** Append the server base path to a relative URL. */
|
||||||
|
addBasePath: (url: string) => string;
|
||||||
|
/** True if the user has permission to access Fleet, false otherwise. */
|
||||||
|
canAccessFleet: boolean;
|
||||||
|
/** True if the user has permission to create a new Data View, false otherwise. */
|
||||||
|
canCreateNewDataView: boolean;
|
||||||
|
/** Observable storing the active, current application ID. */
|
||||||
|
currentAppId$: Observable<string | undefined>;
|
||||||
|
/** A link to information about Data Views in Kibana */
|
||||||
|
dataViewsDocLink: string;
|
||||||
|
/** True if Kibana instance contains any data view, including system-created ones. */
|
||||||
|
hasDataView: () => Promise<boolean>;
|
||||||
|
/** Use Kibana to navigate async to a different URL. */
|
||||||
|
navigateToUrl: (url: string) => Promise<void>;
|
||||||
|
/** A method to open the Data View Editor flow. */
|
||||||
|
openDataViewEditor: (options: DataViewEditorOptions) => () => void;
|
||||||
|
/** Set the Kibana chrome and browser to full screen mode. */
|
||||||
|
setIsFullscreen: (isFullscreen: boolean) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const KibanaNoDataPageContext = React.createContext<KibanaNoDataPageServices | null>(null);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Context Provider that provides services to the component.
|
||||||
|
*/
|
||||||
|
export const KibanaNoDataPageProvider: FC<KibanaNoDataPageServices> = ({
|
||||||
|
children,
|
||||||
|
...services
|
||||||
|
}) => (
|
||||||
|
<KibanaNoDataPageContext.Provider value={services}>
|
||||||
|
<NoDataViewsPromptProvider {...services}>
|
||||||
|
<LegacyServicesProvider {...getLegacyServices(services)}>{children}</LegacyServicesProvider>
|
||||||
|
</NoDataViewsPromptProvider>
|
||||||
|
</KibanaNoDataPageContext.Provider>
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An interface containing a collection of Kibana plugins and services required to
|
||||||
|
* render this component and its dependencies.
|
||||||
|
*/
|
||||||
|
export interface KibanaNoDataPageKibanaDependencies {
|
||||||
|
coreStart: {
|
||||||
|
application: {
|
||||||
|
capabilities: {
|
||||||
|
navLinks: Record<string, boolean>;
|
||||||
|
};
|
||||||
|
currentAppId$: Observable<string | undefined>;
|
||||||
|
navigateToUrl: (url: string) => Promise<void>;
|
||||||
|
};
|
||||||
|
chrome: {
|
||||||
|
setIsVisible: (isVisible: boolean) => void;
|
||||||
|
};
|
||||||
|
docLinks: {
|
||||||
|
links: {
|
||||||
|
indexPatterns: {
|
||||||
|
introduction: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
http: {
|
||||||
|
basePath: {
|
||||||
|
prepend: (url: string) => string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
dataViews: {
|
||||||
|
hasData: {
|
||||||
|
hasDataView: () => Promise<boolean>;
|
||||||
|
hasESData: () => Promise<boolean>;
|
||||||
|
hasUserDataView: () => Promise<boolean>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
dataViewEditor: {
|
||||||
|
openEditor: (options: DataViewEditorOptions) => () => void;
|
||||||
|
userPermissions: {
|
||||||
|
editDataView: () => boolean;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Kibana-specific Provider that maps dependencies to services.
|
||||||
|
*/
|
||||||
|
export const KibanaNoDataPageKibanaProvider: FC<KibanaNoDataPageKibanaDependencies> = ({
|
||||||
|
children,
|
||||||
|
...dependencies
|
||||||
|
}) => {
|
||||||
|
const { coreStart, dataViewEditor, dataViews } = dependencies;
|
||||||
|
const value: KibanaNoDataPageServices = {
|
||||||
|
addBasePath: coreStart.http.basePath.prepend,
|
||||||
|
canAccessFleet: coreStart.application.capabilities.navLinks.integrations,
|
||||||
|
canCreateNewDataView: dataViewEditor.userPermissions.editDataView(),
|
||||||
|
currentAppId$: coreStart.application.currentAppId$,
|
||||||
|
dataViewsDocLink: coreStart.docLinks.links.indexPatterns?.introduction,
|
||||||
|
hasDataView: dataViews.hasData.hasDataView,
|
||||||
|
hasESData: dataViews.hasData.hasESData,
|
||||||
|
hasUserDataView: dataViews.hasData.hasUserDataView,
|
||||||
|
navigateToUrl: coreStart.application.navigateToUrl,
|
||||||
|
openDataViewEditor: dataViewEditor.openEditor,
|
||||||
|
setIsFullscreen: (isVisible: boolean) => coreStart.chrome.setIsVisible(isVisible),
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<KibanaNoDataPageContext.Provider value={value}>
|
||||||
|
<NoDataViewsPromptKibanaProvider {...dependencies}>
|
||||||
|
<LegacyServicesProvider {...getLegacyServices(value)}>{children}</LegacyServicesProvider>
|
||||||
|
</NoDataViewsPromptKibanaProvider>
|
||||||
|
</KibanaNoDataPageContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* React hook for accessing pre-wired services.
|
||||||
|
*/
|
||||||
|
export function useServices() {
|
||||||
|
const context = useContext(KibanaNoDataPageContext);
|
||||||
|
|
||||||
|
if (!context) {
|
||||||
|
throw new Error(
|
||||||
|
'KibanaNoDataPageContext is missing. Ensure your component or React root is wrapped with KibanaNoDataPageContext.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return context;
|
||||||
|
}
|
20
packages/shared-ux/page/kibana_no_data/tsconfig.json
Normal file
20
packages/shared-ux/page/kibana_no_data/tsconfig.json
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"extends": "../../../../tsconfig.bazel.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"declaration": true,
|
||||||
|
"emitDeclarationOnly": true,
|
||||||
|
"outDir": "target_types",
|
||||||
|
"rootDir": "src",
|
||||||
|
"stripInternal": false,
|
||||||
|
"types": [
|
||||||
|
"jest",
|
||||||
|
"node",
|
||||||
|
"react",
|
||||||
|
"@emotion/react/types/css-prop",
|
||||||
|
"@kbn/ambient-ui-types",
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"src/**/*"
|
||||||
|
]
|
||||||
|
}
|
|
@ -1,14 +1,28 @@
|
||||||
**id:** sharedUX/Components/NoDataViewsPrompt
|
---
|
||||||
**slug:** /shared-ux/components/no-data-views
|
id: sharedUX/Prompt/NoDataViews
|
||||||
**title:** No Data Views
|
slug: /shared-ux/prompt/no-data-views
|
||||||
**summary:** A prompt to be displayed when there is data in Elasticsearch, but no data views
|
title: No Data Views Prompt
|
||||||
**tags:** ['shared-ux', 'component']
|
summary: A prompt to be displayed when there is data in Elasticsearch, but no data views
|
||||||
**date:** 2022-02-09
|
tags: ['shared-ux', 'component', 'prompt', 'no-data']
|
||||||
|
date: 2022-02-09
|
||||||
---
|
---
|
||||||
|
|
||||||
When there is data in Elasticsearch, but there haven't been any data views created yet, we want to display an appropriate message to the user and facilitate creation of data views (if appropriate permissions are in place).
|
When there is data in Elasticsearch, but there haven't been any data views created yet, we want to display an appropriate message to the user and facilitate creation of data views, (if appropriate permissions are in place).
|
||||||
|
|
||||||
The pure component, `no_data_views.component.tsx`, is a pre-configured **EuiEmptyPrompt**.
|
The `NoDataViewsPrompt` connected component uses:
|
||||||
|
|
||||||
The connected component, `no_data_views.tsx`, uses services from the `shared_ux` plugin to open Data View Editor. You must wrap your plugin app in the `ServicesContext` provided by the start contract of the `shared_ux` plugin to use it.
|
- `userPermissions.editDataView` from `data_view_editor` to determine if the user has permission to create data views
|
||||||
|
- `openEditor` from `data_view_editor` to facilitate creating a data view.
|
||||||
|
- `docLinks` from `coreStart` to populate documentation links.
|
||||||
|
|
||||||
|
## API
|
||||||
|
| Export | Description |
|
||||||
|
|---|---|
|
||||||
|
| `NoDataViewsPromptProvider` | Provides contextual services to `NoDataViewsPrompt`. |
|
||||||
|
| `NoDataViewsPromptKibanaProvider` | Maps Kibana dependencies to provide contextual services to `NoDataViewsPrompt`. |
|
||||||
|
| `NoDataViewsPrompt` | Uses a `Provider` to access contextual services to populate props on the `NoDataViewsPromptComponent`. |
|
||||||
|
| `NoDataViewsPromptComponent` | The pure component, a pre-configured **EuiEmptyPrompt**. |
|
||||||
|
|
||||||
|
## EUI Promotion Status
|
||||||
|
|
||||||
|
This component is not currently considered for promotion to EUI.
|
|
@ -6,7 +6,13 @@
|
||||||
* Side Public License, v 1.
|
* Side Public License, v 1.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export { NoDataViewsPromptKibanaProvider, NoDataViewsPromptProvider } from './services';
|
|
||||||
export type { NoDataViewsPromptKibanaServices, NoDataViewsPromptServices } from './services';
|
|
||||||
export { NoDataViewsPrompt } from './no_data_views';
|
export { NoDataViewsPrompt } from './no_data_views';
|
||||||
export { NoDataViewsPrompt as NoDataViewsPromptComponent } from './no_data_views.component';
|
export { NoDataViewsPrompt as NoDataViewsPromptComponent } from './no_data_views.component';
|
||||||
|
export { NoDataViewsPromptKibanaProvider, NoDataViewsPromptProvider } from './services';
|
||||||
|
export type { NoDataViewsPromptKibanaServices, NoDataViewsPromptServices } from './services';
|
||||||
|
|
||||||
|
export {
|
||||||
|
getMockServices as getNoDataViewsPromptMockServices,
|
||||||
|
getStoryArgTypes as getNoDataViewsPromptStoryArgTypes,
|
||||||
|
getStoryServices as getNoDataViewsPromptStorybookServices,
|
||||||
|
} from './mocks';
|
||||||
|
|
48
packages/shared-ux/prompt/no_data_views/src/mocks.ts
Normal file
48
packages/shared-ux/prompt/no_data_views/src/mocks.ts
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
/*
|
||||||
|
* 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 { action } from '@storybook/addon-actions';
|
||||||
|
|
||||||
|
import { NoDataViewsPromptServices } from './services';
|
||||||
|
|
||||||
|
export type Params = Record<keyof ReturnType<typeof getStoryArgTypes>, any>;
|
||||||
|
|
||||||
|
export const getStoryServices = (params: Params) => {
|
||||||
|
const services: NoDataViewsPromptServices = {
|
||||||
|
...params,
|
||||||
|
openDataViewEditor: (options) => {
|
||||||
|
action('openDataViewEditor')(options);
|
||||||
|
return () => {};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return services;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getStoryArgTypes = () => ({
|
||||||
|
canCreateNewDataView: {
|
||||||
|
control: 'boolean',
|
||||||
|
defaultValue: true,
|
||||||
|
},
|
||||||
|
dataViewsDocLink: {
|
||||||
|
options: ['some/link', undefined],
|
||||||
|
control: { type: 'radio' },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const getMockServices = (params?: Params) => {
|
||||||
|
const { canCreateNewDataView, dataViewsDocLink } = params || {};
|
||||||
|
|
||||||
|
const services: NoDataViewsPromptServices = {
|
||||||
|
canCreateNewDataView: canCreateNewDataView || true,
|
||||||
|
dataViewsDocLink: dataViewsDocLink || 'some/link',
|
||||||
|
openDataViewEditor: jest.fn(),
|
||||||
|
};
|
||||||
|
|
||||||
|
return services;
|
||||||
|
};
|
|
@ -17,9 +17,13 @@ import { withSuspense } from '@kbn/shared-ux-utility';
|
||||||
import { DocumentationLink } from './documentation_link';
|
import { DocumentationLink } from './documentation_link';
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
|
/** True if the user has permission to create a data view, false otherwise. */
|
||||||
canCreateNewDataView: boolean;
|
canCreateNewDataView: boolean;
|
||||||
|
/** Click handler for create button. **/
|
||||||
onClickCreate?: () => void;
|
onClickCreate?: () => void;
|
||||||
|
/** Link to documentation on data views. */
|
||||||
dataViewsDocLink?: string;
|
dataViewsDocLink?: string;
|
||||||
|
/** The background color of the prompt; defaults to `plain`. */
|
||||||
emptyPromptColor?: EuiEmptyPromptProps['color'];
|
emptyPromptColor?: EuiEmptyPromptProps['color'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,14 +8,17 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { action } from '@storybook/addon-actions';
|
import { action } from '@storybook/addon-actions';
|
||||||
|
|
||||||
import { NoDataViewsPrompt as NoDataViewsPromptComponent, Props } from './no_data_views.component';
|
import { NoDataViewsPrompt as NoDataViewsPromptComponent } from './no_data_views.component';
|
||||||
import { NoDataViewsPrompt } from './no_data_views';
|
import { NoDataViewsPrompt } from './no_data_views';
|
||||||
import { NoDataViewsPromptProvider, NoDataViewsPromptServices } from './services';
|
import { NoDataViewsPromptProvider } from './services';
|
||||||
|
|
||||||
import mdx from '../README.mdx';
|
import mdx from '../README.mdx';
|
||||||
|
import { Params, getStoryArgTypes, getStoryServices } from './mocks';
|
||||||
|
|
||||||
|
const argTypes = getStoryArgTypes();
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
title: 'No Data/No Data Views',
|
title: 'No Data/Prompt',
|
||||||
description: 'A component to display when there are no user-created data views available.',
|
description: 'A component to display when there are no user-created data views available.',
|
||||||
parameters: {
|
parameters: {
|
||||||
docs: {
|
docs: {
|
||||||
|
@ -24,45 +27,36 @@ export default {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
type ConnectedParams = Pick<NoDataViewsPromptServices, 'canCreateNewDataView' | 'dataViewsDocLink'>;
|
export const NoDataViews = (params: Params) => {
|
||||||
|
|
||||||
const openDataViewEditor: NoDataViewsPromptServices['openDataViewEditor'] = (options) => {
|
|
||||||
action('openDataViewEditor')(options);
|
|
||||||
return () => {};
|
|
||||||
};
|
|
||||||
|
|
||||||
export const ConnectedComponent = (params: ConnectedParams) => {
|
|
||||||
return (
|
return (
|
||||||
<NoDataViewsPromptProvider {...{ openDataViewEditor, ...params }}>
|
<NoDataViewsPromptProvider {...getStoryServices(params)}>
|
||||||
<NoDataViewsPrompt onDataViewCreated={action('onDataViewCreated')} />
|
<NoDataViewsPrompt onDataViewCreated={action('onDataViewCreated')} />
|
||||||
</NoDataViewsPromptProvider>
|
</NoDataViewsPromptProvider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
ConnectedComponent.argTypes = {
|
NoDataViews.argTypes = argTypes;
|
||||||
canCreateNewDataView: {
|
|
||||||
control: 'boolean',
|
const componentArgTypes = {
|
||||||
defaultValue: true,
|
...argTypes,
|
||||||
},
|
emptyPromptColor: {
|
||||||
dataViewsDocLink: {
|
options: [
|
||||||
options: ['some/link', undefined],
|
'plain',
|
||||||
control: { type: 'radio' },
|
'transparent',
|
||||||
|
'subdued',
|
||||||
|
'accent',
|
||||||
|
'primary',
|
||||||
|
'success',
|
||||||
|
'warning',
|
||||||
|
'danger',
|
||||||
|
],
|
||||||
|
control: { type: 'select' },
|
||||||
|
defaultValue: 'plain',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
type PureParams = Pick<Props, 'canCreateNewDataView' | 'dataViewsDocLink'>;
|
export const NoDataViewsComponent = (params: Record<keyof typeof componentArgTypes, any>) => {
|
||||||
|
|
||||||
export const PureComponent = (params: PureParams) => {
|
|
||||||
return <NoDataViewsPromptComponent onClickCreate={action('onClick')} {...params} />;
|
return <NoDataViewsPromptComponent onClickCreate={action('onClick')} {...params} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
PureComponent.argTypes = {
|
NoDataViewsComponent.argTypes = componentArgTypes;
|
||||||
canCreateNewDataView: {
|
|
||||||
control: 'boolean',
|
|
||||||
defaultValue: true,
|
|
||||||
},
|
|
||||||
dataViewsDocLink: {
|
|
||||||
options: ['some/link', undefined],
|
|
||||||
control: { type: 'radio' },
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
|
@ -14,19 +14,14 @@ import { EuiButton } from '@elastic/eui';
|
||||||
|
|
||||||
import { NoDataViewsPrompt } from './no_data_views';
|
import { NoDataViewsPrompt } from './no_data_views';
|
||||||
import { NoDataViewsPromptServices, NoDataViewsPromptProvider } from './services';
|
import { NoDataViewsPromptServices, NoDataViewsPromptProvider } from './services';
|
||||||
|
import { getMockServices } from './mocks';
|
||||||
const getServices = (canCreateNewDataView: boolean = true) => ({
|
|
||||||
canCreateNewDataView,
|
|
||||||
openDataViewEditor: jest.fn(),
|
|
||||||
dataViewsDocLink: 'some/link',
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('<NoDataViewsPromptTest />', () => {
|
describe('<NoDataViewsPromptTest />', () => {
|
||||||
let services: NoDataViewsPromptServices;
|
let services: NoDataViewsPromptServices;
|
||||||
let mount: (element: JSX.Element) => ReactWrapper;
|
let mount: (element: JSX.Element) => ReactWrapper;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
services = getServices();
|
services = getMockServices();
|
||||||
mount = (element: JSX.Element) =>
|
mount = (element: JSX.Element) =>
|
||||||
mountWithIntl(<NoDataViewsPromptProvider {...services}>{element}</NoDataViewsPromptProvider>);
|
mountWithIntl(<NoDataViewsPromptProvider {...services}>{element}</NoDataViewsPromptProvider>);
|
||||||
});
|
});
|
||||||
|
|
|
@ -13,6 +13,7 @@ import { useServices, NoDataViewsPromptServices } from './services';
|
||||||
|
|
||||||
// TODO: https://github.com/elastic/kibana/issues/127695
|
// TODO: https://github.com/elastic/kibana/issues/127695
|
||||||
export interface Props {
|
export interface Props {
|
||||||
|
/** Handler for successfully creating a new data view. */
|
||||||
onDataViewCreated: (dataView: unknown) => void;
|
onDataViewCreated: (dataView: unknown) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,10 +23,7 @@ type CloseDataViewEditorFn = ReturnType<NoDataViewsPromptServices['openDataViewE
|
||||||
* A service-enabled component that provides Kibana-specific functionality to the `NoDataViewsPrompt`
|
* A service-enabled component that provides Kibana-specific functionality to the `NoDataViewsPrompt`
|
||||||
* component.
|
* component.
|
||||||
*
|
*
|
||||||
* Use of this component requires both the `EuiTheme` context as well as either a configured Shared UX
|
* Use of this component requires both the `EuiTheme` context as well as a `NoDataViewsPrompt` provider.
|
||||||
* `ServicesProvider` or the `ServicesContext` provided by the Shared UX public plugin contract.
|
|
||||||
*
|
|
||||||
* See shared-ux/public/services for information.
|
|
||||||
*/
|
*/
|
||||||
export const NoDataViewsPrompt = ({ onDataViewCreated }: Props) => {
|
export const NoDataViewsPrompt = ({ onDataViewCreated }: Props) => {
|
||||||
const { canCreateNewDataView, openDataViewEditor, dataViewsDocLink } = useServices();
|
const { canCreateNewDataView, openDataViewEditor, dataViewsDocLink } = useServices();
|
||||||
|
|
|
@ -51,8 +51,14 @@ export const NoDataViewsPromptProvider: FC<NoDataViewsPromptServices> = ({
|
||||||
children,
|
children,
|
||||||
...services
|
...services
|
||||||
}) => {
|
}) => {
|
||||||
|
// Typescript types are widened to accept more than what is needed. Take only what is necessary
|
||||||
|
// so the context remains clean.
|
||||||
|
const { canCreateNewDataView, dataViewsDocLink, openDataViewEditor } = services;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NoDataViewsPromptContext.Provider value={services}>
|
<NoDataViewsPromptContext.Provider
|
||||||
|
value={{ canCreateNewDataView, dataViewsDocLink, openDataViewEditor }}
|
||||||
|
>
|
||||||
{children}
|
{children}
|
||||||
</NoDataViewsPromptContext.Provider>
|
</NoDataViewsPromptContext.Provider>
|
||||||
);
|
);
|
||||||
|
|
|
@ -3319,6 +3319,10 @@
|
||||||
version "0.0.0"
|
version "0.0.0"
|
||||||
uid ""
|
uid ""
|
||||||
|
|
||||||
|
"@kbn/shared-ux-page-kibana-no-data@link:bazel-bin/packages/shared-ux/page/kibana_no_data":
|
||||||
|
version "0.0.0"
|
||||||
|
uid ""
|
||||||
|
|
||||||
"@kbn/shared-ux-prompt-no-data-views@link:bazel-bin/packages/shared-ux/prompt/no_data_views":
|
"@kbn/shared-ux-prompt-no-data-views@link:bazel-bin/packages/shared-ux/prompt/no_data_views":
|
||||||
version "0.0.0"
|
version "0.0.0"
|
||||||
uid ""
|
uid ""
|
||||||
|
@ -6638,6 +6642,10 @@
|
||||||
version "0.0.0"
|
version "0.0.0"
|
||||||
uid ""
|
uid ""
|
||||||
|
|
||||||
|
"@types/kbn__shared-ux-page-kibana-no-data@link:bazel-bin/packages/shared-ux/page/kibana_no_data/npm_module_types":
|
||||||
|
version "0.0.0"
|
||||||
|
uid ""
|
||||||
|
|
||||||
"@types/kbn__shared-ux-prompt-no-data-views@link:bazel-bin/packages/shared-ux/prompt/no_data_views/npm_module_types":
|
"@types/kbn__shared-ux-prompt-no-data-views@link:bazel-bin/packages/shared-ux/prompt/no_data_views/npm_module_types":
|
||||||
version "0.0.0"
|
version "0.0.0"
|
||||||
uid ""
|
uid ""
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue