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

bug: Slot content went missing within dynamic component #4284

Closed
3 tasks done
AaronKow opened this issue Apr 19, 2023 · 7 comments · Fixed by #5120
Closed
3 tasks done

bug: Slot content went missing within dynamic component #4284

AaronKow opened this issue Apr 19, 2023 · 7 comments · Fixed by #5120
Assignees
Labels
Bug: Validated This PR or Issue is verified to be a bug within Stencil slot-related

Comments

@AaronKow
Copy link

AaronKow commented Apr 19, 2023

Prerequisites

Stencil Version

3.2.1

Current Behavior

I have a Stencil web component with an <a> tag element that needs to be changed to <p> tag to prevent the user from clicking it.
The dynamic component is working, however I noticed that the content that goes to the went missing.

I have a simple component code:

import { Component, State, h } from '@stencil/core';

@Component({
  tag: 'my-component',
  styleUrl: 'my-component.css',
  shadow: false,
})
export class MyComponent {
  @State() customTag: string = 'a';

  componentDidLoad() {
    setTimeout(() => {
      this.customTag = 'p';
      alert("I'm changed!");
    }, 3000);
  }

  render() {
    return (
      <this.customTag href="#">
        <slot />
      </this.customTag>
    );
  }
}

In my index.html:

<body>
  <my-component>This is a sample link</my-component>
</body>

It shows the slot content initially, then I tried to change the element dynamically using timeout after 3 seconds.
Then the slot content went missing as show in GIF below:

error-1

Expected Behavior

We have the expectation that the slot content still persist after the component tag is changed after 3s:

expected

System Info

Node version: v18.16.0
NPM version: v9.5.1
Computer: MacOS (Ventura 13.2.1)
Browser: Tried on both Firefox (12.0.1 (64-bit)) and Chrome (112.0.5615.49)

Steps to Reproduce

To replicate it, I did it with Stencil Component Starter.
Please refer to the link below for reproduction.

Code Reproduction URL

https://github.com/AaronKow/stencil-slot-missing-issue

Additional Information

No response

@alicewriteswrongs
Copy link
Contributor

Hey @AaronKow, thanks for filing this issue and for providing a great reproduction case!

I was just able to confirm that although the dynamic <a> -> <p> transition works correctly the slot content is, unfortunately, lost to space and time.

I believe this is a case of an underlying issue with Stencil's runtime handling of slots (see for instance these related issues #2004, #2641, #1968, #1997, #2801).

I'm going to label this for ingestion into the teams' internal backlog so we can prioritize it.

I wasn't able to quickly come up with a straightforward workaround for this issue - possibly you might be able to use a dynamically-applied CSS class to toggle whether your click in clickable or not? Regardless, the current behavior that Stencil has in this regard isn't correct, and apologies for that!

Thanks again for reporting and sorry you're running into this issue!

@LeonardLiutao
Copy link

Hey @AaronKow, thanks for filing this issue and for providing a great reproduction case!

I was just able to confirm that although the dynamic <a> -> <p> transition works correctly the slot content is, unfortunately, lost to space and time.

I believe this is a case of an underlying issue with Stencil's runtime handling of slots (see for instance these related issues #2004, #2641, #1968, #1997, #2801).

I'm going to label this for ingestion into the teams' internal backlog so we can prioritize it.

I wasn't able to quickly come up with a straightforward workaround for this issue - possibly you might be able to use a dynamically-applied CSS class to toggle whether your click in clickable or not? Regardless, the current behavior that Stencil has in this regard isn't correct, and apologies for that!

Thanks again for reporting and sorry you're running into this issue!

Hi @alicewriteswrongs

Is there any workaround if really needs a different dom?

 propA ? <div class='scrollable aaa bbb'> <slot></slot> </div> : <slot></slot>

Actuall, I don't need the div if the propA is false

  • "@stencil/core": "^2.16.0",

tanner-reits added a commit that referenced this issue Nov 30, 2023
If a slot is located in an element and that element's tag is dynamically changed (e.g. from `p` to `span`), we need to re-relocate the slot on re-render

STENCIL-672: slot element loses its parent reference and disappears when its parent is rendered conditionally

Fixes: #4284, #3913
tanner-reits added a commit that referenced this issue Nov 30, 2023
If a slot is located in an element and that element's tag is dynamically changed (e.g. from `p` to `span`), we need to re-relocate the slot on re-render

STENCIL-672: slot element loses its parent reference and disappears when its parent is rendered conditionally

