Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
10 changes: 9 additions & 1 deletion app/src/plugins/prometheus/PrometheusChartDefault.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export interface IPrometheusChartDefaultProps {
unit: string;
stacked: boolean;
legend: string;
color?: string;
metrics: Metrics.AsObject[];
}

Expand All @@ -44,6 +45,7 @@ const PrometheusChartDefault: React.FunctionComponent<IPrometheusChartDefaultPro
unit,
stacked,
legend,
color,
metrics,
}: IPrometheusChartDefaultProps) => {
const refChart = useRef<HTMLDivElement>(null);
Expand Down Expand Up @@ -131,7 +133,13 @@ const PrometheusChartDefault: React.FunctionComponent<IPrometheusChartDefaultPro
>
<ChartAxis dependentAxis={false} showGrid={false} />
<ChartAxis dependentAxis={true} showGrid={true} label={unit} />
{stacked ? <ChartStack>{series}</ChartStack> : <ChartGroup>{series}</ChartGroup>}
{color && series.length === 1 ? (
<ChartGroup color={color}>{series}</ChartGroup>
) : stacked ? (
<ChartStack>{series}</ChartStack>
) : (
<ChartGroup color={color}>{series}</ChartGroup>
)}
</Chart>
</div>
<PrometheusChartDefaultLegend
Expand Down
18 changes: 6 additions & 12 deletions app/src/plugins/prometheus/PrometheusChartDefaultLegend.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,10 @@
import { Button, ButtonVariant } from '@patternfly/react-core';
import { ChartThemeColor, getDarkThemeColors } from '@patternfly/react-charts';
import { EyeSlashIcon, SquareIcon } from '@patternfly/react-icons';
import { TableComposable, TableVariant, Tbody, Td, Th, Thead, Tr } from '@patternfly/react-table';
import React from 'react';

import { Metrics } from 'proto/prometheus_grpc_web_pb';

// 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, 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.
const getLegendColorClass = (index: number): string => {
return colors[index % colors.length];
};
import { getLegendColorClass } from 'plugins/prometheus/helpers';

interface ILegendItem {
childName: string;
Expand All @@ -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<IPrometheusChartDefaultLegendProps> = ({
legend,
legendData,
Expand Down
80 changes: 60 additions & 20 deletions app/src/plugins/prometheus/PrometheusPageData.tsx
Original file line number Diff line number Diff line change
@@ -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[];
Expand All @@ -27,15 +35,15 @@ const PrometheusPageData: React.FunctionComponent<IPrometheusPageDataProps> = ({
}: IPrometheusPageDataProps) => {
const [type, setType] = useState<string>('line');
const [stacked, setStacked] = useState<boolean>(false);
const [selectedMetrics, setSelectedMetrics] = useState<Metrics.AsObject[]>([]);
const [selectedMetrics, setSelectedMetrics] = useState<ISelectedMetrics>({ 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] });
}
};

Expand Down Expand Up @@ -69,23 +77,55 @@ const PrometheusPageData: React.FunctionComponent<IPrometheusPageDataProps> = ({
unit=""
stacked={stacked}
legend="disabled"
metrics={selectedMetrics.length === 0 ? metrics : selectedMetrics}
color={selectedMetrics.metrics.length === 1 ? selectedMetrics.color : undefined}
metrics={selectedMetrics.metrics}
/>

<p>&nbsp;</p>

<SimpleList aria-label="Prometheus Data" isControlled={false}>
{metrics.map((metric, index) => (
<SimpleListItem
key={index}
onClick={(): void => select(metric)}
isActive={selectedMetrics.length === 1 && selectedMetrics[0].label === metric.label}
>
{metric.label === '{}' && metrics.length === queries.length ? queries[index] : metric.label}
<span style={{ float: 'right' }}>{metric.dataList[metric.dataList.length - 1].y}</span>
</SimpleListItem>
))}
</SimpleList>
<TableComposable aria-label="Legend" variant={TableVariant.compact} borders={false}>
<Thead>
<Tr>
<Th>Name</Th>
<Th>Min</Th>
<Th>Max</Th>
<Th>Avg</Th>
<Th>Current</Th>
</Tr>
</Thead>
<Tbody>
{metrics.map((metric, index) => (
<Tr key={index}>
<Td dataLabel="Name">
<Button
className={
selectedMetrics.metrics.length === 1 && selectedMetrics.metrics[0].label !== metric.label
? 'pf-u-color-400'
: ''
}
style={{ color: 'inherit', textDecoration: 'inherit' }}
variant={ButtonVariant.link}
isInline={true}
icon={
selectedMetrics.metrics.length === 1 && selectedMetrics.metrics[0].label !== metric.label ? (
<EyeSlashIcon />
) : (
<SquareIcon color={getLegendColorClass(index)} />
)
}
onClick={(): void => select(metric, getLegendColorClass(index))}
>
{metric.label === '{}' && metrics.length === queries.length ? queries[index] : metric.label}
</Button>
</Td>
<Td dataLabel="Min">{metric.min}</Td>
<Td dataLabel="Max">{metric.max}</Td>
<Td dataLabel="Avg">{metric.avg}</Td>
<Td dataLabel="Current">{metric.dataList[metric.dataList.length - 1].y}</Td>
</Tr>
))}
</Tbody>
</TableComposable>
</CardBody>
</Card>
);
Expand Down
12 changes: 12 additions & 0 deletions app/src/plugins/prometheus/helpers.ts
Original file line number Diff line number Diff line change
@@ -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';

Expand Down Expand Up @@ -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];
};