├── .editorconfig ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── karma.conf.js ├── package.json ├── rollup.config.js ├── src └── index.js └── test └── index.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = tab 5 | end_of_line = lf 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | 10 | [{package.json,.*rc,*.yml}] 11 | indent_style = space 12 | indent_size = 2 13 | 14 | [*.md] 15 | trim_trailing_whitespace = false 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /dist 2 | /node_modules 3 | /npm-debug.log 4 | .DS_Store 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - stable 4 | 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017, Synacor, Inc. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 5 | 6 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 7 | 8 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 9 | 10 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 11 | 12 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # preact-context-provider 2 | 3 | [![npm](https://img.shields.io/npm/v/preact-context-provider.svg)](http://npm.im/preact-context-provider) 4 | [![Build Status](https://travis-ci.org/synacor/preact-context-provider.svg?branch=master)](https://travis-ci.org/synacor/preact-context-provider) 5 | 6 | A generic `` for preact. It exposes any props you pass it into context. Also provides a merging variant ``, and utility functions `provide` and `mergingProvide` 7 | 8 | ## Usage 9 | 10 | Install it via npm: 11 | 12 | ```sh 13 | npm install --save preact-context-provider 14 | 15 | # or, for Preact X support 16 | npm install --save preact-context-provider@preactx 17 | ``` 18 | 19 | Then import it and use: 20 | 21 | ```js 22 | import Provider from 'preact-context-provider'; 23 | 24 | let OBJ = { a: 'b' }; 25 | 26 | const App = (props, context) => { 27 | // now it's exposed to context! 28 | console.log(context.obj === OBJ) // true 29 | }; 30 | 31 | render( 32 | 33 | 34 | 35 | ); 36 | ``` 37 | 38 | ## Preact Version Support 39 | 40 | By default, the `master` branch of this repo supports preact 9 and below, and is published in normal patch/minor/major releases to the `latest` tag in npm. Support for preact X (versions 10+ of preact) is handled in the `preactX` branch and are always published to the `preactx` tag in npm. When preact X obtains widespread adoption, the `master` branch of this project will support preact X and a new major version under `latest` tag will be published to in npm. 41 | 42 | ## API 43 | 44 | 45 | 46 | #### Table of Contents 47 | 48 | - [Provider](#provider) 49 | - [Parameters](#parameters) 50 | - [Examples](#examples) 51 | - [MergingProvider](#mergingprovider) 52 | - [Parameters](#parameters-1) 53 | - [Examples](#examples-1) 54 | - [provide](#provide) 55 | - [Parameters](#parameters-2) 56 | - [Examples](#examples-2) 57 | - [mergingProvide](#mergingprovide) 58 | - [Parameters](#parameters-3) 59 | - [Examples](#examples-3) 60 | 61 | ### Provider 62 | 63 | Adds all passed `props`, `children` into `context`, making them available to all descendants. 64 | 65 | To learn about `context`, see the [React Docs](https://facebook.github.io/react/docs/context.html). 66 | 67 | #### Parameters 68 | 69 | - `props` **[Object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object)** All props are exposed as properties in `context`, except children 70 | 71 | #### Examples 72 | 73 | ```javascript 74 | const Demo = (props, context) => { 75 | console.log(context); // "{ a: 'b' }" 76 | }; 77 | render( 78 | 79 | 80 | 81 | ); 82 | // "{ a: 'b' }" 83 | 84 | // lower-level providers override higher providers for any keys that they define 85 | render( 86 | 87 | 88 | 89 | 90 | 91 | ); 92 | // "{ a: { key3: 'buz' }, b: { key2: 'bar' } }" 93 | ``` 94 | 95 | ### MergingProvider 96 | 97 | Similar to [Provider](#provider), but allows a special `mergeProps` prop to allow parent supplied context keys with the same name as those 98 | provided by the current `MergingProvider` to be deep merged, instead of replaced. 99 | 100 | To learn about `context`, see the [React Docs](https://facebook.github.io/react/docs/context.html). 101 | 102 | #### Parameters 103 | 104 | - `props` **[Object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object)** All props are exposed as properties in `context`, except `children` and `mergeProps` 105 | - `props.mergeProps` **[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)?** If not supplied, all supplied props will be merged with keys already in context. If supplied as an array of strings, 106 | it will deep merge any prop names that are present in the array, and missing prop names be overriden by the child like [Provider](#provider). 107 | 108 | #### Examples 109 | 110 | ```javascript 111 | import Provider, { MergingProvider } from 'preact-context-provider'; 112 | const Demo = (props, context) => { 113 | console.log(context); // "{ a: 'b' }" 114 | }; 115 | 116 | // with mergeProps unspecified, all parent context keys are merged with the ones presently supplied, parent values taking precedence 117 | render( 118 | 119 | 120 | 121 | 122 | 123 | ); 124 | // "{ a: { key1: 'foo', key2: 'bar' } }" 125 | 126 | // when mergeProps is an array, only specified keys are merged, non-specified keys get their value from current node 127 | // in this example, only the 'a' context key is merged. 'b' is overwritten by the lower node 128 | render( 129 | 130 | 131 | 132 | 133 | 134 | ); 135 | // "{ a: { key1: 'foo', key3: 'baz' }, b: {key4: 'buz'} }" 136 | ``` 137 | 138 | ### provide 139 | 140 | Higher Order Component that wraps components in a [Provider](#provider) for the given context. 141 | 142 | #### Parameters 143 | 144 | - `ctx` **[Object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object)** Properties to pass into context (passed to [Provider](#provider)) 145 | 146 | #### Examples 147 | 148 | ```javascript 149 | import {provide} from 'preact-context-provider'; 150 | const Demo = (props, context) => { 151 | console.log(context.a); // "b" 152 | }; 153 | const ProvidedDemo = provide({a: "b"})(Demo); 154 | 155 | ProvidedDemo.getWrappedComponent() === Demo; // true 156 | 157 | render( ); 158 | ``` 159 | 160 | Returns **[Function](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function)** A function that, given a Child component, wraps it in a Provider component for the given context. 161 | 162 | ### mergingProvide 163 | 164 | Higher Order Component that wraps components in a [MergingProvider](#mergingprovider) for the given context. 165 | 166 | #### Parameters 167 | 168 | - `ctx` **[Object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object)** Properties to pass into context (passed to [MergingProvider](#mergingprovider)) 169 | 170 | #### Examples 171 | 172 | ```javascript 173 | import {mergingProvide} from 'preact-context-provider'; 174 | const Demo = (props, context) => { 175 | console.log(context.a); 176 | }; 177 | const ProvidedDemo = mergingProvide({a: "b"})(Demo); 178 | 179 | ProvidedDemo.getWrappedComponent() === Demo; // true 180 | 181 | render( ); // "b" 182 | ``` 183 | 184 | Returns **[Function](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function)** A function that, given a Child component, wraps it in a [MergingProvider](#mergingprovider) component for the given context. 185 | -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | 3 | var path = require('path'); 4 | 5 | var pkg = require('./package.json'); 6 | 7 | var REPORTER = process.env.REPORTER || (process.env.ENVIRONMENT==='ci' && 'junit') || ''; 8 | 9 | module.exports = function(config) { 10 | config.set({ 11 | browsers: ['ChromeHeadless'], 12 | frameworks: ['mocha', 'chai-sinon'], 13 | reporters: ['mocha'].concat(REPORTER.split(/[, ]/)).filter(dedupe), 14 | junitReporter: { 15 | outputDir: 'test-reports', // results will be saved as $outputDir/$browserName.xml 16 | suite: require('./package.json').name 17 | }, 18 | mochaReporter: { showDiff: true }, 19 | files: [ 20 | 'test/**/*.js' 21 | ], 22 | preprocessors: { 23 | '{src,test}/**/*': ['webpack', 'sourcemap'] 24 | }, 25 | webpack: { 26 | mode: 'development', 27 | devtool: 'inline-source-map', 28 | resolve: { 29 | alias: { 30 | 'preact-context-provider': path.resolve(__dirname, process.env.TEST_PRODUCTION ? pkg.main : 'src') 31 | } 32 | }, 33 | module: { 34 | rules: [{ 35 | test: /\.jsx?$/, 36 | exclude: /node_modules/, 37 | loader: 'babel-loader', 38 | options: { 39 | presets: [ '@babel/env' ], 40 | plugins: [ 41 | ['@babel/plugin-transform-react-jsx', { pragma: 'h' }] 42 | ] 43 | } 44 | }] 45 | }, 46 | }, 47 | webpackServer: { stats: 'errors-only' } 48 | }); 49 | }; 50 | 51 | // filters out empties && dupes 52 | function dedupe(v, i, arr) { return v && arr.indexOf(v)===i; } 53 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "preact-context-provider", 3 | "amdName": "Provider", 4 | "version": "1.2.1", 5 | "description": "A generic for preact to put props into context", 6 | "jsnext:main": "dist/preact-context-provider.es.js", 7 | "module": "dist/preact-context-provider.es.js", 8 | "main": "dist/preact-context-provider.umd.js", 9 | "cjs:main": "dist/preact-context-provider.js", 10 | "source": "src/index.js", 11 | "scripts": { 12 | "lint": "eslint src test", 13 | "test": "npm-run-all -p lint test:unit build -s test:prod", 14 | "test:unit": "karma start karma.conf.js --single-run", 15 | "test:prod": "TEST_PRODUCTION=true npm run test:unit", 16 | "test:watch": "npm run test:unit -- --no-single-run", 17 | "build": "npm-run-all --silent clean transpile:* docs size", 18 | "clean": "rimraf dist && mkdirp dist", 19 | "transpile:cjs": "rollup -c -m -f cjs -n $npm_package_amdName -i $npm_package_source -o $npm_package_cjs_main", 20 | "transpile:umd": "rollup -c -m -f umd -n $npm_package_amdName -i $npm_package_source -o $npm_package_main", 21 | "transpile:esm": "rollup -c -m -f es -n $npm_package_amdName -i $npm_package_source -o $npm_package_module", 22 | "docs": "documentation readme src/index.js --section API -q", 23 | "size": "echo \"Gzipped Size: $(strip-json-comments --no-whitespace dist/preact-context-provider.js | gzip-size --raw)b\"", 24 | "prepublishOnly:": "npm run build -s && git commit -am $npm_package_version && git tag $npm_package_version && git push && git push --tags" 25 | }, 26 | "repository": { 27 | "type": "git", 28 | "url": "https://github.com/synacor/preact-context-provider" 29 | }, 30 | "keywords": [ 31 | "preact", 32 | "context", 33 | "provider" 34 | ], 35 | "files": [ 36 | "src", 37 | "dist" 38 | ], 39 | "homepage": "https://github.com/synacor/preact-context-provider", 40 | "authors": [ 41 | "Jason Miller ", 42 | "Bill Neff " 43 | ], 44 | "license": "BSD-3-Clause", 45 | "eslintConfig": { 46 | "extends": "eslint-config-synacor" 47 | }, 48 | "devDependencies": { 49 | "@babel/core": "^7.5.0", 50 | "@babel/plugin-transform-react-jsx": "^7.3.0", 51 | "@babel/preset-env": "^7.5.0", 52 | "babel-eslint": "^10.0.2", 53 | "babel-loader": "^8.0.6", 54 | "babel-plugin-external-helpers": "^6.22.0", 55 | "chai": "^4.2.0", 56 | "diff": "^4.0.1", 57 | "documentation": "^11.0.1", 58 | "eslint": "^5.16.0", 59 | "eslint-config-synacor": "^3.0.4", 60 | "gzip-size-cli": "^3.0.0", 61 | "karma": "^4.1.0", 62 | "karma-chai": "^0.1.0", 63 | "karma-chai-sinon": "^0.1.5", 64 | "karma-chrome-launcher": "^2.2.0", 65 | "karma-cli": "^2.0.0", 66 | "karma-junit-reporter": "^1.2.0", 67 | "karma-mocha": "^1.3.0", 68 | "karma-mocha-reporter": "^2.2.5", 69 | "karma-sourcemap-loader": "^0.3.7", 70 | "karma-webpack": "^4.0.2", 71 | "mkdirp": "^0.5.1", 72 | "mocha": "^6.1.4", 73 | "npm-run-all": "^4.1.5", 74 | "preact": "^8.2.5", 75 | "preact-jsx-chai": "^2.2.1", 76 | "rimraf": "^2.6.3", 77 | "rollup": "^1.16.6", 78 | "rollup-plugin-buble": "^0.19.8", 79 | "rollup-plugin-memory": "^3.0.0", 80 | "rollup-plugin-uglify": "^6.0.2", 81 | "sinon": "^7.3.2", 82 | "sinon-chai": "^3.3.0", 83 | "strip-json-comments-cli": "^1.0.1", 84 | "webpack": "^4.35.3" 85 | }, 86 | "peerDependencies": { 87 | "preact": "< 10" 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import memory from 'rollup-plugin-memory'; 2 | import buble from 'rollup-plugin-buble'; 3 | import { uglify } from 'rollup-plugin-uglify'; 4 | 5 | export default function(config) { 6 | let format = config.format; 7 | return { 8 | external: ['preact'], 9 | output: { 10 | strict: false, 11 | exports: format==='es' ? null : 'named', 12 | globals: { 13 | preact: 'preact' 14 | } 15 | }, 16 | plugins: [ 17 | format==='umd' && memory({ 18 | path: 'src/cjs.js', 19 | contents: "export { default } from './index';" 20 | }), 21 | buble({ jsx: 'h' }), 22 | format!=='es' && uglify() 23 | ].filter(Boolean) 24 | }; 25 | } 26 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import { h } from 'preact'; 2 | 3 | /** 4 | * Adds all passed `props`, `children` into `context`, making them available to all descendants. 5 | * 6 | * To learn about `context`, see the [React Docs](https://facebook.github.io/react/docs/context.html). 7 | * 8 | * @name Provider 9 | * @param {Object} props All props are exposed as properties in `context`, except children 10 | * 11 | * @example 12 | * const Demo = (props, context) => { 13 | * console.log(context); // "{ a: 'b' }" 14 | * }; 15 | * render( 16 | * 17 | * 18 | * 19 | * ); 20 | * // "{ a: 'b' }" 21 | * 22 | * // lower-level providers override higher providers for any keys that they define 23 | * render( 24 | * 25 | * 26 | * 27 | * 28 | * 29 | * ); 30 | * // "{ a: { key3: 'buz' }, b: { key2: 'bar' } }" 31 | */ 32 | export default class Provider { 33 | getChildContext() { 34 | let context = {}; 35 | for (let i in this.props) if (i !== 'children') { 36 | context[i] = this.props[i]; 37 | } 38 | return context; 39 | } 40 | 41 | render(props) { 42 | return props.children[0]; 43 | } 44 | } 45 | 46 | /** 47 | * A simpler Object.assign 48 | * @private 49 | */ 50 | function assign(obj, props) { 51 | for (let i in props) { 52 | if (props.hasOwnProperty(i)) { 53 | obj[i] = props[i]; 54 | } 55 | } 56 | return obj; 57 | } 58 | 59 | /** 60 | * Recursively copy keys from `source` to `target`, skipping truthy values already in `target` so parent values can block child values 61 | * @private 62 | */ 63 | function deepAssign(target, source) { 64 | //if they aren't both objects, use target if it is defined (null/0/false/etc. are OK), otherwise use source 65 | if (!(target && source && typeof target==='object' && typeof source==='object')) { 66 | return typeof target !== 'undefined' ? target : source; 67 | } 68 | 69 | let out = assign({}, target); 70 | for (let i in source) { 71 | if (source.hasOwnProperty(i)) { 72 | out[i] = deepAssign(target[i], source[i]); 73 | } 74 | } 75 | return out; 76 | } 77 | 78 | 79 | /** 80 | * Similar to {@link Provider}, but allows a special `mergeProps` prop to allow parent supplied context keys with the same name as those 81 | * provided by the current `MergingProvider` to be deep merged, instead of replaced. 82 | * 83 | * To learn about `context`, see the [React Docs](https://facebook.github.io/react/docs/context.html). 84 | * 85 | * @name MergingProvider 86 | * @param {Object} props All props are exposed as properties in `context`, except `children` and `mergeProps` 87 | * @param {Array} [props.mergeProps] If not supplied, all supplied props will be merged with keys already in context. If supplied as an array of strings, 88 | * it will deep merge any prop names that are present in the array, and missing prop names be overriden by the child like {@link Provider}. 89 | * 90 | * @example 91 | * import Provider, { MergingProvider } from 'preact-context-provider'; 92 | * const Demo = (props, context) => { 93 | * console.log(context); // "{ a: 'b' }" 94 | * }; 95 | * 96 | * // with mergeProps unspecified, all parent context keys are merged with the ones presently supplied, parent values taking precedence 97 | * render( 98 | * 99 | * 100 | * 101 | * 102 | * 103 | * ); 104 | * // "{ a: { key1: 'foo', key2: 'bar' } }" 105 | * 106 | * // when mergeProps is an array, only specified keys are merged, non-specified keys get their value from current node 107 | * // in this example, only the 'a' context key is merged. 'b' is overwritten by the lower node 108 | * render( 109 | * 110 | * 111 | * 112 | * 113 | * 114 | * ); 115 | * // "{ a: { key1: 'foo', key3: 'baz' }, b: {key4: 'buz'} }" 116 | */ 117 | export class MergingProvider { 118 | getChildContext() { 119 | let context = {}, 120 | props=this.props, 121 | mergeProps = props.mergeProps, 122 | mergeIsArray=Array.isArray(mergeProps); 123 | 124 | for (let i in props) if (i !== 'children' && i !== 'mergeProps') { 125 | context[i] = (!mergeIsArray || ~mergeProps.indexOf(i)) ? deepAssign(this.context[i], props[i]) : props[i]; 126 | } 127 | return context; 128 | } 129 | 130 | render(props) { 131 | return props.children[0]; 132 | } 133 | } 134 | 135 | /** 136 | * Higher Order Component that wraps components in a {@link Provider} for the given context. 137 | * 138 | * @name provide 139 | * @param {Object} ctx Properties to pass into context (passed to {@link Provider}) 140 | * @returns {Function} A function that, given a Child component, wraps it in a Provider component for the given context. 141 | * 142 | * @example 143 | * import {provide} from 'preact-context-provider'; 144 | * const Demo = (props, context) => { 145 | * console.log(context.a); // "b" 146 | * }; 147 | * const ProvidedDemo = provide({a: "b"})(Demo); 148 | * 149 | * ProvidedDemo.getWrappedComponent() === Demo; // true 150 | * 151 | * render( ); 152 | */ 153 | export const provide = ctx => Child => { 154 | const ProviderWrapper = props => h(Provider, ctx, h(Child, props)); 155 | ProviderWrapper.getWrappedComponent = Child && Child.getWrappedComponent || (() => Child); 156 | return ProviderWrapper; 157 | }; 158 | 159 | Provider.provide = provide; 160 | 161 | /** 162 | * Higher Order Component that wraps components in a {@link MergingProvider} for the given context. 163 | * 164 | * @name mergingProvide 165 | * @param {Object} ctx Properties to pass into context (passed to {@link MergingProvider}) 166 | * @returns {Function} A function that, given a Child component, wraps it in a {@link MergingProvider} component for the given context. 167 | * 168 | * @example 169 | * import {mergingProvide} from 'preact-context-provider'; 170 | * const Demo = (props, context) => { 171 | * console.log(context.a); 172 | * }; 173 | * const ProvidedDemo = mergingProvide({a: "b"})(Demo); 174 | * 175 | * ProvidedDemo.getWrappedComponent() === Demo; // true 176 | * 177 | * render( ); // "b" 178 | */ 179 | export const mergingProvide = ctx => Child => { 180 | const MergingProviderWrapper = props => h(MergingProvider, ctx, h(Child, props)); 181 | MergingProviderWrapper.getWrappedComponent = Child && Child.getWrappedComponent || (() => Child); 182 | return MergingProviderWrapper; 183 | }; 184 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | import { h, render } from 'preact'; 2 | import 'preact-jsx-chai'; 3 | import Provider, { MergingProvider, provide, mergingProvide } from 'preact-context-provider'; 4 | 5 | describe('preact-context-provider', () => { 6 | let scratch = document.createElement('div'), 7 | mount = jsx => root = render(jsx, scratch, root), 8 | context = { a: { name: 'a' }, b: 'b' }, 9 | Spy, 10 | root; 11 | 12 | beforeEach( () => { 13 | mount(h(() => null)); 14 | Spy = sinon.spy(); 15 | scratch.innerHTML = ''; 16 | }); 17 | 18 | describe('', () => { 19 | it('should be a function', () => { 20 | expect(Provider).to.be.a('function'); 21 | }); 22 | 23 | it('should expose props into context', () => { 24 | mount(); 25 | expect(Spy).to.have.been.calledOnce.and.calledWith({ children: [] }, context); 26 | }); 27 | 28 | it('should overwrite higher context keys', () => { 29 | mount( 30 | 31 | 32 | 33 | 34 | ); 35 | expect(Spy).to.have.been.calledOnce.and.calledWith({ children: [] }, { a: 'overwrittenA', b: 'b' }); 36 | 37 | }); 38 | 39 | }); 40 | 41 | describe('', () => { 42 | it('should deep merge with higher context keys, giving them precendence, when mergeProps is unset', () => { 43 | mount( 44 | 45 | 46 | 47 | 48 | ); 49 | expect(Spy).to.have.been.calledOnce.and.calledWith({ children: [] }, 50 | { a: { name: 'a', newProp: 'c' }, b: 'b' }); 51 | }); 52 | 53 | it('should deep merge with higher context keys, giving them precendence, when mergeProps is true', () => { 54 | mount( 55 | 56 | 57 | 58 | 59 | ); 60 | expect(Spy).to.have.been.calledOnce.and.calledWith({ children: [] }, 61 | { a: { name: 'a', newProp: 'c' }, b: 'b' }); 62 | }); 63 | 64 | it('should deep merge with selected higher context keys, giving them precendence, when mergeProps is an array', () => { 65 | mount( 66 | 67 | 68 | 69 | 70 | ); 71 | expect(Spy).to.have.been.calledOnce.and.calledWith({ children: [] }, 72 | { a: { name: 'a', newProp: 'c' }, b: { newProp: 'd' } }); 73 | }); 74 | 75 | it('should allow parent to prevent child value from merging by using null value for a key', () => { 76 | mount( 77 | 78 | 79 | 80 | 81 | ); 82 | expect(Spy).to.have.been.calledOnce.and.calledWith({ children: [] }, 83 | { a: null, b: { name: 'b', newProp: 'd' } }); 84 | }); 85 | }); 86 | 87 | describe('provide()', () => { 88 | it('should be a function', () => { 89 | expect(provide).to.be.a('function'); 90 | }); 91 | 92 | it('should wrap a child with a tag with the supplied context, and pass through any props to the child after wrapped', () => { 93 | let ProvidedSpy = provide(context)(Spy); 94 | expect().to.equal(); 95 | }); 96 | 97 | describe('getWrappedComponent()', () => { 98 | 99 | it('should be a function', () => { 100 | let Wrapped = provide(context)(Spy); 101 | expect(Wrapped.getWrappedComponent).to.be.a('function'); 102 | }); 103 | 104 | it('should return the Child component that it is wrapping', () => { 105 | let Wrapped = provide(context)(Spy); 106 | expect(Wrapped.getWrappedComponent()).to.equal(Spy); 107 | }); 108 | 109 | it('should recursively call getWrappedComponent() on Child components to return the first non-decorator Child', () => { 110 | let Wrapped = provide(context)(provide(context)(Spy)); 111 | expect(Wrapped.getWrappedComponent()).to.equal(Spy); 112 | }); 113 | 114 | }); 115 | 116 | }); 117 | 118 | describe('mergingProvide()', () => { 119 | it('should be a function', () => { 120 | expect(mergingProvide).to.be.a('function'); 121 | }); 122 | 123 | it('should wrap a child with a tag with the supplied context, and pass through any props to the child after wrapped', () => { 124 | let MergingProvidedSpy = mergingProvide({ ...context, mergeProps: true })(Spy); 125 | expect().to.equal(); 126 | }); 127 | 128 | describe('getWrappedComponent()', () => { 129 | 130 | it('should be a function', () => { 131 | let MergingProvidedSpy = mergingProvide({ ...context, mergeProps: true })(Spy); 132 | expect(MergingProvidedSpy.getWrappedComponent).to.be.a('function'); 133 | }); 134 | 135 | it('should return the Child component that it is wrapping', () => { 136 | let MergingProvidedSpy = mergingProvide({ ...context, mergeProps: true })(Spy); 137 | expect(MergingProvidedSpy.getWrappedComponent()).to.equal(Spy); 138 | }); 139 | 140 | it('should recursively call getWrappedComponent() on Child components to return the first non-decorator Child', () => { 141 | let MergingProvidedSpy = mergingProvide({ ...context, mergeProps: true })(mergingProvide({ ...context, mergeProps: true })(Spy)); 142 | expect(MergingProvidedSpy.getWrappedComponent()).to.equal(Spy); 143 | }); 144 | 145 | }); 146 | 147 | }); 148 | }); 149 | --------------------------------------------------------------------------------