tane/apps/app_seeds/test/ui/edge_fade_test.dart
vjrj 05227706fd fix(ux): make the scroll cue visible — edge scrim + chevron
The fade-to-transparent was invisible: the row sits on the same pale
canvas, so masking the edge to transparent just revealed more of the
same colour (and often landed on an inter-chip gap). Replace it with a
surface-coloured scrim the chips slide under, topped by an outward
chevron, shown only on an edge with more content. RTL-mirrored.
2026-07-11 07:25:36 +02:00

135 lines
4.5 KiB
Dart

import 'package:tane/ui/edge_fade.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
// A horizontal strip of [count] fixed-width boxes inside an EdgeFade-wrapped
// SingleChildScrollView, laid out at the default 800px test surface width.
Widget harness({
required int count,
TextDirection direction = TextDirection.ltr,
ScrollController? controller,
}) {
return Directionality(
textDirection: direction,
child: EdgeFade(
child: SingleChildScrollView(
controller: controller,
scrollDirection: Axis.horizontal,
child: Row(
children: [
for (var i = 0; i < count; i++)
const SizedBox(width: 100, height: 40),
],
),
),
),
);
}
EdgeFadeState state(WidgetTester tester) =>
tester.state<EdgeFadeState>(find.byType(EdgeFade));
testWidgets('no fade on either edge when content fits', (tester) async {
await tester.pumpWidget(harness(count: 3)); // 300px < 800px
await tester.pump(); // let the post-layout metrics notification land
expect(state(tester).fadeLeft, isFalse);
expect(state(tester).fadeRight, isFalse);
expect(find.byIcon(Icons.chevron_left), findsNothing);
expect(find.byIcon(Icons.chevron_right), findsNothing);
});
testWidgets('a trailing chevron cue appears when the row overflows',
(tester) async {
await tester.pumpWidget(harness(count: 20));
await tester.pump();
expect(find.byIcon(Icons.chevron_right), findsOneWidget);
expect(find.byIcon(Icons.chevron_left), findsNothing);
});
testWidgets('LTR: at start only the trailing (right) edge fades',
(tester) async {
await tester.pumpWidget(harness(count: 20)); // 2000px > 800px
await tester.pump();
expect(state(tester).fadeLeft, isFalse);
expect(state(tester).fadeRight, isTrue);
});
testWidgets('LTR: in the middle both edges fade', (tester) async {
final controller = ScrollController();
addTearDown(controller.dispose);
await tester.pumpWidget(harness(count: 20, controller: controller));
await tester.pump();
controller.jumpTo(600);
await tester.pump();
expect(state(tester).fadeLeft, isTrue);
expect(state(tester).fadeRight, isTrue);
});
testWidgets('LTR: at the end only the leading (left) edge fades',
(tester) async {
final controller = ScrollController();
addTearDown(controller.dispose);
await tester.pumpWidget(harness(count: 20, controller: controller));
await tester.pump();
controller.jumpTo(controller.position.maxScrollExtent);
await tester.pump();
expect(state(tester).fadeLeft, isTrue);
expect(state(tester).fadeRight, isFalse);
});
testWidgets('RTL: at start only the LEFT edge fades (mirrored)',
(tester) async {
await tester
.pumpWidget(harness(count: 20, direction: TextDirection.rtl));
await tester.pump();
expect(state(tester).fadeLeft, isTrue);
expect(state(tester).fadeRight, isFalse);
});
testWidgets('RTL: at the end only the RIGHT edge fades (mirrored)',
(tester) async {
final controller = ScrollController();
addTearDown(controller.dispose);
await tester.pumpWidget(harness(
count: 20,
direction: TextDirection.rtl,
controller: controller,
));
await tester.pump();
controller.jumpTo(controller.position.maxScrollExtent);
await tester.pump();
expect(state(tester).fadeLeft, isFalse);
expect(state(tester).fadeRight, isTrue);
});
testWidgets('RTL: no fade when content fits', (tester) async {
await tester.pumpWidget(harness(count: 3, direction: TextDirection.rtl));
await tester.pump();
expect(state(tester).fadeLeft, isFalse);
expect(state(tester).fadeRight, isFalse);
});
testWidgets('fades clear when content shrinks to fit', (tester) async {
await tester.pumpWidget(harness(count: 20));
await tester.pump();
expect(state(tester).fadeRight, isTrue);
await tester.pumpWidget(harness(count: 3));
await tester.pump();
expect(state(tester).fadeLeft, isFalse);
expect(state(tester).fadeRight, isFalse);
});
testWidgets('scrolling by drag updates the fades', (tester) async {
await tester.pumpWidget(harness(count: 20));
await tester.pump();
expect(state(tester).fadeLeft, isFalse);
await tester.drag(
find.byType(SingleChildScrollView), const Offset(-300, 0));
await tester.pump();
expect(state(tester).fadeLeft, isTrue);
expect(state(tester).fadeRight, isTrue);
});
}