Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions app/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- [DemoApp][Library] For `control item` components, add edge to edge configuration ([#880](https://github.com/Orange-OpenSource/ouds-flutter/issues/880))
- [DemoApp][Library] Create component - `List item` ([#54](https://github.com/Orange-OpenSource/ouds-flutter/issues/54))
### Changed
- [Library] for `Alert Message` component, Add default vocalisation on "info" status ([#806](https://github.com/Orange-OpenSource/ouds-flutter/issues/806))
- [Library] For `Alert Message` component, update to v1.2.0 ([#878](https://github.com/Orange-OpenSource/ouds-flutter/issues/878))
- [Library] For `navigation button` component, update to v3.4.0 ([#870](https://github.com/Orange-OpenSource/ouds-flutter/issues/870))
- [Library] For `button` component, update to version 3.4.0 ([#869](https://github.com/Orange-OpenSource/ouds-flutter/issues/869))
- [Library] update tokens 2.7.0 ([943](https://github.com/Orange-OpenSource/ouds-flutter/issues/943))
Expand All @@ -28,6 +30,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- [DemoApp][Library] For `button` component, update to version 3.3.0 ([#832](https://github.com/Orange-OpenSource/ouds-flutter/issues/832))
- [DemoApp][Library] Update `progress-indicator`: add size parameter to OudsCircularProgressIndicator for button integration ([#876](https://github.com/Orange-OpenSource/ouds-flutter/issues/876))
### Fixed
- [Library] for `Alert Message` component, Close button is not reached with keyboard ([#811](https://github.com/Orange-OpenSource/ouds-flutter/issues/811))
- [Library] Bug A11Y `Top bar` Action button and badge are not grouped in iOS ([#816](https://github.com/Orange-OpenSource/ouds-flutter/issues/816))
- [Library] `Navigation Button` Disabled state is not vocalized ([#931](https://github.com/Orange-OpenSource/ouds-flutter/issues/931))
- [Library] fix: fix Indicator Icon with text direction `list item` to resolve ar ([#925](https://github.com/Orange-OpenSource/ouds-flutter/issues/925))
Expand Down
3 changes: 3 additions & 0 deletions ouds_core/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- [Library] For `control item` components, add edge to edge configuration ([#880](https://github.com/Orange-OpenSource/ouds-flutter/issues/880))
- [Library] Create component - `List item` ([#54](https://github.com/Orange-OpenSource/ouds-flutter/issues/54))
### Changed
- [Library] for `Alert Message` component, Add default vocalisation on "info" status ([#806](https://github.com/Orange-OpenSource/ouds-flutter/issues/806))
- [Library] For `Alert Message` component, update to v1.2.0 ([#878](https://github.com/Orange-OpenSource/ouds-flutter/issues/878))
- [Library] For `navigation button` component, update to v3.4.0 ([#870](https://github.com/Orange-OpenSource/ouds-flutter/issues/870))
- [Library] For `button` component, update to version 3.4.0 ([#869](https://github.com/Orange-OpenSource/ouds-flutter/issues/869))
- [Library] update tokens 2.7.0 ([943](https://github.com/Orange-OpenSource/ouds-flutter/issues/943))
Expand All @@ -25,6 +27,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- [Library] For `button` component, update to version 3.3.0 ([#832](https://github.com/Orange-OpenSource/ouds-flutter/issues/832))
- [Library] Update `progress-indicator`: add size parameter to OudsCircularProgressIndicator for button integration ([#876](https://github.com/Orange-OpenSource/ouds-flutter/issues/876))
### Fixed
- [Library] for `Alert Message` component, Close button is not reached with keyboard ([#811](https://github.com/Orange-OpenSource/ouds-flutter/issues/811))
- [Library] Bug A11Y `Top bar` Action button and badge are not grouped in iOS ([#816](https://github.com/Orange-OpenSource/ouds-flutter/issues/816))
- [Library] `Navigation Button` Disabled state is not vocalized ([#931](https://github.com/Orange-OpenSource/ouds-flutter/issues/931))
- [Library] fix: fix Indicator Icon with text direction `list item` to resolve ar ([#925](https://github.com/Orange-OpenSource/ouds-flutter/issues/925))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,23 @@ class OudsAlertStatusModifier {
return null;
}

/// Returns the Border color based on the alert message status.
Color getBorderStatusColor(OudsIconStatus? status) {
final colorTheme = OudsTheme.of(context).colorScheme(context);

if (status != null) {
return switch (status) {
Neutral() => colorTheme.borderDefault,
Accent() => colorTheme.borderStatusAccent,
Positive() => colorTheme.borderStatusPositive,
Info() => colorTheme.borderStatusInfo,
Warning() => colorTheme.borderStatusWarning,
Negative() => colorTheme.borderStatusNegative,
};
}
return colorTheme.borderDefault;
}

/// Returns the text color based on the alert message status.
Color getStatusTextColor(OudsIconStatus? status) {
final colorTheme = OudsTheme.of(context).colorScheme(context);
Expand Down
113 changes: 79 additions & 34 deletions ouds_core/lib/components/alert/ouds_alert_message.dart
Original file line number Diff line number Diff line change
Expand Up @@ -197,39 +197,61 @@ class _OudsAlertMessageState extends State<OudsAlertMessage> {
final actionLink = widget.actionLayout != null
? OudsLink(
label: widget.actionLayout!.text,
size: OudsLinkSize.defaultSize,
density:
widget.actionLayout?.layout ==
OudsAlertMessageActionLayoutEnum.trailing
? OudsLinkDensity.defaultDensity
: OudsLinkDensity.compact,
onPressed: () {
widget.actionLayout!.onClick?.call();
},
)
: null;

final maxWidthBoxedText = theme.sizeScheme(context).maxWidthBoxedText;

// Build the main text content of the alert, including label, description,
// and bullet list.
final textContentChildren = <Widget>[
// Main label text.
Text(
widget.label,
style: theme.typographyTokens
.typeLabelModerateLarge(context)
.copyWith(
color: alertMessageStatusModifier.getStatusTextColor(
widget.status,
ConstrainedBox(
constraints: BoxConstraints(maxWidth: maxWidthBoxedText),
child: Text(
widget.label,
style: theme.typographyTokens
.typeLabelModerateLarge(context)
.copyWith(
color: alertMessageStatusModifier.getStatusTextColor(
widget.status,
),
),
),
),
),
// Optional description text.
if (widget.description != null && widget.description!.isNotEmpty) ...[
SizedBox(height: alertTokens.spaceRowGap),
_buildDescription(context),
SizedBox(height: alertTokens.spacePaddingBlockBottomContent),
],
// Optional bullet list. A gap is added only if the list is not empty.
if (widget.bulletList != null &&
widget.bulletList!.any((bullet) => bullet.isNotEmpty))
widget.bulletList!.any((bullet) => bullet.isNotEmpty)) ...[
SizedBox(height: alertTokens.spaceRowGap),
// Generate bullet list items, filtering out any empty strings.
...?widget.bulletList
?.where((bullet) => bullet.isNotEmpty)
.map((bullet) => buildBulletList(context, widget.status, bullet)),
// Generate bullet list items, filtering out any empty strings, and
// insert a gap between items only (not after the last one).
for (final entry
in widget.bulletList!
.where((bullet) => bullet.isNotEmpty)
.toList()
.asMap()
.entries) ...[
if (entry.key > 0)
SizedBox(height: alertMessageTokens.spaceRowGapBullet),
buildBulletList(context, widget.status, entry.value),
],
SizedBox(height: alertTokens.spacePaddingBlockBottomContent),
],
];

// Build the close button if a callback is provided.
Expand All @@ -247,6 +269,18 @@ class _OudsAlertMessageState extends State<OudsAlertMessage> {
// Determine if a custom icon is provided for Neutral or Accent statuses.
final nonFunctionalIcon = widget.status?.nonFunctionalIcon;

bool isTrailingActionLink =
widget.actionLayout != null &&
widget.actionLayout!.text.isNotEmpty &&
widget.actionLayout!.layout ==
OudsAlertMessageActionLayoutEnum.trailing;

bool isBottomActionLink =
actionLink != null &&
widget.actionLayout != null &&
widget.actionLayout!.text.isNotEmpty &&
widget.actionLayout!.layout == OudsAlertMessageActionLayoutEnum.bottom;

// Assemble the final alert content layout.
Widget alertContent;
alertContent = Semantics(
Expand All @@ -265,6 +299,7 @@ class _OudsAlertMessageState extends State<OudsAlertMessage> {
Padding(
padding: EdgeInsetsDirectional.only(
top: alertTokens.spacePaddingBlock,
bottom: alertTokens.spacePaddingBlock,
),
child: Container(
color: widget.status?.getBackgroundColor,
Expand Down Expand Up @@ -297,6 +332,7 @@ class _OudsAlertMessageState extends State<OudsAlertMessage> {
Padding(
padding: EdgeInsetsDirectional.only(
top: alertTokens.spacePaddingBlock,
bottom: alertTokens.spacePaddingBlock,
),
child: alertMessageStatusModifier.buildStatusIcon(
context,
Expand All @@ -311,7 +347,6 @@ class _OudsAlertMessageState extends State<OudsAlertMessage> {
child: Padding(
padding: EdgeInsetsDirectional.only(
top: alertTokens.spacePaddingBlock,
end: alertTokens.spaceColumnGap,
bottom: alertTokens.spacePaddingBlock,
),
child: Column(
Expand All @@ -329,19 +364,18 @@ class _OudsAlertMessageState extends State<OudsAlertMessage> {
? OudsLocalizations.of(
context,
)?.core_common_error_a11y
: widget.status is Info
? OudsLocalizations.of(
context,
)?.core_common_info_a11y
: null,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: textContentChildren,
),
),
// Optional action link positioned at the bottom.
if (actionLink != null &&
widget.actionLayout != null &&
widget.actionLayout!.text.isNotEmpty &&
widget.actionLayout!.layout ==
OudsAlertMessageActionLayoutEnum.bottom) ...[
SizedBox(height: alertTokens.spaceRowGapAction),
if (isBottomActionLink) ...[
Semantics(
sortKey: const OrdinalSortKey(2.0),
container: true,
Expand All @@ -356,10 +390,8 @@ class _OudsAlertMessageState extends State<OudsAlertMessage> {
),
),
// Optional action link positioned at the top-end.
if (widget.actionLayout != null &&
widget.actionLayout!.text.isNotEmpty &&
widget.actionLayout!.layout ==
OudsAlertMessageActionLayoutEnum.trailing) ...[
if (isTrailingActionLink) ...[
SizedBox(width: alertTokens.spaceColumnGap),
Semantics(
sortKey: const OrdinalSortKey(2.0),
container: true,
Expand All @@ -373,10 +405,15 @@ class _OudsAlertMessageState extends State<OudsAlertMessage> {
],
// Optional close button.
if (closeButton != null) ...[
SizedBox(
width: isTrailingActionLink ? 0 : alertTokens.spaceColumnGap,
),
Semantics(
sortKey: const OrdinalSortKey(3.0),
button: true,
container: true,
// it's used for iOS Keyboard , for Voice Over it can reached by Button/Label
focusable: true,
label: l10n?.core_alert_alertMessage_close_label_a11y,
child: closeButton,
),
Expand All @@ -394,11 +431,15 @@ class _OudsAlertMessageState extends State<OudsAlertMessage> {
),
padding: EdgeInsetsDirectional.only(
start: alertTokens.spacePaddingInline,
end: isTrailingActionLink == false && closeButton == null
? alertTokens.spacePaddingInline
: 0,
),
decoration: BoxDecoration(
border: OudsBorder().borderAll(
width: alertMessageTokens.borderWidth,
color: Colors.transparent,
color: alertMessageStatusModifier.getBorderStatusColor(widget.status),
strokeAlign: BorderSide.strokeAlignInside,
),
borderRadius: OudsAlertMessageBorderModifier.getBorderRadius(context),
color: alertMessageStatusModifier.getStatusColor(widget.status),
Expand All @@ -411,19 +452,22 @@ class _OudsAlertMessageState extends State<OudsAlertMessage> {
Widget _buildDescription(BuildContext context) {
final theme = OudsTheme.of(context);
final alertMessageStatusModifier = OudsAlertStatusModifier(context);

final textStyle = theme.typographyTokens
.typeLabelDefaultMedium(context)
.copyWith(
color: alertMessageStatusModifier.getStatusTextColor(widget.status),
);

return Text.rich(
MarkdownSpanBuilder.buildRichText(
context,
widget.description ?? '',
baseStyle: textStyle,
onLinkTap: widget.onDescriptionLinkTapped,
final maxWidthBoxedText = theme.sizeScheme(context).maxWidthBoxedText;
return ConstrainedBox(
constraints: BoxConstraints(maxWidth: maxWidthBoxedText),
child: Text.rich(
MarkdownSpanBuilder.buildRichText(
context,
widget.description ?? '',
baseStyle: textStyle,
onLinkTap: widget.onDescriptionLinkTapped,
),
),
);
}
Expand All @@ -439,7 +483,6 @@ class _OudsAlertMessageState extends State<OudsAlertMessage> {
) {
final theme = OudsTheme.of(context);
final alertMessageStatusModifier = OudsAlertStatusModifier(context);
final maxTextWidth = theme.sizeScheme(context).maxWidthLabelMedium;
final textScaler = MediaQuery.textScalerOf(context);
final double iconContainerWidth = textScaler.scale(
theme.sizeScheme(context).iconWithLabelMediumSizeMedium,
Expand All @@ -456,6 +499,8 @@ class _OudsAlertMessageState extends State<OudsAlertMessage> {
theme.sizeScheme(context).iconWithLabelMediumSizeSmall,
);

final maxWidthBoxedText = theme.sizeScheme(context).maxWidthBoxedText;

return IntrinsicHeight(
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
Expand Down Expand Up @@ -487,7 +532,7 @@ class _OudsAlertMessageState extends State<OudsAlertMessage> {
),
Flexible(
child: ConstrainedBox(
constraints: BoxConstraints(maxWidth: maxTextWidth),
constraints: BoxConstraints(maxWidth: maxWidthBoxedText),
child: Text.rich(
MarkdownSpanBuilder.buildBoldOnly(label, baseStyle: textStyle),
),
Expand Down
2 changes: 2 additions & 0 deletions ouds_core/lib/components/alert/ouds_inline_alert.dart
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ class _OudsInlineAlertState extends State<OudsInlineAlert> {
? OudsLocalizations.of(context)?.core_common_warning_a11y
: widget.status is Negative
? OudsLocalizations.of(context)?.core_common_error_a11y
: widget.status is Info
? OudsLocalizations.of(context)?.core_common_info_a11y
: null,
container: true,
child: Row(
Expand Down
6 changes: 5 additions & 1 deletion ouds_core/lib/components/common/OudsBorder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -42,22 +42,26 @@ class OudsBorder {
/// - [width]: The width of the border. If this value is considered "Hairline," no border is created.
/// - [color]: The color of the border.
/// - [style]: The style of the border (default: BorderStyle.solid).
/// - [strokeAlign]: Where the border is painted relative to the box's edge.
/// Defaults to [BorderSide.strokeAlignInside] to match Figma's "Inside" stroke
/// position, which is the alignment used across OUDS components.
///
/// Returns an instance of [Border] if the width is not "Hairline"; otherwise, returns null.
///
Border? borderAll({
required double width,
required Color color,
BorderStyle? style,
double strokeAlign = BorderSide.strokeAlignInside,
}) {

final widthNone = width.takeUnlessHairline();
if (widthNone == null) return null;

return Border.all(
color: color,
width: width,
style: style ?? BorderStyle.solid,
strokeAlign: strokeAlign,
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,12 @@ class OudsSizeScheme {
tablet: sizeTokens.maxWidthLabelXlargeTablet,
);

double get maxWidthBoxedText => OudsWindowSizeClassUtil.selectMobileTablet(
sizeClass: sizeClass,
mobile: sizeTokens.maxWidthBoxedTextMobile,
tablet: sizeTokens.maxWidthBoxedTextTablet,
);

/// Non-responsive tokens (direct mapping)

/// iconDecorative
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ abstract class OudsSizeSemanticTokens {
late double maxWidthBodyMediumTablet;
late double maxWidthBodySmallMobile;
late double maxWidthBodySmallTablet;
late double maxWidthBoxedTextMobile;
late double maxWidthBoxedTextTablet;
late double maxWidthDisplayLargeMobile;
late double maxWidthDisplayLargeTablet;
late double maxWidthDisplayMediumMobile;
Expand Down
Loading
Loading