From d43d920b8adab61a4ffa70cb5d7a953dbb08e88f Mon Sep 17 00:00:00 2001 From: sskoopa Date: Sun, 11 Dec 2016 10:15:12 -0800 Subject: [PATCH 01/16] support nested routes, and a base prop to start from if necessary --- src/index.js | 14 ++++++++++++-- test/index.js | 21 +++++++++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/index.js b/src/index.js index f1c385b0..fb5e1741 100644 --- a/src/index.js +++ b/src/index.js @@ -10,6 +10,7 @@ const EMPTY = {}; // hangs off all elements created by preact const ATTR_KEY = typeof Symbol!=='undefined' ? Symbol.for('preactattr') : '__preactattr_'; +const CONTEXT_KEY = 'preact-router-baseUrl'; function isPreactElement(node) { return ATTR_KEY in node; @@ -137,17 +138,26 @@ const Link = (props) => { class Router extends Component { - constructor(props) { + constructor(props, context) { super(props); + let baseUrl = this.props.base || ''; if (props.history) { customHistory = props.history; } + if (context && context[CONTEXT_KEY]) { + baseUrl = context[CONTEXT_KEY] + baseUrl; + } this.state = { + baseUrl, url: this.props.url || getCurrentUrl() }; } + getChildContext() { + return {CONTEXT_KEY: this.state.baseUrl}; + } + shouldComponentUpdate(props) { if (props.static!==true) return true; return props.url!==this.props.url || props.onChange!==this.props.onChange; @@ -193,7 +203,7 @@ class Router extends Component { getMatchingChildren(children, url, invoke) { return children.slice().sort(pathRankSort).filter( ({ attributes }) => { - let path = attributes.path, + let path = this.state.baseUrl + attributes.path, matches = exec(url, path, attributes); if (matches) { if (invoke!==false) { diff --git a/test/index.js b/test/index.js index ba5a0f34..6f8d7a08 100644 --- a/test/index.js +++ b/test/index.js @@ -73,6 +73,27 @@ describe('preact-router', () => { ).to.equal(children[0]); }); + it('should support nested routes', () => { + let router = new Router({}, {'preact-router-baseUrl': '/foo'}); + let children = [ + , + , + + ]; + + expect( + router.render({ children }, { url:'/foo' }) + ).to.equal(children[1]); + + expect( + router.render({ children }, { url:'/' }) + ).to.equal(children[0]); + + expect( + router.render({ children }, { url:'/asdf/asdf' }) + ).to.equal(children[0]); + }); + it('should support initial route prop', () => { let router = new Router({ url:'/foo' }); let children = [ From c1931cbf4ccee216541063d81d603a26800e0534 Mon Sep 17 00:00:00 2001 From: sskoopa Date: Sun, 11 Dec 2016 18:56:54 -0800 Subject: [PATCH 02/16] update to use props.base instead of state.baseUrl --- src/index.js | 11 +++++------ test/index.js | 2 +- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/index.js b/src/index.js index fb5e1741..bbfdfdb0 100644 --- a/src/index.js +++ b/src/index.js @@ -10,7 +10,7 @@ const EMPTY = {}; // hangs off all elements created by preact const ATTR_KEY = typeof Symbol!=='undefined' ? Symbol.for('preactattr') : '__preactattr_'; -const CONTEXT_KEY = 'preact-router-baseUrl'; +const CONTEXT_KEY = 'preact-router-base'; function isPreactElement(node) { return ATTR_KEY in node; @@ -140,22 +140,21 @@ const Link = (props) => { class Router extends Component { constructor(props, context) { super(props); - let baseUrl = this.props.base || ''; + this.props.base = this.props.base || ''; if (props.history) { customHistory = props.history; } if (context && context[CONTEXT_KEY]) { - baseUrl = context[CONTEXT_KEY] + baseUrl; + this.props.base = context[CONTEXT_KEY] + this.props.base; } this.state = { - baseUrl, url: this.props.url || getCurrentUrl() }; } getChildContext() { - return {CONTEXT_KEY: this.state.baseUrl}; + return {CONTEXT_KEY: this.props.base}; } shouldComponentUpdate(props) { @@ -203,7 +202,7 @@ class Router extends Component { getMatchingChildren(children, url, invoke) { return children.slice().sort(pathRankSort).filter( ({ attributes }) => { - let path = this.state.baseUrl + attributes.path, + let path = this.props.base + attributes.path, matches = exec(url, path, attributes); if (matches) { if (invoke!==false) { diff --git a/test/index.js b/test/index.js index 6f8d7a08..180cb9ec 100644 --- a/test/index.js +++ b/test/index.js @@ -74,7 +74,7 @@ describe('preact-router', () => { }); it('should support nested routes', () => { - let router = new Router({}, {'preact-router-baseUrl': '/foo'}); + let router = new Router({}, {'preact-router-base': '/foo'}); let children = [ , , From 257a761b4b323588ad03369b03cec25a1bc9170a Mon Sep 17 00:00:00 2001 From: Scott Sanders Date: Mon, 12 Dec 2016 11:23:17 -0800 Subject: [PATCH 03/16] move base to this.baseUrl --- src/index.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/index.js b/src/index.js index bbfdfdb0..35633c61 100644 --- a/src/index.js +++ b/src/index.js @@ -140,12 +140,12 @@ const Link = (props) => { class Router extends Component { constructor(props, context) { super(props); - this.props.base = this.props.base || ''; + this.baseUrl = this.props.base || ''; if (props.history) { customHistory = props.history; } if (context && context[CONTEXT_KEY]) { - this.props.base = context[CONTEXT_KEY] + this.props.base; + this.baseUrl = context[CONTEXT_KEY] + this.baseUrl; } this.state = { @@ -154,7 +154,7 @@ class Router extends Component { } getChildContext() { - return {CONTEXT_KEY: this.props.base}; + return {CONTEXT_KEY: this.baseUrl}; } shouldComponentUpdate(props) { @@ -202,7 +202,7 @@ class Router extends Component { getMatchingChildren(children, url, invoke) { return children.slice().sort(pathRankSort).filter( ({ attributes }) => { - let path = this.props.base + attributes.path, + let path = this.baseUrl + attributes.path, matches = exec(url, path, attributes); if (matches) { if (invoke!==false) { From a8fab3a437ff9dfcc28083e49512175347bc3429 Mon Sep 17 00:00:00 2001 From: Scott Sanders Date: Mon, 12 Dec 2016 11:30:35 -0800 Subject: [PATCH 04/16] adding documentation for nested routes --- README.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/README.md b/README.md index d4a5cfbc..274c8f7e 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,24 @@ You can even mix-and-match URL parameters and normal `props`. ``` +### Nesting routers + +Routers will append the parent Routers' URLs together to come up with the matching route for children. + +```js + + //will route '/' + //will route '/app' + + //will route '/app/b' + //will route '/app/c' + + + //will route '/d' + //will route anything not listed above + +``` + --- From 839775c7d7c0394464189a95c009a1ec852c790a Mon Sep 17 00:00:00 2001 From: Scott Sanders Date: Mon, 12 Dec 2016 11:35:16 -0800 Subject: [PATCH 05/16] fix indentation of nested router doc example --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 274c8f7e..4ad98e94 100644 --- a/README.md +++ b/README.md @@ -59,12 +59,12 @@ Routers will append the parent Routers' URLs together to come up with the matchi //will route '/' //will route '/app' - - //will route '/app/b' - //will route '/app/c' - - - //will route '/d' + + //will route '/app/b' + //will route '/app/c' + + + //will route '/d' //will route anything not listed above ``` From d7e5755ada1dc3fc71885016ce05e1511137686f Mon Sep 17 00:00:00 2001 From: Scott Sanders Date: Thu, 15 Dec 2016 14:21:29 -0800 Subject: [PATCH 06/16] interim work on nested routers --- src/index.js | 11 +++++++- test/dom.js | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++ test/index.js | 40 +++++++++++++++++++++------- 3 files changed, 114 insertions(+), 11 deletions(-) diff --git a/src/index.js b/src/index.js index 35633c61..31d631b9 100644 --- a/src/index.js +++ b/src/index.js @@ -141,12 +141,18 @@ class Router extends Component { constructor(props, context) { super(props); this.baseUrl = this.props.base || ''; + if (props.path) { + this.baseUrl = this.baseUrl + this.props.path; + } if (props.history) { customHistory = props.history; } if (context && context[CONTEXT_KEY]) { this.baseUrl = context[CONTEXT_KEY] + this.baseUrl; } + if (this.baseUrl) { + console.log('baseUrl on new is:', this.baseUrl); + } this.state = { url: this.props.url || getCurrentUrl() @@ -154,7 +160,9 @@ class Router extends Component { } getChildContext() { - return {CONTEXT_KEY: this.baseUrl}; + let result = {[CONTEXT_KEY]: this.baseUrl}; + console.log('result', result); + return result; } shouldComponentUpdate(props) { @@ -202,6 +210,7 @@ class Router extends Component { getMatchingChildren(children, url, invoke) { return children.slice().sort(pathRankSort).filter( ({ attributes }) => { + console.log('attributes: ', attributes); let path = this.baseUrl + attributes.path, matches = exec(url, path, attributes); if (matches) { diff --git a/test/dom.js b/test/dom.js index f58a59a4..46c2edb5 100644 --- a/test/dom.js +++ b/test/dom.js @@ -120,6 +120,80 @@ describe('dom', () => { expect(A.prototype.componentWillUnmount).to.have.been.calledOnce; }); + it('should support nested routers', () => { + class X { + componentWillMount() {} + componentWillUnmount() {} + render(){ return
; } + } + sinon.spy(X.prototype, 'componentWillMount'); + sinon.spy(X.prototype, 'componentWillUnmount'); + class Y { + componentWillMount() {} + componentWillUnmount() {} + render(){ return
; } + } + sinon.spy(Y.prototype, 'componentWillMount'); + sinon.spy(Y.prototype, 'componentWillUnmount'); + mount( + + + + + + + ); + expect(X.prototype.componentWillMount).not.to.have.been.called; + expect(Y.prototype.componentWillMount).not.to.have.been.called; + route('/app/x'); + expect(X.prototype.componentWillMount).to.have.been.calledOnce; + expect(X.prototype.componentWillUnmount).not.to.have.been.called; + expect(Y.prototype.componentWillMount).not.to.have.been.called; + expect(Y.prototype.componentWillUnmount).not.to.have.been.called; + route('/app/y'); + expect(X.prototype.componentWillMount).to.have.been.calledOnce; + expect(X.prototype.componentWillUnmount).to.have.been.calledOnce; + expect(Y.prototype.componentWillMount).to.have.been.calledOnce; + expect(Y.prototype.componentWillUnmount).not.to.have.been.called; + }); + + it('should support nested routers2', () => { + class X { + componentWillMount() {} + componentWillUnmount() {} + render(){ return
; } + } + sinon.spy(X.prototype, 'componentWillMount'); + sinon.spy(X.prototype, 'componentWillUnmount'); + class Y { + componentWillMount() {} + componentWillUnmount() {} + render(){ return
; } + } + sinon.spy(Y.prototype, 'componentWillMount'); + sinon.spy(Y.prototype, 'componentWillUnmount'); + mount( + + + + + + + ); + expect(X.prototype.componentWillMount).not.to.have.been.called; + expect(Y.prototype.componentWillMount).not.to.have.been.called; + route('/baz/j'); + expect(X.prototype.componentWillMount).to.have.been.calledOnce; + expect(X.prototype.componentWillUnmount).not.to.have.been.called; + expect(Y.prototype.componentWillMount).not.to.have.been.called; + expect(Y.prototype.componentWillUnmount).not.to.have.been.called; + route('/baz/box/k'); + expect(X.prototype.componentWillMount).to.have.been.calledOnce; + expect(X.prototype.componentWillUnmount).to.have.been.calledOnce; + expect(Y.prototype.componentWillMount).to.have.been.calledOnce; + expect(Y.prototype.componentWillUnmount).not.to.have.been.called; + }); + it('should support re-routing', done => { class A { componentWillMount() { diff --git a/test/index.js b/test/index.js index 180cb9ec..6ec389a6 100644 --- a/test/index.js +++ b/test/index.js @@ -74,24 +74,44 @@ describe('preact-router', () => { }); it('should support nested routes', () => { - let router = new Router({}, {'preact-router-base': '/foo'}); let children = [ - , - , - + + + + + + + , + , + ]; + // let grandchildren = [ + // , + // , + // + // ] + // let grandChildren = [ + // , + // , + // + // ]; + // let children = [new Router({children: grandChildren, path: '/'})]; + let router = new Router({}, {'preact-router-base': '/a'}); expect( - router.render({ children }, { url:'/foo' }) + router.render({ children }, { url:'/a/f' }) ).to.equal(children[1]); expect( - router.render({ children }, { url:'/' }) - ).to.equal(children[0]); + router.render({ children }, { url:'/a/g' }) + ).to.equal(children[2]); - expect( - router.render({ children }, { url:'/asdf/asdf' }) - ).to.equal(children[0]); + // let temp = router.render({ children }, { url:'/a/b/c/e' }); + // let util = require('util'); + // console.log(util.inspect(temp,4)); + // expect( + // temp + // ).to.equal(); }); it('should support initial route prop', () => { From bf1dd22339126e1c113c7e40b1e2b6f82afc4db0 Mon Sep 17 00:00:00 2001 From: sskoopa Date: Sun, 18 Dec 2016 15:01:35 -0800 Subject: [PATCH 07/16] fixup nested route tests in dom --- test/dom.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/test/dom.js b/test/dom.js index 46c2edb5..9ea5ec28 100644 --- a/test/dom.js +++ b/test/dom.js @@ -136,10 +136,10 @@ describe('dom', () => { sinon.spy(Y.prototype, 'componentWillMount'); sinon.spy(Y.prototype, 'componentWillUnmount'); mount( - + - + ); @@ -175,9 +175,11 @@ describe('dom', () => { mount( - - - + + + + + ); expect(X.prototype.componentWillMount).not.to.have.been.called; From 9e4a87bbfaed5ddfb4dab0c49f354a8ccec24843 Mon Sep 17 00:00:00 2001 From: sskoopa Date: Sun, 18 Dec 2016 15:05:18 -0800 Subject: [PATCH 08/16] test nested routers with all path/base combinations --- test/dom.js | 41 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/test/dom.js b/test/dom.js index 9ea5ec28..d6d41a1e 100644 --- a/test/dom.js +++ b/test/dom.js @@ -120,7 +120,7 @@ describe('dom', () => { expect(A.prototype.componentWillUnmount).to.have.been.calledOnce; }); - it('should support nested routers', () => { + it('should support nested routers with default', () => { class X { componentWillMount() {} componentWillUnmount() {} @@ -157,7 +157,44 @@ describe('dom', () => { expect(Y.prototype.componentWillUnmount).not.to.have.been.called; }); - it('should support nested routers2', () => { + it('should support nested routers with path', () => { + class X { + componentWillMount() {} + componentWillUnmount() {} + render(){ return
; } + } + sinon.spy(X.prototype, 'componentWillMount'); + sinon.spy(X.prototype, 'componentWillUnmount'); + class Y { + componentWillMount() {} + componentWillUnmount() {} + render(){ return
; } + } + sinon.spy(Y.prototype, 'componentWillMount'); + sinon.spy(Y.prototype, 'componentWillUnmount'); + mount( + + + + + + + ); + expect(X.prototype.componentWillMount).not.to.have.been.called; + expect(Y.prototype.componentWillMount).not.to.have.been.called; + route('/baz/j'); + expect(X.prototype.componentWillMount).to.have.been.calledOnce; + expect(X.prototype.componentWillUnmount).not.to.have.been.called; + expect(Y.prototype.componentWillMount).not.to.have.been.called; + expect(Y.prototype.componentWillUnmount).not.to.have.been.called; + route('/baz/box/k'); + expect(X.prototype.componentWillMount).to.have.been.calledOnce; + expect(X.prototype.componentWillUnmount).to.have.been.calledOnce; + expect(Y.prototype.componentWillMount).to.have.been.calledOnce; + expect(Y.prototype.componentWillUnmount).not.to.have.been.called; + }); + + it('should support deeply nested routers', () => { class X { componentWillMount() {} componentWillUnmount() {} From 3f36e2825576a3aac8d092a282d2b46b916475a4 Mon Sep 17 00:00:00 2001 From: sskoopa Date: Sun, 18 Dec 2016 16:26:28 -0800 Subject: [PATCH 09/16] new Match component, handle var-based paths --- src/index.js | 47 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 39 insertions(+), 8 deletions(-) diff --git a/src/index.js b/src/index.js index 31d631b9..9045a078 100644 --- a/src/index.js +++ b/src/index.js @@ -1,5 +1,5 @@ import { h, Component } from 'preact'; -import { exec, pathRankSort } from './util'; +import { exec, pathRankSort, segmentize } from './util'; let customHistory = null; @@ -10,6 +10,7 @@ const EMPTY = {}; // hangs off all elements created by preact const ATTR_KEY = typeof Symbol!=='undefined' ? Symbol.for('preactattr') : '__preactattr_'; +//a key for the context to carry baseUrl to nested Router instances const CONTEXT_KEY = 'preact-router-base'; function isPreactElement(node) { @@ -142,7 +143,12 @@ class Router extends Component { super(props); this.baseUrl = this.props.base || ''; if (props.path) { - this.baseUrl = this.baseUrl + this.props.path; + let segments = segmentize(props.path); + segments.forEach(segment => { + if (segment.indexOf(':') == -1) { + this.baseUrl = this.baseUrl + '/' + segment; + } + }); } if (props.history) { customHistory = props.history; @@ -150,9 +156,6 @@ class Router extends Component { if (context && context[CONTEXT_KEY]) { this.baseUrl = context[CONTEXT_KEY] + this.baseUrl; } - if (this.baseUrl) { - console.log('baseUrl on new is:', this.baseUrl); - } this.state = { url: this.props.url || getCurrentUrl() @@ -161,7 +164,6 @@ class Router extends Component { getChildContext() { let result = {[CONTEXT_KEY]: this.baseUrl}; - console.log('result', result); return result; } @@ -210,7 +212,6 @@ class Router extends Component { getMatchingChildren(children, url, invoke) { return children.slice().sort(pathRankSort).filter( ({ attributes }) => { - console.log('attributes: ', attributes); let path = this.baseUrl + attributes.path, matches = exec(url, path, attributes); if (matches) { @@ -258,11 +259,41 @@ const Route = ({ component, url, matches }) => { return h(component, { url, matches }); }; +class Match extends Component { + + constructor(props, context) { + super(props); + this.baseUrl = this.props.base || ''; + if (props.path) { + let segments = segmentize(props.path); + segments.forEach(segment => { + if (segment.indexOf(':') == -1) { + this.baseUrl = this.baseUrl + '/' + segment; + } + }); + } + if (context && context[CONTEXT_KEY]) { + this.baseUrl = context[CONTEXT_KEY] + this.baseUrl; + } + } + + getChildContext() { + let result = {[CONTEXT_KEY]: this.baseUrl}; + return result; + } + + render({ children, url, matches }) { + return h(children[0], { url, matches }); + } + +} + Router.route = route; Router.Router = Router; Router.Route = Route; +Router.Match = Match; Router.Link = Link; -export { route, Router, Route, Link }; +export { route, Router, Route, Match, Link }; export default Router; From abb475ef11d23debef50a1bafb45c0791d2405a5 Mon Sep 17 00:00:00 2001 From: sskoopa Date: Sun, 18 Dec 2016 16:27:18 -0800 Subject: [PATCH 10/16] attempted test for --- test/dom.js | 42 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/test/dom.js b/test/dom.js index d6d41a1e..ac14981f 100644 --- a/test/dom.js +++ b/test/dom.js @@ -1,4 +1,4 @@ -import { Router, Link, route } from 'src'; +import { Router, Link, Match, route } from 'src'; import { h, render } from 'preact'; const Empty = () => null; @@ -233,6 +233,46 @@ describe('dom', () => { expect(Y.prototype.componentWillUnmount).not.to.have.been.called; }); + it('should support nested routers with Match', () => { + class X { + componentWillMount() {} + componentWillUnmount() {} + render(){ return
; } + } + sinon.spy(X.prototype, 'componentWillMount'); + sinon.spy(X.prototype, 'componentWillUnmount'); + class Y { + componentWillMount() {} + componentWillUnmount() {} + render(){ return
; } + } + sinon.spy(Y.prototype, 'componentWillMount'); + sinon.spy(Y.prototype, 'componentWillUnmount'); + mount( + + + +
test
+ + + +
+
+ ); + expect(X.prototype.componentWillMount).not.to.have.been.called; + expect(Y.prototype.componentWillMount).not.to.have.been.called; + route('/ccc/jjj'); + expect(X.prototype.componentWillMount).to.have.been.calledOnce; + expect(X.prototype.componentWillUnmount).not.to.have.been.called; + expect(Y.prototype.componentWillMount).not.to.have.been.called; + expect(Y.prototype.componentWillUnmount).not.to.have.been.called; + route('/ccc/xxx/kkk'); + expect(X.prototype.componentWillMount).to.have.been.calledOnce; + expect(X.prototype.componentWillUnmount).to.have.been.calledOnce; + expect(Y.prototype.componentWillMount).to.have.been.calledOnce; + expect(Y.prototype.componentWillUnmount).not.to.have.been.called; + }); + it('should support re-routing', done => { class A { componentWillMount() { From 218b127492100eb6ab12f895019353ad340ddd82 Mon Sep 17 00:00:00 2001 From: sskoopa Date: Sun, 18 Dec 2016 16:45:08 -0800 Subject: [PATCH 11/16] move nested route testing to dom.js --- test/index.js | 41 ----------------------------------------- 1 file changed, 41 deletions(-) diff --git a/test/index.js b/test/index.js index 6ec389a6..ba5a0f34 100644 --- a/test/index.js +++ b/test/index.js @@ -73,47 +73,6 @@ describe('preact-router', () => { ).to.equal(children[0]); }); - it('should support nested routes', () => { - let children = [ - - - - - - - , - , - - ]; - // let grandchildren = [ - // , - // , - // - // ] - // let grandChildren = [ - // , - // , - // - // ]; - // let children = [new Router({children: grandChildren, path: '/'})]; - let router = new Router({}, {'preact-router-base': '/a'}); - - expect( - router.render({ children }, { url:'/a/f' }) - ).to.equal(children[1]); - - expect( - router.render({ children }, { url:'/a/g' }) - ).to.equal(children[2]); - - // let temp = router.render({ children }, { url:'/a/b/c/e' }); - // let util = require('util'); - // console.log(util.inspect(temp,4)); - // expect( - // temp - // ).to.equal(); - }); - it('should support initial route prop', () => { let router = new Router({ url:'/foo' }); let children = [ From a6114afbc924a6c3d2838602c75437f18132a509 Mon Sep 17 00:00:00 2001 From: sskoopa Date: Sun, 18 Dec 2016 17:08:44 -0800 Subject: [PATCH 12/16] Router and Match finally work --- src/index.js | 8 ++++---- test/dom.js | 1 - 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/index.js b/src/index.js index 9045a078..3768fadc 100644 --- a/src/index.js +++ b/src/index.js @@ -282,8 +282,8 @@ class Match extends Component { return result; } - render({ children, url, matches }) { - return h(children[0], { url, matches }); + render({ children }) { + return children[0]; } } @@ -292,8 +292,8 @@ class Match extends Component { Router.route = route; Router.Router = Router; Router.Route = Route; -Router.Match = Match; Router.Link = Link; +Router.Match = Match; -export { route, Router, Route, Match, Link }; +export { route, Router, Route, Link, Match }; export default Router; diff --git a/test/dom.js b/test/dom.js index ac14981f..6afe0da1 100644 --- a/test/dom.js +++ b/test/dom.js @@ -252,7 +252,6 @@ describe('dom', () => { -
test
From 5ac00bd9b805afca5c74e4457b1def721dfad5ba Mon Sep 17 00:00:00 2001 From: sskoopa Date: Sun, 18 Dec 2016 17:11:52 -0800 Subject: [PATCH 13/16] Router and Match finally work --- src/index.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/index.js b/src/index.js index 3768fadc..52103e14 100644 --- a/src/index.js +++ b/src/index.js @@ -1,4 +1,4 @@ -import { h, Component } from 'preact'; +import { h, Component, cloneElement } from 'preact'; import { exec, pathRankSort, segmentize } from './util'; let customHistory = null; @@ -282,8 +282,8 @@ class Match extends Component { return result; } - render({ children }) { - return children[0]; + render({ children, url, matches }) { + return cloneElement(children[0], {url, matches}); } } From 132b302a23845d39bb5e5643a5101aa1c246c643 Mon Sep 17 00:00:00 2001 From: sskoopa Date: Sun, 18 Dec 2016 17:13:46 -0800 Subject: [PATCH 14/16] updated README example --- README.md | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 4ad98e94..db69e37c 100644 --- a/README.md +++ b/README.md @@ -57,20 +57,16 @@ Routers will append the parent Routers' URLs together to come up with the matchi ```js - //will route '/' - //will route '/app' - - //will route '/app/b' - //will route '/app/c' - - - //will route '/d' - //will route anything not listed above + //will route '/' + //will route '/app/*' (could also use default here) + //will route '/app/b' + //will route '/app/c' + + //will route '/d' + //will route anything not listed above ``` - - --- From dcdd3f455204b5cfde1008a26fe9f90725e94990 Mon Sep 17 00:00:00 2001 From: sskoopa Date: Sun, 18 Dec 2016 17:35:32 -0800 Subject: [PATCH 15/16] added ability to reset base. Defining base will not prepend context base --- src/index.js | 2 +- test/dom.js | 39 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/src/index.js b/src/index.js index 52103e14..9ade285e 100644 --- a/src/index.js +++ b/src/index.js @@ -153,7 +153,7 @@ class Router extends Component { if (props.history) { customHistory = props.history; } - if (context && context[CONTEXT_KEY]) { + if (context && context[CONTEXT_KEY] && !this.props.base) { this.baseUrl = context[CONTEXT_KEY] + this.baseUrl; } diff --git a/test/dom.js b/test/dom.js index 6afe0da1..2e36929b 100644 --- a/test/dom.js +++ b/test/dom.js @@ -213,7 +213,7 @@ describe('dom', () => { - + @@ -272,6 +272,43 @@ describe('dom', () => { expect(Y.prototype.componentWillUnmount).not.to.have.been.called; }); + it('should support nested router reset via base attr', () => { + class X { + componentWillMount() {} + componentWillUnmount() {} + render(){ return
; } + } + sinon.spy(X.prototype, 'componentWillMount'); + sinon.spy(X.prototype, 'componentWillUnmount'); + class Y { + componentWillMount() {} + componentWillUnmount() {} + render(){ return
; } + } + sinon.spy(Y.prototype, 'componentWillMount'); + sinon.spy(Y.prototype, 'componentWillUnmount'); + mount( + + + + + + + ); + expect(X.prototype.componentWillMount).not.to.have.been.called; + expect(Y.prototype.componentWillMount).not.to.have.been.called; + route('/baz/j'); + expect(X.prototype.componentWillMount).to.have.been.calledOnce; + expect(X.prototype.componentWillUnmount).not.to.have.been.called; + expect(Y.prototype.componentWillMount).not.to.have.been.called; + expect(Y.prototype.componentWillUnmount).not.to.have.been.called; + route('/baz/foo/k'); + expect(X.prototype.componentWillMount).to.have.been.calledOnce; + expect(X.prototype.componentWillUnmount).to.have.been.calledOnce; + expect(Y.prototype.componentWillMount).to.have.been.calledOnce; + expect(Y.prototype.componentWillUnmount).not.to.have.been.called; + }); + it('should support re-routing', done => { class A { componentWillMount() { From a26343a35057fb441c241d96f52b0bb39faea824 Mon Sep 17 00:00:00 2001 From: sskoopa Date: Sun, 23 Apr 2017 16:57:26 -0700 Subject: [PATCH 16/16] final merge of both Match components, nested routers work post 2.5.1 --- src/index.js | 9 ++++----- src/match.js | 22 ++++++++++++---------- src/util.js | 2 -- test/dom.js | 24 +++++++++++------------- 4 files changed, 27 insertions(+), 30 deletions(-) diff --git a/src/index.js b/src/index.js index 32096099..e76badf7 100644 --- a/src/index.js +++ b/src/index.js @@ -1,5 +1,5 @@ import { cloneElement, h, Component } from 'preact'; -import { exec, pathRankSort, assign, segmentize, CONTEXT_KEY } from './util'; +import { exec, pathRankSort, assign, segmentize } from './util'; let customHistory = null; @@ -160,10 +160,9 @@ class Router extends Component { if (props.history) { customHistory = props.history; } - if (context && context[CONTEXT_KEY] && !this.props.base) { - this.baseUrl = context[CONTEXT_KEY] + this.baseUrl; + if (context && context['preact-router-base'] && !this.props.base) { + this.baseUrl = context['preact-router-base'] + this.baseUrl; } - this.state = { url: props.url || getCurrentUrl() }; @@ -172,7 +171,7 @@ class Router extends Component { } getChildContext() { - let result = {[CONTEXT_KEY]: this.baseUrl}; + let result = {['preact-router-base']: this.baseUrl}; return result; } diff --git a/src/match.js b/src/match.js index f7b6af79..24ae5179 100644 --- a/src/match.js +++ b/src/match.js @@ -1,6 +1,6 @@ -import { h, Component } from 'preact'; +import { h, Component, cloneElement } from 'preact'; import { subscribers, getCurrentUrl, Link as StaticLink } from 'preact-router'; -import { segmentize, CONTEXT_KEY } from './util'; +import { exec, segmentize } from './util'; export class Match extends Component { constructor(props, context) { @@ -14,8 +14,8 @@ export class Match extends Component { } }); } - if (context && context[CONTEXT_KEY]) { - this.baseUrl = context[CONTEXT_KEY] + this.baseUrl; + if (context && context['preact-router-base']) { + this.baseUrl = context['preact-router-base'] + this.baseUrl; } } update = url => { @@ -29,19 +29,21 @@ export class Match extends Component { subscribers.splice(subscribers.indexOf(this.update)>>>0, 1); } getChildContext() { - let result = {[CONTEXT_KEY]: this.baseUrl}; + let result = {['preact-router-base']: this.baseUrl}; return result; } - render(props) { + render(props, state, context) { let url = this.nextUrl || getCurrentUrl(), path = url.replace(/\?.+$/,''); this.nextUrl = null; - console.log('children', props.children); - return props.children[0] && props.children[0]({ + const newProps = { url, path, - matches: path===props.path - }); + matches: path===props.path || exec(path, context['preact-router-base'] + props.path, {}) + }; + return props.children[0] && + (typeof props.children[0] === 'function' ? + props.children[0](newProps) : cloneElement(props.children[0], newProps)); } } diff --git a/src/util.js b/src/util.js index a9f9fa18..e2031491 100644 --- a/src/util.js +++ b/src/util.js @@ -1,6 +1,4 @@ -//a key for the context to carry baseUrl to nested Router instances -export const CONTEXT_KEY = 'preact-router-base'; const EMPTY = {}; export function assign(obj, props) { diff --git a/test/dom.js b/test/dom.js index 1eb25828..67842a84 100644 --- a/test/dom.js +++ b/test/dom.js @@ -234,7 +234,7 @@ describe('dom', () => { expect(Y.prototype.componentWillUnmount).not.to.have.been.called; }); - it('should support nested routers with Match', () => { + it('should support nested routers and Match(s)', () => { class X { componentWillMount() {} componentWillUnmount() {} @@ -253,24 +253,22 @@ describe('dom', () => { - - ); - expect(X.prototype.componentWillMount).not.to.have.been.called; - expect(Y.prototype.componentWillMount).not.to.have.been.called; + expect(X.prototype.componentWillMount, 'X1').not.to.have.been.called; + expect(Y.prototype.componentWillMount, 'Y1').not.to.have.been.called; route('/ccc/jjj'); - expect(X.prototype.componentWillMount).to.have.been.calledOnce; - expect(X.prototype.componentWillUnmount).not.to.have.been.called; - expect(Y.prototype.componentWillMount).not.to.have.been.called; - expect(Y.prototype.componentWillUnmount).not.to.have.been.called; + expect(X.prototype.componentWillMount, 'X2').to.have.been.calledOnce; + expect(X.prototype.componentWillUnmount, 'X3').not.to.have.been.called; + expect(Y.prototype.componentWillMount, 'Y2').not.to.have.been.called; + expect(Y.prototype.componentWillUnmount, 'Y3').not.to.have.been.called; route('/ccc/xxx/kkk'); - expect(X.prototype.componentWillMount).to.have.been.calledOnce; - expect(X.prototype.componentWillUnmount).to.have.been.calledOnce; - expect(Y.prototype.componentWillMount).to.have.been.calledOnce; - expect(Y.prototype.componentWillUnmount).not.to.have.been.called; + expect(X.prototype.componentWillMount, 'X4').to.have.been.calledOnce; + expect(X.prototype.componentWillUnmount, 'X5').to.have.been.calledOnce; + expect(Y.prototype.componentWillMount, 'Y4').to.have.been.calledOnce; + expect(Y.prototype.componentWillUnmount, 'Y5').not.to.have.been.called; }); it('should support nested router reset via base attr', () => {