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

[@labs/react] support multiple children with multiple slots #2985

Draft
wants to merge 7 commits into
base: main
Choose a base branch
from

Conversation

taylor-vann
Copy link
Contributor

@taylor-vann taylor-vann commented Jun 2, 2022

Related to #2734

This PR supports multiple children by assigning them to multiple slots by wrapping them in a <div styles="display: contents">.

A client can provide a children map similar to how we account for events.
The syntax is as follows:

const BasicElementComponent = createComponent(
  window.React,
  elementName,
  BasicElement,
  basicElementEvents,
  displayName,
  { <react_children_property_name>: <slot_name> },
);

Which results in a children config like:

const BasicElementComponent = createComponent(
  window.React,
  elementName,
  BasicElement,
  basicElementEvents,
  displayName,
  {
    headIcons: 'head-icons',
    content: 'content',
    tailIcons: 'tail-icons',
  },
);

Tests are added to verify rendered elements are rendered with the correct slot names.

@changeset-bot
Copy link

changeset-bot bot commented Jun 2, 2022

🦋 Changeset detected

Latest commit: 05d4b5f

The changes in this PR will be included in the next version bump.

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions
Copy link
Contributor

github-actions bot commented Jun 2, 2022

📊 Tachometer Benchmark Results

Summary

  • lit-element-list
  • lit-html-kitchen-sink
  • lit-html-repeat
  • lit-html-template-heavy
  • reactive-element-list

Results

tachometer-reporter-action v2 for Benchmarks

@taylor-vann taylor-vann marked this pull request as ready for review June 2, 2022 20:28
@taylor-vann taylor-vann self-assigned this Jun 2, 2022
@kevinpschaaf kevinpschaaf requested a review from sorvell June 2, 2022 21:57
Copy link
Member

@sorvell sorvell left a comment

Choose a reason for hiding this comment

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

See comments; we also need to add documentation to the readme for this.

key: slot,
slot,
},
children
Copy link
Member

Choose a reason for hiding this comment

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

I expected this to be an array. Why isn't it?

Copy link
Member

Choose a reason for hiding this comment

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

This is arguable and we should sync up on this before changing the code, but I think it's preferable to set slot attributes where possible rather than to create a wrapper DOM node.

However, if a wrapper node is needed, probably we should just make one and not set slots. So, this would involve iterating the children until you find a non-element node (text nodes or component references): if so making the wrapper; if not, cloneElement and set slots.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I believe React.ReactNode can be any value that react will render: string array ref node element. Children are passed through a single property name in react / jsx (or as "children"). So when a child is asked to createSlottedChild it wraps the entire prop to a slot via one wrap.

I figured this was less operations for the same behavior, choosing to wrap the entire children prop in a single function than search and create multiple. If the CSS display contents let's us avoid styling I figured this would be the most direct way to slot quickly and cheaply

A clarifying question: Are we expecting a user to pass in some value than a React.ReactNode?

Copy link
Member

Choose a reason for hiding this comment

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

Creating the wrapper DOM node itself is likely the most expensive part. If we can avoid it and just set slot attributes, we probably should. I wanted to explore what was possible via their API and so made this experiment which might be useful.

React: typeof ReactModule,
tagName: string,
elementClass: Constructor<I>,
events?: E,
displayName?: string
displayName?: string,
children?: C
Copy link
Member

Choose a reason for hiding this comment

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

I think it makes more sense to call this argument slots.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sounds good!

) => {
const Component = React.Component;
const createElement = React.createElement;
const reactChildren = children;
Copy link
Member

Choose a reason for hiding this comment

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

Why rename this? Let's just choose a better name as suggested above.

@@ -73,11 +73,18 @@ suite('createComponent', () => {
onBar: 'bar',
};

const basicElementChildren = {
Copy link
Member

Choose a reason for hiding this comment

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

Again, recommend calling this basicElementSlots

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sounds good!

@@ -333,6 +340,19 @@ suite('createComponent', () => {
assert.equal(el.firstElementChild!.localName, 'div');
});

test('can set multiple children to multiple slots', async () => {
const children = window.React.createElement('div');
const foos = window.React.createElement('div');
Copy link
Member

Choose a reason for hiding this comment

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

We need to test that you can specify a a set of children for a slot. Ideally we'd be testing with JSX here as well. I think in that case it would be a JSX fragment and without JSX I guess you can make that via a React.createElement(React.Fragment, ...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Nice! Yah I'll add that test case


assert.equal(el.childNodes.length, 3);
assert.equal(el.children?.[0].getAttribute('slot'), null);
assert.equal(el.children?.[1].getAttribute('slot'), 'slot-a');
Copy link
Member

Choose a reason for hiding this comment

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

These should be display: contents right? Let's test for that.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yah let's! Sounds good

@taylor-vann
Copy link
Contributor Author

Ugh dumb thumbs, how do I cancel this review ;_;

React: typeof ReactModule,
tagName: string,
elementClass: Constructor<I>,
events?: E,
displayName?: string
displayName?: string,
children?: C
Copy link
Member

Choose a reason for hiding this comment

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

Nit (naming): The fact that children is ambiguous enough that it needs to be renamed below is a smell... I'd prefer calling this slotMap or something like that; same with events -> eventMap (along with the types Children -> SlotMap and Events -> EventMap.

@khanh-le-otsv
Copy link

khanh-le-otsv commented Feb 17, 2023

Could I ask for why this is not being merged? Thank you.
Do you need help to move this forward?

@augustjk augustjk marked this pull request as draft January 12, 2024 18:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants