diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d3b2c66e..4e8b2ecf6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ NOTE: As semantic versioning states all 0.y.z releases can contain breaking chan ### Changed - [#46](https://github.com/kobsio/kobs/pull/46): Support multiple types for the legend in a Prometheus chart and use a custom component to render the legend. +- [#47](https://github.com/kobsio/kobs/pull/47): Display the legend at the Prometheus page as table and use color of selected metric in chart. ## [v0.2.0](https://github.com/kobsio/kobs/releases/tag/v0.2.0) (2021-04-23) diff --git a/app/src/plugins/prometheus/PrometheusChartDefault.tsx b/app/src/plugins/prometheus/PrometheusChartDefault.tsx index 58151982b..824d4f591 100644 --- a/app/src/plugins/prometheus/PrometheusChartDefault.tsx +++ b/app/src/plugins/prometheus/PrometheusChartDefault.tsx @@ -26,6 +26,7 @@ export interface IPrometheusChartDefaultProps { unit: string; stacked: boolean; legend: string; + color?: string; metrics: Metrics.AsObject[]; } @@ -44,6 +45,7 @@ const PrometheusChartDefault: React.FunctionComponent { const refChart = useRef(null); @@ -131,7 +133,13 @@ const PrometheusChartDefault: React.FunctionComponent - {stacked ? {series} : {series}} + {color && series.length === 1 ? ( + {series} + ) : stacked ? ( + {series} + ) : ( + {series} + )} { - return colors[index % colors.length]; -}; +import { getLegendColorClass } from 'plugins/prometheus/helpers'; interface ILegendItem { childName: string; @@ -29,7 +19,11 @@ export interface IPrometheusChartDefaultLegendProps { toogleMetric: (index: string) => void; } -// +// PrometheusChartDefaultLegend is the component, which renders the legend for the default Prometheus chart. The user +// can decide between the following options: disabled, bottom and table. The bottom option is the default one. +// NOTE: The height of the legend + the chart must be 336px. When the legend is disabled the space is completly used by +// the chart. For the bottom and table option the legend height is 50px/80px plus a margin of 16px. The remaining space +// is used by the chart. const PrometheusChartDefaultLegend: React.FunctionComponent = ({ legend, legendData, diff --git a/app/src/plugins/prometheus/PrometheusPageData.tsx b/app/src/plugins/prometheus/PrometheusPageData.tsx index 4395ddcf0..a31424e94 100644 --- a/app/src/plugins/prometheus/PrometheusPageData.tsx +++ b/app/src/plugins/prometheus/PrometheusPageData.tsx @@ -1,17 +1,25 @@ import { + Button, + ButtonVariant, Card, CardBody, Flex, FlexItem, - SimpleList, - SimpleListItem, ToggleGroup, ToggleGroupItem, } from '@patternfly/react-core'; +import { EyeSlashIcon, SquareIcon } from '@patternfly/react-icons'; import React, { useState } from 'react'; +import { TableComposable, TableVariant, Tbody, Td, Th, Thead, Tr } from '@patternfly/react-table'; import { Metrics } from 'proto/prometheus_grpc_web_pb'; import PrometheusChartDefault from 'plugins/prometheus/PrometheusChartDefault'; +import { getLegendColorClass } from 'plugins/prometheus/helpers'; + +interface ISelectedMetrics { + color?: string; + metrics: Metrics.AsObject[]; +} interface IPrometheusPageDataProps { metrics: Metrics.AsObject[]; @@ -27,15 +35,15 @@ const PrometheusPageData: React.FunctionComponent = ({ }: IPrometheusPageDataProps) => { const [type, setType] = useState('line'); const [stacked, setStacked] = useState(false); - const [selectedMetrics, setSelectedMetrics] = useState([]); + const [selectedMetrics, setSelectedMetrics] = useState({ color: undefined, metrics: metrics }); // select is used to select a single metric, which should be shown in the rendered chart. If the currently selected // metric is clicked again, the filter will be removed and all metrics will be shown in the chart. - const select = (metric: Metrics.AsObject): void => { - if (selectedMetrics.length === 1 && selectedMetrics[0].label === metric.label) { - setSelectedMetrics(metrics); + const select = (metric: Metrics.AsObject, color: string): void => { + if (selectedMetrics.metrics.length === 1 && selectedMetrics.metrics[0].label === metric.label) { + setSelectedMetrics({ color: undefined, metrics: metrics }); } else { - setSelectedMetrics([metric]); + setSelectedMetrics({ color: color, metrics: [metric] }); } }; @@ -69,23 +77,55 @@ const PrometheusPageData: React.FunctionComponent = ({ unit="" stacked={stacked} legend="disabled" - metrics={selectedMetrics.length === 0 ? metrics : selectedMetrics} + color={selectedMetrics.metrics.length === 1 ? selectedMetrics.color : undefined} + metrics={selectedMetrics.metrics} />

 

- - {metrics.map((metric, index) => ( - select(metric)} - isActive={selectedMetrics.length === 1 && selectedMetrics[0].label === metric.label} - > - {metric.label === '{}' && metrics.length === queries.length ? queries[index] : metric.label} - {metric.dataList[metric.dataList.length - 1].y} - - ))} - + + + + Name + Min + Max + Avg + Current + + + + {metrics.map((metric, index) => ( + + + + + {metric.min} + {metric.max} + {metric.avg} + {metric.dataList[metric.dataList.length - 1].y} + + ))} + + ); diff --git a/app/src/plugins/prometheus/helpers.ts b/app/src/plugins/prometheus/helpers.ts index 47154ae33..111882247 100644 --- a/app/src/plugins/prometheus/helpers.ts +++ b/app/src/plugins/prometheus/helpers.ts @@ -1,3 +1,5 @@ +import { ChartThemeColor, getDarkThemeColors } from '@patternfly/react-charts'; + import { Chart, Data, Query, Spec, Variable } from 'proto/prometheus_grpc_web_pb'; import { Plugin } from 'proto/plugins_grpc_web_pb'; @@ -120,3 +122,13 @@ export const transformData = (data: Data.AsObject[], isHidden?: boolean): IData[ return { x: d.x, y: isNaN(d.y) || isHidden ? null : d.y }; }); }; + +// colors is an array with all the supported colors for a chart. These are the same colors as they are used for the +// bars, lines and areas in a chart. +export const colors = getDarkThemeColors(ChartThemeColor.multiOrdered).area.colorScale; + +// getLegendColorClass returns the color class for an item in the legend. When we have more series then colors, we start +// again with the first color. +export const getLegendColorClass = (index: number): string => { + return colors[index % colors.length]; +};