Repository navigation
Question type conversion - #6311
AlexVelezLl wants to merge 6 commits into
Conversation
a234108 to
eb36df2
Compare
eb36df2 to
d7d0bcb
Compare
d7d0bcb to
bd4a38e
Compare
f425d85 to
6172971
Compare
6172971 to
8051484
Compare
🟢 Re-reviewingLast updated: 2026-10-28 16:45 UTC |
There was a problem hiding this comment.
PR #6311: conversion, the confirm modal and focus return work as #6190 specifies. Manual QA covered every conversion row, mouse and keyboard cancel/confirm, and de/fr numerics. It found no regressions.
CI pending.
- Suggestions: scope (inline choice editor, focus on card open, toolbar restyle, global
::selection), an unannounced dropdown restore, and gap match not being reachable yet. - Nitpicks: a hard-coded number in a string, test robustness, duplicated comments, and the toolbar at 360px.
Next step: Needs a full code review by a human engineer, then manual QA once that reviewer sets Needs QA.
- User-visible files changed:
.stylelintrc.js,contentcuration/contentcuration/frontend/shared/styles/vuetify.scss,contentcuration/contentcuration/frontend/shared/views/QTIEditor/components/ChangeQuestionTypeModal/index.vue,contentcuration/contentcuration/frontend/shared/views/QTIEditor/components/InteractionSection/index.vue,contentcuration/contentcuration/frontend/shared/views/QTIEditor/components/QTIItemEditor/index.vue…. - Decide whether the inline choice editor, card-open focus and toolbar restyle ship here or split from the conversion PR.
- Confirm the app-wide
::selectionchange (KDS colour, no Vuetify rule) is acceptable on dark surfaces. - After any split or fixes, re-run conversion QA, including the screen-reader announcement on dropdown undo/paste.
QA captures (50)
Convert a filled single choice and a multi select (two correct, two wrong answers) to numeric input, text entry and single choice — each modal lists only the applicable sentences (I, E, R) per #6190's table, confirm, then check the kept answers, correct marks, shuffle/fixed settings, and that they survive saving and reopening the question
Pick a type change that shows the modal, cancel, then pick the same type again and confirm — once with the mouse and once keyboard-only; the question is unchanged after cancel, the selector reverts to the original type, focus returns to the type selector after cancel and confirm, and the modal title names the picked type
| Step | Screenshot |
|---|---|
| Keyboard modal open | ![]() |
| Mouse after cancel | ![]() |
| Mouse after confirm | ![]() |
| Mouse modal open | ![]() |
| Q5 before | ![]() |
2-keyboard-cancel-confirm.webm
2-mouse-cancel-confirm.webm
Switch types where nothing is lost (single → multi, numeric → text entry, text entry → multi, free response → any type, an empty question → any type) — no modal appears, the type switches straight away, answers carry over, and focus stays on the type selector rather than jumping into the remounted editor
| Step | Screenshot |
|---|---|
| Multi to associate after keyboard | ![]() |
| Numeric to text entry | ![]() |
| Single to multi after | ![]() |
| Text entry to multi | ![]() |
3-free-response-switches.webm
3-single-to-multi.webm
Convert between choice types and ordering — single/multi → ordering shows C and O and the current order becomes the correct order; ordering → single/multi shows N; numeric/text entry → ordering shows O; with one answer the O/N sentences are absent
Convert associate ↔ match with pairs sharing a source and with distractors — match groups shared-source pairs into one row with several matches, the reverse splits them, no modal either way; then associate or match → an answer-list type shows A and removes all answers
| Step | Screenshot |
|---|---|
| Associate before | ![]() |
| Associate split | ![]() |
| Associate to multi modal | ![]() |
| Match grouped | ![]() |
| Match to single modal | ![]() |
| Multi after answers removed | ![]() |
In a German and then a French exercise, convert numeric input with decimal answers (e.g. 3.5 stored) to text entry, single and multi — answers appear as 3,5; convert text entry/choice answers like 3,5 back to numeric and check the stored value and the displayed value after reopening
Written by rtibblesbot, an LLM-based coding agent.
8051484 to
9e7aeb2
Compare
There was a problem hiding this comment.
PR #6311: the More menu opens off-screen in narrow windows. Two prior findings are still open. The other 12 are settled.
CI passing. QA covered ten scenarios: inline choice authoring, keyboard passage editing, validation, narrow toolbars, every type-change family, numeric conversion in German, persistence. Only the More menu failed. Every axe violation was pre-existing.
- blocking: More menu opens off-screen in narrow windows (inline).
- suggestion (open): gap match conversions are still unreachable. Please add
getTypeOptionstogapMatch/Descriptor.js:52and drop it fromHEADLESS_INTERACTIONS. - nitpick (open):
InteractionSection.spec.js:122still reads KSelect's.select-value-text. Please assert on the visible text instead. - nitpick: many new comments explain the branch's own design (inline).
Prior-finding status
ACKNOWLEDGED — interactions/index.js:7 — inline choice editor outside #6190 (stacked on #6303)
ACKNOWLEDGED — inlineChoice/useInlineChoicePassage.js:180 — no announcement when a dropdown comes back (#6303)
RESOLVED — components/QTIItemEditor/index.vue:305 — focus on card open outside scope (withdrawn: 47c85b0a9 ties it to focus returning to the type selector)
ACKNOWLEDGED — TipTapEditor/components/EditorToolbar.vue:140 — toolbar changes outside #6190 (#6303)
ACKNOWLEDGED — TipTapEditor/components/EditorToolbar.vue:404 — Insert/collapse scroll off at 360px (#6303)
UNADDRESSED — interactions/gapMatch/Descriptor.js:52 — gap match conversions unreachable without getTypeOptions
ACKNOWLEDGED — qtiEditorStrings.js:373 — hard-coded 2 (#6303)
RESOLVED — TipTapEditor/tests/TipTapEditor.spec.js — mocked requestAnimationFrame leaks on failure
RESOLVED — interactions/tests/editors.spec.js — TEXTAREA assertion passes on wrong field
ACKNOWLEDGED — inlineChoice/components/InlineChoiceOptions/index.vue:306 — focus lost outside an option <li> (#6303)
RESOLVED — conversion/tests/convertInteraction.spec.js — case-sensitivity test name
UNADDRESSED — components/InteractionSection/tests/InteractionSection.spec.js:122 — depends on KSelect .select-value-text
RESOLVED — interactions/choice/Editor.vue — focus comment copied into six editors
ACKNOWLEDGED — styles/vuetify.scss:2482 — removed rule changes selection app-wide (#6303)
Next step: Needs a full code review by a human engineer, then manual QA once that reviewer sets Needs QA.
- User-visible files changed:
.stylelintrc.js,contentcuration/contentcuration/frontend/shared/styles/vuetify.scss,contentcuration/contentcuration/frontend/shared/views/QTIEditor/components/ChangeQuestionTypeModal/index.vue,contentcuration/contentcuration/frontend/shared/views/QTIEditor/components/InteractionSection/index.vue,contentcuration/contentcuration/frontend/shared/views/QTIEditor/components/QTIItemEditor/index.vue…. - Decide whether converting a question's type should drop answers, correct marks and formatting by the #6190 table, since the stored item changes when the author confirms.
- Confirm the shared conversion core and per-descriptor
convert.jsmodules are a pattern other interaction types should follow. - Once the More menu fix lands, re-check toolbar menus at 360px and 600px in the question and passage editors.
QA captures (50)
Author an inline choice question from scratch: type a passage, insert dropdowns, then use each dropdown's options panel to add, edit, remove and reorder options and mark the correct one — Shuffle hides reordering, Escape/close/passage click return focus to the dropdown, and the question survives closing and reopening its card
| Step | Screenshot |
|---|---|
| Closed card view mode | ![]() |
| Options panel four options correct marked | ![]() |
| Options panel shuffle on no reorder | ![]() |
| Passage two dropdowns | ![]() |
1-options-panel-flow.webm
Edit an inline choice passage with the keyboard only — arrow keys move the caret past dropdowns instead of selecting them, Backspace/Delete beside a dropdown removes it, Left/Right from a focused dropdown returns to the text, undo/redo inside the options panel, and live-region announcements for crossed and removed dropdowns
| Step | Screenshot |
|---|---|
| Backspace removed dropdown | ![]() |
| Caret after dropdown | ![]() |
| Options panel keyboard open | ![]() |
| Undo opens other dropdown | ![]() |
2-keyboard-passage-editing.webm
Check inline choice validation and view mode — errors on the option, dropdown and passage they belong to, the "needs attention" naming in a preview that hides answers, and each dropdown showing its correct answer with Show answers on and "Choose…" with it off
Use the rich text toolbar in a narrow window on both the passage editor and an ordinary question/answer editor — Insert shrinks to its icon and looks like other disabled tools when unavailable, the toolbar scrolls sideways once tools collapse into More, the More menu opens outside the toolbar, and selected text uses KDS's selection colour on other Studio pages
| Step | Screenshot |
|---|---|
| Channel list selection | ![]() |
| Narrow passage insert available | ![]() |
| Narrow passage insert unavailable | ![]() |
| Narrow question toolbar | ![]() |
| Question more menu 800 | ![]() |
| Wide passage insert | ![]() |
| Wide question toolbar | ![]() |
Change type within the answer-list family with the mouse — single→multi switches with no modal, multi→single shows R, multi→text entry shows I and E, single/multi→ordering shows C and O, ordering→single shows N, anything→free response shows A; check each modal lists only the sentences that apply, Cancel leaves the question and type unchanged, and Confirm produces the answers and correct marks the rules in #6190 describe
5-type-change-flow.webm
Convert numeric and text entry answers — numeric→single/multi in a German or French exercise writes numbers as "3,5", text entry→numeric switches without a modal and keeps non-numeric answers with validation pointing them out, and rich single/multi/ordering answers (formatting, image, formula) going to numeric or text entry show F and drop image/formula-only answers
Written by rtibblesbot, an LLM-based coding agent.
9e7aeb2 to
e0b8567
Compare
There was a problem hiding this comment.
PR #6311: the More menu still opens off-screen in narrow windows. 14 of 16 prior findings are resolved or acknowledged; one new nitpick.
- blocking: More menu still opens off-screen in narrow windows (inline). It comes from #6303, so fixing it there carries over.
- nitpick:
isHiddenre-implementstoBeVisible()(inline).
CI passing. Manual QA covered inline choice authoring, validation, undo/redo, keyboard/RTL, and every type-change family. All behaved as specified except the More menu. Axe found only pre-existing violations.
Prior-finding status
RESOLVED — interactions/index.js:7 — inline choice editor outside #6190 scope
RESOLVED — inlineChoice/useInlineChoicePassage.js:180 — no announcement when a dropdown comes back
RESOLVED — QTIItemEditor/index.vue:305 — focusing the question on card open out of scope
RESOLVED — EditorToolbar.vue:140 — toolbar changes outside #6190
RESOLVED — EditorToolbar.vue:404 — Insert/collapse scroll off at 360px
ACKNOWLEDGED — gapMatch/Descriptor.js:52 — gap match conversions unreachable until #6204
RESOLVED — qtiEditorStrings.js — hard-coded 2 in too-few-options message
RESOLVED — TipTapEditor.spec.js — mocked requestAnimationFrame leaks
RESOLVED — editors.spec.js — TEXTAREA focus assertion too weak
RESOLVED — InlineChoiceOptions/index.vue:306 — focus lost outside option <li>
RESOLVED — convertInteraction.spec.js — case-sensitivity test name
RESOLVED — InteractionSection.spec.js — depends on KSelect .select-value-text
RESOLVED — choice/Editor.vue — focus comment copied into six editors
RESOLVED — vuetify.scss:2482 — removed text-selection rule
UNADDRESSED — EditorToolbar.vue:106 — More menu opens off-screen in narrow windows
ACKNOWLEDGED — conversion/answers.js:28 — design-narrating comments (author keeps them as rationale)
Next step: Needs a full code review by a human engineer, then manual QA once that reviewer sets Needs QA.
- User-visible files changed:
.stylelintrc.js,contentcuration/contentcuration/frontend/shared/styles/vuetify.scss,contentcuration/contentcuration/frontend/shared/views/QTIEditor/components/ChangeQuestionTypeModal/index.vue,contentcuration/contentcuration/frontend/shared/views/QTIEditor/components/InteractionSection/index.vue,contentcuration/contentcuration/frontend/shared/views/QTIEditor/components/QTIItemEditor/index.vue…. - Check that the conversion rules in
conversion/answers.js(what each type change keeps or drops) match the product intent of #6190, since other interactions will copy them. - Re-check the More menu at 480px and 360px widths once the
preventOverflowfix lands from #6303.
QA captures (50)
Build an inline choice question from scratch in an exercise's Questions tab, save, close and reopen the card — pick Inline choice in the type selector, type a passage, insert a blank dropdown with the prominent Insert action and convert highlighted text into another, fill options and mark correct ones in the options panel, check the passage, chips and correct answers survive the reopen and the exercise's sync request
| Step | Screenshot |
|---|---|
| Closed card | ![]() |
| Options panel filled | ![]() |
| Passage typed insert action | ![]() |
| Reopened second dropdown | ![]() |
| Second dropdown from highlight | ![]() |
| Type selector | ![]() |
page@674dc52acb9218fcdd6cb3befa1a538d.webm
Edit an inline choice dropdown's options panel with the mouse — open a chip, add and delete options, mark a different option correct, reorder options and confirm reordering is unavailable while shuffle is on, close the panel by its close button and by clicking outside, and check clicks inside the panel don't close the passage editor or lose edits
| Step | Screenshot |
|---|---|
| Closed by close button | ![]() |
| Option 2 marked correct | ![]() |
| Shuffle on no reorder | ![]() |
2-options-panel-mouse.webm
Drive an inline choice passage with the keyboard and a screen reader view of the DOM only — arrow the caret past chips without selecting them, open a chip's options from the keyboard, Tab and Shift+Tab wrap inside the panel's focus trap, delete an option and check focus stays in the panel, Escape returns focus to the chip, and chip/panel aria labels and live announcements (option counts, dropdown removed) are present
| Step | Screenshot |
|---|---|
| Correct option deleted | ![]() |
| Dropdown removed | ![]() |
| Duplicate option error | ![]() |
| Panel opened from keyboard | ![]() |
| Second dropdown panel focus close | ![]() |
3-keyboard-chip-panel.webm
Undo and redo inside an inline choice question — Ctrl/Cmd+Z, Shift+Ctrl/Cmd+Z and Ctrl+Y from the passage, from a focused chip and from inside the options panel after adding, removing and editing options and deleting a chip, checking the passage and panel stay in step and focus stays in the panel
| Step | Screenshot |
|---|---|
| Before undo panel | ![]() |
| Undo opens restored chip | ![]() |
4-panel-undo-redo.webm
Inline choice validation — leave the passage with no dropdown, a dropdown with fewer than the minimum options, empty and duplicate options, no correct option, check each error message appears once and in the right place (chip, panel, question), then fix each and see it clear
| Step | Screenshot |
|---|---|
| Recheck duplicate | ![]() |
Rich text toolbar in a narrow window — shrink the window to a small breakpoint while editing an inline choice passage and another question's rich text field, check the Insert action becomes icon-only with an accessible name, the toolbar scrolls rather than overflowing, the More menu opens outside the toolbar and is usable, the unavailable Insert action looks disabled and doesn't change on hover, and text selection highlight is still visible
| Step | Screenshot |
|---|---|
| Inline choice toolbar 480 | ![]() |
| Insert unavailable hover | ![]() |
| Insert unavailable selection | ![]() |
| Toolbar 320 scrolled | ![]() |
| Toolbar 320 start | ![]() |
Change type within the answer-list family with the mouse — fill a multi select with several correct and wrong answers, switch to single choice, text entry, numeric input and ordering in turn, checking each modal lists exactly the sentences from #6190's table (R, I/E, C/O), Cancel leaves the question and type untouched, Change carries over answers, correct marks and shuffle as the table says, and a lossless switch (single to multi, numeric to text entry, free response to anything) opens no modal
Change type from numeric input and text entry — numeric answers with decimals in an exercise whose language is German or French move to single choice and text entry written as 3,5 rather than 3.5, non-numeric text entry answers going to numeric input are kept and flagged by validation, and rich choice answers with formatting, images or formulas going to text entry show the formatting-removed sentence and drop image-only answers
Change type between pairs and across families — associate to match groups pairs sharing a source into one row and keeps distractors with no modal, match back to associate likewise, then associate or match to single choice warns that all answers will be removed and keeps only the question; check the saved item after each confirmed change
| Step | Screenshot |
|---|---|
| Associate after match | ![]() |
| Associate before | ![]() |
9-associate-to-single-change.webm
Written by rtibblesbot, an LLM-based coding agent.
e0b8567 to
90c2e43
Compare
There was a problem hiding this comment.
PR #6311: 16 of 17 prior findings resolved or acknowledged; 1 blocking still open.
- blocking (contested):
EditorToolbar.vue:106: the More menu still opens above the viewport in narrow windows. On a 480×900 passage its top sits at -231px. Its first tools can't be seen or reached. The KDS #1384 fix isn't released yet. Until it is, keep the menu inside the viewport here, or get a maintainer to agree to ship with it in the open thread.
CI pending. Manual QA covered every conversion in #6190's table by mouse and keyboard. Server raw_data was checked after reload. Nothing else turned up.
Prior-finding status
ACKNOWLEDGED — interactions/index.js:7 — inline choice editor outside #6190 (stacked on #6303)
ACKNOWLEDGED — inlineChoice/useInlineChoicePassage.js:180 — no announcement when a dropdown comes back (#6303)
RESOLVED — QTIItemEditor/index.vue:305 — focus on card open out of scope (withdrawn: the remounted editor for a new type must not take focus, so this belongs to the conversion)
ACKNOWLEDGED — EditorToolbar.vue:140 — toolbar changes outside #6190 (#6303)
ACKNOWLEDGED — EditorToolbar.vue:404 — Insert/collapse scroll off at 360px (#6303)
ACKNOWLEDGED — gapMatch/Descriptor.js:52 — gap match conversions unreachable until #6204
ACKNOWLEDGED — qtiEditorStrings.js — hard-coded 2 (#6303)
RESOLVED — TipTapEditor.spec.js — mocked requestAnimationFrame leaks on failure
RESOLVED — editors.spec.js — TEXTAREA focus assertion too weak
ACKNOWLEDGED — InlineChoiceOptions/index.vue:306 — focus lost outside an option <li> (#6303)
RESOLVED — convertInteraction.spec.js — case-sensitivity test name
RESOLVED — InteractionSection.spec.js — depends on .select-value-text
RESOLVED — choice/Editor.vue — focus comment copied into six editors
ACKNOWLEDGED — vuetify.scss:2482 — removed selection rule (#6303)
ACKNOWLEDGED — conversion/answers.js:28 — design-explaining comments (author keeps them as rationale)
CONTESTED — EditorToolbar.vue:106 — More menu opens off-screen in narrow windows (deferred to KDS #1384; no maintainer agreement)
RESOLVED — InteractionSection.spec.js — isHidden re-implements toBeVisible()
Next step: Needs a full code review by a human engineer, then manual QA once that reviewer sets Needs QA.
- User-visible files changed:
.stylelintrc.js,contentcuration/contentcuration/frontend/shared/styles/vuetify.scss,contentcuration/contentcuration/frontend/shared/views/QTIEditor/components/ChangeQuestionTypeModal/index.vue,contentcuration/contentcuration/frontend/shared/views/QTIEditor/components/InteractionSection/index.vue,contentcuration/contentcuration/frontend/shared/views/QTIEditor/components/QTIItemEditor/index.vue…. - Decide whether to merge with the More menu off-screen in narrow windows until KDS #1384 ships, or to require a local fix first.
- Confirm the conversion rules (what each type change drops, keeps or folds into the prompt) are the product behaviour #6190 intended, since stored items change.
- After #6303 and #6204 land, re-run the type-change flows, including gap match once it is selectable.
QA captures (50)
Convert a filled multi select question to text entry, numeric input and single choice — modal lists only the sentences that apply (I, E, R per #6190), cancel leaves answers and the selector on the old type, picking the same type again reopens the modal, confirm converts correctly and the result survives save and reopen
| Step | Screenshot |
|---|---|
| Modal multi to numeric | ![]() |
| Modal multi to single | ![]() |
| Modal multi to text entry | ![]() |
| Multi filled | ![]() |
| Numeric after reload | ![]() |
| Single after reload | ![]() |
| Text entry after convert | ![]() |
1-multi-to-text-entry-cancel-reopen-confirm.webm
Convert answer-list questions with no loss — numeric and text entry to single/multi (every answer marked correct), single to multi, free response to every type, and back — switches with no modal; non-numeric answers kept going to numeric input are flagged by validation
| Step | Screenshot |
|---|---|
| After reload show answers | ![]() |
| Free response filled | ![]() |
| Single filled | ![]() |
| Single to multi | ![]() |
| Text entry filled | ![]() |
| Text entry to multi | ![]() |
| Text entry to numeric flagged | ![]() |
2-free-response-round-trip.webm
Convert single/multi choice to ordering and ordering back to single/multi — modal shows C and O, or N, only with more than one answer; the converted order and correct marks are what the table says
| Step | Screenshot |
|---|---|
| Multi converted to ordering | ![]() |
| Multi to ordering modal | ![]() |
| Ordering to single modal | ![]() |
3-choice-ordering-round-trip.webm
Convert choice answers with formatting, an image, a formula, and an answer that is only an image to numeric input and text entry — modal adds the F sentence, formatting is stripped and image-only answers are removed, the question's own images and formulas carry over intact
| Step | Screenshot |
|---|---|
| All media answers removed | ![]() |
| Formatting only modal | ![]() |
| Numeric after | ![]() |
| Numeric answers | ![]() |
| Numeric modal | ![]() |
| Seeded choice questions | ![]() |
| Text entry after | ![]() |
| Text entry before | ![]() |
| Text entry modal | ![]() |
4-convert-to-numeric.webm
4-convert-to-text-entry.webm
Convert numeric input answers like 3.5 and 1000 to text entry and single choice in an exercise whose language is German, then French, then English — answers are written as that language shows them, and editing them back to numeric still validates
5-de-round-trips.webm
Convert associate to match and back, with pairs sharing a source and with distractors — no modal, shared sources become one match row with several matches; then convert associate or match to a choice type and confirm the A modal removes all answers
| Step | Screenshot |
|---|---|
| Associate before | ![]() |
| Associate to multi modal | ![]() |
| Associate to multi result | ![]() |
| Match after associate | ![]() |
| Match before choice | ![]() |
| Match to single modal | ![]() |
6-associate-match-round-trip.webm
Written by rtibblesbot, an LLM-based coding agent.
90c2e43 to
6d2cdc2
Compare
rtibblesbot
left a comment
There was a problem hiding this comment.
The branch was rebased since my last review; HEAD is now 6d2cdc2, with no content changes.
Written by rtibblesbot, an LLM-based coding agent.
6d2cdc2 to
f8c0eef
Compare
There was a problem hiding this comment.
PR #6311: switching answers into Numeric in a German exercise changes their values (blocking, inline). Please fix it before merge.
1 of 17 prior findings is still open. It doesn't block this PR.
CI is pending. Manual QA covered every type pair in #6190's table, keyboard-only flows and persistence. Only the German numeric import failed.
Prior-finding status
RESOLVED — interactions/index.js:7 — inline choice editor outside #6190's scope (#6303's code)
RESOLVED — inlineChoice/useInlineChoicePassage.js:180 — no announcement when a dropdown comes back (#6303's code)
RESOLVED — QTIItemEditor/index.vue:305 — focusing the question on card open
RESOLVED — EditorToolbar.vue:140 — toolbar changes outside #6190 (#6303's code)
RESOLVED — EditorToolbar.vue:404 — Insert/collapse scroll off at 360px (#6303's code)
ACKNOWLEDGED — gapMatch/Descriptor.js:52 — gap match conversions unreachable until #6204
RESOLVED — qtiEditorStrings.js — hard-coded 2 (#6303's code)
RESOLVED — TipTapEditor.spec.js — mocked requestAnimationFrame leaks
RESOLVED — editors.spec.js — TEXTAREA assertion passes on the wrong field
RESOLVED — InlineChoiceOptions/index.vue:306 — focus lost outside an option <li> (#6303's code)
RESOLVED — convertInteraction.spec.js — case-sensitivity test name
RESOLVED — InteractionSection.spec.js — depends on .select-value-text
RESOLVED — choice/Editor.vue — focus comment copied into six editors
RESOLVED — vuetify.scss — removed rule changes text selection (#6303's code)
ACKNOWLEDGED — conversion/answers.js:28 — design-rationale comments (author keeps them)
RESOLVED — InteractionSection.spec.js — isHidden re-implements toBeVisible()
CONTESTED — EditorToolbar.vue:106 — More menu opens off-screen in narrow windows (deferred to KDS#1384 with no maintainer agreement; now on unstable via #6303, so it doesn't block this PR)
Next step: Needs a full code review by a human engineer, then manual QA once that reviewer sets Needs QA.
- User-visible files changed:
contentcuration/contentcuration/frontend/shared/views/QTIEditor/components/ChangeQuestionTypeModal/index.vue,contentcuration/contentcuration/frontend/shared/views/QTIEditor/components/InteractionSection/index.vue,contentcuration/contentcuration/frontend/shared/views/QTIEditor/components/QTIItemEditor/index.vue,contentcuration/contentcuration/frontend/shared/views/QTIEditor/components/QuestionTypeSelector/index.vue,contentcuration/contentcuration/frontend/shared/views/QTIEditor/composables/useChoiceInteraction.js…. - Confirm the convention that converted numeric answers travel in the exercise language's display form, and that import reads them back in that language.
- Re-run the German text entry/choice → Numeric switches (
1.000,2.500) and check the storedmap-keyvalues and the console.
QA captures (50)
Open a question card, fill a single choice question with several answers and one correct, switch to multi select then back — no modal on single→multi, answers, correct marks and shuffle/fixed settings carry over, focus stays on the type selector after each switch, and opening the card still focuses the question editor
| Step | Screenshot |
|---|---|
| After switch to multi | ![]() |
| Back to single reopened | ![]() |
| Single choice filled | ![]() |
1-single-multi-round-trip.webm
Fill a multi select question with several correct and wrong answers, switch to single choice, numeric input and text entry in turn, confirming each — modal lists only the sentences #6190's table gives (R; I and E), wrong answers are removed for numeric/text entry, only the first correct stays marked for single, and the saved question reloads the same after closing and reopening the card
| Step | Screenshot |
|---|---|
| Modal multi to numeric | ![]() |
| Modal multi to single | ![]() |
| Modal multi to text entry | ![]() |
| Multi select filled | ![]() |
| Numeric after conversion | ![]() |
| Single after conversion | ![]() |
| Text entry after conversion | ![]() |
2-multi-to-single.webm
Fill a choice question with rich answers (bold, image-only, formula-only, plain text) and switch to text entry — modal adds the formatting sentence, formatting is stripped, image-only and formula-only answers are dropped, plain answers kept
| Step | Screenshot |
|---|---|
| Formula only to text entry | ![]() |
| Modal multi to text entry | ![]() |
| Multi choice rich answers | ![]() |
| Text entry after conversion | ![]() |
| Text entry answers | ![]() |
3-multi-to-text-entry.webm
Fill a numeric input question (including a non-number answer) and a text entry question, switch each to single choice, multi select and to each other — modal only where the table says (R for single with several answers, none for multi or numeric↔text entry), all answers become marked correct in choice types, the non-number answer is kept and validation flags it in numeric
Set an exercise's language to German (or French) in its details, write numeric answers like 3,5 and 1.000, switch numeric input to text entry and to single choice and back to numeric — answers read as the language shows them (3,5), not the stored 3.5, and round-trip without changing value
| Step | Screenshot |
|---|---|
| Numeric after round trip | ![]() |
| Numeric german answers | ![]() |
| Single choice from numeric | ![]() |
| Text entry from numeric | ![]() |
Switch between answer-list types and ordering both ways — choice→ordering lists C and O with more than one answer, ordering→choice lists N, numeric/text entry→ordering lists only O, current order becomes the correct order, item text carries over
6-ordering-choice-round-trip.webm
Fill an associate question with pairs that share a source plus distractors, switch to match and back — no modal, shared-source pairs group into one match row with several matches, distractors survive; then switch associate or match to single choice and confirm the "All answers will be removed" modal and that only the question text stays
| Step | Screenshot |
|---|---|
| Associate after match | ![]() |
| Associate filled | ![]() |
| Associate from match | ![]() |
| Associate to single modal | ![]() |
| Match after associate | ![]() |
| Match to single modal | ![]() |
| Single after associate | ![]() |
Fill an inline choice question with a passage holding two or more dropdowns, switch to single choice, ordering and associate — modal shows the passage sentence with "_____", the passage is appended to the question with each dropdown shown as _____, answers removed; confirm gap match is not offered in the type selector
| Step | Screenshot |
|---|---|
| Converted to associate | ![]() |
| Converted to ordering | ![]() |
| Converted to single | ![]() |
Written by rtibblesbot, an LLM-based coding agent.
f8c0eef to
3cf4919
Compare
There was a problem hiding this comment.
PR #6311: all 18 prior findings are resolved or acknowledged. Manual QA found 3 new suggestions (inline).
Withdrawn: focusing the question on card open is in scope, not out of scope. Editors no longer focus themselves. Without it, a remount for a new type leaves focus on the type select.
CI passing. Manual QA covered every conversion family, the modal by keyboard, and de/fr round trips. No new a11y violations.
Prior-finding status
RESOLVED — interactions/index.js:7 — inline choice editor out of scope (no longer in this PR's diff)
RESOLVED — inlineChoice/useInlineChoicePassage.js:180 — screen readers silent when a dropdown returns (no longer in this PR's diff)
RESOLVED — components/QTIItemEditor/index.vue:305 — focus on card open out of scope (withdrawn)
RESOLVED — EditorToolbar.vue:140 — toolbar changes out of scope (no longer in this PR's diff)
RESOLVED — EditorToolbar.vue:404 — Insert/collapse scroll off at 360px (no longer in this PR's diff)
RESOLVED — EditorToolbar.vue:106 — More menu opens off-screen (no longer in this PR's diff; KDS#1384)
ACKNOWLEDGED — gapMatch/Descriptor.js:52 — gap match conversions unreachable until #6204
RESOLVED — qtiEditorStrings.js — hard-coded 2 (no longer in this PR's diff)
RESOLVED — TipTapEditor.spec.js — leaked requestAnimationFrame mock
RESOLVED — interactions/tests/editors.spec.js — TEXTAREA focus assertion
RESOLVED — InlineChoiceOptions/index.vue:306 — focus lost outside option li (no longer in this PR's diff)
RESOLVED — convertInteraction.spec.js — case-sensitivity test name
RESOLVED — InteractionSection.spec.js — KSelect .select-value-text dependency
RESOLVED — choice/Editor.vue — comment copied into six editors
RESOLVED — shared/styles/vuetify.scss — selection rule removal (no longer in this PR's diff)
ACKNOWLEDGED — conversion/answers.js:28 — design-rationale comments (author keeps them)
RESOLVED — InteractionSection.spec.js — isHidden re-implements toBeVisible()
RESOLVED — textEntry/convert.js — German numeric answers change value on switch
Next step: Needs a full code review by a human engineer, then manual QA once that reviewer sets Needs QA.
- User-visible files changed:
contentcuration/contentcuration/frontend/shared/views/QTIEditor/components/ChangeQuestionTypeModal/index.vue,contentcuration/contentcuration/frontend/shared/views/QTIEditor/components/InteractionSection/index.vue,contentcuration/contentcuration/frontend/shared/views/QTIEditor/components/QTIItemEditor/index.vue,contentcuration/contentcuration/frontend/shared/views/QTIEditor/components/QuestionTypeSelector/index.vue,contentcuration/contentcuration/frontend/shared/views/QTIEditor/composables/useChoiceInteraction.js…. - Decide whether converting answers should dedupe equal numbers and reject ambiguous German group separators, or list them as losses.
- Confirm the per-type loss rules and the modal wording match what product wants for #6190.
- Re-check in the app that opening a card near the list end keeps its focused question on screen after the scroll fix.
QA captures (50)
Switch a filled question between answer-list types that lose nothing (single ↔ multi with one correct, text entry → numeric with plain numbers, empty question to any type) — no modal appears, answers and correct marks carry over, focus stays on the type selector, and the converted question is still there after closing and reopening the exercise editor
| Step | Screenshot |
|---|---|
| Converted to multi | ![]() |
| Empty question | ![]() |
| Numeric after reopen | ![]() |
| Single choice filled | ![]() |
| Text entry filled | ![]() |
1-single-multi-single.webm
Fill a multi-select question with several correct and some incorrect answers, some holding bold text, an image or a formula, then switch it to text entry — the modal names the new type and lists incorrect answers removed, any one correct answer accepted and formatting removed; Cancel leaves the question and selector unchanged, picking text entry again reopens the modal, Change keeps only the correct answers as plain text and drops image-only or formula-only ones
| Step | Screenshot |
|---|---|
| Modal text entry losses | ![]() |
| Multi select filled | ![]() |
| Text entry after change | ![]() |
2-cancel-reopen-change.webm
Switch a text entry or numeric question with several answers to single select and to multi select, and a multi-select question with several correct answers to single select — answers become choices marked correct, the modal says only the first correct answer stays correct where it applies, and the editor shows the result after Change
3-multi-to-single-keyboard.webm
Switch a choice question to ordering and an ordering question back to single and multi select — the modal says the current order becomes the correct order and answers are no longer marked correct, then that the correct order won't be kept; after Change the items keep their order and validation flags the choice question that now has no correct answer
4-choice-to-ordering-and-back.webm
Switch a filled question of each answer-list type to free response and back, and a choice question to associate or match — the modal says all answers will be removed, Change leaves only the question, and switching from an empty question to these types shows no modal
5-single-to-match.webm
Fill an associate question with pairs and distractors, switch it to match and back — pairs and distractors carry over without a modal, match grouping is correct in the editor; then switch the match question to single select and confirm that answers are removed after the modal warns
| Step | Screenshot |
|---|---|
| Associate filled | ![]() |
| Back to associate | ![]() |
| Match grouped | ![]() |
| Match to single modal | ![]() |
| Single after change | ![]() |
6-associate-to-match-and-back.webm
6-match-to-single-modal.webm
Fill an inline choice question with a passage holding several dropdowns, switch it to single select, text entry and ordering — the modal says the passage is added to the question with each blank shown as underscores and its answers removed; after Change the question text holds the passage with underscore blanks and the original question text before it
| Step | Screenshot |
|---|---|
| Inline choice before | ![]() |
| Modal ordering | ![]() |
| Modal single | ![]() |
| Modal text entry | ![]() |
7-inline-to-single.webm
Written by rtibblesbot, an LLM-based coding agent.
Superseded by my review of 3cf4919: no blocking findings remain.
3cf4919 to
19dad3d
Compare
There was a problem hiding this comment.
PR #6311: 21 prior findings resolved or acknowledged; none still open.
- CI pending.
- Manual QA covered nine conversion scenarios, including keyboard, focus, persistence and de/fr locales. No regressions from this PR.
- One pre-existing note on
textEntry/convert.js:64.
Prior-finding status
ACKNOWLEDGED — interactions/index.js:7 — inline choice editor outside #6190's scope (#6303's)
ACKNOWLEDGED — inlineChoice/useInlineChoicePassage.js:180 — no announcement when a dropdown comes back (#6303's)
RESOLVED — QTIItemEditor/index.vue:305 — focusing the question on card open out of scope
ACKNOWLEDGED — EditorToolbar.vue:140 — toolbar changes outside #6190 (#6303's)
ACKNOWLEDGED — EditorToolbar.vue:404 — Insert/collapse scroll off at 360px (#6303's)
ACKNOWLEDGED — gapMatch/Descriptor.js:52 — gap match conversions unreachable until #6204
ACKNOWLEDGED — qtiEditorStrings.js — hard-coded 2 (#6303's)
RESOLVED — TipTapEditor.spec.js — leaked requestAnimationFrame mock
RESOLVED — interactions/tests/editors.spec.js — TEXTAREA focus assertion too weak
ACKNOWLEDGED — InlineChoiceOptions/index.vue:306 — focus lost outside an option <li> (#6303's)
RESOLVED — convertInteraction.spec.js — case-sensitivity test name
RESOLVED — InteractionSection.spec.js — depends on .select-value-text
RESOLVED — choice/Editor.vue — focus comment copied into six editors
ACKNOWLEDGED — styles/vuetify.scss — removed text-selection rule (#6303's)
ACKNOWLEDGED — conversion/answers.js:28 — design-explaining comments
RESOLVED — InteractionSection.spec.js — isHidden re-implements toBeVisible()
RESOLVED — textEntry/convert.js — German exercise changes answer values on switch into Numeric
RESOLVED — textEntry/convert.js — text entry → Numeric duplicate answers
RESOLVED — QTIItemEditor/index.vue — card scrolls before its question opens
ACKNOWLEDGED — textEntry/convert.js:64 — German 3.5 becomes Numeric 35 (author: matches typing; fix belongs in readLocaleNumber)
ACKNOWLEDGED — EditorToolbar.vue:106 — More menu opens off-screen in narrow windows (both threads; not in this branch's diff, deferred to KDS#1384)
Next step: Needs a full code review by a human engineer, then manual QA once that reviewer sets Needs QA.
- User-visible files changed:
contentcuration/contentcuration/frontend/shared/views/QTIEditor/components/ChangeQuestionTypeModal/index.vue,contentcuration/contentcuration/frontend/shared/views/QTIEditor/components/InteractionSection/index.vue,contentcuration/contentcuration/frontend/shared/views/QTIEditor/components/QTIItemEditor/index.vue,contentcuration/contentcuration/frontend/shared/views/QTIEditor/components/QuestionTypeSelector/index.vue,contentcuration/contentcuration/frontend/shared/views/QTIEditor/composables/useChoiceInteraction.js…. - Confirm the per-pair conversion rules (what each switch drops or keeps) are the product behaviour wanted, since authors will rely on them.
- Decide whether German
3.5→35on switch into Numeric needs areadLocaleNumberfix before release. - Check the change dialog in RTL and at narrow widths, which QA did not cover.
QA captures (50)
Change a filled multi-select question (two correct, two incorrect answers, one with bold text and one image-only) to text entry, confirm, then save and reopen the exercise — modal lists incorrect-removed, any-correct-accepted and formatting-removed in that order with the type label in the intro, carried answers are plain text with the image-only one gone, the change persists after reopen
| Step | Screenshot |
|---|---|
| After convert text entry | ![]() |
| Change type modal | ![]() |
| Filled multi select | ![]() |
| Reopened text entry | ![]() |
1-convert-to-text-entry.webm
Change between single, multi, numeric, text entry and free response on filled questions — no-loss switches (single to multi, text entry to multi) apply with no modal, multi with several correct to single keeps only the first correct, any type to free response warns all answers removed, empty new questions switch silently
2-multi-to-single-confirm.webm
Pick a lossy type, cancel the modal, pick the same type again and confirm, using only the keyboard — selector reverts to the original type on cancel, question untouched, the modal reappears on the second pick, focus returns to the type selector after cancel, after confirm and after a no-modal switch
| Step | Screenshot |
|---|---|
| After cancel reverted | ![]() |
| After confirm single | ![]() |
| Dropdown single highlighted | ![]() |
| Modal cancel focused | ![]() |
| Modal open keyboard | ![]() |
| Q8 open type selector focused | ![]() |
3-keyboard-cancel-then-confirm.webm
Change a choice question to ordering and an ordering question back to single and multi select — modal shows order-becomes-answer and correct-answers-not-kept, then order-not-kept, item order and content carry over, no correct mark survives, rich content in items is kept
| Step | Screenshot |
|---|---|
| After multi | ![]() |
| After ordering | ![]() |
| After single | ![]() |
| Before multi select | ![]() |
| Before ordering | ![]() |
| Multi to ordering modal | ![]() |
| No correct to ordering modal | ![]() |
| Ordering to multi modal | ![]() |
| Ordering to single modal | ![]() |
4-multi-to-ordering.webm
4-ordering-to-multi.webm
4-ordering-to-single.webm
Set the exercise language to German (then French), fill a numeric question with decimals like 3,5, change it to text entry, single select and back to numeric — values appear as the language writes them in the new type, round trip returns the same stored number, nothing reads 3,5 as 35, saved XML holds the stored value
| Step | Screenshot |
|---|---|
| De back to numeric | ![]() |
| De single choice | ![]() |
| De text entry to numeric dedup | ![]() |
| De text entry | ![]() |
| Fr text to numeric | ![]() |
| New numeric | ![]() |
5-fr-round-trip.webm
Change an associate question with pairs and a distractor to match and back, then match to single select — pairs and distractors carry with no modal, match grouping is correct, switching to a non-pair type warns all answers removed and keeps the prompt
| Step | Screenshot |
|---|---|
| Associate after match | ![]() |
| Associate before | ![]() |
| Match after associate | ![]() |
| Match to single modal | ![]() |
| Single after match | ![]() |
Change a filled inline choice question to single select, text entry and free response — modal says the passage joins the question with each blank shown as the underscore text and its answers go, the prompt then holds the passage with blanks in place, inline choice options appear nowhere
| Step | Screenshot |
|---|---|
| After free response | ![]() |
| After single | ![]() |
Written by rtibblesbot, an LLM-based coding agent.
| // stay as written, for validation to point out. | ||
| const stored = content => | ||
| questionType === QuestionType.NUMERIC | ||
| ? (readLocaleNumber(content, language) ?? content) |
There was a problem hiding this comment.
pre-existing: In a German exercise, a text entry or choice answer 3.5 converts to Numeric 35 without warning. The saved item has map-key="35". The cause is readLocaleNumber, which treats . as a grouping separator in any position. You said the fix belongs there, since it also fixes typing. Please file a follow-up issue for it. Suggested rule: accept a grouping separator only when every group after it has 3 digits.
There was a problem hiding this comment.
Suggested rule: accept a grouping separator only when every group after it has 3 digits.
That's incorrect, surely? Indian number grouping uses less than 3 digits in a group. I think we'd be better off continuing to reuse the external library that we are relying on for this.
| ...state, | ||
| prompt, | ||
| choices: items.length | ||
| ? items.map(({ content, correct, fixed }) => |
There was a problem hiding this comment.
Text entry → choice drops caseSensitive without a warning, so switching back clears it. We should carry it through or report it as a loss.
There was a problem hiding this comment.
This now covers every setting an author can change, not only case sensitivity: shuffle (single, multiple and inline choice), show answer count (multiple choice), and case sensitivity (text entry). When a setting the author changed has no place in the new type, the modal says "Settings specific to this question type will be lost."
- Each type's conversion format maps the settings it keeps to the value they start at, taken from its parse defaults. Each type exports only the settings changed from those values.
- A setting carries to any type that keeps it, even when the answers don't, e.g. inline choice shuffle → choice. Ordering and gap match keep a shuffle, as learners always see them shuffled.
- Case sensitivity only counts for answers whose letters have case:
H2O, not1,5. - Choice → inline choice can't keep shuffle: inline choice saves it on its dropdowns, and the converted question has none, so it's reported.
In 6dd9d7e.
Written with Claude 🤖, under my guidelines and supervision.
| [CorrectCriterion.ACCEPTED]: (items, filled, losses) => { | ||
| if (filled.some(item => !item.correct)) losses.push(ConversionLoss.INCORRECT_ANSWERS_REMOVED); | ||
| // Every correct choice had to be selected; any one accepted answer is enough. | ||
| if (filled.filter(item => item.correct).length > 1) { |
There was a problem hiding this comment.
Counts correct answers before image-only ones are removed, so E shows when only one survives. We should count after changeContentLevels.
Changing a question's type carries what the question holds into the new type, and names what that would remove or change so the author can confirm first. This adds the part that knows no interaction: each descriptor will export its state into neutral facets (answers, pairs, a passage) and import from them, and the orchestrator moves the facets a type takes, translates what makes answers correct and content levels, and works out the losses from one table. A facet the new type doesn't take is reported as a loss, so a type that takes nothing, as the base descriptor's defaults do, loses the rest of the question only with a warning. A passage a type doesn't take is folded into the question instead, since it is often the whole question. Nothing calls this yet. The unused convertsFrom placeholder on the descriptors goes, replaced by the conversion methods. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Each interaction now exports its state into the conversion facets and imports from them, in its own convert.js, so a question keeps what the new type can hold. The tests convert a filled question of each type to every other type, against one table of the losses each change brings. A convert module makes a new choice, item, answer, row or distractor through its interaction's parse module, which now has the one factory for it; the parse modules and the composables that add one use them too, so each is built the same way wherever it is made. Gap match and inline choice parse modules also write the gaps and dropdowns they read, so how a passage holds them stays their own. Answer lists (single and multi select, numeric, text entry, free response, ordering) differ in what makes an answer correct: - only the correct choices become accepted numeric or text entry answers, since a wrong one would become an accepted one, and when several were correct the author is told any one is now enough - a question becoming single choice keeps only its first correct answer marked, since radio buttons can't show more than one - answers ordering can't mark correct, and an order that becomes the correct answer, are reported so the author can confirm first Answers that aren't numbers stay when switching to numeric, for validation to flag. Shuffle travels between single and multi select, fixed answers and orientation between choice and ordering, and the expected length between the text entry types. Ordering's shuffle isn't the author's to set, and stays on. Pairs (associate and match) carry their pairs and distractors. A match row with several matches becomes one associate pair per match, and associate pairs from the same source become one match row, so a round trip gives back the row it started from. Passages (inline choice and gap match) travel as neutral blanks, so neither needs the other's markup. To a type with no passage, the passage is folded into the question, each blank shown as _____, since it is often the whole question. Between the two, answers move with their blanks: each dropdown's wrong options join gap match's shared pool without a warning, since the pool is the equivalent, and each dropdown made from gap match offers the whole pool, shuffled. Options the conversion repeats are dropped rather than left for validation to flag, and formatting a gap's answer can hold but an option can't is reported. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Each interaction editor opened and focused its question as soon as it mounted in edit mode. That suited a card being opened, but any other mount took focus too: an editor remounted for a new question type pulled focus away from the type selector the author was using. Focus is now asked for, not implied. An editor exposes focus(), which opens its question for editing and so focuses it, and the card calls it, through InteractionSection, when it opens or is created open: the moments it already scrolls itself into view. An editor that only mounts leaves its question closed and focus where it was. The card asks after its section has re-rendered, so an editor that opens its question only in edit mode, as associate and match do, has heard of the mode change by then. TipTapEditor, switched into edit mode before it is ready, now focuses once it is. Its content only joins the page then, and a focus before that was lost: a new question's editor opened its question at once, but the question took no focus. Editors used to be created with autofocus already set, which TipTap applies once ready, so nothing hit this before. A test over the editor registry checks that every editor focuses its question when asked, so a new one can't miss it. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Changing a question's type now converts what the question holds, instead of keeping only the question and dropping the answers without a word. A change that would remove or change part of the question first asks the author to confirm, in a modal; one that loses nothing switches straight away. The modal names the new type and gives one full sentence for each thing that would go or change. Each sentence is a string of its own, so translators never get fragments to join. While the modal is open, the type selector shows the type the author picked, so cancelling puts it back and lets them pick that type again. KSelect keeps its own selection and ignores a pick of the option it already holds. Confirming saves the question as it was converted when the type was picked: the modal blocks editing, so it can't have changed. Every type change now remounts the interaction editor from the converted question, including single and multi select and the text entry types, which used to keep the editor's own state. A free response therefore really clears its answers, rather than keeping them in memory until the next reload. The text entry editor no longer watches its question type: it never sees it change now, as a change remounts it. Its watch moved Numeric answers between how the exercise language writes them and how they are stored, and the remount does the same: leaving Numeric starts the next editor from the values stored, and opening on Numeric shows them in the language, reading text it can. The tests for it switch the type through the section now, where conversion and remount happen. Focus stays on the type selector through a type change. The remounted editor takes none, and before the modal opens the select is focused, so the modal returns focus to it rather than to the dropdown option that was picked, gone by then. Closes learningequality#6190 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A question keeps its content across a type change only if its descriptor exports it, and anything left out is lost without a warning. Every registered interaction now has to have a filled question in the conversion tests, and has to export every word that question holds, from its body or as accepted answers, so a new interaction, or a new field on an existing one, can't be missed. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A numeric answer is stored as xsd:double but shown in the exercise language: 11.2 shows as 11,2 in French. Converting the question to another type carried the stored form, so a French author who switched to text entry found 11.2, an answer French learners wouldn't type, in place of the 11,2 they had seen. A round trip from text entry through numeric changed the accepted answer the same way. Numeric answers now leave as they were shown, in the exercise language, which the conversion is given for that. Answers switching into numeric are read in that language too, as typing them into the numeric field would read them: in German, 1.000 is a thousand and 1,5 is 1.5. Reading them as xsd:double first, as learningequality#6282 did, would turn German 1.000 into 1, and store 1,5 as a float it isn't. Answers that store the same number, such as text entry 2 and 2.0, are kept once, as numeric scores them as one. This reverses what learningequality#6282 chose for leaving numeric, which kept the stored form so learners could type it. The language's own conventions, regional digits included, are what its learners most likely type. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
19dad3d to
6dd9d7e
Compare

























































































































































































































































































































Summary
Changing a question's type used to keep only the question and drop the answers silently. Now answers carry over wherever the new type can hold them. When a change would remove something, or change what counts as correct, a modal lists each change in one sentence and asks for confirmation. A change that loses nothing switches straight away.
focus()method, so focus stays on the type selector through a type change.Gap match can't be picked yet. Its conversions are in place, but gap match has no editor or type option until #6204, so authors can't reach them and only the tests cover them. Making it selectable before its editor exists would leave the question with nothing to edit.
The conversion rules and the decisions behind them are in #6190.
References
Closes #6190. Builds on the inline choice editor (#6303) and the gap match descriptor (#6323).
Reviewer guidance
TipTapEditorfix). Editors used to focus their question whenever they mounted in edit mode, so the remount after a type change took focus away from the type selector, which [QTI] Convert answers or warn when changing question type in the QTI editor #6190's keyboard criterion needs it to keep.InteractionSection; this replaces Read and show Numeric answers in the exercise language #6282'squestionTypewatch inuseTextEntryInteraction3,5in German)AI usage
Claude Code researched the options, wrote the plan with me, implemented it with tests, self-reviewed it, and addressed my review. I made the conversion decisions and reviewed the changes.
🤖 Generated with Claude Code