Code Monkey home page Code Monkey logo

reactshadow's Introduction

ReactShadow

Utilise Shadow DOM in React with all the benefits of style encapsulation.

Travis   Coveralls   npm   License MIT

Screenshot


Getting Started

Creating the shadow root is as simple as using the default export to construct a shadow root using the node name provided – for example root.div would create a div as the host element, and a shadow root as its immediate descendant — all of the child elements would then be descendants of the shadow boundary.

import root from 'react-shadow';
import styles from './styles.css';

export default function Quote() {
    return (
        <root.div className="quote">
            <q>There is strong shadow where there is much light.</q>
            <span className="author">― Johann Wolfgang von Goethe.</span>
            <style type="text/css">{styles}</style>
        </root.div>
    );
}

Edit react-shadow

Applying styles requires either applying the styles directly to the component as a string, or importing the CSS documents as a string as part of your build process. You can then append the style component directly to your shadow boundary via your component's tree. In the example we use the following Webpack configuration to import CSS documents as strings.

{
    test: /\.css$/,
    loader: ['to-string-loader', 'css-loader']
}

Alternatively you can use styled-components normally, as each time a shadow boundary is created, a new StyleSheetManager context is also created which will encapsulate all related styles in their corresponding shadow root — to use this import react-shadow/styled-components instead of import react-shadow, likewise if you'd like to use emotion you can import react-shadow/emotion.

import root from 'react-shadow/styled-components';
import root from 'react-shadow/emotion';

// ...

<root.section />;

You may pass any props you like to the root.* component which will be applied directly to the host element, including event handlers and class names. There are also a handful of options that are used for the attachShadow invocation.

ShadowRoot.propTypes = {
    mode: PropTypes.oneOf(['open', 'closed']),
    delegatesFocus: PropTypes.bool,
    styleSheets: PropTypes.arrayOf(
        PropTypes.instanceOf(globalThis.CSSStyleSheet),
    ),
    children: PropTypes.node,
};

ShadowRoot.defaultProps = {
    mode: 'open',
    delegatesFocus: false,
    styleSheets: [],
    children: null,
};

In cases where you need the underlying element and its associated shadow boundary, you can use a standard ref which will be invoked with the host element – from that you can use shadowRoot to access its shadow root if the mode has been set to the default open value.

const node = useRef(null);

// ...

<root.section ref={node} />;

Recently and at long last there has been some movement in introducing a declarative shadow DOM which react-shadow tentatively supports – as it's experimental, open to sudden spec changes, and React finds it difficult to rehydrate – by using the ssr prop.

const node = useRef(null);

// ...

<root.section ssr />;

reactshadow's People

Contributors

alirezavalizade avatar arizonatribe avatar darthtrevino avatar dependabot[bot] avatar frontendphil avatar georgeclaghorn avatar javadoug avatar layershifter avatar lazarljubenovic avatar lightningspirit avatar lourd avatar meriouma avatar mrspence avatar reklino avatar sebastien-ahkrin avatar shvaikalesh avatar tjwudi avatar wildhoney avatar ziahamza avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

reactshadow's Issues

Impossible to import CSS file.. webpack conf ?

Hello Wildhoney,

Maybe I am coming a bit late I discovered your project this week.

Regarding the example you provided within the project, you have been able to create a build including a few folder including the css/country.css

When using webpack, depending on the configuration, the css files are retrieved from import at the head of js files, but are not retrieved directly from within a React Component ex:

Would you please provide your webpack configuration to create the build you did, it would help me to understand how did you export your country.css successfully without any regular import at the head of the file.

If you are still around, thank you in advance ;)

get node in Shadow DOM

I'm having issues integrating a react app in the shadow dom with packages that use the ReactDOM.findDOMNode function to access a specific node in the react app.

That function will definitely not be used since the app is in the shadow dom, however I'm curious if anyone has ran into this and what you use for the workaround to access the node by its ref?

Usage with styled-components

Hey, thank you for this wonderful library. 😍

Is there an example of how to use react-shadow with styled-components? I'm using client side React and my styles are not applied. I tried both options of styling a React component but the border prop doesn't even show up inside the styles of the devtools.

import 'styled-components/macro'
const Test = () => <div css={`border: solid 1px black;`}>Test</div>

or via an actual styled-component:

import styled from 'styled-components'
import root from 'react-shadow';

export const Test = styled.div`
  border: solid 1px black;
`

export default function ShadowStyles() {
    return (
        <root.div>
           <Test> Test </Test>
        </root.div>
    );
}

Add TypeScript defintion

Hey,

It'd be nice to add TypeScript definitions to the project. I can make a PR if needed.

EDIT: Looking at how compact the src/ is, it might even be worth switching the project to TypeScript, if you feel up to it

contenteditable within Shadow DOM does not fire 'onSelect'

