diff --git a/app/src/main/java/org/dhis2/usescases/datasets/dataSetTable/dataSetSection/DataValuePresenter.kt b/app/src/main/java/org/dhis2/usescases/datasets/dataSetTable/dataSetSection/DataValuePresenter.kt index 5182a469eaf..e953a7820e2 100644 --- a/app/src/main/java/org/dhis2/usescases/datasets/dataSetTable/dataSetSection/DataValuePresenter.kt +++ b/app/src/main/java/org/dhis2/usescases/datasets/dataSetTable/dataSetSection/DataValuePresenter.kt @@ -153,6 +153,7 @@ class DataValuePresenter( id = cell.id ?: "", mainLabel = dataElement?.displayFormName() ?: "-", secondaryLabels = repository.getCatOptComboOptions(ids[1]), + helperText = dataElement?.description(), currentValue = cell.value, keyboardInputType = inputType, error = errors[cell.id], diff --git a/compose-table/src/androidTest/java/org/dhis2/composetable/TableRobot.kt b/compose-table/src/androidTest/java/org/dhis2/composetable/TableRobot.kt index e1a4e08a170..43e8955d4c0 100644 --- a/compose-table/src/androidTest/java/org/dhis2/composetable/TableRobot.kt +++ b/compose-table/src/androidTest/java/org/dhis2/composetable/TableRobot.kt @@ -41,6 +41,7 @@ import org.dhis2.composetable.ui.DataSetTableScreen import org.dhis2.composetable.ui.DataTable import org.dhis2.composetable.ui.DrawableId import org.dhis2.composetable.ui.INPUT_ERROR_MESSAGE_TEST_TAG +import org.dhis2.composetable.ui.INPUT_HELPER_TEXT_TEST_TAG import org.dhis2.composetable.ui.INPUT_ICON_TEST_TAG import org.dhis2.composetable.ui.INPUT_TEST_FIELD_TEST_TAG import org.dhis2.composetable.ui.INPUT_TEST_TAG @@ -126,6 +127,7 @@ class TableRobot( fakeModelType: FakeModelType, tableAppScreenOptions: TableAppScreenOptions = TableAppScreenOptions(), tableConfiguration: TableConfiguration = TableConfiguration(headerActionsEnabled = true), + helperText: String? = null, onSave: (TableCell) -> Unit = {} ): List { var fakeModel: List = emptyList() @@ -152,6 +154,7 @@ class TableRobot( secondaryLabels = fakeModel.find { it.id == tableId }?.tableHeaderModel?.rows?.map { it.cells[cell.column!! % it.cells.size].value } ?: emptyList(), + helperText = helperText, currentValue = cell.value, keyboardInputType = KeyboardInputType.TextInput(), error = null @@ -345,6 +348,12 @@ class TableRobot( .assertTextEquals(expectedErrorMessage) } + fun assertInputComponentHelperTextIsDisplayed(expectedHelperText: String) { + composeTestRule.onNodeWithTag(INPUT_HELPER_TEXT_TEST_TAG) + .assertIsDisplayed() + .assertTextEquals(expectedHelperText) + } + fun assertCellWithErrorSetsErrorMessage( rowIndex: Int, columnIndex: Int, diff --git a/compose-table/src/androidTest/java/org/dhis2/composetable/data/TableInputUiData.kt b/compose-table/src/androidTest/java/org/dhis2/composetable/data/TableInputUiData.kt index 4919beb2bdd..26f8e71d3f2 100644 --- a/compose-table/src/androidTest/java/org/dhis2/composetable/data/TableInputUiData.kt +++ b/compose-table/src/androidTest/java/org/dhis2/composetable/data/TableInputUiData.kt @@ -39,7 +39,8 @@ val tableData = listOf( ), values = mapOf( Pair(0, TableCell("00", 0, 0, "12")), - Pair(1, TableCell("01", 0, 1, value = "-1", error = input_error_message)) + Pair(1, TableCell("01", 0, 1, value = "-1", error = input_error_message)), + Pair(2, TableCell("02", 0, 2, "text")), ), ), TableRowModel( diff --git a/compose-table/src/androidTest/java/org/dhis2/composetable/ui/TextInputUiTest.kt b/compose-table/src/androidTest/java/org/dhis2/composetable/ui/TextInputUiTest.kt index 66934bfb92f..5cb163c10ba 100644 --- a/compose-table/src/androidTest/java/org/dhis2/composetable/ui/TextInputUiTest.kt +++ b/compose-table/src/androidTest/java/org/dhis2/composetable/ui/TextInputUiTest.kt @@ -77,9 +77,26 @@ class TextInputUiTest { } } + @Test + fun shouldDisplayHelperText() { + val helperText = "This is a helper Text" + + tableRobot(composeTestRule) { + val fakeModels = initTableAppScreen( + FakeModelType.MANDATORY_TABLE, + helperText = helperText + ) + clickOnCell(fakeModels.first().id!!, 0, 0) + assertInputComponentHelperTextIsDisplayed(helperText) + } + } + @OptIn(ExperimentalMaterialApi::class) @Composable - private fun TextInputUiTestScreen(onSave: (TableCell) -> Unit) { + private fun TextInputUiTestScreen( + helperText: String? = null, + onSave: (TableCell) -> Unit + ) { val bottomSheetState = rememberBottomSheetScaffoldState( bottomSheetState = rememberBottomSheetState(initialValue = BottomSheetValue.Collapsed) ) @@ -90,7 +107,9 @@ class TextInputUiTest { } var currentInputType by remember { mutableStateOf( - TextInputModel() + TextInputModel( + helperText = helperText + ) ) } diff --git a/compose-table/src/main/java/org/dhis2/composetable/model/TextInputModel.kt b/compose-table/src/main/java/org/dhis2/composetable/model/TextInputModel.kt index 28d51cc9b6f..fc3d10d08f9 100644 --- a/compose-table/src/main/java/org/dhis2/composetable/model/TextInputModel.kt +++ b/compose-table/src/main/java/org/dhis2/composetable/model/TextInputModel.kt @@ -6,6 +6,7 @@ data class TextInputModel( val id: String = "", val mainLabel: String = "", val secondaryLabels: List = emptyList(), + val helperText: String? = null, val currentValue: String? = null, val keyboardInputType: KeyboardInputType = KeyboardInputType.TextInput(), val selection: TextRange? = null, @@ -18,4 +19,6 @@ data class TextInputModel( fun hasErrorOrWarning() = errorOrWarningMessage() != null fun actionIconCanBeClicked(hasFocus: Boolean) = hasFocus && error == null + + fun hasHelperText() = helperText?.isNotEmpty() ?: false } diff --git a/compose-table/src/main/java/org/dhis2/composetable/ui/TextInput.kt b/compose-table/src/main/java/org/dhis2/composetable/ui/TextInput.kt index 545daa3c6fe..61c1b6f3d1a 100644 --- a/compose-table/src/main/java/org/dhis2/composetable/ui/TextInput.kt +++ b/compose-table/src/main/java/org/dhis2/composetable/ui/TextInput.kt @@ -4,6 +4,7 @@ import android.graphics.Rect import android.view.ViewTreeObserver import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Arrangement.Absolute.spacedBy import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer @@ -72,6 +73,7 @@ fun TextInput( shape = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp), ) .padding(start = 16.dp, end = 4.dp, top = 16.dp, bottom = 4.dp), + verticalArrangement = spacedBy(8.dp), ) { InputTitle(textInputModel.mainLabel, textInputModel.secondaryLabels) TextInputContent( @@ -252,6 +254,17 @@ private fun TextInputContent( ), ) } + if (textInputModel.hasHelperText()) { + Text( + modifier = Modifier + .testTag(INPUT_HELPER_TEXT_TEST_TAG), + text = textInputModel.helperText!!, + style = TextStyle( + color = LocalTableColors.current.headerText, + ), + fontSize = 10.sp, + ) + } } } @@ -335,6 +348,26 @@ fun DefaultTextInputStatusPreview() { id = "", mainLabel = "Row", secondaryLabels = listOf("header 1", "header 2"), + helperText = "description", + currentValue = "Test", + ) + + TextInput( + textInputModel = previewTextInput, + textInputInteractions = object : TextInputInteractions {}, + focusRequester = FocusRequester(), + ) +} + +@Preview +@Composable +fun DefaultTextInputErrorStatusPreview() { + val previewTextInput = TextInputModel( + id = "", + mainLabel = "Row", + secondaryLabels = listOf("header 1", "header 2"), + error = "error message", + helperText = "description", currentValue = "Test", ) @@ -349,6 +382,7 @@ const val INPUT_TEST_TAG = "INPUT_TEST_TAG" const val INPUT_TEST_FIELD_TEST_TAG = "INPUT_TEST_FIELD_TEST_TAG" const val INPUT_ERROR_MESSAGE_TEST_TAG = "INPUT_ERROR_MESSAGE_TEST_TAG" const val INPUT_ICON_TEST_TAG = "INPUT_ICON_TEST_TAG" +const val INPUT_HELPER_TEXT_TEST_TAG = "INPUT_HELPER_TEXT_TEST_TAG" val DrawableId = SemanticsPropertyKey("DrawableResId") var SemanticsPropertyReceiver.drawableId by DrawableId diff --git a/stock-usecase/src/main/java/org/dhis2/android/rtsm/ui/home/HomeActivity.kt b/stock-usecase/src/main/java/org/dhis2/android/rtsm/ui/home/HomeActivity.kt index 7539542dd2d..d4e85b7d86d 100644 --- a/stock-usecase/src/main/java/org/dhis2/android/rtsm/ui/home/HomeActivity.kt +++ b/stock-usecase/src/main/java/org/dhis2/android/rtsm/ui/home/HomeActivity.kt @@ -62,6 +62,8 @@ class HomeActivity : AppCompatActivity() { setContent { val settingsUiState by viewModel.settingsUiState.collectAsState() + val helperText by viewModel.helperText.collectAsState() + manageStockViewModel.setHelperText(helperText) updateTheme(settingsUiState.selectedTransactionItem.type) manageStockViewModel.setThemeColor(Color(colorResource(themeColor).toArgb())) MdcTheme { diff --git a/stock-usecase/src/main/java/org/dhis2/android/rtsm/ui/home/HomeViewModel.kt b/stock-usecase/src/main/java/org/dhis2/android/rtsm/ui/home/HomeViewModel.kt index 0e7f387a363..a51d25224cd 100644 --- a/stock-usecase/src/main/java/org/dhis2/android/rtsm/ui/home/HomeViewModel.kt +++ b/stock-usecase/src/main/java/org/dhis2/android/rtsm/ui/home/HomeViewModel.kt @@ -8,6 +8,7 @@ import dagger.hilt.android.lifecycle.HiltViewModel import io.reactivex.disposables.CompositeDisposable import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.update import org.dhis2.android.rtsm.R import org.dhis2.android.rtsm.commons.Constants.INTENT_EXTRA_APP_CONFIG @@ -54,6 +55,9 @@ class HomeViewModel @Inject constructor( private val _settingsUiSate = MutableStateFlow(SettingsUiState(programUid = config.program, transactionItems = transactionItems)) val settingsUiState: StateFlow = _settingsUiSate + private val _helperText = MutableStateFlow(null) + val helperText = _helperText.asStateFlow() + init { loadFacilities() loadDestinations() @@ -100,6 +104,7 @@ class HomeViewModel @Inject constructor( .observeOn(schedulerProvider.ui()) .subscribe( { dataElement -> + _helperText.value = dataElement.description() transactionItems.find { it.type == TransactionType.CORRECTION }?.label = dataElement.displayName() ?: TransactionType.CORRECTION.name _settingsUiSate.update { currentUiState -> currentUiState.copy(transactionItems = transactionItems) diff --git a/stock-usecase/src/main/java/org/dhis2/android/rtsm/ui/managestock/ManageStockViewModel.kt b/stock-usecase/src/main/java/org/dhis2/android/rtsm/ui/managestock/ManageStockViewModel.kt index 896dc1fda08..d0656a03667 100644 --- a/stock-usecase/src/main/java/org/dhis2/android/rtsm/ui/managestock/ManageStockViewModel.kt +++ b/stock-usecase/src/main/java/org/dhis2/android/rtsm/ui/managestock/ManageStockViewModel.kt @@ -116,6 +116,8 @@ class ManageStockViewModel @Inject constructor( val tableConfigurationState: StateFlow = _tableConfigurationState + private var inputHelperText: String? = null + init { configureRelays() } @@ -321,6 +323,7 @@ class ManageStockViewModel @Inject constructor( id = cell.id ?: "", mainLabel = itemName, secondaryLabels = mutableListOf(resources.getString(R.string.quantity)), + helperText = inputHelperText, currentValue = cell.value, keyboardInputType = KeyboardInputType.NumberPassword(), error = stockEntry?.errorMessage, @@ -591,6 +594,10 @@ class ManageStockViewModel @Inject constructor( _bottomSheetState.value = false } + fun setHelperText(text: String?) { + inputHelperText = text + } + private fun refreshTableConfiguration() = TableConfigurationState( overwrittenTableWidth = tableDimensionStore.getTableWidth(), overwrittenRowHeaderWidth = tableDimensionStore.getWidthForSection(),