Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add lazy test coverage, add wrapper in react-dart #407

Merged
merged 3 commits into from
Sep 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions lib/react_client/react_interop.dart
Original file line number Diff line number Diff line change
Expand Up @@ -312,14 +312,27 @@ ReactComponentFactoryProxy lazy(Future<ReactComponentFactoryProxy> Function() lo
() => futureToPromise(
(() async {
final factory = await load();
return jsify({'default': factory.type});
// By using a wrapper uiForwardRef it ensures that we have a matching factory proxy type given to react-dart's lazy,
// a `ReactDartWrappedComponentFactoryProxy`. This is necessary to have consistent prop conversions since we don't
// have access to the original factory proxy outside of this async block.
final wrapper = forwardRef2((props, ref) {
final children = props['children'];
return factory.build(
{...props, 'ref': ref},
[
if (children != null && !(children is List && children.isEmpty)) children,
],
);
});
return jsify({'default': wrapper.type});
})(),
),
),
);

// Setting this version and wrapping with ReactDartWrappedComponentFactoryProxy
// is only okay because it matches the version and factory proxy of the wrapperFactory above.
setProperty(hoc, 'dartComponentVersion', ReactDartComponentVersion.component2);

return ReactDartWrappedComponentFactoryProxy(hoc);
}

Expand Down
37 changes: 25 additions & 12 deletions test/factory/common_factory_tests.dart
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import '../util.dart';
void commonFactoryTests(ReactComponentFactoryProxy factory,
{String? dartComponentVersion,
bool skipPropValuesTest = false,
bool isNonDartComponentWithDartWrapper = false,
ReactElement Function(dynamic children)? renderWrapper}) {
_childKeyWarningTests(
factory,
Expand Down Expand Up @@ -115,7 +116,7 @@ void commonFactoryTests(ReactComponentFactoryProxy factory,
shouldAlwaysBeList: isDartComponent2(factory({})));
});

if (isDartComponent(factory({}))) {
if (isDartComponent(factory({})) && !isNonDartComponentWithDartWrapper) {
group('passes children to the Dart component when specified as', () {
final notCalledSentinelValue = Object();
dynamic childrenFromLastRender;
Expand Down Expand Up @@ -173,7 +174,7 @@ void commonFactoryTests(ReactComponentFactoryProxy factory,
}
}

if (isDartComponent2(factory({}))) {
if (isDartComponent2(factory({})) && !isNonDartComponentWithDartWrapper) {
test('executes Dart render code in the component zone', () {
final oldComponentZone = componentZone;
addTearDown(() => componentZone = oldComponentZone);
Expand All @@ -193,7 +194,10 @@ void commonFactoryTests(ReactComponentFactoryProxy factory,
}
}

void domEventHandlerWrappingTests(ReactComponentFactoryProxy factory) {
void domEventHandlerWrappingTests(
ReactComponentFactoryProxy factory, {
bool isNonDartComponentWithDartWrapper = false,
}) {
Element renderAndGetRootNode(ReactElement content) {
final mountNode = Element.div();
react_dom.render(content, mountNode);
Expand Down Expand Up @@ -270,22 +274,31 @@ void domEventHandlerWrappingTests(ReactComponentFactoryProxy factory) {
}
});

if (isDartComponent(factory({}))) {
if (isDartComponent(factory({})) && !isNonDartComponentWithDartWrapper) {
group('in a way that the handlers are callable from within the Dart component:', () {
setUpAll(() {
expect(propsFromDartRender, isNotNull,
reason: 'test setup: component must pass props into props.onDartRender');
});

late react.SyntheticMouseEvent event;
final divRef = react.createRef<DivElement>();
render(react.div({
'ref': divRef,
'onClick': (react.SyntheticMouseEvent e) => event = e,
}));
rtu.Simulate.click(divRef);
late react.SyntheticMouseEvent dummyEvent;
setUpAll(() {
final mountNode = DivElement();
document.body!.append(mountNode);
addTearDown(() {
react_dom.unmountComponentAtNode(mountNode);
mountNode.remove();
});

final dummyEvent = event;
final divRef = react.createRef<DivElement>();
react_dom.render(
react.div({
'ref': divRef,
'onClick': (react.SyntheticMouseEvent e) => dummyEvent = e,
}),
mountNode);
divRef.current!.click();
});

for (final eventCase in eventCases.where((helper) => helper.isDart)) {
test(eventCase.description, () {
Expand Down
69 changes: 61 additions & 8 deletions test/react_lazy_test.dart
Original file line number Diff line number Diff line change
@@ -1,26 +1,79 @@
@TestOn('browser')
@JS()
library react.react_lazy_test;

import 'dart:js_util';

import 'package:js/js.dart';
import 'package:react/hooks.dart';
import 'package:react/react.dart' as react;
import 'package:react/react_client/component_factory.dart';
import 'package:react/react_client/react_interop.dart';
import 'package:test/test.dart';

import 'factory/common_factory_tests.dart';

main() {
group('lazy', () {
group('- common factory behavior -', () {
final LazyTest = react.lazy(() async => react.registerFunctionComponent((props) {
group('Dart component', () {
final LazyTest = react.lazy(() async => react.forwardRef2((props, ref) {
useImperativeHandle(ref, () => TestImperativeHandle());
props['onDartRender']?.call(props);
return react.div({...props});
}));

commonFactoryTests(
LazyTest,
// ignore: invalid_use_of_protected_member
dartComponentVersion: ReactDartComponentVersion.component2,
renderWrapper: (child) => react.Suspense({'fallback': 'Loading...'}, child),
);
group('- common factory behavior -', () {
commonFactoryTests(
LazyTest,
// ignore: invalid_use_of_protected_member
dartComponentVersion: ReactDartComponentVersion.component2,
renderWrapper: (child) => react.Suspense({'fallback': 'Loading...'}, child),
);
});

group('- dom event handler wrapping -', () {
domEventHandlerWrappingTests(LazyTest);
});

group('- refs -', () {
refTests<TestImperativeHandle>(LazyTest, verifyRefValue: (ref) {
expect(ref, isA<TestImperativeHandle>());
});
});
});

group('JS component', () {
final LazyJsTest = react.lazy(() async => ReactJsComponentFactoryProxy(_JsFoo));

group('- common factory behavior -', () {
commonFactoryTests(
LazyJsTest,
// ignore: invalid_use_of_protected_member
dartComponentVersion: ReactDartComponentVersion.component2,
// This isn't a Dart component, but it's detected as one by tests due to the factory's dartComponentVersion
isNonDartComponentWithDartWrapper: true,
renderWrapper: (child) => react.Suspense({'fallback': 'Loading...'}, child),
);
});

group('- dom event handler wrapping -', () {
domEventHandlerWrappingTests(
LazyJsTest,
// This isn't a Dart component, but it's detected as one by tests due to the factory's dartComponentVersion
isNonDartComponentWithDartWrapper: true,
);
});

group('- refs -', () {
refTests<ReactComponent>(LazyJsTest, verifyRefValue: (ref) {
expect(getProperty(ref as Object, 'constructor'), same(_JsFoo));
});
});
});
});
}

class TestImperativeHandle {}

@JS()
external ReactClass get _JsFoo;
1 change: 1 addition & 0 deletions test/react_lazy_test.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
<script src="packages/react/react_dom.js"></script>
<link rel="x-dart-test" href="react_lazy_test.dart" />
<script src="packages/test/dart.js"></script>
<script src="factory/js_factory_test.js"></script>
</head>
<body></body>
</html>
Loading