Stage 2: Library search, filters and Exercise detail with Flip Animation
- LibraryFilter: word search on names; muscle (primary), equipment and level choices that widen within a filter and narrow across filters - ExerciseBrowser (reusable for the Plan picker) with search field, filter sheets showing live result counts, a clear button and an empty state - Exercise detail: start/end Flip Animation at each Mode's pace (still with reduced motion), facts, main/secondary muscles, numbered steps Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
1289fadb98
commit
786dd52e30
16 files changed
+1272
-32
No files matched your search
@@ -0,0 +1,78 @@
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:zenxercise/src/exercises/exercise.dart';
|
||||
import 'package:zenxercise/src/exercises/library_filter.dart';
|
||||
import 'package:zenxercise/src/modes/mode.dart';
|
||||
|
||||
Exercise _exercise(
|
||||
String name, {
|
||||
List<String> primary = const ['hamstrings'],
|
||||
List<String> secondary = const [],
|
||||
String? equipment = 'body only',
|
||||
String level = 'beginner',
|
||||
}) => Exercise(
|
||||
id: name.replaceAll(' ', '_'),
|
||||
name: name,
|
||||
mode: Mode.workout,
|
||||
category: 'strength',
|
||||
level: level,
|
||||
equipment: equipment,
|
||||
primaryMuscles: primary,
|
||||
secondaryMuscles: secondary,
|
||||
instructions: const [],
|
||||
imageCount: 0,
|
||||
);
|
||||
|
||||
void main() {
|
||||
final squat = _exercise('Barbell Full Squat', primary: ['quadriceps'], secondary: ['glutes'], equipment: 'barbell');
|
||||
final curl = _exercise('Dumbbell Bicep Curl', primary: ['biceps'], equipment: 'dumbbell', level: 'intermediate');
|
||||
final deadlift = _exercise('Romanian Deadlift', primary: ['hamstrings'], equipment: 'barbell', level: 'expert');
|
||||
final mystery = _exercise('Mystery Move', primary: ['glutes'], equipment: null);
|
||||
final all = [squat, curl, deadlift, mystery];
|
||||
|
||||
List<Exercise> apply(LibraryFilter f) => f.apply(all);
|
||||
|
||||
test('an empty filter keeps everything', () {
|
||||
const filter = LibraryFilter();
|
||||
|
||||
expect(filter.isActive, isFalse);
|
||||
expect(apply(filter), all);
|
||||
});
|
||||
|
||||
test('search matches every word of the query anywhere in the name, ignoring case', () {
|
||||
expect(apply(const LibraryFilter(query: 'squat')), [squat]);
|
||||
expect(apply(const LibraryFilter(query: 'barbell SQU')), [squat]);
|
||||
expect(apply(const LibraryFilter(query: ' dead romanian ')), [deadlift]);
|
||||
expect(apply(const LibraryFilter(query: 'curl squat')), isEmpty);
|
||||
});
|
||||
|
||||
test('choices within one filter widen, filters together narrow', () {
|
||||
expect(apply(const LibraryFilter(muscles: {'biceps', 'hamstrings'})), [curl, deadlift]);
|
||||
expect(apply(const LibraryFilter(muscles: {'biceps', 'hamstrings'}, equipment: {'barbell'})), [deadlift]);
|
||||
expect(apply(const LibraryFilter(levels: {'expert', 'intermediate'})), [curl, deadlift]);
|
||||
});
|
||||
|
||||
test('the muscle filter looks at the primary muscles only', () {
|
||||
expect(apply(const LibraryFilter(muscles: {'glutes'})), [mystery]);
|
||||
});
|
||||
|
||||
test('an Exercise without equipment drops out once equipment is filtered', () {
|
||||
expect(apply(const LibraryFilter(equipment: {'barbell', 'dumbbell', 'body only'})), [squat, curl, deadlift]);
|
||||
});
|
||||
|
||||
test('toggling a choice adds and removes it, and clearing keeps the query', () {
|
||||
final filter = const LibraryFilter(query: 'curl').toggleMuscle('biceps').toggleLevel('beginner');
|
||||
|
||||
expect(filter.muscles, {'biceps'});
|
||||
expect(filter.toggleMuscle('biceps').muscles, isEmpty);
|
||||
expect(filter.activeChoices, 2);
|
||||
expect(filter.clearChoices(), const LibraryFilter(query: 'curl'));
|
||||
});
|
||||
|
||||
test('the options are the values present, levels in their natural order', () {
|
||||
final options = FilterOptions.of(all);
|
||||
|
||||
expect(options.muscles, ['biceps', 'glutes', 'hamstrings', 'quadriceps']);
|
||||
expect(options.equipment, ['barbell', 'dumbbell']);
|
||||
expect(options.levels, ['beginner', 'intermediate', 'expert']);
|
||||
});
|
||||
}
|
||||
Reference in new issue
Block a user