Skip to content

Commit

Permalink
Merge pull request #8 from adevinta/feature/counter-sui
Browse files Browse the repository at this point in the history
[Counter] Add a counter on SwiftUI
  • Loading branch information
robergro authored Jan 13, 2025
2 parents ebec804 + 0b37914 commit 7d09505
Show file tree
Hide file tree
Showing 6 changed files with 150 additions and 6 deletions.
18 changes: 17 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,14 @@ The FormField contains some public subviews :

#### Functions:

If the component inside the FormField is inherit from an UITextInput (The Spark TextField and TextEditor for example), a function to set the number of the characters is available:
If the component inside the FormField is inherit from an UITextInput (The Spark TextField and TextEditor for example), two functions to set the number of the characters are available:

```swift
// With the text
func setCounter(on text: String?, limit: Int?)

// Or with the text length
func setCounter(on textLength: Int, limit: Int?)
```

### FormFieldView
Expand All @@ -56,6 +60,18 @@ func setCounter(on text: String?, limit: Int?)
- `attributedDescription`: An option attributed string to change helper message of font or size.
- `isTitleRequired`: A bool value to add asterisk character at the end of title for specifying required field.

#### Modifiers:

Two modifier functions to set the number of the characters are available:

```swift
// With the text
func counter(on text: String, limit: Int?) -> Self

// Or with the text length
func counter(on textLength: Int, limit: Int?) -> Self
```

## Examples

### FormFieldUIView
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,5 @@ public enum FormFieldAccessibilityIdentifier {
public static let formField = "spark-formfield"
public static let formFieldLabel = "spark-formfield-label"
public static let formFieldHelperMessage = "spark-formfield-helper-message"
public static let formFieldInfoMessage = "spark-formfield-secondary-helper-message"
public static let formFieldSecondaryHelperMessage = "spark-formfield-secondary-helper-message"
}
75 changes: 75 additions & 0 deletions Sources/Core/View/SwiftUI/FormFieldView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import SwiftUI
import SparkTheming
@_spi(SI_SPI) import SparkCommon

public struct FormFieldView<Component: View>: View {

Expand All @@ -17,6 +18,10 @@ public struct FormFieldView<Component: View>: View {
@ScaledMetric private var spacing: CGFloat
private let component: Component

private var titleAccessibility: Accessibility = .init()
private var helperAccessibility: Accessibility = .init()
private var secondaryHelperAccessibility: Accessibility = .init()

// MARK: - Initialization

/// Initialize a formField.
Expand Down Expand Up @@ -140,6 +145,7 @@ public struct FormFieldView<Component: View>: View {
.font(self.viewModel.titleFont.font)
.foregroundStyle(self.viewModel.titleColor.color)
.accessibilityIdentifier(FormFieldAccessibilityIdentifier.formFieldLabel)
.accessibility(self.titleAccessibility)
}
self.component

Expand All @@ -149,10 +155,79 @@ public struct FormFieldView<Component: View>: View {
.font(self.viewModel.helperFont.font)
.foregroundStyle(self.viewModel.helperColor.color)
.accessibilityIdentifier(FormFieldAccessibilityIdentifier.formFieldHelperMessage)
.accessibility(self.helperAccessibility)
}

if let secondaryHelper = self.viewModel.secondaryHelper {
Spacer(minLength: 0)

Text(secondaryHelper)
.font(self.viewModel.secondaryHelperFont.font)
.foregroundStyle(self.viewModel.secondaryHelperColor.color)
.accessibilityIdentifier(FormFieldAccessibilityIdentifier.formFieldSecondaryHelperMessage)
.accessibility(self.secondaryHelperAccessibility)
}
}
}
.accessibilityElement(children: .contain)
.accessibilityIdentifier(FormFieldAccessibilityIdentifier.formField)
}

// MARK: - Accessibility Modifier

/// Set accessibility label for the *title* subview.
/// - parameter label: the accessibility label.
/// - Returns: The current view.
public func titleAccessibilityLabel(_ label: String) -> Self {
var copy = self
copy.titleAccessibility.label = label
return copy
}

/// Set accessibility label for the *helper* subview.
/// - parameter label: the accessibility label.
/// - Returns: The current view.
public func helperAccessibilityLabel(_ label: String) -> Self {
var copy = self
copy.helperAccessibility.label = label
return copy
}

/// Set accessibility label for the *secondaryHelper* subview.
/// - parameter label: the accessibility label.
/// - Returns: The current view.
public func secondaryHelperAccessibilityLabel(_ label: String) -> Self {
var copy = self
copy.secondaryHelperAccessibility.label = label
return copy
}

/// Set accessibility value for the *secondaryHelper* subview.
/// - parameter value: the accessibility value.
/// - Returns: The current view.
public func secondaryHelperAccessibilityValue(_ value: String) -> Self {
var copy = self
copy.secondaryHelperAccessibility.value = value
return copy
}

// MARK: - Counter Modifier

