2 * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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
13 * or implied. See the License for the specific language governing
14 * permissions and limitations under the License.
16 import React from 'react';
17 import SVGIcon from 'sdc-ui/lib/react/SVGIcon.js';
18 import Input from 'nfvo-components/input/validation/InputWrapper.jsx';
20 class DualListboxView extends React.Component {
24 availableList: React.PropTypes.arrayOf(React.PropTypes.shape({
25 id: React.PropTypes.string.isRequired,
26 name: React.PropTypes.string.isRequired
28 filterTitle: React.PropTypes.shape({
29 left: React.PropTypes.string,
30 right: React.PropTypes.string
32 selectedValuesList: React.PropTypes.arrayOf(React.PropTypes.string),
34 onChange: React.PropTypes.func.isRequired
37 static defaultProps = {
38 selectedValuesList: [],
47 availableListFilter: '',
48 selectedValuesListFilter: '',
53 let {availableList, selectedValuesList, filterTitle, isReadOnlyMode} = this.props;
54 let {availableListFilter, selectedValuesListFilter} = this.state;
56 let unselectedList = availableList.filter(availableItem => !selectedValuesList.find(value => value === availableItem.id));
57 let selectedList = availableList.filter(availableItem => selectedValuesList.find(value => value === availableItem.id));
58 selectedList = selectedList.sort((a, b) => selectedValuesList.indexOf(a.id) - selectedValuesList.indexOf(b.id));
60 <div className='dual-list-box'>
61 {this.renderListbox(filterTitle.left, unselectedList, {
62 value: availableListFilter,
63 ref: 'availableListFilter',
64 disabled: isReadOnlyMode,
65 onChange: (value) => this.setState({availableListFilter: value})
66 }, {ref: 'availableValues', disabled: isReadOnlyMode, testId: 'available',})}
67 {this.renderOperationsBar(isReadOnlyMode)}
68 {this.renderListbox(filterTitle.right, selectedList, {
69 value: selectedValuesListFilter,
70 ref: 'selectedValuesListFilter',
71 disabled: isReadOnlyMode,
72 onChange: (value) => this.setState({selectedValuesListFilter: value})
73 }, {ref: 'selectedValues', disabled: isReadOnlyMode, testId: 'selected'})}
78 renderListbox(filterTitle, list, filterProps, props) {
79 let regExFilter = new RegExp(escape(filterProps.value), 'i');
80 let matchedItems = list.filter(item => item.name.match(regExFilter));
81 let unMatchedItems = list.filter(item => !item.name.match(regExFilter));
83 <div className='dual-search-multi-select-section'>
85 <div className='dual-text-box-search search-wrapper'>
86 <Input data-test-id={`${props.testId}-search-input`}
87 name='search-input-control' type='text'
88 groupClassName='search-input-control'
90 <SVGIcon name='search' className='search-icon'/>
94 onChange={(event) => this.onSelectItems(event.target.selectedOptions)}
95 groupClassName='dual-list-box-multi-select'
97 name='dual-list-box-multi-select'
98 data-test-id={`${props.testId}-select-input`}
99 disabled={props.disabled}
101 {matchedItems.map(item => this.renderOption(item.id, item.name))}
102 {matchedItems.length && unMatchedItems.length && <option style={{pointerEvents: 'none'}}>--------------------</option>}
103 {unMatchedItems.map(item => this.renderOption(item.id, item.name))}
109 onSelectItems(selectedOptions) {
110 let selectedValues = Object.keys(selectedOptions).map((k) => selectedOptions[k].value);
111 this.setState({selectedValues});
114 renderOption(value, name) {
115 return (<option className='dual-list-box-multi-select-text' key={value} value={value}>{name}</option>);
118 renderOperationsBar(isReadOnlyMode) {
120 <div className={`dual-list-options-bar${isReadOnlyMode ? ' disabled' : ''}`}>
121 {this.renderOperationBarButton(() => this.addToSelectedList(), 'angleRight')}
122 {this.renderOperationBarButton(() => this.removeFromSelectedList(), 'angleLeft')}
123 {this.renderOperationBarButton(() => this.addAllToSelectedList(), 'angleDoubleRight')}
124 {this.renderOperationBarButton(() => this.removeAllFromSelectedList(), 'angleDoubleLeft')}
129 renderOperationBarButton(onClick, iconName){
130 return (<div className='dual-list-option' data-test-id={`operation-icon-${iconName}`} onClick={onClick}><SVGIcon name={iconName}/></div>);
133 addToSelectedList() {
134 this.props.onChange(this.props.selectedValuesList.concat(this.state.selectedValues));
135 this.setState({selectedValues: []});
138 removeFromSelectedList() {
139 const selectedValues = this.state.selectedValues;
140 this.props.onChange(this.props.selectedValuesList.filter(value => !selectedValues.find(selectedValue => selectedValue === value)));
141 this.setState({selectedValues: []});
144 addAllToSelectedList() {
145 this.props.onChange(this.props.availableList.map(item => item.id));
148 removeAllFromSelectedList() {
149 this.props.onChange([]);
153 export default DualListboxView;