Skip to content

Commit

Permalink
Add add button and stepper to reward add-on card (#1250)
Browse files Browse the repository at this point in the history
  • Loading branch information
justinswart authored Jul 15, 2020
1 parent 5be6f02 commit ecd880c
Showing 62 changed files with 275 additions and 78 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"images" : [
{
"filename" : "stepper-decrement-normal-grey.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"template-rendering-intent" : "original"
}
}
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"images" : [
{
"filename" : "stepper-increment-normal-grey.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"template-rendering-intent" : "original"
}
}
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -129,7 +129,7 @@ final class PledgeAmountViewController: UIViewController {
|> checkoutStackViewStyle

_ = self.stepper
|> stepperStyle
|> checkoutStepperStyle

_ = self.maxPledgeAmountErrorLabel
|> maxPledgeAmountErrorLabelStyle
@@ -228,18 +228,6 @@ extension PledgeAmountViewController: UITextFieldDelegate {

// MARK: - Styles

private func stepperStyle(_ stepper: UIStepper) -> UIStepper {
return stepper
|> \.stepValue .~ 1.0
|> \.tintColor .~ UIColor.clear
<> UIStepper.lens.decrementImage(for: .normal) .~ image(named: "stepper-decrement-normal")
<> UIStepper.lens.decrementImage(for: .disabled) .~ image(named: "stepper-decrement-disabled")
<> UIStepper.lens.decrementImage(for: .highlighted) .~ image(named: "stepper-decrement-highlighted")
<> UIStepper.lens.incrementImage(for: .normal) .~ image(named: "stepper-increment-normal")
<> UIStepper.lens.incrementImage(for: .disabled) .~ image(named: "stepper-increment-disabled")
<> UIStepper.lens.incrementImage(for: .highlighted) .~ image(named: "stepper-increment-highlighted")
}

private let subtitleLabelStyle: LabelStyle = { label in
label
|> \.font .~ UIFont.ksr_footnote()
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
@testable import Kickstarter_Framework
@testable import KsApi
@testable import Library
import Prelude
import UIKit

final class RewardAddOnSelectionViewControllerTests: TestCase {
override func setUp() {
super.setUp()
AppEnvironment.pushEnvironment(mainBundle: Bundle.framework)
UIView.setAnimationsEnabled(false)
}

override func tearDown() {
AppEnvironment.popEnvironment()
UIView.setAnimationsEnabled(true)

super.tearDown()
}

func testView_NoShipping() {
let reward = Reward.template
let project = Project.template
let env = RewardAddOnSelectionViewEnvelope.template
let mockService = MockService(fetchRewardAddOnsSelectionViewRewardsResult: .success(env))

combos(Language.allLanguages, Device.allCases).forEach { language, device in
withEnvironment(apiService: mockService) {
let controller = RewardAddOnSelectionViewController.instantiate()
controller.configureWith(project: project, reward: reward, refTag: nil, context: .pledge)
let (parent, _) = traitControllers(device: device, orientation: .portrait, child: controller)
parent.view.frame.size.height = 600

self.scheduler.advance()

FBSnapshotVerifyView(parent.view, identifier: "lang_\(language)_device_\(device)")
}
}
}

func testView_Shipping() {
let shippingRules = [
ShippingRule.template
|> ShippingRule.lens.location .~ .brooklyn,
ShippingRule.template
|> ShippingRule.lens.location .~ .canada,
ShippingRule.template
|> ShippingRule.lens.location .~ .australia
]

let reward = Reward.template
|> Reward.lens.shipping .~ (
.template |> Reward.Shipping.lens.enabled .~ true
)
let project = Project.template
let env = RewardAddOnSelectionViewEnvelope.template
let mockService = MockService(
fetchShippingRulesResult: .success(shippingRules),
fetchRewardAddOnsSelectionViewRewardsResult: .success(env)
)

combos(Language.allLanguages, Device.allCases).forEach { language, device in
withEnvironment(apiService: mockService) {
let controller = RewardAddOnSelectionViewController.instantiate()
controller.configureWith(project: project, reward: reward, refTag: nil, context: .pledge)
let (parent, _) = traitControllers(device: device, orientation: .portrait, child: controller)
parent.view.frame.size.height = 600

self.scheduler.advance()
self.scheduler.advance(by: .seconds(1))

FBSnapshotVerifyView(parent.view, identifier: "lang_\(language)_device_\(device)")
}
}
}
}
75 changes: 61 additions & 14 deletions Kickstarter-iOS/Views/RewardAddOnCardView.swift
Original file line number Diff line number Diff line change
@@ -20,22 +20,38 @@ public final class RewardAddOnCardView: UIView {
|> \.insetsLayoutMarginsFromSafeArea .~ false
}()

private let addButton = UIButton(type: .custom)
private let amountConversionLabel = UILabel(frame: .zero)
private let amountLabel = UILabel(frame: .zero)
private let descriptionLabel = UILabel(frame: .zero)
private let includedItemsSeparator: UIView = UIView(frame: .zero)
private let includedItemsStackView = UIStackView(frame: .zero)
private let includedItemsTitleLabel = UILabel(frame: .zero)
private let includedItemsLabel = UILabel(frame: .zero)
private let quantityLabel = UILabel(frame: .zero)
private let quantityLabelContainer = UIView(frame: .zero)
private let pillsView: PillsView = PillsView(frame: .zero)
private var pillsViewHeightConstraint: NSLayoutConstraint?

private let stepper: UIStepper = UIStepper(frame: .zero)
private let stepperStackView = UIStackView(frame: .zero)
private let rewardTitleLabel = UILabel(frame: .zero)
private let titleAmountStackView = UIStackView(frame: .zero)

override init(frame: CGRect) {
super.init(frame: frame)

self.addButton.addTarget(
self,
action: #selector(RewardAddOnCardView.addButtonTapped),
for: .touchUpInside
)

self.stepper.addTarget(
self,
action: #selector(RewardAddOnCardView.stepperValueChanged(_:)),
for: .valueChanged
)

self.configureViews()
self.setupConstraints()
self.bindViewModel()
@@ -57,6 +73,10 @@ public final class RewardAddOnCardView: UIView {
_ = self
|> checkoutWhiteBackgroundStyle

_ = self.addButton
|> UIButton.lens.title(for: .normal) .~ Strings.Add()
|> blackButtonStyle

_ = [
self.rootStackView,
self.titleAmountStackView,
@@ -104,19 +124,41 @@ public final class RewardAddOnCardView: UIView {
_ = self.amountConversionLabel
|> baseRewardLabelStyle
|> convertedAmountLabelStyle

_ = self.quantityLabelContainer
|> \.layoutMargins .~ .init(topBottom: Styles.grid(1), leftRight: Styles.grid(2))
|> \.layer.borderColor .~ UIColor.ksr_grey_400.cgColor
|> \.layer.borderWidth .~ 1
|> checkoutRoundedCornersStyle

_ = self.quantityLabel
|> \.font .~ UIFont.ksr_headline().monospaced

_ = self.stepper
|> checkoutStepperStyle
|> UIStepper.lens.decrementImage(for: .normal) .~ image(named: "stepper-decrement-normal-grey")
|> UIStepper.lens.incrementImage(for: .normal) .~ image(named: "stepper-increment-normal-grey")

_ = self.stepperStackView
|> \.alignment .~ .center
}

public override func bindViewModel() {
super.bindViewModel()

self.addButton.rac.hidden = self.viewModel.outputs.addButtonHidden
self.amountConversionLabel.rac.hidden = self.viewModel.outputs.amountConversionLabelHidden
self.amountConversionLabel.rac.text = self.viewModel.outputs.amountConversionLabelText
self.descriptionLabel.rac.text = self.viewModel.outputs.descriptionLabelText
self.includedItemsStackView.rac.hidden = self.viewModel.outputs.includedItemsStackViewHidden
self.includedItemsLabel.rac.attributedText = self.viewModel.outputs.includedItemsLabelAttributedText
self.amountLabel.rac.attributedText = self.viewModel.outputs.amountLabelAttributedText
self.pillsView.rac.hidden = self.viewModel.outputs.pillsViewHidden
self.quantityLabel.rac.text = self.viewModel.outputs.quantityLabelText
self.rewardTitleLabel.rac.text = self.viewModel.outputs.rewardTitleLabelText
self.stepperStackView.rac.hidden = self.viewModel.outputs.stepperStackViewHidden
self.stepper.rac.maximumValue = self.viewModel.outputs.stepperMaxValue
self.stepper.rac.value = self.viewModel.outputs.stepperValue

self.viewModel.outputs.rewardSelected
.observeForUI()
@@ -126,13 +168,6 @@ public final class RewardAddOnCardView: UIView {
self.delegate?.rewardAddOnCardView(self, didTapWithRewardId: rewardId)
}

self.viewModel.outputs.cardUserInteractionIsEnabled
.observeForUI()
.observeValues { [weak self] isUserInteractionEnabled in
_ = self
?|> \.isUserInteractionEnabled .~ isUserInteractionEnabled
}

self.viewModel.outputs.reloadPills
.observeForUI()
.observeValues { [weak self] values in
@@ -152,7 +187,9 @@ public final class RewardAddOnCardView: UIView {
self.titleAmountStackView,
self.descriptionLabel,
self.includedItemsStackView,
self.pillsView
self.pillsView,
self.addButton,
self.stepperStackView
]

_ = (rootSubviews, self.rootStackView)
@@ -172,15 +209,21 @@ public final class RewardAddOnCardView: UIView {
_ = (includedItemsViews, self.includedItemsStackView)
|> ksr_addArrangedSubviewsToStackView()

let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.rewardCardTapped))
self.addGestureRecognizer(tapGestureRecognizer)
_ = (self.quantityLabel, self.quantityLabelContainer)
|> ksr_addSubviewToParent()
|> ksr_constrainViewToMarginsInParent()

_ = ([self.stepper, UIView(), self.quantityLabelContainer], self.stepperStackView)
|> ksr_addArrangedSubviewsToStackView()
}

private func setupConstraints() {
let pillsViewHeightConstraint = self.pillsView.heightAnchor.constraint(equalToConstant: 0)
self.pillsViewHeightConstraint = pillsViewHeightConstraint

let pillCollectionViewConstraints = [
self.addButton.heightAnchor.constraint(greaterThanOrEqualToConstant: Styles.minTouchSize.height),
self.stepperStackView.heightAnchor.constraint(equalTo: self.addButton.heightAnchor),
self.includedItemsSeparator.heightAnchor.constraint(equalToConstant: 1),
pillsViewHeightConstraint
]
@@ -217,10 +260,14 @@ public final class RewardAddOnCardView: UIView {
self.layoutIfNeeded()
}

// MARK: - Selectors
// MARK: - Actions

@objc func addButtonTapped() {
self.viewModel.inputs.addButtonTapped()
}

@objc func rewardCardTapped() {
self.viewModel.inputs.rewardAddOnCardTapped()
@objc func stepperValueChanged(_ stepper: UIStepper) {
self.viewModel.inputs.stepperValueChanged(stepper.value)
}
}

4 changes: 4 additions & 0 deletions Kickstarter.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
@@ -438,6 +438,7 @@
8A4E954924525EC100A578CF /* BackingState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A4E954824525EC100A578CF /* BackingState.swift */; };
8A4E954B245268F100A578CF /* ManagePledgeViewBackingEnvelopeTemplate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A4E954A245268F100A578CF /* ManagePledgeViewBackingEnvelopeTemplate.swift */; };
8A61E1E423D1202000CA9603 /* MockQualtricsPropertiesType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A61E1E323D1202000CA9603 /* MockQualtricsPropertiesType.swift */; };
8A64F16624BE6528004917E2 /* RewardAddOnSelectionViewControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A64F16524BE6528004917E2 /* RewardAddOnSelectionViewControllerTests.swift */; };
8A6C58932475E5950098D5A2 /* UIRefreshControl+StartRefreshing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A6C58922475E5950098D5A2 /* UIRefreshControl+StartRefreshing.swift */; };
8A73EACF2339528000FF9051 /* PledgePaymentMethodCellViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A73EACE2339528000FF9051 /* PledgePaymentMethodCellViewModel.swift */; };
8A73EAD12339732900FF9051 /* PledgePaymentMethodCellViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A73EAD02339732900FF9051 /* PledgePaymentMethodCellViewModelTests.swift */; };
@@ -1963,6 +1964,7 @@
8A4E954824525EC100A578CF /* BackingState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackingState.swift; sourceTree = "<group>"; };
8A4E954A245268F100A578CF /* ManagePledgeViewBackingEnvelopeTemplate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ManagePledgeViewBackingEnvelopeTemplate.swift; sourceTree = "<group>"; };
8A61E1E323D1202000CA9603 /* MockQualtricsPropertiesType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockQualtricsPropertiesType.swift; sourceTree = "<group>"; };
8A64F16524BE6528004917E2 /* RewardAddOnSelectionViewControllerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RewardAddOnSelectionViewControllerTests.swift; sourceTree = "<group>"; };
8A6C58922475E5950098D5A2 /* UIRefreshControl+StartRefreshing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIRefreshControl+StartRefreshing.swift"; sourceTree = "<group>"; };
8A73EACE2339528000FF9051 /* PledgePaymentMethodCellViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PledgePaymentMethodCellViewModel.swift; sourceTree = "<group>"; };
8A73EAD02339732900FF9051 /* PledgePaymentMethodCellViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PledgePaymentMethodCellViewModelTests.swift; sourceTree = "<group>"; };
@@ -3436,6 +3438,7 @@
019DDFEB1CB6FF4500BDC113 /* ResetPasswordViewController.swift */,
A7ED20291E8323E900BFFA01 /* ResetPasswordViewControllerTests.swift */,
8ACB32A724ABC2DB00A03968 /* RewardAddOnSelectionViewController.swift */,
8A64F16524BE6528004917E2 /* RewardAddOnSelectionViewControllerTests.swift */,
8A8099F722E2156E00373E66 /* RewardPledgeNavigationController.swift */,
77EFBAA52268D32000DA5C3C /* RewardsCollectionViewController.swift */,
77EFBADE2268D33E00DA5C3C /* RewardsCollectionViewControllerTests.swift */,
@@ -5914,6 +5917,7 @@
8A61E1E423D1202000CA9603 /* MockQualtricsPropertiesType.swift in Sources */,
D60CAB922208A1B60083FA40 /* SelectCurrencyViewControllerTests.swift in Sources */,
77E84E0C2166A8C600DA8891 /* MessageBannerViewControllerTests.swift in Sources */,
8A64F16624BE6528004917E2 /* RewardAddOnSelectionViewControllerTests.swift in Sources */,
A7ED20581E8323E900BFFA01 /* SignupViewControllerTests.swift in Sources */,
D6B6875221923BCF005F5DA7 /* ChangeEmailViewControllerTests.swift in Sources */,
D00A376E225BDAF800F46F47 /* UIAlertControllerTests.swift in Sources */,
12 changes: 12 additions & 0 deletions Library/Styles/CheckoutStyles.swift
Original file line number Diff line number Diff line change
@@ -110,6 +110,18 @@ public let checkoutSwitchControlStyle: SwitchControlStyle = { switchControl in
|> \.tintColor .~ UIColor.ksr_grey_500
}

public let checkoutStepperStyle: (UIStepper) -> UIStepper = { stepper in
stepper
|> \.stepValue .~ 1.0
|> \.tintColor .~ UIColor.clear
<> UIStepper.lens.decrementImage(for: .normal) .~ image(named: "stepper-decrement-normal")
<> UIStepper.lens.decrementImage(for: .disabled) .~ image(named: "stepper-decrement-disabled")
<> UIStepper.lens.decrementImage(for: .highlighted) .~ image(named: "stepper-decrement-highlighted")
<> UIStepper.lens.incrementImage(for: .normal) .~ image(named: "stepper-increment-normal")
<> UIStepper.lens.incrementImage(for: .disabled) .~ image(named: "stepper-increment-disabled")
<> UIStepper.lens.incrementImage(for: .highlighted) .~ image(named: "stepper-increment-highlighted")
}

public let checkoutTitleLabelStyle: LabelStyle = { (label: UILabel) in
label
|> \.accessibilityTraits .~ UIAccessibilityTraits.header
Loading

0 comments on commit ecd880c

Please sign in to comment.