Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[MBL-1815] PLOT plan selector - selected state #2213

Open
wants to merge 8 commits into
base: main
Choose a base branch
from

Conversation

jovaniks
Copy link
Contributor

@jovaniks jovaniks commented Dec 5, 2024

📲 What

This PR implements the "Pledge Over Time" (PLOT) selected state in the Payment Plan Selector. It allows backers to choose the PLOT option and view the corresponding payment increments, including charge dates and amounts.

🤔 Why

The PLOT payment option enables backers to split their pledge amount into four equal payments, improving affordability and flexibility. This change is needed to meet the requirements for the PLOT initiative and ensure proper functionality within the payment plan selector.

🛠 How

  • Added a selectable "Pledge Over Time" (PLOT) option in the Payment Plan Selector.
  • Implemented logic to display mock payment increment records:
    • Included charge dates (PledgePaymentIncrement).
    • Displayed amounts for each payment (PledgePaymentIncrement.amount).
  • Payment increments are displayed in the their own currency.
  • Adapted the UI to follow the provided design:
    • Added explanatory text below the PLOT option.
    • Included Terms of Use link.
  • Mock data was used as the API is not yet ready.

👀 See

Simulator Screen Recording - iPhone SE (3rd generation) - 2024-12-05 at 09 39 41

✅ Acceptance criteria

  • All tests pass
  • Confirm that payment increments (dates and amounts) are displayed correctly.
  • Verify the Terms of Use link functions as expected.
  • Verify the full description is displayed when selected.

⏰ TODO

  • Connect the component to the real API once it becomes available to replace mock data.

@jovaniks jovaniks self-assigned this Dec 5, 2024
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The spacing issue shown here is due to the test locking the layout to a specific screen size, which might not fully represent the component's dynamic behavior. In actual implementation, this component adapts its size based on the content and available screen dimensions, so the extra space seen in the test is expected.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you link the ticket you created here for reference? We should update how we use snapshot tests if we make a habit of testing individual views instead of the entire screen. (I'm a big fan of testing individual views, for the record; we just don't do that currently)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven’t created a ticket for this yet, but I can definitely do so to track this issue and the potential update to how we approach snapshot testing. Let me know if that works!

self.vm.inputs.viewDidLoad()

self.vm.inputs.configure(with: self.selectionData)
self.vm.inputs.didTapTermsOfUse(with: .terms)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@scottkicks I checked the PLOT web version and they are showing the current Terms of use web page.

@jovaniks jovaniks requested a review from scottkicks December 9, 2024 20:57

if let firstDateLabel = dateLabels.first {
dateLabels.forEach { label in
label.widthAnchor.constraint(equalTo: firstDateLabel.widthAnchor).isActive = true
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Intervals dates/amount alignment issue fixe

Explaining the issue: when the increments table mixed dates with one-digit days (4 Jan 2025) and two-digit days (14 Feb 2025), the amountLabel was misaligned. To fix it, a width constraint (all equal width) was added to each dateLabel.

Copy link
Contributor

@scottkicks scottkicks left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good! Just some clean up comments for you to consider.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like there may be some extra spacing after "Charge 1". I know we were having issues without iPad snapshots for this previously. If it's the same issue here, that's fine for now. I'd like to fix that, though, if possible.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Of course, we need to address how we handle snapshot tests. I’ll create a ticket for this purpose. You can follow this thread for more details.

self.delegate?.pledgePaymentPlansViewController(self, didTapTermsOfUseWith: helpType)
}

self.termsOfUseButton.rac.hidden = self.viewModel.outputs.termsOfUseButtonHidden
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need this output? When the option isn't selected the entire PledgePaymentPlanOptionView will be hidden anyway, right? I don't think there's a case where the terms button won't be shown when everything else is.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we need it because the termsOfUseButton is part of the same stack view (optionDescriptorStackView) as the titleLabel, subtitleLabel, paymentIncrementsStackView, etc. Any recommendations or thoughts?

image

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah I thought that there was a stackview just for the items that need to be hidden/shown at the correct time.

}

