@openedx/paragon 22.0.0-alpha.23 → 22.0.0-alpha.25
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/bin/paragon-scripts.js +1 -1
- package/dist/Button/index.js +2 -2
- package/dist/Button/index.js.map +1 -1
- package/dist/Button/index.scss +3 -3
- package/dist/ColorPicker/index.js +48 -18
- package/dist/ColorPicker/index.js.map +1 -1
- package/dist/Container/index.js +6 -2
- package/dist/Container/index.js.map +1 -1
- package/dist/DataTable/index.js +2 -1
- package/dist/DataTable/index.js.map +1 -1
- package/dist/DataTable/selection/BaseSelectionStatus.js +3 -2
- package/dist/DataTable/selection/BaseSelectionStatus.js.map +1 -1
- package/dist/Dropdown/index.js.map +1 -1
- package/dist/Dropzone/index.js +2 -3
- package/dist/Dropzone/index.js.map +1 -1
- package/dist/Form/FormAutosuggest.js +9 -4
- package/dist/Form/FormAutosuggest.js.map +1 -1
- package/dist/Form/FormSwitch.js +3 -0
- package/dist/Form/FormSwitch.js.map +1 -1
- package/dist/Hyperlink/index.js +7 -6
- package/dist/Hyperlink/index.js.map +1 -1
- package/dist/Icon/index.js +18 -11
- package/dist/Icon/index.js.map +1 -1
- package/dist/IconButton/index.js +1 -1
- package/dist/IconButton/index.js.map +1 -1
- package/dist/Layout/index.js.map +1 -1
- package/dist/Menu/MenuItem.js +2 -2
- package/dist/Menu/MenuItem.js.map +1 -1
- package/dist/Modal/ModalDialog.js +3 -0
- package/dist/Modal/ModalDialog.js.map +1 -1
- package/dist/Popover/index.js +8 -8
- package/dist/Popover/index.js.map +1 -1
- package/dist/ProductTour/Checkpoint.js +10 -8
- package/dist/ProductTour/Checkpoint.js.map +1 -1
- package/dist/ProductTour/messages.js +16 -0
- package/dist/SearchField/SearchFieldAdvanced.js +12 -7
- package/dist/SearchField/SearchFieldAdvanced.js.map +1 -1
- package/dist/SearchField/SearchFieldLabel.js +3 -3
- package/dist/SearchField/SearchFieldLabel.js.map +1 -1
- package/dist/SearchField/index.js +0 -1
- package/dist/SearchField/index.js.map +1 -1
- package/dist/Tabs/index.js +13 -13
- package/dist/Tabs/index.js.map +1 -1
- package/dist/core.css +8 -6
- package/dist/core.css.map +1 -1
- package/dist/core.min.css +1 -1
- package/dist/hooks/useIndexOfLastVisibleChild.js +33 -38
- package/dist/hooks/useIndexOfLastVisibleChild.js.map +1 -1
- package/dist/i18n/messages/ar.json +2 -1
- package/dist/i18n/messages/ca.json +2 -1
- package/dist/i18n/messages/es_419.json +2 -1
- package/dist/i18n/messages/es_AR.json +2 -1
- package/dist/i18n/messages/es_ES.json +2 -1
- package/dist/i18n/messages/fr.json +2 -1
- package/dist/i18n/messages/he.json +2 -1
- package/dist/i18n/messages/id.json +2 -1
- package/dist/i18n/messages/it_IT.json +2 -1
- package/dist/i18n/messages/ko_KR.json +2 -1
- package/dist/i18n/messages/pl.json +2 -1
- package/dist/i18n/messages/pt_BR.json +2 -1
- package/dist/i18n/messages/pt_PT.json +2 -1
- package/dist/i18n/messages/ru.json +2 -1
- package/dist/i18n/messages/th.json +2 -1
- package/dist/i18n/messages/tr_TR.json +2 -1
- package/dist/i18n/messages/uk.json +2 -1
- package/dist/i18n/messages/zh_CN.json +2 -1
- package/dist/light.css +11 -11
- package/dist/light.css.map +1 -1
- package/dist/light.min.css +1 -1
- package/icons/es5/RightSidebarFilled.js +15 -0
- package/icons/es5/RightSidebarOutlined.js +15 -0
- package/icons/es5/index.js +2 -0
- package/icons/jsx/RightSidebarFilled.jsx +19 -0
- package/icons/jsx/RightSidebarOutlined.jsx +19 -0
- package/icons/jsx/index.jsx +2 -0
- package/icons/svg/right_sidebar_filled.svg +3 -0
- package/icons/svg/right_sidebar_outlined.svg +3 -0
- package/lib/utils.js +23 -2
- package/package.json +3 -3
- package/src/Button/README.md +94 -68
- package/src/Button/index.jsx +2 -2
- package/src/Button/index.scss +3 -3
- package/src/ColorPicker/ColorPicker.test.jsx +24 -2
- package/src/ColorPicker/index.jsx +56 -16
- package/src/Container/index.jsx +4 -0
- package/src/DataTable/README.md +3 -3
- package/src/DataTable/index.jsx +2 -1
- package/src/DataTable/selection/BaseSelectionStatus.jsx +2 -2
- package/src/DataTable/tablefilters.mdx +3 -3
- package/src/DataTable/tests/DataTable.test.jsx +31 -0
- package/src/Dropdown/index.jsx +4 -0
- package/src/Dropzone/index.jsx +2 -3
- package/src/Form/FormAutosuggest.jsx +11 -5
- package/src/Form/FormSwitch.jsx +3 -0
- package/src/Form/form-autosuggest.mdx +80 -72
- package/src/Form/tests/FormAutosuggest.test.jsx +21 -0
- package/src/Hyperlink/index.jsx +7 -6
- package/src/Icon/index.jsx +18 -11
- package/src/IconButton/index.jsx +1 -1
- package/src/Layout/index.jsx +1 -4
- package/src/Menu/MenuItem.jsx +2 -2
- package/src/Modal/ModalDialog.jsx +3 -0
- package/src/Overlay/README.md +1 -1
- package/src/Popover/README.md +0 -1
- package/src/Popover/index.jsx +11 -11
- package/src/ProductTour/Checkpoint.jsx +9 -6
- package/src/ProductTour/messages.js +16 -0
- package/src/SearchField/SearchFieldAdvanced.jsx +12 -7
- package/src/SearchField/SearchFieldLabel.jsx +3 -3
- package/src/SearchField/index.jsx +0 -1
- package/src/SelectableBox/tests/SelectableBoxSet.test.jsx +1 -1
- package/src/Tabs/index.jsx +19 -13
- package/src/hooks/tests/useIndexOfLastVisibleChild.test.jsx +3 -3
- package/src/hooks/useIndexOfLastVisibleChild.jsx +36 -38
- package/src/hooks/useIndexOfLastVisibleChild.mdx +3 -3
- package/src/i18n/messages/ar.json +2 -1
- package/src/i18n/messages/ca.json +2 -1
- package/src/i18n/messages/es_419.json +2 -1
- package/src/i18n/messages/es_AR.json +2 -1
- package/src/i18n/messages/es_ES.json +2 -1
- package/src/i18n/messages/fr.json +2 -1
- package/src/i18n/messages/he.json +2 -1
- package/src/i18n/messages/id.json +2 -1
- package/src/i18n/messages/it_IT.json +2 -1
- package/src/i18n/messages/ko_KR.json +2 -1
- package/src/i18n/messages/pl.json +2 -1
- package/src/i18n/messages/pt_BR.json +2 -1
- package/src/i18n/messages/pt_PT.json +2 -1
- package/src/i18n/messages/ru.json +2 -1
- package/src/i18n/messages/th.json +2 -1
- package/src/i18n/messages/tr_TR.json +2 -1
- package/src/i18n/messages/uk.json +2 -1
- package/src/i18n/messages/zh_CN.json +2 -1
- package/styles/css/core/variables.css +5 -3
- package/styles/css/themes/light/variables.css +11 -11
- package/tokens/src/core/components/Button/core.json +8 -4
- package/tokens/src/themes/light/alias/color.json +10 -10
|
@@ -14,18 +14,18 @@ devStatus: 'In progress'
|
|
|
14
14
|
---
|
|
15
15
|
|
|
16
16
|
|
|
17
|
-
The ``DataTable`` component is a wrapper that uses the <a href="https://
|
|
17
|
+
The ``DataTable`` component is a wrapper that uses the <a href="https://github.com/TanStack/table/tree/v7/docs/src/pages/docs" target="_blank" rel="noopener noreferrer">react-table</a> library to
|
|
18
18
|
create tables. It can be used as is, or its subcomponents can be used on their own, allowing the developer full control.
|
|
19
19
|
|
|
20
20
|
## Filtering and sorting
|
|
21
21
|
Paragon currently provides a variety of filter types, and you can also define your own filter types.
|
|
22
22
|
|
|
23
23
|
In the example below, a default ``TextFilter`` component is used as the default filter for all columns. A default filter can be passed in,
|
|
24
|
-
or a filter component can be defined on the column using the ``Filter`` attribute. See <a href="https://
|
|
24
|
+
or a filter component can be defined on the column using the ``Filter`` attribute. See <a href="https://github.com/TanStack/table/blob/v7/docs/src/pages/docs/api/useFilters.md" target="_blank" rel="noopener noreferrer">react-table filters documentation</a>
|
|
25
25
|
for more information.
|
|
26
26
|
|
|
27
27
|
## Available filter functions
|
|
28
|
-
A filtering function can be defined on the column as well as the filter component. Custom filtering functions can also be defined, see <a href="https://
|
|
28
|
+
A filtering function can be defined on the column as well as the filter component. Custom filtering functions can also be defined, see <a href="https://github.com/TanStack/table/blob/v7/docs/src/pages/docs/api/useFilters.md#column-options" target="_blank" rel="noopener noreferrer">react-table filters documentation</a>
|
|
29
29
|
for more information.
|
|
30
30
|
Filter functions are defined on the column as the ``filter`` attribute.
|
|
31
31
|
<dl>
|
|
@@ -6,6 +6,8 @@ import { IntlProvider } from 'react-intl';
|
|
|
6
6
|
|
|
7
7
|
import DataTable from '..';
|
|
8
8
|
import DataTableContext from '../DataTableContext';
|
|
9
|
+
import { TextFilter } from '../..';
|
|
10
|
+
import { SELECT_ALL_TEST_ID } from '../selection/data/constants';
|
|
9
11
|
|
|
10
12
|
const additionalColumns = [
|
|
11
13
|
{
|
|
@@ -198,6 +200,35 @@ describe('<DataTable />', () => {
|
|
|
198
200
|
|
|
199
201
|
expect(spinner).toBeTruthy();
|
|
200
202
|
});
|
|
203
|
+
it('displays the total number of items when applying filter and selecting all items', async () => {
|
|
204
|
+
const propsWithSelection = {
|
|
205
|
+
...props,
|
|
206
|
+
isSelectable: true,
|
|
207
|
+
isFilterable: true,
|
|
208
|
+
manualFilters: true,
|
|
209
|
+
defaultColumnValues: { Filter: TextFilter },
|
|
210
|
+
isPaginated: true,
|
|
211
|
+
initialState: { pageSize: 3, pageIndex: 0 },
|
|
212
|
+
pageCount: 3,
|
|
213
|
+
fetchData: jest.fn(),
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
render(<DataTableWrapper {...propsWithSelection} />);
|
|
217
|
+
const filtersButton = screen.getByRole('button', { name: 'Filters' });
|
|
218
|
+
|
|
219
|
+
await userEvent.click(filtersButton);
|
|
220
|
+
|
|
221
|
+
const searchFormControl = screen.getByPlaceholderText('Search coat color');
|
|
222
|
+
await userEvent.type(searchFormControl, 'brown tabby');
|
|
223
|
+
|
|
224
|
+
const selectAllCheckBox = screen.getByTitle('Toggle All Current Page Rows Selected');
|
|
225
|
+
await userEvent.click(selectAllCheckBox);
|
|
226
|
+
|
|
227
|
+
const selectAllButton = screen.getByTestId(SELECT_ALL_TEST_ID);
|
|
228
|
+
// A filtered array is returned from the backend,
|
|
229
|
+
// and the element counter displays its length.
|
|
230
|
+
expect(selectAllButton).toHaveTextContent('Select all 7');
|
|
231
|
+
});
|
|
201
232
|
|
|
202
233
|
describe('[legacy] controlled table selections', () => {
|
|
203
234
|
it('passes initial controlledTableSelections to context', async () => {
|
package/src/Dropdown/index.jsx
CHANGED
|
@@ -68,6 +68,7 @@ const Dropdown = React.forwardRef(
|
|
|
68
68
|
);
|
|
69
69
|
},
|
|
70
70
|
);
|
|
71
|
+
|
|
71
72
|
Dropdown.propTypes = {
|
|
72
73
|
autoClose: PropTypes.oneOfType([
|
|
73
74
|
PropTypes.string,
|
|
@@ -78,6 +79,7 @@ Dropdown.propTypes = {
|
|
|
78
79
|
show: PropTypes.bool,
|
|
79
80
|
variant: PropTypes.oneOf(['light', 'dark']),
|
|
80
81
|
};
|
|
82
|
+
|
|
81
83
|
Dropdown.defaultProps = {
|
|
82
84
|
autoClose: true,
|
|
83
85
|
className: '',
|
|
@@ -126,9 +128,11 @@ Dropdown.Item = React.forwardRef(
|
|
|
126
128
|
);
|
|
127
129
|
},
|
|
128
130
|
);
|
|
131
|
+
|
|
129
132
|
Dropdown.Item.propTypes = {
|
|
130
133
|
className: PropTypes.string,
|
|
131
134
|
};
|
|
135
|
+
|
|
132
136
|
Dropdown.Item.defaultProps = {
|
|
133
137
|
className: undefined,
|
|
134
138
|
};
|
package/src/Dropzone/index.jsx
CHANGED
|
@@ -178,8 +178,7 @@ function Dropzone({
|
|
|
178
178
|
<div
|
|
179
179
|
data-testid="dropzone-container"
|
|
180
180
|
{...getRootProps({
|
|
181
|
-
className: classNames('pgn__dropzone', {
|
|
182
|
-
className,
|
|
181
|
+
className: classNames('pgn__dropzone', className, {
|
|
183
182
|
'pgn__dropzone-validation-error': isMultipleDragged || errors.length > 0 || isDragReject,
|
|
184
183
|
'pgn__dropzone-active': isDragActive && !isDragReject,
|
|
185
184
|
}),
|
|
@@ -271,7 +270,7 @@ Dropzone.propTypes = {
|
|
|
271
270
|
*/
|
|
272
271
|
validator: PropTypes.func,
|
|
273
272
|
/** A component to display initial state of the `Dropzone`. */
|
|
274
|
-
inputComponent: PropTypes.oneOfType([PropTypes.
|
|
273
|
+
inputComponent: PropTypes.oneOfType([PropTypes.elementType, PropTypes.node]),
|
|
275
274
|
};
|
|
276
275
|
|
|
277
276
|
export default Dropzone;
|
|
@@ -6,7 +6,7 @@ import { v4 as uuidv4 } from 'uuid';
|
|
|
6
6
|
import { useIntl } from 'react-intl';
|
|
7
7
|
import { KeyboardArrowUp, KeyboardArrowDown } from '../../icons';
|
|
8
8
|
import Icon from '../Icon';
|
|
9
|
-
import
|
|
9
|
+
import { FormGroupContextProvider, useFormGroupContext } from './FormGroupContext';
|
|
10
10
|
import FormControl from './FormControl';
|
|
11
11
|
import FormControlFeedback from './FormControlFeedback';
|
|
12
12
|
import IconButton from '../IconButton';
|
|
@@ -239,12 +239,18 @@ function FormAutosuggest({
|
|
|
239
239
|
setDisplayValue(e.target.value);
|
|
240
240
|
};
|
|
241
241
|
|
|
242
|
+
const { getControlProps } = useFormGroupContext();
|
|
243
|
+
const controlProps = getControlProps(props);
|
|
244
|
+
|
|
242
245
|
return (
|
|
243
246
|
<div className="pgn__form-autosuggest__wrapper" ref={parentRef}>
|
|
244
247
|
<div aria-live="assertive" className="sr-only" data-testid="autosuggest-screen-reader-options-count">
|
|
245
248
|
{`${state.dropDownItems.length} options found`}
|
|
246
249
|
</div>
|
|
247
|
-
<
|
|
250
|
+
<FormGroupContextProvider
|
|
251
|
+
controlId={controlProps.id}
|
|
252
|
+
isInvalid={!!state.errorMessage}
|
|
253
|
+
>
|
|
248
254
|
<FormControl
|
|
249
255
|
ref={formControlRef}
|
|
250
256
|
aria-expanded={(state.dropDownItems.length > 0).toString()}
|
|
@@ -259,7 +265,7 @@ function FormAutosuggest({
|
|
|
259
265
|
onClick={handleClick}
|
|
260
266
|
trailingElement={iconToggle}
|
|
261
267
|
data-testid="autosuggest-textbox-input"
|
|
262
|
-
{...
|
|
268
|
+
{...controlProps}
|
|
263
269
|
/>
|
|
264
270
|
|
|
265
271
|
{helpMessage && !state.errorMessage && (
|
|
@@ -269,11 +275,11 @@ function FormAutosuggest({
|
|
|
269
275
|
)}
|
|
270
276
|
|
|
271
277
|
{state.errorMessage && (
|
|
272
|
-
<FormControlFeedback type="invalid" feedback-for={
|
|
278
|
+
<FormControlFeedback type="invalid" feedback-for={controlProps.name}>
|
|
273
279
|
{errorMessageText}
|
|
274
280
|
</FormControlFeedback>
|
|
275
281
|
)}
|
|
276
|
-
</
|
|
282
|
+
</FormGroupContextProvider>
|
|
277
283
|
|
|
278
284
|
<ul
|
|
279
285
|
id="pgn__form-autosuggest__dropdown-box"
|
package/src/Form/FormSwitch.jsx
CHANGED
|
@@ -79,8 +79,11 @@ FormSwitch.propTypes = {
|
|
|
79
79
|
children: PropTypes.node.isRequired,
|
|
80
80
|
/** Specifies class name to append to the base element. */
|
|
81
81
|
className: PropTypes.string,
|
|
82
|
+
/** Specifies class name to append to the label element. */
|
|
82
83
|
labelClassName: PropTypes.string,
|
|
84
|
+
/** Specifies helper text to display below the switch. */
|
|
83
85
|
helperText: PropTypes.node,
|
|
86
|
+
/** Determines whether the label should float to the left when the switch is active. */
|
|
84
87
|
floatLabelLeft: PropTypes.bool,
|
|
85
88
|
};
|
|
86
89
|
|
|
@@ -19,93 +19,101 @@ Form auto-suggest enables users to manually select or type to find matching opti
|
|
|
19
19
|
|
|
20
20
|
```jsx live
|
|
21
21
|
() => {
|
|
22
|
-
|
|
22
|
+
const [selected, setSelected] = useState('');
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
24
|
+
return (
|
|
25
|
+
<Form.Group>
|
|
26
|
+
<Form.Label>
|
|
27
|
+
<h4>Programming language</h4>
|
|
28
|
+
</Form.Label>
|
|
29
|
+
<Form.Autosuggest
|
|
30
|
+
aria-label="form autosuggest"
|
|
31
|
+
helpMessage="Select language"
|
|
32
|
+
errorMessageText="Error, no selected value"
|
|
33
|
+
value={selected}
|
|
34
|
+
onSelected={(value) => setSelected(value)}
|
|
35
|
+
>
|
|
36
|
+
<Form.AutosuggestOption>JavaScript</Form.AutosuggestOption>
|
|
37
|
+
<Form.AutosuggestOption>Python</Form.AutosuggestOption>
|
|
38
|
+
<Form.AutosuggestOption>Rube</Form.AutosuggestOption>
|
|
39
|
+
<Form.AutosuggestOption onClick={(e) => alert(e.currentTarget.getAttribute('data-value'))}>
|
|
40
|
+
Option with custom onClick
|
|
41
|
+
</Form.AutosuggestOption>
|
|
42
|
+
</Form.Autosuggest>
|
|
43
|
+
</Form.Group>
|
|
44
|
+
);
|
|
45
|
+
};
|
|
42
46
|
```
|
|
43
47
|
|
|
44
48
|
## Search Usage
|
|
45
49
|
|
|
46
50
|
```jsx live
|
|
47
51
|
() => {
|
|
48
|
-
|
|
52
|
+
const [selected, setSelected] = useState('');
|
|
49
53
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
}
|
|
54
|
+
return (
|
|
55
|
+
<Form.Autosuggest
|
|
56
|
+
placeholder="Type 'T'"
|
|
57
|
+
aria-label="form autosuggest"
|
|
58
|
+
errorMessageText="Error, no selected value"
|
|
59
|
+
helpMessage="Select language"
|
|
60
|
+
value={selected}
|
|
61
|
+
onSelected={(value) => setSelected(value)}
|
|
62
|
+
>
|
|
63
|
+
<Form.AutosuggestOption>PHP</Form.AutosuggestOption>
|
|
64
|
+
<Form.AutosuggestOption>Java</Form.AutosuggestOption>
|
|
65
|
+
<Form.AutosuggestOption>Turbo Pascal</Form.AutosuggestOption>
|
|
66
|
+
<Form.AutosuggestOption>Flask</Form.AutosuggestOption>
|
|
67
|
+
</Form.Autosuggest>
|
|
68
|
+
);
|
|
69
|
+
};
|
|
66
70
|
```
|
|
67
71
|
|
|
68
72
|
## Loading state
|
|
69
73
|
|
|
70
74
|
```jsx live
|
|
71
75
|
() => {
|
|
72
|
-
|
|
73
|
-
|
|
76
|
+
const [data, setData] = useState([]);
|
|
77
|
+
const [showLoading, setShowLoading] = useState(false);
|
|
74
78
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
79
|
+
useEffect(() => {
|
|
80
|
+
setShowLoading(true);
|
|
81
|
+
fetch('https://api.sampleapis.com/coffee/hot')
|
|
82
|
+
.then(data => data.json())
|
|
83
|
+
.then(items => {
|
|
84
|
+
setTimeout(() => {
|
|
85
|
+
setData(items);
|
|
86
|
+
setShowLoading(false);
|
|
87
|
+
}, 1500);
|
|
88
|
+
});
|
|
89
|
+
}, []);
|
|
86
90
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
91
|
+
const searchCoffee = (title) => {
|
|
92
|
+
setShowLoading(true);
|
|
93
|
+
fetch('https://api.sampleapis.com/coffee/hot')
|
|
94
|
+
.then(data => data.json())
|
|
95
|
+
.then(items => setTimeout(() => {
|
|
96
|
+
const filteredCoffee = items.filter(res => res.title.toLowerCase().includes(title.toLowerCase()));
|
|
97
|
+
setShowLoading(false);
|
|
98
|
+
if (filteredCoffee) { return filteredCoffee; }
|
|
99
|
+
return { ...title, filteredCoffee };
|
|
100
|
+
}, 1500));
|
|
101
|
+
};
|
|
98
102
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
}
|
|
103
|
+
return (
|
|
104
|
+
<Form.Group>
|
|
105
|
+
<Form.Label>
|
|
106
|
+
<h4>Café API</h4>
|
|
107
|
+
</Form.Label>
|
|
108
|
+
<Form.Autosuggest
|
|
109
|
+
isLoading={showLoading}
|
|
110
|
+
placeholder="This is placeholder"
|
|
111
|
+
screenReaderText="Loading..."
|
|
112
|
+
onChange={searchCoffee}
|
|
113
|
+
>
|
|
114
|
+
{data.map((item, index) => <Form.AutosuggestOption key={index}>{item.title}</Form.AutosuggestOption>)}
|
|
115
|
+
</Form.Autosuggest>
|
|
116
|
+
</Form.Group>
|
|
117
|
+
);
|
|
118
|
+
};
|
|
111
119
|
```
|
|
@@ -6,6 +6,8 @@ import userEvent from '@testing-library/user-event';
|
|
|
6
6
|
import { IntlProvider } from 'react-intl';
|
|
7
7
|
import FormAutosuggest from '../FormAutosuggest';
|
|
8
8
|
import FormAutosuggestOption from '../FormAutosuggestOption';
|
|
9
|
+
import FormGroup from '../FormGroup';
|
|
10
|
+
import FormLabel from '../FormLabel';
|
|
9
11
|
|
|
10
12
|
function FormAutosuggestWrapper(props) {
|
|
11
13
|
return (
|
|
@@ -31,6 +33,19 @@ function FormAutosuggestTestComponent(props) {
|
|
|
31
33
|
);
|
|
32
34
|
}
|
|
33
35
|
|
|
36
|
+
function FormAutosuggestLabelTestComponent() {
|
|
37
|
+
return (
|
|
38
|
+
<FormGroup>
|
|
39
|
+
<FormLabel data-testid="autosuggest-label">
|
|
40
|
+
<h3>Label</h3>
|
|
41
|
+
</FormLabel>
|
|
42
|
+
<FormAutosuggestWrapper>
|
|
43
|
+
<FormAutosuggestOption>Option</FormAutosuggestOption>
|
|
44
|
+
</FormAutosuggestWrapper>
|
|
45
|
+
</FormGroup>
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
34
49
|
FormAutosuggestTestComponent.defaultProps = {
|
|
35
50
|
onSelected: jest.fn(),
|
|
36
51
|
onClick: jest.fn(),
|
|
@@ -112,6 +127,12 @@ describe('render behavior', () => {
|
|
|
112
127
|
|
|
113
128
|
expect(getByText('3 options found')).toBeInTheDocument();
|
|
114
129
|
});
|
|
130
|
+
|
|
131
|
+
it('associates labels with the input textbox', () => {
|
|
132
|
+
const { getByTestId } = render(<FormAutosuggestLabelTestComponent />);
|
|
133
|
+
|
|
134
|
+
expect(getByTestId('autosuggest-label').getAttribute('for')).toEqual(getByTestId('autosuggest-textbox-input').getAttribute('id'));
|
|
135
|
+
});
|
|
115
136
|
});
|
|
116
137
|
|
|
117
138
|
describe('controlled behavior', () => {
|
package/src/Hyperlink/index.jsx
CHANGED
|
@@ -101,18 +101,19 @@ Hyperlink.propTypes = {
|
|
|
101
101
|
children: PropTypes.node.isRequired,
|
|
102
102
|
/** Custom class names for the hyperlink */
|
|
103
103
|
className: PropTypes.string,
|
|
104
|
-
/** specifies where the link should open. The default behavior is `_self`, which means that the URL will be
|
|
105
|
-
|
|
104
|
+
/** specifies where the link should open. The default behavior is `_self`, which means that the URL will be
|
|
105
|
+
* loaded into the same browsing context as the current one.
|
|
106
|
+
* If the target is `_blank` (opening a new window) `rel='noopener'` will be added to the anchor tag to prevent
|
|
107
|
+
* any potential [reverse tabnabbing attack](https://www.owasp.org/index.php/Reverse_Tabnabbing).
|
|
108
|
+
*/
|
|
106
109
|
target: PropTypes.string,
|
|
107
110
|
/** specifies the callback function when the link is clicked */
|
|
108
111
|
onClick: PropTypes.func,
|
|
109
|
-
// eslint-disable-next-line max-len
|
|
110
112
|
/** specifies the text for links with a `_blank` target (which loads the URL in a new browsing context). */
|
|
111
113
|
externalLinkAlternativeText: isRequiredIf(
|
|
112
114
|
PropTypes.string,
|
|
113
115
|
props => props.target === '_blank',
|
|
114
116
|
),
|
|
115
|
-
// eslint-disable-next-line max-len
|
|
116
117
|
/** specifies the title for links with a `_blank` target (which loads the URL in a new browsing context). */
|
|
117
118
|
externalLinkTitle: isRequiredIf(
|
|
118
119
|
PropTypes.string,
|
|
@@ -120,9 +121,9 @@ Hyperlink.propTypes = {
|
|
|
120
121
|
),
|
|
121
122
|
/** type of hyperlink */
|
|
122
123
|
variant: PropTypes.oneOf(['default', 'muted', 'brand']),
|
|
123
|
-
/** specify the link style. By default it will be underlined. */
|
|
124
|
+
/** specify the link style. By default, it will be underlined. */
|
|
124
125
|
isInline: PropTypes.bool,
|
|
125
|
-
/** specify if we need to show launch Icon. By default it will be visible. */
|
|
126
|
+
/** specify if we need to show launch Icon. By default, it will be visible. */
|
|
126
127
|
showLaunchIcon: PropTypes.bool,
|
|
127
128
|
};
|
|
128
129
|
|
package/src/Icon/index.jsx
CHANGED
|
@@ -70,28 +70,35 @@ function Icon({
|
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
Icon.propTypes = {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
73
|
+
/**
|
|
74
|
+
* An icon component to render.
|
|
75
|
+
* Example import of a Paragon icon component: `import { Check } from '@edx/paragon/icons';`
|
|
76
|
+
*/
|
|
77
|
+
src: PropTypes.oneOfType([PropTypes.element, PropTypes.elementType]),
|
|
76
78
|
/** HTML element attributes to pass through to the underlying svg element */
|
|
77
79
|
svgAttrs: PropTypes.shape({
|
|
78
80
|
'aria-label': PropTypes.string,
|
|
79
81
|
'aria-labelledby': PropTypes.string,
|
|
80
82
|
}),
|
|
81
|
-
|
|
82
|
-
|
|
83
|
+
/**
|
|
84
|
+
* the `id` property of the Icon element, by default this value is generated
|
|
85
|
+
* with the `newId` function with the `prefix` of `Icon`.
|
|
86
|
+
*/
|
|
83
87
|
id: PropTypes.string,
|
|
84
|
-
// eslint-disable-next-line max-len
|
|
85
88
|
/** The size of the icon. */
|
|
86
89
|
size: PropTypes.oneOf(['xs', 'sm', 'md', 'lg']),
|
|
87
|
-
// eslint-disable-next-line max-len
|
|
88
90
|
/** A class name that will define what the Icon looks like. */
|
|
89
91
|
className: PropTypes.string,
|
|
90
|
-
|
|
91
|
-
|
|
92
|
+
/**
|
|
93
|
+
* a boolean that determines the value of `aria-hidden` attribute on the Icon span,
|
|
94
|
+
* this value is `true` by default.
|
|
95
|
+
*/
|
|
92
96
|
hidden: PropTypes.bool,
|
|
93
|
-
|
|
94
|
-
|
|
97
|
+
/**
|
|
98
|
+
* a string or an element that will be used on a secondary span leveraging the `sr-only` style
|
|
99
|
+
* for screenreader only text, this value is `undefined` by default. This value is recommended for use unless
|
|
100
|
+
* the Icon is being used in a way that is purely decorative or provides no additional context for screen
|
|
101
|
+
* reader users. This field should be thought of the same way an `alt` attribute would be used for `image` tags.
|
|
95
102
|
*/
|
|
96
103
|
screenReaderText: PropTypes.oneOfType([PropTypes.string, PropTypes.element]),
|
|
97
104
|
};
|
package/src/IconButton/index.jsx
CHANGED
|
@@ -73,7 +73,7 @@ IconButton.propTypes = {
|
|
|
73
73
|
/** An icon component to render. Example import of a Paragon icon component:
|
|
74
74
|
* `import { Check } from '@edx/paragon/dist/icon';`
|
|
75
75
|
* */
|
|
76
|
-
src: PropTypes.oneOfType([PropTypes.element, PropTypes.
|
|
76
|
+
src: PropTypes.oneOfType([PropTypes.element, PropTypes.elementType]),
|
|
77
77
|
/** Alt text for your icon. For best practice, avoid using alt text to describe
|
|
78
78
|
* the image in the `IconButton`. Instead, we recommend describing the function
|
|
79
79
|
* of the button. */
|
package/src/Layout/index.jsx
CHANGED
package/src/Menu/MenuItem.jsx
CHANGED
|
@@ -40,9 +40,9 @@ MenuItem.propTypes = {
|
|
|
40
40
|
/** Specifies the base element */
|
|
41
41
|
as: PropTypes.elementType,
|
|
42
42
|
/** Specifies the jsx before the content of the ``MenuItem`` */
|
|
43
|
-
iconBefore: PropTypes.oneOfType([PropTypes.element, PropTypes.
|
|
43
|
+
iconBefore: PropTypes.oneOfType([PropTypes.element, PropTypes.elementType]),
|
|
44
44
|
/** Specifies the jsx after the content of the ``MenuItem`` */
|
|
45
|
-
iconAfter: PropTypes.oneOfType([PropTypes.element, PropTypes.
|
|
45
|
+
iconAfter: PropTypes.oneOfType([PropTypes.element, PropTypes.elementType]),
|
|
46
46
|
};
|
|
47
47
|
|
|
48
48
|
MenuItem.defaultProps = {
|
package/src/Overlay/README.md
CHANGED
|
@@ -38,7 +38,7 @@ This component is used to power Tooltips and Popovers.
|
|
|
38
38
|
Click me to see
|
|
39
39
|
</Button>
|
|
40
40
|
</div>
|
|
41
|
-
<Overlay target={target.current} show={
|
|
41
|
+
<Overlay target={target.current} show={isOpen} placement="right">
|
|
42
42
|
{({ placement, arrowProps, show: _show, popper, ...props }) => (
|
|
43
43
|
<div
|
|
44
44
|
{...props}
|
package/src/Popover/README.md
CHANGED
package/src/Popover/index.jsx
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
3
|
import classNames from 'classnames';
|
|
4
|
-
import
|
|
4
|
+
import BasePopover from 'react-bootstrap/Popover';
|
|
5
5
|
import BasePopoverTitle from 'react-bootstrap/PopoverTitle';
|
|
6
6
|
import BasePopoverContent from 'react-bootstrap/PopoverContent';
|
|
7
7
|
|
|
@@ -13,18 +13,18 @@ const PLACEMENT_VARIANTS = [
|
|
|
13
13
|
'right',
|
|
14
14
|
];
|
|
15
15
|
|
|
16
|
-
const
|
|
16
|
+
const Popover = React.forwardRef(({
|
|
17
17
|
children,
|
|
18
18
|
variant,
|
|
19
19
|
...props
|
|
20
20
|
}, ref) => (
|
|
21
|
-
<
|
|
21
|
+
<BasePopover
|
|
22
22
|
{...props}
|
|
23
23
|
className={classNames({ [`popover-${variant}`]: !!variant }, props.className)}
|
|
24
24
|
ref={ref}
|
|
25
25
|
>
|
|
26
26
|
{children}
|
|
27
|
-
</
|
|
27
|
+
</BasePopover>
|
|
28
28
|
));
|
|
29
29
|
|
|
30
30
|
function PopoverTitle(props) {
|
|
@@ -44,8 +44,8 @@ const commonPropTypes = {
|
|
|
44
44
|
PopoverTitle.propTypes = commonPropTypes;
|
|
45
45
|
PopoverContent.propTypes = commonPropTypes;
|
|
46
46
|
|
|
47
|
-
|
|
48
|
-
...
|
|
47
|
+
Popover.propTypes = {
|
|
48
|
+
...BasePopover.propTypes,
|
|
49
49
|
/** An html id attribute, necessary for accessibility. */
|
|
50
50
|
id: PropTypes.string.isRequired,
|
|
51
51
|
/**
|
|
@@ -88,8 +88,8 @@ WrapperPopover.propTypes = {
|
|
|
88
88
|
variant: PropTypes.string,
|
|
89
89
|
};
|
|
90
90
|
|
|
91
|
-
|
|
92
|
-
...
|
|
91
|
+
Popover.defaultProps = {
|
|
92
|
+
...BasePopover.defaultProps,
|
|
93
93
|
placement: 'right',
|
|
94
94
|
title: undefined,
|
|
95
95
|
arrowProps: undefined,
|
|
@@ -111,8 +111,8 @@ PopoverContent.defaultProps = {
|
|
|
111
111
|
bsPrefix: 'popover-body',
|
|
112
112
|
};
|
|
113
113
|
|
|
114
|
-
|
|
115
|
-
|
|
114
|
+
Popover.Title = PopoverTitle;
|
|
115
|
+
Popover.Content = PopoverContent;
|
|
116
116
|
|
|
117
117
|
export { PopoverTitle, PopoverContent };
|
|
118
|
-
export default
|
|
118
|
+
export default Popover;
|
|
@@ -10,6 +10,7 @@ import CheckpointActionRow from './CheckpointActionRow';
|
|
|
10
10
|
import CheckpointBody from './CheckpointBody';
|
|
11
11
|
import CheckpointBreadcrumbs from './CheckpointBreadcrumbs';
|
|
12
12
|
import CheckpointTitle from './CheckpointTitle';
|
|
13
|
+
import messages from './messages';
|
|
13
14
|
|
|
14
15
|
const Checkpoint = React.forwardRef(({
|
|
15
16
|
body,
|
|
@@ -99,10 +100,8 @@ const Checkpoint = React.forwardRef(({
|
|
|
99
100
|
>
|
|
100
101
|
<span className="sr-only">
|
|
101
102
|
<FormattedMessage
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
value={{ step: index + 1 }}
|
|
105
|
-
description="Screen-reader message to indicate the user's position in a sequence of checkpoints."
|
|
103
|
+
{...messages.topPositionText}
|
|
104
|
+
values={{ step: index + 1 }}
|
|
106
105
|
/>
|
|
107
106
|
</span>
|
|
108
107
|
{(title || !isOnlyCheckpoint) && (
|
|
@@ -118,8 +117,12 @@ const Checkpoint = React.forwardRef(({
|
|
|
118
117
|
{...props}
|
|
119
118
|
/>
|
|
120
119
|
<div id="pgn__checkpoint-arrow" data-popper-arrow />
|
|
121
|
-
|
|
122
|
-
|
|
120
|
+
<span className="sr-only">
|
|
121
|
+
<FormattedMessage
|
|
122
|
+
{...messages.bottomPositionText}
|
|
123
|
+
values={{ step: index + 1 }}
|
|
124
|
+
/>
|
|
125
|
+
</span>
|
|
123
126
|
</div>
|
|
124
127
|
);
|
|
125
128
|
});
|