Skip to content
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

How to represent children for ExternalComponent #237

Open
threeseed opened this issue Feb 26, 2019 · 6 comments
Open

How to represent children for ExternalComponent #237

threeseed opened this issue Feb 26, 2019 · 6 comments
Labels
Milestone

Comments

@threeseed
Copy link
Contributor

Would be useful to have documentation for handling children. For example in this component:

https://atlaskit.atlassian.com/packages/core/form/docs/form

@shadaj
Copy link
Owner

shadaj commented Mar 1, 2019

That's a good idea! Slinky does support the children parameter, which can be defined in the case class Props as children: ReactElement*. When combined with the @react annotation, Slinky will automatically move the children parameter into a curried parameter to match the tags API.

@shadaj shadaj added the docs label Mar 1, 2019
@shadaj shadaj added this to the Backlog milestone Mar 1, 2019
@threeseed
Copy link
Contributor Author

threeseed commented Mar 14, 2019

If we take this example. It will not compile since you can't have default arguments with a *-parameter. However I am finding optional arguments to be very common amongst Javascript libraries.

@JSImport("@atlaskit/tag-group", "TagGroup")
@js.native
object ReactTagGroup extends js.Object

@react object TagGroup extends ExternalComponent {
  case class Props(alignment: Option[String] = None, children: ReactElement*)
  override val component = ReactTagGroup
}

Alternatively could you detect if I make it curried myself like this and not do anything.
Currently I get an error, "could not find implicit value for parameter pw: slinky.core.ExternalPropsWriterProvider"

  case class Props(alignment: Option[String] = None)(children: ReactElement*)

@evbo
Copy link

evbo commented May 24, 2021

UPDATE: this solution now permits default arguments and still curries the children

@shadaj @nadenf correct me if I'm wrong, but in the example @nadenf linked, for this to work children must be a function, with formProps as the argument.

I got a very basic example working to demonstrate, but hopefully there's a cleaner solution:

object Form {
  // here are the default args and curried children - gotta love the plain old Object approach
  def apply(onSubmit: () => Unit, disabled: Boolean = false)(children: FormState => ReactElement): ReactElement =
    AtlaskitForm(onSubmit, disabled, children)
}


@JSImport("@atlaskit/form", JSImport.Default)
@js.native
object AtlaskitFormLibrary extends js.Object


@react object AtlaskitForm extends ExternalComponent {
  // see my comment below - children must be a function and cannot be just a functional ReactElement like you might expect
  case class Props(onSubmit: () => Unit, isDisabled: Boolean, children: FormState => ReactElement)
  override val component = AtlaskitFormLibrary
}


@js.native
trait FormState extends js.Object {
  val formProps: FormProps = js.native
  val disabled: Boolean = js.native
  val dirty: Boolean = js.native
  val submitting: Boolean = js.native
  val getValues: () => js.Object = js.native
}


@js.native
trait FormProps extends js.Object {
  val ref: ReactRef[String] = js.native
  val onSubmit: js.Function1[SyntheticEvent[html.Form, Event], Unit] = js.native
}

So, for example this can be used like:

Form(
  onSubmit _,
)(
  (formState: FormState) => {
    form(
      ref := formState.formProps.ref,
      onSubmit := (formState.formProps.onSubmit(_))
    )(
      Button(Primary, Submit)("")
    )
  }
)

I borrowed heavily from here.

@jedahu
Copy link
Contributor

jedahu commented May 25, 2021

This PR #478 provides a workaround for the curried optional arguments problem @nadenf described here: #237 (comment). Likely not relevant to you @evbo.

@evbo
Copy link

evbo commented Jun 5, 2021

@jedahu I updated my earlier comment with an approach for currying children and supporting default args. A little extra code, but supports the general case of children being a function too

@evbo
Copy link

evbo commented Mar 16, 2022

One other side effect of the children: ReactElement* argument is it forces you to specify even if you have no children:

MyComponent(id = 1)() // without "()" you will get error missing curried argument "children"

Would be nice if behavior matched that of, say, div, where the curried children are optional:

div() // no children specified and that's okay!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants