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

Spreadable Arguments #593

Open
wants to merge 13 commits into
base: master
Choose a base branch
from

Conversation

Alonski
Copy link
Member

@Alonski Alonski commented Feb 12, 2020

@Alonski
Copy link
Member Author

Alonski commented Feb 12, 2020

Discussion in fork PR:
Alonski#1

Comment on lines 93 to 94
<!-- Using the same rule on arbitrary spreads though, we no longer have context -->
<SubComponent ...{{this.someSpecificAttrs}} ...{{this.someSpecificArgs}}></SubCompnoent>
Copy link
Contributor

Choose a reason for hiding this comment

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

Arbitrary spreads would be an awesome addition! I'm unsure, whether they might explode the scope of this RFC, but I would still love to have them eventually!


A possible component in the wild that could benfit from this could be
[ember-power-select](https://github.com/cibernox/ember-power-select/blob/master/addon/templates/components/power-select.hbs).
This component has to explicitely pass on many arguments to its child components. With "splarguments" half or more of this template could be cut down.
Copy link
Contributor

@simonihmig simonihmig Feb 13, 2020

Choose a reason for hiding this comment

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

In case someone needs further proof how useful this could be, I'll gladly show you this beauty:
https://github.com/kaliber5/ember-bootstrap-power-select/blob/master/addon/templates/components/bs-form/element/control/power-select.hbs
😱🤯🤪

<SubComponent ...{{this.someSpecificAttrs}} ...{{this.someSpecificArgs}}></SubCompnoent>

<!-- Maybe prepending `@`? -->
<SubComponent ...attributes ...@arguments></SubComponent>
Copy link

@BobrImperator BobrImperator Feb 13, 2020

Choose a reason for hiding this comment

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

I think having an ...@arguments would be amazing, it greatly corresponds to what We have now

@averydev
Copy link

averydev commented Feb 14, 2020

This is awesome.
What about abstracting a standalone ...@?
I'm new to some of this, but it would be very cool to be able to do something like:

<!-- HBS-->
<MyButton ...@buttonArgs/>
// component.js
get buttonArgs(){
    return {color: "blue"}
}

In that case ...@arguments is the equivalent of ...@args

@nightire
Copy link

@averydev Though it is a cool idea, what about there is an argument named @buttonArgs as well?

The reason we use @ or this. in .hbs files is we want to avoid ambiguous references, so it should be like ...this.buttonArgs in your case if it becomes a real feature.

@averydev
Copy link

@nightire good point. The @ does signify that it's being an argument as opposed to the local context, so my example isn't consistent with that.

I only just learned about the ...attributes feature, which is fantastic, and in my understanding relates specifically to the attributes that are applied to the element form of the component:

<!-- Parent HBS-->
<MyButton class="sandwich" @buttonText="Hi!"/>
<!-- My Button Component HBS-->
<button ...attributes>{{@buttonText}}</button>
<!-- Resuting HTML-->
<button class="sandwich">Hi!</button>

Mulling on it a bit more,...attributes is special purpose, and if you {{log attributes}} you get undefined (for good reason). My thought is that it should remain an outlier and continue to stand out, rather than expanding the use of stand-alone ....

How about this: {{...someObject}}

<!-- A local object-->
<MyButton ...attributes {{...this.buttonArgs}}/>

<!-- A proxy, most likely just component arguments-->
<MyButton ...attributes {{...this.args}}/>

<!-- An object / hash passed in as an argument-->
<MyButton ...attributes {{...@someHash}}/>

That makes the whole thing pretty simple to fit into the current mental model. There's just a {{...}} helper that splats an object or proxy into @arguments on a component.

I'm not sure what would happen if the target isn't a component... either it would splat key/values onto the element as attributes onto the element, or ignore them entirely. V0 probably just ignore them for simplicity sake:

Option 1:

<!-- Template-->
<button {{...this.buttonArgs}}>Hi!</button>

//JS:
get buttonArgs(){
return {
  title:"ohhai"
}

<!-- Result -->
<button title="ohhai">Hi!</hi>

Option 2

<!-- Template-->
<button {{...this.buttonArgs}}>Hi!</button>

<!--Result -->
<button>Hi!</hi>

@Alonski
Copy link
Member Author

Alonski commented Feb 15, 2020

@averydev I had a similar idea to creating a custom helper such as {{...this.someObj}} but @pzuraq explained to me via DM that this would be a custom helper and as such makes less sense.
In the Alternatives section I mention the possibility of arbitrary spreads of attributes and arguments like this:

<SubComponent ...{{this.someSpecificAttrs}} ...@{{this.someSpecificArgs}}></SubComponent>

@jelhan
Copy link
Contributor

jelhan commented Feb 16, 2020

I'm not convinced by the ...@arguments syntax to be honest. I fear just doing the same as for attributes is not a good fit for many cases.

If the wrapper component has arguments itself, only a subset of all arguments should be passed through to the wrapped component.

But this wouldn't be supported by the proposed syntax. It would be an all or nothing choice.

I fear many developers would pass all arguments through including the ones of the wrapper component. That might work as long as these arguments aren't used by the wrapped component. But it could break at any point of time as adding new arguments wouldn't require a major version bump.

I think a syntax that covers most use cases must support at least exclusion of some arguments. But such a syntax might get so complex that directly adding support for arbitrary arguments might be more valuable as that one supports even more use cases (e.g. dynamic component invocation with different arguments depending on the component).

It's another story for ... attributes. The component does not have access to these attributes. So there can't be any that are local to the component and shouldn't be passed through.

TL;DR: The proposed syntax would only support a few use cases. Adding support for arbitrary splat arguments directly should be the focus in my opinion.

@pzuraq
Copy link
Contributor

pzuraq commented Feb 16, 2020

@jelhan that’s a very valid concern, and actually exactly why this proposal is what it is. This is already a common issue with ...attributes for instance.

The issue is that creating a fully general spread syntax, that doesn’t just apply to this special case, opens up a large can of worms. What about spreading within helpers and modifiers? What about spreading objects that aren’t ...attributes or ...@arguments? What about spreading collections other than objects (e.g. arrays, sets, maps). Should you be able to destructure as well as spread? Etc.

The goal was to keep the scope of this particular proposal small, without limiting future possibilities, so that we could iterate and continue to add features just like the ones you’ve outlined. I personally would like to see a very generalized spread syntax, as well as the ability to be more precise when spreading and applying attributes, but I think this would make sense as a first step in that direction, and would help solve some immediate needs now.

@jelhan
Copy link
Contributor

jelhan commented Feb 17, 2020

I agree that we should try to solve easy cases first. But I fear that the proposed feature would only support very few use cases. It's easy to assume that it would support more use cases than it can. I think this paragraph in the RFC is misleading:

A possible component in the wild that could benfit from this could be ember-power-select. This component has to explicitely pass on many arguments to its child components. With "splarguments" half or more of this template could be cut down.

This is only true if the child component is provided by the same addon. That's the only scenario in which passing through more arguments than needed is safe. Otherwise it's a footgun cause the child component could add an additional argument that clashes with arguments of the wrapper component in any minor release.

I think this limitation should at least be mentioned in the RFC. And the Ember Power Select example should be updated to avoid wrong assumptions about covered use cases.

I fear an example of such a wrong assumption with ember-bootstrap-power-select by @simonihmig. I agree that it would profit a lot from being able to pass through arguments but I don't think the proposed ...@arguments feature could be used for it. The component provided by that addon passes most of it's arguments through to Ember Power Select but not all. The wrapper component has a optionLabelPath argument of it's own. Passing that one also would break as soon as Ember Power Select adds an argument with that name itself.

@mehulkar
Copy link
Contributor

Generally in favor of some way to "passthrough" args, but in order to determine what ...@arguments does, we need to first define @arguments and its relationship to this.args in glimmer component classes. E.g. if the constructor decides to mutate something inside, does @arguments get the same thing? Like @jelhan says, I think the use case is different than ...attributes since arguments are much more flexible and used for many more things.

@pzuraq
Copy link
Contributor

pzuraq commented Feb 17, 2020

@mehulkar this.args is immutable, so I don't think we need to worry about issues of syncing state between the two. I do think a slightly more formal definition would be nice, and I think it would be something along the lines of:

@arguments is a keyword representing all of the arguments passed to the component. It directly references the arguments' original values, so the values cannot be changed or modified via the component manager or component class.

Eventually, we could add a non-keyword way to spread specific arguments (and attributes).

@nightire
Copy link

nightire commented Feb 18, 2020

I'm thinking about a "picking" ability, something like:

{{! forwarding all passing attributes }}
<div ...attributes>
  <input>
</div> 

{{! forwarding some passing attributes }}
<div ...attributes('class' 'data-theme')>
  <input ...attributes('id' 'name' 'type' 'value')>
</div>

and it can be applied to @arguments if it's a thing, make sense?

@jelhan
Copy link
Contributor

jelhan commented Feb 18, 2020

I'm thinking about a "picking" ability

The main use case of ... attributes is to support HTML attributes you aren't aware of at implementation time. Something similar is needed for arguments l. Therefore it's more about exclusion of some than inclusion of a known list in my opinion.

Not sure but exclusion of some attributes might be already supported with current syntax by setting these attributes after ... attributes to their default value. Or even undefined? 🤔

But something similar wouldn't work for ...@arguments cause they would still be present on this.args of a glimmer component. And the component may work differently if an argument is passed - even if it's value is undefinded.

@pzuraq
Copy link
Contributor

pzuraq commented Feb 18, 2020

FWIW, I think that this would make a lot of sense without a special syntax, if we gave you the ability to spread arbitrary objects:

class MyComponent extends Component {
  get restArgs() {
    let { foo, bar, ...rest } = this.args;

    return rest;
  }
}
<MySubComponent ...@{{this.restArgs}} />

For attributes, we would need to make them accessible to JS somehow I think. But like I said before, I think this could come later, potentially. It could also be added to this RFC, but that may make it take longer to get consensus on and to implement.

@gossi
Copy link

gossi commented Feb 22, 2020

My thoughts on this.

Disclaimer: I read your comments AFTER I had my thoughts sorted, so mine weren't influenced by yours but I will make connections to what you wrote.

First of all I separated the problem into a spreadable syntax and then how to combine it with arguments.

Spreadable Syntax

The idea here is that we have a KVO and when spreaded into something, it will receive the keys with their values as individual arguments. What could it look like:

class DummyComponent extends Component {
	properties = {
		foo: 'bar',
		baz: 'bam'
	};
}
{{! dummy-component/template.hbs }}
<MyComponent @params={{...this.properties}}>

and here is the Args of MyComponent:

interface MyComponentArgs {
	params: {
		foo: string;
		baz: string;
	};
}

which will receive it that way. That would then allow us to use helpers for modifying the arguments being spread in:

<MyComponent @params={{...(filter this.properties 'foo'}}>
<MyComponent @params={{...(assign this.properties this.moreProperties}}>

(I moved @pzuraq example code from TS to hbs)

I didn't thought about all the technical challenges here. Luckily we have a @pzuraq for this:

What about spreading objects that aren’t ...attributes or ...@arguments? What about spreading collections other than objects (e.g. arrays, sets, maps). Should you be able to destructure as well as spread? Etc.

Simply spoken: the spreadable syntax must make sure to pass on a KVO that keeps things such as tracking intact.

Spreadable Arguments

This needs two things: A reserved name and a special slot to pass them down. I think we can all easily agree on using @arguments as the reserved name, which then would open up two possible ways to pass them down:

<MyComponent {{...@arguments}}>
<MyComponent @arguments={{...@arguments}}>

Arguments are available in the backing up component as this.args but not available in the template with a reserved word. The question here is do we need a reserved word? Can we do this instead:

<MyComponent {{...this.args}}>

Technically this would work I guess ;) but would disconnect the idea of arguments being prefixed with @-sigils and make it harder to visually identify them in the template. Basically we would have something like this:

Template Code
@arguments this.args

-> shouldn't they be named the same then?

Serving Multiple Problems

By accident I'd say this would allow to address the problems @jelhan was describing:

I'm not convinced by the ...@arguments syntax to be honest. I fear just doing the same as for attributes is not a good fit for many cases.

Now that would be possible:

<MyComponent {{...(filter @arguments 'foo' 'bar')}} @subsetForSpecialUseCase={{...(assign (filter @arguments 'baz') this.props)}}>

which can be consumed as such:

{{! my-component/template.hbs }}
<MyHeavyComponentBuilder {{...@arguments}} as |builder|>
	<builder.Content {{...@subsetForSpecialUseCase}}>
		{{yield}}
	</builder.Content>
</MyHeavyComponentBuilder>

Synchronosity between @arguments and attributes

As attributes and arguments might work similarly here (on a syntax level) - it might be helpful to think about one pattern of syntax that serves both parameters and would only be need to learned once instead of two different patterns an ember developer has to learn for things that look very similar.

As much as I want this, I can't at the moment see a way to make this happen. Yet I want to make everybody aware that ...attributes and ...@arguments shouldn't be handled much differently from a consumer perspective.

Moving forward

I think it is super fine to keep the scope for the first implementation small and provide solutions for immediate problems. However, the whole context need to be thought through to the end at first and then sliced into implementation chunks.

As much as I want this, I don't want it to be a blocker for ourselves moving forward in half a year or year.

@pzuraq
Copy link
Contributor

pzuraq commented Feb 22, 2020

{{! dummy-component/template.hbs }}
<MyComponent @params={{...this.properties}}>

This seems off to me. Let's think about how this would translate into JS:

let args = {
  params: ...this.properties
};

This isn't valid syntax. The issue is we need to spread into something.

let args = {
  params: { ...this.properties }
};

The natural analog in HBS would be something like:

<MyComponent @params={{hash ...this.properties}}>

The spreadable syntax must make sure to pass on a KVO that keeps things such as tracking intact.

I think that this would be really unfortunate. It would be very convenient to be able to spread an array of arguments into a helper.

{{foo ...this.params}}

Or, thinking about the opposite. Using rest syntax within a block:

<MyComponent as |...params|>
  {{foo ...params}}
</MyComponent>

This would absolutely be valuable. I think the key thing to figure out is for a handlebars expression, how do we tell if a value is meant to be spread as an object or an array. In JS, we know by the context of the spread:

foo(...arr); // spreads like array
let bar = [...arr]; // spreads like array
let baz = { ...arr }; // spreads like object, even though it's an array

Unfortunately we don't have a way to distinguish with mustache statements.

<!-- foo can receive both named and position args, so ...params could be targeting either -->
{{foo ...params}}

I was hoping to punt on this. I think one option would be to allow @ syntax specifically for this within a helper:

<!-- ...params spreads as an array, ...@params spreads as an object -->
{{foo ...params ...@params}} 

But this seems bad to me, since @ isn't used for anything else in that context. Open to other suggestions here as well.

I think it is super fine to keep the scope for the first implementation small and provide solutions for immediate problems. However, the whole context need to be thought through to the end at first and then sliced into implementation chunks.

Agreed here, in that I think we need to make sure we aren't designing a syntax that would prevent future possibilities. I don't think we need to have the whole plan nailed down to land this first though.

@gossi
Copy link

gossi commented Feb 22, 2020

I think the key thing to figure out is for a handlebars expression, how do we tell if a value is meant to be spread as an object or an array.

I was only thinking about the case for them to be passed as KVO/object - I think I made it myself to easy because:

I think that this would be really unfortunate. It would be very convenient to be able to spread an array of arguments into a helper.

I love to extend my thinking on that. Thank you for all that examples you put to it. But as you also said (regarding {{foo ...params ...@params}}):

But this seems bad to me, since @ isn't used for anything else in that context. Open to other suggestions here as well.

That's what I avoided with the limited scope of only treating hashes. The obvious parts then would be:

<MyComponent {{hash (assign ...@arguments (foo (array ...this.props)))}}

I don't like it ! Putting regular javascript around:

<MyComponent {{hash (assign { ...@arguments } (foo [ ...this.props ]))}}

or in simplified version for spreadable args only:

<MyComponent {{hash { ...@arguments }}}

I'd consider it the verbose way that needs syntactic sugar now - would you agree?

@chriskrycho
Copy link
Contributor

I came across a significant piece of additional motivation for this RFC this week and wanted to write it down before I forgot. There is actually a significant gap in what you can do by manually forwarding arguments. Namely: you cannot forward a non-defined argument as non-defined (as opposed to explicitly defining an argument with a value of undefined). This is primarily an interop hazard when wrapping Classic components, so the motivation will decrease over time, but it's visible in a variety of ways at runtime regardless.

Consider this example Classic component as a simple way to motivate the example:

// example.js
import Component from '@ember/component';

export default Component.extend({
  value: 123,
});
{{! example.hbs }}
<p>The value is: {{this.value}}</p>

Because of how arguments are set on the class, overriding the default value from the prototype, users could simply invoke example without an argument and get the default value, because nothing was overriding it.1 So this invocation—

<Example />

—would render this HTML:

<p>The value is: 123</p>

Explicitly passing a value argument would override it. So this invocation—

<Example @value={{456}} />

—would render this HTML:

<p>The value is: 456</p>

...including if you passed undefined explicitly:

<Example @value={{undefined}} />
<p>The value is: </p>

This last bit is the key here, because if we want to wrap a Classic component—say, just hypothetically 😭 because you needed to A/B test the rollout of the conversion of important component in your app to Octane—then you would need to do something like this:

{{#if useNew}}
  <ExampleOctane @value={{@value}} />
{{else}}
  <Example @value={{@value}} />
{{/if}}

This works fine when the caller does pass a value. But when the caller does not pass a value, the result is that we pass <Example @value={{@value}} /> and the {{@value}} is undefined—but we pass it in explicitly, so we are in the third example above.

This all makes perfect sense if we think in terms of JS. Object spread by definition does not include any keys which are not defined on the source objects (how could it?):

const args = {};
const result = { value: 123, ...args }; // { value: 123 };
const args = { value: 456 };
const result = { value: 123, ...args }; // { value: 456 };
const args = { value: undefined };
const result = { value: 123, ...args }; // { value: undefined };

You can work around this, using the {{component}} helper, but it is quite annoying: it means that this concern leaks out of wherever you are doing this forwarding and becomes something callers have to care about.

example of using component

We can create a helper which returns the right class based on a condition. (This example assumes the first-class component feature from Ember 3.25 and the service import from Ember 4.1; you can do the same thing with string names earlier than that.)

import Helper from '@ember/component/helper';
import { service } from '@ember/service';
import ExampleOctane from '../components/example-octane';
import Example from '../components/example';

export default class FeatureTestedExample extends Helper {
  @service featureFlag;

  compute() {
    return this.featureFlags.isEnabled('my-new-thing')
      ? ExampleOctane
      : Example;
  }
}
{{#let (component (feature-tested-example)) as |Example|}}
  <Example />
{{/let}}

(In some cases, you could also do this by yielding a component, but in the case where I hit this, that would have required updating dozens of call sites in a fairly risky way.) Although this workaround gets the job done, it is suggestive of the challenges here, and it is also not intuitive or obvious. And while the problems here are most obvious for Classic components (indeed, this kind of thing was one of several major motivations for putting args in their own object instead of directly onto the backing class), it would similarly be a problem for anything which did actually depend on the difference between an unset field and a *field with value of undefined. ...@arguments would solve that neatly.

Footnotes

  1. Whether this exact approach was a good pattern or not is a separate issue; in practice this was very common and indeed widely recommended.

@amk221
Copy link

amk221 commented Mar 10, 2022

I would like spreadable arguments to avoid having to do this:

{{! simplified example! }}
{{#if @model}}
  <LinkTo @route={{@route}} @model={{@model}}>{{yield}}</LinkTo>
{{else if @models}}
  <LinkTo @route={{@route}} @models={{@models}}>{{yield}}</LinkTo>
{{else}}
  <LinkTo @route={{@route}}>{{yield}}</LinkTo>
{{/if}}

One would expect to be able to write:

<LinkTo
  @route={{@route}}
  @model={{@model}}
  @models={{@models}}
>
  {{yield}}
</LinkTo>

And would prefer to write:

<LinkTo ...@arguments>{{yield}}</LinkTo>

But like @chriskrycho says, you can't forward a non defined argument. So this particular example results in You cannot provide both the '@model' and '@models'

@wagenet
Copy link
Member

wagenet commented Jul 24, 2022

Is there still interest in moving this forward?

@NullVoxPopuli
Copy link
Contributor

yes. I think it's spread is a suuuuuper nice feature for any language to have in 2020+

@Alonski
Copy link
Member Author

Alonski commented Jul 24, 2022

I've had this on my todo list to work on it for almost 2 years 🙃

@chbonser
Copy link

Agreed. Would love to have this feature! I echo @amk221's use case.

@luxzeitlos
Copy link

Indeed, it would be amazing to have this!


@amk221 indeed LinkTo is a place where I missed this a few times. While the solution is a getter like this:

get models() {
  return this.args.model ? [this.args.model] : this.args.models ?? [];
}

and just pass @models={{this.models}}.

this is really not as elegant as <LinkTo ...@arguments>. Especially when new arguments are added to <LinkTo, this would still work, which IMHO is much more important, allowing me to write more future-proof wrapper-components.

@hamish-later
Copy link

This would be huge!

@wagenet wagenet added the S-Proposed In the Proposed Stage label Dec 2, 2022
@wagenet wagenet added S-Exploring In the Exploring RFC Stage and removed S-Proposed In the Proposed Stage labels Jan 27, 2023
@Alonski
Copy link
Member Author

Alonski commented Feb 2, 2023

Does anyone have the bandwidth to help me/others get this over the finish line?
After 3 years + a pandemic... Maybe it is time?

I feel like my legacy in the Ember community will forever be proposing this RFC and then never getting it done 😅

@wycats
Copy link
Member

wycats commented Feb 3, 2023

@Alonski we discussed this RFC today in framework core and we're very interested in moving it forward.

Ping me on Discord to schedule some office hours sessions and we'll get the ball rolling.

@Alonski
Copy link
Member Author

Alonski commented Feb 4, 2023

Dmed you @wycats exciting!

@IgnaceMaes
Copy link
Contributor

I ran into a use case today where this feature would come in handy. For complex design system components like a tooltip or a dropdown it would be great to be able to write a custom component wrapping a base component provided by an addon, but tweaking the layout and setting defaults. To avoid having to maintain the addon's component interface, passing all arguments to the underlying component would be great.

One thing I don't yet see addressed is how this would fit in with the TypeScript/Glint component interface.

Currently there is:

interface Signature {
  Args: {};
  Element: HTMLElement;
  Blocks: {};
}

How would these extra arguments be defined? And for template type checking, the target Ember component where these are used, would have to be known? (similar to Element for splattributes)

@chriskrycho
Copy link
Contributor

From the TS POV, it’s not really any different than doing it for a function, and because TypeScript is structurally typed, the rest of it "just works" in a case like this. You just need your wrapping component's signature to expose its types, possibly "proxying" at the type level if you don't otherwise care, and the rest of it would be relatively straightforward to make work, given that Args is already just an object; the type we would emit in a Glint implementation for a ...@args would be (a) just the type of Args if none had been "plucked off" or (b) a Omit<Args, WhateverWasPluckedOff>.

Imagine something like this:

import Button, { ButtonSignature } from 'my-fancy-ui-lib';
import type { TOC } from '@ember/component/template-only';

interface WrappedButtonSig {
  Args: ButtonSignature['Args'] & {
    onFocus: () => void;
    onBlur: () => void;
  };
  Blocks: ButtonSignature: ['Blocks'];
  Element: ButtonSignature['Element'];
}

export const WrappedButton: TOC<WrappedButtonSig> =
  <template>
    <Button
      {{on "focus" @onFocus}}
      {{on "blur" @onBlur}}
      ...attributes
      ...@args
    >
      {{yield}}
    </Button>
  </template>;

Notice that this is all just hypothetical; the point is that it's straightforward to see how it would work in TS.

@achambers
Copy link
Contributor

@Alonski, @marcemira and @wycats - Sounds like you folks need to get together and align on this.

@ef4
Copy link
Contributor

ef4 commented Sep 1, 2023

This seems like an obvious and uncontroversial feature but we need a "Detailed Design" that isn't "TBD".

People who are interested in this might want to attend the weekly spec meeting to get help, the next one is https://discord.gg/emberjs?event=1147176319147311184

@NullVoxPopuli
Copy link
Contributor

came across another use case for spreadable everything today.

Attributes:

  • <div ...{{entriesHere}}>
    • includes modifiers

Arguments:

  • <Comp ...@{{objectHere}}>

Blocks:

  • <Comp ...:blocks>
  • <Comp :default> (since blocks currently aren't passable
  • <Comp :loading :default>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-Exploring In the Exploring RFC Stage
Projects
None yet
Development

Successfully merging this pull request may close these issues.