Skip to content

Keep a paragraph's text when a composite's replacements lose their strings - #2282

Open
cqnykamp wants to merge 3 commits into
mainfrom
claude/hopeful-hypatia-iufo0h
Open

cqnykamp wants to merge 3 commits into
mainfrom
claude/hopeful-hypatia-iufo0h

Conversation

@cqnykamp

@cqnykamp cqnykamp commented Oct 17, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

A paragraph's text stopped following a <conditionalContent> inside it in one case: the content switches from a case with a string between its components to a branch without one. For example:

<mathInput name="n" />
<p name="p"><conditionalContent>
  <case condition="$n > 0"><text>dog</text> mouse <text>cat</text></case>
  <else><text>bird</text><text>fish</text></else>
</conditionalContent></p>
<p name="show">$p.text</p>

Type 1, then 0, into n. show should change from "dog mouse cat" to "birdfish", but it keeps showing "dog mouse cat".

The paragraph itself renders correctly, because the viewer renders its children, not its text. The bug only shows where something reads text, such as $p.text. The core keeps the old value because computing the new one throws inside the core (Cannot read properties of undefined (reading 'toString') from textFromComponent). A test that asks for all state variables (returnAllStateVariables) receives that error directly.

Cause

ChildDependency (core/dependencies/childDependencies.ts) handles a request for hidden over children that include a string specially. Strings have no hidden, so it adds the hidden of each composite the children came from as extra downstream components, and sets addedCompositeHiddenDependency. Later, getValue strips one trailing value per composite range while that flag is set.

The flag was set when a string appeared and never cleared. Once the replacements changed to ones with no string, no composite dependencies were added, but getValue still stripped the last values. Those values were the real children. The paragraph's text then received undefined children.

Fix

determineDownstreamComponents now resets the flag each time it decides the downstream components, before deciding whether to add the composite hidden dependencies.

Tests

  • New test in conditionalcontent.test.ts: "text of a paragraph when a case with a string gives way to an else without one". It checks both $p.text (as a document reads it) and the paragraph's own text. On main it fails: expected 'dog mouse cat' to equal 'birdfish'. It passes with the fix.
  • Full doenetml-worker-javascript Vitest suite run locally: 5,239 tests pass. One test fails: prefigure/chart-bar.test.ts › "measures a fractional label…". It fails identically on main in the same environment (Node 22), and main's CI passes it.

Found through #2281, whose rewritten conditional-content test hits this case.

🤖 Generated with Claude Code

https://claude.ai/code/session_01XhPqNj8iDfTLu7sq5Pcpd2

…rings

A child dependency that asks for `hidden` depends on the hidden of each
composite its children came from while the children include a string, and
`getValue` strips those extra values off the end while
`addedCompositeHiddenDependency` is set. The flag was set when a string
appeared and never cleared, so once the replacements changed to ones with
no string (a conditional content's case with a string giving way to its
else), `getValue` stripped real children instead, and the paragraph's text
read undefined children and threw. The flag is now decided afresh each time
the downstream components are.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XhPqNj8iDfTLu7sq5Pcpd2
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XhPqNj8iDfTLu7sq5Pcpd2
The paragraph's text, read through a reference, kept the case's text after
the switch to the else; the test now checks that reference too, and the
changeset says what a user saw.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XhPqNj8iDfTLu7sq5Pcpd2
@cqnykamp
cqnykamp marked this pull request as ready for review October 17, 2026 13:49
@cqnykamp
cqnykamp requested a review from dqnykamp October 17, 2026 13:49

Copy link
Copy Markdown
Collaborator Author

Test Main (24.x) failed on 13071131a. All three failures are timeouts in packages this PR doesn't touch; the PR only changes doenetml-worker-javascript and a changeset:

  • @doenet/doenetml-to-pretext: in test/pretext-export.test.ts, the beforeAll hook timed out after 40 s while downloading the browser. Its 152 tests were skipped.
  • @doenet/i18n: catalogLint.test.ts › "is written by no catalog in the roster" took 7.1 s against the 5 s limit.
  • @doenet/prefigure: index-api.test.ts › "returns an assets URL for default index path" took 5.4 s against the 5 s limit, most of it a cold module import.

The same job passed on the previous head, a2043cdc9, and this push changed only one test and the changeset. No fix is needed from this PR. I'm re-running the failed job once.


Generated by Claude Code

This branch has not been deployed

No deployments
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.

2 participants