Radio/Checkbox/Select: answerOption with valueInteger: 0 or valueString: '' never renders
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 61
- Forks
- 29
- Avg merge
- 2d 22h
- Merged PRs (30d)
- 19
Description
Radio/Checkbox/Select: answerOption with valueInteger: 0 or valueString: '' never renders
Describe the bug
An answerOption whose value is a falsy primitive — valueInteger: 0 or valueString: '' — is silently dropped from the rendered Radio and Checkbox option lists entirely (not just shown as unchecked; the input never exists in the DOM), and its label resolves to an empty string in Select contexts.
Root cause is a truthy check used as an existence check in three places:
packages/smart-forms-renderer/src/components/FormComponents/ItemParts/RadioOptionList.tsx—if (option['valueString'])(line 55),if (option['valueInteger'])(line 71)packages/smart-forms-renderer/src/components/FormComponents/ChoiceItems/CheckboxOptionList.tsx— same pattern, three branches:valueCoding/valueString(line 69) /valueInteger(line 87)packages/smart-forms-renderer/src/utils/openChoice.ts,getAnswerOptionLabel(~line 164-169) — same pattern, used for Select option labels
0 and '' are falsy in JavaScript, so if (option.valueInteger) is false when option.valueInteger === 0, and the option is skipped as if it didn't have a valueInteger at all.
To Reproduce
Load this Questionnaire (which renderer/control an item uses is decided purely by the questionnaire-itemControl extension code — radio-button / check-box / anything else or absent, which defaults to Select — repeats plays no part):
{
"resourceType": "Questionnaire",
"id": "FalsyAnswerOptionRender",
"url": "https://smartforms.csiro.au/docs/tests/FalsyAnswerOptionRender",
"name": "FalsyAnswerOptionRender",
"title": "Falsy answerOption render test",
"status": "draft",
"item": [
{
"linkId": "severity-radio",
"text": "Severity (Radio) - expect 0,1,2,3,4,5, six options",
"type": "choice",
"repeats": false,
"extension": [
{
"url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl",
"valueCodeableConcept": {
"coding": [
{ "system": "http://hl7.org/fhir/questionnaire-item-control", "code": "radio-button" }
]
}
}
],
"answerOption": [
{ "valueInteger": 0 },
{ "valueInteger": 1 },
{ "valueInteger": 2 },
{ "valueInteger": 3 },
{ "valueInteger": 4 },
{ "valueInteger": 5 }
]
},
{
"linkId": "severity-checkbox",
"text": "Severity (Checkbox) - expect 0,1,2,3,4,5, six options",
"type": "choice",
"repeats": true,
"extension": [
{
"url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl",
"valueCodeableConcept": {
"coding": [
{ "system": "http://hl7.org/fhir/questionnaire-item-control", "code": "check-box" }
]
}
}
],
"answerOption": [
{ "valueInteger": 0 },
{ "valueInteger": 1 },
{ "valueInteger": 2 },
{ "valueInteger": 3 },
{ "valueInteger": 4 },
{ "valueInteger": 5 }
]
},
{
"linkId": "severity-select",
"text": "Severity (Select) - expect 0,1,2,3,4,5, six options",
"type": "choice",
"repeats": false,
"answerOption": [
{ "valueInteger": 0 },
{ "valueInteger": 1 },
{ "valueInteger": 2 },
{ "valueInteger": 3 },
{ "valueInteger": 4 },
{ "valueInteger": 5 }
]
},
{
"linkId": "blank-option-radio",
"text": "Blank option (Radio) - expect 3 options: '' (blank/unlabeled), 'A', 'B'",
"type": "choice",
"repeats": false,
"extension": [
{
"url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl",
"valueCodeableConcept": {
"coding": [
{ "system": "http://hl7.org/fhir/questionnaire-item-control", "code": "radio-button" }
]
}
}
],
"answerOption": [
{ "valueString": "" },
{ "valueString": "A" },
{ "valueString": "B" }
]
}
]
}
Expected behavior
severity-radio,severity-checkbox,severity-select: six options each,0through5, all selectable.blank-option-radio: three options, an unlabeled/blank one plusAandB.
Actual
severity-radio/severity-checkbox/severity-select: only five options render (1-5). ThevalueInteger: 0option is missing entirely from the DOM — not disabled, not unchecked, absent.blank-option-radio: only two options render (A,B). ThevalueString: ''option is missing entirely.
Additional context
- Found while adding tests for the falsy-value fix to
getAnswerValueString/compareAnswerOptionValue/findInAnswerOptions(the answer-matching helpers, fixed as part of #1931). Those helpers now correctly treat0and''as real answers, but this is the identical bug one layer further out, at the rendering gate rather than the matching logic. - Pre-existing, not introduced by #1931 — deliberately scoped out of that PR to keep it focused on the terminology-lookup-failure fix.
- The matching logic in
packages/smart-forms-renderer/src/utils/choice.tsalready uses the correct!== undefinedcheck; these three spots are the ones that were missed.
Suggested fix
Change the truthy checks to !== undefined checks in all three locations, mirroring utils/choice.ts:
// before
if (option['valueInteger']) { ... }
if (option['valueString']) { ... }
// after
if (option['valueInteger'] !== undefined) { ... }
if (option['valueString'] !== undefined) { ... }
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reading the truthy checks in packages/smart-forms-renderer/src/components/FormComponents/ItemParts/RadioOptionList.tsx, packages/smart-forms-renderer/src/components/FormComponents/ChoiceItems/CheckboxOptionList.tsx, and packages/smart-forms-renderer/src/utils/openChoice.ts. Reproduce the supplied Questionnaire and use the existing renderer tests or test setup to verify that integer 0 and string '' options render, while options 1–5, A, and B remain selectable.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- react, typescript
- Domain
- frontend
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 78/100