Skip to content

Commit

Permalink
OptionExclusive extension support. (#1284)
Browse files Browse the repository at this point in the history
* OptionExclusive extension support.

* Address review comments.
  • Loading branch information
santosh-pingle authored and ktarasenko committed Apr 12, 2022
1 parent 3a00087 commit 7925e2a
Show file tree
Hide file tree
Showing 4 changed files with 153 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,13 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import com.google.android.fhir.datacapture.ChoiceOrientationTypes
import com.google.android.fhir.datacapture.EXTENSION_CHOICE_ORIENTATION_URL
import com.google.android.fhir.datacapture.EXTENSION_OPTION_EXCLUSIVE_URL
import com.google.android.fhir.datacapture.R
import com.google.common.truth.Truth.assertThat
import org.hl7.fhir.r4.model.BooleanType
import org.hl7.fhir.r4.model.CodeType
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
Expand Down Expand Up @@ -254,6 +257,83 @@ class QuestionnaireItemCheckBoxGroupViewHolderFactoryInstrumentedTest {
assertThat(answer[0].valueCoding.display).isEqualTo("Coding 1")
}

@Test
@UiThreadTest
fun optionExclusiveAnswerOption_click_deselectsOtherAnswerOptions() {
val questionnaireItemViewItem =
QuestionnaireItemViewItem(
Questionnaire.QuestionnaireItemComponent().apply {
repeats = true
addAnswerOption(
Questionnaire.QuestionnaireItemAnswerOptionComponent().apply {
value =
Coding().apply {
code = "code-1"
display = "display-1"
}
extension = listOf(Extension(EXTENSION_OPTION_EXCLUSIVE_URL, BooleanType(true)))
}
)
addAnswerOption(
Questionnaire.QuestionnaireItemAnswerOptionComponent().apply {
value =
Coding().apply {
code = "code-2"
display = "display-2"
}
}
)
},
QuestionnaireResponse.QuestionnaireResponseItemComponent()
) {}
viewHolder.bind(questionnaireItemViewItem)
val checkBoxGroup = viewHolder.itemView.findViewById<ConstraintLayout>(R.id.checkbox_group)
(checkBoxGroup.getChildAt(2) as CheckBox).performClick()
(checkBoxGroup.getChildAt(1) as CheckBox).performClick()
val answer = questionnaireItemViewItem.questionnaireResponseItem.answer

assertThat(answer.single().valueCoding.display).isEqualTo("display-1")
}

@Test
@UiThreadTest
fun answerOption_click_deselectsOptionExclusiveAnswerOption() {
val questionnaireItemViewItem =
QuestionnaireItemViewItem(
Questionnaire.QuestionnaireItemComponent().apply {
repeats = true
addAnswerOption(
Questionnaire.QuestionnaireItemAnswerOptionComponent().apply {
value =
Coding().apply {
code = "code-1"
display = "display-1"
}
extension = listOf(Extension(EXTENSION_OPTION_EXCLUSIVE_URL, BooleanType(true)))
}
)
addAnswerOption(
Questionnaire.QuestionnaireItemAnswerOptionComponent().apply {
value =
Coding().apply {
code = "code-2"
display = "display-2"
}
}
)
},
QuestionnaireResponse.QuestionnaireResponseItemComponent()
) {}

viewHolder.bind(questionnaireItemViewItem)
val checkBoxGroup = viewHolder.itemView.findViewById<ConstraintLayout>(R.id.checkbox_group)
(checkBoxGroup.getChildAt(1) as CheckBox).performClick()
(checkBoxGroup.getChildAt(2) as CheckBox).performClick()
val answer = questionnaireItemViewItem.questionnaireResponseItem.answer

assertThat(answer.single().valueCoding.display).isEqualTo("display-2")
}

@Test
@UiThreadTest
fun click_shouldRemoveQuestionnaireResponseItemAnswer() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,12 @@
package com.google.android.fhir.datacapture

import com.google.android.fhir.getLocalizedText
import org.hl7.fhir.r4.model.BooleanType
import org.hl7.fhir.r4.model.Questionnaire

internal const val EXTENSION_OPTION_EXCLUSIVE_URL =
"http://hl7.org/fhir/StructureDefinition/questionnaire-optionExclusive"

val Questionnaire.QuestionnaireItemAnswerOptionComponent.displayString: String
get() {
if (!hasValueCoding()) {
Expand All @@ -31,3 +35,15 @@ val Questionnaire.QuestionnaireItemAnswerOptionComponent.displayString: String
display
}
}

/** Indicates that if this answerOption is selected, no other possible answers may be selected. */
internal val Questionnaire.QuestionnaireItemAnswerOptionComponent.optionExclusive: Boolean
get() {
val extension =
this.extension.singleOrNull { it.url == EXTENSION_OPTION_EXCLUSIVE_URL } ?: return false
val value = extension.value
if (value is BooleanType) {
return value.booleanValue()
}
return false
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import com.google.android.fhir.datacapture.R
import com.google.android.fhir.datacapture.choiceOrientation
import com.google.android.fhir.datacapture.localizedPrefixSpanned
import com.google.android.fhir.datacapture.localizedTextSpanned
import com.google.android.fhir.datacapture.optionExclusive
import com.google.android.fhir.datacapture.subtitleText
import com.google.android.fhir.datacapture.validation.ValidationResult
import com.google.android.fhir.datacapture.validation.getSingleStringValidationMessage
Expand Down Expand Up @@ -124,18 +125,49 @@ internal object QuestionnaireItemCheckBoxGroupViewHolderFactory :
)
setOnClickListener {
when (isChecked) {
true ->
true -> {
questionnaireItemViewItem.addAnswer(
QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent().apply {
value = answerOption.value
}
)
false ->
if (answerOption.optionExclusive) {
// if this answer option has optionExclusive extension, then deselect other
// answer options.
val optionExclusiveIndex = checkboxGroup.indexOfChild(it) - 1
for (i in 0 until questionnaireItemViewItem.answerOption.size) {
if (optionExclusiveIndex == i) {
continue
}
(checkboxGroup.getChildAt(i + 1) as CheckBox).isChecked = false
questionnaireItemViewItem.removeAnswer(
QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent().apply {
value = questionnaireItemViewItem.answerOption[i].value
}
)
}
} else {
// deselect optionExclusive answer option.
for (i in 0 until questionnaireItemViewItem.answerOption.size) {
if (!questionnaireItemViewItem.answerOption[i].optionExclusive) {
continue
}
(checkboxGroup.getChildAt(i + 1) as CheckBox).isChecked = false
questionnaireItemViewItem.removeAnswer(
QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent().apply {
value = questionnaireItemViewItem.answerOption[i].value
}
)
}
}
}
false -> {
questionnaireItemViewItem.removeAnswer(
QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent().apply {
value = answerOption.value
}
)
}
}

onAnswerChanged(checkboxGroup.context)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import android.os.Build
import com.google.common.truth.Truth.assertThat
import java.util.Locale
import kotlin.test.assertFailsWith
import kotlinx.coroutines.runBlocking
import org.hl7.fhir.r4.model.BooleanType
import org.hl7.fhir.r4.model.Coding
import org.hl7.fhir.r4.model.Extension
import org.hl7.fhir.r4.model.Questionnaire
Expand Down Expand Up @@ -102,4 +104,25 @@ class MoreAnswerOptionsTest {

assertThat(answerOption.displayString).isEqualTo("Test Code")
}

@Test
fun optionExclusiveExtension_valueTrue_returnsTrue() = runBlocking {
val answerOptionTest = Coding("test", "option", "1")
val questionnaire =
Questionnaire().apply {
id = "a-questionnaire"
addItem(
Questionnaire.QuestionnaireItemComponent().apply {
answerOption =
listOf(
Questionnaire.QuestionnaireItemAnswerOptionComponent(answerOptionTest).apply {
extension = listOf(Extension(EXTENSION_OPTION_EXCLUSIVE_URL, BooleanType(true)))
},
)
}
)
}

assertThat(questionnaire.item.single().answerOption.single().optionExclusive).isTrue()
}
}

0 comments on commit 7925e2a

Please sign in to comment.