mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2025-01-20 15:16:42 +00:00
vmui: small changes on explore metrics page (#3634)
* fix: change issue link * fix: remove legend toggle * fix: move select graph size * feat: save url params on explore metrics page * wip Co-authored-by: Aliaksandr Valialkin <valyala@victoriametrics.com>
This commit is contained in:
parent
efec0f150f
commit
4295ca2ce2
24 changed files with 147 additions and 180 deletions
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"files": {
|
||||
"main.css": "./static/css/main.e9e7cdb7.css",
|
||||
"main.js": "./static/js/main.d34bbb5e.js",
|
||||
"main.css": "./static/css/main.8692abc6.css",
|
||||
"main.js": "./static/js/main.9c17bdf0.js",
|
||||
"static/js/27.c1ccfd29.chunk.js": "./static/js/27.c1ccfd29.chunk.js",
|
||||
"index.html": "./index.html"
|
||||
},
|
||||
"entrypoints": [
|
||||
"static/css/main.e9e7cdb7.css",
|
||||
"static/js/main.d34bbb5e.js"
|
||||
"static/css/main.8692abc6.css",
|
||||
"static/js/main.9c17bdf0.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.d34bbb5e.js"></script><link href="./static/css/main.e9e7cdb7.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.9c17bdf0.js"></script><link href="./static/css/main.8692abc6.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.9c17bdf0.js
Normal file
2
app/vmselect/vmui/static/js/main.9c17bdf0.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -17,6 +17,7 @@ export interface ChartTooltipProps {
|
|||
u: uPlot,
|
||||
metrics: MetricResult[],
|
||||
series: Series[],
|
||||
yRange: number[];
|
||||
unit?: string,
|
||||
isSticky?: boolean,
|
||||
tooltipOffset: { left: number, top: number },
|
||||
|
@ -30,6 +31,7 @@ const ChartTooltip: FC<ChartTooltipProps> = ({
|
|||
unit = "",
|
||||
metrics,
|
||||
series,
|
||||
yRange,
|
||||
tooltipIdx,
|
||||
tooltipOffset,
|
||||
isSticky,
|
||||
|
@ -46,22 +48,24 @@ const ChartTooltip: FC<ChartTooltipProps> = ({
|
|||
|
||||
const targetPortal = useMemo(() => u.root.querySelector(".u-wrap"), [u]);
|
||||
|
||||
const value = useMemo(() => get(u, ["data", seriesIdx, dataIdx], 0), [u, seriesIdx, dataIdx]);
|
||||
const valueFormat = useMemo(() => formatPrettyNumber(value), [value]);
|
||||
const dataTime = useMemo(() => u.data[0][dataIdx], [u, dataIdx]);
|
||||
const date = useMemo(() => dayjs(dataTime * 1000).tz().format(DATE_FULL_TIMEZONE_FORMAT), [dataTime]);
|
||||
const value = get(u, ["data", seriesIdx, dataIdx], 0);
|
||||
const valueFormat = formatPrettyNumber(value, get(yRange, [0]), get(yRange, [1]));
|
||||
const dataTime = u.data[0][dataIdx];
|
||||
const date = dayjs(dataTime * 1000).tz().format(DATE_FULL_TIMEZONE_FORMAT);
|
||||
|
||||
const color = useMemo(() => series[seriesIdx]?.stroke+"", [series, seriesIdx]);
|
||||
const color = series[seriesIdx]?.stroke+"";
|
||||
|
||||
const name = useMemo(() => {
|
||||
const group = metrics[seriesIdx -1]?.group || 0;
|
||||
return `Query ${group}`;
|
||||
}, [series, seriesIdx]);
|
||||
const groups = new Set();
|
||||
metrics.forEach(m => groups.add(m.group));
|
||||
const groupsSize = groups.size;
|
||||
const group = metrics[seriesIdx-1]?.group || 0;
|
||||
|
||||
const metric = metrics[seriesIdx-1]?.metric || {};
|
||||
const labelNames = Object.keys(metric).filter(x => x != "__name__");
|
||||
const metricName = metric["__name__"] || "value";
|
||||
|
||||
const fields = useMemo(() => {
|
||||
const metric = metrics[seriesIdx - 1]?.metric || {};
|
||||
const fields = Object.keys(metric);
|
||||
return fields.map(key => `${key}=${JSON.stringify(metric[key])}`);
|
||||
return labelNames.map(key => `${key}=${JSON.stringify(metric[key])}`);
|
||||
}, [metrics, seriesIdx]);
|
||||
|
||||
const handleClose = () => {
|
||||
|
@ -136,7 +140,12 @@ const ChartTooltip: FC<ChartTooltipProps> = ({
|
|||
style={position}
|
||||
>
|
||||
<div className="vm-chart-tooltip-header">
|
||||
<div className="vm-chart-tooltip-header__date">{date}</div>
|
||||
<div className="vm-chart-tooltip-header__date">
|
||||
{groupsSize > 1 && (
|
||||
<div>Query {group}</div>
|
||||
)}
|
||||
{date}
|
||||
</div>
|
||||
{isSticky && (
|
||||
<>
|
||||
<Button
|
||||
|
@ -162,7 +171,7 @@ const ChartTooltip: FC<ChartTooltipProps> = ({
|
|||
style={{ background: color }}
|
||||
/>
|
||||
<p>
|
||||
{name}:
|
||||
{metricName}:
|
||||
<b className="vm-chart-tooltip-data__value">{valueFormat}</b>
|
||||
{unit}
|
||||
</p>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
@use "src/styles/variables" as *;
|
||||
$chart-tooltip-width: 300px;
|
||||
$chart-tooltip-width: 325px;
|
||||
$chart-tooltip-icon-width: 25px;
|
||||
$chart-tooltip-half-icon: calc($chart-tooltip-icon-width/2);
|
||||
$chart-tooltip-date-width: $chart-tooltip-width - (2*$chart-tooltip-icon-width) - (2*$padding-global) - $padding-small;
|
||||
|
|
|
@ -50,6 +50,7 @@ const LineChart: FC<LineChartProps> = ({
|
|||
const uPlotRef = useRef<HTMLDivElement>(null);
|
||||
const [isPanning, setPanning] = useState(false);
|
||||
const [xRange, setXRange] = useState({ min: period.start, max: period.end });
|
||||
const [yRange, setYRange] = useState([0, 1]);
|
||||
const [uPlotInst, setUPlotInst] = useState<uPlot>();
|
||||
const layoutSize = useResize(container);
|
||||
|
||||
|
@ -128,6 +129,7 @@ const LineChart: FC<LineChartProps> = ({
|
|||
unit,
|
||||
series,
|
||||
metrics,
|
||||
yRange,
|
||||
tooltipIdx,
|
||||
tooltipOffset,
|
||||
};
|
||||
|
@ -153,7 +155,11 @@ const LineChart: FC<LineChartProps> = ({
|
|||
};
|
||||
|
||||
const getRangeX = (): Range.MinMax => [xRange.min, xRange.max];
|
||||
|
||||
const getRangeY = (u: uPlot, min = 0, max = 1, axis: string): Range.MinMax => {
|
||||
if (axis == "1") {
|
||||
setYRange([min, max]);
|
||||
}
|
||||
if (yaxis.limits.enable) return yaxis.limits.range[axis];
|
||||
return getMinMaxBuffer(min, max);
|
||||
};
|
||||
|
@ -258,6 +264,7 @@ const LineChart: FC<LineChartProps> = ({
|
|||
u={uPlotInst}
|
||||
series={series}
|
||||
metrics={metrics}
|
||||
yRange={yRange}
|
||||
tooltipIdx={tooltipIdx}
|
||||
tooltipOffset={tooltipOffset}
|
||||
id={tooltipId}
|
||||
|
|
|
@ -15,7 +15,6 @@ interface ExploreMetricItemGraphProps {
|
|||
instance: string,
|
||||
rateEnabled: boolean,
|
||||
isBucket: boolean,
|
||||
showLegend: boolean
|
||||
height?: number
|
||||
}
|
||||
|
||||
|
@ -25,7 +24,6 @@ const ExploreMetricItem: FC<ExploreMetricItemGraphProps> = ({
|
|||
instance,
|
||||
rateEnabled,
|
||||
isBucket,
|
||||
showLegend,
|
||||
height
|
||||
}) => {
|
||||
const { customStep, yaxis } = useGraphState();
|
||||
|
@ -118,7 +116,7 @@ with (q = ${queryBase}) (
|
|||
yaxis={yaxis}
|
||||
setYaxisLimits={setYaxisLimits}
|
||||
setPeriod={setPeriod}
|
||||
showLegend={showLegend}
|
||||
showLegend={false}
|
||||
height={height}
|
||||
/>
|
||||
)}
|
||||
|
|
|
@ -3,37 +3,24 @@ import ExploreMetricItemGraph from "../ExploreMetricGraph/ExploreMetricItemGraph
|
|||
import ExploreMetricItemHeader from "../ExploreMetricItemHeader/ExploreMetricItemHeader";
|
||||
import "./style.scss";
|
||||
import useResize from "../../../hooks/useResize";
|
||||
import { GraphSize } from "../../../types";
|
||||
|
||||
interface ExploreMetricItemProps {
|
||||
name: string
|
||||
job: string
|
||||
instance: string
|
||||
index: number
|
||||
size: GraphSize
|
||||
onRemoveItem: (name: string) => void
|
||||
onChangeOrder: (name: string, oldIndex: number, newIndex: number) => void
|
||||
}
|
||||
|
||||
export const sizeVariants = [
|
||||
{
|
||||
id: "small",
|
||||
height: () => window.innerHeight * 0.2
|
||||
},
|
||||
{
|
||||
id: "medium",
|
||||
isDefault: true,
|
||||
height: () => window.innerHeight * 0.4
|
||||
},
|
||||
{
|
||||
id: "large",
|
||||
height: () => window.innerHeight * 0.8
|
||||
},
|
||||
];
|
||||
|
||||
const ExploreMetricItem: FC<ExploreMetricItemProps> = ({
|
||||
name,
|
||||
job,
|
||||
instance,
|
||||
index,
|
||||
size,
|
||||
onRemoveItem,
|
||||
onChangeOrder,
|
||||
}) => {
|
||||
|
@ -42,17 +29,10 @@ const ExploreMetricItem: FC<ExploreMetricItemProps> = ({
|
|||
const isBucket = useMemo(() => /_bucket?/.test(name), [name]);
|
||||
|
||||
const [rateEnabled, setRateEnabled] = useState(isCounter);
|
||||
const [showLegend, setShowLegend] = useState(false);
|
||||
const [size, setSize] = useState(sizeVariants.find(v => v.isDefault) || sizeVariants[0]);
|
||||
|
||||
const windowSize = useResize(document.body);
|
||||
const graphHeight = useMemo(size.height, [size, windowSize]);
|
||||
|
||||
const handleChangeSize = (id: string) => {
|
||||
const target = sizeVariants.find(variant => variant.id === id);
|
||||
if (target) setSize(target);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setRateEnabled(isCounter);
|
||||
}, [job]);
|
||||
|
@ -64,13 +44,10 @@ const ExploreMetricItem: FC<ExploreMetricItemProps> = ({
|
|||
index={index}
|
||||
isBucket={isBucket}
|
||||
rateEnabled={rateEnabled}
|
||||
showLegend={showLegend}
|
||||
size={size.id}
|
||||
onChangeRate={setRateEnabled}
|
||||
onChangeLegend={setShowLegend}
|
||||
onRemoveItem={onRemoveItem}
|
||||
onChangeOrder={onChangeOrder}
|
||||
onChangeSize={handleChangeSize}
|
||||
/>
|
||||
<ExploreMetricItemGraph
|
||||
key={`${name}_${job}_${instance}_${rateEnabled}`}
|
||||
|
@ -79,7 +56,6 @@ const ExploreMetricItem: FC<ExploreMetricItemProps> = ({
|
|||
instance={instance}
|
||||
rateEnabled={rateEnabled}
|
||||
isBucket={isBucket}
|
||||
showLegend={showLegend}
|
||||
height={graphHeight}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -1,24 +1,19 @@
|
|||
import React, { FC, useRef, useState } from "preact/compat";
|
||||
import React, { FC } from "preact/compat";
|
||||
import "./style.scss";
|
||||
import Switch from "../../Main/Switch/Switch";
|
||||
import Tooltip from "../../Main/Tooltip/Tooltip";
|
||||
import Button from "../../Main/Button/Button";
|
||||
import { ArrowDownIcon, CloseIcon, ResizeIcon } from "../../Main/Icons";
|
||||
import Popper from "../../Main/Popper/Popper";
|
||||
import ExploreMetricLayouts from "../ExploreMetricLayouts/ExploreMetricLayouts";
|
||||
import { ArrowDownIcon, CloseIcon } from "../../Main/Icons";
|
||||
|
||||
interface ExploreMetricItemControlsProps {
|
||||
name: string
|
||||
index: number
|
||||
isBucket: boolean
|
||||
rateEnabled: boolean
|
||||
showLegend: boolean
|
||||
size: string
|
||||
onChangeRate: (val: boolean) => void
|
||||
onChangeLegend: (val: boolean) => void
|
||||
onRemoveItem: (name: string) => void
|
||||
onChangeOrder: (name: string, oldIndex: number, newIndex: number) => void
|
||||
onChangeSize: (id: string) => void
|
||||
}
|
||||
|
||||
const ExploreMetricItemHeader: FC<ExploreMetricItemControlsProps> = ({
|
||||
|
@ -26,17 +21,11 @@ const ExploreMetricItemHeader: FC<ExploreMetricItemControlsProps> = ({
|
|||
index,
|
||||
isBucket,
|
||||
rateEnabled,
|
||||
showLegend,
|
||||
size,
|
||||
onChangeRate,
|
||||
onChangeLegend,
|
||||
onRemoveItem,
|
||||
onChangeOrder,
|
||||
onChangeSize
|
||||
}) => {
|
||||
|
||||
const layoutButtonRef = useRef<HTMLDivElement>(null);
|
||||
const [openPopper, setOpenPopper] = useState(false);
|
||||
const handleClickRemove = () => {
|
||||
onRemoveItem(name);
|
||||
};
|
||||
|
@ -49,19 +38,6 @@ const ExploreMetricItemHeader: FC<ExploreMetricItemControlsProps> = ({
|
|||
onChangeOrder(name, index, index - 1);
|
||||
};
|
||||
|
||||
const handleTogglePopper = () => {
|
||||
setOpenPopper(prev => !prev);
|
||||
};
|
||||
|
||||
const handleClosePopper = () => {
|
||||
setOpenPopper(false);
|
||||
};
|
||||
|
||||
const handleChangeSize = (id: string) => {
|
||||
onChangeSize(id);
|
||||
handleClosePopper();
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="vm-explore-metrics-item-header">
|
||||
<div className="vm-explore-metrics-item-header-order">
|
||||
|
@ -97,23 +73,7 @@ const ExploreMetricItemHeader: FC<ExploreMetricItemControlsProps> = ({
|
|||
/>
|
||||
</Tooltip>
|
||||
)}
|
||||
<Switch
|
||||
label="show legend"
|
||||
value={showLegend}
|
||||
onChange={onChangeLegend}
|
||||
/>
|
||||
<div className="vm-explore-metrics-item-header__layout">
|
||||
<Tooltip title="change size the graph">
|
||||
<div ref={layoutButtonRef}>
|
||||
<Button
|
||||
startIcon={<ResizeIcon/>}
|
||||
variant="text"
|
||||
color="gray"
|
||||
size="small"
|
||||
onClick={handleTogglePopper}
|
||||
/>
|
||||
</div>
|
||||
</Tooltip>
|
||||
<Tooltip title="close graph">
|
||||
<Button
|
||||
startIcon={<CloseIcon/>}
|
||||
|
@ -124,18 +84,6 @@ const ExploreMetricItemHeader: FC<ExploreMetricItemControlsProps> = ({
|
|||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
||||
<Popper
|
||||
open={openPopper}
|
||||
onClose={handleClosePopper}
|
||||
placement="bottom-right"
|
||||
buttonRef={layoutButtonRef}
|
||||
>
|
||||
<ExploreMetricLayouts
|
||||
value={size}
|
||||
onChange={handleChangeSize}
|
||||
/>
|
||||
</Popper>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -33,7 +33,6 @@
|
|||
|
||||
&__layout {
|
||||
display: grid;
|
||||
grid-template-columns: auto auto;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,41 +0,0 @@
|
|||
import React, { FC } from "preact/compat";
|
||||
import "./style.scss";
|
||||
import { sizeVariants } from "../ExploreMetricItem/ExploreMetricItem";
|
||||
import classNames from "classnames";
|
||||
import { DoneIcon } from "../../Main/Icons";
|
||||
|
||||
interface ExploreMetricLayoutsProps {
|
||||
value: string
|
||||
onChange: (id: string) => void
|
||||
}
|
||||
|
||||
const ExploreMetricLayouts: FC<ExploreMetricLayoutsProps> = ({
|
||||
value,
|
||||
onChange
|
||||
}) => {
|
||||
|
||||
const createHandlerClick = (id: string) => () => {
|
||||
onChange(id);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="vm-explore-metrics-layouts">
|
||||
{sizeVariants.map(variant => (
|
||||
<div
|
||||
className={classNames({
|
||||
"vm-list-item": true,
|
||||
"vm-list-item_multiselect": true,
|
||||
"vm-list-item_multiselect_selected": variant.id === value
|
||||
})}
|
||||
key={variant.id}
|
||||
onClick={createHandlerClick(variant.id)}
|
||||
>
|
||||
{variant.id === value && <DoneIcon/>}
|
||||
<span>{variant.id}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ExploreMetricLayouts;
|
|
@ -1,5 +0,0 @@
|
|||
@use "src/styles/variables" as *;
|
||||
|
||||
.vm-explore-metrics-layouts {
|
||||
display: grid;
|
||||
}
|
|
@ -5,6 +5,7 @@ import "./style.scss";
|
|||
import { useTimeState } from "../../../state/time/TimeStateContext";
|
||||
import { useGraphDispatch, useGraphState } from "../../../state/graph/GraphStateContext";
|
||||
import usePrevious from "../../../hooks/usePrevious";
|
||||
import { GRAPH_SIZES } from "../../../constants/graph";
|
||||
|
||||
interface ExploreMetricsHeaderProps {
|
||||
jobs: string[]
|
||||
|
@ -12,22 +13,28 @@ interface ExploreMetricsHeaderProps {
|
|||
names: string[]
|
||||
job: string
|
||||
instance: string
|
||||
size: string
|
||||
selectedMetrics: string[]
|
||||
onChangeJob: (job: string) => void
|
||||
onChangeInstance: (instance: string) => void
|
||||
onToggleMetric: (name: string) => void
|
||||
onChangeSize: (sizeId: string) => void
|
||||
}
|
||||
|
||||
const sizeOptions = GRAPH_SIZES.map(s => s.id);
|
||||
|
||||
const ExploreMetricsHeader: FC<ExploreMetricsHeaderProps> = ({
|
||||
jobs,
|
||||
instances,
|
||||
names,
|
||||
job,
|
||||
instance,
|
||||
size,
|
||||
selectedMetrics,
|
||||
onChangeJob,
|
||||
onChangeInstance,
|
||||
onToggleMetric
|
||||
onToggleMetric,
|
||||
onChangeSize
|
||||
}) => {
|
||||
|
||||
const { period: { step }, duration } = useTimeState();
|
||||
|
@ -60,7 +67,7 @@ const ExploreMetricsHeader: FC<ExploreMetricsHeaderProps> = ({
|
|||
label="Job"
|
||||
placeholder="Please select job"
|
||||
onChange={onChangeJob}
|
||||
autofocus
|
||||
autofocus={!job}
|
||||
/>
|
||||
</div>
|
||||
<div className="vm-explore-metrics-header__instance">
|
||||
|
@ -81,6 +88,14 @@ const ExploreMetricsHeader: FC<ExploreMetricsHeaderProps> = ({
|
|||
value={customStep}
|
||||
/>
|
||||
</div>
|
||||
<div className="vm-explore-metrics-header__size">
|
||||
<Select
|
||||
label="Size graphs"
|
||||
value={size}
|
||||
list={sizeOptions}
|
||||
onChange={onChangeSize}
|
||||
/>
|
||||
</div>
|
||||
<div className="vm-explore-metrics-header-metrics">
|
||||
<Select
|
||||
value={selectedMetrics}
|
||||
|
|
|
@ -8,18 +8,13 @@
|
|||
gap: $padding-small calc($padding-small + 10px);
|
||||
|
||||
&__job {
|
||||
flex-grow: 0.5;
|
||||
min-width: 200px;
|
||||
max-width: 300px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&__instance {
|
||||
min-width: 200px;
|
||||
max-width: 500px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&__step {
|
||||
flex-grow: 1;
|
||||
min-width: 300px;
|
||||
}
|
||||
|
||||
&-metrics {
|
||||
|
|
|
@ -19,7 +19,7 @@ const Footer: FC = () => {
|
|||
<a
|
||||
className="vm__link"
|
||||
target="_blank"
|
||||
href="https://github.com/VictoriaMetrics/VictoriaMetrics/issues/new"
|
||||
href="https://github.com/VictoriaMetrics/VictoriaMetrics/issues/new/choose"
|
||||
rel="noreferrer"
|
||||
>
|
||||
create an issue
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
max-width: calc(100% - $padding-global);
|
||||
padding: 0 3px;
|
||||
font-size: $font-size-small;
|
||||
line-height: $font-size-small;
|
||||
line-height: calc($font-size-small + 2px);
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
background-color: $color-background-block;
|
||||
|
@ -46,12 +46,12 @@
|
|||
}
|
||||
|
||||
&__label {
|
||||
top: calc($font-size-small/-2);
|
||||
top: calc(($font-size-small/-2) - 2px);
|
||||
color: $color-text-secondary;
|
||||
}
|
||||
|
||||
&__error {
|
||||
top: calc(100% - ($font-size-small/2));
|
||||
top: calc((100% - ($font-size-small/2)) - 2px);
|
||||
color: $color-error;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,24 @@
|
|||
import { GraphSize } from "../types";
|
||||
|
||||
export const MAX_QUERY_FIELDS = 4;
|
||||
export const DEFAULT_MAX_SERIES = {
|
||||
table: 100,
|
||||
chart: 20,
|
||||
code: 1000,
|
||||
};
|
||||
|
||||
export const GRAPH_SIZES: GraphSize[] = [
|
||||
{
|
||||
id: "small",
|
||||
height: () => window.innerHeight * 0.2
|
||||
},
|
||||
{
|
||||
id: "medium",
|
||||
isDefault: true,
|
||||
height: () => window.innerHeight * 0.4
|
||||
},
|
||||
{
|
||||
id: "large",
|
||||
height: () => window.innerHeight * 0.8
|
||||
},
|
||||
];
|
||||
|
|
|
@ -3,7 +3,14 @@ import { compactObject } from "../../../utils/object";
|
|||
import { useTimeState } from "../../../state/time/TimeStateContext";
|
||||
import { setQueryStringWithoutPageReload } from "../../../utils/query-string";
|
||||
|
||||
export const useSetQueryParams = () => {
|
||||
interface queryProps {
|
||||
job: string
|
||||
instance?: string
|
||||
metrics: string
|
||||
size: string
|
||||
}
|
||||
|
||||
export const useSetQueryParams = ({ job, instance, metrics, size }: queryProps) => {
|
||||
const { duration, relativeTime, period: { date, step } } = useTimeState();
|
||||
|
||||
const setSearchParamsFromState = () => {
|
||||
|
@ -11,12 +18,16 @@ export const useSetQueryParams = () => {
|
|||
["g0.range_input"]: duration,
|
||||
["g0.end_input"]: date,
|
||||
["g0.step_input"]: step,
|
||||
["g0.relative_time"]: relativeTime
|
||||
["g0.relative_time"]: relativeTime,
|
||||
size,
|
||||
job,
|
||||
instance,
|
||||
metrics
|
||||
});
|
||||
|
||||
setQueryStringWithoutPageReload(params);
|
||||
};
|
||||
|
||||
useEffect(setSearchParamsFromState, [duration, relativeTime, date, step]);
|
||||
useEffect(setSearchParamsFromState, [duration, relativeTime, date, step, job, instance, metrics, size]);
|
||||
useEffect(setSearchParamsFromState, []);
|
||||
};
|
||||
|
|
|
@ -8,13 +8,22 @@ import { useFetchNames } from "./hooks/useFetchNames";
|
|||
import "./style.scss";
|
||||
import ExploreMetricItem from "../../components/ExploreMetrics/ExploreMetricItem/ExploreMetricItem";
|
||||
import ExploreMetricsHeader from "../../components/ExploreMetrics/ExploreMetricsHeader/ExploreMetricsHeader";
|
||||
import { GRAPH_SIZES } from "../../constants/graph";
|
||||
import { getQueryStringValue } from "../../utils/query-string";
|
||||
|
||||
const defaultJob = getQueryStringValue("job", "") as string;
|
||||
const defaultInstance = getQueryStringValue("instance", "") as string;
|
||||
const defaultMetricsStr = getQueryStringValue("metrics", "") as string;
|
||||
const defaultSizeId = getQueryStringValue("size", "") as string;
|
||||
const defaultSize = GRAPH_SIZES.find(v => defaultSizeId ? v.id === defaultSizeId : v.isDefault) || GRAPH_SIZES[0];
|
||||
|
||||
const ExploreMetrics: FC = () => {
|
||||
useSetQueryParams();
|
||||
const [job, setJob] = useState(defaultJob);
|
||||
const [instance, setInstance] = useState(defaultInstance);
|
||||
const [metrics, setMetrics] = useState(defaultMetricsStr ? defaultMetricsStr.split("&") : []);
|
||||
const [size, setSize] = useState(defaultSize);
|
||||
|
||||
const [job, setJob] = useState("");
|
||||
const [instance, setInstance] = useState("");
|
||||
const [metrics, setMetrics] = useState<string[]>([]);
|
||||
useSetQueryParams({ job, instance, metrics: metrics.join("&"), size: size.id });
|
||||
|
||||
const { jobs, isLoading: loadingJobs, error: errorJobs } = useFetchJobs();
|
||||
const { instances, isLoading: loadingInstances, error: errorInstances } = useFetchInstances(job);
|
||||
|
@ -36,6 +45,11 @@ const ExploreMetrics: FC = () => {
|
|||
}
|
||||
};
|
||||
|
||||
const handleChangeSize = (sizeId: string) => {
|
||||
const target = GRAPH_SIZES.find(variant => variant.id === sizeId);
|
||||
if (target) setSize(target);
|
||||
};
|
||||
|
||||
const handleChangeOrder = (name: string, oldIndex: number, newIndex: number) => {
|
||||
const maxIndex = newIndex > (metrics.length - 1);
|
||||
const minIndex = newIndex < 0;
|
||||
|
@ -49,8 +63,10 @@ const ExploreMetrics: FC = () => {
|
|||
};
|
||||
|
||||
useEffect(() => {
|
||||
setInstance("");
|
||||
}, [job]);
|
||||
if (instance && instances.length && !instances.includes(instance)) {
|
||||
setInstance("");
|
||||
}
|
||||
}, [instances, instance]);
|
||||
|
||||
return (
|
||||
<div className="vm-explore-metrics">
|
||||
|
@ -59,9 +75,11 @@ const ExploreMetrics: FC = () => {
|
|||
instances={instances}
|
||||
names={names}
|
||||
job={job}
|
||||
size={size.id}
|
||||
instance={instance}
|
||||
selectedMetrics={metrics}
|
||||
onChangeJob={setJob}
|
||||
onChangeSize={handleChangeSize}
|
||||
onChangeInstance={setInstance}
|
||||
onToggleMetric={handleToggleMetric}
|
||||
/>
|
||||
|
@ -78,6 +96,7 @@ const ExploreMetrics: FC = () => {
|
|||
job={job}
|
||||
instance={instance}
|
||||
index={i}
|
||||
size={size}
|
||||
onRemoveItem={handleToggleMetric}
|
||||
onChangeOrder={handleChangeOrder}
|
||||
/>
|
||||
|
|
|
@ -112,3 +112,9 @@ export interface Timezone {
|
|||
utc: string,
|
||||
search?: string
|
||||
}
|
||||
|
||||
export interface GraphSize {
|
||||
id: string,
|
||||
isDefault?: boolean,
|
||||
height: () => number
|
||||
}
|
||||
|
|
|
@ -25,14 +25,26 @@ export const defaultOptions = {
|
|||
};
|
||||
|
||||
export const formatTicks = (u: uPlot, ticks: number[], unit = ""): string[] => {
|
||||
return ticks.map(v => `${formatPrettyNumber(v)} ${unit}`);
|
||||
const min = ticks[0];
|
||||
const max = ticks[ticks.length-1];
|
||||
if (!unit) {
|
||||
return ticks.map(v => formatPrettyNumber(v, min, max));
|
||||
}
|
||||
return ticks.map(v => `${formatPrettyNumber(v, min, max)} ${unit}`);
|
||||
};
|
||||
|
||||
export const formatPrettyNumber = (n: number | null | undefined): string => {
|
||||
export const formatPrettyNumber = (n: number | null | undefined, min = 0, max = 0): string => {
|
||||
if (n === undefined || n === null) {
|
||||
return "";
|
||||
}
|
||||
return n.toLocaleString("en-US", { maximumSignificantDigits: 20 });
|
||||
let digits = 3 + Math.floor(1 + Math.log10(Math.max(Math.abs(min), Math.abs(max))) - Math.log10(Math.abs(min - max)));
|
||||
if (isNaN(digits) || digits > 20) {
|
||||
digits = 20;
|
||||
}
|
||||
return n.toLocaleString("en-US", {
|
||||
minimumSignificantDigits: digits,
|
||||
maximumSignificantDigits: digits,
|
||||
});
|
||||
};
|
||||
|
||||
interface AxisExtend extends Axis {
|
||||
|
|
Loading…
Reference in a new issue