The Garage SDKs, in the open
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
This commit is contained in:
@@ -0,0 +1,137 @@
|
||||
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);
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user