Skip to content

Make QTI answer options a semantic list and pin reorder accessibility - #6125

Merged
AlexVelezLl merged 2 commits into
learningequality:unstablefrom
rtibblesbot:issue-6091-500144
Sep 1, 2026
Merged

Make QTI answer options a semantic list and pin reorder accessibility#6125
AlexVelezLl merged 2 commits into
learningequality:unstablefrom
rtibblesbot:issue-6091-500144

Conversation

@rtibblesbot

@rtibblesbot rtibblesbot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Summary

Screen reader users authoring a Choice question could not tell how many answer options there were or which position one sat in — the options rendered as unstructured <div>s. They now use <ol>/<li> markup like the Ordering editor, with the radiogroup/group role moved onto a wrapper so it no longer overrides the list role. Reordering already announced the new position and kept focus on the moved item's button; both are now pinned by tests in each editor.

References

Fixes #6091. Ordering editor this follows: #6089.

Reviewer guidance

On the QTI demo page (/channels/<id>/#/qti-demo), question 1 is Single Choice, question 6 is Ordering:

  1. Open question 1 for editing with a screen reader running — the answer options announce as a list of 4, each item with its position.
  2. Focus an option's move-down button and press Enter — focus stays on that option's button at its new position and the live region announces "Choice N moved to position X of Y". Move an option into the last position, where its move-down button hides — focus falls back to the visible move-up button.
  3. Drag an option to a new position with the mouse — SortableJS now binds to the <ol> rather than the wrapper it bound to before.

Open risks:

  • role="list" is explicit on both <ol>s because list-style: none drops the implicit role in Safari; I verified the markup in Chromium only.
  • The wrapper binds role="radiogroup" for single-select, duplicating what KRadioButtonGroup's own root sets. Binding undefined instead — the obvious alternative — leaves the key in the vnode's attrs and Vue's next patch strips the child's role; jsdom does not reproduce that, so no test guards the wrapper against being "cleaned up" back to undefined.
Choice editor Ordering editor Keyboard reorder
Choice editor Ordering editor

axe-core AA audit of the demo page in edit mode: one serious color-contrast violation on AddListItemButton's label, outside this diff and untouched by it.

AI usage

Used Claude Code to investigate the three acceptance criteria, write the tests and restructure the Choice markup, following the Ordering editor as the reference pattern; I directed the scope and reviewed the output. Verified with the Jest suite, pre-commit, and manual QA in Chromium covering keyboard reorder, live-region announcements and mouse drag.


@rtibblesbot's comments are generated by an LLM, and should be evaluated accordingly

How was this generated?
  • Ran pre-flight CI checks (lint, format, tests) and verified all pass
  • Rebased onto the target branch and resolved any conflicts
  • Reorganized commit history into clean, logical commits
  • Audited the diff to ensure only issue-relevant files are changed
  • Built PR body from the repository's PR template with evidence blocks
@rtibblesbot

🟡 Waiting for feedback

Last updated: 2026-09-01 16:48 UTC

DragSortWidget announces the move and restores focus; nothing pinned that the
Choice and Ordering editors keep feeding it itemLabel/position/total, or that a
re-key on reorder does not destroy the row and drop focus to <body>.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@rtibblesbot
rtibblesbot force-pushed the issue-6091-500144 branch 2 times, most recently from 34e302b to 40fb9c6 Compare September 1, 2026 16:41
The Choice editor rendered its options as divs, so screen readers conveyed
neither list size nor an option's position. Adopt Ordering's <ol>/<li>.

role="group"/"radiogroup" override an element's implicit list role, so grouping
moves to a wrapper outside DraggableRegion and the <ol> becomes the region's
slot root — SortableJS sorts its direct children, which are now the <li> rows.

role="list" is explicit on both editors' lists: list-style: none drops the
implicit role in Safari.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@rtibblesbot
rtibblesbot marked this pull request as ready for review September 1, 2026 16:48

@AlexVelezLl AlexVelezLl left a comment

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.

Code changes make sense; manual QA checks out. Thanks!

@AlexVelezLl
AlexVelezLl merged commit 4f8c963 into learningequality:unstable Sep 1, 2026
28 checks passed
@rtibblesbot
rtibblesbot deleted the issue-6091-500144 branch September 1, 2026 17:14
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.

[QTI] Improve accessibility of option reordering in QTI Choice and Ordering interaction editors

2 participants