Skip to content

Commit f48e917

Browse files
authored
[Interactive Graph] | (DX) | Shorten interactive-graph-editor.tsx to avoid 1000+ line lint error (#2768)
## Summary: I wanted to add a couple lines of code in a different branch, and I got a lint error saying that this file (interactive-graph-editor.tsx) is too long. Shortening it here by breaking out the individual graph specifications into a separate file. NOTE: I did not make any improvements here like removing Aphrodite styles or anything. This is _only_ moving lines into a different file, unchanged. Issue: none | Type | Before (top) | Before (bottom) | After | | --- | --- | --- | --- | | Angle | <img width="353" height="577" alt="Screenshot 2025-08-06 at 2 43 05 PM" src="https://github.com/user-attachments/assets/2bb42983-043e-4349-b216-0d1e8fd95492" /> | <img width="362" height="517" alt="Screenshot 2025-08-06 at 2 43 11 PM" src="https://github.com/user-attachments/assets/539eb921-9fee-4caa-ad59-a7ff9c7cf464" /> | <img width="346" height="587" alt="Screenshot 2025-08-06 at 2 43 19 PM" src="https://github.com/user-attachments/assets/ce93976f-647b-439c-8131-a0de4e789248" /> | | Point | <img width="353" height="556" alt="Screenshot 2025-08-06 at 2 43 50 PM" src="https://github.com/user-attachments/assets/c224a397-70ea-4128-814d-c688bef546ec" /> | N/A | <img width="344" height="523" alt="Screenshot 2025-08-06 at 2 43 55 PM" src="https://github.com/user-attachments/assets/1b545c7f-5346-41e2-a67d-93be1cbe2093" /> | | Polygon | <img width="343" height="607" alt="Screenshot 2025-08-06 at 2 44 10 PM" src="https://github.com/user-attachments/assets/928c001a-c7ba-40dc-b697-8bddc19de444" /> | <img width="349" height="478" alt="Screenshot 2025-08-06 at 2 44 14 PM" src="https://github.com/user-attachments/assets/2fdacd95-fb9e-4996-ba7c-37b34f039af1" /> | <img width="343" height="654" alt="Screenshot 2025-08-06 at 2 44 24 PM" src="https://github.com/user-attachments/assets/5dd85a56-e4c5-4092-9067-776317de47f9" /> | | Segment | <img width="346" height="522" alt="Screenshot 2025-08-06 at 2 44 51 PM" src="https://github.com/user-attachments/assets/8b7250bb-99f0-49db-a45f-cf2559719458" /> | N/A | <img width="346" height="524" alt="Screenshot 2025-08-06 at 2 44 56 PM" src="https://github.com/user-attachments/assets/54279340-0eb5-4206-ad5b-cea8f0e231b2" /> | ## Test plan: `pnpm jest` Storybook http://localhost:6006/?path=/docs/widgets-interactive-graph-editor-demo--docs - Confirm that all the graph types' specific settings still work right Author: nishasy Reviewers: nishasy, ivyolamit, benchristel, SonicScrewdriver Required Reviewers: Approved By: ivyolamit Checks: ✅ 10 checks were successful Pull Request URL: #2768
1 parent 80758e4 commit f48e917

File tree

7 files changed

+551
-585
lines changed

7 files changed

+551
-585
lines changed

.changeset/strange-emus-nail.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@khanacademy/perseus-editor": patch
3+
---
4+
5+
[Interactive Graph] | (DX) | Shorten interactive-graph-editor.tsx to avoid 1000+ line lint error
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
import {components} from "@khanacademy/perseus";
2+
import {View} from "@khanacademy/wonder-blocks-core";
3+
import {OptionItem, SingleSelect} from "@khanacademy/wonder-blocks-dropdown";
4+
import {Checkbox} from "@khanacademy/wonder-blocks-form";
5+
import {LabelSmall} from "@khanacademy/wonder-blocks-typography";
6+
import * as React from "react";
7+
import invariant from "tiny-invariant";
8+
9+
import styles from "../interactive-graph-editor.module.css";
10+
import LabeledRow from "../locked-figures/labeled-row";
11+
12+
import type {Props as EditorProps} from "../interactive-graph-editor";
13+
import type {
14+
PerseusGraphType,
15+
PerseusGraphTypeAngle,
16+
} from "@khanacademy/perseus-core";
17+
18+
const {InfoTip} = components;
19+
20+
interface Props {
21+
correct: PerseusGraphTypeAngle;
22+
graph: PerseusGraphType | undefined;
23+
onChange: (props: Partial<EditorProps>) => void;
24+
}
25+
26+
export default function AngleAnswerOptions({correct, graph, onChange}: Props) {
27+
return (
28+
<>
29+
<View className={styles.row}>
30+
<Checkbox
31+
label={<LabelSmall>Show angle measures</LabelSmall>}
32+
checked={
33+
// Don't show indeterminate checkbox state
34+
!!correct?.showAngles
35+
}
36+
onChange={(newValue) => {
37+
if (graph?.type === "angle") {
38+
invariant(
39+
correct.type === "angle",
40+
`Expected graph type to be angle, but got ${correct.type}`,
41+
);
42+
onChange({
43+
correct: {
44+
...correct,
45+
showAngles: newValue,
46+
},
47+
graph: {
48+
...graph,
49+
showAngles: newValue,
50+
},
51+
});
52+
}
53+
}}
54+
/>
55+
<InfoTip>
56+
<p>Displays the interior angle measures.</p>
57+
</InfoTip>
58+
</View>
59+
<View className={styles.row}>
60+
<Checkbox
61+
label={<LabelSmall>Allow reflex angles</LabelSmall>}
62+
checked={
63+
// Don't show indeterminate checkbox state
64+
!!correct?.allowReflexAngles
65+
}
66+
onChange={(newValue) => {
67+
invariant(
68+
correct.type === "angle",
69+
`Expected graph type to be angle, but got ${correct.type}`,
70+
);
71+
invariant(
72+
graph?.type === "angle",
73+
`Expected graph type to be angle, but got ${graph?.type}`,
74+
);
75+
76+
const update = {
77+
allowReflexAngles: newValue,
78+
};
79+
80+
onChange({
81+
correct: {
82+
...correct,
83+
...update,
84+
},
85+
graph: {
86+
...graph,
87+
...update,
88+
},
89+
});
90+
}}
91+
/>
92+
<InfoTip>
93+
<p>Allow students to be able to create reflex angles.</p>
94+
</InfoTip>
95+
</View>
96+
<LabeledRow label="Student answer must">
97+
<SingleSelect
98+
selectedValue={correct.match || "exact"}
99+
onChange={(newValue) => {
100+
invariant(
101+
correct.type === "angle",
102+
`Expected graph type to be angle, but got ${correct.type}`,
103+
);
104+
onChange({
105+
correct: {
106+
...correct,
107+
// TODO(benchristel): this cast is necessary
108+
// because "exact" is not actually a valid
109+
// value for `match`; a value of undefined
110+
// means exact matching. The code happens
111+
// to work because "exact" falls through
112+
// to the correct else branch when scoring
113+
match: newValue as PerseusGraphTypeAngle["match"],
114+
},
115+
});
116+
}}
117+
// Never uses placeholder, always has value
118+
placeholder=""
119+
className={styles.singleSelectShort}
120+
>
121+
<OptionItem value="exact" label="match exactly" />
122+
<OptionItem value="congruent" label="be congruent" />
123+
</SingleSelect>
124+
<InfoTip>
125+
<p>
126+
Congruency requires only that the angle measures are the
127+
same. An exact match implies congruency, but also
128+
requires that the angles have the same orientation and
129+
that the vertices are in the same position.
130+
</p>
131+
</InfoTip>
132+
</LabeledRow>
133+
</>
134+
);
135+
}
Lines changed: 50 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,62 @@
11
import {OptionItem, SingleSelect} from "@khanacademy/wonder-blocks-dropdown";
2-
import {StyleSheet} from "aphrodite";
32
import * as React from "react";
43

54
import {UNLIMITED, parsePointCount} from "../../../util/points";
5+
import styles from "../interactive-graph-editor.module.css";
6+
import LabeledRow from "../locked-figures/labeled-row";
67

7-
import type {PointValue} from "../../../util/points";
8+
import type {Props as EditorProps} from "../interactive-graph-editor";
9+
import type {
10+
PerseusGraphType,
11+
PerseusGraphTypePoint,
12+
} from "@khanacademy/perseus-core";
813

9-
const GraphPointsCountSelector = ({
10-
numPoints = 1,
11-
onChange,
12-
}: {
13-
numPoints?: PointValue;
14-
onChange: (points: PointValue) => void;
15-
}) => {
14+
interface Props {
15+
correct: PerseusGraphTypePoint;
16+
graph: PerseusGraphType | undefined;
17+
onChange: (props: Partial<EditorProps>) => void;
18+
}
19+
20+
const GraphPointsCountSelector = ({correct, graph, onChange}: Props) => {
1621
return (
17-
<SingleSelect
18-
selectedValue={`${numPoints}`}
19-
onChange={(newValue) => {
20-
onChange(parsePointCount(newValue));
21-
}}
22-
// Never uses placeholder, always has value
23-
placeholder=""
24-
style={styles.singleSelectShort}
25-
>
26-
{[
27-
...[...Array(7).keys()].map((n) => (
22+
<LabeledRow label="Number of Points:">
23+
<SingleSelect
24+
selectedValue={`${correct.numPoints ?? 1}`}
25+
onChange={(newValue) => {
26+
const points = parsePointCount(newValue);
27+
28+
onChange({
29+
correct: {
30+
type: "point",
31+
numPoints: points,
32+
},
33+
graph: {
34+
type: "point",
35+
numPoints: points,
36+
},
37+
});
38+
}}
39+
// Never uses placeholder, always has value
40+
placeholder=""
41+
className={styles.singleSelectShort}
42+
>
43+
{[
44+
...[...Array(7).keys()].map((n) => (
45+
<OptionItem
46+
key={n}
47+
value={`${n}`}
48+
label={`${n} point${n > 1 ? "s" : ""}`}
49+
/>
50+
)),
2851
<OptionItem
29-
key={n}
30-
value={`${n}`}
31-
label={`${n} point${n > 1 ? "s" : ""}`}
32-
/>
33-
)),
34-
<OptionItem
35-
key="unlimited"
36-
value={UNLIMITED}
37-
label="unlimited"
38-
/>,
39-
]}
40-
</SingleSelect>
52+
key="unlimited"
53+
value={UNLIMITED}
54+
label="unlimited"
55+
/>,
56+
]}
57+
</SingleSelect>
58+
</LabeledRow>
4159
);
4260
};
4361

44-
const styles = StyleSheet.create({
45-
singleSelectShort: {
46-
// Non-standard spacing, but it's the smallest we can go
47-
// without running into styling issues with the dropdown.
48-
height: 26,
49-
},
50-
});
51-
5262
export default GraphPointsCountSelector;

0 commit comments

Comments
 (0)