Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added Support for optionExclusive Extension in Popup #2438

Merged
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import com.google.android.fhir.datacapture.R
import com.google.android.fhir.datacapture.extensions.DisplayItemControlType
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.EXTENSION_OPTION_EXCLUSIVE_URL
import com.google.android.fhir.datacapture.extensions.ItemControlTypes
import com.google.android.fhir.datacapture.test.TestActivity
import com.google.android.fhir.datacapture.test.utilities.assertQuestionnaireResponseAtIndex
Expand All @@ -52,6 +53,7 @@ import com.google.android.material.textfield.TextInputLayout
import com.google.common.truth.StringSubject
import com.google.common.truth.Truth.assertThat
import org.hamcrest.Matchers.not
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.Extension
Expand Down Expand Up @@ -101,6 +103,126 @@ class QuestionnaireItemDialogMultiSelectViewHolderFactoryEspressoTest {
assertQuestionnaireResponseAtIndex(answerHolder!!, "Coding 1", "Coding 3", "Coding 5")
}

@Test
fun multipleChoice_selectMultiple_selectExclusive_clickSave_shouldSaveOnlyExclusiveOption() {
var answerHolder: List<QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent>? = null
val questionnaireViewItem =
QuestionnaireViewItem(
answerOptions(true, "Coding 1", "Coding 2", "Coding 3", "Coding 4", "Coding 5")
.addAnswerOption(Questionnaire.QuestionnaireItemAnswerOptionComponent().apply {
value = Coding().apply { display = "Coding Exclusive" }
extension = listOf(Extension(EXTENSION_OPTION_EXCLUSIVE_URL, BooleanType(true)))
}),
responseOptions(),
validationResult = NotValidated,
answersChangedCallback = { _, _, answers, _ -> answerHolder = answers },
)

runOnUI { viewHolder.bind(questionnaireViewItem) }

endIconClickInTextInputLayout(R.id.multi_select_summary_holder)
clickOnTextInDialog("Coding 1")
clickOnText("Coding 3")
clickOnText("Coding 5")
clickOnText("Coding Exclusive")
clickOnText("Save")

assertDisplayedText().isEqualTo("Coding Exclusive")
assertQuestionnaireResponseAtIndex(answerHolder!!, "Coding Exclusive")
}

@Test
fun multipleChoice_selectExclusive_selectMultiple_clickSave_shouldSaveWithoutExclusiveOption() {
var answerHolder: List<QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent>? = null
val questionnaireViewItem =
QuestionnaireViewItem(
answerOptions(true, "Coding 1", "Coding 2", "Coding 3", "Coding 4", "Coding 5")
.addAnswerOption(Questionnaire.QuestionnaireItemAnswerOptionComponent().apply {
value = Coding().apply { display = "Coding Exclusive" }
extension = listOf(Extension(EXTENSION_OPTION_EXCLUSIVE_URL, BooleanType(true)))
}),
responseOptions(),
validationResult = NotValidated,
answersChangedCallback = { _, _, answers, _ -> answerHolder = answers },
)

runOnUI { viewHolder.bind(questionnaireViewItem) }

endIconClickInTextInputLayout(R.id.multi_select_summary_holder)
clickOnTextInDialog("Coding Exclusive")
clickOnText("Coding 1")
clickOnText("Coding 3")
clickOnText("Coding 5")
clickOnText("Save")

assertDisplayedText().isEqualTo("Coding 1, Coding 3, Coding 5")
assertQuestionnaireResponseAtIndex(answerHolder!!, "Coding 1", "Coding 3", "Coding 5")
}

