[PM-38360] feat: Add reusable DateFieldPicker component#2731
[PM-38360] feat: Add reusable DateFieldPicker component#2731SaintPatrck wants to merge 7 commits into
Conversation
Bitwarden Claude Code ReviewOverall Assessment: APPROVE This PR adds a reusable Code Review DetailsNo new findings warranting inline comments. The latest commits directly address three of the four existing unresolved threads:
The unresolved thread on |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #2731 +/- ##
==========================================
- Coverage 87.21% 86.71% -0.51%
==========================================
Files 1918 2140 +222
Lines 173088 188608 +15520
==========================================
+ Hits 150952 163543 +12591
- Misses 22136 25065 +2929 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
| /// concurrency domains. Uses the POSIX locale and UTC time zone so output and parsing are | ||
| /// deterministic regardless of the device locale, and disables lenient parsing so invalid dates | ||
| /// such as `2023-02-30` fail to parse rather than rolling over. | ||
| static func iso8601DateOnlyFormatter() -> DateFormatter { |
There was a problem hiding this comment.
🤔 How is this different from the built-in ISO8601DateFormatter?
|
@RishikaSG-28 Figma didn't explicitly state which picker should be used for the dates, but I'm assuming this barrel spinner is expected since it's date only. I noticed there is no way to "Clear" a selected date so I've added a small "Clear" button. Can you take a look at the screenshot and let me know what you think. @bitwarden/team-ios @matt-livefront I'm not super familiar with the iOS native date picker. Is the the right approach to add a "Clear" button, or am I missing something that would handle clearing natively? |
|
@SaintPatrck Having it be a popup rather than a pseudo-keyboard or inline feels weird to me. The Contacts app still does a pseudo-keyboard; Reminders and Calendar put the date selector inline with a calendar; what are some Apple apps that do the popup (as it's possible I'm just not thinking of any offhand that do)? |
|
@SaintPatrck, Emily added the date picker when she designed new item types. I've tagged you in it, please have a look. |
|
@KatherineInCode @RishikaSG-28 I've updated the picker based on our conversation. LMK what you think about the updated screenshots. |
fedemkr
left a comment
There was a problem hiding this comment.
Looks good, just a few a11y concerns.
| Button { | ||
| toggleExpanded() | ||
| } label: { | ||
| labelContent() | ||
| } | ||
| .buttonStyle(.plain) | ||
| .accessibilityIdentifier("DateFieldHeaderButton") |
There was a problem hiding this comment.
🤔 Does the user understand that they need to tap to select a different date from VoiceOver?
| if date != nil { | ||
| AccessoryButton( | ||
| asset: SharedAsset.Icons.circleX24, | ||
| accessibilityLabel: Localizations.clear, |
There was a problem hiding this comment.
⛏️ I believe this should say "Clear {title of the header}" as in VoiceOver the user may not know if that clear is for the date or for something that follow this field.
| @ViewBuilder | ||
| private func datePicker() -> some View { | ||
| if let range { | ||
| DatePicker("", selection: selection(), in: range, displayedComponents: [.date]) | ||
| .labelsHidden() | ||
| .datePickerStyle(.graphical) | ||
| } else { | ||
| DatePicker("", selection: selection(), displayedComponents: [.date]) | ||
| .labelsHidden() | ||
| .datePickerStyle(.graphical) | ||
| } | ||
| } |
There was a problem hiding this comment.
When in VoiceOver, .graphical style is really problematic as it's very hard to navigate. I think we should hide graphical pickers from accessibility and provide wheel style for such case which is easier for VoiceOver users to use.
There was a problem hiding this comment.
@maxkpower @RishikaSG-28 thoughts on this? Makes sense, imo.
There was a problem hiding this comment.
That makes sense! If it's doable to add that condition then let's go for it!
Add a reusable SwiftUI `DateFieldPicker` to BitwardenKit for optional date (and optional time) entry. The field renders as a collapsed disclosure row that expands an inline native graphical `DatePicker` on a single tap, and supports clearing back to no selection. This unblocks date fields for the new item types (Driver's License expiration; Passport date of birth, issue, and expiration dates). Also add an ISO 8601 calendar-date (`yyyy-MM-dd`) conversion helper on `Date` for round-tripping the string-backed date fields, rejecting invalid dates.
Tapping the field now presents the native graphical DatePicker in a popover dialog (forced via presentationCompactAdaptation on iOS 16.4+) instead of expanding inline, matching the design.
- Switch the popover dialog to the native wheel date picker style - Remove the unused time (displayedComponents) support — all consumers are date-only (YAGNI) - Bound the wheel height and fix the dialog sizing so the Clear button is no longer clipped against the popover edge
Name the clear control after its field, add a header hint, and switch to a wheel picker under VoiceOver where the graphical calendar is hard to navigate (keeping it expanded so continuous scrubbing does not dismiss it).
65dc4d0 to
641453b
Compare
|
Warning @SaintPatrck Uploading code coverage report failed. Please check the "Upload to codecov.io" step of Process Test Reports job for more details. |
|
Warning @SaintPatrck Uploading code coverage report failed. Please check the "Upload to codecov.io" step of Process Test Reports job for more details. |
🎟️ Tracking
📔 Objective
Adds a reusable
DateFieldPickerSwiftUI component to BitwardenKit for optional calendar-date entry. The field renders as a collapsed row (title + chevron); a tap reveals a native inline graphical calendar (DatePickerwith.datePickerStyle(.graphical)) directly beneath the row, and a Clear control resets the selection back to empty. When no date is set, the field shows only its title — matching the design comps.The calendar and its reveal animation are entirely native. The thin wrapper exists only to add an optional
Date?(empty state + Clear), which a nativeDatePickercannot represent on its own, inside the Add/Edit forms'ScrollView— where the native.compactinline-expansion style isn't available. This mirrors the existingBitwardenMenuFieldprecedent of wrapping a native control for that context.Built once for its consumers, this unblocks date fields for the new item types: Driver's License (date of birth, issue date, expiration) and Passport (date of birth, issue date, expiration).
Also adds an ISO 8601 calendar-date (
yyyy-MM-dd) conversion helper onDatefor round-tripping the string-backed date fields, which rejects invalid calendar dates (e.g. Feb 30). The Clear control uses a new 24ptcircle-x24icon asset per Figma.Tests: 7
DateFieldPickerViewInspector tests + 6DateISO 8601 unit tests (all passing); snapshot tests added, disabled per repo convention.📸 Screenshots
Collapsed
Expanded