diff --git a/Demo/Components/Disclaimer/DisclaimerDemoView.swift b/Demo/Components/Disclaimer/DisclaimerDemoView.swift new file mode 100644 index 0000000000..2a89899d40 --- /dev/null +++ b/Demo/Components/Disclaimer/DisclaimerDemoView.swift @@ -0,0 +1,39 @@ +// +// Copyright © FINN.no AS, Inc. All rights reserved. +// + +import FinniversKit + +public class DisclaimerDemoView: UIView { + + private lazy var disclaimerView: DisclaimerView = { + let view = DisclaimerView(withAutoLayout: true) + view.configure(with: DisclaimerViewModel.default) + return view + }() + + override init(frame: CGRect) { + super.init(frame: frame) + setup() + } + + public required init?(coder aDecoder: NSCoder) { fatalError() } + + private func setup() { + addSubview(disclaimerView) + + NSLayoutConstraint.activate([ + disclaimerView.topAnchor.constraint(equalTo: topAnchor, constant: .largeSpacing), + disclaimerView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: .mediumLargeSpacing), + disclaimerView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -.mediumLargeSpacing) + ]) + } +} + +private extension DisclaimerViewModel { + static var `default`: DisclaimerViewModel { + let disclaimer = "Ved å legge inn din e-postadresse og ditt telefonnummer samtykker du til å motta e-poster samt eventuell henvendelse på telefon om boligprosjektet. Megler/utbygger blir selvstendig behandlingsansvarlig for personinformasjonen de mottar." + let readMoreButtonTitle = "Les mer" + return DisclaimerViewModel(disclaimerText: disclaimer, readMoreButtonTitle: readMoreButtonTitle) + } +} diff --git a/Demo/Demo/Demo.swift b/Demo/Demo/Demo.swift index 2c7324b25b..e0943f5837 100644 --- a/Demo/Demo/Demo.swift +++ b/Demo/Demo/Demo.swift @@ -56,6 +56,7 @@ public enum ComponentViews: String, CaseIterable { case callout case phaseList case iconCollection + case disclaimerView public var viewController: UIViewController { switch self { @@ -127,6 +128,8 @@ public enum ComponentViews: String, CaseIterable { return DemoViewController() case .iconCollection: return DemoViewController() + case .disclaimerView: + return DemoViewController() } } } diff --git a/Demo/Fullscreen/ContactForm/ContactFormDemoView.swift b/Demo/Fullscreen/ContactForm/ContactFormDemoView.swift index a037ce8a43..bce5e1132b 100644 --- a/Demo/Fullscreen/ContactForm/ContactFormDemoView.swift +++ b/Demo/Fullscreen/ContactForm/ContactFormDemoView.swift @@ -37,6 +37,8 @@ extension ContactFormDemoView: ContactFormViewDelegate { public func contactFormView(_ view: ContactFormView, didSubmitWithName name: String, email: String, phoneNumber: String?) { print("Name: \(name), email: \(email), phone number: \(phoneNumber ?? "-")") } + + public func contactFormViewDidTapDisclaimerButton(_ view: ContactFormView) {} } // MARK: - Private types @@ -44,7 +46,6 @@ extension ContactFormDemoView: ContactFormViewDelegate { private struct ViewModel: ContactFormViewModel { let title = "Motta oppdateringer om Elveparken, Jessheim Sør" let detailText = "FINN.no videreformidler denne informasjonen til denne annonsens kontaktperson slik at de kan holde deg fortløpende orientert om prosjektet." - let accessoryText = "Annonsens kontaktperson blir selvstendig behandlingsansvarlig for informasjonen de mottar." let namePlaceholder = "Navn" let emailPlaceholder = "E-post" let showPhoneNumberQuestion = "Vil du også bli kontaktet på telefon?" @@ -53,4 +54,6 @@ private struct ViewModel: ContactFormViewModel { let submitButtonTitle = "Kjør på!" var emailErrorHelpText = "Oppgi en gyldig e-postadresse." var phoneNumberErrorHelpText = "Oppgi et gyldig telefonnummer" + var disclaimerText = "Ved å legge inn din e-postadresse og ditt telefonnummer samtykker du til å motta e-poster samt eventuell henvendelse på telefon om boligprosjektet. Megler/utbygger blir selvstendig behandlingsansvarlig for personinformasjonen de mottar." + var disclaimerReadMoreButtonTitle = "Les mer" } diff --git a/FinniversKit.xcodeproj/project.pbxproj b/FinniversKit.xcodeproj/project.pbxproj index 921badec7d..ede9cddfee 100644 --- a/FinniversKit.xcodeproj/project.pbxproj +++ b/FinniversKit.xcodeproj/project.pbxproj @@ -25,6 +25,9 @@ 08796D9321CB982F0023A441 /* SelectableTableViewCellViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08796D9221CB982F0023A441 /* SelectableTableViewCellViewModel.swift */; }; 08796D9521CB984E0023A441 /* IconTitleTableViewCellViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08796D9421CB984E0023A441 /* IconTitleTableViewCellViewModel.swift */; }; 087A5BB622A7D85500184640 /* KeyboardNotificationInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 087A5BB522A7D85500184640 /* KeyboardNotificationInfo.swift */; }; + 0884C6C922B7B4580015474D /* DisclaimerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0884C6C822B7B4580015474D /* DisclaimerView.swift */; }; + 0884C6CC22B7B5EA0015474D /* DisclaimerDemoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0884C6CB22B7B5EA0015474D /* DisclaimerDemoView.swift */; }; + 0884C6CE22B7BE360015474D /* DisclaimerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0884C6CD22B7BE360015474D /* DisclaimerViewModel.swift */; }; 089DF42321C13178008A8711 /* RectangleGiftView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 089DF42221C13178008A8711 /* RectangleGiftView.swift */; }; 08D70A0E21832546003E2222 /* BasicTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08D70A0A21832546003E2222 /* BasicTableViewCell.swift */; }; 08D70A15218328C6003E2222 /* CheckboxTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08D70A14218328C6003E2222 /* CheckboxTableViewCell.swift */; }; @@ -411,6 +414,9 @@ 08796D9221CB982F0023A441 /* SelectableTableViewCellViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectableTableViewCellViewModel.swift; sourceTree = ""; }; 08796D9421CB984E0023A441 /* IconTitleTableViewCellViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IconTitleTableViewCellViewModel.swift; sourceTree = ""; }; 087A5BB522A7D85500184640 /* KeyboardNotificationInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardNotificationInfo.swift; sourceTree = ""; }; + 0884C6C822B7B4580015474D /* DisclaimerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisclaimerView.swift; sourceTree = ""; }; + 0884C6CB22B7B5EA0015474D /* DisclaimerDemoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisclaimerDemoView.swift; sourceTree = ""; }; + 0884C6CD22B7BE360015474D /* DisclaimerViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisclaimerViewModel.swift; sourceTree = ""; }; 089DF42221C13178008A8711 /* RectangleGiftView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RectangleGiftView.swift; sourceTree = ""; }; 08D70A0A21832546003E2222 /* BasicTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BasicTableViewCell.swift; sourceTree = ""; }; 08D70A14218328C6003E2222 /* CheckboxTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CheckboxTableViewCell.swift; sourceTree = ""; }; @@ -813,6 +819,23 @@ path = Util; sourceTree = ""; }; + 0884C6C722B7B4360015474D /* Disclaimer */ = { + isa = PBXGroup; + children = ( + 0884C6C822B7B4580015474D /* DisclaimerView.swift */, + 0884C6CD22B7BE360015474D /* DisclaimerViewModel.swift */, + ); + path = Disclaimer; + sourceTree = ""; + }; + 0884C6CA22B7B5DA0015474D /* Disclaimer */ = { + isa = PBXGroup; + children = ( + 0884C6CB22B7B5EA0015474D /* DisclaimerDemoView.swift */, + ); + path = Disclaimer; + sourceTree = ""; + }; 089DF42121C13064008A8711 /* ChristmasViews */ = { isa = PBXGroup; children = ( @@ -1253,6 +1276,7 @@ 45B2E06B207E181C00C68B92 /* Consent */, F2BE212E22819ED8004640F3 /* Control */, AA7A854C22255A5700AF5F42 /* Dialogue */, + 0884C6C722B7B4360015474D /* Disclaimer */, CF7C45FD22257A8A001FB92C /* EarthHour */, 9A37D8C01205DC426B502C5F /* Feedback */, 08F0D3F222259AB800968256 /* HappinessRating */, @@ -2013,6 +2037,7 @@ CFCDDA5D22731D4D005E1F4A /* Callout */, CF8F021821DCC624004C4042 /* Consent */, AA7A854F22258A0B00AF5F42 /* Dialogue */, + 0884C6CA22B7B5DA0015474D /* Disclaimer */, CF7C460B22257AE1001FB92C /* EarthHour */, 9A37DBD671995665FDBC64C4 /* Feedback */, 08F0D3F5222691F000968256 /* HappinessRating */, @@ -2978,6 +3003,7 @@ F26852E121F864BA000978C8 /* UserAdsListViewDemoViewHelpers.swift in Sources */, 444246072137D61200D42AB8 /* FavoriteFoldersListViewDemoViewHelpers.swift in Sources */, 6266C0752208235000FB8004 /* AdManagementDemoView.swift in Sources */, + 0884C6CC22B7B5EA0015474D /* DisclaimerDemoView.swift in Sources */, 44AFCE4A215839FB005984DB /* LoadingIndicatorViewDemoView.swift in Sources */, 432CE09E21131B4800F01EC5 /* RoundedImageViewDemoView.swift in Sources */, CF4A054821FB58FA000BB7DA /* PianoDemoView.swift in Sources */, @@ -3164,6 +3190,7 @@ 441D699C1FEA947600CD919A /* LayoutHelpers.swift in Sources */, DAB7FAAA21B690DD00BF8CC6 /* BottomSheetGestureController.swift in Sources */, DAB7FAAE21B6924E00BF8CC6 /* SpringAnimator.swift in Sources */, + 0884C6C922B7B4580015474D /* DisclaimerView.swift in Sources */, 89A7A645225352380053D33B /* NativeAdvertDelegates.swift in Sources */, DA9A223F21BA8D8C001452F0 /* BottomSheetStateController.swift in Sources */, 441FE42C209A24E500B04EF1 /* MarketsGridViewLayoutConfiguration.swift in Sources */, @@ -3225,6 +3252,7 @@ 14DC127021593B0000326DC3 /* HorizontalSlideTransition.swift in Sources */, 4447F6E91FDB2B110033DBC1 /* Ribbon+Style.swift in Sources */, DAB7FAA921B690DD00BF8CC6 /* BottomSheetAnimationController.swift in Sources */, + 0884C6CE22B7BE360015474D /* DisclaimerViewModel.swift in Sources */, CF3DEDB4219465F300DB3E5A /* FrontPageView.swift in Sources */, CF4A053A21FB2ACB000BB7DA /* PianoEffectIndicatorsView.swift in Sources */, 45D7C56B204949E2000E788F /* SwitchView.swift in Sources */, diff --git a/SnapshotTests/ReferenceImages_64/UnitTests.ComponentViewTests/testDisclaimerView@3x.png b/SnapshotTests/ReferenceImages_64/UnitTests.ComponentViewTests/testDisclaimerView@3x.png new file mode 100644 index 0000000000..c2f3784abb Binary files /dev/null and b/SnapshotTests/ReferenceImages_64/UnitTests.ComponentViewTests/testDisclaimerView@3x.png differ diff --git a/SnapshotTests/ReferenceImages_64/UnitTests.FullscreenViewTests/testContactFormView@3x.png b/SnapshotTests/ReferenceImages_64/UnitTests.FullscreenViewTests/testContactFormView@3x.png index d2d51ea75e..51b427cd12 100644 Binary files a/SnapshotTests/ReferenceImages_64/UnitTests.FullscreenViewTests/testContactFormView@3x.png and b/SnapshotTests/ReferenceImages_64/UnitTests.FullscreenViewTests/testContactFormView@3x.png differ diff --git a/Sources/Components/Disclaimer/DisclaimerView.swift b/Sources/Components/Disclaimer/DisclaimerView.swift new file mode 100644 index 0000000000..904b37ff62 --- /dev/null +++ b/Sources/Components/Disclaimer/DisclaimerView.swift @@ -0,0 +1,74 @@ +// +// Copyright © FINN.no AS, Inc. All rights reserved. +// + +import UIKit + +public protocol DisclaimerViewDelegate: AnyObject { + func disclaimerViewDidSelectReadMoreButton(_ disclaimerView: DisclaimerView) +} + +public class DisclaimerView: UIView { + + // MARK: - Public properties + + public weak var delegate: DisclaimerViewDelegate? + + // MARK: - Private properties + + private lazy var disclaimerLabel: Label = { + let label = Label(style: .detail) + label.translatesAutoresizingMaskIntoConstraints = false + label.numberOfLines = 0 + return label + }() + + private lazy var readMoreButton: Button = { + let button = Button(style: .flat, size: .small) + button.translatesAutoresizingMaskIntoConstraints = false + button.addTarget(self, action: #selector(readMoreButtonTapped), for: .touchUpInside) + return button + }() + + // MARK: - Init + + public override init(frame: CGRect) { + super.init(frame: frame) + setup() + } + + public required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + setup() + } + + // MARK: - Setup + + private func setup() { + addSubview(disclaimerLabel) + addSubview(readMoreButton) + + NSLayoutConstraint.activate([ + disclaimerLabel.topAnchor.constraint(equalTo: topAnchor), + disclaimerLabel.leadingAnchor.constraint(equalTo: leadingAnchor), + disclaimerLabel.trailingAnchor.constraint(equalTo: trailingAnchor), + + readMoreButton.topAnchor.constraint(equalTo: disclaimerLabel.bottomAnchor, constant: .mediumSpacing), + readMoreButton.centerXAnchor.constraint(equalTo: centerXAnchor), + readMoreButton.bottomAnchor.constraint(equalTo: bottomAnchor) + ]) + } + + // MARK: - Private methods + + @objc private func readMoreButtonTapped(_ sender: Button) { + delegate?.disclaimerViewDidSelectReadMoreButton(self) + } + + // MARK: - Public methods + + public func configure(with viewModel: DisclaimerViewModel) { + disclaimerLabel.text = viewModel.disclaimerText + readMoreButton.setTitle(viewModel.readMoreButtonTitle, for: .normal) + } +} diff --git a/Sources/Components/Disclaimer/DisclaimerViewModel.swift b/Sources/Components/Disclaimer/DisclaimerViewModel.swift new file mode 100644 index 0000000000..aa7441ee94 --- /dev/null +++ b/Sources/Components/Disclaimer/DisclaimerViewModel.swift @@ -0,0 +1,15 @@ +// +// Copyright © FINN.no AS, Inc. All rights reserved. +// + +import Foundation + +public struct DisclaimerViewModel { + public let disclaimerText: String + public let readMoreButtonTitle: String + + public init(disclaimerText: String, readMoreButtonTitle: String) { + self.disclaimerText = disclaimerText + self.readMoreButtonTitle = readMoreButtonTitle + } +} diff --git a/Sources/Fullscreen/ContactForm/ContactFormView.swift b/Sources/Fullscreen/ContactForm/ContactFormView.swift index 8ed8d9e4d8..67d7e0a284 100644 --- a/Sources/Fullscreen/ContactForm/ContactFormView.swift +++ b/Sources/Fullscreen/ContactForm/ContactFormView.swift @@ -6,6 +6,7 @@ import UIKit public protocol ContactFormViewDelegate: AnyObject { func contactFormView(_ view: ContactFormView, didSubmitWithName name: String, email: String, phoneNumber: String?) + func contactFormViewDidTapDisclaimerButton(_ view: ContactFormView) } public final class ContactFormView: UIView { @@ -46,14 +47,6 @@ public final class ContactFormView: UIView { return label }() - private lazy var accessoryLabel: UILabel = { - let label = UILabel(withAutoLayout: true) - label.font = .detail - label.textColor = .licorice - label.numberOfLines = 0 - return label - }() - private lazy var nameTextField: TextField = { let textField = TextField(inputType: .normal) textField.textField.returnKeyType = .next @@ -95,6 +88,12 @@ public final class ContactFormView: UIView { return button }() + private lazy var disclaimerView: DisclaimerView = { + let view = DisclaimerView(withAutoLayout: true) + view.delegate = self + return view + }() + private var currentTextField: TextField? { if nameTextField.textField.isFirstResponder { return nameTextField @@ -130,7 +129,6 @@ public final class ContactFormView: UIView { public func configure(with viewModel: ContactFormViewModel) { titleLabel.text = viewModel.title detailTextLabel.text = viewModel.detailText - accessoryLabel.text = viewModel.accessoryText nameTextField.placeholderText = viewModel.namePlaceholder emailTextField.placeholderText = viewModel.emailPlaceholder @@ -144,6 +142,9 @@ public final class ContactFormView: UIView { phoneNumberTextField.helpText = viewModel.phoneNumberErrorHelpText submitButton.setTitle(viewModel.submitButtonTitle, for: .normal) + + let disclaimerViewModel = DisclaimerViewModel(disclaimerText: viewModel.disclaimerText, readMoreButtonTitle: viewModel.disclaimerReadMoreButtonTitle) + disclaimerView.configure(with: disclaimerViewModel) } private func setup() { @@ -157,7 +158,6 @@ public final class ContactFormView: UIView { contentView.addSubview(titleLabel) contentView.addSubview(detailTextLabel) - contentView.addSubview(accessoryLabel) contentView.addSubview(nameTextField) contentView.addSubview(emailTextField) contentView.addSubview(showPhoneNumberCheckbox) @@ -167,6 +167,7 @@ public final class ContactFormView: UIView { bottomStackView.axis = .vertical contentView.addSubview(bottomStackView) + contentView.addSubview(disclaimerView) contentView.addSubview(submitButton) scrollView.fillInSuperview() @@ -185,11 +186,7 @@ public final class ContactFormView: UIView { detailTextLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), detailTextLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), - accessoryLabel.topAnchor.constraint(equalTo: detailTextLabel.bottomAnchor, constant: .mediumLargeSpacing), - accessoryLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), - accessoryLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), - - nameTextField.topAnchor.constraint(equalTo: accessoryLabel.bottomAnchor, constant: .mediumLargeSpacing), + nameTextField.topAnchor.constraint(equalTo: detailTextLabel.bottomAnchor, constant: .mediumLargeSpacing), nameTextField.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), nameTextField.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), @@ -205,7 +202,11 @@ public final class ContactFormView: UIView { bottomStackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), bottomStackView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), - submitButton.topAnchor.constraint(equalTo: bottomStackView.bottomAnchor, constant: .largeSpacing), + disclaimerView.topAnchor.constraint(equalTo: bottomStackView.bottomAnchor, constant: .mediumLargeSpacing), + disclaimerView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), + disclaimerView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), + + submitButton.topAnchor.constraint(equalTo: disclaimerView.bottomAnchor, constant: .mediumLargeSpacing), submitButton.centerXAnchor.constraint(equalTo: centerXAnchor), submitButton.widthAnchor.constraint(equalTo: widthAnchor, multiplier: 0.5), submitButton.bottomAnchor.constraint(equalTo: contentView.bottomAnchor), @@ -325,3 +326,11 @@ extension ContactFormView: ContactFormCheckboxDelegate { }) } } + +// MARK: - DisclaimerViewDelegate + +extension ContactFormView: DisclaimerViewDelegate { + public func disclaimerViewDidSelectReadMoreButton(_ disclaimerView: DisclaimerView) { + delegate?.contactFormViewDidTapDisclaimerButton(self) + } +} diff --git a/Sources/Fullscreen/ContactForm/ContactFormViewModel.swift b/Sources/Fullscreen/ContactForm/ContactFormViewModel.swift index c39969ec9b..cb3052e026 100644 --- a/Sources/Fullscreen/ContactForm/ContactFormViewModel.swift +++ b/Sources/Fullscreen/ContactForm/ContactFormViewModel.swift @@ -7,7 +7,6 @@ import Foundation public protocol ContactFormViewModel { var title: String { get } var detailText: String { get } - var accessoryText: String { get } var namePlaceholder: String { get } var emailPlaceholder: String { get } var showPhoneNumberQuestion: String { get } @@ -16,4 +15,6 @@ public protocol ContactFormViewModel { var submitButtonTitle: String { get } var emailErrorHelpText: String { get } var phoneNumberErrorHelpText: String { get } + var disclaimerText: String { get } + var disclaimerReadMoreButtonTitle: String { get } } diff --git a/UnitTests/ComponentViewTests.swift b/UnitTests/ComponentViewTests.swift index 9fc0690b0a..545b27ead8 100644 --- a/UnitTests/ComponentViewTests.swift +++ b/UnitTests/ComponentViewTests.swift @@ -154,4 +154,8 @@ class ComponentViewTests: FBSnapshotTestCase { func testIconCollection() { snapshot(.iconCollection) } + + func testDisclaimerView() { + snapshot(.disclaimerView) + } }