@automattic/vip-design-system 2.9.0 → 2.10.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/Form/RadioBoxGroup.js +7 -107
- package/build/system/Form/RadioBoxGroup.stories.d.ts +0 -1
- package/build/system/Form/RadioBoxGroup.stories.jsx +1 -27
- package/build/system/Form/RadioBoxGroup.test.jsx +2 -2
- package/build/system/Form/RadioGroupChip.d.ts +25 -0
- package/build/system/Form/RadioGroupChip.js +169 -0
- package/build/system/Form/RadioGroupChip.stories.d.ts +35 -0
- package/build/system/Form/RadioGroupChip.stories.js +61 -0
- package/build/system/Form/RadioGroupChip.test.d.ts +1 -0
- package/build/system/Form/RadioGroupChip.test.js +57 -0
- package/build/system/index.d.ts +2 -1
- package/build/system/index.js +2 -0
- package/package.json +1 -1
- package/src/system/Form/RadioBoxGroup.js +7 -107
- package/src/system/Form/RadioBoxGroup.stories.jsx +1 -27
- package/src/system/Form/RadioBoxGroup.test.jsx +2 -2
- package/src/system/Form/RadioGroupChip.stories.tsx +73 -0
- package/src/system/Form/RadioGroupChip.test.tsx +44 -0
- package/src/system/Form/RadioGroupChip.tsx +216 -0
- package/src/system/index.js +2 -0
|
@@ -39,6 +39,7 @@ const RadioOption = ( {
|
|
|
39
39
|
cursor: 'pointer',
|
|
40
40
|
borderRadius: 2,
|
|
41
41
|
minWidth: 220,
|
|
42
|
+
flexGrow: 1,
|
|
42
43
|
textAlign: 'left',
|
|
43
44
|
border: '1px solid',
|
|
44
45
|
borderColor: 'input.radio-box.border.default',
|
|
@@ -94,103 +95,6 @@ const RadioOption = ( {
|
|
|
94
95
|
);
|
|
95
96
|
};
|
|
96
97
|
|
|
97
|
-
const ChipOption = ( {
|
|
98
|
-
defaultValue,
|
|
99
|
-
option: { id, value, label },
|
|
100
|
-
name,
|
|
101
|
-
disabled,
|
|
102
|
-
onChangeHandler,
|
|
103
|
-
} ) => {
|
|
104
|
-
const checked = `${ defaultValue }` === `${ value }`;
|
|
105
|
-
const forLabel = id || value;
|
|
106
|
-
const ref = React.useRef( null );
|
|
107
|
-
const describedById = `input-radio-box-${ forLabel }-description`;
|
|
108
|
-
|
|
109
|
-
return (
|
|
110
|
-
<div
|
|
111
|
-
id={ `o${ forLabel }` }
|
|
112
|
-
onClick={ () => {
|
|
113
|
-
ref.current?.click();
|
|
114
|
-
} }
|
|
115
|
-
sx={ {
|
|
116
|
-
display: 'inline-flex',
|
|
117
|
-
position: 'relative',
|
|
118
|
-
background: checked ? 'layer.4' : undefined,
|
|
119
|
-
color: 'text',
|
|
120
|
-
minHeight: '32px',
|
|
121
|
-
boxShadow: checked ? 'low' : undefined,
|
|
122
|
-
'&:hover': {
|
|
123
|
-
background: checked ? 'layer.4' : 'layer.1',
|
|
124
|
-
},
|
|
125
|
-
borderRadius: 1,
|
|
126
|
-
} }
|
|
127
|
-
>
|
|
128
|
-
<input
|
|
129
|
-
ref={ ref }
|
|
130
|
-
type="radio"
|
|
131
|
-
id={ forLabel }
|
|
132
|
-
disabled={ disabled }
|
|
133
|
-
name={ name }
|
|
134
|
-
checked={ checked }
|
|
135
|
-
aria-checked={ checked }
|
|
136
|
-
value={ value }
|
|
137
|
-
onChange={ onChangeHandler }
|
|
138
|
-
aria-labelledby={ describedById }
|
|
139
|
-
sx={ {
|
|
140
|
-
opacity: 0,
|
|
141
|
-
height: 0,
|
|
142
|
-
width: 0,
|
|
143
|
-
position: 'absolute',
|
|
144
|
-
'&:focus-visible + label': theme => theme.outline,
|
|
145
|
-
} }
|
|
146
|
-
/>
|
|
147
|
-
|
|
148
|
-
<label
|
|
149
|
-
id={ describedById }
|
|
150
|
-
htmlFor={ forLabel }
|
|
151
|
-
sx={ {
|
|
152
|
-
height: '100%',
|
|
153
|
-
display: 'flex',
|
|
154
|
-
flexDirection: 'column',
|
|
155
|
-
justifyContent: 'center',
|
|
156
|
-
width: '100%',
|
|
157
|
-
px: 3,
|
|
158
|
-
fontWeight: 400,
|
|
159
|
-
fontSize: 2,
|
|
160
|
-
cursor: 'pointer',
|
|
161
|
-
borderRadius: 1,
|
|
162
|
-
} }
|
|
163
|
-
>
|
|
164
|
-
{ label }
|
|
165
|
-
</label>
|
|
166
|
-
</div>
|
|
167
|
-
);
|
|
168
|
-
};
|
|
169
|
-
|
|
170
|
-
ChipOption.propTypes = RadioOption.propTypes = {
|
|
171
|
-
defaultValue: PropTypes.string,
|
|
172
|
-
option: PropTypes.object,
|
|
173
|
-
name: PropTypes.string,
|
|
174
|
-
onChangeHandler: PropTypes.func,
|
|
175
|
-
disabled: PropTypes.bool,
|
|
176
|
-
width: PropTypes.string,
|
|
177
|
-
};
|
|
178
|
-
|
|
179
|
-
const groupStyleOverrides = {
|
|
180
|
-
chip: {
|
|
181
|
-
background: 'layer.3',
|
|
182
|
-
p: 1,
|
|
183
|
-
display: 'inline-flex',
|
|
184
|
-
gap: 1,
|
|
185
|
-
borderRadius: 1,
|
|
186
|
-
},
|
|
187
|
-
primary: {
|
|
188
|
-
display: 'inline-block',
|
|
189
|
-
mb: 2,
|
|
190
|
-
p: 0,
|
|
191
|
-
},
|
|
192
|
-
};
|
|
193
|
-
|
|
194
98
|
const RadioBoxGroup = React.forwardRef(
|
|
195
99
|
(
|
|
196
100
|
{
|
|
@@ -204,7 +108,6 @@ const RadioBoxGroup = React.forwardRef(
|
|
|
204
108
|
errorMessage,
|
|
205
109
|
hasError,
|
|
206
110
|
required,
|
|
207
|
-
variant = 'primary',
|
|
208
111
|
...props
|
|
209
112
|
},
|
|
210
113
|
forwardRef
|
|
@@ -219,13 +122,8 @@ const RadioBoxGroup = React.forwardRef(
|
|
|
219
122
|
[ onChange ]
|
|
220
123
|
);
|
|
221
124
|
|
|
222
|
-
let Option = RadioOption;
|
|
223
|
-
if ( variant === 'chip' ) {
|
|
224
|
-
Option = ChipOption;
|
|
225
|
-
}
|
|
226
|
-
|
|
227
125
|
const renderedOptions = options.map( option => (
|
|
228
|
-
<
|
|
126
|
+
<RadioOption
|
|
229
127
|
defaultValue={ defaultValue }
|
|
230
128
|
disabled={ disabled }
|
|
231
129
|
key={ option?.id || option?.value }
|
|
@@ -241,7 +139,9 @@ const RadioBoxGroup = React.forwardRef(
|
|
|
241
139
|
<fieldset
|
|
242
140
|
sx={ {
|
|
243
141
|
border: 0,
|
|
244
|
-
|
|
142
|
+
display: 'inline-block',
|
|
143
|
+
mb: 2,
|
|
144
|
+
p: 0,
|
|
245
145
|
...( hasError
|
|
246
146
|
? { border: '1px solid', borderColor: 'input.border.error', borderRadius: 2, p: 2 }
|
|
247
147
|
: {} ),
|
|
@@ -262,7 +162,8 @@ const RadioBoxGroup = React.forwardRef(
|
|
|
262
162
|
<div
|
|
263
163
|
sx={ {
|
|
264
164
|
display: 'flex',
|
|
265
|
-
|
|
165
|
+
flexWrap: 'wrap',
|
|
166
|
+
gap: 2,
|
|
266
167
|
} }
|
|
267
168
|
>
|
|
268
169
|
{ renderedOptions }
|
|
@@ -293,7 +194,6 @@ RadioBoxGroup.propTypes = {
|
|
|
293
194
|
errorMessage: PropTypes.string,
|
|
294
195
|
hasError: PropTypes.bool,
|
|
295
196
|
required: PropTypes.bool,
|
|
296
|
-
variant: PropTypes.oneOf( [ 'primary', 'chip' ] ),
|
|
297
197
|
};
|
|
298
198
|
|
|
299
199
|
export { RadioBoxGroup };
|
|
@@ -12,5 +12,4 @@ declare namespace _default {
|
|
|
12
12
|
export default _default;
|
|
13
13
|
export function Default(): import("react").JSX.Element;
|
|
14
14
|
export function Errors(): import("react").JSX.Element;
|
|
15
|
-
export function ChipVariant(): import("react").JSX.Element;
|
|
16
15
|
import { RadioBoxGroup } from '..';
|
|
@@ -63,7 +63,7 @@ export const Default = () => {
|
|
|
63
63
|
defaultValue={ value }
|
|
64
64
|
onChange={ e => setValue( e.target.value ) }
|
|
65
65
|
options={ options }
|
|
66
|
-
optionWidth="
|
|
66
|
+
optionWidth="300px"
|
|
67
67
|
/>
|
|
68
68
|
);
|
|
69
69
|
};
|
|
@@ -83,29 +83,3 @@ export const Errors = () => {
|
|
|
83
83
|
/>
|
|
84
84
|
);
|
|
85
85
|
};
|
|
86
|
-
|
|
87
|
-
export const ChipVariant = () => {
|
|
88
|
-
const [ value, setValue ] = useState( 'table' );
|
|
89
|
-
|
|
90
|
-
return (
|
|
91
|
-
<RadioBoxGroup
|
|
92
|
-
defaultValue={ value }
|
|
93
|
-
onChange={ e => setValue( e.target.value ) }
|
|
94
|
-
options={ [
|
|
95
|
-
{
|
|
96
|
-
label: 'Table',
|
|
97
|
-
value: 'table',
|
|
98
|
-
},
|
|
99
|
-
{
|
|
100
|
-
label: 'Grid',
|
|
101
|
-
value: 'grid',
|
|
102
|
-
},
|
|
103
|
-
{
|
|
104
|
-
label: 'Card',
|
|
105
|
-
value: 'card',
|
|
106
|
-
},
|
|
107
|
-
] }
|
|
108
|
-
variant="chip"
|
|
109
|
-
/>
|
|
110
|
-
);
|
|
111
|
-
};
|
|
@@ -31,8 +31,8 @@ const defaultProps = {
|
|
|
31
31
|
};
|
|
32
32
|
|
|
33
33
|
describe( '<RadioBoxGroup />', () => {
|
|
34
|
-
it
|
|
35
|
-
const { container } = render( <RadioBoxGroup { ...defaultProps }
|
|
34
|
+
it( 'renders the component', async () => {
|
|
35
|
+
const { container } = render( <RadioBoxGroup { ...defaultProps } /> );
|
|
36
36
|
|
|
37
37
|
const dom = await screen.findAllByRole( 'radio' );
|
|
38
38
|
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** @jsxImportSource theme-ui */
|
|
2
|
+
/**
|
|
3
|
+
* External dependencies
|
|
4
|
+
*/
|
|
5
|
+
import React from 'react';
|
|
6
|
+
type Option = {
|
|
7
|
+
id?: string;
|
|
8
|
+
value: string;
|
|
9
|
+
label: React.ReactNode | string;
|
|
10
|
+
};
|
|
11
|
+
type RadioGroupChipProps = {
|
|
12
|
+
optionWidth?: string;
|
|
13
|
+
name?: string;
|
|
14
|
+
onChange: (e: React.ChangeEvent<HTMLInputElement>, option?: Option) => void;
|
|
15
|
+
groupLabel?: string;
|
|
16
|
+
defaultValue?: string;
|
|
17
|
+
options: Option[];
|
|
18
|
+
disabled?: boolean;
|
|
19
|
+
errorMessage?: string;
|
|
20
|
+
hasError?: boolean;
|
|
21
|
+
required?: boolean;
|
|
22
|
+
size?: 'small' | 'medium';
|
|
23
|
+
};
|
|
24
|
+
declare const RadioGroupChip: React.ForwardRefExoticComponent<RadioGroupChipProps & React.RefAttributes<unknown>>;
|
|
25
|
+
export { RadioGroupChip };
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
exports.__esModule = true;
|
|
4
|
+
exports.RadioGroupChip = void 0;
|
|
5
|
+
var _react = _interopRequireWildcard(require("react"));
|
|
6
|
+
var _RequiredLabel = require("./RequiredLabel");
|
|
7
|
+
var _Validation = require("./Validation");
|
|
8
|
+
var _ScreenReaderText = _interopRequireDefault(require("../ScreenReaderText"));
|
|
9
|
+
var _jsxRuntime = require("theme-ui/jsx-runtime");
|
|
10
|
+
var _excluded = ["name", "onChange", "groupLabel", "defaultValue", "options", "disabled", "errorMessage", "hasError", "required", "size"];
|
|
11
|
+
/** @jsxImportSource theme-ui */
|
|
12
|
+
/**
|
|
13
|
+
* External dependencies
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* Internal dependencies
|
|
17
|
+
*/
|
|
18
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
|
|
19
|
+
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); }
|
|
20
|
+
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { "default": e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n["default"] = e, t && t.set(e, n), n; }
|
|
21
|
+
function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
|
|
22
|
+
function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
|
|
23
|
+
var ChipOption = function ChipOption(_ref) {
|
|
24
|
+
var defaultValue = _ref.defaultValue,
|
|
25
|
+
_ref$option = _ref.option,
|
|
26
|
+
id = _ref$option.id,
|
|
27
|
+
value = _ref$option.value,
|
|
28
|
+
label = _ref$option.label,
|
|
29
|
+
name = _ref.name,
|
|
30
|
+
disabled = _ref.disabled,
|
|
31
|
+
onChangeHandler = _ref.onChangeHandler,
|
|
32
|
+
_ref$size = _ref.size,
|
|
33
|
+
size = _ref$size === void 0 ? 'medium' : _ref$size;
|
|
34
|
+
var checked = "" + defaultValue === "" + value;
|
|
35
|
+
var forLabel = id || value;
|
|
36
|
+
var ref = _react["default"].useRef(null);
|
|
37
|
+
var describedById = "input-radio-box-" + forLabel + "-description";
|
|
38
|
+
return (0, _jsxRuntime.jsxs)("div", {
|
|
39
|
+
id: "o" + forLabel,
|
|
40
|
+
onClick: function onClick() {
|
|
41
|
+
if (ref.current) {
|
|
42
|
+
ref.current.click();
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
sx: {
|
|
46
|
+
display: 'inline-flex',
|
|
47
|
+
position: 'relative',
|
|
48
|
+
background: checked ? 'layer.4' : undefined,
|
|
49
|
+
color: 'text',
|
|
50
|
+
minHeight: size === 'small' ? '22px' : '32px',
|
|
51
|
+
boxShadow: checked ? 'low' : undefined,
|
|
52
|
+
'&:hover': {
|
|
53
|
+
background: checked ? 'layer.4' : 'layer.1'
|
|
54
|
+
},
|
|
55
|
+
borderRadius: 1
|
|
56
|
+
},
|
|
57
|
+
children: [(0, _jsxRuntime.jsx)("input", {
|
|
58
|
+
ref: ref,
|
|
59
|
+
type: "radio",
|
|
60
|
+
id: forLabel,
|
|
61
|
+
disabled: disabled,
|
|
62
|
+
name: name,
|
|
63
|
+
checked: checked,
|
|
64
|
+
"aria-checked": checked,
|
|
65
|
+
value: value,
|
|
66
|
+
onChange: onChangeHandler,
|
|
67
|
+
"aria-labelledby": describedById,
|
|
68
|
+
sx: {
|
|
69
|
+
opacity: 0,
|
|
70
|
+
height: 0,
|
|
71
|
+
width: 0,
|
|
72
|
+
position: 'absolute',
|
|
73
|
+
'&:focus-visible + label': function focusVisibleLabel(theme) {
|
|
74
|
+
return theme.outline;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}), (0, _jsxRuntime.jsx)("label", {
|
|
78
|
+
id: describedById,
|
|
79
|
+
htmlFor: forLabel,
|
|
80
|
+
sx: {
|
|
81
|
+
height: '100%',
|
|
82
|
+
display: 'flex',
|
|
83
|
+
flexDirection: 'column',
|
|
84
|
+
justifyContent: 'center',
|
|
85
|
+
width: '100%',
|
|
86
|
+
px: size === 'small' ? 1 : 3,
|
|
87
|
+
fontWeight: 400,
|
|
88
|
+
fontSize: size === 'small' ? 1 : 2,
|
|
89
|
+
cursor: 'pointer',
|
|
90
|
+
borderRadius: 1
|
|
91
|
+
},
|
|
92
|
+
children: label
|
|
93
|
+
})]
|
|
94
|
+
});
|
|
95
|
+
};
|
|
96
|
+
var RadioGroupChip = exports.RadioGroupChip = /*#__PURE__*/_react["default"].forwardRef(function (_ref2, forwardRef) {
|
|
97
|
+
var _ref2$name = _ref2.name,
|
|
98
|
+
name = _ref2$name === void 0 ? '' : _ref2$name,
|
|
99
|
+
onChange = _ref2.onChange,
|
|
100
|
+
groupLabel = _ref2.groupLabel,
|
|
101
|
+
defaultValue = _ref2.defaultValue,
|
|
102
|
+
options = _ref2.options,
|
|
103
|
+
disabled = _ref2.disabled,
|
|
104
|
+
errorMessage = _ref2.errorMessage,
|
|
105
|
+
hasError = _ref2.hasError,
|
|
106
|
+
required = _ref2.required,
|
|
107
|
+
_ref2$size = _ref2.size,
|
|
108
|
+
size = _ref2$size === void 0 ? 'medium' : _ref2$size,
|
|
109
|
+
props = _objectWithoutPropertiesLoose(_ref2, _excluded);
|
|
110
|
+
var onChangeHandler = (0, _react.useCallback)(function (e) {
|
|
111
|
+
var optionTriggered = options.find(function (option) {
|
|
112
|
+
return "" + option.value === "" + e.target.value;
|
|
113
|
+
});
|
|
114
|
+
onChange(e, optionTriggered);
|
|
115
|
+
}, [onChange]);
|
|
116
|
+
var renderedOptions = options.map(function (option) {
|
|
117
|
+
return (0, _jsxRuntime.jsx)(ChipOption, {
|
|
118
|
+
defaultValue: defaultValue,
|
|
119
|
+
disabled: disabled,
|
|
120
|
+
name: name,
|
|
121
|
+
option: option,
|
|
122
|
+
onChangeHandler: onChangeHandler,
|
|
123
|
+
size: size
|
|
124
|
+
}, (option == null ? void 0 : option.id) || (option == null ? void 0 : option.value));
|
|
125
|
+
});
|
|
126
|
+
return (0, _jsxRuntime.jsxs)("div", {
|
|
127
|
+
children: [(0, _jsxRuntime.jsxs)("fieldset", _extends({
|
|
128
|
+
sx: _extends({
|
|
129
|
+
border: 0,
|
|
130
|
+
background: 'layer.3',
|
|
131
|
+
p: size === 'small' ? '2px' : 1,
|
|
132
|
+
display: 'inline-flex',
|
|
133
|
+
gap: 1,
|
|
134
|
+
borderRadius: 1
|
|
135
|
+
}, hasError ? {
|
|
136
|
+
border: '1px solid',
|
|
137
|
+
borderColor: 'input.border.error',
|
|
138
|
+
borderRadius: 2,
|
|
139
|
+
p: 2
|
|
140
|
+
} : {})
|
|
141
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
142
|
+
// @ts-expect-error
|
|
143
|
+
,
|
|
144
|
+
ref: forwardRef,
|
|
145
|
+
"aria-required": required,
|
|
146
|
+
role: "radiogroup"
|
|
147
|
+
}, props, {
|
|
148
|
+
children: [groupLabel ? (0, _jsxRuntime.jsxs)("legend", {
|
|
149
|
+
sx: {
|
|
150
|
+
mb: 2
|
|
151
|
+
},
|
|
152
|
+
children: [groupLabel, required ? (0, _jsxRuntime.jsx)(_RequiredLabel.RequiredLabel, {}) : null]
|
|
153
|
+
}) : (0, _jsxRuntime.jsx)(_ScreenReaderText["default"], {
|
|
154
|
+
children: "Choose an option"
|
|
155
|
+
}), (0, _jsxRuntime.jsx)("div", {
|
|
156
|
+
sx: {
|
|
157
|
+
display: 'flex',
|
|
158
|
+
gap: 1
|
|
159
|
+
},
|
|
160
|
+
children: renderedOptions
|
|
161
|
+
})]
|
|
162
|
+
})), hasError && errorMessage && (0, _jsxRuntime.jsx)(_Validation.Validation, {
|
|
163
|
+
isValid: false,
|
|
164
|
+
describedId: groupLabel,
|
|
165
|
+
children: errorMessage
|
|
166
|
+
})]
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
RadioGroupChip.displayName = 'RadioGroupChip';
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
declare const _default: {
|
|
3
|
+
title: string;
|
|
4
|
+
component: import("react").ForwardRefExoticComponent<{
|
|
5
|
+
optionWidth?: string | undefined;
|
|
6
|
+
name?: string | undefined;
|
|
7
|
+
onChange: (e: import("react").ChangeEvent<HTMLInputElement>, option?: {
|
|
8
|
+
id?: string | undefined;
|
|
9
|
+
value: string;
|
|
10
|
+
label: import("react").ReactNode;
|
|
11
|
+
} | undefined) => void;
|
|
12
|
+
groupLabel?: string | undefined;
|
|
13
|
+
defaultValue?: string | undefined;
|
|
14
|
+
options: {
|
|
15
|
+
id?: string | undefined;
|
|
16
|
+
value: string;
|
|
17
|
+
label: import("react").ReactNode;
|
|
18
|
+
}[];
|
|
19
|
+
disabled?: boolean | undefined;
|
|
20
|
+
errorMessage?: string | undefined;
|
|
21
|
+
hasError?: boolean | undefined;
|
|
22
|
+
required?: boolean | undefined;
|
|
23
|
+
size?: "small" | "medium" | undefined;
|
|
24
|
+
} & import("react").RefAttributes<unknown>>;
|
|
25
|
+
parameters: {
|
|
26
|
+
docs: {
|
|
27
|
+
description: {
|
|
28
|
+
component: string;
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
export default _default;
|
|
34
|
+
export declare const MediumSize: () => import("react").JSX.Element;
|
|
35
|
+
export declare const SmallSize: () => import("react").JSX.Element;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
exports.__esModule = true;
|
|
4
|
+
exports["default"] = exports.SmallSize = exports.MediumSize = void 0;
|
|
5
|
+
var _react = require("react");
|
|
6
|
+
var _RadioGroupChip = require("./RadioGroupChip");
|
|
7
|
+
var _jsxRuntime = require("theme-ui/jsx-runtime");
|
|
8
|
+
/**
|
|
9
|
+
* External dependencies
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Internal dependencies
|
|
13
|
+
*/
|
|
14
|
+
var _default = exports["default"] = {
|
|
15
|
+
title: 'RadioGroupChip',
|
|
16
|
+
component: _RadioGroupChip.RadioGroupChip,
|
|
17
|
+
parameters: {
|
|
18
|
+
docs: {
|
|
19
|
+
description: {
|
|
20
|
+
component: "\nA radio-group-chip is a group of radio buttons that are styled as boxes. This component is used\nto allow users to toggle between different options\n\n-------\n\nThis documentation is heavily inspired by the [U.S Web Design System (USWDS)](https://designsystem.digital.gov/components/tooltip/#package). We use USWDS as trusted source of truth for accessibility and usability best practices.\n\n## Component Properties\n"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
var MediumSize = exports.MediumSize = function MediumSize() {
|
|
26
|
+
var _useState = (0, _react.useState)('table'),
|
|
27
|
+
value = _useState[0],
|
|
28
|
+
setValue = _useState[1];
|
|
29
|
+
return (0, _jsxRuntime.jsx)(_RadioGroupChip.RadioGroupChip, {
|
|
30
|
+
defaultValue: value,
|
|
31
|
+
onChange: function onChange(e) {
|
|
32
|
+
return setValue(e.target.value);
|
|
33
|
+
},
|
|
34
|
+
options: [{
|
|
35
|
+
label: 'Table',
|
|
36
|
+
value: 'table'
|
|
37
|
+
}, {
|
|
38
|
+
label: 'Grid',
|
|
39
|
+
value: 'grid'
|
|
40
|
+
}]
|
|
41
|
+
});
|
|
42
|
+
};
|
|
43
|
+
var SmallSize = exports.SmallSize = function SmallSize() {
|
|
44
|
+
var _useState2 = (0, _react.useState)('table'),
|
|
45
|
+
value = _useState2[0],
|
|
46
|
+
setValue = _useState2[1];
|
|
47
|
+
return (0, _jsxRuntime.jsx)(_RadioGroupChip.RadioGroupChip, {
|
|
48
|
+
defaultValue: value,
|
|
49
|
+
onChange: function onChange(e) {
|
|
50
|
+
return setValue(e.target.value);
|
|
51
|
+
},
|
|
52
|
+
options: [{
|
|
53
|
+
label: 'Table',
|
|
54
|
+
value: 'table'
|
|
55
|
+
}, {
|
|
56
|
+
label: 'Grid',
|
|
57
|
+
value: 'grid'
|
|
58
|
+
}],
|
|
59
|
+
size: "small"
|
|
60
|
+
});
|
|
61
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _react = require("@testing-library/react");
|
|
4
|
+
var _jestAxe = require("jest-axe");
|
|
5
|
+
var _RadioGroupChip = require("./RadioGroupChip");
|
|
6
|
+
var _jsxRuntime = require("theme-ui/jsx-runtime");
|
|
7
|
+
function _regeneratorRuntime() { "use strict"; /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */ _regeneratorRuntime = function _regeneratorRuntime() { return e; }; var t, e = {}, r = Object.prototype, n = r.hasOwnProperty, o = Object.defineProperty || function (t, e, r) { t[e] = r.value; }, i = "function" == typeof Symbol ? Symbol : {}, a = i.iterator || "@@iterator", c = i.asyncIterator || "@@asyncIterator", u = i.toStringTag || "@@toStringTag"; function define(t, e, r) { return Object.defineProperty(t, e, { value: r, enumerable: !0, configurable: !0, writable: !0 }), t[e]; } try { define({}, ""); } catch (t) { define = function define(t, e, r) { return t[e] = r; }; } function wrap(t, e, r, n) { var i = e && e.prototype instanceof Generator ? e : Generator, a = Object.create(i.prototype), c = new Context(n || []); return o(a, "_invoke", { value: makeInvokeMethod(t, r, c) }), a; } function tryCatch(t, e, r) { try { return { type: "normal", arg: t.call(e, r) }; } catch (t) { return { type: "throw", arg: t }; } } e.wrap = wrap; var h = "suspendedStart", l = "suspendedYield", f = "executing", s = "completed", y = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} var p = {}; define(p, a, function () { return this; }); var d = Object.getPrototypeOf, v = d && d(d(values([]))); v && v !== r && n.call(v, a) && (p = v); var g = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(p); function defineIteratorMethods(t) { ["next", "throw", "return"].forEach(function (e) { define(t, e, function (t) { return this._invoke(e, t); }); }); } function AsyncIterator(t, e) { function invoke(r, o, i, a) { var c = tryCatch(t[r], t, o); if ("throw" !== c.type) { var u = c.arg, h = u.value; return h && "object" == typeof h && n.call(h, "__await") ? e.resolve(h.__await).then(function (t) { invoke("next", t, i, a); }, function (t) { invoke("throw", t, i, a); }) : e.resolve(h).then(function (t) { u.value = t, i(u); }, function (t) { return invoke("throw", t, i, a); }); } a(c.arg); } var r; o(this, "_invoke", { value: function value(t, n) { function callInvokeWithMethodAndArg() { return new e(function (e, r) { invoke(t, n, e, r); }); } return r = r ? r.then(callInvokeWithMethodAndArg, callInvokeWithMethodAndArg) : callInvokeWithMethodAndArg(); } }); } function makeInvokeMethod(e, r, n) { var o = h; return function (i, a) { if (o === f) throw new Error("Generator is already running"); if (o === s) { if ("throw" === i) throw a; return { value: t, done: !0 }; } for (n.method = i, n.arg = a;;) { var c = n.delegate; if (c) { var u = maybeInvokeDelegate(c, n); if (u) { if (u === y) continue; return u; } } if ("next" === n.method) n.sent = n._sent = n.arg;else if ("throw" === n.method) { if (o === h) throw o = s, n.arg; n.dispatchException(n.arg); } else "return" === n.method && n.abrupt("return", n.arg); o = f; var p = tryCatch(e, r, n); if ("normal" === p.type) { if (o = n.done ? s : l, p.arg === y) continue; return { value: p.arg, done: n.done }; } "throw" === p.type && (o = s, n.method = "throw", n.arg = p.arg); } }; } function maybeInvokeDelegate(e, r) { var n = r.method, o = e.iterator[n]; if (o === t) return r.delegate = null, "throw" === n && e.iterator["return"] && (r.method = "return", r.arg = t, maybeInvokeDelegate(e, r), "throw" === r.method) || "return" !== n && (r.method = "throw", r.arg = new TypeError("The iterator does not provide a '" + n + "' method")), y; var i = tryCatch(o, e.iterator, r.arg); if ("throw" === i.type) return r.method = "throw", r.arg = i.arg, r.delegate = null, y; var a = i.arg; return a ? a.done ? (r[e.resultName] = a.value, r.next = e.nextLoc, "return" !== r.method && (r.method = "next", r.arg = t), r.delegate = null, y) : a : (r.method = "throw", r.arg = new TypeError("iterator result is not an object"), r.delegate = null, y); } function pushTryEntry(t) { var e = { tryLoc: t[0] }; 1 in t && (e.catchLoc = t[1]), 2 in t && (e.finallyLoc = t[2], e.afterLoc = t[3]), this.tryEntries.push(e); } function resetTryEntry(t) { var e = t.completion || {}; e.type = "normal", delete e.arg, t.completion = e; } function Context(t) { this.tryEntries = [{ tryLoc: "root" }], t.forEach(pushTryEntry, this), this.reset(!0); } function values(e) { if (e || "" === e) { var r = e[a]; if (r) return r.call(e); if ("function" == typeof e.next) return e; if (!isNaN(e.length)) { var o = -1, i = function next() { for (; ++o < e.length;) if (n.call(e, o)) return next.value = e[o], next.done = !1, next; return next.value = t, next.done = !0, next; }; return i.next = i; } } throw new TypeError(typeof e + " is not iterable"); } return GeneratorFunction.prototype = GeneratorFunctionPrototype, o(g, "constructor", { value: GeneratorFunctionPrototype, configurable: !0 }), o(GeneratorFunctionPrototype, "constructor", { value: GeneratorFunction, configurable: !0 }), GeneratorFunction.displayName = define(GeneratorFunctionPrototype, u, "GeneratorFunction"), e.isGeneratorFunction = function (t) { var e = "function" == typeof t && t.constructor; return !!e && (e === GeneratorFunction || "GeneratorFunction" === (e.displayName || e.name)); }, e.mark = function (t) { return Object.setPrototypeOf ? Object.setPrototypeOf(t, GeneratorFunctionPrototype) : (t.__proto__ = GeneratorFunctionPrototype, define(t, u, "GeneratorFunction")), t.prototype = Object.create(g), t; }, e.awrap = function (t) { return { __await: t }; }, defineIteratorMethods(AsyncIterator.prototype), define(AsyncIterator.prototype, c, function () { return this; }), e.AsyncIterator = AsyncIterator, e.async = function (t, r, n, o, i) { void 0 === i && (i = Promise); var a = new AsyncIterator(wrap(t, r, n, o), i); return e.isGeneratorFunction(r) ? a : a.next().then(function (t) { return t.done ? t.value : a.next(); }); }, defineIteratorMethods(g), define(g, u, "Generator"), define(g, a, function () { return this; }), define(g, "toString", function () { return "[object Generator]"; }), e.keys = function (t) { var e = Object(t), r = []; for (var n in e) r.push(n); return r.reverse(), function next() { for (; r.length;) { var t = r.pop(); if (t in e) return next.value = t, next.done = !1, next; } return next.done = !0, next; }; }, e.values = values, Context.prototype = { constructor: Context, reset: function reset(e) { if (this.prev = 0, this.next = 0, this.sent = this._sent = t, this.done = !1, this.delegate = null, this.method = "next", this.arg = t, this.tryEntries.forEach(resetTryEntry), !e) for (var r in this) "t" === r.charAt(0) && n.call(this, r) && !isNaN(+r.slice(1)) && (this[r] = t); }, stop: function stop() { this.done = !0; var t = this.tryEntries[0].completion; if ("throw" === t.type) throw t.arg; return this.rval; }, dispatchException: function dispatchException(e) { if (this.done) throw e; var r = this; function handle(n, o) { return a.type = "throw", a.arg = e, r.next = n, o && (r.method = "next", r.arg = t), !!o; } for (var o = this.tryEntries.length - 1; o >= 0; --o) { var i = this.tryEntries[o], a = i.completion; if ("root" === i.tryLoc) return handle("end"); if (i.tryLoc <= this.prev) { var c = n.call(i, "catchLoc"), u = n.call(i, "finallyLoc"); if (c && u) { if (this.prev < i.catchLoc) return handle(i.catchLoc, !0); if (this.prev < i.finallyLoc) return handle(i.finallyLoc); } else if (c) { if (this.prev < i.catchLoc) return handle(i.catchLoc, !0); } else { if (!u) throw new Error("try statement without catch or finally"); if (this.prev < i.finallyLoc) return handle(i.finallyLoc); } } } }, abrupt: function abrupt(t, e) { for (var r = this.tryEntries.length - 1; r >= 0; --r) { var o = this.tryEntries[r]; if (o.tryLoc <= this.prev && n.call(o, "finallyLoc") && this.prev < o.finallyLoc) { var i = o; break; } } i && ("break" === t || "continue" === t) && i.tryLoc <= e && e <= i.finallyLoc && (i = null); var a = i ? i.completion : {}; return a.type = t, a.arg = e, i ? (this.method = "next", this.next = i.finallyLoc, y) : this.complete(a); }, complete: function complete(t, e) { if ("throw" === t.type) throw t.arg; return "break" === t.type || "continue" === t.type ? this.next = t.arg : "return" === t.type ? (this.rval = this.arg = t.arg, this.method = "return", this.next = "end") : "normal" === t.type && e && (this.next = e), y; }, finish: function finish(t) { for (var e = this.tryEntries.length - 1; e >= 0; --e) { var r = this.tryEntries[e]; if (r.finallyLoc === t) return this.complete(r.completion, r.afterLoc), resetTryEntry(r), y; } }, "catch": function _catch(t) { for (var e = this.tryEntries.length - 1; e >= 0; --e) { var r = this.tryEntries[e]; if (r.tryLoc === t) { var n = r.completion; if ("throw" === n.type) { var o = n.arg; resetTryEntry(r); } return o; } } throw new Error("illegal catch attempt"); }, delegateYield: function delegateYield(e, r, n) { return this.delegate = { iterator: values(e), resultName: r, nextLoc: n }, "next" === this.method && (this.arg = t), y; } }, e; }
|
|
8
|
+
function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
|
|
9
|
+
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
|
|
10
|
+
function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; } /**
|
|
11
|
+
* External dependencies
|
|
12
|
+
*/ /**
|
|
13
|
+
* Internal dependencies
|
|
14
|
+
*/
|
|
15
|
+
var defaultProps = {
|
|
16
|
+
options: [{
|
|
17
|
+
label: 'One',
|
|
18
|
+
value: 'one'
|
|
19
|
+
}, {
|
|
20
|
+
label: 'Two',
|
|
21
|
+
value: 'two'
|
|
22
|
+
}, {
|
|
23
|
+
label: 'Three',
|
|
24
|
+
value: 'three'
|
|
25
|
+
}],
|
|
26
|
+
onChange: jest.fn()
|
|
27
|
+
};
|
|
28
|
+
describe('<RadioGroupChip />', function () {
|
|
29
|
+
it('renders the default variant', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee() {
|
|
30
|
+
var _render, container, dom;
|
|
31
|
+
return _regeneratorRuntime().wrap(function _callee$(_context) {
|
|
32
|
+
while (1) switch (_context.prev = _context.next) {
|
|
33
|
+
case 0:
|
|
34
|
+
_render = (0, _react.render)((0, _jsxRuntime.jsx)(_RadioGroupChip.RadioGroupChip, _extends({}, defaultProps))), container = _render.container;
|
|
35
|
+
_context.next = 3;
|
|
36
|
+
return _react.screen.findAllByRole('radio');
|
|
37
|
+
case 3:
|
|
38
|
+
dom = _context.sent;
|
|
39
|
+
expect(dom).toHaveLength(3);
|
|
40
|
+
expect(dom[0]).toHaveAttribute('value', 'one');
|
|
41
|
+
expect(dom[1]).toHaveAttribute('value', 'two');
|
|
42
|
+
expect(dom[2]).toHaveAttribute('value', 'three');
|
|
43
|
+
|
|
44
|
+
// Check for accessibility issues
|
|
45
|
+
_context.t0 = expect;
|
|
46
|
+
_context.next = 11;
|
|
47
|
+
return (0, _jestAxe.axe)(container);
|
|
48
|
+
case 11:
|
|
49
|
+
_context.t1 = _context.sent;
|
|
50
|
+
(0, _context.t0)(_context.t1).toHaveNoViolations();
|
|
51
|
+
case 13:
|
|
52
|
+
case "end":
|
|
53
|
+
return _context.stop();
|
|
54
|
+
}
|
|
55
|
+
}, _callee);
|
|
56
|
+
})));
|
|
57
|
+
});
|
package/build/system/index.d.ts
CHANGED
|
@@ -34,6 +34,7 @@ import { Heading } from './Heading';
|
|
|
34
34
|
import { Hr } from './Hr/Hr';
|
|
35
35
|
import { Input } from './Form';
|
|
36
36
|
import { Label } from './Form';
|
|
37
|
+
import { ScreenReaderText } from './ScreenReaderText';
|
|
37
38
|
import { Spinner } from './Spinner';
|
|
38
39
|
import { Table } from './Table';
|
|
39
40
|
import { TableRow } from './Table';
|
|
@@ -59,4 +60,4 @@ import { Validation } from './Form';
|
|
|
59
60
|
import { Wizard } from './Wizard';
|
|
60
61
|
import { WizardStep } from './Wizard';
|
|
61
62
|
import theme from './theme';
|
|
62
|
-
export { Accordion, Avatar, Badge, Box, Breadcrumbs, Button, ButtonSubmit, ButtonVariant, Card, Checkbox, Code, Dialog, NewDialog, Form, Drawer, Dropdown, DialogButton, DialogMenu, DialogMenuItem, DialogDivider, DialogContent, DialogTrigger, ConfirmationDialog, MobileMenu, MobileMenuTrigger, MobileMenuWrapper, NewConfirmationDialog, Grid, FilterDropdown, Flex, Notice, OptionRow, Heading, Hr, Input, Label, Spinner, Table, TableRow, TableCell, Tooltip, Link, LinkExternal, Radio, RadioBoxGroup, Textarea, Progress, Text, Tabs, Nav, NavItem, TabsTrigger, TabsContent, TabsList, Toggle, ToggleRow, Toolbar, Validation, Wizard, WizardStep, theme };
|
|
63
|
+
export { Accordion, Avatar, Badge, Box, Breadcrumbs, Button, ButtonSubmit, ButtonVariant, Card, Checkbox, Code, Dialog, NewDialog, Form, Drawer, Dropdown, DialogButton, DialogMenu, DialogMenuItem, DialogDivider, DialogContent, DialogTrigger, ConfirmationDialog, MobileMenu, MobileMenuTrigger, MobileMenuWrapper, NewConfirmationDialog, Grid, FilterDropdown, Flex, Notice, OptionRow, Heading, Hr, Input, Label, ScreenReaderText, Spinner, Table, TableRow, TableCell, Tooltip, Link, LinkExternal, Radio, RadioBoxGroup, Textarea, Progress, Text, Tabs, Nav, NavItem, TabsTrigger, TabsContent, TabsList, Toggle, ToggleRow, Toolbar, Validation, Wizard, WizardStep, theme };
|
package/build/system/index.js
CHANGED
|
@@ -48,6 +48,7 @@ import * as Form from './NewForm';
|
|
|
48
48
|
import { Notice } from './Notice';
|
|
49
49
|
import { OptionRow } from './OptionRow';
|
|
50
50
|
import { Progress } from './Progress';
|
|
51
|
+
import { ScreenReaderText } from './ScreenReaderText';
|
|
51
52
|
import { Spinner } from './Spinner';
|
|
52
53
|
import { Table, TableRow, TableCell } from './Table';
|
|
53
54
|
import { Tabs, TabsList, TabsContent, TabsTrigger } from './Tabs';
|
|
@@ -94,6 +95,7 @@ export {
|
|
|
94
95
|
Hr,
|
|
95
96
|
Input,
|
|
96
97
|
Label,
|
|
98
|
+
ScreenReaderText,
|
|
97
99
|
Spinner,
|
|
98
100
|
Table,
|
|
99
101
|
TableRow,
|
package/package.json
CHANGED
|
@@ -39,6 +39,7 @@ const RadioOption = ( {
|
|
|
39
39
|
cursor: 'pointer',
|
|
40
40
|
borderRadius: 2,
|
|
41
41
|
minWidth: 220,
|
|
42
|
+
flexGrow: 1,
|
|
42
43
|
textAlign: 'left',
|
|
43
44
|
border: '1px solid',
|
|
44
45
|
borderColor: 'input.radio-box.border.default',
|
|
@@ -94,103 +95,6 @@ const RadioOption = ( {
|
|
|
94
95
|
);
|
|
95
96
|
};
|
|
96
97
|
|
|
97
|
-
const ChipOption = ( {
|
|
98
|
-
defaultValue,
|
|
99
|
-
option: { id, value, label },
|
|
100
|
-
name,
|
|
101
|
-
disabled,
|
|
102
|
-
onChangeHandler,
|
|
103
|
-
} ) => {
|
|
104
|
-
const checked = `${ defaultValue }` === `${ value }`;
|
|
105
|
-
const forLabel = id || value;
|
|
106
|
-
const ref = React.useRef( null );
|
|
107
|
-
const describedById = `input-radio-box-${ forLabel }-description`;
|
|
108
|
-
|
|
109
|
-
return (
|
|
110
|
-
<div
|
|
111
|
-
id={ `o${ forLabel }` }
|
|
112
|
-
onClick={ () => {
|
|
113
|
-
ref.current?.click();
|
|
114
|
-
} }
|
|
115
|
-
sx={ {
|
|
116
|
-
display: 'inline-flex',
|
|
117
|
-
position: 'relative',
|
|
118
|
-
background: checked ? 'layer.4' : undefined,
|
|
119
|
-
color: 'text',
|
|
120
|
-
minHeight: '32px',
|
|
121
|
-
boxShadow: checked ? 'low' : undefined,
|
|
122
|
-
'&:hover': {
|
|
123
|
-
background: checked ? 'layer.4' : 'layer.1',
|
|
124
|
-
},
|
|
125
|
-
borderRadius: 1,
|
|
126
|
-
} }
|
|
127
|
-
>
|
|
128
|
-
<input
|
|
129
|
-
ref={ ref }
|
|
130
|
-
type="radio"
|
|
131
|
-
id={ forLabel }
|
|
132
|
-
disabled={ disabled }
|
|
133
|
-
name={ name }
|
|
134
|
-
checked={ checked }
|
|
135
|
-
aria-checked={ checked }
|
|
136
|
-
value={ value }
|
|
137
|
-
onChange={ onChangeHandler }
|
|
138
|
-
aria-labelledby={ describedById }
|
|
139
|
-
sx={ {
|
|
140
|
-
opacity: 0,
|
|
141
|
-
height: 0,
|
|
142
|
-
width: 0,
|
|
143
|
-
position: 'absolute',
|
|
144
|
-
'&:focus-visible + label': theme => theme.outline,
|
|
145
|
-
} }
|
|
146
|
-
/>
|
|
147
|
-
|
|
148
|
-
<label
|
|
149
|
-
id={ describedById }
|
|
150
|
-
htmlFor={ forLabel }
|
|
151
|
-
sx={ {
|
|
152
|
-
height: '100%',
|
|
153
|
-
display: 'flex',
|
|
154
|
-
flexDirection: 'column',
|
|
155
|
-
justifyContent: 'center',
|
|
156
|
-
width: '100%',
|
|
157
|
-
px: 3,
|
|
158
|
-
fontWeight: 400,
|
|
159
|
-
fontSize: 2,
|
|
160
|
-
cursor: 'pointer',
|
|
161
|
-
borderRadius: 1,
|
|
162
|
-
} }
|
|
163
|
-
>
|
|
164
|
-
{ label }
|
|
165
|
-
</label>
|
|
166
|
-
</div>
|
|
167
|
-
);
|
|
168
|
-
};
|
|
169
|
-
|
|
170
|
-
ChipOption.propTypes = RadioOption.propTypes = {
|
|
171
|
-
defaultValue: PropTypes.string,
|
|
172
|
-
option: PropTypes.object,
|
|
173
|
-
name: PropTypes.string,
|
|
174
|
-
onChangeHandler: PropTypes.func,
|
|
175
|
-
disabled: PropTypes.bool,
|
|
176
|
-
width: PropTypes.string,
|
|
177
|
-
};
|
|
178
|
-
|
|
179
|
-
const groupStyleOverrides = {
|
|
180
|
-
chip: {
|
|
181
|
-
background: 'layer.3',
|
|
182
|
-
p: 1,
|
|
183
|
-
display: 'inline-flex',
|
|
184
|
-
gap: 1,
|
|
185
|
-
borderRadius: 1,
|
|
186
|
-
},
|
|
187
|
-
primary: {
|
|
188
|
-
display: 'inline-block',
|
|
189
|
-
mb: 2,
|
|
190
|
-
p: 0,
|
|
191
|
-
},
|
|
192
|
-
};
|
|
193
|
-
|
|
194
98
|
const RadioBoxGroup = React.forwardRef(
|
|
195
99
|
(
|
|
196
100
|
{
|
|
@@ -204,7 +108,6 @@ const RadioBoxGroup = React.forwardRef(
|
|
|
204
108
|
errorMessage,
|
|
205
109
|
hasError,
|
|
206
110
|
required,
|
|
207
|
-
variant = 'primary',
|
|
208
111
|
...props
|
|
209
112
|
},
|
|
210
113
|
forwardRef
|
|
@@ -219,13 +122,8 @@ const RadioBoxGroup = React.forwardRef(
|
|
|
219
122
|
[ onChange ]
|
|
220
123
|
);
|
|
221
124
|
|
|
222
|
-
let Option = RadioOption;
|
|
223
|
-
if ( variant === 'chip' ) {
|
|
224
|
-
Option = ChipOption;
|
|
225
|
-
}
|
|
226
|
-
|
|
227
125
|
const renderedOptions = options.map( option => (
|
|
228
|
-
<
|
|
126
|
+
<RadioOption
|
|
229
127
|
defaultValue={ defaultValue }
|
|
230
128
|
disabled={ disabled }
|
|
231
129
|
key={ option?.id || option?.value }
|
|
@@ -241,7 +139,9 @@ const RadioBoxGroup = React.forwardRef(
|
|
|
241
139
|
<fieldset
|
|
242
140
|
sx={ {
|
|
243
141
|
border: 0,
|
|
244
|
-
|
|
142
|
+
display: 'inline-block',
|
|
143
|
+
mb: 2,
|
|
144
|
+
p: 0,
|
|
245
145
|
...( hasError
|
|
246
146
|
? { border: '1px solid', borderColor: 'input.border.error', borderRadius: 2, p: 2 }
|
|
247
147
|
: {} ),
|
|
@@ -262,7 +162,8 @@ const RadioBoxGroup = React.forwardRef(
|
|
|
262
162
|
<div
|
|
263
163
|
sx={ {
|
|
264
164
|
display: 'flex',
|
|
265
|
-
|
|
165
|
+
flexWrap: 'wrap',
|
|
166
|
+
gap: 2,
|
|
266
167
|
} }
|
|
267
168
|
>
|
|
268
169
|
{ renderedOptions }
|
|
@@ -293,7 +194,6 @@ RadioBoxGroup.propTypes = {
|
|
|
293
194
|
errorMessage: PropTypes.string,
|
|
294
195
|
hasError: PropTypes.bool,
|
|
295
196
|
required: PropTypes.bool,
|
|
296
|
-
variant: PropTypes.oneOf( [ 'primary', 'chip' ] ),
|
|
297
197
|
};
|
|
298
198
|
|
|
299
199
|
export { RadioBoxGroup };
|
|
@@ -63,7 +63,7 @@ export const Default = () => {
|
|
|
63
63
|
defaultValue={ value }
|
|
64
64
|
onChange={ e => setValue( e.target.value ) }
|
|
65
65
|
options={ options }
|
|
66
|
-
optionWidth="
|
|
66
|
+
optionWidth="300px"
|
|
67
67
|
/>
|
|
68
68
|
);
|
|
69
69
|
};
|
|
@@ -83,29 +83,3 @@ export const Errors = () => {
|
|
|
83
83
|
/>
|
|
84
84
|
);
|
|
85
85
|
};
|
|
86
|
-
|
|
87
|
-
export const ChipVariant = () => {
|
|
88
|
-
const [ value, setValue ] = useState( 'table' );
|
|
89
|
-
|
|
90
|
-
return (
|
|
91
|
-
<RadioBoxGroup
|
|
92
|
-
defaultValue={ value }
|
|
93
|
-
onChange={ e => setValue( e.target.value ) }
|
|
94
|
-
options={ [
|
|
95
|
-
{
|
|
96
|
-
label: 'Table',
|
|
97
|
-
value: 'table',
|
|
98
|
-
},
|
|
99
|
-
{
|
|
100
|
-
label: 'Grid',
|
|
101
|
-
value: 'grid',
|
|
102
|
-
},
|
|
103
|
-
{
|
|
104
|
-
label: 'Card',
|
|
105
|
-
value: 'card',
|
|
106
|
-
},
|
|
107
|
-
] }
|
|
108
|
-
variant="chip"
|
|
109
|
-
/>
|
|
110
|
-
);
|
|
111
|
-
};
|
|
@@ -31,8 +31,8 @@ const defaultProps = {
|
|
|
31
31
|
};
|
|
32
32
|
|
|
33
33
|
describe( '<RadioBoxGroup />', () => {
|
|
34
|
-
it
|
|
35
|
-
const { container } = render( <RadioBoxGroup { ...defaultProps }
|
|
34
|
+
it( 'renders the component', async () => {
|
|
35
|
+
const { container } = render( <RadioBoxGroup { ...defaultProps } /> );
|
|
36
36
|
|
|
37
37
|
const dom = await screen.findAllByRole( 'radio' );
|
|
38
38
|
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External dependencies
|
|
3
|
+
*/
|
|
4
|
+
import { useState } from 'react';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Internal dependencies
|
|
8
|
+
*/
|
|
9
|
+
import { RadioGroupChip } from './RadioGroupChip';
|
|
10
|
+
|
|
11
|
+
export default {
|
|
12
|
+
title: 'RadioGroupChip',
|
|
13
|
+
component: RadioGroupChip,
|
|
14
|
+
parameters: {
|
|
15
|
+
docs: {
|
|
16
|
+
description: {
|
|
17
|
+
component: `
|
|
18
|
+
A radio-group-chip is a group of radio buttons that are styled as boxes. This component is used
|
|
19
|
+
to allow users to toggle between different options
|
|
20
|
+
|
|
21
|
+
-------
|
|
22
|
+
|
|
23
|
+
This documentation is heavily inspired by the [U.S Web Design System (USWDS)](https://designsystem.digital.gov/components/tooltip/#package). We use USWDS as trusted source of truth for accessibility and usability best practices.
|
|
24
|
+
|
|
25
|
+
## Component Properties
|
|
26
|
+
`,
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export const MediumSize = () => {
|
|
33
|
+
const [ value, setValue ] = useState( 'table' );
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<RadioGroupChip
|
|
37
|
+
defaultValue={ value }
|
|
38
|
+
onChange={ e => setValue( e.target.value ) }
|
|
39
|
+
options={ [
|
|
40
|
+
{
|
|
41
|
+
label: 'Table',
|
|
42
|
+
value: 'table',
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
label: 'Grid',
|
|
46
|
+
value: 'grid',
|
|
47
|
+
},
|
|
48
|
+
] }
|
|
49
|
+
/>
|
|
50
|
+
);
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export const SmallSize = () => {
|
|
54
|
+
const [ value, setValue ] = useState( 'table' );
|
|
55
|
+
|
|
56
|
+
return (
|
|
57
|
+
<RadioGroupChip
|
|
58
|
+
defaultValue={ value }
|
|
59
|
+
onChange={ e => setValue( e.target.value ) }
|
|
60
|
+
options={ [
|
|
61
|
+
{
|
|
62
|
+
label: 'Table',
|
|
63
|
+
value: 'table',
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
label: 'Grid',
|
|
67
|
+
value: 'grid',
|
|
68
|
+
},
|
|
69
|
+
] }
|
|
70
|
+
size="small"
|
|
71
|
+
/>
|
|
72
|
+
);
|
|
73
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External dependencies
|
|
3
|
+
*/
|
|
4
|
+
import { render, screen } from '@testing-library/react';
|
|
5
|
+
import { axe } from 'jest-axe';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Internal dependencies
|
|
9
|
+
*/
|
|
10
|
+
import { RadioGroupChip } from './RadioGroupChip';
|
|
11
|
+
|
|
12
|
+
const defaultProps = {
|
|
13
|
+
options: [
|
|
14
|
+
{
|
|
15
|
+
label: 'One',
|
|
16
|
+
value: 'one',
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
label: 'Two',
|
|
20
|
+
value: 'two',
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
label: 'Three',
|
|
24
|
+
value: 'three',
|
|
25
|
+
},
|
|
26
|
+
],
|
|
27
|
+
onChange: jest.fn(),
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
describe( '<RadioGroupChip />', () => {
|
|
31
|
+
it( 'renders the default variant', async () => {
|
|
32
|
+
const { container } = render( <RadioGroupChip { ...defaultProps } /> );
|
|
33
|
+
|
|
34
|
+
const dom = await screen.findAllByRole( 'radio' );
|
|
35
|
+
|
|
36
|
+
expect( dom ).toHaveLength( 3 );
|
|
37
|
+
expect( dom[ 0 ] ).toHaveAttribute( 'value', 'one' );
|
|
38
|
+
expect( dom[ 1 ] ).toHaveAttribute( 'value', 'two' );
|
|
39
|
+
expect( dom[ 2 ] ).toHaveAttribute( 'value', 'three' );
|
|
40
|
+
|
|
41
|
+
// Check for accessibility issues
|
|
42
|
+
expect( await axe( container ) ).toHaveNoViolations();
|
|
43
|
+
} );
|
|
44
|
+
} );
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
/** @jsxImportSource theme-ui */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* External dependencies
|
|
5
|
+
*/
|
|
6
|
+
import React, { useCallback } from 'react';
|
|
7
|
+
import { Theme } from 'theme-ui';
|
|
8
|
+
|
|
9
|
+
import { RequiredLabel } from './RequiredLabel';
|
|
10
|
+
import { Validation } from './Validation';
|
|
11
|
+
import ScreenReaderText from '../ScreenReaderText';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Internal dependencies
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
interface InputTheme extends Theme {
|
|
18
|
+
outline?: Record< string, string >;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
type Option = {
|
|
22
|
+
id?: string;
|
|
23
|
+
value: string;
|
|
24
|
+
label: React.ReactNode | string;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
type ChipOptionProps = {
|
|
28
|
+
defaultValue?: string;
|
|
29
|
+
option: Option;
|
|
30
|
+
name: string;
|
|
31
|
+
disabled?: boolean;
|
|
32
|
+
onChangeHandler: ( e: React.ChangeEvent< HTMLInputElement > ) => void;
|
|
33
|
+
size: 'small' | 'medium';
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const ChipOption = ( {
|
|
37
|
+
defaultValue,
|
|
38
|
+
option: { id, value, label },
|
|
39
|
+
name,
|
|
40
|
+
disabled,
|
|
41
|
+
onChangeHandler,
|
|
42
|
+
size = 'medium',
|
|
43
|
+
}: ChipOptionProps ) => {
|
|
44
|
+
const checked = `${ defaultValue }` === `${ value }`;
|
|
45
|
+
const forLabel = id || value;
|
|
46
|
+
const ref = React.useRef( null );
|
|
47
|
+
const describedById = `input-radio-box-${ forLabel }-description`;
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<div
|
|
51
|
+
id={ `o${ forLabel }` }
|
|
52
|
+
onClick={ () => {
|
|
53
|
+
if ( ref.current ) {
|
|
54
|
+
( ref.current as HTMLInputElement ).click();
|
|
55
|
+
}
|
|
56
|
+
} }
|
|
57
|
+
sx={ {
|
|
58
|
+
display: 'inline-flex',
|
|
59
|
+
position: 'relative',
|
|
60
|
+
background: checked ? 'layer.4' : undefined,
|
|
61
|
+
color: 'text',
|
|
62
|
+
minHeight: size === 'small' ? '22px' : '32px',
|
|
63
|
+
boxShadow: checked ? 'low' : undefined,
|
|
64
|
+
'&:hover': {
|
|
65
|
+
background: checked ? 'layer.4' : 'layer.1',
|
|
66
|
+
},
|
|
67
|
+
borderRadius: 1,
|
|
68
|
+
} }
|
|
69
|
+
>
|
|
70
|
+
<input
|
|
71
|
+
ref={ ref }
|
|
72
|
+
type="radio"
|
|
73
|
+
id={ forLabel }
|
|
74
|
+
disabled={ disabled }
|
|
75
|
+
name={ name }
|
|
76
|
+
checked={ checked }
|
|
77
|
+
aria-checked={ checked }
|
|
78
|
+
value={ value }
|
|
79
|
+
onChange={ onChangeHandler }
|
|
80
|
+
aria-labelledby={ describedById }
|
|
81
|
+
sx={ {
|
|
82
|
+
opacity: 0,
|
|
83
|
+
height: 0,
|
|
84
|
+
width: 0,
|
|
85
|
+
position: 'absolute',
|
|
86
|
+
'&:focus-visible + label': ( theme: InputTheme ) => theme.outline,
|
|
87
|
+
} }
|
|
88
|
+
/>
|
|
89
|
+
|
|
90
|
+
<label
|
|
91
|
+
id={ describedById }
|
|
92
|
+
htmlFor={ forLabel }
|
|
93
|
+
sx={ {
|
|
94
|
+
height: '100%',
|
|
95
|
+
display: 'flex',
|
|
96
|
+
flexDirection: 'column',
|
|
97
|
+
justifyContent: 'center',
|
|
98
|
+
width: '100%',
|
|
99
|
+
px: size === 'small' ? 1 : 3,
|
|
100
|
+
fontWeight: 400,
|
|
101
|
+
fontSize: size === 'small' ? 1 : 2,
|
|
102
|
+
cursor: 'pointer',
|
|
103
|
+
borderRadius: 1,
|
|
104
|
+
} }
|
|
105
|
+
>
|
|
106
|
+
{ label }
|
|
107
|
+
</label>
|
|
108
|
+
</div>
|
|
109
|
+
);
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
type RadioGroupChipProps = {
|
|
113
|
+
optionWidth?: string;
|
|
114
|
+
name?: string;
|
|
115
|
+
onChange: ( e: React.ChangeEvent< HTMLInputElement >, option?: Option ) => void;
|
|
116
|
+
groupLabel?: string;
|
|
117
|
+
defaultValue?: string;
|
|
118
|
+
options: Option[];
|
|
119
|
+
disabled?: boolean;
|
|
120
|
+
errorMessage?: string;
|
|
121
|
+
hasError?: boolean;
|
|
122
|
+
required?: boolean;
|
|
123
|
+
size?: 'small' | 'medium';
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const RadioGroupChip = React.forwardRef(
|
|
127
|
+
(
|
|
128
|
+
{
|
|
129
|
+
name = '',
|
|
130
|
+
onChange,
|
|
131
|
+
groupLabel,
|
|
132
|
+
defaultValue,
|
|
133
|
+
options,
|
|
134
|
+
disabled,
|
|
135
|
+
errorMessage,
|
|
136
|
+
hasError,
|
|
137
|
+
required,
|
|
138
|
+
size = 'medium',
|
|
139
|
+
...props
|
|
140
|
+
}: RadioGroupChipProps,
|
|
141
|
+
forwardRef
|
|
142
|
+
) => {
|
|
143
|
+
const onChangeHandler = useCallback(
|
|
144
|
+
( e: React.ChangeEvent< HTMLInputElement > ) => {
|
|
145
|
+
const optionTriggered = options.find(
|
|
146
|
+
option => `${ option.value }` === `${ e.target.value }`
|
|
147
|
+
);
|
|
148
|
+
onChange( e, optionTriggered );
|
|
149
|
+
},
|
|
150
|
+
[ onChange ]
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
const renderedOptions = options.map( option => (
|
|
154
|
+
<ChipOption
|
|
155
|
+
defaultValue={ defaultValue }
|
|
156
|
+
disabled={ disabled }
|
|
157
|
+
key={ option?.id || option?.value }
|
|
158
|
+
name={ name }
|
|
159
|
+
option={ option }
|
|
160
|
+
onChangeHandler={ onChangeHandler }
|
|
161
|
+
size={ size }
|
|
162
|
+
/>
|
|
163
|
+
) );
|
|
164
|
+
|
|
165
|
+
return (
|
|
166
|
+
<div>
|
|
167
|
+
<fieldset
|
|
168
|
+
sx={ {
|
|
169
|
+
border: 0,
|
|
170
|
+
background: 'layer.3',
|
|
171
|
+
p: size === 'small' ? '2px' : 1,
|
|
172
|
+
display: 'inline-flex',
|
|
173
|
+
gap: 1,
|
|
174
|
+
borderRadius: 1,
|
|
175
|
+
...( hasError
|
|
176
|
+
? { border: '1px solid', borderColor: 'input.border.error', borderRadius: 2, p: 2 }
|
|
177
|
+
: {} ),
|
|
178
|
+
} }
|
|
179
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
180
|
+
// @ts-expect-error
|
|
181
|
+
ref={ forwardRef }
|
|
182
|
+
aria-required={ required }
|
|
183
|
+
role="radiogroup"
|
|
184
|
+
{ ...props }
|
|
185
|
+
>
|
|
186
|
+
{ groupLabel ? (
|
|
187
|
+
<legend sx={ { mb: 2 } }>
|
|
188
|
+
{ groupLabel }
|
|
189
|
+
{ required ? <RequiredLabel /> : null }
|
|
190
|
+
</legend>
|
|
191
|
+
) : (
|
|
192
|
+
<ScreenReaderText>Choose an option</ScreenReaderText>
|
|
193
|
+
) }
|
|
194
|
+
<div
|
|
195
|
+
sx={ {
|
|
196
|
+
display: 'flex',
|
|
197
|
+
gap: 1,
|
|
198
|
+
} }
|
|
199
|
+
>
|
|
200
|
+
{ renderedOptions }
|
|
201
|
+
</div>
|
|
202
|
+
</fieldset>
|
|
203
|
+
|
|
204
|
+
{ hasError && errorMessage && (
|
|
205
|
+
<Validation isValid={ false } describedId={ groupLabel }>
|
|
206
|
+
{ errorMessage }
|
|
207
|
+
</Validation>
|
|
208
|
+
) }
|
|
209
|
+
</div>
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
);
|
|
213
|
+
|
|
214
|
+
RadioGroupChip.displayName = 'RadioGroupChip';
|
|
215
|
+
|
|
216
|
+
export { RadioGroupChip };
|
package/src/system/index.js
CHANGED
|
@@ -48,6 +48,7 @@ import * as Form from './NewForm';
|
|
|
48
48
|
import { Notice } from './Notice';
|
|
49
49
|
import { OptionRow } from './OptionRow';
|
|
50
50
|
import { Progress } from './Progress';
|
|
51
|
+
import { ScreenReaderText } from './ScreenReaderText';
|
|
51
52
|
import { Spinner } from './Spinner';
|
|
52
53
|
import { Table, TableRow, TableCell } from './Table';
|
|
53
54
|
import { Tabs, TabsList, TabsContent, TabsTrigger } from './Tabs';
|
|
@@ -94,6 +95,7 @@ export {
|
|
|
94
95
|
Hr,
|
|
95
96
|
Input,
|
|
96
97
|
Label,
|
|
98
|
+
ScreenReaderText,
|
|
97
99
|
Spinner,
|
|
98
100
|
Table,
|
|
99
101
|
TableRow,
|