-
Notifications
You must be signed in to change notification settings - Fork 57
FED-7 Move over uiJsComponent + tests #743
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
31 commits
Select commit
Hold shift + click to select a range
e3eae44
Create files for prop_conversion
sydneesampson-wk 100b701
Create tests for prop conversion
sydneesampson-wk c1d2790
Create js_component
sydneesampson-wk 40abc63
Create tests for js_component
sydneesampson-wk 386c85f
Create weak_map (dependency for prop_conversion)
sydneesampson-wk d48eb84
Update react testing library
sydneesampson-wk ea7bd03
Import weak map
sydneesampson-wk 9e1e66d
Update imports for js component tests
sydneesampson-wk 0d110d8
Update imports for prop conv tests, update button
sydneesampson-wk 7adabe0
Update imports
sydneesampson-wk 82b9ba0
Add ref test cases
sydneesampson-wk 1ba0cb3
Update import
sydneesampson-wk 25089ee
Merge branch 'master' into FED-7-utils-tests
sydneesampson-wk e49d4e5
Add button dart to test folder
sydneesampson-wk 679f489
Update prop conv tests
sydneesampson-wk 87ccf42
Import button
sydneesampson-wk 2c94f9b
Update buttons with domProps
sydneesampson-wk 22cad0b
Export js component, prop conversion
sydneesampson-wk f51c233
Update exports
sydneesampson-wk e5c5314
Remove button.dart
sydneesampson-wk 615c47a
Build
sydneesampson-wk b984fc2
Add new tests to over_react_util_test
sydneesampson-wk 1ac4050
Add TestJsComponent to util_test html
sydneesampson-wk 2b4c0ac
Update tests with _TestJsComponent
sydneesampson-wk 3071efe
Build
sydneesampson-wk c853985
Add missing script tag to test
sydneesampson-wk 55a1873
Format
brianphillips-wk 4329621
Fix WeakMap dart2js errors
greglittlefield-wf 72fa33b
Update test/over_react/util/prop_conversion_test.dart
sydneesampson-wk be92ba8
Update comment
brianphillips-wk d326cec
Fix typed props map tests
brianphillips-wk File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| // Copyright 2022 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. | ||
|
|
||
| export 'src/util/js_component.dart'; | ||
| export 'src/util/prop_conversion.dart'; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| import 'package:over_react/over_react.dart'; | ||
| import 'package:react/react_client/component_factory.dart'; | ||
|
|
||
| /// Creates a Dart component factory that wraps a ReactJS [factoryProxy]. | ||
| /// | ||
| /// More in-depth documentation for wrapping JS components is coming soon. | ||
| /// | ||
| /// Example: | ||
| /// ```dart | ||
| /// UiFactory<ButtonProps> Button = uiJsComponent( | ||
| /// ReactJsComponentFactoryProxy(MaterialUI.Button), | ||
| /// _$ButtonConfig, // ignore: undefined_identifier | ||
| /// ); | ||
| /// | ||
| /// @Props(keyNamespace: '') | ||
| /// mixin ButtonProps on UiProps {} | ||
| /// ``` | ||
| UiFactory<TProps> uiJsComponent<TProps extends UiProps>( | ||
sydneesampson-wk marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| ReactJsComponentFactoryProxy factoryProxy, | ||
| dynamic _config, | ||
| ) { | ||
| ArgumentError.checkNotNull(_config, '_config'); | ||
|
|
||
| if (_config is! UiFactoryConfig<TProps>) { | ||
| throw ArgumentError( | ||
| '_config should be a UiFactory<TProps>. 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 as UiFactoryConfig<TProps>).propsFactory; | ||
| ArgumentError.checkNotNull(_config, '_config'); | ||
|
|
||
| TProps _uiFactory([Map backingMap]) { | ||
| TProps builder; | ||
| if (backingMap == null) { | ||
| builder = propsFactory.jsMap(JsBackedMap()); | ||
| } else if (backingMap is JsBackedMap) { | ||
| builder = propsFactory.jsMap(backingMap); | ||
| } else { | ||
| builder = propsFactory.map(backingMap); | ||
| } | ||
| return builder..componentFactory = factoryProxy; | ||
| } | ||
|
|
||
| return _uiFactory; | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,127 @@ | ||
| import 'package:js/js.dart'; | ||
| import 'package:js/js_util.dart'; | ||
| import 'package:meta/meta.dart'; | ||
| import 'package:over_react/over_react.dart' show Ref; | ||
| import 'package:over_react/src/util/weak_map.dart'; | ||
| import 'package:react/react_client/js_backed_map.dart'; | ||
| import 'package:react/react_client/component_factory.dart'; | ||
| import 'package:react/react_client/react_interop.dart' show JsRef; | ||
|
|
||
| // Export JsMap since props that utilize jsifyMapProp/unjsifyMapProp | ||
| // via custom getters/setters will need JsMap to avoid implicit cast errors. | ||
| export 'package:react/react_client/js_backed_map.dart' show JsMap; | ||
|
|
||
| /// Returns a JS-deep-converted version of [value] for storage in the props map, or null if [value] is null. | ||
| /// | ||
| /// For use in JS component prop setters where the component expects a JS object, but typing the getter/setter | ||
| /// as a Dart Map is more convenient to the consumer reading/writing the props. | ||
| /// | ||
| /// Should be used alongside [unjsifyMapProp]. | ||
| /// | ||
| /// For more information on why this conversion is done in the getter/setter, | ||
| /// see documentation around Dart wrapper component prop conversion issues. | ||
| /// FIXME CPLAT-15060 add reference/link to documentation | ||
| JsMap jsifyMapProp(Map value) { | ||
| if (value == null) return null; | ||
| // Use generateJsProps so that, in addition to deeply converting props, we also properly convert the `ref` prop. | ||
| // Dart props get deep converted (just like they would when invoking the ReactJsComponentFactoryProxy anyways), | ||
| // but that's a tradeoff we're willing to make, given that there's no perfect solution, | ||
| // and shouldn't happen often since we shouldn't be passing Dart values to JS components. | ||
| // As a workaround, consumers can wrap any Dart values in an opaque Dart object (similar to DartValueWrapper). | ||
| return generateJsProps(value); | ||
| } | ||
|
|
||
| /// Returns [value] converted to a Dart map and [Map.cast]ed to the provided generics, or null if [value] is null. | ||
| /// | ||
| /// For use in JS component prop getters where the component expects a JS object, but typing the getter/setter | ||
| /// as a Dart Map is more convenient to the consumer reading/writing the props. | ||
| /// | ||
| /// Should be used alongside [jsifyMapProp]. | ||
| /// | ||
| /// For more information on why this conversion is done in the getter/setter, | ||
| /// see documentation around Dart wrapper component prop conversion issues. | ||
| /// FIXME CPLAT-15060 add reference/link to documentation | ||
| Map<K, V> unjsifyMapProp<K, V>(JsMap value) { | ||
| if (value == null) return null; | ||
| // Don't deep unconvert so that JS values don't get converted incorrectly to Maps. See jsifyMapProp for more information. | ||
| return JsBackedMap.backedBy(value).cast(); | ||
| } | ||
|
|
||
| /// Returns [value] converted to its JS ref representation for storage in a props map, or null of the [value] is null. | ||
| /// | ||
| /// For use in JS component prop getters where the component expects a JS ref, but accepting Dart refs | ||
| /// is more convenient to the consumer reading/writing the props. | ||
| /// | ||
| /// Should be used alongside [unjsifyRefProp]. | ||
| /// | ||
| /// Normally ref props get converted in [ReactJsComponentFactoryProxy.build] and [jsifyMapProp], | ||
| /// but that same conversion for props not under the default `'ref'` key doesn't occur automatically, | ||
| /// which is where this function comes in. | ||
| dynamic jsifyRefProp(dynamic value) { | ||
| // Case 1: null | ||
| if (value == null) return null; | ||
|
|
||
| // Case 2a: Dart callback refs | ||
| // Case 2b: JS callback refs | ||
| // allowInterop is technically redundant, since that will get done in ReactJsComponentFactoryProxy.build | ||
| // (or jsifyMapProp for props that accept props maps, like ButtonProps.TouchRippleProps), | ||
| // but we'll do it here anyways since we know it'll be needed. | ||
| if (value is Function) return allowInterop(value); | ||
|
|
||
| // Case 2: Dart ref objects | ||
| if (value is Ref) { | ||
| // Store the original Dart ref so we can retrieve it later in unjsifyRefProp. | ||
| // See _dartRefForJsRef comment for more info. | ||
| _dartRefForJsRef.set(value.jsRef, value); | ||
| return value.jsRef; | ||
| } | ||
|
|
||
| // Case 3: JS ref objects | ||
| return value; | ||
| } | ||
|
|
||
| /// Returns a [JsRef] object converted back into its Dart [Ref] object (if it was converted via [jsifyRefProp], | ||
| /// or if [value] is not a [JsRef], passes through [value] (including null). | ||
| /// | ||
| /// For use in JS component prop getters where the component expects a JS ref, but accepting Dart refs | ||
| /// is more convenient to the consumer reading/writing the props. | ||
| /// | ||
| /// Should be used alongside [unjsifyRefProp]. | ||
| /// | ||
| /// Note that Dart refs currently lose their reified types when jsified/unjsified. | ||
| dynamic unjsifyRefProp(dynamic value, | ||
| {@visibleForTesting bool throwOnUnhandled = false}) { | ||
| // Case 1: null | ||
| if (value == null) return null; | ||
|
|
||
| // Case 2: JS callback refs | ||
| if (value is Function) return value; | ||
|
|
||
| // Case 2: JS ref objects | ||
| if (value is! Ref && value is JsRef && hasProperty(value, 'current')) { | ||
| // Return the original Dart ref is there is one, otherwise return the JsRef itself. | ||
| // See _dartRefForJsRef comment for more info. | ||
| return _dartRefForJsRef.get(value) ?? value; | ||
| } | ||
|
|
||
| // Case 3: unreachable? | ||
| // We shouldn't ever get here, but just pass through the value in case there's | ||
| // a case we didn't handle properly above (or throw for testing purposes). | ||
| if (throwOnUnhandled) { | ||
| throw ArgumentError.value(value, 'value', 'Unhandled case'); | ||
| } | ||
| return value; | ||
| } | ||
|
|
||
| /// A weak mapping from JsRef objects to the original Dart Refs they back. | ||
| /// | ||
| /// Useful for | ||
| /// 1. Preserving the reified type of the Dart ref when it gets jsified/unjsified | ||
| /// 2. Telling whether the ref was originally a Dart or JS ref | ||
| /// | ||
| /// We're using WeakMap here so that we don't have a global object strongly referencing and thus retaining refs, | ||
| /// and not because strong references from the JS ref to the Dart ref would be problematic. | ||
| /// | ||
| /// We also have to use a WeakMap instead of a JS property (or an Expando, whose DDC implementation uses JS properties), | ||
| /// since those can't be used with sealed JS objects (like React.createRef() objects in development builds, and potentially other cases). | ||
| final _dartRefForJsRef = WeakMap(); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| @JS() | ||
| library react_material_ui.weak_map; | ||
|
|
||
| import 'package:js/js.dart'; | ||
|
|
||
| /// Bindings for the JS WeakMap class, a collection of key/value pairs in which the keys are weakly referenced. | ||
| /// | ||
| /// For more info, see: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap | ||
| /// | ||
| /// This API is supported in IE11 and all modern browsers. | ||
| /// | ||
| /// This class has no type parameters since they cause issues in dart2js: https://github.com/dart-lang/sdk/issues/43373 | ||
| @JS() | ||
| class WeakMap { | ||
| external WeakMap(); | ||
|
|
||
| /// Removes any value associated to the [key]. `has(key)` will return false afterwards. | ||
| /// | ||
| /// Returns whether a value was associated with the [key]. | ||
| external bool delete(Object key); | ||
|
|
||
| /// Returns the value associated to the [key], or null if there is none. | ||
| external Object get(Object key); | ||
|
|
||
| /// Returns whether a value has been associated to the key. | ||
| external bool has(Object key); | ||
|
|
||
| /// Sets the value for the [key]. | ||
| // void since IE11 returns undefined, and you can cascade on the object instead of returning it in Dart. | ||
| external void set(Object key, Object value); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
26 changes: 13 additions & 13 deletions
26
test/over_react/component_declaration/redux_component_test/test_reducer.g.dart
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.