[ARVADOS-WORKBENCH2] created: 1.2.0-645-ge2a464e

Git user git at public.curoverse.com
Sat Oct 13 18:01:28 EDT 2018


        at  e2a464e9087120c7569976eb9a172a91d2ec0f09 (commit)


commit e2a464e9087120c7569976eb9a172a91d2ec0f09
Author: Michal Klobukowski <michal.klobukowski at contractors.roche.com>
Date:   Sat Oct 13 23:59:59 2018 +0200

    Update styles to match mui form
    
    Feature #13862
    
    Arvados-DCO-1.1-Signed-off-by: Michal Klobukowski <michal.klobukowski at contractors.roche.com>

diff --git a/src/components/chips-input/chips-input.tsx b/src/components/chips-input/chips-input.tsx
index 96e7b70..12932c5 100644
--- a/src/components/chips-input/chips-input.tsx
+++ b/src/components/chips-input/chips-input.tsx
@@ -4,31 +4,34 @@
 
 import * as React from 'react';
 import { Chips } from '~/components/chips/chips';
-import { Input, withStyles, WithStyles } from '@material-ui/core';
+import { Input as MuiInput, withStyles, WithStyles } from '@material-ui/core';
 import { StyleRulesCallback } from '@material-ui/core/styles';
+import { InputProps } from '@material-ui/core/Input';
 
 interface ChipsInputProps<Value> {
-    values: Value[];
+    value: Value[];
     getLabel?: (value: Value) => string;
     onChange: (value: Value[]) => void;
     createNewValue: (value: string) => Value;
+    inputComponent?: React.ComponentType<InputProps>;
+    inputProps?: InputProps;
 }
 
 type CssRules = 'chips' | 'input' | 'inputContainer';
 
