Description
A <choice> whose content is an <image> has no text representation, so anything that reads a choice as text gets a blank instead of the image's description.
Noticed while fixing #1613 (inline <choiceInput> announcing [object Object]). That fix gives each option an accessible name from choiceTexts, which is built from <choice>'s text state variable via textFromChildren. textFromComponent returns " " for any component that has no string text state variable:
https://github.com/Doenet/DoenetML/blob/main/packages/doenetml-worker-javascript/src/utils/text.ts#L1-L11
<image> is such a component. So an image-only choice went from announcing "[object Object]" to announcing nothing at all — better, but still not usable.
Reproduce
<choiceInput inline name="ci">
<choice><image source="doenet:cid=..."><shortDescription>A cat</shortDescription></image></choice>
<choice><image source="doenet:cid=..."><shortDescription>A dog</shortDescription></image></choice>
</choiceInput>
A screen reader announces the options as blank. $ci.choiceTexts is [" ", " "].
Expected
The choices should read as "A cat" and "A dog" — the text <image> already renders as its alt attribute.
Suggested fix
<image> already computes exactly the right string in its shortDescription state variable (packages/doenetml-worker-javascript/src/components/Image.js), which is what the renderer passes to alt, and which is empty for a decorative image. Expose it as text:
stateVariableDefinitions.text = {
description: "The image's short description, as a text string.",
public: true,
shadowingInstructions: { createComponentOfType: "text" },
returnDependencies: () => ({
shortDescription: {
dependencyType: "stateVariable",
variableName: "shortDescription",
},
}),
definition: ({ dependencyValues }) => ({
setValue: { text: dependencyValues.shortDescription },
}),
};
textFromComponent picks it up with no further changes.
Why this needs its own PR rather than riding along with the a11y fix
choiceTexts is public and does more than name options — it also feeds indicesMatchedByBoundValue and selectedValues in ChoiceInput.js. So an image choice would start matching an <answer> by its description text. That is arguably the right behavior, but it is an answer-checking change and wants its own worker tests.
Giving <image> a text state variable also affects textFromChildren everywhere else an image can appear, so it is worth a deliberate look at the other call sites rather than a drive-by.
Related
🤖 Generated with Claude Code
Description
A
<choice>whose content is an<image>has no text representation, so anything that reads a choice as text gets a blank instead of the image's description.Noticed while fixing #1613 (inline
<choiceInput>announcing[object Object]). That fix gives each option an accessible name fromchoiceTexts, which is built from<choice>'stextstate variable viatextFromChildren.textFromComponentreturns" "for any component that has no stringtextstate variable:https://github.com/Doenet/DoenetML/blob/main/packages/doenetml-worker-javascript/src/utils/text.ts#L1-L11
<image>is such a component. So an image-only choice went from announcing"[object Object]"to announcing nothing at all — better, but still not usable.Reproduce
A screen reader announces the options as blank.
$ci.choiceTextsis[" ", " "].Expected
The choices should read as "A cat" and "A dog" — the text
<image>already renders as itsaltattribute.Suggested fix
<image>already computes exactly the right string in itsshortDescriptionstate variable (packages/doenetml-worker-javascript/src/components/Image.js), which is what the renderer passes toalt, and which is empty for adecorativeimage. Expose it astext:textFromComponentpicks it up with no further changes.Why this needs its own PR rather than riding along with the a11y fix
choiceTextsis public and does more than name options — it also feedsindicesMatchedByBoundValueandselectedValuesinChoiceInput.js. So an image choice would start matching an<answer>by its description text. That is arguably the right behavior, but it is an answer-checking change and wants its own worker tests.Giving
<image>atextstate variable also affectstextFromChildreneverywhere else an image can appear, so it is worth a deliberate look at the other call sites rather than a drive-by.Related
<choiceInput>announced[object Object]; fixed in fix: give inline choiceInput options a text accessible name #1630<choiceInput>display bug found in the same review🤖 Generated with Claude Code