mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2024-11-21 14:44:00 +00:00
app/vmui: small usability improvements
- Show in the line tooltip the number of the query which generates the given line. This simplifies comparison of lines generated by multiple queries. - Show metric name as __name__ label in the line tooltip in the same way as other labels are shown there. This makes the label information in the tooltip more consistent. - Properly quote label values with JSON.stringify(). This prevents from improper formatting when label values contain doublequote chars. - Remove double curly braces artifact at graph legend for lines without names and labels. - Properly use modifier for regular expressions across the code.
This commit is contained in:
parent
c03adc2bce
commit
d794e971fc
15 changed files with 46 additions and 51 deletions
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"files": {
|
||||
"main.css": "./static/css/main.9a291a47.css",
|
||||
"main.js": "./static/js/main.9d62d7df.js",
|
||||
"main.js": "./static/js/main.e3ded72d.js",
|
||||
"static/js/27.c1ccfd29.chunk.js": "./static/js/27.c1ccfd29.chunk.js",
|
||||
"index.html": "./index.html"
|
||||
},
|
||||
"entrypoints": [
|
||||
"static/css/main.9a291a47.css",
|
||||
"static/js/main.9d62d7df.js"
|
||||
"static/js/main.e3ded72d.js"
|
||||
]
|
||||
}
|
|
@ -1 +1 @@
|
|||
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="./favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="VM-UI is a metric explorer for Victoria Metrics"/><link rel="apple-touch-icon" href="./apple-touch-icon.png"/><link rel="icon" type="image/png" sizes="32x32" href="./favicon-32x32.png"><link rel="manifest" href="./manifest.json"/><title>VM UI</title><link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin><link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono&family=Lato:wght@300;400;700&display=swap" rel="stylesheet"><script src="./dashboards/index.js" type="module"></script><script defer="defer" src="./static/js/main.9d62d7df.js"></script><link href="./static/css/main.9a291a47.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
|
||||
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="./favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="VM-UI is a metric explorer for Victoria Metrics"/><link rel="apple-touch-icon" href="./apple-touch-icon.png"/><link rel="icon" type="image/png" sizes="32x32" href="./favicon-32x32.png"><link rel="manifest" href="./manifest.json"/><title>VM UI</title><link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin><link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono&family=Lato:wght@300;400;700&display=swap" rel="stylesheet"><script src="./dashboards/index.js" type="module"></script><script defer="defer" src="./static/js/main.e3ded72d.js"></script><link href="./static/css/main.9a291a47.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
|
File diff suppressed because one or more lines are too long
2
app/vmselect/vmui/static/js/main.e3ded72d.js
Normal file
2
app/vmselect/vmui/static/js/main.e3ded72d.js
Normal file
File diff suppressed because one or more lines are too long
|
@ -1,7 +1,7 @@
|
|||
import React, { FC, useEffect, useMemo, useRef, useState } from "preact/compat";
|
||||
import uPlot, { Series } from "uplot";
|
||||
import { MetricResult } from "../../../api/types";
|
||||
import { formatPrettyNumber, getColorLine, getLegendLabel } from "../../../utils/uplot/helpers";
|
||||
import { formatPrettyNumber, getColorLine } from "../../../utils/uplot/helpers";
|
||||
import dayjs from "dayjs";
|
||||
import { DATE_FULL_TIMEZONE_FORMAT } from "../../../constants/date";
|
||||
import ReactDOM from "react-dom";
|
||||
|
@ -54,14 +54,14 @@ const ChartTooltip: FC<ChartTooltipProps> = ({
|
|||
const color = useMemo(() => getColorLine(series[seriesIdx]?.label || ""), [series, seriesIdx]);
|
||||
|
||||
const name = useMemo(() => {
|
||||
const metricName = (series[seriesIdx]?.label || "").replace(/{.+}/gmi, "").trim();
|
||||
return getLegendLabel(metricName);
|
||||
const group = metrics[seriesIdx -1]?.group || 0;
|
||||
return `Query ${group}`;
|
||||
}, [series, seriesIdx]);
|
||||
|
||||
const fields = useMemo(() => {
|
||||
const metric = metrics[seriesIdx - 1]?.metric || {};
|
||||
const fields = Object.keys(metric).filter(k => k !== "__name__");
|
||||
return fields.map(key => `${key}="${metric[key]}"`);
|
||||
const fields = Object.keys(metric);
|
||||
return fields.map(key => `${key}=${JSON.stringify(metric[key])}`);
|
||||
}, [metrics, seriesIdx]);
|
||||
|
||||
const handleClose = () => {
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import React, { FC, useState, useMemo } from "preact/compat";
|
||||
import { MouseEvent } from "react";
|
||||
import { LegendItemType } from "../../../../utils/uplot/types";
|
||||
import { getLegendLabel } from "../../../../utils/uplot/helpers";
|
||||
import "./style.scss";
|
||||
import classNames from "classnames";
|
||||
import Tooltip from "../../../Main/Tooltip/Tooltip";
|
||||
|
@ -46,27 +45,30 @@ const LegendItem: FC<LegendItemProps> = ({ legend, onChange }) => {
|
|||
/>
|
||||
<div className="vm-legend-item-info">
|
||||
<span className="vm-legend-item-info__label">
|
||||
{getLegendLabel(legend.label)}
|
||||
{legend.freeFormFields["__name__"] || (freeFormFields.length == 0 ? "{}" : "")}
|
||||
</span>
|
||||
|
||||
 {
|
||||
{freeFormFields.map(f => (
|
||||
<Tooltip
|
||||
key={f.id}
|
||||
open={copiedValue === f.id}
|
||||
title={"Copied!"}
|
||||
placement="top-center"
|
||||
>
|
||||
<span
|
||||
className="vm-legend-item-info__free-fields"
|
||||
key={f.key}
|
||||
onClick={createHandlerCopy(f.freeField, f.id)}
|
||||
>
|
||||
{f.freeField}
|
||||
</span>
|
||||
</Tooltip>
|
||||
))}
|
||||
}
|
||||
{freeFormFields.length > 0 &&
|
||||
<span>
|
||||
{
|
||||
{freeFormFields.map(f => (
|
||||
<Tooltip
|
||||
key={f.id}
|
||||
open={copiedValue === f.id}
|
||||
title={"Copied!"}
|
||||
placement="top-center"
|
||||
>
|
||||
<span
|
||||
className="vm-legend-item-info__free-fields"
|
||||
key={f.key}
|
||||
onClick={createHandlerCopy(f.freeField, f.id)}
|
||||
>
|
||||
{f.freeField}
|
||||
</span>
|
||||
</Tooltip>
|
||||
))}
|
||||
}
|
||||
</span>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -4,7 +4,7 @@ export const getFreeFields = (legend: LegendItemType) => {
|
|||
const keys = Object.keys(legend.freeFormFields).filter(f => f !== "__name__");
|
||||
|
||||
return keys.map(f => {
|
||||
const freeField = `${f}="${legend.freeFormFields[f]}"`;
|
||||
const freeField = `${f}=${JSON.stringify(legend.freeFormFields[f])}`;
|
||||
const id = `${legend.label}.${freeField}`;
|
||||
|
||||
return {
|
||||
|
|
|
@ -20,7 +20,7 @@ const TenantsConfiguration: FC = () => {
|
|||
const tenantId = Number(value);
|
||||
dispatch({ type: "SET_TENANT_ID", payload: tenantId });
|
||||
if (serverURL) {
|
||||
const updateServerUrl = serverURL.replace(/(\/select\/)([\d]+)(\/prometheus)/gmi, `$1${tenantId}$3`);
|
||||
const updateServerUrl = serverURL.replace(/(\/select\/)([\d]+)(\/prometheus)/, `$1${tenantId}$3`);
|
||||
dispatch({ type: "SET_SERVER", payload: updateServerUrl });
|
||||
timeDispatch({ type: "RUN_QUERY" });
|
||||
}
|
||||
|
|
|
@ -44,7 +44,7 @@ const TableView: FC<GraphViewProps> = ({ data, displayColumns }) => {
|
|||
const rows: InstantDataSeries[] = useMemo(() => {
|
||||
const rows = data?.map(d => ({
|
||||
metadata: sortedColumns.map(c => (tableCompact
|
||||
? getNameForMetric(d, undefined, "=", true)
|
||||
? getNameForMetric(d)
|
||||
: (d.metric[c.key] || "-")
|
||||
)),
|
||||
value: d.value ? d.value[1] : "-",
|
||||
|
|
|
@ -1,14 +1,12 @@
|
|||
import { MetricBase } from "../api/types";
|
||||
|
||||
export const getNameForMetric = (result: MetricBase, alias?: string, connector = ": ", quoteValue = false): string => {
|
||||
export const getNameForMetric = (result: MetricBase, alias?: string): string => {
|
||||
const { __name__, ...freeFormFields } = result.metric;
|
||||
const name = alias || __name__ || "";
|
||||
|
||||
if (Object.keys(result.metric).length === 0) {
|
||||
return name || `Result ${result.group}`; // a bit better than just {} for case of aggregation functions
|
||||
const name = alias || `[Query ${result.group}] ${__name__ || ""}`;
|
||||
if (Object.keys(freeFormFields).length == 0) {
|
||||
return name;
|
||||
}
|
||||
|
||||
return `${name} {${Object.entries(freeFormFields).map(e =>
|
||||
`${e[0]}${connector}${(quoteValue ? `"${e[1]}"` : e[1])}`
|
||||
return `${name}{${Object.entries(freeFormFields).map(e =>
|
||||
`${e[0]}=${JSON.stringify(e[1])}`
|
||||
).join(", ")}}`;
|
||||
};
|
||||
|
|
|
@ -22,7 +22,7 @@ export const getQueryStringValue = (
|
|||
};
|
||||
|
||||
export const getQueryArray = (): string[] => {
|
||||
const queryLength = window.location.search.match(/g\d+.expr/gmi)?.length || 1;
|
||||
const queryLength = window.location.search.match(/g\d+\.expr/g)?.length || 1;
|
||||
return new Array(queryLength > MAX_QUERY_FIELDS ? MAX_QUERY_FIELDS : queryLength)
|
||||
.fill(1)
|
||||
.map((q, i) => getQueryStringValue(`g${i}.expr`, "") as string);
|
||||
|
|
|
@ -194,8 +194,8 @@ export const getTimezoneList = (search = "") => {
|
|||
return supportedTimezones.reduce((acc: {[key: string]: Timezone[]}, region) => {
|
||||
const zone = (region.match(/^(.*?)\//) || [])[1] || "unknown";
|
||||
const utc = getUTCByTimezone(region);
|
||||
const utcForSearch = utc.replace(/UTC|0/gmi, "");
|
||||
const regionForSearch = region.replace(/[/_]/gmi, " ");
|
||||
const utcForSearch = utc.replace(/UTC|0/, "");
|
||||
const regionForSearch = region.replace(/[/_]/g, " ");
|
||||
const item = {
|
||||
region,
|
||||
utc,
|
||||
|
|
|
@ -66,7 +66,3 @@ export const sizeAxis = (u: uPlot, values: string[], axisIdx: number, cycleNum:
|
|||
export const getColorLine = (label: string): string => getColorFromString(label);
|
||||
|
||||
export const getDashLine = (group: number): number[] => group <= 1 ? [] : [group*4, group*1.2];
|
||||
|
||||
export const getLegendLabel = (label: string): string => {
|
||||
return label.replace(/^\[\d+]/, "").replace(/{.+}/gmi, "");
|
||||
};
|
||||
|
|
|
@ -10,8 +10,7 @@ interface SeriesItem extends Series {
|
|||
}
|
||||
|
||||
export const getSeriesItem = (d: MetricResult, hideSeries: string[], alias: string[]): SeriesItem => {
|
||||
const name = getNameForMetric(d, alias[d.group - 1]);
|
||||
const label = `[${d.group}]${name}`;
|
||||
const label = getNameForMetric(d, alias[d.group - 1]);
|
||||
return {
|
||||
label,
|
||||
freeFormFields: d.metric,
|
||||
|
|
Loading…
Reference in a new issue