mirror of
https://github.com/bitwarden/android.git
synced 2026-10-21 10:40:49 -05:00
PM-42839, PM-42840, PM-42841, PM-42842: bug: Minor DatePicker bug fixes (#7343)
This commit is contained in:
1 parent
301f1069e3
commit
8a3307c042
8 files changed
+200
-89
No files matched your search
+2
@@ -96,6 +96,7 @@ fun LazyListScope.vaultAddEditLicenseItems(
|
||||
currentDate = licenseState.dateOfBirth,
|
||||
onDateSelect = licenseHandlers.onDateOfBirthChange,
|
||||
textFieldTestTag = "LicenseDateOfBirthEntry",
|
||||
allowFutureDates = false,
|
||||
cardStyle = CardStyle.Middle(),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
@@ -148,6 +149,7 @@ fun LazyListScope.vaultAddEditLicenseItems(
|
||||
currentDate = licenseState.issueDate,
|
||||
onDateSelect = licenseHandlers.onIssueDateChange,
|
||||
textFieldTestTag = "LicenseIssueDateEntry",
|
||||
allowFutureDates = false,
|
||||
cardStyle = CardStyle.Middle(),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
|
||||
+2
@@ -69,6 +69,7 @@ fun LazyListScope.vaultAddEditPassportItems(
|
||||
currentDate = passportState.dateOfBirth,
|
||||
onDateSelect = passportHandlers.onDateOfBirthChange,
|
||||
textFieldTestTag = "PassportDateOfBirthEntry",
|
||||
allowFutureDates = false,
|
||||
cardStyle = CardStyle.Middle(),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
@@ -188,6 +189,7 @@ fun LazyListScope.vaultAddEditPassportItems(
|
||||
currentDate = passportState.issueDate,
|
||||
onDateSelect = passportHandlers.onIssueDateChange,
|
||||
textFieldTestTag = "PassportIssueDateEntry",
|
||||
allowFutureDates = false,
|
||||
cardStyle = CardStyle.Middle(),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
|
||||
+1
-1
@@ -1010,7 +1010,7 @@ class GeneratorScreenTest : BitwardenComposeTest() {
|
||||
|
||||
// Opens the menu
|
||||
composeTestRule
|
||||
.onNodeWithContentDescription(label = "null. Service")
|
||||
.onNodeWithContentDescription(label = "Service")
|
||||
.performScrollTo()
|
||||
.performClick()
|
||||
|
||||
|
||||
+90
-43
@@ -2922,7 +2922,7 @@ class VaultAddEditScreenTest : BitwardenComposeTest() {
|
||||
fun `in ItemType_License the date of birth should display the selected date from the state`() {
|
||||
mutableStateFlow.value = DEFAULT_STATE_LICENSE
|
||||
composeTestRule
|
||||
.onNodeWithContentDescriptionAfterScroll(label = "null. Date of birth")
|
||||
.onNodeWithContentDescriptionAfterScroll(label = "Date of birth")
|
||||
.assertIsDisplayed()
|
||||
|
||||
mutableStateFlow.update { currentState ->
|
||||
@@ -2939,13 +2939,21 @@ class VaultAddEditScreenTest : BitwardenComposeTest() {
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `in ItemType_License clicking the date of birth clear button should trigger DateOfBirthChange with null`() {
|
||||
mutableStateFlow.value = DEFAULT_STATE_LICENSE
|
||||
mutableStateFlow.value = DEFAULT_STATE_LICENSE.copy(
|
||||
viewState = VaultAddEditState.ViewState.Content(
|
||||
common = VaultAddEditState.ViewState.Content.Common(),
|
||||
type = VaultAddEditState.ViewState.Content.ItemType.License(
|
||||
dateOfBirth = LocalDate.of(2026, 9, 10),
|
||||
),
|
||||
isIndividualVaultDisabled = false,
|
||||
),
|
||||
)
|
||||
composeTestRule
|
||||
.onNodeWithContentDescriptionAfterScroll(label = "null. Date of birth")
|
||||
.performClick()
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithText(text = "Clear")
|
||||
.onNodeWithContentDescriptionAfterScroll(label = "September 10, 2026. Date of birth")
|
||||
.onChildren()
|
||||
.filterToOne(matcher = hasText(text = "Date of birth"))
|
||||
.onChildren()
|
||||
.filterToOne(matcher = hasContentDescription(value = "Clear"))
|
||||
.performClick()
|
||||
|
||||
verify {
|
||||
@@ -2959,7 +2967,7 @@ class VaultAddEditScreenTest : BitwardenComposeTest() {
|
||||
fun `in ItemType_License the issue date should display the selected date from the state`() {
|
||||
mutableStateFlow.value = DEFAULT_STATE_LICENSE
|
||||
composeTestRule
|
||||
.onNodeWithContentDescriptionAfterScroll(label = "null. Issue date")
|
||||
.onNodeWithContentDescriptionAfterScroll(label = "Issue date")
|
||||
.assertIsDisplayed()
|
||||
|
||||
mutableStateFlow.update { currentState ->
|
||||
@@ -2976,13 +2984,21 @@ class VaultAddEditScreenTest : BitwardenComposeTest() {
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `in ItemType_License clicking the issue date clear button should trigger IssueDateChange with null`() {
|
||||
mutableStateFlow.value = DEFAULT_STATE_LICENSE
|
||||
mutableStateFlow.value = DEFAULT_STATE_LICENSE.copy(
|
||||
viewState = VaultAddEditState.ViewState.Content(
|
||||
common = VaultAddEditState.ViewState.Content.Common(),
|
||||
type = VaultAddEditState.ViewState.Content.ItemType.License(
|
||||
issueDate = LocalDate.of(2026, 9, 10),
|
||||
),
|
||||
isIndividualVaultDisabled = false,
|
||||
),
|
||||
)
|
||||
composeTestRule
|
||||
.onNodeWithContentDescriptionAfterScroll(label = "null. Issue date")
|
||||
.performClick()
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithText(text = "Clear")
|
||||
.onNodeWithContentDescriptionAfterScroll(label = "September 10, 2026. Issue date")
|
||||
.onChildren()
|
||||
.filterToOne(matcher = hasText(text = "Issue date"))
|
||||
.onChildren()
|
||||
.filterToOne(matcher = hasContentDescription(value = "Clear"))
|
||||
.performClick()
|
||||
|
||||
verify {
|
||||
@@ -2997,7 +3013,7 @@ class VaultAddEditScreenTest : BitwardenComposeTest() {
|
||||
fun `in ItemType_License the expiration date should display the selected date from the state`() {
|
||||
mutableStateFlow.value = DEFAULT_STATE_LICENSE
|
||||
composeTestRule
|
||||
.onNodeWithContentDescriptionAfterScroll(label = "null. Expiration date")
|
||||
.onNodeWithContentDescriptionAfterScroll(label = "Expiration date")
|
||||
.assertIsDisplayed()
|
||||
|
||||
mutableStateFlow.update { currentState ->
|
||||
@@ -3014,13 +3030,21 @@ class VaultAddEditScreenTest : BitwardenComposeTest() {
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `in ItemType_License clicking the expiration date clear button should trigger ExpirationDateChange with null`() {
|
||||
mutableStateFlow.value = DEFAULT_STATE_LICENSE
|
||||
mutableStateFlow.value = DEFAULT_STATE_LICENSE.copy(
|
||||
viewState = VaultAddEditState.ViewState.Content(
|
||||
common = VaultAddEditState.ViewState.Content.Common(),
|
||||
type = VaultAddEditState.ViewState.Content.ItemType.License(
|
||||
expirationDate = LocalDate.of(2026, 9, 10),
|
||||
),
|
||||
isIndividualVaultDisabled = false,
|
||||
),
|
||||
)
|
||||
composeTestRule
|
||||
.onNodeWithContentDescriptionAfterScroll(label = "null. Expiration date")
|
||||
.performClick()
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithText(text = "Clear")
|
||||
.onNodeWithContentDescriptionAfterScroll(label = "September 10, 2026. Expiration date")
|
||||
.onChildren()
|
||||
.filterToOne(matcher = hasText(text = "Expiration date"))
|
||||
.onChildren()
|
||||
.filterToOne(matcher = hasContentDescription(value = "Clear"))
|
||||
.performClick()
|
||||
|
||||
verify {
|
||||
@@ -3201,7 +3225,7 @@ class VaultAddEditScreenTest : BitwardenComposeTest() {
|
||||
fun `in ItemType_Passport the date of birth should display the selected date from the state`() {
|
||||
mutableStateFlow.value = DEFAULT_STATE_PASSPORT
|
||||
composeTestRule
|
||||
.onNodeWithContentDescriptionAfterScroll(label = "null. Date of birth")
|
||||
.onNodeWithContentDescriptionAfterScroll(label = "Date of birth")
|
||||
.assertIsDisplayed()
|
||||
|
||||
mutableStateFlow.update { currentState ->
|
||||
@@ -3218,13 +3242,21 @@ class VaultAddEditScreenTest : BitwardenComposeTest() {
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `in ItemType_Passport clicking the date of birth clear button should trigger DateOfBirthChange with null`() {
|
||||
mutableStateFlow.value = DEFAULT_STATE_PASSPORT
|
||||
mutableStateFlow.value = DEFAULT_STATE_PASSPORT.copy(
|
||||
viewState = VaultAddEditState.ViewState.Content(
|
||||
common = VaultAddEditState.ViewState.Content.Common(),
|
||||
type = VaultAddEditState.ViewState.Content.ItemType.Passport(
|
||||
dateOfBirth = LocalDate.of(2026, 9, 10),
|
||||
),
|
||||
isIndividualVaultDisabled = false,
|
||||
),
|
||||
)
|
||||
composeTestRule
|
||||
.onNodeWithContentDescriptionAfterScroll(label = "null. Date of birth")
|
||||
.performClick()
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithText(text = "Clear")
|
||||
.onNodeWithContentDescriptionAfterScroll(label = "September 10, 2026. Date of birth")
|
||||
.onChildren()
|
||||
.filterToOne(matcher = hasText(text = "Date of birth"))
|
||||
.onChildren()
|
||||
.filterToOne(matcher = hasContentDescription(value = "Clear"))
|
||||
.performClick()
|
||||
|
||||
verify {
|
||||
@@ -3234,12 +3266,11 @@ class VaultAddEditScreenTest : BitwardenComposeTest() {
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `in ItemType_Passport the issue date should display the selected date from the state`() {
|
||||
mutableStateFlow.value = DEFAULT_STATE_PASSPORT
|
||||
composeTestRule
|
||||
.onNodeWithContentDescriptionAfterScroll(label = "null. Issue date")
|
||||
.onNodeWithContentDescriptionAfterScroll(label = "Issue date")
|
||||
.assertIsDisplayed()
|
||||
|
||||
mutableStateFlow.update { currentState ->
|
||||
@@ -3256,13 +3287,21 @@ class VaultAddEditScreenTest : BitwardenComposeTest() {
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `in ItemType_Passport clicking the issue date clear button should trigger IssueDateChange with null`() {
|
||||
mutableStateFlow.value = DEFAULT_STATE_PASSPORT
|
||||
mutableStateFlow.value = DEFAULT_STATE_PASSPORT.copy(
|
||||
viewState = VaultAddEditState.ViewState.Content(
|
||||
common = VaultAddEditState.ViewState.Content.Common(),
|
||||
type = VaultAddEditState.ViewState.Content.ItemType.Passport(
|
||||
issueDate = LocalDate.of(2026, 9, 10),
|
||||
),
|
||||
isIndividualVaultDisabled = false,
|
||||
),
|
||||
)
|
||||
composeTestRule
|
||||
.onNodeWithContentDescriptionAfterScroll(label = "null. Issue date")
|
||||
.performClick()
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithText(text = "Clear")
|
||||
.onNodeWithContentDescriptionAfterScroll(label = "September 10, 2026. Issue date")
|
||||
.onChildren()
|
||||
.filterToOne(matcher = hasText(text = "Issue date"))
|
||||
.onChildren()
|
||||
.filterToOne(matcher = hasContentDescription(value = "Clear"))
|
||||
.performClick()
|
||||
|
||||
verify {
|
||||
@@ -3277,7 +3316,7 @@ class VaultAddEditScreenTest : BitwardenComposeTest() {
|
||||
fun `in ItemType_Passport the expiration date should display the selected date from the state`() {
|
||||
mutableStateFlow.value = DEFAULT_STATE_PASSPORT
|
||||
composeTestRule
|
||||
.onNodeWithContentDescriptionAfterScroll(label = "null. Expiration date")
|
||||
.onNodeWithContentDescriptionAfterScroll(label = "Expiration date")
|
||||
.assertIsDisplayed()
|
||||
|
||||
mutableStateFlow.update { currentState ->
|
||||
@@ -3294,13 +3333,21 @@ class VaultAddEditScreenTest : BitwardenComposeTest() {
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `in ItemType_Passport clicking the expiration date clear button should trigger ExpirationDateChange with null`() {
|
||||
mutableStateFlow.value = DEFAULT_STATE_PASSPORT
|
||||
mutableStateFlow.value = DEFAULT_STATE_PASSPORT.copy(
|
||||
viewState = VaultAddEditState.ViewState.Content(
|
||||
common = VaultAddEditState.ViewState.Content.Common(),
|
||||
type = VaultAddEditState.ViewState.Content.ItemType.Passport(
|
||||
expirationDate = LocalDate.of(2026, 9, 10),
|
||||
),
|
||||
isIndividualVaultDisabled = false,
|
||||
),
|
||||
)
|
||||
composeTestRule
|
||||
.onNodeWithContentDescriptionAfterScroll(label = "null. Expiration date")
|
||||
.performClick()
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithText(text = "Clear")
|
||||
.onNodeWithContentDescriptionAfterScroll(label = "September 10, 2026. Expiration date")
|
||||
.onChildren()
|
||||
.filterToOne(matcher = hasText(text = "Expiration date"))
|
||||
.onChildren()
|
||||
.filterToOne(matcher = hasContentDescription(value = "Clear"))
|
||||
.performClick()
|
||||
|
||||
verify {
|
||||
|
||||
+3
-1
@@ -23,6 +23,7 @@ import androidx.compose.ui.semantics.CustomAccessibilityAction
|
||||
import androidx.compose.ui.semantics.Role
|
||||
import androidx.compose.ui.semantics.contentDescription
|
||||
import androidx.compose.ui.semantics.customActions
|
||||
import androidx.compose.ui.semantics.hideFromAccessibility
|
||||
import androidx.compose.ui.semantics.role
|
||||
import androidx.compose.ui.semantics.semantics
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
@@ -113,7 +114,7 @@ fun BitwardenTextSelectionButton(
|
||||
.defaultMinSize(minHeight = 60.dp)
|
||||
.semantics {
|
||||
role = semanticRole
|
||||
contentDescription = "$selectedOption. $label"
|
||||
contentDescription = "${selectedOption?.let { "$it. " }.orEmpty()}$label"
|
||||
customActions = persistentListOfNotNull(
|
||||
helpData?.let {
|
||||
CustomAccessibilityAction(
|
||||
@@ -180,6 +181,7 @@ fun BitwardenTextSelectionButton(
|
||||
bitwardenTextFieldColors()
|
||||
},
|
||||
modifier = Modifier
|
||||
.semantics { this.hideFromAccessibility() }
|
||||
.nullableTestTag(tag = textFieldTestTag)
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
|
||||
+60
-32
@@ -4,8 +4,7 @@ import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.FlowRow
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.heightIn
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.requiredWidth
|
||||
@@ -16,6 +15,7 @@ import androidx.compose.material3.BasicAlertDialog
|
||||
import androidx.compose.material3.DatePicker
|
||||
import androidx.compose.material3.DatePickerColors
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.SelectableDates
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.rememberDatePickerState
|
||||
import androidx.compose.runtime.Composable
|
||||
@@ -29,8 +29,10 @@ import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.bitwarden.ui.platform.components.button.BitwardenTextButton
|
||||
import com.bitwarden.ui.platform.components.field.color.bitwardenTextFieldColors
|
||||
import com.bitwarden.ui.platform.composition.LocalClock
|
||||
import com.bitwarden.ui.platform.resource.BitwardenString
|
||||
import com.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
import java.time.Clock
|
||||
import java.time.Instant
|
||||
import java.time.LocalDate
|
||||
import java.time.ZoneOffset
|
||||
@@ -41,6 +43,10 @@ import java.time.ZoneOffset
|
||||
* @param initialDate The initial [LocalDate] to display.
|
||||
* @param onDateSelect The callback invoked with the selected [LocalDate] when the user confirms.
|
||||
* @param onDismissRequest The callback invoked when the dialog is dismissed.
|
||||
* @param allowPastDates Indicates that this date picker allows past dates.
|
||||
* @param allowFutureDates Indicates that this date picker allows future dates.
|
||||
* @param allowToday Indicates that this date picker allows this date to be selected.
|
||||
* @param clock The system clock.
|
||||
*/
|
||||
@Suppress("LongMethod")
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@@ -49,12 +55,40 @@ fun BitwardenDatePickerDialog(
|
||||
initialDate: LocalDate?,
|
||||
onDateSelect: (LocalDate?) -> Unit,
|
||||
onDismissRequest: () -> Unit,
|
||||
allowPastDates: Boolean = true,
|
||||
allowFutureDates: Boolean = true,
|
||||
allowToday: Boolean = true,
|
||||
clock: Clock = LocalClock.current,
|
||||
) {
|
||||
val datePickerState = rememberDatePickerState(
|
||||
initialSelectedDateMillis = initialDate
|
||||
?.atStartOfDay(ZoneOffset.UTC)
|
||||
?.toInstant()
|
||||
?.toEpochMilli(),
|
||||
selectableDates = object : SelectableDates {
|
||||
override fun isSelectableDate(
|
||||
utcTimeMillis: Long,
|
||||
): Boolean {
|
||||
val today = LocalDate.now(clock)
|
||||
val date = Instant.ofEpochMilli(utcTimeMillis).atZone(ZoneOffset.UTC).toLocalDate()
|
||||
return when {
|
||||
date.isBefore(today) -> allowPastDates
|
||||
date.isAfter(today) -> allowFutureDates
|
||||
else -> allowToday
|
||||
}
|
||||
}
|
||||
|
||||
override fun isSelectableYear(
|
||||
year: Int,
|
||||
): Boolean {
|
||||
val clockYear = clock.instant().atZone(clock.zone).year
|
||||
return when {
|
||||
clockYear > year -> allowPastDates
|
||||
clockYear < year -> allowFutureDates
|
||||
else -> true
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
BasicAlertDialog(
|
||||
onDismissRequest = onDismissRequest,
|
||||
@@ -86,38 +120,32 @@ fun BitwardenDatePickerDialog(
|
||||
colors = colors,
|
||||
)
|
||||
}
|
||||
Row(modifier = Modifier.padding(bottom = 8.dp, start = 12.dp)) {
|
||||
FlowRow(
|
||||
horizontalArrangement = Arrangement.End,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(bottom = 8.dp, start = 16.dp, end = 16.dp),
|
||||
) {
|
||||
BitwardenTextButton(
|
||||
label = stringResource(id = BitwardenString.clear),
|
||||
onClick = { onDateSelect(null) },
|
||||
modifier = Modifier.testTag(tag = "ClearAlertButton"),
|
||||
label = stringResource(id = BitwardenString.cancel),
|
||||
onClick = onDismissRequest,
|
||||
modifier = Modifier.testTag(tag = "DismissAlertButton"),
|
||||
)
|
||||
BitwardenTextButton(
|
||||
label = stringResource(id = BitwardenString.okay),
|
||||
onClick = {
|
||||
datePickerState
|
||||
.selectedDateMillis
|
||||
?.let { millis ->
|
||||
Instant
|
||||
.ofEpochMilli(millis)
|
||||
.atZone(ZoneOffset.UTC)
|
||||
.toLocalDate()
|
||||
}
|
||||
.let(onDateSelect)
|
||||
},
|
||||
modifier = Modifier.testTag(tag = "AcceptAlertButton"),
|
||||
)
|
||||
Spacer(modifier = Modifier.weight(weight = 1f))
|
||||
FlowRow(
|
||||
horizontalArrangement = Arrangement.End,
|
||||
modifier = Modifier.padding(horizontal = 8.dp),
|
||||
) {
|
||||
BitwardenTextButton(
|
||||
label = stringResource(id = BitwardenString.cancel),
|
||||
onClick = onDismissRequest,
|
||||
modifier = Modifier.testTag(tag = "DismissAlertButton"),
|
||||
)
|
||||
BitwardenTextButton(
|
||||
label = stringResource(id = BitwardenString.okay),
|
||||
onClick = {
|
||||
datePickerState
|
||||
.selectedDateMillis
|
||||
?.let { millis ->
|
||||
Instant
|
||||
.ofEpochMilli(millis)
|
||||
.atZone(ZoneOffset.UTC)
|
||||
.toLocalDate()
|
||||
}
|
||||
.let(onDateSelect)
|
||||
},
|
||||
modifier = Modifier.testTag(tag = "AcceptAlertButton"),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+41
-12
@@ -2,22 +2,30 @@ package com.bitwarden.ui.platform.components.dropdown
|
||||
|
||||
import androidx.compose.foundation.layout.ColumnScope
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.RowScope
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.minimumInteractiveComponentSize
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.semantics.onClick
|
||||
import androidx.compose.ui.semantics.semantics
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.bitwarden.core.data.util.toFormattedDateStyle
|
||||
import com.bitwarden.ui.platform.components.animation.AnimateNullableContentVisibility
|
||||
import com.bitwarden.ui.platform.components.button.BitwardenStandardIconButton
|
||||
import com.bitwarden.ui.platform.components.button.BitwardenTextSelectionButton
|
||||
import com.bitwarden.ui.platform.components.button.model.BitwardenHelpButtonData
|
||||
import com.bitwarden.ui.platform.components.dialog.BitwardenDatePickerDialog
|
||||
import com.bitwarden.ui.platform.components.model.CardStyle
|
||||
import com.bitwarden.ui.platform.components.util.rememberVectorPainter
|
||||
import com.bitwarden.ui.platform.composition.LocalClock
|
||||
import com.bitwarden.ui.platform.resource.BitwardenDrawable
|
||||
import com.bitwarden.ui.platform.resource.BitwardenString
|
||||
import com.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
import java.time.Clock
|
||||
import java.time.LocalDate
|
||||
@@ -36,10 +44,10 @@ import java.time.format.FormatStyle
|
||||
* @param helpData An optional [BitwardenHelpButtonData], representing the help button.
|
||||
* @param insets Inner padding to be applied within the card.
|
||||
* @param textFieldTestTag The optional test tag associated with the inner text field.
|
||||
* @param actionsPadding Padding to be applied to the [actions] block.
|
||||
* @param actions A lambda containing the set of actions (usually icons or similar) to display
|
||||
* in the trailing side. This lambda extends [RowScope], allowing flexibility in defining the
|
||||
* layout of the actions.
|
||||
* @param allowPastDates Indicates that the date picker allows past dates.
|
||||
* @param allowFutureDates Indicates that the date picker allows future dates.
|
||||
* @param allowToday Indicates that this date picker allows this date to be selected.
|
||||
* @param clock The system clock.
|
||||
*/
|
||||
@Composable
|
||||
fun BitwardenDatePickerButton(
|
||||
@@ -53,11 +61,13 @@ fun BitwardenDatePickerButton(
|
||||
helpData: BitwardenHelpButtonData? = null,
|
||||
insets: PaddingValues = PaddingValues(),
|
||||
textFieldTestTag: String? = null,
|
||||
actionsPadding: PaddingValues = PaddingValues(end = 4.dp),
|
||||
allowPastDates: Boolean = true,
|
||||
allowFutureDates: Boolean = true,
|
||||
allowToday: Boolean = true,
|
||||
clock: Clock = LocalClock.current,
|
||||
actions: @Composable RowScope.() -> Unit = {},
|
||||
) {
|
||||
var shouldShowDialog by rememberSaveable { mutableStateOf(value = false) }
|
||||
val openCalendarString = stringResource(id = BitwardenString.open_calendar)
|
||||
BitwardenTextSelectionButton(
|
||||
label = label,
|
||||
selectedOption = currentDate?.toFormattedDateStyle(
|
||||
@@ -67,14 +77,29 @@ fun BitwardenDatePickerButton(
|
||||
onClick = { shouldShowDialog = true },
|
||||
cardStyle = cardStyle,
|
||||
enabled = isEnabled,
|
||||
showChevron = true,
|
||||
showChevron = false,
|
||||
supportingContent = supportingContent,
|
||||
helpData = helpData,
|
||||
insets = insets,
|
||||
textFieldTestTag = textFieldTestTag,
|
||||
actionsPadding = actionsPadding,
|
||||
actions = actions,
|
||||
modifier = modifier,
|
||||
actions = {
|
||||
AnimateNullableContentVisibility(targetState = currentDate) {
|
||||
BitwardenStandardIconButton(
|
||||
vectorIconRes = BitwardenDrawable.ic_clear,
|
||||
contentDescription = stringResource(id = BitwardenString.clear),
|
||||
onClick = { onDateSelect(null) },
|
||||
isEnabled = isEnabled,
|
||||
)
|
||||
}
|
||||
Icon(
|
||||
painter = rememberVectorPainter(id = BitwardenDrawable.ic_chevron_down),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.minimumInteractiveComponentSize(),
|
||||
)
|
||||
},
|
||||
modifier = modifier.semantics(mergeDescendants = true) {
|
||||
this.onClick(label = openCalendarString, action = null)
|
||||
},
|
||||
)
|
||||
if (shouldShowDialog) {
|
||||
BitwardenDatePickerDialog(
|
||||
@@ -84,6 +109,10 @@ fun BitwardenDatePickerButton(
|
||||
shouldShowDialog = false
|
||||
},
|
||||
onDismissRequest = { shouldShowDialog = false },
|
||||
allowPastDates = allowPastDates,
|
||||
allowFutureDates = allowFutureDates,
|
||||
allowToday = allowToday,
|
||||
clock = clock,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -363,6 +363,7 @@ Scanning will happen automatically.</string>
|
||||
<string name="passphrase">Passphrase</string>
|
||||
<string name="word_separator">Word separator</string>
|
||||
<string name="clear">Clear</string>
|
||||
<string name="open_calendar">open calendar</string>
|
||||
<string name="generator">Generator</string>
|
||||
<string name="no_folders_to_list">There are no folders to list.</string>
|
||||
<string name="fingerprint_phrase">Fingerprint phrase</string>
|
||||
|
||||
Reference in new issue
Block a user