Merge branch 'master'
authorMichal Klobukowski <michal.klobukowski@contractors.roche.com>
Tue, 2 Oct 2018 12:34:51 +0000 (14:34 +0200)
committerMichal Klobukowski <michal.klobukowski@contractors.roche.com>
Tue, 2 Oct 2018 12:34:51 +0000 (14:34 +0200)
Feature #13863

Arvados-DCO-1.1-Signed-off-by: Michal Klobukowski <michal.klobukowski@contractors.roche.com>

src/components/data-explorer/data-explorer.tsx
src/components/search-bar/search-bar.tsx
src/store/collection-panel/collection-panel-action.ts
src/store/collection-panel/collection-panel-reducer.ts
src/store/run-process-panel/run-process-panel-actions.ts
src/store/run-process-panel/run-process-panel-reducer.ts
src/store/workbench/workbench-actions.ts
src/views/run-process-panel/run-process-first-step.tsx
src/views/run-process-panel/run-process-panel-root.tsx
src/views/run-process-panel/run-process-panel.tsx

index d7abde7bddd2f81d3a668f4587f5063bab010d60..e808351b5257267e340d1498501902c1698b43c9 100644 (file)
@@ -12,7 +12,7 @@ import { DataTableFilterItem } from '../data-table-filters/data-table-filters';
 import { SearchInput } from '../search-input/search-input';
 import { ArvadosTheme } from "~/common/custom-theme";
 