self.termsOfUseButton.rac.hidden = self.viewModel.outputs.termsOfUseButtonHidden
self.paymentIncrementsStackView.rac.hidden = self.viewModel.outputs.paymentIncrementsHidden
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually same question about this stack view as the one above.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as with the termsOfUseButton, I could use the same output to manage all the controls that should be displayed when Pledge Over Time is selected. However, I prefer this solution as it provides control at the level of each individual UI element. Thoughts?

Comment on lines 206 to 208
let titleLabel = UILabel()
applyIncrementTitleLabelStyle(titleLabel)
titleLabel.text = increment.title
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Can we rename this to something like chargeNumberLabel or something that makes it a bit clearer?

private func applyTermsOfUseStyle(_ button: UIButton) {
button.configuration = {
var config = UIButton.Configuration.borderless()
config.contentInsets = NSDirectionalEdgeInsets(top: 1.0, leading: 0, bottom: 1.0, trailing: 0)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: we could throw these inset values, and any others if we have any, in a Constants enum. Similarly to EstimatedShippingCheckoutView.swift

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it, I’ll move these contentInsets values (and any similar ones like spacing things) to a Constants enum for consistency. Thanks for the suggestion!

@@ -94,14 +94,18 @@ public class NoShippingPledgeViewModel: NoShippingPledgeViewModelType, NoShippin

let backing = project.map { $0.personalization.backing }.skipNil()
self.showPledgeOverTimeUI = project.signal
.map { ($0.isPledgeOverTimeAllowed ?? false) && featurePledgeOverTimeEnabled()
}
.map { _ in featurePledgeOverTimeEnabled() }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can keep the project.isPledgeOverTimeAllowed check. Eventually, when this feature is fully rolled out and we clean up the feature flag we'll still only want to show PLOT UI if the project has isPledgeOverTimeAllowed set to true.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ouch my bad, I removed a bit for test, I'll rollback this validation


self.pledgeOverTimeConfigData = self.showPledgeOverTimeUI
.map { showPledgeOverTimeUI -> PledgePaymentPlansAndSelectionData? in
.combineLatest(with: project)
.map { showPledgeOverTimeUI, project -> PledgePaymentPlansAndSelectionData? in
guard showPledgeOverTimeUI else { return nil }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: this guard statement could be replaced with a .filter(showPledgeOverTimeUI). Then this output wouldn't need to return an optional PledgePaymentPlansAndSelectionData

import ReactiveSwift

public typealias PledgePaymentPlanOptionData = (
type: PledgePaymentPlansType,
selectedType: PledgePaymentPlansType
selectedType: PledgePaymentPlansType,
paymentIncrements: [PledgePaymentIncrement], // TODO: replece with API model
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
paymentIncrements: [PledgePaymentIncrement], // TODO: replece with API model
paymentIncrements: [PledgePaymentIncrement], // TODO: replece with API model in [mbl-1838](https://kickstarter.atlassian.net/browse/MBL-1838)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated, thanks for adding the ticket ref

}
}

private func getPledgePaymentIncrementFormatted(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit:

Suggested change
private func getPledgePaymentIncrementFormatted(
private func formattedPledgePaymentIncrement(

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

renamed, good suggestion

public var selectedPlan: PledgePaymentPlansType
public var paymentIncrements: [PledgePaymentIncrement]
public var project: Project
/* TODO: add the necesary properties for the next states (PLOT Selected and Ineligible)
- [MBL-1815](https://kickstarter.atlassian.net/browse/MBL-1815)
Copy link
Contributor

@scottkicks scottkicks Dec 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we be able to remove this comment now?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, removed the line that is refer to the [MBL-1815]

@jovaniks jovaniks requested a review from scottkicks December 11, 2024 18:48
@nativeksr
Copy link
Collaborator

1 Warning
⚠️ Big PR

Generated by 🚫 Danger

Copy link
Contributor

@scottkicks scottkicks left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 just one last minor suggestion from me


self.pledgeOverTimeConfigData = self.showPledgeOverTimeUI
.filter { $0 }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: personally, I'd prefer to make this more explicit like .filter { showUI in showUI == true } but that's a minor nit.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants