@pareto-engineering/design-system 2.0.0-alpha.46 → 2.0.0-alpha.47
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/f/fields/QueryCombobox/QueryCombobox.js +22 -6
- package/dist/cjs/f/fields/QueryCombobox/common/Combobox/Combobox.js +20 -9
- package/dist/cjs/f/fields/QueryCombobox/common/MultipleCombobox/MultipleCombobox.js +29 -11
- package/dist/cjs/f/fields/QueryCombobox/common/index.js +9 -1
- package/dist/cjs/f/fields/QueryCombobox/styles.scss +1 -7
- package/dist/es/f/fields/QueryCombobox/QueryCombobox.js +23 -7
- package/dist/es/f/fields/QueryCombobox/common/Combobox/Combobox.js +20 -9
- package/dist/es/f/fields/QueryCombobox/common/MultipleCombobox/MultipleCombobox.js +29 -11
- package/dist/es/f/fields/QueryCombobox/common/index.js +2 -1
- package/dist/es/f/fields/QueryCombobox/styles.scss +1 -7
- package/package.json +1 -1
- package/src/__snapshots__/Storyshots.test.js.snap +200 -5
- package/src/local.scss +3 -3
- package/src/stories/f/QueryCombobox.stories.jsx +55 -8
- package/src/ui/f/fields/QueryCombobox/QueryCombobox.jsx +22 -6
- package/src/ui/f/fields/QueryCombobox/common/Combobox/Combobox.jsx +15 -7
- package/src/ui/f/fields/QueryCombobox/common/MultipleCombobox/MultipleCombobox.jsx +317 -0
- package/src/ui/f/fields/QueryCombobox/common/MultipleCombobox/index.js +2 -0
- package/src/ui/f/fields/QueryCombobox/common/index.js +1 -0
- package/src/ui/f/fields/QueryCombobox/styles.scss +1 -7
|
@@ -36,7 +36,7 @@ var QueryCombobox = _ref => {
|
|
|
36
36
|
style,
|
|
37
37
|
className,
|
|
38
38
|
query,
|
|
39
|
-
|
|
39
|
+
multiple,
|
|
40
40
|
name,
|
|
41
41
|
label,
|
|
42
42
|
color,
|
|
@@ -46,7 +46,9 @@ var QueryCombobox = _ref => {
|
|
|
46
46
|
graphQlNode,
|
|
47
47
|
searchVariable,
|
|
48
48
|
extraVariables,
|
|
49
|
-
optionsKeyMap
|
|
49
|
+
optionsKeyMap,
|
|
50
|
+
minLength,
|
|
51
|
+
transformSearch // ...otherProps
|
|
50
52
|
|
|
51
53
|
} = _ref;
|
|
52
54
|
(0, React.useLayoutEffect)(() => {
|
|
@@ -115,9 +117,11 @@ var QueryCombobox = _ref => {
|
|
|
115
117
|
value,
|
|
116
118
|
color,
|
|
117
119
|
isFetching,
|
|
118
|
-
className
|
|
120
|
+
className,
|
|
121
|
+
minLength,
|
|
122
|
+
transformSearch
|
|
119
123
|
};
|
|
120
|
-
var Input = _common.Combobox;
|
|
124
|
+
var Input = multiple ? _common.MultipleCombobox : _common.Combobox;
|
|
121
125
|
return /*#__PURE__*/React.createElement(Input, comboboxProps);
|
|
122
126
|
};
|
|
123
127
|
|
|
@@ -199,7 +203,17 @@ QueryCombobox.propTypes = {
|
|
|
199
203
|
/**
|
|
200
204
|
* The variable to be used to search the data
|
|
201
205
|
*/
|
|
202
|
-
searchVariable: _propTypes.default.string
|
|
206
|
+
searchVariable: _propTypes.default.string,
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* The minimum length of the search input to start fetching the options
|
|
210
|
+
*/
|
|
211
|
+
minLength: _propTypes.default.number,
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* The function to transform the search input
|
|
215
|
+
*/
|
|
216
|
+
transformSearch: _propTypes.default.func
|
|
203
217
|
};
|
|
204
218
|
QueryCombobox.defaultProps = {
|
|
205
219
|
optionsKeyMap: {
|
|
@@ -208,7 +222,9 @@ QueryCombobox.defaultProps = {
|
|
|
208
222
|
},
|
|
209
223
|
multiple: false,
|
|
210
224
|
color: 'background2',
|
|
211
|
-
searchVariable: 'search'
|
|
225
|
+
searchVariable: 'search',
|
|
226
|
+
transformSearch: search => search,
|
|
227
|
+
minLength: 2
|
|
212
228
|
};
|
|
213
229
|
var _default = QueryCombobox;
|
|
214
230
|
exports.default = _default;
|
|
@@ -47,8 +47,9 @@ var Combobox = _ref => {
|
|
|
47
47
|
description,
|
|
48
48
|
value,
|
|
49
49
|
color,
|
|
50
|
-
|
|
51
|
-
isFetching
|
|
50
|
+
minLength,
|
|
51
|
+
isFetching,
|
|
52
|
+
transformSearch // ...otherProps
|
|
52
53
|
|
|
53
54
|
} = _ref;
|
|
54
55
|
var {
|
|
@@ -69,7 +70,11 @@ var Combobox = _ref => {
|
|
|
69
70
|
var {
|
|
70
71
|
inputValue
|
|
71
72
|
} = _ref2;
|
|
72
|
-
|
|
73
|
+
var transformedInput = transformSearch(inputValue);
|
|
74
|
+
|
|
75
|
+
if (transformedInput.length > minLength) {
|
|
76
|
+
getOptions(transformedInput);
|
|
77
|
+
}
|
|
73
78
|
}
|
|
74
79
|
}); // If the user has selected an item, we'll set the value of the field
|
|
75
80
|
// or if the combobox state has a selected item, we'll set the value to the formik state
|
|
@@ -88,7 +93,7 @@ var Combobox = _ref => {
|
|
|
88
93
|
var parentRef = (0, React.useRef)(null);
|
|
89
94
|
return /*#__PURE__*/React.createElement("div", {
|
|
90
95
|
id: id,
|
|
91
|
-
className: [baseClassName, componentClassName, userClassName, "y-".concat(color)
|
|
96
|
+
className: [baseClassName, componentClassName, userClassName, "y-".concat(color)].filter(e => e).join(' '),
|
|
92
97
|
style: style,
|
|
93
98
|
ref: parentRef
|
|
94
99
|
}, /*#__PURE__*/React.createElement(_.FormLabel, _extends({}, getLabelProps(), {
|
|
@@ -97,7 +102,9 @@ var Combobox = _ref => {
|
|
|
97
102
|
className: "input-wrapper"
|
|
98
103
|
}), /*#__PURE__*/React.createElement("input", _extends({}, getInputProps(), {
|
|
99
104
|
className: "input"
|
|
100
|
-
})), isFetching && /*#__PURE__*/React.createElement(_a.LoadingCircle,
|
|
105
|
+
})), isFetching && /*#__PURE__*/React.createElement(_a.LoadingCircle, {
|
|
106
|
+
className: "x-main2"
|
|
107
|
+
})), /*#__PURE__*/React.createElement(_a.Popover, {
|
|
101
108
|
isOpen: isOpen,
|
|
102
109
|
parentRef: parentRef
|
|
103
110
|
}, /*#__PURE__*/React.createElement(_Menu.Menu, _extends({
|
|
@@ -183,12 +190,16 @@ Combobox.propTypes = {
|
|
|
183
190
|
isFetching: _propTypes.default.bool.isRequired,
|
|
184
191
|
|
|
185
192
|
/**
|
|
186
|
-
* The
|
|
193
|
+
* The minimum length of the search input to start fetching the options
|
|
194
|
+
*/
|
|
195
|
+
minLength: _propTypes.default.number,
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* The function to transform the search input
|
|
187
199
|
*/
|
|
188
|
-
|
|
200
|
+
transformSearch: _propTypes.default.func
|
|
189
201
|
};
|
|
190
|
-
Combobox.defaultProps = {
|
|
191
|
-
loadingCircleColor: 'main2'
|
|
202
|
+
Combobox.defaultProps = {// someProp: false
|
|
192
203
|
};
|
|
193
204
|
var _default = Combobox;
|
|
194
205
|
exports.default = _default;
|
|
@@ -65,7 +65,8 @@ var MultipleCombobox = _ref => {
|
|
|
65
65
|
value,
|
|
66
66
|
color,
|
|
67
67
|
isFetching,
|
|
68
|
-
|
|
68
|
+
minLength,
|
|
69
|
+
transformSearch // ...otherProps
|
|
69
70
|
|
|
70
71
|
} = _ref;
|
|
71
72
|
var [searchInputValue, setSearchInputValue] = (0, React.useState)('');
|
|
@@ -79,8 +80,12 @@ var MultipleCombobox = _ref => {
|
|
|
79
80
|
} = (0, _downshift.useMultipleSelection)({
|
|
80
81
|
initialSelectedItems: value || []
|
|
81
82
|
});
|
|
83
|
+
/**
|
|
84
|
+
* @returns {Boolean} - Unique items from the options array so that the combobox
|
|
85
|
+
* shows only the options that are not yet selected.
|
|
86
|
+
*/
|
|
82
87
|
|
|
83
|
-
var getFilteredItems = () => items.filter(item => selectedItems.findIndex(e => e.label === item.label) < 0
|
|
88
|
+
var getFilteredItems = () => items.filter(item => selectedItems.findIndex(e => e.label === item.label) < 0);
|
|
84
89
|
|
|
85
90
|
var {
|
|
86
91
|
isOpen,
|
|
@@ -126,9 +131,16 @@ var MultipleCombobox = _ref => {
|
|
|
126
131
|
|
|
127
132
|
switch (type) {
|
|
128
133
|
case _downshift.useCombobox.stateChangeTypes.InputChange:
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
134
|
+
{
|
|
135
|
+
var transformedInput = transformSearch(newSearchInputValue);
|
|
136
|
+
|
|
137
|
+
if (transformedInput.length > minLength) {
|
|
138
|
+
getOptions(transformedInput);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
setSearchInputValue(newSearchInputValue);
|
|
142
|
+
break;
|
|
143
|
+
}
|
|
132
144
|
|
|
133
145
|
case _downshift.useCombobox.stateChangeTypes.InputKeyDownEnter:
|
|
134
146
|
case _downshift.useCombobox.stateChangeTypes.ItemClick:
|
|
@@ -158,7 +170,7 @@ var MultipleCombobox = _ref => {
|
|
|
158
170
|
var parentRef = (0, React.useRef)(null);
|
|
159
171
|
return /*#__PURE__*/React.createElement("div", {
|
|
160
172
|
id: id,
|
|
161
|
-
className: [baseClassName, componentClassName, userClassName, "y-".concat(color)
|
|
173
|
+
className: [baseClassName, componentClassName, userClassName, "y-".concat(color)].filter(e => e).join(' '),
|
|
162
174
|
style: style
|
|
163
175
|
}, /*#__PURE__*/React.createElement(_.FormLabel, _extends({}, getLabelProps(), {
|
|
164
176
|
name: name
|
|
@@ -184,7 +196,9 @@ var MultipleCombobox = _ref => {
|
|
|
184
196
|
preventKeyAction: isOpen
|
|
185
197
|
})), {
|
|
186
198
|
className: "input"
|
|
187
|
-
})), isFetching && /*#__PURE__*/React.createElement(_a.LoadingCircle,
|
|
199
|
+
})), isFetching && /*#__PURE__*/React.createElement(_a.LoadingCircle, {
|
|
200
|
+
className: "x-main2"
|
|
201
|
+
})), /*#__PURE__*/React.createElement(_a.Popover, {
|
|
188
202
|
isOpen: isOpen,
|
|
189
203
|
parentRef: parentRef
|
|
190
204
|
}, /*#__PURE__*/React.createElement(_Menu.Menu, _extends({
|
|
@@ -270,12 +284,16 @@ MultipleCombobox.propTypes = {
|
|
|
270
284
|
isFetching: _propTypes.default.bool.isRequired,
|
|
271
285
|
|
|
272
286
|
/**
|
|
273
|
-
* The
|
|
287
|
+
* The minimum length of the search input to start fetching the options
|
|
288
|
+
*/
|
|
289
|
+
minLength: _propTypes.default.number,
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* The function to transform the search input
|
|
274
293
|
*/
|
|
275
|
-
|
|
294
|
+
transformSearch: _propTypes.default.func
|
|
276
295
|
};
|
|
277
|
-
MultipleCombobox.defaultProps = {
|
|
278
|
-
loadingCircleColor: 'main2'
|
|
296
|
+
MultipleCombobox.defaultProps = {// someProp: false
|
|
279
297
|
};
|
|
280
298
|
var _default = MultipleCombobox;
|
|
281
299
|
exports.default = _default;
|
|
@@ -15,7 +15,15 @@ Object.defineProperty(exports, "Combobox", {
|
|
|
15
15
|
return _Combobox.Combobox;
|
|
16
16
|
}
|
|
17
17
|
});
|
|
18
|
+
Object.defineProperty(exports, "MultipleCombobox", {
|
|
19
|
+
enumerable: true,
|
|
20
|
+
get: function get() {
|
|
21
|
+
return _MultipleCombobox.MultipleCombobox;
|
|
22
|
+
}
|
|
23
|
+
});
|
|
18
24
|
|
|
19
25
|
var _Menu = require("./Menu");
|
|
20
26
|
|
|
21
|
-
var _Combobox = require("./Combobox");
|
|
27
|
+
var _Combobox = require("./Combobox");
|
|
28
|
+
|
|
29
|
+
var _MultipleCombobox = require("./MultipleCombobox");
|
|
@@ -73,12 +73,6 @@ $default-loading-circle-displacement: 1em;
|
|
|
73
73
|
.#{bem.$base}.multiple-combobox {
|
|
74
74
|
>.selected-items {
|
|
75
75
|
display: flex;
|
|
76
|
-
|
|
77
|
-
/* stylelint-disable selector-max-universal -- Allow */
|
|
78
|
-
>*:not(:first-child) {
|
|
79
|
-
margin-left: $default-margin;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/* stylelint-enable selector-max-universal */
|
|
76
|
+
gap: var(--default-gap);
|
|
83
77
|
}
|
|
84
78
|
}
|
|
@@ -5,7 +5,7 @@ import { useField } from 'formik';
|
|
|
5
5
|
import { useRelayEnvironment, fetchQuery } from 'react-relay';
|
|
6
6
|
import PropTypes from 'prop-types'; // Local Definitions
|
|
7
7
|
|
|
8
|
-
import { Combobox } from "./common";
|
|
8
|
+
import { Combobox, MultipleCombobox } from "./common";
|
|
9
9
|
/**
|
|
10
10
|
* This is the component description.
|
|
11
11
|
*/
|
|
@@ -15,7 +15,7 @@ const QueryCombobox = ({
|
|
|
15
15
|
style,
|
|
16
16
|
className,
|
|
17
17
|
query,
|
|
18
|
-
|
|
18
|
+
multiple,
|
|
19
19
|
name,
|
|
20
20
|
label,
|
|
21
21
|
color,
|
|
@@ -25,7 +25,9 @@ const QueryCombobox = ({
|
|
|
25
25
|
graphQlNode,
|
|
26
26
|
searchVariable,
|
|
27
27
|
extraVariables,
|
|
28
|
-
optionsKeyMap
|
|
28
|
+
optionsKeyMap,
|
|
29
|
+
minLength,
|
|
30
|
+
transformSearch // ...otherProps
|
|
29
31
|
|
|
30
32
|
}) => {
|
|
31
33
|
useLayoutEffect(() => {
|
|
@@ -93,9 +95,11 @@ const QueryCombobox = ({
|
|
|
93
95
|
value,
|
|
94
96
|
color,
|
|
95
97
|
isFetching,
|
|
96
|
-
className
|
|
98
|
+
className,
|
|
99
|
+
minLength,
|
|
100
|
+
transformSearch
|
|
97
101
|
};
|
|
98
|
-
const Input = Combobox;
|
|
102
|
+
const Input = multiple ? MultipleCombobox : Combobox;
|
|
99
103
|
return /*#__PURE__*/React.createElement(Input, comboboxProps);
|
|
100
104
|
};
|
|
101
105
|
|
|
@@ -177,7 +181,17 @@ QueryCombobox.propTypes = {
|
|
|
177
181
|
/**
|
|
178
182
|
* The variable to be used to search the data
|
|
179
183
|
*/
|
|
180
|
-
searchVariable: PropTypes.string
|
|
184
|
+
searchVariable: PropTypes.string,
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* The minimum length of the search input to start fetching the options
|
|
188
|
+
*/
|
|
189
|
+
minLength: PropTypes.number,
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* The function to transform the search input
|
|
193
|
+
*/
|
|
194
|
+
transformSearch: PropTypes.func
|
|
181
195
|
};
|
|
182
196
|
QueryCombobox.defaultProps = {
|
|
183
197
|
optionsKeyMap: {
|
|
@@ -186,6 +200,8 @@ QueryCombobox.defaultProps = {
|
|
|
186
200
|
},
|
|
187
201
|
multiple: false,
|
|
188
202
|
color: 'background2',
|
|
189
|
-
searchVariable: 'search'
|
|
203
|
+
searchVariable: 'search',
|
|
204
|
+
transformSearch: search => search,
|
|
205
|
+
minLength: 2
|
|
190
206
|
};
|
|
191
207
|
export default QueryCombobox;
|
|
@@ -29,8 +29,9 @@ const Combobox = ({
|
|
|
29
29
|
description,
|
|
30
30
|
value,
|
|
31
31
|
color,
|
|
32
|
-
|
|
33
|
-
isFetching
|
|
32
|
+
minLength,
|
|
33
|
+
isFetching,
|
|
34
|
+
transformSearch // ...otherProps
|
|
34
35
|
|
|
35
36
|
}) => {
|
|
36
37
|
const {
|
|
@@ -50,7 +51,11 @@ const Combobox = ({
|
|
|
50
51
|
onInputValueChange: ({
|
|
51
52
|
inputValue
|
|
52
53
|
}) => {
|
|
53
|
-
|
|
54
|
+
const transformedInput = transformSearch(inputValue);
|
|
55
|
+
|
|
56
|
+
if (transformedInput.length > minLength) {
|
|
57
|
+
getOptions(transformedInput);
|
|
58
|
+
}
|
|
54
59
|
}
|
|
55
60
|
}); // If the user has selected an item, we'll set the value of the field
|
|
56
61
|
// or if the combobox state has a selected item, we'll set the value to the formik state
|
|
@@ -69,7 +74,7 @@ const Combobox = ({
|
|
|
69
74
|
const parentRef = useRef(null);
|
|
70
75
|
return /*#__PURE__*/React.createElement("div", {
|
|
71
76
|
id: id,
|
|
72
|
-
className: [baseClassName, componentClassName, userClassName, `y-${color}
|
|
77
|
+
className: [baseClassName, componentClassName, userClassName, `y-${color}`].filter(e => e).join(' '),
|
|
73
78
|
style: style,
|
|
74
79
|
ref: parentRef
|
|
75
80
|
}, /*#__PURE__*/React.createElement(FormLabel, _extends({}, getLabelProps(), {
|
|
@@ -78,7 +83,9 @@ const Combobox = ({
|
|
|
78
83
|
className: "input-wrapper"
|
|
79
84
|
}), /*#__PURE__*/React.createElement("input", _extends({}, getInputProps(), {
|
|
80
85
|
className: "input"
|
|
81
|
-
})), isFetching && /*#__PURE__*/React.createElement(LoadingCircle,
|
|
86
|
+
})), isFetching && /*#__PURE__*/React.createElement(LoadingCircle, {
|
|
87
|
+
className: "x-main2"
|
|
88
|
+
})), /*#__PURE__*/React.createElement(Popover, {
|
|
82
89
|
isOpen: isOpen,
|
|
83
90
|
parentRef: parentRef
|
|
84
91
|
}, /*#__PURE__*/React.createElement(Menu, _extends({
|
|
@@ -164,11 +171,15 @@ Combobox.propTypes = {
|
|
|
164
171
|
isFetching: PropTypes.bool.isRequired,
|
|
165
172
|
|
|
166
173
|
/**
|
|
167
|
-
* The
|
|
174
|
+
* The minimum length of the search input to start fetching the options
|
|
175
|
+
*/
|
|
176
|
+
minLength: PropTypes.number,
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* The function to transform the search input
|
|
168
180
|
*/
|
|
169
|
-
|
|
181
|
+
transformSearch: PropTypes.func
|
|
170
182
|
};
|
|
171
|
-
Combobox.defaultProps = {
|
|
172
|
-
loadingCircleColor: 'main2'
|
|
183
|
+
Combobox.defaultProps = {// someProp: false
|
|
173
184
|
};
|
|
174
185
|
export default Combobox;
|
|
@@ -40,7 +40,8 @@ const MultipleCombobox = ({
|
|
|
40
40
|
value,
|
|
41
41
|
color,
|
|
42
42
|
isFetching,
|
|
43
|
-
|
|
43
|
+
minLength,
|
|
44
|
+
transformSearch // ...otherProps
|
|
44
45
|
|
|
45
46
|
}) => {
|
|
46
47
|
const [searchInputValue, setSearchInputValue] = useState('');
|
|
@@ -54,8 +55,12 @@ const MultipleCombobox = ({
|
|
|
54
55
|
} = useMultipleSelection({
|
|
55
56
|
initialSelectedItems: value || []
|
|
56
57
|
});
|
|
58
|
+
/**
|
|
59
|
+
* @returns {Boolean} - Unique items from the options array so that the combobox
|
|
60
|
+
* shows only the options that are not yet selected.
|
|
61
|
+
*/
|
|
57
62
|
|
|
58
|
-
const getFilteredItems = () => items.filter(item => selectedItems.findIndex(e => e.label === item.label) < 0
|
|
63
|
+
const getFilteredItems = () => items.filter(item => selectedItems.findIndex(e => e.label === item.label) < 0);
|
|
59
64
|
|
|
60
65
|
const {
|
|
61
66
|
isOpen,
|
|
@@ -99,9 +104,16 @@ const MultipleCombobox = ({
|
|
|
99
104
|
}) => {
|
|
100
105
|
switch (type) {
|
|
101
106
|
case useCombobox.stateChangeTypes.InputChange:
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
107
|
+
{
|
|
108
|
+
const transformedInput = transformSearch(newSearchInputValue);
|
|
109
|
+
|
|
110
|
+
if (transformedInput.length > minLength) {
|
|
111
|
+
getOptions(transformedInput);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
setSearchInputValue(newSearchInputValue);
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
105
117
|
|
|
106
118
|
case useCombobox.stateChangeTypes.InputKeyDownEnter:
|
|
107
119
|
case useCombobox.stateChangeTypes.ItemClick:
|
|
@@ -131,7 +143,7 @@ const MultipleCombobox = ({
|
|
|
131
143
|
const parentRef = useRef(null);
|
|
132
144
|
return /*#__PURE__*/React.createElement("div", {
|
|
133
145
|
id: id,
|
|
134
|
-
className: [baseClassName, componentClassName, userClassName, `y-${color}
|
|
146
|
+
className: [baseClassName, componentClassName, userClassName, `y-${color}`].filter(e => e).join(' '),
|
|
135
147
|
style: style
|
|
136
148
|
}, /*#__PURE__*/React.createElement(FormLabel, _extends({}, getLabelProps(), {
|
|
137
149
|
name: name
|
|
@@ -157,7 +169,9 @@ const MultipleCombobox = ({
|
|
|
157
169
|
preventKeyAction: isOpen
|
|
158
170
|
})), {
|
|
159
171
|
className: "input"
|
|
160
|
-
})), isFetching && /*#__PURE__*/React.createElement(LoadingCircle,
|
|
172
|
+
})), isFetching && /*#__PURE__*/React.createElement(LoadingCircle, {
|
|
173
|
+
className: "x-main2"
|
|
174
|
+
})), /*#__PURE__*/React.createElement(Popover, {
|
|
161
175
|
isOpen: isOpen,
|
|
162
176
|
parentRef: parentRef
|
|
163
177
|
}, /*#__PURE__*/React.createElement(Menu, _extends({
|
|
@@ -243,11 +257,15 @@ MultipleCombobox.propTypes = {
|
|
|
243
257
|
isFetching: PropTypes.bool.isRequired,
|
|
244
258
|
|
|
245
259
|
/**
|
|
246
|
-
* The
|
|
260
|
+
* The minimum length of the search input to start fetching the options
|
|
261
|
+
*/
|
|
262
|
+
minLength: PropTypes.number,
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* The function to transform the search input
|
|
247
266
|
*/
|
|
248
|
-
|
|
267
|
+
transformSearch: PropTypes.func
|
|
249
268
|
};
|
|
250
|
-
MultipleCombobox.defaultProps = {
|
|
251
|
-
loadingCircleColor: 'main2'
|
|
269
|
+
MultipleCombobox.defaultProps = {// someProp: false
|
|
252
270
|
};
|
|
253
271
|
export default MultipleCombobox;
|
|
@@ -73,12 +73,6 @@ $default-loading-circle-displacement: 1em;
|
|
|
73
73
|
.#{bem.$base}.multiple-combobox {
|
|
74
74
|
>.selected-items {
|
|
75
75
|
display: flex;
|
|
76
|
-
|
|
77
|
-
/* stylelint-disable selector-max-universal -- Allow */
|
|
78
|
-
>*:not(:first-child) {
|
|
79
|
-
margin-left: $default-margin;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/* stylelint-enable selector-max-universal */
|
|
76
|
+
gap: var(--default-gap);
|
|
83
77
|
}
|
|
84
78
|
}
|
package/package.json
CHANGED
|
@@ -10737,7 +10737,7 @@ exports[`Storyshots f/FormInput With Query Combobox 1`] = `
|
|
|
10737
10737
|
</div>
|
|
10738
10738
|
</div>
|
|
10739
10739
|
<div
|
|
10740
|
-
className="base combobox form-input y-background2
|
|
10740
|
+
className="base combobox form-input y-background2"
|
|
10741
10741
|
>
|
|
10742
10742
|
<label
|
|
10743
10743
|
className="base label x-main2"
|
|
@@ -11353,6 +11353,201 @@ exports[`Storyshots f/fields/ChoicesInput Multiple With Grid 1`] = `
|
|
|
11353
11353
|
</form>
|
|
11354
11354
|
`;
|
|
11355
11355
|
|
|
11356
|
+
exports[`Storyshots f/fields/QueryCombobox Multiple Select 1`] = `
|
|
11357
|
+
<form
|
|
11358
|
+
action="#"
|
|
11359
|
+
onReset={[Function]}
|
|
11360
|
+
onSubmit={[Function]}
|
|
11361
|
+
>
|
|
11362
|
+
<div
|
|
11363
|
+
className="base multiple-combobox y-background2"
|
|
11364
|
+
>
|
|
11365
|
+
<label
|
|
11366
|
+
className="base label x-main2"
|
|
11367
|
+
htmlFor="teams"
|
|
11368
|
+
id="downshift-3-label"
|
|
11369
|
+
>
|
|
11370
|
+
Search for a team
|
|
11371
|
+
</label>
|
|
11372
|
+
<div
|
|
11373
|
+
className="selected-items"
|
|
11374
|
+
/>
|
|
11375
|
+
<div
|
|
11376
|
+
aria-expanded={false}
|
|
11377
|
+
aria-haspopup="listbox"
|
|
11378
|
+
aria-owns="downshift-3-menu"
|
|
11379
|
+
className="input-wrapper"
|
|
11380
|
+
role="combobox"
|
|
11381
|
+
>
|
|
11382
|
+
<input
|
|
11383
|
+
aria-autocomplete="list"
|
|
11384
|
+
aria-controls="downshift-3-menu"
|
|
11385
|
+
aria-labelledby="downshift-3-label"
|
|
11386
|
+
autoComplete="off"
|
|
11387
|
+
className="input"
|
|
11388
|
+
id="downshift-3-input"
|
|
11389
|
+
onBlur={[Function]}
|
|
11390
|
+
onChange={[Function]}
|
|
11391
|
+
onClick={[Function]}
|
|
11392
|
+
onKeyDown={[Function]}
|
|
11393
|
+
value=""
|
|
11394
|
+
/>
|
|
11395
|
+
</div>
|
|
11396
|
+
<div
|
|
11397
|
+
className="base popover x-background1 bottom left"
|
|
11398
|
+
>
|
|
11399
|
+
<ul
|
|
11400
|
+
aria-labelledby="downshift-3-label"
|
|
11401
|
+
className="base menu"
|
|
11402
|
+
id="downshift-3-menu"
|
|
11403
|
+
onMouseLeave={[Function]}
|
|
11404
|
+
role="listbox"
|
|
11405
|
+
/>
|
|
11406
|
+
</div>
|
|
11407
|
+
</div>
|
|
11408
|
+
<div
|
|
11409
|
+
style={
|
|
11410
|
+
Object {
|
|
11411
|
+
"alignItems": "flex-end",
|
|
11412
|
+
"display": "flex",
|
|
11413
|
+
"flexDirection": "column",
|
|
11414
|
+
}
|
|
11415
|
+
}
|
|
11416
|
+
>
|
|
11417
|
+
<div
|
|
11418
|
+
className="debugger"
|
|
11419
|
+
>
|
|
11420
|
+
<button
|
|
11421
|
+
className="base button x-main2"
|
|
11422
|
+
onClick={[Function]}
|
|
11423
|
+
type="button"
|
|
11424
|
+
>
|
|
11425
|
+
Open FormDebugger
|
|
11426
|
+
</button>
|
|
11427
|
+
</div>
|
|
11428
|
+
<button
|
|
11429
|
+
className="base button x-main1"
|
|
11430
|
+
onClick={[Function]}
|
|
11431
|
+
type="button"
|
|
11432
|
+
>
|
|
11433
|
+
Add formik values
|
|
11434
|
+
</button>
|
|
11435
|
+
</div>
|
|
11436
|
+
</form>
|
|
11437
|
+
`;
|
|
11438
|
+
|
|
11439
|
+
exports[`Storyshots f/fields/QueryCombobox Multiple Select With Default Formik State 1`] = `
|
|
11440
|
+
<form
|
|
11441
|
+
action="#"
|
|
11442
|
+
onReset={[Function]}
|
|
11443
|
+
onSubmit={[Function]}
|
|
11444
|
+
>
|
|
11445
|
+
<div
|
|
11446
|
+
className="base multiple-combobox y-background2"
|
|
11447
|
+
>
|
|
11448
|
+
<label
|
|
11449
|
+
className="base label x-main2"
|
|
11450
|
+
htmlFor="teams"
|
|
11451
|
+
id="downshift-4-label"
|
|
11452
|
+
>
|
|
11453
|
+
Search for a team
|
|
11454
|
+
</label>
|
|
11455
|
+
<div
|
|
11456
|
+
className="selected-items"
|
|
11457
|
+
>
|
|
11458
|
+
<div
|
|
11459
|
+
onClick={[Function]}
|
|
11460
|
+
onKeyDown={[Function]}
|
|
11461
|
+
tabIndex={-1}
|
|
11462
|
+
>
|
|
11463
|
+
Apple
|
|
11464
|
+
<button
|
|
11465
|
+
className="base button f-icons x-main2 modifierCompact modifierSimple"
|
|
11466
|
+
onClick={[Function]}
|
|
11467
|
+
type="button"
|
|
11468
|
+
>
|
|
11469
|
+
X
|
|
11470
|
+
</button>
|
|
11471
|
+
</div>
|
|
11472
|
+
<div
|
|
11473
|
+
onClick={[Function]}
|
|
11474
|
+
onKeyDown={[Function]}
|
|
11475
|
+
tabIndex={-1}
|
|
11476
|
+
>
|
|
11477
|
+
Pear
|
|
11478
|
+
<button
|
|
11479
|
+
className="base button f-icons x-main2 modifierCompact modifierSimple"
|
|
11480
|
+
onClick={[Function]}
|
|
11481
|
+
type="button"
|
|
11482
|
+
>
|
|
11483
|
+
X
|
|
11484
|
+
</button>
|
|
11485
|
+
</div>
|
|
11486
|
+
</div>
|
|
11487
|
+
<div
|
|
11488
|
+
aria-expanded={false}
|
|
11489
|
+
aria-haspopup="listbox"
|
|
11490
|
+
aria-owns="downshift-4-menu"
|
|
11491
|
+
className="input-wrapper"
|
|
11492
|
+
role="combobox"
|
|
11493
|
+
>
|
|
11494
|
+
<input
|
|
11495
|
+
aria-autocomplete="list"
|
|
11496
|
+
aria-controls="downshift-4-menu"
|
|
11497
|
+
aria-labelledby="downshift-4-label"
|
|
11498
|
+
autoComplete="off"
|
|
11499
|
+
className="input"
|
|
11500
|
+
id="downshift-4-input"
|
|
11501
|
+
onBlur={[Function]}
|
|
11502
|
+
onChange={[Function]}
|
|
11503
|
+
onClick={[Function]}
|
|
11504
|
+
onKeyDown={[Function]}
|
|
11505
|
+
value=""
|
|
11506
|
+
/>
|
|
11507
|
+
</div>
|
|
11508
|
+
<div
|
|
11509
|
+
className="base popover x-background1 bottom left"
|
|
11510
|
+
>
|
|
11511
|
+
<ul
|
|
11512
|
+
aria-labelledby="downshift-4-label"
|
|
11513
|
+
className="base menu"
|
|
11514
|
+
id="downshift-4-menu"
|
|
11515
|
+
onMouseLeave={[Function]}
|
|
11516
|
+
role="listbox"
|
|
11517
|
+
/>
|
|
11518
|
+
</div>
|
|
11519
|
+
</div>
|
|
11520
|
+
<div
|
|
11521
|
+
style={
|
|
11522
|
+
Object {
|
|
11523
|
+
"alignItems": "flex-end",
|
|
11524
|
+
"display": "flex",
|
|
11525
|
+
"flexDirection": "column",
|
|
11526
|
+
}
|
|
11527
|
+
}
|
|
11528
|
+
>
|
|
11529
|
+
<div
|
|
11530
|
+
className="debugger"
|
|
11531
|
+
>
|
|
11532
|
+
<button
|
|
11533
|
+
className="base button x-main2"
|
|
11534
|
+
onClick={[Function]}
|
|
11535
|
+
type="button"
|
|
11536
|
+
>
|
|
11537
|
+
Open FormDebugger
|
|
11538
|
+
</button>
|
|
11539
|
+
</div>
|
|
11540
|
+
<button
|
|
11541
|
+
className="base button x-main1"
|
|
11542
|
+
onClick={[Function]}
|
|
11543
|
+
type="button"
|
|
11544
|
+
>
|
|
11545
|
+
Add formik values
|
|
11546
|
+
</button>
|
|
11547
|
+
</div>
|
|
11548
|
+
</form>
|
|
11549
|
+
`;
|
|
11550
|
+
|
|
11356
11551
|
exports[`Storyshots f/fields/QueryCombobox Single Select 1`] = `
|
|
11357
11552
|
<form
|
|
11358
11553
|
action="#"
|
|
@@ -11360,7 +11555,7 @@ exports[`Storyshots f/fields/QueryCombobox Single Select 1`] = `
|
|
|
11360
11555
|
onSubmit={[Function]}
|
|
11361
11556
|
>
|
|
11362
11557
|
<div
|
|
11363
|
-
className="base combobox y-background2
|
|
11558
|
+
className="base combobox y-background2"
|
|
11364
11559
|
>
|
|
11365
11560
|
<label
|
|
11366
11561
|
className="base label x-main2"
|
|
@@ -11426,7 +11621,7 @@ exports[`Storyshots f/fields/QueryCombobox Single Select 1`] = `
|
|
|
11426
11621
|
onClick={[Function]}
|
|
11427
11622
|
type="button"
|
|
11428
11623
|
>
|
|
11429
|
-
Replace
|
|
11624
|
+
Replace formik value
|
|
11430
11625
|
</button>
|
|
11431
11626
|
</div>
|
|
11432
11627
|
</form>
|
|
@@ -11439,7 +11634,7 @@ exports[`Storyshots f/fields/QueryCombobox Single Select With Default Formik Sta
|
|
|
11439
11634
|
onSubmit={[Function]}
|
|
11440
11635
|
>
|
|
11441
11636
|
<div
|
|
11442
|
-
className="base combobox y-background2
|
|
11637
|
+
className="base combobox y-background2"
|
|
11443
11638
|
>
|
|
11444
11639
|
<label
|
|
11445
11640
|
className="base label x-main2"
|
|
@@ -11505,7 +11700,7 @@ exports[`Storyshots f/fields/QueryCombobox Single Select With Default Formik Sta
|
|
|
11505
11700
|
onClick={[Function]}
|
|
11506
11701
|
type="button"
|
|
11507
11702
|
>
|
|
11508
|
-
Replace
|
|
11703
|
+
Replace formik value
|
|
11509
11704
|
</button>
|
|
11510
11705
|
</div>
|
|
11511
11706
|
</form>
|
package/src/local.scss
CHANGED
|
@@ -5,12 +5,12 @@
|
|
|
5
5
|
--theme-border-style: 1px solid;
|
|
6
6
|
--theme-border-color: var(--paragraph);
|
|
7
7
|
--theme-border: var(--theme-border-style) var(--theme-border-color);
|
|
8
|
+
--default-gap: 1em;
|
|
8
9
|
}
|
|
9
10
|
|
|
10
11
|
html {
|
|
11
12
|
font-family: var(--font-default);
|
|
12
13
|
font-size: 18px;
|
|
13
|
-
font-feature-settings:'liga' on;
|
|
14
|
+
font-feature-settings: 'liga' on;
|
|
14
15
|
scroll-behavior: smooth;
|
|
15
|
-
}
|
|
16
|
-
|
|
16
|
+
}
|
|
@@ -80,7 +80,7 @@ const FETCH_TEAMS_QUERY = graphql`
|
|
|
80
80
|
`
|
|
81
81
|
|
|
82
82
|
// eslint-disable-next-line react/prop-types
|
|
83
|
-
const ResolvedTemplate = ({ multiple, defaultFormikState
|
|
83
|
+
const ResolvedTemplate = ({ multiple, defaultFormikState }) => {
|
|
84
84
|
mockRelayOperation(allTeamsMockData)
|
|
85
85
|
mockRelayOperation(allTeamsMockData)
|
|
86
86
|
mockRelayOperation(allTeamsMockData)
|
|
@@ -88,15 +88,31 @@ const ResolvedTemplate = ({ multiple, defaultFormikState = {} }) => {
|
|
|
88
88
|
const Content = () => {
|
|
89
89
|
const name = multiple ? 'teams' : 'team'
|
|
90
90
|
|
|
91
|
-
const [, , helpers] = useField(name)
|
|
91
|
+
const [, meta, helpers] = useField(name)
|
|
92
|
+
|
|
93
|
+
const { value } = meta
|
|
92
94
|
|
|
93
95
|
const { setValue } = helpers
|
|
94
96
|
|
|
95
97
|
const updateFormikState = () => {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
98
|
+
if (multiple) {
|
|
99
|
+
setValue([
|
|
100
|
+
...(value || []),
|
|
101
|
+
{
|
|
102
|
+
value:'VGVhbU5vZGU6MDAxZTIyOGEtYzA5My00MGI0LWE1MTUtYTNkMTM1NTE1MDNk',
|
|
103
|
+
label:'Matomoko',
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
value:'VGVhbU5vZGU6MDAxZTIyOGEtYzA5My00MGI0LWE1MTUtYTNkMTM1NTE1MDNp',
|
|
107
|
+
label:'Chungwa',
|
|
108
|
+
},
|
|
109
|
+
])
|
|
110
|
+
} else {
|
|
111
|
+
setValue({
|
|
112
|
+
value:'VGVhbU5vZGU6MDAxZTIyOGEtYzA5My00MGI0LWE1MTUtYTNkMTM1NTE1MDNk',
|
|
113
|
+
label:'Kafagoho',
|
|
114
|
+
})
|
|
115
|
+
}
|
|
100
116
|
}
|
|
101
117
|
|
|
102
118
|
return (
|
|
@@ -124,16 +140,24 @@ const ResolvedTemplate = ({ multiple, defaultFormikState = {} }) => {
|
|
|
124
140
|
<Button
|
|
125
141
|
onClick={updateFormikState}
|
|
126
142
|
>
|
|
127
|
-
{multiple ? 'Add
|
|
143
|
+
{multiple ? 'Add formik values' : 'Replace formik value'}
|
|
128
144
|
</Button>
|
|
129
145
|
</div>
|
|
130
146
|
</>
|
|
131
147
|
)
|
|
132
148
|
}
|
|
133
149
|
|
|
150
|
+
let initialValues = defaultFormikState
|
|
151
|
+
|
|
152
|
+
if (!defaultFormikState && multiple) {
|
|
153
|
+
initialValues = []
|
|
154
|
+
} else if (!defaultFormikState && !multiple) {
|
|
155
|
+
initialValues = {}
|
|
156
|
+
}
|
|
157
|
+
|
|
134
158
|
return (
|
|
135
159
|
<Formik
|
|
136
|
-
initialValues={
|
|
160
|
+
initialValues={initialValues}
|
|
137
161
|
>
|
|
138
162
|
<Form>
|
|
139
163
|
<Content />
|
|
@@ -158,6 +182,29 @@ SingleSelectWithDefaultFormikState.args = {
|
|
|
158
182
|
},
|
|
159
183
|
}
|
|
160
184
|
|
|
185
|
+
export const MultipleSelect = ResolvedTemplate.bind({})
|
|
186
|
+
MultipleSelect.args = {
|
|
187
|
+
multiple :true,
|
|
188
|
+
defaultFormikState:{ teams: [] },
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export const MultipleSelectWithDefaultFormikState = ResolvedTemplate.bind({})
|
|
192
|
+
MultipleSelectWithDefaultFormikState.args = {
|
|
193
|
+
multiple :true,
|
|
194
|
+
defaultFormikState:{
|
|
195
|
+
teams:[
|
|
196
|
+
{
|
|
197
|
+
value:'VGVhbU5vZGU6MDAxZTIyOGEtYzA5My00MGI0LWE1MTUtYTNkMTM1NTE1MDNl',
|
|
198
|
+
label:'Apple',
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
value:'VGVhbU5vZGU6MDA0N2U4MzktODY0Zi00N2U5LTg3ZjgtZGUwMmM2Yzg1YWJm',
|
|
202
|
+
label:'Pear',
|
|
203
|
+
},
|
|
204
|
+
],
|
|
205
|
+
},
|
|
206
|
+
}
|
|
207
|
+
|
|
161
208
|
// eslint-disable-next-line react/prop-types
|
|
162
209
|
// const RejectedTemplate = ({ multiple }) => {
|
|
163
210
|
// const Content = () => {
|
|
@@ -11,7 +11,7 @@ import PropTypes from 'prop-types'
|
|
|
11
11
|
|
|
12
12
|
// Local Definitions
|
|
13
13
|
|
|
14
|
-
import { Combobox } from './common'
|
|
14
|
+
import { Combobox, MultipleCombobox } from './common'
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
17
|
* This is the component description.
|
|
@@ -21,7 +21,7 @@ const QueryCombobox = ({
|
|
|
21
21
|
style,
|
|
22
22
|
className,
|
|
23
23
|
query,
|
|
24
|
-
|
|
24
|
+
multiple,
|
|
25
25
|
name,
|
|
26
26
|
label,
|
|
27
27
|
color,
|
|
@@ -32,6 +32,8 @@ const QueryCombobox = ({
|
|
|
32
32
|
searchVariable,
|
|
33
33
|
extraVariables,
|
|
34
34
|
optionsKeyMap,
|
|
35
|
+
minLength,
|
|
36
|
+
transformSearch,
|
|
35
37
|
// ...otherProps
|
|
36
38
|
}) => {
|
|
37
39
|
useLayoutEffect(() => {
|
|
@@ -103,9 +105,11 @@ const QueryCombobox = ({
|
|
|
103
105
|
color,
|
|
104
106
|
isFetching,
|
|
105
107
|
className,
|
|
108
|
+
minLength,
|
|
109
|
+
transformSearch,
|
|
106
110
|
}
|
|
107
111
|
|
|
108
|
-
const Input = Combobox
|
|
112
|
+
const Input = multiple ? MultipleCombobox : Combobox
|
|
109
113
|
|
|
110
114
|
return <Input {...comboboxProps} />
|
|
111
115
|
}
|
|
@@ -192,6 +196,16 @@ QueryCombobox.propTypes = {
|
|
|
192
196
|
* The variable to be used to search the data
|
|
193
197
|
*/
|
|
194
198
|
searchVariable:PropTypes.string,
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* The minimum length of the search input to start fetching the options
|
|
202
|
+
*/
|
|
203
|
+
minLength:PropTypes.number,
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* The function to transform the search input
|
|
207
|
+
*/
|
|
208
|
+
transformSearch:PropTypes.func,
|
|
195
209
|
}
|
|
196
210
|
|
|
197
211
|
QueryCombobox.defaultProps = {
|
|
@@ -199,9 +213,11 @@ QueryCombobox.defaultProps = {
|
|
|
199
213
|
value:'id',
|
|
200
214
|
label:'name',
|
|
201
215
|
},
|
|
202
|
-
multiple
|
|
203
|
-
color
|
|
204
|
-
searchVariable:'search',
|
|
216
|
+
multiple :false,
|
|
217
|
+
color :'background2',
|
|
218
|
+
searchVariable :'search',
|
|
219
|
+
transformSearch:(search) => search,
|
|
220
|
+
minLength :2,
|
|
205
221
|
}
|
|
206
222
|
|
|
207
223
|
export default QueryCombobox
|
|
@@ -37,8 +37,9 @@ const Combobox = ({
|
|
|
37
37
|
description,
|
|
38
38
|
value,
|
|
39
39
|
color,
|
|
40
|
-
|
|
40
|
+
minLength,
|
|
41
41
|
isFetching,
|
|
42
|
+
transformSearch,
|
|
42
43
|
// ...otherProps
|
|
43
44
|
}) => {
|
|
44
45
|
const {
|
|
@@ -56,7 +57,10 @@ const Combobox = ({
|
|
|
56
57
|
initialSelectedItem:value,
|
|
57
58
|
itemToString :(item) => (item ? item.label : ''),
|
|
58
59
|
onInputValueChange :({ inputValue }) => {
|
|
59
|
-
|
|
60
|
+
const transformedInput = transformSearch(inputValue)
|
|
61
|
+
if (transformedInput.length > minLength) {
|
|
62
|
+
getOptions(transformedInput)
|
|
63
|
+
}
|
|
60
64
|
},
|
|
61
65
|
})
|
|
62
66
|
|
|
@@ -87,7 +91,6 @@ const Combobox = ({
|
|
|
87
91
|
componentClassName,
|
|
88
92
|
userClassName,
|
|
89
93
|
`y-${color}`,
|
|
90
|
-
`x-${loadingCircleColor}`,
|
|
91
94
|
]
|
|
92
95
|
.filter((e) => e)
|
|
93
96
|
.join(' ')}
|
|
@@ -101,7 +104,7 @@ const Combobox = ({
|
|
|
101
104
|
<div {...getComboboxProps()} className="input-wrapper">
|
|
102
105
|
<input {...getInputProps()} className="input" />
|
|
103
106
|
{isFetching && (
|
|
104
|
-
<LoadingCircle />
|
|
107
|
+
<LoadingCircle className="x-main2" />
|
|
105
108
|
)}
|
|
106
109
|
</div>
|
|
107
110
|
|
|
@@ -202,13 +205,18 @@ Combobox.propTypes = {
|
|
|
202
205
|
isFetching:PropTypes.bool.isRequired,
|
|
203
206
|
|
|
204
207
|
/**
|
|
205
|
-
* The
|
|
208
|
+
* The minimum length of the search input to start fetching the options
|
|
206
209
|
*/
|
|
207
|
-
|
|
210
|
+
minLength:PropTypes.number,
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* The function to transform the search input
|
|
214
|
+
*/
|
|
215
|
+
transformSearch:PropTypes.func,
|
|
208
216
|
}
|
|
209
217
|
|
|
210
218
|
Combobox.defaultProps = {
|
|
211
|
-
|
|
219
|
+
// someProp: false
|
|
212
220
|
}
|
|
213
221
|
|
|
214
222
|
export default Combobox
|
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
/* @pareto-engineering/generator-front 1.0.12 */
|
|
2
|
+
import * as React from 'react'
|
|
3
|
+
|
|
4
|
+
import { useState, useEffect, useRef } from 'react'
|
|
5
|
+
|
|
6
|
+
import PropTypes from 'prop-types'
|
|
7
|
+
|
|
8
|
+
import styleNames from '@pareto-engineering/bem'
|
|
9
|
+
|
|
10
|
+
import { useCombobox, useMultipleSelection } from 'downshift'
|
|
11
|
+
|
|
12
|
+
import { Button } from 'ui/b'
|
|
13
|
+
|
|
14
|
+
import { Popover, LoadingCircle } from 'ui/a'
|
|
15
|
+
|
|
16
|
+
import { FormDescription, FormLabel } from 'ui/f'
|
|
17
|
+
|
|
18
|
+
// Local Definitions
|
|
19
|
+
|
|
20
|
+
import { Menu } from '../Menu'
|
|
21
|
+
|
|
22
|
+
const baseClassName = styleNames.base
|
|
23
|
+
|
|
24
|
+
const componentClassName = 'multiple-combobox'
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @param {Array[Object]} first - first array to check if it has an item not in the second array.
|
|
28
|
+
* @param {Array[Object]} second - second array to check against the first array.
|
|
29
|
+
*
|
|
30
|
+
* @returns {Boolean} - true if the first array has an item not in the second array.
|
|
31
|
+
*/
|
|
32
|
+
const testIfArraysAreUnique = (first, second) => first
|
|
33
|
+
.filter((objInFirstArray) => !second
|
|
34
|
+
.some((objInSecondArray) => objInFirstArray.value === objInSecondArray.value))
|
|
35
|
+
.length > 0
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* This is the component description.
|
|
39
|
+
*/
|
|
40
|
+
const MultipleCombobox = ({
|
|
41
|
+
id,
|
|
42
|
+
className:userClassName,
|
|
43
|
+
style,
|
|
44
|
+
label,
|
|
45
|
+
name,
|
|
46
|
+
options:items,
|
|
47
|
+
getOptions,
|
|
48
|
+
setValue,
|
|
49
|
+
error,
|
|
50
|
+
description,
|
|
51
|
+
value,
|
|
52
|
+
color,
|
|
53
|
+
isFetching,
|
|
54
|
+
minLength,
|
|
55
|
+
transformSearch,
|
|
56
|
+
// ...otherProps
|
|
57
|
+
}) => {
|
|
58
|
+
const [searchInputValue, setSearchInputValue] = useState('')
|
|
59
|
+
const {
|
|
60
|
+
getSelectedItemProps,
|
|
61
|
+
getDropdownProps,
|
|
62
|
+
addSelectedItem,
|
|
63
|
+
removeSelectedItem,
|
|
64
|
+
setSelectedItems,
|
|
65
|
+
selectedItems,
|
|
66
|
+
} = useMultipleSelection({
|
|
67
|
+
initialSelectedItems:value || [],
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* @returns {Boolean} - Unique items from the options array so that the combobox
|
|
72
|
+
* shows only the options that are not yet selected.
|
|
73
|
+
*/
|
|
74
|
+
const getFilteredItems = () => items
|
|
75
|
+
.filter((item) => selectedItems
|
|
76
|
+
.findIndex((e) => e.label === item.label) < 0)
|
|
77
|
+
|
|
78
|
+
const {
|
|
79
|
+
isOpen,
|
|
80
|
+
getLabelProps,
|
|
81
|
+
getMenuProps,
|
|
82
|
+
getInputProps,
|
|
83
|
+
getComboboxProps,
|
|
84
|
+
highlightedIndex,
|
|
85
|
+
getItemProps,
|
|
86
|
+
} = useCombobox({
|
|
87
|
+
searchInputValue,
|
|
88
|
+
defaultHighlightedIndex:0, // after selection, highlight the first item.
|
|
89
|
+
selectedItem :null,
|
|
90
|
+
items :getFilteredItems(),
|
|
91
|
+
circularNavigation :true,
|
|
92
|
+
stateReducer :(state, actionAndChanges) => {
|
|
93
|
+
const { changes, type } = actionAndChanges
|
|
94
|
+
switch (type) {
|
|
95
|
+
case useCombobox.stateChangeTypes.InputKeyDownEnter:
|
|
96
|
+
case useCombobox.stateChangeTypes.ItemClick:
|
|
97
|
+
return {
|
|
98
|
+
...changes,
|
|
99
|
+
isOpen:true, // keep the menu open after selection.
|
|
100
|
+
}
|
|
101
|
+
default:
|
|
102
|
+
break
|
|
103
|
+
}
|
|
104
|
+
return changes
|
|
105
|
+
},
|
|
106
|
+
onStateChange:({ inputValue:newSearchInputValue, type, selectedItem }) => {
|
|
107
|
+
switch (type) {
|
|
108
|
+
case useCombobox.stateChangeTypes.InputChange: {
|
|
109
|
+
const transformedInput = transformSearch(newSearchInputValue)
|
|
110
|
+
if (transformedInput.length > minLength) {
|
|
111
|
+
getOptions(transformedInput)
|
|
112
|
+
}
|
|
113
|
+
setSearchInputValue(newSearchInputValue)
|
|
114
|
+
break
|
|
115
|
+
}
|
|
116
|
+
case useCombobox.stateChangeTypes.InputKeyDownEnter:
|
|
117
|
+
case useCombobox.stateChangeTypes.ItemClick:
|
|
118
|
+
case useCombobox.stateChangeTypes.InputBlur:
|
|
119
|
+
if (selectedItem) {
|
|
120
|
+
setSearchInputValue('')
|
|
121
|
+
addSelectedItem(selectedItem)
|
|
122
|
+
}
|
|
123
|
+
break
|
|
124
|
+
default:
|
|
125
|
+
break
|
|
126
|
+
}
|
|
127
|
+
},
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
useEffect(() => {
|
|
131
|
+
if (selectedItems?.length > 0) {
|
|
132
|
+
setValue(selectedItems)
|
|
133
|
+
}
|
|
134
|
+
}, [selectedItems])
|
|
135
|
+
|
|
136
|
+
useEffect(() => {
|
|
137
|
+
if (value?.length > 0 && (
|
|
138
|
+
testIfArraysAreUnique(value, selectedItems)
|
|
139
|
+
|| testIfArraysAreUnique(selectedItems, value)
|
|
140
|
+
)) {
|
|
141
|
+
setSelectedItems(value)
|
|
142
|
+
}
|
|
143
|
+
}, [value])
|
|
144
|
+
|
|
145
|
+
const parentRef = useRef(null)
|
|
146
|
+
|
|
147
|
+
return (
|
|
148
|
+
<div
|
|
149
|
+
id={id}
|
|
150
|
+
className={[
|
|
151
|
+
|
|
152
|
+
baseClassName,
|
|
153
|
+
|
|
154
|
+
componentClassName,
|
|
155
|
+
userClassName,
|
|
156
|
+
`y-${color}`,
|
|
157
|
+
]
|
|
158
|
+
.filter((e) => e)
|
|
159
|
+
.join(' ')}
|
|
160
|
+
style={style}
|
|
161
|
+
>
|
|
162
|
+
|
|
163
|
+
<FormLabel {...getLabelProps()} name={name}>
|
|
164
|
+
{label}
|
|
165
|
+
</FormLabel>
|
|
166
|
+
|
|
167
|
+
<div className="selected-items">
|
|
168
|
+
{selectedItems && selectedItems.map((selectedItem, index) => (
|
|
169
|
+
<div
|
|
170
|
+
key={selectedItem.label}
|
|
171
|
+
{...getSelectedItemProps({ selectedItem, index })}
|
|
172
|
+
>
|
|
173
|
+
{selectedItem.label}
|
|
174
|
+
<Button
|
|
175
|
+
className="f-icons"
|
|
176
|
+
onClick={(e) => {
|
|
177
|
+
e.stopPropagation()
|
|
178
|
+
removeSelectedItem(selectedItem)
|
|
179
|
+
}}
|
|
180
|
+
isCompact
|
|
181
|
+
isSimple
|
|
182
|
+
color="main2"
|
|
183
|
+
>
|
|
184
|
+
X
|
|
185
|
+
</Button>
|
|
186
|
+
</div>
|
|
187
|
+
))}
|
|
188
|
+
</div>
|
|
189
|
+
|
|
190
|
+
<div {...getComboboxProps()} className="input-wrapper">
|
|
191
|
+
<input
|
|
192
|
+
{...getInputProps(
|
|
193
|
+
getDropdownProps({ preventKeyAction: isOpen }),
|
|
194
|
+
)}
|
|
195
|
+
className="input"
|
|
196
|
+
/>
|
|
197
|
+
{isFetching && (
|
|
198
|
+
<LoadingCircle className="x-main2" />
|
|
199
|
+
)}
|
|
200
|
+
</div>
|
|
201
|
+
|
|
202
|
+
<Popover
|
|
203
|
+
isOpen={isOpen}
|
|
204
|
+
parentRef={parentRef}
|
|
205
|
+
>
|
|
206
|
+
<Menu
|
|
207
|
+
isOpen={isOpen}
|
|
208
|
+
getItemProps={getItemProps}
|
|
209
|
+
highlightedIndex={highlightedIndex}
|
|
210
|
+
items={getFilteredItems()}
|
|
211
|
+
{...getMenuProps()}
|
|
212
|
+
/>
|
|
213
|
+
</Popover>
|
|
214
|
+
|
|
215
|
+
{(description || error) && (
|
|
216
|
+
<FormDescription isError={!!error}>
|
|
217
|
+
{ error || description }
|
|
218
|
+
</FormDescription>
|
|
219
|
+
)}
|
|
220
|
+
|
|
221
|
+
</div>
|
|
222
|
+
)
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
MultipleCombobox.propTypes = {
|
|
226
|
+
/**
|
|
227
|
+
* The HTML id for this element
|
|
228
|
+
*/
|
|
229
|
+
id:PropTypes.string,
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* The HTML class names for this element
|
|
233
|
+
*/
|
|
234
|
+
className:PropTypes.string,
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* The React-written, css properties for this element.
|
|
238
|
+
*/
|
|
239
|
+
style:PropTypes.objectOf(PropTypes.string),
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* The label of the custom select input
|
|
243
|
+
*/
|
|
244
|
+
label:PropTypes.string,
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* The custom select input options from the backend
|
|
248
|
+
*/
|
|
249
|
+
options:PropTypes.arrayOf(
|
|
250
|
+
PropTypes.shape({
|
|
251
|
+
value:PropTypes.string,
|
|
252
|
+
label:PropTypes.string,
|
|
253
|
+
}),
|
|
254
|
+
),
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* The name of the custom select input
|
|
258
|
+
*/
|
|
259
|
+
name:PropTypes.string,
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* The function to fetch the options from the backend
|
|
263
|
+
*/
|
|
264
|
+
getOptions:PropTypes.func,
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* The function to set the value of the custom select input
|
|
268
|
+
*/
|
|
269
|
+
setValue:PropTypes.func.isRequired,
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* The custom select input description
|
|
273
|
+
*/
|
|
274
|
+
description:PropTypes.string,
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* The error object
|
|
278
|
+
*/
|
|
279
|
+
error:PropTypes.objectOf(PropTypes.string),
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* The value of the custom select input
|
|
283
|
+
*/
|
|
284
|
+
value:PropTypes.arrayOf(
|
|
285
|
+
PropTypes.shape({
|
|
286
|
+
value:PropTypes.string,
|
|
287
|
+
label:PropTypes.string,
|
|
288
|
+
}),
|
|
289
|
+
),
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* The base color of the custom select input
|
|
293
|
+
*/
|
|
294
|
+
color:PropTypes.string,
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Whether the query getting the combobox options is inFlight
|
|
298
|
+
*/
|
|
299
|
+
isFetching:PropTypes.bool.isRequired,
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* The minimum length of the search input to start fetching the options
|
|
303
|
+
*/
|
|
304
|
+
minLength:PropTypes.number,
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* The function to transform the search input
|
|
308
|
+
*/
|
|
309
|
+
transformSearch:PropTypes.func,
|
|
310
|
+
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
MultipleCombobox.defaultProps = {
|
|
314
|
+
// someProp: false
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
export default MultipleCombobox
|
|
@@ -73,12 +73,6 @@ $default-loading-circle-displacement: 1em;
|
|
|
73
73
|
.#{bem.$base}.multiple-combobox {
|
|
74
74
|
>.selected-items {
|
|
75
75
|
display: flex;
|
|
76
|
-
|
|
77
|
-
/* stylelint-disable selector-max-universal -- Allow */
|
|
78
|
-
>*:not(:first-child) {
|
|
79
|
-
margin-left: $default-margin;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/* stylelint-enable selector-max-universal */
|
|
76
|
+
gap: var(--default-gap);
|
|
83
77
|
}
|
|
84
78
|
}
|