salesforce / lwc

⚡️ LWC - A Blazing Fast, Enterprise-Grade Web Components Foundation
https://lwc.dev
Other
1.63k stars 394 forks source link

Synthetic shadow rendering slots incorrectly #2029

Closed mrigdon-zz closed 3 years ago

mrigdon-zz commented 4 years ago

Description

When rendering an LWC component that has slots outside of LWC (i.e. defining it in customElements and adding it to an index.html) with synthetic shadow enabled, the slotted content is not rendered where expected -- instead it is always rendered on top of all the content in the component body.

The issue is only reproducible with synthetic shadow enabled. Here is a full example: https://github.com/mrigdon/lwc-test

Steps to Reproduce

  1. Create a component that has slot(s)
    <!-- widget.html -->
    <template>
    <div>header</div>
    <slot></slot>
    <div>footer</div>
    </template>
  2. Import the component in index.js, define in custom element registry, and bundle with rollup
    import '@lwc/synthetic-shadow';
    import Widget from 'my/widget';
    customElements.define('my-widget', Widget.CustomElementConstructor);
  3. Import the bundle and add component to index.html
    ...
    <body>
    <my-widget>content</my-widget>
    <script src="bundle.js"></script>
    </body>

Expected Results

Browser output should look like this:

header
content
footer

Actual Results

Browser output looks like this (content rendered before header):

content
header
footer

When commenting out the synthetic shadow import, the output is correct.

Browsers Affected

Version

Possible Solution

n/a

Additional context/Screenshots

git2gus[bot] commented 4 years ago

This issue has been linked to a new work item: W-8129990

FabienHuot commented 3 years ago

I have the same issue. Is there something new ?

FabienTaillon commented 3 years ago

I'm having this issue too.

The weird thing is that it doesn't work with an app created with the [create-lwc-app]:(https://github.com/muenzpraeger/create-lwc-app) tool image

However, the exact same OSS code is working on webcomponents.dev: image

Here is the working link: https://webcomponents.dev/edit/4JD17Tza34ZLLZ8HyCeE

Both seems to use the synthetic shadow, so I don't know why the webcomponents.dev works. I don't know where to check which version of the lwc compiler is used for both of them.

FabienTaillon commented 3 years ago

@Gr8Gatsby any input on a fix for this one ? You flagged it as P1 more than 7 months ago and is quite a pain for OSS projects.

caridy commented 3 years ago

Ok, sorry for not paying attention to this issue, somehow it escapes my nets.

This is not a bug, but a limitation of synthetic shadow. A root element (from LWC or from a web component of an LWC) does not accept content to be slotted. We are not going to attempt to make this work, instead, the workaround is quite simple, you create another component that does not expect content, and set the content for my-widget inside its template, and everything will work just fine.

To give you more details, this is the number one reason why in Aura you can't have a LWC in the markup while passing content into it.

FabienTaillon commented 3 years ago

@caridy thank you for the clarification. If I understood correctly, this problem should only happen at the root level, but not for child components, am I right ?

We have an internal project where we're having the same issue but not at the root level. For instance here we have a texei-app component (the one created with createElement), including a texei-child component, including a lightning-card where we can see that the slots aren't rendered correctly even though it's not at the root.

image

I can see the same result as on this issue.

I can't reproduce it on webcomponents.dev though.

If you confirm that it's supposed to work, I'll try to clean the project to have a repro on a public repo.

FabienTaillon commented 3 years ago

Also it doesn't work with a custom component so it's not an issue with base components: image

FabienTaillon commented 3 years ago

@caridy I was finally able to find what's the difference that makes slots working or not.

They are working when import are done in this order:

import '@lwc/synthetic-shadow';
import { createElement } from 'lwc';

However it's not working the other way round:

import { createElement } from 'lwc';
import '@lwc/synthetic-shadow';

I created 2 repos and heroku apps showing this (they are 100% the same, except this import order):

OK

Repo: https://github.com/FabienTaillon/lwc-oss-slots-ok heroku app: https://lwc-oss-slots-ok.herokuapp.com

KO

Repo: https://github.com/FabienTaillon/lwc-oss-slots-ko heroku app: https://lwc-oss-slots-ko.herokuapp.com

I used this blog post to create these apps.

Beside the issues already link to this one, this may also be the issue with some others like #2147.

caridy commented 3 years ago

Ah, yes, synthetic shadow MUST be loaded first. Loading it after LWC could have very unpredictable results. cc @nolanlawson @pmdartus.

To provide more details on "why", it is because LWC will cache certain DOM APIs for better performance to avoid lookups on objects. If those APIs are patched after LWC code evaluates, then LWC might be calling the old version, not the patched one. Remember that any polyfill must be loaded first, and synthetic shadow is not exception to that rule.