Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 40 additions & 18 deletions libs/core/src/lib/components/wrapper-component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import { Many } from '../declarations/many';
import { ReactContentProps } from '../renderer/react-content';
import { isReactNode } from '../renderer/react-node';
import { isReactRendererData } from '../renderer/renderer';
import { createComponentRenderer, createHtmlRenderer, createTemplateRenderer } from '../renderer/renderprop-helpers';
import { createComponentRenderer, createHtmlRenderer, createTemplateRenderer, RenderPropContext } from '../renderer/renderprop-helpers';
import { toObject } from '../utils/object/to-object';
import { afterRenderFinished } from '../utils/render/render-delay';
import { unreachable } from '../utils/types/unreachable';
Expand All @@ -40,11 +40,20 @@ export interface RenderComponentOptions<TContext extends object> {
readonly injector: Injector;
}

/**
* This interface allow to intercept and modify the default props used by defaultRender
*/
export interface RenderPropOptions<TContext extends object> {
readonly getProps: (defaultProps?: TContext) => TContext;
}

export type InputRendererOptions<TContext extends object> =
| TemplateRef<TContext>
| ((context: TContext) => HTMLElement)
| ComponentRef<TContext>
| RenderComponentOptions<TContext>;
| RenderComponentOptions<TContext>
| RenderPropContext<TContext>
| RenderPropOptions<TContext>;

export type JsxRenderFunc<TContext> = (context: TContext) => JSX.Element;

Expand Down Expand Up @@ -214,14 +223,18 @@ export abstract class ReactWrapperComponent<TProps extends {}> implements AfterV
}

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

// Call the function again with the created ComponentRef<TContext>
return this.createInputJsxRenderer(componentRef, additionalProps);
if (input.hasOwnProperty('componentType')) {
const { componentType, factoryResolver, injector } = <any>input;
const componentFactory = factoryResolver.resolveComponentFactory(componentType);
const componentRef = componentFactory.create(injector);

// Call the function again with the created ComponentRef<TContext>
return this.createInputJsxRenderer(componentRef, additionalProps);
} else {
// it's needed to avoid typescript error on unreachable(input)
return undefined;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is this undefined in the DefaultRenderOptions case?

Copy link
Contributor

@bengry bengry Jan 28, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand the technical reason of why needing to handle this case is here - but why is returning undefined specifically? Shouldn't there be a render function call here, or rather, a function that creates a render function.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only because unreachable(input) throw an error during the compilation.
The execution never reach that line of code because the method createRenderPropHandler executes the cases RenderPropOptions and RenderPropContext.

}
}

unreachable(input);
}

Expand All @@ -238,10 +251,19 @@ export abstract class ReactWrapperComponent<TProps extends {}> implements AfterV
additionalProps?: ReactContentProps;
}
): (props?: TProps, defaultRender?: JsxRenderFunc<TProps>) => JSX.Element | null {
const renderer =
(options && options.jsxRenderer) ||
this.createInputJsxRenderer(renderInputValue, options && options.additionalProps);
if (typeof renderInputValue === 'object') {
if (renderInputValue.hasOwnProperty('render')) {
return (<any>renderInputValue).render;
}

if (renderInputValue.hasOwnProperty('getProps')) {
return (props?: TProps, defaultRender?: JsxRenderFunc<TProps>) => {
return typeof defaultRender === 'function' ? defaultRender((<any>renderInputValue).getProps(props)) : null;
};
}
}

const renderer = (options && options.jsxRenderer) || this.createInputJsxRenderer(renderInputValue, options && options.additionalProps);
return (props?: TProps, defaultRender?: JsxRenderFunc<TProps>) => {
if (!renderInputValue) {
return typeof defaultRender === 'function' ? defaultRender(props) : null;
Expand All @@ -265,7 +287,7 @@ export abstract class ReactWrapperComponent<TProps extends {}> implements AfterV
throw new Error(
`[${(this.elementRef
.nativeElement as HTMLElement).tagName.toLowerCase()}] React wrapper components cannot have the '${
attr.name
attr.name
}' attribute set. Use the following alternative: ${alternativeAttrName || ''}`
);
}
Expand All @@ -284,11 +306,11 @@ export abstract class ReactWrapperComponent<TProps extends {}> implements AfterV
const eventHandlersProps =
eventListeners && Object.keys(eventListeners).length
? toObject(
Object.values(eventListeners).map<[string, React.EventHandler<React.SyntheticEvent>]>(([eventListener]) => [
eventListener.type,
(ev: React.SyntheticEvent) => eventListener.listener(ev && ev.nativeEvent),
])
)
Object.values(eventListeners).map<[string, React.EventHandler<React.SyntheticEvent>]>(([eventListener]) => [
eventListener.type,
(ev: React.SyntheticEvent) => eventListener.listener(ev && ev.nativeEvent),
])
)
: {};
{
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ export class FabBaseTextFieldComponent extends ReactWrapperComponent<ITextFieldP
@Output() readonly onChange = new EventEmitter<{ event: Event; newValue?: string }>();
@Output() readonly onBeforeChange = new EventEmitter<{ newValue: any }>();
@Output() readonly onNotifyValidationResult = new EventEmitter<{ errorMessage: string; value: string | undefined }>();
@Output() readonly onBlur = new EventEmitter<{ event: Event }>();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please move these changes to a separate PR. It makes it easier to maintain and create the changelog (this part of the PR looks fine as it is, so if you move it it can get it as-is)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, I'll try to do it but.... I don't know so well GIT :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Basically create a new branch from master and make the same changes there (or you can cherry-pick the commit using git cherry-pick 95e1991 when you're on the new branch.
Then create a new PR from it.


/* Non-React props, more native support for Angular */
// support for two-way data binding for `@Input() checked`.
Expand All @@ -84,6 +85,7 @@ export class FabBaseTextFieldComponent extends ReactWrapperComponent<ITextFieldP
this.onChangeHandler = this.onChangeHandler.bind(this);
this.onBeforeChangeHandler = this.onBeforeChangeHandler.bind(this);
this.onNotifyValidationResultHandler = this.onNotifyValidationResultHandler.bind(this);
this.onBlurHandler = this.onBlurHandler.bind(this);
}

ngOnInit() {
Expand All @@ -95,7 +97,6 @@ export class FabBaseTextFieldComponent extends ReactWrapperComponent<ITextFieldP

onChangeHandler(event: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>, newValue?: string) {
this.onChange.emit({ event: event.nativeEvent, newValue });

this.valueChange.emit(newValue);
}

Expand All @@ -106,4 +107,8 @@ export class FabBaseTextFieldComponent extends ReactWrapperComponent<ITextFieldP
onNotifyValidationResultHandler(errorMessage: string, value: string | undefined) {
this.onNotifyValidationResult.emit({ errorMessage, value });
}

onBlurHandler(event: React.FocusEvent<HTMLInputElement | HTMLTextAreaElement>) {
this.onBlur.emit({ event: event.nativeEvent });
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ import { FabBaseTextFieldComponent } from './base-text-field.component';
[Change]="onChangeHandler"
[BeforeChange]="onBeforeChangeHandler"
[NotifyValidationResult]="onNotifyValidationResultHandler"
[Blur]="onBlurHandler"
>
</MaskedTextField>
`,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ import { FabBaseTextFieldComponent } from './base-text-field.component';
[Change]="onChangeHandler"
[BeforeChange]="onBeforeChangeHandler"
[NotifyValidationResult]="onNotifyValidationResultHandler"
[Blur]="onBlurHandler"
>
</TextField>
`,
Expand Down