mirror of
https://github.com/prometheus/prometheus
synced 2024-12-25 16:02:28 +00:00
Move /targets page discovered labels to expandable section (#12824)
* Move /targets page discovered labels to expandable section The current tooltip for showing the pre-relabeling discovered labels for a target is notoriously unreliable and can get cut off when there are many labels. This PR introduces a (hopefully unobtuse enough) expander/collapser button for the discovered labels of each target, and then the discovered labels are shown in a more persistent way underneath the final target labels, instead of using a tooltip. Fixes https://github.com/prometheus/prometheus/issues/9175#issuecomment-1713074341 Signed-off-by: Julius Volz <julius.volz@gmail.com> * Remove obsolete test snapshot Signed-off-by: Julius Volz <julius.volz@gmail.com> --------- Signed-off-by: Julius Volz <julius.volz@gmail.com>
This commit is contained in:
parent
7c75d233d0
commit
aa7bf083e9
@ -35,12 +35,7 @@ const ScrapePoolContentTable: FC<InfiniteScrollItemsProps<Target>> = ({ items })
|
||||
<Badge color={getColor(target.health)}>{target.health.toUpperCase()}</Badge>
|
||||
</td>
|
||||
<td className={styles.labels}>
|
||||
<TargetLabels
|
||||
discoveredLabels={target.discoveredLabels}
|
||||
labels={target.labels}
|
||||
scrapePool={target.scrapePool}
|
||||
idx={index}
|
||||
/>
|
||||
<TargetLabels discoveredLabels={target.discoveredLabels} labels={target.labels} />
|
||||
</td>
|
||||
<td className={styles['last-scrape']}>{formatRelative(target.lastScrape, now())}</td>
|
||||
<td className={styles['scrape-duration']}>
|
||||
|
@ -1,3 +0,0 @@
|
||||
.discovered {
|
||||
white-space: nowrap;
|
||||
}
|
@ -17,15 +17,12 @@ describe('targetLabels', () => {
|
||||
job: 'node_exporter',
|
||||
foo: 'bar',
|
||||
},
|
||||
idx: 1,
|
||||
scrapePool: 'cortex/node-exporter_group/0',
|
||||
};
|
||||
const targetLabels = shallow(<TargetLabels {...defaultProps} />);
|
||||
|
||||
it('renders a div of series labels', () => {
|
||||
const div = targetLabels.find('div').filterWhere((elem) => elem.hasClass('series-labels-container'));
|
||||
expect(div).toHaveLength(1);
|
||||
expect(div.prop('id')).toEqual('series-labels-cortex/node-exporter_group/0-1');
|
||||
});
|
||||
|
||||
it('wraps each label in a label badge', () => {
|
||||
@ -38,15 +35,4 @@ describe('targetLabels', () => {
|
||||
});
|
||||
expect(targetLabels.find(Badge)).toHaveLength(3);
|
||||
});
|
||||
|
||||
it('renders a tooltip for discovered labels', () => {
|
||||
const tooltip = targetLabels.find(Tooltip);
|
||||
expect(tooltip).toHaveLength(1);
|
||||
expect(tooltip.prop('isOpen')).toBe(false);
|
||||
expect(tooltip.prop('target')).toEqual('series-labels-cortex\\/node-exporter_group\\/0-1');
|
||||
});
|
||||
|
||||
it('renders discovered labels', () => {
|
||||
expect(toJson(targetLabels)).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React, { FC, Fragment, useState } from 'react';
|
||||
import { Badge, Tooltip } from 'reactstrap';
|
||||
import 'css.escape';
|
||||
import styles from './TargetLabels.module.css';
|
||||
import { faChevronDown, faChevronUp } from '@fortawesome/free-solid-svg-icons';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import React, { FC, useState } from 'react';
|
||||
import { Badge, Button } from 'reactstrap';
|
||||
|
||||
interface Labels {
|
||||
[key: string]: string;
|
||||
@ -10,21 +10,14 @@ interface Labels {
|
||||
export interface TargetLabelsProps {
|
||||
discoveredLabels: Labels;
|
||||
labels: Labels;
|
||||
idx: number;
|
||||
scrapePool: string;
|
||||
}
|
||||
|
||||
const formatLabels = (labels: Labels): string[] => Object.keys(labels).map((key) => `${key}="${labels[key]}"`);
|
||||
|
||||
const TargetLabels: FC<TargetLabelsProps> = ({ discoveredLabels, labels, idx, scrapePool }) => {
|
||||
const [tooltipOpen, setTooltipOpen] = useState(false);
|
||||
|
||||
const toggle = (): void => setTooltipOpen(!tooltipOpen);
|
||||
const id = `series-labels-${scrapePool}-${idx}`;
|
||||
const TargetLabels: FC<TargetLabelsProps> = ({ discoveredLabels, labels }) => {
|
||||
const [showDiscovered, setShowDiscovered] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div id={id} className="series-labels-container">
|
||||
<div className="series-labels-container">
|
||||
{Object.keys(labels).map((labelName) => {
|
||||
return (
|
||||
<Badge color="primary" className="mr-1" key={labelName}>
|
||||
@ -32,22 +25,28 @@ const TargetLabels: FC<TargetLabelsProps> = ({ discoveredLabels, labels, idx, sc
|
||||
</Badge>
|
||||
);
|
||||
})}
|
||||
<Button
|
||||
size="sm"
|
||||
color="link"
|
||||
title={`${showDiscovered ? 'Hide' : 'Show'} discovered (pre-relabeling) labels`}
|
||||
onClick={() => setShowDiscovered(!showDiscovered)}
|
||||
style={{ fontSize: '0.8rem' }}
|
||||
>
|
||||
<FontAwesomeIcon icon={showDiscovered ? faChevronUp : faChevronDown} />
|
||||
</Button>
|
||||
</div>
|
||||
<Tooltip
|
||||
isOpen={tooltipOpen}
|
||||
target={CSS.escape(id)}
|
||||
toggle={toggle}
|
||||
placement={'right-end'}
|
||||
style={{ maxWidth: 'none', textAlign: 'left' }}
|
||||
>
|
||||
<b>Before relabeling:</b>
|
||||
{formatLabels(discoveredLabels).map((s: string, labelIndex: number) => (
|
||||
<Fragment key={labelIndex}>
|
||||
<br />
|
||||
<span className={styles.discovered}>{s}</span>
|
||||
</Fragment>
|
||||
))}
|
||||
</Tooltip>
|
||||
{showDiscovered && (
|
||||
<>
|
||||
<div className="mt-3 font-weight-bold">Discovered labels:</div>
|
||||
{Object.keys(discoveredLabels).map((labelName) => (
|
||||
<div key={labelName}>
|
||||
<Badge color="info" className="mr-1">
|
||||
{`${labelName}="${discoveredLabels[labelName]}"`}
|
||||
</Badge>
|
||||
</div>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
@ -1,81 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`targetLabels renders discovered labels 1`] = `
|
||||
<Fragment>
|
||||
<div
|
||||
className="series-labels-container"
|
||||
id="series-labels-cortex/node-exporter_group/0-1"
|
||||
>
|
||||
<Badge
|
||||
className="mr-1"
|
||||
color="primary"
|
||||
key="instance"
|
||||
pill={false}
|
||||
tag="span"
|
||||
>
|
||||
instance="localhost:9100"
|
||||
</Badge>
|
||||
<Badge
|
||||
className="mr-1"
|
||||
color="primary"
|
||||
key="job"
|
||||
pill={false}
|
||||
tag="span"
|
||||
>
|
||||
job="node_exporter"
|
||||
</Badge>
|
||||
<Badge
|
||||
className="mr-1"
|
||||
color="primary"
|
||||
key="foo"
|
||||
pill={false}
|
||||
tag="span"
|
||||
>
|
||||
foo="bar"
|
||||
</Badge>
|
||||
</div>
|
||||
<Tooltip
|
||||
autohide={true}
|
||||
isOpen={false}
|
||||
placement="right-end"
|
||||
placementPrefix="bs-tooltip"
|
||||
style={
|
||||
Object {
|
||||
"maxWidth": "none",
|
||||
"textAlign": "left",
|
||||
}
|
||||
}
|
||||
target="series-labels-cortex\\\\/node-exporter_group\\\\/0-1"
|
||||
toggle={[Function]}
|
||||
trigger="hover focus"
|
||||
>
|
||||
<b>
|
||||
Before relabeling:
|
||||
</b>
|
||||
<br />
|
||||
<span
|
||||
className="discovered"
|
||||
>
|
||||
__address__="localhost:9100"
|
||||
</span>
|
||||
<br />
|
||||
<span
|
||||
className="discovered"
|
||||
>
|
||||
__metrics_path__="/metrics"
|
||||
</span>
|
||||
<br />
|
||||
<span
|
||||
className="discovered"
|
||||
>
|
||||
__scheme__="http"
|
||||
</span>
|
||||
<br />
|
||||
<span
|
||||
className="discovered"
|
||||
>
|
||||
job="node_exporter"
|
||||
</span>
|
||||
</Tooltip>
|
||||
</Fragment>
|
||||
`;
|
Loading…
Reference in New Issue
Block a user