diff --git a/common/src/commonMain/kotlin/org/hisp/dhis/common/screens/toggleableInputs/InputDropDownScreen.kt b/common/src/commonMain/kotlin/org/hisp/dhis/common/screens/toggleableInputs/InputDropDownScreen.kt index 30825a3be..ccec61683 100644 --- a/common/src/commonMain/kotlin/org/hisp/dhis/common/screens/toggleableInputs/InputDropDownScreen.kt +++ b/common/src/commonMain/kotlin/org/hisp/dhis/common/screens/toggleableInputs/InputDropDownScreen.kt @@ -147,5 +147,25 @@ fun InputDropDownScreen() { selectedItem = selectedItem3, ) Spacer(Modifier.size(Spacing.Spacing18)) + + SubTitle("Input Dropdown with 5000 items ", textColor = TextColor.OnSurfaceVariant) + val dropdownItems = mutableListOf() + for (i in 1..5000) { + dropdownItems.add(DropdownItem("$i")) + } + + InputDropDown( + title = "Label", + state = InputShellState.UNFOCUSED, + dropdownItems = dropdownItems, + onResetButtonClicked = { + selectedItem = null + }, + onItemSelected = { + selectedItem = it + }, + selectedItem = selectedItem, + ) + Spacer(Modifier.size(Spacing.Spacing18)) } } diff --git a/common/src/commonMain/kotlin/org/hisp/dhis/common/screens/toggleableInputs/MultiSelectInputScreen.kt b/common/src/commonMain/kotlin/org/hisp/dhis/common/screens/toggleableInputs/MultiSelectInputScreen.kt index 4acd4ff2f..c00ae4cdf 100644 --- a/common/src/commonMain/kotlin/org/hisp/dhis/common/screens/toggleableInputs/MultiSelectInputScreen.kt +++ b/common/src/commonMain/kotlin/org/hisp/dhis/common/screens/toggleableInputs/MultiSelectInputScreen.kt @@ -186,5 +186,35 @@ fun MultiSelectInputScreen() { legendData = null, supportingTextData = null, ) + + val multiSelectItems = mutableListOf() + for (i in 1..5000) { + multiSelectItems.add( + CheckBoxData( + uid = "uid-$i", + checked = i == 2, + enabled = true, + textInput = "Opt. $i", + ), + ) + } + + InputMultiSelection( + items = multiSelectItems, + title = "Multi Select more than 5000 items", + state = InputShellState.UNFOCUSED, + onItemsSelected = { selectedItems -> + selectedItems.forEach { selectedItem -> + val index = multiSelectItems.indexOfFirst { it.uid == selectedItem.uid } + multiSelectItems[index] = selectedItem + } + }, + onClearItemSelection = { + multiSelectItems.replaceAll { it.copy(checked = false) } + }, + isRequired = false, + legendData = null, + supportingTextData = null, + ) } } diff --git a/designsystem/src/commonMain/kotlin/org/hisp/dhis/mobile/ui/designsystem/component/InputDropDown.kt b/designsystem/src/commonMain/kotlin/org/hisp/dhis/mobile/ui/designsystem/component/InputDropDown.kt index 6ce7138a2..481596b48 100644 --- a/designsystem/src/commonMain/kotlin/org/hisp/dhis/mobile/ui/designsystem/component/InputDropDown.kt +++ b/designsystem/src/commonMain/kotlin/org/hisp/dhis/mobile/ui/designsystem/component/InputDropDown.kt @@ -37,6 +37,7 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp +import org.hisp.dhis.mobile.ui.designsystem.resource.provideStringResource import org.hisp.dhis.mobile.ui.designsystem.theme.DHIS2SCustomTextStyles import org.hisp.dhis.mobile.ui.designsystem.theme.Spacing.Spacing0 import org.hisp.dhis.mobile.ui.designsystem.theme.Spacing.Spacing16 @@ -45,6 +46,7 @@ import org.hisp.dhis.mobile.ui.designsystem.theme.SurfaceColor import org.hisp.dhis.mobile.ui.designsystem.theme.TextColor private const val MAX_DROPDOWN_ITEMS = 6 +private const val MAX_DROPDOWN_ITEMS_TO_SHOW = 50 /** * DHIS2 Input dropdown. Wraps DHIS ยท [DropdownInputField]. @@ -81,7 +83,8 @@ fun InputDropDown( onResetButtonClicked: () -> Unit, onItemSelected: (DropdownItem) -> Unit, showSearchBar: Boolean = true, - noResultsFoundString: String = "No results found", + noResultsFoundString: String = provideStringResource("no_results_found"), + searchToFindMoreString: String = provideStringResource("search_to_see_more"), ) { val focusRequester = remember { FocusRequester() } var showDropdown by remember { mutableStateOf(false) } @@ -113,8 +116,12 @@ fun InputDropDown( if (showDropdown) { var searchQuery by remember { mutableStateOf("") } - val filteredOptions = - dropdownItems.filter { it.label.contains(searchQuery, ignoreCase = true) } + var filteredOptions = dropdownItems + + if (searchQuery.isNotEmpty()) { + filteredOptions = + dropdownItems.filter { it.label.contains(searchQuery, ignoreCase = true) } + } BottomSheetShell( modifier = Modifier.testTag("INPUT_DROPDOWN_BOTTOM_SHEET"), @@ -126,16 +133,27 @@ fun InputDropDown( .padding(top = Spacing8), ) { if (filteredOptions.isNotEmpty()) { - filteredOptions.forEachIndexed { index, item -> - DropdownItem( - modifier = Modifier.testTag("INPUT_DROPDOWN_BOTTOM_SHEET_ITEM_$index"), - item = item, - selected = selectedItem == item, - contentPadding = PaddingValues(Spacing8), - onItemClick = { - onItemSelected(item) - showDropdown = false - }, + filteredOptions + .take(MAX_DROPDOWN_ITEMS_TO_SHOW) + .forEachIndexed { index, item -> + DropdownItem( + modifier = Modifier.testTag("INPUT_DROPDOWN_BOTTOM_SHEET_ITEM_$index"), + item = item, + selected = selectedItem == item, + contentPadding = PaddingValues(Spacing8), + onItemClick = { + onItemSelected(item) + showDropdown = false + }, + ) + } + if (filteredOptions.size > MAX_DROPDOWN_ITEMS_TO_SHOW) { + Text( + text = searchToFindMoreString, + textAlign = TextAlign.Center, + modifier = Modifier + .fillMaxWidth() + .padding(24.dp), ) } } else { diff --git a/designsystem/src/commonMain/kotlin/org/hisp/dhis/mobile/ui/designsystem/component/InputMultiSelection.kt b/designsystem/src/commonMain/kotlin/org/hisp/dhis/mobile/ui/designsystem/component/InputMultiSelection.kt index b7beb4570..676ea0bb3 100644 --- a/designsystem/src/commonMain/kotlin/org/hisp/dhis/mobile/ui/designsystem/component/InputMultiSelection.kt +++ b/designsystem/src/commonMain/kotlin/org/hisp/dhis/mobile/ui/designsystem/component/InputMultiSelection.kt @@ -40,6 +40,7 @@ import org.hisp.dhis.mobile.ui.designsystem.theme.Spacing import org.hisp.dhis.mobile.ui.designsystem.theme.SurfaceColor private const val INLINE_CHECKBOXES_MIN_REQ_ITEMS = 6 +private const val MAX_CHECKBOXES_ITEMS_TO_SHOW = 50 /** * DHIS2 input multi selection component. @@ -69,6 +70,7 @@ fun InputMultiSelection( onItemsSelected: (List) -> Unit, modifier: Modifier = Modifier, noResultsFoundString: String = provideStringResource("no_results_found"), + searchToFindMoreString: String = provideStringResource("search_to_see_more"), doneButtonText: String = provideStringResource("done"), inputStyle: InputStyle = InputStyle.DataInputStyle(), onClearItemSelection: () -> Unit, @@ -224,6 +226,7 @@ fun InputMultiSelection( items = items, title = title, noResultsFoundString = noResultsFoundString, + searchToFindMoreString = searchToFindMoreString, doneButtonText = doneButtonText, onItemsSelected = { onItemsSelected(it) @@ -268,6 +271,7 @@ fun MultiSelectBottomSheet( items: List, title: String, noResultsFoundString: String, + searchToFindMoreString: String, doneButtonText: String, onItemsSelected: (List) -> Unit, onDismiss: () -> Unit, @@ -289,19 +293,30 @@ fun MultiSelectBottomSheet( .padding(top = Spacing.Spacing8), ) { if (filteredOptions.isNotEmpty()) { - filteredOptions.forEachIndexed { index, item -> - CheckBox( - checkBoxData = item.copy( - textInput = bottomSheetItemLabel( - text = item.textInput!!, - searchQuery = searchQuery, + filteredOptions + .take(MAX_CHECKBOXES_ITEMS_TO_SHOW) + .forEachIndexed { index, item -> + CheckBox( + checkBoxData = item.copy( + textInput = bottomSheetItemLabel( + text = item.textInput!!, + searchQuery = searchQuery, + ), ), - ), - onCheckedChange = { - filteredOptions[index] = item.copy(checked = it) - itemsModified[index] = item.copy(checked = it) - }, - modifier = Modifier.fillMaxWidth(), + onCheckedChange = { + filteredOptions[index] = item.copy(checked = it) + itemsModified[index] = item.copy(checked = it) + }, + modifier = Modifier.fillMaxWidth(), + ) + } + if (filteredOptions.size > MAX_CHECKBOXES_ITEMS_TO_SHOW) { + Text( + text = searchToFindMoreString, + textAlign = TextAlign.Center, + modifier = Modifier + .fillMaxWidth() + .padding(24.dp), ) } } else { diff --git a/designsystem/src/commonMain/resources/values/strings_en.xml b/designsystem/src/commonMain/resources/values/strings_en.xml index 093a4be2c..4696574ef 100644 --- a/designsystem/src/commonMain/resources/values/strings_en.xml +++ b/designsystem/src/commonMain/resources/values/strings_en.xml @@ -40,4 +40,5 @@ Select date Date out of range Incorrect time format + Not all options are displayed.\n Search to see more. diff --git a/designsystem/src/desktopTest/kotlin/org/hisp/dhis/mobile/ui/designsystem/component/InputDropDownTest.kt b/designsystem/src/desktopTest/kotlin/org/hisp/dhis/mobile/ui/designsystem/component/InputDropDownTest.kt index 115e8e3f1..853a4e499 100644 --- a/designsystem/src/desktopTest/kotlin/org/hisp/dhis/mobile/ui/designsystem/component/InputDropDownTest.kt +++ b/designsystem/src/desktopTest/kotlin/org/hisp/dhis/mobile/ui/designsystem/component/InputDropDownTest.kt @@ -397,4 +397,48 @@ class InputDropDownTest { rule.onNodeWithTag("INPUT_DROPDOWN_BOTTOM_SHEET").assertDoesNotExist() assert(selectedItem == dropdownItems[2]) } + + @Test + fun shouldShowSearchForMoreOptionTextWhenMoreThan50Option() { + val dropdownItems = mutableListOf() + for (i in 1..100) { + dropdownItems.add( + DropdownItem("Option $i"), + ) + } + + val searchSemantics = "Search" + + rule.setContent { + InputDropDown( + title = "Label", + dropdownItems = dropdownItems, + supportingTextData = listOf(SupportingTextData("Supporting text", SupportingTextState.DEFAULT)), + state = InputShellState.UNFOCUSED, + onResetButtonClicked = {}, + onItemSelected = {}, + ) + } + rule.onNodeWithTag("INPUT_DROPDOWN").assertExists() + rule.onNodeWithTag("INPUT_DROPDOWN_ARROW_BUTTON").performClick() + rule.onNodeWithTag("INPUT_DROPDOWN_BOTTOM_SHEET").assertExists() + rule.onNodeWithTag("INPUT_DROPDOWN_BOTTOM_SHEET_ITEMS").onChildren().assertCountEquals(51) + rule.onNodeWithContentDescription(searchSemantics).assertExists() + + rule.onNodeWithTag("INPUT_DROPDOWN_BOTTOM_SHEET_ITEMS").onChildren().assertCountEquals(51) + rule.onNodeWithText("Not all options are displayed.\\n Search to see more.").assertExists() + + // Search + rule.onNodeWithContentDescription(searchSemantics).performTextInput("5") + rule.onNodeWithTag("INPUT_DROPDOWN_BOTTOM_SHEET_ITEMS").onChildren().assertCountEquals(19) + rule.onNodeWithText("Not all options are displayed.\\n Search to see more.").assertDoesNotExist() + + rule.onNodeWithContentDescription(searchSemantics).performTextInput("55") + rule.onNodeWithTag("INPUT_DROPDOWN_BOTTOM_SHEET_ITEMS").onChildren().assertCountEquals(1) + rule.onNodeWithText("Not all options are displayed.\\n Search to see more.").assertDoesNotExist() + + rule.onNodeWithContentDescription(searchSemantics).performTextInput("555") + rule.onNodeWithTag("INPUT_DROPDOWN_BOTTOM_SHEET_ITEMS").onChildren().assertCountEquals(1) + rule.onNodeWithText("No results found").assertExists() + } }