Fixes: #4284, #3913
github-merge-queue bot pushed a commit that referenced this issue Dec 6, 2023
…hanges (#5120)

* fix(runtime): re-relocate slot if parent element's tagname changes

If a slot is located in an element and that element's tag is dynamically changed (e.g. from `p` to `span`), we need to re-relocate the slot on re-render

STENCIL-672: slot element loses its parent reference and disappears when its parent is rendered conditionally

Fixes: #4284, #3913

* add e2e tests

* code documentation

* put changes behind slot fix flag

* resolve new SNC

* Apply suggestions from code review

Co-authored-by: Ryan Waskiewicz <ryanwaskiewicz@gmail.com>

---------

Co-authored-by: Ryan Waskiewicz <ryanwaskiewicz@gmail.com>
github-merge-queue bot pushed a commit that referenced this issue Dec 6, 2023
…onents (#5135)

* fix(runtime): re-relocate slot if parent element's tagname changes

If a slot is located in an element and that element's tag is dynamically changed (e.g. from `p` to `span`), we need to re-relocate the slot on re-render

STENCIL-672: slot element loses its parent reference and disappears when its parent is rendered conditionally

Fixes: #4284, #3913

* add e2e tests

* code documentation

* put changes behind slot fix flag

* resolve new SNC

* reset `hidden` state of nodes on relocate

It is possible for slotted content to still be invisible in the DOM if the slot was not rendered on the first render. This commit resets the `hidden` attribute of a node on successful relocation.

STENCIL-1053

* hide slot content without a home in `scoped` components

Hides any content that is projected through to a Stencil component that does not have a destination slot. Only for `scoped` components.

Fixes #2877

STENCIL-938

* add e2e tests for hiding content without a slot

* revert karma config

* PR feedback

Co-authored-by: Christian Bromann <git@bromann.dev>

---------

Co-authored-by: Christian Bromann <git@bromann.dev>
@christian-bromann
Copy link
Member

A fix for this issue was released in v4.8.2. Make sure you enable experimentalSlotFixes in your Stencil configuration to apply it. Thanks for reporting!

@dutscher
Copy link
Contributor

@christian-bromann is the docu under extra-config https://stenciljs.com/docs/config-extras complete for experimentalSlotFixes
maybe the page needs a config example

import { Config } from '@stencil/core';
const conf: Config = { extras: { experimentalSlotFixes: true } }

I can confirm that the v4.8.2 fix our issue:

render() {
    const CustomTag = this.url ? 'a' : 'button';

    return (
      <Host aria-current={this.disabled ? 'page' : null}>
        {!this.disabled && (
          <CustomTag class="link" href={this.url}>
            {this.label}
            <slot />
          </CustomTag>
        )}
        {this.disabled && (
          <Fragment>
            {this.label}
            <slot />
          </Fragment>
        )}
      </Host>
    );
  }

Thanks for all your good work!

@dutscher
Copy link
Contributor

dutscher commented Dec 14, 2023

At my tests with experimentalSlotFixes i found a bug with the default slot when its not the first element.

Code base:

<my-comp>
    <div style="border: solid 1px red">
      Default Slot from HTML
    </div>
</my-comp>
@Component({
  tag: 'my-comp',
})
export class MyComp {
  render() {
    return (
      <my-other-comp>
        <div slot="content">
          Slot: 'content' for other-comp
          <slot />
        </div>
        <!-- default slot is not the first element -->
        <div style={{ border: 'solid 1px green', padding: '4px' }}>
          Default Text Slot
          <button>Slot: 'default' for other-comp</button>
        </div>
      </my-other-comp>
    );
  }
}
@Component({
  tag: 'my-other-comp',
})
export class MyOtherComp {
  @State() customTag: string = 'a';

  @State() log: string = 'no rerender yet';

  componentDidLoad() {
    setTimeout(() => {
      this.customTag = 'div';
      this.log += 'first rerender';
    }, 10000);
  }

  render() {
    return (
      <this.customTag
        style={{ display: 'flex', gap: '4px', flexDirection: 'column' }}
      >
        {this.log}
        <div
          class="slot-default"
          style={{ border: 'solid 1px green', padding: '4px' }}
        >
          <slot></slot>
        </div>
        <div
          class="slot-content"
          style={{ border: 'solid 1px red', padding: '4px' }}
        >
          <slot name="content"></slot>
        </div>
      </this.customTag>
    );
  }
}

Case 1: without experimentalSlotFixes
image
After rerender in MyOtherComp the slot position is not in the red container:
image

Case 2: with experimentalSlotFixes
image
After rerender
image

The green default slot should be in the green default slot container.

Case 3: with experimentalSlotFixes and if i change in MyComp the default slot before the content slot
image
After rerender
image

Here is a stackblitz playground:
https://stackblitz.com/edit/node-199khh?file=src%2Fcomponents%2Fmy-comp.tsx

Cheers and thanks

@christian-bromann
Copy link
Member

@dutscher interesting 🤔 mind raising a new issue for this so we can triage it?

@dutscher
Copy link
Contributor

dutscher commented Dec 14, 2023

@christian-bromann yes i will do a new issue for that.
but i found another bug :D

if experimentalSlotFixes is true the <div slot="content" /> is relocated correctly but its loses the attribute slot.

<my-comp>
<div slot="content">Hello</div>
</my-comp>

-> will render into:

<my-comp>
<div>Hello</div>
</my-comp>

reproducable aswell in the stackblitz demo

in the 4.8.2 commit i see only a default slot unit test. maybe a named slot unit test will show this issue aswell :)
4303d6a

cheers

i created a issue for that #5215

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug: Validated This PR or Issue is verified to be a bug within Stencil slot-related
Projects
None yet
6 participants