Skip to content

Fix trickle button never re-enabling after a popup closes #267

Description

@swashbuck

Subject of the issue/enhancement/features

On a trickle-gated block, if a popup opens and closes while the step is unlocked and the button has not been clicked, the Continue button stays disabled permanently and the page can never be completed.

Any popup does it. A question's feedback popup is the most common way to hit it, but the drawer does it too, on a page with no questions at all.

This is a regression introduced in 7.9.1 by #264. Version 7.8.0 is not affected.

Your environment

  • Adapt Framework 5.56.2, adapt-contrib-core 6.78.3, adapt-contrib-trickle 7.9.1
  • Also reproduced on adapt-contrib-core 6.77.1, the version Framework 5.56.2 ships with
  • Chrome 151, macOS

Steps to reproduce

The shortest version needs no course JSON and no questions. On any page with a trickled block whose step is unlocked and whose button is enabled, run this in the console:

require('core/js/drawer').open();
require('core/js/drawer').close();

The Continue button is now disabled and never recovers. Opening the drawer with the navigation bar button and closing it again does exactly the same thing.

The way learners hit it in a real course:

  1. Author a block with _trickle._isEnabled: true and _trickle._button._isEnabled: true, leaving _stepLocking at its default so completion is required. Add at least one block after it.
  2. Put a question in that block with _canShowFeedback: true.
  3. Answer the question and submit it, so the feedback popup opens.
  4. Dismiss the feedback popup.

The Continue button is disabled and stays that way.

Two other routes to the same dead end, both confirmed:

  • Open the drawer to look at resources on a trickled page, then close it.
  • With _pageIncompletePrompt enabled, try to leave an incomplete trickled page and answer "No" to stay. Staying is what breaks the page, which leaves the learner with no way forward at all.

Expected behaviour

Once the last popup closes, the Continue button becomes clickable again and the learner can carry on.

Actual behaviour

The button stays disabled forever, so the page cannot be completed. It is left with aria-disabled="true" and the is-disabled is-locked classes while no popup is open. Note it is not natively disabled, so anything checking the button's disabled property will wrongly think it is fine.

The cause is event ordering. Core fires popup:closing before the closing popup is removed from the a11y stack, so any handler on that event still sees a11y.isPopupOpen === true for the very popup that is going away. #264 changed trickle to ask exactly that question at exactly that moment, in two places:

  • the guard at the top of onPopupClosed, which therefore always returns early
  • calculateButtonState, which therefore always recomputes the button as disabled

Before #264 trickle kept its own count of open popups and decremented it at the top of the handler, which cancelled the ordering out. Both call sites need to account for the closing popup, not just the guard: fixing only the guard still leaves the recalculation computing "disabled".

Worth noting for anyone considering a straight revert: #264 exists to fix #263, so reverting would reintroduce unlocking behind popups on returning visits. A fix needs to keep a11y as the source of truth and account for the closing phase instead.

Suggested Fix (if known)

Use a11y.popupStack.length rather than a11y.isPopupOpen at both sites, and discount the closing popup while popup:closing is being handled. A pull request follows.

Posted via collaboration with Claude Code

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions