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

Styles are not applied to react-select inside a shadow-DOM component #3680

Closed
pusulurm opened this issue Jul 12, 2019 · 37 comments
Closed

Styles are not applied to react-select inside a shadow-DOM component #3680

pusulurm opened this issue Jul 12, 2019 · 37 comments
Labels
issue/bug-confirmed Issues about a bug that has been confirmed by a maintainer

Comments

@pusulurm
Copy link

I'm using react-select in my application and it is been working great until I hit this situation.
I have wrapped my react application as a shadow-DOM component to isolate styles from other applications. After this change react-select does not get any styles applied.
As per my analysis, react-select uses emotion library to build dynamic css and these styles are being added to document>head section. Since my application is a Shadow-DOM wrapped application, these external styles are not accessible to react-select.

I can see option to write my custom styles but that is very tidies to write all styles and upgrades will become unmanageable. Please suggest a solution for this case.

@pusulurm
Copy link
Author

I've received a suggestion from emotions library group to use a custom container.
emotion-js/emotion#388

Could not achieve this. Any help on this is greatly appreciated.

@afvieira
Copy link

afvieira commented Aug 28, 2019

I have the same problem. The problem is related to "screen readers" feature.
When I open the menu, it adds a <span> above the dropdown (see the image below).

image

Also, that <span> has a class name css-bl6clz that doesn't exist (in a shadow-DOM component context) and this results in a strange behavior:

image

By the way, is there any way to deactivate this feature ("screen readers"), in order to not render this kind of dynamically html?

@afvieira
Copy link

I've received a suggestion from emotions library group to use a custom container.
emotion-js/emotion#388

Could not achieve this. Any help on this is greatly appreciated.

@pusulurm did you manage to solve this any other way?

@norama
Copy link

norama commented Sep 18, 2019

@afvieira @pusulurm I have managed to solve this with a little workaround:

import React from 'react';
import createCache from '@emotion/cache';
import { NonceProvider } from 'react-select';

import AsyncCreatableSelect from 'react-select/async-creatable';

class MyNonceProvider extends NonceProvider {
    createEmotionCache = (nonce) => {
        return createCache({ nonce, container: this.props.container });
    };
}

const MySelect = () => {
   return (
      <MyNonceProvider container={shadowRootNode}>
            <AsyncCreatableSelect
                ...
            />
      </MyNonceProvider>
   );
};

It would be nice to add this container parameter to NonceProvider.createEmotionCache() in the next release.

@wenisy
Copy link

wenisy commented Oct 10, 2019

same question here, I used it in Iframe, but when I click selector, it would add styles in parent window head.

@pusulurm
Copy link
Author

I've received a suggestion from emotions library group to use a custom container.
emotion-js/emotion#388
Could not achieve this. Any help on this is greatly appreciated.

@pusulurm did you manage to solve this any other way?

@afvieira , As I was in urgency , I ended up downgrading react select to 1.x version. This comes with a css file and I could bundle with my main css file.
Since my bundle css is scoped under shadow DOM it solved my problem. But I had to loose all latest features of react-select.

@norama
Thank you for sharing your solution. I'm glad you have some working solution.
I tried this approach and I have an issue and hope you can suggest on this.
With my own NonceProvider, I am able to get the style sections injected in my webcomponent and react select works fine with it. But I could see duplicate style sections added every time I render react select component. Where I have 10 select elements, the styles are duplicated 10 time.

Is there a way to avoid this flood of style tags from emotion ? Thanks again

@bonzoSPb
Copy link

bonzoSPb commented Apr 5, 2020

@afvieira @pusulurm I have managed to solve this with a little workaround:

import React from 'react';
import createCache from '@emotion/cache';
import { NonceProvider } from 'react-select';

import AsyncCreatableSelect from 'react-select/async-creatable';

class MyNonceProvider extends NonceProvider {
    createEmotionCache = (nonce) => {
        return createCache({ nonce, container: this.props.container });
    };
}

const MySelect = () => {
   return (
      <MyNonceProvider container={shadowRootNode}>
            <AsyncCreatableSelect
                ...
            />
      </MyNonceProvider>
   );
};

It would be nice to add this container parameter to NonceProvider.createEmotionCache() in the next release.

didnt work ontouch on mobile
it looks like onInputBlur works instantly after touch to Control
but it works if touch on Indicator
@JedWatson plz help
react-select

import React, { Component } from 'react';
import createCache from '@emotion/cache';
import Select, { NonceProvider } from 'react-select';
import memoizeOne from 'memoize-one';

class MyNonceProvider extends NonceProvider {
  createEmotionCacheCustom = function (nonce) {
    return createCache({ nonce, key: 'custom-select-style', container: this.props.container });
  };

