preactjs / preact-custom-element

Wrap your component up as a custom element
MIT License
355 stars 52 forks source link

Provide light dom props children #79

Open paull39 opened 1 year ago

paull39 commented 1 year ago

This commit provides a resolution for issue #41, applying a strategy similar to the one used in pull request #56. The commit ensures that the children of a component, such as 'FooComponent', are correctly rendered even without using the Shadow DOM.

An example component which should render now correct in the not shadow-dom:

function FooComponent(props) {
    return (
        <Fragment>
            <h1>My Heading</h1>
            {/* Render the children inside this div */}
            <div>{props.children}</div>
        </Fragment>
    );
}
Schleuse commented 1 year ago

I've played around with this a bunch and this works great :+1:

But I have encountered one issue: when I don't explicitly pass shadow: false as the option the child components are getting rendered double.

Testcase

Components:

const ParentComponent: Preact.FunctionalComponent = (props) => {
  return (
    <div>
      <h1>Headline</h1>
      { props.children }
    </div>
  );
}

const ChildComponent: Preact.FunctionalComponent = (props) => {
  return (
    <div>Child</div>
  );
}

Markup:

<x-parent>
    <x-child></x-child>
    <x-child></x-child>
</x-parent>

✅ With explicit shadow: false - Works Great

register(ParentComponent, 'x-parent', [], { shadow: false });
register(ChildComponent, 'x-child', [], { shadow: false });

Results in:

<x-parent>
  <div>
    <h1>Headline</h1>
    <x-child>
      <div>Child</div>
    </x-child>
    <x-child>
      <div>Child</div>
    </x-child>
  </div>
</x-parent>

🚫 Without passing any options - Renders children both in a <slot> and as a direct child

register(ChildComponent, 'x-child', []);

Results in:

<x-parent>
  <div>
    <h1>Headline</h1>
    <slot>
      <x-child>
        <div>Child</div>
      </x-child>
      <x-child>
        <div>Child</div>
      </x-child>
    </slot>
  </div>
  <x-child>
    <div>Child</div>
  </x-child>
  <x-child>
    <div>Child</div>
  </x-child>
</x-parent>

Reason

I think the reason for this is the check for the shadow-Option in Line 202 & Line 206.

Maybe the determination of shadow mode should be consolidated with Line 13 to a local variable and then used throughout:

const isShadow = options ? options.shadow === true : false;
blopker commented 11 months ago

Would love to get this merged! Just ran into this issue. For now, I've added a global style: [slot] { display: none !important; }, but have to explicitly state all slots:

<x-button>
<span slot="child">MyButton</span>
</x-button>

Would be great to just have:

<x-button>
MyButton
</x-button>

Edit: I've just vendored this change into my codebase. Works great!

djalmajr commented 9 months ago

Any news about that?

blopker commented 9 months ago

It's funny, in practice I've found I never actually use this. I always need at least two slots in my CEs, never just one child. I've gone back to the official build for now.

djalmajr commented 9 months ago

I was wondering if porting such a popular library like Ant Design would be possible using web-components... Here's an example usage: https://codepen.io/djalmajr/pen/OJdoxqQ

But this "little problem" makes it unfeasible.