import "package:flutter/widgets.dart"; import "package:flutter_test/flutter_test.dart"; import "package:garage_ui/garage_ui.dart"; import "support/test_scheme.dart"; // A property row used to be label + control and nothing else, so a page // needing to explain a setting had to either fold it into the label or give up // and hardcode a Text somewhere. Two slots now: // // subtitle - inside the label column, right aligned against the split // description - full width under the whole row // // Both render at density.textXxs, which is the rung below the control font - // it didnt exist before either, because in A&A the control font is already the // smallest thing on screen. Widget _host(Widget child, {Density density = const Density()}) => Directionality( textDirection: TextDirection.ltr, child: GarageTheme( data: ThemeData(colorScheme: testScheme, density: density), child: Center(child: SizedBox(width: 400, child: child)), ), ); double _fontOf(WidgetTester tester, String text) { final widget = tester.widget(find.text(text)); return widget.style!.fontSize!; } void main() { testWidgets("subtitle and description both render at textXxs", ( tester, ) async { await tester.pumpWidget( _host( PropertyRow( label: "Username", subtitle: "Display name used across dashboard", description: "Changing this breaks any links to your old name.", scheme: testScheme, child: const SizedBox(), ), ), ); const density = Density(); expect(density.textXxs, 9.0); // 10 * 0.875, rounded expect(_fontOf(tester, "Display name used across dashboard"), 9.0); expect(_fontOf(tester, "Changing this breaks any links to your old name."), 9.0); }); testWidgets("both slots track the density", (tester) async { await tester.pumpWidget( _host( PropertyRow( label: "Username", subtitle: "a subtitle", scheme: testScheme, child: const SizedBox(), ), density: const Density(fontSize: 16.0, lineHeight: 1.25), ), ); expect(_fontOf(tester, "a subtitle"), 14.0); // 16 * 0.875 }); testWidgets("a description makes the row taller, not the control shorter", ( tester, ) async { Future heightWith({String? description}) async { await tester.pumpWidget( _host( PropertyRow( key: const Key("row"), label: "Username", description: description, scheme: testScheme, child: const SizedBox(), ), ), ); // the row eases between heights now, so the frame straight after the // pump is still the old one. await tester.pumpAndSettle(); return tester.getSize(find.byKey(const Key("row"))).height; } final bare = await heightWith(); final withCopy = await heightWith(description: "some copy"); // the bare row is the row minimum - controlHeight plus a step, so that // neither the control nor the label column is the thing setting it. a // description adds on top of that rather than squeezing into it. expect(bare, const Density().propertyRowHeight); expect(withCopy, greaterThan(bare)); }); testWidgets("the gap between section rows is gapXs, off the density", ( tester, ) async { // the numbers PropertiesSection used to hardcode - 4 between rows, 8 under // the last - are exactly gapXs and gapMd at compact, which is the tell they // were always meant to be these tokens. at normal they should now move. const compact = Density(); const normal = Density.normal(); expect(compact.gapXs, 4.0); expect(normal.gapXs, 5.0); Future gapBetweenRows(Density density) async { await tester.pumpWidget( _host( PropertiesSection( title: "Profile", scheme: testScheme, collapsed: false, onToggle: () {}, rows: [ for (var i = 0; i < 2; i++) PropertyRow( key: Key("row$i"), label: "Row $i", scheme: testScheme, child: const SizedBox(), ), ], ), density: density, ), ); final first = tester.getRect(find.byKey(const Key("row0"))); final second = tester.getRect(find.byKey(const Key("row1"))); return second.top - first.bottom; } expect(await gapBetweenRows(compact), compact.gapXs); expect(await gapBetweenRows(normal), normal.gapXs); }); testWidgets("a subtitle doesnt change the row height", (tester) async { // the whole reason the minimum sits above controlHeight. a label plus a // subtitle is laid out on the font's own metrics; at plain controlHeight // that made the LABEL COLUMN set the row height on some fonts and not // others. both rows have to come out the same. Future h({String? subtitle}) async { await tester.pumpWidget( _host( PropertyRow( key: const Key("row"), label: "Username", subtitle: subtitle, scheme: testScheme, child: const TextField(), ), ), ); // the row eases between heights now, so the frame straight after the // pump is still the old one. await tester.pumpAndSettle(); return tester.getSize(find.byKey(const Key("row"))).height; } expect(await h(subtitle: "a subtitle"), await h()); }); test("the row minimum clears whichever of the two is taller", () { // at compact the LABEL COLUMN is the binding one - 26 against a 23px // control - which is the whole reason this token exists. at the roomier // tiers the control wins again. either way the row clears both, so a // subtitle never changes the height. const compact = Density(); expect(compact.labelColumnHeight, 26.0); expect(compact.controlHeight, 23.0); expect(compact.propertyRowHeight, 28.0); const normal = Density.normal(); expect(normal.labelColumnHeight, 26.0); expect(normal.propertyRowHeight, normal.controlHeight + normal.gapXxs); for (final d in [compact, normal]) { expect(d.propertyRowHeight, greaterThan(d.labelColumnHeight)); expect(d.propertyRowHeight, greaterThan(d.controlHeight)); } }); }