  createEmotionCache = memoizeOne(this.createEmotionCacheCustom);
}

export default class CustomSelect extends Component {
  constructor(props) {
    super(props);
    this.state = {
    };
  }

  render() {
    const shadowRootNode = document.getElementsByTagName('online-widget-osago')[0].shadowRoot.getElementById('osago-widget');
    return (
      <MyNonceProvider container={shadowRootNode}>
        <Select
          {...this.props}
        />
      </MyNonceProvider>
    );
  }
}```

@bonzoSPb
Copy link

bonzoSPb commented Apr 6, 2020

@JedWatson
we add additional check and now it works
Can you update your package?

    _this.onTouchEnd = function (event) {
      if (_this.userIsDragging) return; // close the menu if the user taps outside
      // we're checking on event.target here instead of event.currentTarget, because we want to assert information
      // on events on child elements, not the document (which we've attached this handler to).
 
      if ((_this.controlRef && !_this.controlRef.contains(event.target) && _this.menuListRef && !_this.menuListRef.contains(event.target)) &&
      (_this.controlRef && !_this.controlRef.contains(event.composedPath()[0]) && _this.menuListRef && !_this.menuListRef.contains(event.composedPath()[0]))) {
        _this.blurInput();
      } // reset move vars
 
 
      _this.initialTouchX = 0;
      _this.initialTouchY = 0;
    };`

@startupbuilders777
Copy link

please update jet. thanks

@bladey bladey added the issue/bug-unconfirmed Issues that describe a bug that hasn't been confirmed by a maintainer yet label Jun 3, 2020
@bladey bladey added the issue/reviewed Issue has recently been reviewed (mid-2020) label Jun 18, 2020
@ErikAGriffin
Copy link

ErikAGriffin commented Jul 22, 2020

I'm having an issue where React-Select seems to not be working at all inside the shadow DOM. Focusing the element, onChange, nothing seems to be working properly. Everyone else's issue is just that the styles aren't being applied?

So for anyone using React-Select inside a ShadowDOM, and having issues where events aren't firing:

I was able to add the following indented lines from the code snippet below to where I attach my React component to the page and have events work again. This is being done from within a Chrome Extension, other browsers may behave differently.

Object.defineProperty(reactRoot, 'ownerDocument', { value: shadowRoot });
shadowRoot.createElement = (...args) => document.createElement(...args);
shadowRoot.createElementNS = (...args) => document.createElementNS(...args);
shadowRoot.createTextNode = (...args) => document.createTextNode(...args);

where reactRoot is the element inside the ShadowDOM you are mounting your React component to using ReactDOM.render(...) and shadowRoot is the ShadowRoot.

facebook/react#9242 (comment)

@n3bhmb
Copy link

n3bhmb commented Aug 4, 2020

@bonzoSPb
Hi, where did you add that additionnal check ? In which file ?

@bonzoSPb
Copy link

bonzoSPb commented Aug 4, 2020

@bonzoSPb
Hi, where did you add that additionnal check ? In which file ?

In Select.js, check my fork
https://github.com/bonzoSPb/react-select/blob/master/src/Select.js

@n3bhmb
Copy link

n3bhmb commented Aug 4, 2020

@bonzoSPb
Hi, where did you add that additionnal check ? In which file ?

In Select.js, check my fork
https://github.com/bonzoSPb/react-select/blob/master/src/Select.js

Thank you @bonzoSPb ! 👍 It works great with a shadow DOM ! 💯

@JedWatson Can you please consider the solution of @bonzoSPb ?

@bladey bladey added issue/reviewed Issue has recently been reviewed (mid-2020) and removed issue/reviewed Issue has recently been reviewed (mid-2020) labels Aug 24, 2020
@mikeyyyzhao
Copy link

I'm running react-select in an iframe and getting the same styling bug as @afvieira where the screen reader gets displayed. I've tried running the code that @bonzoSPb and @norama have posted but the styling is still off. I tried console logging the nonce and seeing that it's returning null, could that be an issue?

import React, { Component } from 'react';
import createCache from '@emotion/cache';
import Select, { NonceProvider } from 'react-select';

class MyNonceProvider extends NonceProvider {
  createEmotionCache = (nonce) => {
    console.log('nonce', nonce);
    return createCache({ nonce, container: this.props.container });
  };
}

export default class MySelect extends Component {
  render() {
    let iframeContainer = document.getElementById('custom-iframe-container');

    return (
      <MyNonceProvider container={iframeContainer}>
        <Select
          {...this.props}
        />
      </MyNonceProvider>
    );
  }
};

@ebonow
Copy link
Collaborator

ebonow commented Dec 9, 2020

