Merge branch 'main' of git.arvados.org:arvados-workbench2 into 20377-dataexplorer...
authorStephen Smith <stephen@curii.com>
Thu, 11 May 2023 16:25:08 +0000 (12:25 -0400)
committerStephen Smith <stephen@curii.com>
Thu, 11 May 2023 16:25:08 +0000 (12:25 -0400)
Arvados-DCO-1.1-Signed-off-by: Stephen Smith <stephen@curii.com>

cypress/integration/project.spec.js
package.json
src/components/collection-panel-files/collection-panel-files.tsx
src/routes/route-change-handlers.ts
src/services/groups-service/groups-service.ts
src/store/project-panel/project-panel-action.ts
src/store/store.ts
src/store/workbench/workbench-actions.ts
src/views/process-panel/process-io-card.tsx
yarn.lock

index 68a90133d25270f937bfdb6d4ecb299036902040..eff4d4e9f18651fc322b7374b0d4b03adf613fbb 100644 (file)
@@ -135,6 +135,7 @@ describe('Project tests', function() {
                 });
             });
         cy.get('[data-cy=form-submit-btn]').click();
+        cy.get('[data-cy=form-dialog]').should('not.exist');
 
         const editProjectDescription = (name, type) => {
             cy.get('[data-cy=side-panel-tree]').contains('Home Projects').click();
@@ -545,7 +546,10 @@ describe('Project tests', function() {
                 cy.get('[data-cy=form-submit-btn]').click();
             });
         cy.get('[data-cy=form-dialog]').should("not.exist");
+        cy.get('[data-cy=snackbar]').contains('created');
+        cy.get('[data-cy=snackbar]').should("not.exist");
         cy.get('[data-cy=side-panel-tree]').contains('Projects').click();
+        cy.waitForDom();
         cy.get('[data-cy=project-panel]').contains(projectName).should('be.visible').rightclick();
         cy.get('[data-cy=context-menu]').contains('Copy to clipboard').click();
         cy.window().then((win) => (
index c17fc9174500db0ad7cbe43788d9c18d4c1574e4..0a10da786e5bc1621a0ea84721ffddaf8a634042 100644 (file)
@@ -68,6 +68,7 @@
     "react-virtualized-auto-sizer": "1.0.2",
     "react-window": "1.8.5",
     "redux": "4.0.3",
+    "redux-devtools-extension": "^2.13.9",
     "redux-form": "7.4.2",
     "redux-thunk": "2.3.0",
     "reselect": "4.0.0",
index b75f3018e4894fa02bb8adedafe2b7b2130f126a..ddf30364ad9c76f09f006d33035d407255d20f7b 100644 (file)
@@ -39,7 +39,7 @@ import { setCollectionFiles } from 'store/collection-panel/collection-panel-file
 import { sortBy } from 'lodash';
 import { formatFileSize } from 'common/formatters';
 import { getInlineFileUrl, sanitizeToken } from 'views-components/context-menu/actions/helpers';
-import { extractUuidObjectType, ResourceObjectType } from 'models/resource';
+import { extractUuidKind, ResourceKind } from 'models/resource';
 
 export interface CollectionPanelFilesProps {
     isWritable: boolean;
@@ -261,7 +261,7 @@ export const CollectionPanelFiles = withStyles(styles)(connect((state: RootState
     const rightData = pathData[rightKey];
 
     React.useEffect(() => {
-        if (props.currentItemUuid && extractUuidObjectType(props.currentItemUuid) == ResourceObjectType.COLLECTION) {
+        if (props.currentItemUuid && extractUuidKind(props.currentItemUuid) === ResourceKind.COLLECTION) {
             setPathData({});
             setPath([props.currentItemUuid]);
         }
index cded6d65cd38dcce7e00fac722131399c324feec..eef52058529b69acd74f17416552561382487e03 100644 (file)
@@ -11,6 +11,7 @@ import { dialogActions } from 'store/dialog/dialog-actions';
 import { contextMenuActions } from 'store/context-menu/context-menu-actions';
 import { searchBarActions } from 'store/search-bar/search-bar-actions';
 import { pluginConfig } from 'plugins';
+import { openProjectPanel } from 'store/project-panel/project-panel-action';
 
 export const addRouteChangeHandlers = (history: History, store: RootStore) => {
     const handler = handleLocationChange(store);
@@ -60,7 +61,7 @@ const handleLocationChange = (store: RootStore) => ({ pathname }: Location) => {
     }
 
     if (projectMatch) {
-        store.dispatch(WorkbenchActions.loadProject(projectMatch.params.id));
+        store.dispatch(openProjectPanel(projectMatch.params.id));
     } else if (collectionMatch) {
         store.dispatch(WorkbenchActions.loadCollection(collectionMatch.params.id));
     } else if (favoriteMatch) {
index cdbe8bab22dfc3b7cd43c382635a6b768c1445ce..fc6033002dccea2532e27dfe361eaa354747f860 100644 (file)
@@ -56,7 +56,7 @@ export class GroupsService<
             select: select
                 ? JSON.stringify(select.map(sel => {
                     const sp = sel.split(".");
-                    return sp.length == 2 ? (sp[0] + "." + snakeCase(sp[1])) : snakeCase(sel);
+                    return sp.length === 2 ? (sp[0] + "." + snakeCase(sp[1])) : snakeCase(sel);
                 }))
                 : undefined
         };
index 3b30f4aab5e3577fdc5c3f3632b7fb0dd60f9f50..7ad18b67bdb2f50b5baf1e4a3818abc3d4581d37 100644 (file)
@@ -7,6 +7,7 @@ import { bindDataExplorerActions } from "store/data-explorer/data-explorer-actio
 import { propertiesActions } from "store/properties/properties-actions";
 import { RootState } from 'store/store';
 import { getProperty } from "store/properties/properties";
+import { loadProject } from "store/workbench/workbench-actions";
 
 export const PROJECT_PANEL_ID = "projectPanel";
 export const PROJECT_PANEL_CURRENT_UUID = "projectPanelCurrentUuid";
@@ -14,7 +15,8 @@ export const IS_PROJECT_PANEL_TRASHED = 'isProjectPanelTrashed';
 export const projectPanelActions = bindDataExplorerActions(PROJECT_PANEL_ID);
 
 export const openProjectPanel = (projectUuid: string) =>
-    (dispatch: Dispatch) => {
+    async (dispatch: Dispatch) => {
+        await dispatch<any>(loadProject(projectUuid));
         dispatch(propertiesActions.SET_PROPERTY({ key: PROJECT_PANEL_CURRENT_UUID, value: projectUuid }));
         dispatch(projectPanelActions.RESET_EXPLORER_SEARCH_VALUE());
         dispatch(projectPanelActions.REQUEST_ITEMS());
index 1501fd4fb5be80db4e03d9f832e59116ec95b6f9..ec673d62239c49170ce2c0f8bbc06a270f15ca00 100644 (file)
@@ -77,17 +77,7 @@ import { MiddlewareListReducer } from 'common/plugintypes';
 import { tooltipsMiddleware } from './tooltips/tooltips-middleware';
 import { sidePanelReducer } from './side-panel/side-panel-reducer'
 import { bannerReducer } from './banner/banner-reducer';
-
-declare global {
-    interface Window {
-        __REDUX_DEVTOOLS_EXTENSION_COMPOSE__?: typeof compose;
-    }
-}
-
-const composeEnhancers =
-    (process.env.NODE_ENV === 'development' &&
-        window && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__) ||
-    compose;
+import { composeWithDevTools } from 'redux-devtools-extension';
 
 export type RootState = ReturnType<ReturnType<typeof createRootReducer>>;
 
@@ -187,7 +177,7 @@ export function configureStore(history: History, services: ServiceRepository, co
 
     middlewares = pluginConfig.middlewares.reduce(reduceMiddlewaresFn, middlewares);
 
-    const enhancer = composeEnhancers(applyMiddleware(redirectToMiddleware, ...middlewares));
+    const enhancer = composeWithDevTools({/* options */ })(applyMiddleware(redirectToMiddleware, ...middlewares));
     return createStore(rootReducer, enhancer);
 }
 
index 524337796efe37a6cfc472c9174eb3a7bd6012bd..a3c3a0969b2185224b340e7eabe2c9211a629568 100644 (file)
@@ -13,7 +13,6 @@ import {
 } from 'store/favorite-panel/favorite-panel-action';
 import {
     getProjectPanelCurrentUuid,
-    openProjectPanel,
     projectPanelActions,
     setIsProjectPanelTrashed,
 } from 'store/project-panel/project-panel-action';
@@ -866,7 +865,6 @@ const finishLoadingProject =
     (project: GroupContentsResource | string) =>
         async (dispatch: Dispatch<any>) => {
             const uuid = typeof project === 'string' ? project : project.uuid;
-            dispatch(openProjectPanel(uuid));
             dispatch(loadDetailsPanel(uuid));
             if (typeof project !== 'string') {
                 dispatch(updateResources([project]));
index 43be92406726c3b509faeab7eebc0dcf91540b90..c0dcb7f0886071ff5c97e0dc7934f40feb4be538 100644 (file)
@@ -2,7 +2,7 @@
 //
 // SPDX-License-Identifier: AGPL-3.0
 
-import React, { ReactElement, useState } from 'react';
+import React, { ReactElement, memo, useState } from 'react';
 import { Dispatch } from 'redux';
 import {
     StyleRulesCallback,
@@ -384,7 +384,7 @@ interface ProcessIOPreviewDataProps {
 
 type ProcessIOPreviewProps = ProcessIOPreviewDataProps & WithStyles<CssRules>;
 
-const ProcessIOPreview = withStyles(styles)(
+const ProcessIOPreview = memo(withStyles(styles)(
     ({ classes, data, showImagePreview, valueLabel }: ProcessIOPreviewProps) => {
         const showLabel = data.some((param: ProcessIOParameter) => param.label);
         return <Table className={classes.tableRoot} aria-label="Process IO Preview">
@@ -441,7 +441,7 @@ const ProcessIOPreview = withStyles(styles)(
                 })}
             </TableBody>
         </Table >;
-    });
+    }));
 
 interface ProcessValuePreviewProps {
     value: ProcessIOValue;
@@ -588,16 +588,16 @@ export const getIOParamDisplayValue = (auth: AuthState, input: CommandInputParam
             const fileArrayMainFiles = ((input as FileArrayCommandInputParameter).value || []);
             const firstMainFilePdh = (fileArrayMainFiles.length > 0 && fileArrayMainFiles[0]) ? getResourcePdhUrl(fileArrayMainFiles[0], pdh) : "";
 
-            // Convert each main file into separate arrays of ProcessIOValue to preserve secondaryFile grouping
-            const fileArrayValues = fileArrayMainFiles.map((mainFile: File, i): ProcessIOValue[] => {
-                const secondaryFiles = ((mainFile as unknown) as FileWithSecondaryFiles)?.secondaryFiles || [];
-                return [
+            // Convert each main and secondaryFiles into array of ProcessIOValue preserving ordering
+            let fileArrayValues: ProcessIOValue[] = [];
+            for(let i = 0; i < fileArrayMainFiles.length; i++) {
+                const secondaryFiles = ((fileArrayMainFiles[i] as unknown) as FileWithSecondaryFiles)?.secondaryFiles || [];
+                fileArrayValues.push(
                     // Pass firstMainFilePdh to secondary files and every main file besides the first to hide pdh if equal
-                    ...(mainFile ? [fileToProcessIOValue(mainFile, false, auth, pdh, i > 0 ? firstMainFilePdh : "")] : []),
+                    ...(fileArrayMainFiles[i] ? [fileToProcessIOValue(fileArrayMainFiles[i], false, auth, pdh, i > 0 ? firstMainFilePdh : "")] : []),
                     ...(secondaryFiles.map(file => fileToProcessIOValue(file, true, auth, pdh, firstMainFilePdh)))
-                ];
-                // Reduce each mainFile/secondaryFile group into single array preserving ordering
-            }).reduce((acc: ProcessIOValue[], mainFile: ProcessIOValue[]) => (acc.concat(mainFile)), []);
+                );
+            }
 
             return fileArrayValues.length ?
                 fileArrayValues :
index a60a7dec2744ab4aeda607fd2ea6b80e40e28296..20bbc46bfe1859e4b9321b024538c6ef84036c15 100644 (file)
--- a/yarn.lock
+++ b/yarn.lock
@@ -3811,6 +3811,7 @@ __metadata:
     react-window: 1.8.5
     redux: 4.0.3
     redux-devtools: 3.4.1
+    redux-devtools-extension: ^2.13.9
     redux-form: 7.4.2
     redux-mock-store: 1.5.4
     redux-thunk: 2.3.0
@@ -15448,6 +15449,15 @@ __metadata:
   languageName: node
   linkType: hard
 
+"redux-devtools-extension@npm:^2.13.9":
+  version: 2.13.9
+  resolution: "redux-devtools-extension@npm:2.13.9"
+  peerDependencies:
+    redux: ^3.1.0 || ^4.0.0
+  checksum: 603d48fd6acf3922ef373b251ab3fdbb990035e90284191047b29d25b06ea18122bc4ef01e0704ccae495acb27ab5e47b560937e98213605dd88299470025db9
+  languageName: node
+  linkType: hard
+
 "redux-devtools-instrument@npm:^1.0.1":
   version: 1.10.0
   resolution: "redux-devtools-instrument@npm:1.10.0"