Skip to content

Commit

Permalink
only apply canRequestFocus as true if it has a focusVariant
Browse files Browse the repository at this point in the history
  • Loading branch information
tilucasoli committed Jan 23, 2025
1 parent 3a8a874 commit 88ffb71
Show file tree
Hide file tree
Showing 3 changed files with 257 additions and 112 deletions.
9 changes: 8 additions & 1 deletion packages/mix/lib/src/core/styled_widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,14 @@ class SpecBuilder extends StatelessWidget {
_hasWidgetStateVariant && MixWidgetState.of(context) == null;

if (needsWidgetState || controller != null) {
current = Interactable(controller: controller, child: current);
final canRequestFocus =
style.variants.values.any((attr) => attr.variant is OnFocusedVariant);

current = Interactable(
controller: controller,
canRequestFocus: canRequestFocus,
child: current,
);
}

// Otherwise, directly build the mixed child widget
Expand Down
288 changes: 177 additions & 111 deletions packages/mix/test/src/core/styled_widget_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -85,27 +85,70 @@ void main() {
);
},
);
group('SpecBuilder', () {
testWidgets(
'When a SpecBuilder has a controller, it should wrap the child with Interactable',
(tester) async {
final controller = MixWidgetStateController();
await tester.pumpWidget(
SpecBuilder(
controller: controller,
builder: (context) => const SizedBox(),

testWidgets(
'When a SpecBuilder has a controller, it should wrap the child with Interactable',
(tester) async {
final controller = MixWidgetStateController();
await tester.pumpWidget(
SpecBuilder(
controller: controller,
builder: (context) => const SizedBox(),
),
);

expect(find.byType(InteractiveMixStateWidget), findsOneWidget);
},
);

testWidgets(
'When a SpecBuilder has a style with MixWidgetStateVariant, it should wrap the child with Interactable',
(tester) async {
await tester.pumpWidget(
SpecBuilder(
style: Style(
$box.color(
Colors.red,
),
$on.press(
$box.color(Colors.blue),
),
),
);
builder: (context) => const SizedBox(),
),
);

expect(find.byType(InteractiveMixStateWidget), findsOneWidget);
},
);
expect(find.byType(InteractiveMixStateWidget), findsOneWidget);
},
);

testWidgets(
'When a SpecBuilder has a style with MixWidgetStateVariant, it should wrap the child with Interactable',
(tester) async {
await tester.pumpWidget(
SpecBuilder(
testWidgets(
'When a SpecBuilder has a style with OnHoverVariant, it should wrap the child with MouseRegionMixStateWidget',
(tester) async {
await tester.pumpWidget(
SpecBuilder(
style: Style(
$box.color(Colors.red),
$on.hover.event((event) {
return const Style.empty();
}),
),
builder: (context) => const SizedBox(),
),
);

expect(find.byType(MouseRegionMixStateWidget), findsOneWidget);
expect(find.byType(InteractiveMixStateWidget), findsOneWidget);
},
);

// Create a test that already has a Interactable and ther is only one InteractiveMixStateWidget
testWidgets(
'When a SpecBuilder has a controller and a style with MixWidgetStateVariant, it should wrap the child with Interactable',
(tester) async {
await tester.pumpWidget(
Interactable(
child: SpecBuilder(
style: Style(
$box.color(
Colors.red,
Expand All @@ -116,113 +159,136 @@ void main() {
),
builder: (context) => const SizedBox(),
),
);
),
);

expect(find.byType(InteractiveMixStateWidget), findsOneWidget);
},
);
expect(find.byType(InteractiveMixStateWidget), findsOneWidget);
},
);

testWidgets(
'When a SpecBuilder has a style with OnHoverVariant, it should wrap the child with MouseRegionMixStateWidget',
(tester) async {
await tester.pumpWidget(
SpecBuilder(
style: Style(
$box.color(Colors.red),
$on.hover.event((event) {
return const Style.empty();
}),
),
builder: (context) => const SizedBox(),
),
);
// do not add interactive mix state if its not needed
testWidgets(
'When a SpecBuilder has a controller and a style with MixWidgetStateVariant, it should wrap the child with Interactable',
(tester) async {
await tester.pumpWidget(
SpecBuilder(
style: Style($box.color(Colors.red)),
builder: (context) => const SizedBox(),
),
);

expect(find.byType(MouseRegionMixStateWidget), findsOneWidget);
expect(find.byType(InteractiveMixStateWidget), findsOneWidget);
},
);
expect(find.byType(InteractiveMixStateWidget), findsNothing);
},
);

// Create a test that already has a Interactable and ther is only one InteractiveMixStateWidget
testWidgets(
'When a SpecBuilder has a controller and a style with MixWidgetStateVariant, it should wrap the child with Interactable',
(tester) async {
await tester.pumpWidget(
Interactable(
child: SpecBuilder(
style: Style(
$box.color(
Colors.red,
),
$on.press(
$box.color(Colors.blue),
),
),
builder: (context) => const SizedBox(),
),
),
);
testWidgets(
'When a SpecBuilder has a controller it should wrap the widget',
(tester) async {
await tester.pumpWidget(
SpecBuilder(
controller: MixWidgetStateController(),
style: Style($box.color(Colors.red)),
builder: (context) => const SizedBox(),
),
);

expect(find.byType(InteractiveMixStateWidget), findsOneWidget);
},
);
expect(find.byType(InteractiveMixStateWidget), findsOneWidget);
},
);

// do not add interactive mix state if its not needed
testWidgets(
'When a SpecBuilder has a controller and a style with MixWidgetStateVariant, it should wrap the child with Interactable',
(tester) async {
await tester.pumpWidget(
SpecBuilder(
style: Style($box.color(Colors.red)),
builder: (context) => const SizedBox(),
),
);
testWidgets(
'When a SpecBuilder has an animated style, it should use RenderAnimatedModifiers',
(tester) async {
await tester.pumpWidget(
SpecBuilder(
style: Style($box.color(Colors.red)).animate(),
builder: (context) => const SizedBox(),
),
);

expect(find.byType(InteractiveMixStateWidget), findsNothing);
},
);
expect(find.byType(RenderAnimatedModifiers), findsOneWidget);
},
);

testWidgets(
'When a SpecBuilder has a controller it should wrap the widget',
(tester) async {
await tester.pumpWidget(
SpecBuilder(
controller: MixWidgetStateController(),
style: Style($box.color(Colors.red)),
builder: (context) => const SizedBox(),
),
);
testWidgets(
'When a SpecBuilder has a non-animated style, it should use RenderModifiers',
(tester) async {
await tester.pumpWidget(
SpecBuilder(
style: Style($box.color(Colors.red)),
builder: (context) => const SizedBox(),
),
);

expect(find.byType(InteractiveMixStateWidget), findsOneWidget);
},
);
expect(find.byType(RenderModifiers), findsOneWidget);
},
);

testWidgets(
'When a SpecBuilder has an animated style, it should use RenderAnimatedModifiers',
(tester) async {
await tester.pumpWidget(
SpecBuilder(
style: Style($box.color(Colors.red)).animate(),
builder: (context) => const SizedBox(),
),
);
testWidgets(
'When a SpecBuilder has a non-animated style, it should use RenderModifiers',
(tester) async {
await tester.pumpWidget(
SpecBuilder(
style: Style($box.color(Colors.red)),
builder: (context) => const SizedBox(),
),
);

expect(find.byType(RenderAnimatedModifiers), findsOneWidget);
},
);
expect(find.byType(RenderModifiers), findsOneWidget);
},
);

testWidgets(
'When a SpecBuilder has a non-animated style, it should use RenderModifiers',
testWidgets(
'should set canRequestFocus to false if there is no \$on.focus in Style',
(tester) async {
await tester.pumpWidget(
SpecBuilder(
style: Style($box.color(Colors.red)),
builder: (context) => const SizedBox(),
),
);

expect(find.byType(RenderModifiers), findsOneWidget);
},
await _testFocusability(
tester,
style: Style(
$box.color(Colors.blue),
$on.press(
$box.color(Colors.blue),
),
),
expectedHasFocus: false,
);
});

testWidgets(
'should set canRequestFocus to true if there is a \$on.focus in Style',
(tester) async {
await _testFocusability(
tester,
style: Style(
$box.color(Colors.red),
$on.focus(
$box.color(Colors.blue),
),
),
expectedHasFocus: true,
);
},
);
});
}

Future<void> _testFocusability(
WidgetTester tester, {
required Style style,
required bool expectedHasFocus,
}) async {
await tester.pumpWidget(
SpecBuilder(
style: style,
builder: (context) => const SizedBox(),
),
);

expect(find.byType(InteractiveMixStateWidget), findsOneWidget);
expect(
tester
.firstWidget<InteractiveMixStateWidget>(
find.byType(InteractiveMixStateWidget))
.canRequestFocus,
expectedHasFocus,
);
}
Loading

0 comments on commit 88ffb71

Please sign in to comment.