emberjs / emberjs/ember.js

[Bug] `#in-element` cannot directly render in to shadow-dom

Open
#20,641 2 comments 0 reactions 0 assignees View on GitHub
bug
Dominant language
TypeScript
Stars
22.6k
Forks
4.2k
Avg merge
3d 12h
Merged PRs (30d)
15

Description

### 🐞 Describe the Bug

### 😕 Actual Behavior

```glimmer-ts
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';

import { modifier } from 'ember-modifier';

const attachShadow = modifier((element: Element, [set]: [(shadowRoot: ShadowRoot) => void]) => {
let shadow = element.attachShadow({ mode: 'open' });

set(shadow);
});

// index.html has the production-fingerprinted references to these links
// Ideally, we'd have some pre-processor scan everything for references to
// assets in public, but idk how to set that up
const getStyles = () => [...document.head.querySelectorAll('link')].map((link) => link.href);

export class Shadowed extends Component<{
Element: HTMLDivElement;
Args: {
omitStyles?: boolean;
};
Blocks: { default: [] };
}> {
@tracked shadow: ShadowRoot | undefined;

setShadow = async (shadowRoot: ShadowRoot) => {
await Promise.resolve();

this.shadow = shadowRoot;
}


{{#if this.shadow}}
{{#in-element this.shadow}}
{{#unless @omitStyles}}
{{#each (getStyles) as |styleHref|}}

{{/each}}
{{/unless}}

{{yield}}
{{/in-element}}
{{/if}}

}

export default Shadowed;
```

Used in this test:
```gts
import { find, render } from '@ember/test-helpers';
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';

import { Shadowed } from 'limber/components/shadowed';

module('Rendering | ', function (hooks) {
setupRenderingTest(hooks);

test('it works', async function (assert) {
await render(

out of shadow


in shadow


);

assert.dom().hasText('out of shadow');
assert.dom().doesNotContainText('in shadow');
// assort.dom forgot that ShadowDom is a thing
// assert.dom(find('[data-shadow]')?.shadowRoot).hasText('in shadow');
assert.ok(find('[data-shadow]')?.shadowRoot?.textContent?.includes('in shadow'));
});
});

```

Causes this error:
```
Error occurred:

- While rendering:
-top-level
application
index
_shadowedTest
Shadowed

[runtime.js:5723](http://localhost:4201/assets/@glimmer/runtime.js)

Error occurred:

[runtime.js:4948](http://localhost:4201/assets/@glimmer/runtime.js)
Uncaught (in promise) Error: Got [object ShadowRoot], expected:
{ nodeType: 1,tagName: typeof string,nextSibling: any }
check debug.js:1512 # @glimmer/debug
runtime.js:1508
evaluate runtime.js:1061
```

and doesn't actually render in to the shadow dom:
![image](https://github.com/emberjs/ember.js/assets/199018/ad8bc3ef-38ed-4f08-9795-54c148d116dc)

### 🤔 Expected Behavior
A clear and concise description of what you expected to happen.

### 🌍 Environment

- Ember: - 5.5, 5.6+
- Node.js/npm: -
- OS: -
- Browser: - Firefox

I was able to get desired behavior at the cost of an extra div here:
```glimmer-ts
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';

import { modifier } from 'ember-modifier';

const attachShadow = modifier((element: Element, [set]: [(shadowRoot: HTMLDivElement) => void]) => {
let shadow = element.attachShadow({ mode: 'open' });
let div = document.createElement('div');

shadow.appendChild(div);

set(div);
});

// index.html has the production-fingerprinted references to these links
// Ideally, we'd have some pre-processor scan everything for references to
// assets in public, but idk how to set that up
const getStyles = () => [...document.head.querySelectorAll('link')].map((link) => link.href);

export class Shadowed extends Component<{
Element: HTMLDivElement;
Args: {
omitStyles?: boolean;
};
Blocks: { default: [] };
}> {
@tracked shadow: HTMLDivElement | undefined;

setShadow = async (shadowRoot: HTMLDivElement) => {
await Promise.resolve();

this.shadow = shadowRoot;
}


{{#if this.shadow}}
{{#in-element this.shadow}}
{{#unless @omitStyles}}
{{#each (getStyles) as |styleHref|}}

{{/each}}
{{/unless}}

{{yield}}
{{/in-element}}
{{/if}}

}

export default Shadowed;
```
(this change also makes the test pass, but again, at the cost of an extra div)

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.