Skip to content

[ Interactive Graph | Vector Graph ] PR3: Rendering & Accessibility#3441

Open
SonicScrewdriver wants to merge 9 commits intoLEMS-3971/vector-pr2from
LEMS-3971/vector-pr3
Open

[ Interactive Graph | Vector Graph ] PR3: Rendering & Accessibility#3441
SonicScrewdriver wants to merge 9 commits intoLEMS-3971/vector-pr2from
LEMS-3971/vector-pr3

Conversation

@SonicScrewdriver
Copy link
Copy Markdown
Contributor

@SonicScrewdriver SonicScrewdriver commented Mar 31, 2026

Summary

This PR was created with the help of AI, albeit with heavy oversight and review.

This is part of a series of PRs implementing the vector graph type for the Interactive Graph widget:

PR1 – type definitions and schema
PR2 – state management
▶️ PR3 – rendering & accessibility (this PR)
PR4 – scoring
PR5 – editor support

Issue: LEMS-3971

  • Added the visual rendering component and screen reader support for the vector graph type
  • Extracted a shared PillDragHandle component from the asymptote-specific drag handle, now reused by both vector and exponential/logarithm graphs
  • The graph is now fully visible and interactive for learners — a directed line segment with an arrowhead at the tip, a draggable tip point, and a pill-shaped grab handle on the body for whole-vector translation. This replaces the "Not implemented" stubs from PR 1.

Changes:

New files:

  • graphs/vector.tsx — Rendering component composed from low-level primitives:
    • Body grab handleuseDraggable on a <g> element with transparent hit target, visible line with rounded caps (thickens on hover/focus/drag via CSS class .movable-vector-line), extension line to arrowhead, and a PillDragHandle at 1/3 along the line.
    • Tip pointuseControlPoint hook providing draggable dot with keyboard constraint that prevents overlap with the tail.
    • Arrowhead — rendered last in SVG order (on top of tip dot), positioned 1.5 grid steps past the tip along the vector direction with fixed strokeWidth={2}.
    • Grab handle blurs on drag end (onDragEnd) to prevent lingering focus ring. aria-live="polite" for screen reader announcements.
  • graphs/vector.test.tsx — 10 tests: 5 SR label tests, 2 describeVectorGraph tests, 3 getVectorTipKeyboardConstraint tests.
  • graphs/components/pill-drag-handle.tsx — Shared pill-shaped drag handle extracted from the former AsymptoteDragHandle. Supports arbitrary rotation angles via rotation prop. Used by vector (rotated to match vector angle) and asymptote (0° or 90°).
  • graphs/components/pill-drag-handle.test.tsx — 6 tests covering grip dots, focus ring, rotation, and default state.

Modified files:

  • graphs/components/movable-asymptote.tsx — Now imports PillDragHandle directly instead of the deleted AsymptoteDragHandle wrapper.
  • strings.ts — Four SR strings: srVectorGraph, srVectorPoints, srVectorTipPoint, srVectorGrabHandle.
  • mafs-graph.tsx — Replaced stub with renderVectorGraph() dispatch.
  • interactive-graph.tsx — Replaced stub with getVectorEquationString() showing component form ⟨dx, dy⟩.
  • mafs-styles.css — Added .movable-vector-line (stroke color, weight, rounded caps) and .pill-drag-handle-* classes. Removed old .movable-asymptote-handle-* classes.
  • interactive-graph.stories.tsx — Added Vector Storybook story.

Deleted files:

  • graphs/components/asymptote-drag-handle.tsx — Replaced by PillDragHandle used directly in MovableAsymptote.

Design decisions:

  • Shared PillDragHandle — Extracted from the asymptote-specific handle so vector, exponential, and logarithm graphs all use the same component. The rotation prop replaces the old orientation prop (0° = horizontal, 90° = vertical, arbitrary angles for vector).
  • No focus outline lines — Unlike MovableLine, the vector uses the drag handle pill's focus ring for focus indication, avoiding extra SVG lines on tab focus.
  • Line thickens on hover/focus/drag — Both visible lines apply the movable-dragging CSS class when active, but the arrowhead does not (fixed strokeWidth).
  • Arrowhead on top — Rendered after the tip point in SVG order so it stays visible over the thickened line.
  • CSS-driven styles — Line color (--mafs-blue), weight (--movable-line-stroke-weight), and caps (stroke-linecap: round) are in .movable-vector-line CSS class rather than inline styles.

Test plan:

  • pnpm tsc — no new type errors
  • pnpm test packages/perseus/src/widgets/interactive-graphs/graphs/ — all tests pass including 10 vector tests and 6 pill-drag-handle tests
  • Existing exponential and movable-asymptote tests pass (20 tests) — verified PillDragHandle migration doesn't regress
  • Verified in Storybook: default state, hover (line thickens, arrowhead doesn't), focus (pill focus ring, no extra lines), drag (grab handle blurs on release), arrowhead extends past tip at all angles
  • No existing interactive graph tests regress

Copy link
Copy Markdown

@claude claude bot left a comment

Choose a reason for hiding this comment

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

Claude Code Review

This repository is configured for manual code reviews. Comment @claude review to trigger a review and subscribe this PR to future pushes, or @claude review once for a one-time review.

Tip: disable this comment in your organization's Code Review settings.

@SonicScrewdriver SonicScrewdriver changed the title docs(changeset): Creation of new Vector graph and subcomponents. [ Interactive Graph | Vector Graph ] PR3: Rendering & Accessibility Mar 31, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 31, 2026

🗄️ Schema Change: No Changes ✅

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 31, 2026

🛠️ Item Splitting: No Changes ✅

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 31, 2026

Size Change: +2.21 kB (+0.45%)

Total Size: 497 kB

Filename Size Change
packages/perseus/dist/es/index.js 195 kB +2.03 kB (+1.05%)
packages/perseus/dist/es/strings.js 8.28 kB +183 B (+2.26%)
ℹ️ View Unchanged
Filename Size
packages/kas/dist/es/index.js 20.5 kB
packages/keypad-context/dist/es/index.js 1 kB
packages/kmath/dist/es/index.js 6.21 kB
packages/math-input/dist/es/index.js 98.5 kB
packages/math-input/dist/es/strings.js 1.61 kB
packages/perseus-core/dist/es/index.item-splitting.js 11.9 kB
packages/perseus-core/dist/es/index.js 25.1 kB
packages/perseus-editor/dist/es/index.js 101 kB
packages/perseus-linter/dist/es/index.js 9.3 kB
packages/perseus-score/dist/es/index.js 9.66 kB
packages/perseus-utils/dist/es/index.js 403 B
packages/pure-markdown/dist/es/index.js 1.39 kB
packages/simple-markdown/dist/es/index.js 6.71 kB

compressed-size-action

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 31, 2026

npm Snapshot: Published

Good news!! We've packaged up the latest commit from this PR (d99a420) and published it to npm. You
can install it using the tag PR3441.

Example:

pnpm add @khanacademy/perseus@PR3441

If you are working in Khan Academy's frontend, you can run the below command.

./dev/tools/bump_perseus_version.ts -t PR3441

If you are working in Khan Academy's webapp, you can run the below command.

./dev/tools/bump_perseus_version.js -t PR3441

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants