From 8e6fc92d50317c88e6d4bb6f778fb9a6f1b217ae Mon Sep 17 00:00:00 2001 From: Keal Jones Date: Mon, 9 Sep 2024 17:00:29 -0700 Subject: [PATCH 01/31] first pass at lazy --- example/suspense/lazy.dart | 49 --- example/suspense/main.dart | 13 +- lib/over_react.dart | 1 + lib/src/util/lazy.dart | 68 ++++ pubspec.yaml | 8 + test/over_react/component/lazy_test.dart | 336 ++++++++++++++++++ .../component/lazy_test.over_react.g.dart | 213 +++++++++++ .../over_react/util/prop_conversion_test.dart | 4 +- test/over_react_component_test.dart | 102 +++--- test/over_react_component_test.html | 43 ++- 10 files changed, 725 insertions(+), 112 deletions(-) delete mode 100644 example/suspense/lazy.dart create mode 100644 lib/src/util/lazy.dart create mode 100644 test/over_react/component/lazy_test.dart create mode 100644 test/over_react/component/lazy_test.over_react.g.dart diff --git a/example/suspense/lazy.dart b/example/suspense/lazy.dart deleted file mode 100644 index 3b33d36f3..000000000 --- a/example/suspense/lazy.dart +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2023 Workiva Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import 'dart:js_util'; - -import 'package:js/js.dart'; -import 'package:over_react/over_react.dart'; -import 'package:over_react/src/util/promise_interop.dart'; -import 'package:react/react_client/react_interop.dart' as react_interop; -import 'package:over_react/js_component.dart'; -import 'package:react/react_client/component_factory.dart'; - -@JS('React.lazy') -external react_interop.ReactClass jsLazy(Promise Function() factory); - -// Only intended for testing purposes, Please do not copy/paste this into your repo. -// This will most likely be added to the PUBLIC api in the future, -// but needs more testing and Typing decisions to be made first. -UiFactory lazy(Future> Function() factory, UiFactoryConfig factoryConfig) { - return uiJsComponent( - ReactJsComponentFactoryProxy( - jsLazy( - allowInterop( - () => futureToPromise( - // React.lazy only supports "default exports" from a module. - // This `{default: yourExport}` workaround can be found in the React.lazy RFC comments. - // See: https://github.com/reactjs/rfcs/pull/64#issuecomment-431507924 - (() async { - //resolvedFactory = await factory(); - return jsify({'default': (await factory()).elementType}); - })(), - ), - ), - ), - ), - factoryConfig, - ); -} diff --git a/example/suspense/main.dart b/example/suspense/main.dart index a7c76e69f..c1c93a4a5 100644 --- a/example/suspense/main.dart +++ b/example/suspense/main.dart @@ -17,18 +17,17 @@ import 'dart:html'; import 'package:over_react/over_react.dart'; import 'package:react/react_dom.dart' as react_dom; import 'counter_component.dart' deferred as lazy_component; -import 'lazy.dart'; import 'third_party_file.dart'; -final LazyCounter = lazy(() async { - await lazy_component.loadLibrary(); +UiFactory LazyCounter = lazy(() async { await Future.delayed(Duration(seconds: 5)); + await lazy_component.loadLibrary(); return lazy_component.Counter; }, - UiFactoryConfig( - propsFactory: PropsFactory.fromUiFactory(CounterPropsMapView), - displayName: 'This does nothing...', - )); +UiFactoryConfig( + propsFactory: PropsFactory.fromUiFactory(CounterPropsMapView) + ) +); void main() { react_dom.render( diff --git a/lib/over_react.dart b/lib/over_react.dart index e4f6a35fd..c495700ee 100644 --- a/lib/over_react.dart +++ b/lib/over_react.dart @@ -101,6 +101,7 @@ export 'src/util/guid_util.dart'; export 'src/util/hoc.dart'; export 'src/util/handler_chain_util.dart'; export 'src/util/key_constants.dart'; +export 'src/util/lazy.dart'; export 'src/util/map_util.dart'; export 'src/util/memo.dart'; export 'src/util/pretty_print.dart'; diff --git a/lib/src/util/lazy.dart b/lib/src/util/lazy.dart new file mode 100644 index 000000000..257dad30d --- /dev/null +++ b/lib/src/util/lazy.dart @@ -0,0 +1,68 @@ +// Copyright 2020 Workiva Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +library over_react.lazy; + +import 'package:over_react/over_react.dart'; +import 'package:react/react.dart' as react; + +UiFactory lazy(Future> Function() loadComponent,/* UiFactoryConfig */ dynamic _config) { + ArgumentError.checkNotNull(_config, '_config'); + + if (_config is! UiFactoryConfig) { + throw ArgumentError( + '_config should be a UiFactoryConfig. Make sure you are ' + r'using either the generated factory config (i.e. _$FooConfig) or manually ' + 'declaring your config correctly.'); + } + + final lazyFactoryProxy = react.lazy(() async { + final factory = await loadComponent(); + final wrapper = uiForwardRef( + (props, ref) { + final builder = factory() + ..addProps(props) + ..ref = ref; + return props.children == null || (props.children != null && props.children?.isEmpty != false) ? builder() : builder(props.children); + }, + UiFactoryConfig( + propsFactory: PropsFactory.fromUiFactory(factory), + displayName: 'Lazy${_config.displayName}', + ), + ); + return wrapper().componentFactory!; + // return factory().componentFactory!; + }); + + // ignore: invalid_use_of_protected_member + final propsFactory = _config.propsFactory; + ArgumentError.checkNotNull(propsFactory, '_config.propsFactory'); + propsFactory!; + + TProps _uiFactory([Map? props]) { + TProps builder; + if (props == null) { + // propsFactory should get promoted to non-nullable here, but it does not some reason propsF + builder = propsFactory.jsMap(JsBackedMap()); + } else if (props is JsBackedMap) { + builder = propsFactory.jsMap(props); + } else { + builder = propsFactory.map(props); + } + + return builder..componentFactory = lazyFactoryProxy; + } + + return _uiFactory; +} diff --git a/pubspec.yaml b/pubspec.yaml index d2c0fdc68..5c9059038 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -48,3 +48,11 @@ workiva: core_checks: version: 1 react_boilerplate: disabled + + +dependency_overrides: + react: + git: + url: https://github.com/Workiva/react-dart.git + ref: remotes/origin/FED-3114-lazy + diff --git a/test/over_react/component/lazy_test.dart b/test/over_react/component/lazy_test.dart new file mode 100644 index 000000000..a2d319c0b --- /dev/null +++ b/test/over_react/component/lazy_test.dart @@ -0,0 +1,336 @@ +@TestOn('browser') +@JS() +library rmui.test.unit.component.lazy_test; + +import 'dart:html'; + +import 'package:js/js.dart'; +import 'package:over_react/js_component.dart'; +import 'package:over_react/over_react.dart'; +import 'package:react/react_client/component_factory.dart'; +import 'package:react/react_client/react_interop.dart' hide lazy; +import 'package:react_testing_library/matchers.dart'; +import 'package:react_testing_library/react_testing_library.dart'; + +import 'package:test/test.dart'; +import 'package:over_react/components.dart' as components; + +import '../util/prop_conversion_test.dart'; +import '../util/ref_test_cases.dart'; + +part 'lazy_test.over_react.g.dart'; + +main() { + enableTestMode(); + + group('lazy', () { + group('prop conversions', () { + +test('a custom Dart Map prop', () async { + final renderErrors = []; + + // Use an ErrorBoundary to detect errors on render, since otherwise + // React will just unmount the tree without throwing. + await expectLater(() async { + render((Suspense()..fallback='Loading')((components.ErrorBoundary() + ..onComponentDidCatch = ((error, _) => renderErrors.add(error)) + ..shouldLogErrors = false + ..fallbackUIRenderer = + ((_, __) => Dom.span()('An error occurred during render')) + )( + (LazyTestJS() + ..component = ExpectsDartMapProp.elementType + ..addProps(ExpectsDartMapProp()..dartMapProp = {'foo': 'bar'}) + ..addTestId('componentRoot'))(), + ), + ), + ); + await screen.findByText('An error occurred during render', timeout: Duration(seconds: 5)); + // Use prints as an easy way to swallow `print` calls and + // prevent RTL from forwarding console errors to the test output, + // since React error boundary logging is pretty noisy. + // TODO instead, disable logging in this rtl.render call once that option is available: FED-1641 + }, prints(anything)); + + expect(renderErrors, [ + isA().havingToStringValue(anyOf( + // DDC error message + matches(RegExp( + r"Expected a value of type 'Map[^']*', but got one of type '(Native|Legacy)JavaScriptObject'")), + // dart2js error message + matches(RegExp( + r"type '(Unknown|Plain)JavaScriptObject' is not a subtype of type 'Map[^']*'")), + )), + ]); + + // These are the expectations we'd use if this case didn't error: + // final node = view.getByTestId('componentRoot'); + // expect(node, hasAttribute('data-dart-map-prop', jsonEncode({'foo': 'bar'}))); + }, tags: ['js-interop-tradeoff']); + group('and a ref is set on that component', () { + group('which eventually ends up on a non-Dart component', () { + final testCaseCollection = RefTestCaseCollection(); + + group('and is passed in via a forwarded "ref" prop as a', () { + for (final testCaseName in testCaseCollection.allTestCaseNames) { + test(testCaseName, () async { + final testCase = + testCaseCollection.createCaseByName(testCaseName); + + render((Suspense()..fallback='Loading')((LazyTestJS() + ..addTestId('test-js-component') + ..component = BasicForwardRef.elementType + ..ref = testCase.ref)())); + await screen.findByTestId('test-js-component'); + expect(testCase.getCurrent(), isA()); + expect(testCase.getCurrent(), + hasAttribute(basicForwardRefAttribute), + reason: + 'test setup: ref should have been forwarded to the element we expect'); + }); + } + }); + + group( + 'and is passed in via a custom "ref" prop (with conversion) as a', + () { + for (final testCaseName in testCaseCollection.allTestCaseNames) { + test(testCaseName, () async { + final testCase = + testCaseCollection.createCaseByName(testCaseName); + + render((Suspense()..fallback='Loading')((LazyTestJS() + ..component = Dom.div.elementType + ..addTestId('test-js-component') + ..inputComponent = BasicForwardRef.elementType + ..inputRef = testCase.ref)())); + await screen.findByTestId('test-js-component'); + expect(testCase.getCurrent(), isA()); + expect(testCase.getCurrent(), + hasAttribute(basicForwardRefAttribute), + reason: + 'test setup: ref should have been forwarded to the element we expect'); + }); + } + }); + + group( + 'and is passed in via a "ref" prop in a nested props Map (with conversion) as a', + () { + for (final testCaseName in testCaseCollection.allTestCaseNames) { + test(testCaseName, () async { + final testCase = + testCaseCollection.createCaseByName(testCaseName); + + render((Suspense()..fallback='Loading')((LazyTestJS() + ..component = Dom.div.elementType + ..addTestId('test-js-component') + ..buttonComponent = BasicForwardRef.elementType + ..buttonProps = (domProps()..ref = testCase.ref))())); + await screen.findByTestId('test-js-component'); + expect(testCase.getCurrent(), isA()); + expect(testCase.getCurrent(), + hasAttribute(basicForwardRefAttribute), + reason: + 'test setup: ref should have been forwarded to the element we expect'); + }); + } + }); + }); + + group('which eventually ends up on a Dart class component', () { + final testCaseCollection = + RefTestCaseCollection(); + + setUpAll(() { + // Make sure the special cases we're concerned about are included in this set of test cases: + void assertSingleMetaWhere(bool Function(RefTestCaseMeta) test) { + expect(testCaseCollection.allTestCaseMetas.where(test).toList(), + hasLength(1)); + } + + assertSingleMetaWhere( + (m) => !m.isJs && m.kind.isCallback && m.isStronglyTyped); + assertSingleMetaWhere( + (m) => !m.isJs && m.kind.isCallback && !m.isStronglyTyped); + assertSingleMetaWhere( + (m) => !m.isJs && m.kind.isObject && m.isStronglyTyped); + assertSingleMetaWhere( + (m) => !m.isJs && m.kind.isObject && !m.isStronglyTyped); + }); + + void forEachTestCase( + void Function(String testCaseName, bool isDartCallbackCase, + bool isDartRefObjectCase, bool isTyped) callback) { + for (final testCaseName in testCaseCollection.allTestCaseNames) { + final meta = + testCaseCollection.testCaseMetaByName(testCaseName); + final isDartCallbackCase = !meta.isJs && meta.kind.isCallback; + final isDartRefObjectCase = !meta.isJs && meta.kind.isObject; + final isTyped = meta.isStronglyTyped; + + callback(testCaseName, isDartCallbackCase, isDartRefObjectCase, + isTyped); + } + } + + group('and is passed in via a forwarded "ref" prop as a', () { + forEachTestCase((testCaseName, isDartCallbackCase, + isDartRefObjectCase, isTyped) { + if (isDartCallbackCase && isTyped) { + // Bail since we know typed Dart callback refs will fail with a runtime type error since the JS ReactComponent + // will be passed in instead of the Dart instance; this behavior will be tested in the untyped Dart callback ref test case. + return; + } + + test(testCaseName, () async { + final testCase = + testCaseCollection.createCaseByName(testCaseName); + + render((Suspense()..fallback='Loading')((LazyTestJS() + ..component = Dom.div.elementType + ..addTestId('test-js-component') + ..component = ClassComponent.elementType + ..ref = testCase.ref)())); + await screen.findByTestId('test-js-component'); + if (isDartRefObjectCase) { + expect( + testCase.getCurrent(), isA()); + } else { + expect( + testCase.getCurrent(), + isA().havingDartComponent( + isA())); + } + }, tags: [ + if (isDartCallbackCase) 'js-interop-tradeoff', + ]); + }); + }); + + group( + 'and is passed in via a custom "ref" prop (with conversion) as a', + () { + forEachTestCase((testCaseName, isDartCallbackCase, + isDartRefObjectCase, isTyped) { + if (isDartCallbackCase && isTyped) { + // Bail since we know typed Dart callback refs will fail with a runtime type error since the JS ReactComponent + // will be passed in instead of the Dart instance; this behavior will be tested in the untyped Dart callback ref test case. + return; + } + + test(testCaseName, () async { + final testCase = + testCaseCollection.createCaseByName(testCaseName); + + render((Suspense()..fallback='Loading')((LazyTestJS() + ..component = Dom.div.elementType + ..addTestId('test-js-component') + ..inputComponent = ClassComponent.elementType + ..inputRef = testCase.ref)())); + await screen.findByTestId('test-js-component'); + if (isDartRefObjectCase) { + expect( + testCase.getCurrent(), isA()); + } else { + expect( + testCase.getCurrent(), + isA().havingDartComponent( + isA())); + } + }, tags: [ + if (isDartCallbackCase) 'js-interop-tradeoff', + ]); + }); + }); + + // This is the one case where Dart (and unfortunately, JS) callback refs actually get the class component + group( + 'and is passed in via a "ref" prop in a nested props Map (with conversion) as a', + () { + forEachTestCase((testCaseName, isDartCallbackCase, + isDartRefObjectCase, isTyped) { + final isJsCallbackCase = + testCaseName == RefTestCaseCollection.jsCallbackRefCaseName; + + test(testCaseName, () async { + final testCase = + testCaseCollection.createCaseByName(testCaseName); + + render((Suspense()..fallback='Loading')((LazyTestJS() + ..component = Dom.div.elementType + ..addTestId('test-js-component') + ..buttonComponent = ClassComponent.elementType + ..buttonProps = (domProps()..ref = testCase.ref))())); + await screen.findByTestId('test-js-component'); + // JS callbacks look the same to generateJsProps as Dart callback refs do, + // so they get the Dart component as well. // TODO investigate, decide how concerned we should be about this + if (isDartRefObjectCase || + isDartCallbackCase || + isJsCallbackCase) { + expect( + testCase.getCurrent(), isA()); + } else { + expect( + testCase.getCurrent(), + isA().havingDartComponent( + isA())); + } + }, tags: [ + if (isJsCallbackCase) 'js-interop-tradeoff', + ]); + }); + }); + }); + }); + }); + }); +} + +UiFactory LazyTestJS = lazy(() async => TestJs, +_$TestJsConfig, // ignore: undefined_identifier +); + +// +// OverReact bindings for a JS test component +// to assert correct behavior in more functional-style cases. +// + +@Props(keyNamespace: '') +mixin TestJsProps on UiProps { + @Accessor(key: 'buttonProps') + JsMap? _$raw$buttonProps; + + Map? get buttonProps => unjsifyMapProp(_$raw$buttonProps); + set buttonProps(Map? value) => _$raw$buttonProps = jsifyMapProp(value); + + @Accessor(key: 'listOfProps') + List? _$raw$listOfProps; + + List? get listOfProps => unjsifyMapListProp(_$raw$listOfProps); + set listOfProps(List? value) => _$raw$listOfProps = jsifyMapListProp(value); + + @Accessor(key: 'inputRef') + dynamic _$raw$inputRef; + + dynamic get inputRef => unjsifyRefProp(_$raw$inputRef); + set inputRef(dynamic value) => _$raw$inputRef = jsifyRefProp(value); + + @Accessor(key: 'messageContext') + ReactContext? _$raw$messageContext; + + Context? get messageContext => unjsifyContextProp(_$raw$messageContext); + set messageContext(Context? value) => _$raw$messageContext = jsifyContextProp(value); + + dynamic /*ElementType*/ component; + dynamic /*ElementType*/ inputComponent; + dynamic /*ElementType*/ buttonComponent; +} + +UiFactory TestJs = uiJsComponent( + ReactJsComponentFactoryProxy(_TestJs), + _$TestJsConfig, // ignore: undefined_identifier +); + +@JS('TestJsComponent') +external ReactClass get _TestJs; diff --git a/test/over_react/component/lazy_test.over_react.g.dart b/test/over_react/component/lazy_test.over_react.g.dart new file mode 100644 index 000000000..076e13a6c --- /dev/null +++ b/test/over_react/component/lazy_test.over_react.g.dart @@ -0,0 +1,213 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +// ignore_for_file: deprecated_member_use_from_same_package, unnecessary_null_in_if_null_operators, prefer_null_aware_operators, invalid_use_of_visible_for_overriding_member +part of 'lazy_test.dart'; + +// ************************************************************************** +// OverReactBuilder (package:over_react/src/builder.dart) +// ************************************************************************** + +@Deprecated('This API is for use only within generated code.' + ' Do not reference it in your code, as it may change at any time.' + ' EXCEPTION: this may be used in legacy boilerplate until' + ' it is transitioned to the new mixin-based boilerplate.') +mixin $TestJsProps on TestJsProps { + static const PropsMeta meta = _$metaForTestJsProps; + @override + @Accessor(key: 'buttonProps') + JsMap? get _$raw$buttonProps => + (props[_$key___$raw$buttonProps__TestJsProps] ?? null) as JsMap?; + @override + @Accessor(key: 'buttonProps') + set _$raw$buttonProps(JsMap? value) => + props[_$key___$raw$buttonProps__TestJsProps] = value; + @override + @Accessor(key: 'listOfProps') + List? get _$raw$listOfProps => + (props[_$key___$raw$listOfProps__TestJsProps] ?? null) as List?; + @override + @Accessor(key: 'listOfProps') + set _$raw$listOfProps(List? value) => + props[_$key___$raw$listOfProps__TestJsProps] = value; + @override + @Accessor(key: 'inputRef') + dynamic get _$raw$inputRef => + (props[_$key___$raw$inputRef__TestJsProps] ?? null) as dynamic; + @override + @Accessor(key: 'inputRef') + set _$raw$inputRef(dynamic value) => + props[_$key___$raw$inputRef__TestJsProps] = value; + @override + @Accessor(key: 'messageContext') + ReactContext? get _$raw$messageContext => + (props[_$key___$raw$messageContext__TestJsProps] ?? null) + as ReactContext?; + @override + @Accessor(key: 'messageContext') + set _$raw$messageContext(ReactContext? value) => + props[_$key___$raw$messageContext__TestJsProps] = value; + @override + dynamic get component => + (props[_$key__component__TestJsProps] ?? null) as dynamic; + @override + set component(dynamic value) => props[_$key__component__TestJsProps] = value; + @override + dynamic get inputComponent => + (props[_$key__inputComponent__TestJsProps] ?? null) as dynamic; + @override + set inputComponent(dynamic value) => + props[_$key__inputComponent__TestJsProps] = value; + @override + dynamic get buttonComponent => + (props[_$key__buttonComponent__TestJsProps] ?? null) as dynamic; + @override + set buttonComponent(dynamic value) => + props[_$key__buttonComponent__TestJsProps] = value; + /* GENERATED CONSTANTS */ + static const PropDescriptor _$prop___$raw$buttonProps__TestJsProps = + PropDescriptor(_$key___$raw$buttonProps__TestJsProps); + static const PropDescriptor _$prop___$raw$listOfProps__TestJsProps = + PropDescriptor(_$key___$raw$listOfProps__TestJsProps); + static const PropDescriptor _$prop___$raw$inputRef__TestJsProps = + PropDescriptor(_$key___$raw$inputRef__TestJsProps); + static const PropDescriptor _$prop___$raw$messageContext__TestJsProps = + PropDescriptor(_$key___$raw$messageContext__TestJsProps); + static const PropDescriptor _$prop__component__TestJsProps = + PropDescriptor(_$key__component__TestJsProps); + static const PropDescriptor _$prop__inputComponent__TestJsProps = + PropDescriptor(_$key__inputComponent__TestJsProps); + static const PropDescriptor _$prop__buttonComponent__TestJsProps = + PropDescriptor(_$key__buttonComponent__TestJsProps); + static const String _$key___$raw$buttonProps__TestJsProps = 'buttonProps'; + static const String _$key___$raw$listOfProps__TestJsProps = 'listOfProps'; + static const String _$key___$raw$inputRef__TestJsProps = 'inputRef'; + static const String _$key___$raw$messageContext__TestJsProps = + 'messageContext'; + static const String _$key__component__TestJsProps = 'component'; + static const String _$key__inputComponent__TestJsProps = 'inputComponent'; + static const String _$key__buttonComponent__TestJsProps = 'buttonComponent'; + + static const List $props = [ + _$prop___$raw$buttonProps__TestJsProps, + _$prop___$raw$listOfProps__TestJsProps, + _$prop___$raw$inputRef__TestJsProps, + _$prop___$raw$messageContext__TestJsProps, + _$prop__component__TestJsProps, + _$prop__inputComponent__TestJsProps, + _$prop__buttonComponent__TestJsProps + ]; + static const List $propKeys = [ + _$key___$raw$buttonProps__TestJsProps, + _$key___$raw$listOfProps__TestJsProps, + _$key___$raw$inputRef__TestJsProps, + _$key___$raw$messageContext__TestJsProps, + _$key__component__TestJsProps, + _$key__inputComponent__TestJsProps, + _$key__buttonComponent__TestJsProps + ]; + + @override + @UiProps.$mustCallSuper + void validateRequiredProps() { + super.validateRequiredProps(); + } +} + +@Deprecated('This API is for use only within generated code.' + ' Do not reference it in your code, as it may change at any time.') +const PropsMeta _$metaForTestJsProps = PropsMeta( + fields: $TestJsProps.$props, + keys: $TestJsProps.$propKeys, +); + +final UiFactoryConfig<_$$TestJsProps> _$TestJsConfig = UiFactoryConfig( + propsFactory: PropsFactory( + map: (map) => _$$TestJsProps(map), + jsMap: (map) => _$$TestJsProps$JsMap(map), + ), + displayName: 'TestJs'); + +@Deprecated(r'Use the private variable, _$TestJsConfig, instead ' + 'and update the `over_react` lower bound to version 4.1.0. ' + 'For information on why this is deprecated, see https://github.com/Workiva/over_react/pull/650') +final UiFactoryConfig<_$$TestJsProps> $TestJsConfig = _$TestJsConfig; + +// Concrete props implementation. +// +// Implements constructor and backing map, and links up to generated component factory. +@Deprecated('This API is for use only within generated code.' + ' Do not reference it in your code, as it may change at any time.') +abstract class _$$TestJsProps extends UiProps + with + TestJsProps, + // If this generated mixin is undefined, it's likely because TestJsProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of TestJsProps, and check that $TestJsProps is exported/imported properly. + $TestJsProps { + _$$TestJsProps._(); + + factory _$$TestJsProps(Map? backingMap) { + if (backingMap == null || backingMap is JsBackedMap) { + return _$$TestJsProps$JsMap(backingMap as JsBackedMap?); + } else { + return _$$TestJsProps$PlainMap(backingMap); + } + } + + /// Let `UiProps` internals know that this class has been generated. + @override + bool get $isClassGenerated => true; + + /// The default namespace for the prop getters/setters generated for this class. + @override + String get propKeyNamespace => ''; + + @override + PropsMetaCollection get staticMeta => const PropsMetaCollection({ + // If this generated mixin is undefined, it's likely because TestJsProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of TestJsProps, and check that $TestJsProps is exported/imported properly. + TestJsProps: $TestJsProps.meta, + }); + + @override + String $getPropKey(void Function(Map m) accessMap) => + _$getPropKey$_$$TestJsProps(accessMap, (map) => _$$TestJsProps(map)); +} + +/// An alias for [getPropKey] so it can be referenced within the props class impl +/// without being shadowed by the `getPropKey` instance extension member. +const _$getPropKey$_$$TestJsProps = getPropKey; + +// Concrete props implementation that can be backed by any [Map]. +@Deprecated('This API is for use only within generated code.' + ' Do not reference it in your code, as it may change at any time.') +class _$$TestJsProps$PlainMap extends _$$TestJsProps { + // This initializer of `_props` to an empty map, as well as the reassignment + // of `_props` in the constructor body is necessary to work around a DDC bug: https://github.com/dart-lang/sdk/issues/36217 + _$$TestJsProps$PlainMap(Map? backingMap) + : this._props = {}, + super._() { + this._props = backingMap ?? {}; + } + + /// The backing props map proxied by this class. + @override + Map get props => _props; + Map _props; +} + +// Concrete props implementation that can only be backed by [JsMap], +// allowing dart2js to compile more optimal code for key-value pair reads/writes. +@Deprecated('This API is for use only within generated code.' + ' Do not reference it in your code, as it may change at any time.') +class _$$TestJsProps$JsMap extends _$$TestJsProps { + // This initializer of `_props` to an empty map, as well as the reassignment + // of `_props` in the constructor body is necessary to work around a DDC bug: https://github.com/dart-lang/sdk/issues/36217 + _$$TestJsProps$JsMap(JsBackedMap? backingMap) + : this._props = JsBackedMap(), + super._() { + this._props = backingMap ?? JsBackedMap(); + } + + /// The backing props map proxied by this class. + @override + JsBackedMap get props => _props; + JsBackedMap _props; +} diff --git a/test/over_react/util/prop_conversion_test.dart b/test/over_react/util/prop_conversion_test.dart index 9fe524200..4cdf927f9 100644 --- a/test/over_react/util/prop_conversion_test.dart +++ b/test/over_react/util/prop_conversion_test.dart @@ -1373,12 +1373,12 @@ extension on TypeMatcher { having((ref) => ref.jsThis, 'jsThis', matcher); } -extension on TypeMatcher { +extension HavingToStringValue on TypeMatcher { Matcher havingToStringValue(dynamic matcher) => having((o) => o.toString(), 'toString() value', matcher); } -extension on TypeMatcher { +extension HavingDartComponent on TypeMatcher { Matcher havingDartComponent(dynamic matcher) => having((ref) => ref.dartComponent, 'dartComponent', matcher); } diff --git a/test/over_react_component_test.dart b/test/over_react_component_test.dart index f4ddcc727..faa2aae93 100644 --- a/test/over_react_component_test.dart +++ b/test/over_react_component_test.dart @@ -22,58 +22,60 @@ library over_react_component_test; import 'package:over_react/over_react.dart'; import 'package:test/test.dart'; -import 'over_react/component/_deprecated/abstract_transition_test.dart' - as deprecated_abstract_transition_test; -import 'over_react/component/abstract_transition_test.dart' - as abstract_transition_test; -import 'over_react/component/with_transition_test.dart' - as with_transition_test; -import 'over_react/component/dom_components_test.dart' as dom_components_test; -import 'over_react/component/error_boundary/error_boundary_test.dart' as error_boundary_test; -import 'over_react/component/_deprecated/error_boundary_mixin_test.dart' - as deprecated_error_boundary_mixin_test; -import 'over_react/component/_deprecated/error_boundary_test.dart' - as deprecated_error_boundary_test; -import 'over_react/component/ref_util_test.dart' as ref_test; -import 'over_react/component/element_type_test.dart' as element_type_test; -import 'over_react/component/memo_test.dart' as memo_test; -import 'over_react/component/prop_mixins_test.dart' as prop_mixins_test; -import 'over_react/component/prop_typedefs_test.dart' as prop_typedefs_test; -import 'over_react/component/pure_component_mixin_test.dart' - as pure_component_mixin_test; -import 'over_react/component/_deprecated/resize_sensor_test.dart' - as deprecated_resize_sensor_test; -import 'over_react/component/resize_sensor_test.dart' as resize_sensor_test; -import 'over_react/component/fragment_component_test.dart' - as fragment_component_test; -import 'over_react/component/strictmode_component_test.dart' - as strictmode_component_test; -import 'over_react/component/suspense_component_test.dart' - as suspense_component_test; -import 'over_react/component/context_test.dart' as context_test; -import 'over_react/component/typed_factory_test.dart' as typed_factory_test; +// import 'over_react/component/_deprecated/abstract_transition_test.dart' +// as deprecated_abstract_transition_test; +// import 'over_react/component/abstract_transition_test.dart' +// as abstract_transition_test; +// import 'over_react/component/with_transition_test.dart' +// as with_transition_test; +// import 'over_react/component/dom_components_test.dart' as dom_components_test; +// import 'over_react/component/error_boundary/error_boundary_test.dart' as error_boundary_test; +// import 'over_react/component/_deprecated/error_boundary_mixin_test.dart' +// as deprecated_error_boundary_mixin_test; +// import 'over_react/component/_deprecated/error_boundary_test.dart' +// as deprecated_error_boundary_test; +// import 'over_react/component/ref_util_test.dart' as ref_test; +// import 'over_react/component/element_type_test.dart' as element_type_test; +import 'over_react/component/lazy_test.dart' as lazy_test; +// import 'over_react/component/memo_test.dart' as memo_test; +// import 'over_react/component/prop_mixins_test.dart' as prop_mixins_test; +// import 'over_react/component/prop_typedefs_test.dart' as prop_typedefs_test; +// import 'over_react/component/pure_component_mixin_test.dart' +// as pure_component_mixin_test; +// import 'over_react/component/_deprecated/resize_sensor_test.dart' +// as deprecated_resize_sensor_test; +// import 'over_react/component/resize_sensor_test.dart' as resize_sensor_test; +// import 'over_react/component/fragment_component_test.dart' +// as fragment_component_test; +// import 'over_react/component/strictmode_component_test.dart' +// as strictmode_component_test; +// import 'over_react/component/suspense_component_test.dart' +// as suspense_component_test; +// import 'over_react/component/context_test.dart' as context_test; +// import 'over_react/component/typed_factory_test.dart' as typed_factory_test; void main() { enableTestMode(); - pure_component_mixin_test.main(); - deprecated_abstract_transition_test.main(); - abstract_transition_test.main(); - with_transition_test.main(); - error_boundary_test.main(); - deprecated_error_boundary_mixin_test.main(); - deprecated_error_boundary_test.main(); - ref_test.main(); - element_type_test.main(); - memo_test.main(); - dom_components_test.main(); - prop_mixins_test.main(); - prop_typedefs_test.main(); - deprecated_resize_sensor_test.main(); - resize_sensor_test.main(); - fragment_component_test.main(); - strictmode_component_test.main(); - suspense_component_test.main(); - context_test.main(); - typed_factory_test.main(); + // pure_component_mixin_test.main(); + // deprecated_abstract_transition_test.main(); + // abstract_transition_test.main(); + // with_transition_test.main(); + // error_boundary_test.main(); + // deprecated_error_boundary_mixin_test.main(); + // deprecated_error_boundary_test.main(); + // ref_test.main(); + // element_type_test.main(); + lazy_test.main(); + // memo_test.main(); + // dom_components_test.main(); + // prop_mixins_test.main(); + // prop_typedefs_test.main(); + // deprecated_resize_sensor_test.main(); + // resize_sensor_test.main(); + // fragment_component_test.main(); + // strictmode_component_test.main(); + // suspense_component_test.main(); + // context_test.main(); + // typed_factory_test.main(); } diff --git a/test/over_react_component_test.html b/test/over_react_component_test.html index a5414456d..1aaa83c46 100644 --- a/test/over_react_component_test.html +++ b/test/over_react_component_test.html @@ -16,15 +16,50 @@ - + OverReact Tests - over_react component + - + - - + + From 0ebd41f9e68accaffc67b96a7bb71d1470f5286a Mon Sep 17 00:00:00 2001 From: Keal Jones Date: Mon, 9 Sep 2024 17:03:08 -0700 Subject: [PATCH 02/31] uncomment out tests --- test/over_react_component_test.dart | 100 ++++++++++++++-------------- 1 file changed, 50 insertions(+), 50 deletions(-) diff --git a/test/over_react_component_test.dart b/test/over_react_component_test.dart index faa2aae93..9a481433f 100644 --- a/test/over_react_component_test.dart +++ b/test/over_react_component_test.dart @@ -22,60 +22,60 @@ library over_react_component_test; import 'package:over_react/over_react.dart'; import 'package:test/test.dart'; -// import 'over_react/component/_deprecated/abstract_transition_test.dart' -// as deprecated_abstract_transition_test; -// import 'over_react/component/abstract_transition_test.dart' -// as abstract_transition_test; -// import 'over_react/component/with_transition_test.dart' -// as with_transition_test; -// import 'over_react/component/dom_components_test.dart' as dom_components_test; -// import 'over_react/component/error_boundary/error_boundary_test.dart' as error_boundary_test; -// import 'over_react/component/_deprecated/error_boundary_mixin_test.dart' -// as deprecated_error_boundary_mixin_test; -// import 'over_react/component/_deprecated/error_boundary_test.dart' -// as deprecated_error_boundary_test; -// import 'over_react/component/ref_util_test.dart' as ref_test; -// import 'over_react/component/element_type_test.dart' as element_type_test; +import 'over_react/component/_deprecated/abstract_transition_test.dart' + as deprecated_abstract_transition_test; +import 'over_react/component/abstract_transition_test.dart' + as abstract_transition_test; +import 'over_react/component/with_transition_test.dart' + as with_transition_test; +import 'over_react/component/dom_components_test.dart' as dom_components_test; +import 'over_react/component/error_boundary/error_boundary_test.dart' as error_boundary_test; +import 'over_react/component/_deprecated/error_boundary_mixin_test.dart' + as deprecated_error_boundary_mixin_test; +import 'over_react/component/_deprecated/error_boundary_test.dart' + as deprecated_error_boundary_test; +import 'over_react/component/ref_util_test.dart' as ref_test; +import 'over_react/component/element_type_test.dart' as element_type_test; import 'over_react/component/lazy_test.dart' as lazy_test; -// import 'over_react/component/memo_test.dart' as memo_test; -// import 'over_react/component/prop_mixins_test.dart' as prop_mixins_test; -// import 'over_react/component/prop_typedefs_test.dart' as prop_typedefs_test; -// import 'over_react/component/pure_component_mixin_test.dart' -// as pure_component_mixin_test; -// import 'over_react/component/_deprecated/resize_sensor_test.dart' -// as deprecated_resize_sensor_test; -// import 'over_react/component/resize_sensor_test.dart' as resize_sensor_test; -// import 'over_react/component/fragment_component_test.dart' -// as fragment_component_test; -// import 'over_react/component/strictmode_component_test.dart' -// as strictmode_component_test; -// import 'over_react/component/suspense_component_test.dart' -// as suspense_component_test; -// import 'over_react/component/context_test.dart' as context_test; -// import 'over_react/component/typed_factory_test.dart' as typed_factory_test; +import 'over_react/component/memo_test.dart' as memo_test; +import 'over_react/component/prop_mixins_test.dart' as prop_mixins_test; +import 'over_react/component/prop_typedefs_test.dart' as prop_typedefs_test; +import 'over_react/component/pure_component_mixin_test.dart' + as pure_component_mixin_test; +import 'over_react/component/_deprecated/resize_sensor_test.dart' + as deprecated_resize_sensor_test; +import 'over_react/component/resize_sensor_test.dart' as resize_sensor_test; +import 'over_react/component/fragment_component_test.dart' + as fragment_component_test; +import 'over_react/component/strictmode_component_test.dart' + as strictmode_component_test; +import 'over_react/component/suspense_component_test.dart' + as suspense_component_test; +import 'over_react/component/context_test.dart' as context_test; +import 'over_react/component/typed_factory_test.dart' as typed_factory_test; void main() { enableTestMode(); - // pure_component_mixin_test.main(); - // deprecated_abstract_transition_test.main(); - // abstract_transition_test.main(); - // with_transition_test.main(); - // error_boundary_test.main(); - // deprecated_error_boundary_mixin_test.main(); - // deprecated_error_boundary_test.main(); - // ref_test.main(); - // element_type_test.main(); + pure_component_mixin_test.main(); + deprecated_abstract_transition_test.main(); + abstract_transition_test.main(); + with_transition_test.main(); + error_boundary_test.main(); + deprecated_error_boundary_mixin_test.main(); + deprecated_error_boundary_test.main(); + ref_test.main(); + element_type_test.main(); lazy_test.main(); - // memo_test.main(); - // dom_components_test.main(); - // prop_mixins_test.main(); - // prop_typedefs_test.main(); - // deprecated_resize_sensor_test.main(); - // resize_sensor_test.main(); - // fragment_component_test.main(); - // strictmode_component_test.main(); - // suspense_component_test.main(); - // context_test.main(); - // typed_factory_test.main(); + memo_test.main(); + dom_components_test.main(); + prop_mixins_test.main(); + prop_typedefs_test.main(); + deprecated_resize_sensor_test.main(); + resize_sensor_test.main(); + fragment_component_test.main(); + strictmode_component_test.main(); + suspense_component_test.main(); + context_test.main(); + typed_factory_test.main(); } From 96ba2f8b7e5ed3ab319da12008d98bb71470a3b4 Mon Sep 17 00:00:00 2001 From: Keal Jones Date: Mon, 9 Sep 2024 17:06:11 -0700 Subject: [PATCH 03/31] try different dependency_overrides --- pubspec.yaml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/pubspec.yaml b/pubspec.yaml index 5c9059038..2d3db8397 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -49,10 +49,8 @@ workiva: version: 1 react_boilerplate: disabled - dependency_overrides: react: git: - url: https://github.com/Workiva/react-dart.git - ref: remotes/origin/FED-3114-lazy - + url: git@github.com:Workiva/react-dart.git + ref: FED-3114-lazy From f5605c2cdf428f197704299a45723638f1cdccaa Mon Sep 17 00:00:00 2001 From: Keal Jones Date: Mon, 9 Sep 2024 17:07:57 -0700 Subject: [PATCH 04/31] ugh --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index 2d3db8397..24221bc7b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -52,5 +52,5 @@ workiva: dependency_overrides: react: git: - url: git@github.com:Workiva/react-dart.git + url: https://github.com/Workiva/react-dart.git ref: FED-3114-lazy From 131ceb90042a11cafe0aa3ee92a173484807aa07 Mon Sep 17 00:00:00 2001 From: Keal Jones Date: Thu, 12 Sep 2024 10:19:33 -0700 Subject: [PATCH 05/31] add lazy tests --- example/suspense/index.html | 67 +- example/suspense/main.dart | 51 +- lib/src/util/lazy.dart | 45 +- test/over_react/component/lazy_test.dart | 286 +++++- .../component/lazy_test.over_react.g.dart | 916 ++++++++++++++++++ .../component/suspense_component_test.dart | 32 - test/over_react_component_test.dart | 100 +- 7 files changed, 1338 insertions(+), 159 deletions(-) diff --git a/example/suspense/index.html b/example/suspense/index.html index d37af6770..cee072da8 100644 --- a/example/suspense/index.html +++ b/example/suspense/index.html @@ -16,23 +16,56 @@ - - - - over_react Suspense example - - - + + + + over_react Suspense example + + + - + + + +
- - -
- - - - - - + + + + + diff --git a/example/suspense/main.dart b/example/suspense/main.dart index c1c93a4a5..cb7295621 100644 --- a/example/suspense/main.dart +++ b/example/suspense/main.dart @@ -14,11 +14,60 @@ import 'dart:html'; +import 'package:js/js.dart'; +import 'package:over_react/js_component.dart'; import 'package:over_react/over_react.dart'; +import 'package:react/react_client/component_factory.dart'; +import 'package:react/react_client/react_interop.dart' hide lazy; import 'package:react/react_dom.dart' as react_dom; import 'counter_component.dart' deferred as lazy_component; import 'third_party_file.dart'; +part 'main.over_react.g.dart'; + +@Props(keyNamespace: '') +mixin TestJsProps on UiProps { + @Accessor(key: 'buttonProps') + JsMap? _$raw$buttonProps; + + Map? get buttonProps => unjsifyMapProp(_$raw$buttonProps); + set buttonProps(Map? value) => _$raw$buttonProps = jsifyMapProp(value); + + @Accessor(key: 'listOfProps') + List? _$raw$listOfProps; + + List? get listOfProps => unjsifyMapListProp(_$raw$listOfProps); + set listOfProps(List? value) => _$raw$listOfProps = jsifyMapListProp(value); + + @Accessor(key: 'inputRef') + dynamic _$raw$inputRef; + + dynamic get inputRef => unjsifyRefProp(_$raw$inputRef); + set inputRef(dynamic value) => _$raw$inputRef = jsifyRefProp(value); + + @Accessor(key: 'messageContext') + ReactContext? _$raw$messageContext; + + Context? get messageContext => unjsifyContextProp(_$raw$messageContext); + set messageContext(Context? value) => _$raw$messageContext = jsifyContextProp(value); + + dynamic /*ElementType*/ component; + dynamic /*ElementType*/ inputComponent; + dynamic /*ElementType*/ buttonComponent; +} + +@JS('TestJsComponent') +external ReactClass get _TestJs; + +UiFactory TestJs = uiJsComponent( + ReactJsComponentFactoryProxy(_TestJs), + _$TestJsConfig, // ignore: undefined_identifier +); + +UiFactory LazyTestJs = lazy(() async => TestJs, +_$TestJsConfig, // ignore: undefined_identifier +); + UiFactory LazyCounter = lazy(() async { await Future.delayed(Duration(seconds: 5)); await lazy_component.loadLibrary(); @@ -37,7 +86,7 @@ void main() { 'I am a fallback UI that will show while we load the lazy component! The load time is artificially inflated to last an additional 5 seconds just to prove it\'s working!', ) )( - (LazyCounter()..initialCount = 2)( + (LazyTestJs())( (Dom.div()..id = 'Heyyy!')(), ), ), diff --git a/lib/src/util/lazy.dart b/lib/src/util/lazy.dart index 257dad30d..9b88ea23b 100644 --- a/lib/src/util/lazy.dart +++ b/lib/src/util/lazy.dart @@ -14,10 +14,12 @@ library over_react.lazy; +import 'dart:html'; + import 'package:over_react/over_react.dart'; import 'package:react/react.dart' as react; -UiFactory lazy(Future> Function() loadComponent,/* UiFactoryConfig */ dynamic _config) { +UiFactory lazy(Future> Function() loadComponent,/* UiFactoryConfig */ dynamic _config, {bool useJsFactoryProxy = false}) { ArgumentError.checkNotNull(_config, '_config'); if (_config is! UiFactoryConfig) { @@ -26,34 +28,33 @@ UiFactory lazy(Future> Functio r'using either the generated factory config (i.e. _$FooConfig) or manually ' 'declaring your config correctly.'); } - - final lazyFactoryProxy = react.lazy(() async { - final factory = await loadComponent(); - final wrapper = uiForwardRef( - (props, ref) { - final builder = factory() - ..addProps(props) - ..ref = ref; - return props.children == null || (props.children != null && props.children?.isEmpty != false) ? builder() : builder(props.children); - }, - UiFactoryConfig( - propsFactory: PropsFactory.fromUiFactory(factory), - displayName: 'Lazy${_config.displayName}', - ), - ); - return wrapper().componentFactory!; - // return factory().componentFactory!; - }); - - // ignore: invalid_use_of_protected_member + // ignore: invalid_use_of_protected_member final propsFactory = _config.propsFactory; ArgumentError.checkNotNull(propsFactory, '_config.propsFactory'); propsFactory!; + + final lazyFactoryProxy = react.lazy(() async { + final factory = await loadComponent(); + // final wrapper = uiForwardRef( + // (props, ref) { + // final builder = factory() + // ..addProps(props) + // ..ref = ref; + // return props.children == null || (props.children != null && props.children?.isEmpty != false) ? builder() : builder(props.children); + // }, + // UiFactoryConfig( + // propsFactory: PropsFactory.fromUiFactory(factory), + // displayName: 'Lazy${_config.displayName}', + // ), + // ); + // return wrapper().componentFactory!; + return factory().componentFactory!; + }, useJsFactoryProxy: useJsFactoryProxy); + TProps _uiFactory([Map? props]) { TProps builder; if (props == null) { - // propsFactory should get promoted to non-nullable here, but it does not some reason propsF builder = propsFactory.jsMap(JsBackedMap()); } else if (props is JsBackedMap) { builder = propsFactory.jsMap(props); diff --git a/test/over_react/component/lazy_test.dart b/test/over_react/component/lazy_test.dart index a2d319c0b..503fa83f8 100644 --- a/test/over_react/component/lazy_test.dart +++ b/test/over_react/component/lazy_test.dart @@ -2,12 +2,13 @@ @JS() library rmui.test.unit.component.lazy_test; +import 'dart:convert'; import 'dart:html'; import 'package:js/js.dart'; import 'package:over_react/js_component.dart'; import 'package:over_react/over_react.dart'; -import 'package:react/react_client/component_factory.dart'; +import 'package:react/react_client.dart'; import 'package:react/react_client/react_interop.dart' hide lazy; import 'package:react_testing_library/matchers.dart'; import 'package:react_testing_library/react_testing_library.dart'; @@ -15,15 +16,24 @@ import 'package:react_testing_library/react_testing_library.dart'; import 'package:test/test.dart'; import 'package:over_react/components.dart' as components; -import '../util/prop_conversion_test.dart'; import '../util/ref_test_cases.dart'; part 'lazy_test.over_react.g.dart'; main() { enableTestMode(); + group('lazy wrapped', () { + group('JS component', () { + sharedPropConversionTests(LazyTestJs, isJsComponent: true); + }); +group('Dart component', () { + sharedPropConversionTests(LazyTestDart); +}); + }); - group('lazy', () { +} + +sharedPropConversionTests(UiFactory builder, { bool isJsComponent = false }) { group('prop conversions', () { test('a custom Dart Map prop', () async { @@ -32,20 +42,24 @@ test('a custom Dart Map prop', () async { // Use an ErrorBoundary to detect errors on render, since otherwise // React will just unmount the tree without throwing. await expectLater(() async { - render((Suspense()..fallback='Loading')((components.ErrorBoundary() + render((Suspense()..fallback=Dom.div()('Loading'))((components.ErrorBoundary() ..onComponentDidCatch = ((error, _) => renderErrors.add(error)) ..shouldLogErrors = false ..fallbackUIRenderer = ((_, __) => Dom.span()('An error occurred during render')) )( - (LazyTestJS() - ..component = ExpectsDartMapProp.elementType + (builder() + ..component = (isJsComponent ? ExpectsDartMapProp.elementType : ExpectsDartMapProp) ..addProps(ExpectsDartMapProp()..dartMapProp = {'foo': 'bar'}) ..addTestId('componentRoot'))(), ), ), ); - await screen.findByText('An error occurred during render', timeout: Duration(seconds: 5)); + // if (!isJsComponent) { + await waitForElementToBeRemoved(() => screen.getByText('Loading')); + // } else { + // await screen.findByText('An error occurred during render'); + // } // Use prints as an easy way to swallow `print` calls and // prevent RTL from forwarding console errors to the test output, // since React error boundary logging is pretty noisy. @@ -53,19 +67,21 @@ test('a custom Dart Map prop', () async { }, prints(anything)); expect(renderErrors, [ - isA().havingToStringValue(anyOf( - // DDC error message - matches(RegExp( - r"Expected a value of type 'Map[^']*', but got one of type '(Native|Legacy)JavaScriptObject'")), - // dart2js error message - matches(RegExp( - r"type '(Unknown|Plain)JavaScriptObject' is not a subtype of type 'Map[^']*'")), - )), + // if (isJsComponent) + // isA().havingToStringValue(anyOf( + // // DDC error message + // matches(RegExp( + // r"Expected a value of type 'Map[^']*', but got one of type '(Native|Legacy)JavaScriptObject'")), + // // dart2js error message + // matches(RegExp( + // r"type '(Unknown|Plain)JavaScriptObject' is not a subtype of type 'Map[^']*'")), + // )), ]); - +// if (!isJsComponent) { // These are the expectations we'd use if this case didn't error: - // final node = view.getByTestId('componentRoot'); - // expect(node, hasAttribute('data-dart-map-prop', jsonEncode({'foo': 'bar'}))); + final node = screen.getByTestId('componentRoot'); + expect(node, hasAttribute('data-dart-map-prop', jsonEncode({'foo': 'bar'}))); +// } }, tags: ['js-interop-tradeoff']); group('and a ref is set on that component', () { group('which eventually ends up on a non-Dart component', () { @@ -77,9 +93,9 @@ test('a custom Dart Map prop', () async { final testCase = testCaseCollection.createCaseByName(testCaseName); - render((Suspense()..fallback='Loading')((LazyTestJS() + render((Suspense()..fallback='Loading')((builder() ..addTestId('test-js-component') - ..component = BasicForwardRef.elementType + ..component = (isJsComponent ? BasicForwardRef.elementType : BasicForwardRef) ..ref = testCase.ref)())); await screen.findByTestId('test-js-component'); expect(testCase.getCurrent(), isA()); @@ -99,10 +115,10 @@ test('a custom Dart Map prop', () async { final testCase = testCaseCollection.createCaseByName(testCaseName); - render((Suspense()..fallback='Loading')((LazyTestJS() - ..component = Dom.div.elementType + render((Suspense()..fallback='Loading')((builder() + ..component = (isJsComponent ? Dom.div.elementType : Dom.div) ..addTestId('test-js-component') - ..inputComponent = BasicForwardRef.elementType + ..inputComponent = (isJsComponent ? BasicForwardRef.elementType : BasicForwardRef) ..inputRef = testCase.ref)())); await screen.findByTestId('test-js-component'); expect(testCase.getCurrent(), isA()); @@ -122,10 +138,10 @@ test('a custom Dart Map prop', () async { final testCase = testCaseCollection.createCaseByName(testCaseName); - render((Suspense()..fallback='Loading')((LazyTestJS() - ..component = Dom.div.elementType + render((Suspense()..fallback='Loading')((builder() + ..component = (isJsComponent ? Dom.div.elementType : Dom.div) ..addTestId('test-js-component') - ..buttonComponent = BasicForwardRef.elementType + ..buttonComponent = (isJsComponent ? BasicForwardRef.elementType : BasicForwardRef) ..buttonProps = (domProps()..ref = testCase.ref))())); await screen.findByTestId('test-js-component'); expect(testCase.getCurrent(), isA()); @@ -187,10 +203,10 @@ test('a custom Dart Map prop', () async { final testCase = testCaseCollection.createCaseByName(testCaseName); - render((Suspense()..fallback='Loading')((LazyTestJS() - ..component = Dom.div.elementType +// GREG LOOK HERE + render((Suspense()..fallback='Loading')((builder() ..addTestId('test-js-component') - ..component = ClassComponent.elementType + ..component = (isJsComponent ? ClassComponent.elementType : ClassComponent) ..ref = testCase.ref)())); await screen.findByTestId('test-js-component'); if (isDartRefObjectCase) { @@ -223,10 +239,10 @@ test('a custom Dart Map prop', () async { final testCase = testCaseCollection.createCaseByName(testCaseName); - render((Suspense()..fallback='Loading')((LazyTestJS() - ..component = Dom.div.elementType + render((Suspense()..fallback='Loading')((builder() + ..component = (isJsComponent ? Dom.div.elementType : Dom.div) ..addTestId('test-js-component') - ..inputComponent = ClassComponent.elementType + ..inputComponent = (isJsComponent ? ClassComponent.elementType : ClassComponent) ..inputRef = testCase.ref)())); await screen.findByTestId('test-js-component'); if (isDartRefObjectCase) { @@ -257,10 +273,10 @@ test('a custom Dart Map prop', () async { final testCase = testCaseCollection.createCaseByName(testCaseName); - render((Suspense()..fallback='Loading')((LazyTestJS() - ..component = Dom.div.elementType + render((Suspense()..fallback='Loading')((builder() + ..component = (isJsComponent ? Dom.div.elementType : Dom.div) ..addTestId('test-js-component') - ..buttonComponent = ClassComponent.elementType + ..buttonComponent = (isJsComponent ? ClassComponent.elementType : ClassComponent) ..buttonProps = (domProps()..ref = testCase.ref))())); await screen.findByTestId('test-js-component'); // JS callbacks look the same to generateJsProps as Dart callback refs do, @@ -284,11 +300,177 @@ test('a custom Dart Map prop', () async { }); }); }); - }); } -UiFactory LazyTestJS = lazy(() async => TestJs, +UiFactory LazyTestJs = lazy(() async => TestJs, _$TestJsConfig, // ignore: undefined_identifier +//useJsFactoryProxy: true, +); + +UiFactory LazyTestDart = lazy(() async => TestDart, +_$TestDartConfig, // ignore: undefined_identifier +); + +// +// Matcher convenience functions +// + +Matcher sameOrSameAllowInterop(Function f) => + anyOf(same(allowInterop(f)), same(f)); + +extension on TypeMatcher { + Matcher havingJsRef(dynamic matcher) => + having((ref) => ref.jsRef, 'jsRef', matcher); +} + +extension on TypeMatcher { + Matcher havingJsThis(dynamic matcher) => + having((ref) => ref.jsThis, 'jsThis', matcher); +} + +extension on TypeMatcher { + Matcher havingToStringValue(dynamic matcher) => + having((o) => o.toString(), 'toString() value', matcher); +} + +extension on TypeMatcher { + Matcher havingDartComponent(dynamic matcher) => + having((ref) => ref.dartComponent, 'dartComponent', matcher); +} + +// +// Runtime type verification +// + +/// Verifies the runtime type of object, throwing a cast error if it does not match. +/// +/// Even though [object] typed as [T], we'll verify it explicitly in case +/// the type-check at the function call site gets compiled out. +void verifyType(T? object) { + if (T == Object || T == dynamic || T == Null) { + throw ArgumentError.value(T, 'T', 'must be more specific'); + } + + if (object is! T) { + // Try to get Dart to throw a real type error by explicitly casting. + // ignore: unnecessary_statements, unnecessary_cast + object as T; + + // The above `as` should always fail, but may not if the cast gets compiled out, + // or if there are edge cases in the type system cases where this seemingly-incorrect cast works. + // + // Explicitly throw an error so that this case doesn't go unnoticed. + throw UnexpectedSuccessfulCastError( + 'Casting ${Error.safeToString(object)} to type $T should have failed, but did not for some reason.' + ' Check the implementation of verifyType.'); + } +} + +class UnexpectedSuccessfulCastError extends Error { + final String message; + + UnexpectedSuccessfulCastError(this.message); + + @override + String toString() => message; +} + +// +// Test components for use as JS `component` prop +// + +mixin ExpectsDartMapPropProps on UiProps { + Map? dartMapProp; +} + +UiFactory ExpectsDartMapProp = uiForwardRef( + (props, ref) { + verifyType(props.dartMapProp); + + return (Dom.div() + ..addProps(props) + // Use the prop in a way that would fail if it wasn't the correct type + ..['data-dart-map-prop'] = jsonEncode(props.dartMapProp) + ..ref = ref)(props.children); + }, + _$ExpectsDartMapPropConfig, // ignore: undefined_identifier +); + +mixin ExpectsDartStylePropProps on UiProps {} + +UiFactory ExpectsDartStyleProp = uiForwardRef( + (props, ref) { + verifyType>(props.style); + + return (Dom.div() + ..addProps(props) + ..style = { + // Use the prop in a way that would fail if it wasn't the correct type + ...props.style!, + })(props.children); + }, + _$ExpectsDartStylePropConfig, // ignore: undefined_identifier +); + +mixin ExpectsListChildrenPropProps on UiProps {} + +UiFactory ExpectsListChildrenProp = uiForwardRef( + (props, ref) { + verifyType(props.children); + + return (Dom.div()..addProps(props))( + // Use the prop in a way that would fail if it wasn't the correct type + props.children!.map((child) => child), + ); + }, + _$ExpectsListChildrenPropConfig, // ignore: undefined_identifier +); + +// +// Other test components +// + +UiFactory ClassComponent = + castUiFactory(_$ClassComponent); // ignore: undefined_identifier + +mixin ClassComponentProps on UiProps {} + +class ClassComponentComponent extends UiComponent2 { + @override + render() { + return (Dom.div()..addProps(props))(props.children); + } +} + +mixin BasicForwardRefProps on UiProps {} + +const basicForwardRefAttribute = 'data-basic-forward-ref'; +UiFactory BasicForwardRef = uiForwardRef( + (props, ref) { + return (Dom.span() + ..addProps(props) + ..[basicForwardRefAttribute] = '' + ..ref = ref)(props.children); + }, + _$BasicForwardRefConfig, // ignore: undefined_identifier +); + +mixin DartTestJsWrapperPropsMixin on UiProps { + void Function(DartTestJsWrapperProps props)? onRender; +} + +class DartTestJsWrapperProps = UiProps + with TestJsProps, DartTestJsWrapperPropsMixin; + +UiFactory DartTestJsWrapper = uiForwardRef( + (props, ref) { + props.onRender?.call(props); + + return (TestJs() + ..addProps(props.getPropsToForward(exclude: {DartTestJsWrapperPropsMixin})) + ..ref = ref)(props.children); + }, + _$DartTestJsWrapperConfig, // ignore: undefined_identifier ); // @@ -334,3 +516,33 @@ UiFactory TestJs = uiJsComponent( @JS('TestJsComponent') external ReactClass get _TestJs; + + +final defaultMessageContext = createContextInit('default context value'); + +@Props(keyNamespace: '') +class TestDartProps = UiProps with TestJsProps; + +UiFactory TestDart = uiForwardRef( + (props, ref) { + + final buttonProps = props.buttonProps ?? const {}; + final listOfProps = props.listOfProps ?? const [{}]; + final inputRef = props.inputRef; + final buttonComponent = props.buttonComponent ?? Dom.button; + final inputComponent = props.inputComponent ?? Dom.input; + final component = props.component ?? Dom.span; + final children = props.children; + final messageContext = defaultMessageContext; + + final message = useContext(messageContext); + return Dom.div()( + (buttonComponent()..addProps(buttonProps))(), + (Dom.li()..addProps(listOfProps[0] ?? {}))(), + (inputComponent()..addProps(Dom.input()..type = 'text')..ref = inputRef)(), + (component()..addProps(props.getPropsToForward(exclude: {TestJsProps}))..ref = ref)(children), + (Dom.div()..role = 'alert')(message), + ); + }, + _$TestDartConfig, // ignore: undefined_identifier +); diff --git a/test/over_react/component/lazy_test.over_react.g.dart b/test/over_react/component/lazy_test.over_react.g.dart index 076e13a6c..081552584 100644 --- a/test/over_react/component/lazy_test.over_react.g.dart +++ b/test/over_react/component/lazy_test.over_react.g.dart @@ -7,6 +7,342 @@ part of 'lazy_test.dart'; // OverReactBuilder (package:over_react/src/builder.dart) // ************************************************************************** +// React component factory implementation. +// +// Registers component implementation and links type meta to builder factory. +@Deprecated('This API is for use only within generated code.' + ' Do not reference it in your code, as it may change at any time.') +final $ClassComponentComponentFactory = registerComponent2( + () => _$ClassComponentComponent(), + builderFactory: _$ClassComponent, + componentClass: ClassComponentComponent, + isWrapper: false, + parentType: null, +); + +_$$ClassComponentProps _$ClassComponent([Map? backingProps]) => + backingProps == null + ? _$$ClassComponentProps$JsMap(JsBackedMap()) + : _$$ClassComponentProps(backingProps); + +// Concrete props implementation. +// +// Implements constructor and backing map, and links up to generated component factory. +@Deprecated('This API is for use only within generated code.' + ' Do not reference it in your code, as it may change at any time.') +abstract class _$$ClassComponentProps extends UiProps + with + ClassComponentProps, + // If this generated mixin is undefined, it's likely because ClassComponentProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of ClassComponentProps, and check that $ClassComponentProps is exported/imported properly. + $ClassComponentProps { + _$$ClassComponentProps._(); + + factory _$$ClassComponentProps(Map? backingMap) { + if (backingMap == null || backingMap is JsBackedMap) { + return _$$ClassComponentProps$JsMap(backingMap as JsBackedMap?); + } else { + return _$$ClassComponentProps$PlainMap(backingMap); + } + } + + /// Let `UiProps` internals know that this class has been generated. + @override + bool get $isClassGenerated => true; + + /// The `ReactComponentFactory` associated with the component built by this class. + @override + ReactComponentFactoryProxy get componentFactory => + super.componentFactory ?? $ClassComponentComponentFactory; + + /// The default namespace for the prop getters/setters generated for this class. + @override + String get propKeyNamespace => ''; + + @override + PropsMetaCollection get staticMeta => const PropsMetaCollection({ + // If this generated mixin is undefined, it's likely because ClassComponentProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of ClassComponentProps, and check that $ClassComponentProps is exported/imported properly. + ClassComponentProps: $ClassComponentProps.meta, + }); + + @override + String $getPropKey(void Function(Map m) accessMap) => + _$getPropKey$_$$ClassComponentProps( + accessMap, (map) => _$$ClassComponentProps(map)); +} + +/// An alias for [getPropKey] so it can be referenced within the props class impl +/// without being shadowed by the `getPropKey` instance extension member. +const _$getPropKey$_$$ClassComponentProps = getPropKey; + +// Concrete props implementation that can be backed by any [Map]. +@Deprecated('This API is for use only within generated code.' + ' Do not reference it in your code, as it may change at any time.') +class _$$ClassComponentProps$PlainMap extends _$$ClassComponentProps { + // This initializer of `_props` to an empty map, as well as the reassignment + // of `_props` in the constructor body is necessary to work around a DDC bug: https://github.com/dart-lang/sdk/issues/36217 + _$$ClassComponentProps$PlainMap(Map? backingMap) + : this._props = {}, + super._() { + this._props = backingMap ?? {}; + } + + /// The backing props map proxied by this class. + @override + Map get props => _props; + Map _props; +} + +// Concrete props implementation that can only be backed by [JsMap], +// allowing dart2js to compile more optimal code for key-value pair reads/writes. +@Deprecated('This API is for use only within generated code.' + ' Do not reference it in your code, as it may change at any time.') +class _$$ClassComponentProps$JsMap extends _$$ClassComponentProps { + // This initializer of `_props` to an empty map, as well as the reassignment + // of `_props` in the constructor body is necessary to work around a DDC bug: https://github.com/dart-lang/sdk/issues/36217 + _$$ClassComponentProps$JsMap(JsBackedMap? backingMap) + : this._props = JsBackedMap(), + super._() { + this._props = backingMap ?? JsBackedMap(); + } + + /// The backing props map proxied by this class. + @override + JsBackedMap get props => _props; + JsBackedMap _props; +} + +// Concrete component implementation mixin. +// +// Implements typed props/state factories, defaults `consumedPropKeys` to the keys +// generated for the associated props class. +@Deprecated('This API is for use only within generated code.' + ' Do not reference it in your code, as it may change at any time.') +class _$ClassComponentComponent extends ClassComponentComponent { + late _$$ClassComponentProps$JsMap _cachedTypedProps; + + @override + _$$ClassComponentProps$JsMap get props => _cachedTypedProps; + + @override + set props(Map value) { + assert( + getBackingMap(value) is JsBackedMap, + 'Component2.props should never be set directly in ' + 'production. If this is required for testing, the ' + 'component should be rendered within the test. If ' + 'that does not have the necessary result, the last ' + 'resort is to use typedPropsFactoryJs.'); + super.props = value; + _cachedTypedProps = + typedPropsFactoryJs(getBackingMap(value) as JsBackedMap); + } + + @override + _$$ClassComponentProps$JsMap typedPropsFactoryJs(JsBackedMap? backingMap) => + _$$ClassComponentProps$JsMap(backingMap); + + @override + _$$ClassComponentProps typedPropsFactory(Map? backingMap) => + _$$ClassComponentProps(backingMap); + + /// Let `UiComponent` internals know that this class has been generated. + @override + bool get $isClassGenerated => true; + + @override + String get displayName => 'ClassComponent'; + + /// The default consumed props, comprising all props mixins used by ClassComponentProps. + /// Used in `*ConsumedProps` methods if [consumedProps] is not overridden. + @override + get $defaultConsumedProps => propsMeta.all; + + @override + PropsMetaCollection get propsMeta => const PropsMetaCollection({ + // If this generated mixin is undefined, it's likely because ClassComponentProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of ClassComponentProps, and check that $ClassComponentProps is exported/imported properly. + ClassComponentProps: $ClassComponentProps.meta, + }); +} + +@Deprecated('This API is for use only within generated code.' + ' Do not reference it in your code, as it may change at any time.' + ' EXCEPTION: this may be used in legacy boilerplate until' + ' it is transitioned to the new mixin-based boilerplate.') +mixin $ExpectsDartMapPropProps on ExpectsDartMapPropProps { + static const PropsMeta meta = _$metaForExpectsDartMapPropProps; + @override + Map? get dartMapProp => + (props[_$key__dartMapProp__ExpectsDartMapPropProps] ?? null) as Map?; + @override + set dartMapProp(Map? value) => + props[_$key__dartMapProp__ExpectsDartMapPropProps] = value; + /* GENERATED CONSTANTS */ + static const PropDescriptor _$prop__dartMapProp__ExpectsDartMapPropProps = + PropDescriptor(_$key__dartMapProp__ExpectsDartMapPropProps); + static const String _$key__dartMapProp__ExpectsDartMapPropProps = + 'ExpectsDartMapPropProps.dartMapProp'; + + static const List $props = [ + _$prop__dartMapProp__ExpectsDartMapPropProps + ]; + static const List $propKeys = [ + _$key__dartMapProp__ExpectsDartMapPropProps + ]; + + @override + @UiProps.$mustCallSuper + void validateRequiredProps() { + super.validateRequiredProps(); + } +} + +@Deprecated('This API is for use only within generated code.' + ' Do not reference it in your code, as it may change at any time.') +const PropsMeta _$metaForExpectsDartMapPropProps = PropsMeta( + fields: $ExpectsDartMapPropProps.$props, + keys: $ExpectsDartMapPropProps.$propKeys, +); + +@Deprecated('This API is for use only within generated code.' + ' Do not reference it in your code, as it may change at any time.' + ' EXCEPTION: this may be used in legacy boilerplate until' + ' it is transitioned to the new mixin-based boilerplate.') +mixin $ExpectsDartStylePropProps on ExpectsDartStylePropProps { + static const PropsMeta meta = _$metaForExpectsDartStylePropProps; + /* GENERATED CONSTANTS */ + + static const List $props = []; + static const List $propKeys = []; + + @override + @UiProps.$mustCallSuper + void validateRequiredProps() { + super.validateRequiredProps(); + } +} + +@Deprecated('This API is for use only within generated code.' + ' Do not reference it in your code, as it may change at any time.') +const PropsMeta _$metaForExpectsDartStylePropProps = PropsMeta( + fields: $ExpectsDartStylePropProps.$props, + keys: $ExpectsDartStylePropProps.$propKeys, +); + +@Deprecated('This API is for use only within generated code.' + ' Do not reference it in your code, as it may change at any time.' + ' EXCEPTION: this may be used in legacy boilerplate until' + ' it is transitioned to the new mixin-based boilerplate.') +mixin $ExpectsListChildrenPropProps on ExpectsListChildrenPropProps { + static const PropsMeta meta = _$metaForExpectsListChildrenPropProps; + /* GENERATED CONSTANTS */ + + static const List $props = []; + static const List $propKeys = []; + + @override + @UiProps.$mustCallSuper + void validateRequiredProps() { + super.validateRequiredProps(); + } +} + +@Deprecated('This API is for use only within generated code.' + ' Do not reference it in your code, as it may change at any time.') +const PropsMeta _$metaForExpectsListChildrenPropProps = PropsMeta( + fields: $ExpectsListChildrenPropProps.$props, + keys: $ExpectsListChildrenPropProps.$propKeys, +); + +@Deprecated('This API is for use only within generated code.' + ' Do not reference it in your code, as it may change at any time.' + ' EXCEPTION: this may be used in legacy boilerplate until' + ' it is transitioned to the new mixin-based boilerplate.') +mixin $ClassComponentProps on ClassComponentProps { + static const PropsMeta meta = _$metaForClassComponentProps; + /* GENERATED CONSTANTS */ + + static const List $props = []; + static const List $propKeys = []; + + @override + @UiProps.$mustCallSuper + void validateRequiredProps() { + super.validateRequiredProps(); + } +} + +@Deprecated('This API is for use only within generated code.' + ' Do not reference it in your code, as it may change at any time.') +const PropsMeta _$metaForClassComponentProps = PropsMeta( + fields: $ClassComponentProps.$props, + keys: $ClassComponentProps.$propKeys, +); + +@Deprecated('This API is for use only within generated code.' + ' Do not reference it in your code, as it may change at any time.' + ' EXCEPTION: this may be used in legacy boilerplate until' + ' it is transitioned to the new mixin-based boilerplate.') +mixin $BasicForwardRefProps on BasicForwardRefProps { + static const PropsMeta meta = _$metaForBasicForwardRefProps; + /* GENERATED CONSTANTS */ + + static const List $props = []; + static const List $propKeys = []; + + @override + @UiProps.$mustCallSuper + void validateRequiredProps() { + super.validateRequiredProps(); + } +} + +@Deprecated('This API is for use only within generated code.' + ' Do not reference it in your code, as it may change at any time.') +const PropsMeta _$metaForBasicForwardRefProps = PropsMeta( + fields: $BasicForwardRefProps.$props, + keys: $BasicForwardRefProps.$propKeys, +); + +@Deprecated('This API is for use only within generated code.' + ' Do not reference it in your code, as it may change at any time.' + ' EXCEPTION: this may be used in legacy boilerplate until' + ' it is transitioned to the new mixin-based boilerplate.') +mixin $DartTestJsWrapperPropsMixin on DartTestJsWrapperPropsMixin { + static const PropsMeta meta = _$metaForDartTestJsWrapperPropsMixin; + @override + void Function(DartTestJsWrapperProps props)? get onRender => + (props[_$key__onRender__DartTestJsWrapperPropsMixin] ?? null) as void + Function(DartTestJsWrapperProps props)?; + @override + set onRender(void Function(DartTestJsWrapperProps props)? value) => + props[_$key__onRender__DartTestJsWrapperPropsMixin] = value; + /* GENERATED CONSTANTS */ + static const PropDescriptor _$prop__onRender__DartTestJsWrapperPropsMixin = + PropDescriptor(_$key__onRender__DartTestJsWrapperPropsMixin); + static const String _$key__onRender__DartTestJsWrapperPropsMixin = + 'DartTestJsWrapperPropsMixin.onRender'; + + static const List $props = [ + _$prop__onRender__DartTestJsWrapperPropsMixin + ]; + static const List $propKeys = [ + _$key__onRender__DartTestJsWrapperPropsMixin + ]; + + @override + @UiProps.$mustCallSuper + void validateRequiredProps() { + super.validateRequiredProps(); + } +} + +@Deprecated('This API is for use only within generated code.' + ' Do not reference it in your code, as it may change at any time.') +const PropsMeta _$metaForDartTestJsWrapperPropsMixin = PropsMeta( + fields: $DartTestJsWrapperPropsMixin.$props, + keys: $DartTestJsWrapperPropsMixin.$propKeys, +); + @Deprecated('This API is for use only within generated code.' ' Do not reference it in your code, as it may change at any time.' ' EXCEPTION: this may be used in legacy boilerplate until' @@ -211,3 +547,583 @@ class _$$TestJsProps$JsMap extends _$$TestJsProps { JsBackedMap get props => _props; JsBackedMap _props; } + +final UiFactoryConfig<_$$ExpectsDartMapPropProps> _$ExpectsDartMapPropConfig = + UiFactoryConfig( + propsFactory: PropsFactory( + map: (map) => _$$ExpectsDartMapPropProps(map), + jsMap: (map) => _$$ExpectsDartMapPropProps$JsMap(map), + ), + displayName: 'ExpectsDartMapProp'); + +@Deprecated(r'Use the private variable, _$ExpectsDartMapPropConfig, instead ' + 'and update the `over_react` lower bound to version 4.1.0. ' + 'For information on why this is deprecated, see https://github.com/Workiva/over_react/pull/650') +final UiFactoryConfig<_$$ExpectsDartMapPropProps> $ExpectsDartMapPropConfig = + _$ExpectsDartMapPropConfig; + +// Concrete props implementation. +// +// Implements constructor and backing map, and links up to generated component factory. +@Deprecated('This API is for use only within generated code.' + ' Do not reference it in your code, as it may change at any time.') +abstract class _$$ExpectsDartMapPropProps extends UiProps + with + ExpectsDartMapPropProps, + // If this generated mixin is undefined, it's likely because ExpectsDartMapPropProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of ExpectsDartMapPropProps, and check that $ExpectsDartMapPropProps is exported/imported properly. + $ExpectsDartMapPropProps { + _$$ExpectsDartMapPropProps._(); + + factory _$$ExpectsDartMapPropProps(Map? backingMap) { + if (backingMap == null || backingMap is JsBackedMap) { + return _$$ExpectsDartMapPropProps$JsMap(backingMap as JsBackedMap?); + } else { + return _$$ExpectsDartMapPropProps$PlainMap(backingMap); + } + } + + /// Let `UiProps` internals know that this class has been generated. + @override + bool get $isClassGenerated => true; + + /// The default namespace for the prop getters/setters generated for this class. + @override + String get propKeyNamespace => ''; + + @override + PropsMetaCollection get staticMeta => const PropsMetaCollection({ + // If this generated mixin is undefined, it's likely because ExpectsDartMapPropProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of ExpectsDartMapPropProps, and check that $ExpectsDartMapPropProps is exported/imported properly. + ExpectsDartMapPropProps: $ExpectsDartMapPropProps.meta, + }); + + @override + String $getPropKey(void Function(Map m) accessMap) => + _$getPropKey$_$$ExpectsDartMapPropProps( + accessMap, (map) => _$$ExpectsDartMapPropProps(map)); +} + +/// An alias for [getPropKey] so it can be referenced within the props class impl +/// without being shadowed by the `getPropKey` instance extension member. +const _$getPropKey$_$$ExpectsDartMapPropProps = getPropKey; + +// Concrete props implementation that can be backed by any [Map]. +@Deprecated('This API is for use only within generated code.' + ' Do not reference it in your code, as it may change at any time.') +class _$$ExpectsDartMapPropProps$PlainMap extends _$$ExpectsDartMapPropProps { + // This initializer of `_props` to an empty map, as well as the reassignment + // of `_props` in the constructor body is necessary to work around a DDC bug: https://github.com/dart-lang/sdk/issues/36217 + _$$ExpectsDartMapPropProps$PlainMap(Map? backingMap) + : this._props = {}, + super._() { + this._props = backingMap ?? {}; + } + + /// The backing props map proxied by this class. + @override + Map get props => _props; + Map _props; +} + +// Concrete props implementation that can only be backed by [JsMap], +// allowing dart2js to compile more optimal code for key-value pair reads/writes. +@Deprecated('This API is for use only within generated code.' + ' Do not reference it in your code, as it may change at any time.') +class _$$ExpectsDartMapPropProps$JsMap extends _$$ExpectsDartMapPropProps { + // This initializer of `_props` to an empty map, as well as the reassignment + // of `_props` in the constructor body is necessary to work around a DDC bug: https://github.com/dart-lang/sdk/issues/36217 + _$$ExpectsDartMapPropProps$JsMap(JsBackedMap? backingMap) + : this._props = JsBackedMap(), + super._() { + this._props = backingMap ?? JsBackedMap(); + } + + /// The backing props map proxied by this class. + @override + JsBackedMap get props => _props; + JsBackedMap _props; +} + +final UiFactoryConfig<_$$ExpectsDartStylePropProps> + _$ExpectsDartStylePropConfig = UiFactoryConfig( + propsFactory: PropsFactory( + map: (map) => _$$ExpectsDartStylePropProps(map), + jsMap: (map) => _$$ExpectsDartStylePropProps$JsMap(map), + ), + displayName: 'ExpectsDartStyleProp'); + +@Deprecated(r'Use the private variable, _$ExpectsDartStylePropConfig, instead ' + 'and update the `over_react` lower bound to version 4.1.0. ' + 'For information on why this is deprecated, see https://github.com/Workiva/over_react/pull/650') +final UiFactoryConfig<_$$ExpectsDartStylePropProps> + $ExpectsDartStylePropConfig = _$ExpectsDartStylePropConfig; + +// Concrete props implementation. +// +// Implements constructor and backing map, and links up to generated component factory. +@Deprecated('This API is for use only within generated code.' + ' Do not reference it in your code, as it may change at any time.') +abstract class _$$ExpectsDartStylePropProps extends UiProps + with + ExpectsDartStylePropProps, + // If this generated mixin is undefined, it's likely because ExpectsDartStylePropProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of ExpectsDartStylePropProps, and check that $ExpectsDartStylePropProps is exported/imported properly. + $ExpectsDartStylePropProps { + _$$ExpectsDartStylePropProps._(); + + factory _$$ExpectsDartStylePropProps(Map? backingMap) { + if (backingMap == null || backingMap is JsBackedMap) { + return _$$ExpectsDartStylePropProps$JsMap(backingMap as JsBackedMap?); + } else { + return _$$ExpectsDartStylePropProps$PlainMap(backingMap); + } + } + + /// Let `UiProps` internals know that this class has been generated. + @override + bool get $isClassGenerated => true; + + /// The default namespace for the prop getters/setters generated for this class. + @override + String get propKeyNamespace => ''; + + @override + PropsMetaCollection get staticMeta => const PropsMetaCollection({ + // If this generated mixin is undefined, it's likely because ExpectsDartStylePropProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of ExpectsDartStylePropProps, and check that $ExpectsDartStylePropProps is exported/imported properly. + ExpectsDartStylePropProps: $ExpectsDartStylePropProps.meta, + }); + + @override + String $getPropKey(void Function(Map m) accessMap) => + _$getPropKey$_$$ExpectsDartStylePropProps( + accessMap, (map) => _$$ExpectsDartStylePropProps(map)); +} + +/// An alias for [getPropKey] so it can be referenced within the props class impl +/// without being shadowed by the `getPropKey` instance extension member. +const _$getPropKey$_$$ExpectsDartStylePropProps = getPropKey; + +// Concrete props implementation that can be backed by any [Map]. +@Deprecated('This API is for use only within generated code.' + ' Do not reference it in your code, as it may change at any time.') +class _$$ExpectsDartStylePropProps$PlainMap + extends _$$ExpectsDartStylePropProps { + // This initializer of `_props` to an empty map, as well as the reassignment + // of `_props` in the constructor body is necessary to work around a DDC bug: https://github.com/dart-lang/sdk/issues/36217 + _$$ExpectsDartStylePropProps$PlainMap(Map? backingMap) + : this._props = {}, + super._() { + this._props = backingMap ?? {}; + } + + /// The backing props map proxied by this class. + @override + Map get props => _props; + Map _props; +} + +// Concrete props implementation that can only be backed by [JsMap], +// allowing dart2js to compile more optimal code for key-value pair reads/writes. +@Deprecated('This API is for use only within generated code.' + ' Do not reference it in your code, as it may change at any time.') +class _$$ExpectsDartStylePropProps$JsMap extends _$$ExpectsDartStylePropProps { + // This initializer of `_props` to an empty map, as well as the reassignment + // of `_props` in the constructor body is necessary to work around a DDC bug: https://github.com/dart-lang/sdk/issues/36217 + _$$ExpectsDartStylePropProps$JsMap(JsBackedMap? backingMap) + : this._props = JsBackedMap(), + super._() { + this._props = backingMap ?? JsBackedMap(); + } + + /// The backing props map proxied by this class. + @override + JsBackedMap get props => _props; + JsBackedMap _props; +} + +final UiFactoryConfig<_$$ExpectsListChildrenPropProps> + _$ExpectsListChildrenPropConfig = UiFactoryConfig( + propsFactory: PropsFactory( + map: (map) => _$$ExpectsListChildrenPropProps(map), + jsMap: (map) => _$$ExpectsListChildrenPropProps$JsMap(map), + ), + displayName: 'ExpectsListChildrenProp'); + +@Deprecated( + r'Use the private variable, _$ExpectsListChildrenPropConfig, instead ' + 'and update the `over_react` lower bound to version 4.1.0. ' + 'For information on why this is deprecated, see https://github.com/Workiva/over_react/pull/650') +final UiFactoryConfig<_$$ExpectsListChildrenPropProps> + $ExpectsListChildrenPropConfig = _$ExpectsListChildrenPropConfig; + +// Concrete props implementation. +// +// Implements constructor and backing map, and links up to generated component factory. +@Deprecated('This API is for use only within generated code.' + ' Do not reference it in your code, as it may change at any time.') +abstract class _$$ExpectsListChildrenPropProps extends UiProps + with + ExpectsListChildrenPropProps, + // If this generated mixin is undefined, it's likely because ExpectsListChildrenPropProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of ExpectsListChildrenPropProps, and check that $ExpectsListChildrenPropProps is exported/imported properly. + $ExpectsListChildrenPropProps { + _$$ExpectsListChildrenPropProps._(); + + factory _$$ExpectsListChildrenPropProps(Map? backingMap) { + if (backingMap == null || backingMap is JsBackedMap) { + return _$$ExpectsListChildrenPropProps$JsMap(backingMap as JsBackedMap?); + } else { + return _$$ExpectsListChildrenPropProps$PlainMap(backingMap); + } + } + + /// Let `UiProps` internals know that this class has been generated. + @override + bool get $isClassGenerated => true; + + /// The default namespace for the prop getters/setters generated for this class. + @override + String get propKeyNamespace => ''; + + @override + PropsMetaCollection get staticMeta => const PropsMetaCollection({ + // If this generated mixin is undefined, it's likely because ExpectsListChildrenPropProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of ExpectsListChildrenPropProps, and check that $ExpectsListChildrenPropProps is exported/imported properly. + ExpectsListChildrenPropProps: $ExpectsListChildrenPropProps.meta, + }); + + @override + String $getPropKey(void Function(Map m) accessMap) => + _$getPropKey$_$$ExpectsListChildrenPropProps( + accessMap, (map) => _$$ExpectsListChildrenPropProps(map)); +} + +/// An alias for [getPropKey] so it can be referenced within the props class impl +/// without being shadowed by the `getPropKey` instance extension member. +const _$getPropKey$_$$ExpectsListChildrenPropProps = getPropKey; + +// Concrete props implementation that can be backed by any [Map]. +@Deprecated('This API is for use only within generated code.' + ' Do not reference it in your code, as it may change at any time.') +class _$$ExpectsListChildrenPropProps$PlainMap + extends _$$ExpectsListChildrenPropProps { + // This initializer of `_props` to an empty map, as well as the reassignment + // of `_props` in the constructor body is necessary to work around a DDC bug: https://github.com/dart-lang/sdk/issues/36217 + _$$ExpectsListChildrenPropProps$PlainMap(Map? backingMap) + : this._props = {}, + super._() { + this._props = backingMap ?? {}; + } + + /// The backing props map proxied by this class. + @override + Map get props => _props; + Map _props; +} + +// Concrete props implementation that can only be backed by [JsMap], +// allowing dart2js to compile more optimal code for key-value pair reads/writes. +@Deprecated('This API is for use only within generated code.' + ' Do not reference it in your code, as it may change at any time.') +class _$$ExpectsListChildrenPropProps$JsMap + extends _$$ExpectsListChildrenPropProps { + // This initializer of `_props` to an empty map, as well as the reassignment + // of `_props` in the constructor body is necessary to work around a DDC bug: https://github.com/dart-lang/sdk/issues/36217 + _$$ExpectsListChildrenPropProps$JsMap(JsBackedMap? backingMap) + : this._props = JsBackedMap(), + super._() { + this._props = backingMap ?? JsBackedMap(); + } + + /// The backing props map proxied by this class. + @override + JsBackedMap get props => _props; + JsBackedMap _props; +} + +final UiFactoryConfig<_$$BasicForwardRefProps> _$BasicForwardRefConfig = + UiFactoryConfig( + propsFactory: PropsFactory( + map: (map) => _$$BasicForwardRefProps(map), + jsMap: (map) => _$$BasicForwardRefProps$JsMap(map), + ), + displayName: 'BasicForwardRef'); + +@Deprecated(r'Use the private variable, _$BasicForwardRefConfig, instead ' + 'and update the `over_react` lower bound to version 4.1.0. ' + 'For information on why this is deprecated, see https://github.com/Workiva/over_react/pull/650') +final UiFactoryConfig<_$$BasicForwardRefProps> $BasicForwardRefConfig = + _$BasicForwardRefConfig; + +// Concrete props implementation. +// +// Implements constructor and backing map, and links up to generated component factory. +@Deprecated('This API is for use only within generated code.' + ' Do not reference it in your code, as it may change at any time.') +abstract class _$$BasicForwardRefProps extends UiProps + with + BasicForwardRefProps, + // If this generated mixin is undefined, it's likely because BasicForwardRefProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of BasicForwardRefProps, and check that $BasicForwardRefProps is exported/imported properly. + $BasicForwardRefProps { + _$$BasicForwardRefProps._(); + + factory _$$BasicForwardRefProps(Map? backingMap) { + if (backingMap == null || backingMap is JsBackedMap) { + return _$$BasicForwardRefProps$JsMap(backingMap as JsBackedMap?); + } else { + return _$$BasicForwardRefProps$PlainMap(backingMap); + } + } + + /// Let `UiProps` internals know that this class has been generated. + @override + bool get $isClassGenerated => true; + + /// The default namespace for the prop getters/setters generated for this class. + @override + String get propKeyNamespace => ''; + + @override + PropsMetaCollection get staticMeta => const PropsMetaCollection({ + // If this generated mixin is undefined, it's likely because BasicForwardRefProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of BasicForwardRefProps, and check that $BasicForwardRefProps is exported/imported properly. + BasicForwardRefProps: $BasicForwardRefProps.meta, + }); + + @override + String $getPropKey(void Function(Map m) accessMap) => + _$getPropKey$_$$BasicForwardRefProps( + accessMap, (map) => _$$BasicForwardRefProps(map)); +} + +/// An alias for [getPropKey] so it can be referenced within the props class impl +/// without being shadowed by the `getPropKey` instance extension member. +const _$getPropKey$_$$BasicForwardRefProps = getPropKey; + +// Concrete props implementation that can be backed by any [Map]. +@Deprecated('This API is for use only within generated code.' + ' Do not reference it in your code, as it may change at any time.') +class _$$BasicForwardRefProps$PlainMap extends _$$BasicForwardRefProps { + // This initializer of `_props` to an empty map, as well as the reassignment + // of `_props` in the constructor body is necessary to work around a DDC bug: https://github.com/dart-lang/sdk/issues/36217 + _$$BasicForwardRefProps$PlainMap(Map? backingMap) + : this._props = {}, + super._() { + this._props = backingMap ?? {}; + } + + /// The backing props map proxied by this class. + @override + Map get props => _props; + Map _props; +} + +// Concrete props implementation that can only be backed by [JsMap], +// allowing dart2js to compile more optimal code for key-value pair reads/writes. +@Deprecated('This API is for use only within generated code.' + ' Do not reference it in your code, as it may change at any time.') +class _$$BasicForwardRefProps$JsMap extends _$$BasicForwardRefProps { + // This initializer of `_props` to an empty map, as well as the reassignment + // of `_props` in the constructor body is necessary to work around a DDC bug: https://github.com/dart-lang/sdk/issues/36217 + _$$BasicForwardRefProps$JsMap(JsBackedMap? backingMap) + : this._props = JsBackedMap(), + super._() { + this._props = backingMap ?? JsBackedMap(); + } + + /// The backing props map proxied by this class. + @override + JsBackedMap get props => _props; + JsBackedMap _props; +} + +final UiFactoryConfig<_$$DartTestJsWrapperProps> _$DartTestJsWrapperConfig = + UiFactoryConfig( + propsFactory: PropsFactory( + map: (map) => _$$DartTestJsWrapperProps(map), + jsMap: (map) => _$$DartTestJsWrapperProps$JsMap(map), + ), + displayName: 'DartTestJsWrapper'); + +@Deprecated(r'Use the private variable, _$DartTestJsWrapperConfig, instead ' + 'and update the `over_react` lower bound to version 4.1.0. ' + 'For information on why this is deprecated, see https://github.com/Workiva/over_react/pull/650') +final UiFactoryConfig<_$$DartTestJsWrapperProps> $DartTestJsWrapperConfig = + _$DartTestJsWrapperConfig; + +// Concrete props implementation. +// +// Implements constructor and backing map, and links up to generated component factory. +@Deprecated('This API is for use only within generated code.' + ' Do not reference it in your code, as it may change at any time.') +abstract class _$$DartTestJsWrapperProps extends UiProps + with + TestJsProps, + // If this generated mixin is undefined, it's likely because TestJsProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of TestJsProps, and check that $TestJsProps is exported/imported properly. + $TestJsProps, + DartTestJsWrapperPropsMixin, + // If this generated mixin is undefined, it's likely because DartTestJsWrapperPropsMixin is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of DartTestJsWrapperPropsMixin, and check that $DartTestJsWrapperPropsMixin is exported/imported properly. + $DartTestJsWrapperPropsMixin + implements + DartTestJsWrapperProps { + _$$DartTestJsWrapperProps._(); + + factory _$$DartTestJsWrapperProps(Map? backingMap) { + if (backingMap == null || backingMap is JsBackedMap) { + return _$$DartTestJsWrapperProps$JsMap(backingMap as JsBackedMap?); + } else { + return _$$DartTestJsWrapperProps$PlainMap(backingMap); + } + } + + /// Let `UiProps` internals know that this class has been generated. + @override + bool get $isClassGenerated => true; + + /// The default namespace for the prop getters/setters generated for this class. + @override + String get propKeyNamespace => ''; + + @override + PropsMetaCollection get staticMeta => const PropsMetaCollection({ + // If this generated mixin is undefined, it's likely because TestJsProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of TestJsProps, and check that $TestJsProps is exported/imported properly. + TestJsProps: $TestJsProps.meta, + // If this generated mixin is undefined, it's likely because DartTestJsWrapperPropsMixin is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of DartTestJsWrapperPropsMixin, and check that $DartTestJsWrapperPropsMixin is exported/imported properly. + DartTestJsWrapperPropsMixin: $DartTestJsWrapperPropsMixin.meta, + }); + + @override + String $getPropKey(void Function(Map m) accessMap) => + _$getPropKey$_$$DartTestJsWrapperProps( + accessMap, (map) => _$$DartTestJsWrapperProps(map)); +} + +/// An alias for [getPropKey] so it can be referenced within the props class impl +/// without being shadowed by the `getPropKey` instance extension member. +const _$getPropKey$_$$DartTestJsWrapperProps = getPropKey; + +// Concrete props implementation that can be backed by any [Map]. +@Deprecated('This API is for use only within generated code.' + ' Do not reference it in your code, as it may change at any time.') +class _$$DartTestJsWrapperProps$PlainMap extends _$$DartTestJsWrapperProps { + // This initializer of `_props` to an empty map, as well as the reassignment + // of `_props` in the constructor body is necessary to work around a DDC bug: https://github.com/dart-lang/sdk/issues/36217 + _$$DartTestJsWrapperProps$PlainMap(Map? backingMap) + : this._props = {}, + super._() { + this._props = backingMap ?? {}; + } + + /// The backing props map proxied by this class. + @override + Map get props => _props; + Map _props; +} + +// Concrete props implementation that can only be backed by [JsMap], +// allowing dart2js to compile more optimal code for key-value pair reads/writes. +@Deprecated('This API is for use only within generated code.' + ' Do not reference it in your code, as it may change at any time.') +class _$$DartTestJsWrapperProps$JsMap extends _$$DartTestJsWrapperProps { + // This initializer of `_props` to an empty map, as well as the reassignment + // of `_props` in the constructor body is necessary to work around a DDC bug: https://github.com/dart-lang/sdk/issues/36217 + _$$DartTestJsWrapperProps$JsMap(JsBackedMap? backingMap) + : this._props = JsBackedMap(), + super._() { + this._props = backingMap ?? JsBackedMap(); + } + + /// The backing props map proxied by this class. + @override + JsBackedMap get props => _props; + JsBackedMap _props; +} + +final UiFactoryConfig<_$$TestDartProps> _$TestDartConfig = UiFactoryConfig( + propsFactory: PropsFactory( + map: (map) => _$$TestDartProps(map), + jsMap: (map) => _$$TestDartProps$JsMap(map), + ), + displayName: 'TestDart'); + +@Deprecated(r'Use the private variable, _$TestDartConfig, instead ' + 'and update the `over_react` lower bound to version 4.1.0. ' + 'For information on why this is deprecated, see https://github.com/Workiva/over_react/pull/650') +final UiFactoryConfig<_$$TestDartProps> $TestDartConfig = _$TestDartConfig; + +// Concrete props implementation. +// +// Implements constructor and backing map, and links up to generated component factory. +@Deprecated('This API is for use only within generated code.' + ' Do not reference it in your code, as it may change at any time.') +abstract class _$$TestDartProps extends UiProps + with + TestJsProps, + // If this generated mixin is undefined, it's likely because TestJsProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of TestJsProps, and check that $TestJsProps is exported/imported properly. + $TestJsProps + implements + TestDartProps { + _$$TestDartProps._(); + + factory _$$TestDartProps(Map? backingMap) { + if (backingMap == null || backingMap is JsBackedMap) { + return _$$TestDartProps$JsMap(backingMap as JsBackedMap?); + } else { + return _$$TestDartProps$PlainMap(backingMap); + } + } + + /// Let `UiProps` internals know that this class has been generated. + @override + bool get $isClassGenerated => true; + + /// The default namespace for the prop getters/setters generated for this class. + @override + String get propKeyNamespace => ''; + + @override + PropsMetaCollection get staticMeta => const PropsMetaCollection({ + // If this generated mixin is undefined, it's likely because TestJsProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of TestJsProps, and check that $TestJsProps is exported/imported properly. + TestJsProps: $TestJsProps.meta, + }); + + @override + String $getPropKey(void Function(Map m) accessMap) => + _$getPropKey$_$$TestDartProps(accessMap, (map) => _$$TestDartProps(map)); +} + +/// An alias for [getPropKey] so it can be referenced within the props class impl +/// without being shadowed by the `getPropKey` instance extension member. +const _$getPropKey$_$$TestDartProps = getPropKey; + +// Concrete props implementation that can be backed by any [Map]. +@Deprecated('This API is for use only within generated code.' + ' Do not reference it in your code, as it may change at any time.') +class _$$TestDartProps$PlainMap extends _$$TestDartProps { + // This initializer of `_props` to an empty map, as well as the reassignment + // of `_props` in the constructor body is necessary to work around a DDC bug: https://github.com/dart-lang/sdk/issues/36217 + _$$TestDartProps$PlainMap(Map? backingMap) + : this._props = {}, + super._() { + this._props = backingMap ?? {}; + } + + /// The backing props map proxied by this class. + @override + Map get props => _props; + Map _props; +} + +// Concrete props implementation that can only be backed by [JsMap], +// allowing dart2js to compile more optimal code for key-value pair reads/writes. +@Deprecated('This API is for use only within generated code.' + ' Do not reference it in your code, as it may change at any time.') +class _$$TestDartProps$JsMap extends _$$TestDartProps { + // This initializer of `_props` to an empty map, as well as the reassignment + // of `_props` in the constructor body is necessary to work around a DDC bug: https://github.com/dart-lang/sdk/issues/36217 + _$$TestDartProps$JsMap(JsBackedMap? backingMap) + : this._props = JsBackedMap(), + super._() { + this._props = backingMap ?? JsBackedMap(); + } + + /// The backing props map proxied by this class. + @override + JsBackedMap get props => _props; + JsBackedMap _props; +} diff --git a/test/over_react/component/suspense_component_test.dart b/test/over_react/component/suspense_component_test.dart index 37bca667c..896ec6b23 100644 --- a/test/over_react/component/suspense_component_test.dart +++ b/test/over_react/component/suspense_component_test.dart @@ -15,46 +15,14 @@ library suspense_component_test; import 'dart:async'; -import 'dart:js_util'; import 'package:js/js.dart'; -import 'package:over_react/js_component.dart'; import 'package:over_react/over_react.dart'; -import 'package:over_react/src/util/promise_interop.dart'; -import 'package:react/react_client/component_factory.dart'; -import 'package:react/react_client/react_interop.dart' as react_interop; import 'package:react_testing_library/react_testing_library.dart'; import 'package:test/test.dart'; import 'fixtures/lazy_load_me_props.dart'; import 'fixtures/lazy_load_me_component.dart' deferred as lazy_load_me; -@JS('React.lazy') -external react_interop.ReactClass jsLazy(Promise Function() factory); - -// Only intended for testing purposes, Please do not copy/paste this into your repo. -// This will most likely be added to the PUBLIC api in the future, -// but needs more testing and Typing decisions to be made first. -UiFactory lazy(Future> Function() factory, UiFactoryConfig factoryConfig) { - return uiJsComponent( - ReactJsComponentFactoryProxy( - jsLazy( - allowInterop( - () => futureToPromise( - // React.lazy only supports "default exports" from a module. - // This `{default: yourExport}` workaround can be found in the React.lazy RFC comments. - // See: https://github.com/reactjs/rfcs/pull/64#issuecomment-431507924 - (() async { - //resolvedFactory = await factory(); - return jsify({'default': (await factory()).elementType}); - })(), - ), - ), - ), - ), - factoryConfig, - ); -} - const lazyId = 'lazy'; const gregIsNotLazy = 'gregisnotlazy'; const loadingId = 'loading'; diff --git a/test/over_react_component_test.dart b/test/over_react_component_test.dart index 9a481433f..faa2aae93 100644 --- a/test/over_react_component_test.dart +++ b/test/over_react_component_test.dart @@ -22,60 +22,60 @@ library over_react_component_test; import 'package:over_react/over_react.dart'; import 'package:test/test.dart'; -import 'over_react/component/_deprecated/abstract_transition_test.dart' - as deprecated_abstract_transition_test; -import 'over_react/component/abstract_transition_test.dart' - as abstract_transition_test; -import 'over_react/component/with_transition_test.dart' - as with_transition_test; -import 'over_react/component/dom_components_test.dart' as dom_components_test; -import 'over_react/component/error_boundary/error_boundary_test.dart' as error_boundary_test; -import 'over_react/component/_deprecated/error_boundary_mixin_test.dart' - as deprecated_error_boundary_mixin_test; -import 'over_react/component/_deprecated/error_boundary_test.dart' - as deprecated_error_boundary_test; -import 'over_react/component/ref_util_test.dart' as ref_test; -import 'over_react/component/element_type_test.dart' as element_type_test; +// import 'over_react/component/_deprecated/abstract_transition_test.dart' +// as deprecated_abstract_transition_test; +// import 'over_react/component/abstract_transition_test.dart' +// as abstract_transition_test; +// import 'over_react/component/with_transition_test.dart' +// as with_transition_test; +// import 'over_react/component/dom_components_test.dart' as dom_components_test; +// import 'over_react/component/error_boundary/error_boundary_test.dart' as error_boundary_test; +// import 'over_react/component/_deprecated/error_boundary_mixin_test.dart' +// as deprecated_error_boundary_mixin_test; +// import 'over_react/component/_deprecated/error_boundary_test.dart' +// as deprecated_error_boundary_test; +// import 'over_react/component/ref_util_test.dart' as ref_test; +// import 'over_react/component/element_type_test.dart' as element_type_test; import 'over_react/component/lazy_test.dart' as lazy_test; -import 'over_react/component/memo_test.dart' as memo_test; -import 'over_react/component/prop_mixins_test.dart' as prop_mixins_test; -import 'over_react/component/prop_typedefs_test.dart' as prop_typedefs_test; -import 'over_react/component/pure_component_mixin_test.dart' - as pure_component_mixin_test; -import 'over_react/component/_deprecated/resize_sensor_test.dart' - as deprecated_resize_sensor_test; -import 'over_react/component/resize_sensor_test.dart' as resize_sensor_test; -import 'over_react/component/fragment_component_test.dart' - as fragment_component_test; -import 'over_react/component/strictmode_component_test.dart' - as strictmode_component_test; -import 'over_react/component/suspense_component_test.dart' - as suspense_component_test; -import 'over_react/component/context_test.dart' as context_test; -import 'over_react/component/typed_factory_test.dart' as typed_factory_test; +// import 'over_react/component/memo_test.dart' as memo_test; +// import 'over_react/component/prop_mixins_test.dart' as prop_mixins_test; +// import 'over_react/component/prop_typedefs_test.dart' as prop_typedefs_test; +// import 'over_react/component/pure_component_mixin_test.dart' +// as pure_component_mixin_test; +// import 'over_react/component/_deprecated/resize_sensor_test.dart' +// as deprecated_resize_sensor_test; +// import 'over_react/component/resize_sensor_test.dart' as resize_sensor_test; +// import 'over_react/component/fragment_component_test.dart' +// as fragment_component_test; +// import 'over_react/component/strictmode_component_test.dart' +// as strictmode_component_test; +// import 'over_react/component/suspense_component_test.dart' +// as suspense_component_test; +// import 'over_react/component/context_test.dart' as context_test; +// import 'over_react/component/typed_factory_test.dart' as typed_factory_test; void main() { enableTestMode(); - pure_component_mixin_test.main(); - deprecated_abstract_transition_test.main(); - abstract_transition_test.main(); - with_transition_test.main(); - error_boundary_test.main(); - deprecated_error_boundary_mixin_test.main(); - deprecated_error_boundary_test.main(); - ref_test.main(); - element_type_test.main(); + // pure_component_mixin_test.main(); + // deprecated_abstract_transition_test.main(); + // abstract_transition_test.main(); + // with_transition_test.main(); + // error_boundary_test.main(); + // deprecated_error_boundary_mixin_test.main(); + // deprecated_error_boundary_test.main(); + // ref_test.main(); + // element_type_test.main(); lazy_test.main(); - memo_test.main(); - dom_components_test.main(); - prop_mixins_test.main(); - prop_typedefs_test.main(); - deprecated_resize_sensor_test.main(); - resize_sensor_test.main(); - fragment_component_test.main(); - strictmode_component_test.main(); - suspense_component_test.main(); - context_test.main(); - typed_factory_test.main(); + // memo_test.main(); + // dom_components_test.main(); + // prop_mixins_test.main(); + // prop_typedefs_test.main(); + // deprecated_resize_sensor_test.main(); + // resize_sensor_test.main(); + // fragment_component_test.main(); + // strictmode_component_test.main(); + // suspense_component_test.main(); + // context_test.main(); + // typed_factory_test.main(); } From 6b6fd4c9ce90581132ea67ab0bf4f95e27a1c97e Mon Sep 17 00:00:00 2001 From: Keal Jones Date: Mon, 16 Sep 2024 11:47:25 -0700 Subject: [PATCH 06/31] get lazy prop conversion tests going --- lib/src/util/lazy.dart | 46 +- test/over_react/component/lazy_test.dart | 660 ++++++++++-------- .../component/lazy_test.over_react.g.dart | 411 +++++++++-- 3 files changed, 760 insertions(+), 357 deletions(-) diff --git a/lib/src/util/lazy.dart b/lib/src/util/lazy.dart index 9b88ea23b..cd62ee5b2 100644 --- a/lib/src/util/lazy.dart +++ b/lib/src/util/lazy.dart @@ -14,43 +14,45 @@ library over_react.lazy; -import 'dart:html'; - import 'package:over_react/over_react.dart'; import 'package:react/react.dart' as react; -UiFactory lazy(Future> Function() loadComponent,/* UiFactoryConfig */ dynamic _config, {bool useJsFactoryProxy = false}) { +UiFactory lazy( + Future> Function() loadComponent, /* UiFactoryConfig */ dynamic _config, + {bool useJsFactoryProxy = false}) { ArgumentError.checkNotNull(_config, '_config'); if (_config is! UiFactoryConfig) { - throw ArgumentError( - '_config should be a UiFactoryConfig. Make sure you are ' + throw ArgumentError('_config should be a UiFactoryConfig. Make sure you are ' r'using either the generated factory config (i.e. _$FooConfig) or manually ' 'declaring your config correctly.'); } - // ignore: invalid_use_of_protected_member + // ignore: invalid_use_of_protected_member final propsFactory = _config.propsFactory; ArgumentError.checkNotNull(propsFactory, '_config.propsFactory'); propsFactory!; - final lazyFactoryProxy = react.lazy(() async { final factory = await loadComponent(); - // final wrapper = uiForwardRef( - // (props, ref) { - // final builder = factory() - // ..addProps(props) - // ..ref = ref; - // return props.children == null || (props.children != null && props.children?.isEmpty != false) ? builder() : builder(props.children); - // }, - // UiFactoryConfig( - // propsFactory: PropsFactory.fromUiFactory(factory), - // displayName: 'Lazy${_config.displayName}', - // ), - // ); - // return wrapper().componentFactory!; - return factory().componentFactory!; - }, useJsFactoryProxy: useJsFactoryProxy); + // 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 = uiForwardRef( + (props, ref) { + final builder = factory() + ..addProps(props) + ..ref = ref; + return props.children == null || (props.children != null && props.children?.isEmpty != false) + ? builder() + : builder(props.children); + }, + UiFactoryConfig( + propsFactory: PropsFactory.fromUiFactory(factory), + displayName: 'Lazy${_config.displayName}', + ), + ); + return wrapper().componentFactory!; + }); TProps _uiFactory([Map? props]) { TProps builder; diff --git a/test/over_react/component/lazy_test.dart b/test/over_react/component/lazy_test.dart index 503fa83f8..b08c8d08c 100644 --- a/test/over_react/component/lazy_test.dart +++ b/test/over_react/component/lazy_test.dart @@ -3,13 +3,15 @@ library rmui.test.unit.component.lazy_test; import 'dart:convert'; +import 'dart:developer'; import 'dart:html'; +import 'dart:js_util'; import 'package:js/js.dart'; import 'package:over_react/js_component.dart'; import 'package:over_react/over_react.dart'; import 'package:react/react_client.dart'; -import 'package:react/react_client/react_interop.dart' hide lazy; +import 'package:react/react_client/react_interop.dart' hide lazy, createRef; import 'package:react_testing_library/matchers.dart'; import 'package:react_testing_library/react_testing_library.dart'; @@ -22,320 +24,345 @@ part 'lazy_test.over_react.g.dart'; main() { enableTestMode(); - group('lazy wrapped', () { - group('JS component', () { - sharedPropConversionTests(LazyTestJs, isJsComponent: true); + group('lazy wrapped', () { + group('JS component', () { + test('has correct map prop conversion', () async { + // The map should be a Js object, not a Dart Map + final ref = createRef(); + final renderErrors = []; + bool mapPropFunctionCalled = false; + bool functionPropCalled = false; + + render( + (components.ErrorBoundary() + ..onComponentDidCatch = ((error, _) => renderErrors.add(error)) + ..shouldLogErrors = false + ..fallbackUIRenderer = ((_, __) => Dom.span()('An error occurred during render')))( + (Suspense() + ..fallback = (Dom.div()..addTestId('suspense'))( + 'I am a fallback UI that will show while we load the lazy component! The load time is artificially inflated to last an additional 5 seconds just to prove it\'s working!', + ))( + (LazyJsTypeTester() + ..ref = ref + ..mapProp = { + 'test': 'foo', + 'fun': () { + mapPropFunctionCalled = true; + } + } + ..functionProp = (() { + functionPropCalled = true; + return 'test'; + }))('JS'), + ))); + // Wait for the "lazy" component to display + await screen.findByText('JS'); + expect(renderErrors, isEmpty); + expect(ref.current, isA()); + expect(mapPropFunctionCalled, isTrue); + expect(functionPropCalled, isTrue); + expect(screen.getByTestId('test'), isInTheDocument); }); -group('Dart component', () { - sharedPropConversionTests(LazyTestDart); -}); + sharedNestedPropConversionTests(LazyTestJs, isJsComponent: true); }); - + group('Dart component', () { + test('has correct map prop conversion', () async { + // The map should be a Dart Map, not a JS object + final ref = createRef(); + final renderErrors = []; + bool mapPropFunctionCalled = false; + bool functionPropCalled = false; + + render((components.ErrorBoundary() + ..onComponentDidCatch = ((error, _) => renderErrors.add(error)) + ..shouldLogErrors = false + ..fallbackUIRenderer = ((_, __) => Dom.span()('An error occurred during render')))((Suspense() + ..fallback = (Dom.div()..id = 'suspense')( + 'I am a fallback UI that will show while we load the lazy component! The load time is artificially inflated to last an additional 5 seconds just to prove it\'s working!', + ))( + (LazyUiForwardRefTypeTester() + ..ref = ref + ..mapProp = { + 'test': 'foo', + 'fun': () { + mapPropFunctionCalled = true; + } + } + ..functionProp = (() { + functionPropCalled = true; + return 'test'; + }))('UiForwardRef'), + ))); + // Wait for the "lazy" component to display + await screen.findByText('UiForwardRef'); + expect(ref.current, isA()); + expect(mapPropFunctionCalled, isTrue); + expect(functionPropCalled, isTrue); + expect(screen.getByTestId('test'), isInTheDocument); + }); + sharedNestedPropConversionTests(LazyTestDart); + }); + }); } -sharedPropConversionTests(UiFactory builder, { bool isJsComponent = false }) { - group('prop conversions', () { - -test('a custom Dart Map prop', () async { - final renderErrors = []; - - // Use an ErrorBoundary to detect errors on render, since otherwise - // React will just unmount the tree without throwing. - await expectLater(() async { - render((Suspense()..fallback=Dom.div()('Loading'))((components.ErrorBoundary() +sharedNestedPropConversionTests(UiFactory builder, {bool isJsComponent = false}) { + group('nested prop conversions works as expected in advanced cases:', () { + test('a custom Dart Map prop on a Dart component', () async { + final renderErrors = []; + + // Use an ErrorBoundary to detect errors on render, since otherwise + // React will just unmount the tree without throwing. + await expectLater(() async { + render( + (Suspense()..fallback = Dom.div()('Loading'))( + (components.ErrorBoundary() ..onComponentDidCatch = ((error, _) => renderErrors.add(error)) ..shouldLogErrors = false - ..fallbackUIRenderer = - ((_, __) => Dom.span()('An error occurred during render')) - )( - (builder() - ..component = (isJsComponent ? ExpectsDartMapProp.elementType : ExpectsDartMapProp) - ..addProps(ExpectsDartMapProp()..dartMapProp = {'foo': 'bar'}) - ..addTestId('componentRoot'))(), - ), - ), - ); - // if (!isJsComponent) { - await waitForElementToBeRemoved(() => screen.getByText('Loading')); - // } else { - // await screen.findByText('An error occurred during render'); - // } - // Use prints as an easy way to swallow `print` calls and - // prevent RTL from forwarding console errors to the test output, - // since React error boundary logging is pretty noisy. - // TODO instead, disable logging in this rtl.render call once that option is available: FED-1641 - }, prints(anything)); - - expect(renderErrors, [ - // if (isJsComponent) - // isA().havingToStringValue(anyOf( - // // DDC error message - // matches(RegExp( - // r"Expected a value of type 'Map[^']*', but got one of type '(Native|Legacy)JavaScriptObject'")), - // // dart2js error message - // matches(RegExp( - // r"type '(Unknown|Plain)JavaScriptObject' is not a subtype of type 'Map[^']*'")), - // )), - ]); -// if (!isJsComponent) { - // These are the expectations we'd use if this case didn't error: - final node = screen.getByTestId('componentRoot'); - expect(node, hasAttribute('data-dart-map-prop', jsonEncode({'foo': 'bar'}))); -// } - }, tags: ['js-interop-tradeoff']); - group('and a ref is set on that component', () { - group('which eventually ends up on a non-Dart component', () { - final testCaseCollection = RefTestCaseCollection(); - - group('and is passed in via a forwarded "ref" prop as a', () { - for (final testCaseName in testCaseCollection.allTestCaseNames) { - test(testCaseName, () async { - final testCase = - testCaseCollection.createCaseByName(testCaseName); - - render((Suspense()..fallback='Loading')((builder() - ..addTestId('test-js-component') - ..component = (isJsComponent ? BasicForwardRef.elementType : BasicForwardRef) - ..ref = testCase.ref)())); - await screen.findByTestId('test-js-component'); - expect(testCase.getCurrent(), isA()); - expect(testCase.getCurrent(), - hasAttribute(basicForwardRefAttribute), - reason: - 'test setup: ref should have been forwarded to the element we expect'); - }); - } + ..fallbackUIRenderer = ((_, __) => Dom.span()('An error occurred during render')))( + (builder() + ..component = (isJsComponent ? ExpectsDartMapProp.elementType : ExpectsDartMapProp) + ..addProps(ExpectsDartMapProp()..dartMapProp = {'foo': 'bar'}) + ..addTestId('componentRoot'))(), + ), + ), + ); + + await waitForElementToBeRemoved(() => screen.getByText('Loading')); + + // Use prints as an easy way to swallow `print` calls and + // prevent RTL from forwarding console errors to the test output, + // since React error boundary logging is pretty noisy. + // TODO instead, disable logging in this rtl.render call once that option is available: FED-1641 + }, prints(anything)); + + expect(renderErrors, [ + if (isJsComponent) + isA().havingToStringValue(anyOf( + // DDC error message + matches( + RegExp(r"Expected a value of type 'Map[^']*', but got one of type '(Native|Legacy)JavaScriptObject'")), + // dart2js error message + matches(RegExp(r"type '(Unknown|Plain)JavaScriptObject' is not a subtype of type 'Map[^']*'")), + )), + ]); + if (!isJsComponent) { + // These are the expectations we'd use if this case didn't error: + final node = screen.getByTestId('componentRoot'); + expect(node, hasAttribute('data-dart-map-prop', jsonEncode({'foo': 'bar'}))); + } + }, tags: ['js-interop-tradeoff']); + group('a ref is set on the lazy wrapper', () { + group('which eventually ends up on a non-Dart component', () { + final testCaseCollection = RefTestCaseCollection(); + + group('and is passed in via a forwarded "ref" prop as a', () { + for (final testCaseName in testCaseCollection.allTestCaseNames) { + test(testCaseName, () async { + final testCase = testCaseCollection.createCaseByName(testCaseName); + + render((Suspense()..fallback = 'Loading')((builder() + ..addTestId('test-js-component') + ..component = (isJsComponent ? BasicForwardRef.elementType : BasicForwardRef) + ..ref = testCase.ref)())); + await screen.findByTestId('test-js-component'); + expect(testCase.getCurrent(), isA()); + expect(testCase.getCurrent(), hasAttribute(basicForwardRefAttribute), + reason: 'test setup: ref should have been forwarded to the element we expect'); }); + } + }); - group( - 'and is passed in via a custom "ref" prop (with conversion) as a', - () { - for (final testCaseName in testCaseCollection.allTestCaseNames) { - test(testCaseName, () async { - final testCase = - testCaseCollection.createCaseByName(testCaseName); - - render((Suspense()..fallback='Loading')((builder() - ..component = (isJsComponent ? Dom.div.elementType : Dom.div) - ..addTestId('test-js-component') - ..inputComponent = (isJsComponent ? BasicForwardRef.elementType : BasicForwardRef) - ..inputRef = testCase.ref)())); - await screen.findByTestId('test-js-component'); - expect(testCase.getCurrent(), isA()); - expect(testCase.getCurrent(), - hasAttribute(basicForwardRefAttribute), - reason: - 'test setup: ref should have been forwarded to the element we expect'); - }); - } + group('and is passed in via a custom "ref" prop (with conversion) as a', () { + for (final testCaseName in testCaseCollection.allTestCaseNames) { + test(testCaseName, () async { + final testCase = testCaseCollection.createCaseByName(testCaseName); + + render((Suspense()..fallback = 'Loading')((builder() + ..component = (isJsComponent ? Dom.div.elementType : Dom.div) + ..addTestId('test-js-component') + ..inputComponent = (isJsComponent ? BasicForwardRef.elementType : BasicForwardRef) + ..inputRef = testCase.ref)())); + await screen.findByTestId('test-js-component'); + expect(testCase.getCurrent(), isA()); + expect(testCase.getCurrent(), hasAttribute(basicForwardRefAttribute), + reason: 'test setup: ref should have been forwarded to the element we expect'); }); + } + }); - group( - 'and is passed in via a "ref" prop in a nested props Map (with conversion) as a', - () { - for (final testCaseName in testCaseCollection.allTestCaseNames) { - test(testCaseName, () async { - final testCase = - testCaseCollection.createCaseByName(testCaseName); - - render((Suspense()..fallback='Loading')((builder() - ..component = (isJsComponent ? Dom.div.elementType : Dom.div) - ..addTestId('test-js-component') - ..buttonComponent = (isJsComponent ? BasicForwardRef.elementType : BasicForwardRef) - ..buttonProps = (domProps()..ref = testCase.ref))())); - await screen.findByTestId('test-js-component'); - expect(testCase.getCurrent(), isA()); - expect(testCase.getCurrent(), - hasAttribute(basicForwardRefAttribute), - reason: - 'test setup: ref should have been forwarded to the element we expect'); - }); - } + group('and is passed in via a "ref" prop in a nested props Map (with conversion) as a', () { + for (final testCaseName in testCaseCollection.allTestCaseNames) { + test(testCaseName, () async { + final testCase = testCaseCollection.createCaseByName(testCaseName); + + render((Suspense()..fallback = 'Loading')((builder() + ..component = (isJsComponent ? Dom.div.elementType : Dom.div) + ..addTestId('test-js-component') + ..buttonComponent = (isJsComponent ? BasicForwardRef.elementType : BasicForwardRef) + ..buttonProps = (domProps()..ref = testCase.ref))())); + await screen.findByTestId('test-js-component'); + expect(testCase.getCurrent(), isA()); + expect(testCase.getCurrent(), hasAttribute(basicForwardRefAttribute), + reason: 'test setup: ref should have been forwarded to the element we expect'); }); - }); + } + }); + }); - group('which eventually ends up on a Dart class component', () { - final testCaseCollection = - RefTestCaseCollection(); + group('which eventually ends up on a Dart class component', () { + final testCaseCollection = RefTestCaseCollection(); - setUpAll(() { - // Make sure the special cases we're concerned about are included in this set of test cases: - void assertSingleMetaWhere(bool Function(RefTestCaseMeta) test) { - expect(testCaseCollection.allTestCaseMetas.where(test).toList(), - hasLength(1)); - } + setUpAll(() { + // Make sure the special cases we're concerned about are included in this set of test cases: + void assertSingleMetaWhere(bool Function(RefTestCaseMeta) test) { + expect(testCaseCollection.allTestCaseMetas.where(test).toList(), hasLength(1)); + } - assertSingleMetaWhere( - (m) => !m.isJs && m.kind.isCallback && m.isStronglyTyped); - assertSingleMetaWhere( - (m) => !m.isJs && m.kind.isCallback && !m.isStronglyTyped); - assertSingleMetaWhere( - (m) => !m.isJs && m.kind.isObject && m.isStronglyTyped); - assertSingleMetaWhere( - (m) => !m.isJs && m.kind.isObject && !m.isStronglyTyped); - }); + assertSingleMetaWhere((m) => !m.isJs && m.kind.isCallback && m.isStronglyTyped); + assertSingleMetaWhere((m) => !m.isJs && m.kind.isCallback && !m.isStronglyTyped); + assertSingleMetaWhere((m) => !m.isJs && m.kind.isObject && m.isStronglyTyped); + assertSingleMetaWhere((m) => !m.isJs && m.kind.isObject && !m.isStronglyTyped); + }); - void forEachTestCase( - void Function(String testCaseName, bool isDartCallbackCase, - bool isDartRefObjectCase, bool isTyped) callback) { - for (final testCaseName in testCaseCollection.allTestCaseNames) { - final meta = - testCaseCollection.testCaseMetaByName(testCaseName); - final isDartCallbackCase = !meta.isJs && meta.kind.isCallback; - final isDartRefObjectCase = !meta.isJs && meta.kind.isObject; - final isTyped = meta.isStronglyTyped; - - callback(testCaseName, isDartCallbackCase, isDartRefObjectCase, - isTyped); - } + void forEachTestCase( + void Function(String testCaseName, bool isDartCallbackCase, bool isDartRefObjectCase, bool isTyped) + callback) { + for (final testCaseName in testCaseCollection.allTestCaseNames) { + final meta = testCaseCollection.testCaseMetaByName(testCaseName); + final isDartCallbackCase = !meta.isJs && meta.kind.isCallback; + final isDartRefObjectCase = !meta.isJs && meta.kind.isObject; + final isTyped = meta.isStronglyTyped; + + callback(testCaseName, isDartCallbackCase, isDartRefObjectCase, isTyped); + } + } + + group('and is passed in via a forwarded "ref" prop as a', () { + forEachTestCase((testCaseName, isDartCallbackCase, isDartRefObjectCase, isTyped) { + if (isDartCallbackCase && isTyped) { + // Bail since we know typed Dart callback refs will fail with a runtime type error since the JS ReactComponent + // will be passed in instead of the Dart instance; this behavior will be tested in the untyped Dart callback ref test case. + return; } - group('and is passed in via a forwarded "ref" prop as a', () { - forEachTestCase((testCaseName, isDartCallbackCase, - isDartRefObjectCase, isTyped) { - if (isDartCallbackCase && isTyped) { - // Bail since we know typed Dart callback refs will fail with a runtime type error since the JS ReactComponent - // will be passed in instead of the Dart instance; this behavior will be tested in the untyped Dart callback ref test case. - return; - } - - test(testCaseName, () async { - final testCase = - testCaseCollection.createCaseByName(testCaseName); - -// GREG LOOK HERE - render((Suspense()..fallback='Loading')((builder() - ..addTestId('test-js-component') - ..component = (isJsComponent ? ClassComponent.elementType : ClassComponent) - ..ref = testCase.ref)())); - await screen.findByTestId('test-js-component'); - if (isDartRefObjectCase) { - expect( - testCase.getCurrent(), isA()); - } else { - expect( - testCase.getCurrent(), - isA().havingDartComponent( - isA())); - } - }, tags: [ - if (isDartCallbackCase) 'js-interop-tradeoff', - ]); - }); + test(testCaseName, () async { + final testCase = testCaseCollection.createCaseByName(testCaseName); + final isJsCallbackCase = testCaseName == RefTestCaseCollection.jsCallbackRefCaseName; + + render((Suspense()..fallback = 'Loading')((builder() + ..addTestId('test-js-component') + ..component = (isJsComponent ? ClassComponent.elementType : ClassComponent) + ..ref = testCase.ref)())); + await screen.findByTestId('test-js-component'); + // JS callbacks look the same to generateJsProps as Dart callback refs do, + // so they get the Dart component as well. // TODO investigate, decide how concerned we should be about this + if (isDartRefObjectCase || isDartCallbackCase || isJsCallbackCase) { + expect(testCase.getCurrent(), isA()); + } else { + // When a JS ref object is used. + expect( + testCase.getCurrent(), isA().havingDartComponent(isA())); + } }); + }); + }); - group( - 'and is passed in via a custom "ref" prop (with conversion) as a', - () { - forEachTestCase((testCaseName, isDartCallbackCase, - isDartRefObjectCase, isTyped) { - if (isDartCallbackCase && isTyped) { - // Bail since we know typed Dart callback refs will fail with a runtime type error since the JS ReactComponent - // will be passed in instead of the Dart instance; this behavior will be tested in the untyped Dart callback ref test case. - return; - } - - test(testCaseName, () async { - final testCase = - testCaseCollection.createCaseByName(testCaseName); - - render((Suspense()..fallback='Loading')((builder() - ..component = (isJsComponent ? Dom.div.elementType : Dom.div) - ..addTestId('test-js-component') - ..inputComponent = (isJsComponent ? ClassComponent.elementType : ClassComponent) - ..inputRef = testCase.ref)())); - await screen.findByTestId('test-js-component'); - if (isDartRefObjectCase) { - expect( - testCase.getCurrent(), isA()); - } else { - expect( - testCase.getCurrent(), - isA().havingDartComponent( - isA())); - } - }, tags: [ - if (isDartCallbackCase) 'js-interop-tradeoff', - ]); - }); - }); + group('and is passed in via a custom "ref" prop (with conversion) as a', () { - // This is the one case where Dart (and unfortunately, JS) callback refs actually get the class component - group( - 'and is passed in via a "ref" prop in a nested props Map (with conversion) as a', - () { - forEachTestCase((testCaseName, isDartCallbackCase, - isDartRefObjectCase, isTyped) { - final isJsCallbackCase = - testCaseName == RefTestCaseCollection.jsCallbackRefCaseName; - - test(testCaseName, () async { - final testCase = - testCaseCollection.createCaseByName(testCaseName); - - render((Suspense()..fallback='Loading')((builder() - ..component = (isJsComponent ? Dom.div.elementType : Dom.div) - ..addTestId('test-js-component') - ..buttonComponent = (isJsComponent ? ClassComponent.elementType : ClassComponent) - ..buttonProps = (domProps()..ref = testCase.ref))())); - await screen.findByTestId('test-js-component'); - // JS callbacks look the same to generateJsProps as Dart callback refs do, - // so they get the Dart component as well. // TODO investigate, decide how concerned we should be about this - if (isDartRefObjectCase || - isDartCallbackCase || - isJsCallbackCase) { - expect( - testCase.getCurrent(), isA()); - } else { - expect( - testCase.getCurrent(), - isA().havingDartComponent( - isA())); - } - }, tags: [ - if (isJsCallbackCase) 'js-interop-tradeoff', - ]); - }); + forEachTestCase((testCaseName, isDartCallbackCase, isDartRefObjectCase, isTyped) { + if (isDartCallbackCase && isTyped) { + // Bail since we know typed Dart callback refs will fail with a runtime type error since the JS ReactComponent + // will be passed in instead of the Dart instance; this behavior will be tested in the untyped Dart callback ref test case. + return; + } + + test(testCaseName, () async { + final testCase = testCaseCollection.createCaseByName(testCaseName); + final isJsCallbackCase = testCaseName == RefTestCaseCollection.jsCallbackRefCaseName; + + render((Suspense()..fallback = 'Loading')((builder() + ..component = (isJsComponent ? Dom.div.elementType : Dom.div) + ..addTestId('test-js-component') + ..inputComponent = (isJsComponent ? ClassComponent.elementType : ClassComponent) + ..inputRef = testCase.ref)())); + await screen.findByTestId('test-js-component'); + if ((isDartRefObjectCase || (isDartCallbackCase || isJsCallbackCase) && !isJsComponent)) { + expect(testCase.getCurrent(), isA()); + } else { + // JS wrapped component or JS ref type + expect( + testCase.getCurrent(), isA().havingDartComponent(isA())); + } }); }); }); + + // This is the one case where Dart (and unfortunately, JS) callback refs actually get the class component + group('and is passed in via a "ref" prop in a nested props Map (with conversion) as a', () { + forEachTestCase((testCaseName, isDartCallbackCase, isDartRefObjectCase, isTyped) { + final isJsCallbackCase = testCaseName == RefTestCaseCollection.jsCallbackRefCaseName; + + test(testCaseName, () async { + final testCase = testCaseCollection.createCaseByName(testCaseName); + + render((Suspense()..fallback = 'Loading')((builder() + ..component = (isJsComponent ? Dom.div.elementType : Dom.div) + ..addTestId('test-js-component') + ..buttonComponent = (isJsComponent ? ClassComponent.elementType : ClassComponent) + ..buttonProps = (domProps()..ref = testCase.ref))())); + await screen.findByTestId('test-js-component'); + // JS callbacks look the same to generateJsProps as Dart callback refs do, + // so they get the Dart component as well. // TODO investigate, decide how concerned we should be about this + if (isDartRefObjectCase || isDartCallbackCase || isJsCallbackCase) { + expect(testCase.getCurrent(), isA()); + } else { + expect( + testCase.getCurrent(), isA().havingDartComponent(isA())); + } + }, tags: [ + if (isJsCallbackCase) 'js-interop-tradeoff', + ]); + }); + }); + }); + }); }); } -UiFactory LazyTestJs = lazy(() async => TestJs, -_$TestJsConfig, // ignore: undefined_identifier +UiFactory LazyExpectsDartMapProp = + lazy(() async => ExpectsDartMapProp, _$ExpectsDartMapPropConfig); + +UiFactory LazyTestJs = lazy( + () async => TestJs, + _$TestJsConfig, // ignore: undefined_identifier //useJsFactoryProxy: true, ); -UiFactory LazyTestDart = lazy(() async => TestDart, -_$TestDartConfig, // ignore: undefined_identifier +UiFactory LazyTestDart = lazy( + () async => TestDart, + _$TestDartConfig, // ignore: undefined_identifier ); // // Matcher convenience functions // -Matcher sameOrSameAllowInterop(Function f) => - anyOf(same(allowInterop(f)), same(f)); +Matcher sameOrSameAllowInterop(Function f) => anyOf(same(allowInterop(f)), same(f)); extension on TypeMatcher { - Matcher havingJsRef(dynamic matcher) => - having((ref) => ref.jsRef, 'jsRef', matcher); + Matcher havingJsRef(dynamic matcher) => having((ref) => ref.jsRef, 'jsRef', matcher); } extension on TypeMatcher { - Matcher havingJsThis(dynamic matcher) => - having((ref) => ref.jsThis, 'jsThis', matcher); + Matcher havingJsThis(dynamic matcher) => having((ref) => ref.jsThis, 'jsThis', matcher); } extension on TypeMatcher { - Matcher havingToStringValue(dynamic matcher) => - having((o) => o.toString(), 'toString() value', matcher); + Matcher havingToStringValue(dynamic matcher) => having((o) => o.toString(), 'toString() value', matcher); } extension on TypeMatcher { - Matcher havingDartComponent(dynamic matcher) => - having((ref) => ref.dartComponent, 'dartComponent', matcher); + Matcher havingDartComponent(dynamic matcher) => having((ref) => ref.dartComponent, 'dartComponent', matcher); } // @@ -430,8 +457,7 @@ UiFactory ExpectsListChildrenProp = uiForwardRef( // Other test components // -UiFactory ClassComponent = - castUiFactory(_$ClassComponent); // ignore: undefined_identifier +UiFactory ClassComponent = castUiFactory(_$ClassComponent); // ignore: undefined_identifier mixin ClassComponentProps on UiProps {} @@ -459,8 +485,7 @@ mixin DartTestJsWrapperPropsMixin on UiProps { void Function(DartTestJsWrapperProps props)? onRender; } -class DartTestJsWrapperProps = UiProps - with TestJsProps, DartTestJsWrapperPropsMixin; +class DartTestJsWrapperProps = UiProps with TestJsProps, DartTestJsWrapperPropsMixin; UiFactory DartTestJsWrapper = uiForwardRef( (props, ref) { @@ -525,24 +550,91 @@ class TestDartProps = UiProps with TestJsProps; UiFactory TestDart = uiForwardRef( (props, ref) { - - final buttonProps = props.buttonProps ?? const {}; - final listOfProps = props.listOfProps ?? const [{}]; - final inputRef = props.inputRef; - final buttonComponent = props.buttonComponent ?? Dom.button; - final inputComponent = props.inputComponent ?? Dom.input; - final component = props.component ?? Dom.span; - final children = props.children; - final messageContext = defaultMessageContext; + final buttonProps = props.buttonProps ?? const {}; + final listOfProps = props.listOfProps ?? const [{}]; + final inputRef = props.inputRef; + final buttonComponent = props.buttonComponent ?? Dom.button; + final inputComponent = props.inputComponent ?? Dom.input; + final component = props.component ?? Dom.span; + final children = props.children; + final messageContext = defaultMessageContext; final message = useContext(messageContext); return Dom.div()( (buttonComponent()..addProps(buttonProps))(), (Dom.li()..addProps(listOfProps[0] ?? {}))(), - (inputComponent()..addProps(Dom.input()..type = 'text')..ref = inputRef)(), - (component()..addProps(props.getPropsToForward(exclude: {TestJsProps}))..ref = ref)(children), + (inputComponent() + ..addProps(domProps()..type = 'text') + ..ref = inputRef)(), + (component() + ..addProps(props.getPropsToForward(exclude: {TestJsProps})) + ..ref = ref)(children), (Dom.div()..role = 'alert')(message), ); }, _$TestDartConfig, // ignore: undefined_identifier ); + +@Props(keyNamespace: '') +mixin TypeTesterProps on UiProps { + late Map mapProp; + late Function functionProp; +} + +@Props(keyNamespace: '') +mixin JsTypeTesterProps on UiProps { + @Accessor(key: 'mapProp') + JsMap? _$raw$mapProp; + + Map? get mapProp => unjsifyMapProp(_$raw$mapProp); + set mapProp(Map? value) => _$raw$mapProp = jsifyMapProp(value); + + late Function functionProp; +} + +final LazyJsTypeTester = lazy(() async => JsTypeTester, _$JsTypeTesterConfig); +final LazyUiForwardRefTypeTester = lazy(() async => UiForwardRefTypeTester, _$UiForwardRefTypeTesterConfig); + +@JS('Object.assign') +external Object _objectAssign(Object target, Object source); + +UiFactory JsTypeTester = uiJsComponent( + ReactJsComponentFactoryProxy(React.forwardRef(allowInterop((propsIn, ref) { + final props = _objectAssign(JsMap(), propsIn); + final Object mapProp = getProperty(props, 'mapProp'); + if (!isJavaScriptSimpleObject(mapProp)) { + throw ArgumentError('mapProp should be a simple object'); + } + if (getProperty(mapProp, 'fun') is! Function) { + throw ArgumentError('functionProp should be a function'); + } else { + getProperty(mapProp, 'fun')(); + } + setProperty(props, 'data-test-id', getProperty(props, 'functionProp')()); + setProperty(props, 'ref', ref); + return React.createElement('div', props, getProperty(props,'children')); + }))), + _$JsTypeTesterConfig, // ignore: undefined_identifier +); + +UiFactory UiForwardRefTypeTester = uiForwardRef( + (props, ref) { + verifyType(props.mapProp); + verifyType(props.functionProp); + if (props.mapProp['fun'] is! Function) { + throw ArgumentError('mapProp[\'fun\'] should be a function'); + } else { + props.mapProp['fun'](); + } + return (Dom.div() + ..addTestId((props.functionProp() as String)) + ..ref = ref + ..addProps(props.getPropsToForward(exclude: {TypeTesterProps})))(props.children); + }, + _$UiForwardRefTypeTesterConfig, // ignore: undefined_identifier +); + +final toTest = [ + [JsTypeTester, _$JsTypeTesterConfig], + [UiForwardRefTypeTester, _$UiForwardRefTypeTesterConfig], +]; diff --git a/test/over_react/component/lazy_test.over_react.g.dart b/test/over_react/component/lazy_test.over_react.g.dart index 081552584..bf4de71f6 100644 --- a/test/over_react/component/lazy_test.over_react.g.dart +++ b/test/over_react/component/lazy_test.over_react.g.dart @@ -456,35 +456,155 @@ const PropsMeta _$metaForTestJsProps = PropsMeta( keys: $TestJsProps.$propKeys, ); -final UiFactoryConfig<_$$TestJsProps> _$TestJsConfig = UiFactoryConfig( - propsFactory: PropsFactory( - map: (map) => _$$TestJsProps(map), - jsMap: (map) => _$$TestJsProps$JsMap(map), - ), - displayName: 'TestJs'); +@Deprecated('This API is for use only within generated code.' + ' Do not reference it in your code, as it may change at any time.' + ' EXCEPTION: this may be used in legacy boilerplate until' + ' it is transitioned to the new mixin-based boilerplate.') +mixin $TypeTesterProps on TypeTesterProps { + static const PropsMeta meta = _$metaForTypeTesterProps; + @override + Map get mapProp => (props[_$key__mapProp__TypeTesterProps] ?? null) as Map; + @override + set mapProp(Map value) => props[_$key__mapProp__TypeTesterProps] = value; + @override + Function get functionProp => + (props[_$key__functionProp__TypeTesterProps] ?? null) as Function; + @override + set functionProp(Function value) => + props[_$key__functionProp__TypeTesterProps] = value; + /* GENERATED CONSTANTS */ + static const PropDescriptor _$prop__mapProp__TypeTesterProps = PropDescriptor( + _$key__mapProp__TypeTesterProps, + isRequired: true, + isNullable: true, + isLate: true); + static const PropDescriptor _$prop__functionProp__TypeTesterProps = + PropDescriptor(_$key__functionProp__TypeTesterProps, + isRequired: true, isNullable: true, isLate: true); + static const String _$key__mapProp__TypeTesterProps = 'mapProp'; + static const String _$key__functionProp__TypeTesterProps = 'functionProp'; -@Deprecated(r'Use the private variable, _$TestJsConfig, instead ' + static const List $props = [ + _$prop__mapProp__TypeTesterProps, + _$prop__functionProp__TypeTesterProps + ]; + static const List $propKeys = [ + _$key__mapProp__TypeTesterProps, + _$key__functionProp__TypeTesterProps + ]; + + @override + @UiProps.$mustCallSuper + void validateRequiredProps() { + super.validateRequiredProps(); + if (!props.containsKey('mapProp') && + !requiredPropNamesToSkipValidation.contains('mapProp')) { + throw MissingRequiredPropsError('Required prop `mapProp` is missing.'); + } + + if (!props.containsKey('functionProp') && + !requiredPropNamesToSkipValidation.contains('functionProp')) { + throw MissingRequiredPropsError( + 'Required prop `functionProp` is missing.'); + } + } +} + +@Deprecated('This API is for use only within generated code.' + ' Do not reference it in your code, as it may change at any time.') +const PropsMeta _$metaForTypeTesterProps = PropsMeta( + fields: $TypeTesterProps.$props, + keys: $TypeTesterProps.$propKeys, +); + +@Deprecated('This API is for use only within generated code.' + ' Do not reference it in your code, as it may change at any time.' + ' EXCEPTION: this may be used in legacy boilerplate until' + ' it is transitioned to the new mixin-based boilerplate.') +mixin $JsTypeTesterProps on JsTypeTesterProps { + static const PropsMeta meta = _$metaForJsTypeTesterProps; + @override + @Accessor(key: 'mapProp') + JsMap? get _$raw$mapProp => + (props[_$key___$raw$mapProp__JsTypeTesterProps] ?? null) as JsMap?; + @override + @Accessor(key: 'mapProp') + set _$raw$mapProp(JsMap? value) => + props[_$key___$raw$mapProp__JsTypeTesterProps] = value; + @override + Function get functionProp => + (props[_$key__functionProp__JsTypeTesterProps] ?? null) as Function; + @override + set functionProp(Function value) => + props[_$key__functionProp__JsTypeTesterProps] = value; + /* GENERATED CONSTANTS */ + static const PropDescriptor _$prop___$raw$mapProp__JsTypeTesterProps = + PropDescriptor(_$key___$raw$mapProp__JsTypeTesterProps); + static const PropDescriptor _$prop__functionProp__JsTypeTesterProps = + PropDescriptor(_$key__functionProp__JsTypeTesterProps, + isRequired: true, isNullable: true, isLate: true); + static const String _$key___$raw$mapProp__JsTypeTesterProps = 'mapProp'; + static const String _$key__functionProp__JsTypeTesterProps = 'functionProp'; + + static const List $props = [ + _$prop___$raw$mapProp__JsTypeTesterProps, + _$prop__functionProp__JsTypeTesterProps + ]; + static const List $propKeys = [ + _$key___$raw$mapProp__JsTypeTesterProps, + _$key__functionProp__JsTypeTesterProps + ]; + + @override + @UiProps.$mustCallSuper + void validateRequiredProps() { + super.validateRequiredProps(); + if (!props.containsKey('functionProp') && + !requiredPropNamesToSkipValidation.contains('functionProp')) { + throw MissingRequiredPropsError( + 'Required prop `functionProp` is missing.'); + } + } +} + +@Deprecated('This API is for use only within generated code.' + ' Do not reference it in your code, as it may change at any time.') +const PropsMeta _$metaForJsTypeTesterProps = PropsMeta( + fields: $JsTypeTesterProps.$props, + keys: $JsTypeTesterProps.$propKeys, +); + +final UiFactoryConfig<_$$ExpectsDartMapPropProps> _$ExpectsDartMapPropConfig = + UiFactoryConfig( + propsFactory: PropsFactory( + map: (map) => _$$ExpectsDartMapPropProps(map), + jsMap: (map) => _$$ExpectsDartMapPropProps$JsMap(map), + ), + displayName: 'ExpectsDartMapProp'); + +@Deprecated(r'Use the private variable, _$ExpectsDartMapPropConfig, instead ' 'and update the `over_react` lower bound to version 4.1.0. ' 'For information on why this is deprecated, see https://github.com/Workiva/over_react/pull/650') -final UiFactoryConfig<_$$TestJsProps> $TestJsConfig = _$TestJsConfig; +final UiFactoryConfig<_$$ExpectsDartMapPropProps> $ExpectsDartMapPropConfig = + _$ExpectsDartMapPropConfig; // Concrete props implementation. // // Implements constructor and backing map, and links up to generated component factory. @Deprecated('This API is for use only within generated code.' ' Do not reference it in your code, as it may change at any time.') -abstract class _$$TestJsProps extends UiProps +abstract class _$$ExpectsDartMapPropProps extends UiProps with - TestJsProps, - // If this generated mixin is undefined, it's likely because TestJsProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of TestJsProps, and check that $TestJsProps is exported/imported properly. - $TestJsProps { - _$$TestJsProps._(); + ExpectsDartMapPropProps, + // If this generated mixin is undefined, it's likely because ExpectsDartMapPropProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of ExpectsDartMapPropProps, and check that $ExpectsDartMapPropProps is exported/imported properly. + $ExpectsDartMapPropProps { + _$$ExpectsDartMapPropProps._(); - factory _$$TestJsProps(Map? backingMap) { + factory _$$ExpectsDartMapPropProps(Map? backingMap) { if (backingMap == null || backingMap is JsBackedMap) { - return _$$TestJsProps$JsMap(backingMap as JsBackedMap?); + return _$$ExpectsDartMapPropProps$JsMap(backingMap as JsBackedMap?); } else { - return _$$TestJsProps$PlainMap(backingMap); + return _$$ExpectsDartMapPropProps$PlainMap(backingMap); } } @@ -498,26 +618,27 @@ abstract class _$$TestJsProps extends UiProps @override PropsMetaCollection get staticMeta => const PropsMetaCollection({ - // If this generated mixin is undefined, it's likely because TestJsProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of TestJsProps, and check that $TestJsProps is exported/imported properly. - TestJsProps: $TestJsProps.meta, + // If this generated mixin is undefined, it's likely because ExpectsDartMapPropProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of ExpectsDartMapPropProps, and check that $ExpectsDartMapPropProps is exported/imported properly. + ExpectsDartMapPropProps: $ExpectsDartMapPropProps.meta, }); @override String $getPropKey(void Function(Map m) accessMap) => - _$getPropKey$_$$TestJsProps(accessMap, (map) => _$$TestJsProps(map)); + _$getPropKey$_$$ExpectsDartMapPropProps( + accessMap, (map) => _$$ExpectsDartMapPropProps(map)); } /// An alias for [getPropKey] so it can be referenced within the props class impl /// without being shadowed by the `getPropKey` instance extension member. -const _$getPropKey$_$$TestJsProps = getPropKey; +const _$getPropKey$_$$ExpectsDartMapPropProps = getPropKey; // Concrete props implementation that can be backed by any [Map]. @Deprecated('This API is for use only within generated code.' ' Do not reference it in your code, as it may change at any time.') -class _$$TestJsProps$PlainMap extends _$$TestJsProps { +class _$$ExpectsDartMapPropProps$PlainMap extends _$$ExpectsDartMapPropProps { // This initializer of `_props` to an empty map, as well as the reassignment // of `_props` in the constructor body is necessary to work around a DDC bug: https://github.com/dart-lang/sdk/issues/36217 - _$$TestJsProps$PlainMap(Map? backingMap) + _$$ExpectsDartMapPropProps$PlainMap(Map? backingMap) : this._props = {}, super._() { this._props = backingMap ?? {}; @@ -533,10 +654,10 @@ class _$$TestJsProps$PlainMap extends _$$TestJsProps { // allowing dart2js to compile more optimal code for key-value pair reads/writes. @Deprecated('This API is for use only within generated code.' ' Do not reference it in your code, as it may change at any time.') -class _$$TestJsProps$JsMap extends _$$TestJsProps { +class _$$ExpectsDartMapPropProps$JsMap extends _$$ExpectsDartMapPropProps { // This initializer of `_props` to an empty map, as well as the reassignment // of `_props` in the constructor body is necessary to work around a DDC bug: https://github.com/dart-lang/sdk/issues/36217 - _$$TestJsProps$JsMap(JsBackedMap? backingMap) + _$$ExpectsDartMapPropProps$JsMap(JsBackedMap? backingMap) : this._props = JsBackedMap(), super._() { this._props = backingMap ?? JsBackedMap(); @@ -548,37 +669,35 @@ class _$$TestJsProps$JsMap extends _$$TestJsProps { JsBackedMap _props; } -final UiFactoryConfig<_$$ExpectsDartMapPropProps> _$ExpectsDartMapPropConfig = - UiFactoryConfig( - propsFactory: PropsFactory( - map: (map) => _$$ExpectsDartMapPropProps(map), - jsMap: (map) => _$$ExpectsDartMapPropProps$JsMap(map), - ), - displayName: 'ExpectsDartMapProp'); +final UiFactoryConfig<_$$TestJsProps> _$TestJsConfig = UiFactoryConfig( + propsFactory: PropsFactory( + map: (map) => _$$TestJsProps(map), + jsMap: (map) => _$$TestJsProps$JsMap(map), + ), + displayName: 'TestJs'); -@Deprecated(r'Use the private variable, _$ExpectsDartMapPropConfig, instead ' +@Deprecated(r'Use the private variable, _$TestJsConfig, instead ' 'and update the `over_react` lower bound to version 4.1.0. ' 'For information on why this is deprecated, see https://github.com/Workiva/over_react/pull/650') -final UiFactoryConfig<_$$ExpectsDartMapPropProps> $ExpectsDartMapPropConfig = - _$ExpectsDartMapPropConfig; +final UiFactoryConfig<_$$TestJsProps> $TestJsConfig = _$TestJsConfig; // Concrete props implementation. // // Implements constructor and backing map, and links up to generated component factory. @Deprecated('This API is for use only within generated code.' ' Do not reference it in your code, as it may change at any time.') -abstract class _$$ExpectsDartMapPropProps extends UiProps +abstract class _$$TestJsProps extends UiProps with - ExpectsDartMapPropProps, - // If this generated mixin is undefined, it's likely because ExpectsDartMapPropProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of ExpectsDartMapPropProps, and check that $ExpectsDartMapPropProps is exported/imported properly. - $ExpectsDartMapPropProps { - _$$ExpectsDartMapPropProps._(); + TestJsProps, + // If this generated mixin is undefined, it's likely because TestJsProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of TestJsProps, and check that $TestJsProps is exported/imported properly. + $TestJsProps { + _$$TestJsProps._(); - factory _$$ExpectsDartMapPropProps(Map? backingMap) { + factory _$$TestJsProps(Map? backingMap) { if (backingMap == null || backingMap is JsBackedMap) { - return _$$ExpectsDartMapPropProps$JsMap(backingMap as JsBackedMap?); + return _$$TestJsProps$JsMap(backingMap as JsBackedMap?); } else { - return _$$ExpectsDartMapPropProps$PlainMap(backingMap); + return _$$TestJsProps$PlainMap(backingMap); } } @@ -592,27 +711,26 @@ abstract class _$$ExpectsDartMapPropProps extends UiProps @override PropsMetaCollection get staticMeta => const PropsMetaCollection({ - // If this generated mixin is undefined, it's likely because ExpectsDartMapPropProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of ExpectsDartMapPropProps, and check that $ExpectsDartMapPropProps is exported/imported properly. - ExpectsDartMapPropProps: $ExpectsDartMapPropProps.meta, + // If this generated mixin is undefined, it's likely because TestJsProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of TestJsProps, and check that $TestJsProps is exported/imported properly. + TestJsProps: $TestJsProps.meta, }); @override String $getPropKey(void Function(Map m) accessMap) => - _$getPropKey$_$$ExpectsDartMapPropProps( - accessMap, (map) => _$$ExpectsDartMapPropProps(map)); + _$getPropKey$_$$TestJsProps(accessMap, (map) => _$$TestJsProps(map)); } /// An alias for [getPropKey] so it can be referenced within the props class impl /// without being shadowed by the `getPropKey` instance extension member. -const _$getPropKey$_$$ExpectsDartMapPropProps = getPropKey; +const _$getPropKey$_$$TestJsProps = getPropKey; // Concrete props implementation that can be backed by any [Map]. @Deprecated('This API is for use only within generated code.' ' Do not reference it in your code, as it may change at any time.') -class _$$ExpectsDartMapPropProps$PlainMap extends _$$ExpectsDartMapPropProps { +class _$$TestJsProps$PlainMap extends _$$TestJsProps { // This initializer of `_props` to an empty map, as well as the reassignment // of `_props` in the constructor body is necessary to work around a DDC bug: https://github.com/dart-lang/sdk/issues/36217 - _$$ExpectsDartMapPropProps$PlainMap(Map? backingMap) + _$$TestJsProps$PlainMap(Map? backingMap) : this._props = {}, super._() { this._props = backingMap ?? {}; @@ -628,10 +746,10 @@ class _$$ExpectsDartMapPropProps$PlainMap extends _$$ExpectsDartMapPropProps { // allowing dart2js to compile more optimal code for key-value pair reads/writes. @Deprecated('This API is for use only within generated code.' ' Do not reference it in your code, as it may change at any time.') -class _$$ExpectsDartMapPropProps$JsMap extends _$$ExpectsDartMapPropProps { +class _$$TestJsProps$JsMap extends _$$TestJsProps { // This initializer of `_props` to an empty map, as well as the reassignment // of `_props` in the constructor body is necessary to work around a DDC bug: https://github.com/dart-lang/sdk/issues/36217 - _$$ExpectsDartMapPropProps$JsMap(JsBackedMap? backingMap) + _$$TestJsProps$JsMap(JsBackedMap? backingMap) : this._props = JsBackedMap(), super._() { this._props = backingMap ?? JsBackedMap(); @@ -1127,3 +1245,194 @@ class _$$TestDartProps$JsMap extends _$$TestDartProps { JsBackedMap get props => _props; JsBackedMap _props; } + +final UiFactoryConfig<_$$JsTypeTesterProps> _$JsTypeTesterConfig = + UiFactoryConfig( + propsFactory: PropsFactory( + map: (map) => _$$JsTypeTesterProps(map), + jsMap: (map) => _$$JsTypeTesterProps$JsMap(map), + ), + displayName: 'JsTypeTester'); + +@Deprecated(r'Use the private variable, _$JsTypeTesterConfig, instead ' + 'and update the `over_react` lower bound to version 4.1.0. ' + 'For information on why this is deprecated, see https://github.com/Workiva/over_react/pull/650') +final UiFactoryConfig<_$$JsTypeTesterProps> $JsTypeTesterConfig = + _$JsTypeTesterConfig; + +// Concrete props implementation. +// +// Implements constructor and backing map, and links up to generated component factory. +@Deprecated('This API is for use only within generated code.' + ' Do not reference it in your code, as it may change at any time.') +abstract class _$$JsTypeTesterProps extends UiProps + with + JsTypeTesterProps, + // If this generated mixin is undefined, it's likely because JsTypeTesterProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of JsTypeTesterProps, and check that $JsTypeTesterProps is exported/imported properly. + $JsTypeTesterProps { + _$$JsTypeTesterProps._(); + + factory _$$JsTypeTesterProps(Map? backingMap) { + if (backingMap == null || backingMap is JsBackedMap) { + return _$$JsTypeTesterProps$JsMap(backingMap as JsBackedMap?); + } else { + return _$$JsTypeTesterProps$PlainMap(backingMap); + } + } + + /// Let `UiProps` internals know that this class has been generated. + @override + bool get $isClassGenerated => true; + + /// The default namespace for the prop getters/setters generated for this class. + @override + String get propKeyNamespace => ''; + + @override + PropsMetaCollection get staticMeta => const PropsMetaCollection({ + // If this generated mixin is undefined, it's likely because JsTypeTesterProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of JsTypeTesterProps, and check that $JsTypeTesterProps is exported/imported properly. + JsTypeTesterProps: $JsTypeTesterProps.meta, + }); + + @override + String $getPropKey(void Function(Map m) accessMap) => + _$getPropKey$_$$JsTypeTesterProps( + accessMap, (map) => _$$JsTypeTesterProps(map)); +} + +/// An alias for [getPropKey] so it can be referenced within the props class impl +/// without being shadowed by the `getPropKey` instance extension member. +const _$getPropKey$_$$JsTypeTesterProps = getPropKey; + +// Concrete props implementation that can be backed by any [Map]. +@Deprecated('This API is for use only within generated code.' + ' Do not reference it in your code, as it may change at any time.') +class _$$JsTypeTesterProps$PlainMap extends _$$JsTypeTesterProps { + // This initializer of `_props` to an empty map, as well as the reassignment + // of `_props` in the constructor body is necessary to work around a DDC bug: https://github.com/dart-lang/sdk/issues/36217 + _$$JsTypeTesterProps$PlainMap(Map? backingMap) + : this._props = {}, + super._() { + this._props = backingMap ?? {}; + } + + /// The backing props map proxied by this class. + @override + Map get props => _props; + Map _props; +} + +// Concrete props implementation that can only be backed by [JsMap], +// allowing dart2js to compile more optimal code for key-value pair reads/writes. +@Deprecated('This API is for use only within generated code.' + ' Do not reference it in your code, as it may change at any time.') +class _$$JsTypeTesterProps$JsMap extends _$$JsTypeTesterProps { + // This initializer of `_props` to an empty map, as well as the reassignment + // of `_props` in the constructor body is necessary to work around a DDC bug: https://github.com/dart-lang/sdk/issues/36217 + _$$JsTypeTesterProps$JsMap(JsBackedMap? backingMap) + : this._props = JsBackedMap(), + super._() { + this._props = backingMap ?? JsBackedMap(); + } + + /// The backing props map proxied by this class. + @override + JsBackedMap get props => _props; + JsBackedMap _props; +} + +final UiFactoryConfig<_$$TypeTesterProps> _$UiForwardRefTypeTesterConfig = + UiFactoryConfig( + propsFactory: PropsFactory( + map: (map) => _$$TypeTesterProps(map), + jsMap: (map) => _$$TypeTesterProps$JsMap(map), + ), + displayName: 'UiForwardRefTypeTester'); + +@Deprecated( + r'Use the private variable, _$UiForwardRefTypeTesterConfig, instead ' + 'and update the `over_react` lower bound to version 4.1.0. ' + 'For information on why this is deprecated, see https://github.com/Workiva/over_react/pull/650') +final UiFactoryConfig<_$$TypeTesterProps> $UiForwardRefTypeTesterConfig = + _$UiForwardRefTypeTesterConfig; + +// Concrete props implementation. +// +// Implements constructor and backing map, and links up to generated component factory. +@Deprecated('This API is for use only within generated code.' + ' Do not reference it in your code, as it may change at any time.') +abstract class _$$TypeTesterProps extends UiProps + with + TypeTesterProps, + // If this generated mixin is undefined, it's likely because TypeTesterProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of TypeTesterProps, and check that $TypeTesterProps is exported/imported properly. + $TypeTesterProps { + _$$TypeTesterProps._(); + + factory _$$TypeTesterProps(Map? backingMap) { + if (backingMap == null || backingMap is JsBackedMap) { + return _$$TypeTesterProps$JsMap(backingMap as JsBackedMap?); + } else { + return _$$TypeTesterProps$PlainMap(backingMap); + } + } + + /// Let `UiProps` internals know that this class has been generated. + @override + bool get $isClassGenerated => true; + + /// The default namespace for the prop getters/setters generated for this class. + @override + String get propKeyNamespace => ''; + + @override + PropsMetaCollection get staticMeta => const PropsMetaCollection({ + // If this generated mixin is undefined, it's likely because TypeTesterProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of TypeTesterProps, and check that $TypeTesterProps is exported/imported properly. + TypeTesterProps: $TypeTesterProps.meta, + }); + + @override + String $getPropKey(void Function(Map m) accessMap) => + _$getPropKey$_$$TypeTesterProps( + accessMap, (map) => _$$TypeTesterProps(map)); +} + +/// An alias for [getPropKey] so it can be referenced within the props class impl +/// without being shadowed by the `getPropKey` instance extension member. +const _$getPropKey$_$$TypeTesterProps = getPropKey; + +// Concrete props implementation that can be backed by any [Map]. +@Deprecated('This API is for use only within generated code.' + ' Do not reference it in your code, as it may change at any time.') +class _$$TypeTesterProps$PlainMap extends _$$TypeTesterProps { + // This initializer of `_props` to an empty map, as well as the reassignment + // of `_props` in the constructor body is necessary to work around a DDC bug: https://github.com/dart-lang/sdk/issues/36217 + _$$TypeTesterProps$PlainMap(Map? backingMap) + : this._props = {}, + super._() { + this._props = backingMap ?? {}; + } + + /// The backing props map proxied by this class. + @override + Map get props => _props; + Map _props; +} + +// Concrete props implementation that can only be backed by [JsMap], +// allowing dart2js to compile more optimal code for key-value pair reads/writes. +@Deprecated('This API is for use only within generated code.' + ' Do not reference it in your code, as it may change at any time.') +class _$$TypeTesterProps$JsMap extends _$$TypeTesterProps { + // This initializer of `_props` to an empty map, as well as the reassignment + // of `_props` in the constructor body is necessary to work around a DDC bug: https://github.com/dart-lang/sdk/issues/36217 + _$$TypeTesterProps$JsMap(JsBackedMap? backingMap) + : this._props = JsBackedMap(), + super._() { + this._props = backingMap ?? JsBackedMap(); + } + + /// The backing props map proxied by this class. + @override + JsBackedMap get props => _props; + JsBackedMap _props; +} From 6e112ff79f5e786579dd1eca046081d79f343a56 Mon Sep 17 00:00:00 2001 From: Keal Jones Date: Mon, 16 Sep 2024 12:28:05 -0700 Subject: [PATCH 07/31] remove unused code and imports --- test/over_react/component/lazy_test.dart | 9 --------- 1 file changed, 9 deletions(-) diff --git a/test/over_react/component/lazy_test.dart b/test/over_react/component/lazy_test.dart index b08c8d08c..f76343ce4 100644 --- a/test/over_react/component/lazy_test.dart +++ b/test/over_react/component/lazy_test.dart @@ -3,7 +3,6 @@ library rmui.test.unit.component.lazy_test; import 'dart:convert'; -import 'dart:developer'; import 'dart:html'; import 'dart:js_util'; @@ -349,14 +348,6 @@ UiFactory LazyTestDart = lazy( Matcher sameOrSameAllowInterop(Function f) => anyOf(same(allowInterop(f)), same(f)); -extension on TypeMatcher { - Matcher havingJsRef(dynamic matcher) => having((ref) => ref.jsRef, 'jsRef', matcher); -} - -extension on TypeMatcher { - Matcher havingJsThis(dynamic matcher) => having((ref) => ref.jsThis, 'jsThis', matcher); -} - extension on TypeMatcher { Matcher havingToStringValue(dynamic matcher) => having((o) => o.toString(), 'toString() value', matcher); } From 41d389e19b6eb54473f3fabfc1336587e3e8d08a Mon Sep 17 00:00:00 2001 From: Keal Jones Date: Mon, 16 Sep 2024 14:19:23 -0700 Subject: [PATCH 08/31] add a class component map props test --- test/over_react/component/lazy_test.dart | 134 +++++- .../component/lazy_test.over_react.g.dart | 402 +++++++++++++++++- 2 files changed, 515 insertions(+), 21 deletions(-) diff --git a/test/over_react/component/lazy_test.dart b/test/over_react/component/lazy_test.dart index f76343ce4..42c486d20 100644 --- a/test/over_react/component/lazy_test.dart +++ b/test/over_react/component/lazy_test.dart @@ -64,7 +64,44 @@ main() { }); sharedNestedPropConversionTests(LazyTestJs, isJsComponent: true); }); - group('Dart component', () { + + group('Dart Class component', () { + test('has correct map prop conversion', () async { + // The map should be a Dart Map, not a JS object + final ref = createRef(); + final renderErrors = []; + bool mapPropFunctionCalled = false; + bool functionPropCalled = false; + + render((components.ErrorBoundary() + ..onComponentDidCatch = ((error, _) => renderErrors.add(error)) + ..shouldLogErrors = false + ..fallbackUIRenderer = ((_, __) => Dom.span()('An error occurred during render')))((Suspense() + ..fallback = (Dom.div()..id = 'suspense')( + 'I am a fallback UI that will show while we load the lazy component! The load time is artificially inflated to last an additional 5 seconds just to prove it\'s working!', + ))( + (LazyClassComponentTypeTester() + ..ref = ref + ..mapProp = { + 'test': 'foo', + 'fun': () { + mapPropFunctionCalled = true; + } + } + ..functionProp = (() { + functionPropCalled = true; + return 'test'; + }))('Class'), + ))); + // Wait for the "lazy" component to display + await screen.findByText('Class'); + expect(ref.current, isA()); + expect(mapPropFunctionCalled, isTrue); + expect(functionPropCalled, isTrue); + expect(screen.getByTestId('test'), isInTheDocument); + }); + }); + group('Dart function component', () { test('has correct map prop conversion', () async { // The map should be a Dart Map, not a JS object final ref = createRef(); @@ -160,12 +197,12 @@ sharedNestedPropConversionTests(UiFactory builder, {bool isJsCompon final testCase = testCaseCollection.createCaseByName(testCaseName); render((Suspense()..fallback = 'Loading')((builder() - ..addTestId('test-js-component') + ..addTestId('test-component') ..component = (isJsComponent ? BasicForwardRef.elementType : BasicForwardRef) ..ref = testCase.ref)())); - await screen.findByTestId('test-js-component'); - expect(testCase.getCurrent(), isA()); - expect(testCase.getCurrent(), hasAttribute(basicForwardRefAttribute), + await screen.findByTestId('test-component'); + expect(testCase.getCurrent(), isA()); + expect(testCase.getCurrent(), hasAttribute(basicForwardRefAttribute), reason: 'test setup: ref should have been forwarded to the element we expect'); }); } @@ -178,10 +215,10 @@ sharedNestedPropConversionTests(UiFactory builder, {bool isJsCompon render((Suspense()..fallback = 'Loading')((builder() ..component = (isJsComponent ? Dom.div.elementType : Dom.div) - ..addTestId('test-js-component') + ..addTestId('test-component') ..inputComponent = (isJsComponent ? BasicForwardRef.elementType : BasicForwardRef) ..inputRef = testCase.ref)())); - await screen.findByTestId('test-js-component'); + await screen.findByTestId('test-component'); expect(testCase.getCurrent(), isA()); expect(testCase.getCurrent(), hasAttribute(basicForwardRefAttribute), reason: 'test setup: ref should have been forwarded to the element we expect'); @@ -196,10 +233,10 @@ sharedNestedPropConversionTests(UiFactory builder, {bool isJsCompon render((Suspense()..fallback = 'Loading')((builder() ..component = (isJsComponent ? Dom.div.elementType : Dom.div) - ..addTestId('test-js-component') + ..addTestId('test-component') ..buttonComponent = (isJsComponent ? BasicForwardRef.elementType : BasicForwardRef) ..buttonProps = (domProps()..ref = testCase.ref))())); - await screen.findByTestId('test-js-component'); + await screen.findByTestId('test-component'); expect(testCase.getCurrent(), isA()); expect(testCase.getCurrent(), hasAttribute(basicForwardRefAttribute), reason: 'test setup: ref should have been forwarded to the element we expect'); @@ -249,10 +286,10 @@ sharedNestedPropConversionTests(UiFactory builder, {bool isJsCompon final isJsCallbackCase = testCaseName == RefTestCaseCollection.jsCallbackRefCaseName; render((Suspense()..fallback = 'Loading')((builder() - ..addTestId('test-js-component') + ..addTestId('test-component') ..component = (isJsComponent ? ClassComponent.elementType : ClassComponent) ..ref = testCase.ref)())); - await screen.findByTestId('test-js-component'); + await screen.findByTestId('test-component'); // JS callbacks look the same to generateJsProps as Dart callback refs do, // so they get the Dart component as well. // TODO investigate, decide how concerned we should be about this if (isDartRefObjectCase || isDartCallbackCase || isJsCallbackCase) { @@ -281,10 +318,10 @@ sharedNestedPropConversionTests(UiFactory builder, {bool isJsCompon render((Suspense()..fallback = 'Loading')((builder() ..component = (isJsComponent ? Dom.div.elementType : Dom.div) - ..addTestId('test-js-component') + ..addTestId('test-component') ..inputComponent = (isJsComponent ? ClassComponent.elementType : ClassComponent) ..inputRef = testCase.ref)())); - await screen.findByTestId('test-js-component'); + await screen.findByTestId('test-component'); if ((isDartRefObjectCase || (isDartCallbackCase || isJsCallbackCase) && !isJsComponent)) { expect(testCase.getCurrent(), isA()); } else { @@ -306,10 +343,10 @@ sharedNestedPropConversionTests(UiFactory builder, {bool isJsCompon render((Suspense()..fallback = 'Loading')((builder() ..component = (isJsComponent ? Dom.div.elementType : Dom.div) - ..addTestId('test-js-component') + ..addTestId('test-component') ..buttonComponent = (isJsComponent ? ClassComponent.elementType : ClassComponent) ..buttonProps = (domProps()..ref = testCase.ref))())); - await screen.findByTestId('test-js-component'); + await screen.findByTestId('test-component'); // JS callbacks look the same to generateJsProps as Dart callback refs do, // so they get the Dart component as well. // TODO investigate, decide how concerned we should be about this if (isDartRefObjectCase || isDartCallbackCase || isJsCallbackCase) { @@ -328,6 +365,10 @@ sharedNestedPropConversionTests(UiFactory builder, {bool isJsCompon }); } +UiFactory LazyTestDartClass = lazy(() async => TestDartClass, UiFactoryConfig(propsFactory: PropsFactory.fromUiFactory(TestDartClass))); + +UiFactory LazyClassComponentTypeTester = lazy(() async => ClassComponentTypeTester, UiFactoryConfig(propsFactory: PropsFactory.fromUiFactory(ClassComponentTypeTester))); + UiFactory LazyExpectsDartMapProp = lazy(() async => ExpectsDartMapProp, _$ExpectsDartMapPropConfig); @@ -523,6 +564,8 @@ mixin TestJsProps on UiProps { dynamic /*ElementType*/ component; dynamic /*ElementType*/ inputComponent; dynamic /*ElementType*/ buttonComponent; + + dynamic forwardRef; } UiFactory TestJs = uiJsComponent( @@ -625,7 +668,60 @@ UiFactory UiForwardRefTypeTester = uiForwardRef( _$UiForwardRefTypeTesterConfig, // ignore: undefined_identifier ); -final toTest = [ - [JsTypeTester, _$JsTypeTesterConfig], - [UiForwardRefTypeTester, _$UiForwardRefTypeTesterConfig], -]; + +UiFactory ClassComponentTypeTester = castUiFactory(_$ClassComponentTypeTester); // ignore: undefined_identifier + +@Props(keyNamespace: '') +mixin ClassComponentTypeTesterProps on UiProps { + late Map mapProp; + late Function functionProp; +} + +class ClassComponentTypeTesterComponent extends UiComponent2 { + @override + render() { + verifyType(props.mapProp); + verifyType(props.functionProp); + if (props.mapProp['fun'] is! Function) { + throw ArgumentError('mapProp[\'fun\'] should be a function'); + } else { + props.mapProp['fun'](); + } + return (Dom.div() + ..addTestId((props.functionProp() as String)) + ..addProps(props.getPropsToForward(exclude: {ClassComponentTypeTesterProps})))(props.children); + } +} + +UiFactory TestDartClass = castUiFactory(_$TestDartClass); // ignore: undefined_identifier + +@Props(keyNamespace: '') +class TestDartClassProps = UiProps with TestJsProps; + +class TestDartClassComponent extends UiComponent2 { + @override + get contextType => defaultMessageContext.reactDartContext; + + @override + render() { + final buttonProps = props.buttonProps ?? const {}; + final listOfProps = props.listOfProps ?? const [{}]; + final inputRef = props.inputRef; + final buttonComponent = props.buttonComponent ?? Dom.button; + final inputComponent = props.inputComponent ?? Dom.input; + final component = props.component ?? Dom.span; + final children = props.children; + + return Dom.div()( + (buttonComponent()..addProps(buttonProps))(), + (Dom.li()..addProps(listOfProps[0] ?? {}))(), + (inputComponent() + ..addProps(domProps()..type = 'text') + ..ref = inputRef)(), + (component() + ..addProps(props.getPropsToForward(exclude: {TestJsProps})) + ..ref = props.forwardRef)(children), + (Dom.div()..role = 'alert')(context), + ); + } +} diff --git a/test/over_react/component/lazy_test.over_react.g.dart b/test/over_react/component/lazy_test.over_react.g.dart index bf4de71f6..258d8d558 100644 --- a/test/over_react/component/lazy_test.over_react.g.dart +++ b/test/over_react/component/lazy_test.over_react.g.dart @@ -7,6 +7,327 @@ part of 'lazy_test.dart'; // OverReactBuilder (package:over_react/src/builder.dart) // ************************************************************************** +// React component factory implementation. +// +// Registers component implementation and links type meta to builder factory. +@Deprecated('This API is for use only within generated code.' + ' Do not reference it in your code, as it may change at any time.') +final $TestDartClassComponentFactory = registerComponent2( + () => _$TestDartClassComponent(), + builderFactory: _$TestDartClass, + componentClass: TestDartClassComponent, + isWrapper: false, + parentType: null, +); + +_$$TestDartClassProps _$TestDartClass([Map? backingProps]) => + backingProps == null + ? _$$TestDartClassProps$JsMap(JsBackedMap()) + : _$$TestDartClassProps(backingProps); + +// Concrete props implementation. +// +// Implements constructor and backing map, and links up to generated component factory. +@Deprecated('This API is for use only within generated code.' + ' Do not reference it in your code, as it may change at any time.') +abstract class _$$TestDartClassProps extends UiProps + with + TestJsProps, + // If this generated mixin is undefined, it's likely because TestJsProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of TestJsProps, and check that $TestJsProps is exported/imported properly. + $TestJsProps + implements + TestDartClassProps { + _$$TestDartClassProps._(); + + factory _$$TestDartClassProps(Map? backingMap) { + if (backingMap == null || backingMap is JsBackedMap) { + return _$$TestDartClassProps$JsMap(backingMap as JsBackedMap?); + } else { + return _$$TestDartClassProps$PlainMap(backingMap); + } + } + + /// Let `UiProps` internals know that this class has been generated. + @override + bool get $isClassGenerated => true; + + /// The `ReactComponentFactory` associated with the component built by this class. + @override + ReactComponentFactoryProxy get componentFactory => + super.componentFactory ?? $TestDartClassComponentFactory; + + /// The default namespace for the prop getters/setters generated for this class. + @override + String get propKeyNamespace => ''; + + @override + PropsMetaCollection get staticMeta => const PropsMetaCollection({ + // If this generated mixin is undefined, it's likely because TestJsProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of TestJsProps, and check that $TestJsProps is exported/imported properly. + TestJsProps: $TestJsProps.meta, + }); + + @override + String $getPropKey(void Function(Map m) accessMap) => + _$getPropKey$_$$TestDartClassProps( + accessMap, (map) => _$$TestDartClassProps(map)); +} + +/// An alias for [getPropKey] so it can be referenced within the props class impl +/// without being shadowed by the `getPropKey` instance extension member. +const _$getPropKey$_$$TestDartClassProps = getPropKey; + +// Concrete props implementation that can be backed by any [Map]. +@Deprecated('This API is for use only within generated code.' + ' Do not reference it in your code, as it may change at any time.') +class _$$TestDartClassProps$PlainMap extends _$$TestDartClassProps { + // This initializer of `_props` to an empty map, as well as the reassignment + // of `_props` in the constructor body is necessary to work around a DDC bug: https://github.com/dart-lang/sdk/issues/36217 + _$$TestDartClassProps$PlainMap(Map? backingMap) + : this._props = {}, + super._() { + this._props = backingMap ?? {}; + } + + /// The backing props map proxied by this class. + @override + Map get props => _props; + Map _props; +} + +// Concrete props implementation that can only be backed by [JsMap], +// allowing dart2js to compile more optimal code for key-value pair reads/writes. +@Deprecated('This API is for use only within generated code.' + ' Do not reference it in your code, as it may change at any time.') +class _$$TestDartClassProps$JsMap extends _$$TestDartClassProps { + // This initializer of `_props` to an empty map, as well as the reassignment + // of `_props` in the constructor body is necessary to work around a DDC bug: https://github.com/dart-lang/sdk/issues/36217 + _$$TestDartClassProps$JsMap(JsBackedMap? backingMap) + : this._props = JsBackedMap(), + super._() { + this._props = backingMap ?? JsBackedMap(); + } + + /// The backing props map proxied by this class. + @override + JsBackedMap get props => _props; + JsBackedMap _props; +} + +// Concrete component implementation mixin. +// +// Implements typed props/state factories, defaults `consumedPropKeys` to the keys +// generated for the associated props class. +@Deprecated('This API is for use only within generated code.' + ' Do not reference it in your code, as it may change at any time.') +class _$TestDartClassComponent extends TestDartClassComponent { + late _$$TestDartClassProps$JsMap _cachedTypedProps; + + @override + _$$TestDartClassProps$JsMap get props => _cachedTypedProps; + + @override + set props(Map value) { + assert( + getBackingMap(value) is JsBackedMap, + 'Component2.props should never be set directly in ' + 'production. If this is required for testing, the ' + 'component should be rendered within the test. If ' + 'that does not have the necessary result, the last ' + 'resort is to use typedPropsFactoryJs.'); + super.props = value; + _cachedTypedProps = + typedPropsFactoryJs(getBackingMap(value) as JsBackedMap); + } + + @override + _$$TestDartClassProps$JsMap typedPropsFactoryJs(JsBackedMap? backingMap) => + _$$TestDartClassProps$JsMap(backingMap); + + @override + _$$TestDartClassProps typedPropsFactory(Map? backingMap) => + _$$TestDartClassProps(backingMap); + + /// Let `UiComponent` internals know that this class has been generated. + @override + bool get $isClassGenerated => true; + + @override + String get displayName => 'TestDartClass'; + + /// The default consumed props, comprising all props mixins used by TestDartClassProps. + /// Used in `*ConsumedProps` methods if [consumedProps] is not overridden. + @override + get $defaultConsumedProps => propsMeta.all; + + @override + PropsMetaCollection get propsMeta => const PropsMetaCollection({ + // If this generated mixin is undefined, it's likely because TestJsProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of TestJsProps, and check that $TestJsProps is exported/imported properly. + TestJsProps: $TestJsProps.meta, + }); +} + +// React component factory implementation. +// +// Registers component implementation and links type meta to builder factory. +@Deprecated('This API is for use only within generated code.' + ' Do not reference it in your code, as it may change at any time.') +final $ClassComponentTypeTesterComponentFactory = registerComponent2( + () => _$ClassComponentTypeTesterComponent(), + builderFactory: _$ClassComponentTypeTester, + componentClass: ClassComponentTypeTesterComponent, + isWrapper: false, + parentType: null, +); + +_$$ClassComponentTypeTesterProps _$ClassComponentTypeTester( + [Map? backingProps]) => + backingProps == null + ? _$$ClassComponentTypeTesterProps$JsMap(JsBackedMap()) + : _$$ClassComponentTypeTesterProps(backingProps); + +// Concrete props implementation. +// +// Implements constructor and backing map, and links up to generated component factory. +@Deprecated('This API is for use only within generated code.' + ' Do not reference it in your code, as it may change at any time.') +abstract class _$$ClassComponentTypeTesterProps extends UiProps + with + ClassComponentTypeTesterProps, + // If this generated mixin is undefined, it's likely because ClassComponentTypeTesterProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of ClassComponentTypeTesterProps, and check that $ClassComponentTypeTesterProps is exported/imported properly. + $ClassComponentTypeTesterProps { + _$$ClassComponentTypeTesterProps._(); + + factory _$$ClassComponentTypeTesterProps(Map? backingMap) { + if (backingMap == null || backingMap is JsBackedMap) { + return _$$ClassComponentTypeTesterProps$JsMap(backingMap as JsBackedMap?); + } else { + return _$$ClassComponentTypeTesterProps$PlainMap(backingMap); + } + } + + /// Let `UiProps` internals know that this class has been generated. + @override + bool get $isClassGenerated => true; + + /// The `ReactComponentFactory` associated with the component built by this class. + @override + ReactComponentFactoryProxy get componentFactory => + super.componentFactory ?? $ClassComponentTypeTesterComponentFactory; + + /// The default namespace for the prop getters/setters generated for this class. + @override + String get propKeyNamespace => ''; + + @override + PropsMetaCollection get staticMeta => const PropsMetaCollection({ + // If this generated mixin is undefined, it's likely because ClassComponentTypeTesterProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of ClassComponentTypeTesterProps, and check that $ClassComponentTypeTesterProps is exported/imported properly. + ClassComponentTypeTesterProps: $ClassComponentTypeTesterProps.meta, + }); + + @override + String $getPropKey(void Function(Map m) accessMap) => + _$getPropKey$_$$ClassComponentTypeTesterProps( + accessMap, (map) => _$$ClassComponentTypeTesterProps(map)); +} + +/// An alias for [getPropKey] so it can be referenced within the props class impl +/// without being shadowed by the `getPropKey` instance extension member. +const _$getPropKey$_$$ClassComponentTypeTesterProps = getPropKey; + +// Concrete props implementation that can be backed by any [Map]. +@Deprecated('This API is for use only within generated code.' + ' Do not reference it in your code, as it may change at any time.') +class _$$ClassComponentTypeTesterProps$PlainMap + extends _$$ClassComponentTypeTesterProps { + // This initializer of `_props` to an empty map, as well as the reassignment + // of `_props` in the constructor body is necessary to work around a DDC bug: https://github.com/dart-lang/sdk/issues/36217 + _$$ClassComponentTypeTesterProps$PlainMap(Map? backingMap) + : this._props = {}, + super._() { + this._props = backingMap ?? {}; + } + + /// The backing props map proxied by this class. + @override + Map get props => _props; + Map _props; +} + +// Concrete props implementation that can only be backed by [JsMap], +// allowing dart2js to compile more optimal code for key-value pair reads/writes. +@Deprecated('This API is for use only within generated code.' + ' Do not reference it in your code, as it may change at any time.') +class _$$ClassComponentTypeTesterProps$JsMap + extends _$$ClassComponentTypeTesterProps { + // This initializer of `_props` to an empty map, as well as the reassignment + // of `_props` in the constructor body is necessary to work around a DDC bug: https://github.com/dart-lang/sdk/issues/36217 + _$$ClassComponentTypeTesterProps$JsMap(JsBackedMap? backingMap) + : this._props = JsBackedMap(), + super._() { + this._props = backingMap ?? JsBackedMap(); + } + + /// The backing props map proxied by this class. + @override + JsBackedMap get props => _props; + JsBackedMap _props; +} + +// Concrete component implementation mixin. +// +// Implements typed props/state factories, defaults `consumedPropKeys` to the keys +// generated for the associated props class. +@Deprecated('This API is for use only within generated code.' + ' Do not reference it in your code, as it may change at any time.') +class _$ClassComponentTypeTesterComponent + extends ClassComponentTypeTesterComponent { + late _$$ClassComponentTypeTesterProps$JsMap _cachedTypedProps; + + @override + _$$ClassComponentTypeTesterProps$JsMap get props => _cachedTypedProps; + + @override + set props(Map value) { + assert( + getBackingMap(value) is JsBackedMap, + 'Component2.props should never be set directly in ' + 'production. If this is required for testing, the ' + 'component should be rendered within the test. If ' + 'that does not have the necessary result, the last ' + 'resort is to use typedPropsFactoryJs.'); + super.props = value; + _cachedTypedProps = + typedPropsFactoryJs(getBackingMap(value) as JsBackedMap); + } + + @override + _$$ClassComponentTypeTesterProps$JsMap typedPropsFactoryJs( + JsBackedMap? backingMap) => + _$$ClassComponentTypeTesterProps$JsMap(backingMap); + + @override + _$$ClassComponentTypeTesterProps typedPropsFactory(Map? backingMap) => + _$$ClassComponentTypeTesterProps(backingMap); + + /// Let `UiComponent` internals know that this class has been generated. + @override + bool get $isClassGenerated => true; + + @override + String get displayName => 'ClassComponentTypeTester'; + + /// The default consumed props, comprising all props mixins used by ClassComponentTypeTesterProps. + /// Used in `*ConsumedProps` methods if [consumedProps] is not overridden. + @override + get $defaultConsumedProps => propsMeta.all; + + @override + PropsMetaCollection get propsMeta => const PropsMetaCollection({ + // If this generated mixin is undefined, it's likely because ClassComponentTypeTesterProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of ClassComponentTypeTesterProps, and check that $ClassComponentTypeTesterProps is exported/imported properly. + ClassComponentTypeTesterProps: $ClassComponentTypeTesterProps.meta, + }); +} + // React component factory implementation. // // Registers component implementation and links type meta to builder factory. @@ -399,6 +720,12 @@ mixin $TestJsProps on TestJsProps { @override set buttonComponent(dynamic value) => props[_$key__buttonComponent__TestJsProps] = value; + @override + dynamic get forwardRef => + (props[_$key__forwardRef__TestJsProps] ?? null) as dynamic; + @override + set forwardRef(dynamic value) => + props[_$key__forwardRef__TestJsProps] = value; /* GENERATED CONSTANTS */ static const PropDescriptor _$prop___$raw$buttonProps__TestJsProps = PropDescriptor(_$key___$raw$buttonProps__TestJsProps); @@ -414,6 +741,8 @@ mixin $TestJsProps on TestJsProps { PropDescriptor(_$key__inputComponent__TestJsProps); static const PropDescriptor _$prop__buttonComponent__TestJsProps = PropDescriptor(_$key__buttonComponent__TestJsProps); + static const PropDescriptor _$prop__forwardRef__TestJsProps = + PropDescriptor(_$key__forwardRef__TestJsProps); static const String _$key___$raw$buttonProps__TestJsProps = 'buttonProps'; static const String _$key___$raw$listOfProps__TestJsProps = 'listOfProps'; static const String _$key___$raw$inputRef__TestJsProps = 'inputRef'; @@ -422,6 +751,7 @@ mixin $TestJsProps on TestJsProps { static const String _$key__component__TestJsProps = 'component'; static const String _$key__inputComponent__TestJsProps = 'inputComponent'; static const String _$key__buttonComponent__TestJsProps = 'buttonComponent'; + static const String _$key__forwardRef__TestJsProps = 'forwardRef'; static const List $props = [ _$prop___$raw$buttonProps__TestJsProps, @@ -430,7 +760,8 @@ mixin $TestJsProps on TestJsProps { _$prop___$raw$messageContext__TestJsProps, _$prop__component__TestJsProps, _$prop__inputComponent__TestJsProps, - _$prop__buttonComponent__TestJsProps + _$prop__buttonComponent__TestJsProps, + _$prop__forwardRef__TestJsProps ]; static const List $propKeys = [ _$key___$raw$buttonProps__TestJsProps, @@ -439,7 +770,8 @@ mixin $TestJsProps on TestJsProps { _$key___$raw$messageContext__TestJsProps, _$key__component__TestJsProps, _$key__inputComponent__TestJsProps, - _$key__buttonComponent__TestJsProps + _$key__buttonComponent__TestJsProps, + _$key__forwardRef__TestJsProps ]; @override @@ -574,6 +906,72 @@ const PropsMeta _$metaForJsTypeTesterProps = PropsMeta( keys: $JsTypeTesterProps.$propKeys, ); +@Deprecated('This API is for use only within generated code.' + ' Do not reference it in your code, as it may change at any time.' + ' EXCEPTION: this may be used in legacy boilerplate until' + ' it is transitioned to the new mixin-based boilerplate.') +mixin $ClassComponentTypeTesterProps on ClassComponentTypeTesterProps { + static const PropsMeta meta = _$metaForClassComponentTypeTesterProps; + @override + Map get mapProp => + (props[_$key__mapProp__ClassComponentTypeTesterProps] ?? null) as Map; + @override + set mapProp(Map value) => + props[_$key__mapProp__ClassComponentTypeTesterProps] = value; + @override + Function get functionProp => + (props[_$key__functionProp__ClassComponentTypeTesterProps] ?? null) + as Function; + @override + set functionProp(Function value) => + props[_$key__functionProp__ClassComponentTypeTesterProps] = value; + /* GENERATED CONSTANTS */ + static const PropDescriptor _$prop__mapProp__ClassComponentTypeTesterProps = + PropDescriptor(_$key__mapProp__ClassComponentTypeTesterProps, + isRequired: true, isNullable: true, isLate: true); + static const PropDescriptor + _$prop__functionProp__ClassComponentTypeTesterProps = PropDescriptor( + _$key__functionProp__ClassComponentTypeTesterProps, + isRequired: true, + isNullable: true, + isLate: true); + static const String _$key__mapProp__ClassComponentTypeTesterProps = 'mapProp'; + static const String _$key__functionProp__ClassComponentTypeTesterProps = + 'functionProp'; + + static const List $props = [ + _$prop__mapProp__ClassComponentTypeTesterProps, + _$prop__functionProp__ClassComponentTypeTesterProps + ]; + static const List $propKeys = [ + _$key__mapProp__ClassComponentTypeTesterProps, + _$key__functionProp__ClassComponentTypeTesterProps + ]; + + @override + @UiProps.$mustCallSuper + void validateRequiredProps() { + super.validateRequiredProps(); + if (!props.containsKey('mapProp') && + !requiredPropNamesToSkipValidation.contains('mapProp')) { + throw MissingRequiredPropsError('Required prop `mapProp` is missing.'); + } + + if (!props.containsKey('functionProp') && + !requiredPropNamesToSkipValidation.contains('functionProp')) { + throw MissingRequiredPropsError( + 'Required prop `functionProp` is missing.'); + } + } +} + +@Deprecated('This API is for use only within generated code.' + ' Do not reference it in your code, as it may change at any time.') +const PropsMeta _$metaForClassComponentTypeTesterProps = PropsMeta( + fields: $ClassComponentTypeTesterProps.$props, + keys: $ClassComponentTypeTesterProps.$propKeys, +); + final UiFactoryConfig<_$$ExpectsDartMapPropProps> _$ExpectsDartMapPropConfig = UiFactoryConfig( propsFactory: PropsFactory( From 3fd38658cfac21f9cd16bbfa046d5f27f0f44024 Mon Sep 17 00:00:00 2001 From: Keal Jones Date: Mon, 16 Sep 2024 14:21:10 -0700 Subject: [PATCH 09/31] undo named extensions --- test/over_react/util/prop_conversion_test.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/over_react/util/prop_conversion_test.dart b/test/over_react/util/prop_conversion_test.dart index 4cdf927f9..9fe524200 100644 --- a/test/over_react/util/prop_conversion_test.dart +++ b/test/over_react/util/prop_conversion_test.dart @@ -1373,12 +1373,12 @@ extension on TypeMatcher { having((ref) => ref.jsThis, 'jsThis', matcher); } -extension HavingToStringValue on TypeMatcher { +extension on TypeMatcher { Matcher havingToStringValue(dynamic matcher) => having((o) => o.toString(), 'toString() value', matcher); } -extension HavingDartComponent on TypeMatcher { +extension on TypeMatcher { Matcher havingDartComponent(dynamic matcher) => having((ref) => ref.dartComponent, 'dartComponent', matcher); } From d3b00c7056ca73a8b42ea003f1e4e0f452250b3e Mon Sep 17 00:00:00 2001 From: Keal Jones Date: Mon, 16 Sep 2024 14:22:45 -0700 Subject: [PATCH 10/31] cleanup suspense example --- example/suspense/main.dart | 49 +------------------------------------- 1 file changed, 1 insertion(+), 48 deletions(-) diff --git a/example/suspense/main.dart b/example/suspense/main.dart index cb7295621..54860dce4 100644 --- a/example/suspense/main.dart +++ b/example/suspense/main.dart @@ -14,60 +14,13 @@ import 'dart:html'; -import 'package:js/js.dart'; -import 'package:over_react/js_component.dart'; import 'package:over_react/over_react.dart'; -import 'package:react/react_client/component_factory.dart'; -import 'package:react/react_client/react_interop.dart' hide lazy; import 'package:react/react_dom.dart' as react_dom; import 'counter_component.dart' deferred as lazy_component; import 'third_party_file.dart'; part 'main.over_react.g.dart'; -@Props(keyNamespace: '') -mixin TestJsProps on UiProps { - @Accessor(key: 'buttonProps') - JsMap? _$raw$buttonProps; - - Map? get buttonProps => unjsifyMapProp(_$raw$buttonProps); - set buttonProps(Map? value) => _$raw$buttonProps = jsifyMapProp(value); - - @Accessor(key: 'listOfProps') - List? _$raw$listOfProps; - - List? get listOfProps => unjsifyMapListProp(_$raw$listOfProps); - set listOfProps(List? value) => _$raw$listOfProps = jsifyMapListProp(value); - - @Accessor(key: 'inputRef') - dynamic _$raw$inputRef; - - dynamic get inputRef => unjsifyRefProp(_$raw$inputRef); - set inputRef(dynamic value) => _$raw$inputRef = jsifyRefProp(value); - - @Accessor(key: 'messageContext') - ReactContext? _$raw$messageContext; - - Context? get messageContext => unjsifyContextProp(_$raw$messageContext); - set messageContext(Context? value) => _$raw$messageContext = jsifyContextProp(value); - - dynamic /*ElementType*/ component; - dynamic /*ElementType*/ inputComponent; - dynamic /*ElementType*/ buttonComponent; -} - -@JS('TestJsComponent') -external ReactClass get _TestJs; - -UiFactory TestJs = uiJsComponent( - ReactJsComponentFactoryProxy(_TestJs), - _$TestJsConfig, // ignore: undefined_identifier -); - -UiFactory LazyTestJs = lazy(() async => TestJs, -_$TestJsConfig, // ignore: undefined_identifier -); - UiFactory LazyCounter = lazy(() async { await Future.delayed(Duration(seconds: 5)); await lazy_component.loadLibrary(); @@ -86,7 +39,7 @@ void main() { 'I am a fallback UI that will show while we load the lazy component! The load time is artificially inflated to last an additional 5 seconds just to prove it\'s working!', ) )( - (LazyTestJs())( + (LazyCounter())( (Dom.div()..id = 'Heyyy!')(), ), ), From 677b555b46612210369f41ca4533adc9ef387c9c Mon Sep 17 00:00:00 2001 From: Keal Jones Date: Mon, 16 Sep 2024 14:24:53 -0700 Subject: [PATCH 11/31] woops --- test/over_react_component_test.dart | 100 ++++++++++++++-------------- 1 file changed, 50 insertions(+), 50 deletions(-) diff --git a/test/over_react_component_test.dart b/test/over_react_component_test.dart index faa2aae93..9a481433f 100644 --- a/test/over_react_component_test.dart +++ b/test/over_react_component_test.dart @@ -22,60 +22,60 @@ library over_react_component_test; import 'package:over_react/over_react.dart'; import 'package:test/test.dart'; -// import 'over_react/component/_deprecated/abstract_transition_test.dart' -// as deprecated_abstract_transition_test; -// import 'over_react/component/abstract_transition_test.dart' -// as abstract_transition_test; -// import 'over_react/component/with_transition_test.dart' -// as with_transition_test; -// import 'over_react/component/dom_components_test.dart' as dom_components_test; -// import 'over_react/component/error_boundary/error_boundary_test.dart' as error_boundary_test; -// import 'over_react/component/_deprecated/error_boundary_mixin_test.dart' -// as deprecated_error_boundary_mixin_test; -// import 'over_react/component/_deprecated/error_boundary_test.dart' -// as deprecated_error_boundary_test; -// import 'over_react/component/ref_util_test.dart' as ref_test; -// import 'over_react/component/element_type_test.dart' as element_type_test; +import 'over_react/component/_deprecated/abstract_transition_test.dart' + as deprecated_abstract_transition_test; +import 'over_react/component/abstract_transition_test.dart' + as abstract_transition_test; +import 'over_react/component/with_transition_test.dart' + as with_transition_test; +import 'over_react/component/dom_components_test.dart' as dom_components_test; +import 'over_react/component/error_boundary/error_boundary_test.dart' as error_boundary_test; +import 'over_react/component/_deprecated/error_boundary_mixin_test.dart' + as deprecated_error_boundary_mixin_test; +import 'over_react/component/_deprecated/error_boundary_test.dart' + as deprecated_error_boundary_test; +import 'over_react/component/ref_util_test.dart' as ref_test; +import 'over_react/component/element_type_test.dart' as element_type_test; import 'over_react/component/lazy_test.dart' as lazy_test; -// import 'over_react/component/memo_test.dart' as memo_test; -// import 'over_react/component/prop_mixins_test.dart' as prop_mixins_test; -// import 'over_react/component/prop_typedefs_test.dart' as prop_typedefs_test; -// import 'over_react/component/pure_component_mixin_test.dart' -// as pure_component_mixin_test; -// import 'over_react/component/_deprecated/resize_sensor_test.dart' -// as deprecated_resize_sensor_test; -// import 'over_react/component/resize_sensor_test.dart' as resize_sensor_test; -// import 'over_react/component/fragment_component_test.dart' -// as fragment_component_test; -// import 'over_react/component/strictmode_component_test.dart' -// as strictmode_component_test; -// import 'over_react/component/suspense_component_test.dart' -// as suspense_component_test; -// import 'over_react/component/context_test.dart' as context_test; -// import 'over_react/component/typed_factory_test.dart' as typed_factory_test; +import 'over_react/component/memo_test.dart' as memo_test; +import 'over_react/component/prop_mixins_test.dart' as prop_mixins_test; +import 'over_react/component/prop_typedefs_test.dart' as prop_typedefs_test; +import 'over_react/component/pure_component_mixin_test.dart' + as pure_component_mixin_test; +import 'over_react/component/_deprecated/resize_sensor_test.dart' + as deprecated_resize_sensor_test; +import 'over_react/component/resize_sensor_test.dart' as resize_sensor_test; +import 'over_react/component/fragment_component_test.dart' + as fragment_component_test; +import 'over_react/component/strictmode_component_test.dart' + as strictmode_component_test; +import 'over_react/component/suspense_component_test.dart' + as suspense_component_test; +import 'over_react/component/context_test.dart' as context_test; +import 'over_react/component/typed_factory_test.dart' as typed_factory_test; void main() { enableTestMode(); - // pure_component_mixin_test.main(); - // deprecated_abstract_transition_test.main(); - // abstract_transition_test.main(); - // with_transition_test.main(); - // error_boundary_test.main(); - // deprecated_error_boundary_mixin_test.main(); - // deprecated_error_boundary_test.main(); - // ref_test.main(); - // element_type_test.main(); + pure_component_mixin_test.main(); + deprecated_abstract_transition_test.main(); + abstract_transition_test.main(); + with_transition_test.main(); + error_boundary_test.main(); + deprecated_error_boundary_mixin_test.main(); + deprecated_error_boundary_test.main(); + ref_test.main(); + element_type_test.main(); lazy_test.main(); - // memo_test.main(); - // dom_components_test.main(); - // prop_mixins_test.main(); - // prop_typedefs_test.main(); - // deprecated_resize_sensor_test.main(); - // resize_sensor_test.main(); - // fragment_component_test.main(); - // strictmode_component_test.main(); - // suspense_component_test.main(); - // context_test.main(); - // typed_factory_test.main(); + memo_test.main(); + dom_components_test.main(); + prop_mixins_test.main(); + prop_typedefs_test.main(); + deprecated_resize_sensor_test.main(); + resize_sensor_test.main(); + fragment_component_test.main(); + strictmode_component_test.main(); + suspense_component_test.main(); + context_test.main(); + typed_factory_test.main(); } From 1e0b3a7ea64cfb5610b3dd5f56c904e05db86a5c Mon Sep 17 00:00:00 2001 From: Keal Jones Date: Mon, 16 Sep 2024 14:26:01 -0700 Subject: [PATCH 12/31] remove unused part --- example/suspense/main.dart | 2 -- 1 file changed, 2 deletions(-) diff --git a/example/suspense/main.dart b/example/suspense/main.dart index 54860dce4..3a2b07093 100644 --- a/example/suspense/main.dart +++ b/example/suspense/main.dart @@ -19,8 +19,6 @@ import 'package:react/react_dom.dart' as react_dom; import 'counter_component.dart' deferred as lazy_component; import 'third_party_file.dart'; -part 'main.over_react.g.dart'; - UiFactory LazyCounter = lazy(() async { await Future.delayed(Duration(seconds: 5)); await lazy_component.loadLibrary(); From 9713881b7849bf30a70f5fdd35ea7ba27f5588c3 Mon Sep 17 00:00:00 2001 From: Keal Jones Date: Tue, 17 Sep 2024 10:06:08 -0700 Subject: [PATCH 13/31] fix suspense test lazy typing --- test/over_react/component/suspense_component_test.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/over_react/component/suspense_component_test.dart b/test/over_react/component/suspense_component_test.dart index 896ec6b23..17a5d63a8 100644 --- a/test/over_react/component/suspense_component_test.dart +++ b/test/over_react/component/suspense_component_test.dart @@ -30,7 +30,7 @@ void main() { group('Suspense', () { test('renders fallback UI first followed by the real component', () async { final lazyLoadCompleter = Completer(); - final LazyLoadMe = lazy( + UiFactory LazyLoadMe = lazy( () async { await lazy_load_me.loadLibrary(); await lazyLoadCompleter.future; @@ -60,7 +60,7 @@ void main() { final lazyLoadCompleter = Completer(); const suspenseFallbackTimeout = Duration(milliseconds: 100); - final LazyLoadMe = lazy( + UiFactory LazyLoadMe = lazy( () async { await lazy_load_me.loadLibrary(); await lazyLoadCompleter.future; From aed0afb02a40826492b7b065fd5b847472eb502d Mon Sep 17 00:00:00 2001 From: Keal Jones Date: Tue, 17 Sep 2024 12:45:49 -0700 Subject: [PATCH 14/31] add doc comment --- lib/src/util/lazy.dart | 47 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/lib/src/util/lazy.dart b/lib/src/util/lazy.dart index cd62ee5b2..3c2efdc31 100644 --- a/lib/src/util/lazy.dart +++ b/lib/src/util/lazy.dart @@ -17,6 +17,53 @@ library over_react.lazy; import 'package:over_react/over_react.dart'; import 'package:react/react.dart' as react; + +/// A HOC that creates a "lazy" component that lets you defer loading a component’s code until it is rendered for the first time. +/// +/// Returns a `UiFactory` you can use just render in your tree. While the code for the lazy component is still loading, attempting to render it will suspend. Use to display a loading indicator while it’s loading. +/// +/// It takes 2 arguments, the `load` function that returns a Future>, and a UiFactoryConfig. +/// +/// React will not call the `load` function until the first time the component is rendered. +/// After React first calls `load`, it will wait for it to resolve, and then render the resolved value. +/// Both the returned Future and the Future's resolved value will be cached, so React will not call `load` more than once. +/// If the Future rejects, React will throw the rejection reason for the nearest Error Boundary to handle. +/// +/// Example: +/// ```dart +/// import 'package:over_react/over_react.dart'; +/// +/// part 'main.over_react.g.dart'; +/// +/// mixin ALazyComponentPropsMixin on UiProps {} +/// +/// UiFactory ALazyComponent = lazy( +/// () async { +/// final componentModule = await loadComponent(); +/// return uiForwardRef( +/// (props, ref) { +/// return (componentModule.AnotherComponent() +/// ..ref = ref +/// ..addProps(props) +/// )(props.children); +/// }, +/// _$ALazyComponentConfig, +/// ); +/// }, +/// _$ALazyComponentConfig +/// ); +/// ``` +/// +/// > __NOTE:__ A lazy component MUST be wrapped with a `Suspense` component to provide a fallback ui while it loads. +/// +/// ```dart +/// (Suspense() +/// ..fallback = Dom.p()('Loading...') +/// )( +/// ALazyComponent()(), +/// ); +/// ``` +/// See: . UiFactory lazy( Future> Function() loadComponent, /* UiFactoryConfig */ dynamic _config, {bool useJsFactoryProxy = false}) { From e1704f47ead4b577f1fcaebb70443021626811b8 Mon Sep 17 00:00:00 2001 From: Keal Jones Date: Tue, 17 Sep 2024 13:43:22 -0700 Subject: [PATCH 15/31] fix lazy displyName config --- lib/src/util/lazy.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/src/util/lazy.dart b/lib/src/util/lazy.dart index 3c2efdc31..7675937d5 100644 --- a/lib/src/util/lazy.dart +++ b/lib/src/util/lazy.dart @@ -65,7 +65,7 @@ import 'package:react/react.dart' as react; /// ``` /// See: . UiFactory lazy( - Future> Function() loadComponent, /* UiFactoryConfig */ dynamic _config, + Future> Function([dynamic config]) loadComponent, /* UiFactoryConfig */ dynamic _config, {bool useJsFactoryProxy = false}) { ArgumentError.checkNotNull(_config, '_config'); @@ -95,7 +95,7 @@ UiFactory lazy( }, UiFactoryConfig( propsFactory: PropsFactory.fromUiFactory(factory), - displayName: 'Lazy${_config.displayName}', + displayName: 'lazy(${_config.displayName ?? ''})', ), ); return wrapper().componentFactory!; From 046c0f5b6ccfa774f670360357396f4717854015 Mon Sep 17 00:00:00 2001 From: Keal Jones Date: Wed, 18 Sep 2024 09:50:14 -0700 Subject: [PATCH 16/31] add ability to use Generic Props --- lib/src/util/lazy.dart | 52 +++++++++++++++++++++++++----------------- 1 file changed, 31 insertions(+), 21 deletions(-) diff --git a/lib/src/util/lazy.dart b/lib/src/util/lazy.dart index 7675937d5..24f860e48 100644 --- a/lib/src/util/lazy.dart +++ b/lib/src/util/lazy.dart @@ -17,16 +17,18 @@ library over_react.lazy; import 'package:over_react/over_react.dart'; import 'package:react/react.dart' as react; +import '../component_declaration/function_component.dart'; /// A HOC that creates a "lazy" component that lets you defer loading a component’s code until it is rendered for the first time. /// -/// Returns a `UiFactory` you can use just render in your tree. While the code for the lazy component is still loading, attempting to render it will suspend. Use to display a loading indicator while it’s loading. +/// Returns a `UiFactory` you can use just render in your tree. While the code for the lazy component is still loading, +/// attempting to render it will suspend. Use to display a loading indicator while it’s loading. +/// [load] is a function that should return a `Future>` that resolves to the component to be rendered. +/// [_config] should be a `UiFactoryConfig` or `null` and is only `dynamic` to avoid an unnecessary cast in the boilerplate. /// -/// It takes 2 arguments, the `load` function that returns a Future>, and a UiFactoryConfig. -/// -/// React will not call the `load` function until the first time the component is rendered. -/// After React first calls `load`, it will wait for it to resolve, and then render the resolved value. -/// Both the returned Future and the Future's resolved value will be cached, so React will not call `load` more than once. +/// React will not call [load] until the first time the component is rendered. +/// After React first calls [load], it will wait for it to resolve, and then render the resolved value. +/// Both the returned Future and the Future's resolved value will be cached, so React will not call [load] more than once. /// If the Future rejects, React will throw the rejection reason for the nearest Error Boundary to handle. /// /// Example: @@ -65,22 +67,17 @@ import 'package:react/react.dart' as react; /// ``` /// See: . UiFactory lazy( - Future> Function([dynamic config]) loadComponent, /* UiFactoryConfig */ dynamic _config, - {bool useJsFactoryProxy = false}) { - ArgumentError.checkNotNull(_config, '_config'); - + Future> Function() load, /* UiFactoryConfig */ dynamic _config) { + _config ??= UiFactoryConfig(); if (_config is! UiFactoryConfig) { - throw ArgumentError('_config should be a UiFactoryConfig. Make sure you are ' - r'using either the generated factory config (i.e. _$FooConfig) or manually ' - 'declaring your config correctly.'); + throw ArgumentError('_config is required when using a custom props class and should be a UiFactoryConfig. Make sure you are ' + r'using either the generated factory config (i.e. _$FooConfig) or manually declaring your config correctly.'); } // ignore: invalid_use_of_protected_member - final propsFactory = _config.propsFactory; - ArgumentError.checkNotNull(propsFactory, '_config.propsFactory'); - propsFactory!; + var propsFactory = _config.propsFactory; final lazyFactoryProxy = react.lazy(() async { - final factory = await loadComponent(); + final factory = await load(); // 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. @@ -95,20 +92,33 @@ UiFactory lazy( }, UiFactoryConfig( propsFactory: PropsFactory.fromUiFactory(factory), - displayName: 'lazy(${_config.displayName ?? ''})', + displayName: _config.displayName != null ? 'Lazy(${_config.displayName}' : 'LazyComponent' ), ); return wrapper().componentFactory!; }); + if (propsFactory == null) { + if (TProps != UiProps && TProps != GenericUiProps) { + throw ArgumentError( + 'config.propsFactory must be provided when using custom props classes'); + } + propsFactory = PropsFactory.fromUiFactory( + ([backingMap]) => GenericUiProps(lazyFactoryProxy, backingMap)) + as PropsFactory; + } + // Work around propsFactory not getting promoted to non-nullable in _uiFactory: https://github.com/dart-lang/language/issues/1536 + final nonNullablePropsFactory = propsFactory; + TProps _uiFactory([Map? props]) { TProps builder; if (props == null) { - builder = propsFactory.jsMap(JsBackedMap()); + // propsFactory should get promoted to non-nullable here, but it does not some reason propsF + builder = nonNullablePropsFactory.jsMap(JsBackedMap()); } else if (props is JsBackedMap) { - builder = propsFactory.jsMap(props); + builder = nonNullablePropsFactory.jsMap(props); } else { - builder = propsFactory.map(props); + builder = nonNullablePropsFactory.map(props); } return builder..componentFactory = lazyFactoryProxy; From b5289ec1a3e624607ec335328764b879ab2272e5 Mon Sep 17 00:00:00 2001 From: Keal Jones Date: Wed, 18 Sep 2024 10:43:09 -0700 Subject: [PATCH 17/31] add snippets for lazy uiFunction and lazy uiForwardRef --- snippets/snippets.json | 130 ++++++++++++++++++++++++++++++----------- snippets/snippets.xml | 14 +++++ 2 files changed, 109 insertions(+), 35 deletions(-) diff --git a/snippets/snippets.json b/snippets/snippets.json index 46c4c78d8..4baaabf9f 100644 --- a/snippets/snippets.json +++ b/snippets/snippets.json @@ -1,4 +1,99 @@ { + "lazyFunctionComponent": { + "prefix": "orLzyFunc", + "body": [ + "import 'package:over_react/over_react.dart';", + "", + "part '${TM_FILENAME_BASE}.over_react.g.dart';", + "", + "mixin ${1:MyComponent}PropsMixin on UiProps {}", + "", + "UiFactory<${1:MyComponent}PropsMixin> ${1:MyComponent} = lazy(", + "\t() async {", + "\t\t// Await required futures here.", + "\t\treturn uiFunction(", + "\t\t\t(props) {},", + "\t\t\t_$${1:MyComponent}Config, // ignore: undefined_identifier", + "\t\t);", + "\t},", + "\t_$${1:MyComponent}Config, // ignore: undefined_identifier", + ");" + ], + "description": "Creates an OverReact lazy wrapped uiFunction component with an abbreviated props declaration" + }, + "lazyForwardRefComponent": { + "prefix": "orLzyFwdRef", + "body": [ + "import 'package:over_react/over_react.dart';", + "", + "part '${TM_FILENAME_BASE}.over_react.g.dart';", + "", + "mixin ${1:MyComponent}PropsMixin on UiProps {}", + "", + "/// Must be wrapped in a `Suspense` component to display a fallback ui while loading.", + "UiFactory<${1:MyComponent}PropsMixin> ${1:MyComponent} = lazy(", + "\t() async {", + "\t\t// Await required futures here.", + "\t\treturn uiForwardRef(", + "\t\t\t(props, ref) {},", + "\t\t\t_$${1:MyComponent}Config, // ignore: undefined_identifier", + "\t\t);", + "\t},", + "\t_$${1:MyComponent}Config, // ignore: undefined_identifier", + ");" + ], + "description": "Creates an OverReact lazy wrapped uiForwardRef component with an abbreviated props declaration" + }, + "forwardRefComponent": { + "prefix": "orFwdRef", + "body": [ + "import 'package:over_react/over_react.dart';", + "", + "part '${TM_FILENAME_BASE}.over_react.g.dart';", + "", + "mixin ${1:MyComponent}Props on UiProps {}", + "", + "UiFactory<${1:MyComponent}Props> ${1:MyComponent} = uiForwardRef(", + "\t(props, ref) {},", + "\t_$${1:MyComponent}Config, // ignore: undefined_identifier", + ");" + ], + "description": "Creates an OverReact forwardRef component with an abbreviated props declaration" + }, + "abbreviatedFunctionComponent": { + "prefix": "orFunc", + "body": [ + "import 'package:over_react/over_react.dart';", + "", + "part '${TM_FILENAME_BASE}.over_react.g.dart';", + "", + "UiFactory<${1:MyComponent}Props> ${1:MyComponent} = uiFunction(", + "\t(props) {},", + "\t_$${1:MyComponent}Config, // ignore: undefined_identifier", + ");", + "", + "mixin ${1:MyComponent}Props on UiProps {}" + ], + "description": "Creates an OverReact function component with an abbreviated props declaration" + }, + "functionComponent": { + "prefix": "orAdvFunc", + "body": [ + "import 'package:over_react/over_react.dart';", + "", + "part '${TM_FILENAME_BASE}.over_react.g.dart';", + "", + "mixin ${1:MyComponent}PropsMixin on UiProps {}", + "", + "class ${1:MyComponent}Props = UiProps with ${1:MyComponent}PropsMixin;", + "", + "UiFactory<${1:MyComponent}Props> ${1:MyComponent} = uiFunction(", + "\t(props) {},", + "\t_$${1:MyComponent}Config, // ignore: undefined_identifier", + ");" + ], + "description": "Creates an OverReact function component with a props class alias" + }, "statelessComponent": { "prefix": "orAdvStless", "body": [ @@ -127,40 +222,5 @@ "}" ], "description": "Creates a stateless and connected OverReact component" - }, - "abbreviatedFunctionComponent": { - "prefix": "orFunc", - "body": [ - "import 'package:over_react/over_react.dart';", - "", - "part '${TM_FILENAME_BASE}.over_react.g.dart';", - "", - "UiFactory<${1:MyComponent}Props> ${1:MyComponent} = uiFunction(", - "\t(props) {},", - "\t_$${1:MyComponent}Config, // ignore: undefined_identifier", - ");", - "", - "mixin ${1:MyComponent}Props on UiProps {}" - ], - "description": "Creates an OverReact function component with an abbreviated props declaration" - }, - "functionComponent": { - "prefix": "orAdvFunc", - "body": [ - "import 'package:over_react/over_react.dart';", - "", - "part '${TM_FILENAME_BASE}.over_react.g.dart';", - "", - "UiFactory<${1:MyComponent}Props> ${1:MyComponent} = uiFunction(", - "\t(props) {},", - "\t_$${1:MyComponent}Config, // ignore: undefined_identifier", - ");", - "", - "mixin ${1:MyComponent}PropsMixin on UiProps {}", - "", - "class ${1:MyComponent}Props = UiProps with ${1:MyComponent}PropsMixin;", - "" - ], - "description": "Creates an OverReact function component with a props class alias" } } diff --git a/snippets/snippets.xml b/snippets/snippets.xml index d040b67dc..eb4970a5c 100644 --- a/snippets/snippets.xml +++ b/snippets/snippets.xml @@ -1,3 +1,17 @@ + +