garage_auth, garage_entitlements, garage_iap and garage_ui, moved out of Garage-Services and Metro-Map-Maker into one public repo. MIT, one readme, docs under docs/. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013F4NWNvYcdeSgqbWMT1VQ7
138 lines
4.5 KiB
Dart
138 lines
4.5 KiB
Dart
import "package:flutter/widgets.dart";
|
|
import "package:flutter_test/flutter_test.dart";
|
|
import "package:garage_ui/garage_ui.dart";
|
|
|
|
import "support/test_scheme.dart";
|
|
|
|
Widget _select() => Select<String>(
|
|
value: "off",
|
|
itemBuilder: (ctx, v) => const Text("Off"),
|
|
onChanged: (_) {},
|
|
popup: SelectPopup(
|
|
items: SelectItemList(
|
|
children: const [
|
|
SelectItemButton<String>(value: "off", child: Text("Off")),
|
|
SelectItemButton<String>(
|
|
value: "optional",
|
|
child: Text("Optional"),
|
|
),
|
|
SelectItemButton<String>(
|
|
value: "required",
|
|
child: Text("Required"),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
|
|
Widget _host({required Alignment where, double overlayWidth = 600}) =>
|
|
Directionality(
|
|
textDirection: TextDirection.ltr,
|
|
child: GarageTheme(
|
|
data: ThemeData(colorScheme: testScheme, density: const Density()),
|
|
child: Align(
|
|
alignment: Alignment.topLeft,
|
|
child: SizedBox(
|
|
width: overlayWidth,
|
|
height: 400,
|
|
child: Overlay(
|
|
initialEntries: [
|
|
OverlayEntry(
|
|
builder: (_) => Align(
|
|
alignment: where,
|
|
// narrow, the way a ghost select showing "Off" is narrow
|
|
child: SizedBox(width: 70, child: _select()),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
void main() {
|
|
testWidgets("it sizes to its widest row, not to the room available", (
|
|
tester,
|
|
) async {
|
|
// given a ceiling, a ListView based popup filled the ceiling - a 70px
|
|
// trigger opening a popup most of 600px wide.
|
|
await tester.pumpWidget(_host(where: Alignment.topLeft));
|
|
await tester.tap(find.byType(Select<String>));
|
|
await tester.pumpAndSettle();
|
|
|
|
final row = tester.getRect(
|
|
find.ancestor(
|
|
of: find.text("Required"),
|
|
matching: find.byType(SelectItemButton<String>),
|
|
),
|
|
);
|
|
|
|
// wide enough for the longest option and its tick, nowhere near the 600
|
|
// it was offered
|
|
expect(row.width, greaterThan(70));
|
|
expect(row.width, lessThan(300));
|
|
expect(tester.takeException(), isNull);
|
|
});
|
|
|
|
testWidgets("a narrow trigger near an edge still gets a readable popup", (
|
|
tester,
|
|
) async {
|
|
// the sliver: clamped to the room on the side the placement picked by
|
|
// testing the FLOOR, the popup came out one character wide.
|
|
await tester.pumpWidget(_host(where: Alignment.topRight));
|
|
await tester.tap(find.byType(Select<String>));
|
|
await tester.pumpAndSettle();
|
|
|
|
for (final label in ["Off", "Optional", "Required"]) {
|
|
final row = tester.getRect(
|
|
find.ancestor(
|
|
of: find.text(label),
|
|
matching: find.byType(SelectItemButton<String>),
|
|
),
|
|
);
|
|
// one line each, not a column of letters
|
|
expect(row.height, lessThan(40), reason: label);
|
|
// and never narrower than the trigger it came out of
|
|
expect(row.width, greaterThanOrEqualTo(70.0), reason: label);
|
|
}
|
|
expect(tester.takeException(), isNull);
|
|
});
|
|
|
|
testWidgets("the popup grows past a narrow trigger to fit its rows", (
|
|
tester,
|
|
) async {
|
|
await tester.pumpWidget(_host(where: Alignment.topLeft));
|
|
await tester.tap(find.byType(Select<String>));
|
|
await tester.pumpAndSettle();
|
|
|
|
// the row that wrapped onto two lines when the popup was pinned to a
|
|
// 70px trigger
|
|
final optional = tester.getSize(find.text("Optional"));
|
|
expect(optional.height, lessThan(30));
|
|
expect(tester.takeException(), isNull);
|
|
});
|
|
|
|
testWidgets("and it stays inside the overlay when the trigger is at the "
|
|
"right edge", (tester) async {
|
|
// the case that broke: a select hard against the right of a settings row.
|
|
// Left aligned to the trigger, a wider popup runs off the edge and gets
|
|
// clipped by whatever bounds it - a square corner and rows cut mid-word.
|
|
await tester.pumpWidget(_host(where: Alignment.topRight));
|
|
await tester.tap(find.byType(Select<String>));
|
|
await tester.pumpAndSettle();
|
|
|
|
for (final label in ["Off", "Optional", "Required"]) {
|
|
final row = tester.getRect(
|
|
find.ancestor(
|
|
of: find.text(label),
|
|
matching: find.byType(SelectItemButton<String>),
|
|
),
|
|
);
|
|
expect(row.left, greaterThanOrEqualTo(0.0), reason: label);
|
|
expect(row.right, lessThanOrEqualTo(600.0), reason: label);
|
|
}
|
|
expect(tester.takeException(), isNull);
|
|
});
|
|
}
|