Skip to content

Commit 9b4c194

Browse files
authored
[LX] Make static graphs' interactive elements visually disabled (#2287)
## Summary: Right now, a static graph looks exactly the same as a regular graph that can be interacted with. To distinguish graphs as disabled visually when in static mode, update the color so that the graph elements use Wonder Blocks `offBlack50` when in static mode. I also did some refactoring to make the way we code colors uniform across all the graphs. NOTE: You may notice that `offBlack50` is not the best for contrast. However, it needs to be distinguishable from our regular interactive blue so it's clearly disabled. When using a darker gray, it looks the same as our WB blue in grayscale. We had to make a call here, and we decided to make it lighter and more distinguishable even if it sacrifices color contrast. Issue: https://khanacademy.atlassian.net/browse/LEMS-2784 ## Test plan: `yarn jest packages/perseus/src/widgets/interactive-graphs/interactive-graph-static.test.tsx` Storybook - Go to http://localhost:6006/?path=/docs/perseuseditor-widgets-interactive-graph--docs - For all graph types, use the "Static" toggle at the top of the editor to set the graph into static mode. - Confirm that the interactive elements are no longer blue. | Interactive | Static | | --- | --- | | <img width="442" alt="Screenshot 2025-03-06 at 5 54 08 PM" src="https://github.com/user-attachments/assets/abbd2b0d-5a30-47aa-b05b-ce938ca93f06" /> | <img width="441" alt="Screenshot 2025-03-06 at 5 54 14 PM" src="https://github.com/user-attachments/assets/3674a65c-5e1f-40a1-ab9a-6d853f529d3a" /> | Author: nishasy Reviewers: nishasy, SonicScrewdriver, catandthemachines Required Reviewers: Approved By: SonicScrewdriver Checks: ✅ 8 checks were successful Pull Request URL: #2287
1 parent 7ef0dae commit 9b4c194

18 files changed

+464
-96
lines changed

.changeset/rotten-cups-pay.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@khanacademy/perseus": patch
3+
---
4+
5+
[LX] Make static graphs' interactive elements visually disabled

packages/perseus/src/widgets/interactive-graphs/__snapshots__/interactive-graph.test.tsx.snap

Lines changed: 30 additions & 30 deletions
Large diffs are not rendered by default.

