Skip to content

Commit

Permalink
Add support for traversing wrapper components to getProps
Browse files Browse the repository at this point in the history
UIP-1897
  • Loading branch information
jacehensley-wf committed Feb 14, 2017
1 parent 6543e36 commit a16104e
Show file tree
Hide file tree
Showing 5 changed files with 295 additions and 5 deletions.
27 changes: 26 additions & 1 deletion lib/src/util/react_wrappers.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import 'dart:collection';
import 'dart:html';

import 'package:js/js.dart';
import 'package:over_react/src/component_declaration/component_type_checking.dart';
import 'package:react/react.dart' as react;
import 'package:react/react_client.dart';
import 'package:react/react_client/js_interop_helpers.dart';
Expand Down Expand Up @@ -108,10 +109,34 @@ Expando<UnmodifiableMapView> _elementPropsCache = new Expando('_elementPropsCach
/// For a JS component, this returns the result of [getJsProps] in an unmodifiable Map view.
///
/// Throws if [instance] is not a valid [ReactElement] or composite [ReactComponent] .
Map getProps(/* ReactElement|ReactComponent */ instance) {
Map getProps(/* ReactElement|ReactComponent */ instance, {bool traverseWrappers: false}) {
var isCompositeComponent = _isCompositeComponent(instance);

if (isValidElement(instance) || isCompositeComponent) {
if (traverseWrappers) {
ComponentTypeMeta instanceTypeMeta;

if (isCompositeComponent && isDartComponent(instance)) {
var componentType = getComponentTypeFromAlias(getDartComponent(instance).runtimeType);
instanceTypeMeta = componentType == null ? const ComponentTypeMeta.none() : getComponentTypeMeta(componentType);
} else if (isValidElement(instance)) {
instanceTypeMeta = getComponentTypeMeta(instance.type);
} else {
throw new ArgumentError.value(instance, 'instance',
'must either be a Dart component ReactComponent or ReactElement when traverseWrappers is true.');
}

if (instanceTypeMeta.isWrapper) {
assert(isDartComponent(instance) && 'Non-dart components should not be wrappers' is String);

List children = getProps(instance)['children'];

if (children != null && children.isNotEmpty && isValidElement(children.first)) {
return getProps(children.first, traverseWrappers: true);
}
}
}

if (!isCompositeComponent) {
var cachedView = _elementPropsCache[instance];
if (cachedView != null) return cachedView;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ import 'package:react/react_client.dart';
import 'package:react/react_client/react_interop.dart';
import 'package:test/test.dart';

import 'component_type_checking_test/one_level_wrapper.dart';
import '../../test_util/one_level_wrapper.dart';
import '../../test_util/two_level_wrapper.dart';
import 'component_type_checking_test/test_a.dart';
import 'component_type_checking_test/test_b.dart';
import 'component_type_checking_test/two_level_wrapper.dart';
import 'component_type_checking_test/type_inheritance/abstract_inheritance/abstract.dart';
import 'component_type_checking_test/type_inheritance/abstract_inheritance/extendedtype.dart';
import 'component_type_checking_test/type_inheritance/parent.dart';
Expand Down
265 changes: 265 additions & 0 deletions test/over_react/util/react_wrappers_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ import 'package:react/react_dom.dart' as react_dom;
import 'package:react/react_test_utils.dart' as react_test_utils;
import 'package:test/test.dart';

import '../../test_util/one_level_wrapper.dart';
import '../../test_util/test_util.dart';
import '../../test_util/two_level_wrapper.dart';
import '../../wsd_test_util/test_js_component.dart';

/// Main entry point for react wrappers testing
Expand Down Expand Up @@ -574,6 +576,264 @@ main() {
}));
});

group('traverses children of Wrapper components', () {
group('and retruns props for a', () {
group('composite JS ReactComponent', () {
test('', () {
ReactComponent renderedInstance = render(OneLevelWrapper()(
testJsComponentFactory({
'jsProp': 'js',
'style': testStyle,
}, testChildren)
));

expect(getProps(renderedInstance, traverseWrappers: true), equals({
'jsProp': 'js',
'style': testStyle,
'children': testChildren
}));
});

test('even when there are multiple levels of wrappers', () {
ReactComponent renderedInstance = render(TwoLevelWrapper()(
OneLevelWrapper()(
testJsComponentFactory({
'jsProp': 'js',
'style': testStyle,
}, testChildren)
)
));

expect(getProps(renderedInstance, traverseWrappers: true), equals({
'jsProp': 'js',
'style': testStyle,
'children': testChildren
}));
});

test('even when props change', () {
var mountNode = new DivElement();
ReactComponent renderedInstance = react_dom.render(OneLevelWrapper()(
testJsComponentFactory({
'jsProp': 'js',
'style': testStyle,
}, testChildren)
), mountNode);

expect(getProps(renderedInstance, traverseWrappers: true), equals({
'jsProp': 'js',
'style': testStyle,
'children': testChildren
}));

renderedInstance = react_dom.render(OneLevelWrapper()(
testJsComponentFactory({
'jsProp': 'other js',
'style': testStyle,
}, testChildren)
), mountNode);

expect(getProps(renderedInstance, traverseWrappers: true), equals({
'jsProp': 'other js',
'style': testStyle,
'children': testChildren
}));
});

test('except when traversWrappers is false', () {
ReactComponent renderedInstance = render(OneLevelWrapper()(
testJsComponentFactory({
'jsProp': 'js',
'style': testStyle,
}, testChildren)
));

expect(getProps(renderedInstance), isNot({
'jsProp': 'js',
'style': testStyle,
'children': testChildren
}));
});
});

group('DOM component ReactElement', () {
test('', () {
ReactElement instance = OneLevelWrapper()(
(Dom.div()
..addProp('domProp', 'dom')
..style = testStyle
)(testChildren)
);

expect(getProps(instance, traverseWrappers: true), equals({
'domProp': 'dom',
'style': testStyle,
'children': testChildren
}));
});

test('even when there are multiple levels of wrappers', () {
ReactElement instance = TwoLevelWrapper()(
OneLevelWrapper()(
(Dom.div()
..addProp('domProp', 'dom')
..style = testStyle
)(testChildren)
)
);

expect(getProps(instance, traverseWrappers: true), equals({
'domProp': 'dom',
'style': testStyle,
'children': testChildren
}));
});

test('except when traversWrappers is false', () {
ReactElement instance = OneLevelWrapper()(
(Dom.div()
..addProp('domProp', 'dom')
..style = testStyle
)(testChildren)
);

expect(getProps(instance), isNot({
'domProp': 'dom',
'style': testStyle,
'children': testChildren
}));
});
});

group('Dart component ReactElement', () {
test('', () {
ReactElement instance = OneLevelWrapper()(
TestComponentFactory({
'dartProp': 'dart',
'style': testStyle,
}, testChildren)
);

expect(getProps(instance, traverseWrappers: true), equals({
'dartProp': 'dart',
'style': testStyle,
'children': testChildren
}));
});

test('even when there are multiple levels of wrappers', () {
ReactElement instance = TwoLevelWrapper()(
OneLevelWrapper()(
TestComponentFactory({
'dartProp': 'dart',
'style': testStyle,
}, testChildren)
)
);

expect(getProps(instance, traverseWrappers: true), equals({
'dartProp': 'dart',
'style': testStyle,
'children': testChildren
}));
});

test('except when traversWrappers is false', () {
ReactElement instance = OneLevelWrapper()(
TestComponentFactory({
'dartProp': 'dart',
'style': testStyle,
}, testChildren)
);

expect(getProps(instance), isNot({
'dartProp': 'dart',
'style': testStyle,
'children': testChildren
}));
});
});

group('Dart component ReactComponent', () {
test('', () {
ReactComponent renderedInstance = render(OneLevelWrapper()(
TestComponentFactory({
'jsProp': 'js',
'style': testStyle,
}, testChildren)
));

expect(getProps(renderedInstance, traverseWrappers: true), equals({
'jsProp': 'js',
'style': testStyle,
'children': testChildren
}));
});

test('even when there are multiple levels of wrappers', () {
ReactComponent renderedInstance = render(TwoLevelWrapper()(
OneLevelWrapper()(
TestComponentFactory({
'jsProp': 'js',
'style': testStyle,
}, testChildren)
)
));

expect(getProps(renderedInstance, traverseWrappers: true), equals({
'jsProp': 'js',
'style': testStyle,
'children': testChildren
}));
});

test('even when props change', () {
var mountNode = new DivElement();
ReactComponent renderedInstance = react_dom.render(OneLevelWrapper()(
TestComponentFactory({
'jsProp': 'js',
'style': testStyle,
}, testChildren)
), mountNode);

expect(getProps(renderedInstance, traverseWrappers: true), equals({
'jsProp': 'js',
'style': testStyle,
'children': testChildren
}));

renderedInstance = react_dom.render(OneLevelWrapper()(
TestComponentFactory({
'jsProp': 'other js',
'style': testStyle,
}, testChildren)
), mountNode);

expect(getProps(renderedInstance, traverseWrappers: true), equals({
'jsProp': 'other js',
'style': testStyle,
'children': testChildren
}));
});

test('except when traverseWrapers is false', () {
ReactComponent renderedInstance = render(OneLevelWrapper()(
TestComponentFactory({
'jsProp': 'js',
'style': testStyle,
}, testChildren)
));

expect(getProps(renderedInstance), isNot({
'jsProp': 'other js',
'style': testStyle,
'children': testChildren
}));
});
});
});
});

test('returns props as an unmodifiable map', () {
ReactComponent renderedInstance = render(TestComponentFactory({
'dartProp': 'dart'
Expand All @@ -583,6 +843,11 @@ main() {
});

group('throws when passed', () {
test('a JS ReactComponent and traverseWrappers is true', () {
var renderedInstance = render(testJsComponentFactory({}));
expect(() => getProps(renderedInstance, traverseWrappers: true), throwsArgumentError);
});

test('a DOM ReactComponent (Element)', () {
var renderedInstance = render(Dom.div());
expect(() => getProps(renderedInstance), throwsArgumentError);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

library test_components.one_level_wrapper;
library test_util.one_level_wrapper;

import 'package:over_react/over_react.dart';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

library test_components.two_level_wrapper;
library test_util.two_level_wrapper;

import 'package:over_react/over_react.dart';

Expand Down

0 comments on commit a16104e

Please sign in to comment.