@automattic/vip-design-system 0.21.0 → 0.23.0
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 +35 -7
- package/build/system/NewForm/FormSelect.stories.js +59 -4
- package/build/system/NewForm/FormSelect.test.js +50 -0
- package/build/system/ScreenReaderText/ScreenReaderText.js +16 -13
- package/build/system/Table/Table.js +22 -4
- package/build/system/Table/Table.stories.js +5 -1
- package/package.json +2 -2
- package/src/system/Dropdown/DropdownSeparator.js +1 -1
- package/src/system/NewForm/FormSelect.js +50 -6
- package/src/system/NewForm/FormSelect.stories.jsx +40 -2
- package/src/system/NewForm/FormSelect.test.js +25 -0
- package/src/system/ScreenReaderText/ScreenReaderText.js +14 -17
- package/src/system/Table/Table.js +25 -10
- package/src/system/Table/Table.stories.jsx +4 -1
|
@@ -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,21 @@ 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 optionLabel = (0, _react.useCallback)(function (option) {
|
|
89
|
+
return getOptionLabel ? getOptionLabel(option) : option.label;
|
|
90
|
+
}, [getOptionLabel]);
|
|
91
|
+
var optionValue = (0, _react.useCallback)(function (option) {
|
|
92
|
+
return getOptionValue ? getOptionValue(option) : option.value;
|
|
93
|
+
}, [getOptionValue]);
|
|
94
|
+
var getOptionByValue = (0, _react.useCallback)(function (value) {
|
|
95
|
+
return options.find(function (option) {
|
|
96
|
+
return optionValue(option) === value;
|
|
97
|
+
});
|
|
98
|
+
}, [options, optionValue]);
|
|
99
|
+
var onValueChange = (0, _react.useCallback)(function (event) {
|
|
100
|
+
return onChange ? onChange(getOptionByValue(event.target.value), event) : getOptionByValue(event.target.value);
|
|
101
|
+
}, [onChange, getOptionByValue]);
|
|
102
|
+
|
|
79
103
|
var SelectLabel = function SelectLabel() {
|
|
80
104
|
return (0, _jsxRuntime.jsx)(_Label.Label, {
|
|
81
105
|
htmlFor: forLabel || props.id,
|
|
@@ -89,16 +113,17 @@ var FormSelect = /*#__PURE__*/_react["default"].forwardRef(function (_ref2, forw
|
|
|
89
113
|
isInline: inlineLabel,
|
|
90
114
|
label: inlineLabel ? (0, _jsxRuntime.jsx)(SelectLabel, {}) : null,
|
|
91
115
|
children: [(0, _jsxRuntime.jsxs)("select", (0, _extends2["default"])({
|
|
116
|
+
onChange: onValueChange,
|
|
92
117
|
ref: forwardRef,
|
|
93
118
|
sx: defaultStyles
|
|
94
119
|
}, props, {
|
|
95
120
|
children: [placeholder && (0, _jsxRuntime.jsx)("option", {
|
|
96
121
|
children: placeholder
|
|
97
122
|
}), options.map(function (_ref3) {
|
|
98
|
-
var
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
return value ? renderOption(optionLabel, value) : renderGroup(optionLabel, groupOptions);
|
|
123
|
+
var groupOptions = _ref3.options,
|
|
124
|
+
option = (0, _objectWithoutPropertiesLoose2["default"])(_ref3, _excluded2);
|
|
125
|
+
var value = optionValue(option);
|
|
126
|
+
return value ? renderOption(optionLabel(option), value) : renderGroup(optionLabel(option), groupOptions);
|
|
102
127
|
})]
|
|
103
128
|
})), (0, _jsxRuntime.jsx)(_FormSelectArrow.FormSelectArrow, {})]
|
|
104
129
|
})]
|
|
@@ -112,6 +137,9 @@ FormSelect.propTypes = {
|
|
|
112
137
|
forLabel: _propTypes["default"].string,
|
|
113
138
|
placeholder: _propTypes["default"].string,
|
|
114
139
|
label: _propTypes["default"].string,
|
|
115
|
-
options: _propTypes["default"].array
|
|
140
|
+
options: _propTypes["default"].array,
|
|
141
|
+
getOptionLabel: _propTypes["default"].func,
|
|
142
|
+
getOptionValue: _propTypes["default"].func,
|
|
143
|
+
onChange: _propTypes["default"].func
|
|
116
144
|
};
|
|
117
145
|
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
|
|
|
@@ -59,6 +61,7 @@ var DefaultComponent = function DefaultComponent(_ref) {
|
|
|
59
61
|
label = _ref$label === void 0 ? 'Label' : _ref$label,
|
|
60
62
|
_ref$width = _ref.width,
|
|
61
63
|
width = _ref$width === void 0 ? 250 : _ref$width,
|
|
64
|
+
onChange = _ref.onChange,
|
|
62
65
|
rest = (0, _objectWithoutPropertiesLoose2["default"])(_ref, _excluded);
|
|
63
66
|
return (0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
|
|
64
67
|
children: [(0, _jsxRuntime.jsxs)("p", {
|
|
@@ -79,7 +82,8 @@ var DefaultComponent = function DefaultComponent(_ref) {
|
|
|
79
82
|
},
|
|
80
83
|
children: (0, _jsxRuntime.jsx)(Form.Select, (0, _extends2["default"])({
|
|
81
84
|
id: "form-select",
|
|
82
|
-
label: label
|
|
85
|
+
label: label,
|
|
86
|
+
onChange: onChange
|
|
83
87
|
}, rest))
|
|
84
88
|
})
|
|
85
89
|
})]
|
|
@@ -117,4 +121,55 @@ IsInline.args = {
|
|
|
117
121
|
label: 'Another Group name',
|
|
118
122
|
options: options
|
|
119
123
|
}]
|
|
120
|
-
};
|
|
124
|
+
};
|
|
125
|
+
var WithOptionLabelAndValue = DefaultComponent.bind({});
|
|
126
|
+
exports.WithOptionLabelAndValue = WithOptionLabelAndValue;
|
|
127
|
+
WithOptionLabelAndValue.args = {
|
|
128
|
+
label: 'Select with getOptionLabel / getOptionValue',
|
|
129
|
+
width: '100%',
|
|
130
|
+
options: options.map(function (_ref2) {
|
|
131
|
+
var label = _ref2.label,
|
|
132
|
+
value = _ref2.value;
|
|
133
|
+
return {
|
|
134
|
+
name: label,
|
|
135
|
+
id: value
|
|
136
|
+
};
|
|
137
|
+
}),
|
|
138
|
+
getOptionLabel: function getOptionLabel(option) {
|
|
139
|
+
return option.name;
|
|
140
|
+
},
|
|
141
|
+
getOptionValue: function getOptionValue(option) {
|
|
142
|
+
return option.id;
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
var WithOnChange = function WithOnChange() {
|
|
147
|
+
var _useState = (0, _react.useState)(null),
|
|
148
|
+
option = _useState[0],
|
|
149
|
+
setOption = _useState[1];
|
|
150
|
+
|
|
151
|
+
var onChange = (0, _react.useCallback)(function (val, event) {
|
|
152
|
+
return setOption({
|
|
153
|
+
obj: val,
|
|
154
|
+
eventValue: event.target.value
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
var args = {
|
|
158
|
+
label: 'Select with onChange',
|
|
159
|
+
placeholder: '- Select -',
|
|
160
|
+
width: '100%',
|
|
161
|
+
onChange: onChange,
|
|
162
|
+
options: options
|
|
163
|
+
};
|
|
164
|
+
return (0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
|
|
165
|
+
children: [(0, _jsxRuntime.jsx)(DefaultComponent, (0, _extends2["default"])({
|
|
166
|
+
onChange: onChange
|
|
167
|
+
}, args)), option && (0, _jsxRuntime.jsxs)("p", {
|
|
168
|
+
children: ["Object to JSON: ", JSON.stringify(option.obj)]
|
|
169
|
+
}), option && (0, _jsxRuntime.jsxs)("p", {
|
|
170
|
+
children: ["Original Event Value: ", option.eventValue]
|
|
171
|
+
})]
|
|
172
|
+
});
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
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
|
});
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
4
|
|
|
5
5
|
exports.__esModule = true;
|
|
6
|
-
exports.ScreenReaderText = void 0;
|
|
6
|
+
exports.screenReaderTextClass = exports.ScreenReaderText = void 0;
|
|
7
7
|
|
|
8
8
|
var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
|
|
9
9
|
|
|
@@ -22,21 +22,24 @@ var _jsxRuntime = require("theme-ui/jsx-runtime");
|
|
|
22
22
|
/**
|
|
23
23
|
* Internal dependencies
|
|
24
24
|
*/
|
|
25
|
+
var screenReaderTextClass = {
|
|
26
|
+
border: 'none',
|
|
27
|
+
clip: 'rect(1px, 1px, 1px, 1px)',
|
|
28
|
+
clipPath: 'inset(50%)',
|
|
29
|
+
height: '1px',
|
|
30
|
+
margin: '-1px',
|
|
31
|
+
overflow: 'hidden',
|
|
32
|
+
padding: '0',
|
|
33
|
+
position: 'absolute',
|
|
34
|
+
width: '1px',
|
|
35
|
+
wordWrap: 'normal !important'
|
|
36
|
+
};
|
|
37
|
+
exports.screenReaderTextClass = screenReaderTextClass;
|
|
38
|
+
|
|
25
39
|
var ScreenReaderText = /*#__PURE__*/_react["default"].forwardRef(function (props, forwardRef) {
|
|
26
40
|
return (0, _jsxRuntime.jsx)("span", (0, _extends2["default"])({
|
|
27
41
|
className: "screen-reader-text",
|
|
28
|
-
sx:
|
|
29
|
-
border: 'none',
|
|
30
|
-
clip: 'rect(1px, 1px, 1px, 1px)',
|
|
31
|
-
clipPath: 'inset(50%)',
|
|
32
|
-
height: '1px',
|
|
33
|
-
margin: '-1px',
|
|
34
|
-
overflow: 'hidden',
|
|
35
|
-
padding: '0',
|
|
36
|
-
position: 'absolute',
|
|
37
|
-
width: '1px',
|
|
38
|
-
wordWrap: 'normal !important'
|
|
39
|
-
}
|
|
42
|
+
sx: screenReaderTextClass
|
|
40
43
|
}, props, {
|
|
41
44
|
ref: forwardRef,
|
|
42
45
|
children: props.children
|
|
@@ -15,15 +15,26 @@ var _propTypes = _interopRequireDefault(require("prop-types"));
|
|
|
15
15
|
|
|
16
16
|
var _classnames = _interopRequireDefault(require("classnames"));
|
|
17
17
|
|
|
18
|
+
var _ScreenReaderText = require("../ScreenReaderText/ScreenReaderText");
|
|
19
|
+
|
|
18
20
|
var _jsxRuntime = require("theme-ui/jsx-runtime");
|
|
19
21
|
|
|
20
|
-
var _excluded = ["sx", "className"];
|
|
22
|
+
var _excluded = ["sx", "className", "children", "caption"];
|
|
21
23
|
|
|
22
24
|
var Table = /*#__PURE__*/_react["default"].forwardRef(function (_ref, forwardRef) {
|
|
23
25
|
var sx = _ref.sx,
|
|
24
26
|
className = _ref.className,
|
|
27
|
+
children = _ref.children,
|
|
28
|
+
_ref$caption = _ref.caption,
|
|
29
|
+
caption = _ref$caption === void 0 ? null : _ref$caption,
|
|
25
30
|
props = (0, _objectWithoutPropertiesLoose2["default"])(_ref, _excluded);
|
|
26
|
-
|
|
31
|
+
|
|
32
|
+
if (!caption) {
|
|
33
|
+
// eslint-disable-next-line no-console
|
|
34
|
+
console.warn('[A11Y] Please, add a caption to your table.');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return (0, _jsxRuntime.jsxs)("table", (0, _extends2["default"])({
|
|
27
38
|
sx: (0, _extends2["default"])({
|
|
28
39
|
width: '100%',
|
|
29
40
|
minWidth: 1024
|
|
@@ -32,12 +43,19 @@ var Table = /*#__PURE__*/_react["default"].forwardRef(function (_ref, forwardRef
|
|
|
32
43
|
cellSpacing: 0,
|
|
33
44
|
className: (0, _classnames["default"])('vip-table-component', className),
|
|
34
45
|
ref: forwardRef
|
|
35
|
-
}, props
|
|
46
|
+
}, props, {
|
|
47
|
+
children: [caption && (0, _jsxRuntime.jsx)("caption", {
|
|
48
|
+
sx: _ScreenReaderText.screenReaderTextClass,
|
|
49
|
+
children: caption
|
|
50
|
+
}), children]
|
|
51
|
+
}));
|
|
36
52
|
});
|
|
37
53
|
|
|
38
54
|
exports.Table = Table;
|
|
39
55
|
Table.displayName = 'Table';
|
|
40
56
|
Table.propTypes = {
|
|
41
57
|
sx: _propTypes["default"].object,
|
|
42
|
-
className: _propTypes["default"].any
|
|
58
|
+
className: _propTypes["default"].any,
|
|
59
|
+
children: _propTypes["default"].any,
|
|
60
|
+
caption: _propTypes["default"].string.isRequired
|
|
43
61
|
};
|
|
@@ -7,6 +7,8 @@ var _ = require("..");
|
|
|
7
7
|
|
|
8
8
|
var _jsxRuntime = require("theme-ui/jsx-runtime");
|
|
9
9
|
|
|
10
|
+
/** @jsxImportSource theme-ui */
|
|
11
|
+
|
|
10
12
|
/**
|
|
11
13
|
* Internal dependencies
|
|
12
14
|
*/
|
|
@@ -18,6 +20,7 @@ exports["default"] = _default;
|
|
|
18
20
|
|
|
19
21
|
var Default = function Default() {
|
|
20
22
|
return (0, _jsxRuntime.jsxs)(_.Table, {
|
|
23
|
+
caption: "Storybook Example",
|
|
21
24
|
children: [(0, _jsxRuntime.jsx)("thead", {
|
|
22
25
|
children: (0, _jsxRuntime.jsx)(_.TableRow, {
|
|
23
26
|
head: true,
|
|
@@ -55,7 +58,8 @@ var Default = function Default() {
|
|
|
55
58
|
color: 'muted'
|
|
56
59
|
},
|
|
57
60
|
children: "11th Mar 2020, 16:49:22"
|
|
58
|
-
}, "time")]
|
|
61
|
+
}, "time")],
|
|
62
|
+
gbc: true
|
|
59
63
|
}), (0, _jsxRuntime.jsxs)(_.TableRow, {
|
|
60
64
|
children: [(0, _jsxRuntime.jsx)(_.TableCell, {
|
|
61
65
|
sx: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@automattic/vip-design-system",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.23.0",
|
|
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 } 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,31 @@ const FormSelect = React.forwardRef(
|
|
|
55
68
|
);
|
|
56
69
|
}
|
|
57
70
|
|
|
71
|
+
const optionLabel = useCallback(
|
|
72
|
+
option => ( getOptionLabel ? getOptionLabel( option ) : option.label ),
|
|
73
|
+
[ getOptionLabel ]
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
const optionValue = useCallback(
|
|
77
|
+
option => ( getOptionValue ? getOptionValue( option ) : option.value ),
|
|
78
|
+
[ getOptionValue ]
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
const getOptionByValue = useCallback(
|
|
82
|
+
value => options.find( option => optionValue( option ) === value ),
|
|
83
|
+
[ options, optionValue ]
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
const onValueChange = useCallback(
|
|
87
|
+
event =>
|
|
88
|
+
onChange
|
|
89
|
+
? onChange( getOptionByValue( event.target.value ), event )
|
|
90
|
+
: getOptionByValue( event.target.value ),
|
|
91
|
+
[ onChange, getOptionByValue ]
|
|
92
|
+
);
|
|
93
|
+
|
|
58
94
|
const SelectLabel = () => <Label htmlFor={ forLabel || props.id }>{ label }</Label>;
|
|
95
|
+
|
|
59
96
|
const inlineLabel = !! ( isInline && label );
|
|
60
97
|
|
|
61
98
|
return (
|
|
@@ -63,11 +100,15 @@ const FormSelect = React.forwardRef(
|
|
|
63
100
|
{ label && ! isInline && <SelectLabel /> }
|
|
64
101
|
|
|
65
102
|
<FormSelectContent isInline={ inlineLabel } label={ inlineLabel ? <SelectLabel /> : null }>
|
|
66
|
-
<select ref={ forwardRef } sx={ defaultStyles } { ...props }>
|
|
103
|
+
<select onChange={ onValueChange } ref={ forwardRef } sx={ defaultStyles } { ...props }>
|
|
67
104
|
{ placeholder && <option>{ placeholder }</option> }
|
|
68
|
-
{ options.map( ( {
|
|
69
|
-
|
|
70
|
-
|
|
105
|
+
{ options.map( ( { options: groupOptions, ...option } ) => {
|
|
106
|
+
const value = optionValue( option );
|
|
107
|
+
|
|
108
|
+
return value
|
|
109
|
+
? renderOption( optionLabel( option ), value )
|
|
110
|
+
: renderGroup( optionLabel( option ), groupOptions );
|
|
111
|
+
} ) }
|
|
71
112
|
</select>
|
|
72
113
|
|
|
73
114
|
<FormSelectArrow />
|
|
@@ -84,6 +125,9 @@ FormSelect.propTypes = {
|
|
|
84
125
|
placeholder: PropTypes.string,
|
|
85
126
|
label: PropTypes.string,
|
|
86
127
|
options: PropTypes.array,
|
|
128
|
+
getOptionLabel: PropTypes.func,
|
|
129
|
+
getOptionValue: PropTypes.func,
|
|
130
|
+
onChange: PropTypes.func,
|
|
87
131
|
};
|
|
88
132
|
|
|
89
133
|
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 {
|
|
@@ -26,7 +27,7 @@ const options = [
|
|
|
26
27
|
];
|
|
27
28
|
|
|
28
29
|
// eslint-disable-next-line react/prop-types
|
|
29
|
-
const DefaultComponent = ( { label = 'Label', width = 250, ...rest } ) => (
|
|
30
|
+
const DefaultComponent = ( { label = 'Label', width = 250, onChange, ...rest } ) => (
|
|
30
31
|
<>
|
|
31
32
|
<p>
|
|
32
33
|
This is a simple wrapper at the top of a browser default select component. This component
|
|
@@ -49,7 +50,7 @@ const DefaultComponent = ( { label = 'Label', width = 250, ...rest } ) => (
|
|
|
49
50
|
</p>
|
|
50
51
|
<Form.Root>
|
|
51
52
|
<div sx={ { width } }>
|
|
52
|
-
<Form.Select id="form-select" label={ label } { ...rest } />
|
|
53
|
+
<Form.Select id="form-select" label={ label } onChange={ onChange } { ...rest } />
|
|
53
54
|
</div>
|
|
54
55
|
</Form.Root>
|
|
55
56
|
</>
|
|
@@ -94,3 +95,40 @@ IsInline.args = {
|
|
|
94
95
|
},
|
|
95
96
|
],
|
|
96
97
|
};
|
|
98
|
+
|
|
99
|
+
export const WithOptionLabelAndValue = DefaultComponent.bind( {} );
|
|
100
|
+
|
|
101
|
+
WithOptionLabelAndValue.args = {
|
|
102
|
+
label: 'Select with getOptionLabel / getOptionValue',
|
|
103
|
+
width: '100%',
|
|
104
|
+
options: options.map( ( { label, value } ) => ( {
|
|
105
|
+
name: label,
|
|
106
|
+
id: value,
|
|
107
|
+
} ) ),
|
|
108
|
+
getOptionLabel: option => option.name,
|
|
109
|
+
getOptionValue: option => option.id,
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
export const WithOnChange = () => {
|
|
113
|
+
const [ option, setOption ] = useState( null );
|
|
114
|
+
|
|
115
|
+
const onChange = useCallback( ( val, event ) =>
|
|
116
|
+
setOption( { obj: val, eventValue: event.target.value } )
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
const args = {
|
|
120
|
+
label: 'Select with onChange',
|
|
121
|
+
placeholder: '- Select -',
|
|
122
|
+
width: '100%',
|
|
123
|
+
onChange,
|
|
124
|
+
options,
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
return (
|
|
128
|
+
<>
|
|
129
|
+
<DefaultComponent onChange={ onChange } { ...args } />
|
|
130
|
+
{ option && <p>Object to JSON: { JSON.stringify( option.obj ) }</p> }
|
|
131
|
+
{ option && <p>Original Event Value: { option.eventValue }</p> }
|
|
132
|
+
</>
|
|
133
|
+
);
|
|
134
|
+
};
|
|
@@ -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
|
} );
|
|
@@ -10,24 +10,21 @@ import PropTypes from 'prop-types';
|
|
|
10
10
|
* Internal dependencies
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
+
export const screenReaderTextClass = {
|
|
14
|
+
border: 'none',
|
|
15
|
+
clip: 'rect(1px, 1px, 1px, 1px)',
|
|
16
|
+
clipPath: 'inset(50%)',
|
|
17
|
+
height: '1px',
|
|
18
|
+
margin: '-1px',
|
|
19
|
+
overflow: 'hidden',
|
|
20
|
+
padding: '0',
|
|
21
|
+
position: 'absolute',
|
|
22
|
+
width: '1px',
|
|
23
|
+
wordWrap: 'normal !important',
|
|
24
|
+
};
|
|
25
|
+
|
|
13
26
|
export const ScreenReaderText = React.forwardRef( ( props, forwardRef ) => (
|
|
14
|
-
<span
|
|
15
|
-
className="screen-reader-text"
|
|
16
|
-
sx={ {
|
|
17
|
-
border: 'none',
|
|
18
|
-
clip: 'rect(1px, 1px, 1px, 1px)',
|
|
19
|
-
clipPath: 'inset(50%)',
|
|
20
|
-
height: '1px',
|
|
21
|
-
margin: '-1px',
|
|
22
|
-
overflow: 'hidden',
|
|
23
|
-
padding: '0',
|
|
24
|
-
position: 'absolute',
|
|
25
|
-
width: '1px',
|
|
26
|
-
wordWrap: 'normal !important',
|
|
27
|
-
} }
|
|
28
|
-
{ ...props }
|
|
29
|
-
ref={ forwardRef }
|
|
30
|
-
>
|
|
27
|
+
<span className="screen-reader-text" sx={ screenReaderTextClass } { ...props } ref={ forwardRef }>
|
|
31
28
|
{ props.children }
|
|
32
29
|
</span>
|
|
33
30
|
) );
|
|
@@ -6,23 +6,38 @@
|
|
|
6
6
|
import React from 'react';
|
|
7
7
|
import PropTypes from 'prop-types';
|
|
8
8
|
import classNames from 'classnames';
|
|
9
|
+
import { screenReaderTextClass } from '../ScreenReaderText/ScreenReaderText';
|
|
9
10
|
|
|
10
|
-
const Table = React.forwardRef(
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
11
|
+
const Table = React.forwardRef(
|
|
12
|
+
( { sx, className, children, caption = null, ...props }, forwardRef ) => {
|
|
13
|
+
if ( ! caption ) {
|
|
14
|
+
// eslint-disable-next-line no-console
|
|
15
|
+
console.warn( '[A11Y] Please, add a caption to your table.' );
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
<table
|
|
20
|
+
sx={ { width: '100%', minWidth: 1024, ...sx } }
|
|
21
|
+
cellPadding={ 0 }
|
|
22
|
+
cellSpacing={ 0 }
|
|
23
|
+
className={ classNames( 'vip-table-component', className ) }
|
|
24
|
+
ref={ forwardRef }
|
|
25
|
+
{ ...props }
|
|
26
|
+
>
|
|
27
|
+
{ caption && <caption sx={ screenReaderTextClass }>{ caption }</caption> }
|
|
28
|
+
{ children }
|
|
29
|
+
</table>
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
);
|
|
20
33
|
|
|
21
34
|
Table.displayName = 'Table';
|
|
22
35
|
|
|
23
36
|
Table.propTypes = {
|
|
24
37
|
sx: PropTypes.object,
|
|
25
38
|
className: PropTypes.any,
|
|
39
|
+
children: PropTypes.any,
|
|
40
|
+
caption: PropTypes.string.isRequired,
|
|
26
41
|
};
|
|
27
42
|
|
|
28
43
|
export { Table };
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
/** @jsxImportSource theme-ui */
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Internal dependencies
|
|
3
5
|
*/
|
|
@@ -9,7 +11,7 @@ export default {
|
|
|
9
11
|
};
|
|
10
12
|
|
|
11
13
|
export const Default = () => (
|
|
12
|
-
<Table>
|
|
14
|
+
<Table caption="Storybook Example">
|
|
13
15
|
<thead>
|
|
14
16
|
<TableRow head cells={ [ 'User', 'Command', 'Duration', 'Time' ] } />
|
|
15
17
|
</thead>
|
|
@@ -35,6 +37,7 @@ export const Default = () => (
|
|
|
35
37
|
11th Mar 2020, 16:49:22
|
|
36
38
|
</Text>,
|
|
37
39
|
] }
|
|
40
|
+
gbc
|
|
38
41
|
/>
|
|
39
42
|
<TableRow>
|
|
40
43
|
<TableCell sx={ { backgroundColor: 'lightgray' } }>
|