@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.
@@ -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 optgroup when options are grouped', /*#__PURE__*/(0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee2() {
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
- }, groupedProps))), container = _render2.container;
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.getAllByRole('group')).toHaveLength(2); // Check for accessibility issues
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 = 8;
102
+ _context2.next = 10;
99
103
  return (0, _jestAxe.axe)(container);
100
104
 
101
- case 8:
105
+ case 10:
102
106
  _context2.t1 = _context2.sent;
103
- _context2.next = 11;
107
+ _context2.next = 13;
104
108
  return (0, _context2.t0)(_context2.t1).toHaveNoViolations();
105
109
 
106
- case 11:
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 isInline is true', /*#__PURE__*/(0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee3() {
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
- isInline: true
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(); // Check for accessibility issues
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 = 6;
133
+ _context3.next = 8;
129
134
  return (0, _jestAxe.axe)(container);
130
135
 
131
- case 6:
136
+ case 8:
132
137
  _context3.t1 = _context3.sent;
133
- _context3.next = 9;
138
+ _context3.next = 11;
134
139
  return (0, _context3.t0)(_context3.t1).toHaveNoViolations();
135
140
 
136
- case 9:
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 with nullish options', /*#__PURE__*/(0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee4() {
144
- var nullishOptions, _render4, container;
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
- _render4 = (0, _react.render)((0, _jsxRuntime.jsx)(_FormSelect.FormSelect, (0, _extends2["default"])({
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 = _render4.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
- _context4.t0 = expect;
163
- _context4.next = 7;
197
+ _context5.t0 = expect;
198
+ _context5.next = 7;
164
199
  return (0, _jestAxe.axe)(container);
165
200
 
166
201
  case 7:
167
- _context4.t1 = _context4.sent;
168
- _context4.next = 10;
169
- return (0, _context4.t0)(_context4.t1).toHaveNoViolations();
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 _context4.stop();
208
+ return _context5.stop();
174
209
  }
175
210
  }
176
- }, _callee4);
211
+ }, _callee5);
177
212
  })));
178
- it('renders the FormSelect component when getOptionLabel and getOptionValue', /*#__PURE__*/(0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee5() {
179
- var props, _render5, container;
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 _callee5$(_context5) {
216
+ return _regenerator["default"].wrap(function _callee6$(_context6) {
182
217
  while (1) {
183
- switch (_context5.prev = _context5.next) {
218
+ switch (_context6.prev = _context6.next) {
184
219
  case 0:
185
220
  props = (0, _extends2["default"])({}, defaultProps, {
186
- options: options.map(function (_ref6) {
187
- var label = _ref6.label,
188
- value = _ref6.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
- _render5 = (0, _react.render)((0, _jsxRuntime.jsx)(_FormSelect.FormSelect, (0, _extends2["default"])({
236
+ _render6 = (0, _react.render)((0, _jsxRuntime.jsx)(_FormSelect.FormSelect, (0, _extends2["default"])({
202
237
  id: "my_desert_list"
203
- }, props))), container = _render5.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
- _context5.t0 = expect;
213
- _context5.next = 12;
247
+ _context6.t0 = expect;
248
+ _context6.next = 12;
214
249
  return (0, _jestAxe.axe)(container);
215
250
 
216
251
  case 12:
217
- _context5.t1 = _context5.sent;
218
- _context5.next = 15;
219
- return (0, _context5.t0)(_context5.t1).toHaveNoViolations();
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 _context5.stop();
258
+ return _context6.stop();
224
259
  }
225
260
  }
226
- }, _callee5);
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: 'icon.primary'
33
+ color: 'link'
34
34
  },
35
35
  className: (0, _classnames["default"])('vip-spinner-component', className),
36
36
  ref: forwardRef
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@automattic/vip-design-system",
3
- "version": "0.28.5",
3
+ "version": "0.28.6",
4
4
  "main": "build/system/index.js",
5
5
  "scripts": {
6
6
  "build-storybook": "build-storybook",
@@ -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: 'icon.primary',
16
+ color: 'link',
17
17
  } }
18
18
  className={ classNames( 'vip-spinner-component', className ) }
19
19
  ref={ forwardRef }