diff --git a/web/ui/mantine-ui/src/pages/query/Graph.tsx b/web/ui/mantine-ui/src/pages/query/Graph.tsx index a30d52c822..fda14210b9 100644 --- a/web/ui/mantine-ui/src/pages/query/Graph.tsx +++ b/web/ui/mantine-ui/src/pages/query/Graph.tsx @@ -25,6 +25,7 @@ export interface GraphProps { resolution: GraphResolution; showExemplars: boolean; displayMode: GraphDisplayMode; + yAxisMin: number | null; retriggerIdx: number; onSelectRange: (start: number, end: number) => void; } @@ -37,6 +38,7 @@ const Graph: FC = ({ resolution, showExemplars, displayMode, + yAxisMin, retriggerIdx, onSelectRange, }) => { @@ -222,6 +224,7 @@ const Graph: FC = ({ width={width} showExemplars={showExemplars} displayMode={displayMode} + yAxisMin={yAxisMin} 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 24eb8c59cb..5e41be7bb3 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,39 @@ const QueryPanel: FC = ({ idx, metricNames }) => { // }, ]} /> + + + + + + + + + dispatch( + setVisualizer({ + idx, + visualizer: { + ...panel.visualizer, + yAxisMin: event.currentTarget.checked ? 0 : null, + }, + }) + ) + } + /> + + @@ -301,6 +339,7 @@ const QueryPanel: FC = ({ idx, metricNames }) => { resolution={panel.visualizer.resolution} showExemplars={panel.visualizer.showExemplars} displayMode={panel.visualizer.displayMode} + yAxisMin={panel.visualizer.yAxisMin} 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 b3b2d75578..11e57f40f9 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; + yAxisMin: number | null; onSelectRange: (start: number, end: number) => void; } @@ -34,6 +35,7 @@ const UPlotChart: FC = ({ range: { startTime, endTime, resolution }, width, displayMode, + yAxisMin, onSelectRange, }) => { const [options, setOptions] = useState(null); @@ -60,6 +62,7 @@ const UPlotChart: FC = ({ width, data, useLocalTime, + yAxisMin, theme === "light", onSelectRange ); @@ -81,6 +84,7 @@ const UPlotChart: FC = ({ useLocalTime, theme, onSelectRange, + yAxisMin, ]); 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 3249afd454..ba6cdbae41 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, + yAxisMin: number | null, light: boolean, onSelectRange: (_start: number, _end: number) => void ): uPlot.Options => ({ @@ -330,6 +331,17 @@ export const getUPlotOptions = ( focus: { alpha: 1, }, + scales: + yAxisMin !== null + ? { + y: { + range: (_u, _min, max) => { + const minMax = uPlot.rangeNum(yAxisMin, max, 0.1, true); + return [yAxisMin, 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 ca9988e60d..18b63d9ed4 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("y_axis_min", (value) => { + panel.visualizer.yAxisMin = value === null ? null : parseFloat(value); + }); decodeSetting("show_exemplars", (value) => { panel.visualizer.showExemplars = value === "1"; }); @@ -171,6 +174,11 @@ export const encodePanelOptionsToURLParams = ( } addParam(idx, "display_mode", p.visualizer.displayMode); + addParam( + idx, + "y_axis_min", + p.visualizer.yAxisMin === null ? "" : p.visualizer.yAxisMin.toString() + ); 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 253b3ee9c6..4cf483e2b6 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; + yAxisMin: number | null; } export type Panel = { @@ -86,6 +87,7 @@ export const newDefaultPanel = (): Panel => ({ resolution: { type: "auto", density: "medium" }, displayMode: GraphDisplayMode.Lines, showExemplars: false, + yAxisMin: null, }, });