Skip to content

Commit

Permalink
AI-633: BannerAlert Component (#2027)
Browse files Browse the repository at this point in the history
* No-Jira: Update BPKIconView to adapt it size according to dynamic size

* Record Snapshots

* AI-633: BannerAlert component

* Update readme

* record snapshots

* Update comment

* Update comments

* Address PR comments

* record snapshots

* Fix snapshot tests

* record snapshots

* Updated snapshots

* Change init to match Web API

* record snapshots

---------

Co-authored-by: Alaa Amin <[email protected]>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
  • Loading branch information
3 people authored Aug 1, 2024
1 parent aeb0b64 commit 92aad7b
Show file tree
Hide file tree
Showing 46 changed files with 487 additions and 3 deletions.
107 changes: 107 additions & 0 deletions Backpack-SwiftUI/BannerAlert/Classes/BPKBannerAlert.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/*
* Backpack - Skyscanner's Design System
*
* Copyright 2018-2022 Skyscanner Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import SwiftUI

/// A view that displays an icon and a non-editable text.
public struct BPKBannerAlert: View {
private let type: AlertType
private let style: Style
private let message: String
private let customIcon: (icon: BPKIcon, accessibilityLabel: String)?

/// Creates a `BPKBannerAlert`.
///
/// - Parameters:
/// - type: Controls which default icon to display in the banner.
/// - style: Style of the alert (default, onContrast). It controls the alert background color.
/// - message: The text to display in BPKText.
/// - icon: Custom icon to use in the banner instead of the one associated with the AlertType.
public init(
type: AlertType,
style: Style = .default,
message: String,
icon: (icon: BPKIcon, accessibilityLabel: String)? = nil
) {
self.type = type
self.style = style
self.message = message
self.customIcon = icon
}

@ScaledMetric private var iconTopPadding: CGFloat = 1

public var body: some View {
let verticalPadding = BPKSpacing.md.value + BPKSpacing.sm.value
HStack(alignment: .top, spacing: .md) {
accessory
.padding(.top, iconTopPadding)

BPKText(message, style: .footnote)
.lineLimit(.max)
.accessibilityLabel(message)
}
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.horizontal, .base)
.padding(.vertical, verticalPadding)
.background(style.color)
.clipShape(RoundedRectangle(cornerRadius: .sm))
.accessibilityElement(children: .combine)

}

private var accessory : some View {
let icon: BPKIcon
let accessibilityLabel: String
if let customIcon {
icon = customIcon.icon
accessibilityLabel = customIcon.accessibilityLabel
} else {
icon = type.iconDetails.icon
accessibilityLabel = type.iconDetails.accessibilityLabel
}

return BPKIconView(icon, accessibilityLabel: accessibilityLabel)
.foregroundColor(type.iconDetails.color)
}
}

// swiftlint:disable line_length
struct BPKBannerAlert_Previews: PreviewProvider {
static var previews: some View {
ScrollView {
VStack {
ForEach([0, 1], id: \.self) { id in
let style: BPKBannerAlert.Style = id == 0 ? .default : .onContrast
let title = id == 0 ? "Default" : "On Contrast"
VStack {
Text("\(title) Style")
.font(.title)
BPKBannerAlert(type: .info(accessibilityLabel: "Information"), style: style, message: "Info Banner")
BPKBannerAlert(type: .warning(accessibilityLabel: "Warning"), style: style, message: "Warning Banner")
BPKBannerAlert(type: .success(accessibilityLabel: "Success"), style: style, message: "Success Banner")
BPKBannerAlert(type: .error(accessibilityLabel: "Error"), style: style, message: "Error Banner")
BPKBannerAlert(type: .info(accessibilityLabel: "Info"), style: style, message: "Mutli-line Banner \nThis is a new line")
}
.padding()
.background(id == 0 ? .canvasColor : .canvasContrastColor)
}
}
}
}
}
34 changes: 34 additions & 0 deletions Backpack-SwiftUI/BannerAlert/Classes/BPKBannerAlertStyle.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Backpack - Skyscanner's Design System
*
* Copyright 2018-2022 Skyscanner Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import SwiftUI

extension BPKBannerAlert {
/// The style of the banner, which controls its background color
public enum Style {
case `default`
case onContrast

var color: BPKColor {
switch self {
case .default: return .infoBannerDefaultColor
case .onContrast: return .infoBannerOnContrastColor
}
}
}
}
66 changes: 66 additions & 0 deletions Backpack-SwiftUI/BannerAlert/Classes/BPKBannerAlertType.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
* Backpack - Skyscanner's Design System
*
* Copyright 2018-2022 Skyscanner Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import SwiftUI

extension BPKBannerAlert {
/// The type of the banner, which controls the default icon to display and its color.
public enum AlertType {
case info(accessibilityLabel: String)
case success(accessibilityLabel: String)
case warning(accessibilityLabel: String)
case error(accessibilityLabel: String)

var iconDetails: IconDetails {
switch self {
case .info(let accessibilityLabel):
return IconDetails(
icon: .informationCircle,
color: .textSecondaryColor,
accessibilityLabel: accessibilityLabel
)
case .success(let accessibilityLabel):
return IconDetails(
icon: .tickCircle,
color: .statusSuccessSpotColor,
accessibilityLabel: accessibilityLabel
)
case .warning(let accessibilityLabel):
return IconDetails(
icon: .informationCircle,
color: .statusWarningSpotColor,
accessibilityLabel: accessibilityLabel
)
case .error(let accessibilityLabel):
return IconDetails(
icon: .closeCircle,
color: .statusDangerSpotColor,
accessibilityLabel: accessibilityLabel
)
}
}
}
}

extension BPKBannerAlert {
struct IconDetails {
let icon: BPKIcon
let color: BPKColor
let accessibilityLabel: String
}
}
47 changes: 47 additions & 0 deletions Backpack-SwiftUI/BannerAlert/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Backpack-SwiftUI/BannerAlert

[![Cocoapods](https://img.shields.io/cocoapods/v/Backpack-SwiftUI.svg?style=flat)](hhttps://cocoapods.org/pods/Backpack-SwiftUI)
[![class reference](https://img.shields.io/badge/Class%20reference-iOS-blue)](https://backpack.github.io/ios/versions/latest/swiftui/Structs/BPKBannerAlert.html)
[![view on Github](https://img.shields.io/badge/Source%20code-GitHub-lightgrey)](https://github.com/Skyscanner/backpack-ios/tree/main/Backpack-SwiftUI/BannerAlert)

| Day | Night |
| --- | --- |
| <img src="https://raw.githubusercontent.com/Skyscanner/backpack-ios/main/screenshots/iPhone-swiftui_banner-alert___default_lm.png" alt="" width="375" /> |<img src="https://raw.githubusercontent.com/Skyscanner/backpack-ios/main/screenshots/iPhone-swiftui_banner-alert___default_dm.png" alt="" width="375" /> |

# Usage

Banner alert comes in a number of types to indicate information, success, warning or error. Additionally, It supports two styles.

## BPKBannerAlert

### How to initialize

If you don't specify a `Style` it will use the `.default` style.

Supported `AlertType`s:
`info`, `success`, `warning`, `error`

Supported `Style`s:
`default`, `onContrast`

```swift
BPKBannerAlert(
type: .warning(accessibilityLabel: "Warning"),
style: .onContrast,
message: "Hello World!",
accessibilityIdentifier: "BannerAlertAccessibilityIdentifier"
)
```

### Using a custom icon

The custom icon will replace the type's default icon, but the type' color will be applied to it.

```swift
BPKBannerAlert(
type: .error(accessibilityLabel: "Error"),
style: style,
message: "Mutliline banner with custom icon \nThis is a new line",
icon: (BPKIcon.accountAdd, accessibilityLabel: "Account Add Icon")
)
```
24 changes: 21 additions & 3 deletions Backpack-SwiftUI/Icons/Classes/BPKIconView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,18 @@ import Backpack_Common
public struct BPKIconView: View {
let icon: BPKIcon
let size: BPKIcon.Size
let accessibilityLabel: String?

public init(_ icon: BPKIcon, size: BPKIcon.Size = .small) {
self.icon = icon
self.size = size
self.accessibilityLabel = nil
}

public init(_ icon: BPKIcon, size: BPKIcon.Size = .small, accessibilityLabel: String) {
self.icon = icon
self.size = size
self.accessibilityLabel = accessibilityLabel
}

@ScaledMetric private var scaledSmallSize: CGFloat = 16
Expand All @@ -44,11 +52,16 @@ public struct BPKIconView: View {
}

public var body: some View {
Image(icon: icon, size: size)
let enableAccessibility = accessibilityLabel?.isEmpty == false
Image(icon: icon, size: size, shouldEnableAccessibility: enableAccessibility)
.resizable()
.renderingMode(.template)
.flipsForRightToLeftLayoutDirection(shouldAutoMirror)
.frame(width: dimension, height: dimension)
.if(enableAccessibility, transform: { view in
view.accessibilityLabel(accessibilityLabel ?? "")
})

}

private var shouldAutoMirror: Bool {
Expand All @@ -68,9 +81,14 @@ private extension BPKIcon.Size {
}

private extension Image {
init(icon: BPKIcon, size: BPKIcon.Size = .small) {
init(icon: BPKIcon, size: BPKIcon.Size = .small, shouldEnableAccessibility: Bool) {
let iconName = "\(icon.name)-\(size.suffix)"
self.init(decorative: iconName, bundle: BPKCommonBundle.iconsBundle)
let bundle = BPKCommonBundle.iconsBundle
if shouldEnableAccessibility {
self.init(iconName, bundle: bundle)
} else {
self.init(decorative: iconName, bundle: bundle)
}
}
}

Expand Down
Loading

0 comments on commit 92aad7b

Please sign in to comment.