microsoft / angular-react

Use React components inside Angular
https://microsoft.github.io/angular-react/
MIT License
543 stars 73 forks source link

Generate components #91

Open bengry opened 5 years ago

bengry commented 5 years ago

The issue

Currently creating @angular-react components is done manually, specially in @angular-react/fabric (but the same holds true for any library implementation). This is fine for a few components, and didn't prove to be worth the effort of creating such a tool, but as the library grows, and more components are added - this seems like it would simplify the long-term maintenance, especially in regards to upgrading the underlying React UI framework (e.g. office-ui-fabric-react, Semantic-UI-React).

Proposed solution

There are a number of ways to go about this, but the most likely is to use Angular Schematics to generate components, which as part of the inputs will take some sort of identifier for a *Props interface (e.g. ICheckboxProps), and together with the TypeScript compiler API (or any of its wrappers) and add the relevant Inputs and Outputs for the component, as well as bindings for the template etc.:

  1. Props that are of type string | number | boolean | object should be added as-is. e.g: componentRef?: IRefObject<ICheckbox> -> @Input() componentRef?: ICheckboxProps['componentRef']
  2. Props that conform to (...args: any[]) => void should be added as @Outputs, with the arguments added as an object with properties the same name as the function parameters. e.g.: onChange?: (ev?: React.FormEvent<HTMLElement | HTMLInputElement>, checked?: boolean) => void -> @Output() readonly onChange = new EventEmitter<{ ev?: Event, checked?: boolean }>().
    1. Props that confirm to (...args: any[]) => *non-void* should be added as-is (see 1. above), also as @Inputs.
  3. Props that are of type IRenderFunction<T> should be added as InputRendererOptions<T>. If the prop name is prefixed with an on, omit it (the Angular doesn't let Inputs be prefixed with on), and camel-cased. e.g.: onRenderNavigation?: IRenderFunction<IPanelProps> -> @Input() renderNavigation?: InputRendererOptions<IPanelProps>.
  4. (optional) copy the JDoc as-is.

Open issues:

bengry commented 5 years ago

Exactly, along with the template itself. But that’s the ideal solution, anything that helps making authoring wrapper components would be a good start for this. Once that’s in we can iterate over it to improve it further.

There is not technical limitation here, since the TypeScript compiler API is capable of parsing interfaces & outputting classes (and when using libraries like ts-morph makes working with it even easier). I'm not sure about the template, that probably requires using the Angular Language Service API, or alternatively manually outputting the string.

On 18 Feb 2019, 22:36 +0200, Eswar Prakash notifications@github.com, wrote:

Happy to take a look at it. I am guessing you are keen to see if we can parse the prop interface definition to build the inputs and outputs for the component? — You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub, or mute the thread.

eswarpr commented 5 years ago

Sorry, no idea what happened to my comment. Guess I accidentally deleted it. I am interested in trying this out but need to have a go at the Typescript compiler bit first

eswarpr commented 5 years ago

@bengry - So, after a bit of play around with ts-morph (which by the way is brilliant!) I think we need to put together some blue prints as to the input for the schematic that will help us identify which component file and prop type to process. At a high level, we have the following options:

  1. Accept the component name - for e.g. TextField, find the component from .d.ts in the "lib" folder of "office-ui-fabric-react" folder, traverse through the base class to arrive at the input to React.Component<> base class, and identify ITextFieldProps as the prop type. Once identified, parse and generate the Fab component for it
  2. Accept the prop type - for e.g. ITextFieldProps - derive the potential file that might host it - for e.g. TextField.types.d.ts then parse and generate the Fab component for it.

Looking at the options:

(1) is easier for the end user, since all they need to know is which component we need to generate. Harder for us, since we will need to walk the inheritance tree to find the prop type

(2) is easier for us (maybe) and harder for the end user, since they will need to tell us the prop type to use by browsing the component docs in the MS site.

I like (1) for obvious reasons. Do you have any other thoughts?

bengry commented 5 years ago

I agree that (1) is probably better, especially long-term. It introduces a minor technical challenge of getting the type of props, but if needed - we could extract more from it, without breaking the API.