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`] = ` +
+
+ accessibilityLabel +
+`; -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"