diff --git a/datacapture/sampledata/questionnaire_with_enabled_display_items.json b/datacapture/sampledata/questionnaire_with_enabled_display_items.json new file mode 100644 index 0000000000..21aaf273bf --- /dev/null +++ b/datacapture/sampledata/questionnaire_with_enabled_display_items.json @@ -0,0 +1,66 @@ +{ + "resourceType": "Questionnaire", + "id": "questionnaire.enabled.display", + "name": "Questionnaire Enabled Display", + "title": "Questionnaire Enabled Display", + "status": "active", + "item": [ + { + "linkId": "1", + "text": "Questionnaire Text", + "type": "boolean", + "item": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-displayCategory", + "valueCodeableConcept": { + "coding": [ + { + "system": "http://hl7.org/fhir/questionnaire-display-category", + "code": "instructions" + } + ] + } + }, + { + "url": "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-enableWhenExpression", + "valueExpression": { + "language": "text/fhirpath", + "expression": "%resource.repeat(item).where(linkId='1' and answer.empty().not()).select(answer.value) = false" + } + } + ], + "linkId": "1.1", + "text": "Text when no is selected", + "type": "display" + }, + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-displayCategory", + "valueCodeableConcept": { + "coding": [ + { + "system": "http://hl7.org/fhir/questionnaire-display-category", + "code": "instructions" + } + ] + } + }, + { + "url": "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-enableWhenExpression", + "valueExpression": { + "language": "text/fhirpath", + "expression": "%resource.repeat(item).where(linkId='1' and answer.empty().not()).select(answer.value) = true" + } + } + ], + "linkId": "1.2", + "text": "Text when yes is selected", + "type": "display" + } + ] + } + ] +} diff --git a/datacapture/src/androidTest/java/com/google/android/fhir/datacapture/QuestionnaireUiEspressoTest.kt b/datacapture/src/androidTest/java/com/google/android/fhir/datacapture/QuestionnaireUiEspressoTest.kt index ae47c039b9..cf7e0d351d 100644 --- a/datacapture/src/androidTest/java/com/google/android/fhir/datacapture/QuestionnaireUiEspressoTest.kt +++ b/datacapture/src/androidTest/java/com/google/android/fhir/datacapture/QuestionnaireUiEspressoTest.kt @@ -16,7 +16,9 @@ package com.google.android.fhir.datacapture +import android.view.View import android.widget.FrameLayout +import android.widget.TextView import androidx.fragment.app.commitNow import androidx.test.espresso.Espresso.onView import androidx.test.espresso.action.ViewActions @@ -361,6 +363,41 @@ class QuestionnaireUiEspressoTest { assertThat(exception.message).isEqualTo("minValue cannot be greater than maxValue") } + @Test + fun displayItems_shouldGetEnabled_withAnswerChoice() { + buildFragmentFromQuestionnaire("/questionnaire_with_enabled_display_items.json") + + onView(withId(R.id.hint)).check { view, _ -> + val hintVisibility = (view as TextView).visibility + assertThat(hintVisibility).isEqualTo(View.GONE) + } + + onView(withId(R.id.yes_radio_button)).perform(ViewActions.click()) + + onView(withId(R.id.hint)).check { view, _ -> + val hintVisibility = (view as TextView).visibility + val hintText = view.text.toString() + assertThat(hintVisibility).isEqualTo(View.VISIBLE) + assertThat(hintText).isEqualTo("Text when yes is selected") + } + + onView(withId(R.id.no_radio_button)).perform(ViewActions.click()) + + onView(withId(R.id.hint)).check { view, _ -> + val hintVisibility = (view as TextView).visibility + val hintText = view.text.toString() + assertThat(hintVisibility).isEqualTo(View.VISIBLE) + assertThat(hintText).isEqualTo("Text when no is selected") + } + + onView(withId(R.id.no_radio_button)).perform(ViewActions.click()) + + onView(withId(R.id.hint)).check { view, _ -> + val hintVisibility = (view as TextView).visibility + assertThat(hintVisibility).isEqualTo(View.GONE) + } + } + private fun buildFragmentFromQuestionnaire(fileName: String, isReviewMode: Boolean = false) { val questionnaireJsonString = readFileFromAssets(fileName) val questionnaireFragment = diff --git a/datacapture/src/androidTest/java/com/google/android/fhir/datacapture/views/QuestionnaireItemDialogMultiSelectViewHolderFactoryEspressoTest.kt b/datacapture/src/androidTest/java/com/google/android/fhir/datacapture/views/QuestionnaireItemDialogMultiSelectViewHolderFactoryEspressoTest.kt index b24c0638e3..c7bdde5aa8 100644 --- a/datacapture/src/androidTest/java/com/google/android/fhir/datacapture/views/QuestionnaireItemDialogMultiSelectViewHolderFactoryEspressoTest.kt +++ b/datacapture/src/androidTest/java/com/google/android/fhir/datacapture/views/QuestionnaireItemDialogMultiSelectViewHolderFactoryEspressoTest.kt @@ -200,31 +200,32 @@ class QuestionnaireItemDialogMultiSelectViewHolderFactoryEspressoTest { @Test fun bindView_setHintText() { + val hintItem = + Questionnaire.QuestionnaireItemComponent().apply { + linkId = "1.1" + text = "Select code" + type = Questionnaire.QuestionnaireItemType.DISPLAY + addExtension( + Extension() + .setUrl(EXTENSION_ITEM_CONTROL_URL) + .setValue( + CodeableConcept() + .addCoding( + Coding() + .setCode(DisplayItemControlType.FLYOVER.extensionCode) + .setSystem(EXTENSION_ITEM_CONTROL_SYSTEM) + ) + ) + ) + } val questionnaireViewItem = QuestionnaireViewItem( answerOptions(false, "Coding 1", "Coding 2", "Coding 3", "Coding 4", "Coding 5") - .addItem( - Questionnaire.QuestionnaireItemComponent().apply { - linkId = "1.1" - text = "Select code" - type = Questionnaire.QuestionnaireItemType.DISPLAY - addExtension( - Extension() - .setUrl(EXTENSION_ITEM_CONTROL_URL) - .setValue( - CodeableConcept() - .addCoding( - Coding() - .setCode(DisplayItemControlType.FLYOVER.extensionCode) - .setSystem(EXTENSION_ITEM_CONTROL_SYSTEM) - ) - ) - ) - } - ), + .addItem(hintItem), responseOptions(), validationResult = NotValidated, answersChangedCallback = { _, _, _, _ -> }, + enabledDisplayItems = listOf(hintItem) ) runOnUI { viewHolder.bind(questionnaireViewItem) } diff --git a/datacapture/src/main/java/com/google/android/fhir/datacapture/QuestionnaireViewModel.kt b/datacapture/src/main/java/com/google/android/fhir/datacapture/QuestionnaireViewModel.kt index 5193bef742..3b926b76b0 100644 --- a/datacapture/src/main/java/com/google/android/fhir/datacapture/QuestionnaireViewModel.kt +++ b/datacapture/src/main/java/com/google/android/fhir/datacapture/QuestionnaireViewModel.kt @@ -35,11 +35,9 @@ import com.google.android.fhir.datacapture.extensions.entryMode import com.google.android.fhir.datacapture.extensions.extractAnswerOptions import com.google.android.fhir.datacapture.extensions.flattened import com.google.android.fhir.datacapture.extensions.hasDifferentAnswerSet +import com.google.android.fhir.datacapture.extensions.isDisplayItem import com.google.android.fhir.datacapture.extensions.isFhirPath -import com.google.android.fhir.datacapture.extensions.isFlyoverCode -import com.google.android.fhir.datacapture.extensions.isHelpCode import com.google.android.fhir.datacapture.extensions.isHidden -import com.google.android.fhir.datacapture.extensions.isInstructionsCode import com.google.android.fhir.datacapture.extensions.isPaginated import com.google.android.fhir.datacapture.extensions.isXFhirQuery import com.google.android.fhir.datacapture.extensions.localizedTextSpanned @@ -643,7 +641,12 @@ internal class QuestionnaireViewModel(application: Application, state: SavedStat answersChangedCallback = answersChangedCallback, resolveAnswerValueSet = { resolveAnswerValueSet(it) }, resolveAnswerExpression = { resolveAnswerExpression(it) }, - draftAnswer = draftAnswerMap[questionnaireResponseItem] + draftAnswer = draftAnswerMap[questionnaireResponseItem], + enabledDisplayItems = + questionnaireItem.item.filter { + it.isDisplayItem && + EnablementEvaluator(questionnaireResponse).evaluate(it, questionnaireResponseItem) + } ) ) ) @@ -672,11 +675,7 @@ internal class QuestionnaireViewModel(application: Application, state: SavedStat getQuestionnaireAdapterItems( // If nested display item is identified as instructions or flyover, then do not create // questionnaire state for it. - questionnaireItemList = - questionnaireItem.item.filterNot { - it.type == Questionnaire.QuestionnaireItemType.DISPLAY && - (it.isInstructionsCode || it.isFlyoverCode || it.isHelpCode) - }, + questionnaireItemList = questionnaireItem.item.filterNot { it.isDisplayItem }, questionnaireResponseItemList = nestedResponseItemList, ) ) diff --git a/datacapture/src/main/java/com/google/android/fhir/datacapture/enablement/EnablementEvaluator.kt b/datacapture/src/main/java/com/google/android/fhir/datacapture/enablement/EnablementEvaluator.kt index 6a8768bf66..1bb0ee18b0 100644 --- a/datacapture/src/main/java/com/google/android/fhir/datacapture/enablement/EnablementEvaluator.kt +++ b/datacapture/src/main/java/com/google/android/fhir/datacapture/enablement/EnablementEvaluator.kt @@ -114,6 +114,7 @@ internal class EnablementEvaluator(val questionnaireResponse: QuestionnaireRespo if (enableWhenList.size == 1) { return evaluateEnableWhen( enableWhenList.single(), + questionnaireItem, questionnaireResponseItem, ) } @@ -124,9 +125,9 @@ internal class EnablementEvaluator(val questionnaireResponse: QuestionnaireRespo // enabled if ANY `enableWhen` constraint is satisfied. return when (val value = questionnaireItem.enableBehavior) { Questionnaire.EnableWhenBehavior.ALL -> - enableWhenList.all { evaluateEnableWhen(it, questionnaireResponseItem) } + enableWhenList.all { evaluateEnableWhen(it, questionnaireItem, questionnaireResponseItem) } Questionnaire.EnableWhenBehavior.ANY -> - enableWhenList.any { evaluateEnableWhen(it, questionnaireResponseItem) } + enableWhenList.any { evaluateEnableWhen(it, questionnaireItem, questionnaireResponseItem) } else -> throw IllegalStateException("Unrecognized enable when behavior $value") } } @@ -136,10 +137,15 @@ internal class EnablementEvaluator(val questionnaireResponse: QuestionnaireRespo */ private fun evaluateEnableWhen( enableWhen: Questionnaire.QuestionnaireItemEnableWhenComponent, + questionnaireItem: Questionnaire.QuestionnaireItemComponent, questionnaireResponseItem: QuestionnaireResponse.QuestionnaireResponseItemComponent, ): Boolean { - val targetQuestionnaireResponseItem = - findEnableWhenQuestionnaireResponseItem(questionnaireResponseItem, enableWhen.question) + val targetQuestionnaireResponseItem: QuestionnaireResponse.QuestionnaireResponseItemComponent? = + if (questionnaireItem.type == Questionnaire.QuestionnaireItemType.DISPLAY && + questionnaireResponseItem.linkId == enableWhen.question + ) + questionnaireResponseItem + else findEnableWhenQuestionnaireResponseItem(questionnaireResponseItem, enableWhen.question) return if (Questionnaire.QuestionnaireItemOperator.EXISTS == enableWhen.operator) { // True iff the answer value of the enable when is equal to whether an answer exists in the // target questionnaire response item diff --git a/datacapture/src/main/java/com/google/android/fhir/datacapture/extensions/MoreQuestionnaireItemComponents.kt b/datacapture/src/main/java/com/google/android/fhir/datacapture/extensions/MoreQuestionnaireItemComponents.kt index 92c454e9a2..2f9ad14109 100644 --- a/datacapture/src/main/java/com/google/android/fhir/datacapture/extensions/MoreQuestionnaireItemComponents.kt +++ b/datacapture/src/main/java/com/google/android/fhir/datacapture/extensions/MoreQuestionnaireItemComponents.kt @@ -273,18 +273,6 @@ internal val Questionnaire.QuestionnaireItemComponent.hasHelpButton: Boolean return item.any { it.isHelpCode } } -/** Whether item type is display and [displayItemControl] is [DisplayItemControlType.HELP]. */ -internal val Questionnaire.QuestionnaireItemComponent.isHelpCode: Boolean - get() { - return when (type) { - Questionnaire.QuestionnaireItemType.DISPLAY -> { - displayItemControl == DisplayItemControlType.HELP - } - else -> { - false - } - } - } /** Converts Text with HTML Tag to formatted text. */ private fun String.toSpanned(): Spanned { return HtmlCompat.fromHtml(this, HtmlCompat.FROM_HTML_MODE_COMPACT) @@ -309,9 +297,12 @@ val Questionnaire.QuestionnaireItemComponent.localizedPrefixSpanned: Spanned? * code is used as the instructions of the parent question. */ internal val Questionnaire.QuestionnaireItemComponent.localizedInstructionsSpanned: Spanned? + get() = item.localizedInstructionsSpanned + +/** [localizedInstructionsSpanned] over list of [Questionnaire.QuestionnaireItemComponent] */ +internal val List.localizedInstructionsSpanned: Spanned? get() { - return item - .firstOrNull { questionnaireItem -> + return this.firstOrNull { questionnaireItem -> questionnaireItem.type == Questionnaire.QuestionnaireItemType.DISPLAY && questionnaireItem.isInstructionsCode } @@ -323,9 +314,12 @@ internal val Questionnaire.QuestionnaireItemComponent.localizedInstructionsSpann * present) is used as the fly-over text of the parent question. */ internal val Questionnaire.QuestionnaireItemComponent.localizedFlyoverSpanned: Spanned? + get() = item.localizedFlyoverSpanned + +/** [localizedFlyoverSpanned] over list of [Questionnaire.QuestionnaireItemComponent] */ +internal val List.localizedFlyoverSpanned: Spanned? get() = - item - .firstOrNull { questionnaireItem -> + this.firstOrNull { questionnaireItem -> questionnaireItem.type == Questionnaire.QuestionnaireItemType.DISPLAY && questionnaireItem.displayItemControl == DisplayItemControlType.FLYOVER } @@ -336,9 +330,12 @@ internal val Questionnaire.QuestionnaireItemComponent.localizedFlyoverSpanned: S * code is used as the instructions of the parent question. */ internal val Questionnaire.QuestionnaireItemComponent.localizedHelpSpanned: Spanned? + get() = item.localizedHelpSpanned + +/** [localizedHelpSpanned] over list of [Questionnaire.QuestionnaireItemComponent] */ +internal val List.localizedHelpSpanned: Spanned? get() { - return item - .firstOrNull { questionnaireItem -> questionnaireItem.isHelpCode } + return this.firstOrNull { questionnaireItem -> questionnaireItem.isHelpCode } ?.localizedTextSpanned } @@ -405,6 +402,25 @@ internal val Questionnaire.QuestionnaireItemComponent.isFlyoverCode: Boolean } } +/** Whether item type is display and [displayItemControl] is [DisplayItemControlType.HELP]. */ +internal val Questionnaire.QuestionnaireItemComponent.isHelpCode: Boolean + get() { + return when (type) { + Questionnaire.QuestionnaireItemType.DISPLAY -> { + displayItemControl == DisplayItemControlType.HELP + } + else -> { + false + } + } + } + +/** Whether item type is display. */ +internal val Questionnaire.QuestionnaireItemComponent.isDisplayItem: Boolean + get() = + (type == Questionnaire.QuestionnaireItemType.DISPLAY && + (isInstructionsCode || isFlyoverCode || isHelpCode)) + /** Slider step extension value. */ internal val Questionnaire.QuestionnaireItemComponent.sliderStepValue: Int? get() { diff --git a/datacapture/src/main/java/com/google/android/fhir/datacapture/views/GroupHeaderView.kt b/datacapture/src/main/java/com/google/android/fhir/datacapture/views/GroupHeaderView.kt index 783c2f277c..23c1aa06d0 100644 --- a/datacapture/src/main/java/com/google/android/fhir/datacapture/views/GroupHeaderView.kt +++ b/datacapture/src/main/java/com/google/android/fhir/datacapture/views/GroupHeaderView.kt @@ -28,7 +28,6 @@ import com.google.android.fhir.datacapture.extensions.localizedInstructionsSpann import com.google.android.fhir.datacapture.extensions.localizedPrefixSpanned import com.google.android.fhir.datacapture.extensions.localizedTextSpanned import com.google.android.fhir.datacapture.extensions.updateTextAndVisibility -import org.hl7.fhir.r4.model.Questionnaire internal class GroupHeaderView(context: Context, attrs: AttributeSet?) : LinearLayout(context, attrs) { @@ -37,19 +36,22 @@ internal class GroupHeaderView(context: Context, attrs: AttributeSet?) : LayoutInflater.from(context).inflate(R.layout.group_type_header_view, this, true) } - fun bind(questionnaireItem: Questionnaire.QuestionnaireItemComponent) { - val prefix = findViewById(R.id.prefix) - val question = findViewById(R.id.question) - val hint = findViewById(R.id.hint) + private val prefix = findViewById(R.id.prefix) + private val question = findViewById(R.id.question) + private val hint = findViewById(R.id.hint) + + fun bind(questionnaireViewItem: QuestionnaireViewItem) { initHelpViews( helpButton = findViewById(R.id.helpButton), helpCardView = findViewById(R.id.helpCardView), helpTextView = findViewById(R.id.helpText), - questionnaireItem + questionnaireItem = questionnaireViewItem.questionnaireItem + ) + prefix.updateTextAndVisibility(questionnaireViewItem.questionnaireItem.localizedPrefixSpanned) + question.updateTextAndVisibility(questionnaireViewItem.questionnaireItem.localizedTextSpanned) + hint.updateTextAndVisibility( + questionnaireViewItem.enabledDisplayItems.localizedInstructionsSpanned ) - prefix.updateTextAndVisibility(questionnaireItem.localizedPrefixSpanned) - question.updateTextAndVisibility(questionnaireItem.localizedTextSpanned) - hint.updateTextAndVisibility(questionnaireItem.localizedInstructionsSpanned) visibility = getHeaderViewVisibility(prefix, question, hint) } } diff --git a/datacapture/src/main/java/com/google/android/fhir/datacapture/views/HeaderView.kt b/datacapture/src/main/java/com/google/android/fhir/datacapture/views/HeaderView.kt index 0d77bbd227..e66008eb23 100644 --- a/datacapture/src/main/java/com/google/android/fhir/datacapture/views/HeaderView.kt +++ b/datacapture/src/main/java/com/google/android/fhir/datacapture/views/HeaderView.kt @@ -28,7 +28,6 @@ import com.google.android.fhir.datacapture.extensions.localizedInstructionsSpann import com.google.android.fhir.datacapture.extensions.localizedPrefixSpanned import com.google.android.fhir.datacapture.extensions.localizedTextSpanned import com.google.android.fhir.datacapture.extensions.updateTextAndVisibility -import org.hl7.fhir.r4.model.Questionnaire /** View for the prefix, question, and hint of a questionnaire item. */ internal class HeaderView(context: Context, attrs: AttributeSet?) : LinearLayout(context, attrs) { @@ -37,22 +36,24 @@ internal class HeaderView(context: Context, attrs: AttributeSet?) : LinearLayout LayoutInflater.from(context).inflate(R.layout.header_view, this, true) } - private var prefix: TextView = findViewById(R.id.prefix) - private var question: TextView = findViewById(R.id.question) - private var hint: TextView = findViewById(R.id.hint) - private var errorTextView: TextView = findViewById(R.id.error_text_at_header) + private val prefix = findViewById(R.id.prefix) + private val question = findViewById(R.id.question) + private val hint = findViewById(R.id.hint) + private val errorTextView = findViewById(R.id.error_text_at_header) - fun bind(questionnaireItem: Questionnaire.QuestionnaireItemComponent) { - prefix.updateTextAndVisibility(questionnaireItem.localizedPrefixSpanned) - question.updateTextAndVisibility(questionnaireItem.localizedTextSpanned) - hint.updateTextAndVisibility(questionnaireItem.localizedInstructionsSpanned) + fun bind(questionnaireViewItem: QuestionnaireViewItem) { initHelpViews( helpButton = findViewById(R.id.helpButton), helpCardView = findViewById(R.id.helpCardView), helpTextView = findViewById(R.id.helpText), - questionnaireItem + questionnaireItem = questionnaireViewItem.questionnaireItem ) - // Make the entire view GONE if there is nothing to show. This is to avoid an empty row in the + prefix.updateTextAndVisibility(questionnaireViewItem.questionnaireItem.localizedPrefixSpanned) + question.updateTextAndVisibility(questionnaireViewItem.questionnaireItem.localizedTextSpanned) + hint.updateTextAndVisibility( + questionnaireViewItem.enabledDisplayItems.localizedInstructionsSpanned + ) + // Make the entire view GONE if there is nothing to show. This is to avoid an empty row in the // questionnaire. visibility = getHeaderViewVisibility(prefix, question, hint) } diff --git a/datacapture/src/main/java/com/google/android/fhir/datacapture/views/QuestionnaireViewItem.kt b/datacapture/src/main/java/com/google/android/fhir/datacapture/views/QuestionnaireViewItem.kt index 926275349c..8a2d284fc3 100644 --- a/datacapture/src/main/java/com/google/android/fhir/datacapture/views/QuestionnaireViewItem.kt +++ b/datacapture/src/main/java/com/google/android/fhir/datacapture/views/QuestionnaireViewItem.kt @@ -32,8 +32,9 @@ import org.hl7.fhir.r4.model.QuestionnaireResponse /** * Data item for [QuestionnaireItemViewHolder] in [RecyclerView]. * - * The view should use [questionnaireItem], [answers], [answerOption], and [validationResult] to - * render the data item in the UI. The view SHOULD NOT mutate the data using these properties. + * The view should use [questionnaireItem], [answers], [answerOption], [validationResult] and + * [enabledDisplayItems] to render the data item in the UI. The view SHOULD NOT mutate the data + * using these properties. * * The view should use the following answer APIs to update the answer(s): * - [setAnswer] (for single and repeated answers) @@ -55,6 +56,7 @@ import org.hl7.fhir.r4.model.QuestionnaireResponse * @param resolveAnswerExpression the callback to resolve answer options when answer-expression * extension exists options * @param draftAnswer the draft input that cannot be stored in the [QuestionnaireResponse]. + * @param enabledDisplayItems the enabled display items in the given [questionnaireItem] */ data class QuestionnaireViewItem( val questionnaireItem: Questionnaire.QuestionnaireItemComponent, @@ -78,7 +80,8 @@ data class QuestionnaireViewItem( { emptyList() }, - internal val draftAnswer: Any? = null + internal val draftAnswer: Any? = null, + internal val enabledDisplayItems: List = emptyList() ) { /** diff --git a/datacapture/src/main/java/com/google/android/fhir/datacapture/views/factories/AttachmentViewHolderFactory.kt b/datacapture/src/main/java/com/google/android/fhir/datacapture/views/factories/AttachmentViewHolderFactory.kt index c2830d5969..f8d4357295 100644 --- a/datacapture/src/main/java/com/google/android/fhir/datacapture/views/factories/AttachmentViewHolderFactory.kt +++ b/datacapture/src/main/java/com/google/android/fhir/datacapture/views/factories/AttachmentViewHolderFactory.kt @@ -103,8 +103,8 @@ internal object AttachmentViewHolderFactory : override fun bind(questionnaireViewItem: QuestionnaireViewItem) { this.questionnaireViewItem = questionnaireViewItem + header.bind(questionnaireViewItem) val questionnaireItem = questionnaireViewItem.questionnaireItem - header.bind(questionnaireItem) displayOrClearInitialPreview() displayTakePhotoButton(questionnaireItem) displayUploadButton(questionnaireItem) diff --git a/datacapture/src/main/java/com/google/android/fhir/datacapture/views/factories/AutoCompleteViewHolderFactory.kt b/datacapture/src/main/java/com/google/android/fhir/datacapture/views/factories/AutoCompleteViewHolderFactory.kt index 5336dc3edd..0ebe92e20d 100644 --- a/datacapture/src/main/java/com/google/android/fhir/datacapture/views/factories/AutoCompleteViewHolderFactory.kt +++ b/datacapture/src/main/java/com/google/android/fhir/datacapture/views/factories/AutoCompleteViewHolderFactory.kt @@ -76,7 +76,7 @@ internal object AutoCompleteViewHolderFactory : } override fun bind(questionnaireViewItem: QuestionnaireViewItem) { - header.bind(questionnaireViewItem.questionnaireItem) + header.bind(questionnaireViewItem) val answerOptionString = questionnaireViewItem.answerOption.map { it.value.displayString(header.context) } diff --git a/datacapture/src/main/java/com/google/android/fhir/datacapture/views/factories/BooleanChoiceViewHolderFactory.kt b/datacapture/src/main/java/com/google/android/fhir/datacapture/views/factories/BooleanChoiceViewHolderFactory.kt index 0319b2b21e..d44cc328d2 100644 --- a/datacapture/src/main/java/com/google/android/fhir/datacapture/views/factories/BooleanChoiceViewHolderFactory.kt +++ b/datacapture/src/main/java/com/google/android/fhir/datacapture/views/factories/BooleanChoiceViewHolderFactory.kt @@ -55,10 +55,10 @@ internal object BooleanChoiceViewHolderFactory : override fun bind(questionnaireViewItem: QuestionnaireViewItem) { this.questionnaireViewItem = questionnaireViewItem - val questionnaireItem = questionnaireViewItem.questionnaireItem - header.bind(questionnaireItem) + header.bind(questionnaireViewItem) val choiceOrientation = - questionnaireItem.choiceOrientation ?: ChoiceOrientationTypes.VERTICAL + questionnaireViewItem.questionnaireItem.choiceOrientation + ?: ChoiceOrientationTypes.VERTICAL with(flow) { when (choiceOrientation) { ChoiceOrientationTypes.HORIZONTAL -> { diff --git a/datacapture/src/main/java/com/google/android/fhir/datacapture/views/factories/CheckBoxGroupViewHolderFactory.kt b/datacapture/src/main/java/com/google/android/fhir/datacapture/views/factories/CheckBoxGroupViewHolderFactory.kt index b819bd0fb3..c3fd5a5a5a 100644 --- a/datacapture/src/main/java/com/google/android/fhir/datacapture/views/factories/CheckBoxGroupViewHolderFactory.kt +++ b/datacapture/src/main/java/com/google/android/fhir/datacapture/views/factories/CheckBoxGroupViewHolderFactory.kt @@ -53,11 +53,10 @@ internal object CheckBoxGroupViewHolderFactory : } override fun bind(questionnaireViewItem: QuestionnaireViewItem) { - val questionnaireItem = questionnaireViewItem.questionnaireItem + header.bind(questionnaireViewItem) val choiceOrientation = - questionnaireItem.choiceOrientation ?: ChoiceOrientationTypes.VERTICAL - - header.bind(questionnaireItem) + questionnaireViewItem.questionnaireItem.choiceOrientation + ?: ChoiceOrientationTypes.VERTICAL // Keep the Flow layout which is always the first child checkboxGroup.removeViews(1, checkboxGroup.childCount - 1) diff --git a/datacapture/src/main/java/com/google/android/fhir/datacapture/views/factories/DatePickerViewHolderFactory.kt b/datacapture/src/main/java/com/google/android/fhir/datacapture/views/factories/DatePickerViewHolderFactory.kt index c378c96f80..782daa072e 100644 --- a/datacapture/src/main/java/com/google/android/fhir/datacapture/views/factories/DatePickerViewHolderFactory.kt +++ b/datacapture/src/main/java/com/google/android/fhir/datacapture/views/factories/DatePickerViewHolderFactory.kt @@ -111,7 +111,7 @@ internal object DatePickerViewHolderFactory : @SuppressLint("NewApi") // java.time APIs can be used due to desugaring override fun bind(questionnaireViewItem: QuestionnaireViewItem) { clearPreviousState() - header.bind(questionnaireViewItem.questionnaireItem) + header.bind(questionnaireViewItem) textInputLayout.hint = canonicalizedDatePattern textInputEditText.removeTextChangedListener(textWatcher) diff --git a/datacapture/src/main/java/com/google/android/fhir/datacapture/views/factories/DateTimePickerViewHolderFactory.kt b/datacapture/src/main/java/com/google/android/fhir/datacapture/views/factories/DateTimePickerViewHolderFactory.kt index 0bd0ba7918..aa4d9e4789 100644 --- a/datacapture/src/main/java/com/google/android/fhir/datacapture/views/factories/DateTimePickerViewHolderFactory.kt +++ b/datacapture/src/main/java/com/google/android/fhir/datacapture/views/factories/DateTimePickerViewHolderFactory.kt @@ -124,7 +124,7 @@ internal object DateTimePickerViewHolderFactory : @SuppressLint("NewApi") // java.time APIs can be used due to desugaring override fun bind(questionnaireViewItem: QuestionnaireViewItem) { clearPreviousState() - header.bind(questionnaireViewItem.questionnaireItem) + header.bind(questionnaireViewItem) dateInputLayout.hint = canonicalizedDatePattern dateInputEditText.removeTextChangedListener(textWatcher) diff --git a/datacapture/src/main/java/com/google/android/fhir/datacapture/views/factories/DialogSelectViewHolderFactory.kt b/datacapture/src/main/java/com/google/android/fhir/datacapture/views/factories/DialogSelectViewHolderFactory.kt index 6205812f7b..c188651023 100644 --- a/datacapture/src/main/java/com/google/android/fhir/datacapture/views/factories/DialogSelectViewHolderFactory.kt +++ b/datacapture/src/main/java/com/google/android/fhir/datacapture/views/factories/DialogSelectViewHolderFactory.kt @@ -63,28 +63,28 @@ internal object QuestionnaireItemDialogSelectViewHolderFactory : override fun bind(questionnaireViewItem: QuestionnaireViewItem) { cleanupOldState() - holder.summaryHolder.hint = questionnaireViewItem.questionnaireItem.localizedFlyoverSpanned + holder.summaryHolder.hint = + questionnaireViewItem.enabledDisplayItems.localizedFlyoverSpanned val activity = requireNotNull(holder.header.context.tryUnwrapContext()) { "Can only use dialog select in an AppCompatActivity context" } val viewModel: QuestionnaireItemDialogSelectViewModel by activity.viewModels() - val item = questionnaireViewItem.questionnaireItem - // Bind static data - holder.header.bind(item) + holder.header.bind(questionnaireViewItem) + val questionnaireItem = questionnaireViewItem.questionnaireItem selectedOptionsJob = activity.lifecycleScope.launch { // Set the initial selected options state from the FHIR data model viewModel.updateSelectedOptions( - item.linkId, + questionnaireItem.linkId, questionnaireViewItem.extractInitialOptions(holder.header.context) ) // Listen for changes to selected options to update summary + FHIR data model - viewModel.getSelectedOptionsFlow(item.linkId).collect { selectedOptions -> + viewModel.getSelectedOptionsFlow(questionnaireItem.linkId).collect { selectedOptions -> holder.summary.text = selectedOptions.selectedSummary updateAnswers(selectedOptions) } @@ -95,12 +95,12 @@ internal object QuestionnaireItemDialogSelectViewHolderFactory : View.OnClickListener { val fragment = OptionSelectDialogFragment( - title = item.localizedTextSpanned ?: "", - config = item.buildConfig(), + title = questionnaireItem.localizedTextSpanned ?: "", + config = questionnaireItem.buildConfig(), ) fragment.arguments = bundleOf( - OptionSelectDialogFragment.KEY_QUESTION_LINK_ID to item.linkId, + OptionSelectDialogFragment.KEY_QUESTION_LINK_ID to questionnaireItem.linkId, ) fragment.show(activity.supportFragmentManager, null) } diff --git a/datacapture/src/main/java/com/google/android/fhir/datacapture/views/factories/DisplayViewHolderFactory.kt b/datacapture/src/main/java/com/google/android/fhir/datacapture/views/factories/DisplayViewHolderFactory.kt index 9b382cd0e7..a474898ccb 100644 --- a/datacapture/src/main/java/com/google/android/fhir/datacapture/views/factories/DisplayViewHolderFactory.kt +++ b/datacapture/src/main/java/com/google/android/fhir/datacapture/views/factories/DisplayViewHolderFactory.kt @@ -33,7 +33,7 @@ internal object DisplayViewHolderFactory : } override fun bind(questionnaireViewItem: QuestionnaireViewItem) { - header.bind(questionnaireViewItem.questionnaireItem) + header.bind(questionnaireViewItem) } override fun setReadOnly(isReadOnly: Boolean) { diff --git a/datacapture/src/main/java/com/google/android/fhir/datacapture/views/factories/DropDownViewHolderFactory.kt b/datacapture/src/main/java/com/google/android/fhir/datacapture/views/factories/DropDownViewHolderFactory.kt index 2b788810f4..312a876e3a 100644 --- a/datacapture/src/main/java/com/google/android/fhir/datacapture/views/factories/DropDownViewHolderFactory.kt +++ b/datacapture/src/main/java/com/google/android/fhir/datacapture/views/factories/DropDownViewHolderFactory.kt @@ -58,8 +58,8 @@ internal object DropDownViewHolderFactory : override fun bind(questionnaireViewItem: QuestionnaireViewItem) { cleanupOldState() - header.bind(questionnaireViewItem.questionnaireItem) - textInputLayout.hint = questionnaireViewItem.questionnaireItem.localizedFlyoverSpanned + header.bind(questionnaireViewItem) + textInputLayout.hint = questionnaireViewItem.enabledDisplayItems.localizedFlyoverSpanned val answerOptionList = this.questionnaireViewItem.answerOption .map { diff --git a/datacapture/src/main/java/com/google/android/fhir/datacapture/views/factories/EditTextViewHolderFactory.kt b/datacapture/src/main/java/com/google/android/fhir/datacapture/views/factories/EditTextViewHolderFactory.kt index d67624bd5b..d5a9d05ced 100644 --- a/datacapture/src/main/java/com/google/android/fhir/datacapture/views/factories/EditTextViewHolderFactory.kt +++ b/datacapture/src/main/java/com/google/android/fhir/datacapture/views/factories/EditTextViewHolderFactory.kt @@ -80,8 +80,8 @@ abstract class QuestionnaireItemEditTextViewHolderDelegate(private val rawInputT } override fun bind(questionnaireViewItem: QuestionnaireViewItem) { - header.bind(questionnaireViewItem.questionnaireItem) - textInputLayout.hint = questionnaireViewItem.questionnaireItem.localizedFlyoverSpanned + header.bind(questionnaireViewItem) + textInputLayout.hint = questionnaireViewItem.enabledDisplayItems.localizedFlyoverSpanned displayValidationResult(questionnaireViewItem.validationResult) textInputEditText.removeTextChangedListener(textWatcher) diff --git a/datacapture/src/main/java/com/google/android/fhir/datacapture/views/factories/GroupViewHolderFactory.kt b/datacapture/src/main/java/com/google/android/fhir/datacapture/views/factories/GroupViewHolderFactory.kt index 7b5b7665ab..71a7e6d712 100644 --- a/datacapture/src/main/java/com/google/android/fhir/datacapture/views/factories/GroupViewHolderFactory.kt +++ b/datacapture/src/main/java/com/google/android/fhir/datacapture/views/factories/GroupViewHolderFactory.kt @@ -45,7 +45,7 @@ internal object GroupViewHolderFactory : } override fun bind(questionnaireViewItem: QuestionnaireViewItem) { - header.bind(questionnaireViewItem.questionnaireItem) + header.bind(questionnaireViewItem) addItemButton.visibility = if (questionnaireViewItem.questionnaireItem.repeats) View.VISIBLE else View.GONE addItemButton.setOnClickListener { diff --git a/datacapture/src/main/java/com/google/android/fhir/datacapture/views/factories/RadioGroupViewHolderFactory.kt b/datacapture/src/main/java/com/google/android/fhir/datacapture/views/factories/RadioGroupViewHolderFactory.kt index b271a7b198..913b0477f3 100644 --- a/datacapture/src/main/java/com/google/android/fhir/datacapture/views/factories/RadioGroupViewHolderFactory.kt +++ b/datacapture/src/main/java/com/google/android/fhir/datacapture/views/factories/RadioGroupViewHolderFactory.kt @@ -53,12 +53,12 @@ internal object RadioGroupViewHolderFactory : } override fun bind(questionnaireViewItem: QuestionnaireViewItem) { - val questionnaireItem = questionnaireViewItem.questionnaireItem - header.bind(questionnaireItem) + header.bind(questionnaireViewItem) // Keep the Flow layout which is the first child radioGroup.removeViews(1, radioGroup.childCount - 1) val choiceOrientation = - questionnaireItem.choiceOrientation ?: ChoiceOrientationTypes.VERTICAL + questionnaireViewItem.questionnaireItem.choiceOrientation + ?: ChoiceOrientationTypes.VERTICAL when (choiceOrientation) { ChoiceOrientationTypes.HORIZONTAL -> { flow.setOrientation(Flow.HORIZONTAL) diff --git a/datacapture/src/main/java/com/google/android/fhir/datacapture/views/factories/ReviewViewHolderFactory.kt b/datacapture/src/main/java/com/google/android/fhir/datacapture/views/factories/ReviewViewHolderFactory.kt index cabbf739f4..0eb6194251 100644 --- a/datacapture/src/main/java/com/google/android/fhir/datacapture/views/factories/ReviewViewHolderFactory.kt +++ b/datacapture/src/main/java/com/google/android/fhir/datacapture/views/factories/ReviewViewHolderFactory.kt @@ -69,12 +69,12 @@ internal object ReviewViewHolderFactory : QuestionnaireItemViewHolderFactory(R.l questionnaireViewItem.questionnaireItem.localizedTextSpanned ) hint.updateTextAndVisibility( - questionnaireViewItem.questionnaireItem.localizedInstructionsSpanned + questionnaireViewItem.enabledDisplayItems.localizedInstructionsSpanned ) header.visibility = getHeaderViewVisibility(prefix, question, hint) val localizedFlyoverSpanned = - questionnaireViewItem.questionnaireItem.localizedFlyoverSpanned + questionnaireViewItem.enabledDisplayItems.localizedFlyoverSpanned flyOverTextView.apply { visibility = if (localizedFlyoverSpanned.isNullOrEmpty()) { diff --git a/datacapture/src/main/java/com/google/android/fhir/datacapture/views/factories/SliderViewHolderFactory.kt b/datacapture/src/main/java/com/google/android/fhir/datacapture/views/factories/SliderViewHolderFactory.kt index 8c43d272b5..af119a7bce 100644 --- a/datacapture/src/main/java/com/google/android/fhir/datacapture/views/factories/SliderViewHolderFactory.kt +++ b/datacapture/src/main/java/com/google/android/fhir/datacapture/views/factories/SliderViewHolderFactory.kt @@ -49,7 +49,7 @@ internal object SliderViewHolderFactory : QuestionnaireItemViewHolderFactory(R.l override fun bind(questionnaireViewItem: QuestionnaireViewItem) { this.questionnaireViewItem = questionnaireViewItem - header.bind(questionnaireViewItem.questionnaireItem) + header.bind(questionnaireViewItem) val answer = questionnaireViewItem.answers.singleOrNull() val minValue = getMinValue(questionnaireViewItem.questionnaireItem) val maxValue = getMaxValue(questionnaireViewItem.questionnaireItem) diff --git a/datacapture/src/test/java/com/google/android/fhir/datacapture/QuestionnaireViewModelTest.kt b/datacapture/src/test/java/com/google/android/fhir/datacapture/QuestionnaireViewModelTest.kt index e3e0d905bc..f30a3c98aa 100644 --- a/datacapture/src/test/java/com/google/android/fhir/datacapture/QuestionnaireViewModelTest.kt +++ b/datacapture/src/test/java/com/google/android/fhir/datacapture/QuestionnaireViewModelTest.kt @@ -35,6 +35,7 @@ import com.google.android.fhir.datacapture.extensions.DisplayItemControlType import com.google.android.fhir.datacapture.extensions.EXTENSION_CALCULATED_EXPRESSION_URL import com.google.android.fhir.datacapture.extensions.EXTENSION_DISPLAY_CATEGORY_SYSTEM import com.google.android.fhir.datacapture.extensions.EXTENSION_DISPLAY_CATEGORY_URL +import com.google.android.fhir.datacapture.extensions.EXTENSION_ENABLE_WHEN_EXPRESSION_URL import com.google.android.fhir.datacapture.extensions.EXTENSION_ENTRY_MODE_URL import com.google.android.fhir.datacapture.extensions.EXTENSION_HIDDEN_URL import com.google.android.fhir.datacapture.extensions.EXTENSION_ITEM_CONTROL_SYSTEM @@ -70,6 +71,7 @@ import org.hl7.fhir.r4.model.BooleanType import org.hl7.fhir.r4.model.CodeableConcept import org.hl7.fhir.r4.model.Coding import org.hl7.fhir.r4.model.DateType +import org.hl7.fhir.r4.model.Enumerations import org.hl7.fhir.r4.model.Expression import org.hl7.fhir.r4.model.Extension import org.hl7.fhir.r4.model.HumanName @@ -4115,6 +4117,125 @@ class QuestionnaireViewModelTest { } } + @Test + fun `display item should be enabled when initial value is set`() = runTest { + val enableWhenExpressionExtension: (Boolean) -> Extension = { + Extension().apply { + url = EXTENSION_ENABLE_WHEN_EXPRESSION_URL + setValue( + Expression().apply { + language = Expression.ExpressionLanguage.TEXT_FHIRPATH.toCode() + expression = + "%resource.repeat(item).where(linkId='1' and answer.empty().not()).select(answer.value) = ${if (it) "true" else "false"}" + } + ) + } + } + val displayCategoryExtension = + Extension().apply { + url = EXTENSION_DISPLAY_CATEGORY_URL + setValue( + CodeableConcept().apply { + coding = + listOf( + Coding().apply { + code = INSTRUCTIONS + system = EXTENSION_DISPLAY_CATEGORY_SYSTEM + } + ) + } + ) + } + val questionnaire: (List) -> Questionnaire = { + Questionnaire().apply { + id = "questionnaire.enabled.display" + name = "Questionnaire Enabled Display" + title = "Questionnaire Enabled Display" + status = Enumerations.PublicationStatus.ACTIVE + addItem( + Questionnaire.QuestionnaireItemComponent().apply { + linkId = "1" + text = "Questionnaire Text" + type = Questionnaire.QuestionnaireItemType.BOOLEAN + initial = it + addItem( + Questionnaire.QuestionnaireItemComponent().apply { + extension = listOf(displayCategoryExtension, enableWhenExpressionExtension(false)) + linkId = "1.1" + text = "Text when no is selected" + type = Questionnaire.QuestionnaireItemType.DISPLAY + } + ) + .addItem( + Questionnaire.QuestionnaireItemComponent().apply { + extension = listOf(displayCategoryExtension, enableWhenExpressionExtension(true)) + linkId = "1.2" + text = "Text when yes is selected" + type = Questionnaire.QuestionnaireItemType.DISPLAY + } + ) + } + ) + } + } + + state.set( + EXTRA_QUESTIONNAIRE_JSON_STRING, + printer.encodeResourceToString(questionnaire(emptyList())) + ) + + // empty initial value + var viewModel = QuestionnaireViewModel(context, state) + assertThat(viewModel.getQuestionnaireItemViewItemList().size).isEqualTo(1) + // enabledDisplayItems is 0 when no choice is present + assertThat( + viewModel.getQuestionnaireItemViewItemList()[0].asQuestion().enabledDisplayItems.size + ) + .isEqualTo(0) + + // initial value is set to false + state.set( + EXTRA_QUESTIONNAIRE_JSON_STRING, + printer.encodeResourceToString( + questionnaire( + listOf( + Questionnaire.QuestionnaireItemInitialComponent().apply { value = BooleanType(false) } + ) + ) + ) + ) + + viewModel = QuestionnaireViewModel(context, state) + assertThat(viewModel.getQuestionnaireItemViewItemList().size).isEqualTo(1) + var enabledDisplayItems = + viewModel.getQuestionnaireItemViewItemList()[0].asQuestion().enabledDisplayItems + assertThat(enabledDisplayItems.size).isEqualTo(1) + assertThat(enabledDisplayItems[0].type).isEqualTo(Questionnaire.QuestionnaireItemType.DISPLAY) + assertThat(enabledDisplayItems[0].linkId).isEqualTo("1.1") + assertThat(enabledDisplayItems[0].text).isEqualTo("Text when no is selected") + + // initial value is set to true + state.set( + EXTRA_QUESTIONNAIRE_JSON_STRING, + printer.encodeResourceToString( + questionnaire( + listOf( + Questionnaire.QuestionnaireItemInitialComponent().apply { value = BooleanType(true) } + ) + ) + ) + ) + + viewModel = QuestionnaireViewModel(context, state) + assertThat(viewModel.getQuestionnaireItemViewItemList().size).isEqualTo(1) + enabledDisplayItems = + viewModel.getQuestionnaireItemViewItemList()[0].asQuestion().enabledDisplayItems + assertThat(enabledDisplayItems.size).isEqualTo(1) + assertThat(enabledDisplayItems[0].type).isEqualTo(Questionnaire.QuestionnaireItemType.DISPLAY) + assertThat(enabledDisplayItems[0].linkId).isEqualTo("1.2") + assertThat(enabledDisplayItems[0].text).isEqualTo("Text when yes is selected") + } + private fun createQuestionnaireViewModel( questionnaire: Questionnaire, questionnaireResponse: QuestionnaireResponse? = null, diff --git a/datacapture/src/test/java/com/google/android/fhir/datacapture/views/GroupHeaderViewTest.kt b/datacapture/src/test/java/com/google/android/fhir/datacapture/views/GroupHeaderViewTest.kt index d4b374a775..7dfbdf0cf8 100644 --- a/datacapture/src/test/java/com/google/android/fhir/datacapture/views/GroupHeaderViewTest.kt +++ b/datacapture/src/test/java/com/google/android/fhir/datacapture/views/GroupHeaderViewTest.kt @@ -28,12 +28,14 @@ import com.google.android.fhir.datacapture.extensions.EXTENSION_DISPLAY_CATEGORY import com.google.android.fhir.datacapture.extensions.EXTENSION_ITEM_CONTROL_SYSTEM import com.google.android.fhir.datacapture.extensions.EXTENSION_ITEM_CONTROL_URL import com.google.android.fhir.datacapture.extensions.INSTRUCTIONS +import com.google.android.fhir.datacapture.validation.Valid import com.google.android.material.card.MaterialCardView import com.google.common.truth.Truth.assertThat import org.hl7.fhir.r4.model.CodeableConcept import org.hl7.fhir.r4.model.Coding import org.hl7.fhir.r4.model.Extension import org.hl7.fhir.r4.model.Questionnaire +import org.hl7.fhir.r4.model.QuestionnaireResponse import org.junit.Test import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner @@ -47,9 +49,37 @@ class GroupHeaderViewTest { ) private val view = GroupHeaderView(parent.context, null) + private fun getQuestionnaireViewItemWithQuestionnaireItem( + questionnaireItem: Questionnaire.QuestionnaireItemComponent + ): QuestionnaireViewItem { + return QuestionnaireViewItem( + questionnaireItem = questionnaireItem, + questionnaireResponseItem = QuestionnaireResponse.QuestionnaireResponseItemComponent(), + validationResult = Valid, + answersChangedCallback = { _, _, _, _ -> } + ) + } + + private fun getQuestionnaireViewItemWithQuestionnaireItemAndEnabledDisplayItems( + questionnaireItem: Questionnaire.QuestionnaireItemComponent, + enabledDisplayItems: List + ): QuestionnaireViewItem { + return QuestionnaireViewItem( + questionnaireItem = questionnaireItem, + questionnaireResponseItem = QuestionnaireResponse.QuestionnaireResponseItemComponent(), + validationResult = Valid, + answersChangedCallback = { _, _, _, _ -> }, + enabledDisplayItems = enabledDisplayItems + ) + } + @Test fun shouldShowPrefix() { - view.bind(Questionnaire.QuestionnaireItemComponent().apply { prefix = "Prefix?" }) + view.bind( + getQuestionnaireViewItemWithQuestionnaireItem( + Questionnaire.QuestionnaireItemComponent().apply { prefix = "Prefix?" } + ) + ) assertThat(view.findViewById(R.id.prefix).isVisible).isTrue() assertThat(view.findViewById(R.id.prefix).text.toString()).isEqualTo("Prefix?") @@ -57,7 +87,11 @@ class GroupHeaderViewTest { @Test fun shouldHidePrefix() { - view.bind(Questionnaire.QuestionnaireItemComponent().apply { prefix = "" }) + view.bind( + getQuestionnaireViewItemWithQuestionnaireItem( + Questionnaire.QuestionnaireItemComponent().apply { prefix = "" } + ) + ) assertThat(view.findViewById(R.id.prefix).isVisible).isFalse() } @@ -65,10 +99,12 @@ class GroupHeaderViewTest { @Test fun shouldShowQuestion() { view.bind( - Questionnaire.QuestionnaireItemComponent().apply { - repeats = true - text = "Question?" - } + getQuestionnaireViewItemWithQuestionnaireItem( + Questionnaire.QuestionnaireItemComponent().apply { + repeats = true + text = "Question?" + } + ) ) assertThat(view.findViewById(R.id.question).text.toString()).isEqualTo("Question?") @@ -76,18 +112,20 @@ class GroupHeaderViewTest { @Test fun `shows instructions`() { + val itemList = + listOf( + Questionnaire.QuestionnaireItemComponent().apply { + linkId = "nested-display-question" + text = "subtitle text" + extension = listOf(displayCategoryExtensionWithInstructionsCode) + type = Questionnaire.QuestionnaireItemType.DISPLAY + } + ) view.bind( - Questionnaire.QuestionnaireItemComponent().apply { - item = - listOf( - Questionnaire.QuestionnaireItemComponent().apply { - linkId = "nested-display-question" - text = "subtitle text" - extension = listOf(displayCategoryExtensionWithInstructionsCode) - type = Questionnaire.QuestionnaireItemType.DISPLAY - } - ) - } + getQuestionnaireViewItemWithQuestionnaireItemAndEnabledDisplayItems( + Questionnaire.QuestionnaireItemComponent().apply { item = itemList }, + itemList + ) ) assertThat(view.findViewById(R.id.hint).isVisible).isTrue() @@ -97,15 +135,17 @@ class GroupHeaderViewTest { @Test fun `hides instructions`() { view.bind( - Questionnaire.QuestionnaireItemComponent().apply { - item = - listOf( - Questionnaire.QuestionnaireItemComponent().apply { - linkId = "nested-display-question" - type = Questionnaire.QuestionnaireItemType.DISPLAY - } - ) - } + getQuestionnaireViewItemWithQuestionnaireItem( + Questionnaire.QuestionnaireItemComponent().apply { + item = + listOf( + Questionnaire.QuestionnaireItemComponent().apply { + linkId = "nested-display-question" + type = Questionnaire.QuestionnaireItemType.DISPLAY + } + ) + } + ) ) assertThat(view.findViewById(R.id.hint).visibility).isEqualTo(View.GONE) @@ -114,17 +154,19 @@ class GroupHeaderViewTest { @Test fun `shows helpButton if help code is present`() { view.bind( - Questionnaire.QuestionnaireItemComponent().apply { - item = - listOf( - Questionnaire.QuestionnaireItemComponent().apply { - linkId = "nested-display-question" - text = "help text" - extension = listOf(itemControlExtensionWithHelpCode) - type = Questionnaire.QuestionnaireItemType.DISPLAY - } - ) - } + getQuestionnaireViewItemWithQuestionnaireItem( + Questionnaire.QuestionnaireItemComponent().apply { + item = + listOf( + Questionnaire.QuestionnaireItemComponent().apply { + linkId = "nested-display-question" + text = "help text" + extension = listOf(itemControlExtensionWithHelpCode) + type = Questionnaire.QuestionnaireItemType.DISPLAY + } + ) + } + ) ) assertThat(view.findViewById