Skip to content

Commit 0bec013

Browse files
authored
[Hint Mode: Start Coords] Add start coords UI for polygon graphs (snap to grid only) (#1488)
## Summary: Add the UI to specify start coords for Polygon graph type (not unlimited, snap to grid only) - Add the polygon graph type go start-coords-settings.tsx - No need to create a separate start-coord-polygon.tsx file because it's identical to start-coord-point.tsx. Reusing that component. - Update the selection logic in interactive-graph-editor.tsx so it properly updates the graph with the new snapTo value. (It was stuck on the previously selected value before.) - Add the start coords UI polygon flag - Update the flag tests so they actually test what they're supposed to Issue: https://khanacademy.atlassian.net/browse/LEMS-2072 ## Test plan: `yarn jest` Storybook - Polygon story without default graph settings - http://localhost:6006/?path=/story/perseuseditor-widgets-interactive-graph--interactive-graph-polygon - For default graph settings, go to a different story and select polygon from the dropdown - http://localhost:6006/?path=/story/perseuseditor-widgets-interactive-graph--interactive-graph-point <img width="379" alt="Screenshot 2024-08-05 at 5 29 35 PM" src="https://github.com/user-attachments/assets/712d2114-4644-46b2-a693-73c788564ae7"> Author: nishasy Reviewers: nishasy, Myranae Required Reviewers: Approved By: Myranae Checks: ✅ codecov/project, ✅ codecov/patch, ✅ Upload Coverage (ubuntu-latest, 20.x), ✅ Publish npm snapshot (ubuntu-latest, 20.x), ✅ Lint, Typecheck, Format, and Test (ubuntu-latest, 20.x), ✅ Cypress (ubuntu-latest, 20.x), ✅ Check builds for changes in size (ubuntu-latest, 20.x), ✅ Check for .changeset entries for all changed files (ubuntu-latest, 20.x), ✅ Jest Coverage (ubuntu-latest, 20.x), ✅ Publish Storybook to Chromatic (ubuntu-latest, 20.x), ✅ gerald Pull Request URL: #1488
1 parent 6f20129 commit 0bec013

File tree

11 files changed

+235
-7
lines changed

11 files changed

+235
-7
lines changed

.changeset/unlucky-lamps-draw.md

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@khanacademy/perseus": minor
3+
"@khanacademy/perseus-editor": minor
4+
---
5+
6+
[Hint Mode: Start Coords] Add start coords UI for polygon graphs (snap to grid only)

packages/perseus-editor/src/__stories__/flags-for-api-options.ts

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ export const flags = {
2121
"start-coords-ui-phase-1": true,
2222
"start-coords-ui-phase-2": true,
2323
"start-coords-ui-point": true,
24+
"start-coords-ui-polygon": true,
2425
},
2526
} satisfies APIOptions["flags"];
2627

packages/perseus-editor/src/components/__tests__/start-coords-settings.test.tsx

+86
Original file line numberDiff line numberDiff line change
@@ -606,4 +606,90 @@ describe("StartCoordSettings", () => {
606606
},
607607
);
608608
});
609+
610+
describe("polygon graph", () => {
611+
test("shows the start coordinates UI: 3 sides (default)", () => {
612+
// Arrange
613+
614+
// Act
615+
render(
616+
<StartCoordsSettings
617+
{...defaultProps}
618+
type="polygon"
619+
onChange={() => {}}
620+
/>,
621+
{wrapper: RenderStateRoot},
622+
);
623+
624+
// Assert
625+
expect(screen.getByText("Start coordinates")).toBeInTheDocument();
626+
expect(screen.getByText("Point 1:")).toBeInTheDocument();
627+
expect(screen.getByText("Point 2:")).toBeInTheDocument();
628+
expect(screen.getByText("Point 3:")).toBeInTheDocument();
629+
});
630+
631+
test("shows the start coordinates UI: 6 sides", () => {
632+
// Arrange
633+
634+
// Act
635+
render(
636+
<StartCoordsSettings
637+
{...defaultProps}
638+
type="polygon"
639+
numSides={6}
640+
onChange={() => {}}
641+
/>,
642+
{wrapper: RenderStateRoot},
643+
);
644+
645+
// Assert
646+
expect(screen.getByText("Start coordinates")).toBeInTheDocument();
647+
expect(screen.getByText("Point 1:")).toBeInTheDocument();
648+
expect(screen.getByText("Point 2:")).toBeInTheDocument();
649+
expect(screen.getByText("Point 3:")).toBeInTheDocument();
650+
expect(screen.getByText("Point 4:")).toBeInTheDocument();
651+
expect(screen.getByText("Point 5:")).toBeInTheDocument();
652+
expect(screen.getByText("Point 6:")).toBeInTheDocument();
653+
});
654+
655+
test.each`
656+
pointIndex | coord
657+
${0} | ${"x"}
658+
${0} | ${"y"}
659+
${1} | ${"x"}
660+
${1} | ${"y"}
661+
${2} | ${"x"}
662+
${2} | ${"y"}
663+
`(
664+
"calls onChange when $coord coord is changed (line $pointIndex)",
665+
async ({pointIndex, coord}) => {
666+
// Arrange
667+
const onChangeMock = jest.fn();
668+
render(
669+
<StartCoordsSettings
670+
{...defaultProps}
671+
type="polygon"
672+
onChange={onChangeMock}
673+
/>,
674+
);
675+
676+
// Act
677+
const input = screen.getAllByRole("spinbutton", {
678+
name: `${coord}`,
679+
})[pointIndex];
680+
await userEvent.clear(input);
681+
await userEvent.type(input, "101");
682+
683+
// Assert
684+
const expectedCoords = [
685+
[3, -2],
686+
[0, 4],
687+
[-3, -2],
688+
];
689+
expectedCoords[pointIndex][coord === "x" ? 0 : 1] = 101;
690+
691+
expect(onChangeMock).toHaveBeenLastCalledWith(expectedCoords);
692+
},
693+
);
694+
});
609695
});

