react 16 upgrade
[sdc.git] / openecomp-ui / src / nfvo-components / input / dualListbox / DualListboxView.jsx
1 /*
2  * Copyright © 2016-2018 European Support Limited
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 import React from 'react';
17 import PropTypes from 'prop-types';
18 import SVGIcon from 'sdc-ui/lib/react/SVGIcon.js';
19 import Input from 'nfvo-components/input/validation/Input.jsx';
20
21 class DualListboxView extends React.Component {
22     constructor(props) {
23         super(props);
24         this.availableListRef = React.createRef();
25         this.availableListFilterRef = React.createRef();
26         this.selectedValuesListFilterRef = React.createRef();
27         this.selectedValuesRef = React.createRef();
28     }
29     static propTypes = {
30         availableList: PropTypes.arrayOf(
31             PropTypes.shape({
32                 id: PropTypes.string.isRequired,
33                 name: PropTypes.string.isRequired
34             })
35         ),
36         filterTitle: PropTypes.shape({
37             left: PropTypes.string,
38             right: PropTypes.string
39         }),
40         selectedValuesList: PropTypes.arrayOf(PropTypes.string),
41
42         onChange: PropTypes.func.isRequired
43     };
44
45     static defaultProps = {
46         selectedValuesList: [],
47         availableList: [],
48         filterTitle: {
49             left: '',
50             right: ''
51         }
52     };
53
54     state = {
55         availableListFilter: '',
56         selectedValuesListFilter: '',
57         selectedValues: []
58     };
59
60     render() {
61         let {
62             availableList,
63             selectedValuesList,
64             filterTitle,
65             isReadOnlyMode
66         } = this.props;
67
68         let { availableListFilter, selectedValuesListFilter } = this.state;
69
70         let unselectedList = availableList.filter(
71             availableItem =>
72                 !selectedValuesList.find(value => value === availableItem.id)
73         );
74         let selectedList = availableList.filter(availableItem =>
75             selectedValuesList.find(value => value === availableItem.id)
76         );
77         selectedList = selectedList.sort(
78             (a, b) => availableList.indexOf(a.id) - availableList.indexOf(b.id)
79         );
80         return (
81             <div className="dual-list-box">
82                 {this.renderListbox(
83                     filterTitle.left,
84                     unselectedList,
85                     {
86                         value: availableListFilter,
87                         ref: this.availableListFilterRef,
88                         disabled: isReadOnlyMode,
89                         onChange: value =>
90                             this.setState({ availableListFilter: value })
91                     },
92                     {
93                         ref: this.availableListRef,
94                         disabled: isReadOnlyMode,
95                         testId: 'available'
96                     }
97                 )}
98                 {this.renderOperationsBar(isReadOnlyMode)}
99                 {this.renderListbox(
100                     filterTitle.right,
101                     selectedList,
102                     {
103                         value: selectedValuesListFilter,
104                         ref: this.selectedValuesListFilterRef,
105                         disabled: isReadOnlyMode,
106                         onChange: value =>
107                             this.setState({ selectedValuesListFilter: value })
108                     },
109                     {
110                         ref: this.selectedValuesRef,
111                         disabled: isReadOnlyMode,
112                         testId: 'selected'
113                     }
114                 )}
115             </div>
116         );
117     }
118
119     renderListbox(filterTitle, list, filterProps, props) {
120         let regExFilter = new RegExp(escape(filterProps.value), 'i');
121         let matchedItems = list.filter(item => item.name.match(regExFilter));
122         let unMatchedItems = list.filter(item => !item.name.match(regExFilter));
123         return (
124             <div className="dual-search-multi-select-section">
125                 <p>{filterTitle}</p>
126                 <div className="dual-text-box-search search-wrapper">
127                     <Input
128                         data-test-id={`${props.testId}-search-input`}
129                         name="search-input-control"
130                         type="text"
131                         groupClassName="search-input-control"
132                         {...filterProps}
133                     />
134                     <SVGIcon name="search" className="search-icon" />
135                 </div>
136                 <Input
137                     multiple
138                     onChange={event =>
139                         this.onSelectItems(event.target.selectedOptions)
140                     }
141                     groupClassName="dual-list-box-multi-select"
142                     type="select"
143                     name="dual-list-box-multi-select"
144                     data-test-id={`${props.testId}-select-input`}
145                     disabled={props.disabled}
146                     ref={props.ref}>
147                     {matchedItems.map(item =>
148                         this.renderOption(item.id, item.name)
149                     )}
150                     {matchedItems.length &&
151                         unMatchedItems.length && (
152                             <option style={{ pointerEvents: 'none' }}>
153                                 --------------------
154                             </option>
155                         )}
156                     {unMatchedItems.map(item =>
157                         this.renderOption(item.id, item.name)
158                     )}
159                 </Input>
160             </div>
161         );
162     }
163
164     onSelectItems(selectedOptions) {
165         let selectedValues = Object.keys(selectedOptions).map(
166             k => selectedOptions[k].value
167         );
168         this.setState({ selectedValues });
169     }
170
171     renderOption(value, name) {
172         return (
173             <option
174                 className="dual-list-box-multi-select-text"
175                 key={value}
176                 value={value}>
177                 {name}
178             </option>
179         );
180     }
181
182     renderOperationsBar(isReadOnlyMode) {
183         return (
184             <div
185                 className={`dual-list-options-bar${
186                     isReadOnlyMode ? ' disabled' : ''
187                 }`}>
188                 {this.renderOperationBarButton(
189                     () => this.addToSelectedList(),
190                     'angleRight'
191                 )}
192                 {this.renderOperationBarButton(
193                     () => this.removeFromSelectedList(),
194                     'angleLeft'
195                 )}
196                 {this.renderOperationBarButton(
197                     () => this.addAllToSelectedList(),
198                     'angleDoubleRight'
199                 )}
200                 {this.renderOperationBarButton(
201                     () => this.removeAllFromSelectedList(),
202                     'angleDoubleLeft'
203                 )}
204             </div>
205         );
206     }
207
208     renderOperationBarButton(onClick, iconName) {
209         return (
210             <div
211                 className="dual-list-option"
212                 data-test-id={`operation-icon-${iconName}`}
213                 onClick={onClick}>
214                 <SVGIcon name={iconName} />
215             </div>
216         );
217     }
218
219     addToSelectedList() {
220         this.props.onChange(
221             this.props.selectedValuesList.concat(this.state.selectedValues)
222         );
223         this.setState({ selectedValues: [] });
224     }
225
226     removeFromSelectedList() {
227         const selectedValues = this.state.selectedValues;
228         this.props.onChange(
229             this.props.selectedValuesList.filter(
230                 value =>
231                     !selectedValues.find(
232                         selectedValue => selectedValue === value
233                     )
234             )
235         );
236         this.setState({ selectedValues: [] });
237     }
238
239     addAllToSelectedList() {
240         this.props.onChange(this.props.availableList.map(item => item.id));
241     }
242
243     removeAllFromSelectedList() {
244         this.props.onChange([]);
245     }
246
247     // fix for auto-selection of first value in the list on the first render
248     componentDidMount() {
249         this.availableListRef.current.input.value = '';
250         this.selectedValuesRef.current.input.value = '';
251     }
252 }
253
254 export default DualListboxView;