mirror of
https://github.com/bitwarden/android.git
synced 2026-06-02 11:12:00 -05:00
PM-25003: Migrate bottom sheet to the UI module (#5751)
This commit is contained in:
@@ -1,111 +0,0 @@
|
||||
package com.x8bit.bitwarden.ui.platform.components.bottomsheet
|
||||
|
||||
import androidx.compose.foundation.layout.RowScope
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.ModalBottomSheet
|
||||
import androidx.compose.material3.SheetState
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.material3.rememberModalBottomSheetState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.semantics.SemanticsPropertyKey
|
||||
import androidx.compose.ui.semantics.SemanticsPropertyReceiver
|
||||
import androidx.compose.ui.semantics.semantics
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.bitwarden.ui.platform.components.appbar.BitwardenTopAppBar
|
||||
import com.bitwarden.ui.platform.components.appbar.NavigationIcon
|
||||
import com.bitwarden.ui.platform.components.scaffold.BitwardenScaffold
|
||||
import com.bitwarden.ui.platform.components.util.rememberVectorPainter
|
||||
import com.bitwarden.ui.platform.resource.BitwardenDrawable
|
||||
import com.bitwarden.ui.platform.resource.BitwardenString
|
||||
import com.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
import kotlinx.coroutines.launch
|
||||
import org.jetbrains.annotations.VisibleForTesting
|
||||
|
||||
/**
|
||||
* A reusable modal bottom sheet that applies provides a bottom sheet layout with the
|
||||
* standard [BitwardenScaffold] and [BitwardenTopAppBar] and expected scrolling behavior with
|
||||
* passed in [sheetContent]
|
||||
*
|
||||
* @param sheetTitle The title to display in the [BitwardenTopAppBar]
|
||||
* @param onDismiss The action to perform when the bottom sheet is dismissed will also be performed
|
||||
* when the "close" icon is clicked, caller must handle any desired animation or hiding of the
|
||||
* bottom sheet. This will be invoked _after_ the sheet has been animated away.
|
||||
* @param topBarActions Row of actions to add the top bar of the bottom sheet.
|
||||
* @param showBottomSheet Whether or not to show the bottom sheet, by default this is true assuming
|
||||
* the showing/hiding will be handled by the caller.
|
||||
* @param sheetContent Content to display in the bottom sheet. The content is passed the padding
|
||||
* from the containing [BitwardenScaffold] and a `onDismiss` lambda to be used for manual dismissal
|
||||
* that will include the dismissal animation.
|
||||
*/
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun BitwardenModalBottomSheet(
|
||||
sheetTitle: String,
|
||||
onDismiss: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
topBarActions: @Composable RowScope.(animatedOnDismiss: () -> Unit) -> Unit = {},
|
||||
showBottomSheet: Boolean = true,
|
||||
sheetState: SheetState = rememberModalBottomSheetState(),
|
||||
sheetContent: @Composable (animatedOnDismiss: () -> Unit) -> Unit,
|
||||
) {
|
||||
if (!showBottomSheet) return
|
||||
ModalBottomSheet(
|
||||
onDismissRequest = onDismiss,
|
||||
modifier = modifier.semantics { this.IsBottomSheet = true },
|
||||
dragHandle = null,
|
||||
sheetState = sheetState,
|
||||
contentWindowInsets = {
|
||||
WindowInsets(left = 0, top = 0, right = 0, bottom = 0)
|
||||
},
|
||||
shape = BitwardenTheme.shapes.bottomSheet,
|
||||
) {
|
||||
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior()
|
||||
val animatedOnDismiss = sheetState.createAnimatedDismissAction(onDismiss = onDismiss)
|
||||
BitwardenScaffold(
|
||||
topBar = {
|
||||
BitwardenTopAppBar(
|
||||
title = sheetTitle,
|
||||
navigationIcon = NavigationIcon(
|
||||
navigationIcon = rememberVectorPainter(BitwardenDrawable.ic_close),
|
||||
onNavigationIconClick = animatedOnDismiss,
|
||||
navigationIconContentDescription = stringResource(BitwardenString.close),
|
||||
),
|
||||
actions = {
|
||||
topBarActions(animatedOnDismiss)
|
||||
},
|
||||
scrollBehavior = scrollBehavior,
|
||||
minimumHeight = 64.dp,
|
||||
)
|
||||
},
|
||||
modifier = Modifier
|
||||
.nestedScroll(scrollBehavior.nestedScrollConnection)
|
||||
.fillMaxSize(),
|
||||
) {
|
||||
sheetContent(animatedOnDismiss)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* SemanticPropertyKey used for Unit tests where checking if the content is part of a bottom sheet.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
val IsBottomSheetKey = SemanticsPropertyKey<Boolean>("IsBottomSheet")
|
||||
private var SemanticsPropertyReceiver.IsBottomSheet by IsBottomSheetKey
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun SheetState.createAnimatedDismissAction(onDismiss: () -> Unit): () -> Unit {
|
||||
val scope = rememberCoroutineScope()
|
||||
return {
|
||||
scope
|
||||
.launch { this@createAnimatedDismissAction.hide() }
|
||||
.invokeOnCompletion { onDismiss() }
|
||||
}
|
||||
}
|
||||
@@ -45,6 +45,7 @@ import com.bitwarden.ui.platform.base.util.cardStyle
|
||||
import com.bitwarden.ui.platform.base.util.standardHorizontalMargin
|
||||
import com.bitwarden.ui.platform.base.util.toListItemCardStyle
|
||||
import com.bitwarden.ui.platform.components.appbar.BitwardenTopAppBar
|
||||
import com.bitwarden.ui.platform.components.bottomsheet.BitwardenModalBottomSheet
|
||||
import com.bitwarden.ui.platform.components.button.BitwardenFilledButton
|
||||
import com.bitwarden.ui.platform.components.button.BitwardenOutlinedButton
|
||||
import com.bitwarden.ui.platform.components.content.BitwardenErrorContent
|
||||
@@ -59,7 +60,6 @@ import com.bitwarden.ui.platform.components.util.rememberVectorPainter
|
||||
import com.bitwarden.ui.platform.resource.BitwardenDrawable
|
||||
import com.bitwarden.ui.platform.resource.BitwardenString
|
||||
import com.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
import com.x8bit.bitwarden.ui.platform.components.bottomsheet.BitwardenModalBottomSheet
|
||||
import com.x8bit.bitwarden.ui.platform.composition.LocalPermissionsManager
|
||||
import com.x8bit.bitwarden.ui.platform.manager.permissions.PermissionsManager
|
||||
|
||||
|
||||
@@ -46,6 +46,7 @@ import com.bitwarden.ui.platform.components.appbar.BitwardenTopAppBar
|
||||
import com.bitwarden.ui.platform.components.appbar.NavigationIcon
|
||||
import com.bitwarden.ui.platform.components.appbar.action.BitwardenOverflowActionItem
|
||||
import com.bitwarden.ui.platform.components.appbar.model.OverflowMenuItemData
|
||||
import com.bitwarden.ui.platform.components.bottomsheet.BitwardenModalBottomSheet
|
||||
import com.bitwarden.ui.platform.components.button.BitwardenTextButton
|
||||
import com.bitwarden.ui.platform.components.content.BitwardenErrorContent
|
||||
import com.bitwarden.ui.platform.components.content.BitwardenLoadingContent
|
||||
@@ -67,7 +68,6 @@ import com.bitwarden.ui.platform.resource.BitwardenString
|
||||
import com.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
import com.bitwarden.ui.util.Text
|
||||
import com.x8bit.bitwarden.ui.credentials.manager.CredentialProviderCompletionManager
|
||||
import com.x8bit.bitwarden.ui.platform.components.bottomsheet.BitwardenModalBottomSheet
|
||||
import com.x8bit.bitwarden.ui.platform.components.coachmark.CoachMarkContainer
|
||||
import com.x8bit.bitwarden.ui.platform.components.coachmark.rememberLazyListCoachMarkState
|
||||
import com.x8bit.bitwarden.ui.platform.components.dialog.BitwardenMasterPasswordDialog
|
||||
|
||||
@@ -45,6 +45,7 @@ import com.bitwarden.ui.platform.base.util.standardHorizontalMargin
|
||||
import com.bitwarden.ui.platform.base.util.toAnnotatedString
|
||||
import com.bitwarden.ui.platform.components.appbar.BitwardenTopAppBar
|
||||
import com.bitwarden.ui.platform.components.appbar.NavigationIcon
|
||||
import com.bitwarden.ui.platform.components.bottomsheet.BitwardenModalBottomSheet
|
||||
import com.bitwarden.ui.platform.components.button.BitwardenFilledButton
|
||||
import com.bitwarden.ui.platform.components.button.BitwardenOutlinedButton
|
||||
import com.bitwarden.ui.platform.components.card.BitwardenContentCard
|
||||
@@ -58,7 +59,6 @@ import com.bitwarden.ui.platform.manager.IntentManager
|
||||
import com.bitwarden.ui.platform.resource.BitwardenDrawable
|
||||
import com.bitwarden.ui.platform.resource.BitwardenString
|
||||
import com.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
import com.x8bit.bitwarden.ui.platform.components.bottomsheet.BitwardenModalBottomSheet
|
||||
import com.x8bit.bitwarden.ui.vault.feature.importlogins.components.ImportLoginsInstructionStep
|
||||
import com.x8bit.bitwarden.ui.vault.feature.importlogins.handlers.ImportLoginHandler
|
||||
import com.x8bit.bitwarden.ui.vault.feature.importlogins.handlers.rememberImportLoginHandler
|
||||
|
||||
@@ -2,17 +2,8 @@ package com.x8bit.bitwarden.ui.util
|
||||
|
||||
import androidx.compose.ui.semantics.getOrNull
|
||||
import androidx.compose.ui.test.SemanticsMatcher
|
||||
import com.x8bit.bitwarden.ui.platform.components.bottomsheet.IsBottomSheetKey
|
||||
import com.x8bit.bitwarden.ui.platform.components.coachmark.IsCoachMarkToolTipKey
|
||||
|
||||
/**
|
||||
* A [SemanticsMatcher] user to find nodes used specifically inside a bottom sheet.
|
||||
*/
|
||||
val isBottomSheet: SemanticsMatcher
|
||||
get() = SemanticsMatcher("Node is used to to indicate it is a bottom sheet.") {
|
||||
it.config.getOrNull(IsBottomSheetKey) == true
|
||||
}
|
||||
|
||||
/**
|
||||
* A [SemanticsMatcher] user to find Popup nodes used specifically for CoachMarkToolTips
|
||||
*/
|
||||
|
||||
@@ -45,6 +45,7 @@ import com.bitwarden.ui.platform.manager.IntentManager
|
||||
import com.bitwarden.ui.util.asText
|
||||
import com.bitwarden.ui.util.assertNoDialogExists
|
||||
import com.bitwarden.ui.util.assertScrollableNodeDoesNotExist
|
||||
import com.bitwarden.ui.util.isBottomSheet
|
||||
import com.bitwarden.ui.util.isProgressBar
|
||||
import com.bitwarden.ui.util.onAllNodesWithContentDescriptionAfterScroll
|
||||
import com.bitwarden.ui.util.onAllNodesWithTextAfterScroll
|
||||
@@ -60,7 +61,6 @@ import com.x8bit.bitwarden.ui.platform.manager.biometrics.BiometricsManager
|
||||
import com.x8bit.bitwarden.ui.platform.manager.exit.ExitManager
|
||||
import com.x8bit.bitwarden.ui.platform.manager.permissions.FakePermissionManager
|
||||
import com.x8bit.bitwarden.ui.tools.feature.generator.model.GeneratorMode
|
||||
import com.x8bit.bitwarden.ui.util.isBottomSheet
|
||||
import com.x8bit.bitwarden.ui.util.isCoachMarkToolTip
|
||||
import com.x8bit.bitwarden.ui.vault.feature.addedit.model.CustomFieldAction
|
||||
import com.x8bit.bitwarden.ui.vault.feature.addedit.model.CustomFieldType
|
||||
|
||||
@@ -19,9 +19,9 @@ import com.bitwarden.ui.platform.manager.IntentManager
|
||||
import com.bitwarden.ui.platform.resource.BitwardenString
|
||||
import com.bitwarden.ui.util.asText
|
||||
import com.bitwarden.ui.util.assertNoDialogExists
|
||||
import com.bitwarden.ui.util.isBottomSheet
|
||||
import com.x8bit.bitwarden.data.util.advanceTimeByAndRunCurrent
|
||||
import com.x8bit.bitwarden.ui.platform.base.BitwardenComposeTest
|
||||
import com.x8bit.bitwarden.ui.util.isBottomSheet
|
||||
import io.mockk.every
|
||||
import io.mockk.just
|
||||
import io.mockk.mockk
|
||||
|
||||
Reference in New Issue
Block a user