-
Notifications
You must be signed in to change notification settings - Fork 350
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
Create helper to build public widget options for Numeric Input #2174
Conversation
…t a widget export function to filter out rubric data from widget options for the Numeric Input widget
…options function and type
… of public widget functions
npm Snapshot: PublishedGood news!! We've packaged up the latest commit from this PR (c497be3) and published it to npm. You Example: yarn add @khanacademy/perseus@PR2174 If you are working in Khan Academy's webapp, you can run: ./dev/tools/bump_perseus_version.sh -t PR2174 |
Size Change: +62 B (0%) Total Size: 1.48 MB
ℹ️ View Unchanged
|
@@ -544,6 +545,7 @@ export type WidgetScorerFunction = ( | |||
* A union type of all the functions that provide public widget options. | |||
*/ | |||
export type PublicWidgetOptionsFunction = | |||
| typeof getNumericInputPublicWidgetOptions |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this file the best place for this union? It is used to type the getPublicWidgetOptionsFunction
in the widgets.ts
file, which was created to be the function used in the editor to access the correct getPublicWidgetOptions
function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is fine!
const {answers: _, answerForms: __, ...publicWidgetOptions} = options; | ||
return publicWidgetOptions; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there any reason this would be preferable or not preferable to building out the specific object we're looking for like I was doing previously? ex. {size: options.size, static: options.static, etc.}
I could see this being less brittle as it makes it easier to add fields to the widgetOptions and have them be automatically included. If this is preferable, should I go through and update the functions that do this the previous way?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think it matters too much one way or the other. The tests ensure that we include the correct fields either way.
I guess one way of approaching it might be to ask, "when we add fields to the widget options, are they more likely to be public or private?" If the code is written the way you have it here, we won't need to change it when adding public options, but we will need to change it when adding private options. On the other hand, if we explicitly list the public fields, we won't need to change this code when adding private options, but we will need to change it when adding public options.
Then there's the question of what's easier to read. The current approach has the advantage of being short, but it might be a bit obscure.
Ultimately, it's a judgment call, and a two-way door, so we can just pick an approach and, worst case, change it later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for sharing all that! I didn't think about the difference between adding public versus private options and how either way may require a change. I like this way as it shows what is getting pulled out much more clearly.
type NumericInputPublicWidgetOptions = { | ||
labelText?: PerseusNumericInputWidgetOptions["labelText"]; | ||
size: PerseusNumericInputWidgetOptions["size"]; | ||
coefficient: PerseusNumericInputWidgetOptions["coefficient"]; | ||
rightAlign?: PerseusNumericInputWidgetOptions["rightAlign"]; | ||
static: PerseusNumericInputWidgetOptions["static"]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I left out both answers
and answerForms
as they both appear to contain information related to the correct answer. 'answerForms` even has a note that it's supposedly used in examples, but might not even be used anymore.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think answerForms should actually be considered public information. See e.g. the examples()
method on NumericInput
, which apparently generates learner-facing instructions based on the answerForms
that are accepted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Related to this, I added @SonicScrewdriver as a reviewer (and pinged her) since she's just been looking at this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My concern was that answerForms has information that could tell the user what to do. Specifically, the answerFrom names field:
answerForms: ReadonlyArray<{
simplify: "required" | "correct" | "enforced" | null | undefined;
name: "integer" | "decimal" | "proper" | "improper" | "mixed" | "pi";
}>;
Maybe that information is supposed to be available to the user?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes! The answer forms are displayed to the learner, and should already be specified to the user through how the question is written.
For example: If the question wants an improper fraction answer, the user will be prompted about this in a secondary fashion as well, either in the title of their course ("Improper Fractions") or directly in the question text. ("Please write your answer as an improper fraction").
The tool tips are just additional helpful reminders for users, particularly young ones. I don't think this could be something that anyone could use to cheat or get ahead on a question, as the user should never be surprised about which answer format the question requires.
As a result, I think it's safe to keep the answerForms public!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome! Thank you both! Just added them back in :D
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Requesting changes because I think we want to include answerForms
in the public data, since it is used to display instructions to the learner. Other than that, this looks good!
type NumericInputPublicWidgetOptions = { | ||
labelText?: PerseusNumericInputWidgetOptions["labelText"]; | ||
size: PerseusNumericInputWidgetOptions["size"]; | ||
coefficient: PerseusNumericInputWidgetOptions["coefficient"]; | ||
rightAlign?: PerseusNumericInputWidgetOptions["rightAlign"]; | ||
static: PerseusNumericInputWidgetOptions["static"]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think answerForms should actually be considered public information. See e.g. the examples()
method on NumericInput
, which apparently generates learner-facing instructions based on the answerForms
that are accepted.
const {answers: _, answerForms: __, ...publicWidgetOptions} = options; | ||
return publicWidgetOptions; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think it matters too much one way or the other. The tests ensure that we include the correct fields either way.
I guess one way of approaching it might be to ask, "when we add fields to the widget options, are they more likely to be public or private?" If the code is written the way you have it here, we won't need to change it when adding public options, but we will need to change it when adding private options. On the other hand, if we explicitly list the public fields, we won't need to change this code when adding private options, but we will need to change it when adding public options.
Then there's the question of what's easier to read. The current approach has the advantage of being short, but it might be a bit obscure.
Ultimately, it's a judgment call, and a two-way door, so we can just pick an approach and, worst case, change it later.
@@ -544,6 +545,7 @@ export type WidgetScorerFunction = ( | |||
* A union type of all the functions that provide public widget options. | |||
*/ | |||
export type PublicWidgetOptionsFunction = | |||
| typeof getNumericInputPublicWidgetOptions |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is fine!
…public widget options
Once we have answerForms, I think this looks great! Thank you very much for sorting this out. :) I'm a little less familiar with the server-side-scoring setup for this: will this usurp the If so, I have a bugfix on the Numeric Input branch that will need to be added to the const propsTransform = function (
widgetOptions: PerseusNumericInputWidgetOptions,
): RenderProps {
const {answers: _, ...rendererProps} = {
...widgetOptions,
answerForms: unionAnswerForms(
widgetOptions.answers
.filter((answer) => answer.status === "correct") <--- Just for context
.map((answer) => {
return (answer.answerForms || []).map((form) => {
return {
simplify: answer.simplify,
name: form,
};
});
}),
),
};
return rendererProps;
}; |
I'm not sure if it will overwrite anything, but the transform method on the numeric-input widget export is this propsTransform function. Or are you referring to something else?
Also when we actually hook these functions up, we'll have to do some more work to make sure the widget is getting all the information it needs on the frontend without revealing the answer, so I think that's when this would be addressed, if I am understanding correctly. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM!
LEMS-2767/numeric-input-public-options
Summary:
Adds a function that takes a Numeric Input widget's full widget options and removes correct answer information. It also adds the function to the widget's widget export and adds a test confirming the function works as expected.
Issue: LEMS-2767
Test plan: