mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Alerting: Adds support for toggling common labels (#71497)
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
import { screen, render, waitFor } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import React from 'react';
|
||||
|
||||
import { AlertLabels } from './AlertLabels';
|
||||
|
||||
describe('AlertLabels', () => {
|
||||
it('should toggle show / hide common labels', async () => {
|
||||
const labels = { foo: 'bar', bar: 'baz', baz: 'qux' };
|
||||
const commonLabels = { foo: 'bar', baz: 'qux' };
|
||||
|
||||
render(<AlertLabels labels={labels} commonLabels={commonLabels} />);
|
||||
expect(screen.getByText('+2 common labels')).toBeInTheDocument();
|
||||
|
||||
userEvent.click(screen.getByRole('button'));
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('Hide common labels')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
userEvent.click(screen.getByRole('button'));
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('+2 common labels')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,26 +1,60 @@
|
||||
import { css } from '@emotion/css';
|
||||
import { chain } from 'lodash';
|
||||
import React from 'react';
|
||||
import pluralize from 'pluralize';
|
||||
import React, { useState } from 'react';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { getTagColorsFromName, useStyles2 } from '@grafana/ui';
|
||||
import { Button, getTagColorsFromName, useStyles2 } from '@grafana/ui';
|
||||
|
||||
import { Label, LabelSize } from './Label';
|
||||
|
||||
interface Props {
|
||||
labels: Record<string, string>;
|
||||
commonLabels?: Record<string, string>;
|
||||
size?: LabelSize;
|
||||
}
|
||||
|
||||
export const AlertLabels = ({ labels, size }: Props) => {
|
||||
export const AlertLabels = ({ labels, commonLabels = {}, size }: Props) => {
|
||||
const styles = useStyles2((theme) => getStyles(theme, size));
|
||||
const pairs = chain(labels).toPairs().reject(isPrivateKey).value();
|
||||
const [showCommonLabels, setShowCommonLabels] = useState(false);
|
||||
|
||||
const labelsToShow = chain(labels)
|
||||
.toPairs()
|
||||
.reject(isPrivateKey)
|
||||
.reject(([key]) => (showCommonLabels ? false : key in commonLabels))
|
||||
.value();
|
||||
|
||||
const commonLabelsCount = Object.keys(commonLabels).length;
|
||||
const hasCommonLabels = commonLabelsCount > 0;
|
||||
|
||||
return (
|
||||
<div className={styles.wrapper} role="list" aria-label="Labels">
|
||||
{pairs.map(([label, value]) => (
|
||||
{labelsToShow.map(([label, value]) => (
|
||||
<Label key={label + value} size={size} label={label} value={value} color={getLabelColor(label)} />
|
||||
))}
|
||||
{!showCommonLabels && hasCommonLabels && (
|
||||
<Button
|
||||
variant="secondary"
|
||||
fill="text"
|
||||
onClick={() => setShowCommonLabels(true)}
|
||||
tooltip="Show common labels"
|
||||
tooltipPlacement="top"
|
||||
size="sm"
|
||||
>
|
||||
+{commonLabelsCount} common {pluralize('label', commonLabelsCount)}
|
||||
</Button>
|
||||
)}
|
||||
{showCommonLabels && hasCommonLabels && (
|
||||
<Button
|
||||
variant="secondary"
|
||||
fill="text"
|
||||
onClick={() => setShowCommonLabels(false)}
|
||||
tooltipPlacement="top"
|
||||
size="sm"
|
||||
>
|
||||
Hide common labels
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -35,6 +69,7 @@ const getStyles = (theme: GrafanaTheme2, size?: LabelSize) => ({
|
||||
wrapper: css`
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
|
||||
gap: ${size === 'md' ? theme.spacing() : theme.spacing(0.5)};
|
||||
`,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { useMemo } from 'react';
|
||||
|
||||
import { dateTime } from '@grafana/data';
|
||||
import { dateTime, findCommonLabels } from '@grafana/data';
|
||||
import { Alert, PaginationProps } from 'app/types/unified-alerting';
|
||||
|
||||
import { alertInstanceKey } from '../../utils/rules';
|
||||
@@ -16,17 +16,27 @@ interface Props {
|
||||
footerRow?: JSX.Element;
|
||||
}
|
||||
|
||||
type AlertTableColumnProps = DynamicTableColumnProps<Alert>;
|
||||
type AlertTableItemProps = DynamicTableItemProps<Alert>;
|
||||
interface AlertWithCommonLabels extends Alert {
|
||||
commonLabels?: Record<string, string>;
|
||||
}
|
||||
|
||||
type AlertTableColumnProps = DynamicTableColumnProps<AlertWithCommonLabels>;
|
||||
type AlertTableItemProps = DynamicTableItemProps<AlertWithCommonLabels>;
|
||||
|
||||
export const AlertInstancesTable = ({ instances, pagination, footerRow }: Props) => {
|
||||
const commonLabels = useMemo(() => {
|
||||
// only compute the common labels if we have more than 1 instance, if we don't then that single instance
|
||||
// will have the complete set of common labels and no unique ones
|
||||
return instances.length > 1 ? findCommonLabels(instances.map((instance) => instance.labels)) : {};
|
||||
}, [instances]);
|
||||
|
||||
const items = useMemo(
|
||||
(): AlertTableItemProps[] =>
|
||||
instances.map((instance) => ({
|
||||
data: instance,
|
||||
data: { ...instance, commonLabels },
|
||||
id: alertInstanceKey(instance),
|
||||
})),
|
||||
[instances]
|
||||
[commonLabels, instances]
|
||||
);
|
||||
|
||||
return (
|
||||
@@ -53,7 +63,9 @@ const columns: AlertTableColumnProps[] = [
|
||||
id: 'labels',
|
||||
label: 'Labels',
|
||||
// eslint-disable-next-line react/display-name
|
||||
renderCell: ({ data: { labels } }) => <AlertLabels labels={labels} size="sm" />,
|
||||
renderCell: ({ data: { labels, commonLabels } }) => (
|
||||
<AlertLabels labels={labels} commonLabels={commonLabels} size="sm" />
|
||||
),
|
||||
},
|
||||
{
|
||||
id: 'created',
|
||||
|
||||
Reference in New Issue
Block a user