diff --git a/.changeset/weak-pandas-watch.md b/.changeset/weak-pandas-watch.md
new file mode 100644
index 0000000000..1bba8547a6
--- /dev/null
+++ b/.changeset/weak-pandas-watch.md
@@ -0,0 +1,6 @@
+---
+"@khanacademy/perseus": patch
+"@khanacademy/perseus-editor": patch
+---
+
+Fixing open polygon scoring issues within exercises and editors.
diff --git a/packages/perseus-editor/src/__stories__/interactive-graph-editor.stories.tsx b/packages/perseus-editor/src/__stories__/interactive-graph-editor.stories.tsx
index 51137cfd45..81594258f5 100644
--- a/packages/perseus-editor/src/__stories__/interactive-graph-editor.stories.tsx
+++ b/packages/perseus-editor/src/__stories__/interactive-graph-editor.stories.tsx
@@ -22,6 +22,7 @@ import {
segmentWithStartingCoordsQuestion,
segmentsWithStartingCoordsQuestion,
sinusoidWithStartingCoordsQuestion,
+ unlimitedPolygonWithStartingCoordsQuestion,
} from "../../../perseus/src/widgets/interactive-graphs/interactive-graph.testdata";
import {registerAllWidgetsAndEditorsForTesting} from "../util/register-all-widgets-and-editors-for-testing";
@@ -125,6 +126,14 @@ export const InteractiveGraphPolygon = (): React.ReactElement => {
);
};
+export const InteractiveGraphUnlimitedPolygon = (): React.ReactElement => {
+ return (
+
+ );
+};
+
export const InteractiveGraphAngle = (): React.ReactElement => {
return (
{
}
}
+ // Do not save a unlimited polygon that is open (coords is null).
+ if (
+ this.props.graph?.type === "polygon" &&
+ this.props.graph.numSides === "unlimited" &&
+ this.props.graph.coords === null
+ ) {
+ issues.push("Polygon must be closed.");
+ }
+
return issues;
};
diff --git a/packages/perseus/src/widgets/interactive-graphs/graphs/polygon.tsx b/packages/perseus/src/widgets/interactive-graphs/graphs/polygon.tsx
index 18ec8c1ff6..04b663de0b 100644
--- a/packages/perseus/src/widgets/interactive-graphs/graphs/polygon.tsx
+++ b/packages/perseus/src/widgets/interactive-graphs/graphs/polygon.tsx
@@ -101,6 +101,15 @@ const PolygonGraph = (props: Props) => {
}
}, [props.graphState.focusedPointIndex, pointsRef]);
+ // If the unlimited polygon is rendered with 3 or more coordinates
+ // Close the polygon, but only on first render.
+ React.useEffect(() => {
+ if (numSides === "unlimited" && props.graphState.coords.length > 2) {
+ dispatch(actions.polygon.closePolygon());
+ }
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, []);
+
const statefulProps: StatefulProps = {
...props,
graphConfig,
diff --git a/packages/perseus/src/widgets/interactive-graphs/interactive-graph.testdata.ts b/packages/perseus/src/widgets/interactive-graphs/interactive-graph.testdata.ts
index a2f5e97e07..264e69e048 100644
--- a/packages/perseus/src/widgets/interactive-graphs/interactive-graph.testdata.ts
+++ b/packages/perseus/src/widgets/interactive-graphs/interactive-graph.testdata.ts
@@ -193,6 +193,19 @@ export const polygonWithStartingCoordsQuestion: PerseusRenderer =
})
.build();
+export const unlimitedPolygonWithStartingCoordsQuestion: PerseusRenderer =
+ interactiveGraphQuestionBuilder()
+ .withPolygon("grid", {
+ numSides: "unlimited",
+ coords: [
+ [-4.5, -6],
+ [4.5, -5],
+ [3.5, 0.5],
+ [-4.5, 0],
+ ],
+ })
+ .build();
+
export const polygonWithAnglesQuestion: PerseusRenderer =
interactiveGraphQuestionBuilder()
.withContent(
diff --git a/packages/perseus/src/widgets/interactive-graphs/reducer/interactive-graph-state.test.ts b/packages/perseus/src/widgets/interactive-graphs/reducer/interactive-graph-state.test.ts
index f446a3cb39..25b16a928d 100644
--- a/packages/perseus/src/widgets/interactive-graphs/reducer/interactive-graph-state.test.ts
+++ b/packages/perseus/src/widgets/interactive-graphs/reducer/interactive-graph-state.test.ts
@@ -22,6 +22,26 @@ const defaultAngleState: InteractiveGraphState = {
allowReflexAngles: false,
};
+const defaultUnlimitedPolygonState: InteractiveGraphState = {
+ type: "polygon",
+ closedPolygon: false,
+ focusedPointIndex: 0,
+ coords: [[5, 0]],
+ numSides: "unlimited",
+ hasBeenInteractedWith: true,
+ showRemovePointButton: false,
+ showKeyboardInteractionInvitation: false,
+ interactionMode: "mouse",
+ range: [
+ [-10, 10],
+ [-10, 10],
+ ],
+ snapStep: [1, 1],
+ snapTo: "grid",
+ showSides: true,
+ showAngles: true,
+};
+
describe("getGradableGraph", () => {
/**
* Originally `getGradableGraph` was returning a PerseusGraphType with just a
@@ -117,4 +137,30 @@ describe("getGradableGraph", () => {
[-5, -5],
]);
});
+
+ it("returns null coordinates if the unlimited polygon graph is open", () => {
+ const state: InteractiveGraphState = {
+ ...defaultUnlimitedPolygonState,
+ closedPolygon: false,
+ };
+ const initialGraph: PerseusGraphType = {
+ type: "polygon",
+ };
+ const result = getGradableGraph(state, initialGraph);
+ invariant(result.type === "polygon");
+ expect(result.coords).toEqual(null);
+ });
+
+ it("returns coordinates if the unlimited polygon graph is closed", () => {
+ const state: InteractiveGraphState = {
+ ...defaultUnlimitedPolygonState,
+ closedPolygon: true,
+ };
+ const initialGraph: PerseusGraphType = {
+ type: "polygon",
+ };
+ const result = getGradableGraph(state, initialGraph);
+ invariant(result.type === "polygon");
+ expect(result.coords).toEqual([[5, 0]]);
+ });
});
diff --git a/packages/perseus/src/widgets/interactive-graphs/reducer/interactive-graph-state.ts b/packages/perseus/src/widgets/interactive-graphs/reducer/interactive-graph-state.ts
index 8433cbd8b1..0abae73773 100644
--- a/packages/perseus/src/widgets/interactive-graphs/reducer/interactive-graph-state.ts
+++ b/packages/perseus/src/widgets/interactive-graphs/reducer/interactive-graph-state.ts
@@ -44,6 +44,14 @@ export function getGradableGraph(
}
if (state.type === "polygon" && initialGraph.type === "polygon") {
+ // Unless the polygon is closed it is not considered score-able.
+ if (state.numSides === "unlimited" && !state.closedPolygon) {
+ return {
+ ...initialGraph,
+ coords: null,
+ };
+ }
+
return {
...initialGraph,
coords: state.coords,