diff options
Diffstat (limited to 'sdc-workflow-designer-ui/src/main/frontend/src/features/version/inputOutput')
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"> + ••• + </div> + </div> + </div> + ); + } +} |