From ff301cf5de29dc451bb18e9317556b623e2f4430 Mon Sep 17 00:00:00 2001 From: Greg Harris Date: Tue, 26 May 2026 17:43:51 +0100 Subject: [PATCH 1/8] Handle autoscale and xAxis settings --- src/ui/widgets/EmbeddedDisplay/bobParser.ts | 1 + src/ui/widgets/XYPlot/xyPlot.tsx | 20 ++++-- src/ui/widgets/XYPlot/xyPlot.utilities.ts | 73 +++++++-------------- 3 files changed, 39 insertions(+), 55 deletions(-) diff --git a/src/ui/widgets/EmbeddedDisplay/bobParser.ts b/src/ui/widgets/EmbeddedDisplay/bobParser.ts index bf4d8c5..1481a93 100644 --- a/src/ui/widgets/EmbeddedDisplay/bobParser.ts +++ b/src/ui/widgets/EmbeddedDisplay/bobParser.ts @@ -607,6 +607,7 @@ export const BOB_SIMPLE_PARSERS: ParserDict = { majorTickStepHint: ["major_tick_step_hint", bobParseNumber], maximum: ["maximum", bobParseNumber], minimum: ["minimum", bobParseNumber], + autoscale: ["autoscale", opiParseBoolean], format: ["format", bobParseNumber], emptyColor: ["empty_color", opiParseColor], knobColor: ["knob_color", opiParseColor], diff --git a/src/ui/widgets/XYPlot/xyPlot.tsx b/src/ui/widgets/XYPlot/xyPlot.tsx index 5bcd9e6..d7f5ba5 100644 --- a/src/ui/widgets/XYPlot/xyPlot.tsx +++ b/src/ui/widgets/XYPlot/xyPlot.tsx @@ -11,7 +11,8 @@ import { AxesProp, ArchivedDataPropOpt, IntPropOpt, - TracesPropOpt + TracesPropOpt, + AxisProp } from "../propTypes"; import { registerWidget } from "../register"; import { Box, Typography } from "@mui/material"; @@ -24,7 +25,7 @@ import { LinePlot, MarkPlot } from "@mui/x-charts"; -import { Axes } from "../../../types/axis"; +import { Axes, Axis } from "../../../types/axis"; import { fontToCss, newFont } from "../../../types/font"; import { useStyle } from "../../hooks/useStyle"; import { DatasetElementType } from "@mui/x-charts/internals"; @@ -40,6 +41,7 @@ const widgetName = "xyplot"; const XYPlotProps = { traces: TracesPropOpt, axes: AxesProp, + xAxis: AxisProp, start: StringPropOpt, end: StringPropOpt, foregroundColor: ColorPropOpt, @@ -73,6 +75,7 @@ export const XYPlotComponent = (props: XYPlotComponentProps): JSX.Element => { const { traces, axes, + xAxis, pvData, title, titleFont = newFont(), @@ -83,10 +86,14 @@ export const XYPlotComponent = (props: XYPlotComponentProps): JSX.Element => { } = props; const { yAxes, yAxesStyle } = useMemo(() => buildYAxes(axes as Axes), [axes]); - const { xAxis, hasXAxisData } = useMemo( - () => buildXAxes(traces, style, pvData), - [traces, style, pvData] + const { xAxis: xAxisMui, hasXAxisData } = useMemo( + () => buildXAxes(traces, style, xAxis as Axis), + [traces, style, xAxis] ); + + console.log(xAxis); + console.log(xAxisMui); + const series = useMemo( () => buildSeries(traces, pvData, visible), [traces, pvData, visible] @@ -96,6 +103,7 @@ export const XYPlotComponent = (props: XYPlotComponentProps): JSX.Element => { () => buildPlotDataSet(pvData), [pvData] ); + if (!hasXAxisData) { plotDataSet = plotDataSet.map((point, i) => ({ ...point, x: i })); } @@ -120,7 +128,7 @@ export const XYPlotComponent = (props: XYPlotComponentProps): JSX.Element => { skipAnimation dataset={plotDataSet} series={series} - xAxis={xAxis} + xAxis={xAxisMui} yAxis={yAxes} sx={{ width: "100%", diff --git a/src/ui/widgets/XYPlot/xyPlot.utilities.ts b/src/ui/widgets/XYPlot/xyPlot.utilities.ts index 5125640..a7ece70 100644 --- a/src/ui/widgets/XYPlot/xyPlot.utilities.ts +++ b/src/ui/widgets/XYPlot/xyPlot.utilities.ts @@ -12,7 +12,7 @@ import { CurveType } from "@mui/x-charts"; import { Trace } from "../../../types/trace"; import { UseStyleResult } from "../../hooks/useStyle"; import { getPvValueByPvName } from "../utils"; -import { Axes, newAxis } from "../../../types/axis"; +import { Axes, Axis, newAxis } from "../../../types/axis"; import { fontToCss } from "../../../types/font"; const MARKER_STYLES: (MarkShape | undefined)[] = [ @@ -102,56 +102,31 @@ export const buildSeries = ( export const buildXAxes = ( traces: (Trace | null | undefined)[] | undefined, style: UseStyleResult, - pvData: PvDatum[] + xAxisDefinition: Axis ) => { - const xAxisPvNamesAndIds = ( - traces != null && traces?.length > 0 ? traces : [new Trace()] - ) - ?.filter(trace => trace != null && trace?.xPv != null) - ?.map(trace => { - const { value } = getPvValueByPvName(pvData, trace?.xPv as string); - const controlRange = value?.display?.controlRange; - const pvMin = Number.isNaN(Number(controlRange?.min)) - ? undefined - : controlRange?.min; - const pvMax = Number.isNaN(Number(controlRange?.max)) - ? undefined - : controlRange?.max; - - return { - pvName: trace?.xPv, - axisId: trace?.axis, - pvMin, - pvMax, - scaleType: trace?.traceType === 5 ? "band" : "linear" - }; - }) - ?.reduce((acc, curr) => { - if (!acc.find(item => item.axisId === curr.axisId)) { - acc.push(curr); - } - return acc; - }, [] as any[]); + const isBarChart = traces + ?.some(trace => trace?.traceType === 5); - const hasXAxisData = xAxisPvNamesAndIds.length > 0; - if (!hasXAxisData) { - xAxisPvNamesAndIds.push({ pvName: "x", axisId: 0, scaleType: "band" }); - } - - const xAxis: ReadonlyArray> = xAxisPvNamesAndIds?.map( - xAxisData => { - return { - color: style?.colors?.color, - dataKey: xAxisData.pvName, - id: `${xAxisData?.axisId}`, - min: xAxisData?.pvMin, - max: xAxisData?.pvMax, - scaleType: xAxisData?.scaleType - }; - } - ); - - return { xAxis, hasXAxisData }; + const dataKey = traces + ?.filter(trace => trace != null && trace?.xPv != null) + ?.[0] + ?.xPv; + + const xAxis: ReadonlyArray> = [{ + color: style?.colors?.color, + dataKey: dataKey ?? "x", + id: "0", + label: xAxisDefinition.title, + scaleType: !isBarChart ? (xAxisDefinition.logScale ? "symlog" : "linear") : "band", + min: !xAxisDefinition?.autoscale && Number.isFinite(xAxisDefinition?.minimum) + ? xAxisDefinition?.minimum + : undefined, + max: !xAxisDefinition?.autoscale && Number.isFinite(xAxisDefinition?.maximum) + ? xAxisDefinition?.maximum + : undefined + }]; + + return { xAxis, hasXAxisData: !!dataKey}; }; export const buildYAxes = ( From 951cdf2a83cecad8abd111a0594c55ba11e1f956 Mon Sep 17 00:00:00 2001 From: Greg Harris Date: Wed, 27 May 2026 16:54:20 +0100 Subject: [PATCH 2/8] Improving axes styling, supporting log scales, adjusting visibility. --- src/ui/widgets/XYPlot/xyPlot.tsx | 153 ++++++++++++---------- src/ui/widgets/XYPlot/xyPlot.utilities.ts | 66 ++++++---- 2 files changed, 124 insertions(+), 95 deletions(-) diff --git a/src/ui/widgets/XYPlot/xyPlot.tsx b/src/ui/widgets/XYPlot/xyPlot.tsx index d7f5ba5..d989fad 100644 --- a/src/ui/widgets/XYPlot/xyPlot.tsx +++ b/src/ui/widgets/XYPlot/xyPlot.tsx @@ -17,13 +17,16 @@ import { import { registerWidget } from "../register"; import { Box, Typography } from "@mui/material"; import { - ChartsContainer, BarPlot, ChartsLegend, ChartsXAxis, ChartsYAxis, LinePlot, - MarkPlot + MarkPlot, + ChartsTooltip, + ChartsAxisHighlight, + ChartsSurface, + ChartsDataProvider } from "@mui/x-charts"; import { Axes, Axis } from "../../../types/axis"; import { fontToCss, newFont } from "../../../types/font"; @@ -81,7 +84,7 @@ export const XYPlotComponent = (props: XYPlotComponentProps): JSX.Element => { titleFont = newFont(), scaleFont = newFont(), labelFont = newFont(), - showLegend = false, + showLegend = true, visible = true } = props; @@ -91,9 +94,6 @@ export const XYPlotComponent = (props: XYPlotComponentProps): JSX.Element => { [traces, style, xAxis] ); - console.log(xAxis); - console.log(xAxisMui); - const series = useMemo( () => buildSeries(traces, pvData, visible), [traces, pvData, visible] @@ -108,9 +108,20 @@ export const XYPlotComponent = (props: XYPlotComponentProps): JSX.Element => { plotDataSet = plotDataSet.map((point, i) => ({ ...point, x: i })); } + if (!visible) { + return ; + } + // Use end value - this doesn't seem to do anything in Phoebus? return ( - + { > {title} - - {plotDataSet?.length > 0 && ( - - - { - const trace = traces?.[Number(seriesId)]; - // this hides the line if no line should be visible - if (trace?.traceType === 0) { - return { - stroke: "transparent" - }; - } - return {}; - } - }} - /> - - - - - {showLegend && ( - + {plotDataSet?.length > 0 && ( + + - )} - - )} + > + + + + { + const trace = traces?.[Number(seriesId)]; + // this hides the line if no line should be visible + if (trace?.traceType === 0) { + return { + stroke: "transparent" + }; + } + return {}; + } + }} + /> + + {xAxis?.visible !== false && } + {yAxes.map(axis => + axis.visible !== false ? ( + + ) : null + )} + + + {showLegend && ( + + )} + + )} + ); }; diff --git a/src/ui/widgets/XYPlot/xyPlot.utilities.ts b/src/ui/widgets/XYPlot/xyPlot.utilities.ts index a7ece70..317fb32 100644 --- a/src/ui/widgets/XYPlot/xyPlot.utilities.ts +++ b/src/ui/widgets/XYPlot/xyPlot.utilities.ts @@ -11,7 +11,6 @@ import { dTypeCoerceArray } from "../../../types/dtypes"; import { CurveType } from "@mui/x-charts"; import { Trace } from "../../../types/trace"; import { UseStyleResult } from "../../hooks/useStyle"; -import { getPvValueByPvName } from "../utils"; import { Axes, Axis, newAxis } from "../../../types/axis"; import { fontToCss } from "../../../types/font"; @@ -72,6 +71,7 @@ export const buildSeries = ( const base = { id: `${index}`, dataKey: effectiveYPvName, + yAxisId: String(trace?.axis), label: trace.name || `Series ${index + 1}`, color: visible ? trace.color.colorString : "transparent" }; @@ -104,29 +104,35 @@ export const buildXAxes = ( style: UseStyleResult, xAxisDefinition: Axis ) => { - const isBarChart = traces - ?.some(trace => trace?.traceType === 5); - - const dataKey = traces - ?.filter(trace => trace != null && trace?.xPv != null) - ?.[0] - ?.xPv; - - const xAxis: ReadonlyArray> = [{ - color: style?.colors?.color, - dataKey: dataKey ?? "x", - id: "0", - label: xAxisDefinition.title, - scaleType: !isBarChart ? (xAxisDefinition.logScale ? "symlog" : "linear") : "band", - min: !xAxisDefinition?.autoscale && Number.isFinite(xAxisDefinition?.minimum) - ? xAxisDefinition?.minimum - : undefined, - max: !xAxisDefinition?.autoscale && Number.isFinite(xAxisDefinition?.maximum) - ? xAxisDefinition?.maximum - : undefined - }]; - - return { xAxis, hasXAxisData: !!dataKey}; + const isBarChart = traces?.some(trace => trace?.traceType === 5); + + const dataKey = traces?.filter( + trace => trace != null && trace?.xPv != null + )?.[0]?.xPv; + + const xAxis: ReadonlyArray> = [ + { + color: style?.colors?.color, + dataKey: dataKey ?? "x", + id: "X0", + label: xAxisDefinition.title, + scaleType: !isBarChart + ? xAxisDefinition.logScale + ? "symlog" + : "linear" + : "band", + min: + !xAxisDefinition?.autoscale && Number.isFinite(xAxisDefinition?.minimum) + ? xAxisDefinition?.minimum + : undefined, + max: + !xAxisDefinition?.autoscale && Number.isFinite(xAxisDefinition?.maximum) + ? xAxisDefinition?.maximum + : undefined + } + ]; + + return { xAxis, hasXAxisData: !!dataKey }; }; export const buildYAxes = ( @@ -143,16 +149,16 @@ export const buildYAxes = ( const yAxesStyle: { [k: string]: { [k2: string]: { stroke: string } } } = Object.fromEntries( localAxes.map(({ color }, idx) => [ - `.MuiChartsAxis-id-${idx}`, + `& .MuiChartsAxis-root[data-axis-id="${idx}"]`, { - ".MuiChartsAxis-line": { stroke: color.colorString }, - ".MuiChartsAxis-tick": { stroke: color.colorString } + "& .MuiChartsAxis-line": { stroke: `${color.colorString}` }, + "& .MuiChartsAxis-tick": { stroke: `${color.colorString}` } } ]) ); const yAxes: ReadonlyArray> = localAxes.map( - (item, idx): YAxis => { + (item, idy): YAxis => { const titleFont = fontToCss(item.titleFont); const scaleFont = fontToCss(item.scaleFont); @@ -173,10 +179,12 @@ export const buildYAxes = ( min !== undefined && max !== undefined && min >= max ? undefined : max; return { + visible: item?.visible, width: 55, - id: idx, + id: String(idy), label: item.title, color: item.color?.colorString, + lineColor: item.color?.colorString, labelStyle: { ...titleFont, fill: item.color.colorString From 9caf937d640d046f69df463bd833cde62d99be2b Mon Sep 17 00:00:00 2001 From: Greg Harris Date: Wed, 27 May 2026 17:28:11 +0100 Subject: [PATCH 3/8] Fix tests --- src/ui/widgets/XYPlot/xyPlot.test.tsx | 15 ++++-- .../widgets/XYPlot/xyPlot.utilities.test.ts | 50 ++++++------------- 2 files changed, 26 insertions(+), 39 deletions(-) diff --git a/src/ui/widgets/XYPlot/xyPlot.test.tsx b/src/ui/widgets/XYPlot/xyPlot.test.tsx index 848aec0..722fdb6 100644 --- a/src/ui/widgets/XYPlot/xyPlot.test.tsx +++ b/src/ui/widgets/XYPlot/xyPlot.test.tsx @@ -9,9 +9,15 @@ import { useStyle } from "../../hooks/useStyle"; const linePlotMock = vi.fn(); vi.mock("@mui/x-charts", () => ({ - ChartsContainer: ({ children }: any) => ( -
{children}
+ ChartsDataProvider: ({ children }: any) => ( +
{children}
), + ChartsSurface: ({ children }: any) => ( +
{children}
+ ), + ChartsTooltip: () =>
, + ChartsAxisHighlight: () =>
, + BarPlot: () =>
, LinePlot: (props: any) => { linePlotMock(props); @@ -76,7 +82,8 @@ describe("XYPlotComponent", () => { it("renders chart when dataset exists", () => { render(); - expect(screen.getByTestId("charts-container")).toBeInTheDocument(); + expect(screen.getByTestId("charts-provider")).toBeInTheDocument(); + expect(screen.getByTestId("charts-surface")).toBeInTheDocument(); expect(screen.getByTestId("bar-plot")).toBeInTheDocument(); expect(screen.getByTestId("line-plot")).toBeInTheDocument(); expect(screen.getByTestId("mark-plot")).toBeInTheDocument(); @@ -97,7 +104,7 @@ describe("XYPlotComponent", () => { expect(utils.buildXAxes).toHaveBeenCalledWith( baseProps.traces, mockStyle, - baseProps.pvData + baseProps.xAxis ); expect(utils.buildSeries).toHaveBeenCalledWith( baseProps.traces, diff --git a/src/ui/widgets/XYPlot/xyPlot.utilities.test.ts b/src/ui/widgets/XYPlot/xyPlot.utilities.test.ts index 094fa63..36befb0 100644 --- a/src/ui/widgets/XYPlot/xyPlot.utilities.test.ts +++ b/src/ui/widgets/XYPlot/xyPlot.utilities.test.ts @@ -6,8 +6,8 @@ import { buildYAxes } from "./xyPlot.utilities"; -import { getPvValueByPvName } from "../utils"; import { Trace } from "../../../types/trace"; +import { Axis } from "../../../types/axis"; vi.mock("../../../types/dtypes", () => ({ dTypeCoerceArray: vi.fn(val => val) @@ -130,17 +130,18 @@ describe("buildXAxes", () => { }); it("builds axis from trace with pv min/max", () => { - (getPvValueByPvName as any).mockReturnValue({ - value: { - display: { - controlRange: { min: 0, max: 100 } - } - } - }); - const traces = [{ xPv: "time", axis: 0, traceType: 1 }] as any; - - const result = buildXAxes(traces, style, []); + const xAxis = { + title: "X title", + color: { colorString: "red" }, + autoscale: false, + onRight: false, + logScale: false, + minimum: 0, + maximum: 100 + } as Axis; + + const result = buildXAxes(traces, style, xAxis); expect(result.xAxis[0]).toMatchObject({ dataKey: "time", @@ -150,29 +151,6 @@ describe("buildXAxes", () => { }); expect(result.hasXAxisData).toBe(true); }); - - it("deduplicates axes by axisId", () => { - (getPvValueByPvName as any).mockReturnValue({ value: {} }); - - const traces = [ - { xPv: "time", axis: 0, traceType: 1 }, - { xPv: "time2", axis: 0, traceType: 1 } - ] as any; - - const result = buildXAxes(traces, style, []); - - expect(result.xAxis.length).toBe(1); - }); - - it("uses default axis when none exist", () => { - const result = buildXAxes([], style, []); - - expect(result.hasXAxisData).toBe(false); - expect(result.xAxis[0]).toMatchObject({ - dataKey: "x", - scaleType: "band" - }); - }); }); describe("buildYAxes", () => { @@ -195,7 +173,9 @@ describe("buildYAxes", () => { position: "left" }); - expect(result.yAxesStyle[".MuiChartsAxis-id-0"]).toBeDefined(); + expect( + result.yAxesStyle['& .MuiChartsAxis-root[data-axis-id="0"]'] + ).toBeDefined(); }); it("applies min/max when valid", () => { From ed089c502eec0d724480defd332c02f48fb0170a Mon Sep 17 00:00:00 2001 From: Greg Harris Date: Wed, 27 May 2026 17:48:46 +0100 Subject: [PATCH 4/8] Test improvements --- .../widgets/XYPlot/xyPlot.utilities.test.ts | 106 ++++++++++++++++++ 1 file changed, 106 insertions(+) diff --git a/src/ui/widgets/XYPlot/xyPlot.utilities.test.ts b/src/ui/widgets/XYPlot/xyPlot.utilities.test.ts index 36befb0..8145d42 100644 --- a/src/ui/widgets/XYPlot/xyPlot.utilities.test.ts +++ b/src/ui/widgets/XYPlot/xyPlot.utilities.test.ts @@ -61,6 +61,25 @@ describe("buildPlotDataSet", () => { it("returns empty array when no valid data", () => { expect(buildPlotDataSet([])).toEqual([]); }); + + it("coerces values to numbers", () => { + const pvData = [{ effectivePvName: "a", value: ["1", "2"] }] as any; + + const result = buildPlotDataSet(pvData); + + expect(result).toEqual([{ a: 1 }, { a: 2 }]); + }); + + it("ignores entries with missing effectivePvName", () => { + const pvData = [ + { value: [1, 2] }, // invalid + { effectivePvName: "a", value: [3, 4] } + ] as any; + + const result = buildPlotDataSet(pvData); + + expect(result).toEqual([{ a: 3 }, { a: 4 }]); + }); }); describe("buildSeries", () => { @@ -120,6 +139,35 @@ describe("buildSeries", () => { const result = buildSeries(undefined, [], true); expect(result).toBeDefined(); }); + + it("applies marker shape correctly", () => { + const trace = { + ...baseTrace, + pointType: 3 // diamond + }; + + const result = buildSeries([trace], pvData, true); + + expect(result[0]).toMatchObject({ + shape: "diamond" + }); + }); + + it("uses default label when name is missing", () => { + const trace = { ...baseTrace, name: "" }; + + const result = buildSeries([trace], pvData, true); + + expect(result[0].label).toBe("Series 1"); + }); + + it("sets yAxisId correctly", () => { + const trace = { ...baseTrace, axis: 2 }; + + const result = buildSeries([trace], pvData, true); + + expect(result[0].yAxisId).toBe("2"); + }); }); describe("buildXAxes", () => { @@ -151,6 +199,28 @@ describe("buildXAxes", () => { }); expect(result.hasXAxisData).toBe(true); }); + + it("defaults dataKey to x when none provided", () => { + const result = buildXAxes( + [], + { colors: { color: "black" } } as any, + {} as any + ); + + expect(result.xAxis[0].dataKey).toBe("x"); + expect(result.hasXAxisData).toBe(false); + }); + + it("uses band scale for bar charts", () => { + const traces = [{ traceType: 5 }] as any; + const mockStyle = { + colors: { color: "black" } + } as any; + + const result = buildXAxes(traces, mockStyle, {} as any); + + expect(result.xAxis[0].scaleType).toBe("band"); + }); }); describe("buildYAxes", () => { @@ -259,4 +329,40 @@ describe("buildYAxes", () => { expect(result.yAxes.length).toBe(1); expect(result.yAxes[0].label).toBe("Default"); }); + + it("propagates visible flag", () => { + const axes = [ + { + title: "Y", + color: { colorString: "black" }, + visible: false, + autoscale: true, + onRight: false, + logScale: false + } + ] as any; + + const result = buildYAxes(axes); + + expect(result.yAxes[0].visible).toBe(false); + }); + + it("applies font styles", () => { + const axes = [ + { + title: "Y", + color: { colorString: "black" }, + titleFont: {}, + scaleFont: {}, + autoscale: true, + onRight: false, + logScale: false + } + ] as any; + + const result = buildYAxes(axes); + + expect(result.yAxes[0].labelStyle).toBeDefined(); + expect(result.yAxes[0].tickLabelStyle).toBeDefined(); + }); }); From 122b03029a502c32f34735fb8f95e42f42e0be4d Mon Sep 17 00:00:00 2001 From: Greg Harris Date: Thu, 28 May 2026 09:55:28 +0100 Subject: [PATCH 5/8] Fix legend positioning --- src/ui/widgets/XYPlot/xyPlot.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/ui/widgets/XYPlot/xyPlot.tsx b/src/ui/widgets/XYPlot/xyPlot.tsx index d989fad..2adea49 100644 --- a/src/ui/widgets/XYPlot/xyPlot.tsx +++ b/src/ui/widgets/XYPlot/xyPlot.tsx @@ -133,7 +133,7 @@ export const XYPlotComponent = (props: XYPlotComponentProps): JSX.Element => { > {title} - + {plotDataSet?.length > 0 && ( { Date: Thu, 28 May 2026 09:57:08 +0100 Subject: [PATCH 6/8] bump version number --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index ab4b6c0..d8046d6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@diamondlightsource/cs-web-lib", - "version": "0.10.14", + "version": "0.10.15", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@diamondlightsource/cs-web-lib", - "version": "0.10.14", + "version": "0.10.15", "license": "ISC", "dependencies": { "@mui/icons-material": "^7.3.7", diff --git a/package.json b/package.json index 67fe943..b66c25a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@diamondlightsource/cs-web-lib", - "version": "0.10.14", + "version": "0.10.15", "description": "Control system web library", "main": "./dist/index.cjs", "scripts": { From bb4f7ca3a1c116b231fba2689ca02ba9ea82a0e7 Mon Sep 17 00:00:00 2001 From: Greg Harris Date: Thu, 28 May 2026 14:07:01 +0100 Subject: [PATCH 7/8] Fix sizing of the graph display area --- src/ui/widgets/XYPlot/xyPlot.tsx | 117 +++++++++++++++++-------------- 1 file changed, 65 insertions(+), 52 deletions(-) diff --git a/src/ui/widgets/XYPlot/xyPlot.tsx b/src/ui/widgets/XYPlot/xyPlot.tsx index 2adea49..db4f830 100644 --- a/src/ui/widgets/XYPlot/xyPlot.tsx +++ b/src/ui/widgets/XYPlot/xyPlot.tsx @@ -133,7 +133,9 @@ export const XYPlotComponent = (props: XYPlotComponentProps): JSX.Element => { > {title} - + {plotDataSet?.length > 0 && ( { xAxis={xAxisMui} yAxis={yAxes} > - - - - - { - const trace = traces?.[Number(seriesId)]; - // this hides the line if no line should be visible - if (trace?.traceType === 0) { - return { - stroke: "transparent" - }; - } - return {}; - } + - - {xAxis?.visible !== false && } - {yAxes.map(axis => - axis.visible !== false ? ( - - ) : null + > + + + + { + const trace = traces?.[Number(seriesId)]; + // this hides the line if no line should be visible + if (trace?.traceType === 0) { + return { + stroke: "transparent" + }; + } + return {}; + } + }} + /> + + {xAxis?.visible !== false && } + {yAxes.map(axis => + axis.visible !== false ? ( + + ) : null + )} + + + {showLegend && ( + )} - - - {showLegend && ( - - )} + )} From 1902a5f738c1e7241d6e1606a4180442a4de3da3 Mon Sep 17 00:00:00 2001 From: Greg Harris Date: Thu, 28 May 2026 15:59:04 +0100 Subject: [PATCH 8/8] Hide lines when stroke type is 3 --- src/ui/widgets/XYPlot/xyPlot.test.tsx | 5 +++-- src/ui/widgets/XYPlot/xyPlot.tsx | 7 ++++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/ui/widgets/XYPlot/xyPlot.test.tsx b/src/ui/widgets/XYPlot/xyPlot.test.tsx index 722fdb6..7b61b9c 100644 --- a/src/ui/widgets/XYPlot/xyPlot.test.tsx +++ b/src/ui/widgets/XYPlot/xyPlot.test.tsx @@ -161,8 +161,8 @@ describe("XYPlotComponent", () => { expect(typeof props.slotProps.line).toBe("function"); }); - it("hides line when traceType is 0", () => { - const traces = [{ traceType: 0 }, { traceType: 1 }]; + it("hides line when traceType is 0 or 3", () => { + const traces = [{ traceType: 0 }, { traceType: 1 }, { traceType: 3 }]; render(); @@ -171,5 +171,6 @@ describe("XYPlotComponent", () => { expect(lineFn({ seriesId: "0" })).toEqual({ stroke: "transparent" }); expect(lineFn({ seriesId: "1" })).toEqual({}); + expect(lineFn({ seriesId: "2" })).toEqual({ stroke: "transparent" }); }); }); diff --git a/src/ui/widgets/XYPlot/xyPlot.tsx b/src/ui/widgets/XYPlot/xyPlot.tsx index db4f830..ef9e2ab 100644 --- a/src/ui/widgets/XYPlot/xyPlot.tsx +++ b/src/ui/widgets/XYPlot/xyPlot.tsx @@ -41,6 +41,8 @@ import { const widgetName = "xyplot"; +const traceTypesWithoutLines = [0, 3]; + const XYPlotProps = { traces: TracesPropOpt, axes: AxesProp, @@ -181,7 +183,10 @@ export const XYPlotComponent = (props: XYPlotComponentProps): JSX.Element => { line: ({ seriesId }) => { const trace = traces?.[Number(seriesId)]; // this hides the line if no line should be visible - if (trace?.traceType === 0) { + if ( + trace?.traceType != null && + traceTypesWithoutLines.includes(trace.traceType) + ) { return { stroke: "transparent" };