td-plots 1.11.0 → 1.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -14,4 +14,7 @@ export type LegendBoxPlotItemProps = LegendItemProps & {
14
14
  };
15
15
  export declare const LegendBoxPlotItem: (props: LegendBoxPlotItemProps) => import("react/jsx-runtime").JSX.Element;
16
16
  export declare const LegendColorItem: (props: LegendItemProps) => import("react/jsx-runtime").JSX.Element;
17
- export declare const LegendLineItem: (props: LegendItemProps) => import("react/jsx-runtime").JSX.Element;
17
+ export type LegendLineItemProps = LegendItemProps & {
18
+ strokeDasharray?: string;
19
+ };
20
+ export declare const LegendLineItem: (props: LegendLineItemProps) => import("react/jsx-runtime").JSX.Element;
@@ -18,6 +18,7 @@ export interface SplitBoxPlotProps {
18
18
  text?: string;
19
19
  color?: string;
20
20
  }[];
21
+ yTitleLocationOverride?: number;
21
22
  }
22
23
  declare const SplitBoxPlot: (props: SplitBoxPlotProps) => import("react/jsx-runtime").JSX.Element;
23
24
  export default SplitBoxPlot;
@@ -1,16 +1,16 @@
1
- type SummaryComparisonData = {
2
- comparedMedian?: number;
3
- summarizedMin?: number;
4
- summarizedMax?: number;
5
- summarizedMean?: number;
1
+ type BracketData = {
2
+ userValue?: number;
3
+ populationMin: number;
4
+ populationMax: number;
6
5
  };
7
- type SummaryComparisonGroup = {
6
+ type BracketDataWithMeta = {
8
7
  groupLabel: string;
9
- data: SummaryComparisonData;
8
+ data: BracketData;
10
9
  color?: string;
11
10
  };
12
11
  export type SummaryComparisonPlotProps = {
13
- groups: SummaryComparisonGroup[];
12
+ groups: BracketDataWithMeta[];
13
+ userColor?: string;
14
14
  width?: number;
15
15
  height?: number;
16
16
  title?: string;
@@ -21,6 +21,11 @@ export type SummaryComparisonPlotProps = {
21
21
  tooltipPosition?: "left" | "right";
22
22
  startXAxisAtZero?: boolean;
23
23
  unit?: string;
24
+ tooltipLabels?: {
25
+ populationMinLabel?: string;
26
+ populationMaxLabel?: string;
27
+ userValueLabel?: string;
28
+ };
24
29
  };
25
30
  declare const SummaryComparisonPlot: (props: SummaryComparisonPlotProps) => import("react/jsx-runtime").JSX.Element;
26
31
  export default SummaryComparisonPlot;
package/dist/index.d.ts CHANGED
@@ -19,6 +19,6 @@ export type { LineWithHistogramProps } from "./components/LineWithHistogram";
19
19
  export { default as BarPlot } from "./components/BarPlot";
20
20
  export type { BarPlotProps } from "./components/BarPlot";
21
21
  export { LegendColorItem, LegendLineItem } from "./components/LegendUtils";
22
- export type { LegendItemProps } from "./components/LegendUtils";
22
+ export type { LegendItemProps, LegendLineItemProps, } from "./components/LegendUtils";
23
23
  export { isDateArray, isNumberArray } from "./components/Utils";
24
24
  export type { PlotParams } from "react-plotly.js";
package/dist/index.esm.js CHANGED
@@ -1619,6 +1619,7 @@ const LegendColorItem = (props) => {
1619
1619
  const LegendLineItem = (props) => {
1620
1620
  const lineWidth = props.width || 40;
1621
1621
  const lineHeight = props.height || 4;
1622
+ const dashArray = props.strokeDasharray || "";
1622
1623
  const labelKey = props.label == null
1623
1624
  ? ""
1624
1625
  : typeof props.label === "string"
@@ -1629,7 +1630,7 @@ const LegendLineItem = (props) => {
1629
1630
  alignItems: "center",
1630
1631
  mr: 4,
1631
1632
  fontFamily: "Open Sans, verdana, arial, sans-serif",
1632
- }, children: [jsx("svg", { width: lineWidth, height: lineHeight, style: { marginRight: 8 }, children: jsx("line", { x1: 0, y1: lineHeight / 2, x2: lineWidth, y2: lineHeight / 2, stroke: props.color, strokeWidth: lineHeight }) }), props.label] }, labelKey));
1633
+ }, children: [jsx("svg", { width: lineWidth, height: lineHeight, style: { marginRight: 8 }, children: jsx("line", { x1: 0, y1: lineHeight / 2, x2: lineWidth, y2: lineHeight / 2, stroke: props.color, strokeWidth: lineHeight, strokeDasharray: dashArray }) }), props.label] }, labelKey));
1633
1634
  };
1634
1635
 
1635
1636
  // This component takes grouped data and transforms it for displaying with the BoxPlot component.
@@ -1818,7 +1819,7 @@ const PairedComparisonsBoxPlot = (props) => {
1818
1819
  // This component shows a set of main boxplots as one collection, then allowes for additional
1819
1820
  // boxes to be shown in a subplot above the main boxplots for comparison.
1820
1821
  const SplitBoxPlot = (props) => {
1821
- const { groups, additionalGroups = [], width = 600, height = 400, title = "", xAxisTitle, yAxisTitle, secondXAxisTitle, secondYAxisTitle, containerStyleOverrides, plotId = "paired-comparisons-boxplot", xAnnotations = [], } = props;
1822
+ const { groups, additionalGroups = [], width = 600, height = 400, title = "", xAxisTitle, yAxisTitle, secondXAxisTitle, secondYAxisTitle, containerStyleOverrides, plotId = "paired-comparisons-boxplot", xAnnotations = [], yTitleLocationOverride, } = props;
1822
1823
  // Transform the grouped data into an array for BoxPlot
1823
1824
  const boxPlotData = [
1824
1825
  ...groups.flatMap((group, groupIndex) => {
@@ -1922,6 +1923,57 @@ const SplitBoxPlot = (props) => {
1922
1923
  });
1923
1924
  const totalGroups = groups.length + additionalGroups.length;
1924
1925
  const yAxisSplitProportion = additionalGroups.length > 0 ? groups.length / totalGroups : 1;
1926
+ // Render y-axis titles as annotations so both always land at the same paper x,
1927
+ // Using this annotations approach because we found the standoff prop to be unreliable across subplots with different label widths.
1928
+ const charWidth = 8; // ~px per character for Open Sans 14px (Plotly title font)
1929
+ const primaryMaxChars = Math.max(0, ...groups.map((g) => g.label.length));
1930
+ const secondaryMaxChars = additionalGroups.length > 0
1931
+ ? Math.max(0, ...additionalGroups.map((g) => g.label.length))
1932
+ : primaryMaxChars;
1933
+ const maxLabelChars = Math.max(primaryMaxChars, secondaryMaxChars);
1934
+ const approxLabelPx = maxLabelChars * charWidth;
1935
+ const leftMargin = approxLabelPx + 75; // tick labels + title + gaps
1936
+ const plotContentWidth = Math.max(1, width - leftMargin - 50);
1937
+ // 0.83 corrects for charWidth=8 overestimating rendered tick-label width (Open Sans 12px ≈ 6.6px/char)
1938
+ const titleXPaper = -(approxLabelPx * 0.83) / plotContentWidth;
1939
+ const yAxisTitleAnnotations = [];
1940
+ if (yAxisTitle) {
1941
+ const domainTop = additionalGroups.length > 0 ? yAxisSplitProportion - 0.01 : 1;
1942
+ yAxisTitleAnnotations.push({
1943
+ text: yAxisTitle,
1944
+ x: yTitleLocationOverride || titleXPaper,
1945
+ y: domainTop / 2,
1946
+ xref: "paper",
1947
+ yref: "paper",
1948
+ showarrow: false,
1949
+ textangle: "-90",
1950
+ xanchor: "center",
1951
+ yanchor: "middle",
1952
+ font: {
1953
+ family: '"Open Sans", verdana, arial, sans-serif',
1954
+ size: 14,
1955
+ color: "rgb(68, 68, 68)",
1956
+ },
1957
+ });
1958
+ }
1959
+ if (secondYAxisTitle && additionalGroups.length > 0) {
1960
+ yAxisTitleAnnotations.push({
1961
+ text: secondYAxisTitle,
1962
+ x: yTitleLocationOverride || titleXPaper,
1963
+ y: (yAxisSplitProportion + 1) / 2,
1964
+ xref: "paper",
1965
+ yref: "paper",
1966
+ showarrow: false,
1967
+ textangle: "-90",
1968
+ xanchor: "center",
1969
+ yanchor: "middle",
1970
+ font: {
1971
+ family: '"Open Sans", verdana, arial, sans-serif',
1972
+ size: 14,
1973
+ color: "rgb(68, 68, 68)",
1974
+ }, // styled to look like plotly's default title styling
1975
+ });
1976
+ }
1925
1977
  const extraLayoutConfig = Object.assign(Object.assign({ yaxis: {
1926
1978
  type: "linear", // Use linear axis for numeric positioning of boxes
1927
1979
  tickmode: "array",
@@ -1929,44 +1981,44 @@ const SplitBoxPlot = (props) => {
1929
1981
  ticktext,
1930
1982
  automargin: true,
1931
1983
  range: [-0.5, groups.length - 0.5], // Add some padding to the y-axis range to accommodate the boxes
1932
- domain: additionalGroups ? [0, yAxisSplitProportion - 0.01] : [0, 1], // Ensure primary y-axis takes full height
1984
+ domain: additionalGroups.length > 0 ? [0, yAxisSplitProportion - 0.01] : [0, 1], // Ensure primary y-axis takes full height
1933
1985
  tickcolor: "#ffffff",
1934
1986
  ticklen: 0,
1987
+ ticksuffix: "", // clear the BoxPlot default suffix so label widths match yaxis2
1935
1988
  showgrid: false,
1936
1989
  //@ts-ignore
1937
1990
  ticklabelstandoff: 7,
1938
1991
  ticklabelposition: "outside left",
1939
- title: {
1940
- text: yAxisTitle || "", // Set default to empty string to prevent Plotly from adding its own default title
1941
- standoff: 45,
1942
- },
1992
+ title: { text: "" }, // rendered via annotation instead
1943
1993
  }, xaxis: {
1944
1994
  anchor: "y", // Anchor to primary y-axis
1945
1995
  }, margin: {
1946
- t: title ? 50 : 40,
1996
+ t: title ? 50 : 70,
1947
1997
  r: 50,
1998
+ l: leftMargin,
1948
1999
  }, shapes: [
1949
2000
  ...separatorShapes,
1950
2001
  // ...differenceBetweenMediansLines,
1951
- ...xAnnotations.map((annotation) => ({
2002
+ ...xAnnotations.map((annotation, annotationIndex) => ({
1952
2003
  type: "line",
1953
2004
  x0: annotation.x,
1954
2005
  x1: annotation.x,
1955
2006
  y0: 0,
1956
- y1: 1.05,
2007
+ y1: 1.05 + annotationIndex * 0.05,
1957
2008
  xref: "x",
1958
2009
  yref: "paper",
1959
2010
  line: {
1960
2011
  color: annotation.color || "rgba(255, 0, 0, 0.7)",
1961
2012
  width: 2,
1962
- dash: "dash",
2013
+ dash: "dot",
1963
2014
  },
1964
2015
  })),
1965
2016
  ], annotations: [
2017
+ ...yAxisTitleAnnotations,
1966
2018
  // ...differenceAnnotations,
1967
- ...xAnnotations.map((annotation) => ({
2019
+ ...xAnnotations.map((annotation, annotationIndex) => ({
1968
2020
  x: annotation.x,
1969
- y: 1.06,
2021
+ y: 1.05 + annotationIndex * 0.05, // Stack annotations above the plot with some spacing
1970
2022
  xref: "x",
1971
2023
  yref: "paper",
1972
2024
  text: annotation.text || "",
@@ -1977,10 +2029,11 @@ const SplitBoxPlot = (props) => {
1977
2029
  size: 12,
1978
2030
  },
1979
2031
  })),
1980
- ] }, (additionalGroups && {
2032
+ ] }, (additionalGroups.length > 0 && {
1981
2033
  xaxis2: {
1982
2034
  title: {
1983
2035
  text: secondXAxisTitle || "", // Set default to empty string to prevent Plotly from adding its own default title
2036
+ standoff: 10 + xAnnotations.length * 12, // Add extra space to accommodate annotations.
1984
2037
  },
1985
2038
  side: "top",
1986
2039
  anchor: "y2", // Anchor to secondary y-axis
@@ -1998,17 +2051,16 @@ const SplitBoxPlot = (props) => {
1998
2051
  automargin: true,
1999
2052
  matches: "x", // Match the range of xaxis to keep them synchronized
2000
2053
  },
2001
- })), (additionalGroups && {
2054
+ })), (additionalGroups.length > 0 && {
2002
2055
  yaxis2: {
2003
2056
  domain: [yAxisSplitProportion, 1], // Adjust domain for secondary y-axis (histogram)
2004
- title: {
2005
- text: secondYAxisTitle || "", // Set default to empty string to prevent Plotly from adding its own default title
2006
- standoff: 45, // Add space between title and axis
2007
- },
2057
+ title: { text: "" }, // rendered via annotation instead
2008
2058
  range: [-0.5, additionalGroups.length - 0.5], // Add some padding to the y-axis range to accommodate the boxes
2009
2059
  automargin: true, // Required for standoff to work properly
2010
2060
  showgrid: true,
2011
2061
  zeroline: false,
2062
+ ticklen: 0,
2063
+ ticksuffix: "",
2012
2064
  ticklabelposition: "outside left",
2013
2065
  showline: true,
2014
2066
  mirror: "ticks",
@@ -2025,15 +2077,15 @@ const SplitBoxPlot = (props) => {
2025
2077
  ticklabelstandoff: 7,
2026
2078
  },
2027
2079
  }));
2028
- console.log("extraLayoutConfig:", extraLayoutConfig);
2029
2080
  const containerStyles = Object.assign({ width: width, height: height, position: "relative", display: "flex", flexDirection: "column", gap: 0 }, containerStyleOverrides);
2030
- console.log(boxPlotData);
2031
2081
  return (jsx("div", { style: Object.assign({}, containerStyles), children: jsx(BoxPlot, { data: boxPlotData, width: width, height: height, title: title, xAxisTitle: xAxisTitle, yAxisTitle: yAxisTitle, extraLayoutConfig: extraLayoutConfig, containerStyleOverrides: containerStyleOverrides, plotId: `${plotId}-boxplot` }) }));
2032
2082
  };
2033
2083
 
2034
2084
  const Plot$2 = lazy(() => Promise.resolve().then(function () { return reactPlotlyWrapper$1; }));
2035
2085
  const SummaryComparisonPlot = (props) => {
2036
- const { groups, height = 250, title = "", xAxisTitle, yAxisTitle, containerStyleOverrides, plotId = "summary-comparison-plot", tooltipPosition = "right", startXAxisAtZero = true, unit = "", } = props;
2086
+ const { groups, userColor = "orange", height = 250, title = "", xAxisTitle, yAxisTitle, containerStyleOverrides, plotId = "summary-comparison-plot", tooltipPosition = "right", startXAxisAtZero = true, unit = "",
2087
+ // xAnnotations = [], TO DO
2088
+ tooltipLabels = {}, } = props;
2037
2089
  // Ref for plot container
2038
2090
  const containerRef = useRef(null);
2039
2091
  // State for custom tooltip
@@ -2063,40 +2115,42 @@ const SummaryComparisonPlot = (props) => {
2063
2115
  let tooltipX = event.event.clientX;
2064
2116
  let tooltipY = event.event.clientY;
2065
2117
  content += `<strong>${groupName}</strong><br/>`;
2066
- const summarizedMinText = groupData.summarizedMin
2067
- ? groupData.summarizedMin.toFixed(2)
2068
- : "NA";
2069
- const summarizedMaxText = groupData.summarizedMax
2070
- ? groupData.summarizedMax.toFixed(2)
2118
+ const populationMinText = groupData.populationMin !== undefined
2119
+ ? groupData.populationMin.toFixed(2)
2071
2120
  : "NA";
2072
- const comparedMedianText = groupData.comparedMedian !== undefined
2073
- ? groupData.comparedMedian.toFixed(2)
2121
+ const populationMaxText = groupData.populationMax !== undefined
2122
+ ? groupData.populationMax.toFixed(2)
2074
2123
  : "NA";
2075
2124
  content += `
2076
2125
  <table style="width: 100%; margin-top: 4px;">
2077
2126
  <tr>
2078
- <td style="text-align: left; padding: 2px 18px 2px 0;">Population avg-\u03C3:</td>
2079
- <td style="text-align: right; padding: 2px 0;">${summarizedMinText}</td>
2127
+ <td style="text-align: left; padding: 2px 18px 2px 0;">${tooltipLabels.populationMinLabel || "Population avg-\u03C3"}:</td>
2128
+ <td style="text-align: right; padding: 2px 0;">${populationMinText}</td>
2080
2129
  </tr>
2081
2130
  <tr>
2082
- <td style="text-align: left; padding: 2px 18px 2px 0;">Population avg+\u03C3:</td>
2083
- <td style="text-align: right; padding: 2px 0;">${summarizedMaxText}</td>
2131
+ <td style="text-align: left; padding: 2px 18px 2px 0;">${tooltipLabels.populationMaxLabel || "Population avg+\u03C3"}:</td>
2132
+ <td style="text-align: right; padding: 2px 0;">${populationMaxText}</td>
2084
2133
  </tr>
2134
+ `;
2135
+ if (groupData.userValue !== undefined) {
2136
+ const userValueText = groupData.userValue.toFixed(2);
2137
+ content += `
2085
2138
  <tr>
2086
- <td style="text-align: left; padding: 2px 18px 2px 0;">My Median:</td>
2087
- <td style="text-align: right; padding: 2px 0;">${comparedMedianText}</td>
2139
+ <td style="text-align: left; padding: 2px 18px 2px 0;">${tooltipLabels.userValueLabel || "My value"}:</td>
2140
+ <td style="text-align: right; padding: 2px 0;">${userValueText}</td>
2088
2141
  </tr>
2089
- </table>
2090
- `;
2142
+ `;
2143
+ }
2144
+ content += `</table></div>`;
2091
2145
  contentColor = eventData.line
2092
2146
  ? eventData.line.color
2093
2147
  : eventData.marker.color || contentColor;
2094
- // Position tooltip at the end of the line (75th percentile)
2148
+ // Position tooltip at the end of the line (populationMax for right tooltip, populationMin for left tooltip)
2095
2149
  // Use the xaxis d2p method to convert data coordinate to pixel coordinate
2096
2150
  if (point.xaxis && typeof point.xaxis.d2p === "function") {
2097
2151
  const pixelX = tooltipPosition === "right"
2098
- ? point.xaxis.d2p(groupData.summarizedMax)
2099
- : point.xaxis.d2p(groupData.summarizedMin);
2152
+ ? point.xaxis.d2p(groupData.populationMax)
2153
+ : point.xaxis.d2p(groupData.populationMin);
2100
2154
  const pixelY = point.yaxis.d2p(eventData.y[0]); // Use the y coordinate of the line for vertical positioning
2101
2155
  const containerRect = (_b = containerRef.current) === null || _b === void 0 ? void 0 : _b.getBoundingClientRect();
2102
2156
  if (containerRect) {
@@ -2120,63 +2174,68 @@ const SummaryComparisonPlot = (props) => {
2120
2174
  // Transform the data into a format suitable for a plotly scatterplot
2121
2175
  const plotlyData = groups.flatMap((group, groupIndex) => {
2122
2176
  const traces = [];
2123
- if (group.data.summarizedMin !== undefined &&
2124
- group.data.summarizedMax !== undefined) {
2125
- traces.push({
2126
- type: "scatter",
2127
- mode: "lines",
2128
- name: group.groupLabel,
2129
- x: [group.data.summarizedMin, group.data.summarizedMax],
2130
- y: [groupIndex * 0.1, groupIndex * 0.1],
2131
- line: {
2132
- color: colorToRGBA(group.color || "orange", 1),
2133
- width: 6,
2134
- },
2135
- showlegend: false,
2136
- hoverinfo: "none",
2137
- });
2138
- }
2139
- if (group.data.comparedMedian !== undefined) {
2140
- traces.push({
2141
- type: "scatter",
2142
- mode: "markers",
2143
- name: group.groupLabel,
2144
- x: [group.data.comparedMedian],
2145
- y: [groupIndex * 0.1],
2146
- marker: {
2147
- color: colorToRGBA(group.color || "orange", 1),
2148
- size: 10,
2149
- symbol: "circle",
2150
- line: {
2151
- color: "white",
2152
- width: 1,
2153
- },
2154
- },
2155
- showlegend: false,
2156
- hoverinfo: "none",
2157
- });
2177
+ traces.push({
2178
+ type: "scatter",
2179
+ mode: "lines",
2180
+ name: group.groupLabel,
2181
+ x: [group.data.populationMin, group.data.populationMax],
2182
+ y: [groupIndex * 0.1, groupIndex * 0.1],
2183
+ line: {
2184
+ color: colorToRGBA(group.color || "orange", 1),
2185
+ width: 6,
2186
+ },
2187
+ showlegend: false,
2188
+ hoverinfo: "none",
2189
+ });
2190
+ if (group.data.userValue !== undefined) {
2191
+ const CIRCLE_RADIUS_PX = 6;
2158
2192
  comparedLines.push({
2159
2193
  type: "line",
2160
2194
  name: group.groupLabel,
2161
- x0: group.data.comparedMedian,
2162
- x1: group.data.comparedMedian,
2195
+ x0: group.data.userValue,
2196
+ x1: group.data.userValue,
2163
2197
  y0: 0,
2164
2198
  y1: 1.1,
2165
2199
  yref: "paper",
2166
2200
  line: {
2167
- color: group.color || "orange",
2201
+ color: colorToRGBA(userColor, 1),
2168
2202
  width: 2,
2169
2203
  },
2170
2204
  });
2205
+ comparedLines.push({
2206
+ type: "circle",
2207
+ xref: "x",
2208
+ yref: "y",
2209
+ // Pixel-mode: xanchor/yanchor are data coords for the center;
2210
+ // x0/x1/y0/y1 are pixel offsets, so the circle stays a fixed
2211
+ // visual size regardless of the axis scale.
2212
+ xsizemode: "pixel",
2213
+ ysizemode: "pixel",
2214
+ xanchor: group.data.userValue,
2215
+ yanchor: groupIndex * 0.1,
2216
+ x0: -CIRCLE_RADIUS_PX,
2217
+ x1: CIRCLE_RADIUS_PX,
2218
+ y0: -CIRCLE_RADIUS_PX,
2219
+ y1: CIRCLE_RADIUS_PX,
2220
+ fillcolor: colorToRGBA(userColor, 1),
2221
+ line: {
2222
+ color: "white",
2223
+ width: 1,
2224
+ },
2225
+ });
2171
2226
  comparedAnnotations.push({
2172
- x: group.data.comparedMedian,
2173
- y: 1.08,
2227
+ x: group.data.userValue,
2228
+ y: 1.1,
2174
2229
  xref: "x",
2175
2230
  yref: "paper",
2176
- text: `${group.data.comparedMedian.toFixed(2)} ${unit}`,
2231
+ text: `${group.data.userValue.toFixed(2)} ${unit}`,
2177
2232
  showarrow: false,
2178
2233
  xanchor: "center",
2179
2234
  yanchor: "bottom",
2235
+ font: {
2236
+ color: colorToRGBA(userColor, 1),
2237
+ size: 12,
2238
+ },
2180
2239
  });
2181
2240
  }
2182
2241
  return traces;
@@ -2185,7 +2244,7 @@ const SummaryComparisonPlot = (props) => {
2185
2244
  if (comparedAnnotations.length > 0) {
2186
2245
  comparedAnnotations.push({
2187
2246
  x: -0.03, // Position to the left of the y-axis
2188
- y: 1.03,
2247
+ y: 1.1, // Should match the y position of the compared median annotation
2189
2248
  xref: "paper",
2190
2249
  yref: "paper",
2191
2250
  text: "My AVG",
@@ -2194,14 +2253,17 @@ const SummaryComparisonPlot = (props) => {
2194
2253
  yanchor: "bottom",
2195
2254
  });
2196
2255
  }
2197
- const xRangeMin = groups.reduce((min, group) => {
2198
- var _a, _b;
2199
- return Math.min(min, (_a = group.data.summarizedMin) !== null && _a !== void 0 ? _a : Infinity, (_b = group.data.comparedMedian) !== null && _b !== void 0 ? _b : Infinity);
2200
- }, 0);
2256
+ // xRangeMin a little funky because we want to ensure a non-zero value is calculated
2257
+ // in case startXAxisAtZero is false. populationMin is our anchor and
2258
+ // if that is not defined then we can fall back to a min of 0. If userValue is not defined
2259
+ // we want to listen to populationMin for computing xRangeMin.
2260
+ const xRangeMin = groups.reduce((min, group) => { var _a; return Math.min(min, group.data.populationMin, (_a = group.data.userValue) !== null && _a !== void 0 ? _a : Infinity); }, Infinity);
2261
+ // Similar logic for xRangeMax. If nothing is defined, set the max to 0.
2262
+ // Otherwise, listen to populationMax and userValue if given.
2201
2263
  const xRangeMax = groups.reduce((max, group) => {
2202
- var _a, _b;
2203
- return Math.max(max, (_a = group.data.summarizedMax) !== null && _a !== void 0 ? _a : -Infinity, (_b = group.data.comparedMedian) !== null && _b !== void 0 ? _b : -Infinity);
2204
- }, 0);
2264
+ var _a;
2265
+ return Math.max(max, group.data.populationMax, (_a = group.data.userValue) !== null && _a !== void 0 ? _a : -Infinity);
2266
+ }, -Infinity);
2205
2267
  const layout = {
2206
2268
  width: undefined,
2207
2269
  height: height,
@@ -2250,6 +2312,12 @@ const SummaryComparisonPlot = (props) => {
2250
2312
  linewidth: 1,
2251
2313
  },
2252
2314
  yaxis: {
2315
+ title: {
2316
+ text: yAxisTitle,
2317
+ font: {
2318
+ size: 14,
2319
+ },
2320
+ },
2253
2321
  mirror: "ticks",
2254
2322
  gridcolor: "#efefef",
2255
2323
  gridwidth: 0.2,
@@ -2323,12 +2391,26 @@ const SummaryComparisonPlotLegend = ({ comparedDataLabel, summarizedDataLabel, c
2323
2391
  gap: "20px",
2324
2392
  alignItems: "center",
2325
2393
  flexDirection: "row",
2326
- }, children: [jsxs("div", { style: { display: "flex", gap: "5px", alignItems: "center" }, children: [jsx(Box, { sx: {
2394
+ }, children: [jsxs("div", { style: { display: "flex", gap: "5px", alignItems: "center" }, children: [jsxs("div", { style: {
2395
+ position: "relative",
2327
2396
  width: 13,
2328
- height: 13,
2329
- backgroundColor: color,
2330
- borderRadius: "20px",
2331
- } }), jsx("span", { children: comparedDataLabel })] }), jsxs("div", { style: { display: "flex", gap: "5px", alignItems: "center" }, children: [jsx(Box, { sx: { width: 30, height: 3, backgroundColor: color } }), jsx("span", { children: summarizedDataLabel })] })] }));
2397
+ height: 20,
2398
+ display: "flex",
2399
+ alignItems: "center",
2400
+ justifyContent: "center",
2401
+ }, children: [jsx(Box, { sx: {
2402
+ position: "absolute",
2403
+ width: 3,
2404
+ height: 20,
2405
+ backgroundColor: color,
2406
+ } }), jsx(Box, { sx: {
2407
+ position: "relative",
2408
+ width: 10,
2409
+ height: 10,
2410
+ backgroundColor: color,
2411
+ borderRadius: "20px",
2412
+ border: `1px solid white`,
2413
+ } })] }), jsx("span", { children: comparedDataLabel })] }), jsxs("div", { style: { display: "flex", gap: "5px", alignItems: "center" }, children: [jsx(Box, { sx: { width: 30, height: 5, backgroundColor: color } }), jsx("span", { children: summarizedDataLabel })] })] }));
2332
2414
  };
2333
2415
 
2334
2416
  const Plot$1 = lazy(() => Promise.resolve().then(function () { return reactPlotlyWrapper$1; }));