From 5797299e2337985d7c59c4803c65409843a2de83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sampo=20Kivist=C3=B6?= Date: Sat, 14 Jan 2017 12:51:12 +0200 Subject: [PATCH] pass down context in create-class addresses github#705 --- packages/inferno/dist/inferno-compat.js | 4 +- packages/inferno/dist/inferno-compat.min.js | 2 +- packages/inferno/dist/inferno-compat.node.js | 4 +- packages/inferno/dist/inferno-create-class.js | 4 +- .../inferno/dist/inferno-create-class.min.js | 2 +- .../inferno/dist/inferno-create-class.node.js | 4 +- .../__tests__/createClass.spec.browser.ts | 20 +- .../__tests__/createClass.spec.browser.tsx | 185 ++++++++++++++++++ src/component/createClass.ts | 4 +- 9 files changed, 208 insertions(+), 21 deletions(-) create mode 100644 src/component/__tests__/createClass.spec.browser.tsx diff --git a/packages/inferno/dist/inferno-compat.js b/packages/inferno/dist/inferno-compat.js index 79367048c..d72bcb831 100644 --- a/packages/inferno/dist/inferno-compat.js +++ b/packages/inferno/dist/inferno-compat.js @@ -477,8 +477,8 @@ function applyMixins(inst, mixins) { } function createClass(obj) { var Cl = (function (Component$$1) { - function Cl(props) { - Component$$1.call(this, props); + function Cl(props, context) { + Component$$1.call(this, props, context); this.isMounted = function () { return !this._unmounted; }; diff --git a/packages/inferno/dist/inferno-compat.min.js b/packages/inferno/dist/inferno-compat.min.js index 743338398..abcf753e9 100644 --- a/packages/inferno/dist/inferno-compat.min.js +++ b/packages/inferno/dist/inferno-compat.min.js @@ -1 +1 @@ -!function(n,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("inferno-component"),require("inferno")):"function"==typeof define&&define.amd?define(["exports","inferno-component","inferno"],e):e(n.Inferno=n.Inferno||{},n.Inferno.Component,n.Inferno)}(this,function(n,e,t){"use strict";function r(n,e){return e={exports:{}},n(e,e.exports),e.exports}function o(n){return!d(n.prototype)&&!d(n.prototype.render)}function i(n){return d(n)||f(n)}function u(n){return f(n)||n===!1||p(n)||d(n)}function a(n){return"function"==typeof n}function l(n){return"o"===n[0]&&"n"===n[1]&&n.length>3}function c(n){return"string"==typeof n}function f(n){return null===n}function p(n){return n===!0}function d(n){return void 0===n}function s(n){return"object"==typeof n}function y(n){var e=s(n)&&f(n)===!1;if(e===!1)return!1;var t=n.flags;return!!(3998&t)}function v(n,e,t){for(var r in e)t!==!0&&i(e[r])||(n[r]=e[r]);return n}function m(n){for(var e in n){var t=n[e];"function"!=typeof t||t.__bound||j[e]||((n[e]=t.bind(n)).__bound=!0)}}function h(n,e){void 0===e&&(e={});for(var t=0;t0;)r[i]=arguments[i+2];if(u(n)||s(n))throw new Error("Inferno Error: createElement() name parameter cannot be undefined, null, false or true, It must be a string, class or function.");var a=r,f=null,p=null,y=null,v=0;if(r&&(1===r.length?a=r[0]:0===r.length&&(a=void 0)),c(n)){switch(v=2,n){case"svg":v=128;break;case"input":v=512;break;case"textarea":v=1024;break;case"select":v=2048}for(var m in e)"key"===m?(p=e.key,delete e.key):"children"===m&&d(a)?a=e.children:"ref"===m?f=e.ref:l(m)&&(y||(y={}),y[m]=e[m],delete e[m])}else{v=o(n)?4:8,d(a)||(e||(e={}),e.children=a,a=null);for(var h in e)P[h]?(f||(f={}),f[h]=e[h]):"key"===h&&(p=e.key,delete e.key)}return t.createVNode(v,n,e,a,y,p,f)}function k(n){return t.render(null,n),!0}function w(n){return null===n||void 0===n}function C(n,e){if(("input"===n||"textarea"===n)&&e.onChange){var t,r=e.type;t="checkbox"===r?"onclick":"file"===r?"onchange":"oninput",e[t]||(e[t]=e.onChange,delete e.onChange)}for(var o in e)U[o]&&(e[U[o]]=e[o],delete e[o])}function O(n,e){for(var t in n)if(!(t in e))return!0;for(var r in e)if(n[r]!==e[r])return!0;return!1}function N(n,t){e.call(this,n,t)}function A(n,e,r,o){var i=t.createVNode(4,G,{context:n.context,children:e}),u=t.render(i,r);return o&&o.call(u),u}function I(n){return W.bind(null,n)}e="default"in e?e.default:e;var _="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{},V=r(function(n,e){!function(t,r){r(e,n)}(_,function(n,e){function t(n){var e=n&&(k&&n[k]||n[w]);if("function"==typeof e)return e}function r(n){function e(e,t,r,o,i,u){if(o=o||C,u=u||r,null==t[r]){var a=g[i];return e?new Error("Required "+a+" `"+u+"` was not specified in "+("`"+o+"`.")):null}return n(t,r,o,i,u)}var t=e.bind(null,!1);return t.isRequired=e.bind(null,!0),t}function o(n){function e(e,t,r,o,i){var u=e[t],a=v(u);if(a!==n){var l=g[o],c=m(u);return new Error("Invalid "+l+" `"+i+"` of type "+("`"+c+"` supplied to `"+r+"`, expected ")+("`"+n+"`."))}return null}return r(e)}function i(){return r(E.thatReturns(null))}function u(n){function e(e,t,r,o,i){var u=e[t];if(!Array.isArray(u)){var a=g[o],l=v(u);return new Error("Invalid "+a+" `"+i+"` of type "+("`"+l+"` supplied to `"+r+"`, expected an array."))}for(var c=0;c0;)r[o]=arguments[o+2];var i=t||{},u=i.ref;return"string"==typeof u&&(i.ref=function(n){this&&this.refs&&(this.refs[u]=n)}.bind(R||null)),"string"==typeof e&&C(e,i),n.apply(void 0,[e,i].concat(r))}},W=T(E),q=T(t.cloneVNode),F=t.options.createVNode;t.options.createVNode=function(n){var e=n.children,t=n.props;w(n.props)&&(t=n.props={}),!w(e)&&w(t.children)&&(t.children=e),F&&F(n)},N.prototype=new e({},{}),N.prototype.shouldComponentUpdate=function(n,e){return O(this.props,n)||O(this.state,e)};for(var G=function(n){function e(){n.apply(this,arguments)}return n&&(e.__proto__=n),e.prototype=Object.create(n&&n.prototype),e.prototype.constructor=e,e.prototype.getChildContext=function(){return this.props.context},e.prototype.render=function(n){return n.children},e}(e),$="a abbr address area article aside audio b base bdi bdo big blockquote body br button canvas caption cite code col colgroup data datalist dd del details dfn dialog div dl dt em embed fieldset figcaption figure footer form h1 h2 h3 h4 h5 h6 head header hgroup hr html i iframe img input ins kbd keygen label legend li link main map mark menu menuitem meta meter nav noscript object ol optgroup option output p param picture pre progress q rp rt ruby s samp script section select small source span strong style sub summary sup table tbody td textarea tfoot th thead time title tr track u ul var video wbr circle clipPath defs ellipse g image line linearGradient mask path pattern polygon polyline radialGradient rect stop svg text tspan".split(" "),H={},J=$.length;J--;)H[$[J]]=I($[J]);var z={createVNode:t.createVNode,render:t.render,isValidElement:y,createElement:W,Component:e,PureComponent:N,unmountComponentAtNode:k,cloneElement:q,PropTypes:V,createClass:g,findDOMNode:t.findDOMNode,Children:M,cloneVNode:t.cloneVNode,NO_OP:t.NO_OP,version:S,unstable_renderSubtreeIntoContainer:A,createFactory:I,DOM:H};n.createVNode=t.createVNode,n.render=t.render,n.isValidElement=y,n.createElement=W,n.Component=e,n.PureComponent=N,n.unmountComponentAtNode=k,n.cloneElement=q,n.PropTypes=V,n.createClass=g,n.findDOMNode=t.findDOMNode,n.Children=M,n.cloneVNode=t.cloneVNode,n.NO_OP=t.NO_OP,n.version=S,n.unstable_renderSubtreeIntoContainer=A,n.createFactory=I,n.DOM=H,n.default=z,Object.defineProperty(n,"__esModule",{value:!0})}); +!function(n,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("inferno-component"),require("inferno")):"function"==typeof define&&define.amd?define(["exports","inferno-component","inferno"],e):e(n.Inferno=n.Inferno||{},n.Inferno.Component,n.Inferno)}(this,function(n,e,t){"use strict";function r(n,e){return e={exports:{}},n(e,e.exports),e.exports}function o(n){return!d(n.prototype)&&!d(n.prototype.render)}function i(n){return d(n)||f(n)}function u(n){return f(n)||n===!1||p(n)||d(n)}function a(n){return"function"==typeof n}function l(n){return"o"===n[0]&&"n"===n[1]&&n.length>3}function c(n){return"string"==typeof n}function f(n){return null===n}function p(n){return n===!0}function d(n){return void 0===n}function s(n){return"object"==typeof n}function y(n){var e=s(n)&&f(n)===!1;if(e===!1)return!1;var t=n.flags;return!!(3998&t)}function v(n,e,t){for(var r in e)t!==!0&&i(e[r])||(n[r]=e[r]);return n}function m(n){for(var e in n){var t=n[e];"function"!=typeof t||t.__bound||j[e]||((n[e]=t.bind(n)).__bound=!0)}}function h(n,e){void 0===e&&(e={});for(var t=0;t0;)r[i]=arguments[i+2];if(u(n)||s(n))throw new Error("Inferno Error: createElement() name parameter cannot be undefined, null, false or true, It must be a string, class or function.");var a=r,f=null,p=null,y=null,v=0;if(r&&(1===r.length?a=r[0]:0===r.length&&(a=void 0)),c(n)){switch(v=2,n){case"svg":v=128;break;case"input":v=512;break;case"textarea":v=1024;break;case"select":v=2048}for(var m in e)"key"===m?(p=e.key,delete e.key):"children"===m&&d(a)?a=e.children:"ref"===m?f=e.ref:l(m)&&(y||(y={}),y[m]=e[m],delete e[m])}else{v=o(n)?4:8,d(a)||(e||(e={}),e.children=a,a=null);for(var h in e)P[h]?(f||(f={}),f[h]=e[h]):"key"===h&&(p=e.key,delete e.key)}return t.createVNode(v,n,e,a,y,p,f)}function k(n){return t.render(null,n),!0}function w(n){return null===n||void 0===n}function C(n,e){if(("input"===n||"textarea"===n)&&e.onChange){var t,r=e.type;t="checkbox"===r?"onclick":"file"===r?"onchange":"oninput",e[t]||(e[t]=e.onChange,delete e.onChange)}for(var o in e)U[o]&&(e[U[o]]=e[o],delete e[o])}function O(n,e){for(var t in n)if(!(t in e))return!0;for(var r in e)if(n[r]!==e[r])return!0;return!1}function N(n,t){e.call(this,n,t)}function A(n,e,r,o){var i=t.createVNode(4,G,{context:n.context,children:e}),u=t.render(i,r);return o&&o.call(u),u}function I(n){return W.bind(null,n)}e="default"in e?e.default:e;var _="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{},V=r(function(n,e){!function(t,r){r(e,n)}(_,function(n,e){function t(n){var e=n&&(k&&n[k]||n[w]);if("function"==typeof e)return e}function r(n){function e(e,t,r,o,i,u){if(o=o||C,u=u||r,null==t[r]){var a=g[i];return e?new Error("Required "+a+" `"+u+"` was not specified in "+("`"+o+"`.")):null}return n(t,r,o,i,u)}var t=e.bind(null,!1);return t.isRequired=e.bind(null,!0),t}function o(n){function e(e,t,r,o,i){var u=e[t],a=v(u);if(a!==n){var l=g[o],c=m(u);return new Error("Invalid "+l+" `"+i+"` of type "+("`"+c+"` supplied to `"+r+"`, expected ")+("`"+n+"`."))}return null}return r(e)}function i(){return r(E.thatReturns(null))}function u(n){function e(e,t,r,o,i){var u=e[t];if(!Array.isArray(u)){var a=g[o],l=v(u);return new Error("Invalid "+a+" `"+i+"` of type "+("`"+l+"` supplied to `"+r+"`, expected an array."))}for(var c=0;c0;)r[o]=arguments[o+2];var i=t||{},u=i.ref;return"string"==typeof u&&(i.ref=function(n){this&&this.refs&&(this.refs[u]=n)}.bind(R||null)),"string"==typeof e&&C(e,i),n.apply(void 0,[e,i].concat(r))}},W=T(E),q=T(t.cloneVNode),F=t.options.createVNode;t.options.createVNode=function(n){var e=n.children,t=n.props;w(n.props)&&(t=n.props={}),!w(e)&&w(t.children)&&(t.children=e),F&&F(n)},N.prototype=new e({},{}),N.prototype.shouldComponentUpdate=function(n,e){return O(this.props,n)||O(this.state,e)};for(var G=function(n){function e(){n.apply(this,arguments)}return n&&(e.__proto__=n),e.prototype=Object.create(n&&n.prototype),e.prototype.constructor=e,e.prototype.getChildContext=function(){return this.props.context},e.prototype.render=function(n){return n.children},e}(e),$="a abbr address area article aside audio b base bdi bdo big blockquote body br button canvas caption cite code col colgroup data datalist dd del details dfn dialog div dl dt em embed fieldset figcaption figure footer form h1 h2 h3 h4 h5 h6 head header hgroup hr html i iframe img input ins kbd keygen label legend li link main map mark menu menuitem meta meter nav noscript object ol optgroup option output p param picture pre progress q rp rt ruby s samp script section select small source span strong style sub summary sup table tbody td textarea tfoot th thead time title tr track u ul var video wbr circle clipPath defs ellipse g image line linearGradient mask path pattern polygon polyline radialGradient rect stop svg text tspan".split(" "),H={},J=$.length;J--;)H[$[J]]=I($[J]);var z={createVNode:t.createVNode,render:t.render,isValidElement:y,createElement:W,Component:e,PureComponent:N,unmountComponentAtNode:k,cloneElement:q,PropTypes:V,createClass:g,findDOMNode:t.findDOMNode,Children:M,cloneVNode:t.cloneVNode,NO_OP:t.NO_OP,version:S,unstable_renderSubtreeIntoContainer:A,createFactory:I,DOM:H};n.createVNode=t.createVNode,n.render=t.render,n.isValidElement=y,n.createElement=W,n.Component=e,n.PureComponent=N,n.unmountComponentAtNode=k,n.cloneElement=q,n.PropTypes=V,n.createClass=g,n.findDOMNode=t.findDOMNode,n.Children=M,n.cloneVNode=t.cloneVNode,n.NO_OP=t.NO_OP,n.version=S,n.unstable_renderSubtreeIntoContainer=A,n.createFactory=I,n.DOM=H,n.default=z,Object.defineProperty(n,"__esModule",{value:!0})}); diff --git a/packages/inferno/dist/inferno-compat.node.js b/packages/inferno/dist/inferno-compat.node.js index 79367048c..d72bcb831 100644 --- a/packages/inferno/dist/inferno-compat.node.js +++ b/packages/inferno/dist/inferno-compat.node.js @@ -477,8 +477,8 @@ function applyMixins(inst, mixins) { } function createClass(obj) { var Cl = (function (Component$$1) { - function Cl(props) { - Component$$1.call(this, props); + function Cl(props, context) { + Component$$1.call(this, props, context); this.isMounted = function () { return !this._unmounted; }; diff --git a/packages/inferno/dist/inferno-create-class.js b/packages/inferno/dist/inferno-create-class.js index 807ad6354..3f73309ef 100644 --- a/packages/inferno/dist/inferno-create-class.js +++ b/packages/inferno/dist/inferno-create-class.js @@ -118,8 +118,8 @@ function applyMixins(inst, mixins) { } function createClass$1(obj) { var Cl = (function (Component$$1) { - function Cl(props) { - Component$$1.call(this, props); + function Cl(props, context) { + Component$$1.call(this, props, context); this.isMounted = function () { return !this._unmounted; }; diff --git a/packages/inferno/dist/inferno-create-class.min.js b/packages/inferno/dist/inferno-create-class.min.js index 15e33731c..8d11f9d25 100644 --- a/packages/inferno/dist/inferno-create-class.min.js +++ b/packages/inferno/dist/inferno-create-class.min.js @@ -1 +1 @@ -!function(n,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t(require("inferno-component")):"function"==typeof define&&define.amd?define(["inferno-component"],t):(n.Inferno=n.Inferno||{},n.Inferno.createClass=t(n.Inferno.Component))}(this,function(n){"use strict";function t(n){return i(n)||e(n)}function o(n){return"function"==typeof n}function e(n){return null===n}function i(n){return void 0===n}function r(n,o,e){for(var i in o)e!==!0&&t(o[i])||(n[i]=o[i]);return n}function u(n){for(var t in n){var o=n[t];"function"!=typeof o||o.__bound||s[t]||((n[t]=o.bind(n)).__bound=!0)}}function f(n,t){void 0===t&&(t={});for(var o=0;o { let container; @@ -43,13 +43,13 @@ describe('Components createClass (non-JSX)', () => { render(createElement(LifecycleComponent1 as Function, {}), container); expect(componentWillUpdate).to.equal(true); }); - it('should render a basic component with methods bound', done => { + + it('should have context available in getInitialState', done => { let context; let context2; const BoundComponent = createClass({ getInitialState() { - context = this; - setTimeout(this.foo, 1); + expect(this.context) }, foo() { context2 = this; @@ -65,9 +65,11 @@ describe('Components createClass (non-JSX)', () => { done(); }, 2); }); + it('should have propTypes on created class', () => { const propTypes = { - value() {} + value() { + } }; const Component = createClass({ propTypes, diff --git a/src/component/__tests__/createClass.spec.browser.tsx b/src/component/__tests__/createClass.spec.browser.tsx new file mode 100644 index 000000000..4c21546a0 --- /dev/null +++ b/src/component/__tests__/createClass.spec.browser.tsx @@ -0,0 +1,185 @@ +import {expect} from 'chai'; +import {render} from '../../DOM/rendering'; +import {innerHTML} from '../../tools/utils'; +import createClass from '../createClass'; + +describe('Components createClass (non-JSX)', () => { + let container; + + beforeEach(() => { + container = document.createElement('div'); + container.style.display = 'none'; + document.body.appendChild(container); + }); + + afterEach(() => { + document.body.removeChild(container); + render(null, container); + }); + + /* + describe("mixins", () => { + describe("getDefaultProps", () => { + it('should use a mixin', () => { + const Foo = createClass({ + mixins: [ + {getDefaultProps: () => ({a: true})} + ], + render() { + return
; + } + }); + + expect(Foo.defaultProps).to.eql({ + a: true + }); + }); + + it('should combine the results', () => { + const Foo = createClass({ + mixins: [ + {getDefaultProps: () => ({a: true})}, + {getDefaultProps: () => ({b: true})} + ], + getDefaultProps() { + return {c: true}; + }, + render() { + return
; + } + }); + + expect(Foo.defaultProps).to.eql({ + a: true, + b: true, + c: true + }); + }); + + it('should throw an error for duplicate keys', () => { + expect(() => { + createClass({ + mixins: [ + {getDefaultProps: () => ({a: true})} + ], + getDefaultProps() { + return {a: true}; + }, + render() { + return
; + } + }); + }).to.throw(); + }); + }); + + describe("getInitialState", () => { + it('should combine the results', () => { + const Foo = createClass({ + mixins: [ + {getInitialState: () => ({a: true})}, + {getInitialState: () => ({b: true})} + ], + getInitialState() { + return {c: true}; + }, + render() { + return
; + } + }); + + let a; + render(, container); + + expect(a.state).to.eql({ + a: true, + b: true, + c: true + }); + }); + + it('should throw an error for duplicate keys', () => { + const Foo = createClass({ + mixins: [ + {getInitialState: () => ({a: true})} + ], + getInitialState() { + return {a: true}; + }, + render() { + return
; + } + }); + + expect(() => { + render(, container); + }).to.throw(); + }); + }); + }); +*/ + describe('Context', () => { + it('It should have context defined when context moved to children', () => { + const App = createClass({ + getDefaultProps() { + return { + wrapContext: false, + }; + }, + + getChildContext() { + return { + foo: 'bar baz', + }; + }, + + addPageContexts(children) { + const newChildren = []; + + for (let i = 0; i < children.length; i++) { + newChildren.push(); + } + + return newChildren; + }, + + render() { + let children; + + if (this.props.wrapContext) { + children = this.addPageContexts(this.props.children); + } else { + children = this.props.children; + } + + return ( +
+ {children} +
+ ); + }, + }); + + const Page = createClass({ + getInitialState() { + return { + foo: this.context.foo, + }; + }, + render() { + return
{this.props.greeting} {this.state.foo}
; + }, + }); + + render( + ( + + + + + ), container); + + expect(container.innerHTML).to.eql(innerHTML('
Hello bar baz
Hai bar baz
')); + }); + }); +}); diff --git a/src/component/createClass.ts b/src/component/createClass.ts index 6d8972a33..f8fa1e31d 100644 --- a/src/component/createClass.ts +++ b/src/component/createClass.ts @@ -100,8 +100,8 @@ export default function createClass(obj: ComponentSpec) { static defaultProps = obj.getDefaultProps ? obj.getDefaultProps() : undefined; static mixins = obj.mixins && collateMixins(obj.mixins); - constructor(props) { - super(props); + constructor(props, context) { + super(props, context); extend(this, obj); if (Cl.mixins) { applyMixins(this, Cl.mixins);