mirror of
https://github.com/bitwarden/android.git
synced 2026-10-21 10:40:49 -05:00
PM-43002: feat: Update inline generator UI (#7394)
This commit is contained in:
1 parent
402ba77827
commit
dfeeeb26bf
3 files changed
+205
-66
No files matched your search
+89
-46
@@ -49,7 +49,6 @@ import com.bitwarden.ui.platform.components.appbar.model.OverflowMenuItemData
|
||||
import com.bitwarden.ui.platform.components.appbar.model.TopAppBarDividerStyle
|
||||
import com.bitwarden.ui.platform.components.button.BitwardenFilledButton
|
||||
import com.bitwarden.ui.platform.components.button.BitwardenStandardIconButton
|
||||
import com.bitwarden.ui.platform.components.button.BitwardenTextButton
|
||||
import com.bitwarden.ui.platform.components.button.model.BitwardenButtonData
|
||||
import com.bitwarden.ui.platform.components.button.model.BitwardenHelpButtonData
|
||||
import com.bitwarden.ui.platform.components.card.BitwardenActionCard
|
||||
@@ -182,9 +181,6 @@ fun GeneratorScreen(
|
||||
}
|
||||
}
|
||||
}
|
||||
val onRegenerateClick: () -> Unit = { viewModel.trySendAction(GeneratorAction.RegenerateClick) }
|
||||
|
||||
val onCopyClick: () -> Unit = { viewModel.trySendAction(GeneratorAction.CopyClick) }
|
||||
|
||||
val onMainStateOptionClicked: (GeneratorState.MainTypeOption) -> Unit = {
|
||||
viewModel.trySendAction(GeneratorAction.MainTypeOptionSelect(it))
|
||||
@@ -225,7 +221,6 @@ fun GeneratorScreen(
|
||||
generatorMode = generatorMode,
|
||||
scrollBehavior = scrollBehavior,
|
||||
onCloseClick = { viewModel.trySendAction(GeneratorAction.CloseClick) },
|
||||
onSaveClick = { viewModel.trySendAction(GeneratorAction.SaveClick) },
|
||||
)
|
||||
}
|
||||
|
||||
@@ -259,8 +254,9 @@ fun GeneratorScreen(
|
||||
) {
|
||||
ScrollContent(
|
||||
state = state,
|
||||
onRegenerateClick = onRegenerateClick,
|
||||
onCopyClick = onCopyClick,
|
||||
onRegenerateClick = { viewModel.trySendAction(GeneratorAction.RegenerateClick) },
|
||||
onCopyClick = { viewModel.trySendAction(GeneratorAction.CopyClick) },
|
||||
onSaveClick = { viewModel.trySendAction(GeneratorAction.SaveClick) },
|
||||
onUsernameSubStateOptionClicked = onUsernameOptionClicked,
|
||||
passwordHandlers = passwordHandlers,
|
||||
passphraseHandlers = passphraseHandlers,
|
||||
@@ -328,7 +324,6 @@ private fun ModalAppBar(
|
||||
generatorMode: GeneratorMode.Modal,
|
||||
scrollBehavior: TopAppBarScrollBehavior,
|
||||
onCloseClick: () -> Unit,
|
||||
onSaveClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
BitwardenTopAppBar(
|
||||
@@ -341,13 +336,6 @@ private fun ModalAppBar(
|
||||
GeneratorMode.Modal.Password -> TopAppBarDividerStyle.NONE
|
||||
is GeneratorMode.Modal.Username -> TopAppBarDividerStyle.ON_SCROLL
|
||||
},
|
||||
actions = {
|
||||
BitwardenTextButton(
|
||||
label = stringResource(id = BitwardenString.apply),
|
||||
onClick = onSaveClick,
|
||||
modifier = Modifier.testTag("SaveButton"),
|
||||
)
|
||||
},
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
@@ -362,6 +350,7 @@ private fun CoachMarkScope<ExploreGeneratorCoachMark>.ScrollContent(
|
||||
state: GeneratorState,
|
||||
onRegenerateClick: () -> Unit,
|
||||
onCopyClick: () -> Unit,
|
||||
onSaveClick: () -> Unit,
|
||||
onUsernameSubStateOptionClicked: (GeneratorState.MainType.Username.UsernameTypeOption) -> Unit,
|
||||
passwordHandlers: PasswordHandlers,
|
||||
passphraseHandlers: PassphraseHandlers,
|
||||
@@ -461,6 +450,7 @@ private fun CoachMarkScope<ExploreGeneratorCoachMark>.ScrollContent(
|
||||
onShowPreviousCoachMark = onShowPreviousCoachMark,
|
||||
onDismissCoachMark = onDismissCoachMark,
|
||||
onShowNextCoachMark = onShowNextCoachMark,
|
||||
onCopyClick = onCopyClick.takeIf { state.generatorMode is GeneratorMode.Modal },
|
||||
modifier = Modifier
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth(),
|
||||
@@ -469,40 +459,84 @@ private fun CoachMarkScope<ExploreGeneratorCoachMark>.ScrollContent(
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
coachMarkHighlightItem(
|
||||
key = ExploreGeneratorCoachMark.COPY_PASSWORD_BUTTON,
|
||||
title = BitwardenString.coachmark_6_of_6.asText(),
|
||||
description = BitwardenString
|
||||
.after_you_save_your_new_password_to_bitwarden_don_t_forget_to_update_it_on_your_account_website
|
||||
.asText(),
|
||||
shape = CoachMarkHighlightShape.RoundedRectangle(radius = 50f),
|
||||
onDismiss = onDismissCoachMark,
|
||||
leftAction = {
|
||||
CoachMarkActionText(
|
||||
actionLabel = stringResource(BitwardenString.back),
|
||||
onActionClick = onShowPreviousCoachMark,
|
||||
)
|
||||
},
|
||||
rightAction = {
|
||||
CoachMarkActionText(
|
||||
actionLabel = stringResource(BitwardenString.done_text),
|
||||
onActionClick = onCoachMarkComplete,
|
||||
)
|
||||
},
|
||||
modifier = Modifier.standardHorizontalMargin(windowAdaptiveInfo = windowAdaptiveInfo),
|
||||
) {
|
||||
BitwardenFilledButton(
|
||||
label = stringResource(id = BitwardenString.copy),
|
||||
onClick = onCopyClick,
|
||||
modifier = Modifier
|
||||
.testTag(tag = "CopyValueButton")
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
when (state.generatorMode) {
|
||||
GeneratorMode.Default -> {
|
||||
@Suppress("MaxLineLength")
|
||||
coachMarkHighlightItem(
|
||||
key = ExploreGeneratorCoachMark.COPY_PASSWORD_BUTTON,
|
||||
title = BitwardenString.coachmark_6_of_6.asText(),
|
||||
description = BitwardenString
|
||||
.after_you_save_your_new_password_to_bitwarden_don_t_forget_to_update_it_on_your_account_website
|
||||
.asText(),
|
||||
shape = CoachMarkHighlightShape.RoundedRectangle(radius = 50f),
|
||||
onDismiss = onDismissCoachMark,
|
||||
leftAction = {
|
||||
CoachMarkActionText(
|
||||
actionLabel = stringResource(id = BitwardenString.back),
|
||||
onActionClick = onShowPreviousCoachMark,
|
||||
)
|
||||
},
|
||||
rightAction = {
|
||||
CoachMarkActionText(
|
||||
actionLabel = stringResource(id = BitwardenString.done_text),
|
||||
onActionClick = onCoachMarkComplete,
|
||||
)
|
||||
},
|
||||
modifier = Modifier
|
||||
.standardHorizontalMargin(windowAdaptiveInfo = windowAdaptiveInfo),
|
||||
) {
|
||||
BitwardenFilledButton(
|
||||
label = stringResource(id = BitwardenString.copy),
|
||||
onClick = onCopyClick,
|
||||
modifier = Modifier
|
||||
.testTag(tag = "CopyValueButton")
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
is GeneratorMode.Modal -> {
|
||||
item(key = "use_value_button") {
|
||||
when (state.selectedType) {
|
||||
is GeneratorState.MainType.Passphrase -> {
|
||||
BitwardenFilledButton(
|
||||
label = stringResource(id = BitwardenString.use_this_passphrase),
|
||||
onClick = onSaveClick,
|
||||
modifier = Modifier
|
||||
.testTag(tag = "UsePassphraseButton")
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
|
||||
is GeneratorState.MainType.Password -> {
|
||||
BitwardenFilledButton(
|
||||
label = stringResource(id = BitwardenString.use_this_password),
|
||||
onClick = onSaveClick,
|
||||
modifier = Modifier
|
||||
.testTag(tag = "UsePasswordButton")
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
|
||||
is GeneratorState.MainType.Username -> {
|
||||
BitwardenFilledButton(
|
||||
label = stringResource(id = BitwardenString.use_this_username),
|
||||
onClick = onSaveClick,
|
||||
modifier = Modifier
|
||||
.testTag(tag = "UseUsernameButton")
|
||||
.standardHorizontalMargin()
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
Spacer(modifier = Modifier.height(height = 24.dp))
|
||||
}
|
||||
|
||||
when (val selectedType = state.selectedType) {
|
||||
@@ -574,6 +608,7 @@ private fun CoachMarkScope<ExploreGeneratorCoachMark>.GeneratedStringItem(
|
||||
onShowPreviousCoachMark: () -> Unit,
|
||||
onDismissCoachMark: () -> Unit,
|
||||
onShowNextCoachMark: () -> Unit,
|
||||
onCopyClick: (() -> Unit)?,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
BitwardenTextField(
|
||||
@@ -610,6 +645,14 @@ private fun CoachMarkScope<ExploreGeneratorCoachMark>.GeneratedStringItem(
|
||||
modifier = Modifier.testTag("RegenerateValueButton"),
|
||||
)
|
||||
}
|
||||
onCopyClick?.let {
|
||||
BitwardenStandardIconButton(
|
||||
vectorIconRes = BitwardenDrawable.ic_copy,
|
||||
contentDescription = stringResource(id = BitwardenString.copy),
|
||||
onClick = it,
|
||||
modifier = Modifier.testTag(tag = "CopyValueButton"),
|
||||
)
|
||||
}
|
||||
},
|
||||
onValueChange = {},
|
||||
readOnly = true,
|
||||
|
||||
+113
-20
@@ -86,27 +86,137 @@ class GeneratorScreenTest : BitwardenComposeTest() {
|
||||
composeTestRule
|
||||
.onNodeWithContentDescription(label = "Close")
|
||||
.assertIsDisplayed()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on use this password click should send SaveClick`() {
|
||||
updateState(DEFAULT_STATE.copy(generatorMode = GeneratorMode.Modal.Password))
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithText(text = "Apply")
|
||||
.onNodeWithText(text = "Use this password")
|
||||
.performClick()
|
||||
|
||||
verify {
|
||||
viewModel.trySendAction(GeneratorAction.SaveClick)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on copy icon click with password mode should send CopyClick`() {
|
||||
updateState(
|
||||
DEFAULT_STATE.copy(
|
||||
generatorMode = GeneratorMode.Modal.Password,
|
||||
selectedType = GeneratorState.MainType.Password(),
|
||||
),
|
||||
)
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithContentDescription(label = "Copy")
|
||||
.performClick()
|
||||
|
||||
verify {
|
||||
viewModel.trySendAction(GeneratorAction.CopyClick)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `ModalAppBar should be displayed for Passphrase Mode`() {
|
||||
updateState(
|
||||
DEFAULT_STATE.copy(
|
||||
generatorMode = GeneratorMode.Modal.Password,
|
||||
selectedType = GeneratorState.MainType.Passphrase(),
|
||||
),
|
||||
)
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithContentDescription(label = "Close")
|
||||
.assertIsDisplayed()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on use this passphrase click should send SaveClick`() {
|
||||
updateState(
|
||||
DEFAULT_STATE.copy(
|
||||
generatorMode = GeneratorMode.Modal.Password,
|
||||
selectedType = GeneratorState.MainType.Passphrase(),
|
||||
),
|
||||
)
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithText(text = "Use this passphrase")
|
||||
.performClick()
|
||||
|
||||
verify {
|
||||
viewModel.trySendAction(GeneratorAction.SaveClick)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on copy icon click with passphrase mode should send CopyClick`() {
|
||||
updateState(
|
||||
DEFAULT_STATE.copy(
|
||||
generatorMode = GeneratorMode.Modal.Password,
|
||||
selectedType = GeneratorState.MainType.Passphrase(),
|
||||
),
|
||||
)
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithContentDescription(label = "Copy")
|
||||
.performClick()
|
||||
|
||||
verify {
|
||||
viewModel.trySendAction(GeneratorAction.CopyClick)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `ModalAppBar should be displayed for Username Mode`() {
|
||||
updateState(
|
||||
DEFAULT_STATE.copy(
|
||||
generatorMode = GeneratorMode.Modal.Username(website = null),
|
||||
selectedType = GeneratorState.MainType.Username(),
|
||||
),
|
||||
)
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithContentDescription(label = "Close")
|
||||
.assertIsDisplayed()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on use this username click should send SaveClick`() {
|
||||
updateState(
|
||||
DEFAULT_STATE.copy(
|
||||
generatorMode = GeneratorMode.Modal.Username(website = null),
|
||||
selectedType = GeneratorState.MainType.Username(),
|
||||
),
|
||||
)
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithText(text = "Apply")
|
||||
.assertIsDisplayed()
|
||||
.onNodeWithText(text = "Use this username")
|
||||
.performClick()
|
||||
|
||||
verify {
|
||||
viewModel.trySendAction(GeneratorAction.SaveClick)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on copy icon click with username mode should send CopyClick`() {
|
||||
updateState(
|
||||
DEFAULT_STATE.copy(
|
||||
generatorMode = GeneratorMode.Modal.Username(website = null),
|
||||
selectedType = GeneratorState.MainType.Username(),
|
||||
),
|
||||
)
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithContentDescription(label = "Copy")
|
||||
.performClick()
|
||||
|
||||
verify {
|
||||
viewModel.trySendAction(GeneratorAction.CopyClick)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -126,23 +236,6 @@ class GeneratorScreenTest : BitwardenComposeTest() {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on Apply click should send SaveClick`() {
|
||||
updateState(
|
||||
DEFAULT_STATE.copy(
|
||||
generatorMode = GeneratorMode.Modal.Username(website = null),
|
||||
),
|
||||
)
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithText(text = "Apply")
|
||||
.performClick()
|
||||
|
||||
verify {
|
||||
viewModel.trySendAction(GeneratorAction.SaveClick)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `DefaultAppBar should be displayed for Default Mode`() {
|
||||
updateState(DEFAULT_STATE.copy(generatorMode = GeneratorMode.Default))
|
||||
|
||||
@@ -1443,4 +1443,7 @@ Do you want to switch to this account?</string>
|
||||
<string name="only_allows_invited_users_to_create_accounts">%1$s only allows invited users to create accounts.</string>
|
||||
<string name="stepper_decrement_format" comment="Used to announce the value that will be decremented when a button is pressed">Decrease %1$s</string>
|
||||
<string name="stepper_increment_format" comment="Used to announce the value that will be incremented when a button is pressed">Increase %1$s</string>
|
||||
<string name="use_this_passphrase">Use this passphrase</string>
|
||||
<string name="use_this_password">Use this password</string>
|
||||
<string name="use_this_username">Use this username</string>
|
||||
</resources>
|
||||
Reference in new issue
Block a user