diff --git a/JSONCreator_iOS/JSONCreator/JSON/Samples/FormContactInfo.json b/JSONCreator_iOS/JSONCreator/JSON/Samples/FormContactInfo.json index 9f494ea..5b28f2a 100644 --- a/JSONCreator_iOS/JSONCreator/JSON/Samples/FormContactInfo.json +++ b/JSONCreator_iOS/JSONCreator/JSON/Samples/FormContactInfo.json @@ -19,46 +19,67 @@ "stack": { "moleculeName": "stack", "molecules": [ - { - "moleculeName": "stackItem", - "molecule": { - "moleculeName": "toggle", - "fieldKey": "isActive" - } - }, - { - "moleculeName": "stackItem", - "molecule": { - "moleculeName":"labelToggle", - "label":{ - "moleculeName": "label", - "text":"Label Text Goes Here" - }, - "toggle":{ - "moleculeName": "toggle" - } - } - }, - { - "moleculeName": "stackItem", - "molecule": { - "moleculeName":"headlineBodyToggle", - "headlineBody":{ - "moleculeName": "headlineBody", - "headline":{ - "moleculeName": "label", - "text": "Headline Text Goes Here" - }, - "body":{ - "moleculeName": "label", - "text": "Body Text Goes Here" - } - }, - "toggle":{ - "moleculeName": "toggle" - } + { + "moleculeName": "stackItem", + "molecule": { + "moleculeName": "textView", + "fieldKey": "firstName", + "type": "text", + "errorMessage": "Please enter a valid first name.", + "placeholder": "John A", + "titleLabel": { + "moleculeName": "label", + "text": "First Name" } } + }, + { + "moleculeName": "stackItem", + "molecule": { + "moleculeName": "textField", + "fieldKey": "lastName", + "type": "text", + "placeholder": "Smith", + "errorMessage": "Please enter a valid last name.", + "titleLabel": { + "moleculeName": "label", + "text": "Last Name" + } + } + }, + { + "moleculeName": "stackItem", + "molecule": { + "moleculeName": "textField", + "fieldKey": "phoneNumber", + "type": "phone", + "placeholder": "212-555-1234", + "title": "Contact Phone Number", + "errorMessage": "Please enter a valid phone number." + } + }, + { + "moleculeName": "stackItem", + "molecule": { + "moleculeName": "textField", + "fieldKey": "emailID", + "type": "text", + "placeholder": "JSMith123@gmail.com", + "title": "Email", + "errorMessage": "Please enter a valid greeting name." + } + }, + { + "moleculeName": "stackItem", + "molecule": { + "moleculeName": "textField", + "fieldKey": "zipcode", + "type": "number", + "placeholder": "90210", + "title": "Zip Code", + "errorMessage": "Please enter a valid zip code." + } + } ] }, "footer": { @@ -84,7 +105,36 @@ { "groupName": "default", "rules": [ - + { + "type": "regex", + "fields": [ + "emailID" + ], + "regex": "^[a-zA-Z0-9](\\.?\\_?\\-?[a-zA-Z0-9]){0,}@[a-zA-Z0-9-_]+\\.([a-zA-Z0-9-_]{1,}\\.){0,}[a-zA-Z]{2,}$" + }, + { + "type": "regex", + "fields": [ + "zipcode" + ], + "regex": "^\\d{5}(?:[-\\s]\\d{4})?$" + }, + { + "regex": "^(\\d{3})[\\s.-]{0,1}(\\d{3})[\\s.-]{0,1}(\\d{4})$", + "type": "regex", + "fields": [ + "phoneNumber" + ] + }, + { + "type": "allRequired", + "ruleId": "requiredRule", + "fields": [ + "emailID", + "firstName", + "lastName" + ] + } ] } ] diff --git a/JSONCreator_iOS/JSONCreator/JSON/Samples/TileleListSample.json b/JSONCreator_iOS/JSONCreator/JSON/Samples/TileleListSample.json new file mode 100644 index 0000000..4e56264 --- /dev/null +++ b/JSONCreator_iOS/JSONCreator/JSON/Samples/TileleListSample.json @@ -0,0 +1,110 @@ +{ + "ResponseInfo": {}, + "Page": { + "pageType": "contactUs", + "screenHeading": "Select an international plan", + "template": "list", + "molecules": [ + { + "moleculeName": "listItem", + "molecule": { + "directionalIcon": { + "size": "medium" + }, + "moleculeName": "tilelet", + "subTitle": { + "text": "You are enrolled in Auto Pay & paper-free billing." + }, + "title": { + "text": "You’re getting $50 off on your monthly bill." + }, + "action": { + "actionType": "openPage", + "analyticsData": { + "vzdl.page.displayChannel": "mva", + "vzwi.mvmapp.pageTypeLink": "settingsLanding|Auto Pay discount", + "vzdl.page.id": "settingslanding", + "vzdl.page.linkName": "Auto Pay discount", + "vzdl.page.sourceChannel": "mva", + "vzdl.page.name": "settings landing" + }, + "pageType": "managePmts", + "presentationStyle": "push", + "requestURL": "https://mobile-exp-qa2.vzw.com/mobile/nsa/nos/gw/launchapp/l2/webview", + "extraParameters": { + "pageTitle": "Auto Pay discount", + "screenHeading": "Auto Pay discount", + "browserUrl": "https://vzwqa2.verizonwireless.com/digital/nsa/secure/ui/payment/settings#/enrollAandP", + "locale": "EN", + "flowName": "accountsettings" + }, + "title": "Auto Pay discount" + } + } + }, + { + "moleculeName":"listItem", + "molecule": { + "moleculeName": "twoButtonView", + "primaryButton": { + "moleculeName": "button", + "title": "Edit", + "groupName": "default", + "action": { + "actionType": "openPage", + "pageType": "updateProfile", + "extraParameters": { + "from": "none" + }, + "presentationStyle": "push" + } + } + } + },{ + "moleculeName":"listItem", + "molecule": { + "moleculeName": "label", + "text": "afa\ndasfdsa\nadfadfda\nasadfsafa\nafsafsa\nafsadfas\nadffafaf" + } + },{ + "moleculeName":"listItem", + "molecule": { + "moleculeName": "label", + "text": "afa\ndasfdsa\nadfadfda\nasadfsafa\nafsafsa\nafsadfas\nadffafaf" + } + },{ + "moleculeName":"listItem", + "molecule": { + "moleculeName": "label", + "text": "afa\ndasfdsa\nadfadfda\nasadfsafa\nafsafsa\nafsadfas\nadffafaf\ndafsdssfafs" + } + },{ + "moleculeName":"listItem", + "molecule": { + "moleculeName": "label", + "text": "afa\ndasfdsa\nadfadfda\nasadfsafa\nafsafsa\nafsadfas\nadffafaf\n\nadsfa\nadfs" + } + },{ + "moleculeName":"listItem", + "molecule": { + "moleculeName": "label", + "text": "afa\ndasfdsa\nadfadfda\nasadfsafa\nafsafsa\nafsadfas\nadffafafttttt" + } + },{ + "moleculeName":"listItem", + "molecule": { + "moleculeName": "label", + "text": "afa\ndasfdsa\nadfadfda\nasadfsafa\nafsafsa\nafsadfas\nadffafaf\n\nadsfa\nadfs" + } + },{ + "moleculeName":"listItem", + "molecule": { + "moleculeName": "label", + "text": "afa\ndasfdsa\nadfadfda\nasadfsafa\nafsafsa\nafsadfas\nadffafafttttt" + } + } + ], + "middle": { + } + } +} diff --git a/JSONCreator_iOS/JSONCreator/JSON/Samples/TileletSample.json b/JSONCreator_iOS/JSONCreator/JSON/Samples/TileletSample.json new file mode 100644 index 0000000..e9b5dda --- /dev/null +++ b/JSONCreator_iOS/JSONCreator/JSON/Samples/TileletSample.json @@ -0,0 +1,64 @@ +{ + "Page": { + "template": "stack", + "pageType": "moleculeStack", + "screenHeading": "Tilet Sample", + "hideFabOverlay": true, + "suppressPostLaunchRequests": false, + "tabBarHidden": true, + "header": { + "moleculeName": "header", + "molecule": { + "moleculeName": "headlineBody", + "headline": { + "moleculeName": "label", + "text": "Tilet Variations" + } + } + }, + "stack": { + "moleculeName": "stack", + "molecules": [ + { + "moleculeName": "stackItem", + "molecule": + { + "directionalIcon": { + "size": "medium" + }, + "moleculeName": "tilelet", + "subTitle": { + "text": "You are enrolled in Auto Pay & paper-free billing." + }, + "title": { + "text": "You’re getting $50 off on your monthly bill." + }, + "action": { + "actionType": "openPage", + "analyticsData": { + "vzdl.page.displayChannel": "mva", + "vzwi.mvmapp.pageTypeLink": "settingsLanding|Auto Pay discount", + "vzdl.page.id": "settingslanding", + "vzdl.page.linkName": "Auto Pay discount", + "vzdl.page.sourceChannel": "mva", + "vzdl.page.name": "settings landing" + }, + "pageType": "managePmts", + "presentationStyle": "push", + "requestURL": "https://mobile-exp-qa2.vzw.com/mobile/nsa/nos/gw/launchapp/l2/webview", + "extraParameters": { + "pageTitle": "Auto Pay discount", + "screenHeading": "Auto Pay discount", + "browserUrl": "https://vzwqa2.verizonwireless.com/digital/nsa/secure/ui/payment/settings#/enrollAandP", + "locale": "EN", + "flowName": "accountsettings" + }, + "title": "Auto Pay discount" + } + } + } + ] + }, + "footer": {} + } +} diff --git a/JSONCreator_iOS/JSONCreator/TestToggle.swift b/JSONCreator_iOS/JSONCreator/TestToggle.swift new file mode 100644 index 0000000..61c7c08 --- /dev/null +++ b/JSONCreator_iOS/JSONCreator/TestToggle.swift @@ -0,0 +1,206 @@ +// +// TestToggle.swift +// JSONCreator +// +// Created by Matt Bruce on 10/21/22. +// Copyright © 2022 Verizon Wireless. All rights reserved. +// + +import Foundation +import MVMCore +import MVMCoreUI +import UIKit +import VDS + +/** + A custom implementation of Apple's UISwitch. + + By default this class begins in the off state. + + Container: The background of the toggle control. + Knob: The circular indicator that slides on the container. + */ +open class TestToggle: VDS.Toggle, VDSMoleculeViewProtocol { + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + public var viewModel: TestToggleModel! + public var delegateObject: MVMCoreUIDelegateObject? + public var additionalData: [AnyHashable: Any]? + + //-------------------------------------------------- + // MARK: - Initializers + //-------------------------------------------------- + public override func initialSetup() { + super.initialSetup() + publisher(for: .valueChanged) + .sink {[weak self] toggle in + guard let self = self else { return } + self.valueChanged(isOn: toggle.isOn) + }.store(in: &subscribers) + } + + // MARK:- MVMCoreViewProtocol + open func updateView(_ size: CGFloat) {} + + override open func updateView() { + super.updateView() + + //accessibility + accessibilityLabel = MVMCoreUIUtility.hardcodedString(withKey: "Toggle_buttonlabel") + accessibilityHint = isEnabled ? MVMCoreUIUtility.hardcodedString(withKey: "AccToggleHint") : MVMCoreUIUtility.hardcodedString(withKey: "AccDisabled") + accessibilityValue = isOn ? MVMCoreUIUtility.hardcodedString(withKey: "AccOn") : MVMCoreUIUtility.hardcodedString(withKey: "AccOff") + } + + open func viewModelDidUpdate() { + guard let viewModel else { return } + + additionalData = additionalData.dictionaryAdding(key: KeySourceModel, value: viewModel) + } + + private func valueChanged(isOn: Bool){ + guard let viewModel else { return } + //sync the value on the viewModel + viewModel.selected = isOn + + //tell the form you changed + _ = FormValidator.validate(delegate: self.delegateObject?.formHolderDelegate) + + if viewModel.action != nil || viewModel.alternateAction != nil { + var action: ActionModelProtocol? + if isOn { + action = viewModel.action + } else { + action = viewModel.alternateAction ?? viewModel.action + } + if let action { + MVMCoreUIActionHandler.performActionUnstructured(with: action, + sourceModel: viewModel, + additionalData: additionalData, + delegateObject: delegateObject) + } + } + + print("toggle value changed to: \(isOn)") + print("viewModel server value: \(viewModel.formFieldServerValue()!)") + } + + public static func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { + return 44 + } + + private typealias ActionDefinition = (model: ActionModelProtocol, + sourceModel: MoleculeModelProtocol?) + + private func performActionUnstructured(definition: ActionDefinition) { + MVMCoreUIActionHandler.performActionUnstructured(with: definition.model, + sourceModel: definition.sourceModel, + additionalData: additionalData, + delegateObject: delegateObject) + } +} +// MARK: - MVMCoreUIViewConstrainingProtocol +extension TestToggle { + + public func needsToBeConstrained() -> Bool { true } + + public func horizontalAlignment() -> UIStackView.Alignment { .trailing } +} + +public class TestToggleModel: MoleculeModelProtocol, FormFieldProtocol { + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + + public static var identifier: String = "testToggle" + public var backgroundColor: Color? //not used + + public var selected: Bool = false + public var enabled: Bool = true + public var readOnly: Bool = false + public var action: ActionModelProtocol? + public var alternateAction: ActionModelProtocol? + public var accessibilityText: String? + public var fieldKey: String? + public var groupName: String = FormValidator.defaultGroupName + public var baseValue: AnyHashable? + + //-------------------------------------------------- + // MARK: - Keys + //-------------------------------------------------- + + private enum CodingKeys: String, CodingKey { + case moleculeName + case state + case enabled + case readOnly + case action + case accessibilityIdentifier + case alternateAction + case accessibilityText + case fieldKey + case groupName + } + + //-------------------------------------------------- + // MARK: - Form Valdiation + //-------------------------------------------------- + + public func formFieldValue() -> AnyHashable? { + guard enabled else { return nil } + return selected + } + + //-------------------------------------------------- + // MARK: - Server Value + //-------------------------------------------------- + open func formFieldServerValue() -> AnyHashable? { + return formFieldValue() + } + + //-------------------------------------------------- + // MARK: - Initializer + //-------------------------------------------------- + + public init(_ state: Bool) { + selected = state + baseValue = state + } + + //-------------------------------------------------- + // MARK: - Codec + //-------------------------------------------------- + + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + + if let state = try typeContainer.decodeIfPresent(Bool.self, forKey: .state) { + selected = state + } + action = try typeContainer.decodeModelIfPresent(codingKey: .action) + alternateAction = try typeContainer.decodeModelIfPresent(codingKey: .alternateAction) + accessibilityText = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityText) + baseValue = selected + fieldKey = try typeContainer.decodeIfPresent(String.self, forKey: .fieldKey) + if let gName = try typeContainer.decodeIfPresent(String.self, forKey: .groupName) { + groupName = gName + } + enabled = try typeContainer.decodeIfPresent(Bool.self, forKey: .enabled) ?? true + readOnly = try typeContainer.decodeIfPresent(Bool.self, forKey: .readOnly) ?? false + + + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encodeIfPresent(accessibilityIdentifier, forKey: .accessibilityIdentifier) + try container.encodeModelIfPresent(action, forKey: .action) + try container.encodeModelIfPresent(alternateAction, forKey: .alternateAction) + try container.encode(moleculeName, forKey: .moleculeName) + try container.encode(selected, forKey: .state) + try container.encode(enabled, forKey: .enabled) + try container.encodeIfPresent(fieldKey, forKey: .fieldKey) + try container.encodeIfPresent(groupName, forKey: .groupName) + try container.encode(readOnly, forKey: .readOnly) + } +} diff --git a/dependency.txt b/dependency.txt index e567159..4cf000f 100644 --- a/dependency.txt +++ b/dependency.txt @@ -1,6 +1,6 @@ mvm_core https://gitlab.verizon.com/BPHV_MIPS/mvm_core.git develop -mvm_core_ui https://gitlab.verizon.com/BPHV_MIPS/mvm_core_ui.git develop +mvm_core_ui https://gitlab.verizon.com/BPHV_MIPS/mvm_core_ui.git feature/ONEAPP-2811-Tilet vds_ios https://gitlab.verizon.com/BPHV_MIPS/vds_ios.git develop