Skip to content

Commit 3f1a58f

Browse files
committed
feat(alert-message): add bullet list and update doc
1 parent 793fc85 commit 3f1a58f

6 files changed

Lines changed: 89 additions & 30 deletions

File tree

app/lib/ui/components/alert/alert_message/alert_enum.dart

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,16 @@ import 'package:flutter/material.dart';
1515
import 'package:ouds_flutter_demo/l10n/app_localizations.dart';
1616
import 'package:ouds_flutter_demo/ui/utilities/global_enum.dart';
1717

18+
/// Position of an action link inside an alert message.
19+
///
20+
/// Used to place the actionable link either below the alert content
21+
/// (typically for narrow layouts / multi-line text) or at the top-end
22+
/// corner of the alert
1823
enum ActionLinkPositionEnum {
24+
/// The link is displayed at the bottom of the alert message.
1925
bottom,
26+
27+
/// The link is displayed at the top-end corner of the alert message.
2028
topEnd;
2129

2230
static String enumName(BuildContext context) {
@@ -26,6 +34,7 @@ enum ActionLinkPositionEnum {
2634
}
2735
}
2836

37+
/// Extension providing user-facing string representations for [ActionLinkPositionEnum].
2938
extension CustomElementAppearance on ActionLinkPositionEnum {
3039
String stringValue(BuildContext context) {
3140
switch (this) {

app/lib/ui/components/alert/alert_message/alert_message_customization.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,15 @@ class AlertMessageCustomization extends StatefulWidget {
3636
AlertMessageCustomizationState createState() =>
3737
AlertMessageCustomizationState();
3838

39+
/// The state from the closest instance of this class that encloses the given context.
3940
static AlertMessageCustomizationState? of(BuildContext context) {
4041
return (context
4142
.dependOnInheritedWidgetOfExactType<_AlertMessageCustomization>())
4243
?.data;
4344
}
4445
}
4546

47+
/// State for [AlertMessageCustomization].
4648
class AlertMessageCustomizationState
4749
extends CustomizationWidgetState<AlertMessageCustomization> {
4850
late final StatusState statusState;

app/lib/ui/components/alert/alert_message/alert_message_customization_utils.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import 'package:ouds_flutter_demo/ui/utilities/app_assets.dart';
2020
import 'package:ouds_flutter_demo/ui/utilities/component/status_enum.dart';
2121
import 'package:ouds_theme_contract/ouds_theme.dart';
2222

23+
/// Utility class for `AlertMessageCustomization`.
2324
class AlertMessageCustomizationUtils {
2425
/// Returns the background color based on the alert_message message status.
2526
static Color getStatusColor(
@@ -46,6 +47,7 @@ class AlertMessageCustomizationUtils {
4647
}
4748
}
4849

50+
/// Returns the icon status based on the alert_message message status.
4951
static OudsIconStatus getIconStatus(
5052
BuildContext context,
5153
AlertMessageCustomizationState customizationState,

app/lib/ui/components/alert/alert_message/alert_message_demo_screen.dart

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import 'package:ouds_theme_contract/ouds_component_version.dart';
3535
import 'package:ouds_theme_contract/ouds_theme.dart';
3636
import 'package:provider/provider.dart';
3737

38+
/// Screen for the [OudsAlertMessage] component demo.
3839
class AlertMessageDemoScreen extends StatefulWidget {
3940
final String? previousPageTitle;
4041

@@ -88,7 +89,7 @@ class _AlertMessageDemoScreenState extends State<AlertMessageDemoScreen> {
8889
}
8990
}
9091

91-
/// This widget represents the body of the screen where the alert_message demo and code will be displayed
92+
/// Body of the screen, displaying the component demo and description.
9293
class _Body extends StatelessWidget {
9394
@override
9495
Widget build(BuildContext context) {
@@ -117,10 +118,7 @@ class _Body extends StatelessWidget {
117118
}
118119
}
119120

120-
/// This widget is now a StatefulWidget for the alert_message demo.
121-
///
122-
/// Component [AlertMessageDemo] demonstrates the behavior and functionality of an alert_message.
123-
121+
/// Widget that displays the [OudsAlertMessage] with the current customizations.
124122
class _AlertMessageDemo extends StatefulWidget {
125123
@override
126124
State<_AlertMessageDemo> createState() => _AlertMessageDemoState();
@@ -160,6 +158,7 @@ class _AlertMessageDemoState extends State<_AlertMessageDemo> {
160158
}
161159
}
162160

161+
/// Content of the bottom sheet with customization controls.
163162
class _CustomizationContent extends StatefulWidget {
164163
const _CustomizationContent();
165164

ouds_core/lib/components/alert_message/internal/ouds_alert_message_status.dart

Lines changed: 0 additions & 1 deletion
This file was deleted.

ouds_core/lib/components/alert_message/ouds_alert_message.dart

Lines changed: 72 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,19 @@
1+
/*
2+
* // Software Name: OUDS Flutter
3+
* // SPDX-FileCopyrightText: Copyright (c) Orange SA
4+
* // SPDX-License-Identifier: MIT
5+
* //
6+
* // This software is distributed under the MIT license,
7+
* // the text of which is available at https://opensource.org/license/MIT/
8+
* // or see the "LICENSE" file for more details.
9+
* //
10+
* // Software description: Flutter library of reusable graphical components
11+
* //
12+
*/
13+
14+
/// {@category Alert}
15+
library;
16+
117
import 'package:flutter/material.dart';
218
import 'package:flutter_svg/svg.dart';
319
import 'package:ouds_core/components/alert_message/internal/ouds_alert_message_border_modifier.dart';
@@ -9,31 +25,38 @@ import 'package:ouds_core/components/link/ouds_link.dart';
925
import 'package:ouds_core/components/utilities/app_assets.dart';
1026
import 'package:ouds_theme_contract/ouds_theme.dart';
1127

12-
/// The position of an [OudsAlertMessageActionLink] in the alert_message message.
28+
/// Defines the position of an [OudsAlertMessageActionLink] within the alert message.
1329
enum OudsAlertMessageActionLinkPosition {
14-
///The link is displayed at the bottom of the alert_message message below the main message content.
15-
/// Recommended for mobile or narrow layouts, or when the text spans multiple lines.
16-
/// This vertical structure improves clarity and ensures the action remains
17-
/// visible after the message is read.
30+
/// The link is displayed at the bottom of the alert message, below the main content.
31+
///
32+
/// This is recommended for mobile or narrow layouts, or when the text spans multiple lines.
33+
/// This vertical structure improves clarity and ensures the action remains visible.
1834
bottom,
1935

20-
/// The link is displayed at the top-end corner of the alert_message message.
21-
/// Best suited for wider layouts or short, single-line alerts where horizontal
22-
/// alignment keeps content compact and balanced.
36+
/// The link is displayed at the top-end corner of the alert message.
37+
///
38+
/// This is best suited for wider layouts or short, single-line alerts where
39+
/// horizontal alignment keeps the content compact and balanced.
2340
topEnd,
2441
}
2542

26-
/// Represents an action that can be taken on an alert_message message.
43+
/// Represents a clickable action within an [OudsAlertMessage].
2744
class OudsAlertMessageActionLink {
28-
/// The text label for the action.
45+
/// The text label for the action link.
2946
final String text;
3047

31-
/// The callback to be invoked when the action is pressed.
48+
/// The callback to be invoked when the action link is pressed.
3249
final VoidCallback? onClick;
3350

34-
/// The position of the link within the alert_message message.
51+
/// The position of the link within the alert message.
52+
/// Defaults to [OudsAlertMessageActionLinkPosition.bottom].
3553
OudsAlertMessageActionLinkPosition position;
3654

55+
/// Creates a new action link for an [OudsAlertMessage].
56+
///
57+
/// - [text]: The label for the action.
58+
/// - [onClick]: The callback to execute when pressed.
59+
/// - [position]: The position of the link, defaults to `bottom`.
3760
OudsAlertMessageActionLink({
3861
required this.text,
3962
required this.onClick,
@@ -72,8 +95,7 @@ class OudsAlertMessageActionLink {
7295
/// Add this list when you need to highlight multiple points, such as service features, plan details, or next steps. Each bullet should be short and written
7396
/// as a clear phrase or fragment — avoid long sentences or complex structures.
7497
///
75-
///
76-
/// ## Usage examples:
98+
/// ## Usage Example:
7799
///
78100
/// ```dart
79101
/// OudsAlertMessage(
@@ -84,6 +106,7 @@ class OudsAlertMessageActionLink {
84106
/// ```
85107
///
86108
class OudsAlertMessage extends StatefulWidget {
109+
/// Creates an OudsAlertMessage.
87110
const OudsAlertMessage({
88111
super.key,
89112
required this.label,
@@ -94,16 +117,22 @@ class OudsAlertMessage extends StatefulWidget {
94117
this.bulletList,
95118
});
96119

120+
/// The main message displayed in the alert.
97121
final String label;
98122

123+
/// Optional supplementary text providing more detail.
99124
final String? description;
100125

126+
/// The status of the alert, which determines its background color and icon.
101127
final OudsIconStatus? status;
102128

129+
/// A callback invoked when the close button is clicked. If `null`, the close button is not shown.
103130
final VoidCallback? onClose;
104131

132+
/// An optional clickable link to trigger an action.
105133
final OudsAlertMessageActionLink? actionLink;
106134

135+
/// An optional list of bullet points to display below the main content.
107136
final List<String>? bulletList;
108137

109138
@override
@@ -113,10 +142,12 @@ class OudsAlertMessage extends StatefulWidget {
113142
class _OudsAlertMessageState extends State<OudsAlertMessage> {
114143
@override
115144
Widget build(BuildContext context) {
145+
// Retrieve theme and component-specific tokens and modifiers.
116146
final theme = OudsTheme.of(context);
117147
final alertMessageStatusModifier = OudsAlertMessageStatusModifier(context);
118148
final alertTokens = OudsTheme.of(context).componentsTokens(context).alert;
119149

150+
// Build the action link widget if provided.
120151
final actionLink = widget.actionLink != null
121152
? OudsLink(
122153
label: widget.actionLink!.text,
@@ -126,6 +157,8 @@ class _OudsAlertMessageState extends State<OudsAlertMessage> {
126157
)
127158
: null;
128159

160+
// Build the main text content of the alert, including label, description,
161+
// bullet list, and a bottom-positioned action link.
129162
final textContent = Padding(
130163
padding: EdgeInsetsDirectional.only(
131164
bottom: alertTokens.spacePaddingBlock,
@@ -134,6 +167,7 @@ class _OudsAlertMessageState extends State<OudsAlertMessage> {
134167
crossAxisAlignment: CrossAxisAlignment.start,
135168
mainAxisAlignment: MainAxisAlignment.center,
136169
children: [
170+
// Main label text.
137171
Text(
138172
widget.label,
139173
style: theme.typographyTokens
@@ -144,6 +178,7 @@ class _OudsAlertMessageState extends State<OudsAlertMessage> {
144178
),
145179
),
146180
),
181+
// Optional description text.
147182
if (widget.description != null && widget.description!.isNotEmpty) ...[
148183
SizedBox(height: alertTokens.spaceRowGap),
149184
Text(
@@ -157,12 +192,15 @@ class _OudsAlertMessageState extends State<OudsAlertMessage> {
157192
),
158193
),
159194
],
195+
// Optional bullet list. A gap is added only if the list is not empty.
160196
if (widget.bulletList != null &&
161197
widget.bulletList!.any((bullet) => bullet.isNotEmpty))
162198
SizedBox(height: alertTokens.spaceRowGap),
199+
// Generate bullet list items, filtering out any empty strings.
163200
...?widget.bulletList
164201
?.where((bullet) => bullet.isNotEmpty)
165202
.map((bullet) => buildBulletList(context, widget.status, bullet)),
203+
// Optional action link positioned at the bottom.
166204
if (widget.actionLink != null &&
167205
widget.actionLink!.text.isNotEmpty &&
168206
widget.actionLink!.position ==
@@ -174,6 +212,7 @@ class _OudsAlertMessageState extends State<OudsAlertMessage> {
174212
),
175213
);
176214

215+
// Build the close button if a callback is provided.
177216
final closeButton = widget.onClose != null
178217
? OudsButton(
179218
icon: AppAssets.icons.componentButtonExpurge,
@@ -183,17 +222,19 @@ class _OudsAlertMessageState extends State<OudsAlertMessage> {
183222
)
184223
: null;
185224

225+
// Determine if a custom icon is provided for Neutral or Accent statuses.
186226
final hasIcon = switch (widget.status) {
187227
Neutral(icon: final assets) => assets,
188228
Accent(icon: final assets) => assets,
189229
_ => null,
190230
};
191231

232+
// Assemble the final alert content layout.
192233
Widget alertContent;
193-
194234
alertContent = Row(
195235
crossAxisAlignment: CrossAxisAlignment.start,
196236
children: [
237+
// Display custom icon for Neutral/Accent statuses if available.
197238
if ((widget.status is Neutral || widget.status is Accent) &&
198239
hasIcon != null &&
199240
hasIcon.isNotEmpty) ...[
@@ -215,6 +256,7 @@ class _OudsAlertMessageState extends State<OudsAlertMessage> {
215256
),
216257
SizedBox(width: alertTokens.spaceColumnGap),
217258
],
259+
// Display functional status icon for other statuses.
218260
if (widget.status is! Neutral && widget.status is! Accent) ...[
219261
Padding(
220262
padding: EdgeInsetsDirectional.only(
@@ -224,6 +266,7 @@ class _OudsAlertMessageState extends State<OudsAlertMessage> {
224266
),
225267
SizedBox(width: alertTokens.spaceColumnGap),
226268
],
269+
// Main text content area.
227270
Expanded(
228271
child: Padding(
229272
padding: EdgeInsetsDirectional.only(
@@ -233,6 +276,7 @@ class _OudsAlertMessageState extends State<OudsAlertMessage> {
233276
child: textContent,
234277
),
235278
),
279+
// Optional action link positioned at the top-end.
236280
if (widget.actionLink != null &&
237281
widget.actionLink!.text.isNotEmpty &&
238282
widget.actionLink!.position ==
@@ -246,12 +290,14 @@ class _OudsAlertMessageState extends State<OudsAlertMessage> {
246290
child: actionLink,
247291
),
248292
],
293+
// Optional close button.
249294
if (closeButton != null) ...[closeButton],
250295
],
251296
);
252297

298+
// Wrap the entire component in a Semantics widget for accessibility
299+
// and a decorated Container for styling.
253300
return Semantics(
254-
label: '${widget.label}. ${widget.description ?? ''}',
255301
container: true,
256302
child: Container(
257303
constraints: BoxConstraints(
@@ -274,13 +320,15 @@ class _OudsAlertMessageState extends State<OudsAlertMessage> {
274320
);
275321
}
276322

323+
/// Builds the status icon for the alert message.
324+
///
325+
/// For [Warning] status, it stacks two shapes to create the icon.
326+
/// For other statuses, it returns a single SVG icon.
277327
Widget buildStatusIcon(BuildContext context, OudsIconStatus? status) {
278328
final statusModifier = OudsAlertMessageStatusModifier(context);
279-
//get the asset name from status for neutral and accent status (icon defined by user)
280329
final nonFunctionalIcon = statusModifier.getAssetsName(status);
281330
final functionalIcon = statusModifier.getStatusIcon(status);
282331
final alertTokens = OudsTheme.of(context).componentsTokens(context).alert;
283-
//final iconColorTokens = OudsTheme.of(context).componentsTokens(context).icon; //todo change it when PR token is merged
284332

285333
if (status is Warning) {
286334
return Stack(
@@ -305,9 +353,7 @@ class _OudsAlertMessageState extends State<OudsAlertMessage> {
305353
width: alertTokens.sizeIcon,
306354
height: alertTokens.sizeIcon,
307355
fit: BoxFit.contain,
308-
AppAssets
309-
.icons
310-
.componentAlertWarningInternalShape, // Path to your internal shape SVG
356+
AppAssets.icons.componentAlertWarningInternalShape,
311357
colorFilter: ColorFilter.mode(
312358
Color(0xFF856A00), //todo change it when PR token is merged
313359
BlendMode.srcIn, // Blend mode to apply the tint
@@ -333,21 +379,23 @@ class _OudsAlertMessageState extends State<OudsAlertMessage> {
333379
);
334380
}
335381

382+
/// Builds a single bullet list item for the alert message.
383+
///
384+
/// This widget creates a row containing a bullet icon and a text label,
385+
/// styled according to the alert's status and theme tokens.
336386
Widget buildBulletList(
337387
BuildContext context,
338388
OudsIconStatus? status,
339389
String label,
340390
) {
341391
final theme = OudsTheme.of(context);
342392
final alertMessageStatusModifier = OudsAlertMessageStatusModifier(context);
343-
final alertTokens = theme.componentsTokens(context).alert;
344393
final maxTextWidth = theme.sizeScheme(context).maxWidthTypeBodyMedium;
345394
final textScaler = MediaQuery.textScalerOf(context);
346395
final double iconContainerWidth = textScaler.scale(
347396
theme.sizeScheme(context).iconWithLabelMediumSizeMedium,
348397
);
349398

350-
// Calculate line height for the text to constrain the icon container height
351399
final TextStyle textStyle = theme.typographyTokens
352400
.typeLabelDefaultMedium(context)
353401
.copyWith(color: alertMessageStatusModifier.getStatusTextColor(status));
@@ -382,7 +430,7 @@ class _OudsAlertMessageState extends State<OudsAlertMessage> {
382430
),
383431
),
384432
),
385-
SizedBox(width: 8), //todo bullet list tokens
433+
SizedBox(width: 8), //TODO: Use bullet list tokens when available
386434
Flexible(
387435
child: ConstrainedBox(
388436
constraints: BoxConstraints(maxWidth: maxTextWidth),

0 commit comments

Comments
 (0)