@Test
fun multipleChoice_multipleOptionExclusive_selectMultiple_selectExclusive1_selectExclusive2_clickSave_shouldSaveOnlyLastSelectedExclusiveOption() {
var answerHolder: List<QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent>? = null
val questionnaireViewItem =
QuestionnaireViewItem(
answerOptions(true, "Coding 1", "Coding 2", "Coding 3")
.addAnswerOption(Questionnaire.QuestionnaireItemAnswerOptionComponent().apply {
value = Coding().apply { display = "Coding Exclusive 1" }
extension = listOf(Extension(EXTENSION_OPTION_EXCLUSIVE_URL, BooleanType(true)))
})
.addAnswerOption(Questionnaire.QuestionnaireItemAnswerOptionComponent().apply {
value = Coding().apply { display = "Coding Exclusive 2" }
extension = listOf(Extension(EXTENSION_OPTION_EXCLUSIVE_URL, BooleanType(true)))
}),
responseOptions(),
validationResult = NotValidated,
answersChangedCallback = { _, _, answers, _ -> answerHolder = answers },
)

runOnUI { viewHolder.bind(questionnaireViewItem) }

endIconClickInTextInputLayout(R.id.multi_select_summary_holder)
clickOnTextInDialog("Coding 1")
clickOnText("Coding 3")
clickOnText("Coding Exclusive 1")
clickOnText("Coding Exclusive 2")
clickOnText("Save")

assertDisplayedText().isEqualTo("Coding Exclusive 2")
assertQuestionnaireResponseAtIndex(answerHolder!!, "Coding Exclusive 2")
}

@Test
fun multipleChoice_multipleOptionExclusive_selectExclusive1_selectExclusive2_selectMultiple_clickSave_shouldSaveWithoutAnyExclusiveOption() {
var answerHolder: List<QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent>? = null
val questionnaireViewItem =
QuestionnaireViewItem(
answerOptions(true, "Coding 1", "Coding 2", "Coding 3")
.addAnswerOption(Questionnaire.QuestionnaireItemAnswerOptionComponent().apply {
value = Coding().apply { display = "Coding Exclusive 1" }
extension = listOf(Extension(EXTENSION_OPTION_EXCLUSIVE_URL, BooleanType(true)))
})
.addAnswerOption(Questionnaire.QuestionnaireItemAnswerOptionComponent().apply {
value = Coding().apply { display = "Coding Exclusive 2" }
extension = listOf(Extension(EXTENSION_OPTION_EXCLUSIVE_URL, BooleanType(true)))
}),
responseOptions(),
validationResult = NotValidated,
answersChangedCallback = { _, _, answers, _ -> answerHolder = answers },
)

runOnUI { viewHolder.bind(questionnaireViewItem) }

endIconClickInTextInputLayout(R.id.multi_select_summary_holder)
clickOnTextInDialog("Coding Exclusive 1")
clickOnTextInDialog("Coding Exclusive 2")
clickOnText("Coding 1")
clickOnText("Coding 3")
clickOnText("Save")

assertDisplayedText().isEqualTo("Coding 1, Coding 3")
assertQuestionnaireResponseAtIndex(answerHolder!!, "Coding 1", "Coding 3")
}

