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

Highlight missing fields and Show validation popup. #1671

Merged
merged 29 commits into from
Dec 4, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
219cc76
Show validation popup for missing fields
aditya-07 Oct 17, 2022
8914ba4
Merge branch 'master' into ak/highlight_required_fields
aditya-07 Oct 18, 2022
f370866
Fixed formatting
aditya-07 Oct 18, 2022
8eedb30
Merge branch 'master' into ak/highlight_required_fields
aditya-07 Oct 20, 2022
d174025
Review comments fix
aditya-07 Oct 28, 2022
96804f8
Merge branch 'master' into ak/highlight_required_fields
aditya-07 Oct 28, 2022
4982a4a
Merge branch 'master' into ak/highlight_required_fields
omarismail94 Nov 2, 2022
2ff3b8f
Merge branch 'master' into ak/highlight_required_fields
aditya-07 Nov 3, 2022
a086873
Merge branch 'master' into ak/highlight_required_fields
aditya-07 Nov 4, 2022
90abcb5
Merge branch 'master' into ak/highlight_required_fields
aditya-07 Nov 9, 2022
8b0bda4
Merge branch 'master' into ak/highlight_required_fields
aditya-07 Nov 16, 2022
3087e4a
Added companion object to ErrorMessage dialog fragment
aditya-07 Nov 16, 2022
e3bd8b2
Merge branch 'master' into ak/highlight_required_fields
aditya-07 Nov 18, 2022
e1eee19
Merge branch 'master' into ak/highlight_required_fields
aditya-07 Nov 24, 2022
3d59989
Review changes
aditya-07 Nov 28, 2022
7809af0
Merge branch 'master' into ak/highlight_required_fields
aditya-07 Dec 1, 2022
6d54060
Review Comments: Removed recycler view from popup.
aditya-07 Dec 2, 2022
f146dd9
Merge branch 'master' into ak/highlight_required_fields
aditya-07 Dec 2, 2022
2e9d889
Updated margin for errors
aditya-07 Dec 2, 2022
9f59b1d
Added scrollview for the dialog body.
aditya-07 Dec 2, 2022
91dec20
Update text
jingtang10 Dec 2, 2022
8c42962
Remove unnecessary import
jingtang10 Dec 2, 2022
b9c7d62
Merge branch 'master' into ak/highlight_required_fields
jingtang10 Dec 2, 2022
025f44c
Fix imports
jingtang10 Dec 2, 2022
d7ea140
Merge branch 'master' into ak/highlight_required_fields
jingtang10 Dec 4, 2022
fed6492
Fix errors
jingtang10 Dec 4, 2022
3122a3f
Merge branch 'ak/highlight_required_fields' of https://github.com/adi…
jingtang10 Dec 4, 2022
88096b5
Run spotless apply
jingtang10 Dec 4, 2022
f10166c
Fix missing import
jingtang10 Dec 4, 2022
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
Prev Previous commit
Next Next commit
Review changes
  • Loading branch information
