feat(todo): a project laid out as a board, its columns its sections (ADR 0033, D17)
The Display's new Layout picks List or Board. The board has a column for the tasks in no section, then one a section with its menu, each task a card and each column ending with Add task, a last column adding a section; grouped otherwise, a column a group. Narrower than a medium width it pages one column at a time with dots below; from it on the columns stand side by side. Cards drag within and between columns as rows do (D16), every scrollable they are in scrolling at its edges. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
7d1de304c8
commit
e4fbfb54d1
1 changed files with 714 additions and 111 deletions
|
|
@ -12,6 +12,7 @@ import 'package:halkyon_todo_flutter/src/feature/label/model/label.dart';
|
|||
import 'package:halkyon_todo_flutter/src/feature/project/data/project_repository.dart';
|
||||
import 'package:halkyon_todo_flutter/src/feature/project/model/project.dart';
|
||||
import 'package:halkyon_todo_flutter/src/feature/project/model/project_view.dart';
|
||||
import 'package:halkyon_todo_flutter/src/feature/project/widget/project_board.dart';
|
||||
import 'package:halkyon_todo_flutter/src/feature/section/data/section_repository.dart';
|
||||
import 'package:halkyon_todo_flutter/src/feature/section/model/section.dart';
|
||||
import 'package:halkyon_todo_flutter/src/feature/task/data/task_repository.dart';
|
||||
|
|
@ -495,6 +496,122 @@ void main() {
|
|||
await done(tester);
|
||||
expect(find.byType(DraggableTask), findsNWidgets(3));
|
||||
});
|
||||
|
||||
group('Board', () {
|
||||
/// The Inbox as a board, with a section "Errands" holding "Stamps".
|
||||
Future<String> boardOf(WidgetTester tester) async {
|
||||
await inboxOf(tester);
|
||||
final errands = await tester.runAsync(
|
||||
() => SectionRepository(
|
||||
sectionRows,
|
||||
keys,
|
||||
).add(SectionContent(name: 'Errands', projectId: inbox())),
|
||||
);
|
||||
sectionRows.bringRemoteChanges();
|
||||
await seed(
|
||||
tester,
|
||||
TaskContent(
|
||||
title: 'Stamps',
|
||||
projectId: inbox(),
|
||||
sectionId: errands,
|
||||
),
|
||||
);
|
||||
await display(tester);
|
||||
await tester.tap(find.text('Board'));
|
||||
await tester.pumpAndSettle();
|
||||
await done(tester);
|
||||
return errands!;
|
||||
}
|
||||
|
||||
testWidgets('a column for the tasks in no section and one a section, '
|
||||
'paged on a phone with dots below; kept in the project', (
|
||||
tester,
|
||||
) async {
|
||||
await boardOf(tester);
|
||||
|
||||
expect(find.text('(No section) 3'), findsOneWidget);
|
||||
expect(find.byKey(ProjectBoard.dotsKey), findsOneWidget);
|
||||
final projects = await tester.runAsync(
|
||||
() => ProjectRepository(projectRows, keys).all(),
|
||||
);
|
||||
expect(
|
||||
projects!
|
||||
.singleWhere((project) => project.id == inbox())
|
||||
.content
|
||||
?.view
|
||||
.layout,
|
||||
ProjectLayout.board,
|
||||
);
|
||||
|
||||
await tester.fling(
|
||||
find.byType(PageView),
|
||||
const Offset(-300, 0),
|
||||
1000,
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('Errands 1'), findsOneWidget);
|
||||
expect(find.text('Stamps').hitTestable(), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets("a column's Add task adds into its section", (
|
||||
tester,
|
||||
) async {
|
||||
final errands = await boardOf(tester);
|
||||
await tester.fling(
|
||||
find.byType(PageView),
|
||||
const Offset(-300, 0),
|
||||
1000,
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.tap(
|
||||
find.widgetWithText(TextButton, 'Add task').hitTestable(),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
await tester.enterText(find.byKey(quickAddTitleKey), 'Envelopes');
|
||||
await tester.pump();
|
||||
await tester.tap(find.widgetWithText(FilledButton, 'Add'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final all = await tester.runAsync(() => tasks().all());
|
||||
expect(
|
||||
all!
|
||||
.singleWhere((task) => task.content?.title == 'Envelopes')
|
||||
.content
|
||||
?.sectionId,
|
||||
errands,
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('on a wide window the columns stand side by side, and a '
|
||||
'card dragged onto another column goes last in it', (
|
||||
tester,
|
||||
) async {
|
||||
final errands = await boardOf(tester);
|
||||
tester.view.physicalSize = const Size(1000, 900);
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.byType(PageView), findsNothing);
|
||||
expect(find.text('Errands 1'), findsOneWidget);
|
||||
|
||||
final from = tester.getCenter(row('Apple'));
|
||||
final gesture = await tester.startGesture(from);
|
||||
await tester.pump(
|
||||
kLongPressTimeout + const Duration(milliseconds: 50),
|
||||
);
|
||||
final to = tester.getBottomLeft(row('Stamps')) + const Offset(40, 80);
|
||||
for (var step = 1; step <= 10; step++) {
|
||||
await gesture.moveTo(Offset.lerp(from, to, step / 10)!);
|
||||
await tester.pump();
|
||||
}
|
||||
await gesture.up();
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final all = await tester.runAsync(() => tasks().all());
|
||||
final apple = all!.singleWhere((t) => t.content?.title == 'Apple');
|
||||
expect(apple.content?.sectionId, errands);
|
||||
expect(find.text('Errands 2'), findsOneWidget);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
group('quick add reads what is typed (D15)', () {
|
||||
|
|
|
|||
Loading…
Reference in a new issue