From 4528b31db31fc6d3008c710934ae47647bd08edc Mon Sep 17 00:00:00 2001 From: MarcOne68 <36529713+MarcOne68@users.noreply.github.com> Date: Sun, 27 Jan 2019 10:28:01 +0100 Subject: [PATCH 1/4] #72 Use defaultRender --- .../src/lib/components/wrapper-component.ts | 48 ++++++++++++------- 1 file changed, 31 insertions(+), 17 deletions(-) diff --git a/libs/core/src/lib/components/wrapper-component.ts b/libs/core/src/lib/components/wrapper-component.ts index 77accbf0..1b1f506d 100644 --- a/libs/core/src/lib/components/wrapper-component.ts +++ b/libs/core/src/lib/components/wrapper-component.ts @@ -28,6 +28,7 @@ import { createComponentRenderer, createHtmlRenderer, createTemplateRenderer } f import { toObject } from '../utils/object/to-object'; import { afterRenderFinished } from '../utils/render/render-delay'; import { unreachable } from '../utils/types/unreachable'; +import { IRenderFunction } from 'office-ui-fabric-react'; // Forbidden attributes are still ignored, since they may be set from the wrapper components themselves (forbidden is only applied for users of the wrapper components) const ignoredAttributeMatchers = [/^_?ng-?.*/, /^style$/, /^class$/]; @@ -40,11 +41,16 @@ export interface RenderComponentOptions { readonly injector: Injector; } +export class RenderOverride { + constructor(public renderer: IRenderFunction) { } +} + export type InputRendererOptions = | TemplateRef | ((context: TContext) => HTMLElement) | ComponentRef - | RenderComponentOptions; + | RenderComponentOptions + | RenderOverride; export type JsxRenderFunc = (context: TContext) => JSX.Element; @@ -213,6 +219,10 @@ export abstract class ReactWrapperComponent implements AfterV return (context: TContext) => htmlRenderer.render(context); } + if (input instanceof RenderOverride) { + return undefined; + } + if (typeof input === 'object') { const { componentType, factoryResolver, injector } = input; const componentFactory = factoryResolver.resolveComponentFactory(componentType); @@ -238,17 +248,21 @@ export abstract class ReactWrapperComponent implements AfterV additionalProps?: ReactContentProps; } ): (props?: TProps, defaultRender?: JsxRenderFunc) => JSX.Element | null { - const renderer = - (options && options.jsxRenderer) || - this.createInputJsxRenderer(renderInputValue, options && options.additionalProps); - - return (props?: TProps, defaultRender?: JsxRenderFunc) => { - if (!renderInputValue) { - return typeof defaultRender === 'function' ? defaultRender(props) : null; - } + if (renderInputValue instanceof RenderOverride) { + return renderInputValue.renderer; + } else { + const renderer = + (options && options.jsxRenderer) || + this.createInputJsxRenderer(renderInputValue, options && options.additionalProps); + + return (props?: TProps, defaultRender?: JsxRenderFunc) => { + if (!renderInputValue) { + return typeof defaultRender === 'function' ? defaultRender(props) : null; + } - return renderer(props); - }; + return renderer(props); + }; + } } private _passAttributesAsProps() { @@ -265,7 +279,7 @@ export abstract class ReactWrapperComponent 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 || ''}` ); } @@ -284,11 +298,11 @@ export abstract class ReactWrapperComponent implements AfterV const eventHandlersProps = eventListeners && Object.keys(eventListeners).length ? toObject( - Object.values(eventListeners).map<[string, React.EventHandler]>(([eventListener]) => [ - eventListener.type, - (ev: React.SyntheticEvent) => eventListener.listener(ev && ev.nativeEvent), - ]) - ) + Object.values(eventListeners).map<[string, React.EventHandler]>(([eventListener]) => [ + eventListener.type, + (ev: React.SyntheticEvent) => eventListener.listener(ev && ev.nativeEvent), + ]) + ) : {}; { } From f0d1b3f5a8dc8b4325e9a318f907cc251370f806 Mon Sep 17 00:00:00 2001 From: MarcOne68 <36529713+MarcOne68@users.noreply.github.com> Date: Mon, 28 Jan 2019 14:04:05 +0100 Subject: [PATCH 2/4] #72 Request Change --- .../src/lib/components/wrapper-component.ts | 33 ++++++++++--------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/libs/core/src/lib/components/wrapper-component.ts b/libs/core/src/lib/components/wrapper-component.ts index 1b1f506d..76d569f2 100644 --- a/libs/core/src/lib/components/wrapper-component.ts +++ b/libs/core/src/lib/components/wrapper-component.ts @@ -28,7 +28,6 @@ import { createComponentRenderer, createHtmlRenderer, createTemplateRenderer } f import { toObject } from '../utils/object/to-object'; import { afterRenderFinished } from '../utils/render/render-delay'; import { unreachable } from '../utils/types/unreachable'; -import { IRenderFunction } from 'office-ui-fabric-react'; // Forbidden attributes are still ignored, since they may be set from the wrapper components themselves (forbidden is only applied for users of the wrapper components) const ignoredAttributeMatchers = [/^_?ng-?.*/, /^style$/, /^class$/]; @@ -41,8 +40,8 @@ export interface RenderComponentOptions { readonly injector: Injector; } -export class RenderOverride { - constructor(public renderer: IRenderFunction) { } +export interface DefaultRenderOptions { + readonly getProps: (props?: TContext) => TContext; } export type InputRendererOptions = @@ -50,7 +49,7 @@ export type InputRendererOptions = | ((context: TContext) => HTMLElement) | ComponentRef | RenderComponentOptions - | RenderOverride; + | DefaultRenderOptions; export type JsxRenderFunc = (context: TContext) => JSX.Element; @@ -219,17 +218,17 @@ export abstract class ReactWrapperComponent implements AfterV return (context: TContext) => htmlRenderer.render(context); } - if (input instanceof RenderOverride) { - return undefined; - } - 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 - return this.createInputJsxRenderer(componentRef, additionalProps); + if ('componentType' in input) { + const { componentType, factoryResolver, injector } = input; + const componentFactory = factoryResolver.resolveComponentFactory(componentType); + const componentRef = componentFactory.create(injector); + + // Call the function again with the created ComponentRef + return this.createInputJsxRenderer(componentRef, additionalProps); + } else { + return undefined; + } } unreachable(input); @@ -248,8 +247,10 @@ export abstract class ReactWrapperComponent implements AfterV additionalProps?: ReactContentProps; } ): (props?: TProps, defaultRender?: JsxRenderFunc) => JSX.Element | null { - if (renderInputValue instanceof RenderOverride) { - return renderInputValue.renderer; + if ((typeof renderInputValue === 'object') && ('getProps' in renderInputValue)) { + return (props?: TProps, defaultRender?: JsxRenderFunc) => { + return typeof defaultRender === 'function' ? defaultRender(renderInputValue.getProps(props)): null; + } } else { const renderer = (options && options.jsxRenderer) || From 4e5739d02c5cc70dbcfb4cf615c1deaf1668fad1 Mon Sep 17 00:00:00 2001 From: MarcOne68 <36529713+MarcOne68@users.noreply.github.com> Date: Mon, 28 Jan 2019 17:42:34 +0100 Subject: [PATCH 3/4] #72 --- .../src/lib/components/wrapper-component.ts | 49 +++++++++++-------- 1 file changed, 28 insertions(+), 21 deletions(-) diff --git a/libs/core/src/lib/components/wrapper-component.ts b/libs/core/src/lib/components/wrapper-component.ts index 76d569f2..a85d4348 100644 --- a/libs/core/src/lib/components/wrapper-component.ts +++ b/libs/core/src/lib/components/wrapper-component.ts @@ -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'; @@ -40,8 +40,11 @@ export interface RenderComponentOptions { readonly injector: Injector; } -export interface DefaultRenderOptions { - readonly getProps: (props?: TContext) => TContext; +/** + * This interface allow to intercept and modify the default props used by defaultRender + */ +export interface RenderPropOptions { + readonly getProps: (defaultProps?: TContext) => TContext; } export type InputRendererOptions = @@ -49,7 +52,8 @@ export type InputRendererOptions = | ((context: TContext) => HTMLElement) | ComponentRef | RenderComponentOptions - | DefaultRenderOptions; + | RenderPropContext + | RenderPropOptions; export type JsxRenderFunc = (context: TContext) => JSX.Element; @@ -219,18 +223,18 @@ export abstract class ReactWrapperComponent implements AfterV } if (typeof input === 'object') { - if ('componentType' in input) { - const { componentType, factoryResolver, injector } = input; + if (input.hasOwnProperty('componentType')) { + const { componentType, factoryResolver, injector } = input; const componentFactory = factoryResolver.resolveComponentFactory(componentType); const componentRef = componentFactory.create(injector); // Call the function again with the created ComponentRef return this.createInputJsxRenderer(componentRef, additionalProps); } else { + // it's needed to avoid typescript error on unreachable(input) return undefined; } } - unreachable(input); } @@ -247,23 +251,26 @@ export abstract class ReactWrapperComponent implements AfterV additionalProps?: ReactContentProps; } ): (props?: TProps, defaultRender?: JsxRenderFunc) => JSX.Element | null { - if ((typeof renderInputValue === 'object') && ('getProps' in renderInputValue)) { - return (props?: TProps, defaultRender?: JsxRenderFunc) => { - return typeof defaultRender === 'function' ? defaultRender(renderInputValue.getProps(props)): null; + if (typeof renderInputValue === 'object') { + if (renderInputValue.hasOwnProperty('render')) { + return (renderInputValue).render; } - } else { - const renderer = - (options && options.jsxRenderer) || - this.createInputJsxRenderer(renderInputValue, options && options.additionalProps); - - return (props?: TProps, defaultRender?: JsxRenderFunc) => { - if (!renderInputValue) { - return typeof defaultRender === 'function' ? defaultRender(props) : null; - } - return renderer(props); - }; + if (renderInputValue.hasOwnProperty('getProps')) { + return (props?: TProps, defaultRender?: JsxRenderFunc) => { + return typeof defaultRender === 'function' ? defaultRender((renderInputValue).getProps(props)) : null; + }; + } } + + const renderer = (options && options.jsxRenderer) || this.createInputJsxRenderer(renderInputValue, options && options.additionalProps); + return (props?: TProps, defaultRender?: JsxRenderFunc) => { + if (!renderInputValue) { + return typeof defaultRender === 'function' ? defaultRender(props) : null; + } + + return renderer(props); + }; } private _passAttributesAsProps() { From 95e19913ef405d2f7c359460f86c6791916da5f9 Mon Sep 17 00:00:00 2001 From: MarcOne68 <36529713+MarcOne68@users.noreply.github.com> Date: Tue, 29 Jan 2019 07:18:18 +0100 Subject: [PATCH 4/4] Added onBlur to TextField (future ControlValueAccessor implementation) --- .../lib/components/text-field/base-text-field.component.ts | 7 ++++++- .../components/text-field/masked-text-field.component.ts | 1 + .../src/lib/components/text-field/text-field.component.ts | 1 + 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/libs/fabric/src/lib/components/text-field/base-text-field.component.ts b/libs/fabric/src/lib/components/text-field/base-text-field.component.ts index ea11852b..d59b9349 100644 --- a/libs/fabric/src/lib/components/text-field/base-text-field.component.ts +++ b/libs/fabric/src/lib/components/text-field/base-text-field.component.ts @@ -68,6 +68,7 @@ export class FabBaseTextFieldComponent extends ReactWrapperComponent(); @Output() readonly onBeforeChange = new EventEmitter<{ newValue: any }>(); @Output() readonly onNotifyValidationResult = new EventEmitter<{ errorMessage: string; value: string | undefined }>(); + @Output() readonly onBlur = new EventEmitter<{ event: Event }>(); /* Non-React props, more native support for Angular */ // support for two-way data binding for `@Input() checked`. @@ -84,6 +85,7 @@ export class FabBaseTextFieldComponent extends ReactWrapperComponent, newValue?: string) { this.onChange.emit({ event: event.nativeEvent, newValue }); - this.valueChange.emit(newValue); } @@ -106,4 +107,8 @@ export class FabBaseTextFieldComponent extends ReactWrapperComponent) { + this.onBlur.emit({ event: event.nativeEvent }); + } } diff --git a/libs/fabric/src/lib/components/text-field/masked-text-field.component.ts b/libs/fabric/src/lib/components/text-field/masked-text-field.component.ts index 57127ac6..4afe13d0 100644 --- a/libs/fabric/src/lib/components/text-field/masked-text-field.component.ts +++ b/libs/fabric/src/lib/components/text-field/masked-text-field.component.ts @@ -58,6 +58,7 @@ import { FabBaseTextFieldComponent } from './base-text-field.component'; [Change]="onChangeHandler" [BeforeChange]="onBeforeChangeHandler" [NotifyValidationResult]="onNotifyValidationResultHandler" + [Blur]="onBlurHandler" > `, diff --git a/libs/fabric/src/lib/components/text-field/text-field.component.ts b/libs/fabric/src/lib/components/text-field/text-field.component.ts index 7aa244af..b59ebdd8 100644 --- a/libs/fabric/src/lib/components/text-field/text-field.component.ts +++ b/libs/fabric/src/lib/components/text-field/text-field.component.ts @@ -58,6 +58,7 @@ import { FabBaseTextFieldComponent } from './base-text-field.component'; [Change]="onChangeHandler" [BeforeChange]="onBeforeChangeHandler" [NotifyValidationResult]="onNotifyValidationResultHandler" + [Blur]="onBlurHandler" > `,