@automattic/vip-design-system 0.27.4 → 0.27.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.
@@ -20,19 +20,6 @@ var _themeUi = require("theme-ui");
20
20
  var _jsxRuntime = require("theme-ui/jsx-runtime");
21
21
 
22
22
  var _excluded = ["variant", "label", "forLabel", "hasError", "required", "sx", "errorMessage"];
23
-
24
- var RequiredLabel = function RequiredLabel() {
25
- return (0, _jsxRuntime.jsx)("span", {
26
- sx: {
27
- color: 'error',
28
- display: 'inline-block',
29
- ml: 2,
30
- fontSize: 1
31
- },
32
- children: "(Required)"
33
- });
34
- };
35
-
36
23
  var inputStyles = {
37
24
  unset: 'all',
38
25
  border: '1px solid',
@@ -72,9 +59,10 @@ var Input = /*#__PURE__*/_react["default"].forwardRef(function (_ref, ref) {
72
59
  errorMessage = _ref.errorMessage,
73
60
  props = (0, _objectWithoutPropertiesLoose2["default"])(_ref, _excluded);
74
61
  return (0, _jsxRuntime.jsxs)(_react["default"].Fragment, {
75
- children: [label && (0, _jsxRuntime.jsxs)(_.Label, {
62
+ children: [label && (0, _jsxRuntime.jsx)(_.Label, {
63
+ required: required,
76
64
  htmlFor: forLabel,
77
- children: [label, required && (0, _jsxRuntime.jsx)(RequiredLabel, {})]
65
+ children: label
78
66
  }), (0, _jsxRuntime.jsx)(_themeUi.Input, (0, _extends2["default"])({
79
67
  ref: ref,
80
68
  id: forLabel,
@@ -13,17 +13,18 @@ var _react = _interopRequireDefault(require("react"));
13
13
 
14
14
  var _propTypes = _interopRequireDefault(require("prop-types"));
15
15
 
16
+ var _RequiredLabel = require("./RequiredLabel");
17
+
16
18
  var _jsxRuntime = require("theme-ui/jsx-runtime");
17
19
 
18
- var _excluded = ["sx"];
20
+ var _excluded = ["sx", "children", "required"];
19
21
 
20
- /**
21
- * Internal dependencies
22
- */
23
22
  var Label = /*#__PURE__*/_react["default"].forwardRef(function (_ref, forwardRef) {
24
23
  var sx = _ref.sx,
24
+ children = _ref.children,
25
+ required = _ref.required,
25
26
  rest = (0, _objectWithoutPropertiesLoose2["default"])(_ref, _excluded);
26
- return (0, _jsxRuntime.jsx)("label", (0, _extends2["default"])({
27
+ return (0, _jsxRuntime.jsxs)("label", (0, _extends2["default"])({
27
28
  sx: (0, _extends2["default"])({
28
29
  fontWeight: 500,
29
30
  fontSize: 2,
@@ -33,11 +34,15 @@ var Label = /*#__PURE__*/_react["default"].forwardRef(function (_ref, forwardRef
33
34
  color: 'muted'
34
35
  }, sx),
35
36
  ref: forwardRef
36
- }, rest));
37
+ }, rest, {
38
+ children: [children, required && (0, _jsxRuntime.jsx)(_RequiredLabel.RequiredLabel, {})]
39
+ }));
37
40
  });
38
41
 
39
42
  exports.Label = Label;
40
43
  Label.propTypes = {
44
+ children: _propTypes["default"].object,
45
+ required: _propTypes["default"].bool,
41
46
  sx: _propTypes["default"].object
42
47
  };
43
48
  Label.displayName = 'Label';
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+
5
+ exports.__esModule = true;
6
+ exports["default"] = exports.Required = exports.Default = void 0;
7
+
8
+ var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
9
+
10
+ var _ = require("..");
11
+
12
+ var _jsxRuntime = require("theme-ui/jsx-runtime");
13
+
14
+ /** @jsxImportSource theme-ui */
15
+
16
+ /**
17
+ * Internal dependencies
18
+ */
19
+ var _default = {
20
+ title: 'Form/Label'
21
+ };
22
+ exports["default"] = _default;
23
+
24
+ var DefaultComponent = function DefaultComponent(props) {
25
+ return (0, _jsxRuntime.jsx)(_.Form.Root, {
26
+ children: (0, _jsxRuntime.jsx)(_.Label, (0, _extends2["default"])({}, props, {
27
+ children: "Label"
28
+ }))
29
+ });
30
+ };
31
+
32
+ var Default = function Default() {
33
+ return (0, _jsxRuntime.jsx)(_jsxRuntime.Fragment, {
34
+ children: (0, _jsxRuntime.jsx)(DefaultComponent, {})
35
+ });
36
+ };
37
+
38
+ exports.Default = Default;
39
+
40
+ var Required = function Required() {
41
+ var args = {
42
+ required: true
43
+ };
44
+ return (0, _jsxRuntime.jsx)(_jsxRuntime.Fragment, {
45
+ children: (0, _jsxRuntime.jsx)(DefaultComponent, (0, _extends2["default"])({}, args))
46
+ });
47
+ };
48
+
49
+ exports.Required = Required;
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+
3
+ exports.__esModule = true;
4
+ exports.RequiredLabel = void 0;
5
+
6
+ var _jsxRuntime = require("theme-ui/jsx-runtime");
7
+
8
+ /** @jsxImportSource theme-ui */
9
+
10
+ /**
11
+ * External dependencies
12
+ */
13
+
14
+ /**
15
+ * Internal dependencies
16
+ */
17
+ var RequiredLabel = function RequiredLabel() {
18
+ return (0, _jsxRuntime.jsx)("span", {
19
+ sx: {
20
+ color: 'error',
21
+ display: 'inline-block',
22
+ ml: 2,
23
+ fontSize: 1
24
+ },
25
+ children: "(Required)"
26
+ });
27
+ };
28
+
29
+ exports.RequiredLabel = RequiredLabel;
@@ -29,9 +29,11 @@ var _Label = require("../Form/Label");
29
29
 
30
30
  var _FormSelectSearch = require("./FormSelectSearch");
31
31
 
32
+ var _FormSelectLoading = require("./FormSelectLoading");
33
+
32
34
  var _jsxRuntime = require("theme-ui/jsx-runtime");
33
35
 
34
- var _excluded = ["isInline", "forLabel", "options", "label", "getOptionLabel", "getOptionValue", "onChange", "onInputChange", "value", "showAllValues", "searchIcon", "displayMenu", "noOptionsMessage", "id", "className"];
36
+ var _excluded = ["isInline", "forLabel", "options", "label", "getOptionLabel", "getOptionValue", "onChange", "onInputChange", "value", "showAllValues", "searchIcon", "minLength", "debounce", "loading", "required", "displayMenu", "noOptionsMessage", "id", "className"];
35
37
 
36
38
  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); }
37
39
 
@@ -134,7 +136,8 @@ var searchIconStyles = {
134
136
  var FormAutocomplete = /*#__PURE__*/_react["default"].forwardRef(function (_ref, forwardRef) {
135
137
  var isInline = _ref.isInline,
136
138
  forLabel = _ref.forLabel,
137
- options = _ref.options,
139
+ _ref$options = _ref.options,
140
+ options = _ref$options === void 0 ? [] : _ref$options,
138
141
  label = _ref.label,
139
142
  getOptionLabel = _ref.getOptionLabel,
140
143
  getOptionValue = _ref.getOptionValue,
@@ -144,8 +147,13 @@ var FormAutocomplete = /*#__PURE__*/_react["default"].forwardRef(function (_ref,
144
147
  value = _ref.value,
145
148
  _ref$showAllValues = _ref.showAllValues,
146
149
  showAllValues = _ref$showAllValues === void 0 ? true : _ref$showAllValues,
147
- _ref$searchIcon = _ref.searchIcon,
148
- searchIcon = _ref$searchIcon === void 0 ? false : _ref$searchIcon,
150
+ searchIcon = _ref.searchIcon,
151
+ _ref$minLength = _ref.minLength,
152
+ minLength = _ref$minLength === void 0 ? 0 : _ref$minLength,
153
+ _ref$debounce = _ref.debounce,
154
+ debounce = _ref$debounce === void 0 ? 0 : _ref$debounce,
155
+ loading = _ref.loading,
156
+ required = _ref.required,
149
157
  _ref$displayMenu = _ref.displayMenu,
150
158
  displayMenu = _ref$displayMenu === void 0 ? 'overlay' : _ref$displayMenu,
151
159
  _ref$noOptionsMessage = _ref.noOptionsMessage,
@@ -161,8 +169,11 @@ var FormAutocomplete = /*#__PURE__*/_react["default"].forwardRef(function (_ref,
161
169
  isDirty = _useState[0],
162
170
  setIsDirty = _useState[1];
163
171
 
172
+ var debounceTimeout;
173
+
164
174
  var SelectLabel = function SelectLabel() {
165
175
  return (0, _jsxRuntime.jsx)(_Label.Label, {
176
+ required: required,
166
177
  htmlFor: forLabel || id,
167
178
  children: label
168
179
  });
@@ -198,13 +209,21 @@ var FormAutocomplete = /*#__PURE__*/_react["default"].forwardRef(function (_ref,
198
209
  return optionLabel(option).toLowerCase().indexOf(query.toLowerCase()) >= 0;
199
210
  });
200
211
  }, [options]);
201
- var suggest = (0, _react.useCallback)(function (query, populateResults) {
202
- var data = options;
212
+ var handleInputChange = (0, _react.useCallback)(function (query) {
213
+ clearTimeout(debounceTimeout);
203
214
 
204
- if (isDirty) {
205
- data = onInputChange ? onInputChange(query) : handleTypeChange(query);
215
+ if (!query.length || query.length >= minLength) {
216
+ debounceTimeout = setTimeout(function () {
217
+ onInputChange(query);
218
+ }, debounce);
219
+ }
220
+ }, [onInputChange, debounce, minLength]);
221
+ var suggest = (0, _react.useCallback)(function (query, populateResults) {
222
+ if (isDirty && onInputChange) {
223
+ handleInputChange(query);
206
224
  }
207
225
 
226
+ var data = handleTypeChange(query);
208
227
  populateResults(data.map(function (option) {
209
228
  return optionLabel(option);
210
229
  }));
@@ -229,6 +248,7 @@ var FormAutocomplete = /*#__PURE__*/_react["default"].forwardRef(function (_ref,
229
248
  label: inlineLabel ? (0, _jsxRuntime.jsx)(SelectLabel, {}) : null,
230
249
  children: [searchIcon && (0, _jsxRuntime.jsx)(_FormSelectSearch.FormSelectSearch, {}), (0, _jsxRuntime.jsx)(_react2["default"], (0, _extends2["default"])({
231
250
  id: id,
251
+ "aria-busy": loading,
232
252
  showAllValues: showAllValues,
233
253
  ref: forwardRef,
234
254
  source: suggest,
@@ -236,7 +256,7 @@ var FormAutocomplete = /*#__PURE__*/_react["default"].forwardRef(function (_ref,
236
256
  displayMenu: displayMenu,
237
257
  onConfirm: onValueChange,
238
258
  tNoResults: noOptionsMessage
239
- }, props)), (0, _jsxRuntime.jsx)(_FormSelectArrow.FormSelectArrow, {})]
259
+ }, props)), loading && (0, _jsxRuntime.jsx)(_FormSelectLoading.FormSelectLoading, {}), (0, _jsxRuntime.jsx)(_FormSelectArrow.FormSelectArrow, {})]
240
260
  })
241
261
  })]
242
262
  });
@@ -247,6 +267,8 @@ FormAutocomplete.propTypes = {
247
267
  id: _propTypes["default"].string,
248
268
  showAllValues: _propTypes["default"].bool,
249
269
  searchIcon: _propTypes["default"].bool,
270
+ loading: _propTypes["default"].bool,
271
+ required: _propTypes["default"].bool,
250
272
  isInline: _propTypes["default"].bool,
251
273
  forLabel: _propTypes["default"].string,
252
274
  value: _propTypes["default"].string,
@@ -258,6 +280,8 @@ FormAutocomplete.propTypes = {
258
280
  onInputChange: _propTypes["default"].func,
259
281
  noOptionsMessage: _propTypes["default"].func,
260
282
  onChange: _propTypes["default"].func,
261
- className: _propTypes["default"].any
283
+ className: _propTypes["default"].any,
284
+ minLength: _propTypes["default"].number,
285
+ debounce: _propTypes["default"].number
262
286
  };
263
287
  FormAutocomplete.displayName = 'FormAutocomplete';
@@ -3,12 +3,14 @@
3
3
  var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
4
 
5
5
  exports.__esModule = true;
6
- exports["default"] = exports.WithSearchIcon = exports.WithDefaultValue = exports.WithCustomMessages = exports.Inline = exports.Default = void 0;
6
+ exports["default"] = exports.WithSearchIcon = exports.WithLoading = exports.WithDefaultValue = exports.WithDebounce = exports.WithCustomMessages = exports.Inline = 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");
@@ -81,7 +83,9 @@ var DefaultComponent = function DefaultComponent(_ref) {
81
83
 
82
84
  var Default = function Default() {
83
85
  return (0, _jsxRuntime.jsx)(_jsxRuntime.Fragment, {
84
- children: (0, _jsxRuntime.jsx)(DefaultComponent, (0, _extends2["default"])({}, args))
86
+ children: (0, _jsxRuntime.jsx)(DefaultComponent, (0, _extends2["default"])({
87
+ required: true
88
+ }, args))
85
89
  });
86
90
  };
87
91
 
@@ -120,6 +124,36 @@ var WithSearchIcon = function WithSearchIcon() {
120
124
 
121
125
  exports.WithSearchIcon = WithSearchIcon;
122
126
 
127
+ var WithLoading = function WithLoading() {
128
+ var customArgs = (0, _extends2["default"])({}, args, {
129
+ loading: true
130
+ });
131
+ return (0, _jsxRuntime.jsx)(_jsxRuntime.Fragment, {
132
+ children: (0, _jsxRuntime.jsx)(DefaultComponent, (0, _extends2["default"])({}, customArgs))
133
+ });
134
+ };
135
+
136
+ exports.WithLoading = WithLoading;
137
+
138
+ var WithDebounce = function WithDebounce() {
139
+ var _useState = (0, _react.useState)(null),
140
+ value = _useState[0],
141
+ setValue = _useState[1];
142
+
143
+ var customArgs = (0, _extends2["default"])({}, args, {
144
+ minLength: 3,
145
+ debounce: 300,
146
+ onInputChange: function onInputChange(query) {
147
+ setValue(query);
148
+ }
149
+ });
150
+ return (0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
151
+ children: ["Filter: ", value, (0, _jsxRuntime.jsx)(DefaultComponent, (0, _extends2["default"])({}, customArgs))]
152
+ });
153
+ };
154
+
155
+ exports.WithDebounce = WithDebounce;
156
+
123
157
  var WithCustomMessages = function WithCustomMessages() {
124
158
  var customArgs = (0, _extends2["default"])({}, args, {
125
159
  noOptionsMessage: function noOptionsMessage() {
@@ -45,25 +45,53 @@ describe('<FormAutocomplete />', function () {
45
45
  while (1) {
46
46
  switch (_context.prev = _context.next) {
47
47
  case 0:
48
- _render = (0, _react.render)((0, _jsxRuntime.jsx)(_FormAutocomplete.FormAutocomplete, (0, _extends2["default"])({
49
- id: "my_desert_list"
50
- }, defaultProps))), container = _render.container;
51
- expect(_react.screen.getByLabelText(defaultProps.label)).toBeInTheDocument(); // Check for accessibility issues
48
+ _render = (0, _react.render)((0, _jsxRuntime.jsx)(_FormAutocomplete.FormAutocomplete, {
49
+ id: "my_desert_list",
50
+ label: "This is a label"
51
+ })), container = _render.container; // Check for accessibility issues
52
52
 
53
53
  _context.t0 = expect;
54
- _context.next = 5;
54
+ _context.next = 4;
55
55
  return (0, _jestAxe.axe)(container);
56
56
 
57
- case 5:
57
+ case 4:
58
58
  _context.t1 = _context.sent;
59
- _context.next = 8;
59
+ _context.next = 7;
60
60
  return (0, _context.t0)(_context.t1).toHaveNoViolations();
61
61
 
62
- case 8:
62
+ case 7:
63
63
  case "end":
64
64
  return _context.stop();
65
65
  }
66
66
  }
67
67
  }, _callee);
68
68
  })));
69
+ it('renders the FormAutocomplete component with options', /*#__PURE__*/(0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee2() {
70
+ var _render2, container;
71
+
72
+ return _regenerator["default"].wrap(function _callee2$(_context2) {
73
+ while (1) {
74
+ switch (_context2.prev = _context2.next) {
75
+ case 0:
76
+ _render2 = (0, _react.render)((0, _jsxRuntime.jsx)(_FormAutocomplete.FormAutocomplete, (0, _extends2["default"])({
77
+ id: "my_desert_list"
78
+ }, defaultProps))), container = _render2.container;
79
+ expect(_react.screen.getByLabelText(defaultProps.label)).toBeInTheDocument(); // Check for accessibility issues
80
+
81
+ _context2.t0 = expect;
82
+ _context2.next = 5;
83
+ return (0, _jestAxe.axe)(container);
84
+
85
+ case 5:
86
+ _context2.t1 = _context2.sent;
87
+ _context2.next = 8;
88
+ return (0, _context2.t0)(_context2.t1).toHaveNoViolations();
89
+
90
+ case 8:
91
+ case "end":
92
+ return _context2.stop();
93
+ }
94
+ }
95
+ }, _callee2);
96
+ })));
69
97
  });
@@ -21,7 +21,7 @@ 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", "getOptionLabel", "getOptionValue", "onChange"],
24
+ var _excluded = ["isInline", "placeholder", "forLabel", "options", "required", "label", "getOptionLabel", "getOptionValue", "onChange"],
25
25
  _excluded2 = ["options"];
26
26
 
27
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); }
@@ -79,6 +79,7 @@ var FormSelect = /*#__PURE__*/_react["default"].forwardRef(function (_ref2, forw
79
79
  placeholder = _ref2.placeholder,
80
80
  forLabel = _ref2.forLabel,
81
81
  options = _ref2.options,
82
+ required = _ref2.required,
82
83
  label = _ref2.label,
83
84
  getOptionLabel = _ref2.getOptionLabel,
84
85
  getOptionValue = _ref2.getOptionValue,
@@ -118,6 +119,7 @@ var FormSelect = /*#__PURE__*/_react["default"].forwardRef(function (_ref2, forw
118
119
 
119
120
  var SelectLabel = function SelectLabel() {
120
121
  return (0, _jsxRuntime.jsx)(_Label.Label, {
122
+ required: required,
121
123
  htmlFor: forLabel || props.id,
122
124
  children: label
123
125
  });
@@ -149,6 +151,7 @@ exports.FormSelect = FormSelect;
149
151
  FormSelect.propTypes = {
150
152
  id: _propTypes["default"].string,
151
153
  isInline: _propTypes["default"].bool,
154
+ required: _propTypes["default"].bool,
152
155
  forLabel: _propTypes["default"].string,
153
156
  placeholder: _propTypes["default"].string,
154
157
  label: _propTypes["default"].string,
@@ -101,6 +101,7 @@ var Default = DefaultComponent.bind({});
101
101
  exports.Default = Default;
102
102
  Default.args = {
103
103
  placeholder: '- Select -',
104
+ required: true,
104
105
  options: options
105
106
  };
106
107
  var WithGroup = DefaultComponent.bind({});
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+
5
+ exports.__esModule = true;
6
+ exports.FormSelectLoading = void 0;
7
+
8
+ var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
9
+
10
+ var _react = _interopRequireDefault(require("react"));
11
+
12
+ var _ai = require("react-icons/ai");
13
+
14
+ var _react2 = require("@emotion/react");
15
+
16
+ var _jsxRuntime = require("theme-ui/jsx-runtime");
17
+
18
+ /** @jsxImportSource theme-ui */
19
+
20
+ /**
21
+ * External dependencies
22
+ */
23
+ var kf = (0, _react2.keyframes)({
24
+ from: {
25
+ transform: 'rotate(0deg)'
26
+ },
27
+ to: {
28
+ transform: 'rotate(360deg) '
29
+ }
30
+ });
31
+
32
+ var FormSelectLoading = /*#__PURE__*/_react["default"].forwardRef(function (props, forwardRef) {
33
+ return (0, _jsxRuntime.jsx)(_ai.AiOutlineLoading3Quarters, (0, _extends2["default"])({
34
+ ref: forwardRef,
35
+ "aria-hidden": "true",
36
+ size: 18,
37
+ sx: {
38
+ position: 'absolute',
39
+ right: 40,
40
+ pointerEvents: 'none',
41
+ animation: kf + " 1s infinite linear",
42
+ opacity: 0.5
43
+ }
44
+ }, props));
45
+ });
46
+
47
+ exports.FormSelectLoading = FormSelectLoading;
48
+ FormSelectLoading.displayName = 'FormSelectLoading';
@@ -25,6 +25,19 @@ var _default = {
25
25
  component: _.Wizard
26
26
  };
27
27
  exports["default"] = _default;
28
+ var options = [{
29
+ value: 'chocolate',
30
+ label: 'Chocolate'
31
+ }, {
32
+ value: 'strawberry',
33
+ label: 'Strawberry'
34
+ }, {
35
+ value: 'vanilla',
36
+ label: 'Vanilla'
37
+ }, {
38
+ value: 'coffee',
39
+ label: 'Coffee'
40
+ }];
28
41
 
29
42
  var Default = function Default() {
30
43
  var steps = [{
@@ -37,7 +50,13 @@ var Default = function Default() {
37
50
  }), (0, _jsxRuntime.jsx)(_.Input, {
38
51
  autoFocus: true,
39
52
  placeholder: "yourdomain.com"
53
+ }), (0, _jsxRuntime.jsx)(_.Form.Autocomplete, {
54
+ label: "Autocomplete",
55
+ options: options
40
56
  }), (0, _jsxRuntime.jsx)(_.Button, {
57
+ sx: {
58
+ mt: 3
59
+ },
41
60
  children: "Continue"
42
61
  })]
43
62
  })
@@ -65,7 +65,8 @@ var WizardStep = /*#__PURE__*/_react["default"].forwardRef(function (_ref, forwa
65
65
  borderTopColor: 'borders.2'
66
66
  },
67
67
  borderColor: active ? 'primary' : 'borders.2',
68
- borderLeftColor: borderLeftColor
68
+ borderLeftColor: borderLeftColor,
69
+ overflow: 'inherit'
69
70
  },
70
71
  "data-step": order,
71
72
  "data-active": active || undefined,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@automattic/vip-design-system",
3
- "version": "0.27.4",
3
+ "version": "0.27.6",
4
4
  "main": "build/system/index.js",
5
5
  "scripts": {
6
6
  "build-storybook": "build-storybook",
@@ -12,10 +12,6 @@ import PropTypes from 'prop-types';
12
12
  import { Validation, Label } from '../';
13
13
  import { Input as ThemeInput } from 'theme-ui';
14
14
 
15
- const RequiredLabel = () => (
16
- <span sx={ { color: 'error', display: 'inline-block', ml: 2, fontSize: 1 } }>(Required)</span>
17
- );
18
-
19
15
  const inputStyles = {
20
16
  unset: 'all',
21
17
  border: '1px solid',
@@ -44,9 +40,8 @@ const Input = React.forwardRef(
44
40
  ( { variant, label, forLabel, hasError, required, sx = {}, errorMessage, ...props }, ref ) => (
45
41
  <React.Fragment>
46
42
  { label && (
47
- <Label htmlFor={ forLabel }>
43
+ <Label required={ required } htmlFor={ forLabel }>
48
44
  { label }
49
- { required && <RequiredLabel /> }
50
45
  </Label>
51
46
  ) }
52
47
 
@@ -9,8 +9,9 @@ import PropTypes from 'prop-types';
9
9
  /**
10
10
  * Internal dependencies
11
11
  */
12
+ import { RequiredLabel } from './RequiredLabel';
12
13
 
13
- const Label = React.forwardRef( ( { sx, ...rest }, forwardRef ) => (
14
+ const Label = React.forwardRef( ( { sx, children, required, ...rest }, forwardRef ) => (
14
15
  <label
15
16
  sx={ {
16
17
  fontWeight: 500,
@@ -23,10 +24,15 @@ const Label = React.forwardRef( ( { sx, ...rest }, forwardRef ) => (
23
24
  } }
24
25
  ref={ forwardRef }
25
26
  { ...rest }
26
- />
27
+ >
28
+ { children }
29
+ { required && <RequiredLabel /> }
30
+ </label>
27
31
  ) );
28
32
 
29
33
  Label.propTypes = {
34
+ children: PropTypes.object,
35
+ required: PropTypes.bool,
30
36
  sx: PropTypes.object,
31
37
  };
32
38
 
@@ -0,0 +1,36 @@
1
+ /** @jsxImportSource theme-ui */
2
+
3
+ /**
4
+ * Internal dependencies
5
+ */
6
+ import { Form, Label } from '..';
7
+
8
+ export default {
9
+ title: 'Form/Label',
10
+ };
11
+
12
+ const DefaultComponent = props => (
13
+ <Form.Root>
14
+ <Label { ...props }>Label</Label>
15
+ </Form.Root>
16
+ );
17
+
18
+ export const Default = () => {
19
+ return (
20
+ <>
21
+ <DefaultComponent />
22
+ </>
23
+ );
24
+ };
25
+
26
+ export const Required = () => {
27
+ const args = {
28
+ required: true,
29
+ };
30
+
31
+ return (
32
+ <>
33
+ <DefaultComponent { ...args } />
34
+ </>
35
+ );
36
+ };
@@ -0,0 +1,15 @@
1
+ /** @jsxImportSource theme-ui */
2
+
3
+ /**
4
+ * External dependencies
5
+ */
6
+
7
+ /**
8
+ * Internal dependencies
9
+ */
10
+
11
+ const RequiredLabel = () => (
12
+ <span sx={ { color: 'error', display: 'inline-block', ml: 2, fontSize: 1 } }>(Required)</span>
13
+ );
14
+
15
+ export { RequiredLabel };
@@ -16,6 +16,7 @@ import { FormSelectContent } from './FormSelectContent';
16
16
  import { FormSelectArrow } from './FormSelectArrow';
17
17
  import { Label } from '../Form/Label';
18
18
  import { FormSelectSearch } from './FormSelectSearch';
19
+ import { FormSelectLoading } from './FormSelectLoading';
19
20
 
20
21
  const defaultStyles = {
21
22
  width: '100%',
@@ -96,7 +97,7 @@ const FormAutocomplete = React.forwardRef(
96
97
  {
97
98
  isInline,
98
99
  forLabel,
99
- options,
100
+ options = [],
100
101
  label,
101
102
  getOptionLabel,
102
103
  getOptionValue,
@@ -104,7 +105,11 @@ const FormAutocomplete = React.forwardRef(
104
105
  onInputChange,
105
106
  value,
106
107
  showAllValues = true,
107
- searchIcon = false,
108
+ searchIcon,
109
+ minLength = 0,
110
+ debounce = 0,
111
+ loading,
112
+ required,
108
113
  displayMenu = 'overlay',
109
114
  noOptionsMessage = () => 'No results found.',
110
115
  id = 'vip-autocomplete',
@@ -114,8 +119,13 @@ const FormAutocomplete = React.forwardRef(
114
119
  forwardRef
115
120
  ) => {
116
121
  const [ isDirty, setIsDirty ] = useState( false );
122
+ let debounceTimeout;
117
123
 
118
- const SelectLabel = () => <Label htmlFor={ forLabel || id }>{ label }</Label>;
124
+ const SelectLabel = () => (
125
+ <Label required={ required } htmlFor={ forLabel || id }>
126
+ { label }
127
+ </Label>
128
+ );
119
129
 
120
130
  const inlineLabel = !! ( isInline && label );
121
131
 
@@ -156,12 +166,24 @@ const FormAutocomplete = React.forwardRef(
156
166
  [ options ]
157
167
  );
158
168
 
169
+ const handleInputChange = useCallback(
170
+ query => {
171
+ clearTimeout( debounceTimeout );
172
+ if ( ! query.length || query.length >= minLength ) {
173
+ debounceTimeout = setTimeout( () => {
174
+ onInputChange( query );
175
+ }, debounce );
176
+ }
177
+ },
178
+ [ onInputChange, debounce, minLength ]
179
+ );
180
+
159
181
  const suggest = useCallback(
160
182
  ( query, populateResults ) => {
161
- let data = options;
162
- if ( isDirty ) {
163
- data = onInputChange ? onInputChange( query ) : handleTypeChange( query );
183
+ if ( isDirty && onInputChange ) {
184
+ handleInputChange( query );
164
185
  }
186
+ const data = handleTypeChange( query );
165
187
  populateResults( data.map( option => optionLabel( option ) ) );
166
188
  },
167
189
  [ options, isDirty ]
@@ -203,6 +225,7 @@ const FormAutocomplete = React.forwardRef(
203
225
  { searchIcon && <FormSelectSearch /> }
204
226
  <Autocomplete
205
227
  id={ id }
228
+ aria-busy={ loading }
206
229
  showAllValues={ showAllValues }
207
230
  ref={ forwardRef }
208
231
  source={ suggest }
@@ -212,6 +235,7 @@ const FormAutocomplete = React.forwardRef(
212
235
  tNoResults={ noOptionsMessage }
213
236
  { ...props }
214
237
  />
238
+ { loading && <FormSelectLoading /> }
215
239
  <FormSelectArrow />
216
240
  </FormSelectContent>
217
241
  </div>
@@ -224,6 +248,8 @@ FormAutocomplete.propTypes = {
224
248
  id: PropTypes.string,
225
249
  showAllValues: PropTypes.bool,
226
250
  searchIcon: PropTypes.bool,
251
+ loading: PropTypes.bool,
252
+ required: PropTypes.bool,
227
253
  isInline: PropTypes.bool,
228
254
  forLabel: PropTypes.string,
229
255
  value: PropTypes.string,
@@ -236,6 +262,8 @@ FormAutocomplete.propTypes = {
236
262
  noOptionsMessage: PropTypes.func,
237
263
  onChange: PropTypes.func,
238
264
  className: PropTypes.any,
265
+ minLength: PropTypes.number,
266
+ debounce: PropTypes.number,
239
267
  };
240
268
 
241
269
  FormAutocomplete.displayName = 'FormAutocomplete';
@@ -3,6 +3,7 @@
3
3
  /**
4
4
  * Internal dependencies
5
5
  */
6
+ import { useState } from 'react';
6
7
  import * as Form from '.';
7
8
 
8
9
  export default {
@@ -44,7 +45,7 @@ const DefaultComponent = ( { label = 'Label', width = 250, ...rest } ) => (
44
45
  export const Default = () => {
45
46
  return (
46
47
  <>
47
- <DefaultComponent { ...args } />
48
+ <DefaultComponent required { ...args } />
48
49
  </>
49
50
  );
50
51
  };
@@ -88,6 +89,38 @@ export const WithSearchIcon = () => {
88
89
  );
89
90
  };
90
91
 
92
+ export const WithLoading = () => {
93
+ const customArgs = {
94
+ ...args,
95
+ loading: true,
96
+ };
97
+
98
+ return (
99
+ <>
100
+ <DefaultComponent { ...customArgs } />
101
+ </>
102
+ );
103
+ };
104
+
105
+ export const WithDebounce = () => {
106
+ const [ value, setValue ] = useState( null );
107
+ const customArgs = {
108
+ ...args,
109
+ minLength: 3,
110
+ debounce: 300,
111
+ onInputChange: query => {
112
+ setValue( query );
113
+ },
114
+ };
115
+
116
+ return (
117
+ <>
118
+ Filter: { value }
119
+ <DefaultComponent { ...customArgs } />
120
+ </>
121
+ );
122
+ };
123
+
91
124
  export const WithCustomMessages = () => {
92
125
  const customArgs = {
93
126
  ...args,
@@ -22,6 +22,15 @@ const defaultProps = {
22
22
 
23
23
  describe( '<FormAutocomplete />', () => {
24
24
  it( 'renders the FormAutocomplete component', async () => {
25
+ const { container } = render(
26
+ <FormAutocomplete id="my_desert_list" label="This is a label" />
27
+ );
28
+
29
+ // Check for accessibility issues
30
+ await expect( await axe( container ) ).toHaveNoViolations();
31
+ } );
32
+
33
+ it( 'renders the FormAutocomplete component with options', async () => {
25
34
  const { container } = render( <FormAutocomplete id="my_desert_list" { ...defaultProps } /> );
26
35
 
27
36
  expect( screen.getByLabelText( defaultProps.label ) ).toBeInTheDocument();
@@ -58,6 +58,7 @@ const FormSelect = React.forwardRef(
58
58
  placeholder,
59
59
  forLabel,
60
60
  options,
61
+ required,
61
62
  label,
62
63
  getOptionLabel,
63
64
  getOptionValue,
@@ -105,7 +106,11 @@ const FormSelect = React.forwardRef(
105
106
  [ onChange, getOptionByValue ]
106
107
  );
107
108
 
108
- const SelectLabel = () => <Label htmlFor={ forLabel || props.id }>{ label }</Label>;
109
+ const SelectLabel = () => (
110
+ <Label required={ required } htmlFor={ forLabel || props.id }>
111
+ { label }
112
+ </Label>
113
+ );
109
114
 
110
115
  const inlineLabel = !! ( isInline && label );
111
116
 
@@ -133,6 +138,7 @@ const FormSelect = React.forwardRef(
133
138
  FormSelect.propTypes = {
134
139
  id: PropTypes.string,
135
140
  isInline: PropTypes.bool,
141
+ required: PropTypes.bool,
136
142
  forLabel: PropTypes.string,
137
143
  placeholder: PropTypes.string,
138
144
  label: PropTypes.string,
@@ -70,6 +70,7 @@ const DefaultComponent = ( { label = 'Label', width = 250, onChange, ...rest } )
70
70
  export const Default = DefaultComponent.bind( {} );
71
71
  Default.args = {
72
72
  placeholder: '- Select -',
73
+ required: true,
73
74
  options,
74
75
  };
75
76
 
@@ -0,0 +1,31 @@
1
+ /** @jsxImportSource theme-ui */
2
+
3
+ /**
4
+ * External dependencies
5
+ */
6
+ import React from 'react';
7
+ import { AiOutlineLoading3Quarters } from 'react-icons/ai';
8
+ import { keyframes } from '@emotion/react';
9
+
10
+ const kf = keyframes( {
11
+ from: { transform: 'rotate(0deg)' },
12
+ to: { transform: 'rotate(360deg) ' },
13
+ } );
14
+
15
+ export const FormSelectLoading = React.forwardRef( ( props, forwardRef ) => (
16
+ <AiOutlineLoading3Quarters
17
+ ref={ forwardRef }
18
+ aria-hidden="true"
19
+ size={ 18 }
20
+ sx={ {
21
+ position: 'absolute',
22
+ right: 40,
23
+ pointerEvents: 'none',
24
+ animation: `${ kf } 1s infinite linear`,
25
+ opacity: 0.5,
26
+ } }
27
+ { ...props }
28
+ />
29
+ ) );
30
+
31
+ FormSelectLoading.displayName = 'FormSelectLoading';
@@ -8,13 +8,20 @@ import React from 'react';
8
8
  /**
9
9
  * Internal dependencies
10
10
  */
11
- import { Wizard, Box, Label, Input, Button } from '..';
11
+ import { Wizard, Box, Label, Input, Button, Form } from '..';
12
12
 
13
13
  export default {
14
14
  title: 'Wizard',
15
15
  component: Wizard,
16
16
  };
17
17
 
18
+ const options = [
19
+ { value: 'chocolate', label: 'Chocolate' },
20
+ { value: 'strawberry', label: 'Strawberry' },
21
+ { value: 'vanilla', label: 'Vanilla' },
22
+ { value: 'coffee', label: 'Coffee' },
23
+ ];
24
+
18
25
  export const Default = () => {
19
26
  const steps = [
20
27
  {
@@ -25,7 +32,8 @@ export const Default = () => {
25
32
  <Box>
26
33
  <Label>Domain</Label>
27
34
  <Input autoFocus placeholder="yourdomain.com" />
28
- <Button>Continue</Button>
35
+ <Form.Autocomplete label="Autocomplete" options={ options } />
36
+ <Button sx={ { mt: 3 } }>Continue</Button>
29
37
  </Box>
30
38
  ),
31
39
  },
@@ -50,6 +50,7 @@ const WizardStep = React.forwardRef(
50
50
  },
51
51
  borderColor: active ? 'primary' : 'borders.2',
52
52
  borderLeftColor: borderLeftColor,
53
+ overflow: 'inherit',
53
54
  } }
54
55
  data-step={ order }
55
56
  data-active={ active || undefined }