implemented Ticker page selection persistence and enhanced ProseMirror table display modes
This commit is contained in:
@@ -19,9 +19,19 @@ List<TickerNavSection> _sections() => [
|
||||
),
|
||||
];
|
||||
|
||||
Widget _host() => MaterialApp(
|
||||
Widget _host({
|
||||
List<TickerNavSection>? sections,
|
||||
String? initialSlug,
|
||||
void Function(String? slug)? onSelectionChanged,
|
||||
String? homePublishedAt,
|
||||
VoidCallback? onRefreshHome,
|
||||
}) => MaterialApp(
|
||||
home: TickerScaffold(
|
||||
sections: _sections(),
|
||||
sections: sections ?? _sections(),
|
||||
initialSlug: initialSlug,
|
||||
onSelectionChanged: onSelectionChanged,
|
||||
homePublishedAt: homePublishedAt,
|
||||
onRefreshHome: onRefreshHome,
|
||||
homeBuilder: (context, onLinkTap) => const Text('HOME'),
|
||||
pageBuilder: (context, slug, onLinkTap) => Text('PAGE:$slug'),
|
||||
),
|
||||
@@ -30,29 +40,50 @@ Widget _host() => MaterialApp(
|
||||
Finder _appBarText(String text) =>
|
||||
find.descendant(of: find.byType(AppBar), matching: find.text(text));
|
||||
|
||||
// The "Aktuelles" home action is always present; it is greyed out (disabled)
|
||||
// while home is the current surface, active while a page is open.
|
||||
bool _homeEnabled(WidgetTester tester) =>
|
||||
tester
|
||||
.widget<IconButton>(
|
||||
find.widgetWithIcon(IconButton, Icons.home_outlined),
|
||||
)
|
||||
.onPressed !=
|
||||
null;
|
||||
|
||||
// The home surface stays mounted (IndexedStack) while a page is open, so
|
||||
// "which content is shown" is the stack index, not widget presence.
|
||||
int _shownIndex(WidgetTester tester) =>
|
||||
tester.widget<IndexedStack>(find.byType(IndexedStack)).index!;
|
||||
|
||||
const String _menuTooltip = 'Open navigation menu';
|
||||
const String _navTooltip = 'Seiten';
|
||||
|
||||
void main() {
|
||||
group('narrow layout (< 900)', () {
|
||||
testWidgets('uses a drawer reachable via the burger button', (tester) async {
|
||||
testWidgets('opens the nav in a bottom sheet from the floating button', (
|
||||
tester,
|
||||
) async {
|
||||
await tester.pumpWidget(_host());
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Nav is hidden behind the drawer, not shown as a sidebar.
|
||||
// No drawer, and the nav is hidden until the sheet is opened.
|
||||
expect(find.byType(Drawer), findsNothing);
|
||||
expect(find.text('Über uns'), findsNothing);
|
||||
expect(find.byTooltip(_menuTooltip), findsOneWidget);
|
||||
expect(find.byTooltip(_navTooltip), findsOneWidget);
|
||||
|
||||
await tester.tap(find.byTooltip(_menuTooltip));
|
||||
await tester.tap(find.byTooltip(_navTooltip));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byType(Drawer), findsOneWidget);
|
||||
expect(find.text('Über uns'), findsOneWidget);
|
||||
// Sheet shows the section-grouped nav.
|
||||
expect(find.text('Aktuelles'), findsOneWidget);
|
||||
expect(find.text('Über uns'), findsOneWidget);
|
||||
|
||||
// Selecting a page closes the sheet and switches the content in place.
|
||||
await tester.tap(find.text('Über uns'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Aktuelles'), findsNothing);
|
||||
expect(_shownIndex(tester), 1);
|
||||
expect(_appBarText('Über uns'), findsOneWidget);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -70,7 +101,8 @@ void main() {
|
||||
await pumpWide(tester);
|
||||
|
||||
expect(find.byType(Drawer), findsNothing);
|
||||
expect(find.byTooltip(_menuTooltip), findsNothing);
|
||||
// No bottom-sheet trigger on tablets — the sidebar is permanent.
|
||||
expect(find.byTooltip(_navTooltip), findsNothing);
|
||||
// Sidebar nav item is visible without any interaction.
|
||||
expect(find.text('Über uns'), findsOneWidget);
|
||||
});
|
||||
@@ -83,7 +115,9 @@ void main() {
|
||||
expect(_shownIndex(tester), 0);
|
||||
expect(find.text('PAGE:about'), findsNothing);
|
||||
expect(_appBarText('Ticker'), findsOneWidget);
|
||||
expect(find.byIcon(Icons.home_outlined), findsNothing);
|
||||
// Home action is shown but greyed out on the home surface.
|
||||
expect(find.byIcon(Icons.home_outlined), findsOneWidget);
|
||||
expect(_homeEnabled(tester), isFalse);
|
||||
|
||||
await tester.tap(find.text('Über uns'));
|
||||
await tester.pumpAndSettle();
|
||||
@@ -91,7 +125,7 @@ void main() {
|
||||
expect(_shownIndex(tester), 1);
|
||||
expect(find.text('PAGE:about'), findsOneWidget);
|
||||
expect(_appBarText('Über uns'), findsOneWidget);
|
||||
expect(find.byIcon(Icons.home_outlined), findsOneWidget);
|
||||
expect(_homeEnabled(tester), isTrue);
|
||||
|
||||
await tester.tap(find.byIcon(Icons.home_outlined));
|
||||
await tester.pumpAndSettle();
|
||||
@@ -99,7 +133,145 @@ void main() {
|
||||
expect(_shownIndex(tester), 0);
|
||||
expect(find.text('PAGE:about'), findsNothing);
|
||||
expect(_appBarText('Ticker'), findsOneWidget);
|
||||
expect(find.byIcon(Icons.home_outlined), findsNothing);
|
||||
expect(find.byIcon(Icons.home_outlined), findsOneWidget);
|
||||
expect(_homeEnabled(tester), isFalse);
|
||||
});
|
||||
});
|
||||
|
||||
group('remembered selection', () {
|
||||
Future<void> pumpWide(WidgetTester tester, Widget host) async {
|
||||
tester.view.physicalSize = const Size(1200, 2000);
|
||||
tester.view.devicePixelRatio = 1.0;
|
||||
addTearDown(tester.view.resetPhysicalSize);
|
||||
addTearDown(tester.view.resetDevicePixelRatio);
|
||||
await tester.pumpWidget(host);
|
||||
await tester.pumpAndSettle();
|
||||
}
|
||||
|
||||
testWidgets('reopens the persisted page on mount', (tester) async {
|
||||
await tester.pumpWidget(_host(initialSlug: 'about'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(_shownIndex(tester), 1);
|
||||
expect(find.text('PAGE:about'), findsOneWidget);
|
||||
expect(_appBarText('Über uns'), findsOneWidget);
|
||||
expect(_homeEnabled(tester), isTrue);
|
||||
});
|
||||
|
||||
testWidgets('falls back to home and drops a slug that is gone', (
|
||||
tester,
|
||||
) async {
|
||||
String? persisted = 'ghost';
|
||||
await tester.pumpWidget(
|
||||
_host(initialSlug: 'ghost', onSelectionChanged: (slug) => persisted = slug),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(_shownIndex(tester), 0);
|
||||
expect(_appBarText('Ticker'), findsOneWidget);
|
||||
expect(persisted, isNull);
|
||||
});
|
||||
|
||||
testWidgets('does not restore a redirect slug', (tester) async {
|
||||
await tester.pumpWidget(_host(initialSlug: 'web'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(_shownIndex(tester), 0);
|
||||
});
|
||||
|
||||
testWidgets('reports selection changes so the host can persist them', (
|
||||
tester,
|
||||
) async {
|
||||
final changes = <String?>[];
|
||||
await pumpWide(tester, _host(onSelectionChanged: changes.add));
|
||||
|
||||
await tester.tap(find.text('Über uns'));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(find.byIcon(Icons.home_outlined));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(changes, ['about', null]);
|
||||
});
|
||||
|
||||
testWidgets('drops the open page when a fresh nav no longer lists it', (
|
||||
tester,
|
||||
) async {
|
||||
String? persisted;
|
||||
await pumpWide(
|
||||
tester,
|
||||
_host(onSelectionChanged: (slug) => persisted = slug),
|
||||
);
|
||||
|
||||
await tester.tap(find.text('Über uns'));
|
||||
await tester.pumpAndSettle();
|
||||
expect(_shownIndex(tester), 1);
|
||||
|
||||
await tester.pumpWidget(
|
||||
_host(
|
||||
sections: [
|
||||
TickerNavSection(
|
||||
title: 'Infos',
|
||||
pages: [
|
||||
TickerNavPage(
|
||||
title: 'Anderes',
|
||||
slug: 'other',
|
||||
kind: TickerPageKind.content,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
onSelectionChanged: (slug) => persisted = slug,
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(_shownIndex(tester), 0);
|
||||
expect(_appBarText('Ticker'), findsOneWidget);
|
||||
expect(persisted, isNull);
|
||||
});
|
||||
});
|
||||
|
||||
group('home date button', () {
|
||||
testWidgets('shows the publish date on home and refreshes on tap', (
|
||||
tester,
|
||||
) async {
|
||||
var refreshed = 0;
|
||||
await tester.pumpWidget(
|
||||
_host(
|
||||
homePublishedAt: '2026-02-17T14:30:00',
|
||||
onRefreshHome: () => refreshed++,
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('17.02.2026, 14:30'), findsOneWidget);
|
||||
|
||||
await tester.tap(find.text('17.02.2026, 14:30'));
|
||||
await tester.pumpAndSettle();
|
||||
expect(refreshed, 1);
|
||||
});
|
||||
|
||||
testWidgets('hides the date button when there is no date', (tester) async {
|
||||
await tester.pumpWidget(_host());
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byIcon(Icons.schedule), findsNothing);
|
||||
});
|
||||
|
||||
testWidgets('hides the date button once a page is open', (tester) async {
|
||||
tester.view.physicalSize = const Size(1200, 2000);
|
||||
tester.view.devicePixelRatio = 1.0;
|
||||
addTearDown(tester.view.resetPhysicalSize);
|
||||
addTearDown(tester.view.resetDevicePixelRatio);
|
||||
await tester.pumpWidget(_host(homePublishedAt: '2026-02-17T14:30:00'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byIcon(Icons.schedule), findsOneWidget);
|
||||
|
||||
await tester.tap(find.text('Über uns'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byIcon(Icons.schedule), findsNothing);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user