diff --git a/common/src/commonMain/kotlin/org/hisp/dhis/common/App.kt b/common/src/commonMain/kotlin/org/hisp/dhis/common/App.kt index 36503acb9..dcca4ba45 100644 --- a/common/src/commonMain/kotlin/org/hisp/dhis/common/App.kt +++ b/common/src/commonMain/kotlin/org/hisp/dhis/common/App.kt @@ -24,6 +24,7 @@ import org.hisp.dhis.common.screens.others.ChipsScreen import org.hisp.dhis.common.screens.others.IndicatorScreen import org.hisp.dhis.common.screens.others.LegendScreen import org.hisp.dhis.common.screens.others.MetadataAvatarScreen +import org.hisp.dhis.common.screens.others.NavigationBarScreen import org.hisp.dhis.common.screens.others.ProgressScreen import org.hisp.dhis.common.screens.others.SearchBarScreen import org.hisp.dhis.common.screens.others.SectionScreen @@ -97,6 +98,7 @@ fun Main( Groups.TOGGLEABLE_INPUTS -> ToggleableInputsScreen(imageBitmapLoader) Groups.TAGS -> TagsScreen() Groups.SEARCH_BAR -> SearchBarScreen() + Groups.NAVIGATION_BAR -> NavigationBarScreen() Groups.NO_GROUP_SELECTED -> NoComponentSelectedScreen() } } else { diff --git a/common/src/commonMain/kotlin/org/hisp/dhis/common/screens/Groups.kt b/common/src/commonMain/kotlin/org/hisp/dhis/common/screens/Groups.kt index 8c73b26df..403da1712 100644 --- a/common/src/commonMain/kotlin/org/hisp/dhis/common/screens/Groups.kt +++ b/common/src/commonMain/kotlin/org/hisp/dhis/common/screens/Groups.kt @@ -17,5 +17,6 @@ enum class Groups(val label: String) { METADATA_AVATAR("Metadata Avatar"), INDICATOR("Indicators"), PARAMETER_SELECTOR("Parameter selector"), + NAVIGATION_BAR("Navigation Bar"), NO_GROUP_SELECTED("No group selected"), } diff --git a/common/src/commonMain/kotlin/org/hisp/dhis/common/screens/others/NavigationBarScreen.kt b/common/src/commonMain/kotlin/org/hisp/dhis/common/screens/others/NavigationBarScreen.kt new file mode 100644 index 000000000..2feaf8f9f --- /dev/null +++ b/common/src/commonMain/kotlin/org/hisp/dhis/common/screens/others/NavigationBarScreen.kt @@ -0,0 +1,200 @@ +package org.hisp.dhis.common.screens.others + +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.Assignment +import androidx.compose.material.icons.automirrored.filled.List +import androidx.compose.material.icons.automirrored.filled.StickyNote2 +import androidx.compose.material.icons.automirrored.outlined.Assignment +import androidx.compose.material.icons.automirrored.outlined.List +import androidx.compose.material.icons.automirrored.outlined.StickyNote2 +import androidx.compose.material.icons.filled.BarChart +import androidx.compose.material.icons.filled.Description +import androidx.compose.material.icons.filled.Hub +import androidx.compose.material.icons.filled.Map +import androidx.compose.material.icons.outlined.BarChart +import androidx.compose.material.icons.outlined.Description +import androidx.compose.material.icons.outlined.Hub +import androidx.compose.material.icons.outlined.Map +import androidx.compose.material3.Icon +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import org.hisp.dhis.common.screens.Groups +import org.hisp.dhis.mobile.ui.designsystem.component.ColumnComponentContainer +import org.hisp.dhis.mobile.ui.designsystem.component.ColumnScreenContainer +import org.hisp.dhis.mobile.ui.designsystem.component.navigationBar.NavigationBar +import org.hisp.dhis.mobile.ui.designsystem.component.navigationBar.NavigationBarItem + +@Composable +fun NavigationBarScreen() { + val homeItems = listOf( + NavigationBarItem( + defaultIcon = { + Icon(imageVector = Icons.Outlined.Description, contentDescription = null) + }, + selectedIcon = { + Icon(imageVector = Icons.Filled.Description, contentDescription = null) + }, + label = "Description", + ), + NavigationBarItem( + defaultIcon = { + Icon(imageVector = Icons.Outlined.BarChart, contentDescription = null) + }, + selectedIcon = { + Icon(imageVector = Icons.Filled.BarChart, contentDescription = null) + }, + label = "Charts", + showBadge = true, + ), + ) + + val programItems = listOf( + NavigationBarItem( + defaultIcon = { + Icon(imageVector = Icons.AutoMirrored.Outlined.List, contentDescription = null) + }, + selectedIcon = { + Icon(imageVector = Icons.AutoMirrored.Filled.List, contentDescription = null) + }, + label = "List", + ), + NavigationBarItem( + defaultIcon = { + Icon(imageVector = Icons.Outlined.Map, contentDescription = null) + }, + selectedIcon = { + Icon(imageVector = Icons.Filled.Map, contentDescription = null) + }, + label = "Maps", + ), + NavigationBarItem( + defaultIcon = { + Icon(imageVector = Icons.Outlined.BarChart, contentDescription = null) + }, + selectedIcon = { + Icon(imageVector = Icons.Filled.BarChart, contentDescription = null) + }, + label = "Charts", + showBadge = true, + badgeText = "32", + ), + ) + + val enrollmentItems = listOf( + NavigationBarItem( + defaultIcon = { + Icon(imageVector = Icons.AutoMirrored.Outlined.Assignment, contentDescription = null) + }, + selectedIcon = { + Icon(imageVector = Icons.AutoMirrored.Filled.Assignment, contentDescription = null) + }, + label = "Details", + ), + NavigationBarItem( + defaultIcon = { + Icon(imageVector = Icons.Outlined.BarChart, contentDescription = null) + }, + selectedIcon = { + Icon(imageVector = Icons.Filled.BarChart, contentDescription = null) + }, + label = "Charts", + ), + NavigationBarItem( + defaultIcon = { + Icon(imageVector = Icons.Outlined.Hub, contentDescription = null) + }, + selectedIcon = { + Icon(imageVector = Icons.Filled.Hub, contentDescription = null) + }, + label = "Relationships", + ), + NavigationBarItem( + defaultIcon = { + Icon(imageVector = Icons.AutoMirrored.Outlined.StickyNote2, contentDescription = null) + }, + selectedIcon = { + Icon(imageVector = Icons.AutoMirrored.Filled.StickyNote2, contentDescription = null) + }, + label = "Notes", + showBadge = true, + ), + ) + + val formItems = listOf( + NavigationBarItem( + defaultIcon = { + Icon(imageVector = Icons.Outlined.Description, contentDescription = null) + }, + selectedIcon = { + Icon(imageVector = Icons.Filled.Description, contentDescription = null) + }, + label = "Description", + ), + NavigationBarItem( + defaultIcon = { + Icon(imageVector = Icons.Outlined.BarChart, contentDescription = null) + }, + selectedIcon = { + Icon(imageVector = Icons.Filled.BarChart, contentDescription = null) + }, + label = "Charts", + ), + NavigationBarItem( + defaultIcon = { + Icon(imageVector = Icons.AutoMirrored.Outlined.StickyNote2, contentDescription = null) + }, + selectedIcon = { + Icon(imageVector = Icons.AutoMirrored.Filled.StickyNote2, contentDescription = null) + }, + label = "Notes", + showBadge = true, + badgeText = "3", + ), + ) + + ColumnScreenContainer( + title = Groups.NAVIGATION_BAR.label, + ) { + ColumnComponentContainer("Home") { + var selectedHomeItemIndex by remember { mutableStateOf(null) } + NavigationBar( + items = homeItems, + selectedItemIndex = selectedHomeItemIndex, + ) { + selectedHomeItemIndex = it + } + } + ColumnComponentContainer("Program dashboard") { + var selectedProgramItemIndex by remember { mutableStateOf(null) } + NavigationBar( + items = programItems, + selectedItemIndex = selectedProgramItemIndex, + ) { + selectedProgramItemIndex = it + } + } + + ColumnComponentContainer("Enrollment dashboard") { + var selectedEnrollmentItemIndex by remember { mutableStateOf(null) } + NavigationBar( + items = enrollmentItems, + selectedItemIndex = selectedEnrollmentItemIndex, + ) { + selectedEnrollmentItemIndex = it + } + } + + ColumnComponentContainer("Form") { + var selectedFormItemIndex by remember { mutableStateOf(null) } + NavigationBar( + items = formItems, + selectedItemIndex = selectedFormItemIndex, + ) { + selectedFormItemIndex = it + } + } + } +} diff --git a/designsystem/src/androidUnitTest/kotlin/org/hisp/dhis/mobile/ui/designsystem/NavigationBarSnapShotTest.kt b/designsystem/src/androidUnitTest/kotlin/org/hisp/dhis/mobile/ui/designsystem/NavigationBarSnapShotTest.kt new file mode 100644 index 000000000..0774d7da7 --- /dev/null +++ b/designsystem/src/androidUnitTest/kotlin/org/hisp/dhis/mobile/ui/designsystem/NavigationBarSnapShotTest.kt @@ -0,0 +1,207 @@ +package org.hisp.dhis.mobile.ui.designsystem + +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.Assignment +import androidx.compose.material.icons.automirrored.filled.List +import androidx.compose.material.icons.automirrored.filled.StickyNote2 +import androidx.compose.material.icons.automirrored.outlined.Assignment +import androidx.compose.material.icons.automirrored.outlined.List +import androidx.compose.material.icons.automirrored.outlined.StickyNote2 +import androidx.compose.material.icons.filled.BarChart +import androidx.compose.material.icons.filled.Description +import androidx.compose.material.icons.filled.Hub +import androidx.compose.material.icons.filled.Map +import androidx.compose.material.icons.outlined.BarChart +import androidx.compose.material.icons.outlined.Description +import androidx.compose.material.icons.outlined.Hub +import androidx.compose.material.icons.outlined.Map +import androidx.compose.material3.Icon +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import org.hisp.dhis.mobile.ui.designsystem.component.ColumnComponentContainer +import org.hisp.dhis.mobile.ui.designsystem.component.ColumnScreenContainer +import org.hisp.dhis.mobile.ui.designsystem.component.navigationBar.NavigationBar +import org.hisp.dhis.mobile.ui.designsystem.component.navigationBar.NavigationBarItem +import org.junit.Rule +import org.junit.Test + +class NavigationBarSnapShotTest { + @get:Rule + val paparazzi = paparazzi() + + @Test + fun launchNavigationBar() { + paparazzi.snapshot { + val homeItems = listOf( + NavigationBarItem( + defaultIcon = { + Icon(imageVector = Icons.Outlined.Description, contentDescription = null) + }, + selectedIcon = { + Icon(imageVector = Icons.Filled.Description, contentDescription = null) + }, + label = "Description", + ), + NavigationBarItem( + defaultIcon = { + Icon(imageVector = Icons.Outlined.BarChart, contentDescription = null) + }, + selectedIcon = { + Icon(imageVector = Icons.Filled.BarChart, contentDescription = null) + }, + label = "Charts", + showBadge = true, + ), + ) + + val programItems = listOf( + NavigationBarItem( + defaultIcon = { + Icon(imageVector = Icons.AutoMirrored.Outlined.List, contentDescription = null) + }, + selectedIcon = { + Icon(imageVector = Icons.AutoMirrored.Filled.List, contentDescription = null) + }, + label = "List", + ), + NavigationBarItem( + defaultIcon = { + Icon(imageVector = Icons.Outlined.Map, contentDescription = null) + }, + selectedIcon = { + Icon(imageVector = Icons.Filled.Map, contentDescription = null) + }, + label = "Maps", + ), + NavigationBarItem( + defaultIcon = { + Icon(imageVector = Icons.Outlined.BarChart, contentDescription = null) + }, + selectedIcon = { + Icon(imageVector = Icons.Filled.BarChart, contentDescription = null) + }, + label = "Charts", + showBadge = true, + badgeText = "32", + ), + ) + + val enrollmentItems = listOf( + NavigationBarItem( + defaultIcon = { + Icon(imageVector = Icons.AutoMirrored.Outlined.Assignment, contentDescription = null) + }, + selectedIcon = { + Icon(imageVector = Icons.AutoMirrored.Filled.Assignment, contentDescription = null) + }, + label = "Details", + ), + NavigationBarItem( + defaultIcon = { + Icon(imageVector = Icons.Outlined.BarChart, contentDescription = null) + }, + selectedIcon = { + Icon(imageVector = Icons.Filled.BarChart, contentDescription = null) + }, + label = "Charts", + ), + NavigationBarItem( + defaultIcon = { + Icon(imageVector = Icons.Outlined.Hub, contentDescription = null) + }, + selectedIcon = { + Icon(imageVector = Icons.Filled.Hub, contentDescription = null) + }, + label = "Relationships", + ), + NavigationBarItem( + defaultIcon = { + Icon(imageVector = Icons.AutoMirrored.Outlined.StickyNote2, contentDescription = null) + }, + selectedIcon = { + Icon(imageVector = Icons.AutoMirrored.Filled.StickyNote2, contentDescription = null) + }, + label = "Notes", + showBadge = true, + ), + ) + + val formItems = listOf( + NavigationBarItem( + defaultIcon = { + Icon(imageVector = Icons.Outlined.Description, contentDescription = null) + }, + selectedIcon = { + Icon(imageVector = Icons.Filled.Description, contentDescription = null) + }, + label = "Description", + ), + NavigationBarItem( + defaultIcon = { + Icon(imageVector = Icons.Outlined.BarChart, contentDescription = null) + }, + selectedIcon = { + Icon(imageVector = Icons.Filled.BarChart, contentDescription = null) + }, + label = "Charts", + ), + NavigationBarItem( + defaultIcon = { + Icon(imageVector = Icons.AutoMirrored.Outlined.StickyNote2, contentDescription = null) + }, + selectedIcon = { + Icon(imageVector = Icons.AutoMirrored.Filled.StickyNote2, contentDescription = null) + }, + label = "Notes", + showBadge = true, + badgeText = "3", + ), + ) + + ColumnScreenContainer( + title = "Navigation Bar", + ) { + ColumnComponentContainer("Home") { + var selectedHomeItemIndex by remember { mutableStateOf(null) } + NavigationBar( + items = homeItems, + selectedItemIndex = selectedHomeItemIndex, + ) { + selectedHomeItemIndex = it + } + } + ColumnComponentContainer("Program dashboard") { + var selectedProgramItemIndex by remember { mutableStateOf(null) } + NavigationBar( + items = programItems, + selectedItemIndex = selectedProgramItemIndex, + ) { + selectedProgramItemIndex = it + } + } + + ColumnComponentContainer("Enrollment dashboard") { + var selectedEnrollmentItemIndex by remember { mutableStateOf(null) } + NavigationBar( + items = enrollmentItems, + selectedItemIndex = selectedEnrollmentItemIndex, + ) { + selectedEnrollmentItemIndex = it + } + } + + ColumnComponentContainer("Form") { + var selectedFormItemIndex by remember { mutableStateOf(null) } + NavigationBar( + items = formItems, + selectedItemIndex = selectedFormItemIndex, + ) { + selectedFormItemIndex = it + } + } + } + } + } +} diff --git a/designsystem/src/commonMain/kotlin/org/hisp/dhis/mobile/ui/designsystem/component/navigationBar/NavigationBar.kt b/designsystem/src/commonMain/kotlin/org/hisp/dhis/mobile/ui/designsystem/component/navigationBar/NavigationBar.kt new file mode 100644 index 000000000..edfb4dda3 --- /dev/null +++ b/designsystem/src/commonMain/kotlin/org/hisp/dhis/mobile/ui/designsystem/component/navigationBar/NavigationBar.kt @@ -0,0 +1,122 @@ +package org.hisp.dhis.mobile.ui.designsystem.component.navigationBar + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.offset +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.NavigationBarItem +import androidx.compose.material3.NavigationBarItemDefaults +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.testTag +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.dp +import org.hisp.dhis.mobile.ui.designsystem.component.ErrorBadge +import org.hisp.dhis.mobile.ui.designsystem.component.navigationBar.NavigationBarTestTags.NAVIGATION_BAR +import org.hisp.dhis.mobile.ui.designsystem.component.navigationBar.NavigationBarTestTags.NAVIGATION_BAR_BORDER +import org.hisp.dhis.mobile.ui.designsystem.component.navigationBar.NavigationBarTestTags.NAVIGATION_BAR_CONTAINER +import org.hisp.dhis.mobile.ui.designsystem.component.navigationBar.NavigationBarTestTags.NAVIGATION_BAR_ITEM_BADGE_PREFIX +import org.hisp.dhis.mobile.ui.designsystem.component.navigationBar.NavigationBarTestTags.NAVIGATION_BAR_ITEM_LABEL_PREFIX +import org.hisp.dhis.mobile.ui.designsystem.component.navigationBar.NavigationBarTestTags.NAVIGATION_BAR_ITEM_PREFIX +import org.hisp.dhis.mobile.ui.designsystem.resource.provideFontResource +import org.hisp.dhis.mobile.ui.designsystem.theme.Outline +import org.hisp.dhis.mobile.ui.designsystem.theme.SurfaceColor +import org.hisp.dhis.mobile.ui.designsystem.theme.TextColor + +@Composable +fun NavigationBar( + modifier: Modifier = Modifier, + items: List, + selectedItemIndex: Int? = null, + onItemClick: (Int) -> Unit, +) { + Column( + modifier = modifier.testTag(NAVIGATION_BAR_CONTAINER), + ) { + HorizontalDivider( + modifier = Modifier.testTag(NAVIGATION_BAR_BORDER), + thickness = 0.6.dp, + color = Outline.Light, + ) + androidx.compose.material3.NavigationBar( + modifier = Modifier.testTag(NAVIGATION_BAR), + containerColor = SurfaceColor.ContainerLowest, + ) { + items.forEachIndexed { index, item -> + val selected = selectedItemIndex == index + NavigationBarItem( + modifier = Modifier.testTag("$NAVIGATION_BAR_ITEM_PREFIX${item.label}"), + colors = navigationBarItemColors(), + icon = { + NavigationBarItemIcon(item = item, selected = selected) + }, + label = { + Text( + style = if (selected) { + MaterialTheme.typography.labelMedium.copy( + fontFamily = provideFontResource("roboto_bold"), + fontWeight = FontWeight.Bold, + ) + } else { + MaterialTheme.typography.labelMedium.copy( + color = TextColor.OnSurfaceVariant, + ) + }, + modifier = Modifier.testTag("$NAVIGATION_BAR_ITEM_LABEL_PREFIX${item.label}"), + text = item.label, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + ) + }, + enabled = item.enabled, + selected = selected, + onClick = { + onItemClick(index) + }, + ) + } + } + } +} + +@Composable +fun NavigationBarItemIcon(item: NavigationBarItem, selected: Boolean) { + Box { + if (selected) { + item.selectedIcon() + } else { + item.defaultIcon() + } + if (item.showBadge) { + val badgeXOffset = if (!item.badgeText.isNullOrEmpty()) { + 4.dp * item.badgeText.length + } else { + 0.dp + } + val badgeYOffset = if (!item.badgeText.isNullOrEmpty()) (-4).dp else 0.dp + + ErrorBadge( + modifier = Modifier.testTag("$NAVIGATION_BAR_ITEM_BADGE_PREFIX${item.label}") + .align(Alignment.TopEnd) + .offset( + x = badgeXOffset, + y = badgeYOffset, + ), + text = item.badgeText, + ) + } + } +} + +@Composable +fun navigationBarItemColors() = NavigationBarItemDefaults.colors( + selectedIconColor = SurfaceColor.Primary, + indicatorColor = SurfaceColor.Container, + unselectedIconColor = TextColor.OnSurfaceVariant, + disabledIconColor = TextColor.OnDisabledSurface, + disabledTextColor = TextColor.OnDisabledSurface, +) diff --git a/designsystem/src/commonMain/kotlin/org/hisp/dhis/mobile/ui/designsystem/component/navigationBar/NavigationBarItem.kt b/designsystem/src/commonMain/kotlin/org/hisp/dhis/mobile/ui/designsystem/component/navigationBar/NavigationBarItem.kt new file mode 100644 index 000000000..d47e24ebb --- /dev/null +++ b/designsystem/src/commonMain/kotlin/org/hisp/dhis/mobile/ui/designsystem/component/navigationBar/NavigationBarItem.kt @@ -0,0 +1,12 @@ +package org.hisp.dhis.mobile.ui.designsystem.component.navigationBar + +import androidx.compose.runtime.Composable + +data class NavigationBarItem( + val defaultIcon: @Composable () -> Unit, + val selectedIcon: @Composable () -> Unit, + val label: String, + val enabled: Boolean = true, + val showBadge: Boolean = false, + val badgeText: String? = null, +) diff --git a/designsystem/src/commonMain/kotlin/org/hisp/dhis/mobile/ui/designsystem/component/navigationBar/NavigationBarTestTags.kt b/designsystem/src/commonMain/kotlin/org/hisp/dhis/mobile/ui/designsystem/component/navigationBar/NavigationBarTestTags.kt new file mode 100644 index 000000000..51972d3f6 --- /dev/null +++ b/designsystem/src/commonMain/kotlin/org/hisp/dhis/mobile/ui/designsystem/component/navigationBar/NavigationBarTestTags.kt @@ -0,0 +1,10 @@ +package org.hisp.dhis.mobile.ui.designsystem.component.navigationBar + +object NavigationBarTestTags { + const val NAVIGATION_BAR_CONTAINER = "NAVIGATION_BAR_CONTAINER" + const val NAVIGATION_BAR_BORDER = "NAVIGATION_BAR_BORDER" + const val NAVIGATION_BAR = "NAVIGATION_BAR" + const val NAVIGATION_BAR_ITEM_PREFIX = "NAVIGATION_BAR_ITEM_" + const val NAVIGATION_BAR_ITEM_LABEL_PREFIX = "NAVIGATION_BAR_ITEM_LABEL_" + const val NAVIGATION_BAR_ITEM_BADGE_PREFIX = "NAVIGATION_BAR_ITEM_BADGE_" +} diff --git a/designsystem/src/desktopTest/kotlin/org/hisp/dhis/mobile/ui/designsystem/component/NavigationBarTest.kt b/designsystem/src/desktopTest/kotlin/org/hisp/dhis/mobile/ui/designsystem/component/NavigationBarTest.kt new file mode 100644 index 000000000..f9c2d922b --- /dev/null +++ b/designsystem/src/desktopTest/kotlin/org/hisp/dhis/mobile/ui/designsystem/component/NavigationBarTest.kt @@ -0,0 +1,211 @@ +package org.hisp.dhis.mobile.ui.designsystem.component + +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.BarChart +import androidx.compose.material.icons.filled.Description +import androidx.compose.material.icons.outlined.BarChart +import androidx.compose.material.icons.outlined.Description +import androidx.compose.material3.Icon +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.testTag +import androidx.compose.ui.test.junit4.createComposeRule +import androidx.compose.ui.test.onNodeWithTag +import androidx.compose.ui.test.performClick +import org.hisp.dhis.mobile.ui.designsystem.component.navigationBar.NavigationBar +import org.hisp.dhis.mobile.ui.designsystem.component.navigationBar.NavigationBarItem +import org.hisp.dhis.mobile.ui.designsystem.component.navigationBar.NavigationBarTestTags.NAVIGATION_BAR +import org.hisp.dhis.mobile.ui.designsystem.component.navigationBar.NavigationBarTestTags.NAVIGATION_BAR_BORDER +import org.hisp.dhis.mobile.ui.designsystem.component.navigationBar.NavigationBarTestTags.NAVIGATION_BAR_CONTAINER +import org.hisp.dhis.mobile.ui.designsystem.component.navigationBar.NavigationBarTestTags.NAVIGATION_BAR_ITEM_BADGE_PREFIX +import org.hisp.dhis.mobile.ui.designsystem.component.navigationBar.NavigationBarTestTags.NAVIGATION_BAR_ITEM_LABEL_PREFIX +import org.hisp.dhis.mobile.ui.designsystem.component.navigationBar.NavigationBarTestTags.NAVIGATION_BAR_ITEM_PREFIX +import org.junit.Rule +import org.junit.Test + +class NavigationBarTest { + + @get:Rule + val rule = createComposeRule() + + @Test + fun shouldDisplayNavigationBarCorrectly() { + rule.setContent { + val items = listOf( + NavigationBarItem( + defaultIcon = { + Icon( + modifier = Modifier.testTag("NAVIGATION_BAR_ITEM_DEFAULT_ICON_Description"), + imageVector = Icons.Outlined.Description, + contentDescription = null, + ) + }, + selectedIcon = { + Icon( + modifier = Modifier.testTag("NAVIGATION_BAR_ITEM_SELECTED_ICON_Description"), + imageVector = Icons.Filled.Description, + contentDescription = null, + ) + }, + label = "Description", + ), + NavigationBarItem( + defaultIcon = { + Icon( + modifier = Modifier.testTag("NAVIGATION_BAR_ITEM_DEFAULT_ICON_Charts"), + imageVector = Icons.Outlined.BarChart, + contentDescription = null, + ) + }, + selectedIcon = { + Icon( + modifier = Modifier.testTag("NAVIGATION_BAR_ITEM_SELECTED_ICON_Charts"), + imageVector = Icons.Filled.BarChart, + contentDescription = null, + ) + }, + label = "Charts", + showBadge = true, + ), + ) + NavigationBar( + items = items, + selectedItemIndex = null, + ) { + } + } + + rule.onNodeWithTag(NAVIGATION_BAR_CONTAINER).assertExists() + rule.onNodeWithTag(NAVIGATION_BAR_BORDER).assertExists() + rule.onNodeWithTag(NAVIGATION_BAR).assertExists() + rule.onNodeWithTag("${NAVIGATION_BAR_ITEM_PREFIX}Description", true).assertExists() + rule.onNodeWithTag("${NAVIGATION_BAR_ITEM_LABEL_PREFIX}Description", true).assertExists() + rule.onNodeWithTag("NAVIGATION_BAR_ITEM_DEFAULT_ICON_Description", true).assertExists() + rule.onNodeWithTag("NAVIGATION_BAR_ITEM_SELECTED_ICON_Description", true).assertDoesNotExist() + rule.onNodeWithTag("${NAVIGATION_BAR_ITEM_BADGE_PREFIX}Description", true).assertDoesNotExist() + rule.onNodeWithTag("${NAVIGATION_BAR_ITEM_PREFIX}Charts", true).assertExists() + rule.onNodeWithTag("${NAVIGATION_BAR_ITEM_LABEL_PREFIX}Charts", true).assertExists() + rule.onNodeWithTag("NAVIGATION_BAR_ITEM_DEFAULT_ICON_Charts", true).assertExists() + rule.onNodeWithTag("NAVIGATION_BAR_ITEM_SELECTED_ICON_Charts", true).assertDoesNotExist() + rule.onNodeWithTag("${NAVIGATION_BAR_ITEM_BADGE_PREFIX}Charts", true).assertExists() + } + + @Test + fun shouldUpdateNavigationBarIconCorrectlyOnSelection() { + rule.setContent { + val items = listOf( + NavigationBarItem( + defaultIcon = { + Icon( + modifier = Modifier.testTag("NAVIGATION_BAR_ITEM_DEFAULT_ICON_Description"), + imageVector = Icons.Outlined.Description, + contentDescription = null, + ) + }, + selectedIcon = { + Icon( + modifier = Modifier.testTag("NAVIGATION_BAR_ITEM_SELECTED_ICON_Description"), + imageVector = Icons.Filled.Description, + contentDescription = null, + ) + }, + label = "Description", + ), + NavigationBarItem( + defaultIcon = { + Icon( + modifier = Modifier.testTag("NAVIGATION_BAR_ITEM_DEFAULT_ICON_Charts"), + imageVector = Icons.Outlined.BarChart, + contentDescription = null, + ) + }, + selectedIcon = { + Icon( + modifier = Modifier.testTag("NAVIGATION_BAR_ITEM_SELECTED_ICON_Charts"), + imageVector = Icons.Filled.BarChart, + contentDescription = null, + ) + }, + label = "Charts", + showBadge = true, + ), + ) + var selectedItemIndex by remember { mutableStateOf(null) } + NavigationBar( + items = items, + selectedItemIndex = selectedItemIndex, + ) { + selectedItemIndex = it + } + } + + rule.onNodeWithTag("NAVIGATION_BAR_ITEM_DEFAULT_ICON_Description", true).assertExists() + rule.onNodeWithTag("NAVIGATION_BAR_ITEM_SELECTED_ICON_Description", true).assertDoesNotExist() + rule.onNodeWithTag("${NAVIGATION_BAR_ITEM_PREFIX}Description", true).performClick() + rule.onNodeWithTag("NAVIGATION_BAR_ITEM_DEFAULT_ICON_Description", true).assertDoesNotExist() + rule.onNodeWithTag("NAVIGATION_BAR_ITEM_SELECTED_ICON_Description", true).assertExists() + } + + @Test + fun shouldResetSelectedIconToDefaultWhenNewItemIsClicked() { + rule.setContent { + val items = listOf( + NavigationBarItem( + defaultIcon = { + Icon( + modifier = Modifier.testTag("NAVIGATION_BAR_ITEM_DEFAULT_ICON_Description"), + imageVector = Icons.Outlined.Description, + contentDescription = null, + ) + }, + selectedIcon = { + Icon( + modifier = Modifier.testTag("NAVIGATION_BAR_ITEM_SELECTED_ICON_Description"), + imageVector = Icons.Filled.Description, + contentDescription = null, + ) + }, + label = "Description", + ), + NavigationBarItem( + defaultIcon = { + Icon( + modifier = Modifier.testTag("NAVIGATION_BAR_ITEM_DEFAULT_ICON_Charts"), + imageVector = Icons.Outlined.BarChart, + contentDescription = null, + ) + }, + selectedIcon = { + Icon( + modifier = Modifier.testTag("NAVIGATION_BAR_ITEM_SELECTED_ICON_Charts"), + imageVector = Icons.Filled.BarChart, + contentDescription = null, + ) + }, + label = "Charts", + showBadge = true, + ), + ) + var selectedItemIndex by remember { mutableStateOf(0) } + NavigationBar( + items = items, + selectedItemIndex = selectedItemIndex, + ) { + selectedItemIndex = it + } + } + + rule.onNodeWithTag("NAVIGATION_BAR_ITEM_DEFAULT_ICON_Description", true).assertDoesNotExist() + rule.onNodeWithTag("NAVIGATION_BAR_ITEM_SELECTED_ICON_Description", true).assertExists() + rule.onNodeWithTag("NAVIGATION_BAR_ITEM_DEFAULT_ICON_Charts", true).assertExists() + rule.onNodeWithTag("NAVIGATION_BAR_ITEM_SELECTED_ICON_Charts", true).assertDoesNotExist() + rule.onNodeWithTag("${NAVIGATION_BAR_ITEM_PREFIX}Charts", true).performClick() + rule.onNodeWithTag("NAVIGATION_BAR_ITEM_DEFAULT_ICON_Description", true).assertExists() + rule.onNodeWithTag("NAVIGATION_BAR_ITEM_SELECTED_ICON_Description", true).assertDoesNotExist() + rule.onNodeWithTag("NAVIGATION_BAR_ITEM_DEFAULT_ICON_Charts", true).assertDoesNotExist() + rule.onNodeWithTag("NAVIGATION_BAR_ITEM_SELECTED_ICON_Charts", true).assertExists() + } +} diff --git a/designsystem/src/test/snapshots/images/org.hisp.dhis.mobile.ui.designsystem_NavigationBarSnapShotTest_launchNavigationBar.png b/designsystem/src/test/snapshots/images/org.hisp.dhis.mobile.ui.designsystem_NavigationBarSnapShotTest_launchNavigationBar.png new file mode 100644 index 000000000..48fb470ba Binary files /dev/null and b/designsystem/src/test/snapshots/images/org.hisp.dhis.mobile.ui.designsystem_NavigationBarSnapShotTest_launchNavigationBar.png differ