aehrc / aehrc/smart-forms

Radio/Checkbox/Select: answerOption with valueInteger: 0 or valueString: '' never renders

Open
#2,108 0 comments 0 reactions 0 assignees View on GitHub

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.tsxif (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, 0 through 5, all selectable.
  • blank-option-radio: three options, an unlabeled/blank one plus A and B.

Actual

  • severity-radio / severity-checkbox / severity-select: only five options render (1-5). The valueInteger: 0 option is missing entirely from the DOM — not disabled, not unchecked, absent.
  • blank-option-radio: only two options render (A, B). The valueString: '' 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 treat 0 and '' 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.ts already uses the correct !== undefined check; 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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.