ember.js
ember.js copied to clipboard
[Bug] `#in-element` cannot directly render in to shadow-dom
🐞 Describe the Bug
😕 Actual Behavior
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;
}
<template>
<div data-shadow {{attachShadow this.setShadow}} ...attributes></div>
{{#if this.shadow}}
{{#in-element this.shadow}}
{{#unless @omitStyles}}
{{#each (getStyles) as |styleHref|}}
<link rel="stylesheet" href={{styleHref}} />
{{/each}}
{{/unless}}
{{yield}}
{{/in-element}}
{{/if}}
</template>
}
export default Shadowed;
Used in this test:
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 | <Shadowed>', function (hooks) {
setupRenderingTest(hooks);
test('it works', async function (assert) {
await render(
<template>
out of shadow
<Shadowed>
in shadow
</Shadowed>
</template>
);
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
<anonymous> runtime.js:1508
evaluate runtime.js:1061
and doesn't actually render in to the shadow dom:
🤔 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:
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;
}
<template>
<div data-shadow {{attachShadow this.setShadow}} ...attributes></div>
{{#if this.shadow}}
{{#in-element this.shadow}}
{{#unless @omitStyles}}
{{#each (getStyles) as |styleHref|}}
<link rel="stylesheet" href={{styleHref}} />
{{/each}}
{{/unless}}
{{yield}}
{{/in-element}}
{{/if}}
</template>
}
export default Shadowed;
(this change also makes the test pass, but again, at the cost of an extra div)
Repro: https://github.com/NullVoxPopuli/ember-source-5.6-broke-shadow-dom (check commits)
I have a hunch this has to do with Glimmer-vm changes :thinking: