Skip to content

#41 — feat(renderer): DAG keyboard accessibility + #40 renderer hygiene - #49

Merged
cmbays merged 2 commits into
mainfrom
renderer-40-41-polish
May 24, 2026
Merged

cmbays merged 2 commits into
mainfrom
renderer-40-41-polish

Conversation

@cmbays

@cmbays cmbays commented May 24, 2026

Copy link
Copy Markdown
Contributor

Summary

Bundles cute-dbt#41 (DAG node keyboard accessibility) with the
lightweight subset of cute-dbt#40's post-merge bot findings. The
substantial Gemini-2+4 sqlparser AST refactor is deferred to a
dedicated follow-up PR — #40 stays open until that ships.

Closes / refs

  • Closes #41 — DAG node keyboard accessibility
  • Refs renderer hygiene: fold post-merge bot findings on PR #38 into the next render-layer touch #40 — bot findings folded:
    • CR-2: dropped dead NodePayload::ref_name field (template never read it)
    • CR-3: dropped unused t parameter from buildMermaidSource(m)
    • CR-4: hoisted mermaid.initialize to the $(function) startup block
    • CR-5: visible notice for fixtures with format: csv / format: sql
      (previously rendered an empty table silently)
    • CR-6: dropped unused _model_name parameter from find_import_node_id
      (the caller no longer threads it either)
  • Deferred to follow-up PR (will close renderer hygiene: fold post-merge bot findings on PR #38 into the next render-layer touch #40): Gemini-2+4 AST refactor of
    is_simple_from_select + extract_table_leaf_refs. That change needs an
    engine-API decision (renderer re-parses the body slice as a standalone
    statement, vs. CteNode exposing a Query AST accessor) and deserves
    its own focused review.

#41 — DAG keyboard accessibility

  • Each g.dag-node SVG group gets tabindex="0", role="button", and an
    aria-label of the form Inspect DAG node: <id>.
  • A new activate() closure replaces the inline click handler; it is bound
    to both click and a keydown handler that fires on Enter and Space
    (with preventDefault on Space so the page doesn't scroll).
  • Focus ring: g.dag-node:focus-visible { outline: 2px dashed #1d7484; outline-offset: 3px; }
    isolates keyboard focus from mouse clicks so a click doesn't leave a ring
    stuck on the clicked node.
  • Subtitle copy updated to Click a node (or Tab to it and press Enter) to inspect.

#40 CR-5 — fixture-format notice

When a UnitTestGiven / UnitTestExpect has format set to csv or
sql, the underlying rows is a string rather than the column-keyed
array the table renderer expects. Previously unwrapRows silently
returned [] and the user saw an empty table. Now isNonArrayFormatFixture(obj)
detects the case and the render path emits a .format-notice div
explaining that the fixture is intentional but not yet visualized in
cute-dbt v0.1.

Verification

  • cargo nextest run — 315 / 315 passing (snapshots rebaselined)
  • cargo test --test bdd — 27 scenarios / 153 steps passing (headless
    zero-egress + resource-ref lint both green; new CSS/JS introduces no
    external refs)
  • crap4rs --coverage lcov.info — PASS (0 functions above threshold;
    worst 11.0, well under strict-15 on src/adapters/render.rs)
  • cargo deny check — advisories ok / bans ok / licenses ok / sources ok
  • Playwright smoke pass over http://127.0.0.1:8742/jaffle-shop-report.html:
    • All 3 DAG nodes carry the expected ARIA attributes (tabindex="0",
      role="button", populated aria-label)
    • target.focus(); keyboard.press('Enter') activates the node-detail
      panel (verified data-node-id on .node-detail matches the focused
      node)
    • Synthesized keydown with key=' ' activates with defaultPrevented: true
    • focus-visible computed style matches the rule
      (2px dashed rgb(29,116,132), offset 3px)
    • .format-notice injected into the DOM picks up the right computed
      background / border / code styling
    • 0 console errors / 0 console warnings
  • examples/jaffle-shop-report.html regenerated for the byte-equality gate.

Test plan

  • Snapshots rebaselined and accepted
  • BDD outer loop green (no new resource refs)
  • crap4rs strict-15 green on render.rs
  • Playwright a11y probe (ARIA attrs + keyboard activation + focus-visible)
  • Byte-equal example report regenerated
  • CodeRabbit + Gemini review pass (a11y semantics are subtle — proper
    bot review is worth waiting for here vs. PR 11.5 which was merged under
    CR rate-limiting)

🤖 Generated with Claude Code

Bundles cute-dbt#41 (DAG node keyboard accessibility) with the
lightweight subset of cute-dbt#40's post-merge bot findings. The
Gemini-2+4 sqlparser AST refactor is deferred to a dedicated
follow-up PR — #40 stays open until that ships.

#41 — DAG nodes now tab-focusable with role="button", populated
aria-label, and Enter/Space keydown handlers that share the same
activation closure as click. Focus-visible outline isolates keyboard
focus from mouse clicks. Subtitle copy updated to mention Tab+Enter.

#40 CR-2/3/4/5/6:
- Dropped dead NodePayload::ref_name (template never read node.ref).
- Dropped unused `t` param from buildMermaidSource(m, t).
- Hoisted mermaid.initialize to the startup block (one call total
  rather than per-renderDag).
- Added visible .format-notice for fixtures with format: csv / sql
  (previously rendered as silently empty tables).
- Dropped unused _model_name param from find_import_node_id; the
  caller (build_test_payload) no longer threads it either.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
@coderabbitai

coderabbitai Bot commented May 24, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

@cmbays, we couldn't start this review because you've used your available PR reviews for now.

Your plan currently allows 1 review/hour. Refill in 50 minutes and 49 seconds.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more review capacity refills, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than trial, open-source, and free plans. In all cases, review capacity refills continuously over time.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7a3c076e-1bf7-4b11-8004-908648acee59

📥 Commits

Reviewing files that changed from the base of the PR and between 2e58f2a and 68d0a52.

⛔ Files ignored due to path filters (2)
  • tests/snapshots/golden_report__rendered_report_skeleton.snap is excluded by !**/*.snap
  • tests/snapshots/render_integration__rendered_chrome_jaffle_shop.snap is excluded by !**/*.snap
📒 Files selected for processing (3)
  • examples/jaffle-shop-report.html
  • src/adapters/render.rs
  • templates/report.html
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch renderer-40-41-polish

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request improves keyboard accessibility for the CTE dependency DAG and adds support for displaying notices when non-array fixture formats (CSV, SQL) are used. It also refactors the Rust adapter to remove unused fields and optimizes Mermaid.js initialization. Feedback suggests restoring focus to the selected node after the DAG re-renders to ensure a smooth keyboard navigation experience and removing hardcoded version strings from user-facing notices to simplify future maintenance.

Comment thread templates/report.html
Comment on lines 621 to 622
window.mermaid.render(id, src).then(function (out) {
$host.html(out.svg);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

When a DAG node is activated via keyboard, the renderDag() call re-renders the entire SVG, which causes the browser to lose focus on the currently active element. To maintain a good keyboard navigation experience, focus should be restored to the newly rendered node that matches state.selectedNodeId after the DOM is updated. Since renderDag is asynchronous and the nodes are bound in a subsequent step, you'll need to ensure focus is restored after bindDagNodeClicks completes.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed — see 68d0a52. The diagnosis is correct: renderDag() rebuilds the SVG via mermaid.render → $host.html(out.svg), which detaches every previously focused element.

Fix shape: at the end of bindDagNodeClicks(svgHost, ...), after the per-node loop, look up g.dag-node[data-node-id="<state.selectedNodeId>"] in the freshly rendered SVG and call .focus({ preventScroll: true }) on it. Defensive escape on the selector value handles any CTE name with a quote (rare but possible).

Specifically did not track an activatedViaKeyboard flag — :focus-visible already reflects the originating input heuristic, so programmatic .focus() after a click event leaves the keyboard/pointer signal untouched. A mouse activation will not draw a stuck outline.

Verified via Playwright: focus a node, dispatch Enter, wait for re-render — document.activeElement is the new g.dag-node with the same data-node-id after the SVG was wholly replaced.

Comment thread templates/report.html Outdated
return $("<div>").addClass("format-notice")
.append($("<span>").text("Fixture uses "))
.append($("<code>").text("format: " + format))
.append($("<span>").text(" — not yet visualized in cute-dbt v0.1. See the raw fixture in the manifest."));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The version string v0.1 is hardcoded in the user-facing notice. To avoid maintenance overhead when the project version is bumped, consider removing the specific version number or using a variable if available.

Suggested change
.append($("<span>").text(" — not yet visualized in cute-dbt v0.1. See the raw fixture in the manifest."));
.append($("<span>").text(" — not yet visualized in cute-dbt. See the raw fixture in the manifest."));

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed — see 68d0a52. Suggestion adopted essentially verbatim; the notice now reads:

Fixture uses format: csv — not yet visualized in cute-dbt. See the raw fixture in the manifest.

Reasoning: per the cute-dbt phased roadmap, a future minor (v0.2 or v0.3) may add format-aware rendering, at which point this notice goes away entirely. Anchoring on "v0.1" specifically would either rot or force a copy-edit every minor; dropping the version is the lighter-weight maintenance posture.

…d v0.1

Addresses Gemini's two MEDIUM findings on PR 49:

- bindDagNodeClicks now refocuses the g.dag-node matching
  state.selectedNodeId after every render. The previous keyboard
  activation path lost focus because renderDag() rebuilds the SVG
  and detaches the focused element. :focus-visible reflects the
  originating input heuristic, so mouse activation still won't draw
  a stuck outline.

- buildFormatNotVisualizedNotice no longer hardcodes "v0.1" in the
  user-facing copy. If v0.2 ships format-aware rendering this notice
  goes away entirely; the version string adds maintenance overhead
  without semantic value.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

renderer hygiene: fold post-merge bot findings on PR #38 into the next render-layer touch

1 participant