Comparing version 0.1.2 to 1.0.0
@@ -19,4 +19,4 @@ 'use strict'; | ||
var isPlainObject = require('is-plain-object'); | ||
var createBroadcast = require('./create-broadcast'); | ||
var channel = require('./channel'); | ||
var createBroadcast = require('brcast'); | ||
@@ -30,37 +30,68 @@ /** | ||
function createThemeProvider() { | ||
var _class, _temp; | ||
var _class, _temp2; | ||
var CHANNEL = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : channel; | ||
return _temp = _class = function (_React$Component) { | ||
return _temp2 = _class = function (_React$Component) { | ||
_inherits(ThemeProvider, _React$Component); | ||
function ThemeProvider() { | ||
var _ref; | ||
var _temp, _this, _ret; | ||
_classCallCheck(this, ThemeProvider); | ||
var _this = _possibleConstructorReturn(this, (ThemeProvider.__proto__ || Object.getPrototypeOf(ThemeProvider)).call(this)); | ||
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { | ||
args[_key] = arguments[_key]; | ||
} | ||
_this.getTheme = _this.getTheme.bind(_this); | ||
return _this; | ||
return _ret = (_temp = (_this = _possibleConstructorReturn(this, (_ref = ThemeProvider.__proto__ || Object.getPrototypeOf(ThemeProvider)).call.apply(_ref, [this].concat(args))), _this), _this.broadcast = createBroadcast(_this.getTheme()), _this.setOuterTheme = function (theme) { | ||
_this.outerTheme = theme; | ||
}, _temp), _possibleConstructorReturn(_this, _ret); | ||
} | ||
_createClass(ThemeProvider, [{ | ||
key: 'getTheme', | ||
// Get the theme from the props, supporting both (outerTheme) => {} as well as object notation | ||
value: function getTheme(passedTheme) { | ||
var theme = passedTheme || this.props.theme; | ||
if (isFunction(theme)) { | ||
var mergedTheme = theme(this.outerTheme); | ||
if (!isPlainObject(mergedTheme)) { | ||
throw new Error('[ThemeProvider] Please return an object from your theme function, i.e. theme={() => ({})}!'); | ||
} | ||
return mergedTheme; | ||
} | ||
if (!isPlainObject(theme)) { | ||
throw new Error('[ThemeProvider] Please make your theme prop a plain object'); | ||
} | ||
return _extends({}, this.outerTheme, theme); | ||
} | ||
}, { | ||
key: 'getChildContext', | ||
value: function getChildContext() { | ||
return _extends({}, this.context, _defineProperty({}, CHANNEL, this.broadcast.subscribe)); | ||
return _defineProperty({}, CHANNEL, this.broadcast); | ||
} | ||
}, { | ||
key: 'componentDidMount', | ||
value: function componentDidMount() { | ||
// create a new subscription for keeping track of outer theme, if present | ||
if (this.context[CHANNEL]) { | ||
this.unsubscribe = this.context[CHANNEL].subscribe(this.setOuterTheme); | ||
} | ||
} | ||
// set broadcast state by merging outer theme with own | ||
}, { | ||
key: 'componentWillMount', | ||
value: function componentWillMount() { | ||
var _this2 = this; | ||
// If there is a ThemeProvider wrapper anywhere around this theme provider, merge this theme | ||
// with the outer theme | ||
if (this.context[CHANNEL]) { | ||
var subscribe = this.context[CHANNEL]; | ||
this.unsubscribeToOuter = subscribe(function (theme) { | ||
_this2.outerTheme = theme; | ||
}); | ||
this.setOuterTheme(this.context[CHANNEL].getState()); | ||
this.broadcast.setState(this.getTheme()); | ||
} | ||
this.broadcast = createBroadcast(this.getTheme()); | ||
} | ||
@@ -71,3 +102,3 @@ }, { | ||
if (this.props.theme !== nextProps.theme) { | ||
this.broadcast.publish(this.getTheme(nextProps.theme)); | ||
this.broadcast.setState(this.getTheme(nextProps.theme)); | ||
} | ||
@@ -78,26 +109,7 @@ } | ||
value: function componentWillUnmount() { | ||
if (this.context[CHANNEL]) { | ||
this.unsubscribeToOuter(); | ||
if (typeof this.unsubscribe === 'function') { | ||
this.unsubscribe(); | ||
} | ||
} | ||
// Get the theme from the props, supporting both (outerTheme) => {} as well as object notation | ||
}, { | ||
key: 'getTheme', | ||
value: function getTheme(passedTheme) { | ||
var theme = passedTheme || this.props.theme; | ||
if (isFunction(theme)) { | ||
var mergedTheme = theme(this.outerTheme); | ||
if (!isPlainObject(mergedTheme)) { | ||
throw new Error('[ThemeProvider] Please return an object from your theme function, i.e. theme={() => ({})}!'); | ||
} | ||
return mergedTheme; | ||
} | ||
if (!isPlainObject(theme)) { | ||
throw new Error('[ThemeProvider] Please make your theme prop a plain object'); | ||
} | ||
return _extends({}, this.outerTheme, theme); | ||
} | ||
}, { | ||
key: 'render', | ||
@@ -116,5 +128,5 @@ value: function render() { | ||
theme: PropTypes.oneOfType([PropTypes.shape({}), PropTypes.func]).isRequired | ||
}, _class.contextTypes = _defineProperty({}, CHANNEL, PropTypes.func), _class.childContextTypes = _defineProperty({}, CHANNEL, PropTypes.func.isRequired), _temp; | ||
}, _class.childContextTypes = _defineProperty({}, CHANNEL, PropTypes.object.isRequired), _class.contextTypes = _defineProperty({}, CHANNEL, PropTypes.object), _temp2; | ||
} | ||
module.exports = createThemeProvider; |
@@ -19,2 +19,6 @@ 'use strict'; | ||
var getDisplayName = function getDisplayName(Component) { | ||
return Component.displayName || Component.name || 'Component'; | ||
}; | ||
function createWithTheme() { | ||
@@ -24,28 +28,40 @@ var CHANNEL = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : channel; | ||
return function (Component) { | ||
var _class, _temp; | ||
var _class, _temp2; | ||
return _temp = _class = function (_React$Component) { | ||
_inherits(withTheme, _React$Component); | ||
return _temp2 = _class = function (_React$Component) { | ||
_inherits(WithTheme, _React$Component); | ||
function withTheme() { | ||
_classCallCheck(this, withTheme); | ||
function WithTheme() { | ||
var _ref; | ||
return _possibleConstructorReturn(this, (withTheme.__proto__ || Object.getPrototypeOf(withTheme)).apply(this, arguments)); | ||
var _temp, _this, _ret; | ||
_classCallCheck(this, WithTheme); | ||
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { | ||
args[_key] = arguments[_key]; | ||
} | ||
return _ret = (_temp = (_this = _possibleConstructorReturn(this, (_ref = WithTheme.__proto__ || Object.getPrototypeOf(WithTheme)).call.apply(_ref, [this].concat(args))), _this), _this.state = { theme: {} }, _this.setTheme = function (theme) { | ||
return _this.setState({ theme: theme }); | ||
}, _temp), _possibleConstructorReturn(_this, _ret); | ||
} | ||
_createClass(withTheme, [{ | ||
_createClass(WithTheme, [{ | ||
key: 'componentWillMount', | ||
value: function componentWillMount() { | ||
var _this2 = this; | ||
if (!this.context[CHANNEL]) { | ||
throw new Error('[withTheme] Please use ThemeProvider to be able to use withTheme'); | ||
throw new Error('[WithTheme(${getDisplayName(Component)})] Please use ThemeProvider to be able to use WithTheme'); | ||
} | ||
var subscribe = this.context[CHANNEL]; | ||
this.unsubscribe = subscribe(function (theme) { | ||
_this2.setState({ theme: theme }); | ||
}); | ||
this.setState({ theme: this.context[CHANNEL].getState() }); | ||
} | ||
}, { | ||
key: 'componentDidMount', | ||
value: function componentDidMount() { | ||
if (this.context[CHANNEL]) { | ||
this.unsubscribe = this.context[CHANNEL].subscribe(this.setTheme); | ||
} | ||
} | ||
}, { | ||
key: 'componentWillUnmount', | ||
@@ -67,4 +83,4 @@ value: function componentWillUnmount() { | ||
return withTheme; | ||
}(React.Component), _class.contextTypes = _defineProperty({}, CHANNEL, PropTypes.func), _temp; | ||
return WithTheme; | ||
}(React.Component), _class.displayName = 'WithTheme(' + getDisplayName(Component) + ')', _class.contextTypes = _defineProperty({}, CHANNEL, PropTypes.object.isRequired), _temp2; | ||
}; | ||
@@ -71,0 +87,0 @@ } |
{ | ||
"name": "theming", | ||
"version": "0.1.2", | ||
"description": "Theming High-Order Components toolkit", | ||
"version": "1.0.0", | ||
"description": "Unified CSSinJS theming solution for React", | ||
"main": "dist", | ||
@@ -26,3 +26,4 @@ "files": [ | ||
"require": [ | ||
"babel-register" | ||
"babel-register", | ||
"./.browser-env" | ||
], | ||
@@ -86,2 +87,3 @@ "babel": "inherit" | ||
"dependencies": { | ||
"brcast": "^2.0.0", | ||
"is-function": "^1.0.1", | ||
@@ -88,0 +90,0 @@ "is-plain-object": "^2.0.1", |
@@ -8,4 +8,11 @@ # theming | ||
> Theming High-Order Components toolkit | ||
> Unified CSSinJS theming solution for React | ||
* `ThemeProvider` allows you to pass, update, merge and augment `theme` through context down react tree. | ||
* `withTheme` allows you to receive theme and its updates in your components as a `theme` prop. | ||
* `createTheming` allows you to integrate `theming` into your CSSinJS library with custom `channel` (if you need custom one). | ||
See [Motivation](#motivation) for details. | ||
## Table of Contents | ||
@@ -15,2 +22,4 @@ | ||
* [Usage](#usage) | ||
* [Playground demo](#playground-demo) | ||
* [Motivation](#motivation) | ||
* [API](#api) | ||
@@ -21,2 +30,3 @@ * [channel](#channel) | ||
* [createTheming](#createthemingcustomchannel) | ||
* [Credits](#credits) | ||
* [License](#license) | ||
@@ -71,2 +81,30 @@ | ||
## Playground demo | ||
Be our guest, play with `theming` in this webpackbin: | ||
[https://www.webpackbin.com/bins/-Km8TglfWP84oYhDquT1](https://www.webpackbin.com/bins/-Km8TglfWP84oYhDquT1) | ||
[![theming](https://user-images.githubusercontent.com/559321/26953553-5049761e-4cab-11e7-848f-9056684487cd.gif)](https://www.webpackbin.com/bins/-Km8TglfWP84oYhDquT1) | ||
## Motivation | ||
These components are enabling seamless theming for your react applications. And as far as you dont want to pass theme object to each and every component. Thats why you want to use context. But as far context feature is _experimental API and it is likely to break in future releases of React_ you don't want to use it directly. Here `theming` comes to play. | ||
> If you insist on using context despite these warnings, try to isolate your use of context to a small area and avoid using the context API directly when possible so that it's easier to upgrade when the API changes. | ||
> | ||
> If you insist on using context despite these warnings, try to isolate your use of context to a small area and avoid using the context API directly when possible so that it's easier to upgrade when the API changes. | ||
> — [Context, React documentation](https://facebook.github.io/react/docs/context.html) | ||
Regarding _isolation your use of context to a small area_ and _small areas__ in particular our very own react prophet Dan Abramov have a thing to say: | ||
> Should I use React unstable “context” feature? | ||
> <img src="https://pbs.twimg.com/media/CmeGPNcVYAAM7TR.jpg" alt="![context application areas]" height="300" /> | ||
> — [Dan Abramov @dan_abramov on Twitter](https://twitter.com/dan_abramov/status/749715530454622208?lang=en) | ||
So you are fine to use context for theming. `theming` package provides everything you need to do that: | ||
* `ThemeProvider` allows you to pass and update `theme` through context down react tree. | ||
* `withTheme` allows you to receive theme and its updates in your components as a `theme` prop. | ||
* `createTheming` allows you to integrate `theming` into your CSSinJS library with custom `channel` (if you need custom one). | ||
## API | ||
@@ -144,3 +182,3 @@ | ||
*Required* | ||
Type: `PropTypes.elemenwt` | ||
Type: `PropTypes.element` | ||
@@ -158,5 +196,4 @@ ThemeProvider uses [`React.Children.only`](https://facebook.github.io/react/docs/react-api.html#react.children.only) in render, which returns the only child in children. Throws otherwise. | ||
You need to have `ThemeProvider` with a theme somewhere upper the react tree, after that wrap you component in `withTheme` and your component will get theme as prop.1 `withTheme` will handle initial theme object as well as theme updates. | ||
You need to have `ThemeProvider` with a theme somewhere upper the react tree, after that wrap your component in `withTheme` and your component will get theme as a prop. `withTheme` will handle initial theme object as well as theme updates. | ||
PS. It doesnt break if you have `PureComponent` somewhere in between your ThemeProvider and withTheme (i have tests for that). | ||
@@ -227,2 +264,24 @@ | ||
## Credits | ||
* Thanks to [jss][jss] creator [Oleg Slobodskoi @kof][kof] for immersive help, support and code review. | ||
* Thanks to [styled-components][sc] creator [Max Stoiber @mxstbr][mxstbr] for initial and battle tested implementation of theming support in [styled-components][sc] as well as help and code review. | ||
* Thanks to [styled-components'][sc] core team member [Phil Plückthun @philpl][philpl] for help and code review. | ||
* Thanks to [glamorous][glamorous] creator [Kent C. Dodds @kentcdodds][kentcdodds] for help, support and code review. | ||
* Thanks to [glamorous's][glamorous] core team member [Alessandro Arnodo @vesparny][vesparny] for improved theming support in [glamorous][glamorous] and [brcast][brcast]. | ||
* Thanks to [Gert Sallaerts @gertt][gertt] for the [playground][playground] demo. | ||
[kof]: https://github.com/kof | ||
[mxstbr]: https://github.com/mxstbr | ||
[philpl]: https://github.com/philpl | ||
[kentcdodds]: https://github.com/kentcdodds | ||
[vesparny]: https://github.com/vesparny | ||
[gertt]: https://github.com/gertt | ||
[jss]: https://github.com/cssinjs/jss | ||
[sc]: https://github.com/styled-components/styled-components | ||
[glamorous]: https://github.com/paypal/glamorous | ||
[brcast]: https://github.com/vesparny/brcast | ||
[playground]: https://www.webpackbin.com/bins/-Km8TglfWP84oYhDquT1 | ||
## License | ||
@@ -229,0 +288,0 @@ |
@@ -5,4 +5,4 @@ const React = require('react'); | ||
const isPlainObject = require('is-plain-object'); | ||
const createBroadcast = require('./create-broadcast'); | ||
const channel = require('./channel'); | ||
const createBroadcast = require('brcast'); | ||
@@ -23,43 +23,12 @@ /** | ||
static contextTypes = { | ||
[CHANNEL]: PropTypes.func, | ||
static childContextTypes = { | ||
[CHANNEL]: PropTypes.object.isRequired, | ||
}; | ||
static childContextTypes = { | ||
[CHANNEL]: PropTypes.func.isRequired, | ||
static contextTypes = { | ||
[CHANNEL]: PropTypes.object, | ||
}; | ||
constructor() { | ||
super(); | ||
this.getTheme = this.getTheme.bind(this); | ||
} | ||
broadcast = createBroadcast(this.getTheme()); | ||
getChildContext() { | ||
return { ...this.context, [CHANNEL]: this.broadcast.subscribe }; | ||
} | ||
componentWillMount() { | ||
// If there is a ThemeProvider wrapper anywhere around this theme provider, merge this theme | ||
// with the outer theme | ||
if (this.context[CHANNEL]) { | ||
const subscribe = this.context[CHANNEL]; | ||
this.unsubscribeToOuter = subscribe(theme => { | ||
this.outerTheme = theme; | ||
}); | ||
} | ||
this.broadcast = createBroadcast(this.getTheme()); | ||
} | ||
componentWillReceiveProps(nextProps) { | ||
if (this.props.theme !== nextProps.theme) { | ||
this.broadcast.publish(this.getTheme(nextProps.theme)); | ||
} | ||
} | ||
componentWillUnmount() { | ||
if (this.context[CHANNEL]) { | ||
this.unsubscribeToOuter(); | ||
} | ||
} | ||
// Get the theme from the props, supporting both (outerTheme) => {} as well as object notation | ||
@@ -82,5 +51,41 @@ getTheme(passedTheme) { | ||
} | ||
return { ...this.outerTheme, ...(theme: Object) }; | ||
return { ...this.outerTheme, ...theme }; | ||
} | ||
setOuterTheme = theme => { | ||
this.outerTheme = theme; | ||
}; | ||
getChildContext() { | ||
return { [CHANNEL]: this.broadcast }; | ||
} | ||
componentDidMount() { | ||
// create a new subscription for keeping track of outer theme, if present | ||
if (this.context[CHANNEL]) { | ||
this.unsubscribe = this.context[CHANNEL].subscribe(this.setOuterTheme); | ||
} | ||
} | ||
// set broadcast state by merging outer theme with own | ||
componentWillMount() { | ||
if (this.context[CHANNEL]) { | ||
this.setOuterTheme(this.context[CHANNEL].getState()); | ||
this.broadcast.setState(this.getTheme()); | ||
} | ||
} | ||
componentWillReceiveProps(nextProps) { | ||
if (this.props.theme !== nextProps.theme) { | ||
this.broadcast.setState(this.getTheme(nextProps.theme)); | ||
} | ||
} | ||
componentWillUnmount() { | ||
if (typeof this.unsubscribe === 'function') { | ||
this.unsubscribe(); | ||
} | ||
} | ||
render() { | ||
@@ -87,0 +92,0 @@ if (!this.props.children) { |
import test from 'ava'; | ||
import React, { Component } from 'react'; | ||
import { mount } from 'enzyme'; | ||
import browserEnv from 'browser-env'; | ||
@@ -9,5 +8,12 @@ import isFunction from 'is-function'; | ||
import channel from './channel'; | ||
import { getChannel, Trap, Pure, getInterceptor } from './test-helpers'; | ||
// import createBroadcast from './create-broadcast'; | ||
const createBroadcast = require('brcast'); | ||
browserEnv(['window', 'document', 'navigator']); | ||
import { | ||
getChannel, | ||
Trap, | ||
Pure, | ||
getInterceptor, | ||
mountOptions, | ||
} from './test-helpers'; | ||
@@ -44,2 +50,51 @@ test(`createThemeProvider's type`, t => { | ||
test(`ThemeProvider unsubscribes on unmounting`, t => { | ||
const ThemeProvider = createThemeProvider(); | ||
const theme = { themed: true }; | ||
const broadcast = createBroadcast(theme); | ||
const unsubscribed = getInterceptor(false); | ||
const wrapper = mount( | ||
<ThemeProvider theme={theme} />, | ||
mountOptions(broadcast), | ||
); | ||
t.false(unsubscribed()); | ||
wrapper.instance().unsubscribe = () => unsubscribed(true); | ||
wrapper.unmount(); | ||
t.true(unsubscribed(), `ThemeProvider should unsubscribe on unmounting`); | ||
}); | ||
test(`ThemeProvider and not a plain object theme`, t => { | ||
const ThemeProvider = createThemeProvider(); | ||
t.throws( | ||
() => { | ||
mount(<ThemeProvider theme={false} />); | ||
}, | ||
Error, | ||
`ThemeProvider should throw if theme is not a plain object`, | ||
); | ||
}); | ||
test(`ThemeProvider and broken function theme`, t => { | ||
const ThemeProvider = createThemeProvider(); | ||
const theme = { themed: true }; | ||
const incorrectAugment = () => false; | ||
t.throws( | ||
() => { | ||
mount( | ||
<ThemeProvider theme={theme}> | ||
<ThemeProvider theme={incorrectAugment} /> | ||
</ThemeProvider>, | ||
); | ||
}, | ||
Error, | ||
`ThemeProvider should throw if function theme returns not a plain object`, | ||
); | ||
}); | ||
test(`ThemeProvider passes theme`, t => { | ||
@@ -118,3 +173,4 @@ const ThemeProvider = createThemeProvider(); | ||
); | ||
// console.log({ actual: actual() }); | ||
// t.is(1, 1); | ||
t.deepEqual(actual(), expected, `ThemeProvider should merge themes`); | ||
@@ -121,0 +177,0 @@ }); |
@@ -5,7 +5,14 @@ const React = require('react'); | ||
const getDisplayName = Component => | ||
Component.displayName || Component.name || 'Component'; | ||
function createWithTheme(CHANNEL = channel) { | ||
return Component => | ||
class withTheme extends React.Component { | ||
class WithTheme extends React.Component { | ||
static displayName = `WithTheme(${getDisplayName(Component)})`; | ||
state = { theme: {} }; | ||
setTheme = theme => this.setState({ theme }); | ||
static contextTypes = { | ||
[CHANNEL]: PropTypes.func, | ||
[CHANNEL]: PropTypes.object.isRequired, | ||
}; | ||
@@ -16,12 +23,15 @@ | ||
throw new Error( | ||
'[withTheme] Please use ThemeProvider to be able to use withTheme', | ||
'[WithTheme(${getDisplayName(Component)})] Please use ThemeProvider to be able to use WithTheme', | ||
); | ||
} | ||
const subscribe = this.context[CHANNEL]; | ||
this.unsubscribe = subscribe(theme => { | ||
this.setState({ theme }); | ||
}); | ||
this.setState({ theme: this.context[CHANNEL].getState() }); | ||
} | ||
componentDidMount() { | ||
if (this.context[CHANNEL]) { | ||
this.unsubscribe = this.context[CHANNEL].subscribe(this.setTheme); | ||
} | ||
} | ||
componentWillUnmount() { | ||
@@ -28,0 +38,0 @@ if (typeof this.unsubscribe === 'function') { |
import test from 'ava'; | ||
import React, { Component } from 'react'; | ||
import { mount } from 'enzyme'; | ||
import browserEnv from 'browser-env'; | ||
import { mount, shallow } from 'enzyme'; | ||
@@ -9,3 +8,3 @@ import isFunction from 'is-function'; | ||
import channel from './channel'; | ||
import createBroadcast from './create-broadcast'; | ||
import createBroadcast from 'brcast'; | ||
import { | ||
@@ -20,4 +19,2 @@ getChannel, | ||
browserEnv(['window', 'document', 'navigator']); | ||
test(`createWithTheme's type`, t => { | ||
@@ -55,2 +52,79 @@ const actual = isFunction(createWithTheme); | ||
test(`withTheme(Comp) and stateless component`, t => { | ||
const withTheme = createWithTheme(); | ||
const StatelessComp = (...props) => <div {...props} />; | ||
const ThemedComp = withTheme(StatelessComp); | ||
const theme = { themed: true }; | ||
const broadcast = createBroadcast(theme); | ||
const wrapper = shallow( | ||
<div><ThemedComp /></div>, | ||
mountOptions(broadcast), | ||
).childAt(0); | ||
const actual = wrapper.name(); | ||
const expected = `WithTheme(StatelessComp)`; | ||
t.is( | ||
actual, | ||
expected, | ||
`withTheme(Comp) should include wrapped stateless component's name in the displayName`, | ||
); | ||
}); | ||
test(`withTheme(Comp) and statefull component`, t => { | ||
const withTheme = createWithTheme(); | ||
class StatefullComp extends Component { | ||
render() { | ||
return <div {...this.props} />; | ||
} | ||
} | ||
const ThemedComp = withTheme(StatefullComp); | ||
const theme = { themed: true }; | ||
const broadcast = createBroadcast(theme); | ||
const wrapper = shallow( | ||
<div><ThemedComp /></div>, | ||
mountOptions(broadcast), | ||
).childAt(0); | ||
const actual = wrapper.name(); | ||
const expected = `WithTheme(StatefullComp)`; | ||
t.is( | ||
actual, | ||
expected, | ||
`withTheme(Comp) should include wrapped statefull component's name in the displayName`, | ||
); | ||
}); | ||
test(`withTheme(Comp) unsubscribes on unmounting`, t => { | ||
const withTheme = createWithTheme(); | ||
const theme = { themed: true }; | ||
const ComponentWithTheme = withTheme(Trap.Prop); | ||
const broadcast = createBroadcast(theme); | ||
const unsubscribed = getInterceptor(false); | ||
const wrapper = mount( | ||
<ComponentWithTheme intercept={() => {}} />, | ||
mountOptions(broadcast), | ||
); | ||
t.false(unsubscribed()); | ||
wrapper.instance().unsubscribe = () => unsubscribed(true); | ||
wrapper.unmount(); | ||
t.true(unsubscribed(), `withTheme(Comp) should unsubscribe on unmounting`); | ||
}); | ||
test(`withTheme(Comp) without ThemeProvider`, t => { | ||
const withTheme = createWithTheme(); | ||
const ComponentWithTheme = withTheme(Trap.Prop); | ||
t.throws( | ||
() => { | ||
mount(<ComponentWithTheme intercept={() => {}} />); | ||
}, | ||
Error, | ||
`withTheme(Comp) should throw if used with appropriate context`, | ||
); | ||
}); | ||
test(`withTheme(Comp) receive theme`, t => { | ||
@@ -130,3 +204,3 @@ const withTheme = createWithTheme(); | ||
broadcast.publish(update); | ||
broadcast.setState(update); | ||
@@ -157,3 +231,3 @@ t.deepEqual( | ||
broadcast.publish(update); | ||
broadcast.setState(update); | ||
@@ -160,0 +234,0 @@ t.deepEqual( |
import test from 'ava'; | ||
import React from 'react'; | ||
import { mount } from 'enzyme'; | ||
import browserEnv from 'browser-env'; | ||
import isFunction from 'is-function'; | ||
@@ -9,4 +8,2 @@ import isPlainObject from 'is-plain-object'; | ||
browserEnv(['window', 'document', 'navigator']); | ||
import { channel, createTheming, ThemeProvider, withTheme } from './index'; | ||
@@ -13,0 +10,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
71477
19
1510
0
295
5
1
+ Addedbrcast@^2.0.0
+ Addedbrcast@2.0.2(transitive)