Skip to content

Commit

Permalink
Expose react_dom.render and react_dom.unmountComponentAtNode
Browse files Browse the repository at this point in the history
  • Loading branch information
aaronlademann-wf committed May 4, 2018
1 parent e15dd2e commit 2c6af28
Show file tree
Hide file tree
Showing 20 changed files with 357 additions and 29 deletions.
21 changes: 5 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,14 +81,16 @@ and add an HTML element with a unique identifier where you’ll mount your OverR
> __Note:__ When serving your application in production, use `packages/react/react_with_react_dom_prod.js`
file instead of the un-minified `react.js` / `react_dom.js` files shown in the example above.

4. Import the `over_react` library _(and associated react libraries)_ into `your_app_name.dart`, and initialize
4. Import the `over_react` and `react_dom` libraries into `your_app_name.dart`, and initialize
React within your Dart application. Then [build a custom component](#building-custom-components) and
mount / render it into the HTML element you created in step 3.

> Be sure to namespace the `react_dom.dart` import as `react_dom` to avoid collisions with `UiComponent.render`
when [creating custom components](#building-custom-components).

```dart
import 'dart:html';
import 'package:react/react.dart' as react;
import 'package:react/react_dom.dart' as react_dom;
import 'package:over_react/react_dom.dart' as react_dom;
import 'package:over_react/over_react.dart';
main() {
Expand Down Expand Up @@ -680,10 +682,6 @@ that you get for free from OverReact, you're ready to start building your own cu
* #### Component Boilerplate

```dart
import 'dart:html';
import 'package:react/react.dart' as react;
import 'package:react/react_dom.dart' as react_dom;
import 'package:react/react_client.dart';
import 'package:over_react/over_react.dart';
@Factory()
Expand Down Expand Up @@ -717,9 +715,6 @@ that you get for free from OverReact, you're ready to start building your own cu

```dart
import 'dart:html';
import 'package:react/react.dart' as react;
import 'package:react/react_dom.dart' as react_dom;
import 'package:react/react_client.dart';
import 'package:over_react/over_react.dart';
@Factory()
Expand Down Expand Up @@ -765,9 +760,6 @@ that you get for free from OverReact, you're ready to start building your own cu

```dart
import 'dart:html';
import 'package:react/react.dart' as react;
import 'package:react/react_dom.dart' as react_dom;
import 'package:react/react_client.dart';
import 'package:over_react/over_react.dart';
@Factory()
Expand Down Expand Up @@ -798,9 +790,6 @@ that you get for free from OverReact, you're ready to start building your own cu

```dart
import 'dart:html';
import 'package:react/react.dart' as react;
import 'package:react/react_dom.dart' as react_dom;
import 'package:react/react_client.dart';
import 'package:over_react/over_react.dart';
@Factory()
Expand Down
2 changes: 1 addition & 1 deletion integrate/web/test_import.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

import 'dart:html';

import 'package:react/react_dom.dart' as react_dom;
import 'package:over_react/react_dom.dart' as react_dom;
import 'package:react/react_client.dart' show setClientConfiguration;

import 'package:over_react/over_react.dart';
Expand Down
58 changes: 58 additions & 0 deletions lib/react_dom.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// Copyright 2018 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.

/// Exposes a typed version of `react_dom.render` and `react_dom.unmountComponentAtNode`
/// from the [`react` package](https://pub.dartlang.org/packages/react) so that `over_react` consumers
/// do not have to declare a direct dependency on the `react` package in their `pubspec.yaml`.
///
/// This allows us to insulate our consumers from "breaking" internal changes in the `react` package.
///
/// > __It is strongly suggested that consumers namespace when importing this library to avoid
/// collisions when creating custom `UiComponent`s.__
///
/// import 'package:over_react/react_dom.dart' as react_dom;
/// import 'package:over_react/over_react.dart';
library over_react.react_dom;

import 'dart:html';

import 'package:over_react/over_react.dart';
import 'package:react/react_dom.dart' as react_dom show render, unmountComponentAtNode;

/// Renders the provided [vDomComponent] into the DOM in the provided [mountNode]
/// and returns a reference to it based on its type:
///
/// 1. Returns an [Element] if [vDomComponent] is a [Dom] component _(e.g. [Dom.div])_.
/// 2. Returns a React `Component` if [vDomComponent] is a composite component.
/// 3. Returns `null` if [vDomComponent] is null, or if [vDomComponent] is a stateless component.
///
/// > If the [vDomComponent] was previously rendered into the [mountNode], this will perform an update on it and only
/// mutate the DOM as necessary to reflect the latest React component.
///
/// > Use [unmountComponentAtNode] to unmount the instance.
///
/// > Proxies [react_dom.render].
dynamic render(ReactElement vDomComponent, Element mountNode) {
if (vDomComponent == null) return null;

return react_dom.render(vDomComponent, mountNode);
}

/// Removes a React `Component` that was mounted via [render] from the DOM
/// and cleans up its event handlers and state.
///
/// > Returns `false` if no `Component` was mounted in the [mountNode] specified via [render], otherwise returns `true`.
bool unmountComponentAtNode(Element mountNode) {
return react_dom.unmountComponentAtNode(mountNode);
}
2 changes: 1 addition & 1 deletion lib/src/util/rem_util.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import 'dart:html';

import 'package:over_react/over_react.dart';
import 'package:over_react/src/util/css_value_util.dart';
import 'package:react/react_dom.dart' as react_dom;
import 'package:over_react/react_dom.dart' as react_dom;

double _computeRootFontSize() {
return new CssValue.parse(document.documentElement.getComputedStyle().fontSize).number.toDouble();
Expand Down
2 changes: 1 addition & 1 deletion test/over_react/component/resize_sensor_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import 'dart:html';
import 'package:platform_detect/platform_detect.dart';
import 'package:over_react/over_react.dart';
import 'package:over_react_test/over_react_test.dart';
import 'package:react/react_dom.dart' as react_dom;
import 'package:over_react/react_dom.dart' as react_dom;
import 'package:test/test.dart';

void main() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ library over_react.component_declaration.transformer_integration_tests.constant_
import 'dart:html';

import 'package:over_react/over_react.dart';
import 'package:react/react_dom.dart' as react_dom;
import 'package:over_react/react_dom.dart' as react_dom;
import 'package:test/test.dart';

import '../../../test_util/test_util.dart';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ library over_react.component_declaration.transformer_integration_tests.required_
import 'dart:html';

import 'package:over_react/over_react.dart';
import 'package:react/react_dom.dart' as react_dom;
import 'package:over_react/react_dom.dart' as react_dom;
import 'package:test/test.dart';

import '../../../test_util/test_util.dart';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ library over_react.component_declaration.transformer_integration_tests.required_
import 'dart:html';

import 'package:over_react/over_react.dart';
import 'package:react/react_dom.dart' as react_dom;
import 'package:over_react/react_dom.dart' as react_dom;
import 'package:test/test.dart';

import '../../../test_util/test_util.dart';
Expand Down
42 changes: 42 additions & 0 deletions test/over_react/dom/fixtures/dummy_composite_component.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import 'package:over_react/over_react.dart';

@Factory()
UiFactory<TestCompositeComponentProps> TestCompositeComponent;

@Props()
class TestCompositeComponentProps extends UiProps {
Function onComponentDidMount;
Function onComponentWillUnmount;
Function onComponentDidUpdate;
}

@Component()
class TestCompositeComponentComponent extends UiComponent<TestCompositeComponentProps> {
@override
void componentDidMount() {
if (props.onComponentDidMount != null) {
props.onComponentDidMount();
}
}

@override
void componentDidUpdate(_, __) {
if (props.onComponentDidUpdate != null) {
props.onComponentDidUpdate();
}
}

@override
void componentWillUnmount() {
super.componentWillUnmount();

if (props.onComponentWillUnmount != null) {
props.onComponentWillUnmount();
}
}

@override
render() {
return Dom.div()('oh hai');
}
}
100 changes: 100 additions & 0 deletions test/over_react/dom/render_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// Copyright 2018 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 render_test;

import 'dart:html';

import 'package:over_react/over_react.dart';
import 'package:over_react/react_dom.dart' as react_dom;
import 'package:test/test.dart';

import 'fixtures/dummy_composite_component.dart';

main() {
group('`react_dom.render`', () {
var renderedInstance;
Element mountNode;

setUp(() {
mountNode = new DivElement();
document.body.append(mountNode);
});

tearDown(() {
mountNode.remove();
mountNode = null;
renderedInstance = null;
});

group('mounts and renders', () {
group('a composite component into the DOM', () {
int componentDidMountCount;
int componentDidUpdateCount;

setUp(() {
componentDidMountCount = 0;
componentDidUpdateCount = 0;

renderedInstance = react_dom.render((TestCompositeComponent()
..onComponentDidMount = () { componentDidMountCount++; }
..onComponentDidUpdate = () { componentDidUpdateCount++; }
)(), mountNode);
});

test('', () {
expect(componentDidMountCount, 1);
});

test('and returns a `Component` reference', () {
expect(renderedInstance, isNotNull);
});

test('within the provided `mountNode`', () {
expect(findDomNode(renderedInstance), mountNode.children.single);
});

test('and re-renders it when `react_dom.render` is called again for the same mountNode', () {
react_dom.render((TestCompositeComponent()
..onComponentDidMount = () { componentDidMountCount++; }
..onComponentDidUpdate = () { componentDidUpdateCount++; }
)(), mountNode);

expect(componentDidMountCount, 1);
expect(componentDidUpdateCount, 1);
});
});

group('a Dom component into the DOM', () {
setUp(() {
renderedInstance = react_dom.render(Dom.div()('oh hai'), mountNode);
});

test('and returns an `Element` reference', () {
expect(renderedInstance, const isInstanceOf<Element>());
});

test('within the provided `mountNode`', () {
expect(renderedInstance, mountNode.children.single);
});
});
});

test('returns null when null is provided', () {
renderedInstance = react_dom.render(null, mountNode);

expect(mountNode.children, isEmpty);
});
});
}
74 changes: 74 additions & 0 deletions test/over_react/dom/unmount_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// Copyright 2018 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 unmount_test;

import 'dart:html';

import 'package:over_react/react_dom.dart' as react_dom;
import 'package:test/test.dart';

import 'fixtures/dummy_composite_component.dart';

main() {
group('`react_dom.unmountComponentAtNode`', () {
Element mountNode;
bool unmountComponentAtNodeReturnValue;

setUp(() {
mountNode = new DivElement();
document.body.append(mountNode);
});

tearDown(() {
mountNode.remove();
mountNode = null;
});

group('when called on a mountNode that has a mounted component:', () {
int componentDidMountCount;
bool componentWillUnmount;

setUp(() {
componentDidMountCount = 0;
componentWillUnmount = false;

react_dom.render((TestCompositeComponent()
..onComponentDidMount = () { componentDidMountCount++; }
..onComponentWillUnmount = () { componentWillUnmount = true; }
)(), mountNode);

expect(componentDidMountCount, 1, reason: 'test setup sanity check');

unmountComponentAtNodeReturnValue = react_dom.unmountComponentAtNode(mountNode);
});

test('unmounts the component', () {
expect(componentWillUnmount, isTrue);
});

test('removes the rendered component from the DOM', () {
expect(mountNode.children, isEmpty);
});

test('returns true', () {
expect(unmountComponentAtNodeReturnValue, isTrue);
});
});

test('returns false when called on a mountNode that does not have a mounted component', () {
expect(react_dom.unmountComponentAtNode(mountNode), isFalse);
});
});
}
Loading

0 comments on commit 2c6af28

Please sign in to comment.