@automattic/vip-design-system 0.22.0 → 0.23.1
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/build/system/Dropdown/DropdownSeparator.js +1 -1
- package/build/system/NewForm/FormSelect.js +46 -7
- package/build/system/NewForm/FormSelect.stories.js +68 -18
- package/build/system/NewForm/FormSelect.test.js +50 -0
- package/package.json +2 -2
- package/src/system/Dropdown/DropdownSeparator.js +1 -1
- package/src/system/NewForm/FormSelect.js +59 -6
- package/src/system/NewForm/FormSelect.stories.jsx +53 -22
- package/src/system/NewForm/FormSelect.test.js +25 -0
|
@@ -9,7 +9,7 @@ var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends")
|
|
|
9
9
|
|
|
10
10
|
var _objectWithoutPropertiesLoose2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutPropertiesLoose"));
|
|
11
11
|
|
|
12
|
-
var _react =
|
|
12
|
+
var _react = _interopRequireWildcard(require("react"));
|
|
13
13
|
|
|
14
14
|
var _propTypes = _interopRequireDefault(require("prop-types"));
|
|
15
15
|
|
|
@@ -21,7 +21,13 @@ var _FormSelectContent = require("./FormSelectContent");
|
|
|
21
21
|
|
|
22
22
|
var _jsxRuntime = require("theme-ui/jsx-runtime");
|
|
23
23
|
|
|
24
|
-
var _excluded = ["isInline", "placeholder", "forLabel", "options", "label"]
|
|
24
|
+
var _excluded = ["isInline", "placeholder", "forLabel", "options", "label", "getOptionLabel", "getOptionValue", "onChange"],
|
|
25
|
+
_excluded2 = ["options"];
|
|
26
|
+
|
|
27
|
+
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
|
|
28
|
+
|
|
29
|
+
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { "default": obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj["default"] = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
|
|
30
|
+
|
|
25
31
|
var MAX_SUGGESTED_OPTIONS = 15;
|
|
26
32
|
var isDev = process.env.NODE_ENV !== 'production';
|
|
27
33
|
var defaultStyles = {
|
|
@@ -69,6 +75,9 @@ var FormSelect = /*#__PURE__*/_react["default"].forwardRef(function (_ref2, forw
|
|
|
69
75
|
forLabel = _ref2.forLabel,
|
|
70
76
|
options = _ref2.options,
|
|
71
77
|
label = _ref2.label,
|
|
78
|
+
getOptionLabel = _ref2.getOptionLabel,
|
|
79
|
+
getOptionValue = _ref2.getOptionValue,
|
|
80
|
+
onChange = _ref2.onChange,
|
|
72
81
|
props = (0, _objectWithoutPropertiesLoose2["default"])(_ref2, _excluded);
|
|
73
82
|
|
|
74
83
|
if (isDev && options.length > MAX_SUGGESTED_OPTIONS) {
|
|
@@ -76,6 +85,32 @@ var FormSelect = /*#__PURE__*/_react["default"].forwardRef(function (_ref2, forw
|
|
|
76
85
|
console.info('For 16 or more items, consider using another component with a typeahead capability.');
|
|
77
86
|
}
|
|
78
87
|
|
|
88
|
+
var getAllOptions = (0, _react.useMemo)(function () {
|
|
89
|
+
return [].concat(options.filter(function (option) {
|
|
90
|
+
return !option.options;
|
|
91
|
+
}), options.filter(function (option) {
|
|
92
|
+
return option.options;
|
|
93
|
+
}).map(function (option) {
|
|
94
|
+
return option.options;
|
|
95
|
+
})).reduce(function (a, b) {
|
|
96
|
+
return a.concat(b);
|
|
97
|
+
}, []);
|
|
98
|
+
}, [options]);
|
|
99
|
+
var optionLabel = (0, _react.useCallback)(function (option) {
|
|
100
|
+
return getOptionLabel ? getOptionLabel(option) : option.label;
|
|
101
|
+
}, [getOptionLabel]);
|
|
102
|
+
var optionValue = (0, _react.useCallback)(function (option) {
|
|
103
|
+
return getOptionValue ? getOptionValue(option) : option.value;
|
|
104
|
+
}, [getOptionValue]);
|
|
105
|
+
var getOptionByValue = (0, _react.useCallback)(function (value) {
|
|
106
|
+
return getAllOptions.find(function (option) {
|
|
107
|
+
return "" + optionValue(option) === "" + value;
|
|
108
|
+
});
|
|
109
|
+
}, [getAllOptions, optionValue]);
|
|
110
|
+
var onValueChange = (0, _react.useCallback)(function (event) {
|
|
111
|
+
return onChange ? onChange(getOptionByValue(event.target.value), event) : getOptionByValue(event.target.value);
|
|
112
|
+
}, [onChange, getOptionByValue]);
|
|
113
|
+
|
|
79
114
|
var SelectLabel = function SelectLabel() {
|
|
80
115
|
return (0, _jsxRuntime.jsx)(_Label.Label, {
|
|
81
116
|
htmlFor: forLabel || props.id,
|
|
@@ -89,16 +124,17 @@ var FormSelect = /*#__PURE__*/_react["default"].forwardRef(function (_ref2, forw
|
|
|
89
124
|
isInline: inlineLabel,
|
|
90
125
|
label: inlineLabel ? (0, _jsxRuntime.jsx)(SelectLabel, {}) : null,
|
|
91
126
|
children: [(0, _jsxRuntime.jsxs)("select", (0, _extends2["default"])({
|
|
127
|
+
onChange: onValueChange,
|
|
92
128
|
ref: forwardRef,
|
|
93
129
|
sx: defaultStyles
|
|
94
130
|
}, props, {
|
|
95
131
|
children: [placeholder && (0, _jsxRuntime.jsx)("option", {
|
|
96
132
|
children: placeholder
|
|
97
133
|
}), options.map(function (_ref3) {
|
|
98
|
-
var
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
return value ? renderOption(optionLabel, value) : renderGroup(optionLabel, groupOptions);
|
|
134
|
+
var groupOptions = _ref3.options,
|
|
135
|
+
option = (0, _objectWithoutPropertiesLoose2["default"])(_ref3, _excluded2);
|
|
136
|
+
var value = optionValue(option);
|
|
137
|
+
return value ? renderOption(optionLabel(option), value) : renderGroup(optionLabel(option), groupOptions);
|
|
102
138
|
})]
|
|
103
139
|
})), (0, _jsxRuntime.jsx)(_FormSelectArrow.FormSelectArrow, {})]
|
|
104
140
|
})]
|
|
@@ -112,6 +148,9 @@ FormSelect.propTypes = {
|
|
|
112
148
|
forLabel: _propTypes["default"].string,
|
|
113
149
|
placeholder: _propTypes["default"].string,
|
|
114
150
|
label: _propTypes["default"].string,
|
|
115
|
-
options: _propTypes["default"].array
|
|
151
|
+
options: _propTypes["default"].array,
|
|
152
|
+
getOptionLabel: _propTypes["default"].func,
|
|
153
|
+
getOptionValue: _propTypes["default"].func,
|
|
154
|
+
onChange: _propTypes["default"].func
|
|
116
155
|
};
|
|
117
156
|
FormSelect.displayName = 'FormSelect';
|
|
@@ -3,17 +3,19 @@
|
|
|
3
3
|
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
4
|
|
|
5
5
|
exports.__esModule = true;
|
|
6
|
-
exports["default"] = exports.WithGroup = exports.IsInline = exports.Default = void 0;
|
|
6
|
+
exports["default"] = exports.WithOptionLabelAndValue = exports.WithOnChange = exports.WithGroup = exports.IsInline = exports.Default = void 0;
|
|
7
7
|
|
|
8
8
|
var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
|
|
9
9
|
|
|
10
10
|
var _objectWithoutPropertiesLoose2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutPropertiesLoose"));
|
|
11
11
|
|
|
12
|
+
var _react = require("react");
|
|
13
|
+
|
|
12
14
|
var Form = _interopRequireWildcard(require("."));
|
|
13
15
|
|
|
14
16
|
var _jsxRuntime = require("theme-ui/jsx-runtime");
|
|
15
17
|
|
|
16
|
-
var _excluded = ["label", "width"];
|
|
18
|
+
var _excluded = ["label", "width", "onChange"];
|
|
17
19
|
|
|
18
20
|
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
|
|
19
21
|
|
|
@@ -52,6 +54,13 @@ var options = [{
|
|
|
52
54
|
}, {
|
|
53
55
|
value: 'vanilla',
|
|
54
56
|
label: 'Vanilla'
|
|
57
|
+
}];
|
|
58
|
+
var groupedOptions = [{
|
|
59
|
+
label: 'Group name',
|
|
60
|
+
options: options
|
|
61
|
+
}, {
|
|
62
|
+
label: 'Another Group name',
|
|
63
|
+
options: options
|
|
55
64
|
}]; // eslint-disable-next-line react/prop-types
|
|
56
65
|
|
|
57
66
|
var DefaultComponent = function DefaultComponent(_ref) {
|
|
@@ -59,6 +68,7 @@ var DefaultComponent = function DefaultComponent(_ref) {
|
|
|
59
68
|
label = _ref$label === void 0 ? 'Label' : _ref$label,
|
|
60
69
|
_ref$width = _ref.width,
|
|
61
70
|
width = _ref$width === void 0 ? 250 : _ref$width,
|
|
71
|
+
onChange = _ref.onChange,
|
|
62
72
|
rest = (0, _objectWithoutPropertiesLoose2["default"])(_ref, _excluded);
|
|
63
73
|
return (0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
|
|
64
74
|
children: [(0, _jsxRuntime.jsxs)("p", {
|
|
@@ -79,7 +89,8 @@ var DefaultComponent = function DefaultComponent(_ref) {
|
|
|
79
89
|
},
|
|
80
90
|
children: (0, _jsxRuntime.jsx)(Form.Select, (0, _extends2["default"])({
|
|
81
91
|
id: "form-select",
|
|
82
|
-
label: label
|
|
92
|
+
label: label,
|
|
93
|
+
onChange: onChange
|
|
83
94
|
}, rest))
|
|
84
95
|
})
|
|
85
96
|
})]
|
|
@@ -96,13 +107,7 @@ var WithGroup = DefaultComponent.bind({});
|
|
|
96
107
|
exports.WithGroup = WithGroup;
|
|
97
108
|
WithGroup.args = {
|
|
98
109
|
label: 'Group Label',
|
|
99
|
-
options: [
|
|
100
|
-
label: 'Group name',
|
|
101
|
-
options: options
|
|
102
|
-
}, {
|
|
103
|
-
label: 'Another Group name',
|
|
104
|
-
options: options
|
|
105
|
-
}]
|
|
110
|
+
options: [].concat(options, groupedOptions)
|
|
106
111
|
};
|
|
107
112
|
var IsInline = DefaultComponent.bind({});
|
|
108
113
|
exports.IsInline = IsInline;
|
|
@@ -110,11 +115,56 @@ IsInline.args = {
|
|
|
110
115
|
label: 'Inline Select',
|
|
111
116
|
isInline: true,
|
|
112
117
|
width: '100%',
|
|
113
|
-
options:
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
118
|
+
options: groupedOptions
|
|
119
|
+
};
|
|
120
|
+
var WithOptionLabelAndValue = DefaultComponent.bind({});
|
|
121
|
+
exports.WithOptionLabelAndValue = WithOptionLabelAndValue;
|
|
122
|
+
WithOptionLabelAndValue.args = {
|
|
123
|
+
label: 'Select with getOptionLabel / getOptionValue',
|
|
124
|
+
width: '100%',
|
|
125
|
+
options: options.map(function (_ref2) {
|
|
126
|
+
var label = _ref2.label,
|
|
127
|
+
value = _ref2.value;
|
|
128
|
+
return {
|
|
129
|
+
name: label,
|
|
130
|
+
id: value
|
|
131
|
+
};
|
|
132
|
+
}),
|
|
133
|
+
getOptionLabel: function getOptionLabel(option) {
|
|
134
|
+
return option.name;
|
|
135
|
+
},
|
|
136
|
+
getOptionValue: function getOptionValue(option) {
|
|
137
|
+
return option.id;
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
var WithOnChange = function WithOnChange() {
|
|
142
|
+
var _useState = (0, _react.useState)(null),
|
|
143
|
+
option = _useState[0],
|
|
144
|
+
setOption = _useState[1];
|
|
145
|
+
|
|
146
|
+
var onChange = (0, _react.useCallback)(function (val, event) {
|
|
147
|
+
return setOption({
|
|
148
|
+
obj: val,
|
|
149
|
+
eventValue: event.target.value
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
var args = {
|
|
153
|
+
label: 'Select with onChange',
|
|
154
|
+
placeholder: '- Select -',
|
|
155
|
+
width: '100%',
|
|
156
|
+
onChange: onChange,
|
|
157
|
+
options: [].concat(options, groupedOptions)
|
|
158
|
+
};
|
|
159
|
+
return (0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
|
|
160
|
+
children: [(0, _jsxRuntime.jsx)(DefaultComponent, (0, _extends2["default"])({
|
|
161
|
+
onChange: onChange
|
|
162
|
+
}, args)), option && (0, _jsxRuntime.jsxs)("p", {
|
|
163
|
+
children: ["Object to JSON: ", JSON.stringify(option.obj)]
|
|
164
|
+
}), option && (0, _jsxRuntime.jsxs)("p", {
|
|
165
|
+
children: ["Original Event Value: ", option.eventValue]
|
|
166
|
+
})]
|
|
167
|
+
});
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
exports.WithOnChange = WithOnChange;
|
|
@@ -140,4 +140,54 @@ describe('<FormSelect />', function () {
|
|
|
140
140
|
}
|
|
141
141
|
}, _callee3);
|
|
142
142
|
})));
|
|
143
|
+
it('renders the FormSelect component when getOptionLabel and getOptionValue', /*#__PURE__*/(0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee4() {
|
|
144
|
+
var props, _render4, container;
|
|
145
|
+
|
|
146
|
+
return _regenerator["default"].wrap(function _callee4$(_context4) {
|
|
147
|
+
while (1) {
|
|
148
|
+
switch (_context4.prev = _context4.next) {
|
|
149
|
+
case 0:
|
|
150
|
+
props = (0, _extends2["default"])({}, defaultProps, {
|
|
151
|
+
options: options.map(function (_ref5) {
|
|
152
|
+
var label = _ref5.label,
|
|
153
|
+
value = _ref5.value;
|
|
154
|
+
return {
|
|
155
|
+
name: label,
|
|
156
|
+
id: value
|
|
157
|
+
};
|
|
158
|
+
}),
|
|
159
|
+
getOptionLabel: function getOptionLabel(option) {
|
|
160
|
+
return option.name;
|
|
161
|
+
},
|
|
162
|
+
getOptionValue: function getOptionValue(option) {
|
|
163
|
+
return option.id;
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
_render4 = (0, _react.render)((0, _jsxRuntime.jsx)(_FormSelect.FormSelect, (0, _extends2["default"])({
|
|
167
|
+
id: "my_desert_list"
|
|
168
|
+
}, props))), container = _render4.container;
|
|
169
|
+
expect(_react.screen.getByLabelText(defaultProps.label)).toBeInTheDocument();
|
|
170
|
+
expect(_react.screen.getByRole('combobox')).toBeInTheDocument();
|
|
171
|
+
expect(_react.screen.getAllByRole('option')).toHaveLength(3);
|
|
172
|
+
expect(_react.screen.getByText(options[0].label)).toBeInTheDocument();
|
|
173
|
+
expect(_react.screen.getByText(options[1].label)).toBeInTheDocument();
|
|
174
|
+
expect(_react.screen.getByText(options[2].label)).toBeInTheDocument();
|
|
175
|
+
expect(_react.screen.queryByRole('group')).not.toBeInTheDocument(); // Check for accessibility issues
|
|
176
|
+
|
|
177
|
+
_context4.t0 = expect;
|
|
178
|
+
_context4.next = 12;
|
|
179
|
+
return (0, _jestAxe.axe)(container);
|
|
180
|
+
|
|
181
|
+
case 12:
|
|
182
|
+
_context4.t1 = _context4.sent;
|
|
183
|
+
_context4.next = 15;
|
|
184
|
+
return (0, _context4.t0)(_context4.t1).toHaveNoViolations();
|
|
185
|
+
|
|
186
|
+
case 15:
|
|
187
|
+
case "end":
|
|
188
|
+
return _context4.stop();
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}, _callee4);
|
|
192
|
+
})));
|
|
143
193
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@automattic/vip-design-system",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.23.1",
|
|
4
4
|
"main": "build/system/index.js",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"build-storybook": "build-storybook",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"dependencies": {
|
|
23
23
|
"@radix-ui/react-checkbox": "^1.0.0",
|
|
24
24
|
"@radix-ui/react-dialog": "^1.0.0",
|
|
25
|
-
"@radix-ui/react-dropdown-menu": "^1.0.
|
|
25
|
+
"@radix-ui/react-dropdown-menu": "^1.0.1-rc.6",
|
|
26
26
|
"@radix-ui/react-radio-group": "^1.0.0",
|
|
27
27
|
"@radix-ui/react-switch": "^1.0.0",
|
|
28
28
|
"@radix-ui/react-tooltip": "^1.0.0",
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* External dependencies
|
|
5
5
|
*/
|
|
6
|
-
import React from 'react';
|
|
6
|
+
import React, { useCallback, useMemo } from 'react';
|
|
7
7
|
import PropTypes from 'prop-types';
|
|
8
8
|
import { Label } from '../Form/Label';
|
|
9
9
|
|
|
@@ -47,7 +47,20 @@ const renderGroup = ( groupLabel, groupOptions ) => {
|
|
|
47
47
|
};
|
|
48
48
|
|
|
49
49
|
const FormSelect = React.forwardRef(
|
|
50
|
-
(
|
|
50
|
+
(
|
|
51
|
+
{
|
|
52
|
+
isInline,
|
|
53
|
+
placeholder,
|
|
54
|
+
forLabel,
|
|
55
|
+
options,
|
|
56
|
+
label,
|
|
57
|
+
getOptionLabel,
|
|
58
|
+
getOptionValue,
|
|
59
|
+
onChange,
|
|
60
|
+
...props
|
|
61
|
+
},
|
|
62
|
+
forwardRef
|
|
63
|
+
) => {
|
|
51
64
|
if ( isDev && options.length > MAX_SUGGESTED_OPTIONS ) {
|
|
52
65
|
// eslint-disable-next-line no-console
|
|
53
66
|
console.info(
|
|
@@ -55,7 +68,40 @@ const FormSelect = React.forwardRef(
|
|
|
55
68
|
);
|
|
56
69
|
}
|
|
57
70
|
|
|
71
|
+
const getAllOptions = useMemo(
|
|
72
|
+
() =>
|
|
73
|
+
[
|
|
74
|
+
...options.filter( option => ! option.options ),
|
|
75
|
+
...options.filter( option => option.options ).map( option => option.options ),
|
|
76
|
+
].reduce( ( a, b ) => a.concat( b ), [] ),
|
|
77
|
+
[ options ]
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
const optionLabel = useCallback(
|
|
81
|
+
option => ( getOptionLabel ? getOptionLabel( option ) : option.label ),
|
|
82
|
+
[ getOptionLabel ]
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
const optionValue = useCallback(
|
|
86
|
+
option => ( getOptionValue ? getOptionValue( option ) : option.value ),
|
|
87
|
+
[ getOptionValue ]
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
const getOptionByValue = useCallback(
|
|
91
|
+
value => getAllOptions.find( option => `${ optionValue( option ) }` === `${ value }` ),
|
|
92
|
+
[ getAllOptions, optionValue ]
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
const onValueChange = useCallback(
|
|
96
|
+
event =>
|
|
97
|
+
onChange
|
|
98
|
+
? onChange( getOptionByValue( event.target.value ), event )
|
|
99
|
+
: getOptionByValue( event.target.value ),
|
|
100
|
+
[ onChange, getOptionByValue ]
|
|
101
|
+
);
|
|
102
|
+
|
|
58
103
|
const SelectLabel = () => <Label htmlFor={ forLabel || props.id }>{ label }</Label>;
|
|
104
|
+
|
|
59
105
|
const inlineLabel = !! ( isInline && label );
|
|
60
106
|
|
|
61
107
|
return (
|
|
@@ -63,11 +109,15 @@ const FormSelect = React.forwardRef(
|
|
|
63
109
|
{ label && ! isInline && <SelectLabel /> }
|
|
64
110
|
|
|
65
111
|
<FormSelectContent isInline={ inlineLabel } label={ inlineLabel ? <SelectLabel /> : null }>
|
|
66
|
-
<select ref={ forwardRef } sx={ defaultStyles } { ...props }>
|
|
112
|
+
<select onChange={ onValueChange } ref={ forwardRef } sx={ defaultStyles } { ...props }>
|
|
67
113
|
{ placeholder && <option>{ placeholder }</option> }
|
|
68
|
-
{ options.map( ( {
|
|
69
|
-
|
|
70
|
-
|
|
114
|
+
{ options.map( ( { options: groupOptions, ...option } ) => {
|
|
115
|
+
const value = optionValue( option );
|
|
116
|
+
|
|
117
|
+
return value
|
|
118
|
+
? renderOption( optionLabel( option ), value )
|
|
119
|
+
: renderGroup( optionLabel( option ), groupOptions );
|
|
120
|
+
} ) }
|
|
71
121
|
</select>
|
|
72
122
|
|
|
73
123
|
<FormSelectArrow />
|
|
@@ -84,6 +134,9 @@ FormSelect.propTypes = {
|
|
|
84
134
|
placeholder: PropTypes.string,
|
|
85
135
|
label: PropTypes.string,
|
|
86
136
|
options: PropTypes.array,
|
|
137
|
+
getOptionLabel: PropTypes.func,
|
|
138
|
+
getOptionValue: PropTypes.func,
|
|
139
|
+
onChange: PropTypes.func,
|
|
87
140
|
};
|
|
88
141
|
|
|
89
142
|
FormSelect.displayName = 'FormSelect';
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* Internal dependencies
|
|
5
5
|
*/
|
|
6
|
+
import { useCallback, useState } from 'react';
|
|
6
7
|
import * as Form from '.';
|
|
7
8
|
|
|
8
9
|
export default {
|
|
@@ -25,8 +26,19 @@ const options = [
|
|
|
25
26
|
{ value: 'vanilla', label: 'Vanilla' },
|
|
26
27
|
];
|
|
27
28
|
|
|
29
|
+
const groupedOptions = [
|
|
30
|
+
{
|
|
31
|
+
label: 'Group name',
|
|
32
|
+
options: options,
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
label: 'Another Group name',
|
|
36
|
+
options: options,
|
|
37
|
+
},
|
|
38
|
+
];
|
|
39
|
+
|
|
28
40
|
// eslint-disable-next-line react/prop-types
|
|
29
|
-
const DefaultComponent = ( { label = 'Label', width = 250, ...rest } ) => (
|
|
41
|
+
const DefaultComponent = ( { label = 'Label', width = 250, onChange, ...rest } ) => (
|
|
30
42
|
<>
|
|
31
43
|
<p>
|
|
32
44
|
This is a simple wrapper at the top of a browser default select component. This component
|
|
@@ -49,7 +61,7 @@ const DefaultComponent = ( { label = 'Label', width = 250, ...rest } ) => (
|
|
|
49
61
|
</p>
|
|
50
62
|
<Form.Root>
|
|
51
63
|
<div sx={ { width } }>
|
|
52
|
-
<Form.Select id="form-select" label={ label } { ...rest } />
|
|
64
|
+
<Form.Select id="form-select" label={ label } onChange={ onChange } { ...rest } />
|
|
53
65
|
</div>
|
|
54
66
|
</Form.Root>
|
|
55
67
|
</>
|
|
@@ -65,16 +77,7 @@ export const WithGroup = DefaultComponent.bind( {} );
|
|
|
65
77
|
|
|
66
78
|
WithGroup.args = {
|
|
67
79
|
label: 'Group Label',
|
|
68
|
-
options: [
|
|
69
|
-
{
|
|
70
|
-
label: 'Group name',
|
|
71
|
-
options: options,
|
|
72
|
-
},
|
|
73
|
-
{
|
|
74
|
-
label: 'Another Group name',
|
|
75
|
-
options: options,
|
|
76
|
-
},
|
|
77
|
-
],
|
|
80
|
+
options: [ ...options, ...groupedOptions ],
|
|
78
81
|
};
|
|
79
82
|
|
|
80
83
|
export const IsInline = DefaultComponent.bind( {} );
|
|
@@ -83,14 +86,42 @@ IsInline.args = {
|
|
|
83
86
|
label: 'Inline Select',
|
|
84
87
|
isInline: true,
|
|
85
88
|
width: '100%',
|
|
86
|
-
options:
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
89
|
+
options: groupedOptions,
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
export const WithOptionLabelAndValue = DefaultComponent.bind( {} );
|
|
93
|
+
|
|
94
|
+
WithOptionLabelAndValue.args = {
|
|
95
|
+
label: 'Select with getOptionLabel / getOptionValue',
|
|
96
|
+
width: '100%',
|
|
97
|
+
options: options.map( ( { label, value } ) => ( {
|
|
98
|
+
name: label,
|
|
99
|
+
id: value,
|
|
100
|
+
} ) ),
|
|
101
|
+
getOptionLabel: option => option.name,
|
|
102
|
+
getOptionValue: option => option.id,
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
export const WithOnChange = () => {
|
|
106
|
+
const [ option, setOption ] = useState( null );
|
|
107
|
+
|
|
108
|
+
const onChange = useCallback( ( val, event ) =>
|
|
109
|
+
setOption( { obj: val, eventValue: event.target.value } )
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
const args = {
|
|
113
|
+
label: 'Select with onChange',
|
|
114
|
+
placeholder: '- Select -',
|
|
115
|
+
width: '100%',
|
|
116
|
+
onChange,
|
|
117
|
+
options: [ ...options, ...groupedOptions ],
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
return (
|
|
121
|
+
<>
|
|
122
|
+
<DefaultComponent onChange={ onChange } { ...args } />
|
|
123
|
+
{ option && <p>Object to JSON: { JSON.stringify( option.obj ) }</p> }
|
|
124
|
+
{ option && <p>Original Event Value: { option.eventValue }</p> }
|
|
125
|
+
</>
|
|
126
|
+
);
|
|
96
127
|
};
|
|
@@ -69,4 +69,29 @@ describe( '<FormSelect />', () => {
|
|
|
69
69
|
// Check for accessibility issues
|
|
70
70
|
await expect( await axe( container ) ).toHaveNoViolations();
|
|
71
71
|
} );
|
|
72
|
+
|
|
73
|
+
it( 'renders the FormSelect component when getOptionLabel and getOptionValue', async () => {
|
|
74
|
+
const props = {
|
|
75
|
+
...defaultProps,
|
|
76
|
+
options: options.map( ( { label, value } ) => ( {
|
|
77
|
+
name: label,
|
|
78
|
+
id: value,
|
|
79
|
+
} ) ),
|
|
80
|
+
getOptionLabel: option => option.name,
|
|
81
|
+
getOptionValue: option => option.id,
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const { container } = render( <FormSelect id="my_desert_list" { ...props } /> );
|
|
85
|
+
|
|
86
|
+
expect( screen.getByLabelText( defaultProps.label ) ).toBeInTheDocument();
|
|
87
|
+
expect( screen.getByRole( 'combobox' ) ).toBeInTheDocument();
|
|
88
|
+
expect( screen.getAllByRole( 'option' ) ).toHaveLength( 3 );
|
|
89
|
+
expect( screen.getByText( options[ 0 ].label ) ).toBeInTheDocument();
|
|
90
|
+
expect( screen.getByText( options[ 1 ].label ) ).toBeInTheDocument();
|
|
91
|
+
expect( screen.getByText( options[ 2 ].label ) ).toBeInTheDocument();
|
|
92
|
+
expect( screen.queryByRole( 'group' ) ).not.toBeInTheDocument();
|
|
93
|
+
|
|
94
|
+
// Check for accessibility issues
|
|
95
|
+
await expect( await axe( container ) ).toHaveNoViolations();
|
|
96
|
+
} );
|
|
72
97
|
} );
|