-
Notifications
You must be signed in to change notification settings - Fork 71
#72 Now is possibile to pass IRenderFunction to component Render* #83
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
bengry
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@MarcOne68 Can you give an example use-case for this change + the code in the app that would use it? Why would you prefer to write your own function rather than return one of the already-available options (TemplateRef/ComponentRef/function returning an HTML element).
| } | ||
|
|
||
| if (input instanceof RenderOverride) { | ||
| return undefined; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why not return input.renderer?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How I wrote in Issue #72, these changes help me to set the styles
This is the code example:
private overrideRenderRow(props: IDetailsRowProps, defaultRender: (props: IDetailsRowProps) => JSX.Element): JSX.Element {
props.styles = MY_STYLES;
return defaultRender(props);
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds reasonable - you want to expose the native JSX to Angular-land then. This should be fine for simple cases, since JSX in Angular is something I’d like to avoid (although possible of course).
However, I suggest we change the API, to avoid the boilerplate of creating a class (which would really only have one instance), if possible.
I want to think about an API further, and see if we can somehow avoid exposing JSX to the final user (i.e. the developer using @angular-react/fabric) - perhaps allowing to pass a function that accepts TProps and returns TProps, which will then be fed to the defaultRenderer that office-ui-fabric-react exposes. If you have an idea for an API - I’m open for suggestions, and how it would work.
| 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'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please don’t import stuff from office-ui-fabric-react inside the @angular-react/core package - we want to keep it library-agnostic.
A more generic type is needed here instead therefore.
bengry
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please see my comments
| export class RenderOverride<TContext extends object> { | ||
| constructor(public renderer: IRenderFunction<TContext>) { } | ||
| export interface DefaultRenderOptions<TContext extends object> { | ||
| readonly getProps: (props?: TContext) => TContext; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe rename to defaultProps, to make it clearer.
|
|
||
| // Call the function again with the created ComponentRef<TContext> | ||
| return this.createInputJsxRenderer(componentRef, additionalProps); | ||
| if ('componentType' in input) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please replace this with a user defined type guard, this function does too much at the moment (it was bearable so far since it only had to deal with one object, but in hindsight it should've extracted the "is this a RenderComponentOptions" to a user defined type guard.
Also, you want to check with hasOwnProperty, since it doesn't look up the prototype chain.
| // Call the function again with the created ComponentRef<TContext> | ||
| return this.createInputJsxRenderer(componentRef, additionalProps); | ||
| } else { | ||
| return undefined; |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
| ): (props?: TProps, defaultRender?: JsxRenderFunc<TProps>) => JSX.Element | null { | ||
| if (renderInputValue instanceof RenderOverride) { | ||
| return renderInputValue.renderer; | ||
| if ((typeof renderInputValue === 'object') && ('getProps' in renderInputValue)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please use a user-defined type guard here, as mentioned in the other comment. Making one will also make sure you can re-use the logic of it in multiple places.
|
|
||
| export class RenderOverride<TContext extends object> { | ||
| constructor(public renderer: IRenderFunction<TContext>) { } | ||
| export interface DefaultRenderOptions<TContext extends object> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thinking out load here - the DefaultRenderOptions doesn't convey the message that it allows mutating the props. If you have suggestions for a better/other name - I'm open for suggestions, but if not - please add a JDoc comment explaining what the purpose of this object is.
| @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 }>(); |
There was a problem hiding this comment.
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)
There was a problem hiding this comment.
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 :)
There was a problem hiding this comment.
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.
|
@MarcOne68 This is a good PR (or two, as I mentioned in another comment). |
Continuing work done in #83 by @MarcOne68
I defined the class RenderOverride.
You can create an instance of this class passing a IRenderFunction<...> and set the Render* property of a component with that instance.