From e5f8e7810f8c2705ebbcc98a13a2eb42d2562d8f Mon Sep 17 00:00:00 2001
From: Aaron Lademann
Date: Thu, 23 Jan 2020 13:10:38 -0700
Subject: [PATCH 01/96] Allow displayName to be passed to forwardRef
---
lib/src/component/ref_util.dart | 22 +++++++-
pubspec.yaml | 8 +++
.../component/forward_ref_test.dart | 53 +++++++++++++++++++
3 files changed, 81 insertions(+), 2 deletions(-)
diff --git a/lib/src/component/ref_util.dart b/lib/src/component/ref_util.dart
index 1d518d21a..4a8eb0af7 100644
--- a/lib/src/component/ref_util.dart
+++ b/lib/src/component/ref_util.dart
@@ -12,6 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+import 'dart:js_util';
+
import 'package:react/react_client/react_interop.dart' as react_interop;
import 'package:react/react_client.dart';
import 'package:over_react/component_base.dart';
@@ -91,13 +93,29 @@ Ref createRef() {
///
/// Learn more: .
UiFactory Function(UiFactory) forwardRef(
- Function(TProps props, Ref ref) wrapperFunction) {
+ Function(TProps props, Ref ref) wrapperFunction, {String displayName}) {
UiFactory wrapWithForwardRef(UiFactory factory) {
+ if (displayName == null) {
+ final componentFactoryType = factory().componentFactory.type;
+ if (componentFactoryType is String) {
+ // DomComponent
+ displayName = componentFactoryType;
+ } else if (componentFactoryType is Function) {
+ // JS component factories, Dart function components, Dart composite components
+ displayName = getProperty(componentFactoryType, 'displayName');
+
+ if (displayName == null) {
+ final ctor = getProperty(componentFactoryType, 'constructor');
+ displayName = (ctor == null ? null : getProperty(ctor, 'name')) ?? 'Anonymous';
+ }
+ }
+ }
+
Object wrapProps(Map props, Ref ref) {
return wrapperFunction(factory(props), ref);
}
- ReactComponentFactoryProxy hoc = react_interop.forwardRef(wrapProps);
+ ReactComponentFactoryProxy hoc = react_interop.forwardRef(wrapProps, displayName: displayName);
TProps forwardedFactory([Map props]) {
return factory(props)..componentFactory = hoc;
diff --git a/pubspec.yaml b/pubspec.yaml
index 1437481a4..c40960c28 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -47,3 +47,11 @@ dev_dependencies:
over_react_test: ^2.7.0
pedantic: ^1.8.0
test: ^1.9.1
+
+
+dependency_overrides:
+ react:
+ git:
+ url: https://github.com/cleandart/react-dart.git
+ ref: 5c19d8d43d9d3c7b115da9d1f7c0bd62a41f5a5f # from 5.4.0/improve-forwardref-examples-and-typing
+
diff --git a/test/over_react/component/forward_ref_test.dart b/test/over_react/component/forward_ref_test.dart
index c55125163..eea43f631 100644
--- a/test/over_react/component/forward_ref_test.dart
+++ b/test/over_react/component/forward_ref_test.dart
@@ -15,6 +15,7 @@
library forward_ref_test;
import 'dart:html';
+import 'dart:js_util';
import 'package:test/test.dart';
import 'package:over_react/over_react.dart';
@@ -47,12 +48,64 @@ main() {
expect(refObject.current, TypeMatcher());
});
+
+ group('- sets displayName on the rendered component as expected', () {
+ test('when displayName argument is not passed to forwardRef', () {
+ UiFactory DivForwarded = forwardRef((props, ref) {
+ return (Dom.div()
+ ..ref = ref
+ )();
+ })(Dom.div);
+
+ final refObject = createRef();
+ final vDomElement = (DivForwarded()..ref = refObject)();
+
+ expect(getProperty(getProperty(vDomElement.type, 'render'), 'displayName'), 'div');
+ });
+
+ test('when displayName argument is passed to forwardRef', () {
+ UiFactory DivForwarded = forwardRef((props, ref) {
+ return (Dom.div()
+ ..ref = ref
+ )();
+ }, displayName: 'SomethingCustom')(Dom.div);
+
+ final refObject = createRef();
+ final vDomElement = (DivForwarded()..ref = refObject)();
+
+ expect(getProperty(getProperty(vDomElement.type, 'render'), 'displayName'), 'SomethingCustom');
+ });
+ });
});
group('on a component with a dart component child', () {
forwardRefTest(BasicChild, verifyRefValue: (ref) {
expect(ref, TypeMatcher());
});
+
+ group('- sets displayName on the rendered component as expected', () {
+ test('when displayName argument is not passed to forwardRef', () {
+ UiFactory BasicForwarded = forwardRef((props, ref) {
+ return (BasicChild()..ref = ref)();
+ })(Basic);
+
+ final Ref refObject = createRef();
+ final vDomElement = (BasicForwarded()..ref = refObject)();
+
+ expect(getProperty(getProperty(vDomElement.type, 'render'), 'displayName'), 'Basic');
+ });
+
+ test('when displayName argument is passed to forwardRef', () {
+ UiFactory BasicForwarded = forwardRef((props, ref) {
+ return (BasicChild()..ref = ref)();
+ }, displayName: 'BasicForwarded')(Basic);
+
+ final Ref refObject = createRef();
+ final vDomElement = (BasicForwarded()..ref = refObject)();
+
+ expect(getProperty(getProperty(vDomElement.type, 'render'), 'displayName'), 'BasicForwarded');
+ });
+ });
});
});
}
From 9c8825ed532679fe30cb3b70bbf7755492c19348 Mon Sep 17 00:00:00 2001
From: Aaron Lademann
Date: Thu, 23 Jan 2020 13:19:46 -0700
Subject: [PATCH 02/96] Improve / add forwardRef examples
---
lib/src/component/ref_util.dart | 143 +++++++++++---
web/component2/index.dart | 13 ++
web/component2/index.html | 6 +
web/component2/src/demos.dart | 1 +
web/component2/src/demos/forward_ref.dart | 86 +++++++++
.../src/demos/forward_ref.over_react.g.dart | 181 ++++++++++++++++++
6 files changed, 401 insertions(+), 29 deletions(-)
create mode 100644 web/component2/src/demos/forward_ref.dart
create mode 100644 web/component2/src/demos/forward_ref.over_react.g.dart
diff --git a/lib/src/component/ref_util.dart b/lib/src/component/ref_util.dart
index 4a8eb0af7..724160370 100644
--- a/lib/src/component/ref_util.dart
+++ b/lib/src/component/ref_util.dart
@@ -54,42 +54,127 @@ Ref createRef() {
/// Automatically passes a [Ref] through a component to one of its children.
///
-/// __Example__:
+/// __Example 1:__ Forwarding refs to DOM components
+///
+/// ```dart
+/// import 'dart:html';
+/// import 'package:over_react/over_react.dart';
+/// import 'package:over_react/react_dom.dart' as react_dom;
+///
+/// // ---------- Component Definition ----------
+///
+/// final FancyButton = forwardRef((props, ref) {
+/// final classes = ClassNameBuilder.fromProps(props)
+/// ..add('FancyButton');
+///
+/// return (Dom.button()
+/// ..addProps(getPropsToForward(props, onlyCopyDomProps: true))
+/// ..className = classes.toClassName()
+/// ..ref = ref
+/// )('Click me!');
+/// })(Dom.button);
+///
+/// // ---------- Component Consumption ----------
+///
+/// void main() {
+/// setClientConfiguration();
+/// final ref = createRef();
///
-/// UiFactory DivForwarded = forwardRef((props, ref) {
-/// return (Dom.div()
+/// react_dom.render(
+/// (FancyButton()
/// ..ref = ref
-/// ..className = 'special-class'
-/// )(
-/// props.children
-/// );
-/// })(Dom.div);
+/// ..onClick = (_) {
+/// print(ref.current.outerHtml);
+/// }
+/// )(),
+/// querySelector('#idOfSomeNodeInTheDom')
+/// );
///
-/// ___ OR ___
+/// // You can still get a ref directly to the DOM button:
+/// final buttonNode = ref.current;
+/// }
+/// ```
///
-/// UiFactory FooForwarded = forwardRef((props, ref) {
-/// return (Foo()
-/// ..forwardedRef = ref
-/// )();
-/// })(Foo);
+/// __Example 2:__ Forwarding refs in higher-order components
///
-/// @Factory()
-/// UiFactory Foo = _$Foo;
+/// ```dart
+/// import 'dart:html';
+/// import 'package:over_react/over_react.dart';
+/// import 'package:over_react/react_dom.dart' as react_dom;
///
-/// @Props()
-/// class _$FooProps extends UiProps {
-/// Ref forwardedRef;
-/// }
+/// // ---------- Component Definitions ----------
///
-/// @Component2()
-/// class FooComponent extends UiComponent2 {
-/// @override
-/// render() {
-/// return (Dom.button()
-/// ..ref = props.forwardedRef
-/// )('Click this button');
-/// }
-/// }
+/// final FancyButton = forwardRef((props, ref) {
+/// final classes = ClassNameBuilder.fromProps(props)
+/// ..add('FancyButton');
+///
+/// return (Dom.button()
+/// ..addProps(getPropsToForward(props, onlyCopyDomProps: true))
+/// ..className = classes.toClassName()
+/// ..ref = ref
+/// )('Click me!');
+/// })(Dom.button);
+///
+/// final LogProps = forwardRef((props, ref) {
+/// return (_LogProps()
+/// ..addProps(props)
+/// .._forwardedRef = ref
+/// )('Click me!');
+/// })(_LogProps);
+///
+/// @Factory()
+/// UiFactory _LogProps = _$_LogProps;
+///
+/// @Props()
+/// class _$LogPropsProps extends UiProps {
+/// BuilderOnlyUiFactory builder;
+///
+/// // Private since we only use this to pass along the value of `ref` to
+/// // the return value of forwardRef.
+/// //
+/// // Consumers can set this private field value using the public `ref` setter.
+/// Ref _forwardedRef;
+/// }
+///
+/// @Component2()
+/// class LogPropsComponent extends UiComponent2 {
+/// @override
+/// void componentDidUpdate(Map prevProps, _, [__]) {
+/// print('old props: $prevProps');
+/// print('new props: $props');
+/// }
+///
+/// @override
+/// render() {
+/// return (props.builder()
+/// ..modifyProps(addUnconsumedDomProps)
+/// ..ref = props._forwardedRef
+/// )(props.children);
+/// }
+/// }
+///
+/// // ---------- Component Consumption ----------
+///
+/// void main() {
+/// setClientConfiguration();
+/// final ref = createRef();
+///
+/// react_dom.render(
+/// (LogProps()
+/// ..builder = FancyButton
+/// ..className = 'btn btn-primary'
+/// ..ref = ref
+/// ..onClick = (_) {
+/// print(ref.current.outerHtml);
+/// }
+/// )(),
+/// querySelector('#idOfSomeNodeInTheDom')
+/// );
+///
+/// // You can still get a ref directly to the DOM button:
+/// final buttonNode = ref.current;
+/// }
+/// ```
///
/// Learn more: .
UiFactory Function(UiFactory) forwardRef(
diff --git a/web/component2/index.dart b/web/component2/index.dart
index 686bc9e80..ffd070342 100644
--- a/web/component2/index.dart
+++ b/web/component2/index.dart
@@ -7,6 +7,7 @@ import 'src/demos.dart';
void main() {
setClientConfiguration();
+ final fancyButtonNodeRef = createRef();
react_dom.render(
buttonExamplesDemo(), querySelector('$demoMountNodeSelectorPrefix--button'));
@@ -26,6 +27,18 @@ void main() {
react_dom.render(
radioToggleButtonDemo(), querySelector('$demoMountNodeSelectorPrefix--radio-toggle'));
+ react_dom.render(
+ (LogProps()
+ ..builder = FancyButton
+ ..className = 'btn btn-primary'
+ ..ref = fancyButtonNodeRef
+ ..onClick = (_) {
+ print(fancyButtonNodeRef.current.outerHtml);
+ }
+ )(),
+ querySelector('$demoMountNodeSelectorPrefix--forwardRef'),
+ );
+
react_dom.render(
(ErrorBoundary()
..onComponentDidCatch = (error, info) {
diff --git a/web/component2/index.html b/web/component2/index.html
index 69cc334b3..57cd22da9 100644
--- a/web/component2/index.html
+++ b/web/component2/index.html
@@ -51,6 +51,12 @@ ToggleButton (Radio)
around with the component rendered below.
+
ForwardRef
+
Modify the source of /web/component2/src/demos/forward_ref.dart
to play
+ around with the component rendered below.
+
+