aboutsummaryrefslogtreecommitdiffstats
path: root/sdc-workflow-designer-ui/src/main/frontend/src/features/version/inputOutput
diff options
context:
space:
mode:
Diffstat (limited to 'sdc-workflow-designer-ui/src/main/frontend/src/features/version/inputOutput')
-rw-r--r--sdc-workflow-designer-ui/src/main/frontend/src/features/version/inputOutput/InputOutput.js83
-rw-r--r--sdc-workflow-designer-ui/src/main/frontend/src/features/version/inputOutput/InputOutputView.jsx234
-rw-r--r--sdc-workflow-designer-ui/src/main/frontend/src/features/version/inputOutput/__tests__/inputOutputActions-test.js150
-rw-r--r--sdc-workflow-designer-ui/src/main/frontend/src/features/version/inputOutput/__tests__/inputOutputReducer-test.js191
-rw-r--r--sdc-workflow-designer-ui/src/main/frontend/src/features/version/inputOutput/__tests__/inputOutputSelectors-test.js149
-rw-r--r--sdc-workflow-designer-ui/src/main/frontend/src/features/version/inputOutput/inputOutputActions.js53
-rw-r--r--sdc-workflow-designer-ui/src/main/frontend/src/features/version/inputOutput/inputOutputConstants.js39
-rw-r--r--sdc-workflow-designer-ui/src/main/frontend/src/features/version/inputOutput/inputOutputReducer.js130
-rw-r--r--sdc-workflow-designer-ui/src/main/frontend/src/features/version/inputOutput/inputOutputSelectors.js106
-rw-r--r--sdc-workflow-designer-ui/src/main/frontend/src/features/version/inputOutput/inputOutputValidations.js64
-rw-r--r--sdc-workflow-designer-ui/src/main/frontend/src/features/version/inputOutput/views/DataRow.jsx87
-rw-r--r--sdc-workflow-designer-ui/src/main/frontend/src/features/version/inputOutput/views/NoDataRow.jsx32
-rw-r--r--sdc-workflow-designer-ui/src/main/frontend/src/features/version/inputOutput/views/Tab.js43
-rw-r--r--sdc-workflow-designer-ui/src/main/frontend/src/features/version/inputOutput/views/TableBody.jsx50
-rw-r--r--sdc-workflow-designer-ui/src/main/frontend/src/features/version/inputOutput/views/TableHead.jsx41
15 files changed, 1452 insertions, 0 deletions
diff --git a/sdc-workflow-designer-ui/src/main/frontend/src/features/version/inputOutput/InputOutput.js b/sdc-workflow-designer-ui/src/main/frontend/src/features/version/inputOutput/InputOutput.js
new file mode 100644
index 00000000..c9cd5756
--- /dev/null
+++ b/sdc-workflow-designer-ui/src/main/frontend/src/features/version/inputOutput/InputOutput.js
@@ -0,0 +1,83 @@
+/*
+* Copyright © 2018 European Support Limited
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http: //www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+import { connect } from 'react-redux';
+import {
+ showAlertModalAction,
+ hideModalAction
+} from 'shared/modal/modalWrapperActions';
+
+import {
+ getIsShowInputs,
+ getSearch,
+ getDataRows,
+ getTypes,
+ getError
+} from 'features/version/inputOutput/inputOutputSelectors';
+import { getIsCertified } from 'features/version/general/generalSelectors';
+import {
+ changeError,
+ showInputs,
+ showOutputs,
+ search,
+ add,
+ changeName,
+ changeType,
+ changeMandatory,
+ remove
+} from 'features/version/inputOutput/inputOutputActions';
+import { isWorkflowArchive } from 'features/workflow/workflowSelectors';
+import InputOutputView from 'features/version/inputOutput/InputOutputView';
+
+const mapStateToProps = state => ({
+ isShowInputs: getIsShowInputs(state),
+ search: getSearch(state),
+ dataRows: getDataRows(state),
+ types: getTypes(state),
+ error: getError(state),
+ isReadOnly: getIsCertified(state) || isWorkflowArchive(state)
+});
+
+const mapDispatchToProps = dispatch => ({
+ handleChangeError: payload => dispatch(changeError(payload)),
+ handleShowInputs: () => dispatch(showInputs()),
+ handleShowOutputs: () => dispatch(showOutputs()),
+ handleSearch: value => dispatch(search(value)),
+ handleAdd: () => dispatch(add()),
+ handleNameChange: (name, key) => dispatch(changeName(name, key)),
+ handleTypeChange: (type, key) => dispatch(changeType(type, key)),
+ handleMandatoryChange: (mandatory, key) =>
+ dispatch(changeMandatory(mandatory, key)),
+ handleRemove: (alertProps, key) => {
+ if (alertProps) {
+ return dispatch(
+ showAlertModalAction({
+ ...alertProps,
+ withButtons: true,
+ actionButtonClick: () =>
+ dispatch(hideModalAction()) && dispatch(remove(key))
+ })
+ );
+ }
+
+ return dispatch(remove(key));
+ }
+});
+
+export default connect(
+ mapStateToProps,
+ mapDispatchToProps
+)(InputOutputView);
diff --git a/sdc-workflow-designer-ui/src/main/frontend/src/features/version/inputOutput/InputOutputView.jsx b/sdc-workflow-designer-ui/src/main/frontend/src/features/version/inputOutput/InputOutputView.jsx
new file mode 100644
index 00000000..61e34990
--- /dev/null
+++ b/sdc-workflow-designer-ui/src/main/frontend/src/features/version/inputOutput/InputOutputView.jsx
@@ -0,0 +1,234 @@
+/*
+* Copyright © 2018 European Support Limited
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http: //www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+import React from 'react';
+import PropTypes from 'prop-types';
+import { Translate, I18n } from 'react-redux-i18n';
+import cn from 'classnames';
+import { SVGIcon } from 'onap-ui-react';
+
+import Scrollbars from 'shared/scroll/Scrollbars';
+import SearchInput from 'shared/searchInput/SearchInput';
+import { getValidationsError } from 'features/version/inputOutput/inputOutputValidations';
+import Tab from 'features/version/inputOutput/views/Tab';
+import TableHead from 'features/version/inputOutput/views/TableHead';
+import TableBody from 'features/version/inputOutput/views/TableBody';
+import NoDataRow from 'features/version/inputOutput/views/NoDataRow';
+import DataRow from 'features/version/inputOutput/views/DataRow';
+
+class InputOutputView extends React.Component {
+ componentDidUpdate() {
+ const { dataRows, error, handleChangeError } = this.props;
+
+ const validationsError = getValidationsError(dataRows);
+
+ const isDiff = Object.keys(validationsError).some(errorKey => {
+ if (!error.hasOwnProperty(errorKey)) {
+ return true;
+ }
+
+ return (
+ JSON.stringify(validationsError[errorKey].sort()) !==
+ JSON.stringify(error[errorKey].sort())
+ );
+ });
+
+ if (isDiff) {
+ handleChangeError(validationsError);
+ }
+ }
+
+ handleInputsTabClick = () => {
+ if (!this.props.isShowInputs) {
+ this.props.handleShowInputs();
+ }
+ };
+
+ handleOutputsTabClick = () => {
+ if (this.props.isShowInputs) {
+ this.props.handleShowOutputs();
+ }
+ };
+
+ handleSearchChange = value => {
+ this.props.handleSearch(value);
+ };
+
+ handleNameChange = key => name => {
+ this.props.handleNameChange(name, key);
+ };
+
+ handleTypeChange = key => event => {
+ this.props.handleTypeChange(event.target.value, key);
+ };
+
+ handleMandatoryChange = key => value => {
+ this.props.handleMandatoryChange(value, key);
+ };
+
+ handleRemoveClick = key => () => {
+ const { name } = this.props.dataRows[key];
+
+ let alertProps = false;
+
+ if (name.replace(/\s+/g, '')) {
+ const title = I18n.t('workflow.inputOutput.DELETE');
+ const body = I18n.t('workflow.inputOutput.confirmDelete', {
+ name: name.replace(/s+$/g, '')
+ });
+ const closeButtonText = I18n.t('workflow.inputOutput.CANCEL');
+ const actionButtonText = title;
+
+ alertProps = {
+ title,
+ body,
+ closeButtonText,
+ actionButtonText
+ };
+ }
+
+ this.props.handleRemove(alertProps, key);
+ };
+
+ renderDataRows = () => {
+ const { dataRows, types, error } = this.props;
+
+ if (dataRows.length < 1) {
+ return (
+ <NoDataRow>
+ <Translate value="workflow.inputOutput.noData" />
+ </NoDataRow>
+ );
+ }
+
+ return dataRows.map((data, i) => {
+ let errorMessage = '';
+
+ if (
+ error.invalidCharacters &&
+ error.invalidCharacters.includes(i)
+ ) {
+ errorMessage = I18n.t(
+ 'workflow.errorMessages.invalidCharacters'
+ );
+ } else if (error.alreadyExists && error.alreadyExists.includes(i)) {
+ errorMessage = I18n.t('workflow.errorMessages.alreadyExists');
+ } else if (error.emptyName && error.emptyName.includes(i)) {
+ errorMessage = I18n.t('workflow.errorMessages.emptyName');
+ }
+
+ return (
+ <DataRow
+ key={`data.${i}`}
+ data={data}
+ types={types}
+ nameErrorMessage={errorMessage}
+ dataTestId="wf-input-output-row"
+ handleNameChange={this.handleNameChange(i)}
+ handleTypeChange={this.handleTypeChange(i)}
+ handleMandatoryChange={this.handleMandatoryChange(i)}
+ handleRemoveClick={this.handleRemoveClick(i)}
+ />
+ );
+ });
+ };
+
+ render() {
+ const { isShowInputs, search, handleAdd, isReadOnly } = this.props;
+
+ const addLabel = isShowInputs
+ ? I18n.t('workflow.inputOutput.addInput')
+ : I18n.t('workflow.inputOutput.addOutput');
+
+ const dataRowsView = this.renderDataRows();
+
+ return (
+ <div className="input-output">
+ <div className="input-output__header">
+ <Tab
+ isActive={isShowInputs}
+ dataTestId="wf-input-output-inputs"
+ handleTabClick={this.handleInputsTabClick}>
+ <Translate value="workflow.inputOutput.inputs" />
+ </Tab>
+ <Tab
+ isActive={!isShowInputs}
+ dataTestId="wf-input-output-outputs"
+ handleTabClick={this.handleOutputsTabClick}>
+ <Translate value="workflow.inputOutput.outputs" />
+ </Tab>
+ <div className="input-output__header__right">
+ <div className="input-output__search">
+ <SearchInput
+ dataTestId="wf-input-output-search"
+ onChange={this.handleSearchChange}
+ value={search}
+ />
+ </div>
+ <div
+ className={cn('input-output__add', {
+ disabled: isReadOnly
+ })}
+ data-test-id="wf-input-output-add"
+ onClick={handleAdd}>
+ <SVGIcon
+ label={addLabel}
+ labelPosition="right"
+ color="primary"
+ name="plusThin"
+ />
+ </div>
+ </div>
+ </div>
+ <div className="input-output__table">
+ <TableHead />
+ <TableBody isReadOnly={isReadOnly}>
+ <Scrollbars className="scrollbars">
+ {dataRowsView}
+ </Scrollbars>
+ </TableBody>
+ </div>
+ </div>
+ );
+ }
+}
+
+InputOutputView.propTypes = {
+ isShowInputs: PropTypes.bool,
+ search: PropTypes.string,
+ dataRows: PropTypes.arrayOf(
+ PropTypes.shape({
+ name: PropTypes.string,
+ type: PropTypes.string,
+ mandatory: PropTypes.bool
+ })
+ ),
+ types: PropTypes.array,
+ error: PropTypes.object,
+ isReadOnly: PropTypes.bool,
+ handleChangeError: PropTypes.func,
+ handleShowInputs: PropTypes.func,
+ handleShowOutputs: PropTypes.func,
+ handleSearch: PropTypes.func,
+ handleAdd: PropTypes.func,
+ handleCurrentDataChange: PropTypes.func,
+ handleNameChange: PropTypes.func,
+ handleTypeChange: PropTypes.func,
+ handleMandatoryChange: PropTypes.func,
+ handleRemove: PropTypes.func
+};
+
+export default InputOutputView;
diff --git a/sdc-workflow-designer-ui/src/main/frontend/src/features/version/inputOutput/__tests__/inputOutputActions-test.js b/sdc-workflow-designer-ui/src/main/frontend/src/features/version/inputOutput/__tests__/inputOutputActions-test.js
new file mode 100644
index 00000000..465602fc
--- /dev/null
+++ b/sdc-workflow-designer-ui/src/main/frontend/src/features/version/inputOutput/__tests__/inputOutputActions-test.js
@@ -0,0 +1,150 @@
+/*
+* Copyright © 2018 European Support Limited
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+import {
+ NAME_MAX_LEN,
+ STRING,
+ SET_INPUTS_OUTPUTS,
+ CHANGE_ERROR,
+ SHOW_INPUTS,
+ SHOW_OUTPUTS,
+ SEARCH,
+ ADD,
+ CHANGE_NAME,
+ CHANGE_TYPE,
+ CHANGE_MANDATORY,
+ REMOVE
+} from 'features/version/inputOutput/inputOutputConstants';
+import {
+ setInputsOutputs,
+ changeError,
+ showInputs,
+ showOutputs,
+ search,
+ add,
+ changeName,
+ changeType,
+ changeMandatory,
+ remove
+} from 'features/version/inputOutput/inputOutputActions';
+
+describe('Input/Output Actions', () => {
+ it('should have `setInputsOutputs` action', () => {
+ const inputs = [
+ {
+ name: 'Input',
+ type: STRING,
+ mandatory: false
+ }
+ ];
+
+ const outputs = [
+ {
+ name: 'Output',
+ type: STRING,
+ mandatory: false
+ }
+ ];
+
+ const expected = {
+ type: SET_INPUTS_OUTPUTS,
+ payload: {
+ inputs,
+ outputs
+ }
+ };
+
+ expect(setInputsOutputs({ inputs, outputs })).toEqual(expected);
+ });
+
+ it('should have `changeError` action', () => {
+ const payload = { key: 'value' };
+
+ const expected = { type: CHANGE_ERROR, payload };
+
+ expect(changeError(payload)).toEqual(expected);
+ });
+
+ it('should have `showInputs` action', () => {
+ const expected = { type: SHOW_INPUTS };
+
+ expect(showInputs()).toEqual(expected);
+ });
+
+ it('should have `showOutputs` action', () => {
+ const expected = { type: SHOW_OUTPUTS };
+
+ expect(showOutputs()).toEqual(expected);
+ });
+
+ it('should have `search` action', () => {
+ const payload = 'Search Value';
+
+ const expected = { type: SEARCH, payload };
+
+ expect(search(payload)).toEqual(expected);
+ });
+
+ it('should have `add` action', () => {
+ const expected = { type: ADD };
+
+ expect(add()).toEqual(expected);
+ });
+
+ it('should have `changeName` action', () => {
+ let name = 'This is a long name more that more more more and more';
+ let key = 1;
+
+ const expected = {
+ type: CHANGE_NAME,
+ payload: {
+ name: name.substr(0, NAME_MAX_LEN),
+ key
+ }
+ };
+
+ expect(changeName(name, key)).toEqual(expected);
+ });
+
+ it('should have `changeType` action', () => {
+ const type = 'String';
+ const key = 1;
+
+ const expected = { type: CHANGE_TYPE, payload: { type, key } };
+
+ expect(changeType(type, key)).toEqual(expected);
+ });
+
+ it('should have `changeMandatory` action', () => {
+ const mandatory = true;
+ const key = 1;
+
+ const expected = {
+ type: CHANGE_MANDATORY,
+ payload: { mandatory, key }
+ };
+
+ expect(changeMandatory(mandatory, key)).toEqual(expected);
+ });
+
+ it('should have `remove` action', () => {
+ const payload = 1;
+
+ const expected = { type: REMOVE, payload };
+
+ expect(remove(payload)).toEqual(expected);
+ });
+});
diff --git a/sdc-workflow-designer-ui/src/main/frontend/src/features/version/inputOutput/__tests__/inputOutputReducer-test.js b/sdc-workflow-designer-ui/src/main/frontend/src/features/version/inputOutput/__tests__/inputOutputReducer-test.js
new file mode 100644
index 00000000..b923f4a4
--- /dev/null
+++ b/sdc-workflow-designer-ui/src/main/frontend/src/features/version/inputOutput/__tests__/inputOutputReducer-test.js
@@ -0,0 +1,191 @@
+/*
+* Copyright © 2018 European Support Limited
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+import {
+ STRING,
+ INPUTS,
+ OUTPUTS
+} from 'features/version/inputOutput/inputOutputConstants';
+import inputOutputReducer, {
+ initialState,
+ defaultInputOutput
+} from 'features/version/inputOutput/inputOutputReducer';
+import {
+ setInputsOutputs,
+ changeError,
+ showInputs,
+ showOutputs,
+ search,
+ add,
+ changeName,
+ changeType,
+ changeMandatory,
+ remove
+} from 'features/version/inputOutput/inputOutputActions';
+
+describe('Input/Output Reducer', () => {
+ it('should return initialState', () => {
+ expect(inputOutputReducer(undefined, {})).toEqual(initialState);
+ });
+
+ it('should set inputs/outputs', () => {
+ const payload = {
+ inputs: [
+ {
+ name: 'Input',
+ type: STRING,
+ mandatory: false
+ }
+ ],
+ outputs: {
+ name: 'Output',
+ type: STRING,
+ mandatory: false
+ }
+ };
+
+ expect(
+ inputOutputReducer(undefined, setInputsOutputs(payload))
+ ).toEqual({ ...initialState, ...payload });
+ });
+
+ it('should change input/output error', () => {
+ const payload = {
+ alreadyExists: [1, 2],
+ invalidCharacteres: [3, 4]
+ };
+ [INPUTS, OUTPUTS].forEach(current => {
+ const state = { ...initialState, current };
+ expect(inputOutputReducer(state, changeError(payload))).toEqual({
+ ...state,
+ error: {
+ ...state.error,
+ [current]: payload
+ }
+ });
+ });
+ });
+
+ it('should show inputs', () => {
+ expect(inputOutputReducer(undefined, showInputs())).toEqual({
+ ...initialState,
+ current: INPUTS
+ });
+ });
+
+ it('should show outputs', () => {
+ expect(inputOutputReducer(undefined, showOutputs())).toEqual({
+ ...initialState,
+ current: OUTPUTS
+ });
+ });
+
+ it('should add input/output', () => {
+ [INPUTS, OUTPUTS].forEach(current => {
+ const state = { ...initialState, current };
+ expect(inputOutputReducer(state, add())).toEqual({
+ ...state,
+ [current]: [...state[current], defaultInputOutput[current]]
+ });
+ });
+ });
+
+ it('should add search', () => {
+ const payload = 'Search string';
+ expect(inputOutputReducer(undefined, search(payload))).toEqual({
+ ...initialState,
+ search: payload
+ });
+ });
+
+ it('should change input/output name/type/mandatory', () => {
+ const name = 'New name';
+ const type = 'New Type';
+ const mandatory = true;
+ const key = 0;
+ const state = {
+ ...initialState,
+ [INPUTS]: [
+ {
+ name: 'Old name',
+ type: 'Old type',
+ mandatory: false
+ }
+ ],
+ [OUTPUTS]: [
+ {
+ name: 'Old name',
+ type: 'Old type',
+ mandatory: false
+ }
+ ]
+ };
+ [INPUTS, OUTPUTS].forEach(current => {
+ [
+ {
+ action: changeName(name, key),
+ field: 'name',
+ value: name
+ },
+ {
+ action: changeType(type, key),
+ field: 'type',
+ value: type
+ },
+ {
+ action: changeMandatory(mandatory, key),
+ field: 'mandatory',
+ value: mandatory
+ }
+ ].forEach(actionMap => {
+ const actual = inputOutputReducer(
+ { ...state, current },
+ actionMap.action
+ )[current][key][actionMap.field];
+
+ const expected = actionMap.value;
+
+ expect(actual).toEqual(expected);
+ });
+ });
+ });
+
+ it('should remove input/output ', () => {
+ const key = 0;
+ const state = {
+ ...initialState,
+ [INPUTS]: [
+ {
+ name: 'Name',
+ type: 'String',
+ mandatory: true
+ }
+ ],
+ [OUTPUTS]: [
+ {
+ name: 'Name',
+ type: 'String',
+ mandatory: true
+ }
+ ]
+ };
+ [INPUTS, OUTPUTS].forEach(current => {
+ expect(
+ inputOutputReducer({ ...state, current }, remove(key))[current]
+ ).toEqual([]);
+ });
+ });
+});
diff --git a/sdc-workflow-designer-ui/src/main/frontend/src/features/version/inputOutput/__tests__/inputOutputSelectors-test.js b/sdc-workflow-designer-ui/src/main/frontend/src/features/version/inputOutput/__tests__/inputOutputSelectors-test.js
new file mode 100644
index 00000000..8e9bdbd4
--- /dev/null
+++ b/sdc-workflow-designer-ui/src/main/frontend/src/features/version/inputOutput/__tests__/inputOutputSelectors-test.js
@@ -0,0 +1,149 @@
+/*
+* Copyright © 2018 European Support Limited
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+import {
+ getInputOutput,
+ getCurrent,
+ getIsShowInputs,
+ getSearch,
+ getDataRows,
+ getTypes,
+ getError
+} from 'features/version/inputOutput/inputOutputSelectors';
+
+describe('Input/Output Selectors', () => {
+ const state = {
+ currentVersion: {
+ general: {
+ id: '1e659854c7e240c881f1dd8d5bd833cc',
+ name: '1.0',
+ description: 'Initial version',
+ baseId: null,
+ creationTime: '2018-07-19T13:09:39.066+0000',
+ modificationTime: '2018-07-19T13:09:39.355+0000',
+ state: 'DRAFT',
+ inputs: [],
+ outputs: []
+ },
+ inputOutput: {
+ current: 'outputs',
+ inputs: [
+ {
+ name: 'IP Address',
+ value: 'String',
+ mandatory: true,
+ type: 'Integer'
+ },
+ {
+ name: 'MAC Address',
+ value: 'String',
+ mandatory: false,
+ type: 'Integer'
+ },
+ {
+ name: 'IP',
+ value: 'String',
+ mandatory: true,
+ type: 'Boolean'
+ },
+ {
+ name: 'IP',
+ value: 'String',
+ mandatory: false
+ },
+ {
+ name: '',
+ value: 'String',
+ mandatory: false
+ },
+ {
+ name: '',
+ value: 'String',
+ mandatory: false
+ }
+ ],
+ outputs: [
+ {
+ name: 'IP Address',
+ value: 'String',
+ mandatory: true
+ },
+ {
+ name: 'IP',
+ value: 'String',
+ mandatory: true
+ },
+ {
+ name: 'IP',
+ value: 'String',
+ mandatory: false,
+ type: 'Boolean'
+ }
+ ],
+ search: 'IP',
+ types: ['String', 'Boolean', 'Integer', 'Float'],
+ error: {
+ inputs: {
+ alreadyExists: [1, 2],
+ invalidCharacters: []
+ },
+ outputs: {
+ alreadyExists: [1, 2],
+ invalidCharacters: []
+ }
+ }
+ }
+ }
+ };
+
+ it('should `getInputOutput`', () => {
+ expect(getInputOutput(state)).toEqual(state.currentVersion.inputOutput);
+ });
+
+ it('should `getCurrent`', () => {
+ expect(getCurrent(state)).toEqual(
+ state.currentVersion.inputOutput.current
+ );
+ });
+
+ it('should `getIsShowInputs`', () => {
+ expect(getIsShowInputs(state)).toBeFalsy();
+ });
+
+ it('should `getSearch`', () => {
+ expect(getSearch(state)).toEqual(
+ state.currentVersion.inputOutput.search
+ );
+ });
+
+ it('should `getDataRows`', () => {
+ expect(getDataRows(state)).toEqual(
+ state.currentVersion.inputOutput.outputs
+ );
+ });
+
+ it('should `getTypes`', () => {
+ expect(getTypes(state)).toEqual(state.currentVersion.inputOutput.types);
+ });
+
+ it('should `getError`', () => {
+ expect(getError(state)).toEqual(
+ state.currentVersion.inputOutput.error[
+ state.currentVersion.inputOutput.current
+ ]
+ );
+ });
+});
diff --git a/sdc-workflow-designer-ui/src/main/frontend/src/features/version/inputOutput/inputOutputActions.js b/sdc-workflow-designer-ui/src/main/frontend/src/features/version/inputOutput/inputOutputActions.js
new file mode 100644
index 00000000..a9548592
--- /dev/null
+++ b/sdc-workflow-designer-ui/src/main/frontend/src/features/version/inputOutput/inputOutputActions.js
@@ -0,0 +1,53 @@
+/*
+* Copyright © 2018 European Support Limited
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+import { createActions } from 'redux-actions';
+
+import {
+ NAME_MAX_LEN,
+ NAMESPACE
+} from 'features/version/inputOutput/inputOutputConstants';
+
+export const {
+ [NAMESPACE]: {
+ setInputsOutputs,
+ changeError,
+ showInputs,
+ showOutputs,
+ search,
+ add,
+ changeName,
+ changeType,
+ changeMandatory,
+ remove
+ }
+} = createActions({
+ [NAMESPACE]: {
+ SET_INPUTS_OUTPUTS: undefined,
+ CHANGE_ERROR: undefined,
+ SHOW_INPUTS: undefined,
+ SHOW_OUTPUTS: undefined,
+ SEARCH: undefined,
+ ADD: undefined,
+ CHANGE_NAME: (name, key) => ({
+ name: name.substr(0, NAME_MAX_LEN),
+ key
+ }),
+ CHANGE_TYPE: (type, key) => ({ type, key }),
+ CHANGE_MANDATORY: (mandatory, key) => ({ mandatory, key }),
+ REMOVE: undefined
+ }
+});
diff --git a/sdc-workflow-designer-ui/src/main/frontend/src/features/version/inputOutput/inputOutputConstants.js b/sdc-workflow-designer-ui/src/main/frontend/src/features/version/inputOutput/inputOutputConstants.js
new file mode 100644
index 00000000..30f80a6b
--- /dev/null
+++ b/sdc-workflow-designer-ui/src/main/frontend/src/features/version/inputOutput/inputOutputConstants.js
@@ -0,0 +1,39 @@
+/*
+* Copyright © 2018 European Support Limited
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+export const NAME_MAX_LEN = 50;
+
+export const INPUTS = 'inputs';
+export const OUTPUTS = 'outputs';
+
+export const STRING = 'string';
+export const DEFAULT_STRING = 'STRING';
+export const BOOLEAN = 'boolean';
+export const INTEGER = 'integer';
+export const FLOAT = 'float';
+
+export const NAMESPACE = 'inputOutput';
+
+export const SET_INPUTS_OUTPUTS = `${NAMESPACE}/SET_INPUTS_OUTPUTS`;
+export const CHANGE_ERROR = `${NAMESPACE}/CHANGE_ERROR`;
+export const SHOW_INPUTS = `${NAMESPACE}/SHOW_INPUTS`;
+export const SHOW_OUTPUTS = `${NAMESPACE}/SHOW_OUTPUTS`;
+export const SEARCH = `${NAMESPACE}/SEARCH`;
+export const ADD = `${NAMESPACE}/ADD`;
+export const CHANGE_NAME = `${NAMESPACE}/CHANGE_NAME`;
+export const CHANGE_TYPE = `${NAMESPACE}/CHANGE_TYPE`;
+export const CHANGE_MANDATORY = `${NAMESPACE}/CHANGE_MANDATORY`;
+export const REMOVE = `${NAMESPACE}/REMOVE`;
diff --git a/sdc-workflow-designer-ui/src/main/frontend/src/features/version/inputOutput/inputOutputReducer.js b/sdc-workflow-designer-ui/src/main/frontend/src/features/version/inputOutput/inputOutputReducer.js
new file mode 100644
index 00000000..881322fa
--- /dev/null
+++ b/sdc-workflow-designer-ui/src/main/frontend/src/features/version/inputOutput/inputOutputReducer.js
@@ -0,0 +1,130 @@
+/*
+* Copyright © 2018 European Support Limited
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+import {
+ INPUTS,
+ OUTPUTS,
+ STRING,
+ DEFAULT_STRING,
+ BOOLEAN,
+ INTEGER,
+ FLOAT,
+ SET_INPUTS_OUTPUTS,
+ CHANGE_ERROR,
+ SHOW_INPUTS,
+ SHOW_OUTPUTS,
+ SEARCH,
+ ADD,
+ CHANGE_NAME,
+ CHANGE_TYPE,
+ CHANGE_MANDATORY,
+ REMOVE
+} from 'features/version/inputOutput/inputOutputConstants';
+
+export const defaultInputOutput = {
+ [INPUTS]: {
+ name: '',
+ type: DEFAULT_STRING,
+ mandatory: false
+ },
+ [OUTPUTS]: {
+ name: '',
+ type: DEFAULT_STRING,
+ mandatory: false
+ }
+};
+
+export const initialState = {
+ current: INPUTS,
+ [INPUTS]: [],
+ [OUTPUTS]: [],
+ search: '',
+ types: [STRING, BOOLEAN, INTEGER, FLOAT],
+ error: {
+ [INPUTS]: {},
+ [OUTPUTS]: {}
+ }
+};
+
+const inputOutputReducer = (state = initialState, action) => {
+ const { type, payload } = action;
+ switch (type) {
+ case SET_INPUTS_OUTPUTS:
+ return {
+ ...initialState,
+ ...payload
+ };
+
+ case CHANGE_ERROR:
+ return {
+ ...state,
+ error: {
+ ...state.error,
+ [state.current]: payload
+ }
+ };
+
+ case SHOW_INPUTS:
+ return { ...state, current: INPUTS };
+
+ case SHOW_OUTPUTS:
+ return { ...state, current: OUTPUTS };
+
+ case SEARCH:
+ return { ...state, search: payload };
+
+ case ADD:
+ return {
+ ...state,
+ [state.current]: [
+ ...state[state.current],
+ defaultInputOutput[state.current]
+ ]
+ };
+
+ /* eslint-disable no-case-declarations */
+ case CHANGE_NAME:
+ case CHANGE_TYPE:
+ case CHANGE_MANDATORY:
+ const { key, ...rest } = payload;
+ return {
+ ...state,
+ [state.current]: state[state.current].map(
+ (row, index) =>
+ key === index
+ ? {
+ ...row,
+ ...rest
+ }
+ : row
+ )
+ };
+ /* eslint-enable no-case-declarations */
+
+ case REMOVE:
+ return {
+ ...state,
+ [state.current]: state[state.current].filter(
+ (_, index) => index !== payload
+ )
+ };
+
+ default:
+ return state;
+ }
+};
+
+export default inputOutputReducer;
diff --git a/sdc-workflow-designer-ui/src/main/frontend/src/features/version/inputOutput/inputOutputSelectors.js b/sdc-workflow-designer-ui/src/main/frontend/src/features/version/inputOutput/inputOutputSelectors.js
new file mode 100644
index 00000000..3a6c9e8f
--- /dev/null
+++ b/sdc-workflow-designer-ui/src/main/frontend/src/features/version/inputOutput/inputOutputSelectors.js
@@ -0,0 +1,106 @@
+/*
+* Copyright © 2018 European Support Limited
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+import { createSelector } from 'reselect';
+import isEmpty from 'lodash.isempty';
+
+import { INPUTS } from 'features/version/inputOutput/inputOutputConstants';
+
+export const getInputOutput = state => state.currentVersion.inputOutput;
+export const getInputs = createSelector(getInputOutput, data => data.inputs);
+export const getOutputs = createSelector(getInputOutput, data => data.outputs);
+export const getInputOutputForComposition = state => ({
+ inputs: getInputs(state).map(item => ({
+ ...item,
+ type: item.type.toLowerCase()
+ })),
+ outputs: getOutputs(state).map(item => ({
+ ...item,
+ type: item.type.toLowerCase()
+ }))
+});
+export const getCurrent = createSelector(
+ getInputOutput,
+ inputOutput => inputOutput.current
+);
+
+export const getIsShowInputs = createSelector(
+ getCurrent,
+ current => current === INPUTS
+);
+
+export const getSearch = createSelector(
+ getInputOutput,
+ inputOutput => inputOutput.search
+);
+
+export const getDataRows = createSelector(
+ [getInputOutput, getCurrent],
+ (inputOutput, current) => {
+ if (inputOutput.search) {
+ return inputOutput[current].filter(dataRow =>
+ dataRow.name
+ .toLowerCase()
+ .includes(inputOutput.search.toLowerCase())
+ );
+ }
+
+ return inputOutput[current];
+ }
+);
+
+export const getTypes = createSelector(
+ getInputOutput,
+ inputOutput => inputOutput.types
+);
+
+export const getError = createSelector(
+ [getInputOutput, getCurrent],
+ (inputOutput, current) => inputOutput.error[current]
+);
+
+export const getErrorsInputOutput = createSelector(
+ getInputOutput,
+ ({ error }) => error
+);
+
+export const getInputErrors = createSelector(
+ getErrorsInputOutput,
+ ({ inputs }) =>
+ !isEmpty(inputs) &&
+ Boolean(
+ inputs.alreadyExists.length ||
+ inputs.invalidCharacters.length ||
+ inputs.emptyName.length
+ )
+);
+
+export const getOutputErrors = createSelector(
+ getErrorsInputOutput,
+ ({ outputs }) =>
+ !isEmpty(outputs) &&
+ Boolean(
+ outputs.alreadyExists.length ||
+ outputs.invalidCharacters.length ||
+ outputs.emptyName.length
+ )
+);
+
+export const getIOErrors = createSelector(
+ getInputErrors,
+ getOutputErrors,
+ (inputsErrors, outputsErrors) => inputsErrors || outputsErrors
+);
diff --git a/sdc-workflow-designer-ui/src/main/frontend/src/features/version/inputOutput/inputOutputValidations.js b/sdc-workflow-designer-ui/src/main/frontend/src/features/version/inputOutput/inputOutputValidations.js
new file mode 100644
index 00000000..d4057879
--- /dev/null
+++ b/sdc-workflow-designer-ui/src/main/frontend/src/features/version/inputOutput/inputOutputValidations.js
@@ -0,0 +1,64 @@
+/*
+* Copyright © 2018 European Support Limited
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+export const getValidationsError = dataRows => {
+ const error = {};
+
+ const groupBy = dataRows.reduce((result, value, key) => {
+ const groupKey = value.name.toLowerCase();
+
+ if (groupKey) {
+ if (result.hasOwnProperty(groupKey)) {
+ result[groupKey].push(key);
+ } else {
+ result[groupKey] = [key];
+ }
+ }
+ return result;
+ }, {});
+
+ error.alreadyExists = Object.keys(groupBy).reduce((result, value) => {
+ if (groupBy[value].length > 1) {
+ result = [...result, ...groupBy[value]];
+ }
+
+ return result;
+ }, []);
+
+ error.emptyName = dataRows.reduce((result, value, key) => {
+ const name = value.name;
+
+ if (!name) {
+ result.push(key);
+ }
+
+ return result;
+ }, []);
+
+ error.invalidCharacters = dataRows.reduce((result, value, key) => {
+ const groupKey = value.name;
+
+ if (groupKey) {
+ if (!/^[\w\d]+$/.test(groupKey)) {
+ result.push(key);
+ }
+ }
+
+ return result;
+ }, []);
+
+ return error;
+};
diff --git a/sdc-workflow-designer-ui/src/main/frontend/src/features/version/inputOutput/views/DataRow.jsx b/sdc-workflow-designer-ui/src/main/frontend/src/features/version/inputOutput/views/DataRow.jsx
new file mode 100644
index 00000000..70103f87
--- /dev/null
+++ b/sdc-workflow-designer-ui/src/main/frontend/src/features/version/inputOutput/views/DataRow.jsx
@@ -0,0 +1,87 @@
+/*
+* Copyright © 2018 European Support Limited
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+import React from 'react';
+import PropTypes from 'prop-types';
+
+import { Input, Checkbox, SVGIcon } from 'onap-ui-react';
+
+const DataRow = ({
+ data: { name, type, mandatory },
+ types,
+ nameErrorMessage,
+ dataTestId,
+ handleNameChange,
+ handleNameBlur,
+ handleTypeChange,
+ handleMandatoryChange,
+ handleRemoveClick
+}) => (
+ <div className="input-output__tr">
+ <div className="input-output__td">
+ <Input
+ errorMessage={nameErrorMessage}
+ data-test-id={`${dataTestId}-name`}
+ onChange={handleNameChange}
+ onBlur={handleNameBlur}
+ type="text"
+ value={name}
+ />
+ </div>
+ <div className="input-output__td">
+ <select
+ className="input-output-select"
+ value={type}
+ data-test-id={`${dataTestId}-select`}
+ onChange={handleTypeChange}>
+ {types.map((type, i) => (
+ <option key={`type.${i}`} value={type.toUpperCase()}>
+ {type}
+ </option>
+ ))}
+ </select>
+ </div>
+ <div className="input-output__td input-output__td--unflex">
+ <Checkbox
+ value="myVal"
+ data-test-id={`${dataTestId}-mandatory`}
+ onChange={handleMandatoryChange}
+ checked={mandatory}
+ />
+ </div>
+ <div className="input-output__td input-output__td--unflex input-output__td--icon">
+ <SVGIcon
+ name="trashO"
+ data-test-id={`${dataTestId}-delete`}
+ onClick={handleRemoveClick}
+ />
+ </div>
+ </div>
+);
+
+DataRow.propTypes = {
+ data: PropTypes.object,
+ types: PropTypes.array,
+ nameErrorMessage: PropTypes.string,
+ dataTestId: PropTypes.string,
+ handleNameChange: PropTypes.func,
+ handleNameBlur: PropTypes.func,
+ handleTypeChange: PropTypes.func,
+ handleMandatoryChange: PropTypes.func,
+ handleRemoveClick: PropTypes.func
+};
+
+export default DataRow;
diff --git a/sdc-workflow-designer-ui/src/main/frontend/src/features/version/inputOutput/views/NoDataRow.jsx b/sdc-workflow-designer-ui/src/main/frontend/src/features/version/inputOutput/views/NoDataRow.jsx
new file mode 100644
index 00000000..af75c79e
--- /dev/null
+++ b/sdc-workflow-designer-ui/src/main/frontend/src/features/version/inputOutput/views/NoDataRow.jsx
@@ -0,0 +1,32 @@
+/*
+* Copyright © 2018 European Support Limited
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+import React from 'react';
+import PropTypes from 'prop-types';
+
+const NoDataRow = ({ children }) => (
+ <div className="input-output__tr input-output__tr--no-hover">
+ <div className="input-output__td input-output__td--empty">
+ {children}
+ </div>
+ </div>
+);
+
+NoDataRow.propTypes = {
+ children: PropTypes.node
+};
+
+export default NoDataRow;
diff --git a/sdc-workflow-designer-ui/src/main/frontend/src/features/version/inputOutput/views/Tab.js b/sdc-workflow-designer-ui/src/main/frontend/src/features/version/inputOutput/views/Tab.js
new file mode 100644
index 00000000..ad56dc15
--- /dev/null
+++ b/sdc-workflow-designer-ui/src/main/frontend/src/features/version/inputOutput/views/Tab.js
@@ -0,0 +1,43 @@
+/*
+* Copyright © 2018 European Support Limited
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+import React from 'react';
+import PropTypes from 'prop-types';
+import cn from 'classnames';
+
+const Tab = ({ children, isActive, dataTestId, handleTabClick }) => {
+ const className = cn('input-output__tab', {
+ 'input-output__tab--active': isActive
+ });
+
+ return (
+ <div
+ className={className}
+ data-test-id={`${dataTestId}-tab`}
+ onClick={handleTabClick}>
+ {children}
+ </div>
+ );
+};
+
+Tab.propTypes = {
+ children: PropTypes.node,
+ isActive: PropTypes.bool,
+ dataTestId: PropTypes.string,
+ handleTabClick: PropTypes.func
+};
+
+export default Tab;
diff --git a/sdc-workflow-designer-ui/src/main/frontend/src/features/version/inputOutput/views/TableBody.jsx b/sdc-workflow-designer-ui/src/main/frontend/src/features/version/inputOutput/views/TableBody.jsx
new file mode 100644
index 00000000..ed11bbc5
--- /dev/null
+++ b/sdc-workflow-designer-ui/src/main/frontend/src/features/version/inputOutput/views/TableBody.jsx
@@ -0,0 +1,50 @@
+/*
+* Copyright © 2018 European Support Limited
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http: //www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+/* eslint-disable no-unused-vars */
+
+import React from 'react';
+import PropTypes from 'prop-types';
+import cn from 'classnames';
+
+class TableBody extends React.Component {
+ handleNameInputChange = params => {
+ console.log('handleNameInputChange', { params });
+ };
+
+ handleMandatoryCheckboxChange = params => {
+ console.log('handleMandatoryCheckboxChange: ', { params });
+ };
+
+ render() {
+ const { isReadOnly, children } = this.props;
+
+ return (
+ <div
+ className={cn('input-output__table__tbody', {
+ disabled: isReadOnly
+ })}>
+ {children}
+ </div>
+ );
+ }
+}
+
+TableBody.propTypes = {
+ isReadOnly: PropTypes.bool,
+ children: PropTypes.node
+};
+
+export default TableBody;
diff --git a/sdc-workflow-designer-ui/src/main/frontend/src/features/version/inputOutput/views/TableHead.jsx b/sdc-workflow-designer-ui/src/main/frontend/src/features/version/inputOutput/views/TableHead.jsx
new file mode 100644
index 00000000..37a8cb43
--- /dev/null
+++ b/sdc-workflow-designer-ui/src/main/frontend/src/features/version/inputOutput/views/TableHead.jsx
@@ -0,0 +1,41 @@
+/*
+* Copyright © 2018 European Support Limited
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http: //www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+import React from 'react';
+import { Translate } from 'react-redux-i18n';
+
+export default class TableHead extends React.Component {
+ render() {
+ return (
+ <div className="input-output__table__thead">
+ <div className="input-output__tr input-output__tr--no-hover">
+ <div className="input-output__th">
+ <Translate value="workflow.inputOutput.name" />
+ </div>
+ <div className="input-output__th">
+ <Translate value="workflow.inputOutput.type" />
+ </div>
+ <div className="input-output__th input-output__th--unflex">
+ <Translate value="workflow.inputOutput.mandatory" />
+ </div>
+ <div className="input-output__th input-output__th--unflex input-output__th--icon">
+ &#8226;&#8226;&#8226;
+ </div>
+ </div>
+ </div>
+ );
+ }
+}