From 596b3f19a94adab4a56861dba412328c94e96bf3 Mon Sep 17 00:00:00 2001 From: Johan Date: Sat, 6 Oct 2018 13:28:05 +0200 Subject: [PATCH 01/37] Updated usage section in the README with more examples and comments. --- README.md | 79 +++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 62 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 1ddf7f5..bac1012 100644 --- a/README.md +++ b/README.md @@ -69,53 +69,98 @@ You can find the library on `window.unistore`. import createStore from 'unistore' import { Provider, connect } from 'unistore/preact' -let store = createStore({ count: 0 }) +let store = createStore({ count: 0, stuff: [] }) -// If actions is a function, it gets passed the store: -let actions = store => ({ +let actions = { // Actions can just return a state update: increment(state) { + // The returned object will be merged into the current state return { count: state.count+1 } }, // The above example as an Arrow Function: increment2: ({ count }) => ({ count: count+1 }), - //Actions receive current state as first parameter and any other params next - //check this function as + ) ) -export default () => ( +// First argument to connect can also be a string, array or function while +// second argument can be an object or a function. Here we pass an array and +// a function. +const App2 = connect(['count', 'stuff'], actionFunctions)( + ({ count, stuff, getStuff, clearOutStuff, incrementAfterStuff }) => ( +
+

Count: {count}

+

Stuff: +

+

