Skip to content
This repository has been archived by the owner on Jan 23, 2024. It is now read-only.

[Bug] Missing shadow dom #24

Open
ariefurtado opened this issue Jun 24, 2022 · 10 comments
Open

[Bug] Missing shadow dom #24

ariefurtado opened this issue Jun 24, 2022 · 10 comments
Labels
bug Something isn't working

Comments

@ariefurtado
Copy link

Describe the bug

Currently, it is not possible to use the library with Web Components because of missing shadow dom...

Steps to reproduce the behavior

  1. Create a project with Web Components and Storybook and Interaction tests
  2. Create a component with shadow: true and a stories file for that component
  3. Create a play function that tries to find an element within the component shadow dom
  4. Run the storybook and check the interaction tests console

Expected behavior

Should be able to find the element within the shadow dom.

@ariefurtado ariefurtado added the bug Something isn't working label Jun 24, 2022
@ariefurtado
Copy link
Author

ariefurtado commented Jun 24, 2022

I have forked the project, and based on this comment from an issue of the dom-testing-library, I have swapped the @testing-library/dom dependency for the testing-library__dom.

commentary about the issue:
testing-library/dom-testing-library#742 (comment)

my library fork:
https://github.com/ariefurtado/testing-library

For those that wants to try the forked version, I created a tag within the fork and its working!
https://github.com/ariefurtado/testing-library/tree/v0.0.1_shadow

ps: since this is my first time forking and doing all of this, I ask you folks not to be too harsh with me ^^

ps²: this is not a production ready implementation and cannot be merged into the original project and we have to wait for a stable solution.

@yannbf
Copy link
Member

yannbf commented Aug 4, 2022

Hey @ariefurtado thanks a lot for the work you did there! I tested it out and it's awesome.

Although your solution does add support for web components, there is a slight problem there, given the fact that it's using a community fork of testing library from 2 years ago:

  • It's not open source
  • Has not been updated for 2 years. Any features, bug fixes, security fixes from testing library that were pushed since then are not in that fork.
  • There's no way for us to do anything about it

If you are very interested in making this possible in the actual @testing-library/dom package, can you reach out to me on twitter? There is a possible long term solution but for that I need help from someone who is well versed into the web components world.

@ericclemmons
Copy link

ericclemmons commented Aug 11, 2022

I just ran into this issue and, with interactions, this is how I'm working around it:

MyThing.play = async ({args, canvasElement}) => {
  const wc = canvasElement.querySelector(
    'my-thing',
  ) as HTMLElement;

  // Wait for Shadow DOM to be mounted & `open`
  const root = await waitFor(
    () => (wc.shadowRoot as ShadowRoot).firstElementChild as HTMLElement,
    {
      timeout: 5000,
    },
  );

  const screen = within(root);

  ...
};

Perhaps this could be documented away?

@benelan
Copy link

benelan commented Aug 18, 2022

@ericclemmons Unfortunately this doesn't work for us:

export const WithLabel = WithLabelTemplate.bind({});

WithLabel.play = async ({ canvasElement }) => {
  const wc = canvasElement.querySelector("calcite-input") as HTMLInputElement;

  const root = await waitFor(() => (wc.shadowRoot as ShadowRoot).firstElementChild as HTMLElement, {
    timeout: 5000
  });

  const screen = within(root);
  const input = screen.getByTestId("input-with-label") as HTMLInputElement;
  await userEvent.type(input, "foo bar baz");
};

We get this error:
image

What did you put in the rest of your play function? I'm skeptical about the work around since @testing-library/dom doesn't provide access to web component browser APIs, but I hope I'm wrong.


@yannbf I don't have twitter, but my team and I would like to connect about the long term solution. We are creating Calcite Components and may be able to help with the web component side of the equation. We want to migrate to chromatic and this is the only thing blocking us. Let me know how we can help and/or how we should connect. Thanks!

@ziyafenn
Copy link

Still no support?

@yannbf
Copy link
Member

yannbf commented Sep 26, 2022

@yannbf I don't have twitter, but my team and I would like to connect about the long term solution. We are creating Calcite Components and may be able to help with the web component side of the equation. We want to migrate to chromatic and this is the only thing blocking us. Let me know how we can help and/or how we should connect. Thanks!

Sorry I missed this @benelan, I'll reach out on Linkedin.

@SavvasNicoleCandiotti
Copy link

@yannbf @benelan any updates 😊?

@ariefurtado
Copy link
Author

ariefurtado commented Jun 15, 2023

Hello again @yannbf,
this has been a very long year for me, and a lot of stuff hapenned so i could not see this issue progressing.

I played with the solution presented by @ericclemmons and thought that something was off.
Then I've searched the docs for the waitFor function and came up with a refactor that got me there.

I've created the function withinShadowRoot to make it easy to re-use.

export async function withinShadowRoot(customElement: HTMLElement, selector: string) {
    const webc = customElement.querySelector(selector);
    
    await waitFor(
        () => {
            const shadowRootFirstEl = webc.shadowRoot.firstElementChild as HTMLElement;
            return expect(shadowRootFirstEl).toContainElement(shadowRootFirstEl);
        },
        { timeout: 1000 },
    );

    // force type HTMLElement to ignore the type checking of the "within" function
    return within(webc.shadowRoot as any as HTMLElement);
}

With this function, all that is needed to interact with the elements in the shadowRoot is to await for the function to return and we can use it. Example:

// CSF3 StoryObj
export const Default: StoryObj<PzlButtonStoryArgs> = {
  render: Template.bind({}),

  args: { 
    onClick: event => action('Button Clicked')(event),
    children: <p>click me</p>
  },

  play: async ({ canvasElement }) => {
    const webc = await withinShadowRoot(canvasElement, 'pzl-button');
    const buttonEl = await webc.findByRole('button');
    await userEvent.click(buttonEl);
  }
};

I'm not sure about the efficiency and I have not tested enough, but for now, it is doing the trick.

image

Thanks everyone ^^

ps: please forgive my english... 😳

@SavvasNicoleCandiotti
Copy link

@ariefurtado can't wait to give this a try over the weekend! Cheers!

@sujarajan
Copy link

Hello @yannbf,

any updates on this?

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

8 participants