make Claude sidebar sub-tabs keyboard-activatable

The Activity / Team / Config sub-tabs were built on a raw GestureDetector,
so Tab traversal skipped them and Enter/Space did nothing — a gap against
the repo's a11y contract. Switch to ClideTappable (focusable, Enter/Space
activates) wrapped in button + selected semantics, and add a test that
drives the switch via ActivateIntent rather than a pointer tap.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jeroen Schweitzer
2026-06-03 09:19:35 +02:00
co-authored by Claude Opus 4.8
parent 0f6c5af1d9
commit 6337bec152
3 changed files with 29 additions and 6 deletions
@@ -11,7 +11,7 @@ import 'package:clide/builtin/claude/src/transcript_publisher.dart';
import 'package:clide/builtin/claude/src/transcript_reader.dart';
import 'package:clide/kernel/kernel.dart';
import 'package:flutter/services.dart' show LogicalKeyboardKey;
import 'package:flutter/widgets.dart' show EditableText, SizedBox, Semantics;
import 'package:flutter/widgets.dart' show ActivateIntent, Actions, EditableText, SizedBox, Semantics;
import 'package:flutter_test/flutter_test.dart';
import '../../helpers/kernel_fixture.dart';
@@ -106,6 +106,20 @@ void main() {
expect(find.text('TODAY'), findsOneWidget);
});
testWidgets('a sub-tab is keyboard-activatable, not pointer-only (T-182)', (tester) async {
await tester.pumpWidget(harness(f, sidebar(stats: stats)));
await tester.pumpAndSettle();
// The tab wraps its label in a ClideTappable, so the keymap's Enter/Space
// → ActivateIntent reaches it. Dispatch ActivateIntent through the real
// Actions path (what KeymapService does) — no pointer tap — and the body
// switches. Mirrors test/widgets/src/clide_tappable_test.dart.
Actions.invoke(tester.element(find.text('Team')), const ActivateIntent());
await tester.pump();
expect(find.text('No team active.'), findsOneWidget);
expect(find.text('TODAY'), findsNothing);
});
testWidgets('Config tab renders the settings table over ClaudeConfig', (tester) async {
final dir = Directory.systemTemp.createTempSync('cfg');
addTearDown(() => dir.deleteSync(recursive: true));