aboutsummaryrefslogtreecommitdiffstats
path: root/workflow/workflow-designer-ui/src/main/frontend/src/shared
diff options
context:
space:
mode:
Diffstat (limited to 'workflow/workflow-designer-ui/src/main/frontend/src/shared')
-rw-r--r--workflow/workflow-designer-ui/src/main/frontend/src/shared/components/Description/index.js44
-rw-r--r--workflow/workflow-designer-ui/src/main/frontend/src/shared/components/Select/index.js54
-rw-r--r--workflow/workflow-designer-ui/src/main/frontend/src/shared/components/VersionInfo/index.js59
-rw-r--r--workflow/workflow-designer-ui/src/main/frontend/src/shared/errorResponseHandler/errorResponseHandler.js32
-rw-r--r--workflow/workflow-designer-ui/src/main/frontend/src/shared/loader/Loader.jsx50
-rw-r--r--workflow/workflow-designer-ui/src/main/frontend/src/shared/loader/LoaderConstants.js38
-rw-r--r--workflow/workflow-designer-ui/src/main/frontend/src/shared/loader/LoaderReducer.js49
-rw-r--r--workflow/workflow-designer-ui/src/main/frontend/src/shared/loader/__tests__/loaderReducer-test.js56
-rw-r--r--workflow/workflow-designer-ui/src/main/frontend/src/shared/modal/ModalWrapper.js34
-rw-r--r--workflow/workflow-designer-ui/src/main/frontend/src/shared/modal/ModalWrapperView.jsx99
-rw-r--r--workflow/workflow-designer-ui/src/main/frontend/src/shared/modal/__tests__/modalWrapperActions-test.js85
-rw-r--r--workflow/workflow-designer-ui/src/main/frontend/src/shared/modal/__tests__/modalWrapperReducer-test.js61
-rw-r--r--workflow/workflow-designer-ui/src/main/frontend/src/shared/modal/modalWrapperActions.js46
-rw-r--r--workflow/workflow-designer-ui/src/main/frontend/src/shared/modal/modalWrapperComponents.js27
-rw-r--r--workflow/workflow-designer-ui/src/main/frontend/src/shared/modal/modalWrapperReducer.js32
-rw-r--r--workflow/workflow-designer-ui/src/main/frontend/src/shared/navigationSideBar/NavigationLink.jsx48
-rw-r--r--workflow/workflow-designer-ui/src/main/frontend/src/shared/navigationSideBar/NavigationMenu.jsx39
-rw-r--r--workflow/workflow-designer-ui/src/main/frontend/src/shared/navigationSideBar/NavigationMenuItem.jsx44
-rw-r--r--workflow/workflow-designer-ui/src/main/frontend/src/shared/navigationSideBar/NavigationMenuItems.jsx47
-rw-r--r--workflow/workflow-designer-ui/src/main/frontend/src/shared/navigationSideBar/index.js73
-rw-r--r--workflow/workflow-designer-ui/src/main/frontend/src/shared/notifications/Notifications.js33
-rw-r--r--workflow/workflow-designer-ui/src/main/frontend/src/shared/notifications/NotificationsView.jsx56
-rw-r--r--workflow/workflow-designer-ui/src/main/frontend/src/shared/notifications/__tests__/NotificationView_snapshot-test.js30
-rw-r--r--workflow/workflow-designer-ui/src/main/frontend/src/shared/notifications/__tests__/__snapshots__/NotificationView_snapshot-test.js.snap9
-rw-r--r--workflow/workflow-designer-ui/src/main/frontend/src/shared/notifications/__tests__/notificationsReducer-test.js61
-rw-r--r--workflow/workflow-designer-ui/src/main/frontend/src/shared/notifications/notificationsActions.js60
-rw-r--r--workflow/workflow-designer-ui/src/main/frontend/src/shared/notifications/notificationsConstants.js21
-rw-r--r--workflow/workflow-designer-ui/src/main/frontend/src/shared/notifications/notificationsReducer.js30
-rw-r--r--workflow/workflow-designer-ui/src/main/frontend/src/shared/notifications/notificationsSagas.js33
-rw-r--r--workflow/workflow-designer-ui/src/main/frontend/src/shared/searchInput/SearchInput.jsx123
-rw-r--r--workflow/workflow-designer-ui/src/main/frontend/src/shared/tree/Tree.jsx234
31 files changed, 1707 insertions, 0 deletions
diff --git a/workflow/workflow-designer-ui/src/main/frontend/src/shared/components/Description/index.js b/workflow/workflow-designer-ui/src/main/frontend/src/shared/components/Description/index.js
new file mode 100644
index 00000000..39400fd9
--- /dev/null
+++ b/workflow/workflow-designer-ui/src/main/frontend/src/shared/components/Description/index.js
@@ -0,0 +1,44 @@
+/*
+* 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 { I18n } from 'react-redux-i18n';
+
+const Description = ({ description, onDataChange, dataTestId }) => (
+ <div className="description-part">
+ <div className="sdc-input">
+ <div className="sdc-input__label">
+ {I18n.t('workflow.general.description')}
+ </div>
+ <textarea
+ value={description}
+ data-test-id={dataTestId || 'description'}
+ onChange={event => {
+ onDataChange({ description: event.target.value });
+ }}
+ className="field-section sdc-input__input"
+ />
+ </div>
+ </div>
+);
+
+Description.propTypes = {
+ description: PropTypes.string,
+ onDataChange: PropTypes.func,
+ dataTestId: PropTypes.string
+};
+
+export default Description;
diff --git a/workflow/workflow-designer-ui/src/main/frontend/src/shared/components/Select/index.js b/workflow/workflow-designer-ui/src/main/frontend/src/shared/components/Select/index.js
new file mode 100644
index 00000000..dcf11f2f
--- /dev/null
+++ b/workflow/workflow-designer-ui/src/main/frontend/src/shared/components/Select/index.js
@@ -0,0 +1,54 @@
+/*
+* 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 { isEmpty } from 'lodash';
+
+const Select = props => {
+ const { dataObj, selectedItemValue, disabled, label } = props;
+ return (
+ <div className="select-wrapper version-selector sdc-input">
+ {label && <label>{label}</label>}
+ <select
+ className="inputinput-selector"
+ key={'selector'}
+ value={selectedItemValue}
+ disabled={disabled}
+ data-test-id="vc-select-box">
+ {!isEmpty(dataObj) &&
+ dataObj.map(item => {
+ return (
+ <option
+ key={'select-item' + item.id}
+ value={item.id}
+ data-test-id="vc-option">
+ {item.displayed || item.name}
+ </option>
+ );
+ })}
+ </select>
+ </div>
+ );
+};
+
+Select.propTypes = {
+ dataObj: PropTypes.arrayOf(Object),
+ selectedItemValue: PropTypes.string,
+ disabled: PropTypes.bool,
+ label: PropTypes.string
+};
+
+export default Select;
diff --git a/workflow/workflow-designer-ui/src/main/frontend/src/shared/components/VersionInfo/index.js b/workflow/workflow-designer-ui/src/main/frontend/src/shared/components/VersionInfo/index.js
new file mode 100644
index 00000000..e89d9aa1
--- /dev/null
+++ b/workflow/workflow-designer-ui/src/main/frontend/src/shared/components/VersionInfo/index.js
@@ -0,0 +1,59 @@
+/*
+* 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 { I18n } from 'react-redux-i18n';
+import { Localize } from 'react-redux-i18n';
+export const LabeledValue = ({ title, value }) => (
+ <React.Fragment>
+ <div className="label">{title}</div>
+ <div className="value">{value}</div>
+ </React.Fragment>
+);
+
+LabeledValue.propTypes = {
+ title: PropTypes.string,
+ value: PropTypes.oneOfType([PropTypes.string, PropTypes.object])
+};
+
+export const VersionInfo = ({ created, modified, children }) => (
+ <div className="version-info-part">
+ {created && (
+ <LabeledValue
+ title={I18n.t('workflow.general.created')}
+ value={<Localize value={created} dateFormat="date.short" />}
+ />
+ )}
+ {modified && (
+ <LabeledValue
+ title={I18n.t('workflow.general.modified')}
+ value={<Localize value={modified} dateFormat="date.short" />}
+ />
+ )}
+ {children}
+ </div>
+);
+
+VersionInfo.defaultProps = {
+ created: '',
+ modified: ''
+};
+
+VersionInfo.propTypes = {
+ created: PropTypes.string,
+ modified: PropTypes.string,
+ children: PropTypes.oneOfType([PropTypes.array, PropTypes.object])
+};
diff --git a/workflow/workflow-designer-ui/src/main/frontend/src/shared/errorResponseHandler/errorResponseHandler.js b/workflow/workflow-designer-ui/src/main/frontend/src/shared/errorResponseHandler/errorResponseHandler.js
new file mode 100644
index 00000000..960e255d
--- /dev/null
+++ b/workflow/workflow-designer-ui/src/main/frontend/src/shared/errorResponseHandler/errorResponseHandler.js
@@ -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 store from 'wfapp/store';
+import { showErrorModalAction } from 'shared/modal/modalWrapperActions';
+
+export default error => {
+ const errorData = {
+ title: error.statusText,
+ body: error.responseText ? error.responseText : 'GENERIC ERROR'
+ };
+ store.dispatch(
+ showErrorModalAction({
+ ...errorData,
+ withButtons: true,
+ closeButtonText: 'Ok'
+ })
+ );
+};
diff --git a/workflow/workflow-designer-ui/src/main/frontend/src/shared/loader/Loader.jsx b/workflow/workflow-designer-ui/src/main/frontend/src/shared/loader/Loader.jsx
new file mode 100644
index 00000000..0b4c9f42
--- /dev/null
+++ b/workflow/workflow-designer-ui/src/main/frontend/src/shared/loader/Loader.jsx
@@ -0,0 +1,50 @@
+/*
+ * Copyright © 2016-2017 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 { connect } from 'react-redux';
+
+export const mapStateToProps = ({ loader: { isLoading } }) => {
+ return {
+ isLoading
+ };
+};
+
+export class Loader extends React.Component {
+ shouldComponentUpdate(nextProps) {
+ return nextProps.isLoading !== this.props.isLoading;
+ }
+
+ render() {
+ let { isLoading } = this.props;
+ return (
+ <div className="onboarding-loader">
+ {isLoading && (
+ <div className="onboarding-loader-backdrop">
+ <div className="tlv-loader large" />
+ </div>
+ )}
+ </div>
+ );
+ }
+}
+
+Loader.propTypes = {
+ isLoading: PropTypes.bool
+};
+
+export default connect(mapStateToProps)(Loader);
diff --git a/workflow/workflow-designer-ui/src/main/frontend/src/shared/loader/LoaderConstants.js b/workflow/workflow-designer-ui/src/main/frontend/src/shared/loader/LoaderConstants.js
new file mode 100644
index 00000000..02ce84ca
--- /dev/null
+++ b/workflow/workflow-designer-ui/src/main/frontend/src/shared/loader/LoaderConstants.js
@@ -0,0 +1,38 @@
+/*
+ * Copyright © 2016-2017 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 actionTypes = {
+ SHOW: 'SHOW',
+ HIDE: 'HIDE',
+
+ SEND_REQUEST: 'loader/SEND_REQUEST',
+ RECEIVE_RESPONSE: 'loader/RECEIVE_RESPONSE'
+};
+
+export const initialState = {
+ fetchingRequests: 0,
+ currentlyFetching: [],
+ isLoading: false
+};
+
+export const sendRequestAction = url => ({
+ type: actionTypes.SEND_REQUEST,
+ url
+});
+
+export const recieveResponseAction = url => ({
+ type: actionTypes.RECEIVE_RESPONSE,
+ url
+});
diff --git a/workflow/workflow-designer-ui/src/main/frontend/src/shared/loader/LoaderReducer.js b/workflow/workflow-designer-ui/src/main/frontend/src/shared/loader/LoaderReducer.js
new file mode 100644
index 00000000..d883492e
--- /dev/null
+++ b/workflow/workflow-designer-ui/src/main/frontend/src/shared/loader/LoaderReducer.js
@@ -0,0 +1,49 @@
+/*
+ * Copyright © 2016-2017 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 { actionTypes, initialState } from 'shared/loader/LoaderConstants.js';
+
+export default (state = initialState, action) => {
+ let fetchingRequests = state.fetchingRequests;
+ let newArray;
+ switch (action.type) {
+ case actionTypes.SEND_REQUEST:
+ fetchingRequests++;
+ newArray = state.currentlyFetching.slice();
+ newArray.splice(0, 0, action.url);
+ return {
+ fetchingRequests: fetchingRequests,
+ currentlyFetching: newArray,
+ isLoading: true
+ };
+ case actionTypes.RECEIVE_RESPONSE:
+ fetchingRequests--;
+
+ newArray = state.currentlyFetching.filter(item => {
+ return item !== action.url;
+ });
+ return {
+ currentlyFetching: newArray,
+ fetchingRequests: fetchingRequests,
+ isLoading: fetchingRequests !== 0
+ };
+ case actionTypes.SHOW:
+ return { isLoading: true };
+ case actionTypes.HIDE:
+ return { isLoading: false };
+ default:
+ return state;
+ }
+};
diff --git a/workflow/workflow-designer-ui/src/main/frontend/src/shared/loader/__tests__/loaderReducer-test.js b/workflow/workflow-designer-ui/src/main/frontend/src/shared/loader/__tests__/loaderReducer-test.js
new file mode 100644
index 00000000..3ae40361
--- /dev/null
+++ b/workflow/workflow-designer-ui/src/main/frontend/src/shared/loader/__tests__/loaderReducer-test.js
@@ -0,0 +1,56 @@
+/*
+* 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.
+*/
+
+'use strict';
+
+import loaderReducer from 'shared/loader/LoaderReducer';
+import {
+ initialState,
+ sendRequestAction,
+ recieveResponseAction
+} from 'shared/loader/LoaderConstants';
+
+describe('Loader reducer', () => {
+ it('return initial state', () => {
+ expect(loaderReducer(undefined, {})).toEqual(initialState);
+ });
+
+ it('send request action test', () => {
+ const url = 'TEST';
+ const loaderData = {
+ fetchingRequests: 1,
+ currentlyFetching: [url],
+ isLoading: true
+ };
+
+ expect(loaderReducer(initialState, sendRequestAction(url))).toEqual(
+ loaderData
+ );
+ });
+
+ it('recieve response action test', () => {
+ const url = 'TEST';
+ const loaderData = {
+ fetchingRequests: 1,
+ currentlyFetching: [url],
+ isLoading: true
+ };
+
+ expect(loaderReducer(loaderData, recieveResponseAction(url))).toEqual(
+ initialState
+ );
+ });
+});
diff --git a/workflow/workflow-designer-ui/src/main/frontend/src/shared/modal/ModalWrapper.js b/workflow/workflow-designer-ui/src/main/frontend/src/shared/modal/ModalWrapper.js
new file mode 100644
index 00000000..71bda2be
--- /dev/null
+++ b/workflow/workflow-designer-ui/src/main/frontend/src/shared/modal/ModalWrapper.js
@@ -0,0 +1,34 @@
+/*
+* 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 ModalWrapperView from 'shared/modal/ModalWrapperView';
+import { hideModalAction } from 'shared/modal/modalWrapperActions';
+
+const mapStateToProps = state => ({
+ modal: state.modal
+});
+
+const mapDispatchToProps = dispatch => ({
+ hideModal: () => dispatch(hideModalAction())
+});
+
+const ModalWrapper = connect(mapStateToProps, mapDispatchToProps)(
+ ModalWrapperView
+);
+
+export default ModalWrapper;
diff --git a/workflow/workflow-designer-ui/src/main/frontend/src/shared/modal/ModalWrapperView.jsx b/workflow/workflow-designer-ui/src/main/frontend/src/shared/modal/ModalWrapperView.jsx
new file mode 100644
index 00000000..95be8cda
--- /dev/null
+++ b/workflow/workflow-designer-ui/src/main/frontend/src/shared/modal/ModalWrapperView.jsx
@@ -0,0 +1,99 @@
+/*
+* 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 {
+ Modal,
+ ModalHeader,
+ ModalTitle,
+ ModalBody,
+ ModalFooter
+} from 'sdc-ui/lib/react';
+
+import modalWrapperComponents from 'shared/modal/modalWrapperComponents';
+
+class ModalWrapperView extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.handleClose = this.handleClose.bind(this);
+ }
+
+ handleClose() {
+ const { hideModal, onClose } = this.props;
+
+ hideModal();
+
+ if (typeof onClose === 'function') {
+ onClose();
+ }
+ }
+
+ render() {
+ const { modal } = this.props;
+
+ if (!modal || !modal.type) {
+ return null;
+ }
+
+ const {
+ size,
+ type,
+ title,
+ body,
+ withButtons,
+ actionButtonText,
+ actionButtonClick,
+ closeButtonText,
+ customComponentName,
+ customComponentProps
+ } = modal;
+
+ const CustomComponent =
+ customComponentName && modalWrapperComponents[customComponentName];
+
+ return (
+ <Modal show size={size} type={type}>
+ <ModalHeader onClose={this.handleClose} type={type}>
+ {title && <ModalTitle>{title}</ModalTitle>}
+ </ModalHeader>
+ {body && <ModalBody>{body}</ModalBody>}
+ {CustomComponent && (
+ <CustomComponent {...customComponentProps} />
+ )}
+ {withButtons && (
+ <ModalFooter
+ actionButtonText={actionButtonText}
+ actionButtonClick={actionButtonClick}
+ closeButtonText={closeButtonText}
+ onClose={this.handleClose}
+ withButtons
+ />
+ )}
+ </Modal>
+ );
+ }
+}
+
+ModalWrapperView.propTypes = {
+ hideModal: PropTypes.func,
+ onClose: PropTypes.func,
+ modal: PropTypes.object
+};
+
+export default ModalWrapperView;
diff --git a/workflow/workflow-designer-ui/src/main/frontend/src/shared/modal/__tests__/modalWrapperActions-test.js b/workflow/workflow-designer-ui/src/main/frontend/src/shared/modal/__tests__/modalWrapperActions-test.js
new file mode 100644
index 00000000..0be53de4
--- /dev/null
+++ b/workflow/workflow-designer-ui/src/main/frontend/src/shared/modal/__tests__/modalWrapperActions-test.js
@@ -0,0 +1,85 @@
+/*
+* 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.
+*/
+
+'use strict';
+
+import {
+ SHOW_MODAL,
+ HIDE_MODAL,
+ showInfoModalAction,
+ showAlertModalAction,
+ showErrorModalAction,
+ showCustomModalAction,
+ hideModalAction
+} from 'shared/modal/modalWrapperActions';
+
+describe('Modal Wrapper Actions', () => {
+ const actions = [
+ { fn: showInfoModalAction, type: 'info', size: 'small' },
+ { fn: showAlertModalAction, type: 'alert', size: 'small' },
+ { fn: showErrorModalAction, type: 'error', size: 'small' },
+ { fn: showCustomModalAction, type: 'custom', size: 'medium' }
+ ];
+
+ it('returns correct action', () => {
+ actions.forEach(action => {
+ const payload = {
+ title: 'Title',
+ body: 'Body',
+ withButtons: true,
+ actionButtonText: 'Action Button Text',
+ actionButtonClick: () => {},
+ closeButtonText: 'Close Button Text'
+ };
+
+ const expected = {
+ type: SHOW_MODAL,
+ payload: {
+ size: action.size,
+ ...payload,
+ type: action.type
+ }
+ };
+
+ expect(action.fn(payload)).toEqual(expected);
+ });
+ });
+
+ it('returns correct size in action', () => {
+ actions.forEach(action => {
+ const size = 'large';
+
+ const payload = {
+ size
+ };
+
+ const expected = {
+ type: SHOW_MODAL,
+ payload: { size, type: action.type }
+ };
+
+ expect(action.fn(payload)).toEqual(expected);
+ });
+ });
+
+ it('returns hide modal action', () => {
+ const expected = {
+ type: HIDE_MODAL
+ };
+
+ expect(hideModalAction()).toEqual(expected);
+ });
+});
diff --git a/workflow/workflow-designer-ui/src/main/frontend/src/shared/modal/__tests__/modalWrapperReducer-test.js b/workflow/workflow-designer-ui/src/main/frontend/src/shared/modal/__tests__/modalWrapperReducer-test.js
new file mode 100644
index 00000000..c33190c5
--- /dev/null
+++ b/workflow/workflow-designer-ui/src/main/frontend/src/shared/modal/__tests__/modalWrapperReducer-test.js
@@ -0,0 +1,61 @@
+/*
+* 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.
+*/
+
+'use strict';
+
+import modalWrapperReducer from 'shared/modal/modalWrapperReducer';
+import {
+ showInfoModalAction,
+ hideModalAction
+} from 'shared/modal/modalWrapperActions';
+
+describe('Modal Wrapper Reducer', () => {
+ it('returns the initial state', () => {
+ const state = {
+ type: 'info',
+ title: 'Title'
+ };
+ expect(modalWrapperReducer(state, {})).toEqual(state);
+ });
+
+ it('returns correct state for show modal action', () => {
+ const payload = {
+ size: 'medium',
+ title: 'Title'
+ };
+
+ const action = showInfoModalAction(payload);
+
+ const expected = {
+ ...payload,
+ type: 'info'
+ };
+
+ expect(modalWrapperReducer({}, action)).toEqual(expected);
+ });
+
+ it('returns correct state for hide modal action', () => {
+ const state = {
+ size: 'medium',
+ title: 'Title',
+ type: 'info'
+ };
+
+ const action = hideModalAction();
+
+ expect(modalWrapperReducer(state, action)).toEqual({});
+ });
+});
diff --git a/workflow/workflow-designer-ui/src/main/frontend/src/shared/modal/modalWrapperActions.js b/workflow/workflow-designer-ui/src/main/frontend/src/shared/modal/modalWrapperActions.js
new file mode 100644
index 00000000..fe0ca0a5
--- /dev/null
+++ b/workflow/workflow-designer-ui/src/main/frontend/src/shared/modal/modalWrapperActions.js
@@ -0,0 +1,46 @@
+/*
+* 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 { createAction } from 'redux-actions';
+
+export const SHOW_MODAL = 'modal/SHOW_MODAL';
+export const HIDE_MODAL = 'modal/HIDE_MODAL';
+
+export const showInfoModalAction = createAction(SHOW_MODAL, payload => ({
+ size: 'small',
+ ...payload,
+ type: 'info'
+}));
+
+export const showAlertModalAction = createAction(SHOW_MODAL, payload => ({
+ size: 'small',
+ ...payload,
+ type: 'alert'
+}));
+
+export const showErrorModalAction = createAction(SHOW_MODAL, payload => ({
+ size: 'small',
+ ...payload,
+ type: 'error'
+}));
+
+export const showCustomModalAction = createAction(SHOW_MODAL, payload => ({
+ size: 'medium',
+ ...payload,
+ type: 'custom'
+}));
+
+export const hideModalAction = createAction(HIDE_MODAL);
diff --git a/workflow/workflow-designer-ui/src/main/frontend/src/shared/modal/modalWrapperComponents.js b/workflow/workflow-designer-ui/src/main/frontend/src/shared/modal/modalWrapperComponents.js
new file mode 100644
index 00000000..1e051b2f
--- /dev/null
+++ b/workflow/workflow-designer-ui/src/main/frontend/src/shared/modal/modalWrapperComponents.js
@@ -0,0 +1,27 @@
+/*
+* 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 NewWorkflow from 'features/workflow/create/CreateWorkflow';
+import NewVersion from 'features/version/create/CreateVersion';
+
+export const NEW_WORKFLOW_MODAL = 'NEW_WORKFLOW_MODAL';
+export const NEW_VERSION_MODAL = 'NEW_VERSION_MODAL';
+
+const modalWrapperComponents = {
+ NEW_WORKFLOW_MODAL: NewWorkflow,
+ NEW_VERSION_MODAL: NewVersion
+};
+
+export default modalWrapperComponents;
diff --git a/workflow/workflow-designer-ui/src/main/frontend/src/shared/modal/modalWrapperReducer.js b/workflow/workflow-designer-ui/src/main/frontend/src/shared/modal/modalWrapperReducer.js
new file mode 100644
index 00000000..600998b6
--- /dev/null
+++ b/workflow/workflow-designer-ui/src/main/frontend/src/shared/modal/modalWrapperReducer.js
@@ -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 { SHOW_MODAL, HIDE_MODAL } from 'shared/modal/modalWrapperActions';
+
+const modalWrapperReducer = (state = {}, action) => {
+ const { type, payload } = action;
+
+ switch (type) {
+ case SHOW_MODAL:
+ return { ...payload };
+ case HIDE_MODAL:
+ return {};
+ default:
+ return state;
+ }
+};
+
+export default modalWrapperReducer;
diff --git a/workflow/workflow-designer-ui/src/main/frontend/src/shared/navigationSideBar/NavigationLink.jsx b/workflow/workflow-designer-ui/src/main/frontend/src/shared/navigationSideBar/NavigationLink.jsx
new file mode 100644
index 00000000..6688cb55
--- /dev/null
+++ b/workflow/workflow-designer-ui/src/main/frontend/src/shared/navigationSideBar/NavigationLink.jsx
@@ -0,0 +1,48 @@
+/*!
+ * Copyright © 2016-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 classnames from 'classnames';
+
+function getItemDataTestId(itemId) {
+ return itemId.split('|')[0];
+}
+
+const NavigationLink = ({ item, activeItemId, onClick }) => {
+ return (
+ <div
+ key={'navAction_' + item.id}
+ className={classnames('navigation-group-item-name', {
+ selected: item.id === activeItemId,
+ disabled: item.disabled,
+ 'bold-name': item.expanded,
+ hidden: item.hidden
+ })}
+ onClick={event => onClick(event, item)}
+ data-test-id={'navbar-group-item-' + getItemDataTestId(item.id)}>
+ {item.name}
+ </div>
+ );
+};
+
+NavigationLink.propTypes = {
+ item: PropTypes.object,
+ activeItemId: PropTypes.string,
+ onClick: PropTypes.func
+};
+
+export default NavigationLink;
diff --git a/workflow/workflow-designer-ui/src/main/frontend/src/shared/navigationSideBar/NavigationMenu.jsx b/workflow/workflow-designer-ui/src/main/frontend/src/shared/navigationSideBar/NavigationMenu.jsx
new file mode 100644
index 00000000..f8a7d46f
--- /dev/null
+++ b/workflow/workflow-designer-ui/src/main/frontend/src/shared/navigationSideBar/NavigationMenu.jsx
@@ -0,0 +1,39 @@
+/*!
+ * Copyright © 2016-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 NavigationMenuItems from './NavigationMenuItems';
+
+const NavigationMenu = ({ menu, activeItemId, onNavigationItemClick }) => {
+ return (
+ <div className="navigation-group" key={menu.id}>
+ <NavigationMenuItems
+ items={menu.items}
+ activeItemId={activeItemId}
+ onNavigationItemClick={onNavigationItemClick}
+ />
+ </div>
+ );
+};
+
+NavigationMenu.propTypes = {
+ menu: PropTypes.object,
+ activeItemId: PropTypes.string.isRequired,
+ onNavigationItemClick: PropTypes.func
+};
+
+export default NavigationMenu;
diff --git a/workflow/workflow-designer-ui/src/main/frontend/src/shared/navigationSideBar/NavigationMenuItem.jsx b/workflow/workflow-designer-ui/src/main/frontend/src/shared/navigationSideBar/NavigationMenuItem.jsx
new file mode 100644
index 00000000..540e09c7
--- /dev/null
+++ b/workflow/workflow-designer-ui/src/main/frontend/src/shared/navigationSideBar/NavigationMenuItem.jsx
@@ -0,0 +1,44 @@
+/*!
+ * Copyright © 2016-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 classnames from 'classnames';
+import NavigationLink from './NavigationLink';
+
+const NavigationMenuItem = ({ onNavigationItemClick, item, activeItemId }) => {
+ return (
+ <div
+ className={classnames('navigation-group-item', {
+ 'selected-item': item.id === activeItemId
+ })}
+ key={'item_' + item.id}>
+ <NavigationLink
+ item={item}
+ activeItemId={activeItemId}
+ onClick={onNavigationItemClick}
+ />
+ </div>
+ );
+};
+
+NavigationMenuItem.propTypes = {
+ onNavigationItemClick: PropTypes.func,
+ item: PropTypes.object,
+ activeItemId: PropTypes.string
+};
+
+export default NavigationMenuItem;
diff --git a/workflow/workflow-designer-ui/src/main/frontend/src/shared/navigationSideBar/NavigationMenuItems.jsx b/workflow/workflow-designer-ui/src/main/frontend/src/shared/navigationSideBar/NavigationMenuItems.jsx
new file mode 100644
index 00000000..56bad084
--- /dev/null
+++ b/workflow/workflow-designer-ui/src/main/frontend/src/shared/navigationSideBar/NavigationMenuItems.jsx
@@ -0,0 +1,47 @@
+/*!
+ * Copyright © 2016-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 NavigationMenuItem from './NavigationMenuItem';
+
+const NavigationMenuItems = ({
+ items,
+ activeItemId,
+ onNavigationItemClick
+}) => {
+ return (
+ <div className="navigation-group-items">
+ {items &&
+ items.map(item => (
+ <NavigationMenuItem
+ key={'menuItem_' + item.id}
+ item={item}
+ activeItemId={activeItemId}
+ onNavigationItemClick={onNavigationItemClick}
+ />
+ ))}
+ </div>
+ );
+};
+
+NavigationMenuItems.propTypes = {
+ items: PropTypes.array,
+ activeItemId: PropTypes.string,
+ onNavigationItemClick: PropTypes.func
+};
+
+export default NavigationMenuItems;
diff --git a/workflow/workflow-designer-ui/src/main/frontend/src/shared/navigationSideBar/index.js b/workflow/workflow-designer-ui/src/main/frontend/src/shared/navigationSideBar/index.js
new file mode 100644
index 00000000..b3af6f47
--- /dev/null
+++ b/workflow/workflow-designer-ui/src/main/frontend/src/shared/navigationSideBar/index.js
@@ -0,0 +1,73 @@
+/*!
+ * Copyright © 2016-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, { Component } from 'react';
+import PropTypes from 'prop-types';
+import NavigationMenu from './NavigationMenu';
+
+class NavigationSideBar extends Component {
+ static propTypes = {
+ activeItemId: PropTypes.string,
+ onSelect: PropTypes.func,
+ onToggle: PropTypes.func,
+ groups: PropTypes.array,
+ disabled: PropTypes.bool,
+ onNavigationItemClick: PropTypes.func
+ };
+
+ constructor(props) {
+ super(props);
+ this.state = {
+ activeItemId: null
+ };
+ this.handleItemClicked = this.handleItemClicked.bind(this);
+ }
+
+ render() {
+ let { groups, activeItemId, disabled = false } = this.props;
+
+ return (
+ <div
+ className={`navigation-side-content ${
+ disabled ? 'disabled' : ''
+ }`}>
+ {groups.map(group => (
+ <NavigationMenu
+ menu={group}
+ activeItemId={activeItemId}
+ onNavigationItemClick={this.handleItemClicked}
+ key={'menu_' + group.id}
+ />
+ ))}
+ </div>
+ );
+ }
+
+ handleItemClicked(event, item) {
+ event.stopPropagation();
+ if (this.props.onToggle) {
+ this.props.onToggle(this.props.groups, item.id);
+ }
+ if (item.onSelect) {
+ item.onSelect();
+ }
+ if (this.props.onSelect) {
+ this.props.onSelect(item);
+ }
+ }
+}
+
+export default NavigationSideBar;
diff --git a/workflow/workflow-designer-ui/src/main/frontend/src/shared/notifications/Notifications.js b/workflow/workflow-designer-ui/src/main/frontend/src/shared/notifications/Notifications.js
new file mode 100644
index 00000000..6d9f7242
--- /dev/null
+++ b/workflow/workflow-designer-ui/src/main/frontend/src/shared/notifications/Notifications.js
@@ -0,0 +1,33 @@
+/*
+* 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 NotificationsView from 'shared/notifications/NotificationsView';
+import { notificationActions } from 'shared/notifications/notificationsActions';
+
+const mapStateToProps = state => {
+ return {
+ notifications: state.notifications
+ };
+};
+
+function mapDispatchToProps(dispatch) {
+ return {
+ removeNotifications: payload =>
+ dispatch(notificationActions.removeNotification(payload))
+ };
+}
+
+export default connect(mapStateToProps, mapDispatchToProps)(NotificationsView);
diff --git a/workflow/workflow-designer-ui/src/main/frontend/src/shared/notifications/NotificationsView.jsx b/workflow/workflow-designer-ui/src/main/frontend/src/shared/notifications/NotificationsView.jsx
new file mode 100644
index 00000000..710ffce7
--- /dev/null
+++ b/workflow/workflow-designer-ui/src/main/frontend/src/shared/notifications/NotificationsView.jsx
@@ -0,0 +1,56 @@
+/*
+* 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, { PureComponent } from 'react';
+import PropTypes from 'prop-types';
+import { Notification } from 'sdc-ui/lib/react/';
+import { CSSTransition, TransitionGroup } from 'react-transition-group';
+
+export default class NotificationsView extends PureComponent {
+ static propTypes = {
+ removeNotifications: PropTypes.func,
+ notifications: PropTypes.array
+ };
+
+ constructor(props) {
+ super(props);
+ }
+
+ render() {
+ const { notifications, removeNotifications } = this.props;
+ return (
+ <div className="workflow-notifications-container position-top-right">
+ <TransitionGroup>
+ {notifications.map(item => (
+ <CSSTransition
+ in={true}
+ timeout={500}
+ unmountOnExit
+ classNames="react-transition"
+ key={`notification-transition-${item.id}`}>
+ <Notification
+ key={item.id}
+ type={item.type}
+ title={item.title}
+ message={item.message}
+ onClick={() => removeNotifications(item)}
+ />
+ </CSSTransition>
+ ))}
+ </TransitionGroup>
+ </div>
+ );
+ }
+}
diff --git a/workflow/workflow-designer-ui/src/main/frontend/src/shared/notifications/__tests__/NotificationView_snapshot-test.js b/workflow/workflow-designer-ui/src/main/frontend/src/shared/notifications/__tests__/NotificationView_snapshot-test.js
new file mode 100644
index 00000000..616f10ff
--- /dev/null
+++ b/workflow/workflow-designer-ui/src/main/frontend/src/shared/notifications/__tests__/NotificationView_snapshot-test.js
@@ -0,0 +1,30 @@
+/*
+* 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 renderer from 'react-test-renderer';
+
+import NotificationsView from 'shared/notifications/NotificationsView';
+
+describe('Notification View Snapshot', () => {
+ it('renders correctly', () => {
+ const tree = renderer
+ .create(<NotificationsView notifications={[]} />)
+ .toJSON();
+
+ expect(tree).toMatchSnapshot();
+ });
+});
diff --git a/workflow/workflow-designer-ui/src/main/frontend/src/shared/notifications/__tests__/__snapshots__/NotificationView_snapshot-test.js.snap b/workflow/workflow-designer-ui/src/main/frontend/src/shared/notifications/__tests__/__snapshots__/NotificationView_snapshot-test.js.snap
new file mode 100644
index 00000000..eb4a573f
--- /dev/null
+++ b/workflow/workflow-designer-ui/src/main/frontend/src/shared/notifications/__tests__/__snapshots__/NotificationView_snapshot-test.js.snap
@@ -0,0 +1,9 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Notification View Snapshot renders correctly 1`] = `
+<div
+ className="workflow-notifications-container position-top-right"
+>
+ <div />
+</div>
+`;
diff --git a/workflow/workflow-designer-ui/src/main/frontend/src/shared/notifications/__tests__/notificationsReducer-test.js b/workflow/workflow-designer-ui/src/main/frontend/src/shared/notifications/__tests__/notificationsReducer-test.js
new file mode 100644
index 00000000..d308d22d
--- /dev/null
+++ b/workflow/workflow-designer-ui/src/main/frontend/src/shared/notifications/__tests__/notificationsReducer-test.js
@@ -0,0 +1,61 @@
+/*
+* 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 notificationsReducer from 'shared/notifications/notificationsReducer';
+import { actionTypes } from 'shared/notifications/notificationsConstants';
+
+describe('Notifications Reducer', () => {
+ it('Returns empty state array', () => {
+ expect(notificationsReducer(undefined, {})).toEqual([]);
+ });
+ it('Add notification obj to the notification arr in store', () => {
+ const action = {
+ type: actionTypes.ADD_NOTIFICATION,
+ payload: {
+ title: 'Notification1',
+ message: 'This is a test',
+ type: 'info',
+ id: '4a2b0038-e442-4ccb-b577-5d04eee6fdfb',
+ timeout: 200
+ }
+ };
+ expect(notificationsReducer([], action)).toEqual([action.payload]);
+ });
+ it('Remove notification instance from store', () => {
+ const resultState = [
+ {
+ title: 'Notification1',
+ message: 'This is a test',
+ type: 'info',
+ id: '4a2b0038-e442-4ccb-b577-5d04eee6fdfb',
+ timeout: 200
+ }
+ ];
+ const removeAction = {
+ type: actionTypes.REMOVE_NOTIFICATION,
+ payload: {
+ title: 'Notification1',
+ message: 'This is a test',
+ type: 'info',
+ id: '4a2b0038-e442-4ccb-b577-5d04eee6fdfb',
+ timeout: 200
+ }
+ };
+ expect(notificationsReducer([], removeAction)).toEqual([]);
+ expect(notificationsReducer(resultState, removeAction)).toEqual(
+ resultState.filter(item => item.id !== removeAction.payload.id)
+ );
+ });
+});
diff --git a/workflow/workflow-designer-ui/src/main/frontend/src/shared/notifications/notificationsActions.js b/workflow/workflow-designer-ui/src/main/frontend/src/shared/notifications/notificationsActions.js
new file mode 100644
index 00000000..84f9da83
--- /dev/null
+++ b/workflow/workflow-designer-ui/src/main/frontend/src/shared/notifications/notificationsActions.js
@@ -0,0 +1,60 @@
+/*
+* 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 { actionTypes } from 'shared/notifications/notificationsConstants';
+import UUID from 'uuid-js';
+
+export const notificationActions = {
+ showNotification: item => ({
+ type: actionTypes.ADD_NOTIFICATION,
+ payload: {
+ ...item,
+ id: UUID.create().toString()
+ }
+ }),
+
+ showSuccess: ({ title, message, timeout }) =>
+ notificationActions.showNotification({
+ title,
+ message,
+ timeout,
+ type: 'success'
+ }),
+ showInfo: ({ title, message, timeout }) =>
+ notificationActions.showNotification({
+ title,
+ message,
+ timeout,
+ type: 'info'
+ }),
+ showWarning: ({ title, message, timeout }) =>
+ notificationActions.showNotification({
+ title,
+ message,
+ timeout,
+ type: 'warning'
+ }),
+ showError: ({ title, message, timeout }) =>
+ notificationActions.showNotification({
+ title,
+ message,
+ timeout,
+ type: 'error'
+ }),
+ removeNotification: item => ({
+ type: actionTypes.REMOVE_NOTIFICATION,
+ payload: item
+ })
+};
diff --git a/workflow/workflow-designer-ui/src/main/frontend/src/shared/notifications/notificationsConstants.js b/workflow/workflow-designer-ui/src/main/frontend/src/shared/notifications/notificationsConstants.js
new file mode 100644
index 00000000..3e166424
--- /dev/null
+++ b/workflow/workflow-designer-ui/src/main/frontend/src/shared/notifications/notificationsConstants.js
@@ -0,0 +1,21 @@
+/*
+* 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 actionTypes = {
+ ADD_NOTIFICATION: 'notification/ADD_NOTIFICATION',
+ REMOVE_NOTIFICATION: 'notification/REMOVE_NOTIFICATION'
+};
+export const notificationTimeout = 4000;
diff --git a/workflow/workflow-designer-ui/src/main/frontend/src/shared/notifications/notificationsReducer.js b/workflow/workflow-designer-ui/src/main/frontend/src/shared/notifications/notificationsReducer.js
new file mode 100644
index 00000000..f0989cf5
--- /dev/null
+++ b/workflow/workflow-designer-ui/src/main/frontend/src/shared/notifications/notificationsReducer.js
@@ -0,0 +1,30 @@
+/*
+* 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 { actionTypes } from 'shared/notifications/notificationsConstants';
+
+function notificationsReducer(state = [], action) {
+ switch (action.type) {
+ case actionTypes.ADD_NOTIFICATION:
+ return [...state, action.payload];
+ case actionTypes.REMOVE_NOTIFICATION:
+ return state.filter(item => item.id !== action.payload.id);
+ default:
+ return state;
+ }
+}
+
+export default notificationsReducer;
diff --git a/workflow/workflow-designer-ui/src/main/frontend/src/shared/notifications/notificationsSagas.js b/workflow/workflow-designer-ui/src/main/frontend/src/shared/notifications/notificationsSagas.js
new file mode 100644
index 00000000..c30930a2
--- /dev/null
+++ b/workflow/workflow-designer-ui/src/main/frontend/src/shared/notifications/notificationsSagas.js
@@ -0,0 +1,33 @@
+/*
+* 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 { takeEvery, put } from 'redux-saga/effects';
+import { delay } from 'redux-saga';
+import {
+ actionTypes,
+ notificationTimeout
+} from 'shared/notifications/notificationsConstants.js';
+import { notificationActions } from 'shared/notifications/notificationsActions';
+
+export function* handleComingNotifications(params) {
+ const { timeout, ...data } = params;
+ const interval = timeout || notificationTimeout;
+ yield delay(interval);
+ yield put(notificationActions.removeNotification(data.payload));
+}
+
+export function* watchNotifications() {
+ yield takeEvery(actionTypes.ADD_NOTIFICATION, handleComingNotifications);
+}
diff --git a/workflow/workflow-designer-ui/src/main/frontend/src/shared/searchInput/SearchInput.jsx b/workflow/workflow-designer-ui/src/main/frontend/src/shared/searchInput/SearchInput.jsx
new file mode 100644
index 00000000..e0046926
--- /dev/null
+++ b/workflow/workflow-designer-ui/src/main/frontend/src/shared/searchInput/SearchInput.jsx
@@ -0,0 +1,123 @@
+/*
+* 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 SVGIcon from 'sdc-ui/lib/react/SVGIcon';
+
+class ExpandableInput extends React.Component {
+ static propTypes = {
+ onChange: PropTypes.func,
+ value: PropTypes.string
+ };
+
+ state = { showInput: false };
+
+ handleRef = input => {
+ this.domNode = input;
+
+ this.domNode && this.domNode.focus();
+ };
+
+ showInput = () => {
+ this.setState({ showInput: true });
+ };
+
+ hideInput = () => {
+ this.setState({ showInput: false });
+ };
+
+ closeInput = () => {
+ if (!this.props.value) {
+ this.hideInput();
+ }
+ };
+
+ getValue = () => {
+ return this.props.value;
+ };
+
+ handleChange = e => {
+ this.props.onChange(e.target.value);
+ };
+
+ handleClose() {
+ this.props.onChange('');
+
+ this.hideInput();
+ }
+
+ handleKeyDown = e => {
+ if (e.key === 'Escape') {
+ e.preventDefault();
+
+ this.handleClose();
+ }
+ };
+
+ handleBlur = () => {
+ if (!this.props.value) {
+ this.setState({ showInput: false });
+ }
+ };
+
+ render() {
+ let { value } = this.props;
+
+ const { showInput } = this.state;
+
+ if (!showInput) {
+ return (
+ <div className="search-input-top">
+ <SVGIcon
+ className="search-input-wrapper closed"
+ name="search"
+ onClick={this.showInput}
+ />
+ </div>
+ );
+ }
+
+ return (
+ <div className="search-input-top">
+ <div className="search-input-wrapper opened">
+ <div className="search-input-control">
+ <input
+ type="text"
+ value={value}
+ ref={this.handleRef}
+ className="input-control"
+ onChange={this.handleChange}
+ onKeyDown={this.handleKeyDown}
+ onBlur={this.handleBlur}
+ />
+ </div>
+ {value && (
+ <SVGIcon
+ onClick={() => this.handleClose()}
+ name="close"
+ />
+ )}
+ {!value && (
+ <SVGIcon name="search" onClick={this.handleBlur} />
+ )}
+ </div>
+ </div>
+ );
+ }
+}
+
+export default ExpandableInput;
diff --git a/workflow/workflow-designer-ui/src/main/frontend/src/shared/tree/Tree.jsx b/workflow/workflow-designer-ui/src/main/frontend/src/shared/tree/Tree.jsx
new file mode 100644
index 00000000..7b25ab89
--- /dev/null
+++ b/workflow/workflow-designer-ui/src/main/frontend/src/shared/tree/Tree.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, { Component } from 'react';
+import PropTypes from 'prop-types';
+import { select } from 'd3-selection';
+import { tree, stratify } from 'd3-hierarchy';
+function diagonal(d) {
+ const offset = 50;
+ return (
+ 'M' +
+ d.y +
+ ',' +
+ d.x +
+ 'C' +
+ (d.parent.y + offset) +
+ ',' +
+ d.x +
+ ' ' +
+ (d.parent.y + offset) +
+ ',' +
+ d.parent.x +
+ ' ' +
+ d.parent.y +
+ ',' +
+ d.parent.x
+ );
+}
+
+const nodeRadius = 8;
+const verticalSpaceBetweenNodes = 70;
+const NARROW_HORIZONTAL_SPACES = 47;
+const WIDE_HORIZONTAL_SPACES = 65;
+
+const stratifyFn = stratify()
+ .id(d => d.id)
+ .parentId(d => d.parent);
+
+class Tree extends Component {
+ static propTypes = {
+ name: PropTypes.string,
+ width: PropTypes.number,
+ allowScaleWidth: PropTypes.bool,
+ nodes: PropTypes.arrayOf(
+ PropTypes.shape({
+ id: PropTypes.string,
+ name: PropTypes.string,
+ parent: PropTypes.string
+ })
+ ),
+ selectedNodeId: PropTypes.string,
+ onNodeClick: PropTypes.func,
+ onRenderedBeyondWidth: PropTypes.func,
+ scrollable: PropTypes.bool,
+ toWiden: PropTypes.bool
+ };
+
+ static defaultProps = {
+ width: 500,
+ allowScaleWidth: true,
+ name: 'default-name'
+ };
+
+ render() {
+ let { width, name, scrollable = false } = this.props;
+ return (
+ <div
+ className={`tree-view ${name}-container ${
+ scrollable ? 'scrollable' : ''
+ }`}>
+ <svg width={width} className={name} />
+ </div>
+ );
+ }
+
+ componentDidMount() {
+ this.renderTree();
+ }
+
+ componentDidUpdate(prevProps) {
+ if (
+ this.props.nodes.length !== prevProps.nodes.length ||
+ this.props.selectedNodeId !== prevProps.selectedNodeId
+ ) {
+ this.renderTree();
+ }
+ }
+
+ renderTree() {
+ let {
+ width,
+ nodes,
+ name,
+ allowScaleWidth,
+ selectedNodeId,
+ onRenderedBeyondWidth,
+ toWiden
+ } = this.props;
+ if (nodes.length > 0) {
+ let horizontalSpaceBetweenLeaves = toWiden
+ ? WIDE_HORIZONTAL_SPACES
+ : NARROW_HORIZONTAL_SPACES;
+ const treeFn = tree().nodeSize([
+ horizontalSpaceBetweenLeaves,
+ verticalSpaceBetweenNodes
+ ]); //.size([width - 50, height - 50])
+ let root = stratifyFn(nodes).sort((a, b) =>
+ a.data.name.localeCompare(b.data.name)
+ );
+ let svgHeight =
+ verticalSpaceBetweenNodes * root.height + nodeRadius * 6;
+
+ treeFn(root);
+
+ let nodesXValue = root.descendants().map(node => node.x);
+ let maxX = Math.max(...nodesXValue);
+ let minX = Math.min(...nodesXValue);
+
+ let svgTempWidth =
+ (maxX - minX) / 30 * horizontalSpaceBetweenLeaves;
+ let svgWidth = svgTempWidth < width ? width - 5 : svgTempWidth;
+ const svgEL = select(`svg.${name}`);
+ const container = select(`.tree-view.${name}-container`);
+ svgEL.html('');
+ svgEL.attr('height', svgHeight);
+ let canvasWidth = width;
+ if (svgTempWidth > width) {
+ if (allowScaleWidth) {
+ canvasWidth = svgTempWidth;
+ }
+ // we seems to have a margin of 25px that we can still see with text
+ if (
+ svgTempWidth - 25 > width &&
+ onRenderedBeyondWidth !== undefined
+ ) {
+ onRenderedBeyondWidth();
+ }
+ }
+ svgEL.attr('width', canvasWidth);
+ let rootGroup = svgEL
+ .append('g')
+ .attr(
+ 'transform',
+ `translate(${svgWidth / 2 + nodeRadius},${nodeRadius *
+ 4}) rotate(90)`
+ );
+
+ // handle link
+ rootGroup
+ .selectAll('.link')
+ .data(root.descendants().slice(1))
+ .enter()
+ .append('path')
+ .attr('class', 'link')
+ .attr('d', diagonal);
+
+ let node = rootGroup
+ .selectAll('.node')
+ .data(root.descendants())
+ .enter()
+ .append('g')
+ .attr(
+ 'class',
+ node =>
+ `node ${node.children ? ' has-children' : ' leaf'} ${
+ node.id === selectedNodeId ? 'selectedNode' : ''
+ } ${this.props.onNodeClick ? 'clickable' : ''}`
+ )
+ .attr(
+ 'transform',
+ node => 'translate(' + node.y + ',' + node.x + ')'
+ )
+ .on('click', node => this.onNodeClick(node));
+
+ node
+ .append('circle')
+ .attr('r', nodeRadius)
+ .attr('class', 'outer-circle');
+ node
+ .append('circle')
+ .attr('r', nodeRadius - 3)
+ .attr('class', 'inner-circle');
+
+ node
+ .append('text')
+ .attr('y', nodeRadius / 4 + 1)
+ .attr('x', -nodeRadius * 1.8)
+ .text(node => node.data.name)
+ .attr('transform', 'rotate(-90)');
+
+ let selectedNode = selectedNodeId
+ ? root.descendants().find(node => node.id === selectedNodeId)
+ : null;
+ if (selectedNode) {
+ container.property(
+ 'scrollLeft',
+ svgWidth / 4 +
+ (svgWidth / 4 - 100) -
+ selectedNode.x / 30 * horizontalSpaceBetweenLeaves
+ );
+ container.property(
+ 'scrollTop',
+ selectedNode.y / 100 * verticalSpaceBetweenNodes
+ );
+ } else {
+ container.property(
+ 'scrollLeft',
+ svgWidth / 4 + (svgWidth / 4 - 100)
+ );
+ }
+ }
+ }
+
+ onNodeClick(node) {
+ if (this.props.onNodeClick) {
+ this.props.onNodeClick(node.data);
+ }
+ }
+}
+
+export default Tree;