packages/perseus/src/widgets/interactive-graphs/graphs/angle.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ export function renderAngleGraph(
4545

4646
function AngleGraph(props: AngleGraphProps) {
4747
const {dispatch, graphState} = props;
48-
const {graphDimensionsInPixels} = useGraphConfig();
48+
const {graphDimensionsInPixels, interactiveColor} = useGraphConfig();
4949
const i18n = usePerseusI18n();
5050
const id = React.useId();
5151
const descriptionId = id + "-description";
@@ -83,14 +83,15 @@ function AngleGraph(props: AngleGraphProps) {
8383
start={startPtPx}
8484
end={endPtPx}
8585
style={{
86-
stroke: "var(--movable-line-stroke-color)",
86+
stroke: interactiveColor,
8787
strokeWidth: "var(--movable-line-stroke-weight)",
8888
}}
89+
testId="angle-graph__line"
8990
/>
9091
<Vector
9192
tail={angleLines[i][1]}
9293
tip={endExtend}
93-
color={"var(--movable-line-stroke-color)"}
94+
testId="angle-graph__vector"
9495
/>
9596
</g>
9697
);

packages/perseus/src/widgets/interactive-graphs/graphs/circle.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,8 @@ function MovableCircle(props: {
129129
onMove: (newCenter: vec.Vector2) => unknown;
130130
}) {
131131
const {id, ariaLabel, ariaDescribedBy, center, radius, onMove} = props;
132-
const {snapStep, disableKeyboardInteraction} = useGraphConfig();
132+
const {snapStep, disableKeyboardInteraction, interactiveColor} =
133+
useGraphConfig();
133134
const [focused, setFocused] = React.useState(false);
134135

135136
const draggableRef = useRef<SVGGElement>(null);
@@ -170,6 +171,8 @@ function MovableCircle(props: {
170171
cy={centerPx[Y]}
171172
rx={radiiPx[X]}
172173
ry={radiiPx[Y]}
174+
stroke={interactiveColor}
175+
data-testid="movable-circle__circle"
173176
/>
174177
<DragHandle center={center} dragging={dragging} focused={focused} />
175178
</g>
@@ -186,7 +189,7 @@ function DragHandle(props: {
186189
const {center, dragging, focused} = props;
187190

188191
const [centerPx] = useTransformVectorsToPixels(center);
189-
const {markings} = useGraphConfig();
192+
const {markings, interactiveColor} = useGraphConfig();
190193

191194
const cornerRadius = Math.min(...dragHandleDimensions) / 2;
192195
const topLeft = vec.sub(centerPx, vec.scale(dragHandleDimensions, 0.5));
@@ -204,6 +207,8 @@ function DragHandle(props: {
204207
height={dragHandleDimensions[Y]}
205208
rx={cornerRadius}
206209
ry={cornerRadius}
210+
fill={interactiveColor}
211+
data-testid="movable-circle__handle"
207212
/>
208213
{dragHandleDotPositions.map((offsetPx) => {
209214
const [xPx, yPx] = vec.add(offsetPx, centerPx);

packages/perseus/src/widgets/interactive-graphs/graphs/components/__snapshots__/movable-line.test.tsx.snap

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ exports[`Rendering Does NOT render extensions of line when option is disabled 1`
5858
aria-hidden="true"
5959
class=""
6060
data-testid="movable-line__line"
61-
style="stroke: var(--movable-line-stroke-color); stroke-width: var(--movable-line-stroke-weight);"
61+
style="stroke: var(--mafs-blue); stroke-width: var(--movable-line-stroke-weight);"
6262
x1="0"
6363
x2="0"
6464
y1="0"
@@ -77,7 +77,7 @@ exports[`Rendering Does NOT render extensions of line when option is disabled 1`
7777
aria-hidden="true"
7878
class="movable-point"
7979
data-testid="movable-point"
80-
style="--movable-point-color: #1865f2;"
80+
style="--movable-point-color: var(--mafs-blue);"
8181
>
8282
<circle
8383
class="movable-point-hitbox"
@@ -105,14 +105,14 @@ exports[`Rendering Does NOT render extensions of line when option is disabled 1`
105105
cx="0"
106106
cy="0"
107107
data-testid="movable-point__center"
108-
style="fill: #1865f2;"
108+
style="fill: var(--mafs-blue);"
109109
/>
110110
</g>
111111
<g
112112
aria-hidden="true"
113113
class="movable-point"
114114
data-testid="movable-point"
115-
style="--movable-point-color: #1865f2;"
115+
style="--movable-point-color: var(--mafs-blue);"
116116
>
117117
<circle
118118
class="movable-point-hitbox"
@@ -140,7 +140,7 @@ exports[`Rendering Does NOT render extensions of line when option is disabled 1`
140140
cx="0"
141141
cy="0"
142142
data-testid="movable-point__center"
143-
style="fill: #1865f2;"
143+
style="fill: var(--mafs-blue);"
144144
/>
145145
</g>
146146
</svg>
@@ -206,7 +206,7 @@ exports[`Rendering Does NOT render extensions of line when option is not provide
206206
aria-hidden="true"
207207
class=""
208208
data-testid="movable-line__line"
209-
style="stroke: var(--movable-line-stroke-color); stroke-width: var(--movable-line-stroke-weight);"
209+
style="stroke: var(--mafs-blue); stroke-width: var(--movable-line-stroke-weight);"
210210
x1="0"
211211
x2="0"
212212
y1="0"
@@ -225,7 +225,7 @@ exports[`Rendering Does NOT render extensions of line when option is not provide
225225
aria-hidden="true"
226226
class="movable-point"
227227
data-testid="movable-point"
228-
style="--movable-point-color: #1865f2;"
228+
style="--movable-point-color: var(--mafs-blue);"
229229
>
230230
<circle
231231
class="movable-point-hitbox"
@@ -253,14 +253,14 @@ exports[`Rendering Does NOT render extensions of line when option is not provide
253253
cx="0"
254254
cy="0"
255255
data-testid="movable-point__center"
256-
style="fill: #1865f2;"
256+
style="fill: var(--mafs-blue);"
257257
/>
258258
</g>
259259
<g
260260
aria-hidden="true"
261261
class="movable-point"
262262
data-testid="movable-point"
263-
style="--movable-point-color: #1865f2;"
263+
style="--movable-point-color: var(--mafs-blue);"
264264
>
265265
<circle
266266
class="movable-point-hitbox"
@@ -288,7 +288,7 @@ exports[`Rendering Does NOT render extensions of line when option is not provide
288288
cx="0"
289289
cy="0"
290290
data-testid="movable-point__center"
291-
style="fill: #1865f2;"
291+
style="fill: var(--mafs-blue);"
292292
/>
293293
</g>
294294
</svg>
@@ -354,15 +354,16 @@ exports[`Rendering Does render extensions of line when option is enabled 1`] = `
354354
aria-hidden="true"
355355
class=""
356356
data-testid="movable-line__line"
357-
style="stroke: var(--movable-line-stroke-color); stroke-width: var(--movable-line-stroke-weight);"
357+
style="stroke: var(--mafs-blue); stroke-width: var(--movable-line-stroke-weight);"
358358
x1="0"
359359
x2="0"
360360
y1="0"
361361
y2="0"
362362
/>
363363
</g>
364364
<g
365-
style="stroke: var(--movable-line-stroke-color); stroke-width: 2;"
365+
data-testid="movable-line__vector"
366+
style="stroke: var(--mafs-blue); stroke-width: 2;"
366367
>
367368
<line
368369
aria-hidden="true"
@@ -385,13 +386,14 @@ exports[`Rendering Does render extensions of line when option is enabled 1`] = `
385386
stroke-linecap="round"
386387
stroke-linejoin="round"
387388
stroke-width="2px"
388-
style="stroke: var(--movable-line-stroke-color);"
389+
style="stroke: var(--mafs-blue);"
389390
/>
390391
</g>
391392
</g>
392393
</g>
393394
<g
394-
style="stroke: var(--movable-line-stroke-color); stroke-width: 2;"
395+
data-testid="movable-line__vector"
396+
style="stroke: var(--mafs-blue); stroke-width: 2;"
395397
>
396398
<line
397399
aria-hidden="true"
@@ -414,7 +416,7 @@ exports[`Rendering Does render extensions of line when option is enabled 1`] = `
414416
stroke-linecap="round"
415417
stroke-linejoin="round"
416418
stroke-width="2px"
417-
style="stroke: var(--movable-line-stroke-color);"
419+
style="stroke: var(--mafs-blue);"
418420
/>
419421
</g>
420422
</g>
@@ -431,7 +433,7 @@ exports[`Rendering Does render extensions of line when option is enabled 1`] = `
431433
aria-hidden="true"
432434
class="movable-point"
433435
data-testid="movable-point"
434-
style="--movable-point-color: #1865f2;"
436+
style="--movable-point-color: var(--mafs-blue);"
435437
>
436438
<circle
437439
class="movable-point-hitbox"
@@ -459,14 +461,14 @@ exports[`Rendering Does render extensions of line when option is enabled 1`] = `
459461
cx="0"
460462
cy="0"
461463
data-testid="movable-point__center"
462-
style="fill: #1865f2;"
464+
style="fill: var(--mafs-blue);"
463465
/>
464466
</g>
465467
<g
466468
aria-hidden="true"
467469
class="movable-point"
468470
data-testid="movable-point"
469-
style="--movable-point-color: #1865f2;"
471+
style="--movable-point-color: var(--mafs-blue);"
470472
>
471473
<circle
472474
class="movable-point-hitbox"
@@ -494,7 +496,7 @@ exports[`Rendering Does render extensions of line when option is enabled 1`] = `
494496
cx="0"
495497
cy="0"
496498
data-testid="movable-point__center"
497-
style="fill: #1865f2;"
499+
style="fill: var(--mafs-blue);"
498500
/>
499501
</g>
500502
</svg>

packages/perseus/src/widgets/interactive-graphs/graphs/components/angle-indicators.tsx

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {vec} from "mafs";
44
import * as React from "react";
55

66
import {segmentsIntersect} from "../../math";
7+
import useGraphConfig from "../../reducer/use-graph-config";
78
import {getIntersectionOfRayWithBox as getRangeIntersectionVertex} from "../utils";
89

910
import {MafsCssTransformWrapper} from "./css-transform-wrapper";
@@ -231,6 +232,12 @@ export const Angle = ({
231232
radius,
232233
);
233234

235+
// Determine the color style for the arc when interactive vs disabled
236+
const {disableKeyboardInteraction} = useGraphConfig();
237+
const arcClassName = disableKeyboardInteraction
238+
? "angle-arc-static"
239+
: "angle-arc-interactive";
240+
234241
return (
235242
<>
236243
<defs>
@@ -251,10 +258,10 @@ export const Angle = ({
251258
start={[x1, y1]}
252259
vertex={[x2, y2]}
253260
end={[x3, y3]}
254-
className={"arc-right-angle"}
261+
className={arcClassName}
255262
/>
256263
) : (
257-
<Arc arc={arc} className={"angle-arc"} />
264+
<Arc arc={arc} className={arcClassName} />
258265
)}
259266
{showAngles && (
260267
<TextLabel x={textX} y={textY} color={color.blue}>
@@ -291,6 +298,7 @@ const RightAngleSquare = ({
291298
strokeWidth={0.02}
292299
fill="none"
293300
className={className}
301+
data-testid="angle-indicators__right-angle"
294302
/>
295303
</MafsCssTransformWrapper>
296304
);
@@ -310,6 +318,7 @@ const Arc = ({arc, className}: {arc: string; className?: string}) => {
310318
strokeWidth={0.02}
311319
fill="none"
312320
className={className}
321+
data-testid="angle-indicators__arc"
313322
/>
314323
</MafsCssTransformWrapper>
315324
);

packages/perseus/src/widgets/interactive-graphs/graphs/components/movable-line.tsx

Lines changed: 16 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ type Props = {
2525
};
2626
// Extra graph information to be read by screen readers
2727
ariaDescribedBy?: string;
28-
color?: string;
2928
/* Extends the line to the edge of the graph with an arrow */
3029
extend?: {
3130
start: boolean;
@@ -40,7 +39,6 @@ export const MovableLine = (props: Props) => {
4039
points: [start, end],
4140
ariaLabels,
4241
ariaDescribedBy,
43-
color,
4442
extend,
4543
onMoveLine = () => {},
4644
onMovePoint = () => {},
@@ -76,7 +74,6 @@ export const MovableLine = (props: Props) => {
7674
ariaLive: ariaLives[0],
7775
point: start,
7876
sequenceNumber: 1,
79-
color,
8077
onMove: (p) => {
8178
setAriaLives(["polite", "off", "off"]);
8279
onMovePoint(0, p);
@@ -94,7 +91,6 @@ export const MovableLine = (props: Props) => {
9491
ariaLive: ariaLives[1],
9592
point: end,
9693
sequenceNumber: 2,
97-
color,
9894
onMove: (p) => {
9995
setAriaLives(["off", "polite", "off"]);
10096
onMovePoint(1, p);
@@ -113,7 +109,6 @@ export const MovableLine = (props: Props) => {
113109
ariaLive={ariaLives[2]}
114110
start={start}
115111
end={end}
116-
stroke={color}
117112
extend={extend}
118113
onMove={(delta) => {
119114
setAriaLives(["off", "off", "polite"]);
@@ -133,8 +128,6 @@ export const MovableLine = (props: Props) => {
133128
);
134129
};
135130

136-
const defaultStroke = "var(--movable-line-stroke-color)";
137-
138131
type LineProps = {
139132
start: vec.Vector2;
140133
end: vec.Vector2;
@@ -148,28 +141,20 @@ type LineProps = {
148141
start: boolean;
149142
end: boolean;
150143
};
151-
stroke?: string | undefined;
152144
onMove: (delta: vec.Vector2) => unknown;
153145
};
154146

155147
const Line = (props: LineProps) => {
156-
const {
157-
start,
158-
end,
159-
ariaLabel,
160-
ariaDescribedBy,
161-
ariaLive,
162-
extend,
163-
stroke = defaultStroke,
164-
onMove,
165-
} = props;
148+
const {start, end, ariaLabel, ariaDescribedBy, ariaLive, extend, onMove} =
149+
props;
166150

167151
const [startPtPx, endPtPx] = useTransformVectorsToPixels(start, end);
168152
const {
169153
range,
170154
graphDimensionsInPixels,
171155
snapStep,
172156
disableKeyboardInteraction,
157+
interactiveColor,
173158
} = useGraphConfig();
174159

175160
let startExtend: vec.Vector2 | undefined = undefined;
@@ -235,7 +220,7 @@ const Line = (props: LineProps) => {
235220
start={startPtPx}
236221
end={endPtPx}
237222
style={{
238-
stroke,
223+
stroke: interactiveColor,
239224
strokeWidth: "var(--movable-line-stroke-weight)",
240225
}}
241226
className={dragging ? "movable-dragging" : ""}
@@ -245,9 +230,19 @@ const Line = (props: LineProps) => {
245230

246231
{/* Draw extension vectors outside of movable area */}
247232
{startExtend && (
248-
<Vector tail={start} tip={startExtend} color={stroke} />
233+
<Vector
234+
tail={start}
235+
tip={startExtend}
236+
testId="movable-line__vector"
237+
/>
238+
)}
239+
{endExtend && (
240+
<Vector
241+
tail={end}
242+
tip={endExtend}
243+
testId="movable-line__vector"
244+
/>
249245
)}
250-
{endExtend && <Vector tail={end} tip={endExtend} color={stroke} />}
251246
</>
252247
);
253248
};

0 commit comments

Comments
 (0)