Skip to content

Quiz: a strict window-leave level, and a reusable three-position switch #1745

Closed

Description

@rpgmem

Problem

The quiz reset of #1731 listens for visibilitychange, which fires only when the tab stops being visible. With two windows side by side, the participant moves to the other window and the quiz window stays visible: it loses the focus, not the visibility, so nothing is cleared.

Design

  • A reusable admin control. AdminUI::render_tri_toggle() draws a three-position switch: a neutral track and a knob whose position and colour say the level. Under it are three native radios, so the keyboard (arrow keys), screen readers and forced colours work. The palette is a parameter:

    • severity (default): grey → amber → red;
    • traffic: red → amber → green.

    The level's name always sits next to the knob, so colour is never the only signal.

  • Autosave. Both autosave endpoints learn a closed list of values. A value outside the list is refused, and the field's badge shows the failure. The settings endpoint does this today only for booleans and numbers; the form-meta endpoint does it only for booleans.

  • Quiz. "Reset when leaving" becomes Off / Moderate / Strict, stored in the existing quiz_reset_on_leave as 0 / 1 / 2. Forms saved with 1 stay on Moderate, so there is no migration.

    • Strict adds the window's blur/focus. It ignores focus moving to an iframe of the page, and counts a loss only when it lasts longer than a grace period.
  • Grace period. A global setting, Settings → Certificates → "Focus-loss grace (ms)". Default 1000, range 0–10000, where 0 means every loss counts. It is used only by Strict.

Acceptance criteria

  • AdminUI::render_tri_toggle() with both palettes, with contrast measured in both themes. (Shipped as render_steps(); see the closing comment.)
  • Closed-list values in the settings and form-meta autosave endpoints.
  • Quiz Off / Moderate / Strict, with an existing 1 read as Moderate; the participant notice follows the level.
  • Strict level in ffc-quiz-leave.js: iframe filter and grace period.
  • Global grace setting (required number field; empty is not 0).
  • Documentation (including the component, for reuse), three catalogues, CHANGELOG, PHPUnit and Vitest tests.

A follow-up moves the admin's other ordered three-level selects to this control. (Done in #1747.)

Activity

  1. rpgmem commented on Oct 13, 2026

    @rpgmem
    OwnerAuthor

    Delivered on develop by #1746 and #1747.


    Generated by Claude Code

  2. added 3 commits that reference this issue on Oct 13, 2026
    1d4e7bd
    e974519
    00eb9c8
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions