diff --git a/packages/render-html/src/TNodeRenderer.tsx b/packages/render-html/src/TNodeRenderer.tsx index 968fd5a90..9f17f6828 100644 --- a/packages/render-html/src/TNodeRenderer.tsx +++ b/packages/render-html/src/TNodeRenderer.tsx @@ -10,6 +10,7 @@ import TPhrasingRenderer from './TPhrasingRenderer'; import TTextRenderer from './TTextRenderer'; import { Markers, TNodeRendererProps } from './shared-types'; import { getMarkersFromTNode } from './helpers/getMarkersFromTNode'; +import { useSharedProps } from './context/SharedPropsContext'; export type { TNodeRendererProps } from './shared-types'; @@ -17,8 +18,21 @@ const TNodeRenderer = function TNodeRenderer( props: Omit, 'markers'> & { parentMarkers: Markers } ) { const { tnode } = props; + const { setMarkersForTNode } = useSharedProps(); const markers = getMarkersFromTNode(tnode, props.parentMarkers); - const tnodeProps = { ...props, markers: markers || props.parentMarkers }; + const customMarkers = setMarkersForTNode(tnode, props.parentMarkers); + const resolvedMarkers = + markers && !customMarkers + ? markers + : !markers && customMarkers + ? { ...props.parentMarkers, ...customMarkers } + : markers && customMarkers + ? ({ ...markers, ...customMarkers } as Markers) + : null; + const tnodeProps = { + ...props, + markers: resolvedMarkers || props.parentMarkers + }; if (tnode instanceof TBlock) { return React.createElement(TBlockRenderer, tnodeProps); } diff --git a/packages/render-html/src/__tests__/component.render-html.test.tsx b/packages/render-html/src/__tests__/component.render-html.test.tsx index f9f208427..c10fdfc46 100644 --- a/packages/render-html/src/__tests__/component.render-html.test.tsx +++ b/packages/render-html/src/__tests__/component.render-html.test.tsx @@ -129,6 +129,22 @@ describe('RenderHTML', () => { const em = UNSAFE_getByType(EmRenderer); expect(em.props.markers.lang).toBe('test'); }); + it('should handle setMarkersForTNode prop', () => { + const { UNSAFE_getByType } = render( + Two' + }} + debug={false} + setMarkersForTNode={(tnode) => + tnode.tagName === 'em' ? { em: true } : null + } + contentWidth={100} + /> + ); + const em = UNSAFE_getByType(TTextRenderer); + expect(em.props.markers.em).toBe(true); + }); }); describe('regarding propsFromParent', () => { it('should pass propsForChildren to children', () => { diff --git a/packages/render-html/src/context/SharedPropsContext.ts b/packages/render-html/src/context/SharedPropsContext.ts index aabf038a4..84777b93b 100644 --- a/packages/render-html/src/context/SharedPropsContext.ts +++ b/packages/render-html/src/context/SharedPropsContext.ts @@ -28,7 +28,8 @@ export const defaultSharedPropsContext: Required = { return null; }, defaultWebViewProps: {}, - renderersProps: {} + renderersProps: {}, + setMarkersForTNode: () => null }; const SharedPropsContext = React.createContext>( diff --git a/packages/render-html/src/shared-types.ts b/packages/render-html/src/shared-types.ts index 5d14bcacc..7d722bbae 100644 --- a/packages/render-html/src/shared-types.ts +++ b/packages/render-html/src/shared-types.ts @@ -119,6 +119,20 @@ export interface RenderHTMLSharedProps< * See [@native-html/plugins](https://github.com/native-html/plugins). */ WebView?: ComponentType; + /** + * Set custom markers from a TNode and all its descendants. Markers will be + * accessible in custom renderers via `markers` prop. + * + * @param tnode - The TNode to inspect + * @param parentMarkers - Markers from the parent TNode. + * + * @returns a record of markers if one or many markers should be added, + * `null` otherwise. + */ + setMarkersForTNode?: ( + tnode: TNode, + parentMarkers: Markers + ) => Partial | null; } export interface TransientRenderEngineConfig { @@ -377,10 +391,9 @@ export interface FallbackFontsDefinitions { /** * Markers form an abstraction in which one node provides semantic information - * to itself and all its descendants by the mean of its name or attributes. For - * example, `ins` elements, which stand for "insertion" of content in the - * context of an edit will provide the { edits: 'ins' } marker to all its - * descendants. + * to itself and all its descendants. For example, `ins` elements, which stand + * for "insertion" of content in the context of an edit will provide the { + * edits: 'ins' } marker to all its descendants. * * Custom renderers can use markers to change their layout and convey their * semantic meaning. Markers can be derived from attributes, such as `lang` and