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

feat: 🎸 [JIRA-HCPSDKFIORIUIKIT-2606] Form Cells: Mandator Field #701

Merged
merged 7 commits into from
Jun 12, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ struct KeyValueFormViewExample: View {
@State var showsCharCount = false
@State var allowsBeyondLimit = false
@State var hidesReadonlyHint = false

@State var isRequired = false

var body: some View {
VStack {
Text("KeyValueFormViewExample")
Expand All @@ -61,17 +62,20 @@ struct KeyValueFormViewExample: View {
Toggle("Hides Read-Only Hint", isOn: self.$hidesReadonlyHint)
.padding(.leading, 16)
.padding(.trailing, 16)
Toggle("Mandatory Field", isOn: self.$isRequired)
.padding(.leading, 16)
.padding(.trailing, 16)

Text("Default KeyValueFormView")
KeyValueFormView(title: self.key1, text: self.$valueText1, placeholder: "KeyValueFormView", errorMessage: self.getErrorMessage(), maxTextLength: self.getMaxTextLength(), hintText: self.getHintText(), isCharCountEnabled: self.showsCharCount, allowsBeyondLimit: self.allowsBeyondLimit)
KeyValueFormView(title: self.key1, text: self.$valueText1, placeholder: "KeyValueFormView", errorMessage: self.getErrorMessage(), maxTextLength: self.getMaxTextLength(), hintText: self.getHintText(), isCharCountEnabled: self.showsCharCount, allowsBeyondLimit: self.allowsBeyondLimit, isRequired: self.isRequired)

Text("Existing Text")
.italic()
KeyValueFormView(title: self.key2, text: self.$valueText2, placeholder: "KeyValueFormView", errorMessage: self.getErrorMessage(), maxTextLength: self.getMaxTextLength(), hintText: self.getHintText(), isCharCountEnabled: self.showsCharCount, allowsBeyondLimit: self.allowsBeyondLimit)
KeyValueFormView(title: self.key2, text: self.$valueText2, placeholder: "KeyValueFormView", errorMessage: self.getErrorMessage(), maxTextLength: self.getMaxTextLength(), hintText: self.getHintText(), isCharCountEnabled: self.showsCharCount, allowsBeyondLimit: self.allowsBeyondLimit, isRequired: self.isRequired)

Text("minHeight 50, maxHeight 200")
.italic()
KeyValueFormView(title: self.key3, text: self.$valueText3, placeholder: "Please enter something", errorMessage: self.getErrorMessage(), minTextEditorHeight: 50, maxTextEditorHeight: 200, hintText: self.getHintText(), allowsBeyondLimit: self.allowsBeyondLimit)
KeyValueFormView(title: self.key3, text: self.$valueText3, placeholder: "Please enter something", errorMessage: self.getErrorMessage(), minTextEditorHeight: 50, maxTextEditorHeight: 200, hintText: self.getHintText(), allowsBeyondLimit: self.allowsBeyondLimit, isRequired: self.isRequired)

Text("Disabled")
KeyValueFormView(title: "Disabled", text: self.$disabledText, placeholder: "Disabled", controlState: .disabled, minTextEditorHeight: 50, maxTextEditorHeight: 200)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ struct TextFieldFormViewExample: View {
@State var allowsBeyondLimit = false
@State var hidesReadonlyHint = false
@State var showsAction = false
@State var isRequired = false

@State var text = ""

Expand All @@ -60,23 +61,26 @@ struct TextFieldFormViewExample: View {
Toggle("Shows Action", isOn: self.$showsAction)
.padding(.leading, 16)
.padding(.trailing, 16)
Toggle("Mandatory Field", isOn: self.$isRequired)
.padding(.leading, 16)
.padding(.trailing, 16)

Text("Default TitleForm")
TextFieldFormView(title: self.key1, text: self.$valueText1, placeholder: "TextFieldFormView", errorMessage: self.getErrorMessage(), maxTextLength: self.getMaxTextLength(), hintText: self.getHintText(), isCharCountEnabled: self.showsCharCount, allowsBeyondLimit: self.allowsBeyondLimit, actionIcon: self.getActionIcon(), action: self.getAction())
TextFieldFormView(title: self.key1, text: self.$valueText1, placeholder: "TextFieldFormView", errorMessage: self.getErrorMessage(), maxTextLength: self.getMaxTextLength(), hintText: self.getHintText(), isCharCountEnabled: self.showsCharCount, allowsBeyondLimit: self.allowsBeyondLimit, isRequired: self.isRequired, actionIcon: self.getActionIcon(), action: self.getAction())

Text("Existing Text")
.italic()
TextFieldFormView(title: self.key2, text: self.$valueText2, placeholder: "TextFieldFormView", errorMessage: self.getErrorMessage(), maxTextLength: self.getMaxTextLength(), hintText: self.getHintText(), isCharCountEnabled: self.showsCharCount, allowsBeyondLimit: self.allowsBeyondLimit, actionIcon: self.getActionIcon(), action: self.getAction())
TextFieldFormView(title: self.key2, text: self.$valueText2, placeholder: "TextFieldFormView", errorMessage: self.getErrorMessage(), maxTextLength: self.getMaxTextLength(), hintText: self.getHintText(), isCharCountEnabled: self.showsCharCount, allowsBeyondLimit: self.allowsBeyondLimit, isRequired: self.isRequired, actionIcon: self.getActionIcon(), action: self.getAction())

Text("Empty Text")
.italic()
TextFieldFormView(title: self.key3, text: self.$valueText3, placeholder: "Please enter something", errorMessage: self.getErrorMessage(), maxTextLength: self.getMaxTextLength(), hintText: self.getHintText(), isCharCountEnabled: self.showsCharCount, allowsBeyondLimit: self.allowsBeyondLimit, actionIcon: self.getActionIcon(), action: self.getAction())
TextFieldFormView(title: self.key3, text: self.$valueText3, placeholder: "Please enter something", errorMessage: self.getErrorMessage(), maxTextLength: self.getMaxTextLength(), hintText: self.getHintText(), isCharCountEnabled: self.showsCharCount, allowsBeyondLimit: self.allowsBeyondLimit, isRequired: self.isRequired, actionIcon: self.getActionIcon(), action: self.getAction())

Text("Disabled")
TextFieldFormView(title: "Disabled Cell", text: self.$disabledText, placeholder: "Disabled", controlState: .disabled, actionIcon: self.getActionIcon(), action: self.getAction())
TextFieldFormView(title: "Disabled Cell", text: self.$disabledText, placeholder: "Disabled", controlState: .disabled, isRequired: self.isRequired, actionIcon: self.getActionIcon(), action: self.getAction())

Text("Read-Only")
TextFieldFormView(title: "Read-Only Cell", text: self.$readOnlyText, placeholder: "Read-Only", controlState: .readOnly, hidesReadOnlyHint: self.hidesReadonlyHint, actionIcon: self.getActionIcon(), action: self.getAction())
TextFieldFormView(title: "Read-Only Cell", text: self.$readOnlyText, placeholder: "Read-Only", controlState: .readOnly, hidesReadOnlyHint: self.hidesReadonlyHint, isRequired: self.isRequired, actionIcon: self.getActionIcon(), action: self.getAction())
}
.scrollDismissesKeyboard(.immediately)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ struct SignatureCaptureViewExample2: View {
.hidesXmark(false)
.hidesSignatureLine(true)
.addsTimestampInImage(true)
.isRequired(true)
xiaoqinggrace marked this conversation as resolved.
Show resolved Hide resolved
.timestampFormatter(self.customFormatter())
.watermarkText("A bird in the hand is worth two in the bush. Behind every great man there's a great woman.")
.watermarkTextAlignment(.right)
Expand Down
1 change: 1 addition & 0 deletions Sources/FioriSwiftUICore/Models/ModelDefinitions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,7 @@ public protocol UserConsentPageModel: TitleComponent, BodyAttributedTextComponen
// sourcery: virtualPropWatermarkTextColor = "var watermarkTextColor: Color = .preferredColor(.tertiaryLabel)"
// sourcery: virtualPropAppliesTintColorToImage = "var appliesTintColorToImage = true"
// sourcery: generated_component_composite
// sourcery: virtualPropIsRequired = "var isRequired = false"
public protocol SignatureCaptureViewModel: AnyObject {
// sourcery: default.value = nil
// sourcery: no_view
Expand Down
46 changes: 44 additions & 2 deletions Sources/FioriSwiftUICore/Views/SignatureCaptureView+View.swift
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,12 @@ extension SignatureCaptureView: View {
public var body: some View {
VStack(spacing: 0) {
HStack {
Text(_title ?? NSLocalizedString("Signature", tableName: "FioriSwiftUICore", bundle: Bundle.accessor, comment: ""))
Text(self.getKeyName())
.font(titleFont)
.foregroundColor(titleColor)
.padding(.top, 11)
.padding(.bottom, 11)
.accessibilityLabel(self.getTitleAccessibilityLabel())
Spacer()
cancelAction
.simultaneousGesture(
Expand Down Expand Up @@ -248,7 +249,36 @@ extension SignatureCaptureView: View {
}
}
}


func getKeyName() -> String {
var titleString = _title
if (titleString?.isEmpty) != nil {
titleString = NSLocalizedString("Signature", tableName: "FioriSwiftUICore", bundle: Bundle.accessor, comment: "")
}
if isRequired {
if titleString?.suffix(1) != "*" {
return (titleString ?? "") + "*"
}
} else {
if titleString?.suffix(1) == "*" {
return String(titleString?.dropLast(1) ?? "")
}
}
xiaoqinggrace marked this conversation as resolved.
Show resolved Hide resolved
return titleString ?? ""
}

func getTitleAccessibilityLabel() -> String {
var accString = ""
if isRequired {
let requiredText = NSLocalizedString("Required Field", tableName: "FioriSwiftUICore", bundle: Bundle.accessor, comment: "Required Field")
let labelString = _title?.suffix(1) == "*" ? String(_title?.dropLast(1) ?? "") : (_title ?? "")
accString = labelString + (labelString.isEmpty != nil ? ", " : "") + requiredText
} else {
accString = _title ?? ""
}
return accString
}

func setEditing() {
self.isEditing = true
}
Expand Down Expand Up @@ -508,6 +538,18 @@ public extension SignatureCaptureView {
newSelf.appliesTintColorToImage = appliesTintColorToImage
return newSelf
}

/**
A view modifier to indicate if the component is a mandatory field.

- parameter `isRequired`: A boolean variable to indicate if the cell is a mandatory field.
The default value is `false`.
*/
func isRequired(_ isRequired: Bool) -> Self {
var newSelf = self
newSelf.isRequired = isRequired
return newSelf
}
xiaoqinggrace marked this conversation as resolved.
Show resolved Hide resolved
}

private struct VStackPreferenceKey: PreferenceKey {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,13 @@ protocol _SubtitleComponent {
var subtitle: AttributedString? { get }
}

// sourcery: BaseComponent
protocol _MandatoryIndicatorComponent {
xiaoqinggrace marked this conversation as resolved.
Show resolved Hide resolved
// sourcery: @ViewBuilder
// sourcery: defaultValue = AttributedString("*")
var mandatoryIndicator: AttributedString? { get }
}

// sourcery: BaseComponent
protocol _TagsComponent {
// sourcery: resultBuilder.name = @TagBuilder, resultBuilder.backingComponent = TagStack
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,11 @@ protocol _NoteFormViewComponent: _PlaceholderTextEditorComponent, _FormViewCompo
}

// sourcery: CompositeComponent
protocol _KeyValueFormViewComponent: _TitleComponent, _NoteFormViewComponent {}
protocol _KeyValueFormViewComponent: _TitleComponent, _NoteFormViewComponent, _MandatoryIndicatorComponent {
// sourcery: defaultValue = false
/// Indicates whether the cell is a mandatory field. The default value is `false`.
var isRequired: Bool { get }
xiaoqinggrace marked this conversation as resolved.
Show resolved Hide resolved
}

// sourcery: CompositeComponent
protocol _PlaceholderTextFieldComponent: _TextInputFieldComponent, _PlaceholderComponent {}
Expand All @@ -111,7 +115,10 @@ protocol _TitleFormViewComponent: _PlaceholderTextFieldComponent, _FormViewCompo
}

// sourcery: CompositeComponent
protocol _TextFieldFormViewComponent: _TitleComponent, _TitleFormViewComponent {
protocol _TextFieldFormViewComponent: _TitleComponent, _TitleFormViewComponent, _MandatoryIndicatorComponent {
// sourcery: defaultValue = false
/// Indicates whether the cell is a mandatory field. The default value is `false`.
var isRequired: Bool { get }
/// The icon for the action button.
var actionIcon: Image? { get }
/// The action to be performed when the action button is tapped.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,13 @@ import SwiftUI
public struct KeyValueFormViewBaseStyle: KeyValueFormViewStyle {
public func makeBody(_ configuration: KeyValueFormViewConfiguration) -> some View {
VStack(alignment: .leading) {
configuration.title
HStack(spacing: 0) {
configuration.title
if configuration.isRequired {
configuration.mandatoryIndicator
}
Spacer()
}
configuration._noteFormView
}
}
Expand Down Expand Up @@ -70,4 +76,24 @@ extension KeyValueFormViewFioriStyle {
NoteFormView(configuration)
}
}

struct MandatoryIndicatorFioriStyle: MandatoryIndicatorStyle {
let keyValueFormViewConfiguration: KeyValueFormViewConfiguration

func makeBody(_ configuration: MandatoryIndicatorConfiguration) -> some View {
MandatoryIndicator(configuration)
.foregroundStyle(Color.preferredColor(self.keyValueFormViewConfiguration.controlState == .disabled ? .separator : .primaryLabel))
.font(.fiori(forTextStyle: .subheadline, weight: .semibold))
.accessibilityLabel(self.getIndicatorAccessibilityLabel(self.keyValueFormViewConfiguration))
}

func getIndicatorAccessibilityLabel(_ configuration: KeyValueFormViewConfiguration) -> String {
var accString = ""
if configuration.isRequired {
let requiredText = NSLocalizedString("Required Field", tableName: "FioriSwiftUICore", bundle: Bundle.accessor, comment: "Required Field")
accString = requiredText
}
return accString
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import FioriThemeManager
import Foundation
import SwiftUI

// Base Layout style
public struct MandatoryIndicatorBaseStyle: MandatoryIndicatorStyle {
@ViewBuilder
public func makeBody(_ configuration: MandatoryIndicatorConfiguration) -> some View {
// Add default layout here
configuration.mandatoryIndicator
}
}

// Default fiori styles
public struct MandatoryIndicatorFioriStyle: MandatoryIndicatorStyle {
@ViewBuilder
public func makeBody(_ configuration: MandatoryIndicatorConfiguration) -> some View {
MandatoryIndicator(configuration)
// Add default style here
.foregroundStyle(Color.preferredColor(.primaryLabel))
.font(.fiori(forTextStyle: .headline))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,13 @@ import SwiftUI
public struct TextFieldFormViewBaseStyle: TextFieldFormViewStyle {
public func makeBody(_ configuration: TextFieldFormViewConfiguration) -> some View {
VStack(alignment: .leading) {
configuration.title
HStack(spacing: 0) {
configuration.title
if configuration.isRequired {
configuration.mandatoryIndicator
}
Spacer()
}
configuration._titleFormView
}
}
Expand All @@ -24,6 +30,12 @@ extension TextFieldFormViewFioriStyle {
.foregroundStyle(self.getTitleColor(configuration))
.font(.fiori(forTextStyle: .subheadline, weight: .semibold))
}
.mandatoryIndicatorStyle { indicatorConf in
MandatoryIndicator(indicatorConf)
.foregroundStyle(self.getTitleColor(configuration))
.font(.fiori(forTextStyle: .subheadline, weight: .semibold))
.accessibilityLabel(self.getIndicatorAccessibilityLabel(configuration))
}
.placeholderTextFieldStyle { config in
HStack {
PlaceholderTextField(config)
Expand Down Expand Up @@ -126,6 +138,15 @@ extension TextFieldFormViewFioriStyle {
}
return configuration.actionIcon
}

func getIndicatorAccessibilityLabel(_ configuration: TextFieldFormViewConfiguration) -> String {
var accString = ""
if configuration.isRequired {
let requiredText = NSLocalizedString("Required Field", tableName: "FioriSwiftUICore", bundle: Bundle.accessor, comment: "Required Field")
accString = requiredText
}
return accString
}
}

struct TitleFioriStyle: TitleStyle {
Expand Down Expand Up @@ -159,4 +180,14 @@ extension TextFieldFormViewFioriStyle {
TitleFormView(configuration)
}
}

struct MandatoryIndicatorFioriStyle: MandatoryIndicatorStyle {
let textFieldFormViewConfiguration: TextFieldFormViewConfiguration

func makeBody(_ configuration: MandatoryIndicatorConfiguration) -> some View {
MandatoryIndicator(configuration)
.foregroundStyle(Color.preferredColor(self.textFieldFormViewConfiguration.controlState == .disabled ? .separator : .primaryLabel))
.font(.fiori(forTextStyle: .headline))
}
}
}
Loading
Loading