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] #in-element cannot directly render in to shadow-dom #20641

Open
Tracked by #20644
NullVoxPopuli opened this issue Feb 4, 2024 · 2 comments
Open
Tracked by #20644

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

NullVoxPopuli opened this issue Feb 4, 2024 · 2 comments

Comments

@NullVoxPopuli
Copy link
Contributor

NullVoxPopuli commented Feb 4, 2024

🐞 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:
image

🤔 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)

@NullVoxPopuli
Copy link
Contributor Author

NullVoxPopuli commented Feb 5, 2024

@NullVoxPopuli
Copy link
Contributor Author

I have a hunch this has to do with Glimmer-vm changes 🤔

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant