11// Copyright (c) Cosmo Tech.
22// Licensed under the MIT license.
3- import '@nosferatu500/react-sortable-tree/style.css' ;
4- import React , { useEffect , useMemo , useState , useRef , useReducer } from 'react' ;
3+ import React , { useEffect , useMemo , useState } from 'react' ;
54import PropTypes from 'prop-types' ;
65import {
76 UnfoldMore as UnfoldMoreIcon ,
@@ -13,16 +12,9 @@ import { ScenarioUtils } from '@cosmotech/core';
1312import { SearchBar } from '../../inputs' ;
1413import { FadingTooltip } from '../../misc' ;
1514import { ScenarioSortableTree } from './components' ;
15+ import { DEFAULT_LABELS } from './labels' ;
1616import useStyles from './style' ;
1717
18- const initNodesDict = ( scenarios , defaultExpanded ) => {
19- const nodesDict = { } ;
20- scenarios . forEach ( ( scenario ) => {
21- nodesDict [ scenario . id ] = defaultExpanded ;
22- } ) ;
23- return nodesDict ;
24- } ;
25-
2618const filterMatchesName = ( scenario , searchStr ) => scenario . name . toLowerCase ( ) . indexOf ( searchStr . toLowerCase ( ) ) !== - 1 ;
2719const filterMatchesValidationStatus = ( labels , scenario , searchStr ) => {
2820 if ( ! scenario . validationStatus ) return false ;
@@ -41,47 +33,13 @@ const filterMatchesTag = (scenario, searchString) =>
4133const filterMatchesOwner = ( scenario , searchString ) =>
4234 scenario . ownerName ?. toLowerCase ( ) . includes ( searchString . toLowerCase ( ) ) ;
4335
44- const DEFAULT_LABELS = {
45- status : 'Run status:' ,
46- runTemplateLabel : 'Run type:' ,
47- successful : 'Successful' ,
48- running : 'Running' ,
49- dataingestioninprogress : 'Transferring results' ,
50- failed : 'Failed' ,
51- created : 'Created' ,
52- delete : 'Delete this scenario' ,
53- edit : 'Edit scenario name' ,
54- scenarioRename : {
55- title : 'Scenario name' ,
56- errors : {
57- emptyScenarioName : 'Scenario name cannot be empty' ,
58- forbiddenCharsInScenarioName :
59- 'Scenario name has to start with a letter, and can only contain ' +
60- 'letters, digits, spaces, underscores, hyphens and dots.' ,
61- } ,
62- } ,
63- deleteDialog : {
64- description :
65- 'This operation is irreversible. Dataset(s) will not be removed, but the scenario parameters will be lost. ' +
66- 'If this scenario has children, they will be moved to a new parent. ' +
67- 'The new parent will be the parent of the deleted scenario.' ,
68- cancel : 'Cancel' ,
69- confirm : 'Confirm' ,
70- } ,
71- dataset : 'Dataset:' ,
72- noDataset : 'None' ,
73- datasetNotFound : 'Not Found' ,
74- searchField : 'Filter' ,
75- toolbar : {
76- expandAll : 'Expand all' ,
77- expandTree : 'Expand tree' ,
78- collapseAll : 'Collapse all' ,
79- } ,
80- validationStatus : {
81- rejected : 'Rejected' ,
82- validated : 'Validated' ,
83- } ,
84- } ;
36+ const doesScenarioMatchFilter = ( labels , scenario , searchStr ) =>
37+ filterMatchesName ( scenario , searchStr ) ||
38+ filterMatchesValidationStatus ( labels , scenario , searchStr ) ||
39+ filterMatchesRunStatus ( labels , scenario , searchStr ) ||
40+ filterMatchesDescription ( scenario , searchStr ) ||
41+ filterMatchesTag ( scenario , searchStr ) ||
42+ filterMatchesOwner ( scenario , searchStr ) ;
8543
8644export const ScenarioManagerTreeList = ( props ) => {
8745 const classes = useStyles ( ) ;
@@ -92,42 +50,24 @@ export const ScenarioManagerTreeList = (props) => {
9250 deleteScenario,
9351 onScenarioRename,
9452 checkScenarioNameValue = ( ) => null ,
95- userId,
96- buildSearchInfo,
9753 buildDatasetInfo,
9854 labels : tmpLabels ,
9955 buildScenarioNameToDelete,
100- showDeleteIcon,
10156 canUserDeleteScenario,
10257 canUserRenameScenario = ( ) => true ,
10358 canUpdateScenario,
10459 onScenarioUpdate = ( ) => null ,
10560 } = props ;
10661
10762 const labels = useMemo ( ( ) => ( { ...DEFAULT_LABELS , ...tmpLabels } ) , [ tmpLabels ] ) ;
108-
109- if ( buildSearchInfo ) {
110- console . warn (
111- '"buildSearchInfo" prop is deprecated in ScenarioManagerTreeList. Please consider removing this prop.'
112- ) ;
113- }
114- if ( showDeleteIcon != null ) {
115- console . warn (
116- '"showDeleteIcon" prop is deprecated in ScenarioManagerTreeList. Please use "canUserDeleteScenario" instead.'
117- ) ;
118- }
63+ const allScenarioIds = useMemo ( ( ) => scenarios . map ( ( scenario ) => scenario . id ) , [ scenarios ] ) ;
11964
12065 const [ searchText , setSearchText ] = useState ( '' ) ;
121- const nodesExpandedChildren = useRef ( initNodesDict ( scenarios , true ) ) ;
122- const nodesExpandedDetails = useRef ( initNodesDict ( scenarios , false ) ) ;
66+ const [ treeExpandedNodes , setTreeExpandedNodes ] = useState ( allScenarioIds ) ;
67+ const [ detailExpandedNodes , setDetailExpandedNodes ] = useState ( [ ] ) ;
12368
12469 const formatScenariosToScenariosTree = ( scenariosToFormat ) => {
125- const scenarioTree = scenariosToFormat . map ( ( scenario ) => {
126- const displayDeleteIcon =
127- canUserDeleteScenario != null
128- ? canUserDeleteScenario ( scenario )
129- : showDeleteIcon !== false && scenario . ownerId === userId ;
130-
70+ const scenarioList = scenariosToFormat . map ( ( scenario ) => {
13171 labels . dataset = buildDatasetInfo ( scenario . datasetList ) ;
13272
13373 return {
@@ -137,7 +77,7 @@ export const ScenarioManagerTreeList = (props) => {
13777 scenarioNodeProps : {
13878 datasets,
13979 scenario,
140- showDeleteIcon : displayDeleteIcon ,
80+ showDeleteIcon : canUserDeleteScenario ( scenario ) ,
14181 onScenarioRedirect,
14282 deleteScenario,
14383 checkScenarioNameValue,
@@ -150,7 +90,7 @@ export const ScenarioManagerTreeList = (props) => {
15090 } ,
15191 } ;
15292 } ) ;
153- return ScenarioUtils . getScenarioTree ( scenarioTree , ( scenA , scenB ) => scenA . name . localeCompare ( scenB . name ) ) ;
93+ return ScenarioUtils . getScenarioTree ( scenarioList , ( scenA , scenB ) => scenA . name . localeCompare ( scenB . name ) ) ;
15494 } ;
15595
15696 const scenarioTreeFull = useMemo (
@@ -167,22 +107,17 @@ export const ScenarioManagerTreeList = (props) => {
167107 // eslint-disable-next-line react-hooks/exhaustive-deps
168108 } , [ scenarios , searchText , labels ] ) ;
169109
170- const [ refreshTick , forceRefresh ] = useReducer ( ( x ) => x + 1 , 0 ) ;
171-
172110 const collapseAll = ( ) => {
173- nodesExpandedChildren . current = initNodesDict ( scenarios , false ) ;
174- nodesExpandedDetails . current = initNodesDict ( scenarios , false ) ;
175- forceRefresh ( ) ;
111+ setTreeExpandedNodes ( [ ] ) ;
112+ setDetailExpandedNodes ( [ ] ) ;
176113 } ;
177114 const expandTree = ( ) => {
178- nodesExpandedChildren . current = initNodesDict ( scenarios , true ) ;
179- nodesExpandedDetails . current = initNodesDict ( scenarios , false ) ;
180- forceRefresh ( ) ;
115+ setTreeExpandedNodes ( allScenarioIds ) ;
116+ setDetailExpandedNodes ( [ ] ) ;
181117 } ;
182118 const expandAll = ( ) => {
183- nodesExpandedChildren . current = initNodesDict ( scenarios , true ) ;
184- nodesExpandedDetails . current = initNodesDict ( scenarios , true ) ;
185- forceRefresh ( ) ;
119+ setTreeExpandedNodes ( allScenarioIds ) ;
120+ setDetailExpandedNodes ( allScenarioIds ) ;
186121 } ;
187122
188123 const filterScenarios = ( searchStr ) => {
@@ -192,15 +127,7 @@ export const ScenarioManagerTreeList = (props) => {
192127 return ;
193128 }
194129 // Otherwise, filter scenarios based on their name
195- const filtered = scenarios . filter (
196- ( scenario ) =>
197- filterMatchesName ( scenario , searchStr ) ||
198- filterMatchesValidationStatus ( labels , scenario , searchStr ) ||
199- filterMatchesRunStatus ( labels , scenario , searchStr ) ||
200- filterMatchesDescription ( scenario , searchStr ) ||
201- filterMatchesTag ( scenario , searchStr ) ||
202- filterMatchesOwner ( scenario , searchStr )
203- ) ;
130+ const filtered = scenarios . filter ( ( scenario ) => doesScenarioMatchFilter ( labels , scenario , searchStr ) ) ;
204131 // Format list and set as tree data
205132 setScenariosTree ( formatScenariosToScenariosTree ( filtered ) ) ;
206133 } ;
@@ -233,15 +160,16 @@ export const ScenarioManagerTreeList = (props) => {
233160 </ div >
234161 </ div >
235162 < div data-cy = "scenario-manager-view" className = { classes . treesContainer } >
236- { scenariosTree . map ( ( scenarioTree ) => {
163+ { scenariosTree . map ( ( rootScenario ) => {
237164 return (
238165 < ScenarioSortableTree
239- key = { scenarioTree . id }
166+ key = { rootScenario . id }
240167 classes = { classes }
241- nodesExpandedChildrenRef = { nodesExpandedChildren }
242- nodesExpandedDetailsRef = { nodesExpandedDetails }
243- scenarioTree = { scenarioTree }
244- refreshTick = { refreshTick }
168+ treeExpandedNodes = { treeExpandedNodes }
169+ setTreeExpandedNodes = { setTreeExpandedNodes }
170+ detailExpandedNodes = { detailExpandedNodes }
171+ setDetailExpandedNodes = { setDetailExpandedNodes }
172+ scenarioTree = { rootScenario }
245173 />
246174 ) ;
247175 } ) }
@@ -279,25 +207,10 @@ ScenarioManagerTreeList.propTypes = {
279207 * Function bound to handle a scenario movement (moving a scenario = changing its parent)
280208 */
281209 moveScenario : PropTypes . func . isRequired ,
282- /**
283- * Current user id
284- */
285- userId : PropTypes . string . isRequired ,
286- /**
287- * DEPRECATED: Function building scenario search label
288- */
289- buildSearchInfo : PropTypes . func ,
290210 /**
291211 * Function building scenario dataset label
292212 */
293213 buildDatasetInfo : PropTypes . func . isRequired ,
294- /**
295- * DEPRECATED: this prop is deprecated, use 'canUserDeleteScenario' instead
296- * Boolean value to define whether scenario delete buttons must be shown in ScenarioNode elements:
297- * - false: delete buttons are always hidden
298- * - true: delete buttons are shown if the user id matches the scenario owner id
299- */
300- showDeleteIcon : PropTypes . bool ,
301214 /**
302215 * Function returning whether the current user can delete a given scenario. This function receives as parameter
303216 * the scenario data and must return a boolean.
0 commit comments