It seems that a contenteditable <div> within ReactShadow does not fire an onSelect event. This event is usually fired when you click anywhere within a contenteditable.

I'm actually not certain whether this is a problem with the Shadow DOM spec or a problem with react-shadow, but I thought there might be some insight here into the problem. I've made a minimal codepen that illustrates the issue: https://codepen.io/anon/pen/JJPBaL?editors=1011

If you click within the red area (outside the Shadow DOM), you'll see a log in the console, but if you click within the blue area (inside the Shadow DOM), you don't see anything written to the console.

Clarification Around `styled-components` Support

I can see that built-in styled-components support was added in 637149b, but I am wondering as this library depends on styled-components directly (as seen here), would this not mean that the StyleSheetManager created here would always be the StyleSheetManager coming from the styled-components version this library depends on (currently v4.4.1), not the version that the user of this library is depending upon (potentially styled-components v5+)?

I may be overlooking the following, so let me know if these measures mean that the version the library renders is indeed the version the user of the library is depending upon:

  • The fact that you are using require, instead of importing it statically.
  • The fact that the styled-components version you are depending on in this library is a dev dependency (which might only be being actually used in the examples, and it isn't part of the build output).

I ask this a I want to update react-shadow to v14.2.0+, and styled-components to v5, but I want to make sure that its not going to introduce any weird bugs because the StyleSheetManager injected isn't coming from the same styled-components version as the components I'm creating.

Thanks for your time!

Cannot use <Link> inside shadow dom component

I have an issue using react links inside the shadow dom component like this

<ShadowDOM include={['/zion_resources/widgets/header.css']}> <div> <Link className="item" activeClassName="active" to="preview"> <img src="/img/icons/zion.icon.tb_tablet.svg" alt="" width="23px"/> </Link> </div> </ShadowDOM>

Throws an error s rendered outside of a router context cannot navigate.

Any workarounds for this?

Seems like not working in react 15

Errors:

Uncaught TypeError: Cannot read property 'createShadowRoot' of null
TypeError: this._shadowRoot.querySelector is not a function(…)

Chrome 51

Object doesn't support property or method createShadowRoot - Edge

The ShadowDom doesn't seem to work on edge? I've also tried adding pollyfill but that didn't fix too.

public render() {
    console.log("application state:", this.props);

    try {
      return (
        <div>
          <ShadowDOM include={ browser.extension.getURL('/styles/main.css') }>
            <div>
              {this.currentPanel()}
            </div>
          </ShadowDOM>
        </div>
      );
    } catch (ex) {
      console.log(ex);
      throw ex;
    }
  }

Please suggest
Thanks

componentWillUnmount of children not invoked when ShadowDOM unmounted

Sample code to illustrate the issue provided below. Basically, ShadowDOM is eating the componentWillUnmount propagation somehow. This prevents binding events to the DOM. Is this by design?

/*
index.js {

	import React from 'react'
	import {init} from "./ShadowDomBug"
	init();

	// press Unmount Me button to remove component from DOM

	// expected console:
	// ShadowDomBug.js:66 ini
	// ShadowDomBug.js:72 unMountShadowDomBug
	// ShadowDomBug.js:9 1 BindDOMEventListeners
	// ShadowDomBug.js:9 1 BindDOMEventListeners
	// ShadowDomBug.js:21 2 AnotherBindDOMEventListeners

	// actual console:
	// ShadowDomBug.js:66 ini
	// ShadowDomBug.js:72 unMountShadowDomBug
	// ShadowDomBug.js:9 1 BindDOMEventListeners

}
*/
import React, {Component} from 'react';
import ReactDOM from 'react-dom'
import PropTypes from 'prop-types'
import ShadowDOM from 'react-shadow'

class BindDOMEventListeners extends Component {
	componentWillUnmount() {
		// called every time
		console.log('1 BindDOMEventListeners')
		// window.removeEventListener('keydown', ...);
	}

	render() {
		return (<div>{this.props.children}</div>);
	}
}

class AnotherBindDOMEventListeners extends Component {
	componentWillUnmount() {
		// never called
		console.log('2 AnotherBindDOMEventListeners')
		// window.removeEventListener('keydown', ...);
	}

	render() {
		return (<div> no children </div>);
	}
}

export class ShadowDomBug extends Component {
	render() {
		return (
			<BindDOMEventListeners>
				<ShadowDOM>
					<div>
						<BindDOMEventListeners>
							<AnotherBindDOMEventListeners/>
							<div>
								<button onClick={this.props.unMountMe}>Unmount Me</button>
							</div>
						</BindDOMEventListeners>
					</div>
				</ShadowDOM>
			</BindDOMEventListeners>
		);
	}
}

ShadowDomBug.propTypes = {
	unMountMe: PropTypes.func
};

ShadowDomBug.defaultProps = {
	unMountMe: unMountShadowDomBug
};

export const domRootId = 'ShadowDomBug';

export function init() {
	let root = document.getElementById(domRootId);
	if (!root) {
		root = document.createElement('div');
		root.id = domRootId;
		root.setAttribute('id', domRootId);
		document.body.appendChild(root);
		console.log('ini')
		ReactDOM.render(<ShadowDomBug/>, root);
	}
}

export function unMountShadowDomBug() {
	console.log('unMountShadowDomBug')
	ReactDOM.unmountComponentAtNode(document.getElementById(domRootId))
}

Page's html and body styling still applied

I am building a chrome extension and am injecting code directly in the page. To avoid conflicting styles, I have utilized the react-shadow package and have been mostly successful in isolating the CSS. However, I am still getting issues with leakage of styles into the page.

For instance, in Gmail my extension looks like this:

Screen Shot 2020-01-16 at 5 30 46 PM

But in Stack Overflow it looks like this:
Screen Shot 2020-01-16 at 5 31 08 PM

I looked at the rendered HTML in the browser and it seems like there is still leakage of the styling on the html and body tags from the page into my page. Does anyone know why this might be the case?

Using @import causes many network fetches and defeats browser style caching in Chrome

Chrome (as of this writing version 42) can't cache and de-duplicate style blocks that contain @import statements inside ShadowRoots.

This means that each react component will download the .css file if it doesn't have cache headers, and that the browser won't de-duplicate the <style> declarations which means creating those components will be really expensive as each one is parsing the stylesheet again.

Instead you want to write the css inline, or I suppose xhr it down and cache the string.

Typo in example

Thanks for your work on this project. Just wanted to bring your attention to a small typo. Should be type rather than tyle.

The ShadoDom css issue with firefox

The Shadodom with unsupported browser like firefox and edge may cause trouble with CSS issues. As of firefox documentation

A polyfilled Shadow DOM using the webcomponents.js polyfill doesn't actually encapsulate style, so the styles might bleed through. Be aware that sites built using the polyfill might look different when running in an environment that does support native Shadow DOM.

We just encounterd the CSS Issue when we were using Shadow Dom with firefox latest version.
Is there any work around for this?

Document browser support

I'm excited to try out the lib, but I know shadow dom is not widely supported yet.

Are you polyfilling older versions, or does this only work in Chrome and Safari?

Improve Provider use with ShadowDOM example

The redux example currently fudges a bit by connecting the component outside the ShadowDOM and then manually passing the props through to the components within the ShadowDOM. This works okay for the top level components but not as a general pattern with an app that implements redux. It would be more useful if the example showed how to adjust ShadowDOM to pass the redux store context through to child components.

I wonder though why ShadowDOM forwards router context through by default but not the redux store context. Is this something that the project is willing to modify?

Why to use this library instead of styled-components?

I am trying to figure out what library works best for me. Both ReactShadow and styled-components are solving same problem though with a bit different approach (using shadow DOM vs generating obfuscated classes). Is there any advantage in using ReactShadow instead of styled-components?

Add support for Babel+Browserify

This works:

import React from 'react'
import ReactShadow from 'react-shadow'

export default React.createClass({
  mixins: [ReactShadow],
  cssDocuments: ['styles/style.css'],
  render: function() { ... }
})

This does not:

import React from 'react'
import ReactShadow from 'react-shadow'

export default class FavList extends React.Component {
  mixins: [ReactShadow]
  cssDocuments: ['styles/style.css']
  constructor (_) {
    super(_)
  }
  render() { ... }
}

Element is duplicated inside <script>

It's not clear why the element is duplicated inside a script tag, or why the content is wrapped inside a <main>. This means React is updating twice as many DOM nodes though, and putting yourself as a child of script just makes you display: none, it doesn't do anything else.

first child of ShadowDOM cannot be a custom component

We get the error message: "ReactShadow: Passed child must be a concrete HTML element rather than another React component."

/*
index.js {

	import React from 'react'
	import {init} from "./ShadowDomBug"
	init();

	// expected  output
	// hello, world

	// actual output error
	// ReactShadow: Passed child must be a concrete HTML element rather than another React component.
}
*/

import React, {Component} from 'react';
import ReactDOM from 'react-dom'
import ShadowDOM from 'react-shadow'

class MyComponent extends Component {
	render() {
		return (<div>{this.props.children}</div>);
	}
}

export class ShadowDomBug extends Component {
	render() {
		return (
			<ShadowDOM>
				<MyComponent>
					<div>
						hello, world
					</div>
				</MyComponent>
			</ShadowDOM>
		);
	}
}

export const domRootId = 'ShadowDomBug';

export function init() {
	let root = document.getElementById(domRootId);
	if (!root) {
		root = document.createElement('div');
		root.id = domRootId;
		root.setAttribute('id', domRootId);
		document.body.appendChild(root);
		console.log('ini')
		ReactDOM.render(<ShadowDomBug/>, root);
	}
}

Focus of Draft.js within Shadow DOM

I'm mounting Facebook's Draft Editor within a ShadowDOM. For the most part this works fine except when I try to do a focus().

I've trimmed the problem down in CodePen to illustrate the issue:
https://codepen.io/michael_cox/pen/OmBPBm

When you click on the Focus button, it throws the error Failed to execute 'getComputedStyle' on 'Window': parameter 1 is not of type 'Element'. From tracing through the code it looks like it is a valid element, but I might be misunderstanding things. Admittedly this could be an issue with Draft making certain assumptions about the environment that it's running in, but I'm hoping for a simpler solution I might be missing on the ShadowDOM side?

Unable to use ReactShadow with Component

I'm trying to create a higher order component for this, e.g.:

import React from "react";
import ShadowDOM from "react-shadow";

const withShadow = (...include) => (Component) => {
  return (props) => {
    return (
      <ShadowDOM include={include}>
        <Component {...props} />
      </ShadowDOM>
    );
  };
};

export default withShadow;

Usage:

const AppWrapper = _.flowRight(
  withStore(reducers),
  withShadow("path/to/app.css")
)(App);

ReactDOM.render(<AppWrapper />, ...);

Unfortunately I get an error:

ReactShadow: Passed child must be a concrete HTML element rather than another React component.

I don't see this documented, but I see that it's explicit in the source for this module. Is there a reason I can't use a component? Is it something that's possible to fix? I don't mind contributing, but I'm curious about the reason this is a limitation today.

Make font works in shadow dom

Hello,

I would like to use shadow dom and add icons inside this shadow dom but I can't make it works.

    <div>
        <ShadowDOM include={['https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css']}>
            <div>
                <i className="fa fa-comments fa-2x"></i>icon
            </div>
        </ShadowDOM >
    </div>
    , document.getElementById('root'));`

When running the code above, I have a square icon. It seems the font itself is missing. How can I add the font ?

Thanks for the help

Event retargeting doesn't work on react-bootstrap modal

Using react-bootstrap modal and ReactShadow, I have a rendered DOM like this

<div>
  #shadow-root<open>
  <main>
    <div data-reactid=".2">
     <div data-reactid=".1">
       <div data-reactid=".1.1">
       </div>
     </div>
    </div>
  </main>
  <script> 
    <div data-reactid=".4">
     <div data-reactid=".3">
       <div data-reactid=".3.1">
       </div>
     </div>
    </div>
  </script>
</div>

The translatedId doesn't really get mapped correctly, as .1.1 will be replaced as .4.1 rather than .3.1. I wonder if there is any solution that is based on event.path rather than finding the element by reactid?

Besides, thank you for the great work!

high order component props do not pass down to child components

I am using a MuiThemeProvider from 'material-ui/styles/MuiThemeProvider' that passes a prop to components that subscribe using muiThemeable from 'material-ui/styles/muiThemeable'. However, the Provider props are not passed thru the ShadDOM down to children. Workaround is to wrap Provider again after ShadowDOM. Which is OK for for me however, propagating the props would be more elegant. Is this by design?

Redux context.store issues

It seems the context of elements inside of ShadowDOM seem to have issues with React-Redux. I was using Redux-Fractal to create local state components that I was embedding into a ShadowDOM component. Unfortunately, the library erred out on a call to context.store.getState() due to the store property being undefined.

I haven't had time to see if this is an issue related to Redux plugins, or Redux its self, and I'm not sure if the library was ever built to play nicely with Redux, but I would appreciate this issue resolved. I'll try to offer more information when I have the chance.

Support for IE11

I am trying to use ReactShadow on IE11 but it doesn't seem to work. I get the following error in console:

Warning: React.createElement: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: undefined. 
You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports. Check your code at index.tsx:55.

Line 55 contains the closing tag of the shadow root this:

<shadowRoot.section>
  ...
</shadowRoot.section>

Are there any plans to support IE11 in the future or does anybody know how to fix this? I am using webcomponentsjs, proxy-polyfill and other polyfills for explorer, but that doesn't seem to fix the issue.

Thanks

Help why include is not working and how to use props.theme

@Wildhoney
I have this simple rendering of shadow DOM

render() { return ( <ShadowDOM include={['test.css']}> <div> <input type="text" value={this.props.templateName} onChange={this.handleChange.bind(this)}/> <button onClick={this.saveTemplate.bind(this)}>SAVE</button> </div> </ShadowDOM> ); }

Could you help me why the css not reflecting in test.css?

test.css
:host input{ width: 300px; }

Also what is the purpose of props.theme? The documentation doesn't really explain its purpose

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.