@automattic/vip-design-system 0.9.2 → 0.9.3
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/Avatar/Avatar.test.js +41 -22
- package/build/system/Badge/Badge.test.js +73 -0
- package/build/system/BlankState/BlankState.js +12 -9
- package/build/system/BlankState/BlankState.test.js +124 -0
- package/build/system/Button/Button.test.js +44 -0
- package/build/system/Card/Card.test.js +78 -0
- package/build/system/Code/Code.js +11 -8
- package/build/system/Code/Code.test.js +167 -0
- package/build/system/Dialog/DialogContent.js +3 -1
- package/build/system/Form/Input.js +2 -2
- package/build/system/Form/Select.js +29 -12
- package/build/system/theme/index.js +1 -1
- package/package.json +15 -4
- package/src/system/Avatar/Avatar.test.js +9 -10
- package/src/system/Badge/Badge.test.js +30 -0
- package/src/system/BlankState/BlankState.js +13 -5
- package/src/system/BlankState/BlankState.test.js +58 -0
- package/src/system/Button/Button.test.js +21 -0
- package/src/system/Card/Card.test.js +33 -0
- package/src/system/Code/Code.js +12 -9
- package/src/system/Code/Code.stories.js +2 -0
- package/src/system/Code/Code.test.js +69 -0
- package/src/system/Dialog/DialogContent.js +2 -0
- package/src/system/Form/Input.js +2 -2
- package/src/system/Form/Select.js +16 -6
- package/src/system/Form/Select.stories.js +52 -24
- package/src/system/theme/index.js +1 -1
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
exports.__esModule = true;
|
|
4
4
|
exports.Select = void 0;
|
|
5
5
|
|
|
6
|
+
var _react = _interopRequireDefault(require("react"));
|
|
7
|
+
|
|
6
8
|
var _propTypes = _interopRequireDefault(require("prop-types"));
|
|
7
9
|
|
|
8
10
|
var _SearchSelect = require("./SearchSelect");
|
|
@@ -11,7 +13,7 @@ var _InlineSelect = require("./InlineSelect");
|
|
|
11
13
|
|
|
12
14
|
var _jsxRuntime = require("theme-ui/jsx-runtime");
|
|
13
15
|
|
|
14
|
-
var _excluded = ["isMulti", "isInline", "options", "label", "isSearch"];
|
|
16
|
+
var _excluded = ["isMulti", "isInline", "options", "label", "isSearch", "usePortal"];
|
|
15
17
|
|
|
16
18
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
|
|
17
19
|
|
|
@@ -26,21 +28,35 @@ var Select = function Select(_ref) {
|
|
|
26
28
|
options = _ref.options,
|
|
27
29
|
label = _ref.label,
|
|
28
30
|
isSearch = _ref.isSearch,
|
|
31
|
+
usePortal = _ref.usePortal,
|
|
29
32
|
props = _objectWithoutPropertiesLoose(_ref, _excluded);
|
|
30
33
|
|
|
31
|
-
|
|
32
|
-
|
|
34
|
+
var selectRef = _react["default"].useRef();
|
|
35
|
+
|
|
36
|
+
var portalProps = {};
|
|
37
|
+
|
|
38
|
+
if (usePortal !== undefined) {
|
|
39
|
+
var _selectRef$current;
|
|
40
|
+
|
|
41
|
+
portalProps.menuPortalTarget = selectRef == null ? void 0 : (_selectRef$current = selectRef.current) == null ? void 0 : _selectRef$current.querySelector('.select__control').parentElement;
|
|
42
|
+
portalProps.styles = {
|
|
43
|
+
menuPortal: function menuPortal(base) {
|
|
44
|
+
return _extends({}, base, {
|
|
45
|
+
position: 'fixed'
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
var Component = isInline ? _InlineSelect.InlineSelect : _SearchSelect.SearchSelect;
|
|
52
|
+
return (0, _jsxRuntime.jsx)("div", {
|
|
53
|
+
ref: selectRef,
|
|
54
|
+
children: (0, _jsxRuntime.jsx)(Component, _extends({
|
|
33
55
|
isMulti: isMulti,
|
|
34
56
|
label: label,
|
|
35
57
|
options: options
|
|
36
|
-
}, props))
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
return (0, _jsxRuntime.jsx)(_SearchSelect.SearchSelect, _extends({
|
|
40
|
-
isMulti: isMulti,
|
|
41
|
-
label: label,
|
|
42
|
-
options: options
|
|
43
|
-
}, props));
|
|
58
|
+
}, portalProps, props))
|
|
59
|
+
});
|
|
44
60
|
};
|
|
45
61
|
|
|
46
62
|
exports.Select = Select;
|
|
@@ -49,5 +65,6 @@ Select.propTypes = {
|
|
|
49
65
|
isMulti: _propTypes["default"].bool,
|
|
50
66
|
isSearch: _propTypes["default"].bool,
|
|
51
67
|
label: _propTypes["default"].string,
|
|
52
|
-
options: _propTypes["default"].array
|
|
68
|
+
options: _propTypes["default"].array,
|
|
69
|
+
usePortal: _propTypes["default"].bool
|
|
53
70
|
};
|
|
@@ -90,7 +90,7 @@ var _default = {
|
|
|
90
90
|
muted: _colors.light.grey['90'],
|
|
91
91
|
link: _colors.light.brand['90'],
|
|
92
92
|
card: '#fff',
|
|
93
|
-
border: _colors.light.grey['
|
|
93
|
+
border: _colors.light.grey['60'],
|
|
94
94
|
hover: 'rgba(0,0,0,.02)',
|
|
95
95
|
lightenBackground: 'rgba(255,255,255,.5)',
|
|
96
96
|
darken: 'rgba(0,0,0,.05)',
|
package/package.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@automattic/vip-design-system",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.3",
|
|
4
4
|
"main": "build/system/index.js",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"build-storybook": "build-storybook",
|
|
7
7
|
"build": "cross-env NODE_ENV=production babel src --out-dir build",
|
|
8
8
|
"format": "eslint . --fix",
|
|
9
9
|
"jest": "NODE_ENV=test jest --detectOpenHandles",
|
|
10
|
+
"jest:coverage": "npm run jest && open-cli ./coverage/index.html",
|
|
10
11
|
"jest:watch": "npm run jest --watch",
|
|
11
12
|
"lint": "eslint . -f json | eslines --quiet",
|
|
12
13
|
"storybook": "start-storybook -p 6006",
|
|
@@ -34,12 +35,21 @@
|
|
|
34
35
|
"**/react-dom": "17.0.1"
|
|
35
36
|
},
|
|
36
37
|
"jest": {
|
|
38
|
+
"collectCoverage": true,
|
|
39
|
+
"coverageReporters": [
|
|
40
|
+
"json",
|
|
41
|
+
"html"
|
|
42
|
+
],
|
|
37
43
|
"testURL": "http://localhost",
|
|
44
|
+
"roots": [
|
|
45
|
+
"<rootDir>/src",
|
|
46
|
+
"<rootDir>/test"
|
|
47
|
+
],
|
|
38
48
|
"setupFiles": [
|
|
39
|
-
"
|
|
49
|
+
"<rootDir>/test/setupTests.js"
|
|
40
50
|
],
|
|
41
51
|
"setupFilesAfterEnv": [
|
|
42
|
-
"
|
|
52
|
+
"<rootDir>/test/setupAfterEnv.js"
|
|
43
53
|
],
|
|
44
54
|
"transformIgnorePatterns": [
|
|
45
55
|
".*.stories.js$"
|
|
@@ -49,7 +59,6 @@
|
|
|
49
59
|
}
|
|
50
60
|
},
|
|
51
61
|
"devDependencies": {
|
|
52
|
-
"immer": ">=9.0.6",
|
|
53
62
|
"@axe-core/react": "4.3.2",
|
|
54
63
|
"@babel/cli": "^7.14.3",
|
|
55
64
|
"@babel/core": "7.14.0",
|
|
@@ -85,7 +94,9 @@
|
|
|
85
94
|
"eslint-plugin-no-async-foreach": "0.1.1",
|
|
86
95
|
"eslint-plugin-react": "7.25.3",
|
|
87
96
|
"eslint-plugin-wpcalypso": "4.1.0",
|
|
97
|
+
"immer": ">=9.0.6",
|
|
88
98
|
"jest-axe": "5.0.1",
|
|
99
|
+
"open-cli": "^7.0.1",
|
|
89
100
|
"optimize-css-assets-webpack-plugin": "^6.0.1",
|
|
90
101
|
"react-input-autosize": "^3.0.0",
|
|
91
102
|
"react-refresh": "^0.9.0",
|
|
@@ -10,22 +10,21 @@ import { axe } from 'jest-axe';
|
|
|
10
10
|
import { Avatar } from './Avatar';
|
|
11
11
|
|
|
12
12
|
describe( '<Avatar />', () => {
|
|
13
|
-
it( 'renders the Avatar without an image', () => {
|
|
14
|
-
render( <Avatar name="John Doe" /> );
|
|
13
|
+
it( 'renders the Avatar without an image', async () => {
|
|
14
|
+
const { container } = render( <Avatar name="John Doe" /> );
|
|
15
15
|
|
|
16
16
|
expect( screen.getByText( 'J' ) ).toBeInTheDocument();
|
|
17
|
-
} );
|
|
18
|
-
|
|
19
|
-
it( 'renders the Avatar with image', () => {
|
|
20
|
-
render( <Avatar name="John Doe" src="path/to/image" /> );
|
|
21
17
|
|
|
22
|
-
|
|
18
|
+
// Check for accessibility issues
|
|
19
|
+
await expect( await axe( container ) ).toHaveNoViolations();
|
|
23
20
|
} );
|
|
24
21
|
|
|
25
|
-
it( '
|
|
22
|
+
it( 'renders the Avatar with image', async () => {
|
|
26
23
|
const { container } = render( <Avatar name="John Doe" src="path/to/image" /> );
|
|
27
|
-
const results = await axe( container );
|
|
28
24
|
|
|
29
|
-
expect(
|
|
25
|
+
expect( screen.getByAltText( 'Avatar image from John Doe' ) ).toBeInTheDocument();
|
|
26
|
+
|
|
27
|
+
// Check for accessibility issues
|
|
28
|
+
await expect( await axe( container ) ).toHaveNoViolations();
|
|
30
29
|
} );
|
|
31
30
|
} );
|
|
@@ -0,0 +1,30 @@
|
|
|
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 { Badge } from './Badge';
|
|
11
|
+
|
|
12
|
+
describe( '<Badge />', () => {
|
|
13
|
+
it( 'renders the Badge component', async () => {
|
|
14
|
+
const { container } = render( <Badge>Badge text</Badge> );
|
|
15
|
+
|
|
16
|
+
expect( screen.getByText( 'Badge text' ) ).toBeInTheDocument();
|
|
17
|
+
|
|
18
|
+
// Check for accessibility issues
|
|
19
|
+
await expect( await axe( container ) ).toHaveNoViolations();
|
|
20
|
+
} );
|
|
21
|
+
|
|
22
|
+
it( 'renders the Badge component with a different variant', async () => {
|
|
23
|
+
const { container } = render( <Badge variant="primary">Badge text</Badge> );
|
|
24
|
+
|
|
25
|
+
expect( screen.getByText( 'Badge text' ) ).toBeInTheDocument();
|
|
26
|
+
|
|
27
|
+
// Check for accessibility issues
|
|
28
|
+
await expect( await axe( container ) ).toHaveNoViolations();
|
|
29
|
+
} );
|
|
30
|
+
} );
|
|
@@ -10,10 +10,17 @@ import PropTypes from 'prop-types';
|
|
|
10
10
|
*/
|
|
11
11
|
import { Box, Heading, Text } from '..';
|
|
12
12
|
|
|
13
|
-
const BlankState = ( {
|
|
13
|
+
const BlankState = ( {
|
|
14
|
+
body,
|
|
15
|
+
cta,
|
|
16
|
+
icon,
|
|
17
|
+
image,
|
|
18
|
+
imageAlt = 'Image representing the blank state',
|
|
19
|
+
title,
|
|
20
|
+
} ) => {
|
|
14
21
|
return (
|
|
15
22
|
<Box sx={{ textAlign: 'center', padding: 5 }}>
|
|
16
|
-
{icon ? icon : <img src={image} sx={{ mb: 3 }} alt=
|
|
23
|
+
{icon ? icon : <img src={image} sx={{ mb: 3 }} alt={imageAlt} />}
|
|
17
24
|
<Heading variant="h4">{title}</Heading>
|
|
18
25
|
<Text>{body}</Text>
|
|
19
26
|
<Box sx={{ mt: 3 }}>{cta}</Box>
|
|
@@ -22,11 +29,12 @@ const BlankState = ( { image, icon, title, body, cta } ) => {
|
|
|
22
29
|
};
|
|
23
30
|
|
|
24
31
|
BlankState.propTypes = {
|
|
25
|
-
image: PropTypes.oneOfType( [ PropTypes.object, PropTypes.string ] ),
|
|
26
|
-
icon: PropTypes.node,
|
|
27
|
-
title: PropTypes.node,
|
|
28
32
|
body: PropTypes.node,
|
|
29
33
|
cta: PropTypes.node,
|
|
34
|
+
icon: PropTypes.node,
|
|
35
|
+
image: PropTypes.oneOfType( [ PropTypes.object, PropTypes.string ] ),
|
|
36
|
+
imageAlt: PropTypes.string,
|
|
37
|
+
title: PropTypes.node,
|
|
30
38
|
};
|
|
31
39
|
|
|
32
40
|
export { BlankState };
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External dependencies
|
|
3
|
+
*/
|
|
4
|
+
import { render, screen } from '@testing-library/react';
|
|
5
|
+
import { axe } from 'jest-axe';
|
|
6
|
+
import { MdContentCopy } from 'react-icons/md';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Internal dependencies
|
|
10
|
+
*/
|
|
11
|
+
import { BlankState } from './BlankState';
|
|
12
|
+
import { Link } from '../Link';
|
|
13
|
+
|
|
14
|
+
// eslint-disable-next-line max-len
|
|
15
|
+
const image = "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='79' height='79' viewBox='0 0 79 79' fill='none'%3E%3Cpath d='M66.3001 15.9C66.3001 15.9 66.3 15.8 66.2 15.8C66.1 15.7 66 15.5 65.9001 15.4C65.9001 15.4 65.9001 15.4 65.9001 15.3L44.4001 0.2C44.3001 0.0999998 44.2 0.0999994 44.1 0.0999994C44 0.0999994 44.0001 0 43.9001 0H43.3C43.2 0 43.1 0.0999994 43.1 0.0999994C43 0.0999994 42.9 0.2 42.8 0.2L28.2001 10.3C28.1 10.4 28.0001 10.5 27.9001 10.6C27.9001 10.6 27.9001 10.6 27.9001 10.7C27.8001 10.8 27.7001 11 27.7001 11.2C27.7001 11.2 27.7001 11.2 27.7001 11.3V31.1L13.9 40.8L13.7001 41C13.6001 41.1 13.6 41.1 13.6 41.2L13.5 41.3C13.5 41.4 13.4 41.5 13.4 41.5V41.6C13.4 41.7 13.3 41.8 13.3 42V62.2C13.3 62.4 13.3 62.6 13.4 62.7V62.8C13.5 62.9 13.6001 63.1 13.7001 63.2C13.7001 63.2 13.7 63.2 13.8 63.3L13.9 63.4L35.3 78.6H35.4001C35.4001 78.6 35.5 78.6 35.5 78.7H35.6C35.8 78.8 36 78.8 36.2001 78.8C36.3001 78.8 36.5 78.8 36.6 78.7H36.7001C36.8001 78.7 36.8001 78.7 36.9001 78.6C36.9001 78.6 37 78.6 37 78.5H37.1L66 58.3C66.1 58.2 66.2001 58.1 66.3001 58V15.9ZM34.6 74.5L16.1 61.3V44.7L34.6 57.8V74.5ZM36 55.3L17.2001 41.9L29 33.5L47.9001 46.9L36 55.3ZM49 44.1L30.5 31V14.4L49 27.5V44.1ZM50.5 24.9L31.6 11.5L43.5 3.2L62.4001 16.6L50.5 24.9Z' fill='%23BD9D70'/%3E%3C/svg%3E";
|
|
16
|
+
|
|
17
|
+
const defaultProps = {
|
|
18
|
+
body: 'Sorry, there\'s nothing here yet.',
|
|
19
|
+
cta: <Link as="a">Explore add-ons →</Link>,
|
|
20
|
+
image,
|
|
21
|
+
imageAlt: 'This is the image alt',
|
|
22
|
+
title: 'Power up your application',
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
describe( '<BlankState />', () => {
|
|
26
|
+
it( 'renders the BlankState component', async () => {
|
|
27
|
+
const { container } = render( <BlankState { ...defaultProps } /> );
|
|
28
|
+
|
|
29
|
+
expect( screen.getByText( defaultProps.body ) ).toBeInTheDocument();
|
|
30
|
+
expect( screen.getByText( defaultProps.title ) ).toBeInTheDocument();
|
|
31
|
+
expect( screen.getByText( 'Explore add-ons →' ) ).toBeInTheDocument();
|
|
32
|
+
expect( screen.getByAltText( defaultProps.imageAlt ) ).toBeInTheDocument();
|
|
33
|
+
|
|
34
|
+
// Check for accessibility issues
|
|
35
|
+
await expect( await axe( container ) ).toHaveNoViolations();
|
|
36
|
+
} );
|
|
37
|
+
|
|
38
|
+
it( 'renders the BlankState component with default alt text for the given image', async () => {
|
|
39
|
+
const props = { ...defaultProps, imageAlt: undefined };
|
|
40
|
+
const { container } = render( <BlankState { ...props } /> );
|
|
41
|
+
|
|
42
|
+
expect( screen.getByAltText( 'Image representing the blank state' ) ).toBeInTheDocument();
|
|
43
|
+
|
|
44
|
+
// Check for accessibility issues
|
|
45
|
+
await expect( await axe( container ) ).toHaveNoViolations();
|
|
46
|
+
} );
|
|
47
|
+
|
|
48
|
+
it( 'renders the BlankState component with an icon', async () => {
|
|
49
|
+
const icon = <MdContentCopy title="this is an icon" />;
|
|
50
|
+
const props = { ...defaultProps, icon };
|
|
51
|
+
const { container } = render( <BlankState { ...props } /> );
|
|
52
|
+
|
|
53
|
+
expect( screen.getByTitle( 'this is an icon' ) ).toBeInTheDocument();
|
|
54
|
+
|
|
55
|
+
// Check for accessibility issues
|
|
56
|
+
await expect( await axe( container ) ).toHaveNoViolations();
|
|
57
|
+
} );
|
|
58
|
+
} );
|
|
@@ -0,0 +1,21 @@
|
|
|
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 { Button } from './Button';
|
|
11
|
+
|
|
12
|
+
describe( '<Button />', () => {
|
|
13
|
+
it( 'renders the Button component', async () => {
|
|
14
|
+
const { container } = render( <Button>Button text</Button> );
|
|
15
|
+
|
|
16
|
+
expect( screen.getByText( 'Button text' ) ).toBeInTheDocument();
|
|
17
|
+
|
|
18
|
+
// Check for accessibility issues
|
|
19
|
+
await expect( await axe( container ) ).toHaveNoViolations();
|
|
20
|
+
} );
|
|
21
|
+
} );
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External dependencies
|
|
3
|
+
*/
|
|
4
|
+
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
|
|
5
|
+
import { axe } from 'jest-axe';
|
|
6
|
+
import { MdContentCopy } from 'react-icons/md';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Internal dependencies
|
|
10
|
+
*/
|
|
11
|
+
import { Card } from './Card';
|
|
12
|
+
|
|
13
|
+
const defaultProps = {};
|
|
14
|
+
|
|
15
|
+
describe( '<Card />', () => {
|
|
16
|
+
it( 'renders the Card component', async () => {
|
|
17
|
+
const { container } = render( <Card { ...defaultProps }>This is a Card</Card> );
|
|
18
|
+
|
|
19
|
+
expect( screen.getByText( 'This is a Card' ) ).toBeInTheDocument();
|
|
20
|
+
|
|
21
|
+
// Check for accessibility issues
|
|
22
|
+
await expect( await axe( container ) ).toHaveNoViolations();
|
|
23
|
+
} );
|
|
24
|
+
|
|
25
|
+
it( 'renders the Card component with a different variant', async () => {
|
|
26
|
+
const { container } = render( <Card variant="primary">Card text</Card> );
|
|
27
|
+
|
|
28
|
+
expect( screen.getByText( 'Card text' ) ).toBeInTheDocument();
|
|
29
|
+
|
|
30
|
+
// Check for accessibility issues
|
|
31
|
+
await expect( await axe( container ) ).toHaveNoViolations();
|
|
32
|
+
} );
|
|
33
|
+
} );
|
package/src/system/Code/Code.js
CHANGED
|
@@ -47,18 +47,21 @@ const Code = ( { prompt = false, showCopy = false, onCopy = null, ...props } ) =
|
|
|
47
47
|
>
|
|
48
48
|
{ codeDom }
|
|
49
49
|
{
|
|
50
|
-
<
|
|
50
|
+
<button
|
|
51
|
+
aria-label="Copy"
|
|
51
52
|
sx={ {
|
|
52
|
-
position: 'absolute',
|
|
53
|
-
top: 0,
|
|
54
53
|
bg: 'grey.10',
|
|
55
|
-
right: 0,
|
|
56
|
-
paddingRight: 2,
|
|
57
|
-
paddingLeft: 2,
|
|
58
|
-
paddingTop: 1,
|
|
59
|
-
paddingBottom: 1,
|
|
60
54
|
borderTopRightRadius: 1,
|
|
55
|
+
borderWidth: 0,
|
|
56
|
+
color: 'muted',
|
|
61
57
|
opacity: 0.8,
|
|
58
|
+
paddingBottom: 1,
|
|
59
|
+
paddingLeft: 2,
|
|
60
|
+
paddingRight: 2,
|
|
61
|
+
paddingTop: 1,
|
|
62
|
+
position: 'absolute',
|
|
63
|
+
right: 0,
|
|
64
|
+
top: '-1px',
|
|
62
65
|
'&:hover': {
|
|
63
66
|
opacity: 1,
|
|
64
67
|
cursor: 'pointer',
|
|
@@ -75,7 +78,7 @@ const Code = ( { prompt = false, showCopy = false, onCopy = null, ...props } ) =
|
|
|
75
78
|
} }
|
|
76
79
|
>
|
|
77
80
|
{ copied ? 'Copied!' : <MdContentCopy /> }
|
|
78
|
-
</
|
|
81
|
+
</button>
|
|
79
82
|
}
|
|
80
83
|
</div>
|
|
81
84
|
);
|
|
@@ -16,7 +16,9 @@ export default {
|
|
|
16
16
|
export const Default = () => (
|
|
17
17
|
<React.Fragment>
|
|
18
18
|
<Code>Code</Code>
|
|
19
|
+
<br />
|
|
19
20
|
<Code showCopy={true}>Code with Icon</Code>
|
|
21
|
+
<br />
|
|
20
22
|
<Code showCopy={true} onCopy={
|
|
21
23
|
// eslint-disable-next-line no-console
|
|
22
24
|
() => console.info( 'Hello world' )
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External dependencies
|
|
3
|
+
*/
|
|
4
|
+
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
|
|
5
|
+
import { axe } from 'jest-axe';
|
|
6
|
+
import { MdContentCopy } from 'react-icons/md';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Internal dependencies
|
|
10
|
+
*/
|
|
11
|
+
import { Code } from './Code';
|
|
12
|
+
|
|
13
|
+
const defaultProps = {
|
|
14
|
+
showCopy: false,
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
// Mock navigator.clipboard because jsdom doesn't support it
|
|
18
|
+
Object.defineProperty( window.navigator, 'clipboard', {
|
|
19
|
+
value: {
|
|
20
|
+
writeText: () => {},
|
|
21
|
+
},
|
|
22
|
+
} );
|
|
23
|
+
|
|
24
|
+
describe( '<Code />', () => {
|
|
25
|
+
it( 'renders the Code component', async () => {
|
|
26
|
+
const { container } = render( <Code { ...defaultProps }>This is a code</Code> );
|
|
27
|
+
|
|
28
|
+
expect( screen.getByText( 'This is a code' ) ).toBeInTheDocument();
|
|
29
|
+
|
|
30
|
+
// Check for accessibility issues
|
|
31
|
+
await expect( await axe( container ) ).toHaveNoViolations();
|
|
32
|
+
} );
|
|
33
|
+
|
|
34
|
+
// jsdom currently doesn't support pseudo-elements with getComputedStyle
|
|
35
|
+
it.skip( 'renders "$" in front of the code when in prompt mode', async () => {
|
|
36
|
+
const props = { ...defaultProps, prompt: true };
|
|
37
|
+
const { container } = render( <Code { ...props }>This is a code</Code> );
|
|
38
|
+
const codeElement = screen.getByText( 'This is a code' );
|
|
39
|
+
|
|
40
|
+
expect( window.getComputedStyle( codeElement, ':before' ).content ).toEqual( '$' );
|
|
41
|
+
|
|
42
|
+
// Check for accessibility issues
|
|
43
|
+
await expect( await axe( container ) ).toHaveNoViolations();
|
|
44
|
+
} );
|
|
45
|
+
|
|
46
|
+
it( 'renders the Code component with a copy button', async () => {
|
|
47
|
+
const props = { ...defaultProps, showCopy: true };
|
|
48
|
+
const { container } = render( <Code { ...props }>This is a code</Code> );
|
|
49
|
+
|
|
50
|
+
expect( screen.getByRole( 'button', { name: 'Copy' } ) ).toBeInTheDocument();
|
|
51
|
+
|
|
52
|
+
// Check for accessibility issues
|
|
53
|
+
await expect( await axe( container ) ).toHaveNoViolations();
|
|
54
|
+
} );
|
|
55
|
+
|
|
56
|
+
it( 'updates the the UI after clicking on "Copy"', async () => {
|
|
57
|
+
const props = { ...defaultProps, showCopy: true };
|
|
58
|
+
const { container } = render( <Code { ...props }>This is a code</Code> );
|
|
59
|
+
|
|
60
|
+
fireEvent.click( screen.getByRole( 'button', { name: 'Copy' } ) );
|
|
61
|
+
|
|
62
|
+
await waitFor( () => new Promise( res => setTimeout( res, 0 ) ) );
|
|
63
|
+
|
|
64
|
+
expect( screen.getByText( 'Copied!' ) ).toBeInTheDocument();
|
|
65
|
+
|
|
66
|
+
// Check for accessibility issues
|
|
67
|
+
await expect( await axe( container ) ).toHaveNoViolations();
|
|
68
|
+
} );
|
|
69
|
+
} );
|
package/src/system/Form/Input.js
CHANGED
|
@@ -28,7 +28,7 @@ const Input = React.forwardRef( ( { variant, label, forLabel, hasError, required
|
|
|
28
28
|
required={ required }
|
|
29
29
|
sx={ {
|
|
30
30
|
border: '1px solid',
|
|
31
|
-
borderColor: '
|
|
31
|
+
borderColor: 'border',
|
|
32
32
|
backgroundColor: 'card',
|
|
33
33
|
borderRadius: 1,
|
|
34
34
|
lineHeight: 'inherit',
|
|
@@ -47,7 +47,7 @@ const Input = React.forwardRef( ( { variant, label, forLabel, hasError, required
|
|
|
47
47
|
bg: 'backgroundSecondary',
|
|
48
48
|
},
|
|
49
49
|
'&::placeholder': {
|
|
50
|
-
color: '
|
|
50
|
+
color: 'placeholder',
|
|
51
51
|
},
|
|
52
52
|
} }
|
|
53
53
|
/>
|
|
@@ -3,19 +3,28 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* External dependencies
|
|
5
5
|
*/
|
|
6
|
+
import React from 'react';
|
|
6
7
|
import PropTypes from 'prop-types';
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
|
-
|
|
10
|
-
|
|
10
|
+
* Internal dependencies
|
|
11
|
+
*/
|
|
11
12
|
import { SearchSelect } from './SearchSelect';
|
|
12
13
|
import { InlineSelect } from './InlineSelect';
|
|
13
14
|
|
|
14
|
-
const Select = ( { isMulti = false, isInline, options, label, isSearch, ...props } ) => {
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
const Select = ( { isMulti = false, isInline, options, label, isSearch, usePortal, ...props } ) => {
|
|
16
|
+
const selectRef = React.useRef();
|
|
17
|
+
const portalProps = {};
|
|
18
|
+
|
|
19
|
+
if ( usePortal !== undefined ) {
|
|
20
|
+
portalProps.menuPortalTarget =
|
|
21
|
+
selectRef?.current?.querySelector( '.select__control' ).parentElement;
|
|
22
|
+
portalProps.styles = { menuPortal: base => ( { ...base, position: 'fixed' } ) };
|
|
17
23
|
}
|
|
18
|
-
|
|
24
|
+
|
|
25
|
+
const Component = isInline ? InlineSelect : SearchSelect;
|
|
26
|
+
|
|
27
|
+
return <div ref={selectRef}><Component isMulti={isMulti} label={label} options={options} {...portalProps} {...props} /></div>;
|
|
19
28
|
};
|
|
20
29
|
|
|
21
30
|
Select.propTypes = {
|
|
@@ -24,6 +33,7 @@ Select.propTypes = {
|
|
|
24
33
|
isSearch: PropTypes.bool,
|
|
25
34
|
label: PropTypes.string,
|
|
26
35
|
options: PropTypes.array,
|
|
36
|
+
usePortal: PropTypes.bool,
|
|
27
37
|
};
|
|
28
38
|
|
|
29
39
|
export { Select };
|
|
@@ -6,15 +6,7 @@ import { useState } from 'react';
|
|
|
6
6
|
/**
|
|
7
7
|
* Internal dependencies
|
|
8
8
|
*/
|
|
9
|
-
import {
|
|
10
|
-
Box,
|
|
11
|
-
Dialog,
|
|
12
|
-
DialogMenu,
|
|
13
|
-
DialogMenuItem,
|
|
14
|
-
DialogDivider,
|
|
15
|
-
Select,
|
|
16
|
-
Button,
|
|
17
|
-
} from '..';
|
|
9
|
+
import { Box, Dialog, DialogMenu, DialogMenuItem, DialogDivider, Select, Button } from '..';
|
|
18
10
|
|
|
19
11
|
export default {
|
|
20
12
|
title: 'Select',
|
|
@@ -44,43 +36,79 @@ const DropdownContent = (
|
|
|
44
36
|
const DropdownTrigger = <Button variant="secondary">Trigger Dropdown</Button>;
|
|
45
37
|
|
|
46
38
|
export const Multi = () => {
|
|
47
|
-
const [ value, setValue ] = useState( [
|
|
39
|
+
const [ value, setValue ] = useState( [] );
|
|
48
40
|
|
|
49
41
|
return (
|
|
50
|
-
<Box sx={
|
|
51
|
-
<Select
|
|
42
|
+
<Box sx={{ mr: 2, width: 400 }}>
|
|
43
|
+
<Select
|
|
44
|
+
label="Type"
|
|
45
|
+
value={value}
|
|
46
|
+
isMulti
|
|
47
|
+
placeholder="Select domains..."
|
|
48
|
+
options={options}
|
|
49
|
+
onChange={newValue => setValue( newValue )}
|
|
50
|
+
/>
|
|
51
|
+
</Box>
|
|
52
|
+
);
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export const usePortal = () => {
|
|
56
|
+
const [ value, setValue ] = useState( [] );
|
|
57
|
+
|
|
58
|
+
return (
|
|
59
|
+
<Box sx={{ mr: 2, width: 400 }}>
|
|
60
|
+
<Select
|
|
61
|
+
label="Type"
|
|
62
|
+
value={value}
|
|
63
|
+
isMulti
|
|
64
|
+
placeholder="Select domains..."
|
|
65
|
+
usePortal
|
|
66
|
+
options={options}
|
|
67
|
+
onChange={newValue => setValue( newValue )}
|
|
68
|
+
/>
|
|
52
69
|
</Box>
|
|
53
70
|
);
|
|
54
71
|
};
|
|
55
72
|
|
|
56
73
|
export const Single = () => {
|
|
57
|
-
const [ value, setValue ] = useState( [
|
|
74
|
+
const [ value, setValue ] = useState( [] );
|
|
58
75
|
|
|
59
76
|
return (
|
|
60
|
-
<Box sx={
|
|
61
|
-
<Select
|
|
77
|
+
<Box sx={{ mr: 2, width: 200 }}>
|
|
78
|
+
<Select
|
|
79
|
+
label="User"
|
|
80
|
+
value={value}
|
|
81
|
+
placeholder="Select a domain..."
|
|
82
|
+
options={options}
|
|
83
|
+
onChange={newValue => setValue( newValue )}
|
|
84
|
+
/>
|
|
62
85
|
</Box>
|
|
63
86
|
);
|
|
64
87
|
};
|
|
65
88
|
|
|
66
89
|
export const Inline = () => {
|
|
67
|
-
const [ value, setValue ] = useState( [
|
|
90
|
+
const [ value, setValue ] = useState( [] );
|
|
68
91
|
|
|
69
92
|
return (
|
|
70
|
-
<Box sx={
|
|
71
|
-
<Select
|
|
93
|
+
<Box sx={{ mr: 2, width: 200 }}>
|
|
94
|
+
<Select
|
|
95
|
+
label="User"
|
|
96
|
+
value={value}
|
|
97
|
+
isInline
|
|
98
|
+
isMulti
|
|
99
|
+
noneLabel="Everyone"
|
|
100
|
+
placeholder="Filter by user..."
|
|
101
|
+
options={options}
|
|
102
|
+
onChange={newValue => setValue( newValue )}
|
|
103
|
+
/>
|
|
72
104
|
</Box>
|
|
73
105
|
);
|
|
74
106
|
};
|
|
75
107
|
|
|
76
108
|
export const DropdownMenu = () => {
|
|
77
109
|
return (
|
|
78
|
-
<Box sx={
|
|
79
|
-
<Dialog
|
|
80
|
-
trigger={DropdownTrigger}
|
|
81
|
-
content={DropdownContent}
|
|
82
|
-
sx={{ width: 200 }}
|
|
83
|
-
/>
|
|
110
|
+
<Box sx={{ mr: 2, width: 200 }}>
|
|
111
|
+
<Dialog trigger={DropdownTrigger} content={DropdownContent} sx={{ width: 200 }} />
|
|
84
112
|
</Box>
|
|
85
113
|
);
|
|
86
114
|
};
|
|
@@ -88,7 +88,7 @@ export default {
|
|
|
88
88
|
muted: light.grey[ '90' ],
|
|
89
89
|
link: light.brand[ '90' ],
|
|
90
90
|
card: '#fff',
|
|
91
|
-
border: light.grey[ '
|
|
91
|
+
border: light.grey[ '60' ],
|
|
92
92
|
hover: 'rgba(0,0,0,.02)',
|
|
93
93
|
lightenBackground: 'rgba(255,255,255,.5)',
|
|
94
94
|
darken: 'rgba(0,0,0,.05)',
|