-const styles: StyleRulesCallback = () => ({
+const styles: StyleRulesCallback = ({ spacing }) => ({
     chips: {
-        minHeight: '40px',
+        minHeight: spacing.unit * 5,
         zIndex: 1,
         position: 'relative',
     },
     input: {
-        position: 'relative',
-        top: '-5px',
         zIndex: 1,
+        marginBottom: 8,
+        position: 'relative',
     },
     inputContainer: {
-        top: '-24px',
+        marginTop: -34
     },
 });
 
@@ -58,13 +61,13 @@ export const ChipsInput = withStyles(styles)(
             if (this.state.text) {
                 const newValue = this.props.createNewValue(this.state.text);
                 this.setState({ text: '' });
-                this.props.onChange([...this.props.values, newValue]);
+                this.props.onChange([...this.props.value, newValue]);
             }
         }
 
         deleteLastValue = () => {
-            if (this.state.text.length === 0 && this.props.values.length > 0) {
-                this.props.onChange(this.props.values.slice(0, -1));
+            if (this.state.text.length === 0 && this.props.value.length > 0) {
+                this.props.onChange(this.props.value.slice(0, -1));
             }
         }
 
@@ -77,7 +80,7 @@ export const ChipsInput = withStyles(styles)(
 
         getInputStyles = (): React.CSSProperties => ({
             width: this.filler.current
-                ? this.filler.current.offsetWidth + 8
+                ? this.filler.current.offsetWidth
                 : '100%',
             right: this.filler.current
                 ? `calc(${this.filler.current.offsetWidth}px - 100%)`
@@ -91,27 +94,40 @@ export const ChipsInput = withStyles(styles)(
 
         render() {
             return <>
-                <div className={this.props.classes.chips}>
-                    <Chips
-                        {...this.props}
-                        filler={<div ref={this.filler} />}
-                    />
-                </div>
-                <Input
-                    value={this.state.text}
-                    onChange={this.setText}
-                    onKeyDown={this.handleKeyPress}
-                    inputProps={{
-                        className: this.props.classes.input,
-                        style: this.getInputStyles(),
-                    }}
-                    fullWidth
-                    className={this.props.classes.inputContainer} />
+                {this.renderChips()}
+                {this.renderInput()}
             </>;
         }
 
+        renderChips() {
+            const { classes, value, ...props } = this.props;
+            return <div className={classes.chips}>
+                <Chips
+                    {...props}
+                    values={value}
+                    filler={<div ref={this.filler} />}
+                />
+            </div>;
+        }
+
+        renderInput() {
+            const { inputProps: InputProps, inputComponent: Input = MuiInput, classes } = this.props;
+            return <Input
+                {...InputProps}
+                value={this.state.text}
+                onChange={this.setText}
+                onKeyDown={this.handleKeyPress}
+                inputProps={{
+                    ...(InputProps && InputProps.inputProps),
+                    className: classes.input,
+                    style: this.getInputStyles(),
+                }}
+                fullWidth
+                className={classes.inputContainer} />;
+        }
+
         componentDidUpdate(prevProps: ChipsInputProps<Value>) {
-            if (prevProps.values !== this.props.values) {
+            if (prevProps.value !== this.props.value) {
                 this.updateCursorPosition();
             }
         }
diff --git a/src/components/chips/chips.tsx b/src/components/chips/chips.tsx
index c63b584..8f597de 100644
--- a/src/components/chips/chips.tsx
+++ b/src/components/chips/chips.tsx
@@ -3,94 +3,104 @@
 // SPDX-License-Identifier: AGPL-3.0
 
 import * as React from 'react';
-import { Chip, Grid } from '@material-ui/core';
+import { Chip, Grid, StyleRulesCallback, withStyles } from '@material-ui/core';
 import { DragSource, DragSourceSpec, DragSourceCollector, ConnectDragSource, DropTarget, DropTargetSpec, DropTargetCollector, ConnectDropTarget } from 'react-dnd';
 import { compose } from 'lodash/fp';
+import { WithStyles } from '@material-ui/core/styles';
 interface ChipsProps<Value> {
     values: Value[];
     getLabel?: (value: Value) => string;
     filler?: React.ReactNode;
     onChange: (value: Value[]) => void;
 }
-export class Chips<Value> extends React.Component<ChipsProps<Value>> {
-    render() {
-        const { values, filler } = this.props;
-        return <Grid container spacing={8}>
-            {values.map(this.renderChip)}
-            {filler && <Grid item xs>{filler}</Grid>}
-        </Grid>;
-    }
 
-    renderChip = (value: Value, index: number) =>
-        <Grid item key={index}>
-            <this.chip {...{ value }} />
-        </Grid>
+type CssRules = 'root';
 
-    type = 'chip';
+const styles: StyleRulesCallback<CssRules> = ({ spacing }) => ({
+    root: {
+        margin: `0px -${spacing.unit / 2}px`,
+    },
+});
+export const Chips = withStyles(styles)(
+    class Chips<Value> extends React.Component<ChipsProps<Value> & WithStyles<CssRules>> {
+        render() {
+            const { values, filler } = this.props;
+            return <Grid container spacing={8} className={this.props.classes.root}>
+                {values.map(this.renderChip)}
+                {filler && <Grid item xs>{filler}</Grid>}
+            </Grid>;
+        }
+
+        renderChip = (value: Value, index: number) =>
+            <Grid item key={index}>
+                <this.chip {...{ value }} />
+            </Grid>
+
+        type = 'chip';
 
-    dragSpec: DragSourceSpec<DraggableChipProps<Value>, { value: Value }> = {
-        beginDrag: ({ value }) => ({ value }),
-        endDrag: ({ value: dragValue }, monitor) => {
-            const result = monitor.getDropResult();
-            if (result) {
-                const { value: dropValue } = monitor.getDropResult();
-                const dragIndex = this.props.values.indexOf(dragValue);
-                const dropIndex = this.props.values.indexOf(dropValue);
-                const newValues = this.props.values.slice(0);
-                if (dragIndex < dropIndex) {
-                    newValues.splice(dragIndex, 1);
-                    newValues.splice(dropIndex - 1 || 0, 0, dragValue);
-                } else if (dragIndex > dropIndex) {
-                    newValues.splice(dragIndex, 1);
-                    newValues.splice(dropIndex, 0, dragValue);
+        dragSpec: DragSourceSpec<DraggableChipProps<Value>, { value: Value }> = {
+            beginDrag: ({ value }) => ({ value }),
+            endDrag: ({ value: dragValue }, monitor) => {
+                const result = monitor.getDropResult();
+                if (result) {
+                    const { value: dropValue } = monitor.getDropResult();
+                    const dragIndex = this.props.values.indexOf(dragValue);
+                    const dropIndex = this.props.values.indexOf(dropValue);
+                    const newValues = this.props.values.slice(0);
+                    if (dragIndex < dropIndex) {
+                        newValues.splice(dragIndex, 1);
+                        newValues.splice(dropIndex - 1 || 0, 0, dragValue);
+                    } else if (dragIndex > dropIndex) {
+                        newValues.splice(dragIndex, 1);
+                        newValues.splice(dropIndex, 0, dragValue);
+                    }
+                    this.props.onChange(newValues);
                 }
-                this.props.onChange(newValues);
             }
-        }
-    };
+        };
 
-    dragCollector: DragSourceCollector<{}> = connect => ({
-        connectDragSource: connect.dragSource(),
-    })
+        dragCollector: DragSourceCollector<{}> = connect => ({
+            connectDragSource: connect.dragSource(),
+        })
 
-    dropSpec: DropTargetSpec<DraggableChipProps<Value>> = {
-        drop: ({ value }) => ({ value }),
-    };
+        dropSpec: DropTargetSpec<DraggableChipProps<Value>> = {
+            drop: ({ value }) => ({ value }),
+        };
 
-    dropCollector: DropTargetCollector<{}> = (connect, monitor) => ({
-        connectDropTarget: connect.dropTarget(),
-        isOver: monitor.isOver(),
-    })
-    chip = compose(
-        DragSource(this.type, this.dragSpec, this.dragCollector),
-        DropTarget(this.type, this.dropSpec, this.dropCollector),
-    )(
-        ({ connectDragSource, connectDropTarget, isOver, value }: DraggableChipProps<Value> & CollectedProps) =>
-            compose(
-                connectDragSource,
-                connectDropTarget,
-            )(
-                <span>
-                    <Chip
-                        color={isOver ? 'primary' : 'default'}
-                        onDelete={this.deleteValue(value)}
-                        label={this.props.getLabel ?
-                            this.props.getLabel(value)
-                            : typeof value === 'object'
-                                ? JSON.stringify(value)
-                                : value} />
-                </span>
-            )
-    );
+        dropCollector: DropTargetCollector<{}> = (connect, monitor) => ({
+            connectDropTarget: connect.dropTarget(),
+            isOver: monitor.isOver(),
+        })
+        chip = compose(
+            DragSource(this.type, this.dragSpec, this.dragCollector),
+            DropTarget(this.type, this.dropSpec, this.dropCollector),
+        )(
+            ({ connectDragSource, connectDropTarget, isOver, value }: DraggableChipProps<Value> & CollectedProps) =>
+                compose(
+                    connectDragSource,
+                    connectDropTarget,
+                )(
+                    <span>
+                        <Chip
+                            color={isOver ? 'primary' : 'default'}
+                            onDelete={this.deleteValue(value)}
+                            label={this.props.getLabel ?
+                                this.props.getLabel(value)
+                                : typeof value === 'object'
+                                    ? JSON.stringify(value)
+                                    : value} />
+                    </span>
+                )
+        );
 
-    deleteValue = (value: Value) => () => {
-        const { values } = this.props;
-        const index = values.indexOf(value);
-        const newValues = values.slice(0);
-        newValues.splice(index, 1);
-        this.props.onChange(newValues);
-    }
-}
+        deleteValue = (value: Value) => () => {
+            const { values } = this.props;
+            const index = values.indexOf(value);
+            const newValues = values.slice(0);
+            newValues.splice(index, 1);
+            this.props.onChange(newValues);
+        }
+    });
 
 interface CollectedProps {
     connectDragSource: ConnectDragSource;
diff --git a/src/views/run-process-panel/inputs/string-array-input.tsx b/src/views/run-process-panel/inputs/string-array-input.tsx
index 3b29d1a..dafd946 100644
--- a/src/views/run-process-panel/inputs/string-array-input.tsx
+++ b/src/views/run-process-panel/inputs/string-array-input.tsx
@@ -10,6 +10,7 @@ import { GenericInputProps, GenericInput } from '~/views/run-process-panel/input
 import { ChipsInput } from '~/components/chips-input/chips-input';
 import { identity } from 'lodash';
 import { createSelector } from 'reselect';
+import { Input } from '@material-ui/core';
 
 export interface StringArrayInputProps {
     input: StringArrayCommandInputParameter;
@@ -36,15 +37,19 @@ const required = (value: string[]) =>
 
 const StringArrayInputComponent = (props: GenericInputProps) =>
     <GenericInput
-        component={Input}
+        component={InputComponent}
         {...props} />;
 
-class Input extends React.PureComponent<GenericInputProps>{
+class InputComponent extends React.PureComponent<GenericInputProps>{
     render() {
         return <ChipsInput
-            values={this.props.input.value}
+            value={this.props.input.value}
             onChange={this.handleChange}
-            createNewValue={identity} />;
+            createNewValue={identity}
+            inputComponent={Input}
+            inputProps={{
+                error: this.props.meta.error,
+            }} />;
     }
 
     handleChange = (values: {}[]) => {

commit c72c1ba8048825f15d864753dd247080e29f227b
Author: Michal Klobukowski <michal.klobukowski at contractors.roche.com>
Date:   Sat Oct 13 22:55:56 2018 +0200

    Fix infinite render loop
    
    Feature #13862
    
    Arvados-DCO-1.1-Signed-off-by: Michal Klobukowski <michal.klobukowski at contractors.roche.com>

diff --git a/package.json b/package.json
index d02cc38..8ed84dd 100644
--- a/package.json
+++ b/package.json
@@ -12,6 +12,7 @@
     "@types/react-dropzone": "4.2.2",
     "@types/react-highlight-words": "0.12.0",
     "@types/redux-form": "7.4.5",
+    "@types/reselect": "2.2.0",
     "@types/shell-quote": "1.6.0",
     "axios": "0.18.0",
     "classnames": "2.2.6",
diff --git a/src/components/chips-input/chips-input.tsx b/src/components/chips-input/chips-input.tsx
index 171641a..96e7b70 100644
--- a/src/components/chips-input/chips-input.tsx
+++ b/src/components/chips-input/chips-input.tsx
@@ -69,11 +69,10 @@ export const ChipsInput = withStyles(styles)(
         }
 
         updateCursorPosition = () => {
-            console.log('cursorPoristion');
             if (this.timeout) {
                 clearTimeout(this.timeout);
             }
-            this.timeout = setTimeout(() => this.forceUpdate());
+            this.timeout = setTimeout(() => this.setState({ ...this.state }));
         }
 
         getInputStyles = (): React.CSSProperties => ({
@@ -91,7 +90,6 @@ export const ChipsInput = withStyles(styles)(
         }
 
         render() {
-            console.log(`Render: ${this.props.values}`);
             return <>
                 <div className={this.props.classes.chips}>
                     <Chips
@@ -114,7 +112,6 @@ export const ChipsInput = withStyles(styles)(
 
         componentDidUpdate(prevProps: ChipsInputProps<Value>) {
             if (prevProps.values !== this.props.values) {
-                console.log('didUpdate');
                 this.updateCursorPosition();
             }
         }
diff --git a/src/views/run-process-panel/inputs/string-array-input.tsx b/src/views/run-process-panel/inputs/string-array-input.tsx
index 7454e2a..3b29d1a 100644
--- a/src/views/run-process-panel/inputs/string-array-input.tsx
+++ b/src/views/run-process-panel/inputs/string-array-input.tsx
@@ -7,7 +7,9 @@ import { isRequiredInput, StringArrayCommandInputParameter } from '~/models/work
 import { Field } from 'redux-form';
 import { ERROR_MESSAGE } from '~/validators/require';
 import { GenericInputProps, GenericInput } from '~/views/run-process-panel/inputs/generic-input';
-import { ChipsInput } from '../../../components/chips-input/chips-input';
+import { ChipsInput } from '~/components/chips-input/chips-input';
+import { identity } from 'lodash';
+import { createSelector } from 'reselect';
 
 export interface StringArrayInputProps {
     input: StringArrayCommandInputParameter;
@@ -17,19 +19,39 @@ export const StringArrayInput = ({ input }: StringArrayInputProps) =>
         name={input.id}
         commandInput={input}
         component={StringArrayInputComponent}
-        validate={[
-            isRequiredInput(input)
-                ? (value: string[]) => value.length > 0 ? undefined : ERROR_MESSAGE
-                : () => undefined,
-        ]} />;
+        validate={validationSelector(input)} />;
+
+
+const validationSelector = createSelector(
+    isRequiredInput,
+    isRequired => isRequired
+        ? [required]
+        : undefined
+);
+
+const required = (value: string[]) =>
+    value.length > 0
+        ? undefined
+        : ERROR_MESSAGE;
 
 const StringArrayInputComponent = (props: GenericInputProps) =>
     <GenericInput
         component={Input}
         {...props} />;
 
-const Input = (props: GenericInputProps) =>
-    <ChipsInput
-        values={props.input.value}
-        onChange={props.input.onChange}
-        createNewValue={v => v} />;
+class Input extends React.PureComponent<GenericInputProps>{
+    render() {
+        return <ChipsInput
+            values={this.props.input.value}
+            onChange={this.handleChange}
+            createNewValue={identity} />;
+    }
+
+    handleChange = (values: {}[]) => {
+        const { input, meta } = this.props;
+        if (!meta.touched) {
+            input.onBlur(values);
+        }
+        input.onChange(values);
+    }
+}
diff --git a/src/views/run-process-panel/run-process-inputs-form.tsx b/src/views/run-process-panel/run-process-inputs-form.tsx
index 14d8f63..912be0d 100644
--- a/src/views/run-process-panel/run-process-inputs-form.tsx
+++ b/src/views/run-process-panel/run-process-inputs-form.tsx
@@ -17,6 +17,7 @@ import { Grid, StyleRulesCallback, withStyles, WithStyles } from '@material-ui/c
 import { EnumInput } from './inputs/enum-input';
 import { DirectoryInput } from './inputs/directory-input';
 import { StringArrayInput } from './inputs/string-array-input';
+import { createStructuredSelector, createSelector } from 'reselect';
 
 export const RUN_PROCESS_INPUTS_FORM = 'runProcessInputsForm';
 
@@ -24,12 +25,24 @@ export interface RunProcessInputFormProps {
     inputs: CommandInputParameter[];
 }
 
+const inputsSelector = (props: RunProcessInputFormProps) =>
+    props.inputs;
+
+const initialValuesSelector = createSelector(
+    inputsSelector,
+    inputs => inputs.reduce(
+        (values, input) => ({ ...values, [input.id]: input.default }),
+        {}));
+
+const propsSelector = createStructuredSelector({
+    initialValues: initialValuesSelector,
+});
+
+const mapStateToProps = (_: any, props: RunProcessInputFormProps) =>
+    propsSelector(props);
+
 export const RunProcessInputsForm = compose(
-    connect((_: any, props: RunProcessInputFormProps) => ({
-        initialValues: props.inputs.reduce(
-            (values, input) => ({ ...values, [input.id]: input.default }),
-            {}),
-    })),
+    connect(mapStateToProps),
     reduxForm<WorkflowInputsData, RunProcessInputFormProps>({
         form: RUN_PROCESS_INPUTS_FORM
     }))(
diff --git a/src/views/run-process-panel/run-process-second-step.tsx b/src/views/run-process-panel/run-process-second-step.tsx
index 2585136..0b85638 100644
--- a/src/views/run-process-panel/run-process-second-step.tsx
+++ b/src/views/run-process-panel/run-process-second-step.tsx
@@ -12,6 +12,7 @@ import { RootState } from '~/store/store';
 import { isValid } from 'redux-form';
 import { RUN_PROCESS_INPUTS_FORM } from './run-process-inputs-form';
 import { RunProcessAdvancedForm } from './run-process-advanced-form';
+import { createSelector, createStructuredSelector } from 'reselect';
 
 export interface RunProcessSecondStepFormDataProps {
     inputs: CommandInputParameter[];
@@ -23,10 +24,15 @@ export interface RunProcessSecondStepFormActionProps {
     runProcess: () => void;
 }
 
-const mapStateToProps = (state: RootState): RunProcessSecondStepFormDataProps => ({
-    inputs: state.runProcessPanel.inputs,
-    valid: isValid(RUN_PROCESS_BASIC_FORM)(state) &&
-        isValid(RUN_PROCESS_INPUTS_FORM)(state),
+const inputsSelector = (state: RootState) =>
+    state.runProcessPanel.inputs;
+
+const validSelector = (state: RootState) =>
+    isValid(RUN_PROCESS_BASIC_FORM)(state) && isValid(RUN_PROCESS_INPUTS_FORM)(state);
+
+const mapStateToProps = createStructuredSelector({
+    inputs: inputsSelector,
+    valid: validSelector,
 });
 
 export type RunProcessSecondStepFormProps = RunProcessSecondStepFormDataProps & RunProcessSecondStepFormActionProps;
diff --git a/yarn.lock b/yarn.lock
index 58386c5..f3bf49b 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -206,6 +206,12 @@
     "@types/react" "*"
     redux "^3.6.0 || ^4.0.0"
 
+"@types/reselect at 2.2.0":
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/@types/reselect/-/reselect-2.2.0.tgz#c667206cfdc38190e1d379babe08865b2288575f"
+  dependencies:
+    reselect "*"
+
 "@types/shell-quote at 1.6.0":
   version "1.6.0"
   resolved "https://registry.yarnpkg.com/@types/shell-quote/-/shell-quote-1.6.0.tgz#537b2949a2ebdcb0d353e448fee45b081021963f"
@@ -6904,7 +6910,7 @@ requires-port@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
 
-reselect at 4.0.0:
+reselect@*, reselect at 4.0.0:
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/reselect/-/reselect-4.0.0.tgz#f2529830e5d3d0e021408b246a206ef4ea4437f7"
 

commit 21485ff667209666337db75d88b2a8d375c627b2
Merge: fc51814 a764ad3
Author: Michal Klobukowski <michal.klobukowski at contractors.roche.com>
Date:   Sat Oct 13 19:48:01 2018 +0200

    Merge chips input
    
    Feature #13862
    
    Arvados-DCO-1.1-Signed-off-by: Michal Klobukowski <michal.klobukowski at contractors.roche.com>

diff --cc src/components/chips-input/chips-input.tsx
index ad3d987,6a3142b..171641a
--- a/src/components/chips-input/chips-input.tsx
+++ b/src/components/chips-input/chips-input.tsx
@@@ -42,10 -42,10 +42,6 @@@ export const ChipsInput = withStyles(st
          filler = React.createRef<HTMLDivElement>();
          timeout = -1;
  
-         componentWillUnmount (){
-             clearTimeout(this.timeout);
 -        componentDidMount() {
 -            this.updateCursorPosition();
--        }
--
          setText = (event: React.ChangeEvent<HTMLInputElement>) => {
              this.setState({ text: event.target.value });
          }
@@@ -73,17 -73,25 +69,35 @@@
          }
  
          updateCursorPosition = () => {
++            console.log('cursorPoristion');
              if (this.timeout) {
                  clearTimeout(this.timeout);
              }
              this.timeout = setTimeout(() => this.forceUpdate());
          }
  
-         render() {
 -        componentDidUpdate(prevProps: ChipsInputProps<Value>){
 -            if(prevProps.values !== this.props.values){
 -                this.updateCursorPosition();
 -            }
++        getInputStyles = (): React.CSSProperties => ({
++            width: this.filler.current
++                ? this.filler.current.offsetWidth + 8
++                : '100%',
++            right: this.filler.current
++                ? `calc(${this.filler.current.offsetWidth}px - 100%)`
++                : 0,
++
++        })
++
++        componentDidMount() {
 +            this.updateCursorPosition();
+         }
+ 
+         render() {
++            console.log(`Render: ${this.props.values}`);
              return <>
                  <div className={this.props.classes.chips}>
-                     <Chips {...this.props} filler={<div ref={this.filler} />} />
+                     <Chips
+                         {...this.props}
+                         filler={<div ref={this.filler} />}
+                     />
                  </div>
                  <Input
                      value={this.state.text}
@@@ -98,13 -106,13 +112,13 @@@
              </>;
          }
  
--        getInputStyles = (): React.CSSProperties => ({
--            width: this.filler.current
--                ? this.filler.current.offsetWidth + 8
--                : '100%',
--            right: this.filler.current
--                ? `calc(${this.filler.current.offsetWidth}px - 100%)`
--                : 0,
--
--        })
++        componentDidUpdate(prevProps: ChipsInputProps<Value>) {
++            if (prevProps.values !== this.props.values) {
++                console.log('didUpdate');
++                this.updateCursorPosition();
++            }
++        }
++        componentWillUnmount() {
++            clearTimeout(this.timeout);
++        }
      });

commit fc5181400a6e3c31e2fb975b28878372e420f47d
Author: Michal Klobukowski <michal.klobukowski at contractors.roche.com>
Date:   Sat Oct 13 18:45:46 2018 +0200

    Init string array input
    
    Feature #13862
    
    Arvados-DCO-1.1-Signed-off-by: Michal Klobukowski <michal.klobukowski at contractors.roche.com>

diff --git a/src/components/chips-input/chips-input.tsx b/src/components/chips-input/chips-input.tsx
index 210feae..ad3d987 100644
--- a/src/components/chips-input/chips-input.tsx
+++ b/src/components/chips-input/chips-input.tsx
@@ -42,6 +42,10 @@ export const ChipsInput = withStyles(styles)(
         filler = React.createRef<HTMLDivElement>();
         timeout = -1;
 
+        componentWillUnmount (){
+            clearTimeout(this.timeout);
+        }
+
         setText = (event: React.ChangeEvent<HTMLInputElement>) => {
             this.setState({ text: event.target.value });
         }
diff --git a/src/models/workflow.ts b/src/models/workflow.ts
index d7d97c4..5991513 100644
--- a/src/models/workflow.ts
+++ b/src/models/workflow.ts
@@ -141,6 +141,12 @@ export const isPrimitiveOfType = (input: GenericCommandInputParameter<any, any>,
         ? input.type.indexOf(type) > -1
         : input.type === type;
 
+export const isArrayOfType = (input: GenericCommandInputParameter<any, any>, type: CWLType) =>
+    typeof input.type === 'object' &&
+        input.type.type === 'array'
+        ? input.type.items === type
+        : false;
+
 export const stringifyInputType = ({ type }: CommandInputParameter) => {
     if (typeof type === 'string') {
         return type;
diff --git a/src/views/run-process-panel/inputs/string-array-input.tsx b/src/views/run-process-panel/inputs/string-array-input.tsx
new file mode 100644
index 0000000..7454e2a
--- /dev/null
+++ b/src/views/run-process-panel/inputs/string-array-input.tsx
@@ -0,0 +1,35 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import * as React from 'react';
+import { isRequiredInput, StringArrayCommandInputParameter } from '~/models/workflow';
+import { Field } from 'redux-form';
+import { ERROR_MESSAGE } from '~/validators/require';
+import { GenericInputProps, GenericInput } from '~/views/run-process-panel/inputs/generic-input';
+import { ChipsInput } from '../../../components/chips-input/chips-input';
+
+export interface StringArrayInputProps {
+    input: StringArrayCommandInputParameter;
+}
+export const StringArrayInput = ({ input }: StringArrayInputProps) =>
+    <Field
+        name={input.id}
+        commandInput={input}
+        component={StringArrayInputComponent}
+        validate={[
+            isRequiredInput(input)
+                ? (value: string[]) => value.length > 0 ? undefined : ERROR_MESSAGE
+                : () => undefined,
+        ]} />;
+
+const StringArrayInputComponent = (props: GenericInputProps) =>
+    <GenericInput
+        component={Input}
+        {...props} />;
+
+const Input = (props: GenericInputProps) =>
+    <ChipsInput
+        values={props.input.value}
+        onChange={props.input.onChange}
+        createNewValue={v => v} />;
diff --git a/src/views/run-process-panel/run-process-inputs-form.tsx b/src/views/run-process-panel/run-process-inputs-form.tsx
index 41355b2..14d8f63 100644
--- a/src/views/run-process-panel/run-process-inputs-form.tsx
+++ b/src/views/run-process-panel/run-process-inputs-form.tsx
@@ -7,7 +7,7 @@ import { reduxForm, InjectedFormProps } from 'redux-form';
 import { CommandInputParameter, CWLType, IntCommandInputParameter, BooleanCommandInputParameter, FileCommandInputParameter, DirectoryCommandInputParameter } from '~/models/workflow';
 import { IntInput } from '~/views/run-process-panel/inputs/int-input';
 import { StringInput } from '~/views/run-process-panel/inputs/string-input';
-import { StringCommandInputParameter, FloatCommandInputParameter, isPrimitiveOfType, File, Directory, WorkflowInputsData, EnumCommandInputParameter } from '../../models/workflow';
+import { StringCommandInputParameter, FloatCommandInputParameter, isPrimitiveOfType, File, Directory, WorkflowInputsData, EnumCommandInputParameter, isArrayOfType, StringArrayCommandInputParameter } from '../../models/workflow';
 import { FloatInput } from '~/views/run-process-panel/inputs/float-input';
 import { BooleanInput } from './inputs/boolean-input';
 import { FileInput } from './inputs/file-input';
@@ -16,6 +16,7 @@ import { compose } from 'redux';
 import { Grid, StyleRulesCallback, withStyles, WithStyles } from '@material-ui/core';
 import { EnumInput } from './inputs/enum-input';
 import { DirectoryInput } from './inputs/directory-input';
+import { StringArrayInput } from './inputs/string-array-input';
 
 export const RUN_PROCESS_INPUTS_FORM = 'runProcessInputsForm';
 
@@ -72,7 +73,7 @@ const getInputComponent = (input: CommandInputParameter) => {
 
         case isPrimitiveOfType(input, CWLType.FILE):
             return <FileInput input={input as FileCommandInputParameter} />;
-        
+
         case isPrimitiveOfType(input, CWLType.DIRECTORY):
             return <DirectoryInput input={input as DirectoryCommandInputParameter} />;
 
@@ -81,6 +82,9 @@ const getInputComponent = (input: CommandInputParameter) => {
             input.type.type === 'enum':
             return <EnumInput input={input as EnumCommandInputParameter} />;
 
+        case isArrayOfType(input, CWLType.STRING):
+            return <StringArrayInput input={input as StringArrayCommandInputParameter} />;
+
         default:
             return null;
     }

-----------------------------------------------------------------------


hooks/post-receive
-- 




More information about the arvados-commits mailing list