packages/perseus-editor/src/components/__tests__/util.test.ts

+39
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,45 @@ describe("getDefaultGraphStartCoords", () => {
326326
[5, 0],
327327
]);
328328
});
329+
330+
test("should get default start coords for a polygon graph, triangle (default)", () => {
331+
// Arrange
332+
const graph: PerseusGraphType = {type: "polygon"};
333+
const range = [
334+
[-10, 10],
335+
[-10, 10],
336+
] satisfies [Range, Range];
337+
const step = [1, 1] satisfies [number, number];
338+
339+
// Act
340+
const defaultCoords = getDefaultGraphStartCoords(graph, range, step);
341+
342+
expect(defaultCoords).toEqual([
343+
[3, -2],
344+
[0, 4],
345+
[-3, -2],
346+
]);
347+
});
348+
349+
test("should get default start coords for a polygon graph, square", () => {
350+
// Arrange
351+
const graph: PerseusGraphType = {type: "polygon", numSides: 4};
352+
const range = [
353+
[-10, 10],
354+
[-10, 10],
355+
] satisfies [Range, Range];
356+
const step = [1, 1] satisfies [number, number];
357+
358+
// Act
359+
const defaultCoords = getDefaultGraphStartCoords(graph, range, step);
360+
361+
expect(defaultCoords).toEqual([
362+
[3, -3],
363+
[3, 3],
364+
[-3, 3],
365+
[-3, -3],
366+
]);
367+
});
329368
});
330369