@mikeyyyzhao Would you kindly mind creating a codesandbox demonstrating this issue? It would be much easier to get eyes on this and get this pushed up in priority.

@mikeyyyzhao
Copy link

mikeyyyzhao commented Dec 9, 2020

@ebonow sure thing, here's the codesandbox. As you can see, react-select is showing the screen reader text and style is incorrect. Thanks in advance for your help!

@ebonow
Copy link
Collaborator

ebonow commented Dec 9, 2020

I'll try to review this and get back to you soon. I'm still fairly new at this CSS-in-JS / Emotion thing, but I'll see what I can find out as I do know this is affecting others.

@ebonow ebonow self-assigned this Dec 9, 2020
@ebonow ebonow removed the issue/reviewed Issue has recently been reviewed (mid-2020) label Dec 9, 2020
@kopecmi8
Copy link

@JedWatson First of all thank you for great librarary!

Please consider on @bonzoSPb solution. There is two separate problems when yours library is used in Shadow DOM.

  1. Problem with missing styles
  2. Problem on mobile devices with touch screen

First problem can be easily fixed by adding custom NonceProvider and its not so annoying to fix it on users own.

The second is much worse. It cannot be fixed without modification of library code and its lead to dirty hacks when you need to use it.

Thanks for your interest in.

@carlosribas
Copy link

This is an amazing library, but I am facing the same problem that @mikeyyyzhao described. I hope @ebonow finds a solution for that.

@Methuselah96 Methuselah96 added issue/bug-confirmed Issues about a bug that has been confirmed by a maintainer and removed issue/bug-unconfirmed Issues that describe a bug that hasn't been confirmed by a maintainer yet labels Jan 25, 2021
@ebonow
Copy link
Collaborator

ebonow commented Jan 25, 2021

Thanks for the ping @carlosribas .

I'm currently working on a PR for customized aria-live messages. While working on this, it occurred to me that a11yText should perhaps be its own component. Advantages of this includes:

  • Customizable text with access to more props and state
  • Separation of concerns from aria messaging and Select.js
  • Add additional aria-live messaging without relying on prebaked implementation patterns/templates
  • Ability to remove all aria-live messaging to give priority to other aria attributes
  • Ability to apply style to the shadowDOM should the user want an unstyled Select or one that isn't reliant on Emotion

Regarding @bonzoSPb , I agree that we should add a composedPath check, though we'd likely want to do a null guard check. One improvement upon this bit of code though, would be a check inside the entire Menu and not just the menuListRef should incase someone want to add a Header or Footer to their MenuList. There's an existing issue logged for this, so was hoping to take it on in one go.

@carlosribas
Copy link

Sounds good. I'm really looking forward to this PR. Thank you @ebonow.

@sag1v
Copy link

sag1v commented Feb 17, 2021

With my own NonceProvider, I am able to get the style sections injected in my webcomponent and react select works fine with it. But I could see duplicate style sections added every time I render react select component. Where I have 10 select elements, the styles are duplicated 10 time.

Is there a way to avoid this flood of style tags from emotion ? Thanks again

@pusulurm / @norama

Did you manage to stop the duplication flood? I'm facing the same issue when implementing @norama solution or even by creating my own NonceProvider from scratch.

@erkez
Copy link

erkez commented Mar 29, 2021

I had this issue with styles when some of my application components were inserted into a shadow root.

I followed some of the suggestions here to simply use the emotion CacheProvider with a different container.

import * as React from 'react';

import { CacheProvider } from '@emotion/core';
import createCache, { EmotionCache } from '@emotion/cache';

type Props = {
    stylesRoot?: HTMLElement;
    children: React.ReactNode;
};

export default function EmotionStyleSheetProvider(props: Props): React.ReactElement {
    const [cache, setCache] = React.useState<EmotionCache | null>(null);

    React.useEffect(() => {
        setCache(createCache({ container: props.stylesRoot }));
    }, [props.stylesRoot]);

    return (
        <React.Fragment>
            {cache != null && <CacheProvider value={cache}>{props.children}</CacheProvider>}
        </React.Fragment>
    );
}

In your application root, you could simply get the ref for a base div and pass it to a component similar to the one above. This actually managed to also solve issue #3309 for me.

Hope this helps.

@ptgamr
Copy link

ptgamr commented Jun 1, 2021

@erkez solution works for us for a while.. but after this PR!2361 of @emotion/cache, to fix a bug, make the solution no longer works..

