-
Notifications
You must be signed in to change notification settings - Fork 354
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 stories for Assessment's 5 scorable widgets that use answerless data #2329
base: main
Are you sure you want to change the base?
Conversation
This is required for the check button to still work correctly
… data It was using data without answers before. Although this is what we want, it's not how all the other answerless stories are
…enderer Migrate dropdown stories to ServerItemRendererWithDebugUI instead of RendererWithDebugUI. Also renames the answerless stories and creates PerseusItems from the previously available question objects.
…eusRenderers Some of the test data is also used in tests, so new PerseusItem objects were created in addition to the PerseusRenderer objects for those.
…emRenderer ServerItemRenderer requires PerseusItem objects whereas Renderer just requires PerseusRenderer. Creating PerseusItem objects from the available data for Interactive Graph widget
…ServerItemRenderer Also rename the stories for consistency and ease of locating in storybook.
…erless stories Update the stories to use ServerItemRenderWithDebugUI. Since we need answerless stories to use this, we have to update all the stories since they all use the same render logic.
…tories where Dropdown, Interactive Graph, Numeric Input, Expression, and Radio widgets render using answerless data
…ithDebugUI Updates the numeric-input answerless story specifically
…e with radio story
Size Change: 0 B Total Size: 734 kB ℹ️ View Unchanged
|
npm Snapshot: PublishedGood news!! We've packaged up the latest commit from this PR (b49e6bd) and published it to npm. You Example: pnpm add @khanacademy/perseus@PR2329 If you are working in Khan Academy's webapp, you can run: ./dev/tools/bump_perseus_version.sh -t PR2329 |
…or non answerless Will do a switch to ServerItemRenderWithDebugUI for all stories at some point in the future, perhaps after this PR
…sed on usage" This reverts commit a3104a2.
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 adding these. I have a few comments, but nothing major. :)
|
||
// Data for the interactive graph widget | ||
|
||
const createInteractiveGraphItem = (question: PerseusRenderer): PerseusItem => { |
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.
This seems similar to the createDropdownItem
. Could we have a single createPerseusItem(question: PerseusRenderer)
instead?
@@ -180,6 +182,29 @@ const updateWidgetOptions = ( | |||
}; | |||
}; | |||
|
|||
const createNumericInputItem = (question: PerseusRenderer): PerseusItem => { |
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.
Same note as above about unifying this with the other createXyzItem
functions.
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.
These functions should now be gone in favor of using the previously created utility that Matthew pointed out.
images: { | ||
"web+graphie://ka-perseus-graphie.s3.amazonaws.com/4613e0d9c906b3053fb5523eed83d4f779fdf6bb": | ||
{ | ||
width: 428, | ||
height: 428, | ||
}, | ||
}, |
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.
For test data and stories this is likely ok. The images
key in a perseus renderer object is used to explicitly set the width and the height of an image (using the image URL as the key into images
).
perseus/packages/perseus/src/renderer.tsx
Lines 1259 to 1266 in 5e7a608
// We need to add width and height to images from our | |
// props.images mapping. | |
// We do a _.has check here to avoid weird things like | |
// 'toString' or '__proto__' as a url. | |
const extraAttrs = _.has(this.props.images, node.target) | |
? this.props.images[node.target] | |
: null; |
* Creates a Perseus item with the provided question/PerseusRenderer data. | ||
* Useful for converting test data used with RendererWithDebugUI to test data | ||
* for ServerItemRendererWithDebugUI. | ||
* | ||
* @param {PerseusRenderer} question - The question to be included in the Perseus item. | ||
* @returns {PerseusItem} The created Perseus item. | ||
*/ | ||
export const createPerseusItem = (question: PerseusRenderer): PerseusItem => { | ||
return { | ||
question: question, | ||
answer: null, | ||
itemDataVersion: { | ||
major: 0, | ||
minor: 1, | ||
}, | ||
hints: [], | ||
answerArea: { | ||
calculator: false, | ||
chi2Table: false, | ||
financialCalculatorMonthlyPayment: false, | ||
financialCalculatorTotalAmount: false, | ||
financialCalculatorTimeToPayOff: false, | ||
periodicTable: false, | ||
periodicTableWithKey: false, | ||
tTable: false, | ||
zTable: false, | ||
}, | ||
}; | ||
}; |
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.
@jeremywiebe How does this look for a shared test util? I think I put it in a good place, but wasn't sure on the file naming. Also, tbh, tried having copilot write the doc string. Not sure the parameter and return needs to be listed in the comment 🤔
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.
We also have generateTestPerseusItem
in the perseus
package. LEMS needs to devote a sprint or two to combining our test utils.
Also might be worth looking into how Ned handled answerArea
:
answerArea: Object.fromEntries(
ItemExtras.map((extra) => [extra, false]),
) as PerseusAnswerArea,
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 also points to something Matthew brought up to me in conversation recently, that we have the ServerItemRenderer UI and Renderer UI... they overlap alot and I think we could get away with only the Renderer UI component. I'll look into that this week yet.
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.
Ah, yea when I talked about this with Matthew, we thought we were leaning towards keeping the ServerItemRenderer and moving away from the Renderer one. We can definitely make things work either way, but that's why the answerless stories are using the ServerItemRenderer component.
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.
Removed this duplicate utility function creation and used the one that was already defined.
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.
we thought we were leaning towards keeping the ServerItemRenderer and moving away from the Renderer one
That would be my vote anyway, let's get close to what consumers are mostly using which is ServerItemRenderer
"@khanacademy/perseus": minor | ||
--- | ||
|
||
Update and add stories where Dropdown, Interactive Graph, Numeric Input, Expression, and Radio widgets render using answerless data |
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.
For future PRs, it would be beneficial to have separate PRs. In this case it could have been 5 PRs.
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.
For sure. I thought each was small enough to be combined into one, but I can still separate them out if you think that would be useful?
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.
The main goal (IMO) of small PRs is to make review easier. Since you already have the reviews, I don't think it'd be worth the trouble.
* Creates a Perseus item with the provided question/PerseusRenderer data. | ||
* Useful for converting test data used with RendererWithDebugUI to test data | ||
* for ServerItemRendererWithDebugUI. | ||
* | ||
* @param {PerseusRenderer} question - The question to be included in the Perseus item. | ||
* @returns {PerseusItem} The created Perseus item. | ||
*/ | ||
export const createPerseusItem = (question: PerseusRenderer): PerseusItem => { | ||
return { | ||
question: question, | ||
answer: null, | ||
itemDataVersion: { | ||
major: 0, | ||
minor: 1, | ||
}, | ||
hints: [], | ||
answerArea: { | ||
calculator: false, | ||
chi2Table: false, | ||
financialCalculatorMonthlyPayment: false, | ||
financialCalculatorTotalAmount: false, | ||
financialCalculatorTimeToPayOff: false, | ||
periodicTable: false, | ||
periodicTableWithKey: false, | ||
tTable: false, | ||
zTable: false, | ||
}, | ||
}; | ||
}; |
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.
We also have generateTestPerseusItem
in the perseus
package. LEMS needs to devote a sprint or two to combining our test utils.
Also might be worth looking into how Ned handled answerArea
:
answerArea: Object.fromEntries(
ItemExtras.map((extra) => [extra, false]),
) as PerseusAnswerArea,
): React.ReactElement => { | ||
return ( | ||
<ServerItemRendererWithDebugUI | ||
item={basicDropdownItem} |
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 it's a little overkill to duplicate each story (really we just needed one interactive answerless story per widget). Having a bunch of stories that are nearly identical might be confusing to people especially in a couple of months when all stories should start answerless. Not too worried about this, but maybe we need a post-SSS ticket to come back and simplify the stories.
What I think is definitely overkill is exporting/importing what is essentially the same test data. Rather than redeclare the test data, just wrap the existing test data.
item={basicDropdownItem} | |
item={createPerseusItem(basicDropdownItem)} |
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.
Updated the stories to do inline item creation and reduced the overall number of stories, though I kept one for each type of interactive graph.
@@ -29,6 +35,8 @@ export const angleQuestion: PerseusRenderer = interactiveGraphQuestionBuilder() | |||
}) | |||
.build(); | |||
|
|||
export const angleItem: PerseusItem = createPerseusItem(angleQuestion); |
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.
Same here, we don't need to be redeclaring a bunch of test data when we could just be wrapping the existing test data in createPerseusItem
@@ -0,0 +1,90 @@ | |||
// NOTE(Tamara): ServerItemRenderWithDebugUI does not work with the same props |
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.
Rather than make a new file, we need to update radio.stories.tsx
to use ServerItemRendererWithDebugUI
. The problem isn't that we need stories for both ServerItemRendererWithDebugUI
and RendererWithDebugUI
, the problem is that RendererWithDebugUI
needs to be deprecated and replaced with ServerItemRendererWithDebugUI
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 wanted to preserve Controls functionality for the answerful stories and wasn't sure how to do that with ServerItemRendererWithDebugUI. So I changed it from converting all stories to ServerItem to just the answerless stories in the new file. Just took a second attempt and was able to preserve the Controls functionality for all stories. The answerless ones just need to turn startAnswerless
off or click the Check button first to get full data. Removed this additional Radio stories page.
images: { | ||
"web+graphie://ka-perseus-graphie.s3.amazonaws.com/4613e0d9c906b3053fb5523eed83d4f779fdf6bb": | ||
{ | ||
width: 428, | ||
height: 428, | ||
}, | ||
}, |
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.
This style issue is unrelated to LEMS-2948, likely just a problem with styling in the fake Perseus Chrome we use in SB. Removing the image only sweeps the issue under the rug, we need a separate ticket to look into the styles causing this.
@@ -126,6 +129,9 @@ export const questionWithPassage: PerseusRenderer = { | |||
}, | |||
}; | |||
|
|||
export const questionWithPassageItem: PerseusItem = |
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.
Same here, don't need a new declaration.
@@ -15,7 +15,7 @@ import * as Perseus from "../packages/perseus/src/index"; | |||
import {keScoreFromPerseusScore} from "../packages/perseus/src/util/scoring"; | |||
|
|||
import KEScoreUI from "./ke-score-ui"; | |||
import SideBySide from "./split-view"; | |||
import SplitView from "./split-view"; |
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!
/** | ||
* Creates a Perseus item with the provided question/PerseusRenderer data. | ||
* Useful for converting test data used with RendererWithDebugUI to test data | ||
* for ServerItemRendererWithDebugUI. | ||
* | ||
* @param {PerseusRenderer} question - The question to be included in the Perseus item. | ||
* @returns {PerseusItem} The created Perseus item. | ||
*/ |
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.
Regarding docs. For things that are self-evident, I prefer to avoid adding a doc comment. So I think I'd just recommend this (as the question
param is quite obvious as is the return value):
/** | |
* Creates a Perseus item with the provided question/PerseusRenderer data. | |
* Useful for converting test data used with RendererWithDebugUI to test data | |
* for ServerItemRendererWithDebugUI. | |
* | |
* @param {PerseusRenderer} question - The question to be included in the Perseus item. | |
* @returns {PerseusItem} The created Perseus item. | |
*/ | |
/** | |
* Creates a Perseus item with the provided question/PerseusRenderer data. | |
* Useful for converting test data used with RendererWithDebugUI to test data | |
* for ServerItemRendererWithDebugUI. | |
*/ |
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! Ended up removing this file, but glad to know my thought to remove the parameter definitions was correct.
Update the component to take more props to keep the Controls panel working in Storybook
…nd revert original Bring the original back to using ServerItemRendererWithDebugUI for all radio stories. Fixes Controls for all stories.
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.
The only blocker is the change to ServerItemRenderer
, otherwise it's looking good
@@ -46,7 +46,7 @@ type OwnProps = { | |||
item: PerseusItem; | |||
score?: KEScore | null; | |||
problemNum?: number; | |||
reviewMode?: boolean; | |||
reviewMode?: boolean | null | undefined; |
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.
Curious about this change. It's a change to our external API (ServerItemRenderer
is production code, not test code), so I think this needs to be reverted.
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.
For sure! That makes sense. Instead, I updated the props for ServerItemRendererWithDebugUI
to be in alignment with reviewMode?: boolean;
.
// Renderer Options | ||
startAnswerless: boolean; |
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.
This is more "Testing Options" because this flag doesn't actually get passed down to the renderer, it just affects the code that gets passed to the renderer.
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.
Updated!
// NOTE(Tamara): The answerless radio stories currently lose the user's input | ||
// upon switching to answerless data initially. It will remember the user's | ||
// input after clicking the Check button a second time. | ||
// TODO(LEMS-2948): After investigating a solution, confirm this issue is fixed | ||
|
||
export const AnswerlessSingleSelect = { |
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.
// NOTE(Tamara): The answerless radio stories currently lose the user's input | |
// upon switching to answerless data initially. It will remember the user's | |
// input after clicking the Check button a second time. | |
// TODO(LEMS-2948): After investigating a solution, confirm this issue is fixed | |
export const AnswerlessSingleSelect = { | |
// TODO(LEMS-2948): The answerless radio stories currently lose the user's input | |
// upon switching to answerless data initially. It loses the user's | |
// input after clicking the Check button a second time. | |
export const AnswerlessSingleSelect = { |
I thought we confirmed that it doesn't remember the user input when clicking Check twice?
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 the way I wrote this was confusing. I updated the comment. User input is lost with the first click. If the user selects an option after that and then clicks Check again, it will remember the input. It's just that first time when it switches to answerful data.
reviewMode?: boolean | null | undefined; | ||
showSolutions?: ShowSolutions; |
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'm guessing you added this to verify that rationales/solutions/etc still work; I think that's a great idea.
The problem is that those, by definition, require answerful data. We should never be in a state where static
, reviewMode
, or showSolutions
are true while using answerless data.
🤔 I think short-term maybe we could add some semi-hacky but well commented code to make this work:
// `reviewMode` and `showSolutions` require answerful data by definition,
// so only use answerless data if those are not enabled
const shouldUseAnswerless = useAnswerless && !reviewMode && showSolutions === "none";
const renderedQuestion: PerseusRenderer = shouldUseAnswerless
? splitPerseusItem(item.question)
: item.question;
It might also be beneficial to let people who are using the stories know which version of the data they're rendering with, but that could be a small follow-up ticket.
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.
This only works with Radio because showSolutions is undefined with other widgets. I updated it a bit so that it works with other widgets and also so that the startAnswerless
toggle actually toggles between the data! Not sure how to make the static toggle do this though since it's not in the props and seems to be added by default. Would love to change startAnswerless
to answerless
and have the toggle update based on the status of the other toggles and the answerless status of the question.
@@ -79,10 +88,13 @@ export const ServerItemRendererWithDebugUI = ({ | |||
item={renderedItem} | |||
dependencies={storybookDependenciesV2} | |||
keypadElement={keypadElement} | |||
reviewMode={reviewMode} | |||
{...rest} |
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.
This is just a personal nit, but I try to avoid ...rest
for component props because I think it obfuscates usage.
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.
For sure. Pulled out the last prop I know was needed and removed the rest spread.
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.
startAnswerless
should be removed from the shouldUseAnswerless
, but otherwise looks good
// `reviewMode` and `showSolutions` require answerful data by definition, | ||
// so only use answerless data if those are not enabled. Also updates the | ||
// startAnswerless toggle so it actually switches between answerless and answerful. | ||
const shouldUseAnswerless = | ||
useAnswerless && | ||
!reviewMode && | ||
(showSolutions === "none" || !showSolutions) && | ||
startAnswerless; | ||
|
||
const renderedQuestion: PerseusRenderer = shouldUseAnswerless |
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.
// `reviewMode` and `showSolutions` require answerful data by definition, | |
// so only use answerless data if those are not enabled. Also updates the | |
// startAnswerless toggle so it actually switches between answerless and answerful. | |
const shouldUseAnswerless = | |
useAnswerless && | |
!reviewMode && | |
(showSolutions === "none" || !showSolutions) && | |
startAnswerless; | |
const renderedQuestion: PerseusRenderer = shouldUseAnswerless | |
// `reviewMode` and `showSolutions` require answerful data by definition, | |
// so only use answerless data if those are not enabled. | |
const shouldUseAnswerless = | |
useAnswerless && | |
!reviewMode && | |
(showSolutions === "none" || !showSolutions); | |
const renderedQuestion: PerseusRenderer = shouldUseAnswerless |
You don't want to directly use startAnswerless
because useAnswerless
is the stateful version of startAnswerless
. This also doesn't update startAnswerless
so the comment is a little confusing.
Just realizing useAnswerless
should probably be renamed so it's not confused with a hook 🤦 That was my bad. Maybe just answerless
/setAnswerless
or something. This is getting a little confusing, probably due to bad design decisions on my part...but it's test code, so we can always fix 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.
@handeyeco Okay, I renamed startAnswerless
to answerless/setAnswerless
.
Regarding startAnswerless
being there (on L77 of the highlighted code), when it is there, toggling startAnswerless
in the Controls for answerless stories immediately changes the data from answerless to answerful. It actually acts as a functioning toggle. It doesn't seem to work if startAnswerless
starts out being false though. Those need a page refresh. Should I still take startAnswerless
out from here though?
Also remove reference to startAnswerless
Summary:
In order to confirm Assessment's main widgets can render with answerless data and then still score after that, we are adding stories for each of the widgets to test rendering them with answerless data.
Issue: LEMS-2869
Test plan: