Skip to content

Commit 33f9e6c

Browse files
author
Ben Grynhaus
committed
[Core] Allow passing additional props to ReactContent when creating render inputs
1 parent 48b94bc commit 33f9e6c

File tree

4 files changed

+57
-28
lines changed

4 files changed

+57
-28
lines changed

libs/core/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"$schema": "../../node_modules/ng-packagr/package.schema.json",
33
"name": "@angular-react/core",
4-
"version": "0.2.3",
4+
"version": "0.2.4",
55
"ngPackage": {
66
"lib": {
77
"languageLevel": [

libs/core/src/components/wrapper-component.ts

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import toStyle from 'css-to-style';
1616

1717
import { isReactNode } from '../renderer/react-node';
1818
import { renderComponent, renderFunc, renderTemplate } from '../renderer/renderprop-helpers';
19+
import { ExternalReactContentProps } from '../renderer/react-content';
1920
import { unreachable } from '../utils/types/unreachable';
2021

2122
type AttributeNameAlternative = [string, string | undefined];
@@ -133,32 +134,34 @@ export abstract class ReactWrapperComponent<TProps extends {}> implements AfterV
133134
/**
134135
* Create an JSX renderer for an `@Input` property.
135136
* @param input The input property
137+
* @param additionalProps optional additional props to pass to the `ReactContent` object that will render the content.
136138
*/
137139
protected createInputJsxRenderer<TContext extends object>(
138-
input: InputRendererOptions<TContext>
140+
input: InputRendererOptions<TContext>,
141+
additionalProps?: ExternalReactContentProps
139142
): JsxRenderFunc<TContext> | undefined {
140143
if (input === undefined) {
141144
return undefined;
142145
}
143146

144147
if (input instanceof TemplateRef) {
145-
return (context: TContext) => renderTemplate(input, context);
148+
return (context: TContext) => renderTemplate(input, context, additionalProps);
146149
}
147150

148151
if (input instanceof ComponentRef) {
149-
return (context: TContext) => renderComponent(input, context);
152+
return (context: TContext) => renderComponent(input, context, additionalProps);
150153
}
151154

152155
if (input instanceof Function) {
153-
return (context: TContext) => renderFunc(input, context);
156+
return (context: TContext) => renderFunc(input, context, additionalProps);
154157
}
155158

156159
if (typeof input === 'object') {
157160
const { componentType, factoryResolver, injector } = input;
158161
const componentFactory = factoryResolver.resolveComponentFactory(componentType);
159162
const componentRef = componentFactory.create(injector);
160163

161-
return (context: TContext) => renderComponent(componentRef, context);
164+
return (context: TContext) => renderComponent(componentRef, context, additionalProps);
162165
}
163166

164167
unreachable(input);
@@ -168,12 +171,14 @@ export abstract class ReactWrapperComponent<TProps extends {}> implements AfterV
168171
* Create an event handler for a render prop
169172
* @param renderInputValue the value of the render `@Input` property.
170173
* @param jsxRenderer an optional renderer to use.
174+
* @param additionalProps optional additional props to pass to the `ReactContent` object that will render the content.
171175
*/
172176
protected createRenderPropHandler<TProps extends object>(
173177
renderInputValue: InputRendererOptions<TProps>,
174-
jsxRenderer?: JsxRenderFunc<TProps>
178+
jsxRenderer?: JsxRenderFunc<TProps>,
179+
additionalProps?: ExternalReactContentProps
175180
): (props?: TProps, defaultRender?: JsxRenderFunc<TProps>) => JSX.Element | null {
176-
const renderer = jsxRenderer || this.createInputJsxRenderer(renderInputValue);
181+
const renderer = jsxRenderer || this.createInputJsxRenderer(renderInputValue, additionalProps);
177182

178183
return (props?: TProps, defaultRender?: JsxRenderFunc<TProps>) => {
179184
if (!renderInputValue) {

libs/core/src/renderer/react-content.ts

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@ import * as ReactDOM from 'react-dom';
44
const DEBUG = false;
55
export const CHILDREN_TO_APPEND_PROP = 'children-to-append'; // TODO: Change to Symbol('children-to-append') after upgrade to TS 2.7.
66

7-
export interface ReactContentProps {
8-
readonly 'children-to-append': HTMLElement[]; // TODO: use CHILDREN_TO_APPEND_PROP after upgrade to TS 2.7.
9-
7+
/**
8+
* Props that can be passed to `ReactContent` from users.
9+
*/
10+
export interface ExternalReactContentProps {
1011
/**
1112
* Experimental rendering mode.
1213
* Uses a similar approach to `router-outlet`, where the child elements are added to the parent, instead of this node, and this is hidden.
@@ -15,6 +16,12 @@ export interface ReactContentProps {
1516
legacyRenderMode?: boolean;
1617
}
1718

19+
// NOTE: Separated into `ExternalReactContentProps` since We only want a subset of props to be exposed to external users.
20+
// With Omit type (TS 2.8) we can simply have `type ExternalReactContentProps = Omit<ReactContentProps, 'children-to-append'>`
21+
export interface ReactContentProps extends ExternalReactContentProps {
22+
readonly 'children-to-append': HTMLElement[]; // TODO: use CHILDREN_TO_APPEND_PROP after upgrade to TS 2.7.
23+
}
24+
1825
/**
1926
* Render any `HTMLElement`s (including Angular components) as a child of React components.
2027
* Supports two rendering modes:
@@ -23,11 +30,15 @@ export interface ReactContentProps {
2330
* (similar to how `router-outlet` behaves in Angular).
2431
*/
2532
export class ReactContent extends React.PureComponent<ReactContentProps> {
26-
2733
componentDidMount() {
2834
const element = ReactDOM.findDOMNode(this);
2935
if (this.props[CHILDREN_TO_APPEND_PROP]) {
30-
if (DEBUG) { console.warn('ReactContent Component > componentDidMount > childrenToAppend:', this.props[CHILDREN_TO_APPEND_PROP]); }
36+
if (DEBUG) {
37+
console.warn(
38+
'ReactContent Component > componentDidMount > childrenToAppend:',
39+
this.props[CHILDREN_TO_APPEND_PROP]
40+
);
41+
}
3142

3243
const hostElement = this.props.legacyRenderMode ? element : element.parentElement;
3344
this.props[CHILDREN_TO_APPEND_PROP].forEach(child => hostElement.appendChild(child));
Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,64 @@
1-
import { ComponentRef, TemplateRef } from "@angular/core";
1+
import { ComponentRef, TemplateRef } from '@angular/core';
22
import * as React from 'react';
33

4-
import { CHILDREN_TO_APPEND_PROP, ReactContent } from "../renderer/react-content";
4+
import { CHILDREN_TO_APPEND_PROP, ReactContent, ExternalReactContentProps } from '../renderer/react-content';
55

6-
function renderReactContent(rootNodes: HTMLElement[]): JSX.Element {
7-
return React.createElement(
8-
ReactContent,
9-
{
10-
[CHILDREN_TO_APPEND_PROP]: rootNodes
11-
} as any
12-
);
6+
function renderReactContent(rootNodes: HTMLElement[], additionalProps?: ExternalReactContentProps): JSX.Element {
7+
return React.createElement(ReactContent, {
8+
...additionalProps,
9+
[CHILDREN_TO_APPEND_PROP]: rootNodes,
10+
});
1311
}
1412

1513
/**
1614
* Wrap a `TemplateRef` with a `JSX.Element`.
1715
*
1816
* @param templateRef The template to wrap
1917
* @param context The context to pass to the template
18+
* @param additionalProps optional additional props to pass to the `ReactContent` object that will render the content.
2019
*/
21-
export function renderTemplate<TContext extends object>(templateRef: TemplateRef<TContext>, context?: TContext): JSX.Element {
20+
export function renderTemplate<TContext extends object>(
21+
templateRef: TemplateRef<TContext>,
22+
context?: TContext,
23+
additionalProps?: ExternalReactContentProps
24+
): JSX.Element {
2225
const viewRef = templateRef.createEmbeddedView(context);
2326
viewRef.detectChanges();
2427

25-
return renderReactContent(viewRef.rootNodes);
28+
return renderReactContent(viewRef.rootNodes, additionalProps);
2629
}
2730

2831
/**
2932
* Wrap a function resolving to an `HTMLElement` with a `JSX.Element`.
3033
*
3134
* @param htmlRenderFunc The function to wrap
3235
* @param context The context to pass to the function
36+
* @param additionalProps optional additional props to pass to the `ReactContent` object that will render the content.
3337
*/
34-
export function renderFunc<TContext extends object>(htmlRenderFunc: (context: TContext) => HTMLElement, context?: TContext): JSX.Element {
38+
export function renderFunc<TContext extends object>(
39+
htmlRenderFunc: (context: TContext) => HTMLElement,
40+
context?: TContext,
41+
additionalProps?: ExternalReactContentProps
42+
): JSX.Element {
3543
const rootHtmlElement = htmlRenderFunc(context);
3644

37-
return renderReactContent([rootHtmlElement]);
45+
return renderReactContent([rootHtmlElement], additionalProps);
3846
}
3947

4048
/**
4149
* Wrap a `ComponentRef` with a `JSX.Element`.
4250
*
4351
* @param htmlRenderFunc The component reference to wrap
4452
* @param context The context to pass to the component as `@Input`
53+
* @param additionalProps optional additional props to pass to the `ReactContent` object that will render the content.
4554
*/
46-
export function renderComponent<TContext extends object>(componentRef: ComponentRef<TContext>, context?: TContext): JSX.Element {
55+
export function renderComponent<TContext extends object>(
56+
componentRef: ComponentRef<TContext>,
57+
context?: TContext,
58+
additionalProps?: ExternalReactContentProps
59+
): JSX.Element {
4760
Object.assign(componentRef.instance, context);
4861
componentRef.changeDetectorRef.detectChanges();
4962

50-
return renderReactContent([componentRef.location.nativeElement]);
63+
return renderReactContent([componentRef.location.nativeElement], additionalProps);
5164
}

0 commit comments

Comments
 (0)