diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d53ebcadfe..b5a4ffeb577 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ + # OUDS iOS library changelog All notable changes to this project will be documented in this file. @@ -15,6 +16,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- Update `switch` components to v1.5.0 (Orange-OpenSource/ouds-ios#1138) +- Update `radio` components to v1.4.0 (Orange-OpenSource/ouds-ios#1139) +- Update `checkbox` components to v2.4.0 (Orange-OpenSource/ouds-ios#1137) - `Wireframe` theme `border` semantic tokens (tokens libraries System v2.3) (Orange-OpenSource/ouds-ios#1158) - `size` semantic tokens (tokens libraries System v2.3) (Orange-OpenSource/ouds-ios#1158) - Update `badge` to use internal icons for some status (Orange-OpenSource/ouds-ios#1136) diff --git a/OUDS/Core/Components/Sources/Controls/Checkbox/Internal/CheckboxIndicator.swift b/OUDS/Core/Components/Sources/Controls/Checkbox/Internal/CheckboxIndicator.swift index 8c3c67486fd..63a42ee1291 100644 --- a/OUDS/Core/Components/Sources/Controls/Checkbox/Internal/CheckboxIndicator.swift +++ b/OUDS/Core/Components/Sources/Controls/Checkbox/Internal/CheckboxIndicator.swift @@ -75,7 +75,6 @@ struct CheckboxIndicator: View { OL.fatal("An OUDS Checkbox with a disabled state / read only mode and an error situation has been detected, which is not allowed" + " Only non-error situation are allowed to have a disabled state / read only mode.") } - // Not error case } else { switch interactionState { @@ -89,7 +88,9 @@ struct CheckboxIndicator: View { theme.colors.actionHover case .pressed: theme.colors.actionPressed - case .disabled, .readOnly: + case .readOnly: + theme.colors.actionReadOnlyPrimary + case .disabled: theme.colors.actionDisabled } } diff --git a/OUDS/Core/Components/Sources/Controls/Checkbox/Internal/CheckboxIndicatorModifiers.swift b/OUDS/Core/Components/Sources/Controls/Checkbox/Internal/CheckboxIndicatorModifiers.swift index c8d001e46ea..2cccd275b64 100644 --- a/OUDS/Core/Components/Sources/Controls/Checkbox/Internal/CheckboxIndicatorModifiers.swift +++ b/OUDS/Core/Components/Sources/Controls/Checkbox/Internal/CheckboxIndicatorModifiers.swift @@ -70,7 +70,9 @@ private struct CheckboxIndicatorForegroundModifier: ViewModifier { hoverColor case .pressed: pressedColor - case .disabled, .readOnly: + case .readOnly: + readOnlyColor + case .disabled: disabledColor } } @@ -96,6 +98,14 @@ private struct CheckboxIndicatorForegroundModifier: ViewModifier { isError ? theme.colors.actionNegativePressed : theme.colors.actionPressed } + private var readOnlyColor: MultipleColorSemanticTokens { + guard !isError else { + OL.fatal("An OUDS Checkbox with a read only state and an error situation has been detected, which is not allowed." + + " Only non-error situation are allowed to have a disabled state.") + } + return theme.colors.actionReadOnlyPrimary + } + private var disabledColor: MultipleColorSemanticTokens { guard !isError else { OL.fatal("An OUDS Checkbox with a disabled state and an error situation has been detected, which is not allowed." @@ -134,7 +144,9 @@ private struct CheckboxIndicatorBackgroundModifier: ViewModifier { hoverColor case .pressed: pressedColor - case .disabled, .readOnly: + case .readOnly: + readOnlyColor + case .disabled: disabledColor } } @@ -151,6 +163,14 @@ private struct CheckboxIndicatorBackgroundModifier: ViewModifier { theme.controlItem.colorBgPressed.color(for: colorScheme) } + private var readOnlyColor: Color { + guard !isError else { + OL.fatal("An OUDS Checkbox with a read only state and an error situation has been detected, which is not allowed." + + " Only non-error situation are allowed to have a disabled state.") + } + return Color.clear + } + private var disabledColor: Color { guard !isError else { OL.fatal("An OUDS Checkbox with a disabled state and an error situation has been detected, which is not allowed." @@ -160,7 +180,7 @@ private struct CheckboxIndicatorBackgroundModifier: ViewModifier { } } -// MARK: - Checkbox IndicIndicatorator Border Modifier +// MARK: - Checkbox Indicator Border Modifier private struct CheckboxIndicatorBorderModifier: ViewModifier { @@ -194,7 +214,9 @@ private struct CheckboxIndicatorBorderModifier: ViewModifier { hoverColor case .pressed: pressedColor - case .disabled, .readOnly: + case .readOnly: + readOnlyColor + case .disabled: disabledColor } } @@ -232,6 +254,14 @@ private struct CheckboxIndicatorBorderModifier: ViewModifier { } } + private var readOnlyColor: MultipleColorSemanticTokens { + guard !isError else { + OL.fatal("An OUDS Checkbox with a readonly state and an error situation has been detected, which is not allowed" + + " Only non-error situation are allowed to have a disabled state.") + } + return theme.colors.actionReadOnlySecondary + } + private var disabledColor: MultipleColorSemanticTokens { guard !isError else { OL.fatal("An OUDS Checkbox with a disabled state and an error situation has been detected, which is not allowed" @@ -250,7 +280,9 @@ private struct CheckboxIndicatorBorderModifier: ViewModifier { hoverWidth case .pressed: pressedWidth - case .disabled, .readOnly: + case .readOnly: + readOnlyWidth + case .disabled: disabledWidth } } @@ -282,6 +314,15 @@ private struct CheckboxIndicatorBorderModifier: ViewModifier { } } + private var readOnlyWidth: CGFloat { + switch indicatorState { + case .selected, .indeterminate: + theme.checkbox.borderWidthSelected + case .unselected: + theme.checkbox.borderWidthUnselected + } + } + private var disabledWidth: CGFloat { switch indicatorState { case .selected, .indeterminate: diff --git a/OUDS/Core/Components/Sources/Controls/Checkbox/OUDSCheckbox.swift b/OUDS/Core/Components/Sources/Controls/Checkbox/OUDSCheckbox.swift index bd2d5c442cd..b010afe19d2 100644 --- a/OUDS/Core/Components/Sources/Controls/Checkbox/OUDSCheckbox.swift +++ b/OUDS/Core/Components/Sources/Controls/Checkbox/OUDSCheckbox.swift @@ -42,7 +42,7 @@ import SwiftUI /// /// ## Cases forbidden by design /// -/// **The design system does not allow to have both an error situation and a disabled component.** +/// **The design system does not allow to have both an error or a read-only situation and a disabled component.** /// /// ## Code samples /// @@ -87,15 +87,16 @@ import SwiftUI /// /// ![A checkbox component in light and dark mode with Wireframe theme](component_checkbox_Wireframe) /// -/// - Version: 2.3.0 (Figma component design version) +/// - Version: 2.4.0 (Figma component design version) /// - Since: 0.12.0 @available(iOS 15, macOS 15, visionOS 1, watchOS 11, tvOS 16, *) public struct OUDSCheckbox: View { // MARK: Properties - private let isError: Bool private let a11yLabel: String + private let isError: Bool + private let isReadOnly: Bool @Environment(\.isEnabled) private var isEnabled @Environment(\.theme) private var theme @@ -106,28 +107,31 @@ public struct OUDSCheckbox: View { /// Creates a checkbox with only an indicator. /// - /// **The design system does not allow to have both an error situation and a disabled state for the component.** + /// **The design system does not allow to have both an error or read only situation and a disabled state for the component.** /// /// - Parameters: /// - isOn: A binding to a property that determines wether the indicator is ticked (selected) or not (not selected) /// - accessibilityLabel: The accessibility label the component must have /// - isError: True if the look and feel of the component must reflect an error state, default set to `false` + /// - isReadOnly: True if the look and feel of the component must reflect a read only state, default set to `false` public init(isOn: Binding, accessibilityLabel: String, - isError: Bool = false) + isError: Bool = false, + isReadOnly: Bool = false) { if accessibilityLabel.isEmpty { OL.warning("The OUDSCheckbox should not have an empty accessibility label, think about your disabled users!") } _isOn = isOn - self.isError = isError a11yLabel = accessibilityLabel + self.isError = isError + self.isReadOnly = isReadOnly } // MARK: Body public var body: some View { - InteractionButton { + InteractionButton(isReadOnly: isReadOnly) { $isOn.wrappedValue.toggle() } content: { interactionState in CheckboxIndicator(interactionState: interactionState, indicatorState: convertedState, isError: isError) @@ -159,7 +163,7 @@ public struct OUDSCheckbox: View { /// Forges a string to vocalize with *Voice Over* describing the component hint private func a11yHint() -> String { - if !isEnabled { + if !isEnabled || isReadOnly { "" } else { isOn @@ -171,7 +175,7 @@ public struct OUDSCheckbox: View { /// Forges a string to vocalize with *Voice Over* describing the component state /// - Parameter isDisabled: True if component is disabled, false otherwise private func a11yLabel(isDisabled: Bool) -> String { - let stateDescription = isDisabled ? "core_common_disabled_a11y".localized() : "" + let stateDescription = isDisabled || isReadOnly ? "core_common_disabled_a11y".localized() : "" let errorDescription = isError ? "core_common_onError_a11y".localized() : "" let checkboxA11yTrait = "core_checkbox_trait_a11y".localized() // Fake trait for Voice Over vocalization diff --git a/OUDS/Core/Components/Sources/Controls/Checkbox/OUDSCheckboxIndeterminate.swift b/OUDS/Core/Components/Sources/Controls/Checkbox/OUDSCheckboxIndeterminate.swift index e3fafe499f9..0a1ed62c01b 100644 --- a/OUDS/Core/Components/Sources/Controls/Checkbox/OUDSCheckboxIndeterminate.swift +++ b/OUDS/Core/Components/Sources/Controls/Checkbox/OUDSCheckboxIndeterminate.swift @@ -88,15 +88,16 @@ import SwiftUI /// /// ![A checkbox component in light and dark mode with Wireframe theme](component_checkbox_Wireframe) /// -/// - Version: 2.3.0 (Figma component design version) +/// - Version: 2.4.0 (Figma component design version) /// - Since: 0.12.0 @available(iOS 15, macOS 15, visionOS 1, watchOS 11, tvOS 16, *) public struct OUDSCheckboxIndeterminate: View { // MARK: - Properties - private let isError: Bool private let a11yLabel: String + private let isError: Bool + private let isReadOnly: Bool @Binding var selection: OUDSCheckboxIndicatorState @Environment(\.isEnabled) private var isEnabled @@ -112,22 +113,25 @@ public struct OUDSCheckboxIndeterminate: View { /// - selection: A binding to a property that determines wether the indicator is ticked, unticked or preticked. /// - accessibilityLabel: The accessibility label the component must have /// - isError: True if the look and feel of the component must reflect an error state, default set to `false` + /// - isReadOnly: True if the look and feel of the component must reflect a read only state, default set to `false` public init(selection: Binding, accessibilityLabel: String, - isError: Bool = false) + isError: Bool = false, + isReadOnly: Bool = false) { if accessibilityLabel.isEmpty { OL.warning("The OUDSCheckbox should not have an empty accessibility label, think about your disabled users!") } _selection = selection - self.isError = isError a11yLabel = accessibilityLabel + self.isError = isError + self.isReadOnly = isReadOnly } // MARK: Body public var body: some View { - InteractionButton { + InteractionButton(isReadOnly: isReadOnly) { $selection.wrappedValue.toggle() } content: { interactionState in CheckboxIndicator(interactionState: interactionState, indicatorState: $selection.wrappedValue, isError: isError) @@ -140,13 +144,13 @@ public struct OUDSCheckboxIndeterminate: View { .accessibilityRemoveTraits([.isButton]) // .isToggle trait for iOS 17+ .accessibilityLabel(a11yLabel(isDisabled: !isEnabled)) .accessibilityValue(selection.a11yDescription.localized()) - .accessibilityHint(selection.a11yHint) + .accessibilityHint(isEnabled && !isReadOnly ? selection.a11yHint : "") } /// Forges a string to vocalize with *Voice Over* describing the component state /// - Parameter isDisabled: True if component is disabled, false otherwise private func a11yLabel(isDisabled: Bool) -> String { - let stateDescription = isDisabled ? "core_common_disabled_a11y".localized() : "" + let stateDescription = isDisabled || isReadOnly ? "core_common_disabled_a11y".localized() : "" let errorDescription = isError ? "core_common_onError_a11y".localized() : "" let checkboxA11yTrait = "core_checkbox_trait_a11y".localized() // Fake trait for Voice Over vocalization diff --git a/OUDS/Core/Components/Sources/Controls/Checkbox/OUDSCheckboxItem.swift b/OUDS/Core/Components/Sources/Controls/Checkbox/OUDSCheckboxItem.swift index d64976d3813..5d3314aa30e 100644 --- a/OUDS/Core/Components/Sources/Controls/Checkbox/OUDSCheckboxItem.swift +++ b/OUDS/Core/Components/Sources/Controls/Checkbox/OUDSCheckboxItem.swift @@ -74,15 +74,15 @@ import SwiftUI /// // The default layout will be used here. /// OUDSCheckboxItem(isOn: $isOn, label: "Hello world", isReadOnly: true) /// -/// // A leading checkbox with a label, and an helper text. +/// // A leading checkbox with a label and a description as helper text. /// // The default layout will be used here. -/// OUDSCheckboxItem(isOn: $isOn, label: "Bazinga!", helper: "Doll-Dagga Buzz-Buzz Ziggety-Zag") +/// OUDSCheckboxItem(isOn: $isOn, label: "Bazinga!", description: "Doll-Dagga Buzz-Buzz Ziggety-Zag") /// -/// // A trailing checkbox with a label, an helper text and an icon. +/// // A trailing checkbox with a label, a description and an icon. /// // The reversed layout will be used here. /// OUDSCheckboxItem(isOn: $isOn, /// label: "We live in a fabled world", -/// helper: "Of dreaming boys and wide-eyed girls", +/// description: "Of dreaming boys and wide-eyed girls", /// isReversed: true, /// icon: Image(decorative: "ic_heart")) /// @@ -109,7 +109,7 @@ import SwiftUI /// @Environment(\.layoutDirection) var layoutDirection /// /// OUDSCheckboxItem(isOn: $selection, -/// labelText: "Cocorico !", +/// label: "Cocorico !", /// icon: Image(systemName: "figure.handball"), /// flipIcon: layoutDirection == .rightToLeft, /// isInversed: layoutDirection == .rightToLeft) @@ -142,7 +142,7 @@ import SwiftUI /// /// ![A checkbox item component in light and dark mode with Wireframe theme](component_checkboxItem_Wireframe) /// -/// - Version: 2.3.0 (Figma component design version) +/// - Version: 2.4.0 (Figma component design version) /// - Since: 0.12.0 @available(iOS 15, macOS 15, visionOS 1, watchOS 11, tvOS 16, *) public struct OUDSCheckboxItem: View { @@ -163,16 +163,19 @@ public struct OUDSCheckboxItem: View { /// ```swift /// OUDSCheckboxItem(isOn: $isOn, /// label: "Virgin Holy Lava", - /// helper: "Very spicy", + /// description: "Very spicy", /// icon: Image(systemName: "flame") /// ``` /// /// **The design system does not allow to have both an error situation and a read only mode for the component.** /// + /// **Remark: If `label` and `helper` strings are wording keys from strings catalog stored in `Bundle.main`, they are automatically localized. Else, prefer to + /// provide the localized string if key is stored in another bundle.** + /// /// - Parameters: /// - isOn: A binding to a property that determines wether the indicator is ticked (selected) or not (unselected) /// - label: The main label text of the checkbox. - /// - helper: An additonal helper text, should not be empty, default set to `nil`. Will be repalced by `errorText` in case of error. + /// - description: An additonal helper text, a description, which should not be empty, default set to `nil`. Will be repalced by `errorText` in case of error. /// - icon: An optional icon, default set to `nil` /// - flipIcon: Default set to `false`, set to true` to reverse the image (i.e. flip vertically) /// - isReversed: `true` if the checkbox indicator must be in trailing position,` false` otherwise. Default to `false` @@ -182,12 +185,9 @@ public struct OUDSCheckboxItem: View { /// - isReadOnly: True if component is in read only, i.e. not really disabled but user cannot interact with it yet, default set to `false` /// - hasDivider: If `true` a divider is added at the bottom of the view, by default set to `false` /// - action: An additional action to trigger when the checkbox has been pressed - /// - /// **Remark: If `label` and `helper` strings are wording keys from strings catalog stored in `Bundle.main`, they are automatically localized. Else, prefer to - /// provide the localized string if key is stored in another bundle.** public init(isOn: Binding, label: String, - helper: String? = nil, + description: String? = nil, icon: Image? = nil, flipIcon: Bool = false, isReversed: Bool = false, @@ -201,8 +201,8 @@ public struct OUDSCheckboxItem: View { OL.fatal("It is forbidden by design to have an OUDSCheckboxItem in an error context and in read only mode") } - if let helper, helper.isEmpty { - OL.warning("Helper text given to an OUDSCheckboxItem is defined but empty, is it expected? Prefer use of `nil` value instead") + if let description, description.isEmpty { + OL.warning("Description given to an OUDSCheckboxItem is defined but empty, is it expected? Prefer use of `nil` value instead") } // swiftlint:disable force_unwrapping @@ -214,8 +214,8 @@ public struct OUDSCheckboxItem: View { _isOn = isOn layoutData = .init( label: label.localized(), - additionalLabel: nil, - helper: helper?.localized(), + extraLabel: nil, + description: description?.localized(), icon: icon, flipIcon: flipIcon, isOutlined: false, @@ -254,13 +254,16 @@ public struct OUDSCheckboxItem: View { private var a11yLabel: String { let stateDescription: String = layoutData.isReadOnly || !isEnabled ? "core_common_disabled_a11y".localized() : "" - let errorPrefix = layoutData.isError ? "core_common_onError_a11y".localized() : "" - let errorText = layoutData.errorText?.localized() ?? "" - let errorDescription = "\(errorPrefix), \(errorText)" + var errorDescription = "" + if layoutData.isError { + let errorPrefix = "core_common_onError_a11y".localized() + let errorText = layoutData.errorText?.localized() ?? "" + errorDescription = "\(errorPrefix), \(errorText)" + } let checkboxA11yTrait = "core_checkbox_trait_a11y".localized() // Fake trait for Voice Over vocalization - let result = "\(stateDescription), \(layoutData.label), \(layoutData.helper ?? ""), \(errorDescription), \(checkboxA11yTrait)" + let result = "\(stateDescription), \(layoutData.label), \(layoutData.description ?? ""), \(errorDescription), \(checkboxA11yTrait)" return result } diff --git a/OUDS/Core/Components/Sources/Controls/Checkbox/OUDSCheckboxItemIndeterminate.swift b/OUDS/Core/Components/Sources/Controls/Checkbox/OUDSCheckboxItemIndeterminate.swift index ac96ef9a185..45301ae0041 100644 --- a/OUDS/Core/Components/Sources/Controls/Checkbox/OUDSCheckboxItemIndeterminate.swift +++ b/OUDS/Core/Components/Sources/Controls/Checkbox/OUDSCheckboxItemIndeterminate.swift @@ -74,15 +74,15 @@ import SwiftUI /// // The default layout will be used here. /// OUDSCheckboxItemIndeterminate(selection: $selection, label: "Hello world", isReadOnly: true) /// -/// // A leading checkbox with a label, and an helper text. +/// // A leading checkbox with a label and a description as helper text. /// // The default layout will be used here. -/// OUDSCheckboxItemIndeterminate(selection: $selection, label: "Bazinga!", helper: "Doll-Dagga Buzz-Buzz Ziggety-Zag") +/// OUDSCheckboxItemIndeterminate(selection: $selection, label: "Bazinga!", description: "Doll-Dagga Buzz-Buzz Ziggety-Zag") /// /// // A trailing checkbox with a label, an helper text and an icon. /// // The reversed layout will be used here. /// OUDSCheckboxItemIndeterminate(selection: $selection, /// label: "We live in a fabled world", -/// helper: "Of dreaming boys and wide-eyed girls", +/// description: "Of dreaming boys and wide-eyed girls", /// isReversed: true, /// icon: Image(decorative: "ic_heart")) /// @@ -100,7 +100,7 @@ import SwiftUI /// /// // A leading checkbox with a label and and icon but with the icon flipped vertically /// OUDSCheckboxItemIndeterminate(isOn: $selection, -/// labelText: "Cocorico !", +/// label: "Cocorico !", /// icon: Image(systemName: "figure.handball"), /// flipIcon: true) /// @@ -115,7 +115,7 @@ import SwiftUI /// @Environment(\.layoutDirection) var layoutDirection /// /// OUDSCheckboxItemIndeterminate(isOn: $selection, -/// labelText: "Cocorico !", +/// label: "Cocorico !", /// icon: Image(systemName: "figure.handball"), /// flipIcon: layoutDirection == .rightToLeft, /// isInversed: layoutDirection == .rightToLeft) @@ -148,7 +148,7 @@ import SwiftUI /// /// ![A checkbox item component in light and dark mode with Wireframe theme](component_checkboxItem_Wireframe) /// -/// - Version: 2.3.0 (Figma component design version) +/// - Version: 2.4.0 (Figma component design version) /// - Since: 0.12.0 @available(iOS 15, macOS 15, visionOS 1, watchOS 11, tvOS 16, *) public struct OUDSCheckboxItemIndeterminate: View { @@ -170,7 +170,7 @@ public struct OUDSCheckboxItemIndeterminate: View { /// - Parameters: /// - selection: A binding to a property that determines wether the indicator is ticked, unticker or preticked. /// - label: The main label text of the checkbox. - /// - helper: An additonal helper text, should not be empty + /// - description: A description, an additonal helper text, should not be empty /// - icon: An optional icon /// - flipIcon: Default set to `false`, set to true to reverse the image (i.e. flip vertically) /// - isReversed: `true` of the checkbox indicator must be in trailing position,` false` otherwise. Default to `false` @@ -185,7 +185,7 @@ public struct OUDSCheckboxItemIndeterminate: View { /// provide the localized string if key is stored in another bundle.** public init(selection: Binding, label: String, - helper: String? = nil, + description: String? = nil, icon: Image? = nil, flipIcon: Bool = false, isReversed: Bool = false, @@ -199,8 +199,8 @@ public struct OUDSCheckboxItemIndeterminate: View { OL.fatal("It is forbidden by design to have an OUDSCheckboxItemIndeterminate in an error context and in read only mode") } - if let helper, helper.isEmpty { - OL.warning("Helper text given to an OUDSCheckboxItemIndeterminate is defined but empty, is it expected? Prefer use of `nil` value instead") + if let description, description.isEmpty { + OL.warning("Description text given to an OUDSCheckboxItemIndeterminate is defined but empty, is it expected? Prefer use of `nil` value instead") } // swiftlint:disable force_unwrapping @@ -213,8 +213,8 @@ public struct OUDSCheckboxItemIndeterminate: View { self.action = action layoutData = .init( label: label.localized(), - additionalLabel: nil, - helper: helper?.localized(), + extraLabel: nil, + description: description?.localized(), icon: icon, flipIcon: flipIcon, isOutlined: false, @@ -250,7 +250,7 @@ public struct OUDSCheckboxItemIndeterminate: View { let checkboxA11yTrait = "core_checkbox_trait_a11y".localized() // Fake trait for Voice Over vocalization - let result = "\(stateDescription), \(layoutData.label), \(layoutData.helper ?? "") \(errorDescription), \(checkboxA11yTrait)" + let result = "\(stateDescription), \(layoutData.label), \(layoutData.description ?? "") \(errorDescription), \(checkboxA11yTrait)" return result } diff --git a/OUDS/Core/Components/Sources/Controls/CheckboxPicker/OUDSCheckboxPicker.swift b/OUDS/Core/Components/Sources/Controls/CheckboxPicker/OUDSCheckboxPicker.swift index c223fa76be6..d67b7bd7376 100644 --- a/OUDS/Core/Components/Sources/Controls/CheckboxPicker/OUDSCheckboxPicker.swift +++ b/OUDS/Core/Components/Sources/Controls/CheckboxPicker/OUDSCheckboxPicker.swift @@ -44,7 +44,7 @@ import SwiftUI /// [ /// OUDSCheckboxPickerData(tag: "Choice_1", /// label: "Virgin Holy Lava", -/// additionalLabel: "Very spicy", +/// extraLabel: "Very spicy", /// helper: "No alcohol, only tasty flavors", /// icon: Image(systemName: "flame")), /// @@ -235,7 +235,7 @@ public struct OUDSCheckboxPicker: View where Tag: Hashable { private func content(for checkbox: OUDSCheckboxPickerData, noDivider: Bool) -> some View { OUDSCheckboxItem(isOn: isSelected(tag: checkbox.tag) ? .constant(true) : .constant(false), label: checkbox.label, - helper: checkbox.helper, + description: checkbox.description, icon: checkbox.icon, isReversed: isReversed ? true : checkbox.isReversed, isError: isError ? true : checkbox.isError, diff --git a/OUDS/Core/Components/Sources/Controls/CheckboxPicker/OUDSCheckboxPickerData.swift b/OUDS/Core/Components/Sources/Controls/CheckboxPicker/OUDSCheckboxPickerData.swift index 9ed4be7b01d..910c78c42f4 100644 --- a/OUDS/Core/Components/Sources/Controls/CheckboxPicker/OUDSCheckboxPickerData.swift +++ b/OUDS/Core/Components/Sources/Controls/CheckboxPicker/OUDSCheckboxPickerData.swift @@ -28,7 +28,7 @@ public struct OUDSCheckboxPickerData where Tag: Hashable { let label: String /// An optional helper text the ``OUDSCheckboxItem`` can have - let helper: String? + let description: String? /// An optional image the ``OUDSCheckboxItem`` can have let icon: Image? @@ -54,7 +54,7 @@ public struct OUDSCheckboxPickerData where Tag: Hashable { /// - Parameters: /// - tag: a value to discriminate one checkbox to another /// - label: the mandatory text to add to ``OUDSCheckboxItem`` - /// - helper: Another optional text, default set to nil + /// - description: An optional text, default set to nil /// - icon: An optional image, default set to nil /// - isReversed: True to use to reversed layout of the ``OUDSCheckboxItem``, false otherwise (default) /// - isError: True if in an error context, false otherwise (default) @@ -66,7 +66,7 @@ public struct OUDSCheckboxPickerData where Tag: Hashable { /// provide the localized string if key is stored in another bundle.** public init(tag: Tag, label: String, - helper: String? = nil, + description: String? = nil, icon: Image? = nil, isReversed: Bool = false, isError: Bool = false, @@ -76,7 +76,7 @@ public struct OUDSCheckboxPickerData where Tag: Hashable { { self.tag = tag self.label = label - self.helper = helper + self.description = description self.icon = icon self.isReversed = isReversed self.isError = isError diff --git a/OUDS/Core/Components/Sources/Controls/Radio/Internal/RadioIndicatorModifiers.swift b/OUDS/Core/Components/Sources/Controls/Radio/Internal/RadioIndicatorModifiers.swift index 97b5ede757c..d0367e0a687 100644 --- a/OUDS/Core/Components/Sources/Controls/Radio/Internal/RadioIndicatorModifiers.swift +++ b/OUDS/Core/Components/Sources/Controls/Radio/Internal/RadioIndicatorModifiers.swift @@ -71,7 +71,9 @@ private struct RadioIndicatorForegroundModifier: ViewModifier { hoverColor case .pressed: pressedColor - case .disabled, .readOnly: + case .readOnly: + readOnlyColor + case .disabled: disabledColor } } @@ -96,6 +98,14 @@ private struct RadioIndicatorForegroundModifier: ViewModifier { isError ? theme.colors.actionNegativePressed : theme.colors.actionPressed } + private var readOnlyColor: MultipleColorSemanticTokens { + guard !isError else { + OL.fatal("An OUDSRadio with a read only state and an error situation has been detected, which is not allowed." + + " Only non-error situation are allowed to have a read-only state.") + } + return theme.colors.actionReadOnlyPrimary + } + private var disabledColor: MultipleColorSemanticTokens { guard !isError else { OL.fatal("An OUDSRadio with a disabled state and an error situation has been detected, which is not allowed." @@ -191,7 +201,9 @@ private struct RadioIndicatorBorderModifier: ViewModifier { hoverColor case .pressed: pressedColor - case .disabled, .readOnly: + case .readOnly: + readOnlyColor + case .disabled: disabledColor } } @@ -224,6 +236,14 @@ private struct RadioIndicatorBorderModifier: ViewModifier { } } + private var readOnlyColor: MultipleColorSemanticTokens { + guard !isError else { + OL.fatal("An OUDSRadio with a read-only state and an error situation has been detected, which is not allowed" + + " Only non-error situation are allowed to have a disabled state.") + } + return theme.colors.actionReadOnlySecondary + } + private var disabledColor: MultipleColorSemanticTokens { guard !isError else { OL.fatal("An OUDSRadio with a disabled state and an error situation has been detected, which is not allowed" diff --git a/OUDS/Core/Components/Sources/Controls/Radio/OUDSRadio.swift b/OUDS/Core/Components/Sources/Controls/Radio/OUDSRadio.swift index af7a31246af..593d691acb8 100644 --- a/OUDS/Core/Components/Sources/Controls/Radio/OUDSRadio.swift +++ b/OUDS/Core/Components/Sources/Controls/Radio/OUDSRadio.swift @@ -34,7 +34,7 @@ import SwiftUI /// /// ## Cases forbidden by design /// -/// **The design system does not allow to have both an error situation and a disabled component.** +/// **The design system does not allow to have both an error or read-only situation and a disabled component.** /// /// ## Code samples /// @@ -75,15 +75,16 @@ import SwiftUI /// /// ![A radio button component in light and dark mode with Wireframe theme](component_radio_Wireframe) /// -/// - Version: 1.3.0 (Figma component design version) +/// - Version: 1.4.0 (Figma component design version) /// - Since: 0.12.0 @available(iOS 15, macOS 15, visionOS 1, watchOS 11, tvOS 16, *) public struct OUDSRadio: View { // MARK: - Properties - private let isError: Bool private let accessibilityLabel: String + private let isError: Bool + private let isReadOnly: Bool @Binding var isOn: Bool @Environment(\.isEnabled) private var isEnabled @@ -93,15 +94,17 @@ public struct OUDSRadio: View { /// Creates a radio with only an indicator. /// - /// **The design system does not allow to have both an error situation and a disabled state for the component.** + /// **The design system does not allow to have both an error or a read-only situation and a disabled state for the component.** /// /// - Parameters: /// - isOn: A binding to a property that determines whether the toggle is on or off. /// - accessibilityLabel: The accessibility label the component must have /// - isError: True if the look and feel of the component must reflect an error state, default set to `false` + /// - isReadOnly: True iif the component should be in read only mode, default set to `false` public init(isOn: Binding, accessibilityLabel: String, - isError: Bool = false) + isError: Bool = false, + isReadOnly: Bool = false) { if accessibilityLabel.isEmpty { OL.warning("The OUDSRadio should not have an empty accessibility label, think about your disabled users!") @@ -109,12 +112,13 @@ public struct OUDSRadio: View { _isOn = isOn self.accessibilityLabel = accessibilityLabel.localized() self.isError = isError + self.isReadOnly = isReadOnly } // MARK: Body public var body: some View { - InteractionButton { + InteractionButton(isReadOnly: isReadOnly) { $isOn.wrappedValue.toggle() } content: { interactionState in RadioIndicator(interactionState: interactionState, isOn: isOn, isError: isError) @@ -131,7 +135,7 @@ public struct OUDSRadio: View { /// Forges a string to vocalize with *Voice Over* describing the component state private var a11yLabel: String { - let stateDescription = isEnabled ? "" : "core_common_disabled_a11y".localized() + let stateDescription = !isEnabled || isReadOnly ? "core_common_disabled_a11y".localized() : "" let errorDescription = isError ? "core_common_onError_a11y".localized() : "" let radioA11yTrait = "core_radio_trait_a11y".localized() // Fake trait for Voice Over vocalization @@ -146,7 +150,7 @@ public struct OUDSRadio: View { /// The text to vocalize with *Voice Over* to explain to the user to which state the component will move when tapped private var a11yHint: String { - if !isEnabled { + if !isEnabled || isReadOnly { "" } else { _isOn.wrappedValue diff --git a/OUDS/Core/Components/Sources/Controls/Radio/OUDSRadioItem.swift b/OUDS/Core/Components/Sources/Controls/Radio/OUDSRadioItem.swift index d760fbf4862..58bf7fc0276 100644 --- a/OUDS/Core/Components/Sources/Controls/Radio/OUDSRadioItem.swift +++ b/OUDS/Core/Components/Sources/Controls/Radio/OUDSRadioItem.swift @@ -23,7 +23,7 @@ import SwiftUI /// /// The component can be rendered as two different layouts: /// -/// - **default**: the component has a leading indicator, a label and optional helper texts, and an optional trailing decorative icon +/// - **default**: the component has a leading indicator, a label and optional texts, and an optional trailing decorative icon /// - **reversed**: like the *default* layout but with a trailing radio indicator and a leading optional decorative icon /// /// ## Indicator states @@ -50,7 +50,7 @@ import SwiftUI /// /// ## Accessibility considerations /// -/// *Voice Over* will use several elements to describe the component: if component disabled / read only, if error context, the label and helper texts and a custom radio trait. +/// *Voice Over* will use several elements to describe the component: if component disabled / read only, if error context, the label and optional texts and a custom radio trait. /// No accessibility identifier is defined in OUDS side as this value remains in the users hands. /// /// ## Forbidden by design @@ -77,17 +77,17 @@ import SwiftUI /// /// // A leading radio with a label, and an additional label but without text. /// // The default layout will be used here. -/// OUDSRadioItem(isOn: $selection, label: "Lucy in the Sky with Diamonds", additionalLabel: "The Beatles") +/// OUDSRadioItem(isOn: $selection, label: "Lucy in the Sky with Diamonds", extraLabel: "The Beatles") /// /// // A leading radio with an additional label. /// // The default layout will be used here. -/// OUDSRadioItem(isOn: $selection, label: "Lucy in the Sky with Diamonds", additionalLabel: "The Beatles", helper: "1967") +/// OUDSRadioItem(isOn: $selection, label: "Lucy in the Sky with Diamonds", extraLabel: "The Beatles", description: "1967") /// -/// // A trailing radio with a label, an helper text, an icon, a divider and is about an error. +/// // A trailing radio with a label, a description, an icon, a divider and is about an error. /// // The reversed layout will be used here. /// OUDSRadioItem(isOn: $selection, /// label: "Rescue from this world!", -/// helper: "Put your hand in mine", +/// description: "Put your hand in mine", /// icon: Image(decorative: "ic_heart"), /// isReversed: true, /// isError: true, @@ -144,7 +144,7 @@ import SwiftUI /// /// ![A radio item component in light and dark mode with Wireframe theme](component_radioItem_Wireframe) /// -/// - Version: 1.3.0 (Figma component design version) +/// - Version: 1.4.0 (Figma component design version) /// - Since: 0.12.0 @available(iOS 15, macOS 15, visionOS 1, watchOS 11, tvOS 16, *) public struct OUDSRadioItem: View { @@ -160,14 +160,14 @@ public struct OUDSRadioItem: View { // MARK: - Initializer - /// Creates a radio with label and optional helper text, icon, divider. + /// Creates a radio with label and optional helper text as description, icon, divider. /// Supposed to be integrated inside a ``OUDSRadioPicker``. /// /// ```swift /// OUDSRadioItem(isOn: $selection, /// label: "Virgin Holy Lava", - /// additionalLabel: "Very spicy", - /// helper: "No alcohol, only tasty flavors", + /// extraLabel: "Very spicy", + /// description: "No alcohol, only tasty flavors", /// icon: Image(systemName: "flame") /// ``` /// @@ -176,8 +176,8 @@ public struct OUDSRadioItem: View { /// - Parameters: /// - isOn: A binding to a property that determines whether the toggle is on or off. /// - label: The main label text of the radio. - /// - additionalLabel: An additional label text of the radio, default set to `nil` - /// - helper: An additonal helper text, should not be empty, default set to `nil` + /// - extraLabel: An additional label text of the radio, default set to `nil` + /// - description: An description, like an helper text, should not be empty, default set to `nil` /// - icon: An optional icon, default set to `nil` /// - flipIcon: Default set to `false`, set to true to reverse the image (i.e. flip vertically) /// - isOutlined: Flag to get an outlined radio, default set to `false` @@ -191,12 +191,12 @@ public struct OUDSRadioItem: View { /// /// **Remark 1: As divider and outline effect are not supposed to be displayed at the same time, the divider is not displayed if the outline effect is active.** /// - /// **Remark 2: If `label` and `helper` strings are wording keys from strings catalog stored in `Bundle.main`, they are automatically localized. Else, prefer to + /// **Remark 2: If `label` and `description` strings are wording keys from strings catalog stored in `Bundle.main`, they are automatically localized. Else, prefer to /// provide the localized string if key is stored in another bundle.** public init(isOn: Binding, label: String, - additionalLabel: String? = nil, - helper: String? = nil, + extraLabel: String? = nil, + description: String? = nil, icon: Image? = nil, flipIcon: Bool = false, isOutlined: Bool = false, @@ -211,12 +211,12 @@ public struct OUDSRadioItem: View { OL.fatal("It is forbidden by design to have an OUDSRadioItem in an error context and in read only mode") } - if let helper, helper.isEmpty { - OL.warning("Helper text given to an OUDSRadioItem is defined but empty, is it expected? Prefer use of `nil` value instead") + if let description, description.isEmpty { + OL.warning("Description text given to an OUDSRadioItem is defined but empty, is it expected? Prefer use of `nil` value instead") } - if let additionalLabel, additionalLabel.isEmpty { - OL.warning("Additional label text given to an OUDSRadioItem is defined but empty, is it expected? Prefer use of `nil` value instead") + if let extraLabel, extraLabel.isEmpty { + OL.warning("Extra label text given to an OUDSRadioItem is defined but empty, is it expected? Prefer use of `nil` value instead") } // swiftlint:disable force_unwrapping @@ -228,8 +228,8 @@ public struct OUDSRadioItem: View { _isOn = isOn layoutData = .init( label: label.localized(), - additionalLabel: additionalLabel?.localized(), - helper: helper?.localized(), + extraLabel: extraLabel?.localized(), + description: description?.localized(), icon: icon, flipIcon: flipIcon, isOutlined: isOutlined, @@ -259,12 +259,15 @@ public struct OUDSRadioItem: View { /// Forges a string to vocalize with *Voice Over* describing the component state. private var a11yLabel: String { let stateDescription: String = layoutData.isReadOnly || !isEnabled ? "core_common_disabled_a11y".localized() : "" - let errorPrefix = layoutData.isError ? "core_common_onError_a11y".localized() : "" - let errorText = layoutData.errorText?.localized() ?? "" - let errorDescription = "\(errorPrefix), \(errorText)" + var errorDescription = "" + if layoutData.isError { + let errorPrefix = "core_common_onError_a11y".localized() + let errorText = layoutData.errorText?.localized() ?? "" + errorDescription = "\(errorPrefix), \(errorText)" + } let radioA11yTrait = "core_radio_trait_a11y".localized() // Fake trait for Voice Over vocalization - let result = "\(stateDescription), \(layoutData.label), \(layoutData.additionalLabel ?? ""), \(layoutData.helper ?? "") \(errorDescription), \(radioA11yTrait)" + let result = "\(stateDescription), \(layoutData.label), \(layoutData.extraLabel ?? ""), \(layoutData.description ?? "") \(errorDescription), \(radioA11yTrait)" return result } diff --git a/OUDS/Core/Components/Sources/Controls/RadioPicker/OUDSRadioPicker.swift b/OUDS/Core/Components/Sources/Controls/RadioPicker/OUDSRadioPicker.swift index f703f9941c5..221478a607c 100644 --- a/OUDS/Core/Components/Sources/Controls/RadioPicker/OUDSRadioPicker.swift +++ b/OUDS/Core/Components/Sources/Controls/RadioPicker/OUDSRadioPicker.swift @@ -43,13 +43,13 @@ import SwiftUI /// [ /// OUDSRadioPickerData(tag: "Choice_1", /// label: "Virgin Holy Lava", -/// additionalLabel: "Very spicy", -/// helper: "No alcohol, only tasty flavors", +/// extraLabel: "Very spicy", +/// description: "No alcohol, only tasty flavors", /// icon: Image(systemName: "flame")), /// /// OUDSRadioPickerData(tag: "Choice_2", /// label: "IPA beer", -/// helper: "From Brewdog company", +/// description: "From Brewdog company", /// icon: Image(systemName: "dog.fill")), /// /// OUDSRadioPickerData(tag: "Choice_3", @@ -200,8 +200,8 @@ public struct OUDSRadioPicker: View where Tag: Hashable { private func content(for radio: OUDSRadioPickerData, noDivider: Bool) -> some View { OUDSRadioItem(isOn: selection.wrappedValue == radio.tag ? .constant(true) : .constant(false), label: radio.label, - additionalLabel: radio.additionalLabel, - helper: radio.helper, + extraLabel: radio.extraLabel, + description: radio.description, icon: radio.icon, isOutlined: isOutlined ? true : radio.isOutlined, isReversed: isReversed ? true : radio.isReversed, diff --git a/OUDS/Core/Components/Sources/Controls/RadioPicker/OUDSRadioPickerData.swift b/OUDS/Core/Components/Sources/Controls/RadioPicker/OUDSRadioPickerData.swift index 26e4833847f..656c82e782b 100644 --- a/OUDS/Core/Components/Sources/Controls/RadioPicker/OUDSRadioPickerData.swift +++ b/OUDS/Core/Components/Sources/Controls/RadioPicker/OUDSRadioPickerData.swift @@ -28,10 +28,10 @@ public struct OUDSRadioPickerData where Tag: Hashable { let label: String /// An optional additional label the ``OUDSRadioItem`` can have - let additionalLabel: String? + let extraLabel: String? - /// An optional helper text the ``OUDSRadioItem`` can have - let helper: String? + /// A description the ``OUDSRadioItem`` can have + let description: String? /// An optional image the ``OUDSRadioItem`` can have let icon: Image? @@ -60,8 +60,8 @@ public struct OUDSRadioPickerData where Tag: Hashable { /// - Parameters: /// - tag: a value to discriminate one radio to another /// - label: the mandatory text to add to ``OUDSRadioItem`` - /// - additionalLabel: An optional additinal text, default set to nil - /// - helper: Another optional text, default set to nil + /// - extraLabel: An optional additinal text, default set to nil + /// - description: Another optional text, a description, default set to nil /// - icon: An optional image, default set to nil /// - isOutlined: True to outline the ``OUDSRadioItem``, false otherwise (default) /// - isReversed: True to use to reversed layour of the ``OUDSRadioItem``, false otherwise (default) @@ -70,12 +70,12 @@ public struct OUDSRadioPickerData where Tag: Hashable { /// - hasDivider: True if a divider must be added for the current ``OUDSRadioItem``, false otherwise (default) /// - accessibilityIdentifier: The accessibility identifier to add to the item, nil by default /// - /// **Remark: If `label`, `additionalLabel` and `helper` strings are wording keys from strings catalog stored in `Bundle.main`, they are + /// **Remark: If `label`, `extraLabel` and `helper` strings are wording keys from strings catalog stored in `Bundle.main`, they are /// automatically localized. Else, prefer to provide the localized string if key is stored in another bundle.** public init(tag: Tag, label: String, - additionalLabel: String? = nil, - helper: String? = nil, + extraLabel: String? = nil, + description: String? = nil, icon: Image? = nil, isOutlined: Bool = false, isReversed: Bool = false, @@ -86,8 +86,8 @@ public struct OUDSRadioPickerData where Tag: Hashable { { self.tag = tag self.label = label - self.additionalLabel = additionalLabel - self.helper = helper + self.extraLabel = extraLabel + self.description = description self.icon = icon self.isOutlined = isOutlined self.isReversed = isReversed diff --git a/OUDS/Core/Components/Sources/Controls/Switch/Internal/SwitchIndicator.swift b/OUDS/Core/Components/Sources/Controls/Switch/Internal/SwitchIndicator.swift index c2602ddd505..1f321beeb40 100644 --- a/OUDS/Core/Components/Sources/Controls/Switch/Internal/SwitchIndicator.swift +++ b/OUDS/Core/Components/Sources/Controls/Switch/Internal/SwitchIndicator.swift @@ -53,7 +53,9 @@ struct SwitchIndicator: View { isOn ? theme.switch.colorTrackSelected : theme.switch.colorTrackUnselected case .hover, .pressed: isOn ? theme.switch.colorTrackSelectedInteraction : theme.switch.colorTrackUnselectedInteraction - case .disabled, .readOnly: + case .readOnly: + theme.colors.actionReadOnlySecondary + case .disabled: theme.colors.actionDisabled } } @@ -117,7 +119,9 @@ private struct Cursor: View { theme.switch.colorCheck.color(for: colorScheme) case .pressed: Color.clear - case .disabled, .readOnly: + case .readOnly: + theme.colors.actionReadOnlyPrimary.color(for: colorScheme) + case .disabled: theme.colors.actionDisabled.color(for: colorScheme) } } else { diff --git a/OUDS/Core/Components/Sources/Controls/Switch/OUDSSwitch.swift b/OUDS/Core/Components/Sources/Controls/Switch/OUDSSwitch.swift index 2d1d95831f3..f3dc2d6728f 100644 --- a/OUDS/Core/Components/Sources/Controls/Switch/OUDSSwitch.swift +++ b/OUDS/Core/Components/Sources/Controls/Switch/OUDSSwitch.swift @@ -24,6 +24,10 @@ import SwiftUI /// It is a good pratice (at least) to define a label for a component without text for accessibility reasons. This label will be vocalized by *Voice Over*. /// The vocalization tool will also use, after the label, a description of the component (if disabled, if error context), and a fake trait for switch. /// +/// ## Cases forbidden by design +/// +/// **The design system does not allow to have both a read-only situation and a disabled component.** +/// /// ## Code samples /// /// ```swift @@ -56,7 +60,7 @@ import SwiftUI /// /// ![A switch component in light and dark mode with Wireframe theme](component_switch_Wireframe) /// -/// - Version: 1.4.0 (Figma component design version) +/// - Version: 1.5.0 (Figma component design version) /// - Since: 0.14.0 @available(iOS 15, macOS 15, visionOS 1, watchOS 11, tvOS 16, *) public struct OUDSSwitch: View { @@ -66,6 +70,7 @@ public struct OUDSSwitch: View { @Binding var isOn: Bool private let accessibilityLabel: String + private let isReadOnly: Bool @Environment(\.isEnabled) private var isEnabled @Environment(\.theme) private var theme @@ -74,21 +79,25 @@ public struct OUDSSwitch: View { /// Creates a switch with only an indicator. /// + /// **The design system does not allow to have both a read only situation and a disabled state for the component.** + /// /// - Parameters: /// - isOn: A binding to a property that determines whether the toggle is on or off. /// - accessibilityLabel: The accessibility label the component must have - public init(isOn: Binding, accessibilityLabel: String) { + /// - isReadOnly: True if the look and feel of the component must reflect a read only state, default set to `false` + public init(isOn: Binding, accessibilityLabel: String, isReadOnly: Bool = false) { if accessibilityLabel.isEmpty { OL.warning("The OUDSSwitch should not have an empty accessibility label, think about your disabled users!") } _isOn = isOn self.accessibilityLabel = accessibilityLabel.localized() + self.isReadOnly = isReadOnly } // MARK: Body public var body: some View { - InteractionButton { + InteractionButton(isReadOnly: isReadOnly) { withAnimation(.timingCurve(0.2, 0, 0, 1, duration: 0.150)) { $isOn.wrappedValue.toggle() } @@ -109,7 +118,7 @@ public struct OUDSSwitch: View { /// Forges a string to vocalize with *Voice Over* describing the component state private var a11yLabel: String { - let stateDescription = isEnabled ? "" : "core_common_disabled_a11y".localized() + let stateDescription = !isEnabled || isReadOnly ? "core_common_disabled_a11y".localized() : "" let switchA11yTrait = "core_switch_trait_a11y".localized() // Fake trait for Voice Over vocalization let result = "\(accessibilityLabel), \(stateDescription), \(switchA11yTrait)" @@ -123,7 +132,7 @@ public struct OUDSSwitch: View { /// The text to vocalize with *Voice Over* to explain to the user to which state the component will move when tapped private var a11yHint: String { - if !isEnabled { + if !isEnabled || isReadOnly { "" } else { "core_switch_hint_a11y" <- (_isOn.wrappedValue ? "core_common_unselected_a11y" : "core_common_selected_a11y").localized() diff --git a/OUDS/Core/Components/Sources/Controls/Switch/OUDSSwitchItem.swift b/OUDS/Core/Components/Sources/Controls/Switch/OUDSSwitchItem.swift index e5e3938b134..d5393b825b9 100644 --- a/OUDS/Core/Components/Sources/Controls/Switch/OUDSSwitchItem.swift +++ b/OUDS/Core/Components/Sources/Controls/Switch/OUDSSwitchItem.swift @@ -23,7 +23,7 @@ import SwiftUI /// /// The component can be rendered as two different layouts: /// -/// - **default**: the component has a leading indicator, a label and optional helper texts, and an optional trailing decorative icon +/// - **default**: the component has a leading indicator, a label and optional texts, and an optional trailing decorative icon /// - **inverse**: like the *default* layout but with a trailing switch indicator and a leading optional decorative icon /// /// ## Indicator states @@ -42,7 +42,7 @@ import SwiftUI /// /// ## Accessibility considerations /// -/// *Voice Over* will use several elements to describe the component: if component disabled / read only, if error context, the label and helper texts and a custom switch trait. +/// *Voice Over* will use several elements to describe the component: if component disabled / read only, if error context, the label and optional texts and a custom switch trait. /// /// ## Forbidden by design /// @@ -64,28 +64,28 @@ import SwiftUI /// // The default layout will be used here. /// OUDSSwitchItem("Lucy in the Sky with Diamonds", isOn: $isOn, isReadOnly: true) /// -/// // A leading switch with a label, and an helper text. +/// // A leading switch with a label, and a description text. /// // The default layout will be used here. -/// OUDSSwitchItem("Lucy in the Sky with Diamonds", isOn: $isOn, helper: "The Beatles") +/// OUDSSwitchItem("Lucy in the Sky with Diamonds", isOn: $isOn, description: "The Beatles") /// /// // A leading switch with an additional label. /// // The default layout will be used here. -/// OUDSSwitchItem("Lucy in the Sky with Diamonds", isOn: $isOn, additionalLabel: "The Beatles", helper: "1967") +/// OUDSSwitchItem("Lucy in the Sky with Diamonds", isOn: $isOn, extraLabel: "The Beatles", description: "1967") /// -/// // A trailing switch with a label, an additonal label, an helper text and an icon. +/// // A trailing switch with a label, an additonal label, a description text and an icon. /// // The inverse layout will be used here. /// OUDSSwitchItem("Lucy in the Sky with Diamonds", /// isOn: $isOn, -/// additionalLabel: "The Beatles", -/// helper: "1967", +/// extraLabel: "The Beatles", +/// description: "1967", /// isReversed: true, /// icon: Image(decorative: "ic_heart")) /// -/// // A trailing switch with a label, an helper text, an icon, a divider and is about an error. +/// // A trailing switch with a label, a description text, an icon, a divider and is about an error. /// // The inverse layout will be used here. /// OUDSSwitchItem("Rescue from this world!", /// isOn: $isOn, -/// helper: "Put your hand in mine", +/// description: "Put your hand in mine", /// icon: Image(decorative: "ic_heart"), /// isReversed: true, /// isError: true, @@ -138,7 +138,7 @@ import SwiftUI /// /// ![A switch item component in light and dark mode with Wireframe theme](component_switchItem_Wireframe) /// -/// - Version: 1.4.0 (Figma component design version) +/// - Version: 1.5.0 (Figma component design version) /// - Since: 0.14.0 @available(iOS 15, macOS 15, visionOS 1, watchOS 11, tvOS 16, *) public struct OUDSSwitchItem: View { @@ -152,14 +152,14 @@ public struct OUDSSwitchItem: View { // MARK: - Initializers - /// Creates a switch with label and optional helper text, icon, divider. + /// Creates a switch with label and optional desvription text, icon, divider. /// /// **The design system does not allow to have both an error situation and a read only mode for the component.** /// /// - Parameters: /// - label: The main label text of the switch. /// - isOn: A binding to a property that determines whether the toggle is on or off. - /// - helper: An additonal helper text, should not be empty + /// - description: An additonal helper text, a description, should not be empty /// - icon: An optional icon, default set to `nil` /// - flipIcon: Default set to `false`, set to true to reverse the image (i.e. flip vertically) /// - isReversed: `True` of the switch indicator must be in trailing position,` false` otherwise. Default to `true` @@ -169,11 +169,11 @@ public struct OUDSSwitchItem: View { /// - isReadOnly: True if component is in read only, i.e. not really disabled but user cannot interact with it yet, default set to `false` /// - hasDivider: If `true` a divider is added at the bottom of the view. /// - /// **Remark: If `label` and `helper` strings are wording keys from strings catalog stored in `Bundle.main`, they are + /// **Remark: If `label` and `description` strings are wording keys from strings catalog stored in `Bundle.main`, they are /// automatically localized. Else, prefer to provide the localized string if key is stored in another bundle.** public init(_ label: String, isOn: Binding, - helper: String? = nil, + description: String? = nil, icon: Image? = nil, flipIcon: Bool = false, isReversed: Bool = true, @@ -186,8 +186,8 @@ public struct OUDSSwitchItem: View { OL.fatal("It is forbidden by design to have an OUDSSwitchItem in an error context and in read only mode") } - if let helper, helper.isEmpty { - OL.warning("Helper text given to an OUDSSwitchItem is defined but empty, is it expected? Prefer use of `nil` value instead") + if let description, description.isEmpty { + OL.warning("Description text given to an OUDSSwitchItem is defined but empty, is it expected? Prefer use of `nil` value instead") } // swiftlint:disable force_unwrapping @@ -200,8 +200,8 @@ public struct OUDSSwitchItem: View { layoutData = .init( label: label.localized(), - additionalLabel: nil, - helper: helper?.localized(), + extraLabel: nil, + description: description?.localized(), icon: icon, flipIcon: flipIcon, isOutlined: false, @@ -230,13 +230,17 @@ public struct OUDSSwitchItem: View { /// Forges a string to vocalize with *Voice Over* describing the component state. private var a11yLabel: String { let stateDescription: String = layoutData.isReadOnly || !isEnabled ? "core_common_disabled_a11y".localized() : "" - let errorPrefix = layoutData.isError ? "core_common_onError_a11y".localized() : "" - let errorText = layoutData.errorText?.localized() ?? "" - let errorDescription = "\(errorPrefix), \(errorText)" + + var errorDescription = "" + if layoutData.isError { + let errorPrefix = "core_common_onError_a11y".localized() + let errorText = layoutData.errorText?.localized() ?? "" + errorDescription = "\(errorPrefix), \(errorText)" + } let switchA11yTrait = "core_switch_trait_a11y".localized() // Fake trait for Voice Over vocalization - let result = "\(stateDescription), \(layoutData.label), \(layoutData.additionalLabel ?? ""), \(layoutData.helper ?? ""), \(errorDescription), \(switchA11yTrait)" + let result = "\(stateDescription), \(layoutData.label), \(layoutData.extraLabel ?? ""), \(layoutData.description ?? ""), \(errorDescription), \(switchA11yTrait)" return result } diff --git a/OUDS/Core/Components/Sources/_/Internal/ControlItem/ControlItemContent.swift b/OUDS/Core/Components/Sources/_/Internal/ControlItem/ControlItemContent.swift index 02487373898..f881580bf2b 100644 --- a/OUDS/Core/Components/Sources/_/Internal/ControlItem/ControlItemContent.swift +++ b/OUDS/Core/Components/Sources/_/Internal/ControlItem/ControlItemContent.swift @@ -49,7 +49,8 @@ struct ControlItemContent: View { indicatorContainer() } } - .padding(.all, theme.controlItem.spacePaddingBlockDefault) + .padding(.horizontal, theme.controlItem.spacePaddingInline) + .padding(.vertical, theme.controlItem.spacePaddingBlockDefault) .modifier(ControlItemBackgroundModifier(interactionState: interactionState)) .modifier(ControlItemBordersModifier(interactionState: interactionState, layoutData: layoutData, isOn: isOn)) @@ -57,7 +58,7 @@ struct ControlItemContent: View { Text(errorText) .labelDefaultMedium(theme) .oudsForegroundColor(theme.colors.contentStatusNegative) - .padding(.top, theme.textInput.spacePaddingBlockTopHelperText) + .padding(.top, theme.controlItem.spacePaddingBlockTopErrorText) .padding(.horizontal, theme.controlItem.spacePaddingInline) } } diff --git a/OUDS/Core/Components/Sources/_/Internal/ControlItem/ControlItemLabel.swift b/OUDS/Core/Components/Sources/_/Internal/ControlItem/ControlItemLabel.swift index 8183541744e..1ea88742af7 100644 --- a/OUDS/Core/Components/Sources/_/Internal/ControlItem/ControlItemLabel.swift +++ b/OUDS/Core/Components/Sources/_/Internal/ControlItem/ControlItemLabel.swift @@ -32,8 +32,8 @@ struct ControlItemLabel: View { /// Gathers any details and content to add in the ``ControlItemLabel`` struct LayoutData { let label: String - let additionalLabel: String? - let helper: String? + let extraLabel: String? + let description: String? let icon: Image? let flipIcon: Bool let isOutlined: Bool @@ -62,15 +62,15 @@ struct ControlItemLabel: View { .oudsForegroundStyle(labelColor) .frame(maxWidth: .infinity, alignment: .leading) - if let additionalLabel = layoutData.additionalLabel, !additionalLabel.isEmpty { - Text(additionalLabel) + if let extraLabel = layoutData.extraLabel, !extraLabel.isEmpty { + Text(extraLabel) .labelStrongMedium(theme) .multilineTextAlignment(.leading) - .oudsForegroundStyle(additionalLabelColor) + .oudsForegroundStyle(extraLabelColor) } - if let helper = layoutData.helper, !helper.isEmpty { - Text(helper) + if let description = layoutData.description, !description.isEmpty { + Text(description) .labelDefaultMedium(theme) .multilineTextAlignment(.leading) .oudsForegroundStyle(helperColor) @@ -113,7 +113,7 @@ struct ControlItemLabel: View { } } - private var additionalLabelColor: MultipleColorSemanticTokens { + private var extraLabelColor: MultipleColorSemanticTokens { interactionState == .disabled ? theme.colors.contentDisabled : theme.colors.contentDefault } } diff --git a/OUDS/Core/Components/Sources/_OUDSComponents.docc/Controls.md b/OUDS/Core/Components/Sources/_OUDSComponents.docc/Controls.md index 54ddf8d0e4d..bafd6c1f245 100644 --- a/OUDS/Core/Components/Sources/_OUDSComponents.docc/Controls.md +++ b/OUDS/Core/Components/Sources/_OUDSComponents.docc/Controls.md @@ -96,15 +96,15 @@ It can be be used for two-states (``OUDSCheckboxItem``) or three-states manageme // A leading checkbox with a label, an helper text, and exposing a three-values-based state with selection binding OUDSCheckboxItemIndeterminate(selection: $selection, label: "Dead Robot Zombie Cop", - helper: "from Outer Space II", + description: "from Outer Space II", target: self, - action: action) + action: action) // A trailing checkbox with a label, an helper text, an icon, a divider and is about an error // with a reversed layout, and exposing only two states through isOn binding OUDSCheckboxItem(isOn: $isOn, label: "We live in a fabled world", - helper: "Of dreaming boys and wide-eyed girls", + description: "Of dreaming boys and wide-eyed girls", icon: Image(decorative: "ic_heart"), isReversed: true, isError: true, @@ -126,7 +126,7 @@ It can be be used for two-states (``OUDSCheckboxItem``) or three-states manageme // A leading checkbox with a label, an helper text, and with a three-values-based state with selection binding OUDSUIKit.createCheckboxItemIndeterminate(selection: selection, label: "Dead Robot Zombie Cop", - helper: "from Outer Space II" + description: "from Outer Space II" target: self, action: action) @@ -134,7 +134,7 @@ It can be be used for two-states (``OUDSCheckboxItem``) or three-states manageme // with a reversed layout, and with two states OUDSUIKit.createCheckboxItem(isOn: isOn, label: "We live in a fabled world", - helper: "Of dreaming boys and wide-eyed girls", + description: "Of dreaming boys and wide-eyed girls", icon: Image(decorative: "ic_heart"), isReversed: true, isError: true, @@ -172,13 +172,12 @@ var someDataToPopulate: [OUDSCheckboxPickerData] { [ OUDSCheckboxPickerData(tag: "Choice_1", label: "Virgin Holy Lava", - additionalLabel: "Very spicy", - helper: "No alcohol, only tasty flavors", + description: "No alcohol, only tasty flavors", icon: Image(systemName: "flame")), OUDSCheckboxPickerData(tag: "Choice_2", - label: "IPA beer", - helper: "From Brewdog company", + label: "IPA beer", + description: "From Brewdog company", icon: Image(systemName: "dog.fill")), OUDSCheckboxPickerData(tag: "Choice_3", @@ -280,12 +279,12 @@ The indicator can be leading or trailing. // A leading radio with a label OUDSRadioItem(isOn: $isOn, label: "Lucy in the Sky with Diamonds", target: target, action: action) - // A trailing radio with a label, an additional label, an helper text, an icon, a divider and is about an + // A trailing radio with a label, an additional label, a descrption, an icon, a divider and is about an // error with a reversed layout OUDSRadioItem(isOn: $isOn, label: "Lucy in the Sky with Diamonds", - additionalLabel: "The Beatles" - helper: "1967", + extraLabel: "The Beatles" + description: "1967", icon: Image(decorative: "ic_heart"), isReversed: true, isError: true, @@ -304,11 +303,11 @@ The indicator can be leading or trailing. // A leading radio with a label OUDSUIKit.createRadioItem(isOn: isOn, label: "Lucy in the Sky with Diamonds") - // A trailing radio with a label, an additional label, an helper text, an icon, a divider and is about an rror with a reversed layout + // A trailing radio with a label, an additional label, a description, an icon, a divider and is about an rror with a reversed layout OUDSUIKit.createRadioItem(isOn: isOn, label: "Lucy in the Sky with Diamonds", - additionalLabel: "The Beatles" - helper: "1967", + extraLabel: "The Beatles" + description: "1967", icon: Image(decorative: "ic_heart"), isReversed: true, isError: true, @@ -344,13 +343,13 @@ var someDataToPopulate: [OUDSRadioPickerData] { [ OUDSRadioPickerData(tag: "Choice_1", label: "Virgin Holy Lava", - additionalLabel: "Very spicy", - helper: "No alcohol, only tasty flavors", + extraLabel: "Very spicy", + description: "No alcohol, only tasty flavors", icon: Image(systemName: "flame")), OUDSRadioPickerData(tag: "Choice_2", label: "IPA beer", - helper: "From Brewdog company", + description: "From Brewdog company", icon: Image(systemName: "dog.fill")), OUDSRadioPickerData(tag: "Choice_3", @@ -443,16 +442,16 @@ The indicator can be leading or trailing. // A leading switch with a label and exposing the state through isOn binding OUDSSwitchItem("Hello world", isOn: $isOn) - // A leading switch with a label, an helper text + // A leading switch with a label and a description OUDSSwitchItem("Dead Robot Zombie Cop", isOn: $isOn, - helper: "from Outer Space II") + description: "from Outer Space II") - // A trailing switch with a label, an helper text, an icon, a divider and is about an error + // A trailing switch with a label, a description, an icon, a divider and is about an error // with an inverse layout OUDSSwitchItem("We live in a fabled world", isOn: $isOn, - helper: "Of dreaming boys and wide-eyed girls", + description: "Of dreaming boys and wide-eyed girls", icon: Image(decorative: "ic_heart"), isReversed: true, isError: true, @@ -476,11 +475,11 @@ The indicator can be leading or trailing. target: target, action: action) - // A trailing switch with a label, an helper text, an icon, a divider and is about an error + // A trailing switch with a label, a description text, an icon, a divider and is about an error // with an inverse layout OUDSUIKit.createSwitchItem("We live in a fabled world", isOn: isOn, - helper: "Of dreaming boys and wide-eyed girls", + description: "Of dreaming boys and wide-eyed girls", icon: Image(decorative: "ic_heart"), isReversed: true, isError: true, diff --git a/OUDS/Core/ComponentsUIKit/Sources/Controls/Checkbox/_OUDSCheckbox.swift b/OUDS/Core/ComponentsUIKit/Sources/Controls/Checkbox/_OUDSCheckbox.swift index f89b62ea68a..faca417c323 100644 --- a/OUDS/Core/ComponentsUIKit/Sources/Controls/Checkbox/_OUDSCheckbox.swift +++ b/OUDS/Core/ComponentsUIKit/Sources/Controls/Checkbox/_OUDSCheckbox.swift @@ -55,11 +55,13 @@ public final class OUDSCheckboxViewController: UIViewController { /// - isOn: If the checkbox is checked or not /// - accessibilityLabel: The accessibility label to vocalise for the checkbox /// - isError: If the checkbox is in error state or not - init(isOn: Bool, accessibilityLabel: String, isError: Bool) { + /// - isReadOnly: If the checkbox is in ready only state or not + init(isOn: Bool, accessibilityLabel: String, isError: Bool, isReadOnly: Bool) { checkboxViewModel = OUDSCheckboxViewModel( isOn: isOn, accessibilityLabel: accessibilityLabel, - isError: isError) + isError: isError, + isReadOnly: isReadOnly) targets = [] super.init(nibName: nil, bundle: nil) setupInternalCallback() @@ -157,7 +159,8 @@ struct OUDSCheckboxWrapper: View { OUDSCheckbox( isOn: checkboxBinding, accessibilityLabel: model.accessibilityLabel, - isError: model.isError) + isError: model.isError, + isReadOnly: model.isReadOnly) .environment(\._theme, OUDSUIKit.theme) } } @@ -171,6 +174,8 @@ struct OUDSCheckboxWrapper: View { @Published var isOn: Bool /// For `OUDSCheckbox/isError` var isError: Bool + /// For `OUDSCheckbox/isReadOnly` + var isReadOnly: Bool /// For `OUDSCheckbox/accessibilityLabel` var accessibilityLabel: String @@ -178,11 +183,13 @@ struct OUDSCheckboxWrapper: View { init(isOn: Bool, accessibilityLabel: String, - isError: Bool) + isError: Bool, + isReadOnly: Bool) { self.isOn = isOn self.accessibilityLabel = accessibilityLabel self.isError = isError + self.isReadOnly = isReadOnly } deinit {} @@ -216,11 +223,13 @@ extension OUDSUIKitBrige { /// - isOn: True if checkbox is selected, false otherwise /// - accessibilityLabel: The accessibility label the component must have /// - isError: True if the look and feel of the component must reflect an error state, default set to `false` + /// - isReadOnly: True if the look and feel of the component must reflect a read only state, default set to `false` /// - target: Reference to the `UIViewController` hosting the component to create /// - action: The action to trigger defined in the `target` when the value of the `OUDSCheckbox` has changed @MainActor public static func createCheckbox(isOn: Bool, accessibilityLabel: String, isError: Bool = false, + isReadOnly: Bool = false, target: Any?, action: Selector) -> UIViewController { @@ -229,7 +238,8 @@ extension OUDSUIKitBrige { let uikitCheckboxViewController = OUDSCheckboxViewController( isOn: isOn, accessibilityLabel: accessibilityLabel, - isError: isError) + isError: isError, + isReadOnly: isReadOnly) uikitCheckboxViewController.addTarget(target, action: action, for: .valueChanged) return uikitCheckboxViewController } diff --git a/OUDS/Core/ComponentsUIKit/Sources/Controls/Checkbox/_OUDSCheckboxItem.swift b/OUDS/Core/ComponentsUIKit/Sources/Controls/Checkbox/_OUDSCheckboxItem.swift index 514b6e10848..eb10b7c7394 100644 --- a/OUDS/Core/ComponentsUIKit/Sources/Controls/Checkbox/_OUDSCheckboxItem.swift +++ b/OUDS/Core/ComponentsUIKit/Sources/Controls/Checkbox/_OUDSCheckboxItem.swift @@ -54,7 +54,7 @@ public final class OUDSCheckboxItemViewController: UIViewController { /// - Parameters: /// - isOn: A property that determines wether the indicator is ticked (selected) or not (unselected) /// - label: The main label text of the checkbox. - /// - helper: An additonal helper text, should not be empty + /// - description: An additonal helper text, should not be empty /// - icon: An optional icon /// - flipIcon: Set to true to reverse the image (i.e. flip vertically) /// - isReversed: `true` if the checkbox indicator must be in trailing position,` false` otherwise. @@ -64,7 +64,7 @@ public final class OUDSCheckboxItemViewController: UIViewController { /// - action: An additional action to trigger when the checkbox has been pressed init(isOn: Bool, label: String, - helper: String?, + description: String?, icon: Image?, flipIcon: Bool, isReversed: Bool, @@ -75,7 +75,7 @@ public final class OUDSCheckboxItemViewController: UIViewController { { checkboxViewModel = OUDSCheckboxItemViewModel(isOn: isOn, label: label, - helper: helper, + description: description, icon: icon, flipIcon: flipIcon, isReversed: isReversed, @@ -180,7 +180,7 @@ struct OUDSCheckboxItemWrapper: View { OUDSCheckboxItem( isOn: checkboxBinding, label: model.label, - helper: model.helper, + description: model.description, icon: model.icon, flipIcon: model.flipIcon, isReversed: model.isReversed, @@ -201,8 +201,8 @@ struct OUDSCheckboxItemWrapper: View { @Published var isOn: Bool /// For `OUDSCheckboxItem/label` var label: String - /// For `OUDSCheckboxItem/helper` - var helper: String? + /// For `OUDSCheckboxItem/description` + var description: String? /// For `OUDSCheckboxItem/icon` var icon: Image? /// For `OUDSCheckboxItem/flipIcon` @@ -222,7 +222,7 @@ struct OUDSCheckboxItemWrapper: View { init(isOn: Bool, label: String, - helper: String?, + description: String?, icon: Image?, flipIcon: Bool, isReversed: Bool, @@ -233,7 +233,7 @@ struct OUDSCheckboxItemWrapper: View { { self.isOn = isOn self.label = label - self.helper = helper + self.description = description self.icon = icon self.flipIcon = flipIcon self.isReversed = isReversed @@ -263,7 +263,7 @@ extension OUDSUIKitBrige { /// // Then create the OUDSCheckboxItem inside your UIViewController /// OUDSUIKit.createCheckboxItem(isOn: true, /// label: "An awesome label" - /// helper: "A smaller text", + /// description: "A smaller text", /// icon: Image("ic_heart"), /// action: #selector(checkboxChanged(_:))) /// ``` @@ -273,7 +273,7 @@ extension OUDSUIKitBrige { /// - Parameters: /// - isOn: A flag indicating wether or not the checkbox is selected /// - label: The main label text of the checkbox. - /// - helper: An additonal helper text, should not be empty, default set to `nil` + /// - description: An additonal helper text, should not be empty, default set to `nil` /// - icon: An optional icon, default set to `nil` /// - flipIcon: Default set to `false`, set to `true` to reverse the image (i.e. flip vertically) /// - isReversed: `true` if the checkbox indicator must be in trailing position,` false` otherwise. Default to `false` @@ -285,7 +285,7 @@ extension OUDSUIKitBrige { /// - action: The `Selector` called when the checkbox value changed @MainActor public static func createCheckboxItem(isOn: Bool, label: String, - helper: String? = nil, + description: String? = nil, icon: Image? = nil, flipIcon: Bool = false, isReversed: Bool = false, @@ -300,7 +300,7 @@ extension OUDSUIKitBrige { let uikitCheckboxItemViewController = OUDSCheckboxItemViewController(isOn: isOn, label: label, - helper: helper, + description: description, icon: icon, flipIcon: flipIcon, isReversed: isReversed, diff --git a/OUDS/Core/ComponentsUIKit/Sources/Controls/Checkbox/_OUDSCheckboxItemIndeterminate.swift b/OUDS/Core/ComponentsUIKit/Sources/Controls/Checkbox/_OUDSCheckboxItemIndeterminate.swift index b321607ec10..20dd7b7a9e5 100644 --- a/OUDS/Core/ComponentsUIKit/Sources/Controls/Checkbox/_OUDSCheckboxItemIndeterminate.swift +++ b/OUDS/Core/ComponentsUIKit/Sources/Controls/Checkbox/_OUDSCheckboxItemIndeterminate.swift @@ -56,7 +56,7 @@ public final class OUDSCheckboxItemIndeterminateViewController: UIViewController /// - Parameters: /// - selection:A property that determines wether the indicator is ticked (selected) or not (unselected) /// - label: The main label text of the checkbox. - /// - helper: An additonal helper text, should not be empty + /// - description: An additonal helper text, should not be empty /// - icon: An optional icon /// - flipIcon: Set to true to reverse the image (i.e. flip vertically) /// - isReversed: `true` if the checkbox indicator must be in trailing position,` false` otherwise. @@ -66,7 +66,7 @@ public final class OUDSCheckboxItemIndeterminateViewController: UIViewController /// - action: An additional action to trigger when the checkbox has been pressed init(selection: OUDSCheckboxIndicatorState, label: String, - helper: String?, + description: String?, icon: Image?, flipIcon: Bool, isReversed: Bool, @@ -77,7 +77,7 @@ public final class OUDSCheckboxItemIndeterminateViewController: UIViewController { checkboxViewModel = OUDSCheckboxItemIndeterminateViewModel(selection: selection, label: label, - helper: helper, + description: description, icon: icon, flipIcon: flipIcon, isReversed: isReversed, @@ -184,7 +184,7 @@ struct OUDSCheckboxItemIndeterminateWrapper: View { OUDSCheckboxItemIndeterminate( selection: checkboxBinding, label: model.label, - helper: model.helper, + description: model.description, icon: model.icon, flipIcon: model.flipIcon, isReversed: model.isReversed, @@ -205,8 +205,8 @@ struct OUDSCheckboxItemIndeterminateWrapper: View { @Published var selection: OUDSCheckboxIndicatorState /// For `OUDSCheckboxItemIndeterminate/label` var label: String - /// For `OUDSCheckboxItemIndeterminate/helper` - var helper: String? + /// For `OUDSCheckboxItemIndeterminate/description` + var description: String? /// For `OUDSCheckboxItemIndeterminate/icon` var icon: Image? /// For `OUDSCheckboxItemIndeterminate/flipIcon` @@ -226,7 +226,7 @@ struct OUDSCheckboxItemIndeterminateWrapper: View { init(selection: OUDSCheckboxIndicatorState, label: String, - helper: String?, + description: String?, icon: Image?, flipIcon: Bool, isReversed: Bool, @@ -237,7 +237,7 @@ struct OUDSCheckboxItemIndeterminateWrapper: View { { self.selection = selection self.label = label - self.helper = helper + self.description = description self.icon = icon self.flipIcon = flipIcon self.isReversed = isReversed @@ -267,7 +267,7 @@ extension OUDSUIKitBrige { /// // Then create the OUDSCheckboxItemIndeterminate inside your UIViewController /// OUDSUIKit.createCheckboxItemIndeterminate(selection: .indeterminate, /// label: "An awesome label" - /// helper: "A smaller text", + /// description: "A smaller text", /// icon: Image("ic_heart"), /// action: #selector(checkboxChanged(_:))) /// ``` @@ -277,7 +277,7 @@ extension OUDSUIKitBrige { /// - Parameters: /// - selection: A property indicating wether or not the checkbox is selected or in indeterminate state /// - label: The main label text of the checkbox. - /// - helper: An additonal helper text, should not be empty, default set to `nil` + /// - description: An additonal helper text, should not be empty, default set to `nil` /// - icon: An optional icon, default set to `nil` /// - flipIcon: Default set to `false`, set to `true` to reverse the image (i.e. flip vertically) /// - isReversed: `true` if the checkbox indicator must be in trailing position,` false` otherwise. Default to `false` @@ -289,7 +289,7 @@ extension OUDSUIKitBrige { /// - action: The `Selector` called when the checkbox value changed @MainActor public static func createCheckboxItemIndeterminate(selection: OUDSCheckboxIndicatorState, label: String, - helper: String? = nil, + description: String? = nil, icon: Image? = nil, flipIcon: Bool = false, isReversed: Bool = false, @@ -304,7 +304,7 @@ extension OUDSUIKitBrige { let uikitViewController = OUDSCheckboxItemIndeterminateViewController(selection: selection, label: label, - helper: helper, + description: description, icon: icon, flipIcon: flipIcon, isReversed: isReversed, diff --git a/OUDS/Core/ComponentsUIKit/Sources/Controls/Radio/_OUDSRadio.swift b/OUDS/Core/ComponentsUIKit/Sources/Controls/Radio/_OUDSRadio.swift index 7bcc2d77745..8a89a73b8b8 100644 --- a/OUDS/Core/ComponentsUIKit/Sources/Controls/Radio/_OUDSRadio.swift +++ b/OUDS/Core/ComponentsUIKit/Sources/Controls/Radio/_OUDSRadio.swift @@ -55,11 +55,13 @@ public final class OUDSRadioViewController: UIViewController { /// - isOn: If the radio button is checked or not /// - accessibilityLabel: The accessibility label to vocalise for the radio button /// - isError: If the radio button is in error state or not - init(isOn: Bool, accessibilityLabel: String, isError: Bool) { + /// - isReadOnly: If the radio button is in read only state or not + init(isOn: Bool, accessibilityLabel: String, isError: Bool, isReadOnly: Bool) { radioViewModel = OUDSRadioViewModel( isOn: isOn, accessibilityLabel: accessibilityLabel, - isError: isError) + isError: isError, + isReadOnly: isReadOnly) targets = [] super.init(nibName: nil, bundle: nil) setupInternalCallback() @@ -157,7 +159,8 @@ struct OUDSRadioWrapper: View { OUDSRadio( isOn: radioButtonBinding, accessibilityLabel: model.accessibilityLabel, - isError: model.isError) + isError: model.isError, + isReadOnly: model.isReadOnly) .environment(\._theme, OUDSUIKit.theme) } } @@ -171,6 +174,8 @@ struct OUDSRadioWrapper: View { @Published var isOn: Bool /// For `OUDSRadio/isError` var isError: Bool + /// For `OUDSRadio/isReadOnly` + var isReadOnly: Bool /// For `OUDSRadio/accessibilityLabel` var accessibilityLabel: String @@ -178,11 +183,13 @@ struct OUDSRadioWrapper: View { init(isOn: Bool, accessibilityLabel: String, - isError: Bool) + isError: Bool, + isReadOnly: Bool) { self.isOn = isOn self.accessibilityLabel = accessibilityLabel self.isError = isError + self.isReadOnly = isReadOnly } deinit {} @@ -216,20 +223,23 @@ extension OUDSUIKitBrige { /// - isOn: True if radio is selected, false otherwise /// - accessibilityLabel: The accessibility label the component must have /// - isError: True if the look and feel of the component must reflect an error state, default set to `false` + /// - isReadOnly: True if the look and feel of the component must reflect an read only state, default set to `false` /// - target: Reference to the `UIViewController` hosting the component to create /// - action: The action to trigger defined in the `target` when the value of the `OUDSRadio` has changed @MainActor public static func createRadio(isOn: Bool, accessibilityLabel: String, isError: Bool = false, + isReadOnly: Bool = false, target: Any?, action: Selector) -> UIViewController { - OL.warning("Avoid UIKit wrapper and prefer SwiftUI component instead OUDSRadio(isOn:accessibilityLabel:isError)") + OL.warning("Avoid UIKit wrapper and prefer SwiftUI component instead OUDSRadio(isOn:accessibilityLabel:)") let uikitRadioViewController = OUDSRadioViewController( isOn: isOn, accessibilityLabel: accessibilityLabel, - isError: isError) + isError: isError, + isReadOnly: isReadOnly) uikitRadioViewController.addTarget(target, action: action, for: .valueChanged) return uikitRadioViewController } diff --git a/OUDS/Core/ComponentsUIKit/Sources/Controls/Radio/_OUDSRadioItem.swift b/OUDS/Core/ComponentsUIKit/Sources/Controls/Radio/_OUDSRadioItem.swift index a23af099069..1dc27178b02 100644 --- a/OUDS/Core/ComponentsUIKit/Sources/Controls/Radio/_OUDSRadioItem.swift +++ b/OUDS/Core/ComponentsUIKit/Sources/Controls/Radio/_OUDSRadioItem.swift @@ -54,7 +54,7 @@ public final class OUDSRadioItemViewController: UIViewController { /// - Parameters: /// - isOn: A property that determines wether the indicator is ticked (selected) or not (unselected) /// - label: The main label text of the radio. - /// - helper: An additonal helper text, should not be empty + /// - description: An description text, should not be empty /// - icon: An optional icon /// - flipIcon: Set to true to reverse the image (i.e. flip vertically) /// - isReversed: `true` if the radio indicator must be in trailing position,` false` otherwise. @@ -64,7 +64,7 @@ public final class OUDSRadioItemViewController: UIViewController { /// - action: An additional action to trigger when the radio has been pressed init(isOn: Bool, label: String, - helper: String?, + description: String?, icon: Image?, flipIcon: Bool, isReversed: Bool, @@ -75,7 +75,7 @@ public final class OUDSRadioItemViewController: UIViewController { { radioViewModel = OUDSRadioItemViewModel(isOn: isOn, label: label, - helper: helper, + description: description, icon: icon, flipIcon: flipIcon, isReversed: isReversed, @@ -180,7 +180,7 @@ struct OUDSRadioItemWrapper: View { OUDSRadioItem( isOn: radioBinding, label: model.label, - helper: model.helper, + description: model.description, icon: model.icon, flipIcon: model.flipIcon, isReversed: model.isReversed, @@ -201,8 +201,8 @@ struct OUDSRadioItemWrapper: View { @Published var isOn: Bool /// For `OUDSRadioItem/label` var label: String - /// For `OUDSRadioItem/helper` - var helper: String? + /// For `OUDSRadioItem/description` + var description: String? /// For `OUDSRadioItem/icon` var icon: Image? /// For `OUDSRadioItem/flipIcon` @@ -222,7 +222,7 @@ struct OUDSRadioItemWrapper: View { init(isOn: Bool, label: String, - helper: String?, + description: String?, icon: Image?, flipIcon: Bool, isReversed: Bool, @@ -233,7 +233,7 @@ struct OUDSRadioItemWrapper: View { { self.isOn = isOn self.label = label - self.helper = helper + self.description = description self.icon = icon self.flipIcon = flipIcon self.isReversed = isReversed @@ -263,7 +263,7 @@ extension OUDSUIKitBrige { /// // Then create the OUDSRadioItem inside your UIViewController /// OUDSUIKit.createRadioItem(isOn: true, /// label: "An awesome label" - /// helper: "A smaller text", + /// description: "A smaller text", /// icon: Image("ic_heart"), /// action: #selector(radioChanged(_:))) /// ``` @@ -273,7 +273,7 @@ extension OUDSUIKitBrige { /// - Parameters: /// - isOn: A flag indicating wether or not the radio is selected /// - label: The main label text of the radio. - /// - helper: An additonal helper text, should not be empty, default set to `nil` + /// - description: An descrption, an helper text, should not be empty, default set to `nil` /// - icon: An optional icon, default set to `nil` /// - flipIcon: Default set to `false`, set to `true` to reverse the image (i.e. flip vertically) /// - isReversed: `true` if the radio indicator must be in trailing position,` false` otherwise. Default to `false` @@ -285,7 +285,7 @@ extension OUDSUIKitBrige { /// - action: The `Selector` called when the radio value changed @MainActor public static func createRadioItem(isOn: Bool, label: String, - helper: String? = nil, + description: String? = nil, icon: Image? = nil, flipIcon: Bool = false, isReversed: Bool = false, @@ -300,7 +300,7 @@ extension OUDSUIKitBrige { let uikitRadioItemViewController = OUDSRadioItemViewController(isOn: isOn, label: label, - helper: helper, + description: description, icon: icon, flipIcon: flipIcon, isReversed: isReversed, diff --git a/OUDS/Core/ComponentsUIKit/Sources/Controls/Switch/_OUDSSwitch.swift b/OUDS/Core/ComponentsUIKit/Sources/Controls/Switch/_OUDSSwitch.swift index db1ded93599..ee463e3196e 100644 --- a/OUDS/Core/ComponentsUIKit/Sources/Controls/Switch/_OUDSSwitch.swift +++ b/OUDS/Core/ComponentsUIKit/Sources/Controls/Switch/_OUDSSwitch.swift @@ -54,10 +54,12 @@ public final class OUDSSwitchViewController: UIViewController { /// - Parameters: /// - isOn: If the switch button is checked or not /// - accessibilityLabel: The accessibility label to vocalise for the switch - init(isOn: Bool, accessibilityLabel: String) { + /// - isReadOnly: If component must have look and feel and behavior of a read only component + init(isOn: Bool, accessibilityLabel: String, isReadOnly: Bool) { switchViewModel = OUDSSwitchViewModel( isOn: isOn, - accessibilityLabel: accessibilityLabel) + accessibilityLabel: accessibilityLabel, + isReadOnly: isReadOnly) targets = [] super.init(nibName: nil, bundle: nil) setupInternalCallback() @@ -168,14 +170,18 @@ struct OUDSSwitchWrapper: View { @Published var isOn: Bool /// For `OUDSSwitch/accessibilityLabel` var accessibilityLabel: String + /// For `OUDSSwitch/isReadOnly` + var isReadOnly: Bool var onInternalValueChanged: ((Bool) -> Void)? init(isOn: Bool, - accessibilityLabel: String) + accessibilityLabel: String, + isReadOnly: Bool) { self.isOn = isOn self.accessibilityLabel = accessibilityLabel + self.isReadOnly = isReadOnly } deinit {} @@ -185,6 +191,7 @@ struct OUDSSwitchWrapper: View { extension OUDSUIKitBrige { + // swiftlint:disable function_default_parameter_at_end /// Creates SwiftUI `OUDSSwitch` with only an indicator. /// /// ```swift @@ -206,10 +213,12 @@ extension OUDSUIKitBrige { /// - Parameters: /// - isOn: True if switch is selected, false otherwise /// - accessibilityLabel: The accessibility label the component must have + /// - isReadOnly: If component must have look and feel and behavior of a read only component, default set to false /// - target: Reference to the `UIViewController` hosting the component to create /// - action: The action to trigger defined in the `target` when the value of the `OUDSSwitch` has changed @MainActor public static func createSwitch(isOn: Bool, accessibilityLabel: String, + isReadOnly: Bool = false, target: Any?, action: Selector) -> UIViewController { @@ -217,9 +226,11 @@ extension OUDSUIKitBrige { let uikitSwitchViewController = OUDSSwitchViewController( isOn: isOn, - accessibilityLabel: accessibilityLabel) + accessibilityLabel: accessibilityLabel, + isReadOnly: isReadOnly) uikitSwitchViewController.addTarget(target, action: action, for: .valueChanged) return uikitSwitchViewController } + // swiftlint:enable function_default_parameter_at_end } #endif diff --git a/OUDS/Core/ComponentsUIKit/Sources/Controls/Switch/_OUDSSwitchItem.swift b/OUDS/Core/ComponentsUIKit/Sources/Controls/Switch/_OUDSSwitchItem.swift index e120f81e5b5..4285c600dd1 100644 --- a/OUDS/Core/ComponentsUIKit/Sources/Controls/Switch/_OUDSSwitchItem.swift +++ b/OUDS/Core/ComponentsUIKit/Sources/Controls/Switch/_OUDSSwitchItem.swift @@ -54,7 +54,7 @@ public final class OUDSSwitchItemViewController: UIViewController { /// - Parameters: /// - label: The main label text of the switch. /// - isOn: A property that determines wether the indicator is ticked (selected) or not (unselected) - /// - helper: An additonal helper text, should not be empty + /// - description: An description, should not be empty /// - icon: An optional icon /// - flipIcon: Set to true to reverse the image (i.e. flip vertically) /// - isReversed: `true` if the switch indicator must be in trailing position,` false` otherwise. @@ -63,7 +63,7 @@ public final class OUDSSwitchItemViewController: UIViewController { /// - hasDivider: If `true` a divider is added at the bottom of the view init(label: String, isOn: Bool, - helper: String?, + description: String?, icon: Image?, flipIcon: Bool, isReversed: Bool, @@ -73,7 +73,7 @@ public final class OUDSSwitchItemViewController: UIViewController { { switchItemViewModel = OUDSSwitchItemViewModel(label: label, isOn: isOn, - helper: helper, + description: description, icon: icon, flipIcon: flipIcon, isReversed: isReversed, @@ -176,7 +176,7 @@ struct OUDSSwitchItemWrapper: View { var body: some View { OUDSSwitchItem(model.label, isOn: switchItemBinding, - helper: model.helper, + description: model.description, icon: model.icon, flipIcon: model.flipIcon, isReversed: model.isReversed, @@ -196,8 +196,8 @@ struct OUDSSwitchItemWrapper: View { var label: String /// For `OUDSSwitchItem/isOn` @Published var isOn: Bool - /// For `OUDSSwitchItem/helper` - var helper: String? + /// For `OUDSSwitchItem/description` + var description: String? /// For `OUDSSwitchItem/icon` var icon: Image? /// For `OUDSSwitchItem/flipIcon` @@ -215,7 +215,7 @@ struct OUDSSwitchItemWrapper: View { init(label: String, isOn: Bool, - helper: String?, + description: String?, icon: Image?, flipIcon: Bool, isReversed: Bool, @@ -225,7 +225,7 @@ struct OUDSSwitchItemWrapper: View { { self.label = label self.isOn = isOn - self.helper = helper + self.description = description self.icon = icon self.flipIcon = flipIcon self.isReversed = isReversed @@ -254,7 +254,7 @@ extension OUDSUIKitBrige { /// // Then create the OUDSSwitchItem inside your UIViewController /// OUDSUIKit.createSwitchItem(isOn: true, /// label: "An awesome label" - /// helper: "A smaller text", + /// description: "A smaller text", /// icon: Image("ic_heart"), /// action: #selector(switchChanged(_:))) /// ``` @@ -264,7 +264,7 @@ extension OUDSUIKitBrige { /// - Parameters: /// - label: The main label text of the switch. /// - isOn: A flag indicating wether or not the switch is selected - /// - helper: An additonal helper text, should not be empty, default set to `nil` + /// - description: A description, should not be empty, default set to `nil` /// - icon: An optional icon, default set to `nil` /// - flipIcon: Default set to `false`, set to `true` to reverse the image (i.e. flip vertically) /// - isReversed: `true` if the switch indicator must be in trailing position,` false` otherwise. Default to `false` @@ -275,7 +275,7 @@ extension OUDSUIKitBrige { /// - action: The `Selector` called when the switch value changed @MainActor public static func createSwitchItem(_ label: String, isOn: Bool, - helper: String? = nil, + description: String? = nil, icon: Image? = nil, flipIcon: Bool = false, isReversed: Bool = false, @@ -289,7 +289,7 @@ extension OUDSUIKitBrige { let uikitSwitchItemViewController = OUDSSwitchItemViewController(label: label, isOn: isOn, - helper: helper, + description: description, icon: icon, flipIcon: flipIcon, isReversed: isReversed, diff --git a/OUDS/Core/Themes/Wireframe/Sources/Resources/Icons.xcassets/Components/Radio/ic_radio-button_selected.imageset/Contents.json b/OUDS/Core/Themes/Wireframe/Sources/Resources/Icons.xcassets/Components/Radio/ic_radio-button_selected.imageset/Contents.json index 76ec105dec6..47bee286dd6 100644 --- a/OUDS/Core/Themes/Wireframe/Sources/Resources/Icons.xcassets/Components/Radio/ic_radio-button_selected.imageset/Contents.json +++ b/OUDS/Core/Themes/Wireframe/Sources/Resources/Icons.xcassets/Components/Radio/ic_radio-button_selected.imageset/Contents.json @@ -1,7 +1,7 @@ { "images" : [ { - "filename" : "ic_radio-button_selected.svg", + "filename" : "ic_radio-button-selected.svg", "idiom" : "universal" } ], diff --git a/OUDS/Core/Themes/Wireframe/Sources/Resources/Icons.xcassets/Components/Radio/ic_radio-button_selected.imageset/ic_radio-button-selected.svg b/OUDS/Core/Themes/Wireframe/Sources/Resources/Icons.xcassets/Components/Radio/ic_radio-button_selected.imageset/ic_radio-button-selected.svg new file mode 100644 index 00000000000..0a284925838 --- /dev/null +++ b/OUDS/Core/Themes/Wireframe/Sources/Resources/Icons.xcassets/Components/Radio/ic_radio-button_selected.imageset/ic_radio-button-selected.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/OUDS/Core/Themes/Wireframe/Sources/Resources/Icons.xcassets/Components/Radio/ic_radio-button_selected.imageset/ic_radio-button_selected.svg b/OUDS/Core/Themes/Wireframe/Sources/Resources/Icons.xcassets/Components/Radio/ic_radio-button_selected.imageset/ic_radio-button_selected.svg deleted file mode 100644 index d2adb889598..00000000000 --- a/OUDS/Core/Themes/Wireframe/Sources/Resources/Icons.xcassets/Components/Radio/ic_radio-button_selected.imageset/ic_radio-button_selected.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/OUDS/Core/ThemesContract/Sources/TokenatorConstants.swift b/OUDS/Core/ThemesContract/Sources/TokenatorConstants.swift index ae45d443bbb..cdc18587be4 100644 --- a/OUDS/Core/ThemesContract/Sources/TokenatorConstants.swift +++ b/OUDS/Core/ThemesContract/Sources/TokenatorConstants.swift @@ -54,13 +54,13 @@ public enum OUDSVersions { // MARK: - Components versions - Control /// Version of the Figma specifications for the component checkbox - public static let componentCheckboxVersion = "2.3.0" // NOTE: Manualy changed as v2.4.0 not implemented yet + public static let componentCheckboxVersion = "2.4.0" /// Version of the Figma specifications for the component chip public static let componentChipVersion = "1.3.0" /// Version of the Figma specifications for the component radio button - public static let componentRadioButtonVersion = "1.3.0" // NOTE: Manualy changed as v2.4.0 not implemented yet + public static let componentRadioButtonVersion = "1.4.0" /// Version of the Figma specifications for the component switch - public static let componentSwitchVersion = "1.4.0" // NOTE: Manualy changed as v2.4.0 not implemented yet + public static let componentSwitchVersion = "1.5.0" /// Version of the Figma specifications for the component text input public static let componentTextInputVersion = "1.3.0" /// Version of the Figma specifications for the component phone number input