From c71c2b37c597d226a14c274df16dba1b267eb434 Mon Sep 17 00:00:00 2001 From: Yury Molodov Date: Thu, 29 Feb 2024 23:42:50 +0100 Subject: [PATCH] vmui: add gap display option for charts #5152 (#5862) --- app/vmui/packages/vmui/package-lock.json | 2 +- app/vmui/packages/vmui/package.json | 2 +- .../Chart/Line/LineChart/LineChart.tsx | 8 ++++--- .../GraphSettings/GraphSettings.tsx | 15 +++++++++--- .../LinesConfigurator/LinesConfigurator.tsx | 23 +++++++++++++++++++ .../Configurators/GraphSettings/style.scss | 2 +- .../components/Views/GraphView/GraphView.tsx | 3 +++ .../CustomPanel/CustomPanelTabs/GraphTab.tsx | 8 ++++++- .../PredefinedPanel/PredefinedPanel.tsx | 3 +++ .../QueryAnalyzerView/QueryAnalyzerView.tsx | 8 ++++++- .../packages/vmui/src/state/graph/reducer.ts | 11 ++++++++- .../packages/vmui/src/utils/uplot/series.ts | 4 +++- docs/CHANGELOG.md | 1 + 13 files changed, 77 insertions(+), 13 deletions(-) create mode 100644 app/vmui/packages/vmui/src/components/Configurators/GraphSettings/LinesConfigurator/LinesConfigurator.tsx diff --git a/app/vmui/packages/vmui/package-lock.json b/app/vmui/packages/vmui/package-lock.json index d6089b93f..e3b6fe9d2 100644 --- a/app/vmui/packages/vmui/package-lock.json +++ b/app/vmui/packages/vmui/package-lock.json @@ -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": { diff --git a/app/vmui/packages/vmui/package.json b/app/vmui/packages/vmui/package.json index ecf5e53c8..159525955 100644 --- a/app/vmui/packages/vmui/package.json +++ b/app/vmui/packages/vmui/package.json @@ -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": { diff --git a/app/vmui/packages/vmui/src/components/Chart/Line/LineChart/LineChart.tsx b/app/vmui/packages/vmui/src/components/Chart/Line/LineChart/LineChart.tsx index 2ccff1f0a..570a1a10c 100644 --- a/app/vmui/packages/vmui/src/components/Chart/Line/LineChart/LineChart.tsx +++ b/app/vmui/packages/vmui/src/components/Chart/Line/LineChart/LineChart.tsx @@ -41,6 +41,7 @@ export interface LineChartProps { layoutSize: ElementSize; height?: number; anomalyView?: boolean; + spanGaps?: boolean; } const LineChart: FC = ({ @@ -53,7 +54,8 @@ const LineChart: FC = ({ setPeriod, layoutSize, height, - anomalyView + anomalyView, + spanGaps = false }) => { const { isDarkTheme } = useAppState(); @@ -106,10 +108,10 @@ const LineChart: FC = ({ 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; diff --git a/app/vmui/packages/vmui/src/components/Configurators/GraphSettings/GraphSettings.tsx b/app/vmui/packages/vmui/src/components/Configurators/GraphSettings/GraphSettings.tsx index 6fbffe6b3..09f8fab19 100644 --- a/app/vmui/packages/vmui/src/components/Configurators/GraphSettings/GraphSettings.tsx +++ b/app/vmui/packages/vmui/src/components/Configurators/GraphSettings/GraphSettings.tsx @@ -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 = ({ yaxis, setYaxisLimits, toggleEnableLimits }) => { +const GraphSettings: FC = ({ yaxis, setYaxisLimits, toggleEnableLimits, spanGaps }) => { const popperRef = useRef(null); const buttonRef = useRef(null); @@ -55,6 +60,10 @@ const GraphSettings: FC = ({ yaxis, setYaxisLimits, toggleEn setYaxisLimits={setYaxisLimits} toggleEnableLimits={toggleEnableLimits} /> + diff --git a/app/vmui/packages/vmui/src/components/Configurators/GraphSettings/LinesConfigurator/LinesConfigurator.tsx b/app/vmui/packages/vmui/src/components/Configurators/GraphSettings/LinesConfigurator/LinesConfigurator.tsx new file mode 100644 index 000000000..cb8801c33 --- /dev/null +++ b/app/vmui/packages/vmui/src/components/Configurators/GraphSettings/LinesConfigurator/LinesConfigurator.tsx @@ -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 = ({ spanGaps, onChange }) => { + const { isMobile } = useDeviceDetect(); + + return
+ +
; +}; + +export default LinesConfigurator; diff --git a/app/vmui/packages/vmui/src/components/Configurators/GraphSettings/style.scss b/app/vmui/packages/vmui/src/components/Configurators/GraphSettings/style.scss index cacc4c133..01a003288 100644 --- a/app/vmui/packages/vmui/src/components/Configurators/GraphSettings/style.scss +++ b/app/vmui/packages/vmui/src/components/Configurators/GraphSettings/style.scss @@ -8,7 +8,7 @@ &__body { display: grid; - gap: $padding-small; + gap: $padding-large; padding: 0 $padding-global; } } diff --git a/app/vmui/packages/vmui/src/components/Views/GraphView/GraphView.tsx b/app/vmui/packages/vmui/src/components/Views/GraphView/GraphView.tsx index 622fb54f3..ede3833c3 100644 --- a/app/vmui/packages/vmui/src/components/Views/GraphView/GraphView.tsx +++ b/app/vmui/packages/vmui/src/components/Views/GraphView/GraphView.tsx @@ -41,6 +41,7 @@ export interface GraphViewProps { height?: number; isHistogram?: boolean; anomalyView?: boolean; + spanGaps?: boolean; } const GraphView: FC = ({ @@ -58,6 +59,7 @@ const GraphView: FC = ({ height, isHistogram, anomalyView, + spanGaps }) => { const { isMobile } = useDeviceDetect(); const { timezone } = useTimeState(); @@ -196,6 +198,7 @@ const GraphView: FC = ({ layoutSize={containerSize} height={height} anomalyView={anomalyView} + spanGaps={spanGaps} /> )} {isHistogram && ( diff --git a/app/vmui/packages/vmui/src/pages/CustomPanel/CustomPanelTabs/GraphTab.tsx b/app/vmui/packages/vmui/src/pages/CustomPanel/CustomPanelTabs/GraphTab.tsx index ba87299cb..05185bb24 100644 --- a/app/vmui/packages/vmui/src/pages/CustomPanel/CustomPanelTabs/GraphTab.tsx +++ b/app/vmui/packages/vmui/src/pages/CustomPanel/CustomPanelTabs/GraphTab.tsx @@ -20,7 +20,7 @@ type Props = { const GraphTab: FC = ({ 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 = ({ 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 = ({ isHistogram, graphData, controlsRef, anomalyView yaxis={yaxis} setYaxisLimits={setYaxisLimits} toggleEnableLimits={toggleEnableLimits} + spanGaps={{ value: spanGaps, onChange: setSpanGaps }} /> ); @@ -64,6 +69,7 @@ const GraphTab: FC = ({ isHistogram, graphData, controlsRef, anomalyView height={isMobile ? window.innerHeight * 0.5 : 500} isHistogram={isHistogram} anomalyView={anomalyView} + spanGaps={spanGaps} /> ); diff --git a/app/vmui/packages/vmui/src/pages/PredefinedPanels/PredefinedPanel/PredefinedPanel.tsx b/app/vmui/packages/vmui/src/pages/PredefinedPanels/PredefinedPanel/PredefinedPanel.tsx index e5ba735b8..268d51a93 100644 --- a/app/vmui/packages/vmui/src/pages/PredefinedPanels/PredefinedPanel/PredefinedPanel.tsx +++ b/app/vmui/packages/vmui/src/pages/PredefinedPanels/PredefinedPanel/PredefinedPanel.tsx @@ -34,6 +34,7 @@ const PredefinedPanel: FC = ({ const containerRef = useRef(null); const [visible, setVisible] = useState(false); + const [spanGaps, setSpanGaps] = useState(false); const [yaxis, setYaxis] = useState({ limits: { enable: false, @@ -121,6 +122,7 @@ const PredefinedPanel: FC = ({ yaxis={yaxis} setYaxisLimits={setYaxisLimits} toggleEnableLimits={toggleEnableLimits} + spanGaps={{ value: spanGaps, onChange: setSpanGaps }} />
@@ -140,6 +142,7 @@ const PredefinedPanel: FC = ({ setPeriod={setPeriod} fullWidth={false} height={isMobile ? window.innerHeight * 0.5 : 500} + spanGaps={spanGaps} /> }
diff --git a/app/vmui/packages/vmui/src/pages/QueryAnalyzer/QueryAnalyzerView/QueryAnalyzerView.tsx b/app/vmui/packages/vmui/src/pages/QueryAnalyzer/QueryAnalyzerView/QueryAnalyzerView.tsx index 9fd277fbd..aa7e4c7cd 100644 --- a/app/vmui/packages/vmui/src/pages/QueryAnalyzer/QueryAnalyzerView/QueryAnalyzerView.tsx +++ b/app/vmui/packages/vmui/src/pages/QueryAnalyzer/QueryAnalyzerView/QueryAnalyzerView.tsx @@ -49,7 +49,7 @@ const QueryAnalyzerView: FC = ({ 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 = ({ 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 = ({ data, period }) => { yaxis={yaxis} setYaxisLimits={setYaxisLimits} toggleEnableLimits={toggleEnableLimits} + spanGaps={{ value: spanGaps, onChange: setSpanGaps }} /> )} {displayType === "table" && ( @@ -161,6 +166,7 @@ const QueryAnalyzerView: FC = ({ data, period }) => { setPeriod={() => null} height={isMobile ? window.innerHeight * 0.5 : 500} isHistogram={isHistogram} + spanGaps={spanGaps} /> )} {liveData && (displayType === "code") && ( diff --git a/app/vmui/packages/vmui/src/state/graph/reducer.ts b/app/vmui/packages/vmui/src/state/graph/reducer.ts index 07a42c6c7..d343182f3 100644 --- a/app/vmui/packages/vmui/src/state/graph/reducer.ts +++ b/app/vmui/packages/vmui/src/state/graph/reducer.ts @@ -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(); } diff --git a/app/vmui/packages/vmui/src/utils/uplot/series.ts b/app/vmui/packages/vmui/src/utils/uplot/series.ts index 16163c8ff..ab94e0d4b 100644 --- a/app/vmui/packages/vmui/src/utils/uplot/series.ts +++ b/app/vmui/packages/vmui/src/utils/uplot/series.ts @@ -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); }); }; diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index e995d3d23..3c9bd1d90 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -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).