Add the UI for the overflow menu (#570)

This commit is contained in:
David Perez 2024-01-10 22:17:36 -06:00 committed by Álison Fernandes
parent e84e69b666
commit 0aa24d73d9
4 changed files with 244 additions and 0 deletions

View file

@ -25,11 +25,14 @@ import com.x8bit.bitwarden.ui.platform.components.BitwardenBasicDialog
import com.x8bit.bitwarden.ui.platform.components.BitwardenErrorContent
import com.x8bit.bitwarden.ui.platform.components.BitwardenLoadingContent
import com.x8bit.bitwarden.ui.platform.components.BitwardenLoadingDialog
import com.x8bit.bitwarden.ui.platform.components.BitwardenOverflowActionItem
import com.x8bit.bitwarden.ui.platform.components.BitwardenScaffold
import com.x8bit.bitwarden.ui.platform.components.BitwardenTextButton
import com.x8bit.bitwarden.ui.platform.components.BitwardenTopAppBar
import com.x8bit.bitwarden.ui.platform.components.LoadingDialogState
import com.x8bit.bitwarden.ui.platform.components.OverflowMenuItemData
import com.x8bit.bitwarden.ui.tools.feature.send.addsend.handlers.AddSendHandlers
import kotlinx.collections.immutable.persistentListOf
/**
* Displays new send UX.
@ -86,6 +89,40 @@ fun AddSendScreen(
{ viewModel.trySendAction(AddSendAction.SaveClick) }
},
)
if (!state.isAddMode) {
BitwardenOverflowActionItem(
menuItemDataList = persistentListOf(
OverflowMenuItemData(
text = stringResource(id = R.string.remove_password),
onClick = remember(viewModel) {
{
viewModel.trySendAction(
AddSendAction.RemovePasswordClick,
)
}
},
),
OverflowMenuItemData(
text = stringResource(id = R.string.copy_link),
onClick = remember(viewModel) {
{ viewModel.trySendAction(AddSendAction.CopyLinkClick) }
},
),
OverflowMenuItemData(
text = stringResource(id = R.string.share_link),
onClick = remember(viewModel) {
{ viewModel.trySendAction(AddSendAction.ShareLinkClick) }
},
),
OverflowMenuItemData(
text = stringResource(id = R.string.delete),
onClick = remember(viewModel) {
{ viewModel.trySendAction(AddSendAction.DeleteClick) }
},
),
),
)
}
},
)
},

View file

@ -93,6 +93,10 @@ class AddSendViewModel @Inject constructor(
}
override fun handleAction(action: AddSendAction): Unit = when (action) {
AddSendAction.CopyLinkClick -> handleCopyLinkClick()
AddSendAction.DeleteClick -> handleDeleteClick()
AddSendAction.RemovePasswordClick -> handleRemovePasswordClick()
AddSendAction.ShareLinkClick -> handleShareLinkClick()
is AddSendAction.CloseClick -> handleCloseClick()
is AddSendAction.DeletionDateChange -> handleDeletionDateChange(action)
is AddSendAction.ExpirationDateChange -> handleExpirationDateChange(action)
@ -150,6 +154,26 @@ class AddSendViewModel @Inject constructor(
}
}
private fun handleCopyLinkClick() {
// TODO Add copy link support (BIT-1435)
sendEvent(AddSendEvent.ShowToast("Not yet implemented"))
}
private fun handleDeleteClick() {
// TODO Add deletion support (BIT-1435)
sendEvent(AddSendEvent.ShowToast("Not yet implemented"))
}
private fun handleRemovePasswordClick() {
// TODO Add remove password support (BIT-1435)
sendEvent(AddSendEvent.ShowToast("Not yet implemented"))
}
private fun handleShareLinkClick() {
// TODO Add share link support (BIT-1435)
sendEvent(AddSendEvent.ShowToast("Not yet implemented"))
}
private fun handlePasswordChange(action: AddSendAction.PasswordChange) {
updateCommonContent {
it.copy(passwordInput = action.input)
@ -348,6 +372,11 @@ data class AddSendState(
is AddSendType.EditItem -> R.string.edit_send.asText()
}
/**
* Helper to determine if the UI should display the content in add send mode.
*/
val isAddMode: Boolean get() = addSendType is AddSendType.AddItem
/**
* Represents the specific view states for the [AddSendScreen].
*/
@ -463,6 +492,26 @@ sealed class AddSendEvent {
*/
sealed class AddSendAction {
/**
* User clicked the remove password button.
*/
data object RemovePasswordClick : AddSendAction()
/**
* User clicked the copy link button.
*/
data object CopyLinkClick : AddSendAction()
/**
* User clicked the share link button.
*/
data object ShareLinkClick : AddSendAction()
/**
* User clicked the delete button.
*/
data object DeleteClick : AddSendAction()
/**
* User clicked the close button.
*/

View file

@ -9,6 +9,8 @@ import androidx.compose.ui.test.filterToOne
import androidx.compose.ui.test.hasAnyAncestor
import androidx.compose.ui.test.hasSetTextAction
import androidx.compose.ui.test.isDialog
import androidx.compose.ui.test.isDisplayed
import androidx.compose.ui.test.isPopup
import androidx.compose.ui.test.onAllNodesWithText
import androidx.compose.ui.test.onNodeWithContentDescription
import androidx.compose.ui.test.onNodeWithText
@ -89,6 +91,110 @@ class AddSendScreenTest : BaseComposeTest() {
verify { viewModel.trySendAction(AddSendAction.SaveClick) }
}
@Test
fun `on overflow button click should display overflow menu`() {
mutableStateFlow.value = DEFAULT_STATE.copy(
addSendType = AddSendType.EditItem(sendItemId = "sendId"),
)
composeTestRule
.onNodeWithContentDescription("More")
.performClick()
composeTestRule
.onNodeWithText("Remove password")
.assert(hasAnyAncestor(isPopup()))
.isDisplayed()
composeTestRule
.onNodeWithText("Copy link")
.assert(hasAnyAncestor(isPopup()))
.isDisplayed()
composeTestRule
.onNodeWithText("Share link")
.assert(hasAnyAncestor(isPopup()))
.isDisplayed()
composeTestRule
.onNodeWithText("Delete")
.assert(hasAnyAncestor(isPopup()))
.isDisplayed()
}
@Test
fun `on overflow remove password button click should send RemovePasswordClick`() {
mutableStateFlow.value = DEFAULT_STATE.copy(
addSendType = AddSendType.EditItem(sendItemId = "sendId"),
)
composeTestRule
.onNodeWithContentDescription("More")
.performClick()
composeTestRule
.onNodeWithText("Remove password")
.performClick()
verify(exactly = 1) {
viewModel.trySendAction(AddSendAction.RemovePasswordClick)
}
}
@Test
fun `on overflow remove Share link button click should send ShareLinkClick`() {
mutableStateFlow.value = DEFAULT_STATE.copy(
addSendType = AddSendType.EditItem(sendItemId = "sendId"),
)
composeTestRule
.onNodeWithContentDescription("More")
.performClick()
composeTestRule
.onNodeWithText("Share link")
.performClick()
verify(exactly = 1) {
viewModel.trySendAction(AddSendAction.ShareLinkClick)
}
}
@Test
fun `on overflow remove Delete button click should send DeleteClick`() {
mutableStateFlow.value = DEFAULT_STATE.copy(
addSendType = AddSendType.EditItem(sendItemId = "sendId"),
)
composeTestRule
.onNodeWithContentDescription("More")
.performClick()
composeTestRule
.onNodeWithText("Delete")
.performClick()
verify(exactly = 1) {
viewModel.trySendAction(AddSendAction.DeleteClick)
}
}
@Test
fun `on overflow remove Copy link button click should send CopyLinkClick`() {
mutableStateFlow.value = DEFAULT_STATE.copy(
addSendType = AddSendType.EditItem(sendItemId = "sendId"),
)
composeTestRule
.onNodeWithContentDescription("More")
.performClick()
composeTestRule
.onNodeWithText("Copy link")
.performClick()
verify(exactly = 1) {
viewModel.trySendAction(AddSendAction.CopyLinkClick)
}
}
@Test
fun `on name input change should send NameChange`() {
composeTestRule

View file

@ -168,6 +168,58 @@ class AddSendViewModelTest : BaseViewModelTest() {
)
}
@Test
fun `CopyLinkClick should send ShowToast`() = runTest {
val viewModel = createViewModel(
state = DEFAULT_STATE.copy(addSendType = AddSendType.EditItem("sendId")),
addSendType = AddSendType.EditItem("sendId"),
)
viewModel.eventFlow.test {
viewModel.trySendAction(AddSendAction.CopyLinkClick)
assertEquals(AddSendEvent.ShowToast("Not yet implemented"), awaitItem())
}
}
@Test
fun `DeleteClick should send ShowToast`() = runTest {
val viewModel = createViewModel(
state = DEFAULT_STATE.copy(addSendType = AddSendType.EditItem("sendId")),
addSendType = AddSendType.EditItem("sendId"),
)
viewModel.eventFlow.test {
viewModel.trySendAction(AddSendAction.DeleteClick)
assertEquals(AddSendEvent.ShowToast("Not yet implemented"), awaitItem())
}
}
@Test
fun `RemovePasswordClick should send ShowToast`() = runTest {
val viewModel = createViewModel(
state = DEFAULT_STATE.copy(addSendType = AddSendType.EditItem("sendId")),
addSendType = AddSendType.EditItem("sendId"),
)
viewModel.eventFlow.test {
viewModel.trySendAction(AddSendAction.RemovePasswordClick)
assertEquals(AddSendEvent.ShowToast("Not yet implemented"), awaitItem())
}
}
@Test
fun `ShareLinkClick should send ShowToast`() = runTest {
val viewModel = createViewModel(
state = DEFAULT_STATE.copy(addSendType = AddSendType.EditItem("sendId")),
addSendType = AddSendType.EditItem("sendId"),
)
viewModel.eventFlow.test {
viewModel.trySendAction(AddSendAction.ShareLinkClick)
assertEquals(AddSendEvent.ShowToast("Not yet implemented"), awaitItem())
}
}
@Test
fun `DismissDialogClick should clear the dialog state`() {
val viewModel = createViewModel(