@pareto-engineering/design-system 2.0.0-alpha.45 → 2.0.0-alpha.46
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/FormInput/FormInput.js +8 -1
- package/dist/cjs/f/fields/QueryCombobox/QueryCombobox.js +8 -1
- package/dist/cjs/f/fields/QueryCombobox/common/Combobox/Combobox.js +18 -5
- package/dist/cjs/f/fields/QueryCombobox/common/MultipleCombobox/MultipleCombobox.js +72 -20
- package/dist/cjs/f/fields/QueryCombobox/styles.scss +13 -0
- package/dist/es/f/FormInput/FormInput.js +9 -2
- package/dist/es/f/fields/QueryCombobox/QueryCombobox.js +8 -1
- package/dist/es/f/fields/QueryCombobox/common/Combobox/Combobox.js +19 -6
- package/dist/es/f/fields/QueryCombobox/common/MultipleCombobox/MultipleCombobox.js +72 -21
- package/dist/es/f/fields/QueryCombobox/styles.scss +13 -0
- package/package.json +1 -1
- package/src/__snapshots__/Storyshots.test.js.snap +171 -16
- package/src/stories/f/FormInput.stories.jsx +115 -0
- package/src/stories/f/__generated__/FormInputAllTeamsQuery.graphql.js +139 -0
- package/src/ui/f/FormInput/FormInput.jsx +11 -0
- package/src/ui/f/fields/QueryCombobox/QueryCombobox.jsx +7 -0
- package/src/ui/f/fields/QueryCombobox/common/Combobox/Combobox.jsx +18 -2
- package/src/ui/f/fields/QueryCombobox/styles.scss +13 -0
|
@@ -67,6 +67,13 @@ var FormInput = _ref => {
|
|
|
67
67
|
}, otherProps));
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
+
if (type === 'query-combobox') {
|
|
71
|
+
return /*#__PURE__*/React.createElement(_fields.QueryCombobox, _extends({
|
|
72
|
+
className: newClassName,
|
|
73
|
+
disabled: disabled
|
|
74
|
+
}, otherProps));
|
|
75
|
+
}
|
|
76
|
+
|
|
70
77
|
if (extraTypes !== null && extraTypes !== void 0 && extraTypes[type]) {
|
|
71
78
|
var Component = extraTypes[type];
|
|
72
79
|
return /*#__PURE__*/React.createElement(Component, _extends({
|
|
@@ -91,7 +98,7 @@ FormInput.propTypes = {
|
|
|
91
98
|
/**
|
|
92
99
|
* The HTML class names for this element
|
|
93
100
|
*/
|
|
94
|
-
type: _propTypes.default.oneOf(['text', 'email', 'password', 'number', 'date', 'datetime', 'month', 'tel', 'hidden', 'select', 'choices', 'textarea', // to be removed
|
|
101
|
+
type: _propTypes.default.oneOf(['text', 'email', 'password', 'number', 'date', 'datetime', 'month', 'tel', 'hidden', 'select', 'choices', 'textarea', 'query-combobox', // to be removed
|
|
95
102
|
'extendedTypeInput']),
|
|
96
103
|
|
|
97
104
|
/**
|
|
@@ -32,6 +32,9 @@ function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj &&
|
|
|
32
32
|
*/
|
|
33
33
|
var QueryCombobox = _ref => {
|
|
34
34
|
var {
|
|
35
|
+
id,
|
|
36
|
+
style,
|
|
37
|
+
className,
|
|
35
38
|
query,
|
|
36
39
|
// multiple,
|
|
37
40
|
name,
|
|
@@ -98,6 +101,8 @@ var QueryCombobox = _ref => {
|
|
|
98
101
|
};
|
|
99
102
|
|
|
100
103
|
var comboboxProps = {
|
|
104
|
+
id,
|
|
105
|
+
style,
|
|
101
106
|
options,
|
|
102
107
|
getOptions,
|
|
103
108
|
debounceMs,
|
|
@@ -108,7 +113,9 @@ var QueryCombobox = _ref => {
|
|
|
108
113
|
setValue,
|
|
109
114
|
error,
|
|
110
115
|
value,
|
|
111
|
-
color
|
|
116
|
+
color,
|
|
117
|
+
isFetching,
|
|
118
|
+
className
|
|
112
119
|
};
|
|
113
120
|
var Input = _common.Combobox;
|
|
114
121
|
return /*#__PURE__*/React.createElement(Input, comboboxProps);
|
|
@@ -46,7 +46,9 @@ var Combobox = _ref => {
|
|
|
46
46
|
error,
|
|
47
47
|
description,
|
|
48
48
|
value,
|
|
49
|
-
color
|
|
49
|
+
color,
|
|
50
|
+
loadingCircleColor,
|
|
51
|
+
isFetching // ...otherProps
|
|
50
52
|
|
|
51
53
|
} = _ref;
|
|
52
54
|
var {
|
|
@@ -86,7 +88,7 @@ var Combobox = _ref => {
|
|
|
86
88
|
var parentRef = (0, React.useRef)(null);
|
|
87
89
|
return /*#__PURE__*/React.createElement("div", {
|
|
88
90
|
id: id,
|
|
89
|
-
className: [baseClassName, componentClassName, userClassName, "y-".concat(color)].filter(e => e).join(' '),
|
|
91
|
+
className: [baseClassName, componentClassName, userClassName, "y-".concat(color), "x-".concat(loadingCircleColor)].filter(e => e).join(' '),
|
|
90
92
|
style: style,
|
|
91
93
|
ref: parentRef
|
|
92
94
|
}, /*#__PURE__*/React.createElement(_.FormLabel, _extends({}, getLabelProps(), {
|
|
@@ -95,7 +97,7 @@ var Combobox = _ref => {
|
|
|
95
97
|
className: "input-wrapper"
|
|
96
98
|
}), /*#__PURE__*/React.createElement("input", _extends({}, getInputProps(), {
|
|
97
99
|
className: "input"
|
|
98
|
-
}))), /*#__PURE__*/React.createElement(_a.Popover, {
|
|
100
|
+
})), isFetching && /*#__PURE__*/React.createElement(_a.LoadingCircle, null)), /*#__PURE__*/React.createElement(_a.Popover, {
|
|
99
101
|
isOpen: isOpen,
|
|
100
102
|
parentRef: parentRef
|
|
101
103
|
}, /*#__PURE__*/React.createElement(_Menu.Menu, _extends({
|
|
@@ -173,9 +175,20 @@ Combobox.propTypes = {
|
|
|
173
175
|
/**
|
|
174
176
|
* The base color of the combobox custom select input
|
|
175
177
|
*/
|
|
176
|
-
color: _propTypes.default.string
|
|
178
|
+
color: _propTypes.default.string,
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Whether the query getting the combobox options is inFlight
|
|
182
|
+
*/
|
|
183
|
+
isFetching: _propTypes.default.bool.isRequired,
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* The loading circle color
|
|
187
|
+
*/
|
|
188
|
+
loadingCircleColor: _propTypes.default.string
|
|
177
189
|
};
|
|
178
|
-
Combobox.defaultProps = {
|
|
190
|
+
Combobox.defaultProps = {
|
|
191
|
+
loadingCircleColor: 'main2'
|
|
179
192
|
};
|
|
180
193
|
var _default = Combobox;
|
|
181
194
|
exports.default = _default;
|
|
@@ -15,6 +15,8 @@ var _downshift = require("downshift");
|
|
|
15
15
|
|
|
16
16
|
var _b = require("../../../../../b");
|
|
17
17
|
|
|
18
|
+
var _a = require("../../../../../a");
|
|
19
|
+
|
|
18
20
|
var _ = require("../../../..");
|
|
19
21
|
|
|
20
22
|
var _Menu = require("../Menu");
|
|
@@ -35,10 +37,19 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
|
|
|
35
37
|
|
|
36
38
|
var baseClassName = _bem.default.base;
|
|
37
39
|
var componentClassName = 'multiple-combobox';
|
|
40
|
+
/**
|
|
41
|
+
* @param {Array[Object]} first - first array to check if it has an item not in the second array.
|
|
42
|
+
* @param {Array[Object]} second - second array to check against the first array.
|
|
43
|
+
*
|
|
44
|
+
* @returns {Boolean} - true if the first array has an item not in the second array.
|
|
45
|
+
*/
|
|
46
|
+
|
|
47
|
+
var testIfArraysAreUnique = (first, second) => first.filter(objInFirstArray => !second.some(objInSecondArray => objInFirstArray.value === objInSecondArray.value)).length > 0;
|
|
38
48
|
/**
|
|
39
49
|
* This is the component description.
|
|
40
50
|
*/
|
|
41
51
|
|
|
52
|
+
|
|
42
53
|
var MultipleCombobox = _ref => {
|
|
43
54
|
var {
|
|
44
55
|
id,
|
|
@@ -47,22 +58,29 @@ var MultipleCombobox = _ref => {
|
|
|
47
58
|
label,
|
|
48
59
|
name,
|
|
49
60
|
options: items,
|
|
50
|
-
|
|
61
|
+
getOptions,
|
|
51
62
|
setValue,
|
|
52
63
|
error,
|
|
53
|
-
description
|
|
64
|
+
description,
|
|
65
|
+
value,
|
|
66
|
+
color,
|
|
67
|
+
isFetching,
|
|
68
|
+
loadingCircleColor // ...otherProps
|
|
54
69
|
|
|
55
70
|
} = _ref;
|
|
56
|
-
var [
|
|
71
|
+
var [searchInputValue, setSearchInputValue] = (0, React.useState)('');
|
|
57
72
|
var {
|
|
58
73
|
getSelectedItemProps,
|
|
59
74
|
getDropdownProps,
|
|
60
75
|
addSelectedItem,
|
|
61
76
|
removeSelectedItem,
|
|
77
|
+
setSelectedItems,
|
|
62
78
|
selectedItems
|
|
63
|
-
} = (0, _downshift.useMultipleSelection)(
|
|
79
|
+
} = (0, _downshift.useMultipleSelection)({
|
|
80
|
+
initialSelectedItems: value || []
|
|
81
|
+
});
|
|
64
82
|
|
|
65
|
-
var getFilteredItems = () => items.filter(item => selectedItems.findIndex(e => e.label === item.label) < 0 && item.label.toLowerCase().startsWith(
|
|
83
|
+
var getFilteredItems = () => items.filter(item => selectedItems.findIndex(e => e.label === item.label) < 0 && item.label.toLowerCase().startsWith(searchInputValue.toLowerCase()));
|
|
66
84
|
|
|
67
85
|
var {
|
|
68
86
|
isOpen,
|
|
@@ -73,7 +91,7 @@ var MultipleCombobox = _ref => {
|
|
|
73
91
|
highlightedIndex,
|
|
74
92
|
getItemProps
|
|
75
93
|
} = (0, _downshift.useCombobox)({
|
|
76
|
-
|
|
94
|
+
searchInputValue,
|
|
77
95
|
defaultHighlightedIndex: 0,
|
|
78
96
|
// after selection, highlight the first item.
|
|
79
97
|
selectedItem: null,
|
|
@@ -101,22 +119,22 @@ var MultipleCombobox = _ref => {
|
|
|
101
119
|
},
|
|
102
120
|
onStateChange: _ref2 => {
|
|
103
121
|
var {
|
|
104
|
-
inputValue:
|
|
122
|
+
inputValue: newSearchInputValue,
|
|
105
123
|
type,
|
|
106
124
|
selectedItem
|
|
107
125
|
} = _ref2;
|
|
108
126
|
|
|
109
127
|
switch (type) {
|
|
110
128
|
case _downshift.useCombobox.stateChangeTypes.InputChange:
|
|
111
|
-
|
|
112
|
-
|
|
129
|
+
getOptions(newSearchInputValue);
|
|
130
|
+
setSearchInputValue(newSearchInputValue);
|
|
113
131
|
break;
|
|
114
132
|
|
|
115
133
|
case _downshift.useCombobox.stateChangeTypes.InputKeyDownEnter:
|
|
116
134
|
case _downshift.useCombobox.stateChangeTypes.ItemClick:
|
|
117
135
|
case _downshift.useCombobox.stateChangeTypes.InputBlur:
|
|
118
136
|
if (selectedItem) {
|
|
119
|
-
|
|
137
|
+
setSearchInputValue('');
|
|
120
138
|
addSelectedItem(selectedItem);
|
|
121
139
|
}
|
|
122
140
|
|
|
@@ -128,16 +146,21 @@ var MultipleCombobox = _ref => {
|
|
|
128
146
|
}
|
|
129
147
|
});
|
|
130
148
|
(0, React.useEffect)(() => {
|
|
131
|
-
if (selectedItems.length > 0) {
|
|
132
|
-
setValue(selectedItems
|
|
149
|
+
if ((selectedItems === null || selectedItems === void 0 ? void 0 : selectedItems.length) > 0) {
|
|
150
|
+
setValue(selectedItems);
|
|
133
151
|
}
|
|
134
152
|
}, [selectedItems]);
|
|
153
|
+
(0, React.useEffect)(() => {
|
|
154
|
+
if ((value === null || value === void 0 ? void 0 : value.length) > 0 && (testIfArraysAreUnique(value, selectedItems) || testIfArraysAreUnique(selectedItems, value))) {
|
|
155
|
+
setSelectedItems(value);
|
|
156
|
+
}
|
|
157
|
+
}, [value]);
|
|
158
|
+
var parentRef = (0, React.useRef)(null);
|
|
135
159
|
return /*#__PURE__*/React.createElement("div", {
|
|
136
160
|
id: id,
|
|
137
|
-
className: [baseClassName, componentClassName, userClassName].filter(e => e).join(' '),
|
|
161
|
+
className: [baseClassName, componentClassName, userClassName, "y-".concat(color), "x-".concat(loadingCircleColor)].filter(e => e).join(' '),
|
|
138
162
|
style: style
|
|
139
163
|
}, /*#__PURE__*/React.createElement(_.FormLabel, _extends({}, getLabelProps(), {
|
|
140
|
-
className: "input-label",
|
|
141
164
|
name: name
|
|
142
165
|
}), label), /*#__PURE__*/React.createElement("div", {
|
|
143
166
|
className: "selected-items"
|
|
@@ -155,16 +178,21 @@ var MultipleCombobox = _ref => {
|
|
|
155
178
|
isCompact: true,
|
|
156
179
|
isSimple: true,
|
|
157
180
|
color: "main2"
|
|
158
|
-
}, "X")))), /*#__PURE__*/React.createElement("div",
|
|
181
|
+
}, "X")))), /*#__PURE__*/React.createElement("div", _extends({}, getComboboxProps(), {
|
|
182
|
+
className: "input-wrapper"
|
|
183
|
+
}), /*#__PURE__*/React.createElement("input", _extends({}, getInputProps(getDropdownProps({
|
|
159
184
|
preventKeyAction: isOpen
|
|
160
185
|
})), {
|
|
161
186
|
className: "input"
|
|
162
|
-
}))), /*#__PURE__*/React.createElement(
|
|
187
|
+
})), isFetching && /*#__PURE__*/React.createElement(_a.LoadingCircle, null)), /*#__PURE__*/React.createElement(_a.Popover, {
|
|
188
|
+
isOpen: isOpen,
|
|
189
|
+
parentRef: parentRef
|
|
190
|
+
}, /*#__PURE__*/React.createElement(_Menu.Menu, _extends({
|
|
163
191
|
isOpen: isOpen,
|
|
164
192
|
getItemProps: getItemProps,
|
|
165
193
|
highlightedIndex: highlightedIndex,
|
|
166
194
|
items: getFilteredItems()
|
|
167
|
-
}, getMenuProps())), (description || error) && /*#__PURE__*/React.createElement(_.FormDescription, {
|
|
195
|
+
}, getMenuProps()))), (description || error) && /*#__PURE__*/React.createElement(_.FormDescription, {
|
|
168
196
|
isError: !!error
|
|
169
197
|
}, error || description));
|
|
170
198
|
};
|
|
@@ -206,7 +234,7 @@ MultipleCombobox.propTypes = {
|
|
|
206
234
|
/**
|
|
207
235
|
* The function to fetch the options from the backend
|
|
208
236
|
*/
|
|
209
|
-
|
|
237
|
+
getOptions: _propTypes.default.func,
|
|
210
238
|
|
|
211
239
|
/**
|
|
212
240
|
* The function to set the value of the custom select input
|
|
@@ -221,9 +249,33 @@ MultipleCombobox.propTypes = {
|
|
|
221
249
|
/**
|
|
222
250
|
* The error object
|
|
223
251
|
*/
|
|
224
|
-
error: _propTypes.default.objectOf(_propTypes.default.string)
|
|
252
|
+
error: _propTypes.default.objectOf(_propTypes.default.string),
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* The value of the custom select input
|
|
256
|
+
*/
|
|
257
|
+
value: _propTypes.default.arrayOf(_propTypes.default.shape({
|
|
258
|
+
value: _propTypes.default.string,
|
|
259
|
+
label: _propTypes.default.string
|
|
260
|
+
})),
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* The base color of the custom select input
|
|
264
|
+
*/
|
|
265
|
+
color: _propTypes.default.string,
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Whether the query getting the combobox options is inFlight
|
|
269
|
+
*/
|
|
270
|
+
isFetching: _propTypes.default.bool.isRequired,
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* The loading circle color
|
|
274
|
+
*/
|
|
275
|
+
loadingCircleColor: _propTypes.default.string
|
|
225
276
|
};
|
|
226
|
-
MultipleCombobox.defaultProps = {
|
|
277
|
+
MultipleCombobox.defaultProps = {
|
|
278
|
+
loadingCircleColor: 'main2'
|
|
227
279
|
};
|
|
228
280
|
var _default = MultipleCombobox;
|
|
229
281
|
exports.default = _default;
|
|
@@ -5,11 +5,16 @@
|
|
|
5
5
|
$default-input-padding: .75em .75em .55em;
|
|
6
6
|
$default-padding: 1em;
|
|
7
7
|
$default-margin: 1em;
|
|
8
|
+
$default-loading-circle-displacement: 1em;
|
|
8
9
|
|
|
9
10
|
.#{bem.$base}.combobox,
|
|
10
11
|
.#{bem.$base}.multiple-combobox {
|
|
11
12
|
position: relative;
|
|
12
13
|
|
|
14
|
+
.#{bem.$base}.label {
|
|
15
|
+
margin-bottom: $default-margin
|
|
16
|
+
}
|
|
17
|
+
|
|
13
18
|
.#{bem.$base}.popover {
|
|
14
19
|
width: 100%;
|
|
15
20
|
|
|
@@ -30,6 +35,14 @@ $default-margin: 1em;
|
|
|
30
35
|
}
|
|
31
36
|
|
|
32
37
|
>.input-wrapper {
|
|
38
|
+
position: relative;
|
|
39
|
+
|
|
40
|
+
>.#{bem.$base}.loading-circle {
|
|
41
|
+
position: absolute;
|
|
42
|
+
top: $default-loading-circle-displacement;
|
|
43
|
+
right: $default-loading-circle-displacement;
|
|
44
|
+
}
|
|
45
|
+
|
|
33
46
|
>.input {
|
|
34
47
|
background: var(--light-y);
|
|
35
48
|
border: var(--theme-border-style) var(--dark-y);
|
|
@@ -4,7 +4,7 @@ function _extends() { _extends = Object.assign || function (target) { for (var i
|
|
|
4
4
|
import * as React from 'react';
|
|
5
5
|
import { memo, useLayoutEffect } from 'react';
|
|
6
6
|
import PropTypes from 'prop-types';
|
|
7
|
-
import { TextInput, TextareaInput, ChoicesInput, SelectInput } from "../fields"; // Local Definitions
|
|
7
|
+
import { TextInput, TextareaInput, ChoicesInput, SelectInput, QueryCombobox } from "../fields"; // Local Definitions
|
|
8
8
|
// const baseClassName = styleNames.base
|
|
9
9
|
|
|
10
10
|
const componentClassName = 'form-input';
|
|
@@ -45,6 +45,13 @@ const FormInput = ({
|
|
|
45
45
|
}, otherProps));
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
+
if (type === 'query-combobox') {
|
|
49
|
+
return /*#__PURE__*/React.createElement(QueryCombobox, _extends({
|
|
50
|
+
className: newClassName,
|
|
51
|
+
disabled: disabled
|
|
52
|
+
}, otherProps));
|
|
53
|
+
}
|
|
54
|
+
|
|
48
55
|
if (extraTypes !== null && extraTypes !== void 0 && extraTypes[type]) {
|
|
49
56
|
const Component = extraTypes[type];
|
|
50
57
|
return /*#__PURE__*/React.createElement(Component, _extends({
|
|
@@ -69,7 +76,7 @@ FormInput.propTypes = {
|
|
|
69
76
|
/**
|
|
70
77
|
* The HTML class names for this element
|
|
71
78
|
*/
|
|
72
|
-
type: PropTypes.oneOf(['text', 'email', 'password', 'number', 'date', 'datetime', 'month', 'tel', 'hidden', 'select', 'choices', 'textarea', // to be removed
|
|
79
|
+
type: PropTypes.oneOf(['text', 'email', 'password', 'number', 'date', 'datetime', 'month', 'tel', 'hidden', 'select', 'choices', 'textarea', 'query-combobox', // to be removed
|
|
73
80
|
'extendedTypeInput']),
|
|
74
81
|
|
|
75
82
|
/**
|
|
@@ -11,6 +11,9 @@ import { Combobox } from "./common";
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
const QueryCombobox = ({
|
|
14
|
+
id,
|
|
15
|
+
style,
|
|
16
|
+
className,
|
|
14
17
|
query,
|
|
15
18
|
// multiple,
|
|
16
19
|
name,
|
|
@@ -76,6 +79,8 @@ const QueryCombobox = ({
|
|
|
76
79
|
};
|
|
77
80
|
|
|
78
81
|
const comboboxProps = {
|
|
82
|
+
id,
|
|
83
|
+
style,
|
|
79
84
|
options,
|
|
80
85
|
getOptions,
|
|
81
86
|
debounceMs,
|
|
@@ -86,7 +91,9 @@ const QueryCombobox = ({
|
|
|
86
91
|
setValue,
|
|
87
92
|
error,
|
|
88
93
|
value,
|
|
89
|
-
color
|
|
94
|
+
color,
|
|
95
|
+
isFetching,
|
|
96
|
+
className
|
|
90
97
|
};
|
|
91
98
|
const Input = Combobox;
|
|
92
99
|
return /*#__PURE__*/React.createElement(Input, comboboxProps);
|
|
@@ -7,7 +7,7 @@ import PropTypes from 'prop-types';
|
|
|
7
7
|
import { useCombobox } from 'downshift';
|
|
8
8
|
import styleNames from '@pareto-engineering/bem';
|
|
9
9
|
import { FormLabel, FormDescription } from "../../../..";
|
|
10
|
-
import { Popover } from "../../../../../a"; // Local Definitions
|
|
10
|
+
import { Popover, LoadingCircle } from "../../../../../a"; // Local Definitions
|
|
11
11
|
|
|
12
12
|
import { Menu } from "../Menu";
|
|
13
13
|
const baseClassName = styleNames.base;
|
|
@@ -28,7 +28,9 @@ const Combobox = ({
|
|
|
28
28
|
error,
|
|
29
29
|
description,
|
|
30
30
|
value,
|
|
31
|
-
color
|
|
31
|
+
color,
|
|
32
|
+
loadingCircleColor,
|
|
33
|
+
isFetching // ...otherProps
|
|
32
34
|
|
|
33
35
|
}) => {
|
|
34
36
|
const {
|
|
@@ -67,7 +69,7 @@ const Combobox = ({
|
|
|
67
69
|
const parentRef = useRef(null);
|
|
68
70
|
return /*#__PURE__*/React.createElement("div", {
|
|
69
71
|
id: id,
|
|
70
|
-
className: [baseClassName, componentClassName, userClassName, `y-${color}`].filter(e => e).join(' '),
|
|
72
|
+
className: [baseClassName, componentClassName, userClassName, `y-${color}`, `x-${loadingCircleColor}`].filter(e => e).join(' '),
|
|
71
73
|
style: style,
|
|
72
74
|
ref: parentRef
|
|
73
75
|
}, /*#__PURE__*/React.createElement(FormLabel, _extends({}, getLabelProps(), {
|
|
@@ -76,7 +78,7 @@ const Combobox = ({
|
|
|
76
78
|
className: "input-wrapper"
|
|
77
79
|
}), /*#__PURE__*/React.createElement("input", _extends({}, getInputProps(), {
|
|
78
80
|
className: "input"
|
|
79
|
-
}))), /*#__PURE__*/React.createElement(Popover, {
|
|
81
|
+
})), isFetching && /*#__PURE__*/React.createElement(LoadingCircle, null)), /*#__PURE__*/React.createElement(Popover, {
|
|
80
82
|
isOpen: isOpen,
|
|
81
83
|
parentRef: parentRef
|
|
82
84
|
}, /*#__PURE__*/React.createElement(Menu, _extends({
|
|
@@ -154,8 +156,19 @@ Combobox.propTypes = {
|
|
|
154
156
|
/**
|
|
155
157
|
* The base color of the combobox custom select input
|
|
156
158
|
*/
|
|
157
|
-
color: PropTypes.string
|
|
159
|
+
color: PropTypes.string,
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Whether the query getting the combobox options is inFlight
|
|
163
|
+
*/
|
|
164
|
+
isFetching: PropTypes.bool.isRequired,
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* The loading circle color
|
|
168
|
+
*/
|
|
169
|
+
loadingCircleColor: PropTypes.string
|
|
158
170
|
};
|
|
159
|
-
Combobox.defaultProps = {
|
|
171
|
+
Combobox.defaultProps = {
|
|
172
|
+
loadingCircleColor: 'main2'
|
|
160
173
|
};
|
|
161
174
|
export default Combobox;
|
|
@@ -2,20 +2,30 @@ function _extends() { _extends = Object.assign || function (target) { for (var i
|
|
|
2
2
|
|
|
3
3
|
/* @pareto-engineering/generator-front 1.0.12 */
|
|
4
4
|
import * as React from 'react';
|
|
5
|
-
import { useState, useEffect } from 'react';
|
|
5
|
+
import { useState, useEffect, useRef } from 'react';
|
|
6
6
|
import PropTypes from 'prop-types';
|
|
7
7
|
import styleNames from '@pareto-engineering/bem';
|
|
8
8
|
import { useCombobox, useMultipleSelection } from 'downshift';
|
|
9
9
|
import { Button } from "../../../../../b";
|
|
10
|
+
import { Popover, LoadingCircle } from "../../../../../a";
|
|
10
11
|
import { FormDescription, FormLabel } from "../../../.."; // Local Definitions
|
|
11
12
|
|
|
12
13
|
import { Menu } from "../Menu";
|
|
13
14
|
const baseClassName = styleNames.base;
|
|
14
15
|
const componentClassName = 'multiple-combobox';
|
|
16
|
+
/**
|
|
17
|
+
* @param {Array[Object]} first - first array to check if it has an item not in the second array.
|
|
18
|
+
* @param {Array[Object]} second - second array to check against the first array.
|
|
19
|
+
*
|
|
20
|
+
* @returns {Boolean} - true if the first array has an item not in the second array.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
const testIfArraysAreUnique = (first, second) => first.filter(objInFirstArray => !second.some(objInSecondArray => objInFirstArray.value === objInSecondArray.value)).length > 0;
|
|
15
24
|
/**
|
|
16
25
|
* This is the component description.
|
|
17
26
|
*/
|
|
18
27
|
|
|
28
|
+
|
|
19
29
|
const MultipleCombobox = ({
|
|
20
30
|
id,
|
|
21
31
|
className: userClassName,
|
|
@@ -23,22 +33,29 @@ const MultipleCombobox = ({
|
|
|
23
33
|
label,
|
|
24
34
|
name,
|
|
25
35
|
options: items,
|
|
26
|
-
|
|
36
|
+
getOptions,
|
|
27
37
|
setValue,
|
|
28
38
|
error,
|
|
29
|
-
description
|
|
39
|
+
description,
|
|
40
|
+
value,
|
|
41
|
+
color,
|
|
42
|
+
isFetching,
|
|
43
|
+
loadingCircleColor // ...otherProps
|
|
30
44
|
|
|
31
45
|
}) => {
|
|
32
|
-
const [
|
|
46
|
+
const [searchInputValue, setSearchInputValue] = useState('');
|
|
33
47
|
const {
|
|
34
48
|
getSelectedItemProps,
|
|
35
49
|
getDropdownProps,
|
|
36
50
|
addSelectedItem,
|
|
37
51
|
removeSelectedItem,
|
|
52
|
+
setSelectedItems,
|
|
38
53
|
selectedItems
|
|
39
|
-
} = useMultipleSelection(
|
|
54
|
+
} = useMultipleSelection({
|
|
55
|
+
initialSelectedItems: value || []
|
|
56
|
+
});
|
|
40
57
|
|
|
41
|
-
const getFilteredItems = () => items.filter(item => selectedItems.findIndex(e => e.label === item.label) < 0 && item.label.toLowerCase().startsWith(
|
|
58
|
+
const getFilteredItems = () => items.filter(item => selectedItems.findIndex(e => e.label === item.label) < 0 && item.label.toLowerCase().startsWith(searchInputValue.toLowerCase()));
|
|
42
59
|
|
|
43
60
|
const {
|
|
44
61
|
isOpen,
|
|
@@ -49,7 +66,7 @@ const MultipleCombobox = ({
|
|
|
49
66
|
highlightedIndex,
|
|
50
67
|
getItemProps
|
|
51
68
|
} = useCombobox({
|
|
52
|
-
|
|
69
|
+
searchInputValue,
|
|
53
70
|
defaultHighlightedIndex: 0,
|
|
54
71
|
// after selection, highlight the first item.
|
|
55
72
|
selectedItem: null,
|
|
@@ -76,21 +93,21 @@ const MultipleCombobox = ({
|
|
|
76
93
|
return changes;
|
|
77
94
|
},
|
|
78
95
|
onStateChange: ({
|
|
79
|
-
inputValue:
|
|
96
|
+
inputValue: newSearchInputValue,
|
|
80
97
|
type,
|
|
81
98
|
selectedItem
|
|
82
99
|
}) => {
|
|
83
100
|
switch (type) {
|
|
84
101
|
case useCombobox.stateChangeTypes.InputChange:
|
|
85
|
-
|
|
86
|
-
|
|
102
|
+
getOptions(newSearchInputValue);
|
|
103
|
+
setSearchInputValue(newSearchInputValue);
|
|
87
104
|
break;
|
|
88
105
|
|
|
89
106
|
case useCombobox.stateChangeTypes.InputKeyDownEnter:
|
|
90
107
|
case useCombobox.stateChangeTypes.ItemClick:
|
|
91
108
|
case useCombobox.stateChangeTypes.InputBlur:
|
|
92
109
|
if (selectedItem) {
|
|
93
|
-
|
|
110
|
+
setSearchInputValue('');
|
|
94
111
|
addSelectedItem(selectedItem);
|
|
95
112
|
}
|
|
96
113
|
|
|
@@ -102,16 +119,21 @@ const MultipleCombobox = ({
|
|
|
102
119
|
}
|
|
103
120
|
});
|
|
104
121
|
useEffect(() => {
|
|
105
|
-
if (selectedItems.length > 0) {
|
|
106
|
-
setValue(selectedItems
|
|
122
|
+
if ((selectedItems === null || selectedItems === void 0 ? void 0 : selectedItems.length) > 0) {
|
|
123
|
+
setValue(selectedItems);
|
|
107
124
|
}
|
|
108
125
|
}, [selectedItems]);
|
|
126
|
+
useEffect(() => {
|
|
127
|
+
if ((value === null || value === void 0 ? void 0 : value.length) > 0 && (testIfArraysAreUnique(value, selectedItems) || testIfArraysAreUnique(selectedItems, value))) {
|
|
128
|
+
setSelectedItems(value);
|
|
129
|
+
}
|
|
130
|
+
}, [value]);
|
|
131
|
+
const parentRef = useRef(null);
|
|
109
132
|
return /*#__PURE__*/React.createElement("div", {
|
|
110
133
|
id: id,
|
|
111
|
-
className: [baseClassName, componentClassName, userClassName].filter(e => e).join(' '),
|
|
134
|
+
className: [baseClassName, componentClassName, userClassName, `y-${color}`, `x-${loadingCircleColor}`].filter(e => e).join(' '),
|
|
112
135
|
style: style
|
|
113
136
|
}, /*#__PURE__*/React.createElement(FormLabel, _extends({}, getLabelProps(), {
|
|
114
|
-
className: "input-label",
|
|
115
137
|
name: name
|
|
116
138
|
}), label), /*#__PURE__*/React.createElement("div", {
|
|
117
139
|
className: "selected-items"
|
|
@@ -129,16 +151,21 @@ const MultipleCombobox = ({
|
|
|
129
151
|
isCompact: true,
|
|
130
152
|
isSimple: true,
|
|
131
153
|
color: "main2"
|
|
132
|
-
}, "X")))), /*#__PURE__*/React.createElement("div",
|
|
154
|
+
}, "X")))), /*#__PURE__*/React.createElement("div", _extends({}, getComboboxProps(), {
|
|
155
|
+
className: "input-wrapper"
|
|
156
|
+
}), /*#__PURE__*/React.createElement("input", _extends({}, getInputProps(getDropdownProps({
|
|
133
157
|
preventKeyAction: isOpen
|
|
134
158
|
})), {
|
|
135
159
|
className: "input"
|
|
136
|
-
}))), /*#__PURE__*/React.createElement(
|
|
160
|
+
})), isFetching && /*#__PURE__*/React.createElement(LoadingCircle, null)), /*#__PURE__*/React.createElement(Popover, {
|
|
161
|
+
isOpen: isOpen,
|
|
162
|
+
parentRef: parentRef
|
|
163
|
+
}, /*#__PURE__*/React.createElement(Menu, _extends({
|
|
137
164
|
isOpen: isOpen,
|
|
138
165
|
getItemProps: getItemProps,
|
|
139
166
|
highlightedIndex: highlightedIndex,
|
|
140
167
|
items: getFilteredItems()
|
|
141
|
-
}, getMenuProps())), (description || error) && /*#__PURE__*/React.createElement(FormDescription, {
|
|
168
|
+
}, getMenuProps()))), (description || error) && /*#__PURE__*/React.createElement(FormDescription, {
|
|
142
169
|
isError: !!error
|
|
143
170
|
}, error || description));
|
|
144
171
|
};
|
|
@@ -180,7 +207,7 @@ MultipleCombobox.propTypes = {
|
|
|
180
207
|
/**
|
|
181
208
|
* The function to fetch the options from the backend
|
|
182
209
|
*/
|
|
183
|
-
|
|
210
|
+
getOptions: PropTypes.func,
|
|
184
211
|
|
|
185
212
|
/**
|
|
186
213
|
* The function to set the value of the custom select input
|
|
@@ -195,8 +222,32 @@ MultipleCombobox.propTypes = {
|
|
|
195
222
|
/**
|
|
196
223
|
* The error object
|
|
197
224
|
*/
|
|
198
|
-
error: PropTypes.objectOf(PropTypes.string)
|
|
225
|
+
error: PropTypes.objectOf(PropTypes.string),
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* The value of the custom select input
|
|
229
|
+
*/
|
|
230
|
+
value: PropTypes.arrayOf(PropTypes.shape({
|
|
231
|
+
value: PropTypes.string,
|
|
232
|
+
label: PropTypes.string
|
|
233
|
+
})),
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* The base color of the custom select input
|
|
237
|
+
*/
|
|
238
|
+
color: PropTypes.string,
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Whether the query getting the combobox options is inFlight
|
|
242
|
+
*/
|
|
243
|
+
isFetching: PropTypes.bool.isRequired,
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* The loading circle color
|
|
247
|
+
*/
|
|
248
|
+
loadingCircleColor: PropTypes.string
|
|
199
249
|
};
|
|
200
|
-
MultipleCombobox.defaultProps = {
|
|
250
|
+
MultipleCombobox.defaultProps = {
|
|
251
|
+
loadingCircleColor: 'main2'
|
|
201
252
|
};
|
|
202
253
|
export default MultipleCombobox;
|
|
@@ -5,11 +5,16 @@
|
|
|
5
5
|
$default-input-padding: .75em .75em .55em;
|
|
6
6
|
$default-padding: 1em;
|
|
7
7
|
$default-margin: 1em;
|
|
8
|
+
$default-loading-circle-displacement: 1em;
|
|
8
9
|
|
|
9
10
|
.#{bem.$base}.combobox,
|
|
10
11
|
.#{bem.$base}.multiple-combobox {
|
|
11
12
|
position: relative;
|
|
12
13
|
|
|
14
|
+
.#{bem.$base}.label {
|
|
15
|
+
margin-bottom: $default-margin
|
|
16
|
+
}
|
|
17
|
+
|
|
13
18
|
.#{bem.$base}.popover {
|
|
14
19
|
width: 100%;
|
|
15
20
|
|
|
@@ -30,6 +35,14 @@ $default-margin: 1em;
|
|
|
30
35
|
}
|
|
31
36
|
|
|
32
37
|
>.input-wrapper {
|
|
38
|
+
position: relative;
|
|
39
|
+
|
|
40
|
+
>.#{bem.$base}.loading-circle {
|
|
41
|
+
position: absolute;
|
|
42
|
+
top: $default-loading-circle-displacement;
|
|
43
|
+
right: $default-loading-circle-displacement;
|
|
44
|
+
}
|
|
45
|
+
|
|
33
46
|
>.input {
|
|
34
47
|
background: var(--light-y);
|
|
35
48
|
border: var(--theme-border-style) var(--dark-y);
|
package/package.json
CHANGED
|
@@ -10637,6 +10637,161 @@ exports[`Storyshots f/FormInput Extensible Form Input 1`] = `
|
|
|
10637
10637
|
</form>
|
|
10638
10638
|
`;
|
|
10639
10639
|
|
|
10640
|
+
exports[`Storyshots f/FormInput With Query Combobox 1`] = `
|
|
10641
|
+
<form
|
|
10642
|
+
action="#"
|
|
10643
|
+
onReset={[Function]}
|
|
10644
|
+
onSubmit={[Function]}
|
|
10645
|
+
>
|
|
10646
|
+
<div
|
|
10647
|
+
className="base text-input form-input y-background2"
|
|
10648
|
+
>
|
|
10649
|
+
<label
|
|
10650
|
+
className="base label v50 mb-v x-main2"
|
|
10651
|
+
htmlFor="firstName"
|
|
10652
|
+
>
|
|
10653
|
+
What's your first name ?
|
|
10654
|
+
</label>
|
|
10655
|
+
<input
|
|
10656
|
+
className="input"
|
|
10657
|
+
disabled={false}
|
|
10658
|
+
id="firstName"
|
|
10659
|
+
name="firstName"
|
|
10660
|
+
onBlur={[Function]}
|
|
10661
|
+
onChange={[Function]}
|
|
10662
|
+
type="text"
|
|
10663
|
+
value="Test"
|
|
10664
|
+
/>
|
|
10665
|
+
</div>
|
|
10666
|
+
<div
|
|
10667
|
+
className="base choices-input form-input x-background2 y-main2"
|
|
10668
|
+
style={
|
|
10669
|
+
Object {
|
|
10670
|
+
"--grid-columns-desktop": 3,
|
|
10671
|
+
"--grid-columns-mobile": 2,
|
|
10672
|
+
}
|
|
10673
|
+
}
|
|
10674
|
+
>
|
|
10675
|
+
<p
|
|
10676
|
+
className="base label x-main2"
|
|
10677
|
+
>
|
|
10678
|
+
What are your favourite colors ?
|
|
10679
|
+
</p>
|
|
10680
|
+
<div
|
|
10681
|
+
className="choices"
|
|
10682
|
+
>
|
|
10683
|
+
<div
|
|
10684
|
+
className="base choice"
|
|
10685
|
+
>
|
|
10686
|
+
<input
|
|
10687
|
+
disabled={false}
|
|
10688
|
+
id="undefined-red"
|
|
10689
|
+
name="colors"
|
|
10690
|
+
onBlur={[Function]}
|
|
10691
|
+
onChange={[Function]}
|
|
10692
|
+
type="radio"
|
|
10693
|
+
value="red"
|
|
10694
|
+
/>
|
|
10695
|
+
<label
|
|
10696
|
+
htmlFor="undefined-red"
|
|
10697
|
+
>
|
|
10698
|
+
Color Red
|
|
10699
|
+
</label>
|
|
10700
|
+
</div>
|
|
10701
|
+
<div
|
|
10702
|
+
className="base choice"
|
|
10703
|
+
>
|
|
10704
|
+
<input
|
|
10705
|
+
disabled={false}
|
|
10706
|
+
id="undefined-blue"
|
|
10707
|
+
name="colors"
|
|
10708
|
+
onBlur={[Function]}
|
|
10709
|
+
onChange={[Function]}
|
|
10710
|
+
type="radio"
|
|
10711
|
+
value="blue"
|
|
10712
|
+
/>
|
|
10713
|
+
<label
|
|
10714
|
+
htmlFor="undefined-blue"
|
|
10715
|
+
>
|
|
10716
|
+
Color Blue
|
|
10717
|
+
</label>
|
|
10718
|
+
</div>
|
|
10719
|
+
<div
|
|
10720
|
+
className="base choice"
|
|
10721
|
+
>
|
|
10722
|
+
<input
|
|
10723
|
+
disabled={false}
|
|
10724
|
+
id="undefined-green"
|
|
10725
|
+
name="colors"
|
|
10726
|
+
onBlur={[Function]}
|
|
10727
|
+
onChange={[Function]}
|
|
10728
|
+
type="radio"
|
|
10729
|
+
value="green"
|
|
10730
|
+
/>
|
|
10731
|
+
<label
|
|
10732
|
+
htmlFor="undefined-green"
|
|
10733
|
+
>
|
|
10734
|
+
Color Green
|
|
10735
|
+
</label>
|
|
10736
|
+
</div>
|
|
10737
|
+
</div>
|
|
10738
|
+
</div>
|
|
10739
|
+
<div
|
|
10740
|
+
className="base combobox form-input y-background2 x-main2"
|
|
10741
|
+
>
|
|
10742
|
+
<label
|
|
10743
|
+
className="base label x-main2"
|
|
10744
|
+
htmlFor="team"
|
|
10745
|
+
id="downshift-0-label"
|
|
10746
|
+
>
|
|
10747
|
+
Search for a team
|
|
10748
|
+
</label>
|
|
10749
|
+
<div
|
|
10750
|
+
aria-expanded={false}
|
|
10751
|
+
aria-haspopup="listbox"
|
|
10752
|
+
aria-owns="downshift-0-menu"
|
|
10753
|
+
className="input-wrapper"
|
|
10754
|
+
role="combobox"
|
|
10755
|
+
>
|
|
10756
|
+
<input
|
|
10757
|
+
aria-autocomplete="list"
|
|
10758
|
+
aria-controls="downshift-0-menu"
|
|
10759
|
+
aria-labelledby="downshift-0-label"
|
|
10760
|
+
autoComplete="off"
|
|
10761
|
+
className="input"
|
|
10762
|
+
id="downshift-0-input"
|
|
10763
|
+
onBlur={[Function]}
|
|
10764
|
+
onChange={[Function]}
|
|
10765
|
+
onKeyDown={[Function]}
|
|
10766
|
+
value=""
|
|
10767
|
+
/>
|
|
10768
|
+
</div>
|
|
10769
|
+
<div
|
|
10770
|
+
className="base popover x-background1 bottom left"
|
|
10771
|
+
>
|
|
10772
|
+
<ul
|
|
10773
|
+
aria-labelledby="downshift-0-label"
|
|
10774
|
+
className="base menu"
|
|
10775
|
+
id="downshift-0-menu"
|
|
10776
|
+
onMouseLeave={[Function]}
|
|
10777
|
+
role="listbox"
|
|
10778
|
+
/>
|
|
10779
|
+
</div>
|
|
10780
|
+
</div>
|
|
10781
|
+
<div
|
|
10782
|
+
className="debugger"
|
|
10783
|
+
>
|
|
10784
|
+
<button
|
|
10785
|
+
className="base button x-main2"
|
|
10786
|
+
onClick={[Function]}
|
|
10787
|
+
type="button"
|
|
10788
|
+
>
|
|
10789
|
+
Open FormDebugger
|
|
10790
|
+
</button>
|
|
10791
|
+
</div>
|
|
10792
|
+
</form>
|
|
10793
|
+
`;
|
|
10794
|
+
|
|
10640
10795
|
exports[`Storyshots f/common/Description Base 1`] = `
|
|
10641
10796
|
<div
|
|
10642
10797
|
className="base description x-metadata"
|
|
@@ -11205,29 +11360,29 @@ exports[`Storyshots f/fields/QueryCombobox Single Select 1`] = `
|
|
|
11205
11360
|
onSubmit={[Function]}
|
|
11206
11361
|
>
|
|
11207
11362
|
<div
|
|
11208
|
-
className="base combobox y-background2"
|
|
11363
|
+
className="base combobox y-background2 x-main2"
|
|
11209
11364
|
>
|
|
11210
11365
|
<label
|
|
11211
11366
|
className="base label x-main2"
|
|
11212
11367
|
htmlFor="team"
|
|
11213
|
-
id="downshift-
|
|
11368
|
+
id="downshift-1-label"
|
|
11214
11369
|
>
|
|
11215
11370
|
Search for a team
|
|
11216
11371
|
</label>
|
|
11217
11372
|
<div
|
|
11218
11373
|
aria-expanded={false}
|
|
11219
11374
|
aria-haspopup="listbox"
|
|
11220
|
-
aria-owns="downshift-
|
|
11375
|
+
aria-owns="downshift-1-menu"
|
|
11221
11376
|
className="input-wrapper"
|
|
11222
11377
|
role="combobox"
|
|
11223
11378
|
>
|
|
11224
11379
|
<input
|
|
11225
11380
|
aria-autocomplete="list"
|
|
11226
|
-
aria-controls="downshift-
|
|
11227
|
-
aria-labelledby="downshift-
|
|
11381
|
+
aria-controls="downshift-1-menu"
|
|
11382
|
+
aria-labelledby="downshift-1-label"
|
|
11228
11383
|
autoComplete="off"
|
|
11229
11384
|
className="input"
|
|
11230
|
-
id="downshift-
|
|
11385
|
+
id="downshift-1-input"
|
|
11231
11386
|
onBlur={[Function]}
|
|
11232
11387
|
onChange={[Function]}
|
|
11233
11388
|
onKeyDown={[Function]}
|
|
@@ -11238,9 +11393,9 @@ exports[`Storyshots f/fields/QueryCombobox Single Select 1`] = `
|
|
|
11238
11393
|
className="base popover x-background1 bottom left"
|
|
11239
11394
|
>
|
|
11240
11395
|
<ul
|
|
11241
|
-
aria-labelledby="downshift-
|
|
11396
|
+
aria-labelledby="downshift-1-label"
|
|
11242
11397
|
className="base menu"
|
|
11243
|
-
id="downshift-
|
|
11398
|
+
id="downshift-1-menu"
|
|
11244
11399
|
onMouseLeave={[Function]}
|
|
11245
11400
|
role="listbox"
|
|
11246
11401
|
/>
|
|
@@ -11284,29 +11439,29 @@ exports[`Storyshots f/fields/QueryCombobox Single Select With Default Formik Sta
|
|
|
11284
11439
|
onSubmit={[Function]}
|
|
11285
11440
|
>
|
|
11286
11441
|
<div
|
|
11287
|
-
className="base combobox y-background2"
|
|
11442
|
+
className="base combobox y-background2 x-main2"
|
|
11288
11443
|
>
|
|
11289
11444
|
<label
|
|
11290
11445
|
className="base label x-main2"
|
|
11291
11446
|
htmlFor="team"
|
|
11292
|
-
id="downshift-
|
|
11447
|
+
id="downshift-2-label"
|
|
11293
11448
|
>
|
|
11294
11449
|
Search for a team
|
|
11295
11450
|
</label>
|
|
11296
11451
|
<div
|
|
11297
11452
|
aria-expanded={false}
|
|
11298
11453
|
aria-haspopup="listbox"
|
|
11299
|
-
aria-owns="downshift-
|
|
11454
|
+
aria-owns="downshift-2-menu"
|
|
11300
11455
|
className="input-wrapper"
|
|
11301
11456
|
role="combobox"
|
|
11302
11457
|
>
|
|
11303
11458
|
<input
|
|
11304
11459
|
aria-autocomplete="list"
|
|
11305
|
-
aria-controls="downshift-
|
|
11306
|
-
aria-labelledby="downshift-
|
|
11460
|
+
aria-controls="downshift-2-menu"
|
|
11461
|
+
aria-labelledby="downshift-2-label"
|
|
11307
11462
|
autoComplete="off"
|
|
11308
11463
|
className="input"
|
|
11309
|
-
id="downshift-
|
|
11464
|
+
id="downshift-2-input"
|
|
11310
11465
|
onBlur={[Function]}
|
|
11311
11466
|
onChange={[Function]}
|
|
11312
11467
|
onKeyDown={[Function]}
|
|
@@ -11317,9 +11472,9 @@ exports[`Storyshots f/fields/QueryCombobox Single Select With Default Formik Sta
|
|
|
11317
11472
|
className="base popover x-background1 bottom left"
|
|
11318
11473
|
>
|
|
11319
11474
|
<ul
|
|
11320
|
-
aria-labelledby="downshift-
|
|
11475
|
+
aria-labelledby="downshift-2-label"
|
|
11321
11476
|
className="base menu"
|
|
11322
|
-
id="downshift-
|
|
11477
|
+
id="downshift-2-menu"
|
|
11323
11478
|
onMouseLeave={[Function]}
|
|
11324
11479
|
role="listbox"
|
|
11325
11480
|
/>
|
|
@@ -8,6 +8,15 @@ import {
|
|
|
8
8
|
FormDebugger,
|
|
9
9
|
} from 'ui'
|
|
10
10
|
|
|
11
|
+
import { fruits } from '../utils/testData'
|
|
12
|
+
|
|
13
|
+
import {
|
|
14
|
+
RelayEnvironmentProvider,
|
|
15
|
+
mockRelayOperation,
|
|
16
|
+
} from '../utils/relay'
|
|
17
|
+
|
|
18
|
+
import generateNodeId from '../utils/generateNodeId'
|
|
19
|
+
|
|
11
20
|
export default {
|
|
12
21
|
title :'f/FormInput',
|
|
13
22
|
component :FormInput,
|
|
@@ -25,6 +34,11 @@ export default {
|
|
|
25
34
|
</Form>
|
|
26
35
|
</Formik>
|
|
27
36
|
),
|
|
37
|
+
(storyfn) => (
|
|
38
|
+
<RelayEnvironmentProvider>
|
|
39
|
+
{ storyfn() }
|
|
40
|
+
</RelayEnvironmentProvider>
|
|
41
|
+
),
|
|
28
42
|
],
|
|
29
43
|
argTypes:{
|
|
30
44
|
backgroundColor:{ control: 'color' },
|
|
@@ -99,6 +113,107 @@ export const Base = () => {
|
|
|
99
113
|
)
|
|
100
114
|
}
|
|
101
115
|
|
|
116
|
+
const allTeamsMockData = {
|
|
117
|
+
PageInfo() {
|
|
118
|
+
return {
|
|
119
|
+
hasNextPage :true,
|
|
120
|
+
hasPreviousPage:true,
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
TeamNodeConnection:(args) => {
|
|
124
|
+
const edges = fruits.map((fruit) => ({
|
|
125
|
+
node:{
|
|
126
|
+
id :generateNodeId('TeamNode'),
|
|
127
|
+
name:fruit,
|
|
128
|
+
},
|
|
129
|
+
}))
|
|
130
|
+
|
|
131
|
+
// eslint-disable-next-line camelcase
|
|
132
|
+
const { name_Icontains } = args?.args || {}
|
|
133
|
+
|
|
134
|
+
return ({
|
|
135
|
+
pageInfo:{
|
|
136
|
+
hasNextPage :true,
|
|
137
|
+
hasPreviousPage:true,
|
|
138
|
+
},
|
|
139
|
+
// eslint-disable-next-line camelcase
|
|
140
|
+
edges:name_Icontains?.trim()
|
|
141
|
+
? edges.filter(({ node }) => node.name
|
|
142
|
+
.toLowerCase()
|
|
143
|
+
.includes(name_Icontains.toLowerCase()))
|
|
144
|
+
: [],
|
|
145
|
+
})
|
|
146
|
+
},
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const FETCH_TEAMS_QUERY = graphql`
|
|
150
|
+
query FormInputAllTeamsQuery($name_Icontains: String) {
|
|
151
|
+
allTeams(name_Icontains: $name_Icontains) {
|
|
152
|
+
edges {
|
|
153
|
+
node {
|
|
154
|
+
id
|
|
155
|
+
name
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
`
|
|
161
|
+
|
|
162
|
+
export const WithQueryCombobox = () => {
|
|
163
|
+
const inputMap = [
|
|
164
|
+
{
|
|
165
|
+
type :'text',
|
|
166
|
+
name :'firstName',
|
|
167
|
+
label:"What's your first name ?",
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
type :'choices',
|
|
171
|
+
name :'colors',
|
|
172
|
+
label :'What are your favourite colors ?',
|
|
173
|
+
options:[
|
|
174
|
+
{
|
|
175
|
+
value:'red',
|
|
176
|
+
label:'Color Red',
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
value:'blue',
|
|
180
|
+
label:'Color Blue',
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
value:'green',
|
|
184
|
+
label:'Color Green',
|
|
185
|
+
},
|
|
186
|
+
],
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
type :'query-combobox',
|
|
190
|
+
name :'team',
|
|
191
|
+
query :FETCH_TEAMS_QUERY,
|
|
192
|
+
label :'Search for a team',
|
|
193
|
+
optionsKeyMap:{
|
|
194
|
+
value:'id',
|
|
195
|
+
label:'name',
|
|
196
|
+
},
|
|
197
|
+
graphQlNode :'allTeams',
|
|
198
|
+
searchVariable:'name_Icontains',
|
|
199
|
+
},
|
|
200
|
+
]
|
|
201
|
+
|
|
202
|
+
mockRelayOperation(allTeamsMockData)
|
|
203
|
+
mockRelayOperation(allTeamsMockData)
|
|
204
|
+
mockRelayOperation(allTeamsMockData)
|
|
205
|
+
|
|
206
|
+
return (
|
|
207
|
+
<>
|
|
208
|
+
{
|
|
209
|
+
inputMap.map((input) => <FormInput {...input} key={input.name} />)
|
|
210
|
+
}
|
|
211
|
+
<FormDebugger />
|
|
212
|
+
|
|
213
|
+
</>
|
|
214
|
+
)
|
|
215
|
+
}
|
|
216
|
+
|
|
102
217
|
export const ExtensibleFormInput = () => {
|
|
103
218
|
const ExtendedTextInput = (props) => (
|
|
104
219
|
<TextInput
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @flow
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/* eslint-disable */
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
/*::
|
|
10
|
+
import type { ConcreteRequest } from 'relay-runtime';
|
|
11
|
+
export type FormInputAllTeamsQueryVariables = {|
|
|
12
|
+
name_Icontains?: ?string
|
|
13
|
+
|};
|
|
14
|
+
export type FormInputAllTeamsQueryResponse = {|
|
|
15
|
+
+allTeams: ?{|
|
|
16
|
+
+edges: $ReadOnlyArray<?{|
|
|
17
|
+
+node: ?{|
|
|
18
|
+
+id: string,
|
|
19
|
+
+name: string,
|
|
20
|
+
|}
|
|
21
|
+
|}>
|
|
22
|
+
|}
|
|
23
|
+
|};
|
|
24
|
+
export type FormInputAllTeamsQuery = {|
|
|
25
|
+
variables: FormInputAllTeamsQueryVariables,
|
|
26
|
+
response: FormInputAllTeamsQueryResponse,
|
|
27
|
+
|};
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
/*
|
|
32
|
+
query FormInputAllTeamsQuery(
|
|
33
|
+
$name_Icontains: String
|
|
34
|
+
) {
|
|
35
|
+
allTeams(name_Icontains: $name_Icontains) {
|
|
36
|
+
edges {
|
|
37
|
+
node {
|
|
38
|
+
id
|
|
39
|
+
name
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
*/
|
|
45
|
+
|
|
46
|
+
const node/*: ConcreteRequest*/ = (function(){
|
|
47
|
+
var v0 = [
|
|
48
|
+
{
|
|
49
|
+
"defaultValue": null,
|
|
50
|
+
"kind": "LocalArgument",
|
|
51
|
+
"name": "name_Icontains"
|
|
52
|
+
}
|
|
53
|
+
],
|
|
54
|
+
v1 = [
|
|
55
|
+
{
|
|
56
|
+
"alias": null,
|
|
57
|
+
"args": [
|
|
58
|
+
{
|
|
59
|
+
"kind": "Variable",
|
|
60
|
+
"name": "name_Icontains",
|
|
61
|
+
"variableName": "name_Icontains"
|
|
62
|
+
}
|
|
63
|
+
],
|
|
64
|
+
"concreteType": "TeamNodeConnection",
|
|
65
|
+
"kind": "LinkedField",
|
|
66
|
+
"name": "allTeams",
|
|
67
|
+
"plural": false,
|
|
68
|
+
"selections": [
|
|
69
|
+
{
|
|
70
|
+
"alias": null,
|
|
71
|
+
"args": null,
|
|
72
|
+
"concreteType": "TeamNodeEdge",
|
|
73
|
+
"kind": "LinkedField",
|
|
74
|
+
"name": "edges",
|
|
75
|
+
"plural": true,
|
|
76
|
+
"selections": [
|
|
77
|
+
{
|
|
78
|
+
"alias": null,
|
|
79
|
+
"args": null,
|
|
80
|
+
"concreteType": "TeamNode",
|
|
81
|
+
"kind": "LinkedField",
|
|
82
|
+
"name": "node",
|
|
83
|
+
"plural": false,
|
|
84
|
+
"selections": [
|
|
85
|
+
{
|
|
86
|
+
"alias": null,
|
|
87
|
+
"args": null,
|
|
88
|
+
"kind": "ScalarField",
|
|
89
|
+
"name": "id",
|
|
90
|
+
"storageKey": null
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
"alias": null,
|
|
94
|
+
"args": null,
|
|
95
|
+
"kind": "ScalarField",
|
|
96
|
+
"name": "name",
|
|
97
|
+
"storageKey": null
|
|
98
|
+
}
|
|
99
|
+
],
|
|
100
|
+
"storageKey": null
|
|
101
|
+
}
|
|
102
|
+
],
|
|
103
|
+
"storageKey": null
|
|
104
|
+
}
|
|
105
|
+
],
|
|
106
|
+
"storageKey": null
|
|
107
|
+
}
|
|
108
|
+
];
|
|
109
|
+
return {
|
|
110
|
+
"fragment": {
|
|
111
|
+
"argumentDefinitions": (v0/*: any*/),
|
|
112
|
+
"kind": "Fragment",
|
|
113
|
+
"metadata": null,
|
|
114
|
+
"name": "FormInputAllTeamsQuery",
|
|
115
|
+
"selections": (v1/*: any*/),
|
|
116
|
+
"type": "Query",
|
|
117
|
+
"abstractKey": null
|
|
118
|
+
},
|
|
119
|
+
"kind": "Request",
|
|
120
|
+
"operation": {
|
|
121
|
+
"argumentDefinitions": (v0/*: any*/),
|
|
122
|
+
"kind": "Operation",
|
|
123
|
+
"name": "FormInputAllTeamsQuery",
|
|
124
|
+
"selections": (v1/*: any*/)
|
|
125
|
+
},
|
|
126
|
+
"params": {
|
|
127
|
+
"cacheID": "dc9287c6d087d0f0e1be2e8ef405cb1f",
|
|
128
|
+
"id": null,
|
|
129
|
+
"metadata": {},
|
|
130
|
+
"name": "FormInputAllTeamsQuery",
|
|
131
|
+
"operationKind": "query",
|
|
132
|
+
"text": "query FormInputAllTeamsQuery(\n $name_Icontains: String\n) {\n allTeams(name_Icontains: $name_Icontains) {\n edges {\n node {\n id\n name\n }\n }\n }\n}\n"
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
})();
|
|
136
|
+
// prettier-ignore
|
|
137
|
+
(node/*: any*/).hash = 'c76b6f84436895e8a6d0725bdd41cfe0';
|
|
138
|
+
|
|
139
|
+
module.exports = node;
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
TextareaInput,
|
|
11
11
|
ChoicesInput,
|
|
12
12
|
SelectInput,
|
|
13
|
+
QueryCombobox,
|
|
13
14
|
} from '../fields'
|
|
14
15
|
|
|
15
16
|
// Local Definitions
|
|
@@ -64,6 +65,15 @@ const FormInput = ({
|
|
|
64
65
|
/>
|
|
65
66
|
)
|
|
66
67
|
}
|
|
68
|
+
if (type === 'query-combobox') {
|
|
69
|
+
return (
|
|
70
|
+
<QueryCombobox
|
|
71
|
+
className={newClassName}
|
|
72
|
+
disabled={disabled}
|
|
73
|
+
{...otherProps}
|
|
74
|
+
/>
|
|
75
|
+
)
|
|
76
|
+
}
|
|
67
77
|
if (extraTypes?.[type]) {
|
|
68
78
|
const Component = extraTypes[type]
|
|
69
79
|
return (
|
|
@@ -106,6 +116,7 @@ FormInput.propTypes = {
|
|
|
106
116
|
'select',
|
|
107
117
|
'choices',
|
|
108
118
|
'textarea',
|
|
119
|
+
'query-combobox',
|
|
109
120
|
// to be removed
|
|
110
121
|
'extendedTypeInput',
|
|
111
122
|
]),
|
|
@@ -17,6 +17,9 @@ import { Combobox } from './common'
|
|
|
17
17
|
* This is the component description.
|
|
18
18
|
*/
|
|
19
19
|
const QueryCombobox = ({
|
|
20
|
+
id,
|
|
21
|
+
style,
|
|
22
|
+
className,
|
|
20
23
|
query,
|
|
21
24
|
// multiple,
|
|
22
25
|
name,
|
|
@@ -85,6 +88,8 @@ const QueryCombobox = ({
|
|
|
85
88
|
}
|
|
86
89
|
|
|
87
90
|
const comboboxProps = {
|
|
91
|
+
id,
|
|
92
|
+
style,
|
|
88
93
|
options,
|
|
89
94
|
getOptions,
|
|
90
95
|
debounceMs,
|
|
@@ -96,6 +101,8 @@ const QueryCombobox = ({
|
|
|
96
101
|
error,
|
|
97
102
|
value,
|
|
98
103
|
color,
|
|
104
|
+
isFetching,
|
|
105
|
+
className,
|
|
99
106
|
}
|
|
100
107
|
|
|
101
108
|
const Input = Combobox
|
|
@@ -11,7 +11,7 @@ import styleNames from '@pareto-engineering/bem'
|
|
|
11
11
|
|
|
12
12
|
import { FormLabel, FormDescription } from 'ui/f'
|
|
13
13
|
|
|
14
|
-
import { Popover } from 'ui/a'
|
|
14
|
+
import { Popover, LoadingCircle } from 'ui/a'
|
|
15
15
|
|
|
16
16
|
// Local Definitions
|
|
17
17
|
|
|
@@ -37,6 +37,8 @@ const Combobox = ({
|
|
|
37
37
|
description,
|
|
38
38
|
value,
|
|
39
39
|
color,
|
|
40
|
+
loadingCircleColor,
|
|
41
|
+
isFetching,
|
|
40
42
|
// ...otherProps
|
|
41
43
|
}) => {
|
|
42
44
|
const {
|
|
@@ -85,6 +87,7 @@ const Combobox = ({
|
|
|
85
87
|
componentClassName,
|
|
86
88
|
userClassName,
|
|
87
89
|
`y-${color}`,
|
|
90
|
+
`x-${loadingCircleColor}`,
|
|
88
91
|
]
|
|
89
92
|
.filter((e) => e)
|
|
90
93
|
.join(' ')}
|
|
@@ -97,6 +100,9 @@ const Combobox = ({
|
|
|
97
100
|
|
|
98
101
|
<div {...getComboboxProps()} className="input-wrapper">
|
|
99
102
|
<input {...getInputProps()} className="input" />
|
|
103
|
+
{isFetching && (
|
|
104
|
+
<LoadingCircle />
|
|
105
|
+
)}
|
|
100
106
|
</div>
|
|
101
107
|
|
|
102
108
|
<Popover
|
|
@@ -189,10 +195,20 @@ Combobox.propTypes = {
|
|
|
189
195
|
* The base color of the combobox custom select input
|
|
190
196
|
*/
|
|
191
197
|
color:PropTypes.string,
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Whether the query getting the combobox options is inFlight
|
|
201
|
+
*/
|
|
202
|
+
isFetching:PropTypes.bool.isRequired,
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* The loading circle color
|
|
206
|
+
*/
|
|
207
|
+
loadingCircleColor:PropTypes.string,
|
|
192
208
|
}
|
|
193
209
|
|
|
194
210
|
Combobox.defaultProps = {
|
|
195
|
-
|
|
211
|
+
loadingCircleColor:'main2',
|
|
196
212
|
}
|
|
197
213
|
|
|
198
214
|
export default Combobox
|
|
@@ -5,11 +5,16 @@
|
|
|
5
5
|
$default-input-padding: .75em .75em .55em;
|
|
6
6
|
$default-padding: 1em;
|
|
7
7
|
$default-margin: 1em;
|
|
8
|
+
$default-loading-circle-displacement: 1em;
|
|
8
9
|
|
|
9
10
|
.#{bem.$base}.combobox,
|
|
10
11
|
.#{bem.$base}.multiple-combobox {
|
|
11
12
|
position: relative;
|
|
12
13
|
|
|
14
|
+
.#{bem.$base}.label {
|
|
15
|
+
margin-bottom: $default-margin
|
|
16
|
+
}
|
|
17
|
+
|
|
13
18
|
.#{bem.$base}.popover {
|
|
14
19
|
width: 100%;
|
|
15
20
|
|
|
@@ -30,6 +35,14 @@ $default-margin: 1em;
|
|
|
30
35
|
}
|
|
31
36
|
|
|
32
37
|
>.input-wrapper {
|
|
38
|
+
position: relative;
|
|
39
|
+
|
|
40
|
+
>.#{bem.$base}.loading-circle {
|
|
41
|
+
position: absolute;
|
|
42
|
+
top: $default-loading-circle-displacement;
|
|
43
|
+
right: $default-loading-circle-displacement;
|
|
44
|
+
}
|
|
45
|
+
|
|
33
46
|
>.input {
|
|
34
47
|
background: var(--light-y);
|
|
35
48
|
border: var(--theme-border-style) var(--dark-y);
|