diff --git a/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.js b/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.tsx similarity index 55% rename from src/components/HTMLEngineProvider/BaseHTMLEngineProvider.js rename to src/components/HTMLEngineProvider/BaseHTMLEngineProvider.tsx index 46d04ca9404d..690f2fc6883a 100755 --- a/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.js +++ b/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.tsx @@ -1,27 +1,19 @@ -import PropTypes from 'prop-types'; import React, {useMemo} from 'react'; -import {defaultHTMLElementModels, RenderHTMLConfigProvider, TRenderEngineProvider} from 'react-native-render-html'; -import _ from 'underscore'; +import type {TextProps} from 'react-native'; +import {HTMLContentModel, HTMLElementModel, RenderHTMLConfigProvider, TRenderEngineProvider} from 'react-native-render-html'; import useThemeStyles from '@hooks/useThemeStyles'; import convertToLTR from '@libs/convertToLTR'; import FontUtils from '@styles/utils/FontUtils'; +import type ChildrenProps from '@src/types/utils/ChildrenProps'; import * as HTMLEngineUtils from './htmlEngineUtils'; import htmlRenderers from './HTMLRenderers'; -const propTypes = { +type BaseHTMLEngineProviderProps = ChildrenProps & { /** Whether text elements should be selectable */ - textSelectable: PropTypes.bool, + textSelectable?: boolean; /** Handle line breaks according to the HTML standard (default on web) */ - enableExperimentalBRCollapsing: PropTypes.bool, - - children: PropTypes.node, -}; - -const defaultProps = { - textSelectable: false, - children: null, - enableExperimentalBRCollapsing: false, + enableExperimentalBRCollapsing?: boolean; }; // We are using the explicit composite architecture for performance gains. @@ -29,52 +21,62 @@ const defaultProps = { // context to RenderHTMLSource components. See https://git.io/JRcZb // Beware that each prop should be referentialy stable between renders to avoid // costly invalidations and commits. -function BaseHTMLEngineProvider(props) { +function BaseHTMLEngineProvider({textSelectable = false, children, enableExperimentalBRCollapsing = false}: BaseHTMLEngineProviderProps) { const styles = useThemeStyles(); // Declare nonstandard tags and their content model here + /* eslint-disable @typescript-eslint/naming-convention */ const customHTMLElementModels = useMemo( () => ({ - edited: defaultHTMLElementModels.span.extend({ + edited: HTMLElementModel.fromCustomModel({ tagName: 'edited', + contentModel: HTMLContentModel.textual, }), - 'alert-text': defaultHTMLElementModels.div.extend({ + 'alert-text': HTMLElementModel.fromCustomModel({ tagName: 'alert-text', mixedUAStyles: {...styles.formError, ...styles.mb0}, + contentModel: HTMLContentModel.block, }), - 'muted-text': defaultHTMLElementModels.div.extend({ + 'muted-text': HTMLElementModel.fromCustomModel({ tagName: 'muted-text', mixedUAStyles: {...styles.colorMuted, ...styles.mb0}, + contentModel: HTMLContentModel.block, }), - comment: defaultHTMLElementModels.div.extend({ + comment: HTMLElementModel.fromCustomModel({ tagName: 'comment', mixedUAStyles: {whiteSpace: 'pre'}, + contentModel: HTMLContentModel.block, }), - 'email-comment': defaultHTMLElementModels.div.extend({ + 'email-comment': HTMLElementModel.fromCustomModel({ tagName: 'email-comment', mixedUAStyles: {whiteSpace: 'normal'}, + contentModel: HTMLContentModel.block, }), - strong: defaultHTMLElementModels.span.extend({ + strong: HTMLElementModel.fromCustomModel({ tagName: 'strong', mixedUAStyles: {whiteSpace: 'pre'}, + contentModel: HTMLContentModel.textual, }), - 'mention-user': defaultHTMLElementModels.span.extend({tagName: 'mention-user'}), - 'mention-here': defaultHTMLElementModels.span.extend({tagName: 'mention-here'}), - 'next-step': defaultHTMLElementModels.span.extend({ + 'mention-user': HTMLElementModel.fromCustomModel({tagName: 'mention-user', contentModel: HTMLContentModel.textual}), + 'mention-here': HTMLElementModel.fromCustomModel({tagName: 'mention-here', contentModel: HTMLContentModel.textual}), + 'next-step': HTMLElementModel.fromCustomModel({ tagName: 'next-step', mixedUAStyles: {...styles.textLabelSupporting, ...styles.lh16}, + contentModel: HTMLContentModel.textual, }), - 'next-step-email': defaultHTMLElementModels.span.extend({tagName: 'next-step-email'}), - video: defaultHTMLElementModels.div.extend({ + 'next-step-email': HTMLElementModel.fromCustomModel({tagName: 'next-step-email', contentModel: HTMLContentModel.textual}), + video: HTMLElementModel.fromCustomModel({ tagName: 'video', mixedUAStyles: {whiteSpace: 'pre'}, + contentModel: HTMLContentModel.block, }), }), [styles.colorMuted, styles.formError, styles.mb0, styles.textLabelSupporting, styles.lh16], ); + /* eslint-enable @typescript-eslint/naming-convention */ // We need to memoize this prop to make it referentially stable. - const defaultTextProps = useMemo(() => ({selectable: props.textSelectable, allowFontScaling: false, textBreakStrategy: 'simple'}), [props.textSelectable]); + const defaultTextProps: TextProps = useMemo(() => ({selectable: textSelectable, allowFontScaling: false, textBreakStrategy: 'simple'}), [textSelectable]); const defaultViewProps = {style: [styles.alignItemsStart, styles.userSelectText]}; return ( (text.data = convertToLTR(text.data)), @@ -91,18 +93,17 @@ function BaseHTMLEngineProvider(props) { - {props.children} + {children} ); } BaseHTMLEngineProvider.displayName = 'BaseHTMLEngineProvider'; -BaseHTMLEngineProvider.propTypes = propTypes; -BaseHTMLEngineProvider.defaultProps = defaultProps; export default BaseHTMLEngineProvider; diff --git a/src/components/HTMLEngineProvider/htmlEnginePropTypes.js b/src/components/HTMLEngineProvider/htmlEnginePropTypes.js deleted file mode 100644 index 6c8537c8d228..000000000000 --- a/src/components/HTMLEngineProvider/htmlEnginePropTypes.js +++ /dev/null @@ -1,15 +0,0 @@ -import PropTypes from 'prop-types'; - -const propTypes = { - children: PropTypes.node, - - /** Optional debug flag. Prints the TRT in the console when true. */ - debug: PropTypes.bool, -}; - -const defaultProps = { - children: null, - debug: false, -}; - -export {propTypes, defaultProps}; diff --git a/src/components/HTMLEngineProvider/htmlEngineUtils.js b/src/components/HTMLEngineProvider/htmlEngineUtils.ts similarity index 57% rename from src/components/HTMLEngineProvider/htmlEngineUtils.js rename to src/components/HTMLEngineProvider/htmlEngineUtils.ts index 4495cb8ff136..5f082424a565 100644 --- a/src/components/HTMLEngineProvider/htmlEngineUtils.js +++ b/src/components/HTMLEngineProvider/htmlEngineUtils.ts @@ -1,4 +1,6 @@ -import lodashGet from 'lodash/get'; +import type {TNode} from 'react-native-render-html'; + +type Predicate = (node: TNode) => boolean; const MAX_IMG_DIMENSIONS = 512; @@ -7,12 +9,12 @@ const MAX_IMG_DIMENSIONS = 512; * is used by the HTML component in the default renderer for img tags to scale * down images that would otherwise overflow horizontally. * - * @param {string} tagName - The name of the tag for which max width should be constrained. - * @param {number} contentWidth - The content width provided to the HTML + * @param contentWidth - The content width provided to the HTML * component. - * @returns {number} The minimum between contentWidth and MAX_IMG_DIMENSIONS + * @param tagName - The name of the tag for which max width should be constrained. + * @returns The minimum between contentWidth and MAX_IMG_DIMENSIONS */ -function computeEmbeddedMaxWidth(tagName, contentWidth) { +function computeEmbeddedMaxWidth(contentWidth: number, tagName: string): number { if (tagName === 'img') { return Math.min(MAX_IMG_DIMENSIONS, contentWidth); } @@ -22,21 +24,15 @@ function computeEmbeddedMaxWidth(tagName, contentWidth) { /** * Check if tagName is equal to any of our custom tags wrapping chat comments. * - * @param {string} tagName - * @returns {Boolean} */ -function isCommentTag(tagName) { +function isCommentTag(tagName: string): boolean { return tagName === 'email-comment' || tagName === 'comment'; } /** * Check if there is an ancestor node for which the predicate returns true. - * - * @param {TNode} tnode - * @param {Function} predicate - * @returns {Boolean} */ -function isChildOfNode(tnode, predicate) { +function isChildOfNode(tnode: TNode, predicate: Predicate): boolean { let currentNode = tnode.parent; while (currentNode) { if (predicate(currentNode)) { @@ -50,21 +46,17 @@ function isChildOfNode(tnode, predicate) { /** * Check if there is an ancestor node with name 'comment'. * Finding node with name 'comment' flags that we are rendering a comment. - * @param {TNode} tnode - * @returns {Boolean} */ -function isChildOfComment(tnode) { - return isChildOfNode(tnode, (node) => isCommentTag(lodashGet(node, 'domNode.name', ''))); +function isChildOfComment(tnode: TNode): boolean { + return isChildOfNode(tnode, (node) => node.domNode?.name !== undefined && isCommentTag(node.domNode.name)); } /** * Check if there is an ancestor node with the name 'h1'. * Finding a node with the name 'h1' flags that we are rendering inside an h1 element. - * @param {TNode} tnode - * @returns {Boolean} */ -function isChildOfH1(tnode) { - return isChildOfNode(tnode, (node) => lodashGet(node, 'domNode.name', '').toLowerCase() === 'h1'); +function isChildOfH1(tnode: TNode): boolean { + return isChildOfNode(tnode, (node) => node.domNode?.name !== undefined && node.domNode.name.toLowerCase() === 'h1'); } export {computeEmbeddedMaxWidth, isChildOfComment, isCommentTag, isChildOfH1}; diff --git a/src/components/HTMLEngineProvider/index.js b/src/components/HTMLEngineProvider/index.js deleted file mode 100755 index 8a8e96269411..000000000000 --- a/src/components/HTMLEngineProvider/index.js +++ /dev/null @@ -1,22 +0,0 @@ -import React from 'react'; -import withWindowDimensions from '@components/withWindowDimensions'; -import * as DeviceCapabilities from '@libs/DeviceCapabilities'; -import BaseHTMLEngineProvider from './BaseHTMLEngineProvider'; -import {defaultProps, propTypes} from './htmlEnginePropTypes'; - -function HTMLEngineProvider(props) { - return ( - - {props.children} - - ); -} - -HTMLEngineProvider.displayName = 'HTMLEngineProvider'; -HTMLEngineProvider.propTypes = propTypes; -HTMLEngineProvider.defaultProps = defaultProps; - -export default withWindowDimensions(HTMLEngineProvider); diff --git a/src/components/HTMLEngineProvider/index.native.js b/src/components/HTMLEngineProvider/index.native.js deleted file mode 100755 index f760a5a36649..000000000000 --- a/src/components/HTMLEngineProvider/index.native.js +++ /dev/null @@ -1,20 +0,0 @@ -import React from 'react'; -import BaseHTMLEngineProvider from './BaseHTMLEngineProvider'; -import {defaultProps, propTypes} from './htmlEnginePropTypes'; - -function HTMLEngineProvider(props) { - return ( - - {props.children} - - ); -} - -HTMLEngineProvider.displayName = 'HTMLEngineProvider'; -HTMLEngineProvider.propTypes = propTypes; -HTMLEngineProvider.defaultProps = defaultProps; - -export default HTMLEngineProvider; diff --git a/src/components/HTMLEngineProvider/index.native.tsx b/src/components/HTMLEngineProvider/index.native.tsx new file mode 100755 index 000000000000..c77bcaf7c5e3 --- /dev/null +++ b/src/components/HTMLEngineProvider/index.native.tsx @@ -0,0 +1,11 @@ +import React from 'react'; +import type ChildrenProps from '@src/types/utils/ChildrenProps'; +import BaseHTMLEngineProvider from './BaseHTMLEngineProvider'; + +function HTMLEngineProvider({children}: ChildrenProps) { + return {children}; +} + +HTMLEngineProvider.displayName = 'HTMLEngineProvider'; + +export default HTMLEngineProvider; diff --git a/src/components/HTMLEngineProvider/index.tsx b/src/components/HTMLEngineProvider/index.tsx new file mode 100755 index 000000000000..9addb549d13a --- /dev/null +++ b/src/components/HTMLEngineProvider/index.tsx @@ -0,0 +1,15 @@ +import React from 'react'; +import useWindowDimensions from '@hooks/useWindowDimensions'; +import * as DeviceCapabilities from '@libs/DeviceCapabilities'; +import type ChildrenProps from '@src/types/utils/ChildrenProps'; +import BaseHTMLEngineProvider from './BaseHTMLEngineProvider'; + +function HTMLEngineProvider({children}: ChildrenProps) { + const {isSmallScreenWidth} = useWindowDimensions(); + + return {children}; +} + +HTMLEngineProvider.displayName = 'HTMLEngineProvider'; + +export default HTMLEngineProvider;