diff --git a/.changeset/three-ligers-think.md b/.changeset/three-ligers-think.md new file mode 100644 index 0000000000..12577baf57 --- /dev/null +++ b/.changeset/three-ligers-think.md @@ -0,0 +1,6 @@ +--- +"@khanacademy/perseus": minor +"@khanacademy/perseus-editor": minor +--- + +Add a content editor for the PhET widget diff --git a/packages/perseus-editor/src/widgets/__tests__/phet-sim-editor.test.tsx b/packages/perseus-editor/src/widgets/__tests__/phet-sim-editor.test.tsx new file mode 100644 index 0000000000..50c8736e31 --- /dev/null +++ b/packages/perseus-editor/src/widgets/__tests__/phet-sim-editor.test.tsx @@ -0,0 +1,55 @@ +import {Dependencies} from "@khanacademy/perseus"; +import {render, screen} from "@testing-library/react"; +import {userEvent as userEventLib} from "@testing-library/user-event"; +import * as React from "react"; + +import {testDependencies} from "../../../../../testing/test-dependencies"; +import PhetSimEditor from "../phet-sim-editor"; + +import type {UserEvent} from "@testing-library/user-event"; + +describe("phet-sim editor", () => { + let userEvent: UserEvent; + beforeEach(() => { + userEvent = userEventLib.setup({ + advanceTimers: jest.advanceTimersByTime, + }); + + jest.spyOn(Dependencies, "getDependencies").mockReturnValue( + testDependencies, + ); + }); + + it("renders", async () => { + // Act + render( {}} />); + + // Assert + expect(screen.getByLabelText("URL")).toBeInTheDocument(); + expect(screen.getByLabelText("Description")).toBeInTheDocument(); + }); + + it("should be possible to change URL", async () => { + // Arrange + const onChangeMock = jest.fn(); + + // Act + render(); + await userEvent.type(screen.getByLabelText("URL"), "h"); + + // Assert + expect(onChangeMock).toBeCalledWith({url: "h"}); + }); + + it("should be possible to change Description", async () => { + // Arrange + const onChangeMock = jest.fn(); + + // Act + render(); + await userEvent.type(screen.getByLabelText("Description"), "P"); + + // Assert + expect(onChangeMock).toBeCalledWith({description: "P"}); + }); +}); diff --git a/packages/perseus-editor/src/widgets/phet-sim-editor.tsx b/packages/perseus-editor/src/widgets/phet-sim-editor.tsx index 95fb4b0295..21ac82c44d 100644 --- a/packages/perseus-editor/src/widgets/phet-sim-editor.tsx +++ b/packages/perseus-editor/src/widgets/phet-sim-editor.tsx @@ -1,46 +1,56 @@ /* eslint-disable @khanacademy/ts-no-error-suppressions */ /* eslint-disable react/sort-comp */ -import {Changeable, EditorJsonify} from "@khanacademy/perseus"; +import {LabeledTextField} from "@khanacademy/wonder-blocks-form"; import * as React from "react"; -import BlurInput from "../components/blur-input"; +import type {PerseusPhetSimWidgetOptions} from "@khanacademy/perseus"; -type PhetSimEditorProps = any; +type DefaultProps = { + url: PerseusPhetSimWidgetOptions["url"]; + description: PerseusPhetSimWidgetOptions["description"]; +}; -class PhetSimEditor extends React.Component { - static propTypes = { - ...Changeable.propTypes, - }; - - static widgetName = "phet-sim" as const; +type Props = DefaultProps & { + onChange: (arg1: { + url?: Props["url"]; + description?: Props["description"]; + }) => void; +}; - static defaultProps: PhetSimEditorProps = { +class PhetSimEditor extends React.Component { + static defaultProps: DefaultProps = { url: "", description: "", }; - change: (arg1: any, arg2: any) => any = (...args) => { - return Changeable.change.apply(this, args); - }; + static widgetName = "phet-sim" as const; render(): React.ReactNode { return (
- + this.props.onChange({url})} + /> +
+ + this.props.onChange({description}) + } + />
); } - serialize: () => any = () => { - return EditorJsonify.serialize.call(this); - }; + serialize(): PerseusPhetSimWidgetOptions { + return { + url: this.props.url, + description: this.props.description, + }; + } } export default PhetSimEditor; diff --git a/packages/perseus/src/index.ts b/packages/perseus/src/index.ts index cbf17c1f44..b685fe6638 100644 --- a/packages/perseus/src/index.ts +++ b/packages/perseus/src/index.ts @@ -193,6 +193,7 @@ export type { PerseusInputNumberWidgetOptions, PerseusInteractiveGraphWidgetOptions, PerseusItem, + PerseusPhetSimWidgetOptions, PerseusPlotterWidgetOptions, PerseusPythonProgramWidgetOptions, PerseusRadioWidgetOptions, diff --git a/packages/perseus/src/perseus-types.ts b/packages/perseus/src/perseus-types.ts index beca79b001..eeddf399b9 100644 --- a/packages/perseus/src/perseus-types.ts +++ b/packages/perseus/src/perseus-types.ts @@ -1561,8 +1561,6 @@ export type PerseusPhetSimWidgetOptions = { url: string; // Translatable Text; Description of the sim for Khanmigo and alt text description: string; - // Always false - static: boolean; }; export type PerseusVideoWidgetOptions = { diff --git a/packages/perseus/src/widgets/__testdata__/phet-sim.testdata.ts b/packages/perseus/src/widgets/__testdata__/phet-sim.testdata.ts index b26ac23bee..073a5d7681 100644 --- a/packages/perseus/src/widgets/__testdata__/phet-sim.testdata.ts +++ b/packages/perseus/src/widgets/__testdata__/phet-sim.testdata.ts @@ -13,7 +13,6 @@ export const question1: PerseusRenderer = { options: { url: "https://phet.colorado.edu/sims/html/projectile-data-lab/latest/projectile-data-lab_all.html", description: "Projectile Data Lab", - static: false, }, alignment: "default", }, @@ -32,7 +31,6 @@ export const nonPhetUrl: PerseusRenderer = { options: { url: "https://google.com/", description: "Google", - static: false, }, alignment: "default", },