Basically, the bug is that @emotions/cache modifies style tags 'belonging' to other cacheProviders... in this case, react-select has its own style provider (NonceProvider: https://github.com/JedWatson/react-select/blob/master/packages/react-select/src/NonceProvider.tsx)

<OurCustomerStyleProvider>
  <NonceProvider>
      ..........
  </NoneProvider>
</OurCustomerStyleProvider>

Because of the bug, NoneProvider will modify style tags belong to parent provider. (which is ours)

"The bug".. in this case, actually helping us... but it's now fixed, which mean we have to find another way.

@AndrewKuktenko
Copy link

Any solution here?

@ebonow ebonow removed their assignment Jun 22, 2021
@myorkgitis
Copy link

myorkgitis commented Jul 20, 2021

Any solution here?

Not sure if you're using SSR or next.js but I was able to get the styles to inject in production by hiding a <Select> component in my _app.tsx, outside of the page Component.

My solution is here: #3309 (comment)

@Rall3n
Copy link
Collaborator

Rall3n commented Sep 21, 2021

@jfinnis,

The behavior you are showing against a person you don't know and never met before is highly unacceptable.

We have reported your abusive/harassing behavior.

@Rall3n
Copy link
Collaborator

Rall3n commented Sep 21, 2021

That being said:

I tested around a bit and got an example working.
https://codesandbox.io/s/react-select-in-custom-element-ohlj0

The correct way to insert the styles into the shadow DOM is to use the CacheProvider component from @emotion/react in combination with a custom cache object created by createCache from @emotion/cache with the shadow root as its container attribute. Last but not least, the Select component is rendered as a child of CacheProvider that uses the created cache as its value prop.

// Create shadow and mounting point
class XSelect extends HTMLElement {
  connectedCallback() {
    this.somehowCreateShadowAndMountPoint();
    
    const cache = createCache({
      container: this.shadowRoot,
      key: 'customElementCache'
    });
    
    ReactDOM.render(
      <CacheProvider value={cache}>
        <Select {...} />
      </CacheProvider>,
      this.mountPoint
    );
  }
}

It is important that the container attribute of the cache and the target element for ReactDOM.render must be different elements. If they are identical, there may be a race-condition between emotion-js, which tries to insert its stylesheets, and react, which tries to render the component, with the latter winning.

@jfinnis
Copy link

jfinnis commented Sep 23, 2021

Thanks for taking the time to take a look. I tried the demo and it does fix the issue of css not being inserted into the shadow DOM. But I can't seem to get it to work on mobile, when I open up this demo in Chrome DevTools in mobile mode, I can't select the dropdown at all. I'm guessing it's an interaction between mobile browsers and web components (unless I'm missing something simple) since I've been able to use this successfully with iframe versions of my embedded software.

(Also, I apologize for negative comments the other day, I was not feeling well and took out frustrations among people who do not deserve that at all. I highly appreciate the work that goes into maintaining open source software and respect all the contributors. Thank you for responding to negativity with positiveness and spending time trying to help, that's very classy behavior.)

@jongbelegen
Copy link

@jfinnis, try the patch of @bonzoSPb, this works for me.

@jfinnis
Copy link

jfinnis commented Sep 24, 2021

@jfinnis, try the patch of @bonzoSPb, this works for me.

Thanks for pointing this out, this worked for me as well! This patch allows me to use the select dropdown on mobile: #3680 (comment)

@Rall3n
Copy link
Collaborator

Rall3n commented Oct 13, 2021

I will close this issue now as the main problem has been addressed and a solution has been presented.

@baramik
Copy link

baramik commented Oct 26, 2022

Was there any chance to apply the changes to make it work on mobile?

@CezaryDanielNowak
Copy link

Solutions here are pretty vague and troublesome. IMHO styles should be applied to the react root element, not to the page head by default.

@irhamputra
Copy link

IMHO it would be good that styles also should be provided in separated file, so people can add it manually where in their project

fredrikpe added a commit to navikt/mulighetsrommet that referenced this issue Nov 3, 2023
react-select css forsvinner når man bruker shadowDom element. Dette
fikser det.

se f. eks JedWatson/react-select#3680 og
emotion-js/emotion#3071 (comment) for mer info
fredrikpe added a commit to navikt/mulighetsrommet that referenced this issue Nov 3, 2023
react-select css forsvinner når man bruker shadowDom element. Dette
fikser det.

se f. eks JedWatson/react-select#3680 og
emotion-js/emotion#3071 (comment) for mer info
@forsureitsme
Copy link

So... won't fix and we'll have to add another dependency to our projects. Got it.

@DmitryMasley
Copy link

DmitryMasley commented Jul 26, 2024

From my understanding, we could use NonceProvider. We only need to add a container prop to it in order to overwrite the default insertion point. This way we could avoid extra dependency for @emotion/cache and @emotion/react and would mitigate the risk that select uses a different version of those packages.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
issue/bug-confirmed Issues about a bug that has been confirmed by a maintainer
Projects
None yet
Development

No branches or pull requests