Skip to content

Commit 45ed09f

Browse files
committed
Initialize VM with required data and receive Product entity in convenience init
1 parent 66e8e31 commit 45ed09f

File tree

5 files changed

+96
-31
lines changed

5 files changed

+96
-31
lines changed

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,14 @@ private struct ProductsSection: View {
9595
.headlineStyle()
9696

9797
// TODO: Add a product row for each product added to the order
98-
let viewModel = ProductRowViewModel(product: ProductRowViewModel.sampleProduct, canChangeQuantity: true) // Temporary view model
98+
let viewModel = ProductRowViewModel(id: 1,
99+
name: "Love Ficus",
100+
sku: "123456",
101+
price: "20",
102+
stockStatusKey: "instock",
103+
stockQuantity: 7,
104+
manageStock: true,
105+
canChangeQuantity: true) // Temporary view model with fake data
99106
ProductRow(viewModel: viewModel)
100107

101108
Button(NewOrder.Localization.addProduct) {

WooCommerce/Classes/ViewRelated/Orders/Order Creation/ProductsSection/AddProduct.swift

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,14 @@ struct AddProduct: View {
1313
// TODO: Make the product list searchable
1414
LazyVStack {
1515
// TODO: Add a product row for each non-variable product in the store
16-
let viewModel = ProductRowViewModel(product: ProductRowViewModel.sampleProduct, canChangeQuantity: false) // Temporary view model
16+
let viewModel = ProductRowViewModel(id: 1,
17+
name: "Love Ficus",
18+
sku: "123456",
19+
price: "20",
20+
stockStatusKey: "instock",
21+
stockQuantity: 7,
22+
manageStock: true,
23+
canChangeQuantity: false) // Temporary view model with fake data
1724
ProductRow(viewModel: viewModel)
1825
}
1926
.padding()

WooCommerce/Classes/ViewRelated/Orders/Order Creation/ProductsSection/ProductRow.swift

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ struct ProductRow: View {
2525

2626
// Product details
2727
VStack(alignment: .leading) {
28-
Text(viewModel.nameLabel)
28+
Text(viewModel.name)
2929
Text(viewModel.stockAndPriceLabel)
3030
.subheadlineStyle()
3131
Text(viewModel.skuLabel)
@@ -114,14 +114,28 @@ private enum Localization {
114114

115115
struct ProductRow_Previews: PreviewProvider {
116116
static var previews: some View {
117-
let viewModel = ProductRowViewModel(product: ProductRowViewModel.sampleProduct, canChangeQuantity: true)
118-
let viewModel2 = ProductRowViewModel(product: ProductRowViewModel.sampleProduct, canChangeQuantity: false)
117+
let viewModel = ProductRowViewModel(id: 1,
118+
name: "Love Ficus",
119+
sku: "123456",
120+
price: "20",
121+
stockStatusKey: "instock",
122+
stockQuantity: 7,
123+
manageStock: true,
124+
canChangeQuantity: true)
125+
let viewModelWithoutStepper = ProductRowViewModel(id: 1,
126+
name: "Love Ficus",
127+
sku: "123456",
128+
price: "20",
129+
stockStatusKey: "instock",
130+
stockQuantity: 7,
131+
manageStock: true,
132+
canChangeQuantity: false)
119133

120134
ProductRow(viewModel: viewModel)
121135
.previewDisplayName("ProductRow with stepper")
122136
.previewLayout(.sizeThatFits)
123137

124-
ProductRow(viewModel: viewModel2)
138+
ProductRow(viewModel: viewModelWithoutStepper)
125139
.previewDisplayName("ProductRow without stepper")
126140
.previewLayout(.sizeThatFits)
127141
}

WooCommerce/Classes/ViewRelated/Orders/Order Creation/ProductsSection/ProductRowViewModel.swift

Lines changed: 60 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,43 @@ import Yosemite
44
/// View model for `ProductRow`.
55
///
66
final class ProductRowViewModel: ObservableObject, Identifiable {
7+
private let currencyFormatter: CurrencyFormatter
8+
9+
/// Whether the product quantity can be changed.
10+
/// Controls whether the stepper is rendered.
11+
///
12+
let canChangeQuantity: Bool
13+
14+
// MARK: Product properties
15+
716
/// Product ID
817
/// Required by SwiftUI as a unique identifier
918
///
1019
let id: Int64
1120

12-
private let currencyFormatter: CurrencyFormatter
21+
/// Product name
22+
///
23+
let name: String
1324

14-
/// Whether the product quantity can be changed.
15-
/// Controls whether the stepper is rendered.
25+
/// Product SKU
1626
///
17-
let canChangeQuantity: Bool
27+
private let sku: String?
28+
29+
/// Product price
30+
///
31+
private let price: String
32+
33+
/// Product stock status
34+
///
35+
private let stockStatus: ProductStockStatus
1836

19-
/// Product to display
37+
/// Product stock quantity
2038
///
21-
private let product: Product
39+
private let stockQuantity: Decimal?
2240

23-
/// Label showing product name
41+
/// Whether the product's stock quantity is managed
2442
///
25-
let nameLabel: String
43+
private let manageStock: Bool
2644

2745
/// Label showing product stock status and price.
2846
///
@@ -38,7 +56,7 @@ final class ProductRowViewModel: ObservableObject, Identifiable {
3856
/// Label showing product SKU
3957
///
4058
lazy var skuLabel: String = {
41-
guard let sku = product.sku, sku.isNotEmpty else {
59+
guard let sku = sku, sku.isNotEmpty else {
4260
return ""
4361
}
4462
return String.localizedStringWithFormat(Localization.skuFormat, sku)
@@ -48,36 +66,60 @@ final class ProductRowViewModel: ObservableObject, Identifiable {
4866
///
4967
@Published var quantity: Int64 = 1
5068

51-
init(product: Product,
69+
init(id: Int64,
70+
name: String,
71+
sku: String?,
72+
price: String,
73+
stockStatusKey: String,
74+
stockQuantity: Decimal?,
75+
manageStock: Bool,
5276
canChangeQuantity: Bool,
5377
currencyFormatter: CurrencyFormatter = CurrencyFormatter(currencySettings: ServiceLocator.currencySettings)) {
54-
self.id = product.productID
55-
self.product = product
56-
self.nameLabel = product.name
78+
self.id = id
79+
self.name = name
80+
self.sku = sku
81+
self.price = price
82+
self.stockStatus = .init(rawValue: stockStatusKey)
83+
self.stockQuantity = stockQuantity
84+
self.manageStock = manageStock
5785
self.canChangeQuantity = canChangeQuantity
5886
self.currencyFormatter = currencyFormatter
5987
}
6088

89+
convenience init(product: Product,
90+
canChangeQuantity: Bool,
91+
currencyFormatter: CurrencyFormatter = CurrencyFormatter(currencySettings: ServiceLocator.currencySettings)) {
92+
self.init(id: product.productID,
93+
name: product.name,
94+
sku: product.sku,
95+
price: product.price,
96+
stockStatusKey: product.stockStatusKey,
97+
stockQuantity: product.stockQuantity,
98+
manageStock: product.manageStock,
99+
canChangeQuantity: canChangeQuantity,
100+
currencyFormatter: currencyFormatter)
101+
}
102+
61103
/// Create the stock text based on a product's stock status/quantity.
62104
///
63105
private func createStockText() -> String {
64-
switch product.productStockStatus {
106+
switch stockStatus {
65107
case .inStock:
66-
if let stockQuantity = product.stockQuantity, product.manageStock {
108+
if let stockQuantity = stockQuantity, manageStock {
67109
let localizedStockQuantity = NumberFormatter.localizedString(from: stockQuantity as NSDecimalNumber, number: .decimal)
68110
return String.localizedStringWithFormat(Localization.stockFormat, localizedStockQuantity)
69111
} else {
70-
return product.productStockStatus.description
112+
return stockStatus.description
71113
}
72114
default:
73-
return product.productStockStatus.description
115+
return stockStatus.description
74116
}
75117
}
76118

77119
/// Create the price text based on a product's price.
78120
///
79121
private func createPriceText() -> String? {
80-
let unformattedPrice = product.price.isNotEmpty ? product.price : "0"
122+
let unformattedPrice = price.isNotEmpty ? price : "0"
81123
return currencyFormatter.formatAmount(unformattedPrice)
82124
}
83125
}
@@ -88,8 +130,3 @@ private extension ProductRowViewModel {
88130
static let skuFormat = NSLocalizedString("SKU: %1$@", comment: "SKU label in order details > product row. The variable shows the SKU of the product.")
89131
}
90132
}
91-
92-
// MARK: SwiftUI Preview Helpers
93-
extension ProductRowViewModel {
94-
static let sampleProduct = Product().copy(productID: 2, name: "Love Ficus", sku: "123456", price: "20", stockQuantity: 7, stockStatusKey: "instock")
95-
}

WooCommerce/WooCommerceTests/ViewRelated/Orders/Order Creation/ProductRowViewModelTests.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@ class ProductRowViewModelTests: XCTestCase {
1212
let viewModel = ProductRowViewModel(product: product, canChangeQuantity: false)
1313

1414
// Then
15-
XCTAssertEqual(viewModel.product, product)
15+
XCTAssertEqual(viewModel.id, product.productID)
16+
XCTAssertEqual(viewModel.name, product.name)
1617
XCTAssertFalse(viewModel.canChangeQuantity)
1718
XCTAssertEqual(viewModel.quantity, 1)
18-
XCTAssertEqual(viewModel.nameLabel, product.name)
1919
}
2020

2121
func test_view_model_creates_expected_label_for_product_with_managed_stock() {

0 commit comments

Comments
 (0)