Skip to content

fix(extended-text-entry): commit the response before teardown (PIE-1058) - #3130

Merged
CarlaCostea merged 1 commit into
developfrom
fix/PIE-1058-extended-text-entry-session-flush
Sep 18, 2026
Merged

CarlaCostea merged 1 commit into
developfrom
fix/PIE-1058-extended-text-entry-session-flush

Conversation

@chillenious

@chillenious chillenious commented Sep 18, 2026 •

Copy link
Copy Markdown
Contributor

Online Testing loses a written response when the student clicks Next straight after typing. EditableHtml commits on blur, main.jsx passed that commit into debounce(this.props.onValueChange, 1500), and OT destroys the question view inside that window. No session-changed is dispatched, so the host cannot detect the loss. DNAFORM-1097 and DNAFORM-2207 are closed customer defects on this symptom.

This repo's half of PIE-1058, converged on with @andrei.lakatos-ext in the #pie-team thread. Player side: pie-players#402, pie-player-components#248, pie-api-components#194. Go-forward elements: pie-elements-ng#147. Design doc: pie-players — docs/prds/session-commit-on-teardown.md.

The session write becomes synchronous and only the dispatch is deferred, one debouncer per session field so each keeps its own complete semantics. commitPendingSession() — the name commitPendingSessions in @pie-players/pie-players-shared looks for — flushes on demand; disconnectedCallback flushes as a backstop.

Where to look:

  • Both flush paths ship and they cover different hosts. The method runs while the element is attached, so the event reaches a document-level listener. The disconnectedCallback flush runs after removal, reaching only a listener inside the removed subtree, and is what a host on a player with no commit seam gets.
  • The player-side commit alone does not cover this element. Against the real legacy stack, view removed 304ms after blur, element.session at sweep time was { id: "1" }: the response sat inside the React debounce and was never written, so the player's commit saved an empty session.
  • The 1500ms delay stays, coalescing nothing on a blur-only callback; shortening it is a separate decision. Moving it off the value path avoids the caret loss a shorter round trip through props.markup would carry.

math-inline, math-templated, explicit-constructed-response and multiple-choice have the same shape and are untouched here. OT's keyup workaround on .tiptap.ProseMirror can be dropped once this ships.

Online Testing loses a written response when the student clicks Next straight
after typing: `EditableHtml` commits on blur, `main.jsx` passed that commit into
`debounce(this.props.onValueChange, 1500)`, and OT destroys the question view
inside that window. No `session-changed` is dispatched, so the host cannot
detect the loss - there is no event that failed to arrive. DNAFORM-1097 and
DNAFORM-2207 are closed customer defects on this symptom.

The session write becomes synchronous and only the dispatch is deferred. The
debounce moves from `main.jsx` into the custom element, one debouncer per
session field so each keeps its own `complete` semantics. `this._session` now
holds the response as soon as the editor commits it, which is what lets any
other layer read it - a player's synthesized commit reads exactly that.

The delay is unchanged at 1500ms. It coalesces nothing on a blur-only callback,
so its only remaining effect is to delay the host's first sight of a committed
response; shortening or removing it is a separate decision.

`commitPendingSession()` dispatches every deferred event now, and is a no-op
when nothing is pending. A player calls it before discarding the element, while
the element is still attached and the event can therefore still reach a
`document`-level listener. `disconnectedCallback` also flushes, but it runs
after removal, so an event dispatched there reaches a listener bound inside the
removed subtree and nothing above it. Both paths ship: the method is the one
that covers a host listening on `document`, and the teardown flush is what a
host on a player with no commit seam gets.

The method name is the contract `commitPendingSessions` in
`@pie-players/pie-players-shared` looks for, which both legacy players now sweep
with (pie-player-components, pie-api-components). Nothing is required of a host.

Moving the debounce shortens the round trip through the host and back into
`EditableHtml`, where a `props.markup` change calls `editor.commands.setContent`
and discards the caret when the normalized markup differs from what the editor
holds. For this element the round trip is bounded by the callback firing only on
blur, so there is no caret to lose.

The other four legacy elements the design doc names - `math-inline`,
`math-templated`, `explicit-constructed-response`, `multiple-choice` - have the
same shape and are not touched here. Which of them needs the inline flush
depends on what still ships from this repository when the go-forward work lands;
the design doc's open questions track it.

Five tests on the element's event contract, each mutation-tested: dropping the
two `flush()` calls fails two of them. They drive the element with no model, so
`render()` no-ops and the assertions stay off a React mount of the editor.

Design doc: pie-players - docs/prds/session-commit-on-teardown.md
Discussion: the Online Testing thread in #pie-team, 2026-09-10 to 2026-09-17
import React from 'react';
import { createRoot } from 'react-dom/client';
import debug from 'debug';
import { debounce } from 'lodash-es';

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.

Please use:
import debounce from 'lodash-es/debounce' instead

@CarlaCostea
CarlaCostea merged commit cef51f2 into develop Sep 18, 2026
9 of 10 checks passed
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