Skip to content

[AbsoluteValue] PR3 - rendering & accessibility#3350

Open
handeyeco wants to merge 6 commits intoLEMS-3347/absolute-value-pr2from
LEMS-3347/absolute-value-pr3
Open

[AbsoluteValue] PR3 - rendering & accessibility#3350
handeyeco wants to merge 6 commits intoLEMS-3347/absolute-value-pr2from
LEMS-3347/absolute-value-pr3

Conversation

@handeyeco
Copy link
Contributor

@handeyeco handeyeco commented Mar 13, 2026

Everything is AI

Summary:

This is part of a series of PRs as defined in the first PR:

Initial implementation experiment is Perseus#3304.

Issue: LEMS-3347


Implement rendering and accessibility for absolute-value graph type

Adds the visual component, screen reader strings, equation display, and
unit tests for the absolute-value graph. Depends on PR 1 + PR 2. After this
PR the graph is fully interactive and renderable.

Changes:

  • graphs/absolute-value.tsx (new file) — Implements
    renderAbsoluteValueGraph, the AbsoluteValueGraph component, and two
    exported helpers:
    • getAbsoluteValueCoefficients(coords) — extracts {m, h, v} from the two
      control points, returning undefined if both share the same x. Uses a
      coeffRef to cache the last valid result for transient mid-drag states.
    • getAbsoluteValueKeyboardConstraint(coords, snapStep, pointIndex) —
      keyboard movement constraint that skips any position where the moved point
      would share an x-coordinate with the other point.
    • Renders a single <Plot.OfX y={(x) => m * Math.abs(x - h) + v} /> and
      two components with appropriate aria labels and an
      description.
  • graphs/absolute-value.test.tsx (new file) — Unit tests covering
    coefficient extraction (upward/downward V, varying slopes, same-x returns
    undefined, left/right arm equivalence), keyboard constraint skip behaviour,
    and all screen reader strings (graph label, point labels, description,
    interactive elements text).
  • strings.ts — Adds five new screen reader strings (srAbsoluteValueGraph,
    srAbsoluteValueVertexPoint, srAbsoluteValueSecondPoint,
    srAbsoluteValueDescription, srAbsoluteValueInteractiveElements) with i18n
    context comments, English defaults, and mockStrings implementations.
  • mafs-graph.tsx — Replaces the PR 1 throw stub with
    renderAbsoluteValueGraph.
  • interactive-graph.tsx — Implements getAbsoluteValueEquationString
    (returns y = m.toFixed(3)|x - h.toFixed(3)| + v.toFixed(3)) and wires it
    into getEquationString.
  • types.ts — Exports AbsoluteValueGraphState (previously unexported),
    required by the new component file.

@handeyeco handeyeco self-assigned this Mar 13, 2026
@github-actions
Copy link
Contributor

github-actions bot commented Mar 13, 2026

🗄️ Schema Change: No Changes ✅

@handeyeco handeyeco changed the title fix linting issues [AbsoluteValue] PR3 - rendering & accessibility Mar 13, 2026
@github-actions github-actions bot added item-splitting-change schema-change Attached to PRs when we detect Perseus Schema changes in it labels Mar 13, 2026
@github-actions
Copy link
Contributor

github-actions bot commented Mar 13, 2026

npm Snapshot: Published

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

Example:

pnpm add @khanacademy/perseus@PR3350

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

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

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

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

@github-actions github-actions bot removed the schema-change Attached to PRs when we detect Perseus Schema changes in it label Mar 13, 2026
@github-actions
Copy link
Contributor

github-actions bot commented Mar 13, 2026

Size Change: +1.31 kB (+0.27%)

Total Size: 488 kB

Filename Size Change
packages/perseus/dist/es/index.js 189 kB +1.12 kB (+0.6%)
packages/perseus/dist/es/strings.js 7.66 kB +191 B (+2.56%)
ℹ️ View Unchanged
Filename Size
packages/kas/dist/es/index.js 20.8 kB
packages/keypad-context/dist/es/index.js 1 kB
packages/kmath/dist/es/index.js 5.96 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 kB
packages/perseus-editor/dist/es/index.js 100 kB
packages/perseus-linter/dist/es/index.js 8.82 kB
packages/perseus-score/dist/es/index.js 9.26 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
Contributor

github-actions bot commented Mar 13, 2026

🛠️ Item Splitting: No Changes ✅


const coeffs = getAbsoluteValueCoefficients(coords);
const m = coeffs?.m ?? 1;
const vertexStr = `${srFormatNumber(vertex[X], locale)} comma ${srFormatNumber(vertex[Y], locale)}`;
Copy link

Choose a reason for hiding this comment

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

🟡 The vertex coordinate string in describeAbsoluteValueGraph (line 202) hardcodes the English word "comma" in a template literal, which is then passed as the vertex param to srAbsoluteValueDescription. Because "comma" is embedded inside the interpolated value, it is invisible to translators and will appear untranslated for non-English locales. The fix is to split vertex into separate vertexX/vertexY params in the i18n string, matching the pattern already used by srAbsoluteValueVertexPoint and the angle graph's srAngleGraphAriaDescription.

Extended reasoning...

What the bug is

In describeAbsoluteValueGraph at absolute-value.tsx:202, the vertex coordinate string is built with a hardcoded English word:

const vertexStr = `${srFormatNumber(vertex[X], locale)} comma ${srFormatNumber(vertex[Y], locale)}`;

This vertexStr is then passed as the vertex parameter to strings.srAbsoluteValueDescription({vertex: vertexStr, slope: ...}). The translatable message template is:

"The graph shows an absolute value function with vertex at %(vertex)s and slope %(slope)s."

Since "comma" is embedded inside the %(vertex)s placeholder value, it is part of the JavaScript code, not part of the translatable message. Translators never see it and cannot translate it.

Why existing code does not prevent this

The i18n translation system (used via strings.*) only exposes the message template and its named placeholders to translators. Translators can translate words in the template itself (like "vertex at" or "slope"), but they have no visibility into or control over what values are substituted into placeholders at runtime. The word "comma" is baked into the runtime value, bypassing the translation pipeline entirely.

Proof by comparison with correct patterns

Consider how the angle graph handles the same situation in angle.tsx:202-207:

const srAngleGraphAriaDescription = strings.srAngleGraphAriaDescription({
    angleMeasure,
    vertexX: srFormatNumber(vertex[X], locale),
    vertexY: srFormatNumber(vertex[Y], locale),
    ...
});

The corresponding message template is:

"The angle measure is %(angleMeasure)s degrees with a vertex at %(vertexX)s comma %(vertexY)s..."

Here, "comma" is inside the translatable message string itself, so translators can replace it with the appropriate word in their language. Even within this same file, srAbsoluteValueVertexPoint uses the correct pattern: "Vertex point at %(x)s comma %(y)s." with "comma" in the template, not in the interpolated value.

Impact

For any non-English locale, screen reader users will hear the English word "comma" in the middle of a sentence that is otherwise translated into their language. For example, a Spanish user might hear: "El grafico muestra una funcion de valor absoluto con vertice en 3 comma 5 y pendiente 2" where "comma" should be the locale-appropriate word.

Recommended fix

Refactor srAbsoluteValueDescription to accept vertexX and vertexY as separate parameters, and move "comma" into the translatable message template:

message: "The graph shows an absolute value function with vertex at %(vertexX)s comma %(vertexY)s and slope %(slope)s."

This matches the existing pattern used by srAbsoluteValueVertexPoint, srAbsoluteValueSecondPoint, srAbsoluteValueInteractiveElements, and the angle graph's description string.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

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.

1 participant