fix: support per-stat highlighting for Plotly single-path box plots (#535)#553
fix: support per-stat highlighting for Plotly single-path box plots (#535)#553jooyoungseo wants to merge 4 commits intomainfrom
Conversation
…535) Plotly renders each box plot as a single <path> element containing whiskers, IQ box, and median line, whereas MAIDR expects separate SVG elements for each statistical component. This caused within-box stat navigation and per-stat visual highlighting to fail for Plotly box plots. Add detection for single-path box plots (where all BoxSelector fields resolve to the same DOM element) and create synthetic SVG line overlays at data-driven positions using bounding box coordinate mapping. The existing multi-element code path (matplotlib, etc.) is preserved and only activates when 2+ non-null selectors resolve to different elements. Changes: - Add isSinglePathBox() detection method to BoxTrace - Add createDataDrivenOverlays() for data-to-SVG coordinate mapping - Add Svg.createPositionedLineElement() utility for positioned overlays - Add Plotly-style vertical box plot example https://claude.ai/code/session_012wiRFDsjMCepsrpfVaPJza
PR Review: fix: support per-stat highlighting for Plotly single-path box plotsSummary: The approach is well-reasoned and creative. Detecting single-path boxes and synthesizing overlay elements via data-driven coordinate mapping is a sound strategy for the Plotly case. Below are issues ranging from bugs to suggestions. Bugs / Correctness Issues1. Coordinate mapping assumes bbox fully spans data range (may misfire)
const y = bbox.y + ((dataMax - value) / dataRange) * bbox.height;This assumes the path's bounding box exactly spans from 2. const dataRange = dataMax - dataMin;
if (dataRange === 0) {
return [lowerOutliers, createEmptyElement, ...];
}If 3. Multiple Inside referenceElement.insertAdjacentElement(Constant.AFTER_END, line);When called 5 times for the same reference element (min, q1, q2, q3, max), each insertion goes between the reference and the previously inserted sibling. The DOM order ends up reversed: Architecture / Design4. if (elements.length <= 1) {
return false;
}If only one selector field resolves (e.g., only 5. If a chart author deliberately points two different fields at the same element (to highlight both stats together), the detection would treat it as a single-path box and replace highlighting with synthetic overlays. This is unlikely but worth noting since there is no explicit user signal—the detection is purely structural. 6.
line.setAttribute(Constant.STROKE, style.stroke || '#000000');
line.setAttribute(Constant.STROKE, style.stroke); // no fallbackConsider applying the same Test Coverage7. No unit or E2E tests for the new code paths
A Playwright E2E test for Minor / Nitpick8. Both methods share ~10 lines of identical setup (namespace, x1/y1/x2/y2 setAttribute, stroke, stroke-width, visibility, insertAdjacentElement). A private shared helper accepting coordinates and reference element would reduce duplication and make the fallback fix in point 6 easier to apply consistently. 9. Version bump in The lock file bumps version What's Working Well
Overall the approach is sound for the described Plotly rendering model. Addressing the coordinate-assumption documentation, the reversed-insertion order, and adding at least basic tests for the new logic would make this production-ready. |
… coverage Address PR review feedback: - Refactor createLineElement to delegate to createPositionedLineElement, eliminating duplicated line creation logic between the two methods - Update vertical Plotly example to include outliers (matching the pattern of all existing matplotlib box plot examples with 2-3 lower and 2-3 upper outliers per box) - Add horizontal Plotly box plot example with outliers, ensuring the single-path adapter's horizontal code path is exercised The adapter now has example coverage for all supported box plot variations: - Vertical single-path with outliers (boxplot-plotly-vertical.html) - Horizontal single-path with outliers (boxplot-plotly-horizontal.html) https://claude.ai/code/session_012wiRFDsjMCepsrpfVaPJza
PR Review: fix: support per-stat highlighting for Plotly single-path box plots (#535)Thanks for a well-documented fix for a real accessibility gap. The core idea — detecting Plotly's single-path rendering and synthesizing positional overlays — is practical. Here are my observations: Architecture / DesignMild MVVC boundary concern in
Potential Bugs1.
This works for the examples in this PR, but it could silently misplace highlights if a chart's path uses different coordinate extents (e.g., an axis that doesn't start at the whisker, or a padding offset). A comment explaining the assumption would help future maintainers: // Assumes the bounding box of the single-path element spans from
// the SVG coordinate of dataMin to dataMax (i.e. whisker ends are
// the leftmost/rightmost or topmost/bottommost rendered pixels).2. If exactly one of 3. Redundant null-guard after When Test CoverageNo tests are added for the new code paths:
The new example HTML files are great for manual verification, but unit tests for the detection/mapping logic and an E2E test using those examples would significantly reduce regression risk for future box plot changes. Code Quality
Extracting the shared line-creation logic and making it
Inline The closure is clear and readable, but // min/max map to the bbox edges; q1..q3 are interpolated between themMinor
Summary
Recommended before merge: Add at least one unit test for |
…nt assumption Address remaining PR review feedback: - Remove redundant null-guard after isSinglePathBox: since the method guarantees at least 2 non-null elements, the referenceElement can never be null. Use non-null assertion instead of dead else branch. - Add console.warn in isSinglePathBox when only 1 of 4 selectors resolves, helping debug partial DOM failures. - Document the getBBox coordinate mapping assumption: the bounding box must span exactly from data min to data max (holds for Plotly where whisker endpoints are the outermost drawn coordinates). - Stylesheet placement in body is left as-is: this is the established convention across all 20+ existing examples in the project. https://claude.ai/code/session_012wiRFDsjMCepsrpfVaPJza
Code ReviewThanks for tackling the Plotly single-path box plot issue — this is a meaningful accessibility improvement. Here are my findings: Bugs / Correctness Issues1. In const bbox = svg.getBBox();
const bbox = svg.getBBox();
if (bbox.width === 0 && bbox.height === 0) {
return [lowerOutliers, ...Array(5).fill(Svg.createEmptyElement('line')), upperOutliers];
}Or wrapping in a try/catch. 2.
Design / Architecture3. Fragile non-null assertion after const referenceElement = (original.iq ?? original.min ?? original.max ?? original.q2)\!;The 4. Coordinate mapping assumption is fragile for stroked paths From the JSDoc:
Code Quality5.
6. No unit tests for new methods
Positive Notes
SummaryThe approach is sound and the implementation is well-structured. The main items to address before merging are: the |
Plotly renders each box plot as a single element containing
whiskers, IQ box, and median line, whereas MAIDR expects separate SVG
elements for each statistical component. This caused within-box stat
navigation and per-stat visual highlighting to fail for Plotly box plots.
Add detection for single-path box plots (where all BoxSelector fields
resolve to the same DOM element) and create synthetic SVG line overlays
at data-driven positions using bounding box coordinate mapping. The
existing multi-element code path (matplotlib, etc.) is preserved and
only activates when 2+ non-null selectors resolve to different elements.
Changes:
https://claude.ai/code/session_012wiRFDsjMCepsrpfVaPJza