@Test
fun multipleChoice_SelectNothing_clickSave_shouldSaveNothing() {
val questionnaireViewItem =
Expand Down Expand Up @@ -364,6 +486,85 @@ class QuestionnaireItemDialogMultiSelectViewHolderFactoryEspressoTest {
onView(withId(R.id.add_another)).check(matches(isDisplayed()))
}

@Test
@SdkSuppress(minSdkVersion = 33)
fun selectOther_selectExclusive_shouldHideAddAnotherAnswer() {
val questionnaireItem =
answerOptions(
true,
"Coding 1",
"Coding 2",
"Coding 3",
"Coding 4",
"Coding 5",
"Coding 6",
"Coding 7",
"Coding 8",
).addAnswerOption(Questionnaire.QuestionnaireItemAnswerOptionComponent().apply {
value = Coding().apply { display = "Coding Exclusive" }
extension = listOf(Extension(EXTENSION_OPTION_EXCLUSIVE_URL, BooleanType(true)))
})

questionnaireItem.addExtension(openChoiceType)
val questionnaireViewItem =
QuestionnaireViewItem(
questionnaireItem,
responseOptions(),
validationResult = NotValidated,
answersChangedCallback = { _, _, _, _ -> },
)

runOnUI { viewHolder.bind(questionnaireViewItem) }

endIconClickInTextInputLayout(R.id.multi_select_summary_holder)
onView(withId(R.id.recycler_view))
.perform(RecyclerViewActions.scrollToPosition<RecyclerView.ViewHolder>(9))
clickOnTextInDialog("Other")
clickOnTextInDialog("Coding Exclusive")
onView(withId(R.id.add_another)).check(doesNotExist())
}

@Test
@SdkSuppress(minSdkVersion = 33)
fun selectOther_clickAddAnotherAnswer_selectExclusive_shouldHideAddAnotherAnswerWithEditText() {
val questionnaireItem =
answerOptions(
true,
"Coding 1",
"Coding 2",
"Coding 3",
"Coding 4",
"Coding 5",
"Coding 6",
"Coding 7",
"Coding 8",
).addAnswerOption(Questionnaire.QuestionnaireItemAnswerOptionComponent().apply {
value = Coding().apply { display = "Coding Exclusive" }
extension = listOf(Extension(EXTENSION_OPTION_EXCLUSIVE_URL, BooleanType(true)))
})

questionnaireItem.addExtension(openChoiceType)
val questionnaireViewItem =
QuestionnaireViewItem(
questionnaireItem,
responseOptions(),
validationResult = NotValidated,
answersChangedCallback = { _, _, _, _ -> },
)

runOnUI { viewHolder.bind(questionnaireViewItem) }

endIconClickInTextInputLayout(R.id.multi_select_summary_holder)
onView(withId(R.id.recycler_view))
.perform(RecyclerViewActions.scrollToPosition<RecyclerView.ViewHolder>(9))
clickOnTextInDialog("Other")
onView(withId(R.id.add_another)).perform(delayMainThread())
onView(withId(R.id.add_another)).perform(click())
clickOnTextInDialog("Coding Exclusive")
onView(withId(R.id.add_another)).check(doesNotExist())
onView(withId(R.id.edit_text)).check(doesNotExist())
}

@Test
fun shouldHideErrorTextviewInHeader() {
val questionnaireItem = answerOptions(true, "Coding 1")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.google.android.fhir.datacapture.R
import com.google.android.fhir.datacapture.extensions.itemAnswerOptionImage
import com.google.android.fhir.datacapture.extensions.optionExclusive
import com.google.android.fhir.datacapture.views.factories.OptionSelectOption
import com.google.android.fhir.datacapture.views.factories.QuestionnaireItemDialogSelectViewModel
import com.google.android.fhir.datacapture.views.factories.SelectedOptions
Expand Down Expand Up @@ -263,6 +264,8 @@ private class OptionSelectAdapter(val multiSelectEnabled: Boolean) :
* if "Other" was just deselected, or adding them if "Other" was just selected).
*/
private fun submitSelectedChange(position: Int, selected: Boolean) {
val selectedItem = currentList[position]

val newList: List<OptionSelectRow> =
currentList
.mapIndexed { index, row ->
Expand All @@ -272,8 +275,16 @@ private class OptionSelectAdapter(val multiSelectEnabled: Boolean) :
} else {
// This is some other row
if (multiSelectEnabled) {
// In multi-select mode, the other rows don't need to change
row
// In multi-select mode,
if (selected && ((selectedItem is OptionSelectRow.Option && selectedItem.option.item.optionExclusive)
|| (row is OptionSelectRow.Option && row.option.item.optionExclusive))) {
// if the selected answer option has optionExclusive extension, then deselect other answer options.
// or if the selected answer option does not have optionExclusive extension, then deselect optionExclusive answer option.
row.withSelectedState(selected = false) ?: row
} else {
// the other rows don't need to change
row
}
} else {
// In single-select mode, we need to disable all of the other rows
row.withSelectedState(selected = false) ?: row
Expand Down
Loading