Merge branch 'master' of git.curoverse.com:arvados-workbench2 into 14603-add-controll...
authorMichal Klobukowski <michal.klobukowski@contractors.roche.com>
Tue, 18 Dec 2018 12:55:13 +0000 (13:55 +0100)
committerMichal Klobukowski <michal.klobukowski@contractors.roche.com>
Tue, 18 Dec 2018 12:55:13 +0000 (13:55 +0100)
refs #2
14603

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

src/components/autocomplete/autocomplete.tsx
src/views-components/form-fields/search-bar-form-fields.tsx
src/views-components/resource-properties-form/property-key-field.tsx
src/views-components/resource-properties-form/property-value-field.tsx
src/views-components/search-bar/search-bar-view.tsx

index b250c7b8ecd43bfc89171e4a9de373bf0cd22ffa..4b19b77115b388e3613c6004013ef6501aded2b6 100644 (file)
@@ -152,6 +152,16 @@ export class Autocomplete<Value, Suggestion> extends React.Component<Autocomplet
 
     renderChips() {
         const { items, onDelete } = this.props;
+
+        /**
+         * If input startAdornment prop is not undefined, input's label will stay above the input.
+         * If there is not items, we want the label to go back to placeholder position.
+         * That why we return without a value instead of returning a result of a _map_ which is an empty array.
+         */
+        if (items.length === 0) {
+            return;
+        }
+
         return items.map(
             (item, index) =>
                 <Chip
index da0b12b55311fc045819c8f5d4b7641f0c6df045..85abbe19f3f266769bb70b9f1da0e03d847399f1 100644 (file)
@@ -3,7 +3,7 @@
 // SPDX-License-Identifier: AGPL-3.0
 
 import * as React from "react";
-import { Field, WrappedFieldProps, FieldArray } from 'redux-form';
+import { Field, WrappedFieldProps, FieldArray, formValues } 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';
@@ -14,6 +14,10 @@ import { SEARCH_BAR_ADVANCE_FORM_PICKER_ID } from '~/store/search-bar/search-bar
 import { SearchBarAdvancedPropertiesView } from '~/views-components/search-bar/search-bar-advanced-properties-view';
 import { TreeItem } from "~/components/tree/tree";
 import { ProjectsTreePickerItem } from "~/views-components/projects-tree-picker/generic-projects-tree-picker";
+import { PropertyKeyInput } from '~/views-components/resource-properties-form/property-key-field';
+import { PropertyValueInput, PropertyValueFieldProps } from '~/views-components/resource-properties-form/property-value-field';
+import { VocabularyProp, connectVocabulary } from '~/views-components/resource-properties-form/property-field-common';
+import { compose } from 'redux';
 
 export const SearchBarTypeField = () =>
     <Field
@@ -50,7 +54,7 @@ const ProjectsPicker = (props: WrappedFieldProps) =>
                 (_: any, { id }: TreeItem<ProjectsTreePickerItem>) => {
                     props.input.onChange(id);
                 }
-            }/>
+            } />
     </div>;
 
 export const SearchBarTrashField = () =>
@@ -74,17 +78,22 @@ export const SearchBarPropertiesField = () =>
         name="properties"
         component={SearchBarAdvancedPropertiesView} />;
 
-export const SearchBarKeyField = () =>
-    <Field
-        name='key'
-        component={TextField}
-        label="Key" />;
+export const SearchBarKeyField = connectVocabulary(
+    ({ vocabulary }: VocabularyProp) =>
+        <Field
+            name='key'
+            component={PropertyKeyInput}
+            vocabulary={vocabulary} />);
 
-export const SearchBarValueField = () =>
-    <Field
-        name='value'
-        component={TextField}
-        label="Value" />;
+export const SearchBarValueField = compose(
+    connectVocabulary,
+    formValues({ propertyKey: 'key' })
+)(
+    (props: PropertyValueFieldProps) =>
+        <Field
+            name='value'
+            component={PropertyValueInput}
+            {...props} />);
 
 export const SearchBarSaveSearchField = () =>
     <Field
index e6708a394fd91445a7afbe6aba972b722c4638b2..3fb2d377aeff56574a06a919f2f33e3aea5f142e 100644 (file)
@@ -20,7 +20,7 @@ export const PropertyKeyField = connectVocabulary(
             vocabulary={vocabulary}
             validate={getValidation(vocabulary)} />);
 
-const PropertyKeyInput = ({ vocabulary, ...props }: WrappedFieldProps & VocabularyProp) =>
+export const PropertyKeyInput = ({ vocabulary, ...props }: WrappedFieldProps & VocabularyProp) =>
     <Autocomplete
         label='Key'
         suggestions={getSuggestions(props.input.value, vocabulary)}
index db2db3f7ece5e63ed7656f2f3a417996b3238dad..13dcfeb544278acf62db0b41aca9c113333043d3 100644 (file)
@@ -15,7 +15,7 @@ interface PropertyKeyProp {
     propertyKey: string;
 }
 
-type PropertyValueFieldProps = VocabularyProp & PropertyKeyProp;
+export type PropertyValueFieldProps = VocabularyProp & PropertyKeyProp;
 
 export const PROPERTY_VALUE_FIELD_NAME = 'value';
 
@@ -30,7 +30,7 @@ export const PropertyValueField = compose(
             validate={getValidation(props)}
             {...props} />);
 
