Skip to content

Commit 65dc4d0

Browse files
committed
Add DateFieldPicker showcase to Test Harness
1 parent 038e5c0 commit 65dc4d0

9 files changed

Lines changed: 158 additions & 0 deletions

File tree

TestHarnessShared/UI/Platform/Application/Support/Localizations/en.lproj/Localizable.strings

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,9 @@
1111
"FormValues" = "Form Values";
1212
"UsernameValue" = "Username: %@";
1313
"PasswordValue" = "Password: %@";
14+
"DateFieldPicker" = "Date Field Picker";
15+
"DateFieldPickerDescription" = "Tap the field to expand the inline calendar and select a date.";
16+
"DateField" = "Date Field";
17+
"DateOfBirth" = "Date of birth";
18+
"SelectedDateValue" = "Selected date: %@";
19+
"NoDateSelected" = "No date selected";
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import Foundation
2+
3+
/// Actions that can be processed by a `DateFieldPickerShowcaseProcessor`.
4+
///
5+
enum DateFieldPickerShowcaseAction: Equatable {
6+
/// The selected date was updated.
7+
case dateChanged(Date?)
8+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import Foundation
2+
3+
/// Effects that can be processed by a `DateFieldPickerShowcaseProcessor`.
4+
///
5+
enum DateFieldPickerShowcaseEffect: Equatable {}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import BitwardenKit
2+
import Combine
3+
4+
/// The processor for the date field picker showcase screen.
5+
///
6+
class DateFieldPickerShowcaseProcessor: StateProcessor<
7+
DateFieldPickerShowcaseState,
8+
DateFieldPickerShowcaseAction,
9+
DateFieldPickerShowcaseEffect,
10+
> {
11+
// MARK: Types
12+
13+
typealias Services = HasErrorReporter
14+
15+
// MARK: Private Properties
16+
17+
/// The coordinator that handles navigation.
18+
private let coordinator: AnyCoordinator<RootRoute, Void>
19+
20+
// MARK: Initialization
21+
22+
/// Initialize a `DateFieldPickerShowcaseProcessor`.
23+
///
24+
/// - Parameter coordinator: The coordinator that handles navigation.
25+
///
26+
init(coordinator: AnyCoordinator<RootRoute, Void>) {
27+
self.coordinator = coordinator
28+
super.init(state: DateFieldPickerShowcaseState())
29+
}
30+
31+
// MARK: Methods
32+
33+
override func receive(_ action: DateFieldPickerShowcaseAction) {
34+
switch action {
35+
case let .dateChanged(newValue):
36+
state.selectedDate = newValue
37+
}
38+
}
39+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import Foundation
2+
3+
/// The state for the date field picker showcase screen.
4+
///
5+
struct DateFieldPickerShowcaseState: Equatable {
6+
// MARK: Properties
7+
8+
/// The title of the screen.
9+
var title: String = Localizations.dateFieldPicker
10+
11+
/// The currently selected date, or `nil` if no date has been selected.
12+
var selectedDate: Date?
13+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import BitwardenKit
2+
import SwiftUI
3+
4+
/// A view that showcases the `DateFieldPicker` component for manual testing.
5+
///
6+
struct DateFieldPickerShowcaseView: View {
7+
// MARK: Properties
8+
9+
/// The store used to render the view.
10+
@ObservedObject var store: Store<
11+
DateFieldPickerShowcaseState,
12+
DateFieldPickerShowcaseAction,
13+
DateFieldPickerShowcaseEffect,
14+
>
15+
16+
// MARK: View
17+
18+
var body: some View {
19+
content
20+
.navigationTitle(store.state.title)
21+
.navigationBarTitleDisplayMode(.large)
22+
}
23+
24+
// MARK: Private Views
25+
26+
/// The main content view.
27+
private var content: some View {
28+
Form {
29+
Section {
30+
DateFieldPicker(
31+
title: Localizations.dateOfBirth,
32+
date: store.binding(
33+
get: \.selectedDate,
34+
send: DateFieldPickerShowcaseAction.dateChanged,
35+
),
36+
footer: Localizations.dateFieldPickerDescription,
37+
)
38+
} header: {
39+
Text(Localizations.dateField)
40+
}
41+
42+
Section {
43+
if let selectedDate = store.state.selectedDate {
44+
Text(Localizations.selectedDateValue(selectedDate.formatted(date: .long, time: .omitted)))
45+
.styleGuide(.body)
46+
} else {
47+
Text(Localizations.noDateSelected)
48+
.foregroundColor(.secondary)
49+
.styleGuide(.body)
50+
}
51+
} header: {
52+
Text(Localizations.formValues)
53+
}
54+
}
55+
}
56+
}
57+
58+
// MARK: - Previews
59+
60+
#if DEBUG
61+
#Preview {
62+
NavigationView {
63+
DateFieldPickerShowcaseView(
64+
store: Store(processor: StateProcessor(state: DateFieldPickerShowcaseState())),
65+
)
66+
}
67+
}
68+
#endif

TestHarnessShared/UI/Platform/Root/RootCoordinator.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ class RootCoordinator: Coordinator, HasStackNavigator {
3636

3737
func navigate(to route: RootRoute, context: AnyObject?) {
3838
switch route {
39+
case .dateFieldPickerShowcase:
40+
showDateFieldPickerShowcase()
3941
case .scenarioPicker:
4042
showScenarioPicker()
4143
case .simpleLoginForm:
@@ -49,6 +51,15 @@ class RootCoordinator: Coordinator, HasStackNavigator {
4951

5052
// MARK: Private Methods
5153

54+
/// Shows the date field picker showcase screen.
55+
///
56+
private func showDateFieldPickerShowcase() {
57+
let processor = DateFieldPickerShowcaseProcessor(coordinator: asAnyCoordinator())
58+
let view = DateFieldPickerShowcaseView(store: Store(processor: processor))
59+
let viewController = UIHostingController(rootView: view)
60+
stackNavigator?.push(viewController)
61+
}
62+
5263
/// Shows the scenario picker screen.
5364
///
5465
private func showScenarioPicker() {

TestHarnessShared/UI/Platform/Root/RootRoute.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ import Foundation
33
/// The routes for navigating within the home flow.
44
///
55
public enum RootRoute {
6+
/// A route to the date field picker showcase screen.
7+
case dateFieldPickerShowcase
8+
69
/// A route to the scenario picker home screen.
710
case scenarioPicker
811

TestHarnessShared/UI/Platform/Root/ScenarioPicker/ScenarioPickerState.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ struct ScenarioPickerState: Equatable {
2626
/// The available test scenarios.
2727
var scenarios: [ScenarioItem] = [
2828
ScenarioItem(id: "simpleLoginForm", title: Localizations.simpleLoginForm, route: .simpleLoginForm),
29+
ScenarioItem(
30+
id: "dateFieldPicker",
31+
title: Localizations.dateFieldPicker,
32+
route: .dateFieldPickerShowcase,
33+
),
2934
ScenarioItem(id: "passkeyAutofill", title: Localizations.passkeyAutofill, route: nil),
3035
ScenarioItem(id: "createPasskey", title: Localizations.createPasskey, route: nil),
3136
]

0 commit comments

Comments
 (0)