Some checks are pending
Security / Security check (push) Waiting to run
The icon-only rail forced first-time users to guess (usertest: Senior, a11y, UX personas). Three changes: - Nav tooltips appear instantly and carry the page shortcut (Cmd+1..9, Ctrl on non-mac — Ctrl activators added); expanded labels show the same hint. Explicit button semantics for screen readers on every destination. - A visible 'Search & commands' row above the footer opens the existing Cmd+K palette, which nothing in the UI advertised. - Settings -> Appearance gains 'Keep the navigation expanded': pins the rail with permanent labels (persisted preference). Footer strip and pillar toggle made overflow-safe for the animating rail; responsive test scrolls the by-design scrollable destinations list. Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
84 lines
2.9 KiB
Dart
84 lines
2.9 KiB
Dart
// Locks the sidebar invariant: destination icons sit at the same Y
|
|
// whether the rail is collapsed or expanded. The header rows above the
|
|
// destinations list have FIXED pixel heights (see _SidebarState), so
|
|
// expanding the rail (a width-only change) must not shift any
|
|
// destination vertically. This was a recurring regression source; this
|
|
// test pins it.
|
|
//
|
|
// Expansion is driven via StudioApp(startSidebarExpanded: true), NOT a
|
|
// hover gesture — hover mid-transition trips RenderFlex overflow in
|
|
// widget tests (see widget_test.dart). With the flag the rail's
|
|
// animation controller starts at value 1.0 (fully expanded), so no
|
|
// animation needs to settle.
|
|
|
|
import 'package:flutter/widgets.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:chain_studio/data/hub.dart';
|
|
import 'package:chain_studio/main.dart';
|
|
|
|
const _destinations = <String>[
|
|
'welcome',
|
|
'store',
|
|
'doctor',
|
|
'flows',
|
|
'audit',
|
|
'approvals',
|
|
'runs',
|
|
'federation',
|
|
];
|
|
|
|
Map<String, double> _destinationYs(WidgetTester tester) {
|
|
final ys = <String, double>{};
|
|
for (final id in _destinations) {
|
|
final finder = find.byKey(ValueKey('sidebar-item-$id'));
|
|
expect(finder, findsOneWidget, reason: 'destination "$id" must render');
|
|
ys[id] = tester.getTopLeft(finder).dy;
|
|
}
|
|
return ys;
|
|
}
|
|
|
|
void main() {
|
|
testWidgets(
|
|
'sidebar destination Y positions are stable across rail expansion',
|
|
(tester) async {
|
|
// Desktop-sized surface: the rail carries 8 destinations plus
|
|
// the search row and footer — the 600-px default viewport
|
|
// scrolls the last destination out of the ListView.
|
|
tester.view.physicalSize = const Size(1280, 900);
|
|
tester.view.devicePixelRatio = 1.0;
|
|
addTearDown(tester.view.reset);
|
|
// Collapsed (default).
|
|
await tester.pumpWidget(
|
|
const StudioApp(
|
|
initialThemeMode: ThemeModeValue.system,
|
|
initialLocale: Locale('en'),
|
|
),
|
|
);
|
|
// pump (not pumpAndSettle): the app has long-lived timers/animations
|
|
// that never "settle"; one frame is enough to lay the sidebar out.
|
|
await tester.pump(const Duration(milliseconds: 100));
|
|
final collapsed = _destinationYs(tester);
|
|
|
|
// Expanded: controller starts at 1.0 via the flag, hover disabled.
|
|
await tester.pumpWidget(
|
|
const StudioApp(
|
|
initialThemeMode: ThemeModeValue.system,
|
|
initialLocale: Locale('en'),
|
|
startSidebarExpanded: true,
|
|
),
|
|
);
|
|
await tester.pump(const Duration(milliseconds: 100));
|
|
final expanded = _destinationYs(tester);
|
|
|
|
for (final id in _destinations) {
|
|
expect(
|
|
expanded[id],
|
|
closeTo(collapsed[id]!, 0.5),
|
|
reason: 'destination "$id" Y shifted on expansion '
|
|
'(${collapsed[id]} -> ${expanded[id]}) — the rows above the '
|
|
'destinations list must keep fixed heights',
|
|
);
|
|
}
|
|
},
|
|
);
|
|
}
|