Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
@@ -0,0 +1,20 @@
package com.nativeapptemplate.nativeapptemplatefree.ui.common

import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.padding
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp

/**
* Space below the last row of a list on a screen with a floating action button, so the last row
* (and the actions it reveals when swiped) can scroll above the button instead of staying under it.
* The tallest button is SubmitFab (64.dp; FloatingActionButton is 56.dp), the Scaffold places it
* 16.dp above the bottom, and 16.dp more keeps the row clear of it: 64 + 16 + 16 = 96.
*/
val FabClearance = 96.dp

/** The lists' 24.dp margin, without the bottom: the bottom is [fabClearingContentPadding]. */
val fabListModifier: Modifier = Modifier.padding(start = 24.dp, top = 24.dp, end = 24.dp)

/** Content padding for a list on a screen with a floating action button; see [FabClearance]. */
val fabClearingContentPadding = PaddingValues(bottom = FabClearance)
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ import com.nativeapptemplate.nativeapptemplatefree.ui.common.LoadingView
import com.nativeapptemplate.nativeapptemplatefree.ui.common.MainButtonView
import com.nativeapptemplate.nativeapptemplatefree.ui.common.SnackbarMessageEffect
import com.nativeapptemplate.nativeapptemplatefree.ui.common.SwipeableItemWithActions
import com.nativeapptemplate.nativeapptemplatefree.ui.common.fabClearingContentPadding
import com.nativeapptemplate.nativeapptemplatefree.ui.common.fabListModifier

@Composable
internal fun ItemTagListView(
Expand Down Expand Up @@ -195,7 +197,8 @@ private fun ItemTagListContentView(
) {
LazyColumn(
state = listState,
modifier = Modifier.padding(24.dp),
modifier = fabListModifier,
contentPadding = fabClearingContentPadding,
) {
item {
Text(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ import com.nativeapptemplate.nativeapptemplatefree.ui.common.ErrorView
import com.nativeapptemplate.nativeapptemplatefree.ui.common.LoadingView
import com.nativeapptemplate.nativeapptemplatefree.ui.common.MainButtonView
import com.nativeapptemplate.nativeapptemplatefree.ui.common.SnackbarMessageEffect
import com.nativeapptemplate.nativeapptemplatefree.ui.common.fabClearingContentPadding
import com.nativeapptemplate.nativeapptemplatefree.ui.common.fabListModifier

@Composable
internal fun ShopListView(
Expand Down Expand Up @@ -164,7 +166,8 @@ private fun ShopListContentView(
.padding(padding),
) {
LazyColumn(
Modifier.padding(24.dp),
modifier = fabListModifier,
contentPadding = fabClearingContentPadding,
) {
item {
if (!uiState.didShowTapShopBelowTip) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,17 @@ package com.nativeapptemplate.nativeapptemplatefree.ui.shop_settings.item_tag_li

import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.test.getUnclippedBoundsInRoot
import androidx.compose.ui.test.hasScrollToIndexAction
import androidx.compose.ui.test.hasScrollToKeyAction
import androidx.compose.ui.test.junit4.v2.createComposeRule
import androidx.compose.ui.test.onNodeWithContentDescription
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performScrollToIndex
import androidx.compose.ui.test.performScrollToKey
import androidx.lifecycle.SavedStateHandle
import androidx.navigation.testing.invoke
import com.nativeapptemplate.nativeapptemplatefree.R
import com.nativeapptemplate.nativeapptemplatefree.data.item_tag.ItemTagRepository
import com.nativeapptemplate.nativeapptemplatefree.model.Attributes
import com.nativeapptemplate.nativeapptemplatefree.model.Data
Expand All @@ -36,6 +39,7 @@ import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import org.robolectric.RuntimeEnvironment

/**
* These tests use Robolectric because the subject under test (the ViewModel) uses
Expand Down Expand Up @@ -298,6 +302,35 @@ class ItemTagListViewModelTest {
composeTestRule.onNodeWithText("A007").assertExists()
}

// The last row (and the Delete button its swipe reveals) must be able to scroll above the "+"
// button; without bottom content padding the list ends behind the button and the row stays under it.
@Test
fun lastRow_scrollsAboveTheAddButton() {
// 20 tags on one page: longer than the screen, so the list scrolls.
itemTagRepository.itemTagsForPage = { page -> pagedItemTags(page = page ?: 1, totalPages = 1, perPage = 20) }
shopRepository.sendShop(testInputShop)
composeTestRule.setContent {
val uiState by viewModel.uiState.collectAsState()
ItemTagListView(
viewModel = viewModel,
uiState = uiState,
onItemClick = {},
onAddItemTagClick = {},
onBackClick = {},
)
}
composeTestRule.runOnIdle { viewModel.reload() }

// Scrolling to the last tag scrolls the list to its end. pagedItemTags names tag 20 "A020".
composeTestRule.onNode(hasScrollToKeyAction()).performScrollToKey("item-tag-20")
val context = RuntimeEnvironment.getApplication()
val lastRowBottom = composeTestRule.onNodeWithText("A020").getUnclippedBoundsInRoot().bottom
val addButtonTop = composeTestRule.onNodeWithContentDescription(context.getString(R.string.label_add_item_tag))
.getUnclippedBoundsInRoot().top

assertTrue("last row ends at $lastRowBottom, the add button starts at $addButtonTop", lastRowBottom <= addButtonTop)
}

// The API rejects it: the coded error is shown, the spinner stops, and the list stays.
@Test
fun deleteItemTag_whenTheApiRejectsIt_showsTheErrorAndKeepsTheList() = runTest {
Expand Down
Loading