From c2bd6712c65c6834fcaa6f4cc8a7bcc110934ca3 Mon Sep 17 00:00:00 2001 From: rashidakanchwala Date: Wed, 6 Nov 2024 10:08:53 +0000 Subject: [PATCH 01/13] wip Signed-off-by: rashidakanchwala --- src/components/node-list/index.js | 10 +-- src/components/node-list/node-list-tree.js | 51 +++++--------- src/components/node-list/node-list.js | 2 - src/selectors/disabled.js | 81 +++++++++++++++------- 4 files changed, 73 insertions(+), 71 deletions(-) diff --git a/src/components/node-list/index.js b/src/components/node-list/index.js index 74353d8944..fb330418db 100644 --- a/src/components/node-list/index.js +++ b/src/components/node-list/index.js @@ -14,10 +14,7 @@ import { isModularPipelineType, } from '../../selectors/node-types'; import { getTagData, getTagNodeCounts } from '../../selectors/tags'; -import { - getFocusedModularPipeline, - getModularPipelinesSearchResult, -} from '../../selectors/modular-pipelines'; +import { getFocusedModularPipeline } from '../../selectors/modular-pipelines'; import { getGroupedNodes, getNodeSelected, @@ -94,10 +91,6 @@ const NodeListProvider = ({ inputOutputDataNodes, }); - const modularPipelinesSearchResult = searchValue - ? getModularPipelinesSearchResult(modularPipelinesTree, searchValue) - : null; - const groups = getGroups({ items }); const onItemClick = (item) => { @@ -305,7 +298,6 @@ const NodeListProvider = ({ faded={faded} items={items} modularPipelinesTree={modularPipelinesTree} - modularPipelinesSearchResult={modularPipelinesSearchResult} groups={groups} searchValue={searchValue} onUpdateSearchValue={debounce(updateSearchValue, 250)} diff --git a/src/components/node-list/node-list-tree.js b/src/components/node-list/node-list-tree.js index fa89c3fec8..2820bfc0b5 100644 --- a/src/components/node-list/node-list-tree.js +++ b/src/components/node-list/node-list-tree.js @@ -10,7 +10,9 @@ import sortBy from 'lodash/sortBy'; import { loadNodeData } from '../../actions/nodes'; import { getNodeSelected } from '../../selectors/nodes'; +import { getNodeDisabledViaModularPipeline } from '../../selectors/disabled'; import { isModularPipelineType } from '../../selectors/node-types'; +import { getModularPipelinesSearchResult } from '../../selectors/modular-pipelines'; import NodeListTreeItem from './node-list-tree-item'; import VisibleIcon from '../icons/visible'; import InvisibleIcon from '../icons/invisible'; @@ -36,20 +38,6 @@ const StyledTreeView = styled(TreeView)({ padding: '0 0 0 20px', }); -/** - * Return whether the given modular pipeline ID is on focus mode path, i.e. - * it's not the currently focused pipeline nor one of its children. - * @param {String} focusModeID The currently focused modular pipeline ID. - * @param {String} modularPipelineID The modular pipeline ID to check. - * @return {Boolean} Whether the given modular pipeline ID is on focus mode path. - */ -const isOnFocusedModePath = (focusModeID, modularPipelineID) => { - return ( - modularPipelineID === focusModeID || - modularPipelineID.startsWith(`${focusModeID}.`) - ); -}; - /** * Return the data of a modular pipeline to display as a row in the node list. * @param {Object} params @@ -78,8 +66,8 @@ const getModularPipelineRowData = ({ focusModeIcon: focusModeIcon, active: data.active, selected: false, - faded: disabled || !checked, - visible: !disabled && checked, + faded: !checked, + visible: checked, enabled: true, disabled: disabled, focused: focused, @@ -103,7 +91,7 @@ const getNodeRowData = (node, disabled, selected, highlight) => { active: node.active, selected, highlight, - faded: disabled || node.disabledNode, + faded: node.disabledNode, visible: !disabled && checked, checked, disabled, @@ -112,7 +100,8 @@ const getNodeRowData = (node, disabled, selected, highlight) => { const TreeListProvider = ({ nodeSelected, - modularPipelinesSearchResult, + nodeDisabledViaModularPipeline, + searchValue, modularPipelinesTree, onItemChange, onItemMouseEnter, @@ -120,39 +109,32 @@ const TreeListProvider = ({ onItemClick, onNodeToggleExpanded, focusMode, - disabledModularPipeline, expanded, onToggleNodeSelected, slicedPipeline, isSlicingPipelineApplied, }) => { // render a leaf node in the modular pipelines tree + + const modularPipelinesSearchResult = searchValue + ? getModularPipelinesSearchResult(modularPipelinesTree, searchValue) + : null; + const renderLeafNode = (node) => { // As part of the slicing pipeline logic, child nodes not included in the sliced pipeline are assigned an empty data object. // Therefore, if a child node has an empty data object, it indicates it's not part of the slicing pipeline and should not be rendered. - if (Object.keys(node).length === 0) { + if (!node || Object.keys(node).length === 0) { return null; } const disabled = node.disabledTag || node.disabledType || - (focusMode && - !node.modularPipelines - .map((modularPipelineID) => - isOnFocusedModePath(focusMode.id, modularPipelineID) - ) - .some(Boolean)) || - (node.modularPipelines && - node.modularPipelines - .map( - (modularPipelineID) => disabledModularPipeline[modularPipelineID] - ) - .some(Boolean)); + nodeDisabledViaModularPipeline[node.id]; const selected = nodeSelected[node.id]; - const highlight = slicedPipeline.includes(node.id); + const data = getNodeRowData(node, disabled, selected, highlight); return ( @@ -220,7 +202,7 @@ const TreeListProvider = ({ const data = getModularPipelineRowData({ ...node, focusModeIcon, - disabled: focusMode && !isOnFocusedModePath(focusMode.id, node.id), + disabled: nodeDisabledViaModularPipeline[node.id], focused: isFocusedModularPipeline, highlight, }); @@ -272,6 +254,7 @@ const TreeListProvider = ({ export const mapStateToProps = (state) => ({ nodeSelected: getNodeSelected(state), + nodeDisabledViaModularPipeline: getNodeDisabledViaModularPipeline(state), expanded: state.modularPipeline.expanded, slicedPipeline: getSlicedPipeline(state), isSlicingPipelineApplied: state.slice.apply, diff --git a/src/components/node-list/node-list.js b/src/components/node-list/node-list.js index 0106c0594c..c71c2f085f 100644 --- a/src/components/node-list/node-list.js +++ b/src/components/node-list/node-list.js @@ -15,7 +15,6 @@ const NodeList = ({ faded, items, modularPipelinesTree, - modularPipelinesSearchResult, groups, searchValue, getGroupState, @@ -58,7 +57,6 @@ const NodeList = ({ >
+ arrayToObject(nodeIDs, (id) => { + let isDisabledViaModularPipeline = + disabledModularPipeline[nodeModularPipelines[id]]; + + let isDisabledViaFocusedModularPipeline = false; + + if (focusedModularPipeline) { + const inputOutputNodeIDs = [ + ...modularPipelinesTree[focusedModularPipeline.id].inputs, + ...modularPipelinesTree[focusedModularPipeline.id].outputs, + ]; + + if (nodeType[id] === 'modularPipeline') { + return ( + id !== focusedModularPipeline.id && + !id.startsWith(`${focusedModularPipeline.id}.`) + ); + } + + isDisabledViaFocusedModularPipeline = + !nodeModularPipelines[id].includes(focusedModularPipeline.id) && + !inputOutputNodeIDs.includes(id); + } + + return [ + isDisabledViaFocusedModularPipeline, + isDisabledViaModularPipeline, + ].some(Boolean); + }) +); + /** * Set disabled status if the node is specifically hidden, and/or via a tag/view/type/modularPipeline */ @@ -72,11 +123,9 @@ export const getNodeDisabled = createSelector( getNodeDisabledNode, getNodeDisabledTag, getNodeDisabledPipeline, + getNodeDisabledViaModularPipeline, getNodeType, getNodeTypeDisabled, - getNodeModularPipelines, - getModularPipelinesTree, - getFocusedModularPipeline, getVisibleSidebarNodes, getVisibleModularPipelineInputsOutputs, getDisabledModularPipeline, @@ -88,11 +137,9 @@ export const getNodeDisabled = createSelector( nodeDisabledNode, nodeDisabledTag, nodeDisabledPipeline, + nodeDisabledViaModularPipeline, nodeType, typeDisabled, - nodeModularPipelines, - modularPipelinesTree, - focusedModularPipeline, visibleSidebarNodes, visibleModularPipelineInputsOutputs, disabledModularPipeline, @@ -100,9 +147,6 @@ export const getNodeDisabled = createSelector( isSliceApplied ) => arrayToObject(nodeIDs, (id) => { - let isDisabledViaModularPipeline = - disabledModularPipeline[nodeModularPipelines[id]]; - let isDisabledViaSlicedPipeline = false; if (isSliceApplied && slicedPipeline.length > 0) { isDisabledViaSlicedPipeline = !slicedPipeline.includes(id); @@ -112,22 +156,8 @@ export const getNodeDisabled = createSelector( !visibleSidebarNodes[id] && !visibleModularPipelineInputsOutputs.has(id); - let isDisabledViaFocusedModularPipeline = false; - if (focusedModularPipeline) { - const inputOutputNodeIDs = [ - ...modularPipelinesTree[focusedModularPipeline.id].inputs, - ...modularPipelinesTree[focusedModularPipeline.id].outputs, - ]; - if (nodeType[id] === 'modularPipeline') { - isDisabledViaFocusedModularPipeline = - id !== focusedModularPipeline.id && - !id.startsWith(`${focusedModularPipeline.id}.`); - } else { - isDisabledViaFocusedModularPipeline = - !nodeModularPipelines[id].includes(focusedModularPipeline.id) && - !inputOutputNodeIDs.includes(id); - } - } + const isDisabledViaModularPipeline = nodeDisabledViaModularPipeline[id]; + return [ nodeDisabledNode[id], nodeDisabledTag[id], @@ -136,7 +166,6 @@ export const getNodeDisabled = createSelector( typeDisabled[nodeType[id]], isDisabledViaSidebar, isDisabledViaModularPipeline, - isDisabledViaFocusedModularPipeline, isDisabledViaSlicedPipeline, ].some(Boolean); }) From c7e08e8f6f69045cbd16fce556d37f34fd9b383f Mon Sep 17 00:00:00 2001 From: rashidakanchwala Date: Tue, 12 Nov 2024 12:49:21 +0000 Subject: [PATCH 02/13] done Signed-off-by: rashidakanchwala --- src/utils/graph/index.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/utils/graph/index.js b/src/utils/graph/index.js index a76602ee70..80d81f9b61 100644 --- a/src/utils/graph/index.js +++ b/src/utils/graph/index.js @@ -7,6 +7,14 @@ import { graph } from './graph'; * which don't affect layout. */ export const graphNew = ({ nodes, edges, layers }) => { + /* + Sort edges to ensure consistent ordering and eliminate randomness in + graph calculations for each render + */ + const sortedEdges = edges.sort((a, b) => a.id.localeCompare(b.id)); + + console.log(nodes, sortedEdges, layers); + for (const node of nodes) { node.iconSize = node.iconSize || 24; node.icon = node.icon || 'node'; @@ -25,7 +33,7 @@ export const graphNew = ({ nodes, edges, layers }) => { node.iconOffset = node.iconOffset || -innerWidth / 2; } - const result = graph(nodes, edges, layers); + const result = graph(nodes, sortedEdges, layers); return { ...result, From 6288fa1ec29c761dd92c92b7e7a56a44522820fe Mon Sep 17 00:00:00 2001 From: rashidakanchwala <37628668+rashidakanchwala@users.noreply.github.com> Date: Tue, 12 Nov 2024 13:08:19 +0000 Subject: [PATCH 03/13] Update index.js Signed-off-by: rashidakanchwala <37628668+rashidakanchwala@users.noreply.github.com> --- src/components/node-list/index.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/components/node-list/index.js b/src/components/node-list/index.js index fb330418db..74353d8944 100644 --- a/src/components/node-list/index.js +++ b/src/components/node-list/index.js @@ -14,7 +14,10 @@ import { isModularPipelineType, } from '../../selectors/node-types'; import { getTagData, getTagNodeCounts } from '../../selectors/tags'; -import { getFocusedModularPipeline } from '../../selectors/modular-pipelines'; +import { + getFocusedModularPipeline, + getModularPipelinesSearchResult, +} from '../../selectors/modular-pipelines'; import { getGroupedNodes, getNodeSelected, @@ -91,6 +94,10 @@ const NodeListProvider = ({ inputOutputDataNodes, }); + const modularPipelinesSearchResult = searchValue + ? getModularPipelinesSearchResult(modularPipelinesTree, searchValue) + : null; + const groups = getGroups({ items }); const onItemClick = (item) => { @@ -298,6 +305,7 @@ const NodeListProvider = ({ faded={faded} items={items} modularPipelinesTree={modularPipelinesTree} + modularPipelinesSearchResult={modularPipelinesSearchResult} groups={groups} searchValue={searchValue} onUpdateSearchValue={debounce(updateSearchValue, 250)} From e42eba216f94451ad7563f4c290c3ea3584199e6 Mon Sep 17 00:00:00 2001 From: rashidakanchwala <37628668+rashidakanchwala@users.noreply.github.com> Date: Tue, 12 Nov 2024 13:09:09 +0000 Subject: [PATCH 04/13] Update node-list-tree.js Signed-off-by: rashidakanchwala <37628668+rashidakanchwala@users.noreply.github.com> --- src/components/node-list/node-list-tree.js | 51 ++++++++++++++-------- 1 file changed, 34 insertions(+), 17 deletions(-) diff --git a/src/components/node-list/node-list-tree.js b/src/components/node-list/node-list-tree.js index 2820bfc0b5..fa89c3fec8 100644 --- a/src/components/node-list/node-list-tree.js +++ b/src/components/node-list/node-list-tree.js @@ -10,9 +10,7 @@ import sortBy from 'lodash/sortBy'; import { loadNodeData } from '../../actions/nodes'; import { getNodeSelected } from '../../selectors/nodes'; -import { getNodeDisabledViaModularPipeline } from '../../selectors/disabled'; import { isModularPipelineType } from '../../selectors/node-types'; -import { getModularPipelinesSearchResult } from '../../selectors/modular-pipelines'; import NodeListTreeItem from './node-list-tree-item'; import VisibleIcon from '../icons/visible'; import InvisibleIcon from '../icons/invisible'; @@ -38,6 +36,20 @@ const StyledTreeView = styled(TreeView)({ padding: '0 0 0 20px', }); +/** + * Return whether the given modular pipeline ID is on focus mode path, i.e. + * it's not the currently focused pipeline nor one of its children. + * @param {String} focusModeID The currently focused modular pipeline ID. + * @param {String} modularPipelineID The modular pipeline ID to check. + * @return {Boolean} Whether the given modular pipeline ID is on focus mode path. + */ +const isOnFocusedModePath = (focusModeID, modularPipelineID) => { + return ( + modularPipelineID === focusModeID || + modularPipelineID.startsWith(`${focusModeID}.`) + ); +}; + /** * Return the data of a modular pipeline to display as a row in the node list. * @param {Object} params @@ -66,8 +78,8 @@ const getModularPipelineRowData = ({ focusModeIcon: focusModeIcon, active: data.active, selected: false, - faded: !checked, - visible: checked, + faded: disabled || !checked, + visible: !disabled && checked, enabled: true, disabled: disabled, focused: focused, @@ -91,7 +103,7 @@ const getNodeRowData = (node, disabled, selected, highlight) => { active: node.active, selected, highlight, - faded: node.disabledNode, + faded: disabled || node.disabledNode, visible: !disabled && checked, checked, disabled, @@ -100,8 +112,7 @@ const getNodeRowData = (node, disabled, selected, highlight) => { const TreeListProvider = ({ nodeSelected, - nodeDisabledViaModularPipeline, - searchValue, + modularPipelinesSearchResult, modularPipelinesTree, onItemChange, onItemMouseEnter, @@ -109,32 +120,39 @@ const TreeListProvider = ({ onItemClick, onNodeToggleExpanded, focusMode, + disabledModularPipeline, expanded, onToggleNodeSelected, slicedPipeline, isSlicingPipelineApplied, }) => { // render a leaf node in the modular pipelines tree - - const modularPipelinesSearchResult = searchValue - ? getModularPipelinesSearchResult(modularPipelinesTree, searchValue) - : null; - const renderLeafNode = (node) => { // As part of the slicing pipeline logic, child nodes not included in the sliced pipeline are assigned an empty data object. // Therefore, if a child node has an empty data object, it indicates it's not part of the slicing pipeline and should not be rendered. - if (!node || Object.keys(node).length === 0) { + if (Object.keys(node).length === 0) { return null; } const disabled = node.disabledTag || node.disabledType || - nodeDisabledViaModularPipeline[node.id]; + (focusMode && + !node.modularPipelines + .map((modularPipelineID) => + isOnFocusedModePath(focusMode.id, modularPipelineID) + ) + .some(Boolean)) || + (node.modularPipelines && + node.modularPipelines + .map( + (modularPipelineID) => disabledModularPipeline[modularPipelineID] + ) + .some(Boolean)); const selected = nodeSelected[node.id]; - const highlight = slicedPipeline.includes(node.id); + const highlight = slicedPipeline.includes(node.id); const data = getNodeRowData(node, disabled, selected, highlight); return ( @@ -202,7 +220,7 @@ const TreeListProvider = ({ const data = getModularPipelineRowData({ ...node, focusModeIcon, - disabled: nodeDisabledViaModularPipeline[node.id], + disabled: focusMode && !isOnFocusedModePath(focusMode.id, node.id), focused: isFocusedModularPipeline, highlight, }); @@ -254,7 +272,6 @@ const TreeListProvider = ({ export const mapStateToProps = (state) => ({ nodeSelected: getNodeSelected(state), - nodeDisabledViaModularPipeline: getNodeDisabledViaModularPipeline(state), expanded: state.modularPipeline.expanded, slicedPipeline: getSlicedPipeline(state), isSlicingPipelineApplied: state.slice.apply, From 5186e45bcd109c0ab95c60701d60d9ca9e038d34 Mon Sep 17 00:00:00 2001 From: rashidakanchwala <37628668+rashidakanchwala@users.noreply.github.com> Date: Tue, 12 Nov 2024 13:09:46 +0000 Subject: [PATCH 05/13] Update node-list.js Signed-off-by: rashidakanchwala <37628668+rashidakanchwala@users.noreply.github.com> --- src/components/node-list/node-list.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/node-list/node-list.js b/src/components/node-list/node-list.js index c71c2f085f..0106c0594c 100644 --- a/src/components/node-list/node-list.js +++ b/src/components/node-list/node-list.js @@ -15,6 +15,7 @@ const NodeList = ({ faded, items, modularPipelinesTree, + modularPipelinesSearchResult, groups, searchValue, getGroupState, @@ -57,6 +58,7 @@ const NodeList = ({ >
Date: Tue, 12 Nov 2024 13:10:20 +0000 Subject: [PATCH 06/13] Update disabled.js Signed-off-by: rashidakanchwala <37628668+rashidakanchwala@users.noreply.github.com> --- src/selectors/disabled.js | 81 +++++++++++++-------------------------- 1 file changed, 26 insertions(+), 55 deletions(-) diff --git a/src/selectors/disabled.js b/src/selectors/disabled.js index 7150e93f65..2822be9711 100644 --- a/src/selectors/disabled.js +++ b/src/selectors/disabled.js @@ -63,57 +63,6 @@ export const getNodeDisabledTag = createSelector( }) ); -/** - * Determine if a node is disabled via disabled modular pipeline or focused modular pipeline. - */ -export const getNodeDisabledViaModularPipeline = createSelector( - [ - getNodeIDs, - getNodeType, - getNodeModularPipelines, - getModularPipelinesTree, - getFocusedModularPipeline, - getDisabledModularPipeline, - ], - ( - nodeIDs, - nodeType, - nodeModularPipelines, - modularPipelinesTree, - focusedModularPipeline, - disabledModularPipeline - ) => - arrayToObject(nodeIDs, (id) => { - let isDisabledViaModularPipeline = - disabledModularPipeline[nodeModularPipelines[id]]; - - let isDisabledViaFocusedModularPipeline = false; - - if (focusedModularPipeline) { - const inputOutputNodeIDs = [ - ...modularPipelinesTree[focusedModularPipeline.id].inputs, - ...modularPipelinesTree[focusedModularPipeline.id].outputs, - ]; - - if (nodeType[id] === 'modularPipeline') { - return ( - id !== focusedModularPipeline.id && - !id.startsWith(`${focusedModularPipeline.id}.`) - ); - } - - isDisabledViaFocusedModularPipeline = - !nodeModularPipelines[id].includes(focusedModularPipeline.id) && - !inputOutputNodeIDs.includes(id); - } - - return [ - isDisabledViaFocusedModularPipeline, - isDisabledViaModularPipeline, - ].some(Boolean); - }) -); - /** * Set disabled status if the node is specifically hidden, and/or via a tag/view/type/modularPipeline */ @@ -123,9 +72,11 @@ export const getNodeDisabled = createSelector( getNodeDisabledNode, getNodeDisabledTag, getNodeDisabledPipeline, - getNodeDisabledViaModularPipeline, getNodeType, getNodeTypeDisabled, + getNodeModularPipelines, + getModularPipelinesTree, + getFocusedModularPipeline, getVisibleSidebarNodes, getVisibleModularPipelineInputsOutputs, getDisabledModularPipeline, @@ -137,9 +88,11 @@ export const getNodeDisabled = createSelector( nodeDisabledNode, nodeDisabledTag, nodeDisabledPipeline, - nodeDisabledViaModularPipeline, nodeType, typeDisabled, + nodeModularPipelines, + modularPipelinesTree, + focusedModularPipeline, visibleSidebarNodes, visibleModularPipelineInputsOutputs, disabledModularPipeline, @@ -147,6 +100,9 @@ export const getNodeDisabled = createSelector( isSliceApplied ) => arrayToObject(nodeIDs, (id) => { + let isDisabledViaModularPipeline = + disabledModularPipeline[nodeModularPipelines[id]]; + let isDisabledViaSlicedPipeline = false; if (isSliceApplied && slicedPipeline.length > 0) { isDisabledViaSlicedPipeline = !slicedPipeline.includes(id); @@ -156,8 +112,22 @@ export const getNodeDisabled = createSelector( !visibleSidebarNodes[id] && !visibleModularPipelineInputsOutputs.has(id); - const isDisabledViaModularPipeline = nodeDisabledViaModularPipeline[id]; - + let isDisabledViaFocusedModularPipeline = false; + if (focusedModularPipeline) { + const inputOutputNodeIDs = [ + ...modularPipelinesTree[focusedModularPipeline.id].inputs, + ...modularPipelinesTree[focusedModularPipeline.id].outputs, + ]; + if (nodeType[id] === 'modularPipeline') { + isDisabledViaFocusedModularPipeline = + id !== focusedModularPipeline.id && + !id.startsWith(`${focusedModularPipeline.id}.`); + } else { + isDisabledViaFocusedModularPipeline = + !nodeModularPipelines[id].includes(focusedModularPipeline.id) && + !inputOutputNodeIDs.includes(id); + } + } return [ nodeDisabledNode[id], nodeDisabledTag[id], @@ -166,6 +136,7 @@ export const getNodeDisabled = createSelector( typeDisabled[nodeType[id]], isDisabledViaSidebar, isDisabledViaModularPipeline, + isDisabledViaFocusedModularPipeline, isDisabledViaSlicedPipeline, ].some(Boolean); }) From 54e09b47b441eeb6c23db2c014fc72bb4e13b39f Mon Sep 17 00:00:00 2001 From: rashidakanchwala Date: Tue, 12 Nov 2024 13:11:28 +0000 Subject: [PATCH 07/13] done Signed-off-by: rashidakanchwala --- src/utils/graph/index.js | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/utils/graph/index.js b/src/utils/graph/index.js index 80d81f9b61..8bb7b8afb7 100644 --- a/src/utils/graph/index.js +++ b/src/utils/graph/index.js @@ -7,14 +7,9 @@ import { graph } from './graph'; * which don't affect layout. */ export const graphNew = ({ nodes, edges, layers }) => { - /* - Sort edges to ensure consistent ordering and eliminate randomness in - graph calculations for each render - */ + // Sort edges to ensure consistent ordering and eliminate randomness in graph calculations const sortedEdges = edges.sort((a, b) => a.id.localeCompare(b.id)); - console.log(nodes, sortedEdges, layers); - for (const node of nodes) { node.iconSize = node.iconSize || 24; node.icon = node.icon || 'node'; From b90845113fa2cf9ad3a3e886523a3c6829674f1e Mon Sep 17 00:00:00 2001 From: rashidakanchwala Date: Thu, 14 Nov 2024 15:08:08 +0000 Subject: [PATCH 08/13] fixed layer sort Signed-off-by: rashidakanchwala --- package/kedro_viz/services/layers.py | 3 +++ src/store/normalize-data.js | 10 ++++++++++ src/utils/graph/index.js | 5 +---- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/package/kedro_viz/services/layers.py b/package/kedro_viz/services/layers.py index 7cba369aa1..a51c3269ae 100644 --- a/package/kedro_viz/services/layers.py +++ b/package/kedro_viz/services/layers.py @@ -106,6 +106,9 @@ def find_child_layers(node_id: str) -> Set[str]: if layer not in layer_dependencies: layer_dependencies[layer] = set() + # Sort `layer_dependencies` keys for consistent ordering of layers with the same dependencies + layer_dependencies = {k: layer_dependencies[k] for k in sorted(layer_dependencies)} + # Use graphlib.TopologicalSorter to sort the layer dependencies. try: sorter = TopologicalSorter(layer_dependencies) diff --git a/src/store/normalize-data.js b/src/store/normalize-data.js index 15aaacaa94..708f28736c 100644 --- a/src/store/normalize-data.js +++ b/src/store/normalize-data.js @@ -250,6 +250,15 @@ const getNodeTypesFromUrl = (state, typeQueryParams) => { return state; }; +/** + * Sort the edges, nodes, and layers in the state object to ensure deterministic graph layout + * @param {Object} state The state object to sort + */ +const organizeStateElements = (state) => { + state.edge?.ids?.sort((a, b) => a.localeCompare(b)); + state.node?.ids?.sort((a, b) => a.localeCompare(b)); +}; + /** * Updates the state with filters from the URL. * @param {Object} state - State object @@ -331,6 +340,7 @@ const normalizeData = (data, expandAllPipelines) => { data.layers.forEach(addLayer(state)); } + organizeStateElements(state); const updatedState = updateStateWithFilters(state, data.tags); return updatedState; }; diff --git a/src/utils/graph/index.js b/src/utils/graph/index.js index 8bb7b8afb7..a76602ee70 100644 --- a/src/utils/graph/index.js +++ b/src/utils/graph/index.js @@ -7,9 +7,6 @@ import { graph } from './graph'; * which don't affect layout. */ export const graphNew = ({ nodes, edges, layers }) => { - // Sort edges to ensure consistent ordering and eliminate randomness in graph calculations - const sortedEdges = edges.sort((a, b) => a.id.localeCompare(b.id)); - for (const node of nodes) { node.iconSize = node.iconSize || 24; node.icon = node.icon || 'node'; @@ -28,7 +25,7 @@ export const graphNew = ({ nodes, edges, layers }) => { node.iconOffset = node.iconOffset || -innerWidth / 2; } - const result = graph(nodes, sortedEdges, layers); + const result = graph(nodes, edges, layers); return { ...result, From 030bd8e2476b3363c5f8404b4a2a39b9ac0cde35 Mon Sep 17 00:00:00 2001 From: rashidakanchwala Date: Thu, 14 Nov 2024 17:18:00 +0000 Subject: [PATCH 09/13] fix tests Signed-off-by: rashidakanchwala --- src/selectors/sliced-pipeline.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/selectors/sliced-pipeline.test.js b/src/selectors/sliced-pipeline.test.js index 2a76049b2b..632eb63a9f 100644 --- a/src/selectors/sliced-pipeline.test.js +++ b/src/selectors/sliced-pipeline.test.js @@ -12,13 +12,13 @@ describe('Selectors', () => { const expected = [ '23c94afb', '47b81aa6', + '90ebe5f3', 'daf35ba0', 'c09084f2', '0abef172', 'e5a9ec27', 'b7bb7198', 'f192326a', - '90ebe5f3', 'data_processing', ]; const newState = reducer(mockState.spaceflights, { From dc1b05f9e2829710587269c34326c22a0bbacb0d Mon Sep 17 00:00:00 2001 From: rashidakanchwala Date: Thu, 14 Nov 2024 21:49:07 +0000 Subject: [PATCH 10/13] done tests Signed-off-by: rashidakanchwala --- package/tests/test_services/test_layers.py | 28 +- src/actions/graph.test.js | 78 ++++- src/store/normalize-data.test.js | 17 + .../data/spaceflights_reordered.mock.json | 311 ++++++++++++++++++ src/utils/state.mock.js | 2 + 5 files changed, 418 insertions(+), 18 deletions(-) create mode 100644 src/utils/data/spaceflights_reordered.mock.json diff --git a/package/tests/test_services/test_layers.py b/package/tests/test_services/test_layers.py index c949a9f98b..358cf8f505 100644 --- a/package/tests/test_services/test_layers.py +++ b/package/tests/test_services/test_layers.py @@ -154,6 +154,32 @@ {"node_1": {}, "node_2": {}}, ["a", "b"], ), + ( + # Case where if two layers e.g. `int` and `primary` layers share the same dependencies, they get sorted alphabetically. + """ + node_1(layer=raw) -> node_3(layer=int) + node_2(layer=raw) -> node_4(layer=primary) + node_3(layer=int) -> node_5(layer=feature) + node_4(layer=primary) -> node_6(layer=feature) + """, + { + "node_1": {"id": "node_1", "layer": "raw"}, + "node_2": {"id": "node_2", "layer": "raw"}, + "node_3": {"id": "node_3", "layer": "int"}, + "node_4": {"id": "node_4", "layer": "primary"}, + "node_5": {"id": "node_5", "layer": "feature"}, + "node_6": {"id": "node_6", "layer": "feature"}, + }, + { + "node_1": {"node_3"}, + "node_2": {"node_4"}, + "node_3": {"node_5"}, + "node_4": {"node_6"}, + "node_5": set(), + "node_6": set(), + }, + ["raw", "int", "primary", "feature"], + ), ], ) def test_sort_layers(graph_schema, nodes, node_dependencies, expected): @@ -170,7 +196,7 @@ def test_sort_layers(graph_schema, nodes, node_dependencies, expected): for node_id, node_dict in nodes.items() } sorted_layers = sort_layers(nodes, node_dependencies) - assert sorted(sorted_layers) == sorted(expected), graph_schema + assert sorted_layers == expected, graph_schema def test_sort_layers_should_return_empty_list_on_cyclic_layers(mocker): diff --git a/src/actions/graph.test.js b/src/actions/graph.test.js index 1ff8df3436..991eb5ad66 100644 --- a/src/actions/graph.test.js +++ b/src/actions/graph.test.js @@ -1,25 +1,37 @@ import { createStore } from 'redux'; import reducer from '../reducers'; -import { mockState } from '../utils/state.mock'; import { calculateGraph, updateGraph } from './graph'; import { getGraphInput } from '../selectors/layout'; +import { prepareState } from '../utils/state.mock'; +import spaceflights from '../utils/data/spaceflights.mock.json'; +import spaceflightsReordered from '../utils/data/spaceflights_reordered.mock.json'; +import { toggleModularPipelinesExpanded } from '../actions/modular-pipelines'; describe('graph actions', () => { + const getMockState = (data) => + prepareState({ + data, + beforeLayoutActions: [ + () => + toggleModularPipelinesExpanded(['data_science', 'data_processing']), + ], + }); + describe('calculateGraph', () => { it('returns updateGraph action if input is falsey', () => { expect(calculateGraph(null)).toEqual(updateGraph(null)); }); it('sets loading to true immediately', () => { - const store = createStore(reducer, mockState.spaceflights); + const store = createStore(reducer, getMockState(spaceflights)); expect(store.getState().loading.graph).not.toBe(true); - calculateGraph(getGraphInput(mockState.spaceflights))(store.dispatch); + calculateGraph(getGraphInput(getMockState(spaceflights)))(store.dispatch); expect(store.getState().loading.graph).toBe(true); }); it('sets loading to false and graph visibility to true after finishing calculation', () => { - const store = createStore(reducer, mockState.spaceflights); - return calculateGraph(getGraphInput(mockState.spaceflights))( + const store = createStore(reducer, getMockState(spaceflights)); + return calculateGraph(getGraphInput(getMockState(spaceflights)))( store.dispatch ).then(() => { const state = store.getState(); @@ -29,19 +41,51 @@ describe('graph actions', () => { }); it('calculates a graph', () => { - const state = Object.assign({}, mockState.spaceflights); - delete state.graph; - const store = createStore(reducer, state); + const initialState = { ...getMockState(spaceflights), graph: {} }; + const store = createStore(reducer, initialState); expect(store.getState().graph).toEqual({}); - return calculateGraph(getGraphInput(state))(store.dispatch).then(() => { - expect(store.getState().graph).toEqual( - expect.objectContaining({ - nodes: expect.any(Array), - edges: expect.any(Array), - size: expect.any(Object), - }) - ); - }); + return calculateGraph(getGraphInput(initialState))(store.dispatch).then( + () => { + expect(store.getState().graph).toEqual( + expect.objectContaining({ + nodes: expect.any(Array), + edges: expect.any(Array), + size: expect.any(Object), + }) + ); + } + ); + }); + + it('compares deterministic flowchart of two differently ordered same projects', () => { + const store1 = createStore(reducer, getMockState(spaceflights)); + const store2 = createStore(reducer, getMockState(spaceflightsReordered)); + + return calculateGraph(getGraphInput(getMockState(spaceflights)))( + store1.dispatch + ) + .then(() => + calculateGraph(getGraphInput(getMockState(spaceflightsReordered)))( + store2.dispatch + ) + ) + .then(() => { + // Get node coordinates for both graphs + const graph1Coords = store1.getState().graph.nodes.map((node) => ({ + id: node.id, + x: node.x, + y: node.y, + })); + + const graph2Coords = store2.getState().graph.nodes.map((node) => ({ + id: node.id, + x: node.x, + y: node.y, + })); + + // Verify coordinates consistency between both graphs + expect(graph1Coords).toEqual(expect.arrayContaining(graph2Coords)); + }); }); }); }); diff --git a/src/store/normalize-data.test.js b/src/store/normalize-data.test.js index fbbc5dddd7..b02e7b95b4 100644 --- a/src/store/normalize-data.test.js +++ b/src/store/normalize-data.test.js @@ -1,5 +1,6 @@ import normalizeData, { createInitialPipelineState } from './normalize-data'; import spaceflights from '../utils/data/spaceflights.mock.json'; +import spaceflightsReordered from '../utils/data/spaceflights_reordered.mock.json'; const initialState = createInitialPipelineState(); @@ -90,4 +91,20 @@ describe('normalizeData', () => { expect(node).toHaveProperty('name'); }); }); + + it('should have identical nodes and edges, in the same order, regardless of the different ordering from the api', () => { + // Normalize both datasets + const initialState = normalizeData(spaceflights, true); + const reorderedState = normalizeData(spaceflightsReordered, true); + + // Compare nodes and edges by converting to JSON for deep equality + // Directly compare specific properties of nodes and edges, ensuring order and content + expect(initialState.node.ids).toEqual(reorderedState.node.ids); + expect(initialState.node.name).toEqual(reorderedState.node.name); + expect(initialState.node.type).toEqual(reorderedState.node.type); + + expect(initialState.edge.ids).toEqual(reorderedState.edge.ids); + expect(initialState.edge.sources).toEqual(reorderedState.edge.sources); + expect(initialState.edge.targets).toEqual(reorderedState.edge.targets); + }); }); diff --git a/src/utils/data/spaceflights_reordered.mock.json b/src/utils/data/spaceflights_reordered.mock.json new file mode 100644 index 0000000000..3dea125c60 --- /dev/null +++ b/src/utils/data/spaceflights_reordered.mock.json @@ -0,0 +1,311 @@ +{ + "nodes": [ + { + "id": "f192326a", + "name": "data_processing.shuttles", + "tags": ["preprocessing"], + "pipelines": ["dp", "__default__"], + "type": "data", + "modular_pipelines": ["data_processing"], + "layer": "raw", + "dataset_type": "pandas.excel_dataset.ExcelDataset" + }, + { + "id": "b7bb7198", + "name": "preprocess_shuttles_node", + "tags": ["preprocessing"], + "pipelines": ["dp", "__default__"], + "type": "task", + "modular_pipelines": ["data_processing"], + "parameters": {} + }, + { + "id": "e5a9ec27", + "name": "data_processing.preprocessed_shuttles", + "tags": ["features", "preprocessing"], + "pipelines": ["dp", "__default__"], + "type": "data", + "modular_pipelines": ["data_processing"], + "layer": "intermediate", + "dataset_type": "pandas.csv_dataset.CSVDataset" + }, + { + "id": "0abef172", + "name": "data_processing.companies", + "tags": ["preprocessing"], + "pipelines": ["dp", "__default__"], + "type": "data", + "modular_pipelines": ["data_processing"], + "layer": "raw", + "dataset_type": "pandas.csv_dataset.CSVDataset" + }, + { + "id": "daf35ba0", + "name": "data_processing.preprocessed_companies", + "tags": ["features", "preprocessing"], + "pipelines": ["dp", "__default__"], + "type": "data", + "modular_pipelines": ["data_processing"], + "layer": "intermediate", + "dataset_type": "pandas.csv_dataset.CSVDataset" + }, + { + "id": "c09084f2", + "name": "preprocess_companies_node", + "tags": ["preprocessing"], + "pipelines": ["dp", "__default__"], + "type": "task", + "modular_pipelines": ["data_processing"], + "parameters": {} + }, + { + "id": "47b81aa6", + "name": "create_model_input_table_node", + "tags": ["features"], + "pipelines": ["dp", "__default__"], + "type": "task", + "modular_pipelines": ["data_processing"], + "parameters": {} + }, + { + "id": "90ebe5f3", + "name": "data_processing.reviews", + "tags": ["features"], + "pipelines": ["dp", "__default__"], + "type": "data", + "modular_pipelines": ["data_processing"], + "layer": "raw", + "dataset_type": "pandas.csv_dataset.CSVDataset" + }, + { + "id": "65d0d789", + "name": "split_data_node", + "tags": ["split"], + "pipelines": ["__default__", "ds"], + "type": "task", + "modular_pipelines": ["data_science"], + "parameters": { + "test_size": 0.2, + "random_state": 3, + "features": [ + "engines", + "passenger_capacity", + "crew", + "d_check_complete", + "moon_clearance_complete", + "iata_approved", + "company_rating", + "review_scores_rating" + ] + } + }, + { + "id": "f1f1425b", + "name": "parameters", + "tags": ["split"], + "pipelines": ["__default__", "ds"], + "type": "parameters", + "modular_pipelines": [], + "layer": null, + "dataset_type": null + }, + { + "id": "23c94afb", + "name": "model_input_table", + "tags": ["features", "split"], + "pipelines": ["dp", "__default__", "ds"], + "type": "data", + "modular_pipelines": [], + "layer": "primary", + "dataset_type": "pandas.csv_dataset.CSVDataset" + }, + { + "id": "172a0602", + "name": "data_science.X_train", + "tags": ["split", "train"], + "pipelines": ["__default__", "ds"], + "type": "data", + "modular_pipelines": ["data_science"], + "layer": null, + "dataset_type": null + }, + { + "id": "9c2a8a5e", + "name": "data_science.X_test", + "tags": ["split"], + "pipelines": ["__default__", "ds"], + "type": "data", + "modular_pipelines": ["data_science"], + "layer": null, + "dataset_type": null + }, + { + "id": "e5cee9e2", + "name": "data_science.y_train", + "tags": ["split", "train"], + "pipelines": ["__default__", "ds"], + "type": "data", + "modular_pipelines": ["data_science"], + "layer": null, + "dataset_type": null + }, + { + "id": "ecc63a8c", + "name": "data_science.y_test", + "tags": ["split"], + "pipelines": ["__default__", "ds"], + "type": "data", + "modular_pipelines": ["data_science"], + "layer": null, + "dataset_type": null + }, + { + "id": "90f15f9d", + "name": "train_model_node", + "tags": ["train"], + "pipelines": ["__default__", "ds"], + "type": "task", + "modular_pipelines": ["data_science"], + "parameters": {} + }, + { + "id": "04424659", + "name": "data_science.regressor", + "tags": ["train"], + "pipelines": ["__default__", "ds"], + "type": "data", + "modular_pipelines": ["data_science"], + "layer": "models", + "dataset_type": "pickle.pickle_dataset.PickleDataset" + }, + { + "id": "f5e8d7df", + "name": "evaluate_model_node", + "tags": [], + "pipelines": ["__default__", "ds"], + "type": "task", + "modular_pipelines": ["data_science"], + "parameters": {} + }, + { + "id": "966b9734", + "name": "metrics", + "tags": [], + "pipelines": ["__default__", "ds"], + "type": "data", + "modular_pipelines": [], + "layer": null, + "dataset_type": "tracking.metrics_dataset.MetricsDataset" + }, + { + "id": "data_processing", + "name": "data_processing", + "tags": [], + "pipelines": ["dp", "__default__"], + "type": "modularPipeline", + "modular_pipelines": null, + "layer": null, + "dataset_type": null + }, + { + "id": "data_science", + "name": "data_science", + "tags": [], + "pipelines": ["__default__", "ds"], + "type": "modularPipeline", + "modular_pipelines": null, + "layer": null, + "dataset_type": null + } + ], + "edges": [ + { "source": "23c94afb", "target": "65d0d789" }, + { "source": "04424659", "target": "f5e8d7df" }, + { "source": "90ebe5f3", "target": "47b81aa6" }, + { "source": "23c94afb", "target": "data_science" }, + { "source": "daf35ba0", "target": "47b81aa6" }, + { "source": "data_science", "target": "966b9734" }, + { "source": "47b81aa6", "target": "23c94afb" }, + { "source": "e5cee9e2", "target": "90f15f9d" }, + { "source": "f192326a", "target": "b7bb7198" }, + { "source": "65d0d789", "target": "172a0602" }, + { "source": "f1f1425b", "target": "65d0d789" }, + { "source": "90ebe5f3", "target": "data_processing" }, + { "source": "f5e8d7df", "target": "966b9734" }, + { "source": "e5a9ec27", "target": "47b81aa6" }, + { "source": "65d0d789", "target": "9c2a8a5e" }, + { "source": "data_processing", "target": "23c94afb" }, + { "source": "f1f1425b", "target": "data_science" }, + { "source": "f192326a", "target": "data_processing" }, + { "source": "172a0602", "target": "90f15f9d" }, + { "source": "9c2a8a5e", "target": "f5e8d7df" }, + { "source": "ecc63a8c", "target": "f5e8d7df" }, + { "source": "b7bb7198", "target": "e5a9ec27" }, + { "source": "90f15f9d", "target": "04424659" }, + { "source": "65d0d789", "target": "e5cee9e2" }, + { "source": "0abef172", "target": "data_processing" }, + { "source": "65d0d789", "target": "ecc63a8c" }, + { "source": "0abef172", "target": "c09084f2" }, + { "source": "c09084f2", "target": "daf35ba0" } + ], + "layers": ["raw", "intermediate", "primary", "models"], + "tags": [ + { "id": "features", "name": "features" }, + { "id": "preprocessing", "name": "preprocessing" }, + { "id": "split", "name": "split" }, + { "id": "train", "name": "train" } + ], + "pipelines": [ + { "id": "__default__", "name": "__default__" }, + { "id": "dp", "name": "dp" }, + { "id": "ds", "name": "ds" } + ], + "modular_pipelines": { + "__root__": { + "id": "__root__", + "name": "__root__", + "inputs": [], + "outputs": [], + "children": [ + { "id": "data_processing", "type": "modularPipeline" }, + { "id": "23c94afb", "type": "data" }, + { "id": "966b9734", "type": "data" }, + { "id": "f1f1425b", "type": "parameters" }, + { "id": "data_science", "type": "modularPipeline" } + ] + }, + "data_processing": { + "id": "data_processing", + "name": "data_processing", + "inputs": ["90ebe5f3", "0abef172", "f192326a"], + "outputs": ["23c94afb"], + "children": [ + { "id": "e5a9ec27", "type": "data" }, + { "id": "f192326a", "type": "data" }, + { "id": "90ebe5f3", "type": "data" }, + { "id": "daf35ba0", "type": "data" }, + { "id": "47b81aa6", "type": "task" }, + { "id": "0abef172", "type": "data" }, + { "id": "c09084f2", "type": "task" }, + { "id": "b7bb7198", "type": "task" } + ] + }, + "data_science": { + "id": "data_science", + "name": "data_science", + "inputs": ["f1f1425b", "23c94afb"], + "outputs": ["966b9734"], + "children": [ + { "id": "ecc63a8c", "type": "data" }, + { "id": "172a0602", "type": "data" }, + { "id": "04424659", "type": "data" }, + { "id": "e5cee9e2", "type": "data" }, + { "id": "9c2a8a5e", "type": "data" }, + { "id": "f5e8d7df", "type": "task" }, + { "id": "65d0d789", "type": "task" }, + { "id": "90f15f9d", "type": "task" } + ] + } + }, + "selected_pipeline": "__default__" +} diff --git a/src/utils/state.mock.js b/src/utils/state.mock.js index 6b5d3268ac..e2141406bd 100644 --- a/src/utils/state.mock.js +++ b/src/utils/state.mock.js @@ -4,6 +4,7 @@ import { mount, shallow } from 'enzyme'; import configureStore from '../store'; import getInitialState from '../store/initial-state'; import spaceflights from './data/spaceflights.mock.json'; +import spaceflightsReordered from './data/spaceflights_reordered.mock.json'; import demo from './data/demo.mock.json'; import reducer from '../reducers'; import { getGraphInput } from '../selectors/layout'; @@ -53,6 +54,7 @@ export const mockState = { json: prepareState({ data: 'json' }), demo: prepareState({ data: demo }), spaceflights: prepareState({ data: spaceflights }), + spaceflightsReordered: prepareState({ data: spaceflightsReordered }), }; /** From 46aa3332e0562b29c4134fe5ba40a1179e926aa5 Mon Sep 17 00:00:00 2001 From: rashidakanchwala Date: Thu, 14 Nov 2024 22:02:19 +0000 Subject: [PATCH 11/13] fix lint Signed-off-by: rashidakanchwala --- package/kedro_viz/services/layers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package/kedro_viz/services/layers.py b/package/kedro_viz/services/layers.py index a51c3269ae..6eaae1d0e0 100644 --- a/package/kedro_viz/services/layers.py +++ b/package/kedro_viz/services/layers.py @@ -108,7 +108,7 @@ def find_child_layers(node_id: str) -> Set[str]: # Sort `layer_dependencies` keys for consistent ordering of layers with the same dependencies layer_dependencies = {k: layer_dependencies[k] for k in sorted(layer_dependencies)} - + # Use graphlib.TopologicalSorter to sort the layer dependencies. try: sorter = TopologicalSorter(layer_dependencies) From e0f5855b3576979c05f8e363de10ef93f4c296d0 Mon Sep 17 00:00:00 2001 From: rashidakanchwala Date: Thu, 14 Nov 2024 23:11:44 +0000 Subject: [PATCH 12/13] fix lint Signed-off-by: rashidakanchwala --- package/kedro_viz/services/layers.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/package/kedro_viz/services/layers.py b/package/kedro_viz/services/layers.py index 6eaae1d0e0..b7a46dd789 100644 --- a/package/kedro_viz/services/layers.py +++ b/package/kedro_viz/services/layers.py @@ -107,7 +107,9 @@ def find_child_layers(node_id: str) -> Set[str]: layer_dependencies[layer] = set() # Sort `layer_dependencies` keys for consistent ordering of layers with the same dependencies - layer_dependencies = {k: layer_dependencies[k] for k in sorted(layer_dependencies)} + layer_dependencies = defaultdict( + set, {k: layer_dependencies[k] for k in sorted(layer_dependencies)} + ) # Use graphlib.TopologicalSorter to sort the layer dependencies. try: From e393e017536278592eb17bd9c48d170f03278cfa Mon Sep 17 00:00:00 2001 From: rashidakanchwala Date: Fri, 15 Nov 2024 09:31:26 +0000 Subject: [PATCH 13/13] change func name Signed-off-by: rashidakanchwala --- src/store/normalize-data.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/store/normalize-data.js b/src/store/normalize-data.js index 708f28736c..b38d4a2166 100644 --- a/src/store/normalize-data.js +++ b/src/store/normalize-data.js @@ -251,10 +251,10 @@ const getNodeTypesFromUrl = (state, typeQueryParams) => { }; /** - * Sort the edges, nodes, and layers in the state object to ensure deterministic graph layout + * Sort the edges, nodes in the state object to ensure deterministic graph layout * @param {Object} state The state object to sort */ -const organizeStateElements = (state) => { +const sortNodesEdges = (state) => { state.edge?.ids?.sort((a, b) => a.localeCompare(b)); state.node?.ids?.sort((a, b) => a.localeCompare(b)); }; @@ -340,7 +340,7 @@ const normalizeData = (data, expandAllPipelines) => { data.layers.forEach(addLayer(state)); } - organizeStateElements(state); + sortNodesEdges(state); const updatedState = updateStateWithFilters(state, data.tags); return updatedState; };