aditya-07 committed Nov 28, 2022
commit 3d5998933db966d2825ab20e7b62b8815b3002a2
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,7 @@ open class QuestionnaireFragment : Fragment() {
paginationNextButton.setOnClickListener { viewModel.goToNextPage() }
view.findViewById<Button>(R.id.submit_questionnaire).setOnClickListener {
viewModel.validateQuestionnaireAndUpdateUI().let { validationMap ->
if (validationMap
.filter { (_, validations) -> validations.filterIsInstance<Invalid>().isNotEmpty() }
.isEmpty()
) {
if (validationMap.values.flatten().filterIsInstance<Invalid>().isEmpty()) {
setFragmentResult(SUBMIT_REQUEST_KEY, Bundle.EMPTY)
} else {
val errorViewModel: QuestionnaireValidationErrorViewModel by activityViewModels()
Expand Down Expand Up @@ -178,6 +175,10 @@ open class QuestionnaireFragment : Fragment() {
submitButton.visibility = if (state.pagination.showSubmitButton) View.VISIBLE else View.GONE
}
}
requireActivity().supportFragmentManager.setFragmentResultListener(
QuestionnaireValidationErrorMessageDialogFragment.RESULT_CALLBACK,
viewLifecycleOwner
) { _, _ -> setFragmentResult(SUBMIT_REQUEST_KEY, Bundle.EMPTY) }
}

/** Calculates the progress percentage from given [count] and [totalCount] values. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import androidx.appcompat.view.ContextThemeWrapper
import androidx.core.content.res.use
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.activityViewModels
import androidx.fragment.app.setFragmentResult
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.DiffUtil
Expand All @@ -53,7 +54,14 @@ internal class QuestionnaireValidationErrorMessageDialogFragment(

override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
isCancelable = false
return MaterialAlertDialogBuilder(requireContext()).setView(onCreateCustomView()).create()
return MaterialAlertDialogBuilder(requireContext())
.setView(onCreateCustomView())
.setPositiveButton(android.R.string.ok) { dialog, _ -> dialog?.dismiss() }
.setNegativeButton(R.string.submit_button_text) { dialog, _ ->
setFragmentResult(RESULT_CALLBACK, Bundle.EMPTY)
dialog?.dismiss()
}
.create()
}

@VisibleForTesting
Expand Down Expand Up @@ -84,7 +92,6 @@ internal class QuestionnaireValidationErrorMessageDialogFragment(
adapter =
ErrorAdapter().apply { submitList(viewModel.getItemsTextWithValidationErrors()) }
}
findViewById<View>(R.id.positive_button).setOnClickListener { dialog?.dismiss() }
}
}

Expand Down Expand Up @@ -115,6 +122,7 @@ internal class QuestionnaireValidationErrorMessageDialogFragment(

companion object {
const val TAG = "QuestionnaireValidationErrorMessageDialogFragment"
const val RESULT_CALLBACK = "QuestionnaireValidationResultCallback"
}
}

Expand Down Expand Up @@ -161,4 +169,13 @@ internal class QuestionnaireValidationErrorViewModel : ViewModel() {
}
}

data class ValidationErrorDataModel(val linkId: String, val questionnaireItemText: String)
/**
* Data model for showing validation error of a particular [QuestionnaireItemComponent] in the
* Dialog.
*/
data class ValidationErrorDataModel(
/** Id of the [QuestionnaireItemComponent] with error */
val linkId: String,
/** Text of the [QuestionnaireItemComponent] with error */
val questionnaireItemText: String
)
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ internal class QuestionnaireViewModel(application: Application, state: SavedStat
private var isPaginationButtonPressed = false

/** Forces response validation each time [getQuestionnaireItemViewItems] is called. */
private var forceValidate = false
private var hasPressedSubmitButton = false

init {
when {
Expand Down Expand Up @@ -312,8 +312,8 @@ internal class QuestionnaireViewModel(application: Application, state: SavedStat
}

/**
* Validates questionnaire and return the validation results. As a side effect, it triggers the UI
* update to show errors in case there are any validation errors.
* Validates entire questionnaire and return the validation results. As a side effect, it triggers
* the UI update to show errors in case there are any validation errors.
*/
internal fun validateQuestionnaireAndUpdateUI(): Map<String, List<ValidationResult>> =
QuestionnaireResponseValidator.validateQuestionnaireResponse(
Expand All @@ -323,7 +323,7 @@ internal class QuestionnaireViewModel(application: Application, state: SavedStat
)
.also { result ->
if (result.values.flatten().filterIsInstance<Invalid>().isNotEmpty()) {
forceValidate = true
hasPressedSubmitButton = true
modificationCount.update { it + 1 }
}
}
Expand Down Expand Up @@ -624,7 +624,7 @@ internal class QuestionnaireViewModel(application: Application, state: SavedStat
val validationResult =
if (modifiedQuestionnaireResponseItemSet.contains(questionnaireResponseItem) ||
isPaginationButtonPressed ||
forceValidate
hasPressedSubmitButton
) {
QuestionnaireResponseItemValidator.validate(
questionnaireItem,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
<com.google.android.material.textview.MaterialTextView
<TextView
xmlns:android="https://schemas.android.com/apk/res/android"
xmlns:tools="https://schemas.android.com/tools"
android:id="@+id/text_view"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,16 +47,4 @@
android:layout_weight="1"
tools:listitem="@layout/questionnaire_validation_dialog_item"
/>

<com.google.android.material.button.MaterialButton
android:layout_marginTop="@dimen/padding_modal_card"
android:layout_gravity="right"
android:id="@+id/positive_button"
style="?attr/questionnaireValidationDialogButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="@dimen/padding_half_size"
android:text="@android:string/ok"
/>

</LinearLayout>
10 changes: 5 additions & 5 deletions datacapture/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -44,15 +44,15 @@
<string name="not_answered">"Not Answered"</string>
<string name="help">Help</string>
<string name="space_asterisk">\u0020\u002a</string>
<string
name="questionnaire_validation_error_supporting_text"
>Fields that need to be completed:</string>
<string name="group_header_add_item_button">Add item</string>
<!-- Questionnaire validation popup texts-->
<string
name="questionnaire_validation_error_headline"
>Complete all required fields</string>
<string
name="questionnaire_validation_error_supporting_text"
>Fields that need to be completed:</string>
<string
name="questionnaire_validation_error_item_text_with_bullet"
>• %s</string>
<string name="group_header_add_item_button">Add item</string>

</resources>
Original file line number Diff line number Diff line change
Expand Up @@ -90,41 +90,6 @@ class QuestionnaireValidationErrorMessageDialogFragmentTest {
assertThat(holder.textView.text).isEqualTo("• First Name")
}

@Test
fun `createCustomView with a valid questionnaire response validation result`() {
val questionnaire =
Questionnaire().apply {
url = "questionnaire-1"
addItem(
Questionnaire.QuestionnaireItemComponent().apply {
linkId = "name-id"
text = "First Name"
type = Questionnaire.QuestionnaireItemType.STRING
}
)
}
val questionnaireResponse =
QuestionnaireResponse().apply {
this.questionnaire = "questionnaire-1"
addItem(QuestionnaireResponse.QuestionnaireResponseItemComponent(StringType("name-id")))
}

val scenario =
launchFragmentInContainer<QuestionnaireValidationErrorMessageDialogFragment>(
initialState = Lifecycle.State.CREATED,
factory = createDialogFragmentFactoryForTests(questionnaire, questionnaireResponse)
)

val result = scenario.withFragment { onCreateCustomView() }

assertThat(result.findViewById<TextView>(R.id.dialog_title).text)
.isEqualTo("Complete all required fields")
assertThat(result.findViewById<TextView>(R.id.dialog_subtitle).text)
.isEqualTo("Fields that need to be completed:")
assertThat(result.findViewById<RecyclerView>(R.id.recycler_view).adapter!!.itemCount)
.isEqualTo(0)
}

private fun createTestValidationErrorViewModel(
questionnaire: Questionnaire,
questionnaireResponse: QuestionnaireResponse
Expand All @@ -147,7 +112,7 @@ class QuestionnaireValidationErrorMessageDialogFragmentTest {
val fragmentFactory: FragmentFactory = mock()
val factory =
object : ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return createTestValidationErrorViewModel(questionnaire, questionnaireResponse) as T
}
}
Expand Down