@automattic/vip-design-system 0.28.5 → 0.28.6
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/NewForm/FormSelect.js +11 -1
- package/build/system/NewForm/FormSelect.stories.js +10 -1
- package/build/system/NewForm/FormSelect.test.js +80 -45
- package/build/system/Spinner/Spinner.js +1 -1
- package/package.json +1 -1
- package/src/system/NewForm/FormSelect.js +11 -1
- package/src/system/NewForm/FormSelect.stories.jsx +9 -0
- package/src/system/NewForm/FormSelect.test.js +21 -0
- package/src/system/Spinner/Spinner.js +1 -1
|
@@ -21,9 +21,11 @@ var _FormSelectContent = require("./FormSelectContent");
|
|
|
21
21
|
|
|
22
22
|
var _Input = require("../Form/Input.styles");
|
|
23
23
|
|
|
24
|
+
var _Form = require("../Form");
|
|
25
|
+
|
|
24
26
|
var _jsxRuntime = require("theme-ui/jsx-runtime");
|
|
25
27
|
|
|
26
|
-
var _excluded = ["isInline", "placeholder", "forLabel", "options", "required", "label", "getOptionLabel", "getOptionValue", "onChange"],
|
|
28
|
+
var _excluded = ["isInline", "placeholder", "forLabel", "options", "required", "label", "getOptionLabel", "getOptionValue", "onChange", "hasError", "errorMessage"],
|
|
27
29
|
_excluded2 = ["options"];
|
|
28
30
|
|
|
29
31
|
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); }
|
|
@@ -70,6 +72,8 @@ var FormSelect = /*#__PURE__*/_react["default"].forwardRef(function (_ref2, forw
|
|
|
70
72
|
getOptionLabel = _ref2.getOptionLabel,
|
|
71
73
|
getOptionValue = _ref2.getOptionValue,
|
|
72
74
|
onChange = _ref2.onChange,
|
|
75
|
+
hasError = _ref2.hasError,
|
|
76
|
+
errorMessage = _ref2.errorMessage,
|
|
73
77
|
props = (0, _objectWithoutPropertiesLoose2["default"])(_ref2, _excluded);
|
|
74
78
|
|
|
75
79
|
if (isDev && options.length > MAX_SUGGESTED_OPTIONS) {
|
|
@@ -129,6 +133,10 @@ var FormSelect = /*#__PURE__*/_react["default"].forwardRef(function (_ref2, forw
|
|
|
129
133
|
return groupOptions ? renderGroup(optionLabel(option), groupOptions) : renderOption(optionLabel(option), optionValue(option));
|
|
130
134
|
})]
|
|
131
135
|
})), (0, _jsxRuntime.jsx)(_FormSelectArrow.FormSelectArrow, {})]
|
|
136
|
+
}), hasError && errorMessage && (0, _jsxRuntime.jsx)(_Form.Validation, {
|
|
137
|
+
isValid: false,
|
|
138
|
+
describedId: forLabel,
|
|
139
|
+
children: errorMessage
|
|
132
140
|
})]
|
|
133
141
|
});
|
|
134
142
|
});
|
|
@@ -144,6 +152,8 @@ FormSelect.propTypes = {
|
|
|
144
152
|
options: _propTypes["default"].array,
|
|
145
153
|
getOptionLabel: _propTypes["default"].func,
|
|
146
154
|
getOptionValue: _propTypes["default"].func,
|
|
155
|
+
hasError: _propTypes["default"].bool,
|
|
156
|
+
errorMessage: _propTypes["default"].string,
|
|
147
157
|
onChange: _propTypes["default"].func
|
|
148
158
|
};
|
|
149
159
|
FormSelect.displayName = 'FormSelect';
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
4
|
|
|
5
5
|
exports.__esModule = true;
|
|
6
|
-
exports["default"] = exports.WithOptionLabelAndValue = exports.WithOnChange = exports.WithGroup = exports.IsInline = exports.Default = void 0;
|
|
6
|
+
exports["default"] = exports.WithOptionLabelAndValue = exports.WithOnChange = exports.WithGroup = exports.WithErrors = exports.IsInline = exports.Default = void 0;
|
|
7
7
|
|
|
8
8
|
var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
|
|
9
9
|
|
|
@@ -106,6 +106,15 @@ Default.args = {
|
|
|
106
106
|
required: true,
|
|
107
107
|
options: options
|
|
108
108
|
};
|
|
109
|
+
var WithErrors = DefaultComponent.bind({});
|
|
110
|
+
exports.WithErrors = WithErrors;
|
|
111
|
+
WithErrors.args = {
|
|
112
|
+
placeholder: '- Select -',
|
|
113
|
+
required: true,
|
|
114
|
+
hasError: true,
|
|
115
|
+
errorMessage: 'This is an error message',
|
|
116
|
+
options: options
|
|
117
|
+
};
|
|
109
118
|
var WithGroup = DefaultComponent.bind({});
|
|
110
119
|
exports.WithGroup = WithGroup;
|
|
111
120
|
WithGroup.args = {
|
|
@@ -79,38 +79,42 @@ describe('<FormSelect />', function () {
|
|
|
79
79
|
}
|
|
80
80
|
}, _callee);
|
|
81
81
|
})));
|
|
82
|
-
it('renders the FormSelect component with
|
|
83
|
-
var _render2, container;
|
|
82
|
+
it('renders the FormSelect component with an error if hasError=true', /*#__PURE__*/(0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee2() {
|
|
83
|
+
var errorMessage, _render2, container;
|
|
84
84
|
|
|
85
85
|
return _regenerator["default"].wrap(function _callee2$(_context2) {
|
|
86
86
|
while (1) {
|
|
87
87
|
switch (_context2.prev = _context2.next) {
|
|
88
88
|
case 0:
|
|
89
|
+
errorMessage = 'This is an error message';
|
|
89
90
|
_render2 = (0, _react.render)((0, _jsxRuntime.jsx)(_FormSelect.FormSelect, (0, _extends2["default"])({
|
|
90
|
-
id: "my_desert_list"
|
|
91
|
-
|
|
91
|
+
id: "my_desert_list",
|
|
92
|
+
hasError: true,
|
|
93
|
+
errorMessage: errorMessage
|
|
94
|
+
}, defaultProps))), container = _render2.container;
|
|
92
95
|
expect(_react.screen.getByLabelText(defaultProps.label)).toBeInTheDocument();
|
|
93
96
|
expect(_react.screen.getByRole('combobox')).toBeInTheDocument();
|
|
94
97
|
expect(_react.screen.getAllByRole('option')).toHaveLength(3);
|
|
95
|
-
expect(_react.screen.
|
|
98
|
+
expect(_react.screen.queryByRole('group')).not.toBeInTheDocument();
|
|
99
|
+
expect(_react.screen.getByText(errorMessage)).toBeInTheDocument(); // Check for accessibility issues
|
|
96
100
|
|
|
97
101
|
_context2.t0 = expect;
|
|
98
|
-
_context2.next =
|
|
102
|
+
_context2.next = 10;
|
|
99
103
|
return (0, _jestAxe.axe)(container);
|
|
100
104
|
|
|
101
|
-
case
|
|
105
|
+
case 10:
|
|
102
106
|
_context2.t1 = _context2.sent;
|
|
103
|
-
_context2.next =
|
|
107
|
+
_context2.next = 13;
|
|
104
108
|
return (0, _context2.t0)(_context2.t1).toHaveNoViolations();
|
|
105
109
|
|
|
106
|
-
case
|
|
110
|
+
case 13:
|
|
107
111
|
case "end":
|
|
108
112
|
return _context2.stop();
|
|
109
113
|
}
|
|
110
114
|
}
|
|
111
115
|
}, _callee2);
|
|
112
116
|
})));
|
|
113
|
-
it('renders the FormSelect component when
|
|
117
|
+
it('renders the FormSelect component with optgroup when options are grouped', /*#__PURE__*/(0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee3() {
|
|
114
118
|
var _render3, container;
|
|
115
119
|
|
|
116
120
|
return _regenerator["default"].wrap(function _callee3$(_context3) {
|
|
@@ -118,74 +122,105 @@ describe('<FormSelect />', function () {
|
|
|
118
122
|
switch (_context3.prev = _context3.next) {
|
|
119
123
|
case 0:
|
|
120
124
|
_render3 = (0, _react.render)((0, _jsxRuntime.jsx)(_FormSelect.FormSelect, (0, _extends2["default"])({
|
|
121
|
-
id: "my_desert_list"
|
|
122
|
-
|
|
123
|
-
}, defaultProps))), container = _render3.container;
|
|
125
|
+
id: "my_desert_list"
|
|
126
|
+
}, groupedProps))), container = _render3.container;
|
|
124
127
|
expect(_react.screen.getByLabelText(defaultProps.label)).toBeInTheDocument();
|
|
125
|
-
expect(_react.screen.getByRole('combobox')).toBeInTheDocument();
|
|
128
|
+
expect(_react.screen.getByRole('combobox')).toBeInTheDocument();
|
|
129
|
+
expect(_react.screen.getAllByRole('option')).toHaveLength(3);
|
|
130
|
+
expect(_react.screen.getAllByRole('group')).toHaveLength(2); // Check for accessibility issues
|
|
126
131
|
|
|
127
132
|
_context3.t0 = expect;
|
|
128
|
-
_context3.next =
|
|
133
|
+
_context3.next = 8;
|
|
129
134
|
return (0, _jestAxe.axe)(container);
|
|
130
135
|
|
|
131
|
-
case
|
|
136
|
+
case 8:
|
|
132
137
|
_context3.t1 = _context3.sent;
|
|
133
|
-
_context3.next =
|
|
138
|
+
_context3.next = 11;
|
|
134
139
|
return (0, _context3.t0)(_context3.t1).toHaveNoViolations();
|
|
135
140
|
|
|
136
|
-
case
|
|
141
|
+
case 11:
|
|
137
142
|
case "end":
|
|
138
143
|
return _context3.stop();
|
|
139
144
|
}
|
|
140
145
|
}
|
|
141
146
|
}, _callee3);
|
|
142
147
|
})));
|
|
143
|
-
it('renders the FormSelect
|
|
144
|
-
var
|
|
148
|
+
it('renders the FormSelect component when isInline is true', /*#__PURE__*/(0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee4() {
|
|
149
|
+
var _render4, container;
|
|
145
150
|
|
|
146
151
|
return _regenerator["default"].wrap(function _callee4$(_context4) {
|
|
147
152
|
while (1) {
|
|
148
153
|
switch (_context4.prev = _context4.next) {
|
|
154
|
+
case 0:
|
|
155
|
+
_render4 = (0, _react.render)((0, _jsxRuntime.jsx)(_FormSelect.FormSelect, (0, _extends2["default"])({
|
|
156
|
+
id: "my_desert_list",
|
|
157
|
+
isInline: true
|
|
158
|
+
}, defaultProps))), container = _render4.container;
|
|
159
|
+
expect(_react.screen.getByLabelText(defaultProps.label)).toBeInTheDocument();
|
|
160
|
+
expect(_react.screen.getByRole('combobox')).toBeInTheDocument(); // Check for accessibility issues
|
|
161
|
+
|
|
162
|
+
_context4.t0 = expect;
|
|
163
|
+
_context4.next = 6;
|
|
164
|
+
return (0, _jestAxe.axe)(container);
|
|
165
|
+
|
|
166
|
+
case 6:
|
|
167
|
+
_context4.t1 = _context4.sent;
|
|
168
|
+
_context4.next = 9;
|
|
169
|
+
return (0, _context4.t0)(_context4.t1).toHaveNoViolations();
|
|
170
|
+
|
|
171
|
+
case 9:
|
|
172
|
+
case "end":
|
|
173
|
+
return _context4.stop();
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}, _callee4);
|
|
177
|
+
})));
|
|
178
|
+
it('renders the FormSelect with nullish options', /*#__PURE__*/(0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee5() {
|
|
179
|
+
var nullishOptions, _render5, container;
|
|
180
|
+
|
|
181
|
+
return _regenerator["default"].wrap(function _callee5$(_context5) {
|
|
182
|
+
while (1) {
|
|
183
|
+
switch (_context5.prev = _context5.next) {
|
|
149
184
|
case 0:
|
|
150
185
|
nullishOptions = [].concat(options, [{
|
|
151
186
|
value: null,
|
|
152
187
|
label: 'Empty'
|
|
153
188
|
}]);
|
|
154
|
-
|
|
189
|
+
_render5 = (0, _react.render)((0, _jsxRuntime.jsx)(_FormSelect.FormSelect, (0, _extends2["default"])({
|
|
155
190
|
id: "my_desert_list"
|
|
156
191
|
}, defaultProps, {
|
|
157
192
|
options: nullishOptions
|
|
158
|
-
}))), container =
|
|
193
|
+
}))), container = _render5.container;
|
|
159
194
|
expect(_react.screen.getByLabelText(defaultProps.label)).toBeInTheDocument();
|
|
160
195
|
expect(_react.screen.getByRole('combobox')).toBeInTheDocument(); // Check for accessibility issues
|
|
161
196
|
|
|
162
|
-
|
|
163
|
-
|
|
197
|
+
_context5.t0 = expect;
|
|
198
|
+
_context5.next = 7;
|
|
164
199
|
return (0, _jestAxe.axe)(container);
|
|
165
200
|
|
|
166
201
|
case 7:
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
return (0,
|
|
202
|
+
_context5.t1 = _context5.sent;
|
|
203
|
+
_context5.next = 10;
|
|
204
|
+
return (0, _context5.t0)(_context5.t1).toHaveNoViolations();
|
|
170
205
|
|
|
171
206
|
case 10:
|
|
172
207
|
case "end":
|
|
173
|
-
return
|
|
208
|
+
return _context5.stop();
|
|
174
209
|
}
|
|
175
210
|
}
|
|
176
|
-
},
|
|
211
|
+
}, _callee5);
|
|
177
212
|
})));
|
|
178
|
-
it('renders the FormSelect component when getOptionLabel and getOptionValue', /*#__PURE__*/(0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function
|
|
179
|
-
var props,
|
|
213
|
+
it('renders the FormSelect component when getOptionLabel and getOptionValue', /*#__PURE__*/(0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee6() {
|
|
214
|
+
var props, _render6, container;
|
|
180
215
|
|
|
181
|
-
return _regenerator["default"].wrap(function
|
|
216
|
+
return _regenerator["default"].wrap(function _callee6$(_context6) {
|
|
182
217
|
while (1) {
|
|
183
|
-
switch (
|
|
218
|
+
switch (_context6.prev = _context6.next) {
|
|
184
219
|
case 0:
|
|
185
220
|
props = (0, _extends2["default"])({}, defaultProps, {
|
|
186
|
-
options: options.map(function (
|
|
187
|
-
var label =
|
|
188
|
-
value =
|
|
221
|
+
options: options.map(function (_ref7) {
|
|
222
|
+
var label = _ref7.label,
|
|
223
|
+
value = _ref7.value;
|
|
189
224
|
return {
|
|
190
225
|
name: label,
|
|
191
226
|
id: value
|
|
@@ -198,9 +233,9 @@ describe('<FormSelect />', function () {
|
|
|
198
233
|
return option.id;
|
|
199
234
|
}
|
|
200
235
|
});
|
|
201
|
-
|
|
236
|
+
_render6 = (0, _react.render)((0, _jsxRuntime.jsx)(_FormSelect.FormSelect, (0, _extends2["default"])({
|
|
202
237
|
id: "my_desert_list"
|
|
203
|
-
}, props))), container =
|
|
238
|
+
}, props))), container = _render6.container;
|
|
204
239
|
expect(_react.screen.getByLabelText(defaultProps.label)).toBeInTheDocument();
|
|
205
240
|
expect(_react.screen.getByRole('combobox')).toBeInTheDocument();
|
|
206
241
|
expect(_react.screen.getAllByRole('option')).toHaveLength(3);
|
|
@@ -209,20 +244,20 @@ describe('<FormSelect />', function () {
|
|
|
209
244
|
expect(_react.screen.getByText(options[2].label)).toBeInTheDocument();
|
|
210
245
|
expect(_react.screen.queryByRole('group')).not.toBeInTheDocument(); // Check for accessibility issues
|
|
211
246
|
|
|
212
|
-
|
|
213
|
-
|
|
247
|
+
_context6.t0 = expect;
|
|
248
|
+
_context6.next = 12;
|
|
214
249
|
return (0, _jestAxe.axe)(container);
|
|
215
250
|
|
|
216
251
|
case 12:
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
return (0,
|
|
252
|
+
_context6.t1 = _context6.sent;
|
|
253
|
+
_context6.next = 15;
|
|
254
|
+
return (0, _context6.t0)(_context6.t1).toHaveNoViolations();
|
|
220
255
|
|
|
221
256
|
case 15:
|
|
222
257
|
case "end":
|
|
223
|
-
return
|
|
258
|
+
return _context6.stop();
|
|
224
259
|
}
|
|
225
260
|
}
|
|
226
|
-
},
|
|
261
|
+
}, _callee6);
|
|
227
262
|
})));
|
|
228
263
|
});
|
|
@@ -30,7 +30,7 @@ var Spinner = /*#__PURE__*/_react["default"].forwardRef(function (_ref, forwardR
|
|
|
30
30
|
strokeWidth: 2,
|
|
31
31
|
sx: {
|
|
32
32
|
fill: 'none',
|
|
33
|
-
color: '
|
|
33
|
+
color: 'link'
|
|
34
34
|
},
|
|
35
35
|
className: (0, _classnames["default"])('vip-spinner-component', className),
|
|
36
36
|
ref: forwardRef
|
package/package.json
CHANGED
|
@@ -13,6 +13,7 @@ import { Label } from '../Form/Label';
|
|
|
13
13
|
import { FormSelectArrow } from './FormSelectArrow';
|
|
14
14
|
import { FormSelectContent } from './FormSelectContent';
|
|
15
15
|
import { baseControlStyle } from '../Form/Input.styles';
|
|
16
|
+
import { Validation } from '../Form';
|
|
16
17
|
|
|
17
18
|
const MAX_SUGGESTED_OPTIONS = 15;
|
|
18
19
|
const isDev = process.env.NODE_ENV !== 'production';
|
|
@@ -55,6 +56,8 @@ const FormSelect = React.forwardRef(
|
|
|
55
56
|
getOptionLabel,
|
|
56
57
|
getOptionValue,
|
|
57
58
|
onChange,
|
|
59
|
+
hasError,
|
|
60
|
+
errorMessage,
|
|
58
61
|
...props
|
|
59
62
|
},
|
|
60
63
|
forwardRef
|
|
@@ -119,9 +122,14 @@ const FormSelect = React.forwardRef(
|
|
|
119
122
|
: renderOption( optionLabel( option ), optionValue( option ) )
|
|
120
123
|
) }
|
|
121
124
|
</select>
|
|
122
|
-
|
|
123
125
|
<FormSelectArrow />
|
|
124
126
|
</FormSelectContent>
|
|
127
|
+
|
|
128
|
+
{ hasError && errorMessage && (
|
|
129
|
+
<Validation isValid={ false } describedId={ forLabel }>
|
|
130
|
+
{ errorMessage }
|
|
131
|
+
</Validation>
|
|
132
|
+
) }
|
|
125
133
|
</>
|
|
126
134
|
);
|
|
127
135
|
}
|
|
@@ -137,6 +145,8 @@ FormSelect.propTypes = {
|
|
|
137
145
|
options: PropTypes.array,
|
|
138
146
|
getOptionLabel: PropTypes.func,
|
|
139
147
|
getOptionValue: PropTypes.func,
|
|
148
|
+
hasError: PropTypes.bool,
|
|
149
|
+
errorMessage: PropTypes.string,
|
|
140
150
|
onChange: PropTypes.func,
|
|
141
151
|
};
|
|
142
152
|
|
|
@@ -75,6 +75,15 @@ Default.args = {
|
|
|
75
75
|
options,
|
|
76
76
|
};
|
|
77
77
|
|
|
78
|
+
export const WithErrors = DefaultComponent.bind( {} );
|
|
79
|
+
WithErrors.args = {
|
|
80
|
+
placeholder: '- Select -',
|
|
81
|
+
required: true,
|
|
82
|
+
hasError: true,
|
|
83
|
+
errorMessage: 'This is an error message',
|
|
84
|
+
options,
|
|
85
|
+
};
|
|
86
|
+
|
|
78
87
|
export const WithGroup = DefaultComponent.bind( {} );
|
|
79
88
|
|
|
80
89
|
WithGroup.args = {
|
|
@@ -48,6 +48,27 @@ describe( '<FormSelect />', () => {
|
|
|
48
48
|
await expect( await axe( container ) ).toHaveNoViolations();
|
|
49
49
|
} );
|
|
50
50
|
|
|
51
|
+
it( 'renders the FormSelect component with an error if hasError=true', async () => {
|
|
52
|
+
const errorMessage = 'This is an error message';
|
|
53
|
+
const { container } = render(
|
|
54
|
+
<FormSelect
|
|
55
|
+
id="my_desert_list"
|
|
56
|
+
hasError={ true }
|
|
57
|
+
errorMessage={ errorMessage }
|
|
58
|
+
{ ...defaultProps }
|
|
59
|
+
/>
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
expect( screen.getByLabelText( defaultProps.label ) ).toBeInTheDocument();
|
|
63
|
+
expect( screen.getByRole( 'combobox' ) ).toBeInTheDocument();
|
|
64
|
+
expect( screen.getAllByRole( 'option' ) ).toHaveLength( 3 );
|
|
65
|
+
expect( screen.queryByRole( 'group' ) ).not.toBeInTheDocument();
|
|
66
|
+
expect( screen.getByText( errorMessage ) ).toBeInTheDocument();
|
|
67
|
+
|
|
68
|
+
// Check for accessibility issues
|
|
69
|
+
await expect( await axe( container ) ).toHaveNoViolations();
|
|
70
|
+
} );
|
|
71
|
+
|
|
51
72
|
it( 'renders the FormSelect component with optgroup when options are grouped', async () => {
|
|
52
73
|
const { container } = render( <FormSelect id="my_desert_list" { ...groupedProps } /> );
|
|
53
74
|
|
|
@@ -13,7 +13,7 @@ const Spinner = React.forwardRef( ( { sx, className = null, ...props }, forwardR
|
|
|
13
13
|
strokeWidth={ 2 }
|
|
14
14
|
sx={ {
|
|
15
15
|
fill: 'none',
|
|
16
|
-
color: '
|
|
16
|
+
color: 'link',
|
|
17
17
|
} }
|
|
18
18
|
className={ classNames( 'vip-spinner-component', className ) }
|
|
19
19
|
ref={ forwardRef }
|