From 64c250f46b951c5197c8782e1386aa23fb634088 Mon Sep 17 00:00:00 2001 From: Chris Cowan Date: Wed, 23 Oct 2019 17:12:19 -0700 Subject: [PATCH] Convert layouts to use React compontents - This PR closes #48808 - Move all files under pages/metrics --- .../inventory_models/container/index.ts | 8 + .../inventory_models/container/layout.v2.tsx | 222 +++++++++++ .../inventory_models/host/detail_page.tsx | 142 +++++++ .../common/inventory_models/host/index.ts | 18 + .../inventory_models/host/layout.v2.tsx | 352 ++++++++++++++++++ .../infra/common/inventory_models/index.ts | 2 +- .../infra/common/inventory_models/layouts.ts | 44 +++ .../common/inventory_models/pod/index.ts | 9 + .../common/inventory_models/pod/layout.v2.tsx | 154 ++++++++ .../shared/layouts/aws.v2.tsx | 237 ++++++++++++ .../shared/layouts/nginx.v2.tsx | 103 +++++ .../shared/metrics/required_metrics.ts | 21 ++ .../infra/common/inventory_models/types.ts | 2 + .../infra/public/components/metrics/index.tsx | 128 ------- .../public/components/metrics/section.tsx | 42 --- .../metrics/sections/gauges_section.tsx | 104 ------ .../components/metrics/sections/index.ts | 13 - .../public/components/metrics/side_nav.tsx | 81 ---- .../metadata/lib/get_filtered_metrics.ts | 25 ++ .../containers/metadata/use_metadata.ts | 6 +- .../redirect_to_host_detail_via_ip.tsx | 2 +- .../pages/link_to/redirect_to_node_detail.tsx | 2 +- .../metrics/components/chart_section_vis.tsx} | 51 +-- .../metrics/components}/error_message.tsx | 0 .../metrics/components/gauges_section_vis.tsx | 101 +++++ .../metrics/components/helpers.ts} | 50 ++- .../metrics/components}/invalid_node.tsx | 6 +- .../metrics/components}/node_details.tsx | 4 +- .../pages/metrics/components/page_body.tsx | 72 ++++ .../pages/metrics/components/page_error.tsx | 45 +++ .../pages/metrics/components/section.tsx | 67 ++++ .../metrics/components}/series_chart.tsx | 0 .../pages/metrics/components/side_nav.tsx | 52 +++ .../pages/metrics/components/sub_section.tsx | 55 +++ .../components}/time_controls.test.tsx | 2 +- .../metrics/components}/time_controls.tsx | 4 +- .../metrics/containers}/metrics.gql_query.ts | 0 .../metrics/containers}/metrics_time.test.tsx | 0 .../metrics/containers}/with_metrics.tsx | 21 +- .../metrics/containers}/with_metrics_time.tsx | 4 +- .../infra/public/pages/metrics/index.tsx | 120 +++--- .../pages/metrics/lib/side_nav_context.ts | 24 ++ .../public/pages/metrics/page_providers.tsx | 2 +- .../infra/public/pages/metrics/types.ts | 62 +++ .../infra/public/utils/formatters/index.ts | 17 +- 45 files changed, 1947 insertions(+), 529 deletions(-) create mode 100644 x-pack/legacy/plugins/infra/common/inventory_models/container/layout.v2.tsx create mode 100644 x-pack/legacy/plugins/infra/common/inventory_models/host/detail_page.tsx create mode 100644 x-pack/legacy/plugins/infra/common/inventory_models/host/layout.v2.tsx create mode 100644 x-pack/legacy/plugins/infra/common/inventory_models/layouts.ts create mode 100644 x-pack/legacy/plugins/infra/common/inventory_models/pod/layout.v2.tsx create mode 100644 x-pack/legacy/plugins/infra/common/inventory_models/shared/layouts/aws.v2.tsx create mode 100644 x-pack/legacy/plugins/infra/common/inventory_models/shared/layouts/nginx.v2.tsx create mode 100644 x-pack/legacy/plugins/infra/common/inventory_models/shared/metrics/required_metrics.ts delete mode 100644 x-pack/legacy/plugins/infra/public/components/metrics/index.tsx delete mode 100644 x-pack/legacy/plugins/infra/public/components/metrics/section.tsx delete mode 100644 x-pack/legacy/plugins/infra/public/components/metrics/sections/gauges_section.tsx delete mode 100644 x-pack/legacy/plugins/infra/public/components/metrics/sections/index.ts delete mode 100644 x-pack/legacy/plugins/infra/public/components/metrics/side_nav.tsx create mode 100644 x-pack/legacy/plugins/infra/public/containers/metadata/lib/get_filtered_metrics.ts rename x-pack/legacy/plugins/infra/public/{components/metrics/sections/chart_section.tsx => pages/metrics/components/chart_section_vis.tsx} (72%) rename x-pack/legacy/plugins/infra/public/{components/metrics/sections => pages/metrics/components}/error_message.tsx (100%) create mode 100644 x-pack/legacy/plugins/infra/public/pages/metrics/components/gauges_section_vis.tsx rename x-pack/legacy/plugins/infra/public/{components/metrics/sections/helpers/index.ts => pages/metrics/components/helpers.ts} (58%) rename x-pack/legacy/plugins/infra/public/{components/metrics => pages/metrics/components}/invalid_node.tsx (92%) rename x-pack/legacy/plugins/infra/public/{components/metrics => pages/metrics/components}/node_details.tsx (97%) create mode 100644 x-pack/legacy/plugins/infra/public/pages/metrics/components/page_body.tsx create mode 100644 x-pack/legacy/plugins/infra/public/pages/metrics/components/page_error.tsx create mode 100644 x-pack/legacy/plugins/infra/public/pages/metrics/components/section.tsx rename x-pack/legacy/plugins/infra/public/{components/metrics/sections => pages/metrics/components}/series_chart.tsx (100%) create mode 100644 x-pack/legacy/plugins/infra/public/pages/metrics/components/side_nav.tsx create mode 100644 x-pack/legacy/plugins/infra/public/pages/metrics/components/sub_section.tsx rename x-pack/legacy/plugins/infra/public/{components/metrics => pages/metrics/components}/time_controls.test.tsx (95%) rename x-pack/legacy/plugins/infra/public/{components/metrics => pages/metrics/components}/time_controls.tsx (92%) rename x-pack/legacy/plugins/infra/public/{containers/metrics => pages/metrics/containers}/metrics.gql_query.ts (100%) rename x-pack/legacy/plugins/infra/public/{containers/metrics => pages/metrics/containers}/metrics_time.test.tsx (100%) rename x-pack/legacy/plugins/infra/public/{containers/metrics => pages/metrics/containers}/with_metrics.tsx (82%) rename x-pack/legacy/plugins/infra/public/{containers/metrics => pages/metrics/containers}/with_metrics_time.tsx (98%) create mode 100644 x-pack/legacy/plugins/infra/public/pages/metrics/lib/side_nav_context.ts create mode 100644 x-pack/legacy/plugins/infra/public/pages/metrics/types.ts diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/container/index.ts b/x-pack/legacy/plugins/infra/common/inventory_models/container/index.ts index 254ed7b7593009..63d13696cf0417 100644 --- a/x-pack/legacy/plugins/infra/common/inventory_models/container/index.ts +++ b/x-pack/legacy/plugins/infra/common/inventory_models/container/index.ts @@ -13,4 +13,12 @@ export const container: InventoryModel = { requiredModules: ['docker'], layout, metrics, + requiredMetrics: [ + 'containerOverview', + 'containerCpuUsage', + 'containerMemory', + 'containerNetworkTraffic', + 'containerDiskIOBytes', + 'containerDiskIOOps', + ], }; diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/container/layout.v2.tsx b/x-pack/legacy/plugins/infra/common/inventory_models/container/layout.v2.tsx new file mode 100644 index 00000000000000..00da70f1d96a5b --- /dev/null +++ b/x-pack/legacy/plugins/infra/common/inventory_models/container/layout.v2.tsx @@ -0,0 +1,222 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import React from 'react'; +import { i18n } from '@kbn/i18n'; +import { LayoutPropsWithTheme } from '../../../public/pages/metrics/types'; +import { Section } from '../../../public/pages/metrics/components/section'; +import { SubSection } from '../../../public/pages/metrics/components/sub_section'; +import { GaugesSectionVis } from '../../../public/pages/metrics/components/gauges_section_vis'; +import { ChartSectionVis } from '../../../public/pages/metrics/components/chart_section_vis'; +import { withTheme } from '../../../../../common/eui_styled_components'; + +export const Layout = withTheme(({ metrics, theme }: LayoutPropsWithTheme) => ( + +
+ + + + + + + + + + + + + + + + + + +
+
+)); diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/host/detail_page.tsx b/x-pack/legacy/plugins/infra/common/inventory_models/host/detail_page.tsx new file mode 100644 index 00000000000000..7ff69af87aa3a5 --- /dev/null +++ b/x-pack/legacy/plugins/infra/common/inventory_models/host/detail_page.tsx @@ -0,0 +1,142 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { FunctionComponent, useEffect, useMemo } from 'react'; +import { EuiSideNav, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { InfraMetricData } from '../../../public/graphql/types'; +import { Section } from '../../../public/pages/metrics/components/section'; +import { SubSection } from '../../../public/pages/metrics/components/sub_section'; +import { NavItem, SideNavContext } from '../../../public/pages/metrics/lib/side_nav_context'; + +interface VisSectionProps { + metric?: InfraMetricData; +} + +const VisSection: FunctionComponent = ({ metric }) => { + if (metric) { + return ( +
+
VISUALIZATION GOES HERE
+
+ ); + } + return
Gauges without Metric (you should never see this)
; +}; + +interface WithMetricsDataProps { + source: string; + ids: string[]; + children: (props: LayoutProps) => React.ReactElement; +} + +const WithMetricsData = ({ ids, children }: WithMetricsDataProps) => { + if (!children) { + return null; + } + + // eslint-disable-next-line no-console + useEffect(() => console.log('Fetching', ids), [ids]); + + const metrics = useMemo(() => { + return ids.map(m => ({ id: m, series: [] })); + }, [ids]) as InfraMetricData[]; + + return children({ metrics }); +}; + +interface LayoutProps { + metrics: InfraMetricData[]; +} + +interface MetricsLayout { + requiredMetrics: string[]; + Layout: FunctionComponent; +} + +const Aws: MetricsLayout = { + requiredMetrics: ['awsOverview', 'awsCpuUsage'], + Layout: ({ metrics }) => ( +
+ + + + + + +
+ ), +}; +const Nginx: MetricsLayout = { + requiredMetrics: ['nginxOverview'], + Layout: ({ metrics }) => ( +
+ + + +
+ ), +}; + +export const Host: MetricsLayout = { + requiredMetrics: [ + 'hostSystemOverview', + 'hostSystemCPU', + ...Aws.requiredMetrics, + ...Nginx.requiredMetrics, + ], + Layout: ({ metrics }) => ( + +
+ + + + + + +
+ + +
+ ), +}; + +export const DetailPage: FunctionComponent = () => { + const [sideNav, setSideNav] = React.useState([]); + + const addNavItem = React.useCallback( + (item: NavItem) => { + if (!sideNav.some(n => n.id === item.id)) { + setSideNav([item, ...sideNav]); + } + }, + [sideNav] + ); + + return ( +
+ + + + + + + + {({ metrics }) => } + + + + +
+ ); +}; diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/host/index.ts b/x-pack/legacy/plugins/infra/common/inventory_models/host/index.ts index e29f5878bfcb3f..e69535e9d0f7da 100644 --- a/x-pack/legacy/plugins/infra/common/inventory_models/host/index.ts +++ b/x-pack/legacy/plugins/infra/common/inventory_models/host/index.ts @@ -7,10 +7,28 @@ import { layout } from './layout'; import { metrics } from './metrics'; import { InventoryModel } from '../types'; +import { + aws as awsRequiredMetrics, + nginx as nginxRequireMetrics, +} from '../shared/metrics/required_metrics'; export const host: InventoryModel = { id: 'host', requiredModules: ['system'], layout, metrics, + requiredMetrics: [ + 'hostSystemOverview', + 'hostCpuUsage', + 'hostLoad', + 'hostMemoryUsage', + 'hostNetworkTraffic', + 'hostK8sOverview', + 'hostK8sCpuCap', + 'hostK8sMemoryCap', + 'hostK8sDiskCap', + 'hostK8sPodCap', + ...awsRequiredMetrics, + ...nginxRequireMetrics, + ], }; diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/host/layout.v2.tsx b/x-pack/legacy/plugins/infra/common/inventory_models/host/layout.v2.tsx new file mode 100644 index 00000000000000..b7dcc29b19d3d0 --- /dev/null +++ b/x-pack/legacy/plugins/infra/common/inventory_models/host/layout.v2.tsx @@ -0,0 +1,352 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import React from 'react'; +import { i18n } from '@kbn/i18n'; +import { LayoutPropsWithTheme } from '../../../public/pages/metrics/types'; +import { Section } from '../../../public/pages/metrics/components/section'; +import { SubSection } from '../../../public/pages/metrics/components/sub_section'; +import { GaugesSectionVis } from '../../../public/pages/metrics/components/gauges_section_vis'; +import { ChartSectionVis } from '../../../public/pages/metrics/components/chart_section_vis'; +import { withTheme } from '../../../../../common/eui_styled_components'; +import * as AWS from '../shared/layouts/aws.v2'; +import * as Ngnix from '../shared/layouts/nginx.v2'; + +export const Layout = withTheme(({ metrics, theme }: LayoutPropsWithTheme) => ( + +
+ + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + +
+ + +
+)); diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/index.ts b/x-pack/legacy/plugins/infra/common/inventory_models/index.ts index ec518cb9cb9509..79aad7b2ccf6fe 100644 --- a/x-pack/legacy/plugins/infra/common/inventory_models/index.ts +++ b/x-pack/legacy/plugins/infra/common/inventory_models/index.ts @@ -11,7 +11,7 @@ import { container } from './container'; import { InventoryItemType } from './types'; export { metrics } from './metrics'; -export const inventoryModels = [host, pod, container]; +const inventoryModels = [host, pod, container]; export const findInventoryModel = (type: InventoryItemType) => { const model = inventoryModels.find(m => m.id === type); diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/layouts.ts b/x-pack/legacy/plugins/infra/common/inventory_models/layouts.ts new file mode 100644 index 00000000000000..f69a30e51c3d6d --- /dev/null +++ b/x-pack/legacy/plugins/infra/common/inventory_models/layouts.ts @@ -0,0 +1,44 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +/** + * WHY ARE THE LAYOUTS A SEPERATE FILE? + * + * Files with React can not be included on the server without + * crashing due to the requirement of the `window` object. + */ + +import { idx } from '@kbn/elastic-idx'; +import { i18n } from '@kbn/i18n'; + +import { ReactNode, FunctionComponent } from 'react'; +import { Layout as HostLayout } from './host/layout.v2'; +import { Layout as PodLayout } from './pod/layout.v2'; +import { Layout as ContainerLayout } from './container/layout.v2'; +import { InventoryItemType } from './types'; +import { LayoutProps } from '../../public/pages/metrics/types'; + +interface Layouts { + [type: string]: ReactNode; +} + +const Layouts: Layouts = { + host: HostLayout, + pod: PodLayout, + container: ContainerLayout, +}; + +export const findLayout = (type: InventoryItemType) => { + const Layout = idx(Layouts, _ => _[type]); + if (!Layout) { + throw new Error( + i18n.translate('xpack.infra.inventoryModels.findLayout.error', { + defaultMessage: "The layout you've attempted to find does not exist", + }) + ); + } + return Layout as FunctionComponent; +}; diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/pod/index.ts b/x-pack/legacy/plugins/infra/common/inventory_models/pod/index.ts index 1cf839b7876570..a3d619b08eb05c 100644 --- a/x-pack/legacy/plugins/infra/common/inventory_models/pod/index.ts +++ b/x-pack/legacy/plugins/infra/common/inventory_models/pod/index.ts @@ -7,9 +7,18 @@ import { layout } from './layout'; import { metrics } from './metrics'; import { InventoryModel } from '../types'; +import { nginx as nginxRequiredMetrics } from '../shared/metrics/required_metrics'; + export const pod: InventoryModel = { id: 'pod', requiredModules: ['kubernetes'], layout, metrics, + requiredMetrics: [ + 'podOverview', + 'podCpuUsage', + 'podMemoryUsage', + 'podNetworkTraffic', + ...nginxRequiredMetrics, + ], }; diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/pod/layout.v2.tsx b/x-pack/legacy/plugins/infra/common/inventory_models/pod/layout.v2.tsx new file mode 100644 index 00000000000000..3afcf9488f216c --- /dev/null +++ b/x-pack/legacy/plugins/infra/common/inventory_models/pod/layout.v2.tsx @@ -0,0 +1,154 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import React from 'react'; +import { i18n } from '@kbn/i18n'; +import { LayoutPropsWithTheme } from '../../../public/pages/metrics/types'; +import { Section } from '../../../public/pages/metrics/components/section'; +import { SubSection } from '../../../public/pages/metrics/components/sub_section'; +import { GaugesSectionVis } from '../../../public/pages/metrics/components/gauges_section_vis'; +import { ChartSectionVis } from '../../../public/pages/metrics/components/chart_section_vis'; +import { withTheme } from '../../../../../common/eui_styled_components'; +import * as Nginx from '../shared/layouts/nginx.v2'; + +export const Layout = withTheme(({ metrics, theme }: LayoutPropsWithTheme) => ( + +
+ + + + + + + + + + + + +
+ +
+)); diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/shared/layouts/aws.v2.tsx b/x-pack/legacy/plugins/infra/common/inventory_models/shared/layouts/aws.v2.tsx new file mode 100644 index 00000000000000..2cabbe4c33ff30 --- /dev/null +++ b/x-pack/legacy/plugins/infra/common/inventory_models/shared/layouts/aws.v2.tsx @@ -0,0 +1,237 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import React from 'react'; +import { i18n } from '@kbn/i18n'; +import { LayoutPropsWithTheme } from '../../../../public/pages/metrics/types'; +import { Section } from '../../../../public/pages/metrics/components/section'; +import { SubSection } from '../../../../public/pages/metrics/components/sub_section'; +import { GaugesSectionVis } from '../../../../public/pages/metrics/components/gauges_section_vis'; +import { ChartSectionVis } from '../../../../public/pages/metrics/components/chart_section_vis'; +import { withTheme } from '../../../../../../common/eui_styled_components'; + +export const Layout = withTheme(({ metrics, theme }: LayoutPropsWithTheme) => ( + +
+ + + + + + + + + + + + + + + + + + +
+
+)); diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/shared/layouts/nginx.v2.tsx b/x-pack/legacy/plugins/infra/common/inventory_models/shared/layouts/nginx.v2.tsx new file mode 100644 index 00000000000000..9d31ffa775d213 --- /dev/null +++ b/x-pack/legacy/plugins/infra/common/inventory_models/shared/layouts/nginx.v2.tsx @@ -0,0 +1,103 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import React from 'react'; +import { i18n } from '@kbn/i18n'; +import { LayoutPropsWithTheme } from '../../../../public/pages/metrics/types'; +import { Section } from '../../../../public/pages/metrics/components/section'; +import { SubSection } from '../../../../public/pages/metrics/components/sub_section'; +import { ChartSectionVis } from '../../../../public/pages/metrics/components/chart_section_vis'; +import { withTheme } from '../../../../../../common/eui_styled_components'; + +export const Layout = withTheme(({ metrics, theme }: LayoutPropsWithTheme) => ( + +
+ + + + + + + + + + + + +
+
+)); diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/shared/metrics/required_metrics.ts b/x-pack/legacy/plugins/infra/common/inventory_models/shared/metrics/required_metrics.ts new file mode 100644 index 00000000000000..7bd888ab7c5896 --- /dev/null +++ b/x-pack/legacy/plugins/infra/common/inventory_models/shared/metrics/required_metrics.ts @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export const nginx = [ + 'nginxHits', + 'nginxRequestRate', + 'nginxActiveConnections', + 'nginxRequestsPerConnection', +]; + +export const aws = [ + 'awsOverview', + 'awsCpuUtilization', + 'awsNetworkBytes', + 'awsNetworkPackets', + 'awsDiskioOps', + 'awsDiskioBytes', +]; diff --git a/x-pack/legacy/plugins/infra/common/inventory_models/types.ts b/x-pack/legacy/plugins/infra/common/inventory_models/types.ts index 9c9f11048ee63c..b1d0e1dcad5cc0 100644 --- a/x-pack/legacy/plugins/infra/common/inventory_models/types.ts +++ b/x-pack/legacy/plugins/infra/common/inventory_models/types.ts @@ -32,6 +32,7 @@ export const InventoryFormatterTypeRT = rt.keyof({ number: null, percent: null, }); +export type InventoryFormatterType = rt.TypeOf; export type InventoryItemType = rt.TypeOf; export const InventoryMetricRT = rt.string; @@ -282,4 +283,5 @@ export interface InventoryModel { requiredModules: string[]; layout: InventoryDetailLayoutCreator; metrics: InventoryMetrics; + requiredMetrics: InventoryMetric[]; } diff --git a/x-pack/legacy/plugins/infra/public/components/metrics/index.tsx b/x-pack/legacy/plugins/infra/public/components/metrics/index.tsx deleted file mode 100644 index a80525e3a9cacb..00000000000000 --- a/x-pack/legacy/plugins/infra/public/components/metrics/index.tsx +++ /dev/null @@ -1,128 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import { EuiPageContentBody, EuiTitle } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n/react'; -import React from 'react'; - -import { InfraMetricData } from '../../graphql/types'; -import { NoData } from '../empty_states'; -import { InfraLoadingPanel } from '../loading'; -import { Section } from './section'; -import { MetricsTimeInput } from '../../containers/metrics/with_metrics_time'; -import { - InventoryDetailLayout, - InventoryDetailSection, -} from '../../../common/inventory_models/types'; - -interface Props { - metrics: InfraMetricData[]; - layouts: InventoryDetailLayout[]; - loading: boolean; - refetch: () => void; - nodeId: string; - label: string; - onChangeRangeTime?: (time: MetricsTimeInput) => void; - isLiveStreaming?: boolean; - stopLiveStreaming?: () => void; -} - -interface State { - crosshairValue: number | null; -} - -export const Metrics = class extends React.PureComponent { - public static displayName = 'Metrics'; - public readonly state = { - crosshairValue: null, - }; - - public render() { - if (this.props.loading) { - return ( - - ); - } else if (!this.props.loading && this.props.metrics && this.props.metrics.length === 0) { - return ( - - ); - } - - return {this.props.layouts.map(this.renderLayout)}; - } - - private handleRefetch = () => { - this.props.refetch(); - }; - - private renderLayout = (layout: InventoryDetailLayout) => { - return ( - - - -

- -

-
-
- {layout.sections.map(this.renderSection(layout))} -
- ); - }; - - private renderSection = (layout: InventoryDetailLayout) => (section: InventoryDetailSection) => { - let sectionProps = {}; - if (section.type === 'chart') { - const { onChangeRangeTime, isLiveStreaming, stopLiveStreaming } = this.props; - sectionProps = { - onChangeRangeTime, - isLiveStreaming, - stopLiveStreaming, - crosshairValue: this.state.crosshairValue, - onCrosshairUpdate: this.onCrosshairUpdate, - }; - } - return ( -
- ); - }; - - private onCrosshairUpdate = (crosshairValue: number) => { - this.setState({ - crosshairValue, - }); - }; -}; diff --git a/x-pack/legacy/plugins/infra/public/components/metrics/section.tsx b/x-pack/legacy/plugins/infra/public/components/metrics/section.tsx deleted file mode 100644 index ff416bf4541b21..00000000000000 --- a/x-pack/legacy/plugins/infra/public/components/metrics/section.tsx +++ /dev/null @@ -1,42 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import React from 'react'; -import { InfraMetricData } from '../../graphql/types'; -import { sections } from './sections'; -import { MetricsTimeInput } from '../../containers/metrics/with_metrics_time'; -import { InventoryDetailSection } from '../../../common/inventory_models/types'; - -interface Props { - section: InventoryDetailSection; - metrics: InfraMetricData[]; - onChangeRangeTime?: (time: MetricsTimeInput) => void; - crosshairValue?: number; - onCrosshairUpdate?: (crosshairValue: number) => void; - isLiveStreaming?: boolean; - stopLiveStreaming?: () => void; -} - -export class Section extends React.PureComponent { - public render() { - const metric = this.props.metrics.find(m => m.id === this.props.section.id); - if (!metric) { - return null; - } - let sectionProps = {}; - if (this.props.section.type === 'chart') { - sectionProps = { - onChangeRangeTime: this.props.onChangeRangeTime, - crosshairValue: this.props.crosshairValue, - onCrosshairUpdate: this.props.onCrosshairUpdate, - isLiveStreaming: this.props.isLiveStreaming, - stopLiveStreaming: this.props.stopLiveStreaming, - }; - } - const Component = sections[this.props.section.type]; - return ; - } -} diff --git a/x-pack/legacy/plugins/infra/public/components/metrics/sections/gauges_section.tsx b/x-pack/legacy/plugins/infra/public/components/metrics/sections/gauges_section.tsx deleted file mode 100644 index 6b73ac62872c9b..00000000000000 --- a/x-pack/legacy/plugins/infra/public/components/metrics/sections/gauges_section.tsx +++ /dev/null @@ -1,104 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import { - EuiFlexItem, - EuiPageContentBody, - EuiPanel, - EuiProgress, - EuiSpacer, - EuiText, - EuiTitle, -} from '@elastic/eui'; -import { get, last, max } from 'lodash'; -import React, { ReactText } from 'react'; - -import euiStyled from '../../../../../../common/eui_styled_components'; -import { InfraMetricData } from '../../../graphql/types'; -import { InfraFormatterType } from '../../../lib/lib'; -import { createFormatter } from '../../../utils/formatters'; -import { InventoryDetailSection } from '../../../../common/inventory_models/types'; - -interface Props { - section: InventoryDetailSection; - metric: InfraMetricData; -} - -const getFormatter = (section: InventoryDetailSection, seriesId: string) => (val: ReactText) => { - if (val == null) { - return ''; - } - const defaultFormatter = get(section, ['visConfig', 'formatter'], InfraFormatterType.number); - const defaultFormatterTemplate = get(section, ['visConfig', 'formatterTemplate'], '{{value}}'); - const formatter = get( - section, - ['visConfig', 'seriesOverrides', seriesId, 'formatter'], - defaultFormatter - ); - const formatterTemplate = get( - section, - ['visConfig', 'seriesOverrides', seriesId, 'formatterTemplate'], - defaultFormatterTemplate - ); - return createFormatter(formatter, formatterTemplate)(val); -}; - -export class GaugesSection extends React.PureComponent { - public render() { - const { metric, section } = this.props; - return ( - - - - {metric.series.map(series => { - const lastDataPoint = last(series.data); - if (!lastDataPoint) { - return null; - } - const formatter = getFormatter(section, series.id); - const value = formatter(lastDataPoint.value || 0); - const name = get( - section, - ['visConfig', 'seriesOverrides', series.id, 'name'], - series.id - ); - const dataMax = max(series.data.map(d => d.value || 0)); - const gaugeMax = get( - section, - ['visConfig', 'seriesOverrides', series.id, 'gaugeMax'], - dataMax - ); - return ( - - - - {name} - - -

{value}

-
- -
-
- ); - })} -
- -
- ); - } -} - -const GroupBox = euiStyled.div` - display: flex; - flex-flow: row wrap; - justify-content: space-evenly; -`; diff --git a/x-pack/legacy/plugins/infra/public/components/metrics/sections/index.ts b/x-pack/legacy/plugins/infra/public/components/metrics/sections/index.ts deleted file mode 100644 index 39844868ecbea9..00000000000000 --- a/x-pack/legacy/plugins/infra/public/components/metrics/sections/index.ts +++ /dev/null @@ -1,13 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import { GaugesSection } from './gauges_section'; -import { ChartSection } from './chart_section'; - -export const sections = { - chart: ChartSection, - gauges: GaugesSection, -}; diff --git a/x-pack/legacy/plugins/infra/public/components/metrics/side_nav.tsx b/x-pack/legacy/plugins/infra/public/components/metrics/side_nav.tsx deleted file mode 100644 index f20adadce6042d..00000000000000 --- a/x-pack/legacy/plugins/infra/public/components/metrics/side_nav.tsx +++ /dev/null @@ -1,81 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import { EuiHideFor, EuiPageSideBar, EuiShowFor, EuiSideNav } from '@elastic/eui'; - -import React from 'react'; - -import euiStyled from '../../../../../common/eui_styled_components'; -import { - InventoryDetailLayout, - InventoryDetailSection, -} from '../../../common/inventory_models/types'; - -interface Props { - layouts: InventoryDetailLayout[]; - loading: boolean; - nodeName: string; - handleClick: (section: InventoryDetailSection) => () => void; -} - -export const MetricsSideNav = class extends React.PureComponent { - public static displayName = 'MetricsSideNav'; - - public readonly state = { - isOpenOnMobile: false, - }; - - public render() { - let content; - let mobileContent; - if (!this.props.loading) { - const entries = this.props.layouts.map(item => { - return { - name: item.label, - id: item.id, - items: item.sections.map(section => ({ - id: section.id, - name: section.label, - onClick: this.props.handleClick(section), - })), - }; - }); - content = ; - mobileContent = ( - - ); - } - return ( - - - {content} - - {mobileContent} - - ); - } - - private toggleOpenOnMobile = () => { - this.setState({ - isOpenOnMobile: !this.state.isOpenOnMobile, - }); - }; -}; - -const SideNavContainer = euiStyled.div` - position: fixed; - z-index: 1; - height: 88vh; - padding-left: 16px; - margin-left: -16px; - overflow-y: auto; - overflow-x: hidden; -`; diff --git a/x-pack/legacy/plugins/infra/public/containers/metadata/lib/get_filtered_metrics.ts b/x-pack/legacy/plugins/infra/public/containers/metadata/lib/get_filtered_metrics.ts new file mode 100644 index 00000000000000..b485c90700145a --- /dev/null +++ b/x-pack/legacy/plugins/infra/public/containers/metadata/lib/get_filtered_metrics.ts @@ -0,0 +1,25 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { InfraMetadataFeature } from '../../../../common/http_api/metadata_api'; +import { InventoryMetric } from '../../../../common/inventory_models/types'; +import { metrics } from '../../../../common/inventory_models/metrics'; + +export const getFilteredMetrics = ( + requiredMetrics: InventoryMetric[], + metadata: Array +) => { + const metricMetadata = metadata + .filter(data => data && data.source === 'metrics') + .map(data => data && data.name); + return requiredMetrics.filter(metric => { + const metricModelCreator = metrics.tsvb[metric]; + // We just need to get a dummy version of the model so we can filter + // using the `requires` attribute. + const metricModel = metricModelCreator('@timestamp', 'test', '>=1m'); + return metricMetadata.some(m => m && metricModel.requires.includes(m)); + }); +}; diff --git a/x-pack/legacy/plugins/infra/public/containers/metadata/use_metadata.ts b/x-pack/legacy/plugins/infra/public/containers/metadata/use_metadata.ts index 540e32a27b319a..9db0ef5089a4a3 100644 --- a/x-pack/legacy/plugins/infra/public/containers/metadata/use_metadata.ts +++ b/x-pack/legacy/plugins/infra/public/containers/metadata/use_metadata.ts @@ -13,12 +13,14 @@ import { InfraMetadata, InfraMetadataRT } from '../../../common/http_api/metadat import { getFilteredLayouts } from './lib/get_filtered_layouts'; import { useHTTPRequest } from '../../hooks/use_http_request'; import { throwErrors, createPlainError } from '../../../common/runtime_types'; -import { InventoryDetailLayout } from '../../../common/inventory_models/types'; +import { InventoryDetailLayout, InventoryMetric } from '../../../common/inventory_models/types'; +import { getFilteredMetrics } from './lib/get_filtered_metrics'; export function useMetadata( nodeId: string, nodeType: InfraNodeType, layouts: InventoryDetailLayout[], + requiredMetrics: InventoryMetric[], sourceId: string ) { const decodeResponse = (response: any) => { @@ -48,6 +50,8 @@ export function useMetadata( return { name: (response && response.name) || '', filteredLayouts: (response && getFilteredLayouts(layouts, response.features)) || [], + filteredRequiredMetrics: + (response && getFilteredMetrics(requiredMetrics, response.features)) || [], error: (error && error.message) || null, loading, metadata: response, diff --git a/x-pack/legacy/plugins/infra/public/pages/link_to/redirect_to_host_detail_via_ip.tsx b/x-pack/legacy/plugins/infra/public/pages/link_to/redirect_to_host_detail_via_ip.tsx index d06abb2aa1060f..c1b0814f550a36 100644 --- a/x-pack/legacy/plugins/infra/public/pages/link_to/redirect_to_host_detail_via_ip.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/link_to/redirect_to_host_detail_via_ip.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { Redirect, RouteComponentProps } from 'react-router-dom'; import { i18n } from '@kbn/i18n'; -import { replaceMetricTimeInQueryString } from '../../containers/metrics/with_metrics_time'; +import { replaceMetricTimeInQueryString } from '../metrics/containers/with_metrics_time'; import { useHostIpToName } from './use_host_ip_to_name'; import { getFromFromLocation, getToFromLocation } from './query_params'; import { LoadingPage } from '../../components/loading_page'; diff --git a/x-pack/legacy/plugins/infra/public/pages/link_to/redirect_to_node_detail.tsx b/x-pack/legacy/plugins/infra/public/pages/link_to/redirect_to_node_detail.tsx index ad513076417807..a2cebbb96a4f0d 100644 --- a/x-pack/legacy/plugins/infra/public/pages/link_to/redirect_to_node_detail.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/link_to/redirect_to_node_detail.tsx @@ -7,7 +7,7 @@ import React from 'react'; import { Redirect, RouteComponentProps } from 'react-router-dom'; -import { replaceMetricTimeInQueryString } from '../../containers/metrics/with_metrics_time'; +import { replaceMetricTimeInQueryString } from '../metrics/containers/with_metrics_time'; import { InfraNodeType } from '../../graphql/types'; import { getFromFromLocation, getToFromLocation } from './query_params'; diff --git a/x-pack/legacy/plugins/infra/public/components/metrics/sections/chart_section.tsx b/x-pack/legacy/plugins/infra/public/pages/metrics/components/chart_section_vis.tsx similarity index 72% rename from x-pack/legacy/plugins/infra/public/components/metrics/sections/chart_section.tsx rename to x-pack/legacy/plugins/infra/public/pages/metrics/components/chart_section_vis.tsx index cf148eefe8e8fe..425b5a43f793f7 100644 --- a/x-pack/legacy/plugins/infra/public/components/metrics/sections/chart_section.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/metrics/components/chart_section_vis.tsx @@ -6,8 +6,6 @@ import React, { useCallback } from 'react'; import moment from 'moment'; import { i18n } from '@kbn/i18n'; - -import { get } from 'lodash'; import { Axis, Chart, @@ -17,10 +15,8 @@ import { Settings, TooltipValue, } from '@elastic/charts'; -import { EuiPageContentBody, EuiTitle } from '@elastic/eui'; -import { InfraMetricData } from '../../../graphql/types'; -import { getChartTheme } from '../../metrics_explorer/helpers/get_chart_theme'; -import { InfraFormatterType } from '../../../lib/lib'; +import { EuiPageContentBody } from '@elastic/eui'; +import { getChartTheme } from '../../../components/metrics_explorer/helpers/get_chart_theme'; import { SeriesChart } from './series_chart'; import { getFormatter, @@ -32,28 +28,24 @@ import { } from './helpers'; import { ErrorMessage } from './error_message'; import { useKibanaUiSetting } from '../../../utils/use_kibana_ui_setting'; -import { MetricsTimeInput } from '../../../containers/metrics/with_metrics_time'; -import { InventoryDetailSection } from '../../../../common/inventory_models/types'; - -interface Props { - section: InventoryDetailSection; - metric: InfraMetricData; - onChangeRangeTime?: (time: MetricsTimeInput) => void; - isLiveStreaming?: boolean; - stopLiveStreaming?: () => void; -} +import { VisSectionProps } from '../types'; -export const ChartSection = ({ +export const ChartSectionVis = ({ + id, onChangeRangeTime, - section, metric, stopLiveStreaming, isLiveStreaming, -}: Props) => { - const { visConfig } = section; + formatter, + formatterTemplate, + stacked, + seriesOverrides, + type, +}: VisSectionProps) => { + if (!metric || !id) { + return null; + } const [dateFormat] = useKibanaUiSetting('dateFormat'); - const formatter = get(visConfig, 'formatter', InfraFormatterType.number); - const formatterTemplate = get(visConfig, 'formatterTemplate', '{{value}}'); const valueFormatter = useCallback(getFormatter(formatter, formatterTemplate), [ formatter, formatterTemplate, @@ -109,9 +101,6 @@ export const ChartSection = ({ return ( - -

{section.label}

-
( ))} (val: ReactText) => { + if (val == null) { + return ''; + } + const formatter = get(seriesOverrides, [seriesId, 'formatter'], defaultFormatter); + const formatterTemplate = get( + seriesOverrides, + [seriesId, 'formatterTemplate'], + defaultFormatterTemplate + ); + return createFormatter(formatter, formatterTemplate)(val); +}; + +export const GaugesSectionVis = ({ + id, + metric, + seriesOverrides, + formatter, + formatterTemplate, +}: VisSectionProps) => { + if (!metric || !id) { + return null; + } + return ( + + + + {metric.series.map(series => { + const lastDataPoint = last(series.data); + if (!lastDataPoint) { + return null; + } + const formatterFn = getFormatter( + formatter, + formatterTemplate, + seriesOverrides, + series.id + ); + const value = formatterFn(lastDataPoint.value || 0); + const name = getChartName(seriesOverrides, series.id, series.id); + const dataMax = max(series.data.map(d => d.value || 0)); + const gaugeMax = get(seriesOverrides, [series.id, 'gaugeMax'], dataMax); + return ( + + + + {name} + + +

{value}

+
+ +
+
+ ); + })} +
+ +
+ ); +}; + +const GroupBox = euiStyled.div` + display: flex; + flex-flow: row wrap; + justify-content: space-evenly; +`; diff --git a/x-pack/legacy/plugins/infra/public/components/metrics/sections/helpers/index.ts b/x-pack/legacy/plugins/infra/public/pages/metrics/components/helpers.ts similarity index 58% rename from x-pack/legacy/plugins/infra/public/components/metrics/sections/helpers/index.ts rename to x-pack/legacy/plugins/infra/public/pages/metrics/components/helpers.ts index bfc1385c0431a2..0da73bcd7a531b 100644 --- a/x-pack/legacy/plugins/infra/public/components/metrics/sections/helpers/index.ts +++ b/x-pack/legacy/plugins/infra/public/pages/metrics/components/helpers.ts @@ -7,19 +7,22 @@ import { ReactText } from 'react'; import Color from 'color'; import { get, first, last, min, max } from 'lodash'; -import { InfraFormatterType } from '../../../../lib/lib'; -import { createFormatter } from '../../../../utils/formatters'; -import { InfraDataSeries, InfraMetricData } from '../../../../graphql/types'; +import { createFormatter } from '../../../utils/formatters'; +import { InfraDataSeries, InfraMetricData } from '../../../graphql/types'; import { - InventoryDetailSection, InventoryVisTypeRT, -} from '../../../../../common/inventory_models/types'; + InventoryFormatterType, + InventoryVisType, +} from '../../../../common/inventory_models/types'; +import { SeriesOverrides } from '../types'; /** * Returns a formatter */ -export const getFormatter = (formatter: InfraFormatterType, template: string) => (val: ReactText) => - val != null ? createFormatter(formatter, template)(val) : ''; +export const getFormatter = ( + formatter: InventoryFormatterType = 'number', + template: string = '{{value}}' +) => (val: ReactText) => (val != null ? createFormatter(formatter, template)(val) : ''); /** * Does a series have more then two points? @@ -53,16 +56,25 @@ export const getMaxMinTimestamp = (metric: InfraMetricData): [number, number] => * Returns the chart name from the visConfig based on the series id, otherwise it * just returns the seriesId */ -export const getChartName = (section: InventoryDetailSection, seriesId: string, label: string) => { - return get(section, ['visConfig', 'seriesOverrides', seriesId, 'name'], label); +export const getChartName = ( + seriesOverrides: SeriesOverrides | undefined, + seriesId: string, + label: string +) => { + if (!seriesOverrides) { + return label; + } + return get(seriesOverrides, [seriesId, 'name'], label); }; /** * Returns the chart color from the visConfig based on the series id, otherwise it * just returns null if the color doesn't exists in the overrides. */ -export const getChartColor = (section: InventoryDetailSection, seriesId: string) => { - const rawColor: string | null = get(section, ['visConfig', 'seriesOverrides', seriesId, 'color']); +export const getChartColor = (seriesOverrides: SeriesOverrides | undefined, seriesId: string) => { + const rawColor: string | null = seriesOverrides + ? get(seriesOverrides, [seriesId, 'color']) + : null; if (!rawColor) { return null; } @@ -73,14 +85,20 @@ export const getChartColor = (section: InventoryDetailSection, seriesId: string) /** * Gets the chart type based on the section and seriesId */ -export const getChartType = (section: InventoryDetailSection, seriesId: string) => { - const value = get(section, ['visConfig', 'type']); - const overrideValue = get(section, ['visConfig', 'seriesOverrides', seriesId, 'type']); +export const getChartType = ( + seriesOverrides: SeriesOverrides | undefined, + type: InventoryVisType | undefined, + seriesId: string +) => { + if (!seriesOverrides || !type) { + return 'line'; + } + const overrideValue = get(seriesOverrides, [seriesId, 'type']); if (InventoryVisTypeRT.is(overrideValue)) { return overrideValue; } - if (InventoryVisTypeRT.is(value)) { - return value; + if (InventoryVisTypeRT.is(type)) { + return type; } return 'line'; }; diff --git a/x-pack/legacy/plugins/infra/public/components/metrics/invalid_node.tsx b/x-pack/legacy/plugins/infra/public/pages/metrics/components/invalid_node.tsx similarity index 92% rename from x-pack/legacy/plugins/infra/public/components/metrics/invalid_node.tsx rename to x-pack/legacy/plugins/infra/public/pages/metrics/components/invalid_node.tsx index 673bca91904c7e..f9e56791746f5c 100644 --- a/x-pack/legacy/plugins/infra/public/components/metrics/invalid_node.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/metrics/components/invalid_node.tsx @@ -8,12 +8,12 @@ import { EuiButton, EuiEmptyPrompt, EuiFlexGroup, EuiFlexItem } from '@elastic/e import { FormattedMessage } from '@kbn/i18n/react'; import React from 'react'; -import euiStyled from '../../../../../common/eui_styled_components'; -import { WithKibanaChrome } from '../../containers/with_kibana_chrome'; +import euiStyled from '../../../../../../common/eui_styled_components'; +import { WithKibanaChrome } from '../../../containers/with_kibana_chrome'; import { ViewSourceConfigurationButton, ViewSourceConfigurationButtonHrefBase, -} from '../../components/source_configuration'; +} from '../../../components/source_configuration'; interface InvalidNodeErrorProps { nodeName: string; diff --git a/x-pack/legacy/plugins/infra/public/components/metrics/node_details.tsx b/x-pack/legacy/plugins/infra/public/pages/metrics/components/node_details.tsx similarity index 97% rename from x-pack/legacy/plugins/infra/public/components/metrics/node_details.tsx rename to x-pack/legacy/plugins/infra/public/pages/metrics/components/node_details.tsx index 41331ed73afce1..5329ea992c493d 100644 --- a/x-pack/legacy/plugins/infra/public/components/metrics/node_details.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/metrics/components/node_details.tsx @@ -8,8 +8,8 @@ import React, { useState, useCallback, useMemo } from 'react'; import { EuiButtonIcon, EuiFlexGrid, EuiFlexItem, EuiTitle, EuiText } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { get } from 'lodash'; -import { InfraMetadata } from '../../../common/http_api'; -import euiStyled from '../../../../../common/eui_styled_components'; +import { InfraMetadata } from '../../../../common/http_api'; +import euiStyled from '../../../../../../common/eui_styled_components'; interface Props { metadata?: InfraMetadata | null; diff --git a/x-pack/legacy/plugins/infra/public/pages/metrics/components/page_body.tsx b/x-pack/legacy/plugins/infra/public/pages/metrics/components/page_body.tsx new file mode 100644 index 00000000000000..81c96c0ffe68d7 --- /dev/null +++ b/x-pack/legacy/plugins/infra/public/pages/metrics/components/page_body.tsx @@ -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; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { i18n } from '@kbn/i18n'; +import { findLayout } from '../../../../common/inventory_models/layouts'; +import { InventoryItemType } from '../../../../common/inventory_models/types'; +import { InfraMetricData } from '../../../graphql/types'; +import { MetricsTimeInput } from '../containers/with_metrics_time'; +import { InfraLoadingPanel } from '../../../components/loading'; +import { NoData } from '../../../components/empty_states'; + +interface Props { + loading: boolean; + refetch: () => void; + type: InventoryItemType; + metrics: InfraMetricData[]; + onChangeRangeTime?: (time: MetricsTimeInput) => void; + isLiveStreaming?: boolean; + stopLiveStreaming?: () => void; +} + +export const PageBody = ({ + loading, + refetch, + type, + metrics, + onChangeRangeTime, + isLiveStreaming, + stopLiveStreaming, +}: Props) => { + if (loading) { + return ( + + ); + } else if (!loading && metrics && metrics.length === 0) { + return ( + + ); + } + + const Layout = findLayout(type); + return ( + + ); +}; diff --git a/x-pack/legacy/plugins/infra/public/pages/metrics/components/page_error.tsx b/x-pack/legacy/plugins/infra/public/pages/metrics/components/page_error.tsx new file mode 100644 index 00000000000000..69ba80b85e3b2f --- /dev/null +++ b/x-pack/legacy/plugins/infra/public/pages/metrics/components/page_error.tsx @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { GraphQLFormattedError } from 'graphql'; +import React from 'react'; +import { i18n } from '@kbn/i18n'; +import { ApolloError } from 'apollo-client'; +import { InvalidNodeError } from './invalid_node'; +import { InfraMetricsErrorCodes } from '../../../../common/errors'; +import { DocumentTitle } from '../../../components/document_title'; +import { ErrorPageBody } from '../../error'; + +interface Props { + name: string; + error: ApolloError; +} + +export const PageError = ({ error, name }: Props) => { + const invalidNodeError = error.graphQLErrors.some( + (err: GraphQLFormattedError) => err.code === InfraMetricsErrorCodes.invalid_node + ); + + return ( + <> + + i18n.translate('xpack.infra.metricDetailPage.documentTitleError', { + defaultMessage: '{previousTitle} | Uh oh', + values: { + previousTitle, + }, + }) + } + /> + {invalidNodeError ? ( + + ) : ( + + )} + + ); +}; diff --git a/x-pack/legacy/plugins/infra/public/pages/metrics/components/section.tsx b/x-pack/legacy/plugins/infra/public/pages/metrics/components/section.tsx new file mode 100644 index 00000000000000..9a87557713616c --- /dev/null +++ b/x-pack/legacy/plugins/infra/public/pages/metrics/components/section.tsx @@ -0,0 +1,67 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { FunctionComponent, useCallback } from 'react'; +import { EuiTitle } from '@elastic/eui'; +import { SideNavContext, SubNavItem } from '../lib/side_nav_context'; +import { LayoutProps } from '../types'; + +type SectionProps = LayoutProps & { + navLabel: string; + sectionLabel: string; +}; + +export const Section: FunctionComponent = ({ + children, + metrics, + navLabel, + sectionLabel, + onChangeRangeTime, + isLiveStreaming, + stopLiveStreaming, +}) => { + const { addNavItem } = React.useContext(SideNavContext); + const subNavItems: SubNavItem[] = []; + + const childrenWithProps = React.Children.map(children, child => { + if (React.isValidElement(child)) { + const metric = (metrics && metrics.find(m => m.id === child.props.id)) || null; + if (metric) { + subNavItems.push({ + id: child.props.id, + name: child.props.label, + onClick: useCallback(() => { + const el = document.getElementById(child.props.id); + if (el) { + el.scrollIntoView(); + } + }, []), + }); + } + return React.cloneElement(child, { + metrics, + onChangeRangeTime, + isLiveStreaming, + stopLiveStreaming, + }); + } + return null; + }); + + if (metrics && subNavItems.length) { + addNavItem({ id: navLabel, name: navLabel, items: subNavItems }); + return ( +
+ +

{sectionLabel}

+
+ {childrenWithProps} +
+ ); + } + + return null; +}; diff --git a/x-pack/legacy/plugins/infra/public/components/metrics/sections/series_chart.tsx b/x-pack/legacy/plugins/infra/public/pages/metrics/components/series_chart.tsx similarity index 100% rename from x-pack/legacy/plugins/infra/public/components/metrics/sections/series_chart.tsx rename to x-pack/legacy/plugins/infra/public/pages/metrics/components/series_chart.tsx diff --git a/x-pack/legacy/plugins/infra/public/pages/metrics/components/side_nav.tsx b/x-pack/legacy/plugins/infra/public/pages/metrics/components/side_nav.tsx new file mode 100644 index 00000000000000..8e922818222b4f --- /dev/null +++ b/x-pack/legacy/plugins/infra/public/pages/metrics/components/side_nav.tsx @@ -0,0 +1,52 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { EuiHideFor, EuiPageSideBar, EuiShowFor, EuiSideNav } from '@elastic/eui'; +import React, { useState, useCallback } from 'react'; +import euiStyled from '../../../../../../common/eui_styled_components'; +import { NavItem } from '../lib/side_nav_context'; + +interface Props { + loading: boolean; + name: string; + items: NavItem[]; +} + +export const MetricsSideNav = ({ loading, name, items }: Props) => { + const [isOpenOnMobile, setMobileState] = useState(false); + + const toggle = useCallback(() => { + setMobileState(!isOpenOnMobile); + }, [isOpenOnMobile]); + + const content = loading ? null : ; + const mobileContent = loading ? null : ( + + ); + return ( + + + {content} + + {mobileContent} + + ); +}; + +const SideNavContainer = euiStyled.div` + position: fixed; + z-index: 1; + height: 88vh; + padding-left: 16px; + margin-left: -16px; + overflow-y: auto; + overflow-x: hidden; +`; diff --git a/x-pack/legacy/plugins/infra/public/pages/metrics/components/sub_section.tsx b/x-pack/legacy/plugins/infra/public/pages/metrics/components/sub_section.tsx new file mode 100644 index 00000000000000..80253db52d61c9 --- /dev/null +++ b/x-pack/legacy/plugins/infra/public/pages/metrics/components/sub_section.tsx @@ -0,0 +1,55 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { FunctionComponent } from 'react'; +import { EuiTitle } from '@elastic/eui'; +import { InventoryMetric } from '../../../../common/inventory_models/types'; +import { LayoutProps } from '../types'; + +type SubSectionProps = LayoutProps & { + id: InventoryMetric; + label?: string; +}; + +export const SubSection: FunctionComponent = ({ + id, + label, + children, + metrics, + onChangeRangeTime, + isLiveStreaming, + stopLiveStreaming, +}) => { + if (!children || !metrics) { + return null; + } + const metric = metrics.find(m => m.id === id); + if (!metric) { + return null; + } + const childrenWithProps = React.Children.map(children, child => { + if (React.isValidElement(child)) { + return React.cloneElement(child, { + metric, + id, + onChangeRangeTime, + isLiveStreaming, + stopLiveStreaming, + }); + } + return null; + }); + return ( +
+ {label ? ( + +

{label}

+
+ ) : null} + {childrenWithProps} +
+ ); +}; diff --git a/x-pack/legacy/plugins/infra/public/components/metrics/time_controls.test.tsx b/x-pack/legacy/plugins/infra/public/pages/metrics/components/time_controls.test.tsx similarity index 95% rename from x-pack/legacy/plugins/infra/public/components/metrics/time_controls.test.tsx rename to x-pack/legacy/plugins/infra/public/pages/metrics/components/time_controls.test.tsx index 61872f52615a02..624a2bb4a6f0f7 100644 --- a/x-pack/legacy/plugins/infra/public/components/metrics/time_controls.test.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/metrics/components/time_controls.test.tsx @@ -7,7 +7,7 @@ import React from 'react'; import { MetricsTimeControls } from './time_controls'; import { mount } from 'enzyme'; -import { MetricsTimeInput } from '../../containers/metrics/with_metrics_time'; +import { MetricsTimeInput } from '../containers/with_metrics_time'; describe('MetricsTimeControls', () => { it('should set a valid from and to value for Today', () => { diff --git a/x-pack/legacy/plugins/infra/public/components/metrics/time_controls.tsx b/x-pack/legacy/plugins/infra/public/pages/metrics/components/time_controls.tsx similarity index 92% rename from x-pack/legacy/plugins/infra/public/components/metrics/time_controls.tsx rename to x-pack/legacy/plugins/infra/public/pages/metrics/components/time_controls.tsx index 7d236cf0a3ea78..d181aa37f59aa6 100644 --- a/x-pack/legacy/plugins/infra/public/components/metrics/time_controls.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/metrics/components/time_controls.tsx @@ -6,8 +6,8 @@ import { EuiSuperDatePicker, OnRefreshChangeProps, OnTimeChangeProps } from '@elastic/eui'; import React from 'react'; -import euiStyled from '../../../../../common/eui_styled_components'; -import { MetricsTimeInput } from '../../containers/metrics/with_metrics_time'; +import euiStyled from '../../../../../../common/eui_styled_components'; +import { MetricsTimeInput } from '../containers/with_metrics_time'; interface MetricsTimeControlsProps { currentTimeRange: MetricsTimeInput; diff --git a/x-pack/legacy/plugins/infra/public/containers/metrics/metrics.gql_query.ts b/x-pack/legacy/plugins/infra/public/pages/metrics/containers/metrics.gql_query.ts similarity index 100% rename from x-pack/legacy/plugins/infra/public/containers/metrics/metrics.gql_query.ts rename to x-pack/legacy/plugins/infra/public/pages/metrics/containers/metrics.gql_query.ts diff --git a/x-pack/legacy/plugins/infra/public/containers/metrics/metrics_time.test.tsx b/x-pack/legacy/plugins/infra/public/pages/metrics/containers/metrics_time.test.tsx similarity index 100% rename from x-pack/legacy/plugins/infra/public/containers/metrics/metrics_time.test.tsx rename to x-pack/legacy/plugins/infra/public/pages/metrics/containers/metrics_time.test.tsx diff --git a/x-pack/legacy/plugins/infra/public/containers/metrics/with_metrics.tsx b/x-pack/legacy/plugins/infra/public/pages/metrics/containers/with_metrics.tsx similarity index 82% rename from x-pack/legacy/plugins/infra/public/containers/metrics/with_metrics.tsx rename to x-pack/legacy/plugins/infra/public/pages/metrics/containers/with_metrics.tsx index 95f130878fc52b..6f7e411628d27b 100644 --- a/x-pack/legacy/plugins/infra/public/containers/metrics/with_metrics.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/metrics/containers/with_metrics.tsx @@ -13,9 +13,9 @@ import { InfraNodeType, MetricsQuery, InfraTimerangeInput, -} from '../../graphql/types'; +} from '../../../graphql/types'; import { metricsQuery } from './metrics.gql_query'; -import { InventoryDetailLayout, InventoryMetric } from '../../../common/inventory_models/types'; +import { InventoryMetric, InventoryMetricRT } from '../../../../common/inventory_models/types'; interface WithMetricsArgs { metrics: InfraMetricData[]; @@ -26,7 +26,7 @@ interface WithMetricsArgs { interface WithMetricsProps { children: (args: WithMetricsArgs) => React.ReactNode; - layouts: InventoryDetailLayout[]; + requiredMetrics: InventoryMetric[]; nodeType: InfraNodeType; nodeId: string; cloudId: string; @@ -35,26 +35,19 @@ interface WithMetricsProps { } const isInfraMetrics = (subject: any[]): subject is InfraMetric[] => { - return subject.every(s => !!InfraMetric[s]); + return subject.every(s => InventoryMetricRT.is(s)); }; export const WithMetrics = ({ children, - layouts, + requiredMetrics, sourceId, timerange, nodeType, nodeId, cloudId, }: WithMetricsProps) => { - const metrics = layouts.reduce( - (acc, item) => { - return acc.concat(item.sections.map(s => s.id)); - }, - [] as InventoryMetric[] - ); - - if (!isInfraMetrics(metrics)) { + if (!isInfraMetrics(requiredMetrics)) { throw new Error( i18n.translate('xpack.infra.invalidInventoryMetricsError', { defaultMessage: 'One of the InfraMetric is invalid', @@ -69,7 +62,7 @@ export const WithMetrics = ({ notifyOnNetworkStatusChange variables={{ sourceId, - metrics, + metrics: requiredMetrics, nodeType, nodeId, cloudId, diff --git a/x-pack/legacy/plugins/infra/public/containers/metrics/with_metrics_time.tsx b/x-pack/legacy/plugins/infra/public/pages/metrics/containers/with_metrics_time.tsx similarity index 98% rename from x-pack/legacy/plugins/infra/public/containers/metrics/with_metrics_time.tsx rename to x-pack/legacy/plugins/infra/public/pages/metrics/containers/with_metrics_time.tsx index 1afc3a04acd654..6a89e756794680 100644 --- a/x-pack/legacy/plugins/infra/public/containers/metrics/with_metrics_time.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/metrics/containers/with_metrics_time.tsx @@ -11,8 +11,8 @@ import moment from 'moment'; import dateMath from '@elastic/datemath'; import * as rt from 'io-ts'; import { isRight } from 'fp-ts/lib/Either'; -import { replaceStateKeyInQueryString, UrlStateContainer } from '../../utils/url_state'; -import { InfraTimerangeInput } from '../../graphql/types'; +import { replaceStateKeyInQueryString, UrlStateContainer } from '../../../utils/url_state'; +import { InfraTimerangeInput } from '../../../graphql/types'; export interface MetricsTimeInput { from: string; diff --git a/x-pack/legacy/plugins/infra/public/pages/metrics/index.tsx b/x-pack/legacy/plugins/infra/public/pages/metrics/index.tsx index 1996d51b4f26b4..92865b954bcbbb 100644 --- a/x-pack/legacy/plugins/infra/public/pages/metrics/index.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/metrics/index.tsx @@ -14,34 +14,28 @@ import { EuiTitle, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { GraphQLFormattedError } from 'graphql'; -import React, { useCallback, useContext } from 'react'; +import React, { useContext, useState } from 'react'; import { UICapabilities } from 'ui/capabilities'; import { injectUICapabilities } from 'ui/capabilities/react'; import euiStyled, { EuiTheme, withTheme } from '../../../../../common/eui_styled_components'; -import { InfraMetricsErrorCodes } from '../../../common/errors'; import { AutoSizer } from '../../components/auto_sizer'; import { DocumentTitle } from '../../components/document_title'; import { Header } from '../../components/header'; -import { Metrics } from '../../components/metrics'; -import { InvalidNodeError } from '../../components/metrics/invalid_node'; -import { MetricsSideNav } from '../../components/metrics/side_nav'; -import { MetricsTimeControls } from '../../components/metrics/time_controls'; +import { MetricsSideNav } from './components/side_nav'; +import { MetricsTimeControls } from './components/time_controls'; import { ColumnarPage, PageContent } from '../../components/page'; -import { WithMetrics } from '../../containers/metrics/with_metrics'; -import { - WithMetricsTime, - WithMetricsTimeUrlState, -} from '../../containers/metrics/with_metrics_time'; +import { WithMetrics } from './containers/with_metrics'; +import { WithMetricsTime, WithMetricsTimeUrlState } from './containers/with_metrics_time'; import { InfraNodeType } from '../../graphql/types'; -import { ErrorPageBody } from '../error'; import { withMetricPageProviders } from './page_providers'; import { useMetadata } from '../../containers/metadata/use_metadata'; import { Source } from '../../containers/source'; import { InfraLoadingPanel } from '../../components/loading'; -import { NodeDetails } from '../../components/metrics/node_details'; +import { NodeDetails } from './components/node_details'; import { findInventoryModel } from '../../../common/inventory_models'; -import { InventoryDetailSection } from '../../../common/inventory_models/types'; +import { PageError } from './components/page_error'; +import { NavItem, SideNavContext } from './lib/side_nav_context'; +import { PageBody } from './components/page_body'; const DetailPageContent = euiStyled(PageContent)` overflow: auto; @@ -72,12 +66,25 @@ export const MetricDetail = withMetricPageProviders( const layoutCreator = inventoryModel.layout; const { sourceId } = useContext(Source.Context); const layouts = layoutCreator(theme); - const { name, filteredLayouts, loading: metadataLoading, cloudId, metadata } = useMetadata( - nodeId, - nodeType, - layouts, - sourceId + const { + name, + filteredRequiredMetrics, + loading: metadataLoading, + cloudId, + metadata, + } = useMetadata(nodeId, nodeType, layouts, inventoryModel.requiredMetrics, sourceId); + + const [sideNav, setSideNav] = useState([]); + + const addNavItem = React.useCallback( + (item: NavItem) => { + if (!sideNav.some(n => n.id === item.id)) { + setSideNav([item, ...sideNav]); + } + }, + [sideNav] ); + const breadcrumbs = [ { href: '#/', @@ -88,18 +95,7 @@ export const MetricDetail = withMetricPageProviders( { text: name }, ]; - const handleClick = useCallback( - (section: InventoryDetailSection) => () => { - const id = section.linkToId || section.id; - const el = document.getElementById(id); - if (el) { - el.scrollIntoView(); - } - }, - [] - ); - - if (metadataLoading && !filteredLayouts.length) { + if (metadataLoading && !filteredRequiredMetrics.length) { return ( {({ metrics, error, loading, refetch }) => { if (error) { - const invalidNodeError = error.graphQLErrors.some( - (err: GraphQLFormattedError) => - err.code === InfraMetricsErrorCodes.invalid_node - ); - - return ( - <> - - i18n.translate('xpack.infra.metricDetailPage.documentTitleError', { - defaultMessage: '{previousTitle} | Uh oh', - values: { - previousTitle, - }, - }) - } - /> - {invalidNodeError ? ( - - ) : ( - - )} - - ); + return ; } return ( - + {({ measureRef, bounds: { width = 0 } }) => { const w = width ? `${width}px` : `100%`; @@ -209,19 +177,19 @@ export const MetricDetail = withMetricPageProviders( - 0 && isAutoReloading ? false : loading - } - refetch={refetch} - onChangeRangeTime={setTimeRange} - isLiveStreaming={isAutoReloading} - stopLiveStreaming={() => setAutoReload(false)} - /> + + 0 && isAutoReloading ? false : loading + } + refetch={refetch} + type={nodeType} + metrics={metrics} + onChangeRangeTime={setTimeRange} + isLiveStreaming={isAutoReloading} + stopLiveStreaming={() => setAutoReload(false)} + /> + diff --git a/x-pack/legacy/plugins/infra/public/pages/metrics/lib/side_nav_context.ts b/x-pack/legacy/plugins/infra/public/pages/metrics/lib/side_nav_context.ts new file mode 100644 index 00000000000000..3afd91ef59e934 --- /dev/null +++ b/x-pack/legacy/plugins/infra/public/pages/metrics/lib/side_nav_context.ts @@ -0,0 +1,24 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; + +export interface SubNavItem { + id: string; + name: string; + onClick: () => void; +} + +export interface NavItem { + id: string | number; + name: string; + items: SubNavItem[]; +} + +export const SideNavContext = React.createContext({ + items: [] as NavItem[], + addNavItem: (item: NavItem) => {}, +}); diff --git a/x-pack/legacy/plugins/infra/public/pages/metrics/page_providers.tsx b/x-pack/legacy/plugins/infra/public/pages/metrics/page_providers.tsx index 5e43e79ab7c891..0abbd597dd65cd 100644 --- a/x-pack/legacy/plugins/infra/public/pages/metrics/page_providers.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/metrics/page_providers.tsx @@ -6,7 +6,7 @@ import React from 'react'; -import { MetricsTimeContainer } from '../../containers/metrics/with_metrics_time'; +import { MetricsTimeContainer } from './containers/with_metrics_time'; import { Source } from '../../containers/source'; export const withMetricPageProviders = (Component: React.ComponentType) => ( diff --git a/x-pack/legacy/plugins/infra/public/pages/metrics/types.ts b/x-pack/legacy/plugins/infra/public/pages/metrics/types.ts new file mode 100644 index 00000000000000..e752164796150f --- /dev/null +++ b/x-pack/legacy/plugins/infra/public/pages/metrics/types.ts @@ -0,0 +1,62 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import rt from 'io-ts'; +import { EuiTheme } from '../../../../../common/eui_styled_components'; +import { InfraMetricData } from '../../graphql/types'; +import { InventoryFormatterTypeRT } from '../../../common/inventory_models/types'; +import { MetricsTimeInput } from './containers/with_metrics_time'; + +export interface LayoutProps { + metrics?: InfraMetricData[]; + onChangeRangeTime?: (time: MetricsTimeInput) => void; + isLiveStreaming?: boolean; + stopLiveStreaming?: () => void; +} + +export type LayoutPropsWithTheme = LayoutProps & { theme: EuiTheme }; + +const ChartTypesRT = rt.keyof({ + area: null, + bar: null, + line: null, +}); + +export const SeriesOverridesObjectRT = rt.intersection([ + rt.type({ + color: rt.string, + }), + rt.partial({ + name: rt.string, + formatter: InventoryFormatterTypeRT, + formatterTemplate: rt.string, + gaugeMax: rt.number, + type: ChartTypesRT, + }), +]); + +export const SeriesOverridesRT = rt.record( + rt.string, + rt.union([rt.undefined, SeriesOverridesObjectRT]) +); + +export type SeriesOverrides = rt.TypeOf; + +export const VisSectionPropsRT = rt.partial({ + type: ChartTypesRT, + stacked: rt.boolean, + formatter: InventoryFormatterTypeRT, + formatterTemplate: rt.string, + seriesOverrides: SeriesOverridesRT, +}); + +export type VisSectionProps = rt.TypeOf & { + id?: string; + metric?: InfraMetricData; + onChangeRangeTime?: (time: MetricsTimeInput) => void; + isLiveStreaming?: boolean; + stopLiveStreaming?: () => void; +}; diff --git a/x-pack/legacy/plugins/infra/public/utils/formatters/index.ts b/x-pack/legacy/plugins/infra/public/utils/formatters/index.ts index 4006e672d8b742..efb20e71a9ce45 100644 --- a/x-pack/legacy/plugins/infra/public/utils/formatters/index.ts +++ b/x-pack/legacy/plugins/infra/public/utils/formatters/index.ts @@ -5,26 +5,25 @@ */ import Mustache from 'mustache'; -import { InfraFormatterType, InfraWaffleMapDataFormat } from '../../lib/lib'; +import { InfraWaffleMapDataFormat } from '../../lib/lib'; import { createBytesFormatter } from './bytes'; import { formatNumber } from './number'; import { formatPercent } from './percent'; +import { InventoryFormatterType } from '../../../common/inventory_models/types'; export const FORMATTERS = { - [InfraFormatterType.number]: formatNumber, + number: formatNumber, // Because the implimentation for formatting large numbers is the same as formatting // bytes we are re-using the same code, we just format the number using the abbreviated number format. - [InfraFormatterType.abbreviatedNumber]: createBytesFormatter( - InfraWaffleMapDataFormat.abbreviatedNumber - ), + abbreviatedNumber: createBytesFormatter(InfraWaffleMapDataFormat.abbreviatedNumber), // bytes in bytes formatted string out - [InfraFormatterType.bytes]: createBytesFormatter(InfraWaffleMapDataFormat.bytesDecimal), + bytes: createBytesFormatter(InfraWaffleMapDataFormat.bytesDecimal), // bytes in bits formatted string out - [InfraFormatterType.bits]: createBytesFormatter(InfraWaffleMapDataFormat.bitsDecimal), - [InfraFormatterType.percent]: formatPercent, + bits: createBytesFormatter(InfraWaffleMapDataFormat.bitsDecimal), + percent: formatPercent, }; -export const createFormatter = (format: InfraFormatterType, template: string = '{{value}}') => ( +export const createFormatter = (format: InventoryFormatterType, template: string = '{{value}}') => ( val: string | number ) => { if (val == null) {