diff --git a/example/lib/pages/components/chat_item_example.dart b/example/lib/pages/components/chat_item_example.dart index 8c76d190..81263db7 100644 --- a/example/lib/pages/components/chat_item_example.dart +++ b/example/lib/pages/components/chat_item_example.dart @@ -3,7 +3,7 @@ import 'package:zeta_example/widgets.dart'; import 'package:zeta_flutter/zeta_flutter.dart'; class ChatItemExample extends StatelessWidget { - static const String name = 'Chat Item'; + static const String name = 'ChatItem'; const ChatItemExample({Key? key}) : super(key: key); @@ -14,6 +14,31 @@ class ChatItemExample extends StatelessWidget { child: SingleChildScrollView( child: Column( children: [ + ZetaChatItem( + leading: const ZetaAvatar(initials: 'AZ'), + slidableActions: [ + ZetaSlidableAction( + onPressed: () {}, + paleColor: true, + icon: Icons.star, + ), + ZetaSlidableAction( + onPressed: () {}, + paleColor: true, + icon: Icons.delete, + ), + ZetaSlidableAction( + onPressed: () {}, + icon: Icons.call, + ), + ZetaSlidableAction( + onPressed: () {}, + icon: Icons.message, + ), + ], + title: Text('title'), + subtitle: Text('subtitle'), + ), ZetaChatItem( explicitChildNodes: false, time: DateTime.now(), @@ -22,6 +47,7 @@ class ChatItemExample extends StatelessWidget { leading: const ZetaAvatar(initials: 'AZ'), count: 100, onTap: () {}, + paleButtonColors: true, slidableActions: [ ZetaSlidableAction.menuMore(onPressed: () {}), ZetaSlidableAction.call(onPressed: () {}), diff --git a/lib/src/components/chat_item/chat_item.dart b/lib/src/components/chat_item/chat_item.dart index 1bba9db3..d447b288 100644 --- a/lib/src/components/chat_item/chat_item.dart +++ b/lib/src/components/chat_item/chat_item.dart @@ -27,6 +27,7 @@ class ZetaChatItem extends ZetaStatelessWidget { this.starred, this.slidableActions = const [], this.explicitChildNodes = true, + this.paleButtonColors, @Deprecated('Use slidableActions instead.' ' This variable has been replaced as of 0.12.1') this.onMenuMoreTap, @Deprecated('Use slidableActions instead.' ' This variable has been replaced as of 0.12.1') this.onCallTap, @Deprecated('Use slidableActions instead.' ' This variable has been replaced as of 0.12.1') this.onDeleteTap, @@ -78,6 +79,11 @@ class ZetaChatItem extends ZetaStatelessWidget { /// Whether to show explicit child nodes in the semantics tree. final bool explicitChildNodes; + /// Whether to apply pale color. + /// + /// Pale buttons was the default behavior before 0.15.2, but now buttons have darker colors by default. + final bool? paleButtonColors; + /// Callback for slidable action - menu more. @Deprecated('Use slidableActions instead.' ' This variable has been replaced as of 0.12.1') final VoidCallback? onMenuMoreTap; @@ -109,8 +115,7 @@ class ZetaChatItem extends ZetaStatelessWidget { final actionWith = slidableActionsCount * ZetaSpacing.xl_10; final maxButtonWidth = actionWith / maxScreenWidth; final extend = actionWith / maxScreenWidth; - - return extend.clamp(0, maxButtonWidth); + return extend.clamp(0, maxButtonWidth).toDouble(); } Widget? get _formatLeading { @@ -126,6 +131,7 @@ class ZetaChatItem extends ZetaStatelessWidget { final actions = [...slidableActions]; + // coverage:ignore-start if (onMenuMoreTap != null) { actions.add( ZetaSlidableAction(onPressed: onMenuMoreTap, color: colors.purple, icon: ZetaIcons.more_vertical), @@ -144,6 +150,7 @@ class ZetaChatItem extends ZetaStatelessWidget { if (onDeleteTap != null) { actions.add(ZetaSlidableAction(onPressed: onDeleteTap, color: colors.red, icon: ZetaIcons.delete)); } + // coverage:ignore-end return ZetaRoundedScope( rounded: context.rounded, @@ -154,12 +161,18 @@ class ZetaChatItem extends ZetaStatelessWidget { builder: (context, constraints) { return Slidable( enabled: actions.isNotEmpty, - endActionPane: ActionPane( - extentRatio: - _getSlidableExtend(slidableActionsCount: actions.length, maxScreenWidth: constraints.maxWidth), - motion: const ScrollMotion(), - children: actions, - ), + endActionPane: actions.isEmpty + ? null + : ActionPane( + extentRatio: _getSlidableExtend( + slidableActionsCount: actions.length, + maxScreenWidth: constraints.maxWidth, + ), + motion: const ScrollMotion(), + children: paleButtonColors != null + ? actions.map((action) => action.copyWith(paleColor: paleButtonColors)).toList() + : actions, + ), child: ColoredBox( color: highlighted ? colors.blue.shade10 : colors.surfacePrimary, child: Material( @@ -329,7 +342,8 @@ class ZetaChatItem extends ZetaStatelessWidget { ..add(ObjectFlagProperty.has('onCallTap', onCallTap)) ..add(ObjectFlagProperty.has('onDeleteTap', onDeleteTap)) ..add(ObjectFlagProperty.has('onPttTap', onPttTap)) - ..add(DiagnosticsProperty('explicitChildNodes', explicitChildNodes)); + ..add(DiagnosticsProperty('explicitChildNodes', explicitChildNodes)) + ..add(DiagnosticsProperty('paleButtonColors', paleButtonColors)); } } @@ -360,21 +374,35 @@ class ZetaSlidableAction extends StatelessWidget { super.key, this.onPressed, required this.icon, - this.color, + this.color = ZetaColorBase.blue, this.customForegroundColor, this.customBackgroundColor, this.semanticLabel, + this.paleColor = false, }) : _type = _ZetaSlidableActionType.custom, assert( color != null || (customForegroundColor != null && customBackgroundColor != null), 'Ensure either color, or both customForegroundColor and customBackgroundColor are provided.', ); + const ZetaSlidableAction._({ + super.key, + this.onPressed, + required this.icon, + this.color, + this.customForegroundColor, + this.customBackgroundColor, + this.semanticLabel, + this.paleColor = false, + _ZetaSlidableActionType? type, + }) : _type = type ?? _ZetaSlidableActionType.custom; + /// Constructs a More menu [ZetaSlidableAction]. const ZetaSlidableAction.menuMore({ super.key, this.onPressed, this.semanticLabel = 'More', + this.paleColor = false, }) : icon = ZetaIcons.more_vertical, color = null, customForegroundColor = null, @@ -386,6 +414,7 @@ class ZetaSlidableAction extends StatelessWidget { super.key, this.onPressed, this.semanticLabel = 'Call', + this.paleColor = false, }) : icon = ZetaIcons.phone, color = null, customForegroundColor = null, @@ -397,6 +426,7 @@ class ZetaSlidableAction extends StatelessWidget { super.key, this.onPressed, this.semanticLabel = 'PTT', + this.paleColor = false, }) : icon = ZetaIcons.ptt, color = null, customForegroundColor = null, @@ -408,6 +438,7 @@ class ZetaSlidableAction extends StatelessWidget { super.key, this.onPressed, this.semanticLabel = 'Delete', + this.paleColor = false, }) : icon = ZetaIcons.delete, color = null, customForegroundColor = null, @@ -448,6 +479,9 @@ class ZetaSlidableAction extends StatelessWidget { /// {@macro zeta-widget-semantic-label} final String? semanticLabel; + /// Whether to apply pale color. + final bool paleColor; + @override Widget build(BuildContext context) { return Expanded( @@ -462,8 +496,9 @@ class ZetaSlidableAction extends StatelessWidget { child: IconButton( onPressed: () => onPressed?.call(), style: IconButton.styleFrom( - backgroundColor: customBackgroundColor ?? (color ?? _type.getColor(context)).shade10, - foregroundColor: customForegroundColor ?? (color ?? _type.getColor(context)).shade60, + backgroundColor: customBackgroundColor ?? (color ?? _type.getColor(context)).shade(paleColor ? 10 : 60), + foregroundColor: customForegroundColor ?? + (paleColor ? (color ?? _type.getColor(context)).shade60 : Zeta.of(context).colors.surfaceDefault), shape: const RoundedRectangleBorder(borderRadius: ZetaRadius.minimal), side: BorderSide.none, ), @@ -475,16 +510,39 @@ class ZetaSlidableAction extends StatelessWidget { ); } + /// Creates a copy of this [ZetaSlidableAction] but with the given fields replaced with the new values. + ZetaSlidableAction copyWith({ + VoidCallback? onPressed, + IconData? icon, + Color? customForegroundColor, + Color? customBackgroundColor, + ZetaColorSwatch? color, + String? semanticLabel, + bool? paleColor, + }) { + return ZetaSlidableAction._( + key: key, + onPressed: onPressed ?? this.onPressed, + icon: icon ?? this.icon, + customForegroundColor: customForegroundColor ?? this.customForegroundColor, + customBackgroundColor: customBackgroundColor ?? this.customBackgroundColor, + color: color ?? this.color, + semanticLabel: semanticLabel ?? this.semanticLabel, + paleColor: paleColor ?? this.paleColor, + type: _type, + ); + } + @override void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); properties ..add(ObjectFlagProperty.has('onPressed', onPressed)) - ..add(DiagnosticsProperty('icon', icon)) - ..add(ColorProperty('foregroundColor', customForegroundColor)) - ..add(ColorProperty('backgroundColor', customBackgroundColor)) - ..add(DiagnosticsProperty('icon', icon)) + ..add(DiagnosticsProperty('paleColor', paleColor)) + ..add(StringProperty('semanticLabel', semanticLabel)) ..add(ColorProperty('color', color)) - ..add(StringProperty('semanticLabel', semanticLabel)); + ..add(ColorProperty('customBackgroundColor', customBackgroundColor)) + ..add(ColorProperty('customForegroundColor', customForegroundColor)) + ..add(DiagnosticsProperty('icon', icon)); } } diff --git a/test/src/components/chat_item/chat_item_test.dart b/test/src/components/chat_item/chat_item_test.dart new file mode 100644 index 00000000..d2a685b5 --- /dev/null +++ b/test/src/components/chat_item/chat_item_test.dart @@ -0,0 +1,484 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:intl/intl.dart'; +import 'package:path/path.dart'; +import 'package:zeta_flutter/zeta_flutter.dart'; + +import '../../../test_utils/test_app.dart'; +import '../../../test_utils/tolerant_comparator.dart'; +import '../../../test_utils/utils.dart'; + +void main() { + setUpAll(() { + final testUri = Uri.parse(getCurrentPath('chat_item')); + goldenFileComparator = TolerantComparator(testUri, tolerance: 0.01); + }); + + group('ZetaChatItem Tests', () { + testWidgets('ZetaChatItem displays correctly', (WidgetTester tester) async { + tester.view.devicePixelRatio = 1.0; + tester.view.physicalSize = const Size(481, 480); + const title = Text('John Doe'); + const subtitle = Text('Hello, how are you?'); + final time = DateTime.now(); + final timeFormat = DateFormat('hh:mm a'); + + await tester.pumpWidget( + TestApp( + home: Scaffold( + body: Column( + children: [ + ZetaChatItem( + explicitChildNodes: false, + time: time, + enabledWarningIcon: true, + enabledNotificationIcon: true, + leading: const ZetaAvatar(initials: 'AZ'), + count: 100, + onTap: () {}, + paleButtonColors: true, + slidableActions: [ + ZetaSlidableAction.menuMore(onPressed: () {}), + ZetaSlidableAction.call(onPressed: () {}), + ZetaSlidableAction.ptt(onPressed: () {}), + ZetaSlidableAction.delete(onPressed: () {}), + ], + title: title, + subtitle: subtitle, + ), + ], + ), + ), + ), + ); + + // Verify that the title, subtitle, and time are displayed correctly + expect(find.text('John Doe'), findsOneWidget); + expect(find.text('Hello, how are you?'), findsOneWidget); + expect(find.text(timeFormat.format(time)), findsOneWidget); + + final chatItemFinder = find.byType(ZetaChatItem); + + // Verify that the widget is tappable + await tester.tap(chatItemFinder); + await tester.pumpAndSettle(); + expect(tester.takeException(), isNull); + + await expectLater( + chatItemFinder, + matchesGoldenFile(join(getCurrentPath('chat_item'), 'chat_item_default.png')), + ); + }); + }); + + testWidgets('ZetaChatItem highlighted', (WidgetTester tester) async { + tester.view.devicePixelRatio = 1.0; + tester.view.physicalSize = const Size(481, 480); + const title = Text('John Doe'); + const subtitle = Text('Hello, how are you?'); + final time = DateTime.now(); + final timeFormat = DateFormat('hh:mm a'); + + await tester.pumpWidget( + TestApp( + home: Column( + children: [ + ZetaChatItem( + explicitChildNodes: false, + time: time, + enabledWarningIcon: true, + enabledNotificationIcon: true, + leading: const ZetaAvatar(initials: 'AZ'), + count: 100, + onTap: () {}, + paleButtonColors: false, + starred: true, + slidableActions: [ + ZetaSlidableAction.menuMore(onPressed: () {}), + ZetaSlidableAction.call(onPressed: () {}), + ZetaSlidableAction.ptt(onPressed: () {}), + ZetaSlidableAction.delete(onPressed: () {}), + ], + highlighted: true, + title: title, + subtitle: subtitle, + ), + ], + ), + ), + ); + + // Verify that the title, subtitle, and time are displayed correctly + expect(find.text('John Doe'), findsOneWidget); + expect(find.text('Hello, how are you?'), findsOneWidget); + expect(find.text(timeFormat.format(time)), findsOneWidget); + + final chatItemFinder = find.byType(ZetaChatItem); + + // Verify that the widget is tappable + await tester.tap(chatItemFinder); + await tester.pumpAndSettle(); + expect(tester.takeException(), isNull); + + await expectLater( + chatItemFinder, + matchesGoldenFile(join(getCurrentPath('chat_item'), 'chat_item_highlighted.png')), + ); + }); + + testWidgets('ZetaChatItem slidable actions', (WidgetTester tester) async { + tester.view.devicePixelRatio = 1.0; + tester.view.physicalSize = const Size(481, 480); + const title = Text('John Doe'); + const subtitle = Text('Hello, how are you?'); + final time = DateTime.now(); + + await tester.pumpWidget( + TestApp( + home: Column( + children: [ + ZetaChatItem( + time: time, + leading: const ZetaAvatar(initials: 'AZ'), + slidableActions: [ + ZetaSlidableAction.menuMore(onPressed: () {}), + ZetaSlidableAction.call(onPressed: () {}), + ZetaSlidableAction.ptt(onPressed: () {}), + ZetaSlidableAction.delete(onPressed: () {}), + ], + title: title, + subtitle: subtitle, + ), + ], + ), + ), + ); + + final chatItemFinder = find.byType(ZetaChatItem); + + await tester.drag(chatItemFinder, const Offset(-200, 0)); + await tester.pumpAndSettle(); + + // Verify that the slidable actions are displayed correctly + expect(find.byIcon(ZetaIcons.more_vertical_round), findsOneWidget); + expect(find.byIcon(ZetaIcons.phone_round), findsOneWidget); + expect(find.byIcon(ZetaIcons.ptt_round), findsOneWidget); + expect(find.byIcon(ZetaIcons.delete_round), findsOneWidget); + + // Verify that tapping on the slidable actions triggers the corresponding callbacks + await tester.tap(find.byIcon(ZetaIcons.more_vertical_round)); + await tester.pumpAndSettle(); + expect(tester.takeException(), isNull); + + await tester.tap(find.byIcon(ZetaIcons.phone_round)); + await tester.pumpAndSettle(); + expect(tester.takeException(), isNull); + + await tester.tap(find.byIcon(ZetaIcons.ptt_round)); + await tester.pumpAndSettle(); + expect(tester.takeException(), isNull); + + await tester.tap(find.byIcon(ZetaIcons.delete_round)); + await tester.pumpAndSettle(); + expect(tester.takeException(), isNull); + + await expectLater( + chatItemFinder, + matchesGoldenFile(join(getCurrentPath('chat_item'), 'chat_item_slidable_actions.png')), + ); + }); + + testWidgets('ZetaChatItem with pale slidable button colors', (WidgetTester tester) async { + tester.view.devicePixelRatio = 1.0; + tester.view.physicalSize = const Size(481, 480); + const title = Text('John Doe'); + const subtitle = Text('Hello, how are you?'); + final time = DateTime.now(); + + await tester.pumpWidget( + TestApp( + home: Column( + children: [ + ZetaChatItem( + time: time, + leading: const ZetaAvatar(initials: 'AZ'), + paleButtonColors: true, + slidableActions: [ + ZetaSlidableAction.menuMore( + onPressed: () {}, + ), + ZetaSlidableAction.call( + onPressed: () {}, + ), + ZetaSlidableAction.ptt( + onPressed: () {}, + ), + ZetaSlidableAction.delete( + onPressed: () {}, + ), + ], + title: title, + subtitle: subtitle, + ), + ], + ), + ), + ); + + final chatItemFinder = find.byType(ZetaChatItem); + + await tester.drag(chatItemFinder, const Offset(-200, 0)); + await tester.pumpAndSettle(); + + // Verify that the slidable actions have pale button colors + expect(find.byIcon(ZetaIcons.more_vertical_round), findsOneWidget); + expect(find.byIcon(ZetaIcons.phone_round), findsOneWidget); + expect(find.byIcon(ZetaIcons.ptt_round), findsOneWidget); + expect(find.byIcon(ZetaIcons.delete_round), findsOneWidget); + + await expectLater( + chatItemFinder, + matchesGoldenFile(join(getCurrentPath('chat_item'), 'chat_item_pale_slidable_buttons.png')), + ); + }); + + testWidgets('ZetaChatItem with 2 pale buttons and 2 regular buttons', (WidgetTester tester) async { + tester.view.devicePixelRatio = 1.0; + tester.view.physicalSize = const Size(481, 480); + const title = Text('John Doe'); + const subtitle = Text('Hello, how are you?'); + final time = DateTime.now(); + + await tester.pumpWidget( + TestApp( + home: Column( + children: [ + ZetaChatItem( + time: time, + leading: const ZetaAvatar(initials: 'AZ'), + slidableActions: [ + ZetaSlidableAction( + onPressed: () {}, + paleColor: true, + icon: Icons.star, + ), + ZetaSlidableAction( + onPressed: () {}, + paleColor: true, + icon: Icons.delete, + ), + ZetaSlidableAction( + onPressed: () {}, + icon: Icons.call, + ), + ZetaSlidableAction( + onPressed: () {}, + icon: Icons.message, + ), + ], + title: title, + subtitle: subtitle, + ), + ], + ), + ), + ); + + final chatItemFinder = find.byType(ZetaChatItem); + + await tester.drag(chatItemFinder, const Offset(-200, 0)); + await tester.pumpAndSettle(); + + // Verify that the slidable actions are displayed correctly + expect(find.byIcon(Icons.star), findsOneWidget); + expect(find.byIcon(Icons.delete), findsOneWidget); + expect(find.byIcon(Icons.call), findsOneWidget); + expect(find.byIcon(Icons.message), findsOneWidget); + + // Verify that the pale buttons have the correct color + final paleButtons = tester.widgetList( + find.byWidgetPredicate((widget) => widget is ZetaSlidableAction && widget.paleColor), + ); + expect(paleButtons.length, 2); + + // Verify that the non-pale buttons have the correct color + final nonPaleButtons = tester.widgetList( + find.byWidgetPredicate((widget) => widget is ZetaSlidableAction && !widget.paleColor), + ); + expect(nonPaleButtons.length, 2); + + // Verify that tapping on the slidable actions triggers the corresponding callbacks + await tester.tap(find.byIcon(Icons.star)); + await tester.pumpAndSettle(); + expect(tester.takeException(), isNull); + + await tester.tap(find.byIcon(Icons.delete)); + await tester.pumpAndSettle(); + expect(tester.takeException(), isNull); + + await tester.tap(find.byIcon(Icons.call)); + await tester.pumpAndSettle(); + expect(tester.takeException(), isNull); + + await tester.tap(find.byIcon(Icons.message)); + await tester.pumpAndSettle(); + expect(tester.takeException(), isNull); + + await expectLater( + chatItemFinder, + matchesGoldenFile(join(getCurrentPath('chat_item'), 'chat_item_pale_and_regular_buttons.png')), + ); + }); + + testWidgets('ZetaChatItem with custom leading widget', (WidgetTester tester) async { + tester.view.devicePixelRatio = 1.0; + tester.view.physicalSize = const Size(481, 480); + const title = Text('John Doe'); + const subtitle = Text('Hello, how are you?'); + final time = DateTime.now(); + + await tester.pumpWidget( + TestApp( + home: Column( + children: [ + ZetaChatItem( + time: time, + leading: Container( + width: 40, + height: 40, + color: Colors.blue, + ), + slidableActions: [ + ZetaSlidableAction.menuMore(onPressed: () {}), + ZetaSlidableAction.call(onPressed: () {}), + ZetaSlidableAction.ptt(onPressed: () {}), + ZetaSlidableAction.delete(onPressed: () {}), + ], + title: title, + subtitle: subtitle, + ), + ], + ), + ), + ); + + final chatItemFinder = find.byType(ZetaChatItem); + + // Verify that the custom leading widget is displayed correctly + expect(find.byType(Container), findsOneWidget); + expect(find.byWidgetPredicate((widget) => widget is Container && widget.color == Colors.blue), findsOneWidget); + + await expectLater( + chatItemFinder, + matchesGoldenFile(join(getCurrentPath('chat_item'), 'chat_item_custom_leading.png')), + ); + }); + + testWidgets('ZetaChatItem with custom slidable buttons', (WidgetTester tester) async { + tester.view.devicePixelRatio = 1.0; + tester.view.physicalSize = const Size(481, 480); + const title = Text('John Doe'); + const subtitle = Text('Hello, how are you?'); + final time = DateTime.now(); + + await tester.pumpWidget( + TestApp( + home: Column( + children: [ + ZetaChatItem( + time: time, + leading: const ZetaAvatar(initials: 'AZ'), + slidableActions: [ + ZetaSlidableAction( + onPressed: () {}, + color: ZetaColorBase.orange, + icon: Icons.star, + ), + ZetaSlidableAction( + onPressed: () {}, + color: ZetaColorBase.red, + icon: Icons.delete, + ), + ], + title: title, + subtitle: subtitle, + ), + ], + ), + ), + ); + + final chatItemFinder = find.byType(ZetaChatItem); + + await tester.drag(chatItemFinder, const Offset(-200, 0)); + await tester.pumpAndSettle(); + + // Verify that the slidable actions are displayed correctly + expect(find.byIcon(Icons.star), findsOneWidget); + expect(find.byIcon(Icons.delete), findsOneWidget); + + // Verify that tapping on the slidable actions triggers the corresponding callbacks + await tester.tap(find.byIcon(Icons.star)); + await tester.pumpAndSettle(); + expect(tester.takeException(), isNull); + + await tester.tap(find.byIcon(Icons.delete)); + await tester.pumpAndSettle(); + expect(tester.takeException(), isNull); + + await expectLater( + chatItemFinder, + matchesGoldenFile(join(getCurrentPath('chat_item'), 'chat_item_custom_slidable_buttons.png')), + ); + }); + + testWidgets('debugFillProperties works correctly', (WidgetTester tester) async { + final diagnosticsZetaChatItem = DiagnosticPropertiesBuilder(); + final time = DateTime.now(); + ZetaChatItem( + title: const Text('Title'), + subtitle: const Text('Subtitle'), + time: time, + leading: const ZetaAvatar(initials: 'AZ'), + slidableActions: [ + ZetaSlidableAction.menuMore(onPressed: () {}), + ZetaSlidableAction.call(onPressed: () {}), + ZetaSlidableAction.ptt(onPressed: () {}), + ZetaSlidableAction.delete(onPressed: () {}), + ], + count: 1, + enabledNotificationIcon: true, + highlighted: true, + enabledWarningIcon: true, + starred: true, + ).debugFillProperties(diagnosticsZetaChatItem); + + expect(diagnosticsZetaChatItem.finder('rounded'), 'null'); + expect(diagnosticsZetaChatItem.finder('highlighted'), 'true'); + expect(diagnosticsZetaChatItem.finder('time'), time.toString()); + expect(diagnosticsZetaChatItem.finder('timeFormat'), 'null'); + expect(diagnosticsZetaChatItem.finder('enabledWarningIcon'), 'true'); + expect(diagnosticsZetaChatItem.finder('enabledNotificationIcon'), 'true'); + expect(diagnosticsZetaChatItem.finder('count'), '1'); + expect(diagnosticsZetaChatItem.finder('onTap'), 'null'); + expect(diagnosticsZetaChatItem.finder('starred'), 'true'); + expect(diagnosticsZetaChatItem.finder('onMenuMoreTap'), 'null'); + expect(diagnosticsZetaChatItem.finder('onCallTap'), 'null'); + expect(diagnosticsZetaChatItem.finder('onDeleteTap'), 'null'); + expect(diagnosticsZetaChatItem.finder('onPttTap'), 'null'); + expect(diagnosticsZetaChatItem.finder('explicitChildNodes'), 'true'); + expect(diagnosticsZetaChatItem.finder('paleButtonColors'), 'null'); + + final diagnosticsZetaSlidableAction = DiagnosticPropertiesBuilder(); + const ZetaSlidableAction(icon: Icons.star).debugFillProperties(diagnosticsZetaSlidableAction); + + expect(diagnosticsZetaSlidableAction.finder('onPressed'), 'null'); + expect(diagnosticsZetaSlidableAction.finder('icon'), 'IconData(U+0E5F9)'); + expect(diagnosticsZetaSlidableAction.finder('foregroundColor'), null); + expect(diagnosticsZetaSlidableAction.finder('backgroundColor'), null); + expect(diagnosticsZetaSlidableAction.finder('color'), ZetaColorBase.blue.toString()); + expect(diagnosticsZetaSlidableAction.finder('semanticLabel'), 'null'); + expect(diagnosticsZetaSlidableAction.finder('paleColor'), 'false'); + }); +} diff --git a/test/src/components/chat_item/golden/chat_item_custom_leading.png b/test/src/components/chat_item/golden/chat_item_custom_leading.png new file mode 100644 index 00000000..668000b0 Binary files /dev/null and b/test/src/components/chat_item/golden/chat_item_custom_leading.png differ diff --git a/test/src/components/chat_item/golden/chat_item_custom_slidable_buttons.png b/test/src/components/chat_item/golden/chat_item_custom_slidable_buttons.png new file mode 100644 index 00000000..47655b7b Binary files /dev/null and b/test/src/components/chat_item/golden/chat_item_custom_slidable_buttons.png differ diff --git a/test/src/components/chat_item/golden/chat_item_default.png b/test/src/components/chat_item/golden/chat_item_default.png new file mode 100644 index 00000000..f6fa9d75 Binary files /dev/null and b/test/src/components/chat_item/golden/chat_item_default.png differ diff --git a/test/src/components/chat_item/golden/chat_item_highlighted.png b/test/src/components/chat_item/golden/chat_item_highlighted.png new file mode 100644 index 00000000..27b3a7bd Binary files /dev/null and b/test/src/components/chat_item/golden/chat_item_highlighted.png differ diff --git a/test/src/components/chat_item/golden/chat_item_pale_and_regular_buttons.png b/test/src/components/chat_item/golden/chat_item_pale_and_regular_buttons.png new file mode 100644 index 00000000..876c8d22 Binary files /dev/null and b/test/src/components/chat_item/golden/chat_item_pale_and_regular_buttons.png differ diff --git a/test/src/components/chat_item/golden/chat_item_pale_slidable_buttons.png b/test/src/components/chat_item/golden/chat_item_pale_slidable_buttons.png new file mode 100644 index 00000000..cd042946 Binary files /dev/null and b/test/src/components/chat_item/golden/chat_item_pale_slidable_buttons.png differ diff --git a/test/src/components/chat_item/golden/chat_item_slidable_actions.png b/test/src/components/chat_item/golden/chat_item_slidable_actions.png new file mode 100644 index 00000000..797a78f2 Binary files /dev/null and b/test/src/components/chat_item/golden/chat_item_slidable_actions.png differ