Skip to content

Commit

Permalink
Merge pull request #235 from adevinta/feature/component/icon
Browse files Browse the repository at this point in the history
[Icon#205] Create UIKit icon component
  • Loading branch information
jacklyn-situmorang authored Jul 27, 2023
2 parents 351e403 + 6a3d631 commit 5ac238e
Show file tree
Hide file tree
Showing 12 changed files with 864 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//
// IconAccessibilityIdentifier.swift
// SparkCore
//
// Created by Jacklyn Situmorang on 20.07.23.
// Copyright © 2023 Adevinta. All rights reserved.
//

import Foundation

public enum IconAccessibilityIdentifier {

// MARK: - Properties

public static let view = "spark-icon-image"
}
19 changes: 19 additions & 0 deletions core/Sources/Components/Icon/Enum/IconIntent.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//
// IconIntent.swift
// Spark
//
// Created by Jacklyn Situmorang on 10.07.23.
// Copyright © 2023 Adevinta. All rights reserved.
//

import Foundation

/// Intents of the icon.
public enum IconIntent: CaseIterable {
case alert
case error
case neutral
case primary
case secondary
case success
}
48 changes: 48 additions & 0 deletions core/Sources/Components/Icon/Enum/IconSize.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
//
// IconSize.swift
// SparkCore
//
// Created by Jacklyn Situmorang on 11.07.23.
// Copyright © 2023 Adevinta. All rights reserved.
//

import Foundation

/// Different sizes of icon.
public enum IconSize: CaseIterable {
/// Small icon with size of 16x16px
case small

/// Small icon with size of 24x24px
case medium

/// Small icon with size of 32x32px
case large

/// Small icon with size of 40x40px
case extraLarge
}

// MARK: - Extension
extension IconSize {
public var value: CGFloat {
switch self {
case .small:
return Constants.valueSmall
case .medium:
return Constants.valueMedium
case .large:
return Constants.valueLarge
case .extraLarge:
return Constants.valueExtraLarge
}
}
}

// MARK: - Constants
private enum Constants {
static var valueSmall: CGFloat = 16
static var valueMedium: CGFloat = 24
static var valueLarge: CGFloat = 32
static var valueExtraLarge: CGFloat = 40
}
36 changes: 36 additions & 0 deletions core/Sources/Components/Icon/UseCase/IconGetColorUseCase.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
//
// IconGetColorUseCase.swift
// Spark
//
// Created by Jacklyn Situmorang on 10.07.23.
// Copyright © 2023 Adevinta. All rights reserved.
//

import Foundation

// sourcery: AutoMockable
protocol IconGetColorUseCaseable {
func execute(for intent: IconIntent, colors: Colors) -> any ColorToken
}

struct IconGetColorUseCase: IconGetColorUseCaseable {

// MARK: - Methods

func execute(for intent: IconIntent, colors: Colors) -> any ColorToken {
switch intent {
case .alert :
return colors.feedback.alert
case .error:
return colors.feedback.error
case .neutral:
return colors.feedback.neutral
case .primary:
return colors.primary.primary
case .secondary:
return colors.secondary.secondary
case .success:
return colors.feedback.success
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
//
// IconGetColorUseCaseTests.swift
// Spark
//
// Created by Jacklyn Situmorang on 10.07.23.
// Copyright © 2023 Adevinta. All rights reserved.
//

import SwiftUI
import XCTest

@testable import SparkCore

final class IconGetColorUseCaseTests: XCTestCase {

// MARK: - Properties

private let colorsMock = ColorsGeneratedMock.mocked()

// MARK: - Tests

func test_execute_when_icon_is_alert_case() {
testExecute(givenIntent: .alert, expectedColorToken: self.colorsMock.feedback.alert)
}

func test_execute_when_icon_is_error_case() {
testExecute(givenIntent: .error, expectedColorToken: self.colorsMock.feedback.error)
}

func test_execute_when_icon_is_neutral_case() {
testExecute(givenIntent: .neutral, expectedColorToken: self.colorsMock.feedback.neutral)
}

func test_execute_when_icon_is_primary_case() {
testExecute(givenIntent: .primary, expectedColorToken: self.colorsMock.primary.primary)
}

func test_execute_when_icon_is_secondary_case() {
testExecute(givenIntent: .secondary, expectedColorToken: self.colorsMock.secondary.secondary)
}

func test_execute_when_icon_is_success_case() {
testExecute(givenIntent: .success, expectedColorToken: self.colorsMock.feedback.success)
}
}

// MARK: - Extension

private extension IconGetColorUseCaseTests {
func testExecute(
givenIntent: IconIntent,
expectedColorToken: any ColorToken
) {
// GIVEN
let useCase = IconGetColorUseCase()

// WHEN
let colorToken = useCase.execute(
for: givenIntent,
colors: colorsMock
)

// THEN
XCTAssertIdentical(
colorToken as? ColorTokenGeneratedMock,
expectedColorToken as? ColorTokenGeneratedMock,
"Wrong color for .\(givenIntent) case"
)
}
}
64 changes: 64 additions & 0 deletions core/Sources/Components/Icon/View/Model/IconViewModel.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
//
// IconViewModel.swift
// SparkCore
//
// Created by Jacklyn Situmorang on 11.07.23.
// Copyright © 2023 Adevinta. All rights reserved.
//

import Foundation

final class IconViewModel: ObservableObject {

// MARK: - Properties

private(set) var theme: Theme
private(set) var intent: IconIntent
private let getColorUseCase: IconGetColorUseCaseable

// MARK: - Published properties

@Published var color: any ColorToken
@Published var size: IconSize

// MARK: - Initializers

init(
theme: Theme,
intent: IconIntent,
size: IconSize,
getColorUseCase: IconGetColorUseCaseable = IconGetColorUseCase()
) {
self.theme = theme
self.intent = intent
self.size = size
self.getColorUseCase = getColorUseCase
self.color = getColorUseCase.execute(for: intent, colors: theme.colors)
}

// MARK: - Setters

func set(theme: Theme) {
self.theme = theme
self.updateColor()
}

func set(intent: IconIntent) {
if self.intent != intent {
self.intent = intent
self.updateColor()
}
}

func set(size: IconSize) {
if self.size != size {
self.size = size
}
}

// MARK: - Private funcs

private func updateColor() {
self.color = self.getColorUseCase.execute(for: self.intent, colors: self.theme.colors)
}
}
Loading

0 comments on commit 5ac238e

Please sign in to comment.