Release v1.2.0 — themes, accessibility, exports, linked cursor, calendar heatmap#47
Merged
mortonanalytics merged 77 commits intomainfrom Apr 19, 2026
Merged
Release v1.2.0 — themes, accessibility, exports, linked cursor, calendar heatmap#47mortonanalytics merged 77 commits intomainfrom
mortonanalytics merged 77 commits intomainfrom
Conversation
Exclude the auto-generated CRAN-SUBMISSION file from package builds. Document strict semver and backward-compatibility requirements for all minor/patch releases. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds `sparkline = TRUE` parameter to `myIO()` that renders compact, axes-free charts at 20px height — suitable for embedding in reactable, DT, and gt table cells. Strips legend, interactions, and reference lines. Supports line, bar, and area layer types. R: sparkline param on myIO(), type validation in addIoLayer() JS: applySparklineOverrides() in Chart constructor, sparkline CSS Tests: 12 R tests, 7 JS vitest tests (19 total) Docs: sparklines.Rmd vignette, NEWS.md v1.2.0 entry Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add sparkline mode for compact table-cell charts
Adds mode-based theming to setTheme(): "light", "dark", and "auto" modes with WCAG AA-verified palettes. Auto mode detects OS preference and Quarto/Bootstrap data-bs-theme via MutationObserver + matchMedia. R: setTheme() gains mode/preset/overrides params (backward compatible) JS: ThemeManager class with palettes, auto-detection, lifecycle cleanup CSS: high-contrast and reduced-motion media queries Tests: 18 R tests, 7 JS vitest tests (25 total) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add dark mode / theme system with auto-detection
…(v1.2)
Adds SVG download button, clipboard copy (SVG + PNG via Clipboard API),
and CSS custom property resolution for theme-correct exported SVGs.
New setExportOptions() R function controls toolbar button visibility.
R: setExportOptions() with png/svg/pdf/clipboard/csv/title flags
JS: resolve-css-vars.js, export-clipboard.js, downloadSVG(), SVG button
in toolbar with config.export-aware filtering
Tests: 15 R tests, 5 JS vitest tests (20 total)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add export enhancements: SVG download, clipboard, CSS var resolution
Adds setFacet() to split charts into a CSS grid of panels, one per unique
value of the faceting variable. FacetController manages lifecycle and
FacetPanel renders mini-charts with own SVG, scales, and axes.
R: setFacet(var, ncol, min_width, scales, label_position)
JS: FacetController + FacetPanel classes, Chart.js integration,
htmlwidgets resize handling for faceted mode
CSS: facet grid layout, panel labels, responsive breakpoints
Tests: 17 R tests, existing JS suite passes
Phases 3-5 (free scales, cross-panel interactions, performance) deferred.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add small multiples / faceting with CSS grid layout
Adds two new chart types to the myIO renderer registry: - lollipop: vertical stem + circle head, categorical x-axis, supports mean/summary transforms - dumbbell: connected dots showing low_y to high_y range Both support flipAxis, themed colors, and standard tooltip formatting. R: types added to ALLOWED_TYPES, COMPATIBILITY_GROUPS, VALID_COMBINATIONS JS: LollipopRenderer + DumbbellRenderer with full renderer interface CSS: crisp stems, hover enlargement on heads/dots Tests: 5 lollipop + 6 dumbbell R contract tests Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add lollipop and dumbbell chart types
Completes the new chart types roadmap: - waffle: 10x10 grid of colored squares for proportions (standalone) - beeswarm: dodge-positioned points with inline collision detection - bump: smooth S-curves for rank/value changes over time (grouped) R: types added to ALLOWED_TYPES, COMPATIBILITY_GROUPS, VALID_COMBINATIONS JS: WaffleRenderer + BeeswarmRenderer + BumpRenderer CSS: hover effects for all three types Tests: 6 waffle + 4 beeswarm + 4 bump R contract tests Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add waffle, beeswarm, and bump chart types
Adds cross-cutting accessibility features: - ARIA roles (graphics-document, graphics-object, graphics-symbol) on SVG structure with auto-generated labels from axis config - Keyboard navigation state machine: arrow keys traverse layers/points, live region announcements with 150ms debounce, focus ring styling - Hidden data table fallback for screen reader access (500-row cap) - Screen-reader-only utility class, focus ring CSS All features skip sparkline mode. Existing functionality preserved. JS: a11y/aria.js, descriptions.js, keyboard-nav.js, data-table.js CSS: focus ring, sr-only, data table styles Tests: 6 JS a11y contract tests Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add SVG accessibility: ARIA roles, keyboard nav, data table
… Session 1) Three new features completing the statistical visualization story: - Kaplan-Meier survival curves: transform_survfit computes KM estimator + Greenwood CI using base R only. composite_survfit expands to step-curve line + CI band + censored markers. Group stratification supported. - Distribution fitting: transform_fit_distribution fits normal/lognormal/ exponential via MLE. composite_histogram_fit overlays fitted density on histogram. - Layer opacity: setLayerOpacity(label, opacity) for per-layer transparency. Applied in Chart.js routeLayers(). Tests: 23 survfit + 7 histogram_fit + 6 opacity = 36 new R tests Total: 770 R tests, 194 JS tests, all passing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add survival curves, distribution fitting, and layer opacity
Side-by-side code comparisons for 8 common patterns: scatter, line, bar, histogram, box, regression+CI, statistical annotations, and dark mode. Addresses the plotly documentation retirement and broken features (#1472, #1687) as migration motivators. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…guide Add plotly migration guide vignette
Two adoption features to close the gap with ggiraph and echarts4r: - linkCharts(): cross-chart selection with bidirectional mode and aggregation-to-source-row key resolution - Group-by auto-series: pass a grouped tibble to addIoLayer() and it auto-creates one layer per group with Okabe-Ito colors. Soft dplyr dep. Tests: 7 grouped_df + 13 linkCharts = 20 new R tests Total: 790 R tests, 194 JS tests, all passing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add linked brushing and group-by auto-series
12 named theme presets: midnight, ocean, forest, sunset, monochrome, neon, corporate, academic, nature, minimal, retro, warm. Applied via setTheme(preset = "name"). Presets override the base light/dark palette but user CSS custom property overrides still take highest priority. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add theme gallery with 12 named presets
…ion 4) Three new standalone chart types bringing the total to 30+: - radar: spider/radar chart with radial axes and polygon data fill - funnel: narrowing horizontal bars for conversion pipelines - parallel: parallel coordinates for multivariate exploration R: types registered in ALLOWED_TYPES, COMPATIBILITY_GROUPS, VALID_COMBINATIONS JS: RadarRenderer, FunnelRenderer, ParallelRenderer with standalone rendering CSS: hover effects for all three types Tests: 4 radar + 4 funnel + 3 parallel R tests, 4 JS render tests Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add radar, funnel, and parallel coordinates chart types
Adds tabs for 11 new chart types (lollipop, dumbbell, waffle, beeswarm, bump, radar, funnel, survival curves, distribution fit, sparklines, small multiples) and theme preset selector. Updates home page to reflect 30 chart types and 12 theme presets. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Update gallery app with all v1.2 features
- Bump chart: add defineCategoricalAxis() so x-axis renders Q1-Q4 - Beeswarm: swap to point chart (beeswarm needs categorical y-axis, demo data uses numeric y — will fix renderer in follow-up) - Sparklines: use sparkline = TRUE parameter instead of manual suppress - Survival curve: add setAxisLimits(ylim = 0-1) Known remaining issues (renderer-level, not app-level): - Survival curve: x-axis scale derivation compresses time values - Distribution fit: JS error "undefined is not iterable" in composite - Sparklines: axes/FAB still faintly visible (CSS specificity issue) - Bump: all lines same color (colorScale not applied per group) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Fix gallery app visual issues from review
Three root-cause fixes: 1. Distribution fit composite: changed histogram sublayer from type "histogram" to "bar" with pre-binned data. HistogramRenderer expects layer.bins from the derive pipeline, which composites bypass. 2. Survival curve composite: fixed scaleHints to include x_var/y_var in extent fields so the derive pipeline computes correct x/y domains instead of normalizing to [-1, 1]. 3. Scaffold: add "myIO-container" CSS class to the widget element so CSS selectors for sparkline hiding, high-contrast, and reduced-motion actually match. This was preventing sparkline FAB/axis suppression. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Resolved merge conflict in buttons.js data2Use logic. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- T1: Simplified icon set (iconLegend, iconDownload, iconImage), stroke-width 1.5→2 at 16px, data-first labels, removed dead addButtons() - T3: FAB responsive position (top-right desktop, bottom-right mobile), close button styles, circle swatches, divider, Show All reset, adaptive grid, action row layout, removed .buttonDiv styles - T4: Removed .buttonDiv from scaffold.js and Chart.js cleanup selectors Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Header row with close button (X) replacing bare handle - Section wrapper holds legend body + divider (hides together) - Removed section titles (h2) and On/Off state text - Circle swatches (no border), adaptive grid for 5+ items on mobile - Show All reset button rendered outside grid in section wrapper - Swipe-to-dismiss on mobile (header-only, 80px threshold) - Data-first action order with visible labels - FAB icon: iconLegend(), aria-label: "Legend and actions" - try/finally on toggleOrdinalSegment suppress flag Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- T5: Updated bottom-sheet tests for new DOM structure (13 tests)
- Close button, Show All reset, no On/Off text, visible labels,
Escape-to-close, initial focus, suppressLegend hides divider
- T6: Added resetLegendVisibility tests for layer and ordinal reset
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Regenerated myIOapi.js via esbuild with all legend redesign changes. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Cross-agent review found that toggleOrdinalSegment and
resetLegendVisibility("ordinal") suppressed the legend rebuild but
never re-rendered the panel legend items. Added renderSheetLegend(chart)
after both finally blocks so aria-checked and Show All stay in sync.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Create export-pdf.js with lazy jsPDF loading and A4 page fitting - Add PDF and clipboard handlers to buttons.js handleAction - Add PDF, SVG, clipboard actions to bottom-sheet buildActionData - Fix SVG default: all export buttons visible unless explicitly disabled - Add export CSS (copy menu, success feedback, sr-announce) - Update contract tests and bottom-sheet tests for new action count Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New tab with toggleable PNG/SVG/PDF/CSV/clipboard controls and theme selector to test CSS variable resolution in exports. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Legend UI: FAB + bottom-sheet panel replacing old buttonDiv toolbar. Export enhancements: PDF export (lazy jsPDF), clipboard copy (SVG/PNG), expanded CSS var resolution, all 5 export buttons default visible. Gallery: new Export Demo tab. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add missing svg branch to handleAction; Save as SVG was a silent no-op - Move jspdf.umd.min.js into the widget dir so Shiny htmlwidgets serves it; switch load-jspdf.js to URL-based path resolution instead of string trim - Fix resolve-css-vars regex to handle var(--x, fallback) syntax - Inject resolved chart vars as inline style on SVG root so embedded CSS rules inherit the active theme in standalone exports Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The single "Copy to clipboard" action hardcoded PNG; splitting gives users a discoverable way to copy vector SVG without reaching for R config. Both entries are controlled by the existing export.clipboard flag. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
linkCharts() and setLinked() gain a cursor=TRUE argument (default FALSE for strict-semver). When enabled, hovering any element with an x-value in one chart draws a vertical crosshair at the matching x on every sibling chart in the same link group. New setLinkedCursor() composes onto a pre-linked widget. Architecture: - linked-cursor.js owns a module-level Map<group, Set<chart>> registry. - Charts register on mount from Chart.js when config.interactions.linked .cursor===true; unregister from cleanupLinked in linked.js. - rollover.js emits a CursorEvent at the three hover tails (showElementHover, showGroupedBar, showOverlayTooltip) and a clear event from their mouseout paths. Receivers map xValue through their own xScale (not source pixels). - Out-of-domain xValues clear rather than drawing off-canvas. - Echo suppression (self-receive skipped), stale-ts drop, and teardown cleanup are all covered by tests. Covers design criteria 1-7 and 9. Criterion 8 (config round-trip) is verified by R tests. rAF throttling deferred to v1.3 if profiling shows need. Donut/treemap excluded — no x-axis to sync on. Design: docs/linked-cursor-design.md Contract: docs/linked-cursor-contract.md Plan: md/todo/linked-cursor.md Tests: 29 R assertions + 10 JS unit + 13 JS integration = 52 new. Total suite: 248 JS tests, 822 R tests, all passing. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
drawCrosshair appended line.myIO-hover-rule to chart.svg (root) but used xPx from chart.xScale, which is in plot-area-local coordinates. On real charts the plot group is translated by (margin.left, margin.top), so the crosshair rendered 30-50px left of the actual data. The JSDOM integration tests didn't catch this because their fixture mounts skip the plot group. Attach to chart.plot when present, fall back to chart.svg for tests. Clamp y-extent to inner plot height so the rule doesn't extend past the x-axis into the margin band. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
expand_grouped_df() calls dplyr::group_vars() and dplyr::ungroup() behind a requireNamespace guard, and test_grouped_df.R uses dplyr::group_by() for fixtures via skip_if_not_installed. Both call sites are optional, so Suggests is the correct declaration. Resolves two WARNINGs: undeclared :: import and undeclared requireNamespace. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
GitHub-contributions-style grid: 7 rows (weekdays) x ~52 week columns,
month labels across the top. Single calendar year per layer; multi-year
data is rejected with a pointer to a future setFacet() path. Default
week start is Sunday; Monday is available via options$weekStart.
Piping grouped data just works:
addIoLayer(type = "calendarHeatmap",
mapping = list(date = "day", value = "x"))
Participates in the shared pipeline for free — theming, PNG/SVG/PDF/CSV
export, tooltips, and cross-chart linked-cursor sync on the date key.
Two new hooks were needed: rollover.js now recognizes calendarHeatmap as
an element-bound layer and derives its xValue from mapping.date as a
Date, so linked cursor maps through each receiver's own week-column
scale. Three CSS vars theme the grid: --chart-calendar-cell-gap,
--chart-calendar-cell-stroke, --chart-calendar-empty-fill.
Tests: 6 testthat cases (mapping, numeric, coercion, multi-year,
empty, composition) and 9 vitest cases (registration, cell count,
both week-start conventions, color scale, month labels, tooltip,
linked-cursor emit + receive).
Design: md/design/calendar-heatmap.md. Plan: md/todo/calendar-heatmap.md.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Slice A (bslib): myIO() widgets now declare a sizing policy with
fill = TRUE so they render as bslib fill items inside card(),
Quarto Dashboard {.fill} cards, and flexdashboard layouts. Added
a resize guard against zero-dimension containers that appear
briefly during fill-layout transitions.
Slice B (crosstalk): LINKABLE_TYPES extended from 5 to 9 entries,
adding waffle, beeswarm, lollipop, and dumbbell (all four already
stamp _source_key in their renderers). New crosstalk-linking
vignette demonstrates linking myIO with DT and reactable, with
plotly and leaflet shown as static code listings.
Suggests: add DT, reactable, htmltools for the vignette and tests.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Transitive dev dependency via glob. npm audit fix bumps brace-expansion from 2.0.2 -> 2.1.0 and 5.0.4 -> 5.0.5, resolving GHSA-f886-m6hf-6m8v (zero-step sequence hang). Dev-only; not shipped to users. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Documents the myIO↔pymyIO boundary for AI coding agents and auto-opens a tracking issue in pymyIO whenever engine files change on main. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Also Rbuildignores AGENTS.md so dev-only guidance stays out of the package build. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Enables manual firing for smoke tests and for backfilling tracking issues after a missed sync. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
gh issue create routes through GraphQL, which fine-grained PATs don't fully support and returns 401. Using the REST POST /issues endpoint directly with jq-built payload works with a fine-grained token scoped to Issues: Read and write. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Both gh api and gh issue create probe GraphQL during token verification and reject fine-grained PATs with 401 even for pure REST calls. Plain curl with a Bearer header works. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Temporarily probe /user before POST /issues to isolate whether the workflow's 401 is a token-storage issue or a token-permission issue. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The diagnostic surfaced a secret-install bug (gh secret set --body - stored literal "-" instead of stdin); that's now fixed. The workflow runs cleanly against a correctly-installed fine-grained PAT. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
# Conflicts: # .Rbuildignore
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Cuts myIO v1.2.0 from
developintomain. 76 commits since the v1.1.0 tag, covering the full v1.2 NEWS.md changelog. Brings the package up to 18+ chart types, a themeable dark-mode system, full ARIA/keyboard accessibility, a redesigned export pipeline, bidirectional linking (brush + cursor), and the new calendar heatmap.Highlights (grouped, full list in NEWS.md)
calendarHeatmap(GitHub-style daily grid),lollipop,dumbbell,waffle,beeswarm,bump,radar,funnel,parallel,survfit(Kaplan-Meier),histogram_fit(MLE distribution overlay).setFacet(var, ncol, scales)with CSS-grid layout and responsive breakpoints.setTheme(preset=)across 12 named presets + light/dark/auto mode; CSS-var-driven; WCAG AA dark palette; high-contrast and reduced-motion support.linkCharts()andsetLinked()grow acursor = TRUEarg;setLinkedCursor()retrofits. Crosstalk path extended towaffle,beeswarm,lollipop,dumbbell.html-fill-item; fit insidebslib::card()and Quarto{.fill}without boilerplate.setExportOptions()toolbar toggles, SVG download, PNG+SVG clipboard copy, CSS-var resolution in exported SVGs (dark mode exports correctly), lazy-loaded PDF via vendored jsPDF.myIO(sparkline = TRUE)strips axes/legend for table-cell embedding.dplyr::group_by()tibble toaddIoLayer()and it expands per-group with Okabe-Ito colors..Rbuildignore; vite/brace-expansion security bumps; engine-bump-notify cross-repo workflow and AGENTS.md.What changed in files
setFacet,setLinkedCursor,setExportOptions,setLayerOpacity, and the newtypevalues.DESCRIPTIONgrows Suggests:dplyr,DT,reactable,htmltools,crosstalk (>= 1.2.0).CalendarHeatmapRenderer,LollipopRenderer,DumbbellRenderer,WaffleRenderer,BeeswarmRenderer,BumpRenderer,RadarRenderer,FunnelRenderer,ParallelRenderer) and expanded export/tooltip/rollover/linked-cursor wiring.chart-types,crosstalk-linking, accessibility/troubleshooting articles updated.Test plan
npm test)Rscript -e 'devtools::test()')app/app.RShiny gallery loads and each tab rendersbslib::card()dashboardR CMD build .and inspect the tarball contents — confirm.Rbuildignoreexcludesmd/, smoke PNGs, and other dev artifacts before any CRAN submissionRelease notes
Primary source of truth:
NEWS.mdunder# myIO 1.2.0 (development). Rename the header to# myIO 1.2.0at tag time.Requirements decision
No behavioral requirements doc gate applies — myIO does not maintain a separate
md/requirements/source-of-truth. Exported function roxygen docs +NEWS.md+ vignettes are the contract. Those are updated in-tree.Preflight gate
The
/prskill template's per-feature preflight gate (.claude/state/preflight-{slug}.txt) does not apply here: this is a release-cut PR fromdevelop, not a single-feature branch. Feature-level verification happened at each commit's merge todevelopduring the v1.2 cycle.Pipeline docs archived locally
Two completed v1.2 plans moved to
md/archive/in this session (local only;md/is gitignored):md/archive/calendar-heatmap-plan.mdmd/archive/bslib-fill-and-crosstalk-expansion-plan.md🤖 Generated with Claude Code