From 4a41dff5d174471091e9af99658cfdfdf3df45f7 Mon Sep 17 00:00:00 2001 From: "Jules Sam. Randolph" Date: Tue, 9 Feb 2021 10:09:30 -0300 Subject: [PATCH] feat: graceful handling of missing `source` prop Also, the user will receive messages in dev mode about outdated props such as `html` and `uri`. --- packages/render-html/jest.config.js | 5 ++- packages/render-html/src/RenderHTML.tsx | 8 ++-- packages/render-html/src/RenderHTMLDebug.tsx | 41 +++++++++++++----- packages/render-html/src/SourceLoader.tsx | 3 ++ .../component.render-html-dev.test.tsx | 43 +++++++++++++++++++ ...est.tsx => component.render-html.test.tsx} | 18 ++++++-- 6 files changed, 100 insertions(+), 18 deletions(-) create mode 100644 packages/render-html/src/__tests__/component.render-html-dev.test.tsx rename packages/render-html/src/__tests__/{component.html.test.tsx => component.render-html.test.tsx} (62%) diff --git a/packages/render-html/jest.config.js b/packages/render-html/jest.config.js index b454f9464..e401087c4 100644 --- a/packages/render-html/jest.config.js +++ b/packages/render-html/jest.config.js @@ -1,5 +1,8 @@ module.exports = { preset: 'react-native', testRegex: 'src/.*(/__tests__/.*|(\\.|/)(test|spec))\\.[jt]sx?$', - coveragePathIgnorePatterns: ['/node_modules/', '__tests__'] + coveragePathIgnorePatterns: ['/node_modules/', '__tests__'], + globals: { + __DEV__: true + } }; diff --git a/packages/render-html/src/RenderHTML.tsx b/packages/render-html/src/RenderHTML.tsx index 18c02272a..beffc370c 100644 --- a/packages/render-html/src/RenderHTML.tsx +++ b/packages/render-html/src/RenderHTML.tsx @@ -1,6 +1,6 @@ import React, { useMemo } from 'react'; import PropTypes from 'prop-types'; -import { Platform } from 'react-native'; +import { Dimensions, Platform } from 'react-native'; import { RenderResolvedHTMLProps, RenderHTMLProps } from './shared-types'; import useTTree from './hooks/useTTree'; import SharedPropsContext, { @@ -149,7 +149,8 @@ const defaultProps: { ] }), triggerTREInvalidationPropNames: [], - debug: __DEV__ + debug: __DEV__, + contentWidth: undefined }; function RenderResolvedHTML(props: RenderResolvedHTMLProps) { @@ -168,11 +169,12 @@ export default function RenderHTML({ ...props }: RenderHTMLProps) { const normalizedProps = { + contentWidth: Dimensions.get('window').width, ...props, defaultTextProps: { ...defaultProps.defaultTextProps, ...defaultTextProps } }; return ( - + }> diff --git a/packages/render-html/src/RenderHTMLDebug.tsx b/packages/render-html/src/RenderHTMLDebug.tsx index 2d88f0f7e..20b75b68b 100644 --- a/packages/render-html/src/RenderHTMLDebug.tsx +++ b/packages/render-html/src/RenderHTMLDebug.tsx @@ -1,25 +1,46 @@ import React, { Fragment } from 'react'; import { PropsWithChildren } from 'react'; import { useEffect } from 'react'; +import lookupRecord from './helpers/lookupRecord'; import { RenderHTMLProps } from './shared-types'; -function RenderHTMLProd(props: PropsWithChildren) { +export function RenderHTMLProd(props: PropsWithChildren) { return {props.children}; } -function RenderHTMLDev(props: PropsWithChildren) { +export const messages = { + outdatedUriProp: + "You're attempting to use an outdated prop, 'uri'. This prop has been discontinued since version 6. " + + "Use 'source={{ uri }}' instead.", + outdatedHtmlProp: + "You're attempting to use an outdated prop, 'html'. This prop has been discontinued since version 6. " + + "Use 'source={{ html }}' instead.", + noSource: + 'No source prop was provided to RenderHTML. Nothing will be rendered', + contentWidth: + 'You should always pass contentWidth prop to properly handle screen rotations ' + + 'and have a seemless support for images scaling. ' + + 'In the meantime, HTML will fallback to Dimensions.window().width, but its ' + + 'layout will become inconsistent after screen rotations. ' + + 'You are encouraged to use useWindowDimensions hook, see: ' + + 'https://reactnative.dev/docs/usewindowdimensions' +}; + +export function RenderHTMLDev(props: PropsWithChildren) { useEffect(() => { if (typeof props.contentWidth !== 'number') { - console.warn( - 'You should always pass contentWidth prop to properly handle screen rotations ' + - 'and have a seemless support for images scaling. ' + - 'In the meantime, HTML will fallback to Dimensions.window().width, but its ' + - 'layout will become inconsistent after screen rotations. ' + - 'You are encouraged to use useWindowDimensions hook, see: ' + - 'https://reactnative.dev/docs/usewindowdimensions' - ); + console.warn(messages.contentWidth); } }, [props.contentWidth]); + if (!props.source) { + console.warn(messages.noSource); + } + if (lookupRecord(props, 'html')) { + console.warn(messages.outdatedHtmlProp); + } + if (lookupRecord(props, 'uri')) { + console.warn(messages.outdatedUriProp); + } return {props.children}; } diff --git a/packages/render-html/src/SourceLoader.tsx b/packages/render-html/src/SourceLoader.tsx index 73b3a065a..83867261d 100644 --- a/packages/render-html/src/SourceLoader.tsx +++ b/packages/render-html/src/SourceLoader.tsx @@ -40,6 +40,9 @@ export default function SourceLoader({ source, ...props }: SourceLoaderProps): ReactElement | null { + if (!source) { + return null; + } if (isUriSource(source)) { return React.createElement(UriSourceLoader, { source, ...props }); } diff --git a/packages/render-html/src/__tests__/component.render-html-dev.test.tsx b/packages/render-html/src/__tests__/component.render-html-dev.test.tsx new file mode 100644 index 000000000..15b32c659 --- /dev/null +++ b/packages/render-html/src/__tests__/component.render-html-dev.test.tsx @@ -0,0 +1,43 @@ +import React from 'react'; +import { render } from 'react-native-testing-library'; +import { RenderHTMLDev, messages } from '../RenderHTMLDebug'; + +describe('RenderHTMLDev', () => { + it('should warn when contentWidth has not been provided', () => { + console.warn = jest.fn(); + render( + Hello world

' }} debug={false} /> + ); + expect(console.warn).toHaveBeenNthCalledWith(1, messages.contentWidth); + }); + it('should warn when source has not been provided', () => { + console.warn = jest.fn(); + //@ts-expect-error + render(); + expect(console.warn).toHaveBeenNthCalledWith(1, messages.noSource); + }); + it('should warn when outdated html prop has been provided', () => { + console.warn = jest.fn(); + render( + React.createElement(RenderHTMLDev, { + source: { html: 'hello world' }, + //@ts-expect-error + html: 'hello world', + debug: false + }) + ); + expect(console.warn).toHaveBeenNthCalledWith(1, messages.outdatedHtmlProp); + }); + it('should warn when outdated uri prop has been provided', () => { + console.warn = jest.fn(); + render( + React.createElement(RenderHTMLDev, { + source: { html: 'hello world' }, + //@ts-expect-error + uri: 'https://foo.bar/', + debug: false + }) + ); + expect(console.warn).toHaveBeenNthCalledWith(1, messages.outdatedUriProp); + }); +}); diff --git a/packages/render-html/src/__tests__/component.html.test.tsx b/packages/render-html/src/__tests__/component.render-html.test.tsx similarity index 62% rename from packages/render-html/src/__tests__/component.html.test.tsx rename to packages/render-html/src/__tests__/component.render-html.test.tsx index 352f4d7ef..e81e3bde6 100644 --- a/packages/render-html/src/__tests__/component.html.test.tsx +++ b/packages/render-html/src/__tests__/component.render-html.test.tsx @@ -1,14 +1,24 @@ import React from 'react'; import { render } from 'react-native-testing-library'; -import HTML from '../RenderHTML'; +import RenderHTML from '../RenderHTML'; import ImgTag from '../elements/IMGElement'; -describe('ImgTag', () => { +describe('RenderHTML', () => { + it('should render without error when providing a source', () => { + expect(() => + render( + Hello world

' }} debug={false} /> + ) + ).not.toThrow(); + }); + it('should render without error when missing a source', () => { + expect(() => render()).not.toThrow(); + }); it('should update ImgTag contentWidth when contentWidth prop changes', () => { const contentWidth = 300; const nextContentWidth = 200; const { UNSAFE_getByType, update } = render( - ' }} debug={false} contentWidth={contentWidth} @@ -16,7 +26,7 @@ describe('ImgTag', () => { ); expect(UNSAFE_getByType(ImgTag).props.contentWidth).toBe(contentWidth); update( - ' }} debug={false} contentWidth={nextContentWidth}