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

[CFU] Add initial free response summary page #50606

Merged
merged 24 commits into from
Mar 14, 2023
Merged

Conversation

rshipp
Copy link
Contributor

@rshipp rshipp commented Mar 6, 2023

Add an initial implementation of the "Check for Understanding" or "summary" view for free response levels.

When reviewing the JSX component, it may help to compare it with the free response files I copied this code from:

I'm not tied to the file/folder names I'm using here, so feel free to suggest different ones. I intentionally didn't refactor out components for free-response-specific things or reusable pieces, since we wanted to get Free Response out and in front of people first for feedback.

image

image
image

Outdated screenshot with more student answers

image

Links

Testing story

Manual testing in LTR and RTL languages.

@rshipp rshipp requested a review from dju90 March 8, 2023 17:34
@dju90
Copy link
Contributor

dju90 commented Mar 8, 2023

Very cool that we were able to add the "X/Y students submitted"! If you change the section in the dropdown, does the entire page reload or does it fetch new student submissions asynchronously?
Also, maybe this is just a weird thing with the screenshot - but is there a reason why there are 5 student submissions on the page but only 2 students in the teacher panel on the right?

@rshipp
Copy link
Contributor Author

rshipp commented Mar 8, 2023

I'm just reloading the entire page, at least for now, because it's easier to pass in the responses from the HAML. If we wanted to change it to load responses via ajax, we could set up a new API endpoint for that.

And yeah, the 5/5 was a manual edit I did just for the screenshot, there are actually only 2 students in this section 😅.

@dju90
Copy link
Contributor

dju90 commented Mar 8, 2023

Yeah, don't worry about loading responses via ajax for now. Thanks! Looks great so far :)

<textarea
className={styles.freeResponse}
id={`level_${data.level.id}`}
aria-label={i18n.yourAnswer()}
Copy link
Contributor Author

@rshipp rshipp Mar 9, 2023

Choose a reason for hiding this comment

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

i don't love this aria-label, but I couldn't think of a way to do a visible label that made sense

Copy link
Contributor

Choose a reason for hiding this comment

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

Better this than nothing!

Comment on lines +25 to +33
// To avoid confusion, if a teacher tries to view the summary as a student,
// send them back to the level in Participant mode instead.
if (viewAs === ViewType.Participant) {
const paramString = document.location.search
.replace(SUMMARY_PARAM, '')
.replace('&&', '&')
.replace('?&', '?');
document.location.replace(currentLevel.url + paramString);
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

this feels messy, but I wasn't sure a better way that handled all the cases (url?view=summary, url?otherParams=blah&view=summary&moreParams=blah, etc)

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm interested in how a student would get into this case- would they have to type the url in manually? And this seems fine to me- I think in an ideal world (or maybe a later version?) we would have some sort of feedback/alert in red. Aside from that though, leaving them on the same level seems fine (instead of sending them back to the url they came from, which then might allow us to avoid writing them a new url).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If a student tries to visit the url, it just looks like this:

image

I figured it didn't matter, at least for the MVP, because there aren't any links to this page that a student would see.

I agree that an alert like the existing redirect warning if you try to visit an old version of a course, would be ideal.

Comment on lines +99 to +102
<label>
{i18n.responsesForClassSection()}
<SectionSelector reloadOnChange={true} />
</label>
Copy link
Contributor Author

Choose a reason for hiding this comment

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

unfortunately, the SectionSelector doesn't have an id, so I have to wrap the label instead of using a for attribute

}
end

teacher_markdown = if level.respond_to?(:solution) && level.solution.present? && Policies::InlineAnswer.visible_for_script_level?(current_user, @script_level)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

this respond_to? just makes it so the page doesn't crash if you try to view a non-free-response level in the summary view.

@rshipp rshipp marked this pull request as ready for review March 9, 2023 19:34
@rshipp rshipp requested a review from a team as a code owner March 9, 2023 19:34
@rshipp rshipp requested a review from a team March 9, 2023 19:34
Copy link
Contributor

@hannahbergam hannahbergam left a comment

Choose a reason for hiding this comment

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

Looks good! Some comments but none blocking. Additionally- is there a way we could test the case where a non-teacher tries to access this page? Thanks for getting all the A11y and translation work in on draft1.

const FREE_RESPONSE = 'FreeResponse';

const CheckForUnderstanding = ({
isRtl,
Copy link
Contributor

Choose a reason for hiding this comment

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

Glad this is included in V1:)

Comment on lines +25 to +33
// To avoid confusion, if a teacher tries to view the summary as a student,
// send them back to the level in Participant mode instead.
if (viewAs === ViewType.Participant) {
const paramString = document.location.search
.replace(SUMMARY_PARAM, '')
.replace('&&', '&')
.replace('?&', '?');
document.location.replace(currentLevel.url + paramString);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm interested in how a student would get into this case- would they have to type the url in manually? And this seems fine to me- I think in an ideal world (or maybe a later version?) we would have some sort of feedback/alert in red. Aside from that though, leaving them on the same level seems fine (instead of sending them back to the url they came from, which then might allow us to avoid writing them a new url).


const questionMarkdown = data.level.properties.long_instructions;
const teacherMarkdown = data.teacher_markdown;
const height = data.level.height || '80';
Copy link
Contributor

Choose a reason for hiding this comment

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

Is 80 a min height? Or default height? Would it ever change?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm not sure about this one, I copied this from the haml. The textarea can be resized, and I assume that there's somewhere in the levelbuilder editor that lets curriculum writers set this property?

)}
</p>

