Skip to content

Commit

Permalink
feat: graceful handling of missing source prop
Browse files Browse the repository at this point in the history
Also, the user will receive messages in dev mode about outdated props
such as `html` and `uri`.
  • Loading branch information
jsamr committed Feb 9, 2021
1 parent 4b8fb84 commit 4a41dff
Show file tree
Hide file tree
Showing 6 changed files with 100 additions and 18 deletions.
5 changes: 4 additions & 1 deletion packages/render-html/jest.config.js
Original file line number Diff line number Diff line change
@@ -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
}
};
8 changes: 5 additions & 3 deletions packages/render-html/src/RenderHTML.tsx
Original file line number Diff line number Diff line change
@@ -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, {
Expand Down Expand Up @@ -149,7 +149,8 @@ const defaultProps: {
]
}),
triggerTREInvalidationPropNames: [],
debug: __DEV__
debug: __DEV__,
contentWidth: undefined
};

function RenderResolvedHTML(props: RenderResolvedHTMLProps) {
Expand All @@ -168,11 +169,12 @@ export default function RenderHTML({
...props
}: RenderHTMLProps) {
const normalizedProps = {
contentWidth: Dimensions.get('window').width,
...props,
defaultTextProps: { ...defaultProps.defaultTextProps, ...defaultTextProps }
};
return (
<RenderHTMLDebug {...normalizedProps}>
<RenderHTMLDebug {...props}>
<RenderRegistryProvider renderers={normalizedProps.renderers}>
<SharedPropsContext.Provider
value={normalizedProps as Required<RenderHTMLProps>}>
Expand Down
41 changes: 31 additions & 10 deletions packages/render-html/src/RenderHTMLDebug.tsx
Original file line number Diff line number Diff line change
@@ -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<RenderHTMLProps>) {
export function RenderHTMLProd(props: PropsWithChildren<RenderHTMLProps>) {
return <Fragment>{props.children}</Fragment>;
}

function RenderHTMLDev(props: PropsWithChildren<RenderHTMLProps>) {
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<RenderHTMLProps>) {
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 <Fragment>{props.children}</Fragment>;
}

Expand Down
3 changes: 3 additions & 0 deletions packages/render-html/src/SourceLoader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 });
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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(
<RenderHTMLDev source={{ html: '<p>Hello world</p>' }} 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(<RenderHTMLDev debug={false} />);
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);
});
});
Original file line number Diff line number Diff line change
@@ -1,22 +1,32 @@
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(
<RenderHTML source={{ html: '<p>Hello world</p>' }} debug={false} />
)
).not.toThrow();
});
it('should render without error when missing a source', () => {
expect(() => render(<RenderHTML debug={false} />)).not.toThrow();
});
it('should update ImgTag contentWidth when contentWidth prop changes', () => {
const contentWidth = 300;
const nextContentWidth = 200;
const { UNSAFE_getByType, update } = render(
<HTML
<RenderHTML
source={{ html: '<img src="https://img.com/1" />' }}
debug={false}
contentWidth={contentWidth}
/>
);
expect(UNSAFE_getByType(ImgTag).props.contentWidth).toBe(contentWidth);
update(
<HTML
<RenderHTML
source={{ html: '<img src="https://img.com/1" />' }}
debug={false}
contentWidth={nextContentWidth}
Expand Down

0 comments on commit 4a41dff

Please sign in to comment.