Skip to content

Parsons Block Overflow and Layout Fixes#1246

Open
loua wants to merge 3 commits into
RunestoneInteractive:mainfrom
loua:parsons-fix
Open

Parsons Block Overflow and Layout Fixes#1246
loua wants to merge 3 commits into
RunestoneInteractive:mainfrom
loua:parsons-fix

Conversation

@loua

@loua loua commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR fixes several related layout bugs in Parsons problems and improves performance for language="math" and language="natural" problems. The PR is related to issue #1214

Changes

parsons.js

Bug fix: left-numbered blocks overflow the source area

When numbered="left" is set on <blocks>, the 25px label column was not being subtracted from maxSourceAreaWidth before the area width was halved, causing blocks to overflow their container by a small but visible amount. The fix deducts the label width before the halving calculation so the source area is sized correctly.

Bug fix: source area height grows on repeated resets

The block measurement loop was setting each block's width to the running maximum as it went, meaning earlier blocks were sized to a narrower width than later ones. On reset, those previously-set widths became the starting outerWidth() for the next measurement pass, causing areaHeight to drift upward with each reset. The fix separates measurement into three explicit passes: render, measure widths, then apply the final uniform width and measure heights.

Bug fix: language="math" blocks have incorrect spacing and positioning on initial load

For language="math" and language="natural" problems, block positions and paired distractor brackets were calculated before MathJax had finished rendering, using pre-render block heights. Clicking reset corrected the layout because by then MathJax had already rendered. The fix replaces the per-block serial queueMathJax calls with a single MathJax.typesetPromise over all blocks at once, then defers the re-layout with Promise.resolve().then() to allow the browser to reflow before outerHeight() is measured.

Performance improvement: language="math" rendering

The previous implementation called queueMathJax once per block, resulting in N+1 serial MathJax typeset passes for a problem with N blocks. These are now replaced with a single MathJax.typesetPromise([...allBlocks]) call, substantially reducing render time for problems with many blocks.

Cleanup: removed legacy self = this pattern

let self = this was used to capture this for use inside function() callbacks. The rewritten code uses arrow functions throughout, making self unnecessary.

parsons.css

CSS: override MathJax automatic font scaling inside Parsons blocks

MathJax computes a scaling factor (typically ~118%) relative to the surrounding element's font size and sets it as an inline style on mjx-container. This caused math to render noticeably larger than surrounding text. Setting font-size: 1em !important on .parsons .block mjx-container overrides the inline style and keeps math at the same size as surrounding text.

Test cases

A test page covering 14 distinct option combinations is available at:
https://loua.github.io/pretext-examples/parsons-overflow-tests-v1.html
https://loua.github.io/pretext-examples/parsons-overflow-tests-v2.html

The test cases cover: baseline reproduction cases, non-adaptive (static) exercises, long lines, 10+ blocks (double-digit labels), Java and natural language, right-numbered controls, multiple indent levels, no-numbering controls, language="math" with MathJax async sizing, the removeIndentation() adaptive path, multi-line blocks, DAG grader, maximum indent depth, and explicit shuffle order.

@loua loua requested a review from bnmnetp as a code owner June 22, 2026 14:59
@bnmnetp

bnmnetp commented Jun 22, 2026

Copy link
Copy Markdown
Member

Thanks for the careful work on this! I'm going to give it a spin later today.

One nit, we have a goal (maybe not clearly stated) to eliminate jQuery from our codebase. So to the extent you can do this update without using jQuery that would be great.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR targets Parsons problem layout correctness (notably with left-numbered blocks and MathJax-rendered content) and reduces MathJax rendering overhead for language="math" / language="natural" by batching typesetting and stabilizing measurement passes.

Changes:

  • Fixes source-area width calculation when block labels are present to prevent overflow.
  • Refactors block sizing to measure widths/heights in explicit passes to prevent height drift across resets.
  • Adds CSS to normalize MathJax sizing inside Parsons blocks and adjusts label styling/alignment.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
bases/rsptx/interactives/runestone/parsons/js/parsons.js Reworks sizing/typesetting and adds a post-MathJax re-layout path to stabilize widths/heights and prevent overflow.
bases/rsptx/interactives/runestone/parsons/css/parsons.css Tweaks block label styling and forces MathJax containers in blocks to use 1em font size.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +733 to +744
if (isMath) {
const blockElements = blocks.map(b => $(b.view)[0]);
if (typeof runestoneMathReady !== "undefined") {
await runestoneMathReady.then(
async () => await MathJax.typesetPromise(blockElements)
);
} else {
if (typeof MathJax.startup !== "undefined") {
await MathJax.typesetPromise(blockElements);
}
}
areaWidth = Math.max(areaWidth, item.outerWidth(true));
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

this one is important.

Comment on lines +885 to +887
if (this.options.numbered != undefined) {
newAreaWidth += 25;
}
Comment on lines 860 to +865
if (
this.options.language == "natural" ||
this.options.language == "math"
) {
if (typeof MathJax.startup !== "undefined") {
self.queueMathJax(self.outerDiv);
// initializeAreas already typeset all blocks in a single batch call,
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