[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