Skip to content

Commit

Permalink
PM-16900: Update form card style for vault (#1291)
Browse files Browse the repository at this point in the history
  • Loading branch information
matt-livefront authored Jan 28, 2025
1 parent 497a0f9 commit 142b5e8
Show file tree
Hide file tree
Showing 146 changed files with 912 additions and 340 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,26 @@ struct StyleGuideFont {

/// The default font size for this style, in px
let size: CGFloat

/// The text style for the font, used to determine how the font scales with dynamic type.
let textStyle: SwiftUI.Font.TextStyle

// MARK: Initialization

/// Initialize a `StyleGuideFont`.
///
/// - Parameters:
/// - font: The font to use for the style.
/// - lineHeight: The line height for this style, in px.
/// - size: The default font size for this style, in px
/// - textStyle: The text style for the font, used to determine how the font scales with dynamic type.
///
init(font: SwiftUI.Font, lineHeight: CGFloat, size: CGFloat, textStyle: SwiftUI.Font.TextStyle = .body) {
self.font = font
self.lineHeight = lineHeight
self.size = size
self.textStyle = textStyle
}
}

extension StyleGuideFont {
Expand All @@ -30,6 +50,7 @@ extension StyleGuideFont {
self.font = font.swiftUIFont(size: size, relativeTo: textStyle)
self.lineHeight = lineHeight
self.size = size
self.textStyle = textStyle
}

/// Returns a `StyleGuideFont` that uses the DMSans font.
Expand All @@ -44,6 +65,20 @@ extension StyleGuideFont {
FontFamily.registerAllCustomFonts()
return self.init(font: FontFamily.DMSans.regular, lineHeight: lineHeight, size: size, textStyle: textStyle)
}

/// Returns a new `StyleGuideFont` with same properties but different font.
///
/// - Parameter font: The `FontConvertible` font for this style.
/// - Returns: A `StyleGuideFont` modified to use the specified font.
///
private func with(font: FontConvertible) -> StyleGuideFont {
StyleGuideFont(
font: font,
lineHeight: lineHeight,
size: size,
textStyle: textStyle
)
}
}

// MARK: - StyleGuideFont Constants
Expand Down Expand Up @@ -71,7 +106,7 @@ extension StyleGuideFont {
static let body = StyleGuideFont.dmSans(lineHeight: 20, size: 15, textStyle: .body)

/// The font for the bold body style.
static let bodyBold = StyleGuideFont(font: FontFamily.DMSans.bold, lineHeight: 20, size: 15, textStyle: .body)
static let bodyBold = body.with(font: FontFamily.DMSans.bold)

/// The font for the monospaced body style.
static let bodyMonospaced = StyleGuideFont(font: .system(.body, design: .monospaced), lineHeight: 22, size: 17)
Expand All @@ -80,11 +115,14 @@ extension StyleGuideFont {
static let callout = StyleGuideFont.dmSans(lineHeight: 18, size: 13, textStyle: .callout)

/// The font for the callout style.
static let calloutBold = StyleGuideFont(font: FontFamily.DMSans.bold, lineHeight: 18, size: 13, textStyle: .callout)
static let calloutBold = callout.with(font: FontFamily.DMSans.bold)

/// The font for the subheadline style.
static let subheadline = StyleGuideFont.dmSans(lineHeight: 16, size: 12, textStyle: .subheadline)

/// The font for the subheadline semibold style.
static let subheadlineSemibold = subheadline.with(font: FontFamily.DMSans.semiBold)

/// The font for the footnote style.
static let footnote = StyleGuideFont.dmSans(lineHeight: 18, size: 12, textStyle: .footnote)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import SwiftUI

// MARK: - BitwardenBorderlessButtonStyle

/// The style for a borderless button in this application.
///
struct BitwardenBorderlessButtonStyle: ButtonStyle {
// MARK: Properties

/// A value indicating whether the button is currently enabled or disabled.
@Environment(\.isEnabled) var isEnabled: Bool

/// The color of the foreground elements, including text and template images.
var foregroundColor: Color {
isEnabled
? Asset.Colors.buttonOutlinedForeground.swiftUIColor
: Asset.Colors.buttonOutlinedDisabledForeground.swiftUIColor
}

// MARK: ButtonStyle

func makeBody(configuration: Configuration) -> some View {
configuration.label
.foregroundStyle(foregroundColor)
.padding(.vertical, 14)
.styleGuide(.subheadlineSemibold)
.opacity(configuration.isPressed ? 0.5 : 1)
}
}

// MARK: ButtonStyle

extension ButtonStyle where Self == BitwardenBorderlessButtonStyle {
/// The style for a borderless button in this application.
///
static var bitwardenBorderless: BitwardenBorderlessButtonStyle {
BitwardenBorderlessButtonStyle()
}
}

// MARK: Previews

#if DEBUG
#Preview() {
VStack {
Button("Bitwarden") {}

Button("Bitwarden") {}
.disabled(true)
}
.buttonStyle(.bitwardenBorderless)
}
#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import SnapshotTesting
import SwiftUI
import XCTest

@testable import BitwardenShared

final class ButtonStylesTests: BitwardenTestCase {
// MARK: Types

/// A view that displays all of the button styles for snapshotting.
struct ButtonStyles: View {
var body: some View {
HStack(alignment: .top, spacing: 20) {
VStack {
Group {
titleView("Primary")

Button("Enabled") {}
Button("Disabled") {}
.disabled(true)
}
.buttonStyle(.primary())
}

VStack {
Group {
titleView("Primary Destructive")

Button("Enabled") {}
Button("Disabled") {}
.disabled(true)
}
.buttonStyle(.primary(isDestructive: true))
}

VStack {
titleView("Secondary")

Button("Enabled") {}
Button("Disabled") {}
.disabled(true)
}
.buttonStyle(.secondary())

VStack {
titleView("Borderless")

Button("Enabled") {}
Button("Disabled") {}
.disabled(true)
}
.buttonStyle(.bitwardenBorderless)

VStack {
titleView("Field Label Icon")

Button {} label: {
Label("Options", image: Asset.Images.cog16.swiftUIImage)
}
Button {} label: {
Label("Options", image: Asset.Images.cog16.swiftUIImage)
}
.disabled(true)
}
.buttonStyle(.fieldLabelIcon)

VStack {
titleView("Circle (FAB)")

Button {} label: {
Asset.Images.cog24.swiftUIImage
}
Button {} label: {
Asset.Images.cog24.swiftUIImage
}
.disabled(true)
}
.buttonStyle(CircleButtonStyle())
}
.padding()
.frame(maxHeight: .infinity, alignment: .top)
}

func titleView(_ title: String) -> some View {
Text(title)
.styleGuide(.title3, weight: .bold)
.foregroundStyle(Asset.Colors.textPrimary.swiftUIColor)
}
}

// MARK: Tests

/// Render a snapshot of the app's button styles.
func test_snapshot_buttonStyles() {
let subject = ButtonStyles()
assertSnapshot(of: subject, as: .fixedSize(width: 1000, height: 300))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import SwiftUI

// MARK: - FieldLabelIconButtonStyle

/// The style for a button containing an icon displayed next to a label in a form field.
///
struct FieldLabelIconButtonStyle: ButtonStyle {
// MARK: Properties

/// A value indicating whether the button is currently enabled or disabled.
@Environment(\.isEnabled) var isEnabled: Bool

/// The color of the foreground elements, including text and template images.
var foregroundColor: Color {
isEnabled
? Asset.Colors.buttonOutlinedForeground.swiftUIColor
: Asset.Colors.buttonOutlinedDisabledForeground.swiftUIColor
}

// MARK: ButtonStyle

func makeBody(configuration: Configuration) -> some View {
configuration.label
.frame(width: 16, height: 16)
.foregroundColor(foregroundColor)
.opacity(configuration.isPressed ? 0.5 : 1)
.contentShape(Rectangle())
}
}

// MARK: ButtonStyle

extension ButtonStyle where Self == FieldLabelIconButtonStyle {
/// The style for a field label icon button in this application.
///
static var fieldLabelIcon: FieldLabelIconButtonStyle {
FieldLabelIconButtonStyle()
}
}

// MARK: Previews

#if DEBUG
#Preview() {
VStack {
Button {} label: {
Asset.Images.cog16.swiftUIImage
}

Button {} label: {
Asset.Images.cog16.swiftUIImage
}
.disabled(true)
}
.buttonStyle(.fieldLabelIcon)
}
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,12 @@ struct PrimaryButtonStyle: ButtonStyle {

/// The background color of this button.
var backgroundColor: Color {
if isDestructive {
Asset.Colors.error.swiftUIColor
} else {
isEnabled
? Asset.Colors.buttonFilledBackground.swiftUIColor
: Asset.Colors.buttonFilledDisabledBackground.swiftUIColor
guard isEnabled else {
return Asset.Colors.buttonFilledDisabledBackground.swiftUIColor
}
return isDestructive
? Asset.Colors.error.swiftUIColor
: Asset.Colors.buttonFilledBackground.swiftUIColor
}

/// The color of the foreground elements in this button, including text and template
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
17 changes: 17 additions & 0 deletions BitwardenShared/UI/Platform/Application/Extensions/Label.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import SwiftUI

extension Label {
/// Initialize a label with a title and image.
///
/// - Parameters:
/// - title: The title of the label.
/// - image: The image to display in the label.
///
init(_ title: String, image: Image) where Title == Text, Icon == Image {
self.init {
Text(title)
} icon: {
image
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"images" : [
{
"filename" : "cog16.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"preserves-vector-representation" : true,
"template-rendering-intent" : "template"
}
}
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -678,7 +678,7 @@
"EnterKeyManually" = "Enter key manually";
"AddTotp" = "Add TOTP";
"SetupTotp" = "Set up TOTP";
"OnceTheKeyIsSuccessfullyEntered" = "Once the key is successfully entered,\nselect Add TOTP to store the key safely";
"OnceTheKeyIsSuccessfullyEntered" = "Once the key is successfully entered, select Save to store the key safely.";
"NeverLockWarning" = "Setting your lock options to “Never” keeps your vault available to anyone with access to your device. If you use this option, you should ensure that you keep your device properly protected.";
"EnvironmentPageUrlsError" = "One or more of the URLs entered are invalid. Please revise it and try to save again.";
"GenericErrorMessage" = "We were unable to process your request. Please try again or contact us.";
Expand Down Expand Up @@ -1099,3 +1099,4 @@
"TextToShare" = "Text to share";
"SendDetails" = "Send details";
"SendNameRequired" = "Send name (required)";
"CheckPasswordForDataBreaches" = "Check password for data breaches";
Loading

0 comments on commit 142b5e8

Please sign in to comment.