Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rough out UserInput type #1639

Open
wants to merge 51 commits into
base: main
Choose a base branch
from
Open

Conversation

handeyeco
Copy link
Contributor

@handeyeco handeyeco commented Sep 17, 2024

Summary:

I might have gone the wrong direction with this (as it is now).

I thought it would be beneficial to have widget Rubrics and UserInput in one place: validation.types.ts. This way we have the information we need from the learner and the information we need to determine if the user input is correct/incorrect.

However as I was going through this, I started to feel like we misnamed something: WidgetProps<RenderProps, Rubric>. For some of these widgets, they were the same thing! For instance definition:

type RenderProps = PerseusDefinitionWidgetOptions;

type Rubric = PerseusDefinitionWidgetOptions;

type UserInput = Empty;

type DefinitionProps = WidgetProps<RenderProps, Rubric> & {
    widgets: PerseusRenderer["widgets"];
};

RenderProps is the same as Rubric and then we just stick on another random prop (not to mention we have UserInput even though it's empty). On top of this, when we use WidgetProps<RenderProps, Rubric>, we only use Rubric for reviewModeRubric which is only used by a couple of components. Here's my guess at what's going on:

  • WidgetOptions: these were meant to represent stored configuration options for widgets - the stuff we keep in the database.
  • RenderProps: these were meant to be the "transformed" configuration options that we use when rendering - we take the WidgetOptions and transform them to the props we hand the widget (for instance if we need to upgrade the widget version).
  • Rubric: this is the stuff we need to grade the widget - Expression might need times and buttonSets to display the widget, but it only needs answerForms to grade the user input.
  • My guess is WidgetProps has reviewModeRubric as a legacy way of configuring or grading widgets. My guess is we need:
    • WidgetProps<RenderProps>, removing/replacing reviewModeRubric
    • RenderProps should be scoped to just the stuff it needs (per widget) to render, relying on the types from the widget's options when possible
    • WidgetOptions per widget should continue to be focused on our external API
    • Rubric per widget should be focused on just the stuff we need to grade the widget
    • UserInput should be focused on just the things the learner sends for a widget to be graded

Expression is a good example:

// The public API, what gets passed from the Perseus JSON
type PerseusExpressionWidgetOptions = {
    answerForms: ReadonlyArray<PerseusExpressionAnswerForm>;
    buttonSets: LegacyButtonSets;
    functions: ReadonlyArray<string>;
    times: boolean;
    visibleLabel?: string;
    ariaLabel?: string;
    buttonsVisible?: "always" | "never" | "focused";
};

// The props it needs to render, note it drops `answerForms` and adds `keypadConfiguration`
type RenderProps = {
    buttonSets: PerseusExpressionWidgetOptions["buttonSets"];
    buttonsVisible?: PerseusExpressionWidgetOptions["buttonsVisible"];
    functions: PerseusExpressionWidgetOptions["functions"];
    times: PerseusExpressionWidgetOptions["times"];
    visibleLabel: PerseusExpressionWidgetOptions["visibleLabel"];
    ariaLabel: PerseusExpressionWidgetOptions["ariaLabel"];
    keypadConfiguration: ReturnType<typeof keypadConfigurationForProps>;
};

type Rubric = PerseusExpressionWidgetOptions; // <- how it is now is not specific enough
// It should only have the stuff it needs to grade with
type Rubric = {
    answerForms: ReadonlyArray<PerseusExpressionAnswerForm>;
}

// The user input type should be whatever the user sends for grading
type UserInput = string;

// Props should *only* be combining the widget-specific render props
// and the shared, universal widget props
type Props = WidgetProps<RenderProps>;
// or a little more obvious IMO
type Props = WidgetProps & RenderProps;
// or very obvious by explicitly saying which props are used
type Props = RenderProps & {
  onBlur: WidgetProps["onBlur"];
{

The more I look at it, the more I think it's kind of a mess. Anyway this PR doesn't address any of this. But I was intentional about creating a specific UserInput and Rubric type for most widgets even though they need further refinement.

Issue: LEMS-2328

Test plan:

@handeyeco handeyeco self-assigned this Sep 17, 2024
@handeyeco handeyeco changed the title rough out some of the complex types Rough out UserInput type Sep 17, 2024
Copy link
Contributor

github-actions bot commented Sep 17, 2024

npm Snapshot: Published

Good news!! We've packaged up the latest commit from this PR (dcb4699) and published it to npm. You
can install it using the tag PR1639.

Example:

yarn add @khanacademy/perseus@PR1639

If you are working in Khan Academy's webapp, you can run:

./dev/tools/bump_perseus_version.sh -t PR1639

Copy link
Contributor

github-actions bot commented Sep 17, 2024

Size Change: -156 B (-0.02%)

Total Size: 862 kB

Filename Size Change
packages/perseus/dist/es/index.js 417 kB -156 B (-0.04%)
ℹ️ View Unchanged
Filename Size
packages/kas/dist/es/index.js 38.3 kB
packages/keypad-context/dist/es/index.js 760 B
packages/kmath/dist/es/index.js 4.27 kB
packages/math-input/dist/es/index.js 78 kB
packages/math-input/dist/es/strings.js 1.79 kB
packages/perseus-core/dist/es/index.js 1.48 kB
packages/perseus-editor/dist/es/index.js 279 kB
packages/perseus-linter/dist/es/index.js 22.2 kB
packages/perseus/dist/es/strings.js 3.36 kB
packages/pure-markdown/dist/es/index.js 3.66 kB
packages/simple-markdown/dist/es/index.js 12.4 kB

compressed-size-action

Copy link

codecov bot commented Sep 17, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 70.49%. Comparing base (391641a) to head (dcb4699).

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1639      +/-   ##
==========================================
- Coverage   70.59%   70.49%   -0.11%     
==========================================
  Files         561      583      +22     
  Lines      108194   112978    +4784     
  Branches     7959    11387    +3428     
==========================================
+ Hits        76384    79639    +3255     
- Misses      31688    33339    +1651     
+ Partials      122        0     -122     

Impacted file tree graph

see 1139 files with indirect coverage changes


Continue to review full report in Codecov by Sentry.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 391641a...dcb4699. Read the comment docs.

@handeyeco handeyeco marked this pull request as ready for review September 23, 2024 22:10
@khan-actions-bot khan-actions-bot requested a review from a team September 23, 2024 22:10
@khan-actions-bot
Copy link
Contributor

khan-actions-bot commented Sep 23, 2024

Gerald

Required Reviewers
  • @Khan/perseus for changes to .changeset/dull-kids-pretend.md, packages/perseus/src/index.ts, packages/perseus/src/perseus-types.ts, packages/perseus/src/renderer.tsx, packages/perseus/src/types.ts, packages/perseus/src/validation.types.ts, packages/perseus/src/__tests__/renderer-api.test.tsx, packages/perseus/src/mixins/widget-prop-denylist.ts, packages/perseus/src/widgets/interactive-graph.test.tsx, packages/perseus/src/widgets/interactive-graph.tsx, packages/perseus/src/widgets/categorizer/categorizer-validator.test.ts, packages/perseus/src/widgets/categorizer/categorizer-validator.ts, packages/perseus/src/widgets/categorizer/categorizer.tsx, packages/perseus/src/widgets/cs-program/cs-program.tsx, packages/perseus/src/widgets/definition/definition.tsx, packages/perseus/src/widgets/dropdown/dropdown-validator.test.tsx, packages/perseus/src/widgets/dropdown/dropdown-validator.tsx, packages/perseus/src/widgets/dropdown/dropdown.tsx, packages/perseus/src/widgets/explanation/explanation.tsx, packages/perseus/src/widgets/expression/expression-validator.ts, packages/perseus/src/widgets/expression/expression.tsx, packages/perseus/src/widgets/graded-group/graded-group.tsx, packages/perseus/src/widgets/graded-group-set/graded-group-set.tsx, packages/perseus/src/widgets/grapher/grapher.tsx, packages/perseus/src/widgets/group/group.tsx, packages/perseus/src/widgets/iframe/iframe.tsx, packages/perseus/src/widgets/image/image.tsx, packages/perseus/src/widgets/input-number/input-number-validator.test.ts, packages/perseus/src/widgets/input-number/input-number-validator.ts, packages/perseus/src/widgets/input-number/input-number.tsx, packages/perseus/src/widgets/interaction/interaction.tsx, packages/perseus/src/widgets/interactive-graphs/stateful-mafs-graph.tsx, packages/perseus/src/widgets/label-image/label-image.tsx, packages/perseus/src/widgets/matcher/matcher.tsx, packages/perseus/src/widgets/matrix/matrix.tsx, packages/perseus/src/widgets/molecule/molecule.tsx, packages/perseus/src/widgets/number-line/number-line.tsx, packages/perseus/src/widgets/numeric-input/numeric-input-validator.test.ts, packages/perseus/src/widgets/numeric-input/numeric-input-validator.ts, packages/perseus/src/widgets/numeric-input/numeric-input.test.ts, packages/perseus/src/widgets/numeric-input/numeric-input.tsx, packages/perseus/src/widgets/orderer/orderer.tsx, packages/perseus/src/widgets/passage/passage.tsx, packages/perseus/src/widgets/passage-ref/passage-ref.tsx, packages/perseus/src/widgets/passage-ref-target/passage-ref-target.tsx, packages/perseus/src/widgets/phet-simulation/phet-simulation.tsx, packages/perseus/src/widgets/plotter/plotter.tsx, packages/perseus/src/widgets/radio/radio-component.tsx, packages/perseus/src/widgets/sorter/sorter-validator.test.ts, packages/perseus/src/widgets/sorter/sorter-validator.ts, packages/perseus/src/widgets/sorter/sorter.tsx, packages/perseus/src/widgets/table/table.tsx, packages/perseus/src/widgets/video/video.tsx

Don't want to be involved in this pull request? Comment #removeme and we won't notify you of further changes.

Copy link
Collaborator

@jeremywiebe jeremywiebe left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice. Really like the standardization and I agree with moving all these types. When we split things more aggressively for server-side scoring, this will be extremely helpful.

@@ -149,7 +145,7 @@ export type Widget = {
// TODO(jeremy): I think this is actually a callback
focus?: () => unknown,
) => void;
getUserInput?: () => WidgetUserInput | null | undefined;
getUserInput?: () => UserInput | null | undefined;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a hunch that my request will lead down a rabbit hole of fighting TypeScript, but would it be possible to make this Widget type generic so that the UserInput type would be a generic type? That way, the numeric-input could only legally return PerseusNumericInputUserInput.

If 5 min of trying doesn't work, we can push that out, but it's been a thought I've often had as I worked with this Widget type. (the same goes for return type of getSerializedState)

packages/perseus/src/types.ts Show resolved Hide resolved

type Props = WidgetProps<RenderProps, Rubric> & {
type Props = WidgetProps<RenderProps, PerseusCategorizerRubric> & {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thought: Originally, when Perseus lived in webapp, we built the types using a Perseus prefix. Today with Perseus being in its own repo and npm package, I feel like we could begin moving away from prefixing everything with Perseus. It's unrelated to this PR, but seeing all the PerseusXyz types brought it to my mind again.

@@ -66,6 +66,7 @@ type Point = {
y: number;
};

// TODO: should this be using WidgetProps / PerseusLabelImageWidgetOptions?
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. Or... we use this approach for all widget RenderProps types where we explicitly define each key. I think it makes this more verbose but potentially clearer. @benchristel I think you've had thoughts about this before, haven't you?

Although not part of this PR, this should also be named RenderProps for consistency.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants