Skip to content

Commit 5cd1068

Browse files
authored
Merge pull request #6578 from Juanpe/issue/6500-add-button-to-dismiss-new-order-modal
Add a cancel button to allow merchants to cancel the flow of creating a new order.
2 parents 32b1587 + ec359d6 commit 5cd1068

File tree

5 files changed

+78
-13
lines changed

5 files changed

+78
-13
lines changed

WooCommerce/Classes/ViewRelated/Orders/Order Creation/NewOrder.swift

Lines changed: 46 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ final class NewOrderHostingController: UIHostingController<NewOrder> {
1313
init(viewModel: NewOrderViewModel) {
1414
self.viewModel = viewModel
1515
super.init(rootView: NewOrder(viewModel: viewModel))
16+
17+
// Needed because a `SwiftUI` cannot be dismissed when being presented by a UIHostingController
18+
rootView.dismissHandler = { [weak self] in
19+
self?.dismiss(animated: true)
20+
}
1621
}
1722

1823
required dynamic init?(coder aDecoder: NSCoder) {
@@ -40,7 +45,9 @@ final class NewOrderHostingController: UIHostingController<NewOrder> {
4045
extension NewOrderHostingController {
4146
override func shouldPopOnBackButton() -> Bool {
4247
guard !viewModel.hasChanges else {
43-
presentDiscardChangesActionSheet()
48+
presentDiscardChangesActionSheet(onDiscard: { [weak self] in
49+
self?.discardOrderAndPop()
50+
})
4451
return false
4552
}
4653
return true
@@ -49,13 +56,6 @@ extension NewOrderHostingController {
4956
override func shouldPopOnSwipeBack() -> Bool {
5057
return shouldPopOnBackButton()
5158
}
52-
53-
private func presentDiscardChangesActionSheet() {
54-
UIAlertController.presentDiscardChangesActionSheet(viewController: self, onDiscard: { [weak self] in
55-
self?.viewModel.discardOrder()
56-
self?.navigationController?.popViewController(animated: true)
57-
})
58-
}
5959
}
6060

6161
/// Intercepts to the dismiss drag gesture.
@@ -66,16 +66,37 @@ extension NewOrderHostingController: UIAdaptivePresentationControllerDelegate {
6666
}
6767

6868
func presentationControllerDidAttemptToDismiss(_ presentationController: UIPresentationController) {
69-
UIAlertController.presentDiscardChangesActionSheet(viewController: self, onDiscard: { [weak self] in
70-
self?.viewModel.discardOrder()
71-
self?.dismiss(animated: true, completion: nil)
69+
presentDiscardChangesActionSheet(onDiscard: { [weak self] in
70+
self?.discardOrderAndDismiss()
7271
})
7372
}
7473
}
7574

75+
/// Private methods
76+
///
77+
private extension NewOrderHostingController {
78+
func presentDiscardChangesActionSheet(onDiscard: @escaping () -> Void) {
79+
UIAlertController.presentDiscardChangesActionSheet(viewController: self, onDiscard: onDiscard)
80+
}
81+
82+
func discardOrderAndDismiss() {
83+
viewModel.discardOrder()
84+
dismiss(animated: true)
85+
}
86+
87+
func discardOrderAndPop() {
88+
viewModel.discardOrder()
89+
navigationController?.popViewController(animated: true)
90+
}
91+
}
92+
7693
/// View to create a new manual order
7794
///
7895
struct NewOrder: View {
96+
/// Set this closure with UIKit dismiss code. Needed because we need access to the UIHostingController `dismiss` method.
97+
///
98+
var dismissHandler: (() -> Void) = {}
99+
79100
@ObservedObject var viewModel: NewOrderViewModel
80101

81102
/// Fix for breaking navbar button
@@ -113,14 +134,21 @@ struct NewOrder: View {
113134
.navigationTitle(Localization.title)
114135
.navigationBarTitleDisplayMode(.inline)
115136
.toolbar {
137+
ToolbarItem(placement: .cancellationAction) {
138+
Button(Localization.cancelButton) {
139+
dismissHandler()
140+
}
141+
.accessibilityIdentifier(Accessibility.cancelButtonIdentifier)
142+
.renderedIf(viewModel.shouldShowCancelButton)
143+
}
116144
ToolbarItem(placement: .confirmationAction) {
117145
switch viewModel.navigationTrailingItem {
118146
case .create:
119147
Button(Localization.createButton) {
120148
viewModel.createOrder()
121149
}
122150
.id(navigationButtonID)
123-
.accessibilityIdentifier("new-order-create-button")
151+
.accessibilityIdentifier(Accessibility.createButtonIdentifier)
124152
.disabled(viewModel.disabled)
125153

126154
case .loading:
@@ -213,11 +241,17 @@ private extension NewOrder {
213241
enum Localization {
214242
static let title = NSLocalizedString("New Order", comment: "Title for the order creation screen")
215243
static let createButton = NSLocalizedString("Create", comment: "Button to create an order on the New Order screen")
244+
static let cancelButton = NSLocalizedString("Cancel", comment: "Button to cancel the creation of an order on the New Order screen")
216245
static let products = NSLocalizedString("Products", comment: "Title text of the section that shows the Products when creating a new order")
217246
static let addProduct = NSLocalizedString("Add Product", comment: "Title text of the button that adds a product when creating a new order")
218247
static let productRowAccessibilityHint = NSLocalizedString("Opens product detail.",
219248
comment: "Accessibility hint for selecting a product in a new order")
220249
}
250+
251+
enum Accessibility {
252+
static let createButtonIdentifier = "new-order-create-button"
253+
static let cancelButtonIdentifier = "new-order-cancel-button"
254+
}
221255
}
222256

223257
struct NewOrder_Previews: PreviewProvider {

WooCommerce/Classes/ViewRelated/Orders/Order Creation/NewOrderViewModel.swift

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import Yosemite
22
import Combine
33
import protocol Storage.StorageManagerType
4+
import Experiments
45

56
/// View model for `NewOrder`.
67
///
@@ -9,6 +10,7 @@ final class NewOrderViewModel: ObservableObject {
910
private let stores: StoresManager
1011
private let storageManager: StorageManagerType
1112
private let currencyFormatter: CurrencyFormatter
13+
private let featureFlagService: FeatureFlagService
1214

1315
private var cancellables: Set<AnyCancellable> = []
1416

@@ -18,6 +20,12 @@ final class NewOrderViewModel: ObservableObject {
1820
orderSynchronizer.order != OrderFactory.emptyNewOrder
1921
}
2022

23+
/// Indicates whether the cancel button is visible.
24+
///
25+
var shouldShowCancelButton: Bool {
26+
featureFlagService.isFeatureFlagEnabled(.splitViewInOrdersTab)
27+
}
28+
2129
/// Active navigation bar trailing item.
2230
/// Defaults to create button.
2331
///
@@ -192,13 +200,15 @@ final class NewOrderViewModel: ObservableObject {
192200
stores: StoresManager = ServiceLocator.stores,
193201
storageManager: StorageManagerType = ServiceLocator.storageManager,
194202
currencySettings: CurrencySettings = ServiceLocator.currencySettings,
195-
analytics: Analytics = ServiceLocator.analytics) {
203+
analytics: Analytics = ServiceLocator.analytics,
204+
featureFlagService: FeatureFlagService = ServiceLocator.featureFlagService) {
196205
self.siteID = siteID
197206
self.stores = stores
198207
self.storageManager = storageManager
199208
self.currencyFormatter = CurrencyFormatter(currencySettings: currencySettings)
200209
self.analytics = analytics
201210
self.orderSynchronizer = RemoteOrderSynchronizer(siteID: siteID, stores: stores, currencySettings: currencySettings)
211+
self.featureFlagService = featureFlagService
202212

203213
// Set a temporary initial view model, as a workaround to avoid making it optional.
204214
// Needs to be reset before the view model is used.

WooCommerce/UITestsFoundation/Screens/Orders/NewOrderScreen.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,20 @@ public final class NewOrderScreen: ScreenObject {
77
$0.buttons["new-order-create-button"]
88
}
99

10+
private let cancelButtonGetter: (XCUIApplication) -> XCUIElement = {
11+
$0.buttons["new-order-cancel-button"]
12+
}
13+
1014
private let orderStatusEditButtonGetter: (XCUIApplication) -> XCUIElement = {
1115
$0.buttons["order-status-section-edit-button"]
1216
}
1317

1418
private var createButton: XCUIElement { createButtonGetter(app) }
1519

20+
/// Cancel button in the Navigation bar.
21+
///
22+
private var cancelButton: XCUIElement { cancelButtonGetter(app) }
23+
1624
/// Edit button in the Order Status section.
1725
///
1826
private var orderStatusEditButton: XCUIElement { orderStatusEditButtonGetter(app) }
@@ -32,6 +40,12 @@ public final class NewOrderScreen: ScreenObject {
3240
return try SingleOrderScreen()
3341
}
3442

43+
@discardableResult
44+
public func cancelOrderCreation() throws -> OrdersScreen {
45+
cancelButton.tap()
46+
return try OrdersScreen()
47+
}
48+
3549
/// Opens the Order Status screen (to set a new order status).
3650
/// - Returns: Order Status screen object.
3751
@discardableResult

WooCommerce/WooCommerceUITests/Tests/OrdersTests.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,10 @@ final class OrdersTests: XCTestCase {
3232
try NewOrderFlow.editOrderStatus()
3333
.createOrder()
3434
}
35+
36+
func test_cancel_order_creation() throws {
37+
try TabNavComponent().goToOrdersScreen()
38+
.startOrderCreation()
39+
.cancelOrderCreation()
40+
}
3541
}

docs/UI-TESTS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ The following flows are covered/planned to be covered by UI tests. Tests that ar
1818
- [ ] Tap chart on stats
1919
3. [Orders](../WooCommerce/WooCommerceUITests/Tests/OrdersTests.swift)
2020
- [x] Orders list and single order screens load
21+
- [x] Dismissal of order creation flow when tapping the Close button
2122
- [ ] View product on single order screen
2223
- [ ] Add customer note
2324
- [ ] Add order note

0 commit comments

Comments
 (0)