vmui: add gap display option for charts #5152 (#5862)

This commit is contained in:
Yury Molodov 2024-02-29 23:42:50 +01:00 committed by GitHub
parent 5aa3dfbd20
commit e130f29659
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 77 additions and 13 deletions

View file

@ -30,7 +30,7 @@
"sass": "^1.56.0",
"source-map-explorer": "^2.5.3",
"typescript": "~4.6.2",
"uplot": "^1.6.19",
"uplot": "^1.6.30",
"web-vitals": "^3.3.2"
},
"devDependencies": {

View file

@ -26,7 +26,7 @@
"sass": "^1.56.0",
"source-map-explorer": "^2.5.3",
"typescript": "~4.6.2",
"uplot": "^1.6.19",
"uplot": "^1.6.30",
"web-vitals": "^3.3.2"
},
"scripts": {

View file

@ -41,6 +41,7 @@ export interface LineChartProps {
layoutSize: ElementSize;
height?: number;
anomalyView?: boolean;
spanGaps?: boolean;
}
const LineChart: FC<LineChartProps> = ({
@ -53,7 +54,8 @@ const LineChart: FC<LineChartProps> = ({
setPeriod,
layoutSize,
height,
anomalyView
anomalyView,
spanGaps = false
}) => {
const { isDarkTheme } = useAppState();
@ -106,10 +108,10 @@ const LineChart: FC<LineChartProps> = ({
useEffect(() => {
if (!uPlotInst) return;
delSeries(uPlotInst);
addSeries(uPlotInst, series);
addSeries(uPlotInst, series, spanGaps);
setBand(uPlotInst, series);
uPlotInst.redraw();
}, [series]);
}, [series, spanGaps]);
useEffect(() => {
if (!uPlotInst) return;

View file

@ -7,16 +7,21 @@ import Popper from "../../Main/Popper/Popper";
import "./style.scss";
import Tooltip from "../../Main/Tooltip/Tooltip";
import useBoolean from "../../../hooks/useBoolean";
import LinesConfigurator from "./LinesConfigurator/LinesConfigurator";
const title = "Axes settings";
const title = "Graph settings";
interface GraphSettingsProps {
yaxis: YaxisState,
setYaxisLimits: (limits: AxisRange) => void,
toggleEnableLimits: () => void
toggleEnableLimits: () => void,
spanGaps: {
value: boolean,
onChange: (value: boolean) => void,
},
}
const GraphSettings: FC<GraphSettingsProps> = ({ yaxis, setYaxisLimits, toggleEnableLimits }) => {
const GraphSettings: FC<GraphSettingsProps> = ({ yaxis, setYaxisLimits, toggleEnableLimits, spanGaps }) => {
const popperRef = useRef<HTMLDivElement>(null);
const buttonRef = useRef<HTMLDivElement>(null);
@ -55,6 +60,10 @@ const GraphSettings: FC<GraphSettingsProps> = ({ yaxis, setYaxisLimits, toggleEn
setYaxisLimits={setYaxisLimits}
toggleEnableLimits={toggleEnableLimits}
/>
<LinesConfigurator
spanGaps={spanGaps.value}
onChange={spanGaps.onChange}
/>
</div>
</div>
</Popper>

View file

@ -0,0 +1,23 @@
import React, { FC } from "preact/compat";
import Switch from "../../../Main/Switch/Switch";
import useDeviceDetect from "../../../../hooks/useDeviceDetect";
interface Props {
spanGaps: boolean,
onChange: (value: boolean) => void,
}
const LinesConfigurator: FC<Props> = ({ spanGaps, onChange }) => {
const { isMobile } = useDeviceDetect();
return <div>
<Switch
value={spanGaps}
onChange={onChange}
label="Connect null values"
fullWidth={isMobile}
/>
</div>;
};
export default LinesConfigurator;

View file

@ -8,7 +8,7 @@
&__body {
display: grid;
gap: $padding-small;
gap: $padding-large;
padding: 0 $padding-global;
}
}

View file

@ -41,6 +41,7 @@ export interface GraphViewProps {
height?: number;
isHistogram?: boolean;
anomalyView?: boolean;
spanGaps?: boolean;
}
const GraphView: FC<GraphViewProps> = ({
@ -58,6 +59,7 @@ const GraphView: FC<GraphViewProps> = ({
height,
isHistogram,
anomalyView,
spanGaps
}) => {
const { isMobile } = useDeviceDetect();
const { timezone } = useTimeState();
@ -196,6 +198,7 @@ const GraphView: FC<GraphViewProps> = ({
layoutSize={containerSize}
height={height}
anomalyView={anomalyView}
spanGaps={spanGaps}
/>
)}
{isHistogram && (

View file

@ -20,7 +20,7 @@ type Props = {
const GraphTab: FC<Props> = ({ isHistogram, graphData, controlsRef, anomalyView }) => {
const { isMobile } = useDeviceDetect();
const { customStep, yaxis } = useGraphState();
const { customStep, yaxis, spanGaps } = useGraphState();
const { period } = useTimeState();
const { query } = useQueryState();
@ -35,6 +35,10 @@ const GraphTab: FC<Props> = ({ isHistogram, graphData, controlsRef, anomalyView
graphDispatch({ type: "TOGGLE_ENABLE_YAXIS_LIMITS" });
};
const setSpanGaps = (value: boolean) => {
graphDispatch({ type: "SET_SPAN_GAPS", payload: value });
};
const setPeriod = ({ from, to }: {from: Date, to: Date}) => {
timeDispatch({ type: "SET_PERIOD", payload: { from, to } });
};
@ -46,6 +50,7 @@ const GraphTab: FC<Props> = ({ isHistogram, graphData, controlsRef, anomalyView
yaxis={yaxis}
setYaxisLimits={setYaxisLimits}
toggleEnableLimits={toggleEnableLimits}
spanGaps={{ value: spanGaps, onChange: setSpanGaps }}
/>
</div>
);
@ -64,6 +69,7 @@ const GraphTab: FC<Props> = ({ isHistogram, graphData, controlsRef, anomalyView
height={isMobile ? window.innerHeight * 0.5 : 500}
isHistogram={isHistogram}
anomalyView={anomalyView}
spanGaps={spanGaps}
/>
</>
);

View file

@ -34,6 +34,7 @@ const PredefinedPanel: FC<PredefinedPanelsProps> = ({
const containerRef = useRef<HTMLDivElement>(null);
const [visible, setVisible] = useState(false);
const [spanGaps, setSpanGaps] = useState(false);
const [yaxis, setYaxis] = useState<YaxisState>({
limits: {
enable: false,
@ -121,6 +122,7 @@ const PredefinedPanel: FC<PredefinedPanelsProps> = ({
yaxis={yaxis}
setYaxisLimits={setYaxisLimits}
toggleEnableLimits={toggleEnableLimits}
spanGaps={{ value: spanGaps, onChange: setSpanGaps }}
/>
</div>
<div className="vm-predefined-panel-body">
@ -140,6 +142,7 @@ const PredefinedPanel: FC<PredefinedPanelsProps> = ({
setPeriod={setPeriod}
fullWidth={false}
height={isMobile ? window.innerHeight * 0.5 : 500}
spanGaps={spanGaps}
/>
}
</div>

View file

@ -49,7 +49,7 @@ const QueryAnalyzerView: FC<Props> = ({ data, period }) => {
}, [data]);
const [displayType, setDisplayType] = useState(tabs[0].value);
const { yaxis } = useGraphState();
const { yaxis, spanGaps } = useGraphState();
const graphDispatch = useGraphDispatch();
const setYaxisLimits = (limits: AxisRange) => {
@ -60,6 +60,10 @@ const QueryAnalyzerView: FC<Props> = ({ data, period }) => {
graphDispatch({ type: "TOGGLE_ENABLE_YAXIS_LIMITS" });
};
const setSpanGaps = (value: boolean) => {
graphDispatch({ type: "SET_SPAN_GAPS", payload: value });
};
const handleChangeDisplayType = (newValue: string) => {
setDisplayType(newValue as DisplayType);
};
@ -137,6 +141,7 @@ const QueryAnalyzerView: FC<Props> = ({ data, period }) => {
yaxis={yaxis}
setYaxisLimits={setYaxisLimits}
toggleEnableLimits={toggleEnableLimits}
spanGaps={{ value: spanGaps, onChange: setSpanGaps }}
/>
)}
{displayType === "table" && (
@ -161,6 +166,7 @@ const QueryAnalyzerView: FC<Props> = ({ data, period }) => {
setPeriod={() => null}
height={isMobile ? window.innerHeight * 0.5 : 500}
isHistogram={isHistogram}
spanGaps={spanGaps}
/>
)}
{liveData && (displayType === "code") && (

View file

@ -15,6 +15,8 @@ export interface GraphState {
customStep: string
yaxis: YaxisState
isHistogram: boolean
/** when true, null data values will not cause line breaks */
spanGaps: boolean
}
export type GraphAction =
@ -22,13 +24,15 @@ export type GraphAction =
| { type: "SET_YAXIS_LIMITS", payload: AxisRange }
| { type: "SET_CUSTOM_STEP", payload: string}
| { type: "SET_IS_HISTOGRAM", payload: boolean }
| { type: "SET_SPAN_GAPS", payload: boolean }
export const initialGraphState: GraphState = {
customStep: getQueryStringValue("g0.step_input", "") as string,
yaxis: {
limits: { enable: false, range: { "1": [0, 0] } }
},
isHistogram: false
isHistogram: false,
spanGaps: false,
};
export function reducer(state: GraphState, action: GraphAction): GraphState {
@ -65,6 +69,11 @@ export function reducer(state: GraphState, action: GraphAction): GraphState {
...state,
isHistogram: action.payload
};
case "SET_SPAN_GAPS":
return {
...state,
spanGaps: action.payload
};
default:
throw new Error();
}

View file

@ -94,6 +94,7 @@ export const getSeriesItemContext = (data: MetricResult[], hideSeries: string[],
width,
stroke,
points,
spanGaps: false,
forecast: forecast.value,
forecastGroup: forecast.group,
freeFormFields: d.metric,
@ -165,8 +166,9 @@ export const delSeries = (u: uPlot) => {
}
};
export const addSeries = (u: uPlot, series: uPlotSeries[]) => {
export const addSeries = (u: uPlot, series: uPlotSeries[], spanGaps = false) => {
series.forEach((s) => {
if (s.label) s.spanGaps = spanGaps;
u.addSeries(s);
});
};

View file

@ -41,6 +41,7 @@ See also [LTS releases](https://docs.victoriametrics.com/LTS-releases.html).
* FEATURE: [vmagent](https://docs.victoriametrics.com/vmagent.html): add support for `enable_compression` option in [scrape_configs](https://docs.victoriametrics.com/sd_configs/#scrape_configs) in order to be compatible with Prometheus scrape configs. See [this pull request](https://github.com/prometheus/prometheus/pull/13166) and [this feature request](https://github.com/prometheus/prometheus/issues/12319). Note that `vmagent` was always supporting [`disable_compression` option](https://docs.victoriametrics.com/vmagent/#scrape_config-enhancements) before Prometheus added `enable_compression` option.
* FEATURE: [vmctl](https://docs.victoriametrics.com/vmctl.html): support client-side TLS configuration for [InfluxDB](https://docs.victoriametrics.com/vmctl/#migrating-data-from-influxdb-1x), [Remote Read protocol](https://docs.victoriametrics.com/vmctl/#migrating-data-by-remote-read-protocol) and [OpenTSDB](https://docs.victoriametrics.com/vmctl/#migrating-data-from-opentsdb). See [this feature request](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/5748). Thanks to @khushijain21 for pull requests [1](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/5783), [2](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/5798), [3](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/5797).
* FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): preserve [`WITH` templates](https://play.victoriametrics.com/select/accounting/1/6a716b0f-38bc-4856-90ce-448fd713e3fe/expand-with-exprs) when clicking the `prettify query` button at the right side of query input field. See [this feature request](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/5383).
* FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): add a feature that allows to choose how zero values, representing gaps in data, are displayed on a chart: time series data points with gaps are either connected or show breaks. See [this feature request](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/5152).
* FEATURE: [vmalert](https://docs.victoriametrics.com/#vmalert): support filtering by group, rule or labels in [vmalert's UI](https://docs.victoriametrics.com/vmalert/#web) for `/groups` and `/alerts` pages. See [the pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/5791) by @victoramsantos.
* FEATURE: [docker-compose](https://github.com/VictoriaMetrics/VictoriaMetrics/tree/master/deployment/docker#docker-compose-environment-for-victoriametrics): create a separate [docker-compose environment](https://github.com/VictoriaMetrics/VictoriaMetrics/blob/master/deployment/docker/docker-compose-victorialogs.yml) for VictoriaLogs installation, including fluentbit and [VictoriaLogs Grafana datasource](https://github.com/VictoriaMetrics/victorialogs-datasource).
* FEATURE: [vmbackupmanager](https://docs.victoriametrics.com/vmbackupmanager/): wait for up 30 seconds before making a [snapshot](https://docs.victoriametrics.com/#how-to-work-with-snapshots) for backup if `vmstorage` is temporarily unavailalbe. This should prevent from `vmbackupmanager` termination in this case. See [this feature request](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/5859).