diff --git a/README.md b/README.md index 1d93036..38c28ea 100755 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ npm install --save apprun # peer dependency ``` ### Pretty Links -As of 0.4.0, this router supports arbitrary depth path segments, dynamic path segments, and query strings along with HTML5 History. +As of version 0.4.0, this router supports arbitrary depth path segments, dynamic path segments, and query strings along with HTML5 History. #### Pretty Links and HTML5 History @@ -47,7 +47,7 @@ import { IPrettyRoute } from "apprun-router/pretty"; #### Query strings -Query strings (the part of the URI after a ?) are supported, generally, with no extra effort. By default the separator is a `&`, but this can be changed using the `setPrettyLinkQuerySeparator` method. This change affects all routes. +Query strings (the part of the URI after a ?) are supported, generally, with no extra effort. By default the field separator is a `&`, but this can be changed using the `setPrettyLinkQuerySeparator` method. This change affects all routes. ``` import {setPrettyLinkQuerySeparator} from "apprun-router/pretty"; @@ -70,14 +70,15 @@ class YourComponent extends Component { ... update = { - "EVENT_NAME": (state: any, dynamicSegments: IPrettyRouteDynamicSegments | undefined, queries: IPrettyRouteQueries | undefined) => state + "EVENT_NAME": (state: any, dynamicSegments: IPrettyRouteDynamicSegments, queries: IPrettyRouteQueries) => state }; .... } - ``` +Any found query fields will be provided as a mapping `{field1: value1, etc}`. If there is no query string, and hence no fields, the mapping will be empty (`{}`). + #### Dynamic segments Dynamic segments (generic path segments) are supported. Unsurprisingly, they require a small amount of configuration. @@ -98,7 +99,7 @@ class YourComponent extends Component { state: any = {}; update = { - [addDynamicRoute(URL_FOO)]: (state: any, dynamicSegments: IPrettyRouteDynamicSegments | undefined, queries: IPrettyRouteQueries | undefined) => { + [addDynamicRoute(URL_FOO)]: (state: any, dynamicSegments: IPrettyRouteDynamicSegments, queries: IPrettyRouteQueries) => { return { dynamicSegments: dynamicSegments, queries: queries, @@ -121,6 +122,8 @@ In this example there are a few points to recognize: 3. If for some reason you no longer need a dynamic route, you will have to remove it using the `removeDynamicRoute` method. In the case of the example above it would be `removeDynamicRoute(URL_FOO)`. +4. If the route is entirely static the dynamic parameter provided to the update method will be an empty mapping (`{}`). + ##### Downside of the dynamic segments implementation diff --git a/src/pretty.ts b/src/pretty.ts index 7ef8932..480fb6f 100755 --- a/src/pretty.ts +++ b/src/pretty.ts @@ -9,7 +9,7 @@ export interface IPrettyRouteDynamicSegments { } export interface IPrettyRouteQueries { - [field: string]: string | undefined; + [field: string]: string; } const dynamicRoutes: IPrettyRouteDynamicSegments = {}; @@ -79,13 +79,13 @@ function prettyLinkRouter(url: string, popstate: boolean = false): void { const origUrl = url; // Any queries? - let queries: {[name: string]: string} | undefined; + let queries: IPrettyRouteQueries = {}; const queryPos = url.search(/\?/); if(queryPos >= 0) { const queryStr = url.substring(queryPos + 1); const queryStrings = queryStr.split(prettyLinkQuerySeparator); - queries = {}; + for(const query of queryStrings) { const [field, value] = query.split("="); queries[field] = value; @@ -95,7 +95,7 @@ function prettyLinkRouter(url: string, popstate: boolean = false): void { } // Any dynamic segements? - let dynamic: {[name: string]: string} | undefined; + let dynamic: IPrettyRouteDynamicSegments = {}; const matchingDynamicRoute = Object.keys(dynamicRoutes).find((route) => { return url.startsWith(route + "/"); }); @@ -105,8 +105,6 @@ function prettyLinkRouter(url: string, popstate: boolean = false): void { console.assert(dynamicParams.length === urlDynamicParams.length, `expected dynamic and actual dynamic length don't match ${dynamicParams.length} vs. ${urlDynamicParams.length}`); - dynamic = {}; - // First param will always be a "", so ignore it. for(let i = 1; i < dynamicParams.length; ++i) { // Is this parameter in the dynamic section actually dynamic? diff --git a/test/pretty.spec.tsx b/test/pretty.spec.tsx index 989819b..3bc4bff 100644 --- a/test/pretty.spec.tsx +++ b/test/pretty.spec.tsx @@ -22,56 +22,56 @@ const urlStatic = "/basic"; const urlStaticWithQuery = "/foo"; const urlStaticWithQueryStatic = "/foo"; const urlStaticWithQueryExample = "/foo?bar=7"; -const urlStaticWithQueryExampleDynamicArgs = undefined; -const urlStaticWithQueryExampleQueryArgs = {bar: "7"}; +const urlStaticWithQueryExampleDynamicArgs: IPrettyRouteDynamicSegments = {}; +const urlStaticWithQueryExampleQueryArgs: IPrettyRouteQueries = {bar: "7"}; const urlStaticWithSlashWithQuery = "/foo/"; const urlStaticWithSlashWithQueryStatic = "/foo/"; const urlStaticWithSlashWithQueryExample = "/foo/?bar=9"; -const urlStaticWithSlashWithQueryExampleDynamicArgs = undefined; -const urlStaticWithSlashWithQueryExampleQueryArgs = {bar: "9"}; +const urlStaticWithSlashWithQueryExampleDynamicArgs: IPrettyRouteDynamicSegments = {}; +const urlStaticWithSlashWithQueryExampleQueryArgs: IPrettyRouteQueries = {bar: "9"}; const urlStaticWithMultipleQuery = "/foo"; const urlStaticWithMultipleQueryStatic = "/foo"; const urlStaticWithMultipleQueryExample = "/foo?name=fred&age=88"; -const urlStaticWithMultipleQueryExampleDynamicArgs = undefined; -const urlStaticWithMultipleQueryExampleQueryArgs = {name: "fred", age: "88"}; +const urlStaticWithMultipleQueryExampleDynamicArgs: IPrettyRouteDynamicSegments = {}; +const urlStaticWithMultipleQueryExampleQueryArgs: IPrettyRouteQueries = {name: "fred", age: "88"}; const urlStaticWithMultipleQueryUnusualSeparation = "/foo"; const urlStaticWithMultipleQueryUnusualSeparationStatic = "/foo"; const urlStaticWithMultipleQueryUnusualSeparationExample = "/foo?name=bob;age=99"; -const urlStaticWithMultipleQueryUnusualSeparationExampleDynamicArgs = undefined; -const urlStaticWithMultipleQueryUnusualSeparationExampleQueryArgs = {name: "bob", age: "99"}; +const urlStaticWithMultipleQueryUnusualSeparationExampleDynamicArgs: IPrettyRouteDynamicSegments = {}; +const urlStaticWithMultipleQueryUnusualSeparationExampleQueryArgs: IPrettyRouteQueries = {name: "bob", age: "99"}; const urlOneDynamicSegments = "/foo/:bar"; const urlOneDynamicSegmentsStatic = "/foo"; const urlOneDynamicSegmentsExample = "/foo/fred"; -const urlOneDynamicSegmentsExampleDynamicArgs = {bar: "fred"}; -const urlOneDynamicSegmentsExampleQueryArgs = undefined; +const urlOneDynamicSegmentsExampleDynamicArgs: IPrettyRouteDynamicSegments = {bar: "fred"}; +const urlOneDynamicSegmentsExampleQueryArgs: IPrettyRouteQueries = {}; const urlStaticSimilarToOneDynamicSegments = "/foobar"; const urlStaticSimilarToOneDynamicSegmentsStatic = "/foobar"; const urlStaticSimilarToOneDynamicSegmentsExample = "/foobar"; -const urlStaticSimilarToOneDynamicSegmentsExampleDynamicArgs = undefined; -const urlStaticSimilarToOneDynamicSegmentsExampleQueryArgs = undefined; +const urlStaticSimilarToOneDynamicSegmentsExampleDynamicArgs: IPrettyRouteDynamicSegments = {}; +const urlStaticSimilarToOneDynamicSegmentsExampleQueryArgs: IPrettyRouteQueries = {}; const urlTwoDynamicSegments = "/foo/:bar/:baz"; const urlTwoDynamicSegmentsStatic = "/foo"; const urlTwoDynamicSegmentsExample = "/foo/fred/barney"; -const urlTwoDynamicSegmentsExampleDynamicArgs = {bar: "fred", baz: "barney"}; -const urlTwoDynamicSegmentsExampleQueryArgs = undefined; +const urlTwoDynamicSegmentsExampleDynamicArgs: IPrettyRouteDynamicSegments = {bar: "fred", baz: "barney"}; +const urlTwoDynamicSegmentsExampleQueryArgs: IPrettyRouteQueries = {}; const urlTwoDynamicSegmentsAndQuery = "/foo/:bar/:baz"; const urlTwoDynamicSegmentsAndQueryStatic = "/foo"; const urlTwoDynamicSegmentsAndQueryStaticExample = "/foo/seg1/seg2?boo=8"; -const urlTwoDynamicSegmentsAndQueryStaticExampleDynamicArgs = {bar: "seg1", baz: "seg2"}; -const urlTwoDynamicSegmentsAndQueryStaticExampleQueryArgs = {boo: "8"}; +const urlTwoDynamicSegmentsAndQueryStaticExampleDynamicArgs: IPrettyRouteDynamicSegments = {bar: "seg1", baz: "seg2"}; +const urlTwoDynamicSegmentsAndQueryStaticExampleQueryArgs: IPrettyRouteQueries = {boo: "8"}; const urlTwoDynamicSegmentsWithStaticAndQuery = "/foo/:bar/foo2/:baz"; const urlTwoDynamicSegmentsWithStaticAndQueryStatic = "/foo"; const urlTwoDynamicSegmentsWithStaticAndQueryStaticExample = "/foo/crazy/foo2/food?planet=earth&kingdom=bacteria"; -const urlTwoDynamicSegmentsWithStaticAndQueryStaticExampleDynamicArgs = {bar: "crazy", baz: "food"}; -const urlTwoDynamicSegmentsWithStaticAndQueryStaticExampleQueryArgs = {planet: "earth", kingdom: "bacteria"}; +const urlTwoDynamicSegmentsWithStaticAndQueryStaticExampleDynamicArgs: IPrettyRouteDynamicSegments = {bar: "crazy", baz: "food"}; +const urlTwoDynamicSegmentsWithStaticAndQueryStaticExampleQueryArgs: IPrettyRouteQueries = {planet: "earth", kingdom: "bacteria"}; describe("pretty router", () => { it("should overwrite the default router simply by importing", () => { @@ -252,9 +252,9 @@ describe("pretty router - link clicking", () => { class TestComponent extends Component { public update = { - [URL_FOO]: (_: any, dynamicSegments: IPrettyRouteDynamicSegments | undefined, queries: IPrettyRouteQueries | undefined) => { - expect(dynamicSegments).toEqual(undefined); - expect(queries).toEqual(undefined); + [URL_FOO]: (_: any, dynamicSegments: IPrettyRouteDynamicSegments, queries: IPrettyRouteQueries) => { + expect(dynamicSegments).toEqual({}); + expect(queries).toEqual({}); test.unmount(); div.remove(); @@ -289,7 +289,7 @@ describe("pretty router - link clicking", () => { class TestComponent extends Component { public update = { - [addDynamicRoute(URL_FOO)]: (_: any, dynamicSegments: IPrettyRouteDynamicSegments | undefined, queries: IPrettyRouteQueries | undefined) => { + [addDynamicRoute(URL_FOO)]: (_: any, dynamicSegments: IPrettyRouteDynamicSegments, queries: IPrettyRouteQueries) => { expect(dynamicSegments).toEqual(URL_DYNAMICS); expect(queries).toEqual(URL_QUERIES); @@ -328,7 +328,7 @@ describe("pretty router - link clicking", () => { app.run("route", urlTwoDynamicSegmentsWithStaticAndQueryStaticExample); - expect(router404EventFn).toHaveBeenCalledWith(url404, undefined, urlTwoDynamicSegmentsWithStaticAndQueryStaticExampleQueryArgs); + expect(router404EventFn).toHaveBeenCalledWith(url404, {}, urlTwoDynamicSegmentsWithStaticAndQueryStaticExampleQueryArgs); app.off(ROUTER_404_EVENT, router404EventFn); }); @@ -400,7 +400,7 @@ describe("pretty router - dynamic segments", () => { app.run("route", urlTwoDynamicSegmentsExample); expect(routerEventFn).toHaveBeenCalled(); - expect(router404EventFn).toHaveBeenCalledWith(urlTwoDynamicSegmentsExample, undefined, undefined); + expect(router404EventFn).toHaveBeenCalledWith(urlTwoDynamicSegmentsExample, {}, {}); expect(routerRouteFn).not.toHaveBeenCalled(); app.off(ROUTER_EVENT, routerEventFn);