From 75bd8833c8e16ec43beecbb5903feb155fe0afd4 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Mon, 20 May 2019 09:45:11 +0100 Subject: [PATCH 01/26] Removed parseLink from fluent navigation Manually built data and crumbs instead of using expensive parseLink --- Navigation/src/FluentNavigator.ts | 33 +++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/Navigation/src/FluentNavigator.ts b/Navigation/src/FluentNavigator.ts index e560415d9..6b638439a 100644 --- a/Navigation/src/FluentNavigator.ts +++ b/Navigation/src/FluentNavigator.ts @@ -11,9 +11,14 @@ interface FluentNavigator { } function createFluentNavigator(states: { [index: string]: State }, stateHandler: StateHandler, stateContext = new StateContext()): FluentNavigator { - function navigateLink(url): FluentNavigator { - var { state, data } = stateHandler.parseLink(url); - var { [state.crumbTrailKey]: crumbs, ...data } = data; + function getCrumbTrail(state: State, navigationData: any, crumbs: Crumb[], nextCrumb: Crumb): Crumb[] { + crumbs = crumbs.slice(); + if (nextCrumb) + crumbs.push(nextCrumb); + return state.truncateCrumbTrail(state, navigationData, crumbs); + } + + function navigateLink(state: State, data: any, crumbs: Crumb[], url: string): FluentNavigator { var fluentContext = new StateContext(); fluentContext.state = state; fluentContext.url = url; @@ -26,28 +31,36 @@ function createFluentNavigator(states: { [index: string]: State }, stateHandler: return { url: stateContext.url, navigate: function(stateKey: string, navigationData?: any): FluentNavigator { - if (!states[stateKey]) + var state = states[stateKey]; + var {crumbs, nextCrumb} = stateContext; + if (!state) throw new Error(stateKey + ' is not a valid State'); if (typeof navigationData === 'function') navigationData = navigationData(stateContext.data); - var url = stateHandler.getLink(states[stateKey], navigationData, stateContext.crumbs, stateContext.nextCrumb); + var url = stateHandler.getLink(state, navigationData, crumbs, nextCrumb); if (url == null) throw new Error('Invalid route data, a mandatory route parameter has not been supplied a value'); - return navigateLink(url); + var data = { ...state.defaults, ...navigationData }; + var crumbs = getCrumbTrail(state, navigationData, crumbs, nextCrumb); + return navigateLink(state, data, crumbs, url); }, navigateBack: function(distance: number): FluentNavigator { if (!(distance <= stateContext.crumbs.length && distance > 0)) throw new Error('The distance parameter must be greater than zero and less than or equal to the number of Crumbs (' + stateContext.crumbs.length + ')'); - var url = stateContext.crumbs[stateContext.crumbs.length - distance].url; - return navigateLink(url); + var {state, data, url} = stateContext.crumbs[stateContext.crumbs.length - distance]; + var crumbs = stateContext.crumbs.slice(0, stateContext.crumbs.length - distance); + return navigateLink(state, data, crumbs, url); }, refresh: function(navigationData?: any): FluentNavigator { + var {state, crumbs, nextCrumb} = stateContext; if (typeof navigationData === 'function') navigationData = navigationData(stateContext.data); - var url = stateHandler.getLink(stateContext.state, navigationData, stateContext.crumbs, stateContext.nextCrumb); + var url = stateHandler.getLink(state, navigationData, crumbs, nextCrumb); if (url == null) throw new Error('Invalid route data, a mandatory route parameter has not been supplied a value'); - return navigateLink(url); + var data = { ...state.defaults, ...navigationData }; + var crumbs = getCrumbTrail(state, navigationData, crumbs, nextCrumb); + return navigateLink(state, data, crumbs, url); } } } From 44077ae092c6397588a1fd957013a365ea0d35e5 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Mon, 20 May 2019 09:50:13 +0100 Subject: [PATCH 02/26] Returned empty crumbs if not tracking --- Navigation/src/FluentNavigator.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Navigation/src/FluentNavigator.ts b/Navigation/src/FluentNavigator.ts index 6b638439a..3d48b8319 100644 --- a/Navigation/src/FluentNavigator.ts +++ b/Navigation/src/FluentNavigator.ts @@ -12,6 +12,8 @@ interface FluentNavigator { function createFluentNavigator(states: { [index: string]: State }, stateHandler: StateHandler, stateContext = new StateContext()): FluentNavigator { function getCrumbTrail(state: State, navigationData: any, crumbs: Crumb[], nextCrumb: Crumb): Crumb[] { + if (!state.trackCrumbTrail) + return []; crumbs = crumbs.slice(); if (nextCrumb) crumbs.push(nextCrumb); From 1d78b63701e5bf1f5369f43ba037dbc809871c2a Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Mon, 20 May 2019 09:54:31 +0100 Subject: [PATCH 03/26] Passed data and defaults to truncate crumb --- Navigation/src/FluentNavigator.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Navigation/src/FluentNavigator.ts b/Navigation/src/FluentNavigator.ts index 3d48b8319..f7ead7264 100644 --- a/Navigation/src/FluentNavigator.ts +++ b/Navigation/src/FluentNavigator.ts @@ -43,7 +43,7 @@ function createFluentNavigator(states: { [index: string]: State }, stateHandler: if (url == null) throw new Error('Invalid route data, a mandatory route parameter has not been supplied a value'); var data = { ...state.defaults, ...navigationData }; - var crumbs = getCrumbTrail(state, navigationData, crumbs, nextCrumb); + var crumbs = getCrumbTrail(state, data, crumbs, nextCrumb); return navigateLink(state, data, crumbs, url); }, navigateBack: function(distance: number): FluentNavigator { @@ -61,7 +61,7 @@ function createFluentNavigator(states: { [index: string]: State }, stateHandler: if (url == null) throw new Error('Invalid route data, a mandatory route parameter has not been supplied a value'); var data = { ...state.defaults, ...navigationData }; - var crumbs = getCrumbTrail(state, navigationData, crumbs, nextCrumb); + var crumbs = getCrumbTrail(state, data, crumbs, nextCrumb); return navigateLink(state, data, crumbs, url); } } From cbe06165a3b31e6a28ec5206ac7350a8537eb639 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Mon, 20 May 2019 10:00:25 +0100 Subject: [PATCH 04/26] Navigated link for constraint errors Shows removing parseLink is better because fluent test matches navigate link tests. They don't throw when building the url, only throw when navigating link. Before fluent threw when building url --- Navigation/test/NavigationDataTest.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Navigation/test/NavigationDataTest.ts b/Navigation/test/NavigationDataTest.ts index 137e02bfd..5639fc011 100644 --- a/Navigation/test/NavigationDataTest.ts +++ b/Navigation/test/NavigationDataTest.ts @@ -6899,7 +6899,8 @@ describe('Navigation Data', function () { describe('Fluent Navigate', function() { it('should throw error', function() { - assert.throws(() => stateNavigator.fluent().navigate('s', individualNavigationData), /The Url .+ is invalid/); + var link = stateNavigator.fluent().navigate('s', individualNavigationData).url; + assert.throws(() => stateNavigator.navigateLink(link), /The Url .+ is invalid/); }); }); }); @@ -6939,7 +6940,8 @@ describe('Navigation Data', function () { describe('Fluent Navigate', function() { it('should throw error', function() { - assert.throws(() => stateNavigator.fluent().navigate('s', arrayNavigationData), /The Url .+ is invalid/); + var link = stateNavigator.fluent().navigate('s', arrayNavigationData).url; + assert.throws(() => stateNavigator.navigateLink(link), /The Url .+ is invalid/); }); }); }); From b7a6c7b65b9da697d89580c45f57f5d390ea6833 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Mon, 20 May 2019 10:10:28 +0100 Subject: [PATCH 05/26] Destructured crumbs --- Navigation/src/FluentNavigator.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Navigation/src/FluentNavigator.ts b/Navigation/src/FluentNavigator.ts index f7ead7264..f64825ef6 100644 --- a/Navigation/src/FluentNavigator.ts +++ b/Navigation/src/FluentNavigator.ts @@ -47,10 +47,11 @@ function createFluentNavigator(states: { [index: string]: State }, stateHandler: return navigateLink(state, data, crumbs, url); }, navigateBack: function(distance: number): FluentNavigator { - if (!(distance <= stateContext.crumbs.length && distance > 0)) + var {crumbs} = stateContext; + if (!(distance <= crumbs.length && distance > 0)) throw new Error('The distance parameter must be greater than zero and less than or equal to the number of Crumbs (' + stateContext.crumbs.length + ')'); - var {state, data, url} = stateContext.crumbs[stateContext.crumbs.length - distance]; - var crumbs = stateContext.crumbs.slice(0, stateContext.crumbs.length - distance); + var {state, data, url} = crumbs[crumbs.length - distance]; + var crumbs = crumbs.slice(0, crumbs.length - distance); return navigateLink(state, data, crumbs, url); }, refresh: function(navigationData?: any): FluentNavigator { @@ -61,7 +62,7 @@ function createFluentNavigator(states: { [index: string]: State }, stateHandler: if (url == null) throw new Error('Invalid route data, a mandatory route parameter has not been supplied a value'); var data = { ...state.defaults, ...navigationData }; - var crumbs = getCrumbTrail(state, data, crumbs, nextCrumb); + var crumbs = getCrumbTrail(state, navigationData, crumbs, nextCrumb); return navigateLink(state, data, crumbs, url); } } From 8a217d4f1eed48643ecbdbbdc6c20cf9d29e3245 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Mon, 20 May 2019 10:18:15 +0100 Subject: [PATCH 06/26] Tested fluent refresh doesn't pass null data --- Navigation/src/FluentNavigator.ts | 2 +- Navigation/test/NavigationDataTest.ts | 60 +++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 1 deletion(-) diff --git a/Navigation/src/FluentNavigator.ts b/Navigation/src/FluentNavigator.ts index f64825ef6..e3aef2004 100644 --- a/Navigation/src/FluentNavigator.ts +++ b/Navigation/src/FluentNavigator.ts @@ -62,7 +62,7 @@ function createFluentNavigator(states: { [index: string]: State }, stateHandler: if (url == null) throw new Error('Invalid route data, a mandatory route parameter has not been supplied a value'); var data = { ...state.defaults, ...navigationData }; - var crumbs = getCrumbTrail(state, navigationData, crumbs, nextCrumb); + var crumbs = getCrumbTrail(state, data, crumbs, nextCrumb); return navigateLink(state, data, crumbs, url); } } diff --git a/Navigation/test/NavigationDataTest.ts b/Navigation/test/NavigationDataTest.ts index 5639fc011..5947d1d19 100644 --- a/Navigation/test/NavigationDataTest.ts +++ b/Navigation/test/NavigationDataTest.ts @@ -2192,6 +2192,66 @@ describe('Navigation Data', function () { } }); + describe('Refresh Individual Data Custom Trail', function() { + var stateNavigator: StateNavigator; + beforeEach(function() { + stateNavigator = new StateNavigator([ + { key: 's', route: 'r', trackCrumbTrail: true } + ]); + var state = stateNavigator.states['s']; + state.truncateCrumbTrail = (state, data, crumbs) => { + if (data['string'] === 'Hello' && data['boolean'] === true + && data['number'] === 0 && +data['date'] === +new Date(2010, 3, 7)) + return crumbs; + return []; + }; + }); + var individualNavigationData = {}; + individualNavigationData['string'] = 'Hello'; + individualNavigationData['boolean'] = true; + individualNavigationData['number'] = 0; + individualNavigationData['date'] = new Date(2010, 3, 7); + + describe('Navigate', function() { + beforeEach(function() { + stateNavigator.navigate('s'); + stateNavigator.refresh(); + stateNavigator.refresh(individualNavigationData); + }); + test(); + }); + + describe('Navigate Link', function() { + beforeEach(function() { + var link = stateNavigator.getNavigationLink('s'); + stateNavigator.navigateLink(link); + var link = stateNavigator.getRefreshLink(); + stateNavigator.navigateLink(link); + link = stateNavigator.getRefreshLink(individualNavigationData); + stateNavigator.navigateLink(link); + }); + test(); + }); + + describe('Fluent Navigate', function() { + beforeEach(function() { + var link = stateNavigator.fluent() + .navigate('s') + .refresh() + .refresh(individualNavigationData) + .url; + stateNavigator.navigateLink(link); + }); + test(); + }); + + function test() { + it('should populate crumb trail', function() { + assert.equal(stateNavigator.stateContext.crumbs.length, 1); + }); + } + }); + describe('Array Data Custom Trail', function() { var stateNavigator: StateNavigator; beforeEach(function() { From f3a98336af65367da2d2a13a7f5fdc25e9aa9c06 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Mon, 20 May 2019 10:44:01 +0100 Subject: [PATCH 07/26] Tested data plus defaults passed in fluent --- Navigation/test/NavigationDataTest.ts | 62 +++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/Navigation/test/NavigationDataTest.ts b/Navigation/test/NavigationDataTest.ts index 5947d1d19..3412eafc9 100644 --- a/Navigation/test/NavigationDataTest.ts +++ b/Navigation/test/NavigationDataTest.ts @@ -1866,6 +1866,68 @@ describe('Navigation Data', function () { } }); + describe('Wizard Data Defaults', function() { + var stateNavigator: StateNavigator; + beforeEach(function() { + stateNavigator = new StateNavigator([ + { key: 's0', route: 'r0' }, + { key: 's1', route: 'r1', trackCrumbTrail: true, defaults: { b: true } }, + { key: 's2', route: 'r2', trackCrumbTrail: true } + ]); + }); + var data = { + s: 'Hello', + n: 5 + }; + + describe('Navigate', function() { + beforeEach(function() { + stateNavigator.navigate('s0'); + stateNavigator.navigate('s1', data); + stateNavigator.navigate('s2', stateNavigator.stateContext.includeCurrentData(null)); + }); + test(); + }); + + describe('Navigate Link', function() { + beforeEach(function() { + var link = stateNavigator.getNavigationLink('s0'); + stateNavigator.navigateLink(link); + link = stateNavigator.getNavigationLink('s1', data); + stateNavigator.navigateLink(link); + link = stateNavigator.getNavigationLink('s2', stateNavigator.stateContext.includeCurrentData(null)); + stateNavigator.navigateLink(link); + }); + test(); + }); + + describe('Fluent Navigate', function() { + beforeEach(function() { + var link = stateNavigator.fluent() + .navigate('s0') + .navigate('s1', data) + .navigate('s2', currentData => currentData) + .url; + stateNavigator.navigateLink(link); + }); + test(); + }); + + function test() { + it('should populate data', function () { + assert.strictEqual(stateNavigator.stateContext.previousData['s'], 'Hello'); + assert.strictEqual(stateNavigator.stateContext.previousData['n'], 5); + assert.strictEqual(stateNavigator.stateContext.previousData['b'], true); + assert.strictEqual(stateNavigator.stateContext.crumbs[1].data['s'], 'Hello'); + assert.strictEqual(stateNavigator.stateContext.crumbs[1].data['n'], 5); + assert.strictEqual(stateNavigator.stateContext.crumbs[1].data['b'], true); + assert.strictEqual(stateNavigator.stateContext.data['s'], 'Hello'); + assert.strictEqual(stateNavigator.stateContext.data['n'], 5); + assert.strictEqual(stateNavigator.stateContext.data['b'], true); + }); + } + }); + describe('Transition Transition', function() { var stateNavigator: StateNavigator; beforeEach(function() { From f428c3a478f26c2b0b6330e186e0c903b3d1326c Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Mon, 20 May 2019 11:14:37 +0100 Subject: [PATCH 08/26] Tested data + defs passed in fluent refresh --- Navigation/test/NavigationDataTest.ts | 62 +++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/Navigation/test/NavigationDataTest.ts b/Navigation/test/NavigationDataTest.ts index 3412eafc9..50ee3139f 100644 --- a/Navigation/test/NavigationDataTest.ts +++ b/Navigation/test/NavigationDataTest.ts @@ -1928,6 +1928,68 @@ describe('Navigation Data', function () { } }); + describe.only('Wizard Refresh Data Defaults', function() { + var stateNavigator: StateNavigator; + beforeEach(function() { + stateNavigator = new StateNavigator([ + { key: 's0', route: 'r0' }, + { key: 's1', route: 'r1', trackCrumbTrail: true, defaults: { b: true } }, + { key: 's2', route: 'r2', trackCrumbTrail: true } + ]); + }); + var data = { + s: 'Hello', + n: 5 + }; + + describe('Navigate', function() { + beforeEach(function() { + stateNavigator.navigate('s1'); + stateNavigator.refresh(data); + stateNavigator.navigate('s2', stateNavigator.stateContext.includeCurrentData(null)); + }); + test(); + }); + + describe('Navigate Link', function() { + beforeEach(function() { + var link = stateNavigator.getNavigationLink('s1'); + stateNavigator.navigateLink(link); + link = stateNavigator.getRefreshLink(data); + stateNavigator.navigateLink(link); + link = stateNavigator.getNavigationLink('s2', stateNavigator.stateContext.includeCurrentData(null)); + stateNavigator.navigateLink(link); + }); + test(); + }); + + describe('Fluent Navigate', function() { + beforeEach(function() { + var link = stateNavigator.fluent() + .navigate('s1') + .refresh(data) + .navigate('s2', currentData => currentData) + .url; + stateNavigator.navigateLink(link); + }); + test(); + }); + + function test() { + it('should populate data', function () { + assert.strictEqual(stateNavigator.stateContext.previousData['s'], 'Hello'); + assert.strictEqual(stateNavigator.stateContext.previousData['n'], 5); + assert.strictEqual(stateNavigator.stateContext.previousData['b'], true); + assert.strictEqual(stateNavigator.stateContext.crumbs[1].data['s'], 'Hello'); + assert.strictEqual(stateNavigator.stateContext.crumbs[1].data['n'], 5); + assert.strictEqual(stateNavigator.stateContext.crumbs[1].data['b'], true); + assert.strictEqual(stateNavigator.stateContext.data['s'], 'Hello'); + assert.strictEqual(stateNavigator.stateContext.data['n'], 5); + assert.strictEqual(stateNavigator.stateContext.data['b'], true); + }); + } + }); + describe('Transition Transition', function() { var stateNavigator: StateNavigator; beforeEach(function() { From fcc1888fa4ac3eb404b62e169234f27641e48b22 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Mon, 20 May 2019 11:48:23 +0100 Subject: [PATCH 09/26] Removed accidentaly only --- Navigation/test/NavigationDataTest.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Navigation/test/NavigationDataTest.ts b/Navigation/test/NavigationDataTest.ts index 50ee3139f..cabc0969a 100644 --- a/Navigation/test/NavigationDataTest.ts +++ b/Navigation/test/NavigationDataTest.ts @@ -1928,7 +1928,7 @@ describe('Navigation Data', function () { } }); - describe.only('Wizard Refresh Data Defaults', function() { + describe('Wizard Refresh Data Defaults', function() { var stateNavigator: StateNavigator; beforeEach(function() { stateNavigator = new StateNavigator([ From 95f489af1a3aaf8e28c990fb1b7e8b3371743bdf Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Mon, 20 May 2019 11:55:38 +0100 Subject: [PATCH 10/26] Tested fluent refresh data overrides defaults --- Navigation/test/NavigationDataTest.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Navigation/test/NavigationDataTest.ts b/Navigation/test/NavigationDataTest.ts index cabc0969a..1a34501d7 100644 --- a/Navigation/test/NavigationDataTest.ts +++ b/Navigation/test/NavigationDataTest.ts @@ -5759,8 +5759,8 @@ describe('Navigation Data', function () { describe('Navigate', function() { beforeEach(function() { - stateNavigator.navigate('s0'); - stateNavigator.navigate('s1', data); + stateNavigator.navigate('s1'); + stateNavigator.refresh(data); stateNavigator.refresh(stateNavigator.stateContext.includeCurrentData(null)); }); test(); @@ -5768,9 +5768,9 @@ describe('Navigation Data', function () { describe('Navigate Link', function() { beforeEach(function() { - var link = stateNavigator.getNavigationLink('s0'); + var link = stateNavigator.getNavigationLink('s1'); stateNavigator.navigateLink(link); - link = stateNavigator.getNavigationLink('s1', data); + link = stateNavigator.getRefreshLink(data); stateNavigator.navigateLink(link); link = stateNavigator.getRefreshLink(stateNavigator.stateContext.includeCurrentData(null)); stateNavigator.navigateLink(link); @@ -5781,8 +5781,8 @@ describe('Navigation Data', function () { describe('Fluent Navigate', function() { beforeEach(function() { var link = stateNavigator.fluent() - .navigate('s0') - .navigate('s1', data) + .navigate('s1') + .refresh(data) .refresh((currentData) => currentData) .url; stateNavigator.navigateLink(link); From dcfcfd72f0b38d35a688403511f4a5feb5964ab8 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Mon, 20 May 2019 14:14:03 +0100 Subject: [PATCH 11/26] Added FluentLink component --- NavigationReact/src/FluentLink.tsx | 16 ++++++++++++++++ NavigationReact/src/NavigationReact.ts | 3 ++- NavigationReact/src/Props.ts | 8 +++++++- 3 files changed, 25 insertions(+), 2 deletions(-) create mode 100644 NavigationReact/src/FluentLink.tsx diff --git a/NavigationReact/src/FluentLink.tsx b/NavigationReact/src/FluentLink.tsx new file mode 100644 index 000000000..4f40a9a7e --- /dev/null +++ b/NavigationReact/src/FluentLink.tsx @@ -0,0 +1,16 @@ +import LinkUtility from './LinkUtility'; +import withStateNavigator from './withStateNavigator'; +import { FluentLinkProps } from './Props'; +import * as React from 'react'; + +var FluentLink = (props: FluentLinkProps) => { + var htmlProps = LinkUtility.toHtmlProps(props); + var { withContext, navigate, stateNavigator } = props; + try { + var link = navigate(stateNavigator.fluent(!!withContext)).url; + } catch {} + htmlProps.href = link && stateNavigator.historyManager.getHref(link); + htmlProps.onClick = link && LinkUtility.getOnClick(stateNavigator, props, link); + return ; +} +export default withStateNavigator(FluentLink); diff --git a/NavigationReact/src/NavigationReact.ts b/NavigationReact/src/NavigationReact.ts index a5e3386cb..d7fcd78f5 100644 --- a/NavigationReact/src/NavigationReact.ts +++ b/NavigationReact/src/NavigationReact.ts @@ -4,5 +4,6 @@ import NavigationHandler from './NavigationHandler'; import NavigationBackLink from './NavigationBackLink'; import NavigationLink from './NavigationLink'; import RefreshLink from './RefreshLink'; +import FluentLink from './FluentLink'; -export { AsyncStateNavigator, NavigationContext, NavigationHandler, NavigationBackLink, NavigationLink, RefreshLink }; +export { AsyncStateNavigator, NavigationContext, NavigationHandler, NavigationBackLink, NavigationLink, RefreshLink, FluentLink }; diff --git a/NavigationReact/src/Props.ts b/NavigationReact/src/Props.ts index 04192ef33..b937285d5 100644 --- a/NavigationReact/src/Props.ts +++ b/NavigationReact/src/Props.ts @@ -1,5 +1,6 @@ import AsyncStateNavigator from './AsyncStateNavigator'; import { AnchorHTMLAttributes, DetailedHTMLProps, MouseEvent } from 'react'; +import { FluentNavigator } from 'navigation'; interface LinkProps extends DetailedHTMLProps, HTMLAnchorElement> { historyAction?: 'add' | 'replace' | 'none'; @@ -25,4 +26,9 @@ interface NavigationBackLinkProps extends LinkProps { distance: number; } -export { LinkProps, RefreshLinkProps, NavigationLinkProps, NavigationBackLinkProps } +interface FluentLinkProps extends LinkProps { + withContext?: boolean; + navigate: (fluentNavigator: FluentNavigator) => FluentNavigator; +} + +export { LinkProps, RefreshLinkProps, NavigationLinkProps, NavigationBackLinkProps, FluentLinkProps } From 0ef66765f8845d1d82eee7ab699e2a7e3f4c4a35 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Mon, 20 May 2019 14:34:38 +0100 Subject: [PATCH 12/26] Added first FluentLink component test --- NavigationReact/test/FluentLinkTest.tsx | 41 +++++++++++++++++++ .../node_modules/@types/navigation-react.d.ts | 22 +++++++++- gulpfile.js | 3 +- 3 files changed, 63 insertions(+), 3 deletions(-) create mode 100644 NavigationReact/test/FluentLinkTest.tsx diff --git a/NavigationReact/test/FluentLinkTest.tsx b/NavigationReact/test/FluentLinkTest.tsx new file mode 100644 index 000000000..d5a98dc09 --- /dev/null +++ b/NavigationReact/test/FluentLinkTest.tsx @@ -0,0 +1,41 @@ +import * as assert from 'assert'; +import * as mocha from 'mocha'; +import { StateNavigator } from 'navigation'; +import { FluentLink, NavigationHandler, NavigationContext } from 'navigation-react'; +import * as React from 'react'; +import * as ReactDOM from 'react-dom'; +import { Simulate } from 'react-dom/test-utils'; +import { JSDOM } from 'jsdom'; + +declare var global: any; +var { window } = new JSDOM(''); +window.addEventListener = () => {}; +global.window = window; +global.document = window.document; + +describe('FluentLinkTest', function () { + describe('Fluent Link', function () { + it('should render', function(){ + var stateNavigator = new StateNavigator([ + { key: 's0', route: 'r0' }, + { key: 's1', route: 'r1', trackCrumbTrail: true } + ]); + var container = document.createElement('div'); + ReactDOM.render( + + ( + fluentNavigator + .navigate('s0') + .navigate('s1') + )}> + link text + + , + container + ); + var link = container.querySelector('a'); + assert.equal(link.hash, '#/r1?crumb=%2Fr0'); + assert.equal(link.innerHTML, 'link text'); + }) + }); +}); diff --git a/NavigationReact/test/node_modules/@types/navigation-react.d.ts b/NavigationReact/test/node_modules/@types/navigation-react.d.ts index 2e6216495..ce39a8dc2 100644 --- a/NavigationReact/test/node_modules/@types/navigation-react.d.ts +++ b/NavigationReact/test/node_modules/@types/navigation-react.d.ts @@ -1,4 +1,4 @@ -import { State, StateNavigator, StateContext } from 'navigation'; +import { State, StateNavigator, StateContext, FluentNavigator } from 'navigation'; import { Component, Context, AnchorHTMLAttributes, DetailedHTMLProps, MouseEvent } from 'react'; /** @@ -163,7 +163,7 @@ export class NavigationLink extends Component { } /** * Defines the Navigation Back Link Props contract */ -export interface NavigationBackLinkProps extends RefreshLinkProps { +export interface NavigationBackLinkProps extends LinkProps { /** * Starting at 1, The number of Crumb steps to go back */ @@ -175,3 +175,21 @@ export interface NavigationBackLinkProps extends RefreshLinkProps { */ export class NavigationBackLink extends Component { } +/** + * Defines the Fluent Link Props contract + */ +export interface FluentLinkProps extends LinkProps { + /** + * Indicates whether to inherit the current context + */ + withContext?: boolean; + /** + * The function that fluently navigates to a State + */ + navigate: (fluentNavigator: FluentNavigator) => FluentNavigator; +} + +/** + * Hyperlink Component that fluently navigates to a State + */ +export class FluentLink extends Component { } diff --git a/gulpfile.js b/gulpfile.js index 0bac9480a..092f65f81 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -99,7 +99,8 @@ var tests = [ { name: 'FluentNavigation', to: 'fluentNavigation.test.js' }, { name: 'NavigationLink', to: 'navigationLink.test.js', folder: 'React', ext: 'tsx' }, { name: 'NavigationBackLink', to: 'navigationBackLink.test.js', folder: 'React', ext: 'tsx' }, - { name: 'RefreshLink', to: 'refreshLink.test.js', folder: 'React', ext: 'tsx' } + { name: 'RefreshLink', to: 'refreshLink.test.js', folder: 'React', ext: 'tsx' }, + { name: 'FluentLink', to: 'fluentLink.test.js', folder: 'React', ext: 'tsx' } ]; function testTask(name, input, file) { var globals = [ From d24cc49516d0f8673f7471e7161a1c809e49ef67 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Mon, 20 May 2019 14:34:49 +0100 Subject: [PATCH 13/26] Excluded navigate prop from rendered output --- NavigationReact/src/LinkUtility.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NavigationReact/src/LinkUtility.ts b/NavigationReact/src/LinkUtility.ts index e749ad7da..eb92d906d 100644 --- a/NavigationReact/src/LinkUtility.ts +++ b/NavigationReact/src/LinkUtility.ts @@ -53,7 +53,7 @@ class LinkUtility { if (key !== 'stateNavigator' && key !== 'stateKey' && key !== 'navigationData' && key !== 'includeCurrentData' && key !== 'currentDataKeys'&& key !== 'activeStyle' && key !== 'activeCssClass' && key !== 'disableActive' && key !== 'distance' - && key !== 'historyAction' && key !== 'navigating' && key !== 'defer') + && key !== 'historyAction' && key !== 'navigating' && key !== 'navigate' && key !== 'defer') htmlProps[key] = props[key]; } return htmlProps; From f8154933e06ee120fa28aa216f633880d65e2fee Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Mon, 20 May 2019 14:35:29 +0100 Subject: [PATCH 14/26] Fixed typos --- .../test/node_modules/@types/navigation-react.d.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/NavigationReact/test/node_modules/@types/navigation-react.d.ts b/NavigationReact/test/node_modules/@types/navigation-react.d.ts index ce39a8dc2..403e4d1f8 100644 --- a/NavigationReact/test/node_modules/@types/navigation-react.d.ts +++ b/NavigationReact/test/node_modules/@types/navigation-react.d.ts @@ -141,7 +141,7 @@ export interface RefreshLinkProps extends LinkProps { } /** - * Hyperlink Component the navigates to the current State + * Hyperlink Component that navigates to the current State */ export class RefreshLink extends Component { } @@ -156,7 +156,7 @@ export interface NavigationLinkProps extends RefreshLinkProps { } /** - * Hyperlink Component the navigates to a State + * Hyperlink Component that navigates to a State */ export class NavigationLink extends Component { } @@ -171,7 +171,7 @@ export interface NavigationBackLinkProps extends LinkProps { } /** - * Hyperlink Component the navigates back along the crumb trail + * Hyperlink Component that navigates back along the crumb trail */ export class NavigationBackLink extends Component { } From 51d14860bd825a487d56a58e06305ed0d2fa42c3 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Mon, 20 May 2019 14:44:35 +0100 Subject: [PATCH 15/26] Tested fluent link renders without navigator --- NavigationReact/test/FluentLinkTest.tsx | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/NavigationReact/test/FluentLinkTest.tsx b/NavigationReact/test/FluentLinkTest.tsx index d5a98dc09..740f625a9 100644 --- a/NavigationReact/test/FluentLinkTest.tsx +++ b/NavigationReact/test/FluentLinkTest.tsx @@ -38,4 +38,21 @@ describe('FluentLinkTest', function () { assert.equal(link.innerHTML, 'link text'); }) }); + + describe('Without State Navigator Fluent Link', function () { + it('should render', function(){ + var container = document.createElement('div'); + ReactDOM.render( + ( + fluentNavigator.navigate('s') + )}> + link text + , + container + ); + var link = container.querySelector('a'); + assert.equal(link.hash, ''); + assert.equal(link.innerHTML, 'link text'); + }) + }); }); From ab203ed980aacaf095ae43baaa8be2aacde5975b Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Mon, 20 May 2019 14:46:48 +0100 Subject: [PATCH 16/26] Tested inavlid fluent navigation --- NavigationReact/test/FluentLinkTest.tsx | 27 +++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/NavigationReact/test/FluentLinkTest.tsx b/NavigationReact/test/FluentLinkTest.tsx index 740f625a9..956beca1c 100644 --- a/NavigationReact/test/FluentLinkTest.tsx +++ b/NavigationReact/test/FluentLinkTest.tsx @@ -55,4 +55,31 @@ describe('FluentLinkTest', function () { assert.equal(link.innerHTML, 'link text'); }) }); + + describe('Invalid Fluent Link', function () { + it('should render', function(){ + var stateNavigator = new StateNavigator([ + { key: 's0', route: 'r0' }, + { key: 's1', route: 'r1', trackCrumbTrail: true } + ]); + stateNavigator.navigate('s0'); + stateNavigator.navigate('s1'); + var container = document.createElement('div'); + ReactDOM.render( + + ( + fluentNavigator + .navigate('s0') + .navigate('x') + )}> + link text + + , + container + ); + var link = container.querySelector('a'); + assert.equal(link.hash, ''); + assert.equal(link.innerHTML, 'link text'); + }) + }); }); From 0f7d1f151d18a6f8264f061b047a1f663f5aaf8e Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Mon, 20 May 2019 14:53:13 +0100 Subject: [PATCH 17/26] Tested fluent link renders attributes --- NavigationReact/test/FluentLinkTest.tsx | 39 ++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/NavigationReact/test/FluentLinkTest.tsx b/NavigationReact/test/FluentLinkTest.tsx index 956beca1c..d21b1ca03 100644 --- a/NavigationReact/test/FluentLinkTest.tsx +++ b/NavigationReact/test/FluentLinkTest.tsx @@ -62,15 +62,13 @@ describe('FluentLinkTest', function () { { key: 's0', route: 'r0' }, { key: 's1', route: 'r1', trackCrumbTrail: true } ]); - stateNavigator.navigate('s0'); - stateNavigator.navigate('s1'); var container = document.createElement('div'); ReactDOM.render( ( fluentNavigator - .navigate('s0') - .navigate('x') + .navigate('s0') + .navigate('x') )}> link text @@ -82,4 +80,37 @@ describe('FluentLinkTest', function () { assert.equal(link.innerHTML, 'link text'); }) }); + + describe('Attributes Fluent Link', function () { + it('should render', function(){ + var stateNavigator = new StateNavigator([ + { key: 's0', route: 'r0' }, + { key: 's1', route: 'r1', trackCrumbTrail: true } + ]); + var container = document.createElement('div'); + ReactDOM.render( + + ( + fluentNavigator + .navigate('s0') + .navigate('s1') + )} + historyAction='replace' + navigating={() => false} + aria-label="z" + target="_blank"> + link text + + , + container + ); + var link = container.querySelector('a'); + assert.equal(link.hash, '#/r1?crumb=%2Fr0'); + assert.equal(link.innerHTML, 'link text'); + assert.equal(link.getAttribute('aria-label'), 'z'); + assert.equal(link.target, '_blank'); + assert.equal(link.attributes.length, 3); + }) + }); }); From 5e9699a9481e583ec1ecb30018f21107d28892a0 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Mon, 20 May 2019 15:08:37 +0100 Subject: [PATCH 18/26] Copied back link tests to fluen link tests --- NavigationReact/src/LinkUtility.ts | 3 +- NavigationReact/test/FluentLinkTest.tsx | 379 ++++++++++++++++++++++++ 2 files changed, 381 insertions(+), 1 deletion(-) diff --git a/NavigationReact/src/LinkUtility.ts b/NavigationReact/src/LinkUtility.ts index eb92d906d..be5375dc1 100644 --- a/NavigationReact/src/LinkUtility.ts +++ b/NavigationReact/src/LinkUtility.ts @@ -53,7 +53,8 @@ class LinkUtility { if (key !== 'stateNavigator' && key !== 'stateKey' && key !== 'navigationData' && key !== 'includeCurrentData' && key !== 'currentDataKeys'&& key !== 'activeStyle' && key !== 'activeCssClass' && key !== 'disableActive' && key !== 'distance' - && key !== 'historyAction' && key !== 'navigating' && key !== 'navigate' && key !== 'defer') + && key !== 'historyAction' && key !== 'navigating' && key !== 'navigate' + && key !== 'withContext' && key !== 'defer') htmlProps[key] = props[key]; } return htmlProps; diff --git a/NavigationReact/test/FluentLinkTest.tsx b/NavigationReact/test/FluentLinkTest.tsx index d21b1ca03..51ce8eab3 100644 --- a/NavigationReact/test/FluentLinkTest.tsx +++ b/NavigationReact/test/FluentLinkTest.tsx @@ -113,4 +113,383 @@ describe('FluentLinkTest', function () { assert.equal(link.attributes.length, 3); }) }); + + describe('Click Fluent Link', function () { + it('should navigate', function(){ + var stateNavigator = new StateNavigator([ + { key: 's0', route: 'r0' }, + { key: 's1', route: 'r1', trackCrumbTrail: true } + ]); + var container = document.createElement('div'); + ReactDOM.render( + + ( + fluentNavigator + .navigate('s0') + .navigate('s1') + )}> + link text + + , + container + ); + var link = container.querySelector('a'); + Simulate.click(link); + assert.equal(stateNavigator.stateContext.previousState, stateNavigator.states['s0']); + assert.equal(stateNavigator.stateContext.state, stateNavigator.states['s1']); + }) + }); + + describe('Ctrl + Click Fluent Link', function () { + it('should not navigate', function(){ + var stateNavigator = new StateNavigator([ + { key: 's0', route: 'r0' }, + { key: 's1', route: 'r1', trackCrumbTrail: true } + ]); + var container = document.createElement('div'); + ReactDOM.render( + + ( + fluentNavigator + .navigate('s0') + .navigate('s1') + )}> + link text + + , + container + ); + var link = container.querySelector('a'); + Simulate.click(link, { ctrlKey: true }); + assert.equal(stateNavigator.stateContext.state, null); + }) + }); + + describe('Shift + Click Fluent Link', function () { + it('should not navigate', function(){ + var stateNavigator = new StateNavigator([ + { key: 's0', route: 'r0' }, + { key: 's1', route: 'r1', trackCrumbTrail: true } + ]); + var container = document.createElement('div'); + ReactDOM.render( + + ( + fluentNavigator + .navigate('s0') + .navigate('s1') + )}> + link text + + , + container + ); + var link = container.querySelector('a'); + Simulate.click(link, { shiftKey: true }); + assert.equal(stateNavigator.stateContext.state, null); + }) + }); + + describe('Meta + Click Fluent Link', function () { + it('should not navigate', function(){ + var stateNavigator = new StateNavigator([ + { key: 's0', route: 'r0' }, + { key: 's1', route: 'r1', trackCrumbTrail: true } + ]); + var container = document.createElement('div'); + ReactDOM.render( + + ( + fluentNavigator + .navigate('s0') + .navigate('s1') + )}> + link text + + , + container + ); + var link = container.querySelector('a'); + Simulate.click(link, { metaKey: true }); + assert.equal(stateNavigator.stateContext.state, null); + }) + }); + + describe('Alt + Click Fluent Link', function () { + it('should not navigate', function(){ + var stateNavigator = new StateNavigator([ + { key: 's0', route: 'r0' }, + { key: 's1', route: 'r1', trackCrumbTrail: true } + ]); + var container = document.createElement('div'); + ReactDOM.render( + + ( + fluentNavigator + .navigate('s0') + .navigate('s1') + )}> + link text + + , + container + ); + var link = container.querySelector('a'); + Simulate.click(link, { altKey: true }); + assert.equal(stateNavigator.stateContext.state, null); + }) + }); + + describe('Button + Click Fluent Link', function () { + it('should not navigate', function(){ + var stateNavigator = new StateNavigator([ + { key: 's0', route: 'r0' }, + { key: 's1', route: 'r1', trackCrumbTrail: true } + ]); + var container = document.createElement('div'); + ReactDOM.render( + + ( + fluentNavigator + .navigate('s0') + .navigate('s1') + )}> + link text + + , + container + ); + var link = container.querySelector('a'); + Simulate.click(link, { button: 1 }); + assert.equal(stateNavigator.stateContext.state, null); + }) + }); + + describe('Navigating Click Fluent Link', function () { + it('should navigate', function(){ + var stateNavigator = new StateNavigator([ + { key: 's0', route: 'r0' }, + { key: 's1', route: 'r1', trackCrumbTrail: true } + ]); + var container = document.createElement('div'); + ReactDOM.render( + + ( + fluentNavigator + .navigate('s0') + .navigate('s1') + )} + navigating={() => true}> + link text + + , + container + ); + var link = container.querySelector('a'); + Simulate.click(link); + assert.equal(stateNavigator.stateContext.previousState, stateNavigator.states['s0']); + assert.equal(stateNavigator.stateContext.state, stateNavigator.states['s1']); + }) + }); + + describe('Not Navigating Click Fluent Link', function () { + it('should not navigate', function(){ + var stateNavigator = new StateNavigator([ + { key: 's0', route: 'r0' }, + { key: 's1', route: 'r1', trackCrumbTrail: true } + ]); + var container = document.createElement('div'); + ReactDOM.render( + + ( + fluentNavigator + .navigate('s0') + .navigate('s1') + )} + navigating={() => false}> + link text + + , + container + ); + var link = container.querySelector('a'); + Simulate.click(link); + assert.equal(stateNavigator.stateContext.state, null); + }) + }); + + describe('Navigating Params Click Fluent Link', function () { + it('should navigate', function(){ + var stateNavigator = new StateNavigator([ + { key: 's0', route: 'r0' }, + { key: 's1', route: 'r1', trackCrumbTrail: true } + ]); + var navigatingEvt, navigatingLink; + var container = document.createElement('div'); + ReactDOM.render( + + ( + fluentNavigator + .navigate('s0') + .navigate('s1') + )} + navigating={(e, link) => { + navigatingEvt = e; + navigatingLink = link; + return true; + }}> + link text + + , + container + ); + var link = container.querySelector('a'); + Simulate.click(link, { clientX: 224 }); + assert.strictEqual(navigatingEvt.clientX, 224); + assert.equal(navigatingLink, '/r1?crumb=%2Fr0'); + }) + }); + + describe('History Click Fluent Link', function () { + it('should navigate', function(){ + var stateNavigator = new StateNavigator([ + { key: 's0', route: 'r0' }, + { key: 's1', route: 'r1', trackCrumbTrail: true } + ]); + var container = document.createElement('div'); + ReactDOM.render( + + ( + fluentNavigator + .navigate('s0') + .navigate('s1') + )}> + link text + + , + container + ); + var link = container.querySelector('a'); + var addHistory; + stateNavigator.historyManager.addHistory = (url, replace) => { addHistory = !replace }; + Simulate.click(link); + assert.strictEqual(addHistory, true); + }) + }); + + describe('Replace History Click Fluent Link', function () { + it('should navigate', function(){ + var stateNavigator = new StateNavigator([ + { key: 's0', route: 'r0' }, + { key: 's1', route: 'r1', trackCrumbTrail: true } + ]); + var container = document.createElement('div'); + ReactDOM.render( + + ( + fluentNavigator + .navigate('s0') + .navigate('s1') + )} + historyAction="replace"> + link text + + , + container + ); + var link = container.querySelector('a'); + var replaceHistory; + stateNavigator.historyManager.addHistory = (url, replace) => { replaceHistory = replace }; + Simulate.click(link); + assert.strictEqual(replaceHistory, true); + }) + }); + + describe('None History Click Fluent Link', function () { + it('should navigate', function(){ + var stateNavigator = new StateNavigator([ + { key: 's0', route: 'r0' }, + { key: 's1', route: 'r1', trackCrumbTrail: true } + ]); + var container = document.createElement('div'); + ReactDOM.render( + + ( + fluentNavigator + .navigate('s0') + .navigate('s1') + )} + historyAction="none"> + link text + + , + container + ); + var link = container.querySelector('a'); + var noneHistory = true; + stateNavigator.historyManager.addHistory = () => { noneHistory = false }; + Simulate.click(link); + assert.strictEqual(noneHistory, true); + }) + }); + + describe('Crumb Trail Navigate Fluent Link', function () { + it('should update', function(){ + var stateNavigator = new StateNavigator([ + { key: 's0', route: 'r0' }, + { key: 's1', route: 'r1', trackCrumbTrail: true } + ]); + var container = document.createElement('div'); + ReactDOM.render( + + ( + fluentNavigator + .navigate('s1') + )}> + link text + + , + container + ); + var link = container.querySelector('a'); + assert.equal(link.hash, '#/r1'); + stateNavigator.navigate('s0'); + assert.equal(link.hash, '#/r1?crumb=%2Fr0'); + }) + }); + + describe('Click Custom Href Fluent Link', function () { + it('should navigate', function(){ + var stateNavigator = new StateNavigator([ + { key: 's0', route: 'r0' }, + { key: 's1', route: 'r1', trackCrumbTrail: true } + ]); + stateNavigator.historyManager.getHref = () => '#/hello/world'; + var container = document.createElement('div'); + ReactDOM.render( + + ( + fluentNavigator + .navigate('s0') + .navigate('s1') + )}> + link text + + , + container + ); + var link = container.querySelector('a'); + assert.equal(link.hash, '#/hello/world'); + Simulate.click(link); + assert.equal(stateNavigator.stateContext.previousState, stateNavigator.states['s0']); + assert.equal(stateNavigator.stateContext.state, stateNavigator.states['s1']); + }) + }); }); From 9ec267f1ea6ce7b4d61a3c8bcb78dba4b3f4a159 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Mon, 20 May 2019 15:31:29 +0100 Subject: [PATCH 19/26] Tested fluent link with consumer --- NavigationReact/test/FluentLinkTest.tsx | 38 +++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/NavigationReact/test/FluentLinkTest.tsx b/NavigationReact/test/FluentLinkTest.tsx index 51ce8eab3..d0bf47905 100644 --- a/NavigationReact/test/FluentLinkTest.tsx +++ b/NavigationReact/test/FluentLinkTest.tsx @@ -492,4 +492,42 @@ describe('FluentLinkTest', function () { assert.equal(stateNavigator.stateContext.state, stateNavigator.states['s1']); }) }); + + describe('Consumer Fluent Link', function () { + it('should update', function(){ + var stateNavigator = new StateNavigator([ + { key: 's0', route: 'r0' }, + { key: 's1', route: 'r1', trackCrumbTrail: true }, + { key: 's2', route: 'r2', trackCrumbTrail: true } + ]); + var {s0, s1, s2} = stateNavigator.states; + s0.renderView = () => ( + ( + fluentNavigator + .navigate('s1') + .navigate('s2', {hello: 'world'}) + )}> + link text + + ); + s1.renderView = () =>

s1

+ s2.renderView = ({hello}) =>

{hello}

+ stateNavigator.navigate('s0'); + var container = document.createElement('div'); + ReactDOM.render( + + + {({state, data}) => state.renderView(data)} + + , + container + ); + var link = container.querySelector('a'); + Simulate.click(link); + var header = container.querySelector('h1'); + assert.equal(header.innerHTML, 'world'); + }) + }); }); From 35c8061066721c181ba3c6025e8a5e7e72570036 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Mon, 20 May 2019 15:36:17 +0100 Subject: [PATCH 20/26] Tested on before cancel fluent link --- NavigationReact/test/FluentLinkTest.tsx | 33 +++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/NavigationReact/test/FluentLinkTest.tsx b/NavigationReact/test/FluentLinkTest.tsx index d0bf47905..7e5dede0d 100644 --- a/NavigationReact/test/FluentLinkTest.tsx +++ b/NavigationReact/test/FluentLinkTest.tsx @@ -530,4 +530,37 @@ describe('FluentLinkTest', function () { assert.equal(header.innerHTML, 'world'); }) }); + + describe('On Before Cancel Fluent Link', function () { + it('should not navigate', function(){ + var stateNavigator = new StateNavigator([ + { key: 's0', route: 'r0' }, + { key: 's1', route: 'r1', trackCrumbTrail: true }, + ]); + stateNavigator.navigate('s0'); + var container = document.createElement('div'); + ReactDOM.render( + + ( + fluentNavigator + .navigate('s1') + .navigate('s1') + )}> + link text + + , + container + ); + var link = container.querySelectorAll('a')[0]; + Simulate.click(link); + assert.equal(stateNavigator.stateContext.crumbs.length, 2); + assert.equal(stateNavigator.stateContext.state.key, 's1'); + stateNavigator.onBeforeNavigate(() => false); + Simulate.click(link); + assert.equal(stateNavigator.stateContext.crumbs.length, 2); + assert.equal(stateNavigator.stateContext.state.key, 's1'); + }) + }); }); From fd01429c43c2e7b9c0a9bc549a72d80cffe2f54f Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Mon, 20 May 2019 15:39:43 +0100 Subject: [PATCH 21/26] Tested cancelling fluent navigation --- NavigationReact/test/FluentLinkTest.tsx | 31 +++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/NavigationReact/test/FluentLinkTest.tsx b/NavigationReact/test/FluentLinkTest.tsx index 7e5dede0d..5a9e1732f 100644 --- a/NavigationReact/test/FluentLinkTest.tsx +++ b/NavigationReact/test/FluentLinkTest.tsx @@ -563,4 +563,35 @@ describe('FluentLinkTest', function () { assert.equal(stateNavigator.stateContext.state.key, 's1'); }) }); + + describe('On Before Component Cancel Fluent Navigation', function () { + it('should not navigate', function(){ + var stateNavigator = new StateNavigator([ + { key: 's0', route: 'r0' }, + { key: 's1', route: 'r1', trackCrumbTrail: true } + ]); + class Blocker extends React.Component<{ stateNavigator: StateNavigator }> { + componentDidMount() { + this.props.stateNavigator.onBeforeNavigate(() => false); + } + render() { + return null; + } + } + stateNavigator.navigate('s0'); + var container = document.createElement('div'); + ReactDOM.render( + + + {({ stateNavigator }) => } + + , + container + ); + assert.equal(stateNavigator.stateContext.state.key, 's0'); + var link = stateNavigator.fluent(true).navigate('s1').url; + stateNavigator.navigateLink(link); + assert.equal(stateNavigator.stateContext.state.key, 's0'); + }) + }); }); From a332d6c7384142644fa8f62742355df04099bc1f Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Mon, 20 May 2019 15:46:59 +0100 Subject: [PATCH 22/26] Tested click fluent navigate --- NavigationReact/test/FluentLinkTest.tsx | 27 +++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/NavigationReact/test/FluentLinkTest.tsx b/NavigationReact/test/FluentLinkTest.tsx index 5a9e1732f..75e29fa27 100644 --- a/NavigationReact/test/FluentLinkTest.tsx +++ b/NavigationReact/test/FluentLinkTest.tsx @@ -594,4 +594,31 @@ describe('FluentLinkTest', function () { assert.equal(stateNavigator.stateContext.state.key, 's0'); }) }); + + describe('Click Fluent Navigate', function () { + it('should navigate', function(){ + var stateNavigator = new StateNavigator([ + { key: 's0', route: 'r0' }, + { key: 's1', route: 'r1', trackCrumbTrail: true } + ]); + var container = document.createElement('div'); + ReactDOM.render( + + + {({stateNavigator}) => ( +
stateNavigator.navigateLink(stateNavigator.fluent() + .navigate('s0') + .navigate('s1').url) + } /> + )} + + , + container + ); + var div = container.querySelector('div'); + Simulate.click(div); + assert.equal(stateNavigator.stateContext.previousState, stateNavigator.states['s0']); + assert.equal(stateNavigator.stateContext.state, stateNavigator.states['s1']); + }) + }); }); From 3be162865720db00618897d7dd87518e9f3f9b99 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Mon, 20 May 2019 15:51:08 +0100 Subject: [PATCH 23/26] Tested fluent link with context --- NavigationReact/test/FluentLinkTest.tsx | 29 +++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/NavigationReact/test/FluentLinkTest.tsx b/NavigationReact/test/FluentLinkTest.tsx index 75e29fa27..cb3f7b2c3 100644 --- a/NavigationReact/test/FluentLinkTest.tsx +++ b/NavigationReact/test/FluentLinkTest.tsx @@ -114,6 +114,35 @@ describe('FluentLinkTest', function () { }) }); + describe('With Context Fluent Link', function () { + it('should render', function(){ + var stateNavigator = new StateNavigator([ + { key: 's0', route: 'r0' }, + { key: 's1', route: 'r1', trackCrumbTrail: true }, + { key: 's2', route: 'r2', trackCrumbTrail: true } + ]); + stateNavigator.navigate('s0'); + var container = document.createElement('div'); + ReactDOM.render( + + ( + fluentNavigator + .navigate('s1') + .navigate('s2') + )}> + link text + + , + container + ); + var link = container.querySelector('a'); + assert.equal(link.hash, '#/r2?crumb=%2Fr0&crumb=%2Fr1'); + assert.equal(link.innerHTML, 'link text'); + }) + }); + describe('Click Fluent Link', function () { it('should navigate', function(){ var stateNavigator = new StateNavigator([ From e891c0667c5d92be8a006b70bf46fd6a71b7b4e6 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Mon, 20 May 2019 15:51:48 +0100 Subject: [PATCH 24/26] Tested fluent link wihtout context --- NavigationReact/test/FluentLinkTest.tsx | 29 +++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/NavigationReact/test/FluentLinkTest.tsx b/NavigationReact/test/FluentLinkTest.tsx index cb3f7b2c3..b660f3659 100644 --- a/NavigationReact/test/FluentLinkTest.tsx +++ b/NavigationReact/test/FluentLinkTest.tsx @@ -143,6 +143,35 @@ describe('FluentLinkTest', function () { }) }); + describe('Without Context Fluent Link', function () { + it('should render', function(){ + var stateNavigator = new StateNavigator([ + { key: 's0', route: 'r0' }, + { key: 's1', route: 'r1', trackCrumbTrail: true }, + { key: 's2', route: 'r2', trackCrumbTrail: true } + ]); + stateNavigator.navigate('s0'); + var container = document.createElement('div'); + ReactDOM.render( + + ( + fluentNavigator + .navigate('s1') + .navigate('s2') + )}> + link text + + , + container + ); + var link = container.querySelector('a'); + assert.equal(link.hash, '#/r2?crumb=%2Fr1'); + assert.equal(link.innerHTML, 'link text'); + }) + }); + describe('Click Fluent Link', function () { it('should navigate', function(){ var stateNavigator = new StateNavigator([ From 863faf8287453b429795306db4b1b41ab59580e7 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Mon, 20 May 2019 16:09:05 +0100 Subject: [PATCH 25/26] Copied fluent link typings into npm package --- .../navigation-react/navigation.react.d.ts | 28 +++++++++++++++---- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/build/npm/navigation-react/navigation.react.d.ts b/build/npm/navigation-react/navigation.react.d.ts index 082dc9be2..4a0cc6906 100644 --- a/build/npm/navigation-react/navigation.react.d.ts +++ b/build/npm/navigation-react/navigation.react.d.ts @@ -1,4 +1,4 @@ -import { State, StateNavigator } from 'navigation'; +import { State, StateNavigator, FluentNavigator } from 'navigation'; import { Component, Context, AnchorHTMLAttributes, DetailedHTMLProps, MouseEvent } from 'react'; /** @@ -82,7 +82,7 @@ export interface RefreshLinkProps extends LinkProps { } /** - * Hyperlink Component the navigates to the current State + * Hyperlink Component that navigates to the current State */ export class RefreshLink extends Component { } @@ -97,14 +97,14 @@ export interface NavigationLinkProps extends RefreshLinkProps { } /** - * Hyperlink Component the navigates to a State + * Hyperlink Component that navigates to a State */ export class NavigationLink extends Component { } /** * Defines the Navigation Back Link Props contract */ -export interface NavigationBackLinkProps extends RefreshLinkProps { +export interface NavigationBackLinkProps extends LinkProps { /** * Starting at 1, The number of Crumb steps to go back */ @@ -112,7 +112,25 @@ export interface NavigationBackLinkProps extends RefreshLinkProps { } /** - * Hyperlink Component the navigates back along the crumb trail + * Hyperlink Component that navigates back along the crumb trail */ export class NavigationBackLink extends Component { } +/** + * Defines the Fluent Link Props contract + */ +export interface FluentLinkProps extends LinkProps { + /** + * Indicates whether to inherit the current context + */ + withContext?: boolean; + /** + * The function that fluently navigates to a State + */ + navigate: (fluentNavigator: FluentNavigator) => FluentNavigator; +} + +/** + * Hyperlink Component that fluently navigates to a State + */ +export class FluentLink extends Component { } From 7a86fdc1ac63581e5353a99dd10735e222bd0125 Mon Sep 17 00:00:00 2001 From: Graham Mendick Date: Mon, 20 May 2019 16:11:30 +0100 Subject: [PATCH 26/26] Defaulted withContext in destructure --- NavigationReact/src/FluentLink.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/NavigationReact/src/FluentLink.tsx b/NavigationReact/src/FluentLink.tsx index 4f40a9a7e..9224cb4ca 100644 --- a/NavigationReact/src/FluentLink.tsx +++ b/NavigationReact/src/FluentLink.tsx @@ -5,9 +5,9 @@ import * as React from 'react'; var FluentLink = (props: FluentLinkProps) => { var htmlProps = LinkUtility.toHtmlProps(props); - var { withContext, navigate, stateNavigator } = props; + var { withContext = false, navigate, stateNavigator } = props; try { - var link = navigate(stateNavigator.fluent(!!withContext)).url; + var link = navigate(stateNavigator.fluent(withContext)).url; } catch {} htmlProps.href = link && stateNavigator.historyManager.getHref(link); htmlProps.onClick = link && LinkUtility.getOnClick(stateNavigator, props, link);