From 41237003fa349b51a8ead6364035d26737e90814 Mon Sep 17 00:00:00 2001 From: Wildhoney Date: Sun, 16 Jun 2019 21:46:24 +0100 Subject: [PATCH] Added tests --- README.md | 73 ++++++++++++++---------------------------- ava.config.js | 8 +++++ helpers/browser-env.js | 3 ++ helpers/enzyme.js | 4 +++ src/index.test.js | 55 +++++++++++++++++++++++++++++++ 5 files changed, 94 insertions(+), 49 deletions(-) create mode 100644 ava.config.js create mode 100644 helpers/browser-env.js create mode 100644 helpers/enzyme.js create mode 100644 src/index.test.js diff --git a/README.md b/README.md index b13a1fe..d5a2315 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,8 @@   ![License MIT](https://img.shields.io/badge/license-MIT-lightgrey.svg?style=flat-square) -* **npm**: `npm i react-shadow --save` +* **npm**: `npm i react-shadow` +* **yarn**: `yarn add react-shadow` * **Heroku**: [http://react-shadow.herokuapp.com/](http://react-shadow.herokuapp.com/) ![Screenshot](media/screenshot.png) @@ -19,65 +20,39 @@ ## Getting Started -By using `ReactShadow` you have all the benefits of [Shadow DOM](https://www.w3.org/TR/shadow-dom/) in React. +Creating the [shadow root](https://www.w3.org/TR/shadow-dom/) 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. -```javascript -import ShadowDOM from 'react-shadow'; - -export default props => { +```jsx +import root from 'react-shadow'; +import styles from './styles.css'; +export default function Quote { return ( - -

Calendar for {props.date}

-
+ + “There is strong shadow where there is much light.” +
― Johann Wolfgang von Goethe.
+ +
); - } ``` -In the above example the `h1` element will become the host element with a shadow boundary — and the two defined CSS documents will be fetched and appended. - -## Preventing FOUC +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](https://github.com/Wildhoney/ReactShadow/tree/master/example) we use the following Webpack configuration to import CSS documents as strings. -As the CSS documents are being fetched over the network, the host element will have a `className` of `resolving` for you to avoid the dreaded [FOUC](https://en.wikipedia.org/wiki/Flash_of_unstyled_content). Once **all** of the documents have been attached the `className` will change to `resolved`. - -Using the `resolved` class name you could then allow the component to appear once all styles have been applied. - -```css -.component { - opacity: 0; - transform: scale(.75); - transition: all .35s cubic-bezier(0.175, 0.885, 0.32, 1.275); -} - -.component.resolved { - opacity: 1; - transform: scale(1); +```javascript +{ + test: /\.css$/, + loader: ['to-string-loader', 'css-loader'] } ``` -## Cached Documents - -Oftentimes components share the same documents, however only **one** instance will be fetched due to `memoize` of the [`fetchInclude`](https://github.com/Wildhoney/ReactShadow/blob/master/src/react-shadow.js#L34-L45) function. - -## Inline Styles - -Instead of defining external CSS documents to fetch, you could choose to add all of the component's styles to the component itself by simply embedding a `style` node in your component. Naturally all styles added this way will be encapsulated within the shadow boundary. +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. ```javascript -export default props => { - - const styles = `:host { background-color: ${props.theme} }`; - - return ( - -
-

Calendar for {props.date}

- -
-
- ); -} +ShadowRoot.propTypes = { + mode: PropTypes.oneOf(['open', 'closed']), + delegatesFocus: PropTypes.bool, + styleSheets: PropTypes.arrayOf(PropTypes.string), + children: PropTypes.node.isRequired, +}; ``` - -> **Note**: Using inline styles will **not** combine styles into one `style` node. diff --git a/ava.config.js b/ava.config.js new file mode 100644 index 0000000..0f1186f --- /dev/null +++ b/ava.config.js @@ -0,0 +1,8 @@ +export default { + require: [ + '@babel/register', + '@babel/polyfill', + './helpers/enzyme.js', + './helpers/browser-env.js', + ], +}; diff --git a/helpers/browser-env.js b/helpers/browser-env.js new file mode 100644 index 0000000..c6107a8 --- /dev/null +++ b/helpers/browser-env.js @@ -0,0 +1,3 @@ +import browserEnv from 'browser-env'; + +browserEnv(); diff --git a/helpers/enzyme.js b/helpers/enzyme.js new file mode 100644 index 0000000..fc7b0dc --- /dev/null +++ b/helpers/enzyme.js @@ -0,0 +1,4 @@ +import Enzyme from 'enzyme'; +import Adapter from 'enzyme-adapter-react-16'; + +Enzyme.configure({ adapter: new Adapter() }); diff --git a/src/index.test.js b/src/index.test.js new file mode 100644 index 0000000..daf7edf --- /dev/null +++ b/src/index.test.js @@ -0,0 +1,55 @@ +import React, { useState } from 'react'; +import test from 'ava'; +import { mount } from 'enzyme'; +import sinon from 'sinon'; +import root from './'; + +test('It should be able to create the shadow boundary;', t => { + const wrapper = mount(Hello Adam!); + t.truthy(wrapper.getDOMNode().shadowRoot); + t.is(wrapper.getDOMNode().shadowRoot.innerHTML, 'Hello Adam!'); +}); + +test('It should be able to register events in the shadow boundary;', t => { + const spies = { onClick: sinon.spy() }; + const wrapper = mount( + +
spies.onClick('Maria')}>Hello Maria
+
, + ); + const node = wrapper.getDOMNode().shadowRoot.querySelector('div'); + node.click(); + t.is(spies.onClick.callCount, 1); + t.true(spies.onClick.calledWith('Maria')); +}); + +test('It should be able to applie styles to the shadow boundary components;', t => { + const wrapper = mount( + + Hello Adam! + + , + ); + const node = wrapper.getDOMNode().shadowRoot.querySelector('style'); + t.is(node.innerHTML, '* { border: 1px solid red; }'); +}); + +test('It should be able re-render the component tree from the event handlers;', t => { + function Name() { + const [name, setName] = useState(null); + return ( + <> + {name &&
Hello {name}!
} + + + + + ); + } + + const wrapper = mount(); + const node = wrapper.getDOMNode().shadowRoot.querySelector('button'); + node.click(); + wrapper.update(); + t.is(wrapper.find('div').text(), 'Hello Adam!'); +});