+ + + +
+ ) +) + +export const getApp1 = () => ( - + ) + +export const getApp2 = () => ( + + + +) + ``` ### Debug From b47ab9dbafdbe9ddb6668b46e259994ba6654933 Mon Sep 17 00:00:00 2001 From: Jason Miller Date: Fri, 4 Jan 2019 17:48:40 -0500 Subject: [PATCH 02/37] 3.2.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5260a7b..9a11e02 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "unistore", - "version": "3.1.1", + "version": "3.2.0", "description": "Dead simple centralized state container (store) with preact and react bindings.", "source": "src/index.js", "module": "dist/unistore.es.js", From 6e26e4b5905b1757925cb587333867738178e3f7 Mon Sep 17 00:00:00 2001 From: Jason Miller Date: Fri, 4 Jan 2019 18:01:48 -0500 Subject: [PATCH 03/37] [ts] Allow any arguments to BoundAction --- index.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.d.ts b/index.d.ts index 7402c2a..e3b8a12 100644 --- a/index.d.ts +++ b/index.d.ts @@ -6,7 +6,7 @@ export type Listener = (state: K, action?: Action) => void; export type Unsubscribe = () => void; export type Action = (state: K, ...args: any[]) => void; -export type BoundAction = () => void; +export type BoundAction = (...args: any[]) => void; export interface Store { action(action: Action): BoundAction; From 374b43713d7816e3d1049b68a6a716510bd3f94f Mon Sep 17 00:00:00 2001 From: Jason Miller Date: Fri, 4 Jan 2019 18:04:12 -0500 Subject: [PATCH 04/37] 3.2.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9a11e02..84d1fc5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "unistore", - "version": "3.2.0", + "version": "3.2.1", "description": "Dead simple centralized state container (store) with preact and react bindings.", "source": "src/index.js", "module": "dist/unistore.es.js", From 04620c605926467ebafb4bb8749939cce8a691a2 Mon Sep 17 00:00:00 2001 From: Jason Miller Date: Fri, 4 Jan 2019 18:04:44 -0500 Subject: [PATCH 05/37] fix missing devtools.d.ts --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 84d1fc5..f40559c 100644 --- a/package.json +++ b/package.json @@ -73,7 +73,8 @@ "index.d.ts", "preact.d.ts", "react.d.ts", - "devtools.js" + "devtools.js", + "devtools.d.ts" ], "keywords": [ "preact", From 23dd9af9ac64f5dc9407a3b1530df3345c9ab0fb Mon Sep 17 00:00:00 2001 From: Nathan Graule Date: Thu, 10 Jan 2019 23:19:10 +0100 Subject: [PATCH 06/37] Update index.d.ts --- index.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.d.ts b/index.d.ts index e3b8a12..02bddf5 100644 --- a/index.d.ts +++ b/index.d.ts @@ -18,7 +18,7 @@ export interface Store { export default function createStore(state?: K): Store; -export type ActionFn = (state: K) => object; +export type ActionFn = (state: K, ...args: any[]) => Partial | void; export interface ActionMap { [actionName: string]: ActionFn; From ac9244f0057c6046565ecd71c8e8f4f9312230c0 Mon Sep 17 00:00:00 2001 From: Nathan Graule Date: Thu, 10 Jan 2019 23:23:26 +0100 Subject: [PATCH 07/37] Also allow async functions as actions --- index.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.d.ts b/index.d.ts index 02bddf5..98f9353 100644 --- a/index.d.ts +++ b/index.d.ts @@ -18,7 +18,7 @@ export interface Store { export default function createStore(state?: K): Store; -export type ActionFn = (state: K, ...args: any[]) => Partial | void; +export type ActionFn = (state: K, ...args: any[]) => Promise> | Partial | void; export interface ActionMap { [actionName: string]: ActionFn; From 0e587ac7e6abca2dc019b657df0f6d27eaa9b5ee Mon Sep 17 00:00:00 2001 From: Jason Miller Date: Thu, 28 Feb 2019 09:11:26 -0800 Subject: [PATCH 08/37] Make react and preact peerDependencies both optional to avoid npm warnings --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index f40559c..2a4b133 100644 --- a/package.json +++ b/package.json @@ -106,7 +106,7 @@ "strip-json-comments-cli": "^1.0.1" }, "peerDependencies": { - "preact": "*", - "react": "*" + "preact": "optional:*", + "react": "optional:*" } } From 3b5affa6f4b9bff39ad19b30c22e88e42cf0328b Mon Sep 17 00:00:00 2001 From: Jason Miller Date: Thu, 7 Mar 2019 14:46:07 -0500 Subject: [PATCH 09/37] 3.3.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2a4b133..409914a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "unistore", - "version": "3.2.1", + "version": "3.3.0", "description": "Dead simple centralized state container (store) with preact and react bindings.", "source": "src/index.js", "module": "dist/unistore.es.js", From 46f2e1b0499c08ef28b25b6d645cef2d404baaf8 Mon Sep 17 00:00:00 2001 From: Jason Miller Date: Thu, 7 Mar 2019 16:56:33 -0500 Subject: [PATCH 10/37] remove peerDependencies --- package.json | 4 ---- 1 file changed, 4 deletions(-) diff --git a/package.json b/package.json index 409914a..1095e00 100644 --- a/package.json +++ b/package.json @@ -104,9 +104,5 @@ "react": "^16.2.0", "react-dom": "^16.2.0", "strip-json-comments-cli": "^1.0.1" - }, - "peerDependencies": { - "preact": "optional:*", - "react": "optional:*" } } From ade48dd1ca9cbf396a1fb78bd2d0d75263eca1f0 Mon Sep 17 00:00:00 2001 From: Joe Haddad Date: Thu, 7 Mar 2019 17:45:51 -0500 Subject: [PATCH 11/37] Use newer optional peer dependencies RFC The behavior was changed before the final implementation was made. Sorry I didn't catch this in my original issue. See https://github.com/yarnpkg/rfcs/blob/master/accepted/0000-optional-peer-dependencies.md#2-detailed-design for usage. --- package.json | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 409914a..f6a0011 100644 --- a/package.json +++ b/package.json @@ -106,7 +106,15 @@ "strip-json-comments-cli": "^1.0.1" }, "peerDependencies": { - "preact": "optional:*", - "react": "optional:*" + "preact": "*", + "react": "*" + }, + "peerDependenciesMeta": { + "preact": { + "optional": true + }, + "react": { + "optional": true + } } } From ccbfc47048ed5e6de1076ba4f5de93fcb019a5c6 Mon Sep 17 00:00:00 2001 From: Jason Miller Date: Thu, 7 Mar 2019 17:46:56 -0500 Subject: [PATCH 12/37] update microbundle to get --external option --- package.json | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/package.json b/package.json index 1095e00..fd32066 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "unistore", - "version": "3.3.0", + "version": "3.3.1", "description": "Dead simple centralized state container (store) with preact and react bindings.", "source": "src/index.js", "module": "dist/unistore.es.js", @@ -10,8 +10,8 @@ "scripts": { "build": "npm-run-all --silent -p build:main build:integrations build:combined -s size docs", "build:main": "microbundle", - "build:integrations": "microbundle src/integrations/*.js -o x.js -f cjs", - "build:combined": "microbundle src/combined/*.js -o full/x.js", + "build:integrations": "microbundle src/integrations/*.js -o x.js -f cjs --external react,preact", + "build:combined": "microbundle src/combined/*.js -o full/x.js --external react,preact", "size": "strip-json-comments --no-whitespace dist/unistore.js | gzip-size && bundlesize", "docs": "documentation readme src/index.js src/integrations/preact.js -q --section API && npm run -s fixreadme", "fixreadme": "node -e 'var fs=require(\"fs\");fs.writeFileSync(\"README.md\", fs.readFileSync(\"README.md\", \"utf8\").replace(/^- /gm, \"- \"))'", @@ -32,7 +32,7 @@ }, { "path": "dist/unistore.js", - "maxSize": "400b" + "maxSize": "350b" }, { "path": "preact.js", @@ -89,20 +89,20 @@ "babel-jest": "^21.2.0", "babel-plugin-transform-react-jsx": "^6.24.1", "babel-preset-env": "^1.6.1", - "bundlesize": "^0.15.3", + "bundlesize": "^0.17.1", "documentation": "^4.0.0", - "enzyme": "^3.2.0", - "enzyme-adapter-react-16": "^1.1.1", + "enzyme": "^3.9.0", + "enzyme-adapter-react-16": "^1.10.0", "eslint": "^4.16.0", "eslint-config-developit": "^1.1.1", "gzip-size-cli": "^2.1.0", "jest": "^23.6.0", - "microbundle": "^0.4.3", + "microbundle": "^0.11.0", "npm-run-all": "^4.1.2", - "preact": "^8.3.1", + "preact": "^8.4.2", "raf": "^3.4.0", - "react": "^16.2.0", - "react-dom": "^16.2.0", + "react": "^16.8.4", + "react-dom": "^16.8.4", "strip-json-comments-cli": "^1.0.1" } } From d38aa3dcec87d301ad4fa4fec636d99bd1876ffe Mon Sep 17 00:00:00 2001 From: Jason Miller Date: Thu, 7 Mar 2019 17:51:59 -0500 Subject: [PATCH 13/37] account for sourcemap size --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index fd32066..ff17401 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,7 @@ }, { "path": "dist/unistore.js", - "maxSize": "350b" + "maxSize": "400b" }, { "path": "preact.js", From 2af6cd366ec02d6d1cdb25892a4ec8c14bb3ef6f Mon Sep 17 00:00:00 2001 From: Jason Miller Date: Thu, 7 Mar 2019 18:02:41 -0500 Subject: [PATCH 14/37] 3.3.2 --- .travis.yml | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 98d32a7..5ae4894 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,3 @@ language: node_js node_js: - - stable \ No newline at end of file + - 10 diff --git a/package.json b/package.json index c665e06..98d6819 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "unistore", - "version": "3.3.1", + "version": "3.3.2", "description": "Dead simple centralized state container (store) with preact and react bindings.", "source": "src/index.js", "module": "dist/unistore.es.js", From 2128016b230acf201966f343c51fb56e2f27acd0 Mon Sep 17 00:00:00 2001 From: Jason Miller Date: Thu, 7 Mar 2019 18:24:20 -0500 Subject: [PATCH 15/37] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1ddf7f5..f4d4fd4 100644 --- a/README.md +++ b/README.md @@ -6,9 +6,9 @@ # unistore -> A tiny ~650b centralized state container with component bindings for [Preact] & [React]. +> A tiny 350b centralized state container with component bindings for [Preact] & [React]. -- **Small** footprint complements Preact nicely +- **Small** footprint complements Preact nicely _(unistore + unistore/preact is ~650b)_ - **Familiar** names and ideas from Redux-like libraries - **Useful** data selectors to extract properties from state - **Portable** actions can be moved into a common place and imported From 13282c1aafeac6e107689af6afc82d0b0eaf558a Mon Sep 17 00:00:00 2001 From: 38elements Date: Fri, 8 Mar 2019 23:48:41 +0900 Subject: [PATCH 16/37] Change Provider for Preact X --- src/integrations/preact.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/integrations/preact.js b/src/integrations/preact.js index 9300c4a..50f8010 100644 --- a/src/integrations/preact.js +++ b/src/integrations/preact.js @@ -1,4 +1,4 @@ -import { h, Component } from 'preact'; +import { h, Component, toChildArray } from 'preact'; import { assign, mapActions, select } from '../util'; /** @@ -60,4 +60,4 @@ export function connect(mapStateToProps, actions) { export function Provider(props) { this.getChildContext = () => ({ store: props.store }); } -Provider.prototype.render = props => props.children[0]; +Provider.prototype.render = props => toChildArray(props.children)[0]; From 606203e6e117d94f5ec0701c31e6f17eaa53c555 Mon Sep 17 00:00:00 2001 From: 38elements Date: Sat, 9 Mar 2019 08:10:00 +0900 Subject: [PATCH 17/37] for setState() --- package.json | 4 ++-- src/integrations/preact.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 98d6819..2179d72 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "bundlesize": [ { "path": "full/preact.js", - "maxSize": "750b" + "maxSize": "765b" }, { "path": "dist/unistore.js", @@ -99,7 +99,7 @@ "jest": "^23.6.0", "microbundle": "^0.11.0", "npm-run-all": "^4.1.2", - "preact": "^8.4.2", + "preact": "^10.0.0-alpha.1", "raf": "^3.4.0", "react": "^16.8.4", "react-dom": "^16.8.4", diff --git a/src/integrations/preact.js b/src/integrations/preact.js index 50f8010..4d2ac10 100644 --- a/src/integrations/preact.js +++ b/src/integrations/preact.js @@ -27,11 +27,11 @@ export function connect(mapStateToProps, actions) { let mapped = mapStateToProps(store ? store.getState() : {}, this.props); for (let i in mapped) if (mapped[i]!==state[i]) { state = mapped; - return this.setState(); + return this.setState({}); } for (let i in state) if (!(i in mapped)) { state = mapped; - return this.setState(); + return this.setState({}); } }; this.componentWillReceiveProps = update; From bd4c4eb8a48dd6dcdb11c41aac46ae206860605a Mon Sep 17 00:00:00 2001 From: Jason Miller Date: Fri, 8 Mar 2019 19:10:44 -0500 Subject: [PATCH 18/37] update tests and integration for preact x --- package.json | 2 +- src/integrations/preact.js | 24 ++++++++++++++++-------- test/preact/unistore.test.js | 2 +- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index 2179d72..1c7ebcc 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "bundlesize": [ { "path": "full/preact.js", - "maxSize": "765b" + "maxSize": "760b" }, { "path": "dist/unistore.js", diff --git a/src/integrations/preact.js b/src/integrations/preact.js index 4d2ac10..49c013e 100644 --- a/src/integrations/preact.js +++ b/src/integrations/preact.js @@ -1,4 +1,4 @@ -import { h, Component, toChildArray } from 'preact'; +import { h, Component } from 'preact'; import { assign, mapActions, select } from '../util'; /** @@ -16,15 +16,16 @@ import { assign, mapActions, select } from '../util'; * export class Foo { render({ foo, bar }) { } } */ export function connect(mapStateToProps, actions) { - if (typeof mapStateToProps!=='function') { - mapStateToProps = select(mapStateToProps || []); + if (typeof mapStateToProps!='function') { + mapStateToProps = select(mapStateToProps || {}); } return Child => { - function Wrapper(props, { store }) { + function Wrapper(props, context) { + const store = context.store; let state = mapStateToProps(store ? store.getState() : {}, props); - let boundActions = actions ? mapActions(actions, store) : { store }; + const boundActions = actions ? mapActions(actions, store) : { store }; let update = () => { - let mapped = mapStateToProps(store ? store.getState() : {}, this.props); + let mapped = mapStateToProps(store ? store.getState() : {}, props); for (let i in mapped) if (mapped[i]!==state[i]) { state = mapped; return this.setState({}); @@ -34,7 +35,10 @@ export function connect(mapStateToProps, actions) { return this.setState({}); } }; - this.componentWillReceiveProps = update; + this.componentWillReceiveProps = p => { + props = p; + update(); + }; this.componentDidMount = () => { store.subscribe(update); }; @@ -60,4 +64,8 @@ export function connect(mapStateToProps, actions) { export function Provider(props) { this.getChildContext = () => ({ store: props.store }); } -Provider.prototype.render = props => toChildArray(props.children)[0]; +// Provider.prototype.getChildContext = function () { +// return { store: this.props.store }; +// }; +Provider.prototype.render = props => props.children && props.children[0] || props.children; +// Provider.prototype.render = props => Array.isArray(props.children) ? props.children[0] : props.children; diff --git a/test/preact/unistore.test.js b/test/preact/unistore.test.js index d7169f0..96b3fd6 100644 --- a/test/preact/unistore.test.js +++ b/test/preact/unistore.test.js @@ -243,7 +243,7 @@ describe('connect()', () => { ); expect(mapStateToProps).toHaveBeenCalledTimes(1); - expect(mapStateToProps).toHaveBeenCalledWith({}, { children: expect.anything() }); + expect(mapStateToProps).toHaveBeenCalledWith({ }, { a: 'b', children: expect.anything() }); // outer props were changed expect(Child).toHaveBeenCalledWith( { mappings: 2, a: 'b', store, children: expect.anything() }, From 7495843576a1d83cb308f8000c410f427c1c2dde Mon Sep 17 00:00:00 2001 From: Jason Miller Date: Fri, 8 Mar 2019 19:13:06 -0500 Subject: [PATCH 19/37] 3.4.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1c7ebcc..8b1ff68 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "unistore", - "version": "3.3.2", + "version": "3.4.0", "description": "Dead simple centralized state container (store) with preact and react bindings.", "source": "src/index.js", "module": "dist/unistore.es.js", From a84fc5c42b64595ea304dd08d0a717db466b6e43 Mon Sep 17 00:00:00 2001 From: Jason Miller Date: Fri, 8 Mar 2019 19:36:50 -0500 Subject: [PATCH 20/37] Also update react integration to likely fix #122 --- src/integrations/preact.js | 4 ---- src/integrations/react.js | 11 +++++++---- test/react/unistore.test.js | 2 +- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/integrations/preact.js b/src/integrations/preact.js index 49c013e..81c09e3 100644 --- a/src/integrations/preact.js +++ b/src/integrations/preact.js @@ -64,8 +64,4 @@ export function connect(mapStateToProps, actions) { export function Provider(props) { this.getChildContext = () => ({ store: props.store }); } -// Provider.prototype.getChildContext = function () { -// return { store: this.props.store }; -// }; Provider.prototype.render = props => props.children && props.children[0] || props.children; -// Provider.prototype.render = props => Array.isArray(props.children) ? props.children[0] : props.children; diff --git a/src/integrations/react.js b/src/integrations/react.js index 1c35ef2..6b1fd4c 100644 --- a/src/integrations/react.js +++ b/src/integrations/react.js @@ -25,11 +25,11 @@ export function connect(mapStateToProps, actions) { return Child => { function Wrapper(props, context) { Component.call(this, props, context); - let { store } = context; + const store = context.store; let state = mapStateToProps(store ? store.getState() : {}, props); - let boundActions = actions ? mapActions(actions, store) : { store }; + const boundActions = actions ? mapActions(actions, store) : { store }; let update = () => { - let mapped = mapStateToProps(store ? store.getState() : {}, this.props); + let mapped = mapStateToProps(store ? store.getState() : {}, props); for (let i in mapped) if (mapped[i]!==state[i]) { state = mapped; return this.forceUpdate(); @@ -39,7 +39,10 @@ export function connect(mapStateToProps, actions) { return this.forceUpdate(); } }; - this.componentWillReceiveProps = update; + this.componentWillReceiveProps = p => { + props = p; + update(); + }; this.componentDidMount = () => { store.subscribe(update); }; diff --git a/test/react/unistore.test.js b/test/react/unistore.test.js index d8756e6..2c64b23 100644 --- a/test/react/unistore.test.js +++ b/test/react/unistore.test.js @@ -240,7 +240,7 @@ describe('', () => { // await sleep(100); expect(mapStateToProps).toHaveBeenCalledTimes(1); - expect(mapStateToProps).toHaveBeenCalledWith({}, {}); + expect(mapStateToProps).toHaveBeenCalledWith({}, { a: 'b' }); // outer props were changed expect(Child).toHaveBeenCalledWith( { mappings: 2, a: 'b', store }, From b44c1b9f5e3ed00cb142e90fac07f0d15fa3e5ad Mon Sep 17 00:00:00 2001 From: Jason Miller Date: Sat, 9 Mar 2019 22:52:36 -0500 Subject: [PATCH 21/37] Split up core and integration tests, refactor preact tests for X. --- test/preact/preact.test.js | 216 ++++++++++++++ test/preact/unistore.test.js | 270 ------------------ .../react/{unistore.test.js => react.test.js} | 78 +---- test/unistore.test.js | 97 +++++++ 4 files changed, 314 insertions(+), 347 deletions(-) create mode 100644 test/preact/preact.test.js delete mode 100644 test/preact/unistore.test.js rename test/react/{unistore.test.js => react.test.js} (65%) create mode 100644 test/unistore.test.js diff --git a/test/preact/preact.test.js b/test/preact/preact.test.js new file mode 100644 index 0000000..0f4f9d1 --- /dev/null +++ b/test/preact/preact.test.js @@ -0,0 +1,216 @@ +import { h, render } from 'preact'; +import createStore from '../../src'; +import { Provider, connect } from '../../src/integrations/preact'; + +const sleep = ms => new Promise( r => setTimeout(r, ms) ); + +describe('integrations/preact', () => { + describe('', () => { + afterEach(() => { + render(null, document.body); + }); + + it('should provide props into context', () => { + const Child = jest.fn(); + + render( + + + , + document.body + ); + expect(Child).toHaveBeenCalledWith(expect.anything(), { store: 'a' }); + + render(null, document.body); + + let store = { name: 'obj' }; + render( + + + , + document.body + ); + expect(Child).toHaveBeenCalledWith(expect.anything(), { store }); + }); + }); + + describe('connect()', () => { + afterEach(async () => { + render(null, document.body); + await sleep(1); + }); + + it('should pass mapped state as props', () => { + let state = { a: 'b' }; + const store = { subscribe: jest.fn(), unsubscribe: jest.fn(), getState: () => state }; + const Child = jest.fn(); + const ConnectedChild = connect(Object)(Child); + render( + + + , + document.body + ); + expect(Child).toHaveBeenCalledWith( + { a: 'b', store }, + expect.anything() + ); + expect(store.subscribe).toBeCalled(); + }); + + it('should transform string selector', () => { + let state = { a: 'b', b: 'c', c: 'd' }; + const store = { subscribe: jest.fn(), unsubscribe: jest.fn(), getState: () => state }; + const Child = jest.fn(); + const ConnectedChild = connect('a, b')(Child); + render( + + + , + document.body + ); + expect(Child).toHaveBeenCalledWith( + { a: 'b', b: 'c', store }, + expect.anything() + ); + expect(store.subscribe).toBeCalled(); + }); + + it('should subscribe to store on mount', async () => { + const store = { subscribe: jest.fn(), unsubscribe: jest.fn(), getState: () => ({}) }; + jest.spyOn(store, 'subscribe'); + const ConnectedChild = connect(Object)(() => null); + + render( + + + , + document.body + ); + + expect(store.subscribe).toBeCalledWith(expect.any(Function)); + }); + + it('should unsubscribe from store when unmounted', async () => { + const store = createStore(); + jest.spyOn(store, 'unsubscribe'); + const ConnectedChild = connect(Object)(() => null); + render( + + + , + document.body + ); + await sleep(1); + render(null, document.body); + expect(store.unsubscribe).toBeCalled(); + }); + + it('should subscribe to store', async () => { + const store = createStore(); + const Child = jest.fn(); + jest.spyOn(store, 'subscribe'); + jest.spyOn(store, 'unsubscribe'); + const ConnectedChild = connect(Object)(Child); + + render( + + + , + document.body + ); + + expect(store.subscribe).toBeCalledWith(expect.any(Function)); + expect(Child).toHaveBeenCalledWith( + { store }, + expect.anything() + ); + + Child.mockClear(); + + store.setState({ a: 'b' }); + await sleep(1); + expect(Child).toHaveBeenCalledWith( + { a: 'b', store }, + expect.anything() + ); + + render(null, document.body); + expect(store.unsubscribe).toBeCalled(); + + Child.mockClear(); + + store.setState({ c: 'd' }); + await sleep(1); + expect(Child).not.toHaveBeenCalled(); + }); + + it('should run mapStateToProps and update when outer props change', async () => { + let state = {}; + const store = { subscribe: jest.fn(), unsubscribe: () => {}, getState: () => state }; + const Child = jest.fn().mockName('').mockReturnValue(42); + let mappings = 0; + + // Jest mock return values are broken :( + const mapStateToProps = jest.fn((state, props) => ({ + mappings: ++mappings, + ...props + })); + + const ConnectedChild = connect(mapStateToProps)(Child); + const ref = {}; + render( + + + , + document.body + ); + let c = ref.current; + + expect(mapStateToProps).toHaveBeenCalledTimes(1); + expect(mapStateToProps).toHaveBeenCalledWith({}, { }); + // first render calls mapStateToProps + expect(Child).toHaveBeenCalledWith( + { mappings: 1, store }, + expect.anything() + ); + + mapStateToProps.mockClear(); + Child.mockClear(); + + render( + + + , + document.body + ); + + expect(mapStateToProps).toHaveBeenCalledTimes(1); + expect(mapStateToProps).toHaveBeenCalledWith({ }, { a: 'b' }); + // outer props were changed + expect(Child).toHaveBeenCalledWith( + { mappings: 2, a: 'b', store }, + expect.anything() + ); + + mapStateToProps.mockClear(); + Child.mockClear(); + + render( + + + , + document.body + ); + + expect(mapStateToProps).toHaveBeenCalledTimes(1); + expect(mapStateToProps).toHaveBeenCalledWith({ }, { a: 'b' }); + + // re-rendered, but outer props were not changed + expect(Child).toHaveBeenCalledWith( + { mappings: 3, a: 'b', store }, + expect.anything() + ); + }); + }); +}); diff --git a/test/preact/unistore.test.js b/test/preact/unistore.test.js deleted file mode 100644 index 96b3fd6..0000000 --- a/test/preact/unistore.test.js +++ /dev/null @@ -1,270 +0,0 @@ -import { h, render } from 'preact'; -import createStore from '../../src'; -import { Provider, connect } from '../../src/integrations/preact'; - -const sleep = ms => new Promise( r => setTimeout(r, ms) ); - -describe('createStore()', () => { - it('should be instantiable', () => { - let store = createStore(); - expect(store).toMatchObject({ - setState: expect.any(Function), - getState: expect.any(Function), - subscribe: expect.any(Function), - unsubscribe: expect.any(Function) - }); - }); - - it('should update state in-place', () => { - let store = createStore(); - expect(store.getState()).toMatchObject({}); - store.setState({ a: 'b' }); - expect(store.getState()).toMatchObject({ a: 'b' }); - store.setState({ c: 'd' }); - expect(store.getState()).toMatchObject({ a: 'b', c: 'd' }); - store.setState({ a: 'x' }); - expect(store.getState()).toMatchObject({ a: 'x', c: 'd' }); - store.setState({ c: null }); - expect(store.getState()).toMatchObject({ a: 'x', c: null }); - store.setState({ c: undefined }); - expect(store.getState()).toMatchObject({ a: 'x', c: undefined }); - }); - - it('should invoke subscriptions', () => { - let store = createStore(); - - let sub1 = jest.fn(); - let sub2 = jest.fn(); - let action; - - let rval = store.subscribe(sub1); - expect(rval).toBeInstanceOf(Function); - - store.setState({ a: 'b' }); - expect(sub1).toBeCalledWith(store.getState(), action); - - store.subscribe(sub2); - store.setState({ c: 'd' }); - - expect(sub1).toHaveBeenCalledTimes(2); - expect(sub1).toHaveBeenLastCalledWith(store.getState(), action); - expect(sub2).toBeCalledWith(store.getState(), action); - }); - - it('should unsubscribe', () => { - let store = createStore(); - - let sub1 = jest.fn(); - let sub2 = jest.fn(); - let sub3 = jest.fn(); - - store.subscribe(sub1); - store.subscribe(sub2); - let unsub3 = store.subscribe(sub3); - - store.setState({ a: 'b' }); - expect(sub1).toBeCalled(); - expect(sub2).toBeCalled(); - expect(sub3).toBeCalled(); - - sub1.mockClear(); - sub2.mockClear(); - sub3.mockClear(); - - store.unsubscribe(sub2); - - store.setState({ c: 'd' }); - expect(sub1).toBeCalled(); - expect(sub2).not.toBeCalled(); - expect(sub3).toBeCalled(); - - sub1.mockClear(); - sub2.mockClear(); - sub3.mockClear(); - - store.unsubscribe(sub1); - - store.setState({ e: 'f' }); - expect(sub1).not.toBeCalled(); - expect(sub2).not.toBeCalled(); - expect(sub3).toBeCalled(); - - sub3.mockClear(); - - unsub3(); - - store.setState({ g: 'h' }); - expect(sub1).not.toBeCalled(); - expect(sub2).not.toBeCalled(); - expect(sub3).not.toBeCalled(); - }); -}); - -describe('', () => { - it('should provide props into context', () => { - const Child = jest.fn(); - - render( - - - , - document.body - ); - expect(Child).toHaveBeenCalledWith(expect.anything(), { store: 'a' }); - - let store = { name: 'obj' }; - render( - - - , - document.body - ); - expect(Child).toHaveBeenCalledWith(expect.anything(), { store }); - }); -}); - -describe('connect()', () => { - it('should pass mapped state as props', () => { - let state = { a: 'b' }; - const store = { subscribe: jest.fn(), getState: () => state }; - const Child = jest.fn(); - const ConnectedChild = connect(Object)(Child); - render( - - - , - document.body - ); - expect(Child).toHaveBeenCalledWith( - { a: 'b', store, children: expect.anything() }, - expect.anything() - ); - expect(store.subscribe).toBeCalled(); - }); - - it('should transform string selector', () => { - let state = { a: 'b', b: 'c', c: 'd' }; - const store = { subscribe: jest.fn(), getState: () => state }; - const Child = jest.fn(); - const ConnectedChild = connect('a, b')(Child); - render( - - - , - document.body - ); - expect(Child).toHaveBeenCalledWith( - { a: 'b', b: 'c', store, children: expect.anything() }, - expect.anything() - ); - expect(store.subscribe).toBeCalled(); - }); - - it('should subscribe to store', async () => { - const store = createStore(); - const Child = jest.fn(); - jest.spyOn(store, 'subscribe'); - jest.spyOn(store, 'unsubscribe'); - const ConnectedChild = connect(Object)(Child); - - let root = render( - - - , - document.body - ); - - expect(store.subscribe).toBeCalledWith(expect.any(Function)); - expect(Child).toHaveBeenCalledWith( - { store, children: expect.anything() }, - expect.anything() - ); - - Child.mockClear(); - - store.setState({ a: 'b' }); - await sleep(1); - expect(Child).toHaveBeenCalledWith( - { a: 'b', store, children: expect.anything() }, - expect.anything() - ); - - render(null, document.body, root); - expect(store.unsubscribe).toBeCalled(); - - Child.mockClear(); - - store.setState({ c: 'd' }); - await sleep(1); - expect(Child).not.toHaveBeenCalled(); - }); - - it('should run mapStateToProps and update when outer props change', () => { - let state = {}; - const store = { subscribe: jest.fn(), getState: () => state }; - const Child = jest.fn().mockName(''); - let mappings = 0; - - // Jest mock return values are broken :( - const mapStateToProps = jest.fn((state, props) => ({ mappings: ++mappings, ...props })); - - // const mapper = jest.fn(); - // const mapStateToProps = (state, props) => { - // mapper(state, props); - // mappings++; - // return { mappings, ...props }; - // }; - - const ConnectedChild = connect(mapStateToProps)(Child); - let root = render( - - - , - document.body - ); - - expect(mapStateToProps).toHaveBeenCalledTimes(1); - expect(mapStateToProps).toHaveBeenCalledWith({}, { children: expect.anything() }); - // first render calls mapStateToProps - expect(Child).toHaveBeenCalledWith( - { mappings: 1, store, children: expect.anything() }, - expect.anything() - ); - - mapStateToProps.mockClear(); - Child.mockClear(); - - render( - - - , - document.body, - root - ); - - expect(mapStateToProps).toHaveBeenCalledTimes(1); - expect(mapStateToProps).toHaveBeenCalledWith({ }, { a: 'b', children: expect.anything() }); - // outer props were changed - expect(Child).toHaveBeenCalledWith( - { mappings: 2, a: 'b', store, children: expect.anything() }, - expect.anything() - ); - - mapStateToProps.mockClear(); - Child.mockClear(); - - render( - - - , - document.body, - root - ); - - // re-rendered, but outer props were not changed - expect(Child).toHaveBeenCalledWith( - { mappings: 3, a: 'b', store, children: expect.anything() }, - expect.anything() - ); - }); -}); diff --git a/test/react/unistore.test.js b/test/react/react.test.js similarity index 65% rename from test/react/unistore.test.js rename to test/react/react.test.js index 2c64b23..00ca647 100644 --- a/test/react/unistore.test.js +++ b/test/react/react.test.js @@ -12,83 +12,7 @@ configure({ adapter: new Adapter() }); const sleep = ms => new Promise(r => setTimeout(r, ms)); -describe('createStore()', () => { - it('should be instantiable', () => { - let store = createStore(); - expect(store).toMatchObject({ - setState: expect.any(Function), - getState: expect.any(Function), - subscribe: expect.any(Function), - unsubscribe: expect.any(Function) - }); - }); - it('should update state in-place', () => { - let store = createStore(); - expect(store.getState()).toMatchObject({}); - store.setState({ a: 'b' }); - expect(store.getState()).toMatchObject({ a: 'b' }); - store.setState({ c: 'd' }); - expect(store.getState()).toMatchObject({ a: 'b', c: 'd' }); - store.setState({ a: 'x' }); - expect(store.getState()).toMatchObject({ a: 'x', c: 'd' }); - store.setState({ c: null }); - expect(store.getState()).toMatchObject({ a: 'x', c: null }); - store.setState({ c: undefined }); - expect(store.getState()).toMatchObject({ a: 'x', c: undefined }); - }); - it('should invoke subscriptions', () => { - let store = createStore(); - let sub1 = jest.fn(); - let sub2 = jest.fn(); - let rval = store.subscribe(sub1); - expect(rval).toBeInstanceOf(Function); - store.setState({ a: 'b' }); - expect(sub1).toHaveBeenCalledTimes(1); - expect(sub1).toHaveBeenCalledWith(store.getState(), undefined); - store.subscribe(sub2); - store.setState({ c: 'd' }); - expect(sub1).toHaveBeenCalledTimes(2); - expect(sub1).toHaveBeenLastCalledWith(store.getState(), undefined); - expect(sub2).toBeCalledWith(store.getState(), undefined); - }); - it('should unsubscribe', () => { - let store = createStore(); - let sub1 = jest.fn(); - let sub2 = jest.fn(); - let sub3 = jest.fn(); - store.subscribe(sub1); - store.subscribe(sub2); - let unsub3 = store.subscribe(sub3); - store.setState({ a: 'b' }); - expect(sub1).toBeCalled(); - expect(sub2).toBeCalled(); - expect(sub3).toBeCalled(); - sub1.mockClear(); - sub2.mockClear(); - sub3.mockClear(); - store.unsubscribe(sub2); - store.setState({ c: 'd' }); - expect(sub1).toBeCalled(); - expect(sub2).not.toBeCalled(); - expect(sub3).toBeCalled(); - sub1.mockClear(); - sub2.mockClear(); - sub3.mockClear(); - store.unsubscribe(sub1); - store.setState({ e: 'f' }); - expect(sub1).not.toBeCalled(); - expect(sub2).not.toBeCalled(); - expect(sub3).toBeCalled(); - sub3.mockClear(); - unsub3(); - store.setState({ g: 'h' }); - expect(sub1).not.toBeCalled(); - expect(sub2).not.toBeCalled(); - expect(sub3).not.toBeCalled(); - }); -}); - -describe('', () => { +describe('integrations/react', () => { const createChild = (storeKey = 'store') => { class Child extends Component { render() { diff --git a/test/unistore.test.js b/test/unistore.test.js new file mode 100644 index 0000000..7bc3a40 --- /dev/null +++ b/test/unistore.test.js @@ -0,0 +1,97 @@ +import createStore from '../src'; + +describe('createStore()', () => { + it('should be instantiable', () => { + let store = createStore(); + expect(store).toMatchObject({ + setState: expect.any(Function), + getState: expect.any(Function), + subscribe: expect.any(Function), + unsubscribe: expect.any(Function) + }); + }); + + it('should update state in-place', () => { + let store = createStore(); + expect(store.getState()).toMatchObject({}); + store.setState({ a: 'b' }); + expect(store.getState()).toMatchObject({ a: 'b' }); + store.setState({ c: 'd' }); + expect(store.getState()).toMatchObject({ a: 'b', c: 'd' }); + store.setState({ a: 'x' }); + expect(store.getState()).toMatchObject({ a: 'x', c: 'd' }); + store.setState({ c: null }); + expect(store.getState()).toMatchObject({ a: 'x', c: null }); + store.setState({ c: undefined }); + expect(store.getState()).toMatchObject({ a: 'x', c: undefined }); + }); + + it('should invoke subscriptions', () => { + let store = createStore(); + + let sub1 = jest.fn(); + let sub2 = jest.fn(); + let action; + + let rval = store.subscribe(sub1); + expect(rval).toBeInstanceOf(Function); + + store.setState({ a: 'b' }); + expect(sub1).toBeCalledWith(store.getState(), action); + + store.subscribe(sub2); + store.setState({ c: 'd' }); + + expect(sub1).toHaveBeenCalledTimes(2); + expect(sub1).toHaveBeenLastCalledWith(store.getState(), action); + expect(sub2).toBeCalledWith(store.getState(), action); + }); + + it('should unsubscribe', () => { + let store = createStore(); + + let sub1 = jest.fn(); + let sub2 = jest.fn(); + let sub3 = jest.fn(); + + store.subscribe(sub1); + store.subscribe(sub2); + let unsub3 = store.subscribe(sub3); + + store.setState({ a: 'b' }); + expect(sub1).toBeCalled(); + expect(sub2).toBeCalled(); + expect(sub3).toBeCalled(); + + sub1.mockClear(); + sub2.mockClear(); + sub3.mockClear(); + + store.unsubscribe(sub2); + + store.setState({ c: 'd' }); + expect(sub1).toBeCalled(); + expect(sub2).not.toBeCalled(); + expect(sub3).toBeCalled(); + + sub1.mockClear(); + sub2.mockClear(); + sub3.mockClear(); + + store.unsubscribe(sub1); + + store.setState({ e: 'f' }); + expect(sub1).not.toBeCalled(); + expect(sub2).not.toBeCalled(); + expect(sub3).toBeCalled(); + + sub3.mockClear(); + + unsub3(); + + store.setState({ g: 'h' }); + expect(sub1).not.toBeCalled(); + expect(sub2).not.toBeCalled(); + expect(sub3).not.toBeCalled(); + }); +}); From 56fd45f23bd31f0a52d373c97b6e424ba0df70a8 Mon Sep 17 00:00:00 2001 From: Jason Miller Date: Sun, 10 Mar 2019 16:39:09 -0400 Subject: [PATCH 22/37] Add tests for Preact 8! --- package.json | 6 +++--- test/fixtures/preact-8.min.js | 2 ++ test/preact/preact-8.test.js | 13 +++++++++++++ test/preact/preact.test.js | 31 +++++++++++++++---------------- 4 files changed, 33 insertions(+), 19 deletions(-) create mode 100644 test/fixtures/preact-8.min.js create mode 100644 test/preact/preact-8.test.js diff --git a/package.json b/package.json index 8b1ff68..751fe8a 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,7 @@ "env", { "targets": { - "node": true + "node": "current" } } ] @@ -86,7 +86,7 @@ "author": "Jason Miller ", "license": "MIT", "devDependencies": { - "babel-jest": "^21.2.0", + "babel-jest": "^24.3.1", "babel-plugin-transform-react-jsx": "^6.24.1", "babel-preset-env": "^1.6.1", "bundlesize": "^0.17.1", @@ -96,7 +96,7 @@ "eslint": "^4.16.0", "eslint-config-developit": "^1.1.1", "gzip-size-cli": "^2.1.0", - "jest": "^23.6.0", + "jest": "^24.3.1", "microbundle": "^0.11.0", "npm-run-all": "^4.1.2", "preact": "^10.0.0-alpha.1", diff --git a/test/fixtures/preact-8.min.js b/test/fixtures/preact-8.min.js new file mode 100644 index 0000000..1b32b1b --- /dev/null +++ b/test/fixtures/preact-8.min.js @@ -0,0 +1,2 @@ +/* eslint-disable */ +!function(){"use strict";function e(e,t){var n,o,r,i,l=W;for(i=arguments.length;i-- >2;)P.push(arguments[i]);t&&null!=t.children&&(P.length||P.push(t.children),delete t.children);while(P.length)if((o=P.pop())&&void 0!==o.pop)for(i=o.length;i--;)P.push(o[i]);else"boolean"==typeof o&&(o=null),(r="function"!=typeof e)&&(null==o?o="":"number"==typeof o?o+="":"string"!=typeof o&&(r=!1)),r&&n?l[l.length-1]+=o:l===W?l=[o]:l.push(o),n=r;var a=new T;return a.nodeName=e,a.children=l,a.attributes=null==t?void 0:t,a.key=null==t?void 0:t.key,void 0!==M.vnode&&M.vnode(a),a}function t(e,t){for(var n in t)e[n]=t[n];return e}function n(e,t){null!=e&&("function"==typeof e?e(t):e.current=t)}function o(n,o){return e(n.nodeName,t(t({},n.attributes),o),arguments.length>2?[].slice.call(arguments,2):n.children)}function r(e){!e.__d&&(e.__d=!0)&&1==V.push(e)&&(M.debounceRendering||D)(i)}function i(){var e;while(e=V.pop())e.__d&&x(e)}function l(e,t,n){return"string"==typeof t||"number"==typeof t?void 0!==e.splitText:"string"==typeof t.nodeName?!e._componentConstructor&&a(e,t.nodeName):n||e._componentConstructor===t.nodeName}function a(e,t){return e.__n===t||e.nodeName.toLowerCase()===t.toLowerCase()}function u(e){var n=t({},e.attributes);n.children=e.children;var o=e.nodeName.defaultProps;if(void 0!==o)for(var r in o)void 0===n[r]&&(n[r]=o[r]);return n}function c(e,t){var n=t?document.createElementNS("http://www.w3.org/2000/svg",e):document.createElement(e);return n.__n=e,n}function p(e){var t=e.parentNode;t&&t.removeChild(e)}function s(e,t,o,r,i){if("className"===t&&(t="class"),"key"===t);else if("ref"===t)n(o,null),n(r,e);else if("class"!==t||i)if("style"===t){if(r&&"string"!=typeof r&&"string"!=typeof o||(e.style.cssText=r||""),r&&"object"==typeof r){if("string"!=typeof o)for(var l in o)l in r||(e.style[l]="");for(var l in r)e.style[l]="number"==typeof r[l]&&!1===E.test(l)?r[l]+"px":r[l]}}else if("dangerouslySetInnerHTML"===t)r&&(e.innerHTML=r.__html||"");else if("o"==t[0]&&"n"==t[1]){var a=t!==(t=t.replace(/Capture$/,""));t=t.toLowerCase().substring(2),r?o||e.addEventListener(t,_,a):e.removeEventListener(t,_,a),(e.__l||(e.__l={}))[t]=r}else if("list"!==t&&"type"!==t&&!i&&t in e){try{e[t]=null==r?"":r}catch(e){}null!=r&&!1!==r||"spellcheck"==t||e.removeAttribute(t)}else{var u=i&&t!==(t=t.replace(/^xlink:?/,""));null==r||!1===r?u?e.removeAttributeNS("http://www.w3.org/1999/xlink",t.toLowerCase()):e.removeAttribute(t):"function"!=typeof r&&(u?e.setAttributeNS("http://www.w3.org/1999/xlink",t.toLowerCase(),r):e.setAttribute(t,r))}else e.className=r||""}function _(e){return this.__l[e.type](M.event&&M.event(e)||e)}function f(){var e;while(e=A.shift())M.afterMount&&M.afterMount(e),e.componentDidMount&&e.componentDidMount()}function d(e,t,n,o,r,i){H++||(R=null!=r&&void 0!==r.ownerSVGElement,B=null!=e&&!("__preactattr_"in e));var l=h(e,t,n,o,i);return r&&l.parentNode!==r&&r.appendChild(l),--H||(B=!1,i||f()),l}function h(e,t,n,o,r){var i=e,l=R;if(null!=t&&"boolean"!=typeof t||(t=""),"string"==typeof t||"number"==typeof t)return e&&void 0!==e.splitText&&e.parentNode&&(!e._component||r)?e.nodeValue!=t&&(e.nodeValue=t):(i=document.createTextNode(t),e&&(e.parentNode&&e.parentNode.replaceChild(i,e),v(e,!0))),i.__preactattr_=!0,i;var u=t.nodeName;if("function"==typeof u)return N(e,t,n,o);if(R="svg"===u||"foreignObject"!==u&&R,u+="",(!e||!a(e,u))&&(i=c(u,R),e)){while(e.firstChild)i.appendChild(e.firstChild);e.parentNode&&e.parentNode.replaceChild(i,e),v(e,!0)}var p=i.firstChild,s=i.__preactattr_,_=t.children;if(null==s){s=i.__preactattr_={};for(var f=i.attributes,d=f.length;d--;)s[f[d].name]=f[d].value}return!B&&_&&1===_.length&&"string"==typeof _[0]&&null!=p&&void 0!==p.splitText&&null==p.nextSibling?p.nodeValue!=_[0]&&(p.nodeValue=_[0]):(_&&_.length||null!=p)&&m(i,_,n,o,B||null!=s.dangerouslySetInnerHTML),y(i,t.attributes,s),R=l,i}function m(e,t,n,o,r){var i,a,u,c,s,_=e.childNodes,f=[],d={},m=0,b=0,y=_.length,g=0,w=t?t.length:0;if(0!==y)for(var C=0;C { + const preact = require('../fixtures/preact-8.min.js'); + jest.setMock('preact', preact); + + // Patch Preact 8's render to work like Preact 10: + let render = preact.render; + preact.render = (vnode, parent) => parent._root = render(vnode, parent, parent._root); + + global.IS_PREACT_8 = true; + require('./preact.test'); + delete global.IS_PREACT_8; +}); diff --git a/test/preact/preact.test.js b/test/preact/preact.test.js index 0f4f9d1..a0c1ce9 100644 --- a/test/preact/preact.test.js +++ b/test/preact/preact.test.js @@ -4,7 +4,9 @@ import { Provider, connect } from '../../src/integrations/preact'; const sleep = ms => new Promise( r => setTimeout(r, ms) ); -describe('integrations/preact', () => { +const NO_CHILDREN = global.IS_PREACT_8 ? expect.anything() : undefined; + +describe(`integrations/preact${global.IS_PREACT_8 ? '-8' : ''}`, () => { describe('', () => { afterEach(() => { render(null, document.body); @@ -35,9 +37,8 @@ describe('integrations/preact', () => { }); describe('connect()', () => { - afterEach(async () => { + afterEach(() => { render(null, document.body); - await sleep(1); }); it('should pass mapped state as props', () => { @@ -52,7 +53,7 @@ describe('integrations/preact', () => { document.body ); expect(Child).toHaveBeenCalledWith( - { a: 'b', store }, + { a: 'b', store, children: NO_CHILDREN }, expect.anything() ); expect(store.subscribe).toBeCalled(); @@ -70,7 +71,7 @@ describe('integrations/preact', () => { document.body ); expect(Child).toHaveBeenCalledWith( - { a: 'b', b: 'c', store }, + { a: 'b', b: 'c', store, children: NO_CHILDREN }, expect.anything() ); expect(store.subscribe).toBeCalled(); @@ -122,7 +123,7 @@ describe('integrations/preact', () => { expect(store.subscribe).toBeCalledWith(expect.any(Function)); expect(Child).toHaveBeenCalledWith( - { store }, + { store, children: NO_CHILDREN }, expect.anything() ); @@ -131,7 +132,7 @@ describe('integrations/preact', () => { store.setState({ a: 'b' }); await sleep(1); expect(Child).toHaveBeenCalledWith( - { a: 'b', store }, + { a: 'b', store, children: NO_CHILDREN }, expect.anything() ); @@ -158,20 +159,18 @@ describe('integrations/preact', () => { })); const ConnectedChild = connect(mapStateToProps)(Child); - const ref = {}; render( - + , document.body ); - let c = ref.current; expect(mapStateToProps).toHaveBeenCalledTimes(1); - expect(mapStateToProps).toHaveBeenCalledWith({}, { }); + expect(mapStateToProps).toHaveBeenCalledWith({}, { children: NO_CHILDREN }); // first render calls mapStateToProps expect(Child).toHaveBeenCalledWith( - { mappings: 1, store }, + { mappings: 1, store, children: NO_CHILDREN }, expect.anything() ); @@ -186,10 +185,10 @@ describe('integrations/preact', () => { ); expect(mapStateToProps).toHaveBeenCalledTimes(1); - expect(mapStateToProps).toHaveBeenCalledWith({ }, { a: 'b' }); + expect(mapStateToProps).toHaveBeenCalledWith({ }, { a: 'b', children: NO_CHILDREN }); // outer props were changed expect(Child).toHaveBeenCalledWith( - { mappings: 2, a: 'b', store }, + { mappings: 2, a: 'b', store, children: NO_CHILDREN }, expect.anything() ); @@ -204,11 +203,11 @@ describe('integrations/preact', () => { ); expect(mapStateToProps).toHaveBeenCalledTimes(1); - expect(mapStateToProps).toHaveBeenCalledWith({ }, { a: 'b' }); + expect(mapStateToProps).toHaveBeenCalledWith({ }, { a: 'b', children: NO_CHILDREN }); // re-rendered, but outer props were not changed expect(Child).toHaveBeenCalledWith( - { mappings: 3, a: 'b', store }, + { mappings: 3, a: 'b', store, children: NO_CHILDREN }, expect.anything() ); }); From c266d1fb93b7c585d1a9110f4da35af0991a624c Mon Sep 17 00:00:00 2001 From: Jason Miller Date: Sun, 10 Mar 2019 16:39:50 -0400 Subject: [PATCH 23/37] 3.4.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 751fe8a..4345921 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "unistore", - "version": "3.4.0", + "version": "3.4.1", "description": "Dead simple centralized state container (store) with preact and react bindings.", "source": "src/index.js", "module": "dist/unistore.es.js", From 4252714a5eb794997728807390d45a9427668cf5 Mon Sep 17 00:00:00 2001 From: Peter <10147685+pmkroeker@users.noreply.github.com> Date: Mon, 3 Jun 2019 08:08:30 -0700 Subject: [PATCH 24/37] Fix JSX now namespaced (preact-X) As of preact X, JSX is now namespaced under preact. Updated the typings to reflect this change. --- preact.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/preact.d.ts b/preact.d.ts index bc8fcf6..46448fa 100644 --- a/preact.d.ts +++ b/preact.d.ts @@ -18,6 +18,6 @@ declare module "unistore/preact" { } export class Provider extends Preact.Component> { - render(props: ProviderProps): JSX.Element; + render(props: ProviderProps): Preact.JSX.Element; } } From 55dae5f414a7d89127f20a6ab847a17513a0c857 Mon Sep 17 00:00:00 2001 From: Eric Liu Date: Sun, 28 Jul 2019 12:08:03 -0700 Subject: [PATCH 25/37] feat(typings): add intersection type for injected props --- preact.d.ts | 2 +- react.d.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/preact.d.ts b/preact.d.ts index bc8fcf6..52f70be 100644 --- a/preact.d.ts +++ b/preact.d.ts @@ -10,7 +10,7 @@ declare module "unistore/preact" { export function connect( mapStateToProps: string | Array | StateMapper, actions?: ActionCreator | object - ): (Child: Preact.ComponentConstructor | Preact.AnyComponent) => Preact.ComponentConstructor; + ): (Child: Preact.ComponentConstructor | Preact.AnyComponent) => Preact.ComponentConstructor; export interface ProviderProps { diff --git a/react.d.ts b/react.d.ts index b935f1a..751c951 100644 --- a/react.d.ts +++ b/react.d.ts @@ -10,7 +10,7 @@ declare module "unistore/react" { export function connect( mapStateToProps: string | Array | StateMapper, actions?: ActionCreator | object - ): (Child: ((props: T & I) => React.ReactNode) | React.ComponentClass) => React.ComponentClass; + ): (Child: ((props: T & I) => React.ReactNode) | React.ComponentClass) => React.ComponentClass; export interface ProviderProps { store: Store; From 9cd95d4a643172c25ecefaef1cab5532bb31c267 Mon Sep 17 00:00:00 2001 From: Eric Liu Date: Fri, 16 Aug 2019 12:57:55 -0700 Subject: [PATCH 26/37] feat(typings): return union of T & I in ComponentClass props --- preact.d.ts | 11 ++++++----- react.d.ts | 12 +++++++----- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/preact.d.ts b/preact.d.ts index 52f70be..27fd3a9 100644 --- a/preact.d.ts +++ b/preact.d.ts @@ -3,15 +3,16 @@ // K - Store state // I - Injected props to wrapped component -declare module "unistore/preact" { - import * as Preact from "preact"; - import { ActionCreator, StateMapper, Store } from "unistore"; +declare module 'unistore/preact' { + import * as Preact from 'preact'; + import { ActionCreator, StateMapper, Store } from 'unistore'; export function connect( mapStateToProps: string | Array | StateMapper, actions?: ActionCreator | object - ): (Child: Preact.ComponentConstructor | Preact.AnyComponent) => Preact.ComponentConstructor; - + ): ( + Child: Preact.ComponentConstructor | Preact.AnyComponent + ) => Preact.ComponentConstructor; export interface ProviderProps { store: Store; diff --git a/react.d.ts b/react.d.ts index 751c951..0eb36d2 100644 --- a/react.d.ts +++ b/react.d.ts @@ -3,14 +3,16 @@ // K - Store state // I - Injected props to wrapped component -declare module "unistore/react" { - import * as React from "react"; - import { ActionCreator, StateMapper, Store } from "unistore"; +declare module 'unistore/react' { + import * as React from 'react'; + import { ActionCreator, StateMapper, Store } from 'unistore'; export function connect( mapStateToProps: string | Array | StateMapper, actions?: ActionCreator | object - ): (Child: ((props: T & I) => React.ReactNode) | React.ComponentClass) => React.ComponentClass; + ): ( + Child: ((props: T & I) => React.ReactNode) | React.ComponentClass + ) => React.ComponentClass; export interface ProviderProps { store: Store; @@ -21,6 +23,6 @@ declare module "unistore/react" { } interface ComponentConstructor

{ - new(props: P, context?: any): React.Component; + new (props: P, context?: any): React.Component; } } From fd29c99178f8ff31cc31dd28eecd000c226d9309 Mon Sep 17 00:00:00 2001 From: Eduardo Borges Date: Sun, 25 Aug 2019 17:59:49 -0300 Subject: [PATCH 27/37] feat: Add connect to React.FC --- react.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/react.d.ts b/react.d.ts index b935f1a..d4c373d 100644 --- a/react.d.ts +++ b/react.d.ts @@ -10,7 +10,7 @@ declare module "unistore/react" { export function connect( mapStateToProps: string | Array | StateMapper, actions?: ActionCreator | object - ): (Child: ((props: T & I) => React.ReactNode) | React.ComponentClass) => React.ComponentClass; + ): (Child: ((props: T & I) => React.ReactNode) | React.ComponentClass | React.FC) => React.ComponentClass | React.FC; export interface ProviderProps { store: Store; From a37980cb29be39e73a3f6db7519c229e81512f77 Mon Sep 17 00:00:00 2001 From: Eduardo Borges Date: Sun, 25 Aug 2019 20:58:18 -0300 Subject: [PATCH 28/37] Remove warnings --- src/integrations/preact.js | 2 +- src/integrations/react.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/integrations/preact.js b/src/integrations/preact.js index 81c09e3..dc6f6cc 100644 --- a/src/integrations/preact.js +++ b/src/integrations/preact.js @@ -35,7 +35,7 @@ export function connect(mapStateToProps, actions) { return this.setState({}); } }; - this.componentWillReceiveProps = p => { + this.UNSAFE_componentWillReceiveProps = p => { props = p; update(); }; diff --git a/src/integrations/react.js b/src/integrations/react.js index 6b1fd4c..672b3db 100644 --- a/src/integrations/react.js +++ b/src/integrations/react.js @@ -39,7 +39,7 @@ export function connect(mapStateToProps, actions) { return this.forceUpdate(); } }; - this.componentWillReceiveProps = p => { + this.UNSAFE_componentWillReceiveProps = p => { props = p; update(); }; From 7ec1c8adf6e6291236afdde3fcc1d0daaaf1305f Mon Sep 17 00:00:00 2001 From: Eduardo Borges Date: Sun, 25 Aug 2019 21:11:54 -0300 Subject: [PATCH 29/37] Undo :P --- src/integrations/preact.js | 2 +- src/integrations/react.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/integrations/preact.js b/src/integrations/preact.js index dc6f6cc..81c09e3 100644 --- a/src/integrations/preact.js +++ b/src/integrations/preact.js @@ -35,7 +35,7 @@ export function connect(mapStateToProps, actions) { return this.setState({}); } }; - this.UNSAFE_componentWillReceiveProps = p => { + this.componentWillReceiveProps = p => { props = p; update(); }; diff --git a/src/integrations/react.js b/src/integrations/react.js index 672b3db..6b1fd4c 100644 --- a/src/integrations/react.js +++ b/src/integrations/react.js @@ -39,7 +39,7 @@ export function connect(mapStateToProps, actions) { return this.forceUpdate(); } }; - this.UNSAFE_componentWillReceiveProps = p => { + this.componentWillReceiveProps = p => { props = p; update(); }; From 911392cd7781f53451d48850a206977bf3159292 Mon Sep 17 00:00:00 2001 From: shoojx Date: Wed, 16 Oct 2019 13:57:01 -0700 Subject: [PATCH 30/37] Update Preact X version to release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4345921..ff0041d 100644 --- a/package.json +++ b/package.json @@ -99,7 +99,7 @@ "jest": "^24.3.1", "microbundle": "^0.11.0", "npm-run-all": "^4.1.2", - "preact": "^10.0.0-alpha.1", + "preact": "^10.0.0", "raf": "^3.4.0", "react": "^16.8.4", "react-dom": "^16.8.4", From ba43d16cf1f66b545182847d74e7be81e1b7c593 Mon Sep 17 00:00:00 2001 From: Jason Miller Date: Thu, 17 Oct 2019 16:08:36 -0400 Subject: [PATCH 31/37] incrementWith -> incrementBy --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index bac1012..4f153f5 100644 --- a/README.md +++ b/README.md @@ -82,8 +82,8 @@ let actions = { increment2: ({ count }) => ({ count: count+1 }), // Actions receive current state as first parameter and any other params next - // See the "Increment with 10"-button below - incrementWith: ({ count }, incrementAmount) => { + // See the "Increment by 10"-button below + incrementBy: ({ count }, incrementAmount) => { return { count: count+incrementAmount } }, } @@ -121,11 +121,11 @@ let actionFunctions = store => ({ // Connecting a react/preact component to get current state and to bind actions const App1 = connect('count', actions)( - ({ count, increment, incrementWith }) => ( + ({ count, increment, incrementBy }) => (

Count: {count}

- +
) ) From 8078bb255cc01f8af25ea9f955e75f813ea22ef7 Mon Sep 17 00:00:00 2001 From: Jason Miller Date: Thu, 17 Oct 2019 16:48:55 -0400 Subject: [PATCH 32/37] 3.5.0 --- README.md | 1 - package.json | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 3cffb74..f307157 100644 --- a/README.md +++ b/README.md @@ -160,7 +160,6 @@ export const getApp2 = () => (
) - ``` ### Debug diff --git a/package.json b/package.json index ff0041d..91d512a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "unistore", - "version": "3.4.1", + "version": "3.5.0", "description": "Dead simple centralized state container (store) with preact and react bindings.", "source": "src/index.js", "module": "dist/unistore.es.js", From 2c6d9ad4676d5bbf8e3740aeff6e80fae33e795a Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 30 Oct 2019 18:14:16 +0100 Subject: [PATCH 33/37] Remove state from `React.FC` type definition. Fixes #177 --- react.d.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/react.d.ts b/react.d.ts index 8e206d0..1c95355 100644 --- a/react.d.ts +++ b/react.d.ts @@ -11,8 +11,8 @@ declare module 'unistore/react' { mapStateToProps: string | Array | StateMapper, actions?: ActionCreator | object ): ( - Child: ((props: T & I) => React.ReactNode) | React.ComponentClass | React.FC - ) => React.ComponentClass | React.FC; + Child: ((props: T & I) => React.ReactNode) | React.ComponentClass | React.FC + ) => React.ComponentClass | React.FC; export interface ProviderProps { store: Store; From b08e0c5660b7141f2c0a92c668c2acb397bc527f Mon Sep 17 00:00:00 2001 From: Jason Miller Date: Tue, 5 Nov 2019 11:23:30 -0500 Subject: [PATCH 34/37] 3.5.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 91d512a..60f688e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "unistore", - "version": "3.5.0", + "version": "3.5.1", "description": "Dead simple centralized state container (store) with preact and react bindings.", "source": "src/index.js", "module": "dist/unistore.es.js", From dd919e5d0b3f50460fb5425ba5152d6f99bd7a20 Mon Sep 17 00:00:00 2001 From: Marvin Hagemeister Date: Sun, 24 May 2020 21:14:55 +0200 Subject: [PATCH 35/37] Add missing async keyword in README example (#189) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f307157..3d82d21 100644 --- a/README.md +++ b/README.md @@ -105,7 +105,7 @@ let actionFunctions = store => ({ // Remember that the state passed to the action function could be stale after // doing async work, so use getState() instead: - incrementAfterStuff(state) { + async incrementAfterStuff(state) { const res = await fetch('foo.json') const resJson = await res.json() // the variable 'state' above could now be old, From eafd7960f7fa7e255600daf698bc6e95003ea342 Mon Sep 17 00:00:00 2001 From: Alexander CherryTea Date: Thu, 18 Jun 2020 02:46:59 +0300 Subject: [PATCH 36/37] fix #171 - remove warnings about deprecated hook on react 16.9.0^ (#172) * fix #171 - remove warnings about deprecated hook on react 16.9.0^ * revert change in preact intecgration * Update src/integrations/react.js Co-Authored-By: Jason Miller * revert: back to componentWillReceiveProps Co-authored-by: Jason Miller --- src/integrations/react.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/integrations/react.js b/src/integrations/react.js index 6b1fd4c..672b3db 100644 --- a/src/integrations/react.js +++ b/src/integrations/react.js @@ -39,7 +39,7 @@ export function connect(mapStateToProps, actions) { return this.forceUpdate(); } }; - this.componentWillReceiveProps = p => { + this.UNSAFE_componentWillReceiveProps = p => { props = p; update(); }; From f3a4e99d4c62b27773fa2e520998e55f129f3819 Mon Sep 17 00:00:00 2001 From: Jason Miller Date: Wed, 17 Jun 2020 19:38:17 -0400 Subject: [PATCH 37/37] 3.5.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 60f688e..67ec7ca 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "unistore", - "version": "3.5.1", + "version": "3.5.2", "description": "Dead simple centralized state container (store) with preact and react bindings.", "source": "src/index.js", "module": "dist/unistore.es.js",