Skip to content

Commit

Permalink
Merge pull request #660 from adevinta/feature/component/rating-input-…
Browse files Browse the repository at this point in the history
…1676

[RatingInput#1676] The rating input component
  • Loading branch information
michael-zimmermann authored Dec 14, 2023
2 parents 304271f + 9ada879 commit fb08951
Show file tree
Hide file tree
Showing 24 changed files with 916 additions and 33 deletions.
17 changes: 17 additions & 0 deletions core/Sources/Common/Foundation/Extension/CGPoint-Distance.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//
// CGPoint-Distance.swift
// SparkCore
//
// Created by Michael Zimmermann on 30.11.23.
// Copyright © 2023 Adevinta. All rights reserved.
//

import Foundation

extension CGPoint {

/// Returns the distance between two points
func distance(to other: CGPoint) -> CGFloat {
CGFloat(hypotf(Float(self.x - other.x), Float(self.y - other.y)))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//
// CGPointDistanceTests.swift
// SparkCoreUnitTests
//
// Created by Michael Zimmermann on 30.11.23.
// Copyright © 2023 Adevinta. All rights reserved.
//

import XCTest

@testable import SparkCore

final class CGPointDistanceTests: XCTestCase {

func test_distance_same() throws {
let point1 = CGPoint(x: 10, y: 10)
let point2 = CGPoint(x: 10, y: -100)

let distance1 = point1.distance(to: point2)
let distance2 = point2.distance(to: point1)

XCTAssertEqual(distance1, 110.0, "Expected distance does not match")
XCTAssertEqual(distance1, distance2, "Expected both distances to be the same")
}
}
26 changes: 26 additions & 0 deletions core/Sources/Common/Foundation/Extension/CGRect-Center.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//
// CGRect.swift
// SparkCore
//
// Created by Michael Zimmermann on 30.11.23.
// Copyright © 2023 Adevinta. All rights reserved.
//

import Foundation

extension CGRect {
/// Returns the center of the x-coordinate of the rect
var centerX: CGFloat {
return (self.minX + self.maxX)/2
}

/// Returns the center of the y-coordinate of the rect
var centerY: CGFloat {
return (self.minY + self.maxY)/2
}

/// The center point of the rect
var center: CGPoint {
return CGPoint(x: self.centerX, y: self.centerY)
}
}
24 changes: 24 additions & 0 deletions core/Sources/Common/Foundation/Extension/CGRectCenterTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//
// CGRectCenterTests.swift
// SparkCoreUnitTests
//
// Created by Michael Zimmermann on 30.11.23.
// Copyright © 2023 Adevinta. All rights reserved.
//

import XCTest

@testable import SparkCore

final class CGRectCenterTests: XCTestCase {

func testExample() throws {
let rect = CGRect(x: 10, y: 10, width: 110, height: 30)

XCTAssertEqual(rect.centerX, 65, "CenterX doesn't match expected value")
XCTAssertEqual(rect.centerY, 25, "CenterY doesn't match expected value")

XCTAssertEqual(rect.center, CGPoint(x: 65, y: 25), "Center point is not correct")
}

}
24 changes: 24 additions & 0 deletions core/Sources/Common/Foundation/Extension/UIView-Closest.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//
// UIView-Closest.swift
// SparkCore
//
// Created by Michael Zimmermann on 30.11.23.
// Copyright © 2023 Adevinta. All rights reserved.
//

import Foundation
import UIKit

extension Array where Element: UIView {

/// Returns the index of the array of views which is closest to the point.
func index(closestTo location: CGPoint) -> Int? {
let distances = self.map{ view in
view.frame.center.distance(to: location)
}
let nearest = distances.enumerated().min { (left, right) in
return left.element < right.element
}
return nearest?.offset
}
}
24 changes: 24 additions & 0 deletions core/Sources/Common/Foundation/Extension/UIViewClosestTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//
// UIViewClosestTests.swift
// SparkCoreUnitTests
//
// Created by Michael Zimmermann on 30.11.23.
// Copyright © 2023 Adevinta. All rights reserved.
//

import XCTest
@testable import SparkCore

final class UIViewClosestTests: XCTestCase {

func test_closest() throws {
let positions = [0, 100, 200, 300]
let views = positions.map{ CGRect(x: $0, y: 10, width: 50, height: 50) }.map(UIView.init(frame:))

for (index, position) in positions.enumerated() {
let closestIndex = views.index(closestTo: CGPoint(x: position+50, y: 100))
XCTAssertEqual(closestIndex, index, "Expected \(String(describing: closestIndex)) to be equal to \(index)")
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//
// RatingInputAccessibilityIdentifier.swift
// SparkCore
//
// Created by Michael Zimmermann on 27.11.23.
// Copyright © 2023 Adevinta. All rights reserved.
//

import Foundation

/// The accessibility identifiers of the rating input.
public enum RatingInputAccessibilityIdentifier {

// MARK: - Properties

/// The accessibility identifier.
public static let identifier = "spark-rating-input"
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
//

import Foundation

import UIKit

@testable import SparkCore

struct RatingDisplayConfigurationSnapshotTests {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@
//

import Foundation

@testable import SparkCore
import UIKit
import SwiftUI

@testable import SparkCore

enum RatingDisplayScenarioSnapshotTests: String, CaseIterable {
case test1
case test2
Expand Down Expand Up @@ -41,7 +41,7 @@ enum RatingDisplayScenarioSnapshotTests: String, CaseIterable {
/// Description: To various rating values
///
/// Content:
/// - ratings: [0.0, 1.0, 2.5, 5.5]
/// - ratings: [1.0, 2.5, 5.5]
/// - size: medium
/// - count: five (number of stars)
/// - modes: all
Expand Down Expand Up @@ -90,7 +90,7 @@ enum RatingDisplayScenarioSnapshotTests: String, CaseIterable {
/// Content:
/// - ratings: [2.5]
/// - size: [small, medium, input]
/// - count: one (number of stars)
/// - count: five (number of stars)
/// - modes: default
/// - accessibility sizes: default
private func test3(isSwiftUIComponent: Bool) -> [RatingDisplayConfigurationSnapshotTests] {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
//
// RatingInputConfigurationSnapshotTests.swift
// SparkCoreUnitTests
//
// Created by Michael Zimmermann on 30.11.23.
// Copyright © 2023 Adevinta. All rights reserved.
//

import Foundation
import UIKit

@testable import SparkCore

struct RatingInputConfigurationSnapshotTests {

// MARK: - Properties

let scenario: RatingInputScenarioSnapshotTests

let rating: CGFloat
let intent = RatingIntent.main

let modes: [ComponentSnapshotTestMode]
let sizes: [UIContentSizeCategory]
let state: RatingInputState

// MARK: - Getter

func testName() -> String {
return [
"\(self.scenario.rawValue)",
"\(self.intent)",
"\(self.rating)",
"\(self.state)"
].joined(separator: "-")
}
}

enum RatingInputState: CaseIterable {
case enabled
case disabled
case pressed
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
//
// RatingInputScenarioSnapshotTests.swift
// SparkCoreUnitTests
//
// Created by Michael Zimmermann on 30.11.23.
// Copyright © 2023 Adevinta. All rights reserved.
//

import Foundation
import UIKit
import SwiftUI

@testable import SparkCore

enum RatingInputScenarioSnapshotTests: String, CaseIterable {

case test1
case test2
case test3

// MARK: - Type Alias

typealias Constants = ComponentSnapshotTestConstants


// MARK: - Configurations
func configuration(isSwiftUIComponent: Bool) -> [RatingInputConfigurationSnapshotTests] {
switch self {
case .test1:
return self.test1(isSwiftUIComponent: isSwiftUIComponent)
case .test2:
return self.test2(isSwiftUIComponent: isSwiftUIComponent)
case .test3:
return self.test3(isSwiftUIComponent: isSwiftUIComponent)
}
}

// MARK: - Scenarios

/// Test 1
///
/// Description: To various rating values
///
/// Content:
/// - ratings: 2.0
/// - states: enabled
/// - modes: all
/// - accessibility sizes: default
private func test1(isSwiftUIComponent: Bool) -> [RatingInputConfigurationSnapshotTests] {
let ratings: [CGFloat] = [1.0, 5.0]

return ratings.map { rating in
return .init(
scenario: self,
rating: rating,
modes: Constants.Modes.all,
sizes: Constants.Sizes.default,
state: .enabled
)
}
}

/// Test 2
///
///
/// Description: To various accessibility sizes
///
/// Content:
/// - ratings: [1.0]
/// - modes: default
/// - accessibility sizes: all
/// - states: all
private func test2(isSwiftUIComponent: Bool) -> [RatingInputConfigurationSnapshotTests] {
return [.init(
scenario: self,
rating: 1.0,
modes: Constants.Modes.default,
sizes: Constants.Sizes.all,
state: .enabled
)]
}

/// Test 3
///
/// Description: To various rating values
///
/// Content:
/// - ratings: [1.0, 5.0]
/// - states: disabled, pressed
/// - modes: all
/// - accessibility sizes: default
private func test3(isSwiftUIComponent: Bool) -> [RatingInputConfigurationSnapshotTests] {

return [RatingInputState.disabled, .pressed].map { state in
return .init(
scenario: self,
rating: 2.0,
modes: Constants.Modes.all,
sizes: Constants.Sizes.default,
state: state
)
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ struct RatingGetColorsUseCase: RatingGetColorsUseCaseable {

switch intent {
case .main: colors = RatingColors(
fillColor: state.isPressed ? theme.colors.main.onMain : theme.colors.main.mainVariant,
fillColor: state.isPressed ? theme.colors.states.mainVariantPressed : theme.colors.main.mainVariant,
strokeColor: theme.colors.base.onSurface.opacity(theme.dims.dim3), opacity: theme.dims.none
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ final class RatingGetColorsUseCaseUnitTests: XCTestCase {

// Then
let expectedColors = RatingColors(
fillColor: theme.colors.main.onMain,
fillColor: theme.colors.states.mainVariantPressed,
strokeColor: theme.colors.base.onSurface.opacity(theme.dims.dim3),
opacity: theme.dims.none)

Expand Down
Loading

0 comments on commit fb08951

Please sign in to comment.