{/* Question Title */}
Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks for the comment labels here!

<textarea
className={styles.freeResponse}
id={`level_${data.level.id}`}
aria-label={i18n.yourAnswer()}
Copy link
Contributor

Choose a reason for hiding this comment

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

Better this than nothing!

id={`level_${data.level.id}`}
aria-label={i18n.yourAnswer()}
placeholder={
data.level.properties.placeholder || i18n.enterYourAnswerHere()
Copy link
Contributor

Choose a reason for hiding this comment

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

Would there a way to get a screen reader to read out this string instead? But then to your point- having it labeled as 'your answer' is helpful context too.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

i could reuse this for the label, but I didn't think it made as much sense, since it's intended for the placeholder. this could be weird if curriculum designers write something important as a placeholder though 🙃

};

export default connect(
// NOTE: Some of this state data is loaded in by the teacher panel. If you
Copy link
Contributor

Choose a reason for hiding this comment

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

👍🏻

border-bottom: unset;
}

p,
Copy link
Contributor

Choose a reason for hiding this comment

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

Out of curiosity- how does this syntax work?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

the comma selectors say to style "any of these": https://developer.mozilla.org/en-US/docs/Web/CSS/Selector_list

expect(wrapper.find('SectionSelector option').length).to.eq(2);
});

it('applies correct classes when rtl', () => {
Copy link
Contributor

Choose a reason for hiding this comment

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

🙌🏼

Comment on lines +2 to +6
level ||= if @level.contained_levels.empty?
@level
else
@level.contained_levels.first
end
Copy link
Contributor Author

Choose a reason for hiding this comment

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

important fix to support contained levels

@rshipp rshipp merged commit c0f4546 into staging Mar 14, 2023
@rshipp rshipp deleted the free-response-summary branch March 14, 2023 21:42
@rshipp rshipp changed the title [Check for Understanding] Add initial free response summary page [CFU] Add initial free response summary page Apr 4, 2023
@rshipp
Copy link
Contributor Author

rshipp commented Apr 4, 2023

For posterity, some notes on CFU implementation as of this writing:

Most of the code is in a new folder: apps/src/templates/levelSummary. There are also some pieces in apps/src/sites/studio/levels, dashboard/app/views/levels, and script_levels_controller.rb. The summary page route is /s/XXX/lessons/YYY/levels/ZZZ/summary. The summary page is still rendered by script_levels#show / levels/show.html.haml, the same as the level view page itself.

There is a new @responses variable available from the script_levels controller (and through it, all level views/partials). See dashboard/app/views/levels/_summary.html.haml partial for example usage.

Try not to expose the raw content of the level variable to the DOM, since it has a lot of things (like a full audit log with employee email addresses) that don't need to be exposed to users. Expose only what you need. This is the reason for the big js_data object in _summary.html.haml.

The summary view defaults to using answers from contained levels, whenever a contained level exists. The code for this is in the script_levels controller, if that ever needs to change.

Gotchas:

  • "Check for Understanding", "Summary", "Level Summary", "Gallery", are all words that have been used to describe the same page, with the student answer display.
  • "Check For Understanding" is also a level type that includes Free Response questions. Not all Free Response levels are CFU levels; some are Predict levels instead.
  • Note the comments about the Teacher Panel in the new React components. The Teacher Panel cannot be removed from the summary page without first loading student/section data into the component from the APIs.

Want to refactor:

  • Normalize to one name, e.g. rename "CheckForUnderstanding" to "LevelSummary" or something so that we can clear up some of the ambiguity.

Multi implementation notes:

  • Will need to add the entry point in two places in dashboard/app/views/levels/_single_multi.html.haml, one for contained and one for standalone.
  • Refactor the #summaryEntryPoint code out of apps/src/sites/studio/pages/levels/_free_response.js and into its own file, then include that in _free_response.html.haml and _single_multi.html.haml.
  • Break CheckForUnderstanding into reusable pieces and level-type-specific pieces, as part of implementing the summary view for Multi.
  • When adding any new level type, change the @responses code in script_levels_controller.rb to include that type, or you won't see student responses showing up in the summary view.

@dju90
Copy link
Contributor

dju90 commented Apr 4, 2023

"Check for Understanding", "Summary", "Level Summary", "Gallery", are all words that have been used to describe the same page, with the student answer display.

Oops, this is my bad :P

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.

None yet

3 participants