merge master
authorPawel Kowalczyk <pawel.kowalczyk@contractors.roche.com>
Tue, 16 Oct 2018 08:49:47 +0000 (10:49 +0200)
committerPawel Kowalczyk <pawel.kowalczyk@contractors.roche.com>
Tue, 16 Oct 2018 08:49:47 +0000 (10:49 +0200)
Feature #14277

Arvados-DCO-1.1-Signed-off-by: Pawel Kowalczyk <pawel.kowalczyk@contractors.roche.com>

package.json
src/index.tsx
src/models/search-bar.ts
src/store/search-bar/search-bar-actions.ts
src/views-components/form-fields/search-bar-form-fields.tsx
src/views-components/search-bar/search-bar-advanced-properties-view.tsx [new file with mode: 0644]
src/views-components/search-bar/search-bar-advanced-view.tsx

index 94f00dc43cad86c0dc2a7ad794e8f5f47f3d8ea8..8ed84dd9b818b121be780d176760b7cc3fc40922 100644 (file)
@@ -44,7 +44,7 @@
   "scripts": {
     "start": "react-scripts-ts start",
     "build": "REACT_APP_BUILD_NUMBER=$BUILD_NUMBER REACT_APP_GIT_COMMIT=$GIT_COMMIT react-scripts-ts build",
-    "test": "react-scripts-ts test --env=jsdom",
+    "test": "CI=true react-scripts-ts test --env=jsdom",
     "eject": "react-scripts-ts eject",
     "lint": "tslint src/** -t verbose"
   },
index 62557f6150113632ece7ecf60faf7cabe8afd540..0fc72217ba13bed4e0c4a7f23e3acd5897d8936e 100644 (file)
@@ -46,6 +46,7 @@ import { setBuildInfo } from '~/store/app-info/app-info-actions';
 import { getBuildInfo } from '~/common/app-info';
 import { DragDropContextProvider } from 'react-dnd';
 import HTML5Backend from 'react-dnd-html5-backend';
+import { initAdvanceFormProjectsTree } from '~/store/search-bar/search-bar-actions';
 
 console.log(`Starting arvados [${getBuildInfo()}]`);
 
@@ -114,6 +115,8 @@ const initListener = (history: History, store: RootStore, services: ServiceRepos
             initWebSocket(config, services.authService, store);
             await store.dispatch(loadWorkbench());
             addRouteChangeHandlers(history, store);
+            // ToDo: move to searchBar component
+            store.dispatch(initAdvanceFormProjectsTree());
         }
     };
 };
index 9fadc2af4a9d616f93ccbff5829799e39cf18bda..4df5c38f2527278babfa187210681d89310685cb 100644 (file)
@@ -4,15 +4,21 @@
 
 import { ResourceKind } from '~/models/resource';
 
-export interface SearchBarAdvanceFormData {
+export type SearchBarAdvanceFormData = {
     type?: ResourceKind;
     cluster?: ClusterObjectType;
-    project?: string;
+    projectUuid?: string;
     inTrash: boolean;
     dateFrom: string;
     dateTo: string;
     saveQuery: boolean;
     searchQuery: string;
+    properties: PropertyValues[];
+} & PropertyValues;
+
+export interface PropertyValues {
+    key: string;
+    value: string;
 }
 
 export enum ClusterObjectType {
index f32c1287f7d7197eff3d601692bf3db26f94de77..9ddfc9c5175b0d6331ae471ae32f76f423e908af 100644 (file)
@@ -5,7 +5,9 @@
 import { unionize, ofType, UnionOf } from "~/common/unionize";
 import { GroupContentsResource, GroupContentsResourcePrefix } from '~/services/groups-service/groups-service';
 import { Dispatch } from 'redux';
+import { change, arrayPush } from 'redux-form';
 import { RootState } from '~/store/store';
+import { initUserProject } from '~/store/tree-picker/tree-picker-actions';
 import { ServiceRepository } from '~/services/services';
 import { FilterBuilder } from "~/services/api/filter-builder";
 import { ResourceKind } from '~/models/resource';
@@ -13,8 +15,8 @@ import { GroupClass } from '~/models/group';
 import { SearchView } from '~/store/search-bar/search-bar-reducer';
 import { navigateToSearchResults, navigateTo } from '~/store/navigation/navigation-action';
 import { snackbarActions, SnackbarKind } from '~/store/snackbar/snackbar-actions';
-import { SearchBarAdvanceFormData } from '~/models/search-bar';
 import { initialize } from 'redux-form';
+import { SearchBarAdvanceFormData, PropertyValues } from '~/models/search-bar';
 
 export const searchBarActions = unionize({
     SET_CURRENT_VIEW: ofType<string>(),
@@ -29,6 +31,8 @@ export type SearchBarActions = UnionOf<typeof searchBarActions>;
 
 export const SEARCH_BAR_ADVANCE_FORM_NAME = 'searchBarAdvanceFormName';
 
+export const SEARCH_BAR_ADVANCE_FORM_PICKER_ID = 'searchBarAdvanceFormPickerId';
+
 export const goToView = (currentView: string) => searchBarActions.SET_CURRENT_VIEW(currentView);
 
 export const saveRecentQuery = (query: string) =>
@@ -116,3 +120,18 @@ export const getFilters = (filterName: string, searchValue: string): string => {
         .addEqual('groupClass', GroupClass.PROJECT, GroupContentsResourcePrefix.PROJECT)
         .getFilters();
 };
+
+export const initAdvanceFormProjectsTree = () => 
+    (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
+        dispatch<any>(initUserProject(SEARCH_BAR_ADVANCE_FORM_PICKER_ID));
+    };
+
+export const changeAdvanceFormProperty = (property: string, value: PropertyValues[] | string = '') => 
+    (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
+        dispatch(change(SEARCH_BAR_ADVANCE_FORM_NAME, property, value));
+    };
+
+export const updateAdvanceFormProperties = (propertyValues: PropertyValues) => 
+    (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
+        dispatch(arrayPush(SEARCH_BAR_ADVANCE_FORM_NAME, 'properties', propertyValues));
+    };
\ No newline at end of file
index 4a7c4b11f5bdce86940fc7820abfd498b3ea778f..fbb2cbac1eea7b3820d79ca7f03ec059f4d48cd7 100644 (file)
@@ -3,12 +3,14 @@
 // SPDX-License-Identifier: AGPL-3.0
 
 import * as React from "react";
-import { Field } from 'redux-form';
+import { Field, WrappedFieldProps } from 'redux-form';
 import { TextField, DateTextField } from "~/components/text-field/text-field";
 import { CheckboxField } from '~/components/checkbox-field/checkbox-field';
 import { NativeSelectField } from '~/components/select-field/select-field';
 import { ResourceKind } from '~/models/resource';
 import { ClusterObjectType } from '~/models/search-bar';
+import { HomeTreePicker } from '~/views-components/projects-tree-picker/home-tree-picker';
+import { SEARCH_BAR_ADVANCE_FORM_PICKER_ID } from '~/store/search-bar/search-bar-actions';
 
 export const SearchBarTypeField = () =>
     <Field
@@ -33,7 +35,14 @@ export const SearchBarClusterField = () =>
         ]} />;
 
 export const SearchBarProjectField = () => 
-    <div>Box</div>;
+    <Field
+        name='projectUuid'
+        component={ProjectsPicker} />;
+
+const ProjectsPicker = (props: WrappedFieldProps) =>
+    <div style={{ height: '100px', display: 'flex', flexDirection: 'column', overflow: 'overlay' }}>
+        <HomeTreePicker pickerId={SEARCH_BAR_ADVANCE_FORM_PICKER_ID} />
+    </div>;
 
 export const SearchBarTrashField = () => 
     <Field
diff --git a/src/views-components/search-bar/search-bar-advanced-properties-view.tsx b/src/views-components/search-bar/search-bar-advanced-properties-view.tsx
new file mode 100644 (file)
index 0000000..01fc6a1
--- /dev/null
@@ -0,0 +1,101 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import * as React from 'react';
+import { Dispatch } from 'redux';
+import { connect } from 'react-redux';
+import { InjectedFormProps, formValueSelector } from 'redux-form';
+import { Grid, withStyles, StyleRulesCallback, WithStyles, Button } from '@material-ui/core';
+import { RootState } from '~/store/store';
+import { 
+    SEARCH_BAR_ADVANCE_FORM_NAME, 
+    changeAdvanceFormProperty, 
+    updateAdvanceFormProperties 
+} from '~/store/search-bar/search-bar-actions';
+import { PropertyValues } from '~/models/search-bar';
+import { ArvadosTheme } from '~/common/custom-theme';
+import { SearchBarKeyField, SearchBarValueField } from '~/views-components/form-fields/search-bar-form-fields';
+import { Chips } from '~/components/chips/chips';
+
+type CssRules = 'label' | 'button';
+
+const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
+    label: {
+        color: theme.palette.grey["500"],
+        fontSize: '0.8125rem',
+        alignSelf: 'center'
+    },
+    button: {
+        boxShadow: 'none'
+    }
+});
+
+interface SearchBarAdvancedPropertiesViewDataProps {
+    submitting: boolean;
+    invalid: boolean;
+    pristine: boolean;
+    propertyValues: PropertyValues;
+    fields: PropertyValues[];
+}
+
+interface SearchBarAdvancedPropertiesViewActionProps {
+    setProps: () => void;
+    addProp: (propertyValues: PropertyValues) => void;
+    getAllFields: (propertyValues: PropertyValues[]) => PropertyValues[] | [];
+}
+
+type SearchBarAdvancedPropertiesViewProps = SearchBarAdvancedPropertiesViewDataProps 
+    & SearchBarAdvancedPropertiesViewActionProps 
+    & InjectedFormProps & WithStyles<CssRules>;
+
+const selector = formValueSelector(SEARCH_BAR_ADVANCE_FORM_NAME);
+const mapStateToProps = (state: RootState) => {
+    return {
+        propertyValues: selector(state, 'key', 'value')
+    };
+};
+
+const mapDispatchToProps = (dispatch: Dispatch) => ({
+    setProps: (propertyValues: PropertyValues[]) => {
+        dispatch<any>(changeAdvanceFormProperty('properties', propertyValues));
+    },
+    addProp: (propertyValues: PropertyValues) => {
+        dispatch<any>(updateAdvanceFormProperties(propertyValues));
+        dispatch<any>(changeAdvanceFormProperty('key'));
+        dispatch<any>(changeAdvanceFormProperty('value'));
+    },
+    getAllFields: (fields: any) => {
+        return fields.getAll() || [];
+    }
+});
+
+export const SearchBarAdvancedPropertiesView = connect(mapStateToProps, mapDispatchToProps)(
+    withStyles(styles)(
+        ({ classes, fields, propertyValues, setProps, addProp, getAllFields }: SearchBarAdvancedPropertiesViewProps) =>
+            <Grid container item xs={12} spacing={16}>
+                <Grid item xs={2} className={classes.label}>Properties</Grid>
+                <Grid item xs={4}>
+                    <SearchBarKeyField />
+                </Grid>
+                <Grid item xs={4}>
+                    <SearchBarValueField />
+                </Grid>
+                <Grid container item xs={2} justify='flex-end' alignItems="center">
+                    <Button className={classes.button} onClick={() => addProp(propertyValues)}
+                        color="primary"
+                        size='small'
+                        variant="contained">
+                        Add
+                    </Button>
+                </Grid>
+                <Grid item xs={2} />
+                <Grid container item xs={10} spacing={8}>
+                    <Chips values={getAllFields(fields)} 
+                        deletable
+                        onChange={setProps} 
+                        getLabel={(field: PropertyValues) => `${field.key}: ${field.value}`} />
+                </Grid>
+            </Grid>
+    )
+);
\ No newline at end of file
index 4d9bd97bcfaec524539757d87ff7c956e2d78177..1a836a0fea9a8146dc2fdda876f44401b019620f 100644 (file)
@@ -3,7 +3,7 @@
 // SPDX-License-Identifier: AGPL-3.0
 
 import * as React from 'react';