-const PropertyValueInput = ({ vocabulary, propertyKey, ...props }: WrappedFieldProps & PropertyValueFieldProps) =>
+export const PropertyValueInput = ({ vocabulary, propertyKey, ...props }: WrappedFieldProps & PropertyValueFieldProps) =>
     <Autocomplete
         label='Value'
         suggestions={getSuggestions(props.input.value, propertyKey, vocabulary)}
index 51ea3fa14793fe9ff3fe19b939b867c3db52a497..09b75bbf65893a7c811a75f563eebc8ffcd9f8c4 100644 (file)
@@ -11,7 +11,7 @@ import {
     WithStyles,
     Tooltip,
     InputAdornment, Input,
-    ClickAwayListener
+    Popover,
 } from '@material-ui/core';
 import SearchIcon from '@material-ui/icons/Search';
 import ArrowDropDownIcon from '@material-ui/icons/ArrowDropDown';
@@ -144,49 +144,98 @@ const handleDropdownClick = (e: React.MouseEvent, props: SearchBarViewProps) =>
 };
 
 export const SearchBarView = withStyles(styles)(
-    (props : SearchBarViewProps) => {
-        const { classes, isPopoverOpen } = props;
-        return (
-            <ClickAwayListener onClickAway={props.closeView}>
-                <Paper className={isPopoverOpen ? classes.containerSearchViewOpened : classes.container} >
-                    <form onSubmit={props.onSubmit}>
-                        <Input
-                            className={classes.input}
-                            onChange={props.onChange}
-                            placeholder="Search"
-                            value={props.searchValue}
-                            fullWidth={true}
-                            disableUnderline={true}
-                            onClick={e => handleInputClick(e, props)}
-                            onKeyDown={e => handleKeyDown(e, props)}
-                            startAdornment={
-                                <InputAdornment position="start">
-                                    <Tooltip title='Search'>
-                                        <IconButton type="submit">
-                                            <SearchIcon />
-                                        </IconButton>
-                                    </Tooltip>
-                                </InputAdornment>
-                            }
-                            endAdornment={
-                                <InputAdornment position="end">
-                                    <Tooltip title='Advanced search'>
-                                        <IconButton onClick={e => handleDropdownClick(e, props)}>
-                                            <ArrowDropDownIcon />
-                                        </IconButton>
-                                    </Tooltip>
-                                </InputAdornment>
-                            } />
-                    </form>
-                    <div className={classes.view}>
-                        {isPopoverOpen && getView({...props})}
+    class SearchBarView extends React.Component<SearchBarViewProps> {
+
+        viewAnchorRef = React.createRef<HTMLDivElement>();
+
+        render() {
+            const { children, ...props } = this.props;
+            const { classes, isPopoverOpen } = props;
+            return (
+                <Paper
+                    className={isPopoverOpen ? classes.containerSearchViewOpened : classes.container}>
+                    <div ref={this.viewAnchorRef}>
+                        <form onSubmit={props.onSubmit}>
+                            <SearchInput {...props} />
+                        </form>
                     </div>
+                    <SearchViewContainer
+                        {...props}
+                        width={this.getViewWidth()}
+                        anchorEl={this.viewAnchorRef.current}>
+                        {
+                            getView({ ...props })
+                        }
+                    </SearchViewContainer>
                 </Paper >
-            </ClickAwayListener>
-        );
+            );
+        }
+
+        getViewWidth() {
+            const { current } = this.viewAnchorRef;
+            return current ? current.offsetWidth : 0;
+        }
     }
+
 );
 
+const SearchInput = (props: SearchBarViewProps) => {
+    const { classes } = props;
+    return <Input
+        className={classes.input}
+        onChange={props.onChange}
+        placeholder="Search"
+        value={props.searchValue}
+        fullWidth={true}
+        disableUnderline={true}
+        onClick={e => handleInputClick(e, props)}
+        onKeyDown={e => handleKeyDown(e, props)}
+        startAdornment={
+            <InputAdornment position="start">
+                <Tooltip title='Search'>
+                    <IconButton type="submit">
+                        <SearchIcon />
+                    </IconButton>
+                </Tooltip>
+            </InputAdornment>
+        }
+        endAdornment={
+            <InputAdornment position="end">
+                <Tooltip title='Advanced search'>
+                    <IconButton onClick={e => handleDropdownClick(e, props)}>
+                        <ArrowDropDownIcon />
+                    </IconButton>
+                </Tooltip>
+            </InputAdornment>
+        } />;
+};
+
+const SearchViewContainer = (props: SearchBarViewProps & { width: number, anchorEl: HTMLElement | null, children: React.ReactNode }) =>
+    <Popover
+        PaperProps={{
+            style: {
+                width: props.width,
+                borderRadius: '0 0 4px 4px',
+            }
+        }}
+        anchorEl={props.anchorEl}
+        anchorOrigin={{
+            vertical: 'bottom',
+            horizontal: 'center',
+        }}
+        transformOrigin={{
+            vertical: 'top',
+            horizontal: 'center',
+        }}
+        disableAutoFocus
+        open={props.isPopoverOpen}
+        onClose={props.closeView}>
+        {
+            props.children
+        }
+    </Popover>;
+
+
 const getView = (props: SearchBarViewProps) => {
     switch (props.currentView) {
         case SearchView.AUTOCOMPLETE: