From 8f533b3f2af5fd9fcb1d3039bf0d788a9c27d762 Mon Sep 17 00:00:00 2001 From: Markus Edenhauser <1720843+maxmarkus@users.noreply.github.com> Date: Fri, 24 Jan 2020 12:22:25 +0100 Subject: [PATCH 1/7] working half way, wrong viewUrl and non-updating on navigation --- core/src/navigation/services/navigation.js | 80 ++++++++++++++++++++-- 1 file changed, 76 insertions(+), 4 deletions(-) diff --git a/core/src/navigation/services/navigation.js b/core/src/navigation/services/navigation.js index c8669e6a08..9cbb969ad2 100644 --- a/core/src/navigation/services/navigation.js +++ b/core/src/navigation/services/navigation.js @@ -61,7 +61,7 @@ class NavigationClass { this.rootNode = { children: topNavNodes }; } - await this.getChildren(this.rootNode); // keep it, mutates and filters children + await this.getChildren(this.rootNode, null, activePath); // keep it, mutates and filters children } const nodeNamesInCurrentPath = activePath.split('/'); @@ -171,7 +171,8 @@ class NavigationClass { nodesInCurrentPath, childrenOfCurrentNode, context, - pathParams = {} + pathParams = {}, + virtualPathNames ) { if (!context.parentNavigationContexts) { context.parentNavigationContexts = []; @@ -205,14 +206,22 @@ class NavigationClass { pathParams ); try { - let children = await this.getChildren(node, newContext); + /** + * If its a virtual tree, + * build static children + */ + virtualPathNames = this.buildVirtualTree(node, nodeNamesInCurrentPath, virtualPathNames); + + // STANDARD PROCEDURE + let children = await this.getChildren(node, newContext, nodeNamesInCurrentPath); const newNodeNamesInCurrentPath = nodeNamesInCurrentPath.slice(1); result = this.buildNode( newNodeNamesInCurrentPath, nodesInCurrentPath, children, newContext, - pathParams + pathParams, + virtualPathNames ); } catch (err) { console.error('Error getting nodes children', err); @@ -222,6 +231,69 @@ class NavigationClass { return result; } + cleanObj(input, keys) { + const res = {}; + for (const key in input) { + if (input.hasOwnProperty(key)) { + const noFullMatch = keys.filter(k => key.includes(k)).length === 0; + const noPartialMatch = keys.filter(k => k.endsWith('*')) + .map(k => k.slice(0, -1)) + .filter(k => key.startsWith(k)).length === 0; + if (noFullMatch && noPartialMatch) { + res[key] = input[key]; + } + } + } + return res; + } + + + buildVirtualTree(node, nodeNamesInCurrentPath) { + if ((node.isVirtualTree || node._isVirtualTree) && nodeNamesInCurrentPath[0]) { + const isVirtualTreeRoot = node.isVirtualTree; + // Temporary store values that will be cleaned up when creating a copy + let _virtualPathNames = node._virtualPathNames; + let _virtualPathIndex = node._virtualPathIndex; + if (!_virtualPathNames) { + _virtualPathNames = nodeNamesInCurrentPath.slice(); // take without first segment, which is the parent one. + _virtualPathIndex = 0; + if(!node.context) { + node.context = {}; + } + } + // In case of defined virtualTree, when it got directly accessed + // Or when someone tries to target a to long url + const maxPathDepth = 50; + if(!_virtualPathNames.length || _virtualPathIndex > maxPathDepth) { + return; + } + + // console.log('== buildVirtualTree', this.isVirtualTree, _virtualPathIndex, _virtualPathNames.join('/'), 'nniCP', nodeNamesInCurrentPath.join('/')) + + const vPath = _virtualPathNames.slice(0, _virtualPathIndex).join('/'); + _virtualPathIndex++; + // TODO: VIEWURL IS NOT WORKING, HAVE CHANGED INDEX + VPATH ORDER + + const keysToClean = ['_*', 'parent', 'isVirtualTree', 'viewUrl', 'children']; + const newChild = this.cleanObj(node, keysToClean); + + Object.assign(newChild, { + // _prevSegment: nodeNamesInCurrentPath[0], // just for debugging + pathSegment: ':virtualSegment', + label: ':virtualSegment', + viewUrl: node.virtualViewUrl.replace(':virtualPath', vPath), + _isVirtualTree: true, + _virtualPath: vPath, + _virtualPathNames, + _virtualPathIndex + }); + + // override .children with a represence of the current node + node.children = [newChild]; + return _virtualPathNames; + } + } + findMatchingNode(urlPathElement, nodes) { let result = null; const segmentsLength = nodes.filter(n => !!n.pathSegment).length; From f1c4023d7a2960b1b565c5f72155d441a7b1b017 Mon Sep 17 00:00:00 2001 From: Markus Edenhauser <1720843+maxmarkus@users.noreply.github.com> Date: Mon, 27 Jan 2020 12:28:14 +0100 Subject: [PATCH 2/7] working simplified solution --- core/src/navigation/services/navigation.js | 79 ++++++++----------- core/src/utilities/helpers/generic-helpers.js | 27 +++++++ 2 files changed, 61 insertions(+), 45 deletions(-) diff --git a/core/src/navigation/services/navigation.js b/core/src/navigation/services/navigation.js index 9cbb969ad2..ce6ac5e86f 100644 --- a/core/src/navigation/services/navigation.js +++ b/core/src/navigation/services/navigation.js @@ -171,8 +171,7 @@ class NavigationClass { nodesInCurrentPath, childrenOfCurrentNode, context, - pathParams = {}, - virtualPathNames + pathParams = {} ) { if (!context.parentNavigationContexts) { context.parentNavigationContexts = []; @@ -210,18 +209,21 @@ class NavigationClass { * If its a virtual tree, * build static children */ - virtualPathNames = this.buildVirtualTree(node, nodeNamesInCurrentPath, virtualPathNames); + this.buildVirtualTree(node, nodeNamesInCurrentPath, pathParams); // STANDARD PROCEDURE - let children = await this.getChildren(node, newContext, nodeNamesInCurrentPath); + let children = await this.getChildren( + node, + newContext, + nodeNamesInCurrentPath + ); const newNodeNamesInCurrentPath = nodeNamesInCurrentPath.slice(1); result = this.buildNode( newNodeNamesInCurrentPath, nodesInCurrentPath, children, newContext, - pathParams, - virtualPathNames + pathParams ); } catch (err) { console.error('Error getting nodes children', err); @@ -231,66 +233,53 @@ class NavigationClass { return result; } - cleanObj(input, keys) { - const res = {}; - for (const key in input) { - if (input.hasOwnProperty(key)) { - const noFullMatch = keys.filter(k => key.includes(k)).length === 0; - const noPartialMatch = keys.filter(k => k.endsWith('*')) - .map(k => k.slice(0, -1)) - .filter(k => key.startsWith(k)).length === 0; - if (noFullMatch && noPartialMatch) { - res[key] = input[key]; - } + buildVirtualViewUrl(str, pathParams, _virtualPathIndex) { + let newStr = '/'; + for (const key in pathParams) { + if (key.startsWith('virtualSegment')) { + newStr += ':' + key + '/'; } } - return res; + newStr += ':virtualSegment_' + _virtualPathIndex + '/'; + return str.replace(':virtualPath', newStr); } - - buildVirtualTree(node, nodeNamesInCurrentPath) { - if ((node.isVirtualTree || node._isVirtualTree) && nodeNamesInCurrentPath[0]) { - const isVirtualTreeRoot = node.isVirtualTree; + buildVirtualTree(node, nodeNamesInCurrentPath, pathParams) { + const isVirtualTreeRoot = node.isVirtualTree; + const isVirtualTreeChild = node._isVirtualTree; + if ( + (isVirtualTreeRoot || isVirtualTreeChild) && + nodeNamesInCurrentPath[0] + ) { // Temporary store values that will be cleaned up when creating a copy - let _virtualPathNames = node._virtualPathNames; let _virtualPathIndex = node._virtualPathIndex; - if (!_virtualPathNames) { - _virtualPathNames = nodeNamesInCurrentPath.slice(); // take without first segment, which is the parent one. + if (isVirtualTreeRoot) { _virtualPathIndex = 0; - if(!node.context) { - node.context = {}; - } } // In case of defined virtualTree, when it got directly accessed // Or when someone tries to target a to long url + // Or if end of indexes reached const maxPathDepth = 50; - if(!_virtualPathNames.length || _virtualPathIndex > maxPathDepth) { + if (_virtualPathIndex > maxPathDepth) { return; } - // console.log('== buildVirtualTree', this.isVirtualTree, _virtualPathIndex, _virtualPathNames.join('/'), 'nniCP', nodeNamesInCurrentPath.join('/')) - - const vPath = _virtualPathNames.slice(0, _virtualPathIndex).join('/'); _virtualPathIndex++; - // TODO: VIEWURL IS NOT WORKING, HAVE CHANGED INDEX + VPATH ORDER - - const keysToClean = ['_*', 'parent', 'isVirtualTree', 'viewUrl', 'children']; - const newChild = this.cleanObj(node, keysToClean); - + const keysToClean = ['_*', 'parent', 'isVirtualTree', 'children']; + const newChild = GenericHelpers.cleanObject(node, keysToClean); Object.assign(newChild, { - // _prevSegment: nodeNamesInCurrentPath[0], // just for debugging - pathSegment: ':virtualSegment', - label: ':virtualSegment', - viewUrl: node.virtualViewUrl.replace(':virtualPath', vPath), + pathSegment: ':virtualSegment_' + _virtualPathIndex, + label: ':virtualSegment_' + _virtualPathIndex, + viewUrl: this.buildVirtualViewUrl( + node.virtualViewUrl, + pathParams, + _virtualPathIndex + ), _isVirtualTree: true, - _virtualPath: vPath, - _virtualPathNames, _virtualPathIndex }); - // override .children with a represence of the current node node.children = [newChild]; - return _virtualPathNames; } } diff --git a/core/src/utilities/helpers/generic-helpers.js b/core/src/utilities/helpers/generic-helpers.js index 4da695492c..9b89a432a6 100644 --- a/core/src/utilities/helpers/generic-helpers.js +++ b/core/src/utilities/helpers/generic-helpers.js @@ -262,6 +262,33 @@ class GenericHelpersClass { input ); } + + /** + * Returns a new Object with the same object, + * without the keys that were given. + * References still stay. + * Allows wildcard ending keys + * + * @param {Object} input any given object + * @param {Array} of keys, allows also wildcards at the end, like: _* + */ + cleanObject(input, keys) { + const res = {}; + for (const key in input) { + if (input.hasOwnProperty(key)) { + const noFullMatch = keys.filter(k => key.includes(k)).length === 0; + const noPartialMatch = + keys + .filter(k => k.endsWith('*')) + .map(k => k.slice(0, -1)) + .filter(k => key.startsWith(k)).length === 0; + if (noFullMatch && noPartialMatch) { + res[key] = input[key]; + } + } + } + return res; + } } export const GenericHelpers = new GenericHelpersClass(); From a7245923b214d2a5d6835ee73a65d58cabc1a29d Mon Sep 17 00:00:00 2001 From: Markus Edenhauser <1720843+maxmarkus@users.noreply.github.com> Date: Mon, 27 Jan 2020 14:17:24 +0100 Subject: [PATCH 3/7] added unit tets --- core/src/navigation/services/navigation.js | 27 +++- core/src/utilities/helpers/generic-helpers.js | 8 +- core/test/services/navigation.spec.js | 146 +++++++++++++++++- .../utilities/helpers/generic-helpers.spec.js | 15 ++ 4 files changed, 187 insertions(+), 9 deletions(-) diff --git a/core/src/navigation/services/navigation.js b/core/src/navigation/services/navigation.js index ce6ac5e86f..7a69fe007f 100644 --- a/core/src/navigation/services/navigation.js +++ b/core/src/navigation/services/navigation.js @@ -233,8 +233,17 @@ class NavigationClass { return result; } + /** + * Requires str to include :virtualPath + * and pathParams consist of :virtualSegment_N + * for deep nested virtual tree building + * + * @param {string} str + * @param {Object} pathParams + * @param {number} _virtualPathIndex + */ buildVirtualViewUrl(str, pathParams, _virtualPathIndex) { - let newStr = '/'; + let newStr = ''; for (const key in pathParams) { if (key.startsWith('virtualSegment')) { newStr += ':' + key + '/'; @@ -251,14 +260,22 @@ class NavigationClass { (isVirtualTreeRoot || isVirtualTreeChild) && nodeNamesInCurrentPath[0] ) { + // Check requirements + if (isVirtualTreeRoot && !node.virtualViewUrl) { + console.error( + '[ERROR] node is declared as virtual tree, but no virtualViewUrl parameter found in node.', + node + ); + return; + } + // Temporary store values that will be cleaned up when creating a copy let _virtualPathIndex = node._virtualPathIndex; if (isVirtualTreeRoot) { _virtualPathIndex = 0; } - // In case of defined virtualTree, when it got directly accessed - // Or when someone tries to target a to long url - // Or if end of indexes reached + + // Allowing maximum of 50 path segments to avoid memory issues const maxPathDepth = 50; if (_virtualPathIndex > maxPathDepth) { return; @@ -266,7 +283,7 @@ class NavigationClass { _virtualPathIndex++; const keysToClean = ['_*', 'parent', 'isVirtualTree', 'children']; - const newChild = GenericHelpers.cleanObject(node, keysToClean); + const newChild = GenericHelpers.removeProperties(node, keysToClean); Object.assign(newChild, { pathSegment: ':virtualSegment_' + _virtualPathIndex, label: ':virtualSegment_' + _virtualPathIndex, diff --git a/core/src/utilities/helpers/generic-helpers.js b/core/src/utilities/helpers/generic-helpers.js index 9b89a432a6..95c72b08ea 100644 --- a/core/src/utilities/helpers/generic-helpers.js +++ b/core/src/utilities/helpers/generic-helpers.js @@ -272,8 +272,14 @@ class GenericHelpersClass { * @param {Object} input any given object * @param {Array} of keys, allows also wildcards at the end, like: _* */ - cleanObject(input, keys) { + removeProperties(input, keys) { const res = {}; + if (!keys instanceof Array || !keys.length) { + console.error( + '[ERROR] removeProperties requires second parameter: array of keys to remove from object.' + ); + return input; + } for (const key in input) { if (input.hasOwnProperty(key)) { const noFullMatch = keys.filter(k => key.includes(k)).length === 0; diff --git a/core/test/services/navigation.spec.js b/core/test/services/navigation.spec.js index 9367d04be7..8b9a0c911e 100644 --- a/core/test/services/navigation.spec.js +++ b/core/test/services/navigation.spec.js @@ -71,11 +71,16 @@ describe('Navigation', function() { beforeEach(() => { Navigation._rootNodeProviderUsed = undefined; Navigation.rootNode = undefined; + console.warn = sinon.spy(); + console.error = sinon.spy(); + console.warn.resetHistory(); + console.error.resetHistory(); }); afterEach(() => { // reset LuigiConfig.config = {}; sinon.restore(); + sinon.reset(); }); describe('getNavigationPath', function() { it('should not fail for undefined arguments', () => { @@ -434,9 +439,6 @@ describe('Navigation', function() { ] }); - console.warn = sinon.spy(); - console.error = sinon.spy(); - // truthy tests // when const resStaticOk = Navigation.findMatchingNode('other', [staticNode()]); @@ -914,4 +916,142 @@ describe('Navigation', function() { assert.deepEqual(result, expected); }); }); + describe('buildVirtualViewUrl', () => { + it('returns same if :virtualPath is not defined', () => { + const given = 'https://mf.luigi-project.io'; + assert.equal(Navigation.buildVirtualViewUrl(given), given); + }); + it('returns valid substituted string without proper pathParams', () => { + const mock = { + url: 'https://mf.luigi-project.io#!/:virtualPath', + pathParams: { + otherParam: 'foo' + }, + index: 1 + }; + const expected = 'https://mf.luigi-project.io#!/:virtualSegment_1/'; + + assert.equal( + Navigation.buildVirtualViewUrl(mock.url, mock.pathParams, mock.index), + expected + ); + }); + it('returns valid substituted string with pathParams', () => { + const mock = { + url: 'https://mf.luigi-project.io#!/:virtualPath', + pathParams: { + otherParam: 'foo', + virtualSegment_1: 'one', + virtualSegment_2: 'two' + }, + index: 3 + }; + const expected = + 'https://mf.luigi-project.io#!/:virtualSegment_1/:virtualSegment_2/:virtualSegment_3/'; + + assert.equal( + Navigation.buildVirtualViewUrl(mock.url, mock.pathParams, mock.index), + expected + ); + }); + }); + describe('buildVirtualTree', () => { + it('unchanged node if not a virtual tree root', () => { + const given = { + label: 'Luigi' + }; + const expected = Object.assign({}, given); + + Navigation.buildVirtualTree(given); + + assert.deepEqual(given, expected); + }); + it('unchanged if directly accessing a node which is defined as virtual tree root', () => { + const mockNode = { + label: 'Luigi', + isVirtualTree: true, + virtualViewUrl: 'foo' + }; + const mockNodeNames = []; // no further child segments + + const expected = Object.assign({}, mockNode); + + Navigation.buildVirtualTree(mockNode, mockNodeNames); + + assert.deepEqual(mockNode, expected); + }); + it('throws and error if virtualViewUrl is undefined', () => { + const mockNode = { + label: 'Luigi', + isVirtualTree: true + }; + const expected = Object.assign({}, mockNode); + + Navigation.buildVirtualTree(mockNode, ['one']); + + sinon.assert.calledOnce(console.error); + assert.deepEqual(mockNode, expected); + }); + it('with first virtual tree segment', () => { + const mockNode = { + label: 'Luigi', + isVirtualTree: true, + virtualViewUrl: 'http://mf.luigi-project.io/:virtualPath' + }; + const mockNodeNames = ['foo']; + + const expected = Object.assign({}, mockNode, { + children: [ + { + _isVirtualTree: true, + _virtualPathIndex: 1, + label: ':virtualSegment_1', + pathSegment: ':virtualSegment_1', + viewUrl: 'http://mf.luigi-project.io/:virtualSegment_1/', + virtualViewUrl: 'http://mf.luigi-project.io/:virtualPath' + } + ] + }); + + Navigation.buildVirtualTree(mockNode, mockNodeNames); + + assert.deepEqual(mockNode, expected); + }); + it('with a deep nested virtual tree segment', () => { + const mockNode = { + _isVirtualTree: true, + _virtualPathIndex: 3, + label: ':virtualSegment_3', + pathSegment: ':virtualSegment_3', + viewUrl: + 'http://mf.luigi-project.io/:virtualSegment_2/:virtualSegment_3/', + virtualViewUrl: 'http://mf.luigi-project.io/:virtualPath' + }; + const mockNodeNames = ['foo']; + const pathParams = { + otherParam: 'foo', + virtualSegment_1: 'one', + virtualSegment_2: 'two', + virtualSegment_3: 'three' + }; + + const expected = Object.assign({}, mockNode, { + children: [ + { + _isVirtualTree: true, + _virtualPathIndex: 4, + label: ':virtualSegment_4', + pathSegment: ':virtualSegment_4', + viewUrl: + 'http://mf.luigi-project.io/:virtualSegment_1/:virtualSegment_2/:virtualSegment_3/:virtualSegment_4/', + virtualViewUrl: 'http://mf.luigi-project.io/:virtualPath' + } + ] + }); + + Navigation.buildVirtualTree(mockNode, mockNodeNames, pathParams); + + assert.deepEqual(mockNode, expected); + }); + }); }); diff --git a/core/test/utilities/helpers/generic-helpers.spec.js b/core/test/utilities/helpers/generic-helpers.spec.js index f3a70cd762..ff984aab2e 100644 --- a/core/test/utilities/helpers/generic-helpers.spec.js +++ b/core/test/utilities/helpers/generic-helpers.spec.js @@ -77,4 +77,19 @@ describe('Generic-helpers', () => { }; assert.deepEqual(GenericHelpers.removeInternalProperties(input), expected); }); + it('removeProperties', () => { + const input = { + some: true, + value: true, + _internal: true, + _somefn: () => true, + internalOne: true, + internalTwo: true + }; + const keys = ['_*', 'value', 'internal*']; + const expected = { + some: true + }; + assert.deepEqual(GenericHelpers.removeProperties(input, keys), expected); + }); }); From 18f21a0b5c801908f669494b0a977a43a48968fd Mon Sep 17 00:00:00 2001 From: Markus Edenhauser <1720843+maxmarkus@users.noreply.github.com> Date: Thu, 30 Jan 2020 18:22:51 +0100 Subject: [PATCH 4/7] refactored to simple virtualTree setting, docu updates --- core/src/navigation/services/navigation.js | 35 +++++++++++++--------- docs/navigation-parameters-reference.md | 4 +++ 2 files changed, 25 insertions(+), 14 deletions(-) diff --git a/core/src/navigation/services/navigation.js b/core/src/navigation/services/navigation.js index 7a69fe007f..acd8fc38f7 100644 --- a/core/src/navigation/services/navigation.js +++ b/core/src/navigation/services/navigation.js @@ -250,20 +250,18 @@ class NavigationClass { } } newStr += ':virtualSegment_' + _virtualPathIndex + '/'; - return str.replace(':virtualPath', newStr); + return str + '/' + newStr; } buildVirtualTree(node, nodeNamesInCurrentPath, pathParams) { - const isVirtualTreeRoot = node.isVirtualTree; - const isVirtualTreeChild = node._isVirtualTree; - if ( - (isVirtualTreeRoot || isVirtualTreeChild) && - nodeNamesInCurrentPath[0] - ) { + const virtualTreeRoot = node.virtualTree; + const virtualTreeChild = node._virtualTree; + const _virtualViewUrl = node._virtualViewUrl || node.viewUrl; + if ((virtualTreeRoot || virtualTreeChild) && nodeNamesInCurrentPath[0]) { // Check requirements - if (isVirtualTreeRoot && !node.virtualViewUrl) { + if (virtualTreeRoot && !_virtualViewUrl) { console.error( - '[ERROR] node is declared as virtual tree, but no virtualViewUrl parameter found in node.', + '[ERROR] node is declared as virtual tree, but no _virtualViewUrl parameter found in node.', node ); return; @@ -271,8 +269,9 @@ class NavigationClass { // Temporary store values that will be cleaned up when creating a copy let _virtualPathIndex = node._virtualPathIndex; - if (isVirtualTreeRoot) { + if (virtualTreeRoot) { _virtualPathIndex = 0; + node.keepSelectedForChildren = true; } // Allowing maximum of 50 path segments to avoid memory issues @@ -282,18 +281,26 @@ class NavigationClass { } _virtualPathIndex++; - const keysToClean = ['_*', 'parent', 'isVirtualTree', 'children']; + const keysToClean = [ + '_*', + 'virtualTree', + 'parent', + 'children', + 'keepSelectedForChildren', + 'navigationContext' + ]; const newChild = GenericHelpers.removeProperties(node, keysToClean); Object.assign(newChild, { pathSegment: ':virtualSegment_' + _virtualPathIndex, label: ':virtualSegment_' + _virtualPathIndex, viewUrl: this.buildVirtualViewUrl( - node.virtualViewUrl, + _virtualViewUrl, pathParams, _virtualPathIndex ), - _isVirtualTree: true, - _virtualPathIndex + _virtualTree: true, + _virtualPathIndex, + _virtualViewUrl }); node.children = [newChild]; diff --git a/docs/navigation-parameters-reference.md b/docs/navigation-parameters-reference.md index 279cac6486..939b2e58b9 100644 --- a/docs/navigation-parameters-reference.md +++ b/docs/navigation-parameters-reference.md @@ -243,6 +243,10 @@ settings: { - **type**: boolean or "exclusive" - **description**: when set to `true`, the node is always accessible. When set to `exclusive`, the node is only visible in logged-out state. Requires **auth.disableAutoLogin** to be set to `true`. **anonymousAccess** needs to be defined both on parent and child nodes. +### virtualTree +- **type**: boolean +- **description**: marks the node as the beginning of a virtual tree. Allows navigation to any of its child path without the need of specifying nested children. **keepSelectedForChildren** is automatically applied. + ## Context switcher The context switcher is a drop-down list available in the top navigation bar. It allows you to switch between a curated list of navigation elements such as Environments. To do so, add the **contextSwitcher** parameter to the **navigation** object using the following optional parameters: From cca66eeb3be2850b4e53e282f43c5fa16d2f0408 Mon Sep 17 00:00:00 2001 From: Markus Edenhauser <1720843+maxmarkus@users.noreply.github.com> Date: Fri, 31 Jan 2020 17:56:04 +0100 Subject: [PATCH 5/7] cleanup and fix of unit tets --- core/src/navigation/services/navigation.js | 11 +----- core/test/services/navigation.spec.js | 45 ++++++++-------------- 2 files changed, 16 insertions(+), 40 deletions(-) diff --git a/core/src/navigation/services/navigation.js b/core/src/navigation/services/navigation.js index acd8fc38f7..460d42e4dc 100644 --- a/core/src/navigation/services/navigation.js +++ b/core/src/navigation/services/navigation.js @@ -255,19 +255,10 @@ class NavigationClass { buildVirtualTree(node, nodeNamesInCurrentPath, pathParams) { const virtualTreeRoot = node.virtualTree; + // Temporary store values that will be cleaned up when creating a copy const virtualTreeChild = node._virtualTree; const _virtualViewUrl = node._virtualViewUrl || node.viewUrl; if ((virtualTreeRoot || virtualTreeChild) && nodeNamesInCurrentPath[0]) { - // Check requirements - if (virtualTreeRoot && !_virtualViewUrl) { - console.error( - '[ERROR] node is declared as virtual tree, but no _virtualViewUrl parameter found in node.', - node - ); - return; - } - - // Temporary store values that will be cleaned up when creating a copy let _virtualPathIndex = node._virtualPathIndex; if (virtualTreeRoot) { _virtualPathIndex = 0; diff --git a/core/test/services/navigation.spec.js b/core/test/services/navigation.spec.js index 8b9a0c911e..c6b17ec778 100644 --- a/core/test/services/navigation.spec.js +++ b/core/test/services/navigation.spec.js @@ -917,13 +917,9 @@ describe('Navigation', function() { }); }); describe('buildVirtualViewUrl', () => { - it('returns same if :virtualPath is not defined', () => { - const given = 'https://mf.luigi-project.io'; - assert.equal(Navigation.buildVirtualViewUrl(given), given); - }); it('returns valid substituted string without proper pathParams', () => { const mock = { - url: 'https://mf.luigi-project.io#!/:virtualPath', + url: 'https://mf.luigi-project.io#!', pathParams: { otherParam: 'foo' }, @@ -938,7 +934,7 @@ describe('Navigation', function() { }); it('returns valid substituted string with pathParams', () => { const mock = { - url: 'https://mf.luigi-project.io#!/:virtualPath', + url: 'https://mf.luigi-project.io#!', pathParams: { otherParam: 'foo', virtualSegment_1: 'one', @@ -969,8 +965,8 @@ describe('Navigation', function() { it('unchanged if directly accessing a node which is defined as virtual tree root', () => { const mockNode = { label: 'Luigi', - isVirtualTree: true, - virtualViewUrl: 'foo' + virtualTree: true, + viewUrl: 'foo' }; const mockNodeNames = []; // no further child segments @@ -980,52 +976,41 @@ describe('Navigation', function() { assert.deepEqual(mockNode, expected); }); - it('throws and error if virtualViewUrl is undefined', () => { - const mockNode = { - label: 'Luigi', - isVirtualTree: true - }; - const expected = Object.assign({}, mockNode); - - Navigation.buildVirtualTree(mockNode, ['one']); - - sinon.assert.calledOnce(console.error); - assert.deepEqual(mockNode, expected); - }); it('with first virtual tree segment', () => { const mockNode = { label: 'Luigi', - isVirtualTree: true, - virtualViewUrl: 'http://mf.luigi-project.io/:virtualPath' + virtualTree: true, + viewUrl: 'http://mf.luigi-project.io' }; const mockNodeNames = ['foo']; const expected = Object.assign({}, mockNode, { + keepSelectedForChildren: true, children: [ { - _isVirtualTree: true, + _virtualTree: true, _virtualPathIndex: 1, label: ':virtualSegment_1', pathSegment: ':virtualSegment_1', viewUrl: 'http://mf.luigi-project.io/:virtualSegment_1/', - virtualViewUrl: 'http://mf.luigi-project.io/:virtualPath' + _virtualViewUrl: 'http://mf.luigi-project.io' } ] }); Navigation.buildVirtualTree(mockNode, mockNodeNames); - assert.deepEqual(mockNode, expected); + assert.deepEqual(expected, mockNode); }); it('with a deep nested virtual tree segment', () => { const mockNode = { - _isVirtualTree: true, + _virtualTree: true, _virtualPathIndex: 3, label: ':virtualSegment_3', pathSegment: ':virtualSegment_3', viewUrl: 'http://mf.luigi-project.io/:virtualSegment_2/:virtualSegment_3/', - virtualViewUrl: 'http://mf.luigi-project.io/:virtualPath' + _virtualViewUrl: 'http://mf.luigi-project.io' }; const mockNodeNames = ['foo']; const pathParams = { @@ -1038,20 +1023,20 @@ describe('Navigation', function() { const expected = Object.assign({}, mockNode, { children: [ { - _isVirtualTree: true, + _virtualTree: true, _virtualPathIndex: 4, label: ':virtualSegment_4', pathSegment: ':virtualSegment_4', viewUrl: 'http://mf.luigi-project.io/:virtualSegment_1/:virtualSegment_2/:virtualSegment_3/:virtualSegment_4/', - virtualViewUrl: 'http://mf.luigi-project.io/:virtualPath' + _virtualViewUrl: 'http://mf.luigi-project.io' } ] }); Navigation.buildVirtualTree(mockNode, mockNodeNames, pathParams); - assert.deepEqual(mockNode, expected); + assert.deepEqual(expected, mockNode); }); }); }); From da914fe985e6b105351545333db3b124818778e5 Mon Sep 17 00:00:00 2001 From: Markus <1720843+maxmarkus@users.noreply.github.com> Date: Mon, 10 Feb 2020 09:54:13 +0100 Subject: [PATCH 6/7] Update docs/navigation-parameters-reference.md Co-Authored-By: Alexandra Simeonova <49867570+alexandra-simeonova@users.noreply.github.com> --- docs/navigation-parameters-reference.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/navigation-parameters-reference.md b/docs/navigation-parameters-reference.md index 89afd05590..b8bc7e7bcc 100644 --- a/docs/navigation-parameters-reference.md +++ b/docs/navigation-parameters-reference.md @@ -257,7 +257,7 @@ settings: { ### virtualTree - **type**: boolean -- **description**: marks the node as the beginning of a virtual tree. Allows navigation to any of its child path without the need of specifying nested children. **keepSelectedForChildren** is automatically applied. +- **description**: marks the node as the beginning of a virtual tree. Allows navigation to any of its children's paths without the need of specifying nested children. **keepSelectedForChildren** is automatically applied. ## Context switcher From 973977fdc36d390b9001883bcd1254a389a08e93 Mon Sep 17 00:00:00 2001 From: Markus Edenhauser <1720843+maxmarkus@users.noreply.github.com> Date: Thu, 13 Feb 2020 17:15:29 +0100 Subject: [PATCH 7/7] small docu improvement --- docs/navigation-parameters-reference.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/docs/navigation-parameters-reference.md b/docs/navigation-parameters-reference.md index b8bc7e7bcc..bf94b0acc9 100644 --- a/docs/navigation-parameters-reference.md +++ b/docs/navigation-parameters-reference.md @@ -257,7 +257,18 @@ settings: { ### virtualTree - **type**: boolean -- **description**: marks the node as the beginning of a virtual tree. Allows navigation to any of its children's paths without the need of specifying nested children. **keepSelectedForChildren** is automatically applied. +- **description**: marks the node as the beginning of a virtual tree. Allows navigation to any of its children's paths without the need of specifying nested children. The path that comes after the node marked as **virtualTree** is appended to its **viewUrl**. [**keepSelectedForChildren**](#keepSelectedForChildren) is automatically applied. +- **example**: + In this example, navigating to `core.tld/settings/some/nested/view` will result in opening `/sampleapp.html#/settings/some/nested/view`. + ```javascript + { + pathSegment: 'settings', + label: 'Settings', + viewUrl: '/sampleapp.html#/settings', + navigationContext: 'settings', + virtualTree: true + } + ``` ## Context switcher