mirror of
https://github.com/bitwarden/android.git
synced 2026-11-10 01:15:13 -06:00
[PM-42843] fix: Stop date picker flashing when switching to calendar (#7536)
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
package com.bitwarden.ui.platform.base.util
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.ui.layout.LocalPinnableContainer
|
||||
import androidx.compose.ui.layout.PinnableContainer
|
||||
|
||||
/**
|
||||
* Pins the calling composable in its [pinnableContainer] for as long as it stays in the
|
||||
* composition, so a lazy layout keeps it composed even after it scrolls out of view.
|
||||
*
|
||||
* @param pinnableContainer The [PinnableContainer] to pin, typically the current lazy item.
|
||||
*/
|
||||
@Composable
|
||||
fun PinThisComposable(
|
||||
pinnableContainer: PinnableContainer? = LocalPinnableContainer.current,
|
||||
) {
|
||||
DisposableEffect(pinnableContainer) {
|
||||
val pinnedHandle = pinnableContainer?.pin()
|
||||
onDispose { pinnedHandle?.release() }
|
||||
}
|
||||
}
|
||||
+5
@@ -16,6 +16,7 @@ import androidx.compose.ui.semantics.onClick
|
||||
import androidx.compose.ui.semantics.semantics
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.bitwarden.core.data.util.toFormattedDateStyle
|
||||
import com.bitwarden.ui.platform.base.util.PinThisComposable
|
||||
import com.bitwarden.ui.platform.components.animation.AnimateNullableContentVisibility
|
||||
import com.bitwarden.ui.platform.components.button.BitwardenStandardIconButton
|
||||
import com.bitwarden.ui.platform.components.button.BitwardenTextSelectionButton
|
||||
@@ -102,6 +103,10 @@ fun BitwardenDatePickerButton(
|
||||
},
|
||||
)
|
||||
if (shouldShowDialog) {
|
||||
// Keep this button composed while the dialog is showing. In a lazy list, the button can
|
||||
// scroll out of view when the soft keyboard opens, and disposing it would dismiss the
|
||||
// dialog and recreate it once the keyboard closes.
|
||||
PinThisComposable()
|
||||
BitwardenDatePickerDialog(
|
||||
initialDate = currentDate,
|
||||
onDateSelect = { date ->
|
||||
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
package com.bitwarden.ui.platform.components.dropdown
|
||||
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.test.assertIsDisplayed
|
||||
import androidx.compose.ui.test.onNodeWithContentDescription
|
||||
import androidx.compose.ui.test.onNodeWithTag
|
||||
import androidx.compose.ui.test.onNodeWithText
|
||||
import androidx.compose.ui.test.performClick
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import com.bitwarden.ui.platform.base.BaseComposeTest
|
||||
import com.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.Test
|
||||
import java.time.Clock
|
||||
import java.time.Instant
|
||||
import java.time.ZoneOffset
|
||||
|
||||
class BitwardenDatePickerButtonTest : BaseComposeTest() {
|
||||
|
||||
@Test
|
||||
fun `dialog should stay open when switching modes after the button scrolls away`() {
|
||||
// Match a device, where tapping does not move focus to the tapped button.
|
||||
InstrumentationRegistry.getInstrumentation().setInTouchMode(true)
|
||||
lateinit var lazyListState: LazyListState
|
||||
setTestContent {
|
||||
BitwardenTheme {
|
||||
lazyListState = rememberLazyListState()
|
||||
LazyColumn(state = lazyListState) {
|
||||
item {
|
||||
BitwardenDatePickerButton(
|
||||
label = LABEL,
|
||||
currentDate = null,
|
||||
onDateSelect = {},
|
||||
cardStyle = null,
|
||||
clock = FIXED_CLOCK,
|
||||
)
|
||||
}
|
||||
items(count = 50) {
|
||||
Spacer(modifier = Modifier.height(height = 200.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
composeTestRule.onNodeWithText(text = LABEL).performClick()
|
||||
composeTestRule
|
||||
.onNodeWithContentDescription(label = "Switch to text input mode")
|
||||
.performClick()
|
||||
composeTestRule.onNodeWithText(text = "Entered date").assertIsDisplayed()
|
||||
|
||||
composeTestRule.runOnUiThread {
|
||||
runBlocking { lazyListState.scrollToItem(index = 40) }
|
||||
}
|
||||
composeTestRule
|
||||
.onNodeWithContentDescription(label = "Switch to calendar input mode")
|
||||
.performClick()
|
||||
|
||||
composeTestRule.onNodeWithTag(testTag = "DatePickerDialog").assertIsDisplayed()
|
||||
composeTestRule.onNodeWithText(text = "Selected date").assertIsDisplayed()
|
||||
}
|
||||
}
|
||||
|
||||
private const val LABEL: String = "Date of birth"
|
||||
|
||||
private val FIXED_CLOCK: Clock = Clock.fixed(
|
||||
Instant.parse("2026-10-26T12:00:00Z"),
|
||||
ZoneOffset.UTC,
|
||||
)
|
||||
Reference in New Issue
Block a user