diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index a55af954..04b742f7 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -1,32 +1,30 @@ name: CI - Pull Request on: pull_request_target: - + # Pull Request Runs on the same branch will be cancelled concurrency: group: ${{ github.head_ref }} cancel-in-progress: true - jobs: code-quality: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - repository: ${{github.event.pull_request.head.repo.full_name}} - ref: ${{ github.head_ref }} - fetch-depth: 0 - - uses: subosito/flutter-action@v2 - with: - cache: true - - run: dart run build_runner build --delete-conflicting-outputs - - uses: ZebraDevs/flutter-code-quality@main - with: - token: ${{secrets.GITHUB_TOKEN}} - coverage-pass-score: '90' - - + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + repository: ${{github.event.pull_request.head.repo.full_name}} + ref: ${{ github.head_ref }} + fetch-depth: 0 + - uses: subosito/flutter-action@v2 + with: + cache: true + - run: dart run build_runner build --delete-conflicting-outputs + - uses: ZebraDevs/flutter-code-quality@main + with: + token: ${{secrets.GITHUB_TOKEN}} + coverage-pass-score: "80" + check-secret: runs-on: ubuntu-latest outputs: diff --git a/example/lib/pages/components/top_app_bar_example.dart b/example/lib/pages/components/top_app_bar_example.dart index 6adc0827..5c5d30fe 100644 --- a/example/lib/pages/components/top_app_bar_example.dart +++ b/example/lib/pages/components/top_app_bar_example.dart @@ -15,20 +15,7 @@ class TopAppBarExample extends StatefulWidget { } class _TopAppBarExampleState extends State { - final _searchControllerExtended = ZetaSearchController(); - final _searchControllerRegular = ZetaSearchController(); - - void _showHideSearchExtended() { - _searchControllerExtended.isEnabled - ? _searchControllerExtended.closeSearch() - : _searchControllerExtended.startSearch(); - } - - void _showHideSearchRegular() { - _searchControllerRegular.isEnabled - ? _searchControllerRegular.closeSearch() - : _searchControllerRegular.startSearch(); - } + final _searchController = ZetaSearchController(); @override Widget build(BuildContext context) { @@ -78,8 +65,7 @@ class _TopAppBarExampleState extends State { ], ), Text('Centered', style: ZetaTextStyles.titleLarge), - ZetaTopAppBar( - type: ZetaTopAppBarType.centeredTitle, + ZetaTopAppBar.centered( leading: IconButton( onPressed: () {}, icon: ZetaIcon(Icons.menu), @@ -119,24 +105,20 @@ class _TopAppBarExampleState extends State { ], ), Text('Search', style: ZetaTextStyles.titleLarge), - ZetaTopAppBar( - type: ZetaTopAppBarType.centeredTitle, - leading: BackButton(), + ZetaTopAppBar.search( + leading: IconButton( + onPressed: () {}, + icon: ZetaIcon(Icons.menu), + ), title: Text("Title"), - actions: [ - IconButton( - onPressed: _showHideSearchRegular, - icon: ZetaIcon(ZetaIcons.search), - ) - ], - searchController: _searchControllerRegular, + searchController: _searchController, onSearch: (text) => debugPrint('search text: $text'), onSearchMicrophoneIconPressed: () async { var sampleTexts = ['This is a sample text', 'Another sample', 'Speech recognition text', 'Example']; var generatedText = sampleTexts[Random().nextInt(sampleTexts.length)]; - _searchControllerRegular.text = generatedText; + _searchController.text = generatedText; }, ), Text('Extended', style: ZetaTextStyles.titleLarge), @@ -188,48 +170,6 @@ class _TopAppBarExampleState extends State { ], ), ), - Text('Extended Search', style: ZetaTextStyles.titleLarge), - SizedBox( - width: 800, - height: 200, - child: CustomScrollView( - slivers: [ - ZetaTopAppBar.extended( - leading: BackButton(), - title: Text("Title"), - actions: [ - IconButton( - onPressed: _showHideSearchExtended, - icon: ZetaIcon(ZetaIcons.search), - ) - ], - searchController: _searchControllerExtended, - onSearch: (text) => debugPrint('search text: $text'), - onSearchMicrophoneIconPressed: () async { - var sampleTexts = [ - 'This is a sample text', - 'Another sample', - 'Speech recognition text', - 'Example' - ]; - var generatedText = sampleTexts[Random().nextInt(sampleTexts.length)]; - _searchControllerExtended.text = generatedText; - }, - ), - SliverToBoxAdapter( - child: Container( - width: 800, - height: 800, - color: Zeta.of(context).colors.surfaceSelectedHover, - child: CustomPaint( - painter: Painter(context: context), - size: Size(800, 800), - ), - ), - ), - ], - ), - ), ].gap(20), ), ), diff --git a/example/widgetbook/pages/components/top_app_bar_widgetbook.dart b/example/widgetbook/pages/components/top_app_bar_widgetbook.dart index 96f80616..ecc9b378 100644 --- a/example/widgetbook/pages/components/top_app_bar_widgetbook.dart +++ b/example/widgetbook/pages/components/top_app_bar_widgetbook.dart @@ -13,7 +13,7 @@ Widget defaultTopAppBarUseCase(BuildContext context) { label: "Title Alignment", options: [ ZetaTopAppBarType.defaultAppBar, - ZetaTopAppBarType.centeredTitle, + ZetaTopAppBarType.centered, ], initialOption: ZetaTopAppBarType.defaultAppBar, labelBuilder: (option) { @@ -53,7 +53,7 @@ Widget defaultTopAppBarUseCase(BuildContext context) { icon: ZetaIcon(ZetaIcons.more_vertical), ) ] - : null, + : [], ), ], )); @@ -84,7 +84,7 @@ class _SearchUseCaseState extends State<_SearchUseCase> { label: "Title Alignment", options: [ ZetaTopAppBarType.defaultAppBar, - ZetaTopAppBarType.centeredTitle, + ZetaTopAppBarType.centered, ], initialOption: ZetaTopAppBarType.defaultAppBar, labelBuilder: (option) { @@ -101,7 +101,7 @@ class _SearchUseCaseState extends State<_SearchUseCase> { initialValue: false, ); - return ZetaTopAppBar( + return ZetaTopAppBar.search( leading: IconButton( onPressed: () {}, icon: ZetaIcon(leadingIcon), @@ -118,43 +118,26 @@ class _SearchUseCaseState extends State<_SearchUseCase> { searchController.text = generatedText; } : null, - actions: [ - IconButton( - onPressed: () { - searchController.isEnabled ? searchController.closeSearch() : searchController.startSearch(); - }, - icon: ZetaIcon(ZetaIcons.search)), - ], ); } } -Widget extendedTopAppBarUseCase(BuildContext context) => ExtendedSearch(); +Widget extendedTopAppBarUseCase(BuildContext context) => ExtendedTopAppBar(); -class ExtendedSearch extends StatefulWidget { - const ExtendedSearch({super.key}); +class ExtendedTopAppBar extends StatefulWidget { + const ExtendedTopAppBar({super.key}); @override - State createState() => _ExtendedSearchState(); + State createState() => _ExtendedTopAppBarState(); } -class _ExtendedSearchState extends State { - final _searchControllerExtended = ZetaSearchController(); - - void _showHideSearchExtended() { - _searchControllerExtended.isEnabled - ? _searchControllerExtended.closeSearch() - : _searchControllerExtended.startSearch(); - } - +class _ExtendedTopAppBarState extends State { @override Widget build(BuildContext context) { final title = context.knobs.string(label: "Title", initialValue: "Title"); final leadingIcon = iconKnob(context, name: 'Leading Icon', initial: ZetaIcons.hamburger_menu); - final showSearch = context.knobs.boolean(label: 'Search variant', initialValue: false); - return WidgetbookScaffold( removeBody: true, builder: (context, constraints) => SafeArea( @@ -166,41 +149,20 @@ class _ExtendedSearchState extends State { ZetaTopAppBar.extended( leading: IconButton(icon: ZetaIcon(leadingIcon), onPressed: () {}), title: Text(title), - actions: showSearch - ? [ - IconButton( - onPressed: _showHideSearchExtended, - icon: ZetaIcon(ZetaIcons.search), - ) - ] - : [ - IconButton( - onPressed: () {}, - icon: ZetaIcon(Icons.language), - ), - IconButton( - onPressed: () {}, - icon: ZetaIcon(Icons.favorite), - ), - IconButton( - onPressed: () {}, - icon: ZetaIcon(ZetaIcons.more_vertical), - ) - ], - searchController: showSearch ? _searchControllerExtended : null, - onSearch: showSearch ? (text) => debugPrint('search text: $text') : null, - onSearchMicrophoneIconPressed: showSearch - ? () async { - var sampleTexts = [ - 'This is a sample text', - 'Another sample', - 'Speech recognition text', - 'Example' - ]; - var generatedText = sampleTexts[Random().nextInt(sampleTexts.length)]; - _searchControllerExtended.text = generatedText; - } - : null, + actions: [ + IconButton( + onPressed: () {}, + icon: ZetaIcon(Icons.language), + ), + IconButton( + onPressed: () {}, + icon: ZetaIcon(Icons.favorite), + ), + IconButton( + onPressed: () {}, + icon: ZetaIcon(ZetaIcons.more_vertical), + ) + ], ), SliverToBoxAdapter( child: Container( @@ -232,7 +194,7 @@ class Painter extends CustomPainter { var p1 = Offset(i, -10); var p2 = Offset(constraints.maxHeight + i, constraints.maxHeight * 4); var paint = Paint() - ..color = Zeta.of(context).colors.primary + ..color = Zeta.of(context).colors.surfacePrimarySubtle ..strokeWidth = Zeta.of(context).spacing.minimum; canvas.drawLine(p1, p2, paint); } diff --git a/lib/src/components/screen_header_bar/screen_header_bar.dart b/lib/src/components/screen_header_bar/screen_header_bar.dart index 544eefe6..4212abf0 100644 --- a/lib/src/components/screen_header_bar/screen_header_bar.dart +++ b/lib/src/components/screen_header_bar/screen_header_bar.dart @@ -50,7 +50,7 @@ class ZetaScreenHeaderBar extends ZetaStatelessWidget { title: title, titleTextStyle: ZetaTextStyles.titleLarge, actions: actionButtonLabel == null - ? null + ? [] : [ ZetaButton( label: actionButtonLabel!, diff --git a/lib/src/components/top_app_bar/extended_top_app_bar.dart b/lib/src/components/top_app_bar/extended_top_app_bar.dart index b2577646..c6a8d289 100644 --- a/lib/src/components/top_app_bar/extended_top_app_bar.dart +++ b/lib/src/components/top_app_bar/extended_top_app_bar.dart @@ -15,7 +15,6 @@ class ZetaExtendedAppBarDelegate extends SliverPersistentHeaderDelegate { required this.shrinks, this.actions, this.leading, - this.searchController, }); /// Title of the app bar. @@ -27,56 +26,49 @@ class ZetaExtendedAppBarDelegate extends SliverPersistentHeaderDelegate { /// Widget displayed first in the app bar row. final Widget? leading; - /// Used to control the search textfield and states. - final ZetaSearchController? searchController; - /// If `ZetaTopAppBarType.extend` shrinks. Does not affect other types of app bar. final bool shrinks; static const double _maxExtent = 104; - static const double _minExtent = 52; + static const double _minExtent = 56; @override Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) { - final searchBarOffsetTop = Zeta.of(context).spacing.minimum * 1.5; - final searchBarOffsetRight = Zeta.of(context).spacing.minimum * 22; - final maxExtent = Zeta.of(context).spacing.minimum * 26; - final leftMin = Zeta.of(context).spacing.large; - final topMin = Zeta.of(context).spacing.xl; - final topMax = Zeta.of(context).spacing.minimum * 15; + final spacing = Zeta.of(context).spacing; + + final maxExtent = spacing.minimum * 26; + final leftMin = spacing.large; + final topMin = spacing.xl; + final topMax = spacing.minimum * 15; /// If there is no leading widget, the left margin should not change /// If there is a leading widget, the left margin should be the same as the leading widget's width plus padding - final leftMax = leading == null ? leftMin : _minExtent + Zeta.of(context).spacing.small; + final leftMax = leading == null ? leftMin : _minExtent + spacing.small; + + final top = shrinks + ? (topMax + (-1 * shrinkOffset)).clamp( + topMin - (spacing.minimum), + topMax - (spacing.minimum), + ) + : topMax; return ConstrainedBox( - constraints: BoxConstraints(minHeight: Zeta.of(context).spacing.xl_9, maxHeight: maxExtent), + constraints: BoxConstraints(minHeight: spacing.xl_9, maxHeight: maxExtent), child: ColoredBox( color: Zeta.of(context).colors.surfacePrimary, child: Stack( children: [ Positioned( - top: shrinks - ? (topMax + (-1 * shrinkOffset)).clamp( - topMin - - (searchController != null && searchController!.isEnabled - ? searchBarOffsetTop - : Zeta.of(context).spacing.none), - topMax, - ) - : topMax, + top: top, left: shrinks ? ((shrinkOffset / maxExtent) * _maxExtent).clamp(leftMin, leftMax) : leftMin, - right: searchController != null && searchController!.isEnabled - ? searchBarOffsetRight - : Zeta.of(context).spacing.none, + right: spacing.none, child: title, ), - if (leading != null) - Positioned(top: Zeta.of(context).spacing.medium, left: Zeta.of(context).spacing.small, child: leading!), + if (leading != null) Positioned(top: spacing.small, left: spacing.small, child: leading!), if (actions != null) Positioned( - top: Zeta.of(context).spacing.medium, - right: Zeta.of(context).spacing.small, + top: spacing.small, + right: spacing.small, child: Row(children: actions!), ), ], diff --git a/lib/src/components/top_app_bar/search_top_app_bar.dart b/lib/src/components/top_app_bar/search_top_app_bar.dart index a05b15f4..7507541f 100644 --- a/lib/src/components/top_app_bar/search_top_app_bar.dart +++ b/lib/src/components/top_app_bar/search_top_app_bar.dart @@ -66,20 +66,12 @@ class _ZetaTopAppBarSearchFieldState extends State wit @override void initState() { - _textFocusNode.addListener(_onFocusChanged); widget.searchController?.addListener(_onSearchControllerChanged); widget.searchController?.textEditingController ??= TextEditingController(); super.initState(); } - void _onFocusChanged() { - final text = widget.searchController?.text ?? ''; - final shouldCloseSearch = _isSearching && text.isEmpty && !_textFocusNode.hasFocus; - - if (shouldCloseSearch) _closeSearch(); - } - void _onSearchControllerChanged() { final controller = widget.searchController; if (controller == null) return; @@ -151,7 +143,7 @@ class _ZetaTopAppBarSearchFieldState extends State wit children: [ Row( mainAxisAlignment: - widget.type == ZetaTopAppBarType.centeredTitle ? MainAxisAlignment.center : MainAxisAlignment.start, + widget.type == ZetaTopAppBarType.centered ? MainAxisAlignment.center : MainAxisAlignment.start, children: [ widget.child ?? const Nothing(), ], diff --git a/lib/src/components/top_app_bar/top_app_bar.dart b/lib/src/components/top_app_bar/top_app_bar.dart index 21f11e5b..07030848 100644 --- a/lib/src/components/top_app_bar/top_app_bar.dart +++ b/lib/src/components/top_app_bar/top_app_bar.dart @@ -8,6 +8,8 @@ import 'search_top_app_bar.dart'; export 'search_top_app_bar.dart' hide ZetaTopAppBarSearchField; /// Top app bars provide content and actions related to the current screen. +/// +/// To create Extended, Centered, or Search app bars, use the respective constructors. /// {@category Components} /// /// Figma: https://www.figma.com/design/JesXQFLaPJLc1BdBM4sisI/%F0%9F%A6%93-ZDS---Components?node-id=229-37&node-type=canvas&m=dev @@ -18,33 +20,76 @@ class ZetaTopAppBar extends ZetaStatefulWidget implements PreferredSizeWidget { const ZetaTopAppBar({ super.key, super.rounded, - this.actions, + this.actions = const [], this.automaticallyImplyLeading = true, - this.searchController, this.leading, this.title, this.titleTextStyle, this.type = ZetaTopAppBarType.defaultAppBar, - this.onSearch, - this.searchHintText = 'Search', - this.onSearchMicrophoneIconPressed, - }) : shrinks = false; + @Deprecated('Use ZetaTopAppBar.search instead. ' 'Deprecated as of 0.16.0') ValueChanged? onSearch, + @Deprecated('Use ZetaTopAppBar.search instead. ' 'Deprecated as of 0.16.0') String? searchHintText, + @Deprecated('Use ZetaTopAppBar.search instead. ' 'Deprecated as of 0.16.0') ZetaSearchController? searchController, + @Deprecated('Use ZetaTopAppBar.search instead. ' 'Deprecated as of 0.16.0') + VoidCallback? onSearchMicrophoneIconPressed, + }) : shrinks = false, + onSearch = null, + searchHintText = null, + searchController = null, + clearSemanticLabel = null, + microphoneSemanticLabel = null, + searchBackSemanticLabel = null, + searchSemanticLabel = null, + onSearchMicrophoneIconPressed = null; /// Creates a ZetaTopAppBar with centered title. const ZetaTopAppBar.centered({ super.key, super.rounded, - this.actions, + this.actions = const [], + this.automaticallyImplyLeading = true, + this.leading, + this.title, + this.titleTextStyle, + @Deprecated('Use ZetaTopAppBar.search instead. ' 'Deprecated as of 0.16.0') ValueChanged? onSearch, + @Deprecated('Use ZetaTopAppBar.search instead. ' 'Deprecated as of 0.16.0') String? searchHintText, + @Deprecated('Use ZetaTopAppBar.search instead. ' 'Deprecated as of 0.16.0') ZetaSearchController? searchController, + @Deprecated('Use ZetaTopAppBar.search instead. ' 'Deprecated as of 0.16.0') + VoidCallback? onSearchMicrophoneIconPressed, + }) : type = ZetaTopAppBarType.centered, + onSearch = null, + searchHintText = null, + searchController = null, + onSearchMicrophoneIconPressed = null, + clearSemanticLabel = null, + searchBackSemanticLabel = null, + microphoneSemanticLabel = null, + searchSemanticLabel = null, + shrinks = false; + + /// Creates a ZetaTopAppBar with an expanding search field. + /// This will append a search icon to the right of the app bar. + /// When the search icon is pressed, the search field will expand and replace the title widget. + /// It will replace the leading widget with a back button which closes the search field. + /// The search field can be controlled externally by the [searchController]. + const ZetaTopAppBar.search({ + super.key, + super.rounded, + this.type = ZetaTopAppBarType.defaultAppBar, this.automaticallyImplyLeading = true, this.searchController, this.leading, this.title, this.titleTextStyle, this.onSearch, - this.searchHintText = 'Search', + this.searchHintText, this.onSearchMicrophoneIconPressed, - }) : type = ZetaTopAppBarType.centeredTitle, - shrinks = false; + this.actions = const [], + this.clearSemanticLabel, + this.microphoneSemanticLabel, + this.searchSemanticLabel, + this.searchBackSemanticLabel, + }) : shrinks = false, + assert(type != ZetaTopAppBarType.extended, 'Search app bars cannot be extended'); /// Creates a ZetaTopAppBar with an extended title over 2 lines. /// @@ -52,23 +97,32 @@ class ZetaTopAppBar extends ZetaStatefulWidget implements PreferredSizeWidget { const ZetaTopAppBar.extended({ super.key, super.rounded, - this.actions, + this.actions = const [], this.automaticallyImplyLeading = true, - this.searchController, this.leading, this.title, this.titleTextStyle, - this.onSearch, - this.searchHintText = 'Search', - this.onSearchMicrophoneIconPressed, this.shrinks = true, - }) : type = ZetaTopAppBarType.extendedTitle; + @Deprecated('Use ZetaTopAppBar.search instead. ' 'Deprecated as of 0.16.0') ValueChanged? onSearch, + @Deprecated('Use ZetaTopAppBar.search instead. ' 'Deprecated as of 0.16.0') String? searchHintText, + @Deprecated('Use ZetaTopAppBar.search instead. ' 'Deprecated as of 0.16.0') ZetaSearchController? searchController, + @Deprecated('Use ZetaTopAppBar.search instead. ' 'Deprecated as of 0.16.0') + VoidCallback? onSearchMicrophoneIconPressed, + }) : type = ZetaTopAppBarType.extended, + onSearch = null, + searchHintText = null, + onSearchMicrophoneIconPressed = null, + clearSemanticLabel = null, + microphoneSemanticLabel = null, + searchSemanticLabel = null, + searchBackSemanticLabel = null, + searchController = null; /// Called when text in the search field is submitted. - final void Function(String)? onSearch; + final ValueChanged? onSearch; /// A list of Widgets to display in a row after the [title] widget. - final List? actions; + final List actions; /// Configures whether the back button to be displayed. final bool automaticallyImplyLeading; @@ -97,6 +151,18 @@ class ZetaTopAppBar extends ZetaStatefulWidget implements PreferredSizeWidget { /// If `ZetaTopAppBarType.extend` shrinks. Does not affect other types of app bar. final bool shrinks; + /// The semantic label for the clear icon. + final String? clearSemanticLabel; + + /// The semantic label for the microphone icon. + final String? microphoneSemanticLabel; + + /// The semantic label for the search icon. + final String? searchSemanticLabel; + + /// The semantic label for the back icon when search is open. + final String? searchBackSemanticLabel; + @override State createState() => _ZetaTopAppBarState(); @@ -113,34 +179,28 @@ class ZetaTopAppBar extends ZetaStatefulWidget implements PreferredSizeWidget { ..add(DiagnosticsProperty('searchController', searchController)) ..add(StringProperty('searchHintText', searchHintText)) ..add(EnumProperty('type', type)) - ..add(DiagnosticsProperty('shrinks', shrinks)); + ..add(DiagnosticsProperty('shrinks', shrinks)) + ..add(StringProperty('clearSemanticLabel', clearSemanticLabel)) + ..add(StringProperty('microphoneSemanticLabel', microphoneSemanticLabel)) + ..add(StringProperty('searchSemanticLabel', searchSemanticLabel)) + ..add(StringProperty('searchBackSemanticLabel', searchBackSemanticLabel)); } } class _ZetaTopAppBarState extends State { - bool _isSearchEnabled = false; + late ZetaSearchController _searchController; + bool get _searchEnabled => widget.searchController != null || widget.onSearch != null; + bool get _searchActive => _searchController.isEnabled; @override void initState() { - widget.searchController?.addListener(_onSearchControllerChanged); + _searchController = widget.searchController ?? ZetaSearchController(); + _searchController.addListener(() => setState(() {})); super.initState(); } - void _onSearchControllerChanged() { - final controller = widget.searchController; - if (controller == null) return; - - setState(() => _isSearchEnabled = controller.isEnabled); - } - - @override - void dispose() { - widget.searchController?.removeListener(_onSearchControllerChanged); - super.dispose(); - } - - Widget _getTitleText(ZetaColors colors) { - var title = widget.title; + Widget _getTitle(ZetaColors colors) { + Widget? title = widget.title; if (widget.title is Row) { final oldRow = widget.title! as Row; title = Row( @@ -169,43 +229,67 @@ class _ZetaTopAppBarState extends State { } List? _getActions(ZetaColors colors) { - return _isSearchEnabled - ? [ - IconButtonTheme( - data: IconButtonThemeData( - style: IconButton.styleFrom(iconSize: Zeta.of(context).spacing.xl), - ), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - IconButton( - color: colors.cool.shade50, - onPressed: () => widget.searchController?.clearText(), - icon: const ZetaIcon(ZetaIcons.cancel), - ), - if (widget.onSearchMicrophoneIconPressed != null) ...[ - SizedBox( - height: Zeta.of(context).spacing.xl_2, - child: VerticalDivider(width: ZetaBorders.medium, color: colors.cool.shade70), - ), - IconButton( - onPressed: widget.onSearchMicrophoneIconPressed, - icon: const ZetaIcon(ZetaIcons.microphone), - ), - ], - ], + if (_searchActive) { + return [ + Row( + mainAxisSize: MainAxisSize.min, + children: [ + Semantics( + label: widget.clearSemanticLabel, + button: true, + child: IconButton( + color: colors.cool.shade50, + onPressed: () => _searchController.clearText(), + icon: ZetaIcon( + ZetaIcons.cancel, + size: Zeta.of(context).spacing.xl, + ), ), ), - ] - : widget.actions; + if (widget.onSearchMicrophoneIconPressed != null) ...[ + SizedBox( + height: Zeta.of(context).spacing.xl_2, + child: VerticalDivider(width: ZetaBorders.medium, color: colors.cool.shade70), + ), + Semantics( + label: widget.microphoneSemanticLabel, + button: true, + child: IconButton( + onPressed: widget.onSearchMicrophoneIconPressed, + icon: const ZetaIcon(ZetaIcons.microphone), + ), + ), + ], + ], + ), + ]; + } + + if (_searchEnabled) { + return [ + ...widget.actions, + Semantics( + label: widget.searchSemanticLabel, + button: true, + child: IconButton( + onPressed: () => setState(() { + _searchController.startSearch(); + }), + icon: const ZetaIcon(ZetaIcons.search), + ), + ), + ]; + } + return widget.actions; } @override Widget build(BuildContext context) { final colors = Zeta.of(context).colors; + final spacing = Zeta.of(context).spacing; final actions = _getActions(colors); - final titleText = _getTitleText(colors); + final titleText = _getTitle(colors); final title = widget.searchController != null ? ZetaTopAppBarSearchField( @@ -213,48 +297,55 @@ class _ZetaTopAppBarState extends State { hintText: widget.searchHintText ?? 'Search', onSearch: widget.onSearch, type: widget.type, - isExtended: widget.type == ZetaTopAppBarType.extendedTitle, + isExtended: widget.type == ZetaTopAppBarType.extended, child: titleText, ) : titleText; - if (widget.type == ZetaTopAppBarType.extendedTitle) { + if (widget.type == ZetaTopAppBarType.extended) { return SliverPersistentHeader( pinned: true, delegate: ZetaExtendedAppBarDelegate( actions: actions, leading: widget.leading, - searchController: widget.searchController, title: title, shrinks: widget.shrinks, ), ); } + Widget? leading = widget.leading; + + if (_searchActive) { + leading = Semantics( + label: widget.searchBackSemanticLabel, + button: true, + child: IconButton( + onPressed: _searchController.closeSearch, + icon: const ZetaIcon(ZetaIcons.arrow_back), + ), + ); + } + return ZetaRoundedScope( rounded: context.rounded, - child: ColoredBox( - color: colors.surfacePrimary, - child: IconButtonTheme( - data: IconButtonThemeData(style: IconButton.styleFrom(tapTargetSize: MaterialTapTargetSize.shrinkWrap)), - child: Padding( - padding: EdgeInsets.symmetric(horizontal: Zeta.of(context).spacing.minimum), - child: AppBar( - elevation: 0, - scrolledUnderElevation: 0, - iconTheme: IconThemeData(color: colors.cool.shade90), - leadingWidth: Zeta.of(context).spacing.xl_6, - leading: widget.leading, - automaticallyImplyLeading: widget.automaticallyImplyLeading, - surfaceTintColor: Colors.transparent, - centerTitle: widget.type == ZetaTopAppBarType.centeredTitle, - titleTextStyle: widget.titleTextStyle == null - ? ZetaTextStyles.bodyLarge.copyWith(color: colors.textDefault) - : widget.titleTextStyle!.copyWith(color: colors.textDefault), - title: title, - actions: actions, - ), - ), + child: Padding( + padding: EdgeInsets.symmetric(horizontal: spacing.minimum), + child: AppBar( + elevation: 0, + scrolledUnderElevation: 0, + backgroundColor: colors.surfacePrimary, + iconTheme: IconThemeData(color: colors.iconDefault), + leading: leading, + toolbarHeight: spacing.xl_9, + automaticallyImplyLeading: widget.automaticallyImplyLeading, + surfaceTintColor: Colors.transparent, + centerTitle: widget.type == ZetaTopAppBarType.centered, + titleTextStyle: widget.titleTextStyle == null + ? ZetaTextStyles.bodyLarge.copyWith(color: colors.textDefault) + : widget.titleTextStyle!.copyWith(color: colors.textDefault), + title: title, + actions: actions, ), ), ); @@ -267,8 +358,16 @@ enum ZetaTopAppBarType { defaultAppBar, /// Title in the center. + @Deprecated('Use ZetaTopAppBar.centered instead. ' 'Deprecated as of 0.16.0') centeredTitle, + /// Aligns the title to the center of the app bar. + centered, + /// Title below the app bar. + @Deprecated('Use ZetaTopAppBar.extended instead. ' 'Deprecated as of 0.16.0') extendedTitle, + + /// Title extends over 2 lines and collapses when scrolled. + extended, } diff --git a/test/src/components/avatar/avatar_test.dart b/test/src/components/avatar/avatar_test.dart index 7e02d661..10ab2880 100644 --- a/test/src/components/avatar/avatar_test.dart +++ b/test/src/components/avatar/avatar_test.dart @@ -322,7 +322,6 @@ void main() { ZetaAvatar( size: size, ), - ZetaAvatar, 'avatar_default_${size.toString().split('.').last}', ); goldenTest( @@ -331,7 +330,6 @@ void main() { initials: 'AB', size: size, ), - ZetaAvatar, 'avatar_initials_${size.toString().split('.').last}', ); goldenTest( @@ -340,7 +338,6 @@ void main() { image: Image.file(File('/assets/maxresdefault.jpg')), size: size, ), - ZetaAvatar, 'avatar_image_${size.toString().split('.').last}', ); goldenTest( @@ -349,7 +346,6 @@ void main() { name: 'John Doe', size: size, ), - ZetaAvatar, 'avatar_from_name_${size.toString().split('.').last}', ); goldenTest( @@ -358,7 +354,6 @@ void main() { upperBadge: const ZetaAvatarBadge.notification(value: 3), size: size, ), - ZetaAvatar, 'avatar_upper_badge_${size.toString().split('.').last}', ); goldenTest( @@ -367,7 +362,6 @@ void main() { lowerBadge: const ZetaAvatarBadge.icon(icon: Icons.star), size: size, ), - ZetaAvatar, 'avatar_lower_badge_${size.toString().split('.').last}', ); } diff --git a/test/src/components/badge/indicator_test.dart b/test/src/components/badge/indicator_test.dart index 12f768e5..43c683a8 100644 --- a/test/src/components/badge/indicator_test.dart +++ b/test/src/components/badge/indicator_test.dart @@ -157,8 +157,8 @@ void main() { group('Styling Tests', () {}); group('Interaction Tests', () {}); group('Golden Tests', () { - goldenTest(goldenFile, const ZetaIndicator(), ZetaIndicator, 'indicator_default'); - goldenTest(goldenFile, const ZetaIndicator.icon(), ZetaIndicator, 'indicator_icon_default'); + goldenTest(goldenFile, const ZetaIndicator(), 'indicator_default'); + goldenTest(goldenFile, const ZetaIndicator.icon(), 'indicator_icon_default'); goldenTest( goldenFile, const ZetaIndicator.icon( @@ -167,10 +167,9 @@ void main() { inverse: true, size: ZetaWidgetSize.medium, ), - ZetaIndicator, 'indicator_icon_values', ); - goldenTest(goldenFile, const ZetaIndicator.notification(), ZetaIndicator, 'indicator_notification_default'); + goldenTest(goldenFile, const ZetaIndicator.notification(), 'indicator_notification_default'); goldenTest( goldenFile, const ZetaIndicator.notification( @@ -180,7 +179,6 @@ void main() { size: ZetaWidgetSize.small, value: 1, ), - ZetaIndicator, 'indicator_notification_values', ); }); diff --git a/test/src/components/badge/label_test.dart b/test/src/components/badge/label_test.dart index ee6fa91e..24d1e99b 100644 --- a/test/src/components/badge/label_test.dart +++ b/test/src/components/badge/label_test.dart @@ -104,33 +104,29 @@ void main() { group('Styling Tests', () {}); group('Interaction Tests', () {}); group('Golden Tests', () { - goldenTest(goldenFile, const ZetaLabel(label: 'Test Label'), ZetaLabel, 'label_default'); + goldenTest(goldenFile, const ZetaLabel(label: 'Test Label'), 'label_default'); goldenTest( goldenFile, const ZetaLabel(label: 'Test Label', status: ZetaWidgetStatus.positive), - ZetaLabel, 'label_positive', ); goldenTest( goldenFile, const ZetaLabel(label: 'Test Label', status: ZetaWidgetStatus.warning), - ZetaLabel, 'label_warning', ); goldenTest( goldenFile, const ZetaLabel(label: 'Test Label', status: ZetaWidgetStatus.negative), - ZetaLabel, 'label_negative', ); goldenTest( goldenFile, const ZetaLabel(label: 'Test Label', status: ZetaWidgetStatus.neutral), - ZetaLabel, 'label_neutral', ); - goldenTest(goldenFile, const ZetaLabel(label: 'Test Label'), ZetaLabel, 'label_dark', themeMode: ThemeMode.dark); - goldenTest(goldenFile, const ZetaLabel(label: 'Test Label', rounded: false), ZetaLabel, 'label_sharp'); + goldenTest(goldenFile, const ZetaLabel(label: 'Test Label'), 'label_dark', themeMode: ThemeMode.dark); + goldenTest(goldenFile, const ZetaLabel(label: 'Test Label', rounded: false), 'label_sharp'); }); group('Performance Tests', () {}); } diff --git a/test/src/components/badge/priority_pill_test.dart b/test/src/components/badge/priority_pill_test.dart index 0c4b281f..92c120bb 100644 --- a/test/src/components/badge/priority_pill_test.dart +++ b/test/src/components/badge/priority_pill_test.dart @@ -121,7 +121,7 @@ void main() { group('Styling Tests', () {}); group('Interaction Tests', () {}); group('Golden Tests', () { - goldenTest(goldenFile, const ZetaPriorityPill(), ZetaPriorityPill, 'priority_pill_default'); + goldenTest(goldenFile, const ZetaPriorityPill(), 'priority_pill_default'); goldenTest( goldenFile, const ZetaPriorityPill( @@ -131,7 +131,6 @@ void main() { rounded: false, size: ZetaPriorityPillSize.small, ), - ZetaPriorityPill, 'priority_pill_high', ); goldenTest( @@ -140,7 +139,6 @@ void main() { type: ZetaPriorityPillType.medium, isBadge: true, ), - ZetaPriorityPill, 'priority_pill_medium', ); goldenTest( @@ -150,7 +148,6 @@ void main() { size: ZetaPriorityPillSize.small, isBadge: true, ), - ZetaPriorityPill, 'priority_pill_low', ); }); diff --git a/test/src/components/badge/status_label_test.dart b/test/src/components/badge/status_label_test.dart index 2198a529..992f6bad 100644 --- a/test/src/components/badge/status_label_test.dart +++ b/test/src/components/badge/status_label_test.dart @@ -57,14 +57,13 @@ void main() { group('Styling Tests', () {}); group('Interaction Tests', () {}); group('Golden Tests', () { - goldenTest(goldenFile, const ZetaStatusLabel(label: 'Test Label'), ZetaStatusLabel, 'status_label_default'); + goldenTest(goldenFile, const ZetaStatusLabel(label: 'Test Label'), 'status_label_default'); goldenTest( goldenFile, const ZetaStatusLabel( label: 'Custom Icon', customIcon: Icons.person, ), - ZetaStatusLabel, 'status_label_custom', ); }); diff --git a/test/src/components/badge/tag_test.dart b/test/src/components/badge/tag_test.dart index c7990a11..dc1cdba5 100644 --- a/test/src/components/badge/tag_test.dart +++ b/test/src/components/badge/tag_test.dart @@ -50,8 +50,8 @@ void main() { group('Styling Tests', () {}); group('Interaction Tests', () {}); group('Golden Tests', () { - goldenTest(goldenFile, const ZetaTag.right(label: 'Tag', rounded: false), ZetaTag, 'tag_right'); - goldenTest(goldenFile, const ZetaTag.left(label: 'Tag', rounded: true), ZetaTag, 'tag_left'); + goldenTest(goldenFile, const ZetaTag.right(label: 'Tag', rounded: false), 'tag_right'); + goldenTest(goldenFile, const ZetaTag.left(label: 'Tag', rounded: true), 'tag_left'); }); group('Performance Tests', () {}); } diff --git a/test/src/components/banner/banner_test.dart b/test/src/components/banner/banner_test.dart index 44d69c56..a221f5fb 100644 --- a/test/src/components/banner/banner_test.dart +++ b/test/src/components/banner/banner_test.dart @@ -347,8 +347,8 @@ void main() { ); }, ), - ZetaBanner, 'banner_${type.toString().split('.').last}', + widgetType: ZetaBanner, ); } }); diff --git a/test/src/components/button/button_test.dart b/test/src/components/button/button_test.dart index fb72ec0e..140d7537 100644 --- a/test/src/components/button/button_test.dart +++ b/test/src/components/button/button_test.dart @@ -258,47 +258,44 @@ void main() { }); group('Golden Tests', () { - goldenTest(goldenFile, ZetaButton.primary(onPressed: () {}, label: 'Test Button'), ZetaButton, 'button_primary'); + goldenTest( + goldenFile, + ZetaButton.primary(onPressed: () {}, label: 'Test Button'), + 'button_primary', + ); goldenTest( goldenFile, ZetaButton.secondary(onPressed: () {}, label: 'Test Button', leadingIcon: Icons.abc, size: ZetaWidgetSize.small), - ZetaButton, 'button_secondary', ); goldenTest( goldenFile, ZetaButton.positive(onPressed: () {}, label: 'Test Button', trailingIcon: Icons.abc), - ZetaButton, 'button_positive', ); goldenTest( goldenFile, ZetaButton.negative(onPressed: () {}, label: 'Test Button', size: ZetaWidgetSize.small), - ZetaButton, 'button_negative', ); goldenTest( goldenFile, ZetaButton.outline(onPressed: () {}, label: 'Test Button', size: ZetaWidgetSize.large), - ZetaButton, 'button_outline', ); goldenTest( goldenFile, const ZetaButton.outlineSubtle(label: 'Test Button', borderType: ZetaWidgetBorder.sharp), - ZetaButton, 'button_outline_subtle', ); goldenTest( goldenFile, ZetaButton.text(onPressed: () {}, label: 'Test Button', borderType: ZetaWidgetBorder.full), - ZetaButton, 'button_text', ); goldenTest( goldenFile, const ZetaButton.text(label: 'Test Button', borderType: ZetaWidgetBorder.full), - ZetaButton, 'button_disabled', ); }); diff --git a/test/src/components/chat_item/chat_item_test.dart b/test/src/components/chat_item/chat_item_test.dart index e72a00bf..88475d70 100644 --- a/test/src/components/chat_item/chat_item_test.dart +++ b/test/src/components/chat_item/chat_item_test.dart @@ -518,7 +518,7 @@ void main() { const subtitle = Text('Hello, how are you?'); final time = DateTime.now(); - goldenTestWithCallbacks( + goldenTest( goldenFile, Scaffold( body: Column( @@ -544,15 +544,15 @@ void main() { ], ), ), - ZetaChatItem, 'chat_item_default', - before: (tester) async { + widgetType: ZetaChatItem, + setUp: (tester) async { tester.view.devicePixelRatio = 1.0; tester.view.physicalSize = const Size(481, 480); }, ); - goldenTestWithCallbacks( + goldenTest( goldenFile, Column( children: [ @@ -578,15 +578,15 @@ void main() { ), ], ), - ZetaChatItem, 'chat_item_highlighted', - before: (tester) async { + widgetType: ZetaChatItem, + setUp: (tester) async { tester.view.devicePixelRatio = 1.0; tester.view.physicalSize = const Size(481, 480); }, ); - goldenTestWithCallbacks( + goldenTest( goldenFile, Column( children: [ @@ -604,20 +604,20 @@ void main() { ), ], ), - ZetaChatItem, 'chat_item_slidable_actions', - before: (tester) async { + widgetType: ZetaChatItem, + setUp: (tester) async { tester.view.devicePixelRatio = 1.0; tester.view.physicalSize = const Size(481, 480); }, - after: (tester) async { + beforeComparison: (tester) async { final chatItemFinder = find.byType(ZetaChatItem); await tester.drag(chatItemFinder, const Offset(-200, 0)); await tester.pumpAndSettle(); }, ); - goldenTestWithCallbacks( + goldenTest( goldenFile, Column( children: [ @@ -644,20 +644,20 @@ void main() { ), ], ), - ZetaChatItem, 'chat_item_pale_slidable_buttons', - before: (tester) async { + widgetType: ZetaChatItem, + setUp: (tester) async { tester.view.devicePixelRatio = 1.0; tester.view.physicalSize = const Size(481, 480); }, - after: (tester) async { + beforeComparison: (tester) async { final chatItemFinder = find.byType(ZetaChatItem); await tester.drag(chatItemFinder, const Offset(-200, 0)); await tester.pumpAndSettle(); }, ); - goldenTestWithCallbacks( + goldenTest( goldenFile, Column( children: [ @@ -689,20 +689,20 @@ void main() { ), ], ), - ZetaChatItem, 'chat_item_pale_and_regular_buttons', - before: (tester) async { + widgetType: ZetaChatItem, + setUp: (tester) async { tester.view.devicePixelRatio = 1.0; tester.view.physicalSize = const Size(481, 480); }, - after: (tester) async { + beforeComparison: (tester) async { final chatItemFinder = find.byType(ZetaChatItem); await tester.drag(chatItemFinder, const Offset(-200, 0)); await tester.pumpAndSettle(); }, ); - goldenTestWithCallbacks( + goldenTest( goldenFile, Column( children: [ @@ -724,15 +724,15 @@ void main() { ), ], ), - ZetaChatItem, 'chat_item_custom_leading', - before: (tester) async { + widgetType: ZetaChatItem, + setUp: (tester) async { tester.view.devicePixelRatio = 1.0; tester.view.physicalSize = const Size(481, 480); }, ); - goldenTestWithCallbacks( + goldenTest( goldenFile, Column( children: [ @@ -756,20 +756,20 @@ void main() { ), ], ), - ZetaChatItem, 'chat_item_custom_slidable_buttons', - before: (tester) async { + widgetType: ZetaChatItem, + setUp: (tester) async { tester.view.devicePixelRatio = 1.0; tester.view.physicalSize = const Size(481, 480); }, - after: (tester) async { + beforeComparison: (tester) async { final chatItemFinder = find.byType(ZetaChatItem); await tester.drag(chatItemFinder, const Offset(-200, 0)); await tester.pumpAndSettle(); }, ); - goldenTestWithCallbacks( + goldenTest( goldenFile, Column( children: [ @@ -788,13 +788,13 @@ void main() { ), ], ), - ZetaChatItem, 'chat_item_small_screen_slidable_button', - before: (tester) async { + widgetType: ZetaChatItem, + setUp: (tester) async { tester.view.devicePixelRatio = 1.0; tester.view.physicalSize = const Size(315, 480); }, - after: (tester) async { + beforeComparison: (tester) async { final chatItemFinder = find.byType(ZetaChatItem); await tester.drag(chatItemFinder, const Offset(-200, 0)); await tester.pumpAndSettle(); diff --git a/test/src/components/checkbox/checkbox_test.dart b/test/src/components/checkbox/checkbox_test.dart index b718cef9..f0b6c598 100644 --- a/test/src/components/checkbox/checkbox_test.dart +++ b/test/src/components/checkbox/checkbox_test.dart @@ -139,14 +139,13 @@ void main() { }); }); group('Golden Tests', () { - goldenTestWithCallbacks( + goldenTest( goldenFile, ZetaCheckbox( onChanged: (value) {}, ), - ZetaCheckbox, 'checkbox_hover', - after: (tester) async { + beforeComparison: (tester) async { final checkboxFinder = find.byType(ZetaCheckbox); // Hover state @@ -165,7 +164,6 @@ void main() { value: true, onChanged: print, ), - ZetaCheckbox, 'checkbox_enabled', ); @@ -174,7 +172,6 @@ void main() { ZetaCheckbox( value: true, ), - ZetaCheckbox, 'checkbox_disabled', ); }); diff --git a/test/src/components/comms_button/comms_button_test.dart b/test/src/components/comms_button/comms_button_test.dart index 2ff9c648..539792c0 100644 --- a/test/src/components/comms_button/comms_button_test.dart +++ b/test/src/components/comms_button/comms_button_test.dart @@ -217,7 +217,6 @@ void main() { icon: ZetaIcons.phone, type: type, ), - ZetaCommsButton, 'CommsButton_${type.name}', ); } diff --git a/test/src/components/dialpad/dialpad_test.dart b/test/src/components/dialpad/dialpad_test.dart index b4f5cc7e..37575292 100644 --- a/test/src/components/dialpad/dialpad_test.dart +++ b/test/src/components/dialpad/dialpad_test.dart @@ -287,21 +287,18 @@ void main() { onNumber: print, onText: print, ), - ZetaDialPad, 'dialpad_enabled', screenSize: const Size(1000, 1000), ); goldenTest( goldenFile, const ZetaDialPad(), - ZetaDialPad, 'dialpad_disabled', screenSize: const Size(1000, 1000), ); goldenTest( goldenFile, const ZetaDialPadButton(primary: '1'), - ZetaDialPadButton, 'dialpadbutton', screenSize: const Size(1000, 1000), ); diff --git a/test/src/components/fab/fab_test.dart b/test/src/components/fab/fab_test.dart index 2820f8ef..636f8b80 100644 --- a/test/src/components/fab/fab_test.dart +++ b/test/src/components/fab/fab_test.dart @@ -211,15 +211,13 @@ void main() { label: 'Label', onPressed: () {}, ), - ZetaFAB, 'FAB_default', ); - goldenTestWithCallbacks( + goldenTest( goldenFile, ZetaFAB(scrollController: ScrollController(), label: 'Label', onPressed: () => {}), - ZetaFAB, 'FAB_pressed', - after: (tester) async { + beforeComparison: (tester) async { await tester.press(find.byType(ZetaFAB)); await tester.pumpAndSettle(); }, @@ -233,15 +231,13 @@ void main() { shape: ZetaWidgetBorder.rounded, size: ZetaFabSize.large, ), - ZetaFAB, 'FAB_inverse', ); - goldenTestWithCallbacks( + goldenTest( goldenFile, ZetaFAB(scrollController: ScrollController(), label: 'Label', onPressed: () => {}), - ZetaFAB, 'FAB_pressed', - after: (tester) async { + beforeComparison: (tester) async { await tester.press(find.byType(ZetaFAB)); await tester.pumpAndSettle(); }, @@ -255,13 +251,11 @@ void main() { type: ZetaFabType.secondary, shape: ZetaWidgetBorder.sharp, ), - ZetaFAB, 'FAB_secondary', ); goldenTest( goldenFile, ZetaFAB(scrollController: ScrollController(), label: 'Disabled'), - ZetaFAB, 'FAB_disabled', ); }); diff --git a/test/src/components/in_page_banner/in_page_banner_test.dart b/test/src/components/in_page_banner/in_page_banner_test.dart index f29bffed..8d840214 100644 --- a/test/src/components/in_page_banner/in_page_banner_test.dart +++ b/test/src/components/in_page_banner/in_page_banner_test.dart @@ -145,19 +145,16 @@ void main() { goldenTest( goldenFile, const ZetaInPageBanner(content: Text('Test'), title: 'Title'), - ZetaInPageBanner, 'in_page_banner_default', ); goldenTest( goldenFile, ZetaInPageBanner(content: const Text('Test'), onClose: () {}, status: ZetaWidgetStatus.negative), - ZetaInPageBanner, 'in_page_banner_negative', ); goldenTest( goldenFile, const ZetaInPageBanner(content: Text('Test'), status: ZetaWidgetStatus.positive), - ZetaInPageBanner, 'in_page_banner_positive', ); goldenTest( @@ -172,7 +169,6 @@ void main() { ), ], ), - ZetaInPageBanner, 'in_page_banner_buttons', ); }); diff --git a/test/src/components/password/password_input_test.dart b/test/src/components/password/password_input_test.dart index c67a7ad6..d31f0479 100644 --- a/test/src/components/password/password_input_test.dart +++ b/test/src/components/password/password_input_test.dart @@ -90,9 +90,9 @@ void main() { group('Styling Tests', () {}); group('Interaction Tests', () {}); group('Golden Tests', () { - goldenTest(goldenFile, ZetaPasswordInput(), ZetaPasswordInput, 'password_default'); + goldenTest(goldenFile, ZetaPasswordInput(), 'password_default'); final formKey = GlobalKey(); - goldenTestWithCallbacks( + goldenTest( goldenFile, Form( key: formKey, @@ -106,9 +106,8 @@ void main() { rounded: false, ), ), - ZetaPasswordInput, 'password_error', - after: (tester) async { + beforeComparison: (tester) async { formKey.currentState?.validate(); await tester.pump(); }, diff --git a/test/src/components/search_bar/search_bar_test.dart b/test/src/components/search_bar/search_bar_test.dart index 42e1ed7b..6869471a 100644 --- a/test/src/components/search_bar/search_bar_test.dart +++ b/test/src/components/search_bar/search_bar_test.dart @@ -219,14 +219,13 @@ void main() { }); }); group('Golden Tests', () { - goldenTest(goldenFile, ZetaSearchBar(), ZetaSearchBar, 'search_bar_default'); - goldenTest(goldenFile, ZetaSearchBar(), ZetaSearchBar, 'search_bar_medium'); + goldenTest(goldenFile, ZetaSearchBar(), 'search_bar_default'); + goldenTest(goldenFile, ZetaSearchBar(), 'search_bar_medium'); goldenTest( goldenFile, ZetaSearchBar( size: ZetaWidgetSize.small, ), - ZetaSearchBar, 'search_bar_small', ); goldenTest( @@ -234,7 +233,6 @@ void main() { ZetaSearchBar( shape: ZetaWidgetBorder.full, ), - ZetaSearchBar, 'search_bar_full', ); goldenTest( @@ -242,7 +240,6 @@ void main() { ZetaSearchBar( shape: ZetaWidgetBorder.sharp, ), - ZetaSearchBar, 'search_bar_sharp', ); }); diff --git a/test/src/components/tooltip/tooltip_test.dart b/test/src/components/tooltip/tooltip_test.dart index ef2e6efc..31453509 100644 --- a/test/src/components/tooltip/tooltip_test.dart +++ b/test/src/components/tooltip/tooltip_test.dart @@ -204,8 +204,8 @@ void main() { child: Text('Tooltip up'), ), ), - ZetaTooltip, 'arrow_up', + widgetType: ZetaTooltip, ); goldenTest( goldenFile, @@ -214,8 +214,8 @@ void main() { child: Text('Tooltip down'), ), ), - ZetaTooltip, 'arrow_down', + widgetType: ZetaTooltip, ); goldenTest( goldenFile, @@ -225,8 +225,8 @@ void main() { child: Text('Tooltip left'), ), ), - ZetaTooltip, 'arrow_left', + widgetType: ZetaTooltip, ); goldenTest( goldenFile, @@ -236,8 +236,8 @@ void main() { child: Text('Tooltip right'), ), ), - ZetaTooltip, 'arrow_right', + widgetType: ZetaTooltip, ); }); group('Performance Tests', () {}); diff --git a/test/src/components/top_app_bar/extended_top_app_bar_test.dart b/test/src/components/top_app_bar/extended_top_app_bar_test.dart deleted file mode 100644 index ecc49885..00000000 --- a/test/src/components/top_app_bar/extended_top_app_bar_test.dart +++ /dev/null @@ -1,169 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.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() { - const goldenFile = GoldenFiles(component: 'top_app_bar'); - - setUpAll(() { - goldenFileComparator = TolerantComparator(goldenFile.uri); - }); - - testWidgets('ZetaExtendedAppBarDelegate builds correctly', (WidgetTester tester) async { - const title = Text('Title'); - final actions = [IconButton(icon: const Icon(Icons.search), onPressed: () {})]; - final leading = IconButton(icon: const Icon(Icons.menu), onPressed: () {}); - const boxKey = Key('box'); - tester.view.devicePixelRatio = 1.0; - tester.view.physicalSize = const Size(481, 480); - - await tester.pumpWidget( - TestApp( - home: Builder( - builder: (context) { - return SizedBox( - child: CustomScrollView( - slivers: [ - ZetaTopAppBar.extended(leading: leading, title: title, actions: actions), - SliverToBoxAdapter( - child: Container( - width: 800, - height: 700, - color: Zeta.of(context).colors.surfaceSelectedHover, - key: boxKey, - ), - ), - ], - ), - ); - }, - ), - ), - ); - - final boxFinder = find.byKey(boxKey); - expect(boxFinder, findsOneWidget); - - await tester.drag(boxFinder.first, const Offset(0, -100)); - await tester.pumpAndSettle(); - - final appBarFinder = find.byType(ZetaTopAppBar); - expect(appBarFinder, findsOneWidget); - - final titleFinder = find.descendant(of: appBarFinder, matching: find.byWidget(title)); - expect(titleFinder, findsOneWidget); - - final actionsFinder = find.descendant(of: appBarFinder, matching: find.byWidget(actions[0])); - expect(actionsFinder, findsOneWidget); - - final leadingFinder = find.descendant(of: appBarFinder, matching: find.byWidget(leading)); - expect(leadingFinder, findsOneWidget); - - await expectLater( - appBarFinder, - matchesGoldenFile(goldenFile.getFileUri('extended_app_bar_shrinks')), - ); - }); - - testWidgets('ZetaExtendedAppBarDelegate shrinks correctly with padding', (WidgetTester tester) async { - const title = Text('Title'); - final actions = [IconButton(icon: const Icon(Icons.search), onPressed: () {})]; - final leading = IconButton(icon: const Icon(Icons.menu), onPressed: () {}); - const boxKey = Key('box'); - tester.view.devicePixelRatio = 1.0; - tester.view.physicalSize = const Size(481, 480); - - await tester.pumpWidget( - TestApp( - home: Builder( - builder: (context) { - return SizedBox( - child: CustomScrollView( - slivers: [ - ZetaTopAppBar.extended(leading: leading, title: title, actions: actions), - SliverToBoxAdapter( - child: Container( - width: 800, - height: 700, - color: Zeta.of(context).colors.surfaceSelectedHover, - key: boxKey, - ), - ), - ], - ), - ); - }, - ), - ), - ); - - final boxFinder = find.byKey(boxKey); - expect(boxFinder, findsOneWidget); - - await tester.drag(boxFinder.first, const Offset(0, -100)); - await tester.pumpAndSettle(); - - final appBarFinder = find.byType(ZetaTopAppBar); - expect(appBarFinder, findsOneWidget); - - final positionedFinder = find.descendant(of: appBarFinder, matching: find.byType(Positioned)); - - final positionedWidget = tester.widget(positionedFinder.first); - expect(positionedWidget.left, 60); - }); - testWidgets('ZetaExtendedAppBarDelegate shrinks correctly with padding and no leading', (WidgetTester tester) async { - const title = Text('Title'); - final actions = [IconButton(icon: const Icon(Icons.search), onPressed: () {})]; - - const boxKey = Key('box'); - tester.view.devicePixelRatio = 1.0; - tester.view.physicalSize = const Size(481, 480); - - await tester.pumpWidget( - TestApp( - home: Builder( - builder: (context) { - return SizedBox( - child: CustomScrollView( - slivers: [ - ZetaTopAppBar.extended(title: title, actions: actions), - SliverToBoxAdapter( - child: Container( - width: 800, - height: 700, - color: Zeta.of(context).colors.surfaceSelectedHover, - key: boxKey, - ), - ), - ], - ), - ); - }, - ), - ), - ); - - final boxFinder = find.byKey(boxKey); - expect(boxFinder, findsOneWidget); - - await tester.drag(boxFinder.first, const Offset(0, -100)); - await tester.pumpAndSettle(); - - final appBarFinder = find.byType(ZetaTopAppBar); - expect(appBarFinder, findsOneWidget); - - final positionedFinder = find.descendant(of: appBarFinder, matching: find.byType(Positioned)); - - final positionedWidget = tester.widget(positionedFinder.first); - expect(positionedWidget.left, 16); - - await expectLater( - appBarFinder, - matchesGoldenFile(goldenFile.getFileUri('extended_app_bar_shrinks_with_no_leading')), - ); - }); -} diff --git a/test/src/components/top_app_bar/golden/extended_app_bar_shrinks.png b/test/src/components/top_app_bar/golden/extended_app_bar_shrinks.png index 6a0c1237..b821a451 100644 Binary files a/test/src/components/top_app_bar/golden/extended_app_bar_shrinks.png and b/test/src/components/top_app_bar/golden/extended_app_bar_shrinks.png differ diff --git a/test/src/components/top_app_bar/golden/extended_app_bar_shrinks_with_no_leading.png b/test/src/components/top_app_bar/golden/extended_app_bar_shrinks_with_no_leading.png index 87b9ce7d..7b4e3f6e 100644 Binary files a/test/src/components/top_app_bar/golden/extended_app_bar_shrinks_with_no_leading.png and b/test/src/components/top_app_bar/golden/extended_app_bar_shrinks_with_no_leading.png differ diff --git a/test/src/components/top_app_bar/golden/top_app_bar_centered.png b/test/src/components/top_app_bar/golden/top_app_bar_centered.png new file mode 100644 index 00000000..ce49f585 Binary files /dev/null and b/test/src/components/top_app_bar/golden/top_app_bar_centered.png differ diff --git a/test/src/components/top_app_bar/golden/top_app_bar_centered_actions.png b/test/src/components/top_app_bar/golden/top_app_bar_centered_actions.png new file mode 100644 index 00000000..2e6ac890 Binary files /dev/null and b/test/src/components/top_app_bar/golden/top_app_bar_centered_actions.png differ diff --git a/test/src/components/top_app_bar/golden/top_app_bar_default.png b/test/src/components/top_app_bar/golden/top_app_bar_default.png new file mode 100644 index 00000000..ae2508eb Binary files /dev/null and b/test/src/components/top_app_bar/golden/top_app_bar_default.png differ diff --git a/test/src/components/top_app_bar/golden/top_app_bar_default_actions.png b/test/src/components/top_app_bar/golden/top_app_bar_default_actions.png new file mode 100644 index 00000000..725391c6 Binary files /dev/null and b/test/src/components/top_app_bar/golden/top_app_bar_default_actions.png differ diff --git a/test/src/components/top_app_bar/golden/top_app_bar_extended.png b/test/src/components/top_app_bar/golden/top_app_bar_extended.png new file mode 100644 index 00000000..11817f09 Binary files /dev/null and b/test/src/components/top_app_bar/golden/top_app_bar_extended.png differ diff --git a/test/src/components/top_app_bar/golden/top_app_bar_extended_actions.png b/test/src/components/top_app_bar/golden/top_app_bar_extended_actions.png new file mode 100644 index 00000000..9677a10e Binary files /dev/null and b/test/src/components/top_app_bar/golden/top_app_bar_extended_actions.png differ diff --git a/test/src/components/top_app_bar/golden/top_app_bar_search.png b/test/src/components/top_app_bar/golden/top_app_bar_search.png new file mode 100644 index 00000000..00604a33 Binary files /dev/null and b/test/src/components/top_app_bar/golden/top_app_bar_search.png differ diff --git a/test/src/components/top_app_bar/golden/top_app_bar_search_active.png b/test/src/components/top_app_bar/golden/top_app_bar_search_active.png new file mode 100644 index 00000000..378baace Binary files /dev/null and b/test/src/components/top_app_bar/golden/top_app_bar_search_active.png differ diff --git a/test/src/components/top_app_bar/golden/top_app_bar_search_centered.png b/test/src/components/top_app_bar/golden/top_app_bar_search_centered.png new file mode 100644 index 00000000..e65986b0 Binary files /dev/null and b/test/src/components/top_app_bar/golden/top_app_bar_search_centered.png differ diff --git a/test/src/components/top_app_bar/top_app_bar_test.dart b/test/src/components/top_app_bar/top_app_bar_test.dart new file mode 100644 index 00000000..8ae60e83 --- /dev/null +++ b/test/src/components/top_app_bar/top_app_bar_test.dart @@ -0,0 +1,557 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.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() { + const String parentFolder = 'top_app_bar'; + + const goldenFile = GoldenFiles(component: parentFolder); + setUpAll(() { + goldenFileComparator = TolerantComparator(goldenFile.uri); + }); + + group('ZetaTopAppBar Accessibility Tests', () { + testWidgets('ZetaTopAppBar meets accessibility requirements', (WidgetTester tester) async { + final SemanticsHandle handle = tester.ensureSemantics(); + await tester.pumpWidget( + TestApp( + home: ZetaTopAppBar( + title: const Text('Title'), + actions: [ + IconButton( + icon: const Icon(Icons.search), + tooltip: 'Search', + onPressed: () {}, + ), + ], + leading: IconButton( + icon: const Icon(Icons.menu), + tooltip: 'Menu', + onPressed: () {}, + ), + ), + ), + ); + await expectLater(tester, meetsGuideline(androidTapTargetGuideline)); + await expectLater(tester, meetsGuideline(iOSTapTargetGuideline)); + await expectLater(tester, meetsGuideline(labeledTapTargetGuideline)); + await expectLater(tester, meetsGuideline(textContrastGuideline)); + + handle.dispose(); + }); + + testWidgets('ZetaTopAppBar passes semantic labels to the search actions', (WidgetTester tester) async { + const microphoneSemanticLabel = 'Search with voice'; + const clearSemanticLabel = 'Clear search'; + const searchBackSemanticLabel = 'Back'; + const searchSemanticLabel = 'Search'; + final ZetaSearchController searchController = ZetaSearchController(); + await tester.pumpWidget( + TestApp( + home: ZetaTopAppBar.search( + title: const Text('Title'), + microphoneSemanticLabel: microphoneSemanticLabel, + clearSemanticLabel: clearSemanticLabel, + searchController: searchController, + searchSemanticLabel: searchSemanticLabel, + searchBackSemanticLabel: searchBackSemanticLabel, + onSearchMicrophoneIconPressed: () {}, + ), + ), + ); + + expect( + find.bySemanticsLabel(searchSemanticLabel), + findsOneWidget, + ); + + searchController.startSearch(); + await tester.pumpAndSettle(); + + expect( + find.bySemanticsLabel(microphoneSemanticLabel), + findsOneWidget, + ); + expect( + find.bySemanticsLabel(clearSemanticLabel), + findsOneWidget, + ); + expect( + find.bySemanticsLabel(searchBackSemanticLabel), + findsOneWidget, + ); + }); + }); + + group('ZetaTopAppBar Content Tests', () { + final debugFillProperties = { + 'titleTextStyle': 'null', + 'onSearch': 'null', + 'automaticallyImplyLeading': 'true', + 'onSearchMicrophoneIconPressed': 'null', + 'searchController': 'null', + 'searchHintText': 'null', + 'type': 'defaultAppBar', + 'shrinks': 'false', + 'clearSemanticLabel': 'null', + 'microphoneSemanticLabel': 'null', + 'searchSemanticLabel': 'null', + 'searchBackSemanticLabel': 'null', + }; + debugFillPropertiesTest( + const ZetaTopAppBar(), + debugFillProperties, + ); + + test( + 'ZetaTopAppBar Search throws an assertion error if its type is set to extended', + () { + expect( + () => ZetaTopAppBar.search( + title: const Text('Title'), + type: ZetaTopAppBarType.extended, + ), + throwsA(isA()), + ); + }, + ); + + testWidgets('ZetaTopAppBar displays the title correctly', (WidgetTester tester) async { + await tester.pumpWidget( + const TestApp( + home: ZetaTopAppBar( + title: Text('Title'), + ), + ), + ); + + expect(find.text('Title'), findsOneWidget); + }); + + testWidgets('ZetaTopAppBar displays the leading widget correctly', (WidgetTester tester) async { + await tester.pumpWidget( + TestApp( + home: ZetaTopAppBar( + title: const Text('Title'), + leading: IconButton( + icon: const Icon(Icons.menu), + onPressed: () {}, + ), + ), + ), + ); + + expect(find.byIcon(Icons.menu), findsOneWidget); + }); + + testWidgets('ZetaTopAppBar displays the actions correctly', (WidgetTester tester) async { + await tester.pumpWidget( + TestApp( + home: ZetaTopAppBar( + title: const Text('Title'), + actions: [ + IconButton( + icon: const Icon(Icons.search), + onPressed: () {}, + ), + ], + ), + ), + ); + + expect(find.byIcon(Icons.search), findsOneWidget); + }); + + testWidgets('ZetaExtendedAppBarDelegate builds correctly', (WidgetTester tester) async { + const title = Text('Title'); + final actions = [IconButton(icon: const Icon(Icons.search), onPressed: () {})]; + final leading = IconButton(icon: const Icon(Icons.menu), onPressed: () {}); + const boxKey = Key('box'); + + await tester.pumpWidget( + TestApp( + home: Builder( + builder: (context) { + return SizedBox( + child: CustomScrollView( + slivers: [ + ZetaTopAppBar.extended(leading: leading, title: title, actions: actions), + SliverToBoxAdapter( + child: Container( + width: 800, + height: 700, + color: Zeta.of(context).colors.surfaceSelectedHover, + key: boxKey, + ), + ), + ], + ), + ); + }, + ), + ), + ); + + final boxFinder = find.byKey(boxKey); + expect(boxFinder, findsOneWidget); + + await tester.drag(boxFinder.first, const Offset(0, -100)); + await tester.pumpAndSettle(); + + final appBarFinder = find.byType(ZetaTopAppBar); + expect(appBarFinder, findsOneWidget); + + final titleFinder = find.descendant(of: appBarFinder, matching: find.byWidget(title)); + expect(titleFinder, findsOneWidget); + + final actionsFinder = find.descendant(of: appBarFinder, matching: find.byWidget(actions[0])); + expect(actionsFinder, findsOneWidget); + + final leadingFinder = find.descendant(of: appBarFinder, matching: find.byWidget(leading)); + expect(leadingFinder, findsOneWidget); + }); + }); + + group('ZetaTopAppBar Dimensions Tests', () { + testWidgets('ZetaTopAppBar has the correct height', (WidgetTester tester) async { + await tester.pumpWidget( + const TestApp( + home: ZetaTopAppBar( + title: Text('Title'), + ), + ), + ); + + final appBar = tester.widget(find.byType(AppBar)); + expect(appBar.preferredSize.height, 64); + }); + }); + + group('ZetaTopAppBar Styling Tests', () { + testWidgets('ZetaTopAppBar has the correct background color', (WidgetTester tester) async { + await tester.pumpWidget( + const TestApp( + home: ZetaTopAppBar( + title: Text('Title'), + ), + ), + ); + + final BuildContext context = tester.element(find.byType(MaterialApp)); + final appBar = tester.widget(find.byType(AppBar)); + expect(appBar.backgroundColor, Zeta.of(context).colors.surfaceDefault); + }); + }); + + group('ZetaTopAppBar Interaction Tests', () { + late ZetaSearchController searchController; + const searchLabel = 'Search'; + const clearLabel = 'Clear'; + const backLabel = 'Back'; + + late Widget subject; + + setUp(() { + searchController = ZetaSearchController(); + subject = TestApp( + home: ZetaTopAppBar.search( + title: const Text('Title'), + searchController: searchController, + searchSemanticLabel: searchLabel, + clearSemanticLabel: clearLabel, + searchBackSemanticLabel: backLabel, + ), + ); + }); + + testWidgets( + 'ZetaTopAppBar Search opens and closes the search bar when the search/back icon is tapped', + (WidgetTester tester) async { + await tester.pumpWidget(subject); + + expect(searchController.isEnabled, isFalse); + await tester.tap(find.bySemanticsLabel(searchLabel)); + await tester.pumpAndSettle(); + + expect(searchController.isEnabled, isTrue); + + await tester.tap(find.bySemanticsLabel(backLabel)); + await tester.pumpAndSettle(); + + expect(searchController.isEnabled, isFalse); + }, + ); + + testWidgets( + 'ZetaTopAppBar Search allows text to be typed in the search field', + (WidgetTester tester) async { + await tester.pumpWidget(subject); + + searchController.startSearch(); + await tester.pumpAndSettle(); + + await tester.enterText(find.byType(TextField), 'Search text'); + expect(searchController.text, 'Search text'); + }, + ); + + testWidgets( + 'ZetaTopAppBar Search gets cleared when the clear button is tapped', + (WidgetTester tester) async { + await tester.pumpWidget(subject); + + searchController.startSearch(); + await tester.pumpAndSettle(); + + await tester.enterText(find.byType(TextField), 'Search text'); + await tester.tap(find.bySemanticsLabel(clearLabel)); + expect(searchController.text, ''); + }, + ); + + testWidgets( + 'ZetaTopAppBar Search submits the correct text when the search input is submitted', + (WidgetTester tester) async { + String inputtedText = ''; + await tester.pumpWidget( + TestApp( + home: ZetaTopAppBar.search( + title: const Text('Title'), + searchController: searchController, + onSearch: (String text) { + inputtedText = text; + }, + ), + ), + ); + + searchController.startSearch(); + await tester.pumpAndSettle(); + + await tester.enterText(find.byType(TextField), 'Search text'); + await tester.testTextInput.receiveAction(TextInputAction.done); + expect(inputtedText, 'Search text'); + }, + ); + + testWidgets('ZetaExtendedAppBarDelegate shrinks correctly with padding', (WidgetTester tester) async { + const title = Text('Title'); + final actions = [IconButton(icon: const Icon(Icons.search), onPressed: () {})]; + final leading = IconButton(icon: const Icon(Icons.menu), onPressed: () {}); + const boxKey = Key('box'); + + await tester.pumpWidget( + TestApp( + home: Builder( + builder: (context) { + return SizedBox( + child: CustomScrollView( + slivers: [ + ZetaTopAppBar.extended(leading: leading, title: title, actions: actions), + SliverToBoxAdapter( + child: Container( + width: 800, + height: 700, + color: Zeta.of(context).colors.surfaceSelectedHover, + key: boxKey, + ), + ), + ], + ), + ); + }, + ), + ), + ); + + final boxFinder = find.byKey(boxKey); + expect(boxFinder, findsOneWidget); + + await tester.drag(boxFinder.first, const Offset(0, -100)); + await tester.pumpAndSettle(); + + final appBarFinder = find.byType(ZetaTopAppBar); + expect(appBarFinder, findsOneWidget); + + final positionedFinder = find.descendant(of: appBarFinder, matching: find.byType(Positioned)); + + final positionedWidget = tester.widget(positionedFinder.first); + expect(positionedWidget.left, 64); + }); + + testWidgets('ZetaExtendedAppBarDelegate shrinks correctly with padding and no leading', + (WidgetTester tester) async { + const title = Text('Title'); + final actions = [IconButton(icon: const Icon(Icons.search), onPressed: () {})]; + + const boxKey = Key('box'); + + await tester.pumpWidget( + TestApp( + home: Builder( + builder: (context) { + return SizedBox( + child: CustomScrollView( + slivers: [ + ZetaTopAppBar.extended(title: title, actions: actions), + SliverToBoxAdapter( + child: Container( + width: 800, + height: 700, + color: Zeta.of(context).colors.surfaceSelectedHover, + key: boxKey, + ), + ), + ], + ), + ); + }, + ), + ), + ); + + final boxFinder = find.byKey(boxKey); + expect(boxFinder, findsOneWidget); + + await tester.drag(boxFinder.first, const Offset(0, -100)); + await tester.pumpAndSettle(); + + final appBarFinder = find.byType(ZetaTopAppBar); + expect(appBarFinder, findsOneWidget); + + final positionedFinder = find.descendant(of: appBarFinder, matching: find.byType(Positioned)); + + final positionedWidget = tester.widget(positionedFinder.first); + expect(positionedWidget.left, 16); + }); + }); + + group('ZetaTopAppBar Golden Tests', () { + goldenTest( + goldenFile, + const ZetaTopAppBar( + title: Text('Title'), + ), + 'top_app_bar_default', + ); + + goldenTest( + goldenFile, + ZetaTopAppBar( + title: const Text('Title'), + leading: IconButton(onPressed: () {}, icon: const Icon(Icons.menu)), + actions: [ + IconButton( + icon: const Icon(Icons.search), + tooltip: 'Search', + onPressed: () {}, + ), + ], + ), + 'top_app_bar_default_actions', + ); + + goldenTest( + goldenFile, + const ZetaTopAppBar.centered( + title: Text('Title'), + ), + 'top_app_bar_centered', + ); + + goldenTest( + goldenFile, + ZetaTopAppBar.centered( + leading: IconButton(onPressed: () {}, icon: const Icon(Icons.menu)), + actions: [ + IconButton( + icon: const Icon(Icons.search), + tooltip: 'Search', + onPressed: () {}, + ), + ], + title: const Text('Title'), + ), + 'top_app_bar_centered_actions', + ); + + goldenTest( + goldenFile, + const ZetaTopAppBar.search( + title: Text('Search'), + ), + 'top_app_bar_search', + ); + + goldenTest( + goldenFile, + const ZetaTopAppBar.search( + title: Text('Search'), + type: ZetaTopAppBarType.centered, + ), + 'top_app_bar_search_centered', + ); + + final searchController = ZetaSearchController(); + goldenTest( + goldenFile, + ZetaTopAppBar.search( + title: const Text('Search'), + type: ZetaTopAppBarType.centered, + searchController: searchController, + ), + beforeComparison: (tester) async { + searchController.startSearch(); + await tester.pumpAndSettle(); + }, + 'top_app_bar_search_active', + ); + + goldenTest( + goldenFile, + const CustomScrollView( + slivers: [ + ZetaTopAppBar.extended( + title: Text('Title'), + ), + ], + ), + widgetType: ZetaTopAppBar, + beforeComparison: (tester) async { + searchController.startSearch(); + await tester.pumpAndSettle(); + }, + 'top_app_bar_extended', + ); + + goldenTest( + goldenFile, + CustomScrollView( + slivers: [ + ZetaTopAppBar.extended( + title: const Text('Title'), + actions: [ + IconButton( + icon: const Icon(Icons.search), + tooltip: 'Search', + onPressed: () {}, + ), + ], + ), + ], + ), + widgetType: ZetaTopAppBar, + beforeComparison: (tester) async { + searchController.startSearch(); + await tester.pumpAndSettle(); + }, + 'top_app_bar_extended_actions', + ); + }); + + group('ZetaTopAppBar Performance Tests', () {}); +} diff --git a/test/test_utils/utils.dart b/test/test_utils/utils.dart index 5117d045..ec5c9704 100644 --- a/test/test_utils/utils.dart +++ b/test/test_utils/utils.dart @@ -34,13 +34,19 @@ class GoldenFiles { void goldenTest( GoldenFiles goldenFile, Widget widget, - Type widgetType, String fileName, { + Type? widgetType, ThemeMode themeMode = ThemeMode.system, Size? screenSize, bool? rounded, + Future Function(WidgetTester)? setUp, + Future Function(WidgetTester)? beforeComparison, }) { testWidgets('$fileName golden', (WidgetTester tester) async { + final computedType = widgetType ?? widget.runtimeType; + if (setUp != null) { + await setUp(tester); + } await tester.pumpWidget( TestApp( screenSize: screenSize, @@ -50,42 +56,12 @@ void goldenTest( ), ); - await expectLater( - find.byType(widgetType), - matchesGoldenFile(goldenFile.getFileUri(fileName)), - ); - }); -} - -void goldenTestWithCallbacks( - GoldenFiles goldenFile, - Widget widget, - Type widgetType, - String fileName, { - Future Function(WidgetTester)? before, - Future Function(WidgetTester)? after, - bool darkMode = false, - Size? screenSize, -}) { - testWidgets('$fileName golden', (WidgetTester tester) async { - if (before != null) { - await before(tester); - } - - await tester.pumpWidget( - TestApp( - screenSize: screenSize, - themeMode: darkMode ? ThemeMode.dark : ThemeMode.light, - home: widget, - ), - ); - - if (after != null) { - await after(tester); + if (beforeComparison != null) { + await beforeComparison(tester); } await expectLater( - find.byType(widgetType), + find.byType(computedType), matchesGoldenFile(goldenFile.getFileUri(fileName)), ); });