diff --git a/package.json b/package.json
index eafce2e7..521e3045 100644
--- a/package.json
+++ b/package.json
@@ -33,6 +33,7 @@
"@babel/preset-env": "^7.2.3",
"@babel/preset-flow": "^7.0.0",
"@babel/preset-react": "^7.0.0",
+ "@testing-library/react": "^8.0.5",
"babel-eslint": "^10.0.0",
"babel-jest": "24.0.0-alpha.9",
"babel-loader": "^8.0.0",
diff --git a/packages/react-native-web/src/exports/Image/__tests__/__snapshots__/index-test.js.snap b/packages/react-native-web/src/exports/Image/__tests__/__snapshots__/index-test.js.snap
index 8ce8ae13..e5999cd9 100644
--- a/packages/react-native-web/src/exports/Image/__tests__/__snapshots__/index-test.js.snap
+++ b/packages/react-native-web/src/exports/Image/__tests__/__snapshots__/index-test.js.snap
@@ -1,23 +1,406 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
-exports[`components/Image prop "blurRadius" 1`] = `"blur(5px)"`;
+exports[`components/Image prop "accessibilityLabel" 1`] = `
+
+
+
+
+`;
-exports[`components/Image prop "defaultSource" sets background image when value is a string 1`] = `"url(\\"https://google.com/favicon.ico\\")"`;
+exports[`components/Image prop "accessible" 1`] = `
+
+`;
-exports[`components/Image prop "defaultSource" sets background image when value is an object 1`] = `"url(\\"https://google.com/favicon.ico\\")"`;
+exports[`components/Image prop "blurRadius" 1`] = `
+
+
+
+
+`;
-exports[`components/Image prop "resizeMode" value "contain" 1`] = `"contain"`;
+exports[`components/Image prop "defaultSource" does not override "height" and "width" styles 1`] = `
+
+
+
+
+`;
-exports[`components/Image prop "resizeMode" value "cover" 1`] = `"cover"`;
+exports[`components/Image prop "defaultSource" sets "height" and "width" styles if missing 1`] = `
+
+
+
+
+`;
-exports[`components/Image prop "resizeMode" value "none" 1`] = `"auto"`;
+exports[`components/Image prop "defaultSource" sets background image when value is a string 1`] = `
+
+
+
+
+`;
-exports[`components/Image prop "resizeMode" value "repeat" 1`] = `"auto"`;
+exports[`components/Image prop "defaultSource" sets background image when value is an object 1`] = `
+
+
+
+
+`;
-exports[`components/Image prop "resizeMode" value "stretch" 1`] = `"100% 100%"`;
+exports[`components/Image prop "draggable" 1`] = `
+
+
+
+
+`;
-exports[`components/Image prop "resizeMode" value "undefined" 1`] = `"cover"`;
+exports[`components/Image prop "resizeMode" value "contain" 1`] = `
+
+`;
-exports[`components/Image prop "style" supports "resizeMode" property 1`] = `"contain"`;
+exports[`components/Image prop "resizeMode" value "cover" 1`] = `
+
+`;
-exports[`components/Image prop "style" supports "shadow" properties (convert to filter) 1`] = `"drop-shadow(1px 1px 0px rgba(255,0,0,1.00))"`;
+exports[`components/Image prop "resizeMode" value "none" 1`] = `
+
+`;
+
+exports[`components/Image prop "resizeMode" value "repeat" 1`] = `
+
+`;
+
+exports[`components/Image prop "resizeMode" value "stretch" 1`] = `
+
+`;
+
+exports[`components/Image prop "resizeMode" value "undefined" 1`] = `
+
+`;
+
+exports[`components/Image prop "source" is correctly updated only when loaded if defaultSource provided 1`] = `
+
+
+
+
+`;
+
+exports[`components/Image prop "source" is correctly updated only when loaded if defaultSource provided 2`] = `
+
+
+
+
+`;
+
+exports[`components/Image prop "source" is correctly updated when missing in initial render 1`] = `
+
+
+
+
+`;
+
+exports[`components/Image prop "source" is not set immediately if the image has not already been loaded 1`] = `
+
+
+
+
+`;
+
+exports[`components/Image prop "source" is set immediately if the image has already been loaded 1`] = `
+
+
+
+
+`;
+
+exports[`components/Image prop "source" is set immediately if the image has already been loaded 2`] = `
+
+
+
+
+`;
+
+exports[`components/Image prop "source" is set immediately if the image was preloaded 1`] = `
+
+
+
+
+`;
+
+exports[`components/Image prop "style" removes other unsupported View styles 1`] = `
+
+`;
+
+exports[`components/Image prop "style" supports "resizeMode" property 1`] = `
+
+`;
+
+exports[`components/Image prop "style" supports "shadow" properties (convert to filter) 1`] = `
+
+`;
+
+exports[`components/Image prop "style" supports "tintcolor" property (convert to filter) 1`] = `
+
+
+
+
+
+
+
+
+
+
+
+
+`;
+
+exports[`components/Image prop "testID" 1`] = `
+
+`;
diff --git a/packages/react-native-web/src/exports/Image/__tests__/index-test.js b/packages/react-native-web/src/exports/Image/__tests__/index-test.js
index b9dfa9ae..23e3b723 100644
--- a/packages/react-native-web/src/exports/Image/__tests__/index-test.js
+++ b/packages/react-native-web/src/exports/Image/__tests__/index-test.js
@@ -5,13 +5,10 @@ import Image from '../';
import ImageLoader from '../../../modules/ImageLoader';
import ImageUriCache from '../ImageUriCache';
import React from 'react';
-import { shallow } from 'enzyme';
-import StyleSheet from '../../StyleSheet';
+import { render } from '@testing-library/react';
const originalImage = window.Image;
-const findImageSurfaceStyle = wrapper => StyleSheet.flatten(wrapper.childAt(0).prop('style'));
-
describe('components/Image', () => {
beforeEach(() => {
ImageUriCache._entries = {};
@@ -24,37 +21,35 @@ describe('components/Image', () => {
test('prop "accessibilityLabel"', () => {
const defaultSource = { uri: 'https://google.com/favicon.ico' };
- const component = shallow(
+ const { container } = render(
);
- const img = component.find('img');
- expect(component.prop('accessibilityLabel')).toBe('accessibilityLabel');
- expect(img.prop('alt')).toBe('accessibilityLabel');
+ expect(container.firstChild).toMatchSnapshot();
});
test('prop "accessible"', () => {
- const component = shallow( );
- expect(component.prop('accessible')).toBe(false);
+ const { container } = render( );
+ expect(container.firstChild).toMatchSnapshot();
});
test('prop "blurRadius"', () => {
const defaultSource = { uri: 'https://google.com/favicon.ico' };
- const component = shallow( );
- expect(findImageSurfaceStyle(component).filter).toMatchSnapshot();
+ const { container } = render( );
+ expect(container.firstChild).toMatchSnapshot();
});
describe('prop "defaultSource"', () => {
test('sets background image when value is an object', () => {
const defaultSource = { uri: 'https://google.com/favicon.ico' };
- const component = shallow( );
- expect(findImageSurfaceStyle(component).backgroundImage).toMatchSnapshot();
+ const { container } = render( );
+ expect(container.firstChild).toMatchSnapshot();
});
test('sets background image when value is a string', () => {
// emulate require-ed asset
const defaultSource = 'https://google.com/favicon.ico';
- const component = shallow( );
- expect(findImageSurfaceStyle(component).backgroundImage).toMatchSnapshot();
+ const { container } = render( );
+ expect(container.firstChild).toMatchSnapshot();
});
test('sets "height" and "width" styles if missing', () => {
@@ -63,10 +58,8 @@ describe('components/Image', () => {
height: 10,
width: 20
};
- const component = shallow( );
- const { height, width } = StyleSheet.flatten(component.prop('style'));
- expect(height).toBe(10);
- expect(width).toBe(20);
+ const { container } = render( );
+ expect(container.firstChild).toMatchSnapshot();
});
test('does not override "height" and "width" styles', () => {
@@ -75,21 +68,17 @@ describe('components/Image', () => {
height: 10,
width: 20
};
- const component = shallow(
+ const { container } = render(
);
- const { height, width } = StyleSheet.flatten(component.prop('style'));
- expect(height).toBe(20);
- expect(width).toBe(40);
+ expect(container.firstChild).toMatchSnapshot();
});
});
test('prop "draggable"', () => {
const defaultSource = { uri: 'https://google.com/favicon.ico' };
- const component = shallow( );
- expect(component.find('img').prop('draggable')).toBe(false);
- component.setProps({ defaultSource, draggable: true });
- expect(component.find('img').prop('draggable')).toBe(true);
+ const { container } = render( );
+ expect(container.firstChild).toMatchSnapshot();
});
describe('prop "onLoad"', () => {
@@ -99,7 +88,7 @@ describe('components/Image', () => {
onLoad();
});
const onLoadStub = jest.fn();
- shallow( );
+ render( );
jest.runOnlyPendingTimers();
expect(ImageLoader.load).toBeCalled();
expect(onLoadStub).toBeCalled();
@@ -113,7 +102,7 @@ describe('components/Image', () => {
const onLoadStub = jest.fn();
const uri = 'https://test.com/img.jpg';
ImageUriCache.add(uri);
- shallow( );
+ render( );
jest.runOnlyPendingTimers();
expect(ImageLoader.load).not.toBeCalled();
expect(onLoadStub).toBeCalled();
@@ -122,17 +111,19 @@ describe('components/Image', () => {
test('is called on update if "uri" is different', () => {
const onLoadStub = jest.fn();
- const uri = 'https://test.com/img.jpg';
- const component = shallow( );
- component.setProps({ source: 'https://blah.com/img.png' });
+ const { rerender } = render(
+
+ );
+ rerender( );
expect(onLoadStub.mock.calls.length).toBe(2);
});
test('is not called on update if "uri" is the same', () => {
const onLoadStub = jest.fn();
- const uri = 'https://test.com/img.jpg';
- const component = shallow( );
- component.setProps({ resizeMode: 'stretch' });
+ const { rerender } = render(
+
+ );
+ rerender( );
expect(onLoadStub.mock.calls.length).toBe(1);
});
});
@@ -140,8 +131,8 @@ describe('components/Image', () => {
describe('prop "resizeMode"', () => {
['contain', 'cover', 'none', 'repeat', 'stretch', undefined].forEach(resizeMode => {
test(`value "${resizeMode}"`, () => {
- const component = shallow( );
- expect(findImageSurfaceStyle(component).backgroundSize).toMatchSnapshot();
+ const { container } = render( );
+ expect(container.firstChild).toMatchSnapshot();
});
});
});
@@ -150,15 +141,15 @@ describe('components/Image', () => {
test('does not throw', () => {
const sources = [null, '', {}, { uri: '' }, { uri: 'https://google.com' }];
sources.forEach(source => {
- expect(() => shallow( )).not.toThrow();
+ expect(() => render( )).not.toThrow();
});
});
test('is not set immediately if the image has not already been loaded', () => {
const uri = 'https://google.com/favicon.ico';
const source = { uri };
- const component = shallow( );
- expect(component.find('img')).toBeUndefined;
+ const { container } = render( );
+ expect(container.firstChild).toMatchSnapshot();
});
test('is set immediately if the image was preloaded', () => {
@@ -168,8 +159,8 @@ describe('components/Image', () => {
});
return Image.prefetch(uri).then(() => {
const source = { uri };
- const component = shallow( , { disableLifecycleMethods: true });
- expect(component.find('img').prop('src')).toBe(uri);
+ const { container } = render( , { disableLifecycleMethods: true });
+ expect(container.firstChild).toMatchSnapshot();
ImageUriCache.remove(uri);
});
});
@@ -181,36 +172,20 @@ describe('components/Image', () => {
ImageUriCache.add(uriTwo);
// initial render
- const component = shallow( );
+ const { container, rerender } = render( );
ImageUriCache.remove(uriOne);
- expect(
- component
- .render()
- .find('img')
- .attr('src')
- ).toBe(uriOne);
-
+ expect(container.firstChild).toMatchSnapshot();
// props update
- component.setProps({ source: { uri: uriTwo } });
+ rerender( );
ImageUriCache.remove(uriTwo);
- expect(
- component
- .render()
- .find('img')
- .attr('src')
- ).toBe(uriTwo);
+ expect(container.firstChild).toMatchSnapshot();
});
test('is correctly updated when missing in initial render', () => {
const uri = 'https://testing.com/img.jpg';
- const component = shallow( );
- component.setProps({ source: { uri } });
- expect(
- component
- .render()
- .find('img')
- .attr('src')
- ).toBe(uri);
+ const { container, rerender } = render( );
+ rerender( );
+ expect(container.firstChild).toMatchSnapshot();
});
test('is correctly updated only when loaded if defaultSource provided', () => {
@@ -220,53 +195,43 @@ describe('components/Image', () => {
ImageLoader.load = jest.fn().mockImplementationOnce((_, onLoad, onError) => {
loadCallback = onLoad;
});
- const component = shallow( );
- expect(component.find('img').prop('src')).toBe(defaultUri);
+ const { container } = render( );
+ expect(container.firstChild).toMatchSnapshot();
loadCallback();
- expect(component.find('img').prop('src')).toBe(uri);
+ expect(container.firstChild).toMatchSnapshot();
});
});
describe('prop "style"', () => {
test('supports "resizeMode" property', () => {
- const component = shallow( );
- expect(findImageSurfaceStyle(component).backgroundSize).toMatchSnapshot();
+ const { container } = render( );
+ expect(container.firstChild).toMatchSnapshot();
});
test('supports "shadow" properties (convert to filter)', () => {
- const component = shallow(
+ const { container } = render(
);
- expect(findImageSurfaceStyle(component).filter).toMatchSnapshot();
+ expect(container.firstChild).toMatchSnapshot();
});
test('supports "tintcolor" property (convert to filter)', () => {
const defaultSource = { uri: 'https://google.com/favicon.ico' };
- const component = shallow(
+ const { container } = render(
);
- // filter
- expect(findImageSurfaceStyle(component).filter).toContain('url(#tint-');
- // svg
- expect(component.childAt(2).type()).toBe('svg');
+ expect(container.firstChild).toMatchSnapshot();
});
test('removes other unsupported View styles', () => {
- const component = shallow( );
- expect(component.props().style.overlayColor).toBeUndefined();
- expect(component.props().style.tintColor).toBeUndefined();
+ const { container } = render( );
+ expect(container.firstChild).toMatchSnapshot();
});
});
test('prop "testID"', () => {
- const component = shallow( );
- expect(component.prop('testID')).toBe('testID');
- });
-
- test('passes other props through to underlying View', () => {
- const fn = () => {};
- const component = shallow( );
- expect(component.prop('onResponderGrant')).toBe(fn);
+ const { container } = render( );
+ expect(container.firstChild).toMatchSnapshot();
});
test('queryCache', () => {
diff --git a/packages/react-native-web/src/exports/Image/index.js b/packages/react-native-web/src/exports/Image/index.js
index 2262a68e..dada392b 100644
--- a/packages/react-native-web/src/exports/Image/index.js
+++ b/packages/react-native-web/src/exports/Image/index.js
@@ -20,6 +20,7 @@ import ImageStylePropTypes from './ImageStylePropTypes';
import ImageUriCache from './ImageUriCache';
import StyleSheet from '../StyleSheet';
import StyleSheetPropType from '../../modules/StyleSheetPropType';
+import TextAncestorContext from '../Text/TextAncestorContext';
import View from '../View';
import ViewPropTypes from '../ViewPropTypes';
import { bool, func, number, oneOf, shape } from 'prop-types';
@@ -97,10 +98,6 @@ type State = {
class Image extends Component<*, State> {
static displayName = 'Image';
- static contextTypes = {
- isInAParentText: bool
- };
-
static propTypes = {
...ViewPropTypes,
blurRadius: number,
@@ -198,7 +195,7 @@ class Image extends Component<*, State> {
this._isMounted = false;
}
- render() {
+ renderImage(hasTextAncestor) {
const { shouldDisplaySource } = this.state;
const {
accessibilityLabel,
@@ -286,12 +283,7 @@ class Image extends Component<*, State> {
accessibilityLabel={accessibilityLabel}
accessible={accessible}
onLayout={this._createLayoutHandler(finalResizeMode)}
- style={[
- styles.root,
- this.context.isInAParentText && styles.inline,
- imageSizeStyle,
- flatStyle
- ]}
+ style={[styles.root, hasTextAncestor && styles.inline, imageSizeStyle, flatStyle]}
testID={testID}
>
{
);
}
+ render() {
+ return (
+
+ {hasTextAncestor => this.renderImage(hasTextAncestor)}
+
+ );
+ }
+
_createImageLoader() {
const { source } = this.props;
this._destroyImageLoader();
diff --git a/packages/react-native-web/src/exports/Text/TextAncestorContext.js b/packages/react-native-web/src/exports/Text/TextAncestorContext.js
new file mode 100644
index 00000000..020069de
--- /dev/null
+++ b/packages/react-native-web/src/exports/Text/TextAncestorContext.js
@@ -0,0 +1,12 @@
+/**
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @flow strict
+ */
+
+import * as React from 'react';
+const TextAncestorContext = React.createContext(false);
+export default (TextAncestorContext: React.Context);
diff --git a/packages/react-native-web/src/exports/Text/__tests__/__snapshots__/index-test.js.snap b/packages/react-native-web/src/exports/Text/__tests__/__snapshots__/index-test.js.snap
new file mode 100644
index 00000000..47271731
--- /dev/null
+++ b/packages/react-native-web/src/exports/Text/__tests__/__snapshots__/index-test.js.snap
@@ -0,0 +1,28 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`components/Text allows "dir" to be overridden 1`] = `
+
+`;
+
+exports[`components/Text default 1`] = `
+
+`;
+
+exports[`components/Text nested 1`] = `
+
+
+
+`;
diff --git a/packages/react-native-web/src/exports/Text/__tests__/index-test.js b/packages/react-native-web/src/exports/Text/__tests__/index-test.js
index 291f3085..4d7957d0 100644
--- a/packages/react-native-web/src/exports/Text/__tests__/index-test.js
+++ b/packages/react-native-web/src/exports/Text/__tests__/index-test.js
@@ -2,36 +2,23 @@
/* eslint-disable react/jsx-no-bind */
import React from 'react';
-import { render, shallow } from 'enzyme';
+import { render } from '@testing-library/react';
import Text from '../';
describe('components/Text', () => {
- describe('rendered element', () => {
- test('is a "div" by default', () => {
- const component = shallow( );
- expect(component.type()).toBe('div');
- });
-
- test('is a "span" when inside ', () => {
- const component = render( } />);
- expect(component.find('span').length).toEqual(1);
- });
-
- test('includes dir="auto" prop', () => {
- const component = shallow( );
- expect(component.prop('dir')).toBe('auto');
- });
-
- test('allows "dir" to be overridden', () => {
- const component = shallow( );
- expect(component.prop('dir')).toBe('rtl');
- });
+ test('default', () => {
+ const { container } = render( );
+ expect(container.firstChild).toMatchSnapshot();
});
- test('prop "children"', () => {
- const children = ;
- const component = shallow( );
- expect(component.contains(children)).toEqual(true);
+ test('nested', () => {
+ const { container } = render( } />);
+ expect(container.firstChild).toMatchSnapshot();
+ });
+
+ test('allows "dir" to be overridden', () => {
+ const { container } = render( );
+ expect(container.firstChild).toMatchSnapshot();
});
test('prop "numberOfLines"', () => {});
diff --git a/packages/react-native-web/src/exports/Text/index.js b/packages/react-native-web/src/exports/Text/index.js
index 52398c1f..5fec0ab0 100644
--- a/packages/react-native-web/src/exports/Text/index.js
+++ b/packages/react-native-web/src/exports/Text/index.js
@@ -10,32 +10,20 @@
import applyLayout from '../../modules/applyLayout';
import applyNativeMethods from '../../modules/applyNativeMethods';
-import { bool } from 'prop-types';
-import { Component } from 'react';
import createElement from '../createElement';
import css from '../StyleSheet/css';
import warning from 'fbjs/lib/warning';
+import React from 'react';
import StyleSheet from '../StyleSheet';
+import TextAncestorContext from './TextAncestorContext';
import TextPropTypes from './TextPropTypes';
-class Text extends Component<*> {
+class Text extends React.Component<*> {
static displayName = 'Text';
static propTypes = TextPropTypes;
- static childContextTypes = {
- isInAParentText: bool
- };
-
- static contextTypes = {
- isInAParentText: bool
- };
-
- getChildContext() {
- return { isInAParentText: true };
- }
-
- render() {
+ renderText(hasTextAncestor) {
const {
dir,
numberOfLines,
@@ -60,8 +48,6 @@ class Text extends Component<*> {
...otherProps
} = this.props;
- const { isInAParentText } = this.context;
-
if (process.env.NODE_ENV !== 'production') {
warning(this.props.className == null, 'Using the "className" prop on is deprecated.');
}
@@ -75,7 +61,7 @@ class Text extends Component<*> {
otherProps.classList = [
this.props.className,
classes.text,
- this.context.isInAParentText === true && classes.textHasAncestor,
+ hasTextAncestor === true && classes.textHasAncestor,
numberOfLines === 1 && classes.textOneLine,
numberOfLines > 1 && classes.textMultiLine
];
@@ -88,11 +74,26 @@ class Text extends Component<*> {
onPress && styles.pressable
];
- const component = isInAParentText ? 'span' : 'div';
+ const component = hasTextAncestor ? 'span' : 'div';
return createElement(component, otherProps);
}
+ render() {
+ return (
+
+ {hasTextAncestor => {
+ const element = this.renderText(hasTextAncestor);
+ return hasTextAncestor ? (
+ element
+ ) : (
+ {element}
+ );
+ }}
+
+ );
+ }
+
_createEnterHandler(fn) {
return e => {
if (e.keyCode === 13) {
diff --git a/packages/react-native-web/src/exports/View/__tests__/__snapshots__/index-test.js.snap b/packages/react-native-web/src/exports/View/__tests__/__snapshots__/index-test.js.snap
index f7c10dbe..48dc1be5 100644
--- a/packages/react-native-web/src/exports/View/__tests__/__snapshots__/index-test.js.snap
+++ b/packages/react-native-web/src/exports/View/__tests__/__snapshots__/index-test.js.snap
@@ -1,16 +1,46 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
+exports[`components/View default 1`] = `
+
+`;
+
+exports[`components/View non-text is rendered 1`] = `
+
+`;
+
exports[`components/View prop "hitSlop" handles partial offsets 1`] = `
-Object {
- "top": "-10px",
-}
+
+
+
`;
exports[`components/View prop "hitSlop" renders a span with negative position offsets 1`] = `
-Object {
- "bottom": "-10px",
- "left": "-5px",
- "right": "-5px",
- "top": "-10px",
-}
+
+
+
+`;
+
+exports[`components/View prop "pointerEvents" 1`] = `
+
`;
diff --git a/packages/react-native-web/src/exports/View/__tests__/index-test.js b/packages/react-native-web/src/exports/View/__tests__/index-test.js
index 781da280..be68620b 100644
--- a/packages/react-native-web/src/exports/View/__tests__/index-test.js
+++ b/packages/react-native-web/src/exports/View/__tests__/index-test.js
@@ -1,59 +1,52 @@
/* eslint-env jasmine, jest */
import React from 'react';
-import { shallow } from 'enzyme';
+import { render } from '@testing-library/react';
import View from '../';
describe('components/View', () => {
- describe('rendered element', () => {
- test('is a "div" by default', () => {
- const component = shallow( );
- expect(component.type()).toBe('div');
- });
+ test('default', () => {
+ const { container } = render( );
+ expect(container.firstChild).toMatchSnapshot();
});
- describe('prop "children"', () => {
- test('text node throws error (single)', () => {
- const render = () => shallow('hello' );
- expect(render).toThrow();
- });
+ test('text node throws error (single)', () => {
+ const subject = () => render(hello );
+ expect(subject).toThrow();
+ });
- test('text node throws error (array)', () => {
- const render = () =>
- shallow(
-
-
- 'hello'
-
-
- );
- expect(render).toThrow();
- });
+ test('text node throws error (array)', () => {
+ const subject = () =>
+ render(
+
+
+ hello
+
+
+ );
+ expect(subject).toThrow();
+ });
- test('non-text is rendered', () => {
- const children = ;
- const component = shallow({children} );
- expect(component.contains(children)).toEqual(true);
- });
+ test('non-text is rendered', () => {
+ const children = ;
+ const { container } = render({children} );
+ expect(container.firstChild).toMatchSnapshot();
});
describe('prop "hitSlop"', () => {
it('renders a span with negative position offsets', () => {
- const component = shallow( );
- const span = component.find('span');
- expect(span.length).toBe(1);
- expect(span.prop('style')).toMatchSnapshot();
+ const { container } = render( );
+ expect(container.firstChild).toMatchSnapshot();
});
it('handles partial offsets', () => {
- const component = shallow( );
- const span = component.find('span');
- expect(span.prop('style')).toMatchSnapshot();
+ const { container } = render( );
+ expect(container.firstChild).toMatchSnapshot();
});
});
test('prop "pointerEvents"', () => {
- const component = shallow( );
- expect(component.prop('className').indexOf('pointerEvents') > -1).toBe(true);
+ const { container } = render( );
+ expect(container.firstChild).toMatchSnapshot();
});
});
diff --git a/packages/react-native-web/src/exports/View/index.js b/packages/react-native-web/src/exports/View/index.js
index 25d386d0..16fc937d 100644
--- a/packages/react-native-web/src/exports/View/index.js
+++ b/packages/react-native-web/src/exports/View/index.js
@@ -10,13 +10,13 @@
import applyLayout from '../../modules/applyLayout';
import applyNativeMethods from '../../modules/applyNativeMethods';
-import { bool } from 'prop-types';
import createElement from '../createElement';
import css from '../StyleSheet/css';
import filterSupportedProps from './filterSupportedProps';
import invariant from 'fbjs/lib/invariant';
import warning from 'fbjs/lib/warning';
import StyleSheet from '../StyleSheet';
+import TextAncestorContext from '../Text/TextAncestorContext';
import ViewPropTypes, { type ViewProps } from './ViewPropTypes';
import React, { Component } from 'react';
@@ -34,13 +34,9 @@ const calculateHitSlopStyle = hitSlop => {
class View extends Component {
static displayName = 'View';
- static contextTypes = {
- isInAParentText: bool
- };
-
static propTypes = ViewPropTypes;
- render() {
+ renderView(hasTextAncestor) {
const hitSlop = this.props.hitSlop;
const supportedProps = filterSupportedProps(this.props);
@@ -55,11 +51,9 @@ class View extends Component {
});
}
- const { isInAParentText } = this.context;
-
supportedProps.classList = [this.props.className, classes.view];
supportedProps.style = StyleSheet.compose(
- isInAParentText && styles.inline,
+ hasTextAncestor && styles.inline,
this.props.style
);
@@ -74,6 +68,14 @@ class View extends Component {
return createElement('div', supportedProps);
}
+
+ render() {
+ return (
+
+ {hasTextAncestor => this.renderView(hasTextAncestor)}
+
+ );
+ }
}
const classes = css.create({
diff --git a/scripts/jest/config.js b/scripts/jest/config.js
index 9e2667a3..6c8b1a50 100644
--- a/scripts/jest/config.js
+++ b/scripts/jest/config.js
@@ -11,7 +11,10 @@ module.exports = {
rootDir: process.cwd(),
roots: ['/packages'],
setupFiles: ['jest-canvas-mock'],
- setupFilesAfterEnv: [require.resolve('./setupFramework.js')],
+ setupFilesAfterEnv: [
+ '@testing-library/react/cleanup-after-each',
+ require.resolve('./setupFramework.js')
+ ],
snapshotSerializers: ['enzyme-to-json/serializer'],
testEnvironment: 'jsdom',
timers: 'fake'
diff --git a/yarn.lock b/yarn.lock
index 34e027d1..8578c450 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -679,6 +679,13 @@
dependencies:
regenerator-runtime "^0.12.0"
+"@babel/runtime@^7.4.5", "@babel/runtime@^7.5.4":
+ version "7.5.5"
+ resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.5.5.tgz#74fba56d35efbeca444091c7850ccd494fd2f132"
+ integrity sha512-28QvEGyQyNkB0/m2B4FU7IEZGK2NUrcMtT6BZEFALTguLk+AUT6ofsHtPk5QyjAdUkpMJ+/Em+quwz4HOt30AQ==
+ dependencies:
+ regenerator-runtime "^0.13.2"
+
"@babel/template@^7.0.0", "@babel/template@^7.1.0", "@babel/template@^7.1.2", "@babel/template@^7.2.2":
version "7.2.2"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.2.2.tgz#005b3fdf0ed96e88041330379e0da9a708eb2907"
@@ -764,12 +771,26 @@
version "0.2.2"
resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.2.2.tgz#63985d3d8b02530e0869962f4da09142ee8e200e"
+"@jest/types@^24.8.0":
+ version "24.8.0"
+ resolved "https://registry.yarnpkg.com/@jest/types/-/types-24.8.0.tgz#f31e25948c58f0abd8c845ae26fcea1491dea7ad"
+ integrity sha512-g17UxVr2YfBtaMUxn9u/4+siG1ptg9IGYAYwvpwn61nBg779RXnjE/m7CxYcIzEt0AbHZZAHSEZNhkE2WxURVg==
+ dependencies:
+ "@types/istanbul-lib-coverage" "^2.0.0"
+ "@types/istanbul-reports" "^1.1.1"
+ "@types/yargs" "^12.0.9"
+
"@samverschueren/stream-to-observable@^0.3.0":
version "0.3.0"
resolved "https://registry.yarnpkg.com/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.0.tgz#ecdf48d532c58ea477acfcab80348424f8d0662f"
dependencies:
any-observable "^0.3.0"
+"@sheerun/mutationobserver-shim@^0.3.2":
+ version "0.3.2"
+ resolved "https://registry.yarnpkg.com/@sheerun/mutationobserver-shim/-/mutationobserver-shim-0.3.2.tgz#8013f2af54a2b7d735f71560ff360d3a8176a87b"
+ integrity sha512-vTCdPp/T/Q3oSqwHmZ5Kpa9oI7iLtGl3RQaA/NyLHikvcrPxACkkKVr/XzkSPJWXHRhKGzVvb0urJsbMlRxi1Q==
+
"@storybook/addon-actions@3.4.11":
version "3.4.11"
resolved "https://registry.yarnpkg.com/@storybook/addon-actions/-/addon-actions-3.4.11.tgz#a51418c5b684fd3df2788c19ef266845d6068baf"
@@ -976,6 +997,45 @@
react-split-pane "^0.1.77"
react-treebeard "^2.1.0"
+"@testing-library/dom@^5.5.4":
+ version "5.6.0"
+ resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-5.6.0.tgz#18a7c162a6a79964e731ad7b810022a28218047c"
+ integrity sha512-nAsRvQLr/b6TGNjuHMEbWXCNPLrQYnzqa/KKQZL7wBOtfptUxsa4Ah9aqkHW0ZmCSFmUDj4nFUxWPVTeMu0iCw==
+ dependencies:
+ "@babel/runtime" "^7.4.5"
+ "@sheerun/mutationobserver-shim" "^0.3.2"
+ aria-query "3.0.0"
+ pretty-format "^24.8.0"
+ wait-for-expect "^1.2.0"
+
+"@testing-library/react@^8.0.5":
+ version "8.0.5"
+ resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-8.0.5.tgz#2301011a8c5567eba59691860df19a3cfc9d7425"
+ integrity sha512-2EzVi7HjUUF8gKzB4s+oCJ1+F4VOrphO+DlUO6Ptgtcz1ko4J2zqnr0t7g+T7uedXXjJ0wdq70zQMhJXP3w37A==
+ dependencies:
+ "@babel/runtime" "^7.5.4"
+ "@testing-library/dom" "^5.5.4"
+
+"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0":
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.1.tgz#42995b446db9a48a11a07ec083499a860e9138ff"
+ integrity sha512-hRJD2ahnnpLgsj6KWMYSrmXkM3rm2Dl1qkx6IOFD5FnuNPXJIG5L0dhgKXCYTRMGzU4n0wImQ/xfmRc4POUFlg==
+
+"@types/istanbul-lib-report@*":
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-1.1.1.tgz#e5471e7fa33c61358dd38426189c037a58433b8c"
+ integrity sha512-3BUTyMzbZa2DtDI2BkERNC6jJw2Mr2Y0oGI7mRxYNBPxppbtEK1F66u3bKwU2g+wxwWI7PAoRpJnOY1grJqzHg==
+ dependencies:
+ "@types/istanbul-lib-coverage" "*"
+
+"@types/istanbul-reports@^1.1.1":
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-1.1.1.tgz#7a8cbf6a406f36c8add871625b278eaf0b0d255a"
+ integrity sha512-UpYjBi8xefVChsCoBpKShdxTllC9pwISirfoZsUa2AAdQg/Jd2KQGtSbw+ya7GPo7x/wAPlH6JBhKhAsXUEZNA==
+ dependencies:
+ "@types/istanbul-lib-coverage" "*"
+ "@types/istanbul-lib-report" "*"
+
"@types/lodash@^4.14.122":
version "4.14.122"
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.122.tgz#3e31394c38cf1e5949fb54c1192cbc406f152c6c"
@@ -1019,6 +1079,11 @@
"@types/prop-types" "*"
csstype "^2.2.0"
+"@types/yargs@^12.0.9":
+ version "12.0.12"
+ resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-12.0.12.tgz#45dd1d0638e8c8f153e87d296907659296873916"
+ integrity sha512-SOhuU4wNBxhhTHxYaiG5NY4HBhDIDnJF60GU+2LqHAdKKer86//e4yg69aENCtQ04n0ovz+tq2YPME5t5yp4pw==
+
"@webassemblyjs/ast@1.7.11":
version "1.7.11"
resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.7.11.tgz#b988582cafbb2b095e8b556526f30c90d057cace"
@@ -1351,6 +1416,14 @@ argparse@^1.0.7:
dependencies:
sprintf-js "~1.0.2"
+aria-query@3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-3.0.0.tgz#65b3fcc1ca1155a8c9ae64d6eee297f15d5133cc"
+ integrity sha1-ZbP8wcoRVajJrmTW7uKX8V1RM8w=
+ dependencies:
+ ast-types-flow "0.0.7"
+ commander "^2.11.0"
+
arr-diff@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-2.0.0.tgz#8f3b827f955a8bd669697e4a4256ac3ceae356cf"
@@ -1474,6 +1547,11 @@ assign-symbols@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367"
+ast-types-flow@0.0.7:
+ version "0.0.7"
+ resolved "https://registry.yarnpkg.com/ast-types-flow/-/ast-types-flow-0.0.7.tgz#f70b735c6bca1a5c9c22d982c3e39e7feba3bdad"
+ integrity sha1-9wtzXGvKGlycItmCw+Oef+ujva0=
+
ast-types@0.11.6:
version "0.11.6"
resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.11.6.tgz#4e2266c2658829aef3b40cc33ad599c4e9eb89ef"
@@ -3202,6 +3280,11 @@ commander@2.17.x, commander@~2.17.1:
version "2.17.1"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf"
+commander@^2.11.0:
+ version "2.20.0"
+ resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.0.tgz#d58bb2b5c1ee8f87b0d340027e9e94e222c5a422"
+ integrity sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==
+
commander@^2.14.1, commander@^2.15.0, commander@^2.18.0, commander@^2.19.0, commander@^2.8.1, commander@^2.9.0:
version "2.19.0"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a"
@@ -8368,6 +8451,16 @@ pretty-format@^24.0.0-alpha.9:
ansi-regex "^4.0.0"
ansi-styles "^3.2.0"
+pretty-format@^24.8.0:
+ version "24.8.0"
+ resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-24.8.0.tgz#8dae7044f58db7cb8be245383b565a963e3c27f2"
+ integrity sha512-P952T7dkrDEplsR+TuY7q3VXDae5Sr7zmQb12JU/NDQa/3CH7/QW0yvqLcGN6jL+zQFKaoJcPc+yJxMTGmosqw==
+ dependencies:
+ "@jest/types" "^24.8.0"
+ ansi-regex "^4.0.0"
+ ansi-styles "^3.2.0"
+ react-is "^16.8.4"
+
prismjs@^1.15.0:
version "1.15.0"
resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.15.0.tgz#8801d332e472091ba8def94976c8877ad60398d9"
@@ -8714,6 +8807,11 @@ react-is@^16.8.1:
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.8.4.tgz#90f336a68c3a29a096a3d648ab80e87ec61482a2"
integrity sha512-PVadd+WaUDOAciICm/J1waJaSvgq+4rHE/K70j0PFqKhkTBsPv/82UGQJNXAngz1fOQLLxI6z1sEDmJDQhCTAA==
+react-is@^16.8.4:
+ version "16.8.6"
+ resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.8.6.tgz#5bbc1e2d29141c9fbdfed456343fe2bc430a6a16"
+ integrity sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA==
+
react-jss@^8.6.1:
version "8.6.1"
resolved "https://registry.yarnpkg.com/react-jss/-/react-jss-8.6.1.tgz#a06e2e1d2c4d91b4d11befda865e6c07fbd75252"
@@ -9010,6 +9108,11 @@ regenerator-runtime@^0.12.0:
version "0.12.1"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz#fa1a71544764c036f8c49b13a08b2594c9f8a0de"
+regenerator-runtime@^0.13.2:
+ version "0.13.2"
+ resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.2.tgz#32e59c9a6fb9b1a4aff09b4930ca2d4477343447"
+ integrity sha512-S/TQAZJO+D3m9xeN1WTI8dLKBBiRgXBlTJvbWjCThHWZj9EvHK70Ff50/tYj2J/fvBY6JtFVwRuazHN2E7M9BA==
+
regenerator-transform@^0.10.0:
version "0.10.1"
resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.10.1.tgz#1e4996837231da8b7f3cf4114d71b5691a0680dd"
@@ -10534,6 +10637,11 @@ w3c-hr-time@^1.0.1:
dependencies:
browser-process-hrtime "^0.1.2"
+wait-for-expect@^1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/wait-for-expect/-/wait-for-expect-1.2.0.tgz#fdab6a26e87d2039101db88bff3d8158e5c3e13f"
+ integrity sha512-EJhKpA+5UHixduMBEGhTFuLuVgQBKWxkFbefOdj2bbk2/OpA5Opsc4aUTGmF+qJ+v3kTGxDRNYwKaT4j6g5n8Q==
+
walker@~1.0.5:
version "1.0.7"
resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.7.tgz#2f7f9b8fd10d677262b18a884e28d19618e028fb"