/// Display a counter value (X/Y) in the secondary helper label with a text and the limit.
/// - parameter text: the text where the characters must be counted.
/// - parameter limit: the counter limit. If the value is nil, the counter is not displayed.
/// - Returns: The current view.
public func counter(on text: String, limit: Int?) -> Self {
self.viewModel.setCounter(textLength: text.count, limit: limit)
return self
}

/// Display a counter value (X/Y) in the secondary helper label with a text length and the limit.
/// - parameter textLength: the text length.
/// - parameter limit: the counter limit. If the value is nil, the counter is not displayed.
/// - Returns: The current view.
public func counter(on textLength: Int, limit: Int?) -> Self {
self.viewModel.setCounter(textLength: textLength, limit: limit)
return self
}
}
21 changes: 18 additions & 3 deletions Sources/Core/View/UIKit/FormFieldUIView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public final class FormFieldUIView<Component: UIView>: UIView {
stackView.axis = .vertical
stackView.spacing = self.spacing
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.accessibilityUserInputLabels
return stackView
}()

Expand Down Expand Up @@ -73,7 +74,7 @@ public final class FormFieldUIView<Component: UIView>: UIView {
label.backgroundColor = .clear
label.adjustsFontForContentSizeCategory = true
label.isHidden = true
label.accessibilityIdentifier = FormFieldAccessibilityIdentifier.formFieldInfoMessage
label.accessibilityIdentifier = FormFieldAccessibilityIdentifier.formFieldSecondaryHelperMessage
label.isAccessibilityElement = true
label.setContentCompressionResistancePriority(.required, for: .horizontal)
return label
Expand Down Expand Up @@ -410,7 +411,14 @@ public extension FormFieldUIView where Component: UITextInput {
/// - parameter text: the text where the characters must be counted.
/// - parameter limit: the counter limit. If the value is nil, the counter is not displayed.
func setCounter(on text: String?, limit: Int?) {
self.viewModel.setCounter(text: text, limit: limit)
self.viewModel.setCounter(textLength: text?.count, limit: limit)
}

/// Display a counter value (X/Y) in the secondary helper label with a text length and the limit.
/// - parameter textLength: the text length.
/// - parameter limit: the counter limit. If the value is nil, the counter is not displayed.
func setCounter(on textLength: Int, limit: Int?) {
self.viewModel.setCounter(textLength: textLength, limit: limit)
}
}

Expand All @@ -422,6 +430,13 @@ public extension FormFieldUIView where Component: TextFieldAddonsUIView {
/// - parameter text: the text where the characters must be counted.
/// - parameter limit: the counter limit. If the value is nil, the counter is not displayed.
func setCounter(on text: String?, limit: Int?) {
self.viewModel.setCounter(text: text, limit: limit)
self.viewModel.setCounter(textLength: text?.count, limit: limit)
}

/// Display a counter value (X/Y) in the secondary helper label with a text length and the limit.
/// - parameter textLength: the text length.
/// - parameter limit: the counter limit. If the value is nil, the counter is not displayed.
func setCounter(on textLength: Int, limit: Int?) {
self.viewModel.setCounter(textLength: textLength, limit: limit)
}
}
6 changes: 5 additions & 1 deletion Sources/Core/ViewModel/FormFieldViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -92,12 +92,16 @@ final class FormFieldViewModel<AS: SparkAttributedString>: ObservableObject {
}

func setCounter(text: String?, limit: Int?) {
self.setCounter(textLength: text?.count, limit: limit)
}

func setCounter(textLength: Int?, limit: Int?) {
guard let limit else {
self.secondaryHelper = nil
return
}

self.secondaryHelper = "\(text?.count ?? 0)/\(limit)"
self.secondaryHelper = "\(textLength ?? 0)/\(limit)"
}

// MARK: - Private Update
Expand Down
34 changes: 34 additions & 0 deletions Tests/UnitTests/ViewModel/FormFieldViewModelTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,40 @@ final class FormFieldViewModelTests: XCTestCase {
XCTAssertEqual(viewModel.secondaryHelper, "0/100")
}

func test_setCounter_with_textLength() {
// Given
let viewModel = FormFieldViewModel<NSAttributedString>(
theme: self.theme,
feedbackState: .default,
title: NSAttributedString("Title"),
helper: NSAttributedString("Helper"),
isTitleRequired: true
)

// When
viewModel.setCounter(textLength: 4, limit: 100)

// Then
XCTAssertEqual(viewModel.secondaryHelper, "4/100")
}

func test_setCounter_without_textLength() {
// Given
let viewModel = FormFieldViewModel<NSAttributedString>(
theme: self.theme,
feedbackState: .default,
title: NSAttributedString("Title"),
helper: NSAttributedString("Helper"),
isTitleRequired: true
)

// When
viewModel.setCounter(textLength: nil, limit: 100)

// Then
XCTAssertEqual(viewModel.secondaryHelper, "0/100")
}

func test_setCounter_without_limit() {
// Given
let viewModel = FormFieldViewModel<NSAttributedString>(
Expand Down

0 comments on commit 7d09505

Please sign in to comment.