-import { reduxForm, reset, InjectedFormProps } from 'redux-form';
+import { reduxForm, reset, InjectedFormProps, FieldArray } from 'redux-form';
 import { compose, Dispatch } from 'redux';
 import { Paper, StyleRulesCallback, withStyles, WithStyles, Button, Grid, IconButton, CircularProgress } from '@material-ui/core';
 import { SearchView } from '~/store/search-bar/search-bar-reducer';
@@ -11,9 +11,10 @@ import { SEARCH_BAR_ADVANCE_FORM_NAME, saveQuery } from '~/store/search-bar/sear
 import { ArvadosTheme } from '~/common/custom-theme';
 import { CloseIcon } from '~/components/icon/icon';
 import { SearchBarAdvanceFormData } from '~/models/search-bar';
+import { SearchBarAdvancedPropertiesView } from './search-bar-advanced-properties-view';
 import { 
     SearchBarTypeField, SearchBarClusterField, SearchBarProjectField, SearchBarTrashField, 
-    SearchBarDataFromField, SearchBarDataToField, SearchBarKeyField, SearchBarValueField,
+    SearchBarDataFromField, SearchBarDataToField,
     SearchBarSaveSearchField, SearchBarQuerySearchField
 } from '~/views-components/form-fields/search-bar-form-fields';
 
@@ -83,7 +84,7 @@ export const SearchBarAdvancedView = compose(
         }
     }),
     withStyles(styles))(
-        ({ classes, setView, handleSubmit, invalid, submitting, pristine }: SearchBarAdvancedViewProps) =>
+        ({ classes, setView, handleSubmit, submitting }: SearchBarAdvancedViewProps) =>
             <Paper className={classes.searchView}>
                 <form onSubmit={handleSubmit}>
                     <Grid container direction="column" justify="flex-start" alignItems="flex-start">
@@ -126,23 +127,7 @@ export const SearchBarAdvancedView = compose(
                             </Grid>
                         </Grid>
                         <Grid container item xs={12} className={classes.container}>
-                            <Grid container item xs={12} spacing={16}>
-                                <Grid item xs={2} className={classes.label}>Properties</Grid>
-                                <Grid item xs={4}>
-                                    <SearchBarKeyField />
-                                </Grid>
-                                <Grid item xs={4}>
-                                    <SearchBarValueField />
-                                </Grid>
-                                <Grid container item xs={2} justify='flex-end' alignItems="center">
-                                    <Button className={classes.button}
-                                        color="primary"
-                                        size='small'
-                                        variant="contained">
-                                        Add
-                                    </Button>
-                                </Grid>
-                            </Grid>
+                            <FieldArray name="properties" component={SearchBarAdvancedPropertiesView} />
                             <Grid container item xs={12} justify="flex-start" alignItems="center" spacing={16}>
                                 <Grid item xs={2} className={classes.label} />
                                 <Grid item xs={4}>