331370
describe("getSinusoidEquation", () => {

packages/perseus-editor/src/components/start-coords-settings.tsx

+7-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
getLineCoords,
55
getLinearSystemCoords,
66
getPointCoords,
7+
getPolygonCoords,
78
getQuadraticCoords,
89
getSegmentCoords,
910
getSinusoidCoords,
@@ -91,8 +92,13 @@ const StartCoordsSettingsInner = (props: Props) => {
9192
onChange={onChange}
9293
/>
9394
);
95+
// Graphs with startCoords of type ReadonlyArray<Coord>
9496
case "point":
95-
const pointCoords = getPointCoords(props, range, step);
97+
case "polygon":
98+
const pointCoords =
99+
type === "point"
100+
? getPointCoords(props, range, step)
101+
: getPolygonCoords(props, range, step);
96102
return (
97103
<StartCoordsPoint
98104
startCoords={pointCoords}

packages/perseus-editor/src/components/util.ts

+19
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
getLineCoords,
55
getLinearSystemCoords,
66
getPointCoords,
7+
getPolygonCoords,
78
getQuadraticCoords,
89
getSegmentCoords,
910
getSinusoidCoords,
@@ -201,6 +202,12 @@ export function getDefaultGraphStartCoords(
201202
range,
202203
step,
203204
);
205+
case "polygon":
206+
return getPolygonCoords(
207+
{...graph, startCoords: undefined},
208+
range,
209+
step,
210+
);
204211
default:
205212
return undefined;
206213
}
@@ -275,6 +282,7 @@ export const shouldShowStartCoordsUI = (flags, graph) => {
275282
const startCoordsPhase1 = flags?.mafs?.["start-coords-ui-phase-1"];
276283
const startCoordsPhase2 = flags?.mafs?.["start-coords-ui-phase-2"];
277284
const startCoordsPoint = flags?.mafs?.["start-coords-ui-point"];
285+
const startCoordsPolygon = flags?.mafs?.["start-coords-ui-polygon"];
278286

279287
if (startCoordsPhase1 && startCoordsUiPhase1Types.includes(graph.type)) {
280288
return true;
@@ -292,5 +300,16 @@ export const shouldShowStartCoordsUI = (flags, graph) => {
292300
return true;
293301
}
294302

303+
if (
304+
startCoordsPolygon &&
305+
graph.type === "polygon" &&
306+
graph.numPoints !== "unlimited" &&
307+
// Pre-initialized graph with undefined snapTo value
308+
// initializes to snapTo="grid"
309+
(graph.snapTo === "grid" || graph.snapTo === undefined)
310+
) {
311+
return true;
312+
}
313+
295314
return false;
296315
};

packages/perseus-editor/src/widgets/__tests__/interactive-graph-editor.test.tsx

+66-4
Original file line numberDiff line numberDiff line change
@@ -700,9 +700,10 @@ describe("InteractiveGraphEditor", () => {
700700
...flags,
701701
mafs: {
702702
...flags.mafs,
703-
"start-coords-ui-phase-1": shouldRender,
703+
"start-coords-ui-phase-1": true,
704704
"start-coords-ui-phase-2": false,
705705
"start-coords-ui-point": false,
706+
"start-coords-ui-polygon": false,
706707
},
707708
},
708709
}}
@@ -738,7 +739,7 @@ describe("InteractiveGraphEditor", () => {
738739
${"linear-system"} | ${false}
739740
${"segment"} | ${false}
740741
${"circle"} | ${false}
741-
${"quadratic"} | ${false}
742+
${"quadratic"} | ${true}
742743
${"sinusoid"} | ${true}
743744
${"polygon"} | ${false}
744745
${"angle"} | ${false}
@@ -759,8 +760,9 @@ describe("InteractiveGraphEditor", () => {
759760
mafs: {
760761
...flags.mafs,
761762
"start-coords-ui-phase-1": false,
762-
"start-coords-ui-phase-2": shouldRender,
763+
"start-coords-ui-phase-2": true,
763764
"start-coords-ui-point": false,
765+
"start-coords-ui-polygon": false,
764766
},
765767
},
766768
}}
@@ -818,7 +820,67 @@ describe("InteractiveGraphEditor", () => {
818820
...flags.mafs,
819821
"start-coords-ui-phase-1": false,
820822
"start-coords-ui-phase-2": false,
821-
"start-coords-ui-point": shouldRender,
823+
"start-coords-ui-point": true,
824+
"start-coords-ui-polygon": false,
825+
},
826+
},
827+
}}
828+
graph={{type}}
829+
correct={{type}}
830+
/>,
831+
{
832+
wrapper: RenderStateRoot,
833+
},
834+
);
835+
836+
// Assert
837+
if (shouldRender) {
838+
expect(
839+
await screen.findByRole("button", {
840+
name: "Use default start coordinates",
841+
}),
842+
).toBeInTheDocument();
843+
} else {
844+
expect(
845+
screen.queryByRole("button", {
846+
name: "Use default start coordinates",
847+
}),
848+
).toBeNull();
849+
}
850+
},
851+
);
852+
853+
test.each`
854+
type | shouldRender
855+
${"linear"} | ${false}
856+
${"ray"} | ${false}
857+
${"linear-system"} | ${false}
858+
${"segment"} | ${false}
859+
${"circle"} | ${false}
860+
${"quadratic"} | ${false}
861+
${"sinusoid"} | ${false}
862+
${"polygon"} | ${true}
863+
${"angle"} | ${false}
864+
${"point"} | ${false}
865+
`(
866+
"should render for $type graphs if polygon flag is on: $shouldRender",
867+
async ({type, shouldRender}) => {
868+
// Arrange
869+
870+
// Act
871+
render(
872+
<InteractiveGraphEditor
873+
{...baseProps}
874+
apiOptions={{
875+
...ApiOptions.defaults,
876+
flags: {
877+
...flags,
878+
mafs: {
879+
...flags.mafs,
880+
"start-coords-ui-phase-1": false,
881+
"start-coords-ui-phase-2": false,
882+
"start-coords-ui-point": false,
883+
"start-coords-ui-polygon": true,
822884
},
823885
},
824886
}}

packages/perseus-editor/src/widgets/interactive-graph-editor.tsx

+4-1
Original file line numberDiff line numberDiff line change
@@ -354,7 +354,10 @@ class InteractiveGraphEditor extends React.Component<Props> {
354354
coords: null,
355355
};
356356

357-
this.props.onChange({correct: graph});
357+
this.props.onChange({
358+
correct: graph,
359+
graph: graph,
360+
});
358361
}}
359362
style={styles.singleSelectShort}
360363
>

packages/perseus/src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ export {
123123
getLineCoords,
124124
getLinearSystemCoords,
125125
getPointCoords,
126+
getPolygonCoords,
126127
getSegmentCoords,
127128
getSinusoidCoords,
128129
getQuadraticCoords,

packages/perseus/src/types.ts

+5
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,11 @@ export const InteractiveGraphEditorFlags = [
172172
* Includes point graph.
173173
*/
174174
"start-coords-ui-point",
175+
/**
176+
* Enables the UI for setting the start coordinates of a graph.
177+
* Includes polygon graph.
178+
*/
179+
"start-coords-ui-polygon",
175180
] as const;
176181

177182
/**

packages/perseus/src/widgets/interactive-graphs/reducer/initialize-graph-state.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,7 @@ export function getLinearSystemCoords(
277277
);
278278
}
279279

280-
function getPolygonCoords(
280+
export function getPolygonCoords(
281281
graph: PerseusGraphTypePolygon,
282282
range: [x: Interval, y: Interval],
283283
step: [x: number, y: number],

0 commit comments

Comments
 (0)