diff --git a/web/ui/mantine-ui/src/pages/query/Graph.tsx b/web/ui/mantine-ui/src/pages/query/Graph.tsx index d037546cf..d833ac057 100644 --- a/web/ui/mantine-ui/src/pages/query/Graph.tsx +++ b/web/ui/mantine-ui/src/pages/query/Graph.tsx @@ -24,6 +24,7 @@ export interface GraphProps { resolution: GraphResolution; showExemplars: boolean; displayMode: GraphDisplayMode; + startYAtZero: boolean; retriggerIdx: number; onSelectRange: (start: number, end: number) => void; } @@ -36,6 +37,7 @@ const Graph: FC = ({ resolution, showExemplars, displayMode, + startYAtZero, retriggerIdx, onSelectRange, }) => { @@ -182,6 +184,7 @@ const Graph: FC = ({ width={width} showExemplars={showExemplars} displayMode={displayMode} + startYAtZero={startYAtZero} onSelectRange={onSelectRange} /> diff --git a/web/ui/mantine-ui/src/pages/query/QueryPanel.tsx b/web/ui/mantine-ui/src/pages/query/QueryPanel.tsx index 24eb8c59c..64bf22e9c 100644 --- a/web/ui/mantine-ui/src/pages/query/QueryPanel.tsx +++ b/web/ui/mantine-ui/src/pages/query/QueryPanel.tsx @@ -7,8 +7,12 @@ import { SegmentedControl, Stack, Skeleton, + ActionIcon, + Popover, + Checkbox, } from "@mantine/core"; import { + IconAdjustmentsHorizontal, IconChartAreaFilled, IconChartLine, IconGraph, @@ -37,6 +41,7 @@ import ErrorBoundary from "../../components/ErrorBoundary"; import ASTNode from "../../promql/ast"; import serializeNode from "../../promql/serialize"; import ExplainView from "./ExplainViews/ExplainView"; +import { actionIconStyle } from "../../styles"; export interface PanelProps { idx: number; @@ -290,6 +295,34 @@ const QueryPanel: FC = ({ idx, metricNames }) => { // }, ]} /> + + + + + + + + + dispatch( + setVisualizer({ + idx, + visualizer: { + ...panel.visualizer, + startYAtZero: event.currentTarget.checked, + }, + }) + ) + } + /> + + @@ -301,6 +334,7 @@ const QueryPanel: FC = ({ idx, metricNames }) => { resolution={panel.visualizer.resolution} showExemplars={panel.visualizer.showExemplars} displayMode={panel.visualizer.displayMode} + startYAtZero={panel.visualizer.startYAtZero} retriggerIdx={retriggerIdx} onSelectRange={onSelectRange} /> diff --git a/web/ui/mantine-ui/src/pages/query/UPlotChart.tsx b/web/ui/mantine-ui/src/pages/query/UPlotChart.tsx index f105cb54d..3be730883 100644 --- a/web/ui/mantine-ui/src/pages/query/UPlotChart.tsx +++ b/web/ui/mantine-ui/src/pages/query/UPlotChart.tsx @@ -24,6 +24,7 @@ export interface UPlotChartProps { width: number; showExemplars: boolean; displayMode: GraphDisplayMode; + startYAtZero: boolean; onSelectRange: (start: number, end: number) => void; } @@ -34,6 +35,7 @@ const UPlotChart: FC = ({ range: { startTime, endTime, resolution }, width, displayMode, + startYAtZero, onSelectRange, }) => { const [options, setOptions] = useState(null); @@ -60,6 +62,7 @@ const UPlotChart: FC = ({ width, data, useLocalTime, + startYAtZero, theme === "light", onSelectRange ); @@ -81,6 +84,7 @@ const UPlotChart: FC = ({ useLocalTime, theme, onSelectRange, + startYAtZero, ]); if (options === null || processedData === null) { diff --git a/web/ui/mantine-ui/src/pages/query/uPlotChartHelpers.ts b/web/ui/mantine-ui/src/pages/query/uPlotChartHelpers.ts index b7a81831c..aabcb1529 100644 --- a/web/ui/mantine-ui/src/pages/query/uPlotChartHelpers.ts +++ b/web/ui/mantine-ui/src/pages/query/uPlotChartHelpers.ts @@ -289,6 +289,7 @@ export const getUPlotOptions = ( width: number, result: RangeSamples[], useLocalTime: boolean, + startYAtZero: boolean, light: boolean, onSelectRange: (_start: number, _end: number) => void ): uPlot.Options => ({ @@ -329,6 +330,16 @@ export const getUPlotOptions = ( focus: { alpha: 1, }, + scales: startYAtZero + ? { + y: { + range: (_u, _min, max) => { + const minMax = uPlot.rangeNum(0, max, 0.1, true); + return [0, minMax[1]]; + }, + }, + } + : undefined, axes: [ // X axis (time). { diff --git a/web/ui/mantine-ui/src/pages/query/urlStateEncoding.ts b/web/ui/mantine-ui/src/pages/query/urlStateEncoding.ts index f948205a1..1e7841cff 100644 --- a/web/ui/mantine-ui/src/pages/query/urlStateEncoding.ts +++ b/web/ui/mantine-ui/src/pages/query/urlStateEncoding.ts @@ -63,6 +63,9 @@ export const decodePanelOptionsFromURLParams = (query: string): Panel[] => { panel.visualizer.displayMode = value === "1" ? GraphDisplayMode.Stacked : GraphDisplayMode.Lines; }); + decodeSetting("start_y_at_zero", (value) => { + panel.visualizer.startYAtZero = value === "1"; + }); decodeSetting("show_exemplars", (value) => { panel.visualizer.showExemplars = value === "1"; }); @@ -171,6 +174,7 @@ export const encodePanelOptionsToURLParams = ( } addParam(idx, "display_mode", p.visualizer.displayMode); + addParam(idx, "start_y_at_zero", p.visualizer.startYAtZero ? "1" : "0"); addParam(idx, "show_exemplars", p.visualizer.showExemplars ? "1" : "0"); }); diff --git a/web/ui/mantine-ui/src/state/queryPageSlice.ts b/web/ui/mantine-ui/src/state/queryPageSlice.ts index 253b3ee9c..1e5654d8b 100644 --- a/web/ui/mantine-ui/src/state/queryPageSlice.ts +++ b/web/ui/mantine-ui/src/state/queryPageSlice.ts @@ -58,6 +58,7 @@ export interface Visualizer { resolution: GraphResolution; displayMode: GraphDisplayMode; showExemplars: boolean; + startYAtZero: boolean; } export type Panel = { @@ -86,6 +87,7 @@ export const newDefaultPanel = (): Panel => ({ resolution: { type: "auto", density: "medium" }, displayMode: GraphDisplayMode.Lines, showExemplars: false, + startYAtZero: false, }, });