-type CssRules = 'searchBox' | "toolbar" | "root";
+type CssRules = 'searchBox' | "toolbar" | "footer" | "root";
 
 const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
     searchBox: {
@@ -21,6 +21,9 @@ const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
     toolbar: {
         paddingTop: theme.spacing.unit * 2
     },
+    footer: {
+        overflow: 'auto'
+    },
     root: {
         height: '100%'
     }
@@ -94,7 +97,7 @@ export const DataExplorer = withStyles(styles)(
                     working={working}
                     defaultView={dataTableDefaultView}
                 />
-                <Toolbar>
+                <Toolbar className={classes.footer}>
                     <Grid container justify="flex-end">
                         <TablePagination
                             count={itemsAvailable}
index f01b5692f204ca3a72683f71cdecdbaf194a4108..366d70563e3b8060af35b764cd4486d9c75ea697 100644 (file)
@@ -3,10 +3,18 @@
 // SPDX-License-Identifier: AGPL-3.0
 
 import * as React from 'react';
-import { IconButton, Paper, StyleRulesCallback, withStyles, WithStyles, Tooltip } from '@material-ui/core';
+import {
+    IconButton,
+    Paper,
+    StyleRulesCallback,
+    withStyles,
+    WithStyles,
+    Tooltip,
+    InputAdornment, Input
+} from '@material-ui/core';
 import SearchIcon from '@material-ui/icons/Search';
 
-type CssRules = 'container' | 'input' | 'button';
+type CssRules = 'container' | 'input';
 
 const styles: StyleRulesCallback<CssRules> = theme => {
     return {
@@ -17,17 +25,7 @@ const styles: StyleRulesCallback<CssRules> = theme => {
         input: {
             border: 'none',
             borderRadius: theme.spacing.unit / 4,
-            boxSizing: 'border-box',
-            padding: theme.spacing.unit,
-            paddingRight: theme.spacing.unit * 4,
-            width: '100%',
-        },
-        button: {
-            position: 'absolute',
-            top: theme.spacing.unit / 2,
-            right: theme.spacing.unit / 2,
-            width: theme.spacing.unit * 3,
-            height: theme.spacing.unit * 3
+            padding: `${theme.spacing.unit / 2}px ${theme.spacing.unit}px`
         }
     };
 };
@@ -61,17 +59,22 @@ export const SearchBar = withStyles(styles)(
             const { classes } = this.props;
             return <Paper className={classes.container}>
                 <form onSubmit={this.handleSubmit}>
-                    <input
+                    <Input
                         className={classes.input}
                         onChange={this.handleChange}
                         placeholder="Search"
                         value={this.state.value}
-                    />
-                    <Tooltip title='Search'>
-                        <IconButton className={classes.button}>
-                            <SearchIcon />
-                        </IconButton>
-                    </Tooltip>
+                        fullWidth={true}
+                        disableUnderline={true}
+                        endAdornment={
+                            <InputAdornment position="end">
+                                <Tooltip title='Search'>
+                                    <IconButton>
+                                        <SearchIcon />
+                                    </IconButton>
+                                </Tooltip>
+                            </InputAdornment>
+                        }/>
                 </form>
             </Paper>;
         }
index 5e991619e77e3a405722ae3b30115b555c3e62af..265b5cf8310f3a752ba16302852241ea90fd879b 100644 (file)
@@ -15,6 +15,7 @@ import { resourcesActions } from "~/store/resources/resources-actions";
 import { unionize, ofType, UnionOf } from '~/common/unionize';
 
 export const collectionPanelActions = unionize({
+    SET_COLLECTION: ofType<CollectionResource>(),
     LOAD_COLLECTION: ofType<{ uuid: string }>(),
     LOAD_COLLECTION_SUCCESS: ofType<{ item: CollectionResource }>()
 });
@@ -34,7 +35,7 @@ export const loadCollectionPanel = (uuid: string) =>
         return collection;
     };
 
-export const createCollectionTag = (data: TagProperty) => 
+export const createCollectionTag = (data: TagProperty) =>
     async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
         const item = getState().collectionPanel.item;
         const uuid = item ? item.uuid : '';
index b4951b81826b14426b72e813eda7e93d14c2f8f6..55829cb5d094b8e4b9dbddd313dffa256f78eb6f 100644 (file)
@@ -16,5 +16,6 @@ const initialState = {
 export const collectionPanelReducer = (state: CollectionPanelState = initialState, action: CollectionPanelAction) =>
     collectionPanelActions.match(action, {
         default: () => state,
+        SET_COLLECTION: (item) => ({ ...state, item }),
         LOAD_COLLECTION_SUCCESS: ({ item }) => ({ ...state, item }),
     });
index a58eff8ad03ad45d1db963788226754dd97fa912..7112f715b4c449d2ee7aa15ac6786f783a4c0d2b 100644 (file)
@@ -21,6 +21,7 @@ export const runProcessPanelActions = unionize({
     SET_CURRENT_STEP: ofType<number>(),
     SET_WORKFLOWS: ofType<WorkflowResource[]>(),
     SET_SELECTED_WORKFLOW: ofType<WorkflowResource>(),
+    SEARCH_WORKFLOWS: ofType<string>()
 });
 
 export interface RunProcessSecondStepDataFormProps {
@@ -90,4 +91,5 @@ const normalizeInputKeys = (inputs: WorkflowInputsData): WorkflowInputsData =>
     Object.keys(inputs).reduce((normalizedInputs, key) => ({
         ...normalizedInputs,
         [key.split('/').slice(1).join('/')]: inputs[key],
-    }), {});
\ No newline at end of file
+    }), {});
+export const searchWorkflows = (term: string) => runProcessPanelActions.SEARCH_WORKFLOWS(term);
index 2470de1436e30605356dfbbdb83cdb764859eb11..0ad06bee58da253d83e2ad31fbad04a65b1bd7d6 100644 (file)
@@ -9,6 +9,7 @@ interface RunProcessPanel {
     processOwnerUuid: string;
     currentStep: number;
     workflows: WorkflowResource[];
+    searchWorkflows: WorkflowResource[];
     selectedWorkflow: WorkflowResource | undefined;
     inputs: CommandInputParameter[];
 }
@@ -19,17 +20,19 @@ const initialState: RunProcessPanel = {
     workflows: [],
     selectedWorkflow: undefined,
     inputs: [],
+    searchWorkflows: [],
 };
 
 export const runProcessPanelReducer = (state = initialState, action: RunProcessPanelAction): RunProcessPanel =>
     runProcessPanelActions.match(action, {
         SET_PROCESS_OWNER_UUID: processOwnerUuid => ({ ...state, processOwnerUuid }),
         SET_CURRENT_STEP: currentStep => ({ ...state, currentStep }),
-        SET_WORKFLOWS: workflows => ({ ...state, workflows }),
         SET_SELECTED_WORKFLOW: selectedWorkflow => ({
             ...state,
             selectedWorkflow,
             inputs: getWorkflowInputs(parseWorkflowDefinition(selectedWorkflow)) || [],
         }),
+        SET_WORKFLOWS: workflows => ({ ...state, workflows, searchWorkflows: workflows }), 
+        SEARCH_WORKFLOWS: term => ({ ...state, searchWorkflows: state.workflows.filter(workflow => workflow.name.includes(term)) }),
         default: () => state
     });
\ No newline at end of file
index f2d04188f3b3d93f45f8b60943ad603a9a2b3d18..56e3401d5ae0e56af72463992689e9bd17323bd5 100644 (file)
@@ -48,6 +48,8 @@ import { GroupContentsResource } from '~/services/groups-service/groups-service'
 import { unionize, ofType, UnionOf, MatchCases } from '~/common/unionize';
 import { loadRunProcessPanel } from '~/store/run-process-panel/run-process-panel-actions';
 import { loadCollectionFiles } from '~/store/collection-panel/collection-panel-files/collection-panel-files-actions';
+import { collectionPanelActions } from "~/store/collection-panel/collection-panel-action";
+import { CollectionResource } from "~/models/collection";
 
 export const WORKBENCH_LOADING_SCREEN = 'workbenchLoadingScreen';
 
@@ -196,18 +198,21 @@ export const loadCollection = (uuid: string) =>
                 const match = await loadGroupContentsResource({ uuid, userUuid, services });
                 match({
                     OWNED: async collection => {
+                        dispatch(collectionPanelActions.SET_COLLECTION(collection as CollectionResource));
                         dispatch(updateResources([collection]));
                         await dispatch(activateSidePanelTreeItem(collection.ownerUuid));
                         dispatch(setSidePanelBreadcrumbs(collection.ownerUuid));
                         dispatch(loadCollectionFiles(collection.uuid));
                     },
                     SHARED: collection => {
+                        dispatch(collectionPanelActions.SET_COLLECTION(collection as CollectionResource));
                         dispatch(updateResources([collection]));
                         dispatch<any>(setSharedWithMeBreadcrumbs(collection.ownerUuid));
                         dispatch(activateSidePanelTreeItem(SidePanelTreeCategory.SHARED_WITH_ME));
                         dispatch(loadCollectionFiles(collection.uuid));
                     },
                     TRASHED: collection => {
+                        dispatch(collectionPanelActions.SET_COLLECTION(collection as CollectionResource));
                         dispatch(updateResources([collection]));
                         dispatch(setTrashBreadcrumbs(''));
                         dispatch(activateSidePanelTreeItem(SidePanelTreeCategory.TRASH));
index ff7b2c3d16300462cc8e799fe9390bf94661dd7b..fe93ef85bc098027d7d94dc7dc129d66510e8821 100644 (file)
@@ -8,11 +8,18 @@ import { ArvadosTheme } from '~/common/custom-theme';
 import { WorkflowResource } from '~/models/workflow';
 import { WorkflowIcon } from '~/components/icon/icon';
 import { WorkflowDetailsCard } from '../workflow-panel/workflow-description-card';
+import { SearchInput } from '~/components/search-input/search-input';
 
-type CssRules = 'rightGrid' | 'list' | 'listItem' | 'itemSelected' | 'listItemText' | 'listItemIcon';
+type CssRules = 'root' | 'searchGrid' | 'workflowDetailsGrid' | 'list' | 'listItem' | 'itemSelected' | 'listItemText' | 'listItemIcon';
 
 const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
-    rightGrid: {
+    root: {
+        alignSelf: 'flex-start'
+    },
+    searchGrid: {
+        marginBottom: theme.spacing.unit * 2
+    },
+    workflowDetailsGrid: {
         borderLeft: `1px solid ${theme.palette.grey["300"]}`
     },
     list: {
@@ -40,6 +47,7 @@ export interface RunProcessFirstStepDataProps {
 }
 
 export interface RunProcessFirstStepActionProps {
+    onSearch: (term: string) => void;
     onSetStep: (step: number) => void;
     onSetWorkflow: (workflow: WorkflowResource) => void;
 }
@@ -47,11 +55,11 @@ export interface RunProcessFirstStepActionProps {
 type RunProcessFirstStepProps = RunProcessFirstStepDataProps & RunProcessFirstStepActionProps & WithStyles<CssRules>;
 
 export const RunProcessFirstStep = withStyles(styles)(
-    ({ onSetStep, onSetWorkflow, workflows, selectedWorkflow, classes }: RunProcessFirstStepProps) =>
+    ({ onSearch, onSetStep, onSetWorkflow, workflows, selectedWorkflow, classes }: RunProcessFirstStepProps) =>
         <Grid container spacing={16}>
-            <Grid container item xs={6}>
-                <Grid item xs={12}>
-                    {/* TODO: add filters */}
+            <Grid container item xs={6} className={classes.root}>
+                <Grid item xs={12} className={classes.searchGrid}>
+                    <SearchInput value='' onSearch={onSearch} />
                 </Grid>
                 <Grid item xs={12}>
                     <List className={classes.list}>
@@ -69,7 +77,7 @@ export const RunProcessFirstStep = withStyles(styles)(
                     </List>
                 </Grid>
             </Grid>
-            <Grid item xs={6} className={classes.rightGrid}>
+            <Grid item xs={6} className={classes.workflowDetailsGrid}>
                 <WorkflowDetailsCard workflow={selectedWorkflow}/>
             </Grid>
             <Grid item xs={12}>
index da21d704d766e48577ecc96f9eb40d4b495998a2..1489527530fa427ea9e72f72a7ec316e7d41416a 100644 (file)
@@ -17,7 +17,7 @@ export type RunProcessPanelRootActionProps = RunProcessFirstStepActionProps & {
 
 type RunProcessPanelRootProps = RunProcessPanelRootDataProps & RunProcessPanelRootActionProps;
 
-export const RunProcessPanelRoot = ({ runProcess, currentStep, onSetStep, onSetWorkflow, workflows, selectedWorkflow }: RunProcessPanelRootProps) =>
+export const RunProcessPanelRoot = ({ runProcess, currentStep, onSearch, onSetStep, onSetWorkflow, workflows, selectedWorkflow }: RunProcessPanelRootProps) =>
     <Stepper activeStep={currentStep} orientation="vertical" elevation={2}>
         <Step>
             <StepLabel>Choose a workflow</StepLabel>
@@ -25,7 +25,8 @@ export const RunProcessPanelRoot = ({ runProcess, currentStep, onSetStep, onSetW
                 <RunProcessFirstStep
                     workflows={workflows}
                     selectedWorkflow={selectedWorkflow}
-                    onSetStep={onSetStep}
+                    onSearch={onSearch}
+                    onSetStep={onSetStep} 
                     onSetWorkflow={onSetWorkflow} />
             </StepContent>
         </Step>
index 68917c2cf94ae108383be387488e475fae0d249a..42324ab032455c023ddd0c752895f24ec63a8c97 100644 (file)
@@ -6,12 +6,12 @@ import { Dispatch } from 'redux';
 import { connect } from 'react-redux';
 import { RootState } from '~/store/store';
 import { RunProcessPanelRootDataProps, RunProcessPanelRootActionProps, RunProcessPanelRoot } from '~/views/run-process-panel/run-process-panel-root';
-import { goToStep, setWorkflow, runProcess } from '~/store/run-process-panel/run-process-panel-actions';
+import { goToStep, setWorkflow, runProcess, searchWorkflows } from '~/store/run-process-panel/run-process-panel-actions';
 import { WorkflowResource } from '~/models/workflow';
 
 const mapStateToProps = ({ runProcessPanel }: RootState): RunProcessPanelRootDataProps => {
     return {
-        workflows: runProcessPanel.workflows,
+        workflows: runProcessPanel.searchWorkflows,
         currentStep: runProcessPanel.currentStep,
         selectedWorkflow: runProcessPanel.selectedWorkflow
     };
@@ -26,6 +26,9 @@ const mapDispatchToProps = (dispatch: Dispatch): RunProcessPanelRootActionProps
     },
     runProcess: () => {
         dispatch<any>(runProcess);
+    },
+    onSearch: (term: string) => {
+        dispatch<any>(searchWorkflows(term));
     }
 });