@dhis2-ui/button 9.5.0 → 9.7.0
Sign up to get free protection for your applications and to get access to all the features.
- package/build/cjs/button/__tests__/Button.test.js +31 -0
- package/build/cjs/button/button.js +8 -0
- package/build/cjs/dropdown-button/__tests__/dropdown-button.test.js +65 -5
- package/build/cjs/dropdown-button/dropdown-button.js +21 -1
- package/build/cjs/locales/en/translations.json +3 -0
- package/build/cjs/locales/index.js +27 -0
- package/build/cjs/split-button/split-button.js +30 -4
- package/build/cjs/split-button/split-button.test.js +99 -0
- package/build/es/button/__tests__/Button.test.js +31 -0
- package/build/es/button/button.js +8 -0
- package/build/es/dropdown-button/__tests__/dropdown-button.test.js +48 -0
- package/build/es/dropdown-button/dropdown-button.js +21 -1
- package/build/es/locales/en/translations.json +3 -0
- package/build/es/locales/index.js +13 -0
- package/build/es/split-button/split-button.js +29 -4
- package/build/es/split-button/split-button.test.js +77 -0
- package/package.json +6 -6
@@ -11,6 +11,37 @@ var _button = require("../button.js");
|
|
11
11
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
12
12
|
|
13
13
|
describe('<Button>', () => {
|
14
|
+
let consoleSpy;
|
15
|
+
beforeEach(() => {
|
16
|
+
consoleSpy = jest.spyOn(console, 'debug').mockImplementation();
|
17
|
+
});
|
18
|
+
afterEach(() => {
|
19
|
+
consoleSpy.mockRestore();
|
20
|
+
});
|
21
|
+
describe('warning for missing aria-label and title', () => {
|
22
|
+
it('No warning if children exist but aria-label and title is missing', () => {
|
23
|
+
(0, _react.render)( /*#__PURE__*/_react2.default.createElement(_button.Button, null, "Children content"));
|
24
|
+
expect(consoleSpy).not.toHaveBeenCalled();
|
25
|
+
});
|
26
|
+
it('does not warn if aria-label and title is present', () => {
|
27
|
+
(0, _react.render)( /*#__PURE__*/_react2.default.createElement(_button.Button, {
|
28
|
+
"aria-label": "Test",
|
29
|
+
title: "Test"
|
30
|
+
}, "Children content"));
|
31
|
+
expect(consoleSpy).not.toHaveBeenCalled();
|
32
|
+
});
|
33
|
+
it('warns if no children are present with no arial-label and title', () => {
|
34
|
+
(0, _react.render)( /*#__PURE__*/_react2.default.createElement(_button.Button, null));
|
35
|
+
expect(consoleSpy).toHaveBeenCalledWith('Button component has no children but is missing title and ariaLabel attribute.');
|
36
|
+
});
|
37
|
+
it('No warning if there are no children but arial label and title', () => {
|
38
|
+
(0, _react.render)( /*#__PURE__*/_react2.default.createElement(_button.Button, {
|
39
|
+
"aria-label": "Test",
|
40
|
+
title: "Test"
|
41
|
+
}));
|
42
|
+
expect(consoleSpy).not.toHaveBeenCalled();
|
43
|
+
});
|
44
|
+
});
|
14
45
|
it('renders a default data-test attribute', () => {
|
15
46
|
const dataTest = 'dhis2-uicore-button';
|
16
47
|
const wrapper = (0, _enzyme.mount)( /*#__PURE__*/_react2.default.createElement(_button.Button, {
|
@@ -58,6 +58,14 @@ const Button = _ref => {
|
|
58
58
|
ref.current.focus();
|
59
59
|
}
|
60
60
|
}, [initialFocus, ref.current]);
|
61
|
+
const {
|
62
|
+
'aria-label': ariaLabel,
|
63
|
+
title
|
64
|
+
} = otherProps;
|
65
|
+
|
66
|
+
if (!children && !title && !ariaLabel) {
|
67
|
+
console.debug('Button component has no children but is missing title and ariaLabel attribute.');
|
68
|
+
}
|
61
69
|
|
62
70
|
const handleClick = event => onClick && onClick({
|
63
71
|
value,
|
@@ -4,12 +4,16 @@ var _layer = require("@dhis2-ui/layer");
|
|
4
4
|
|
5
5
|
var _popper = require("@dhis2-ui/popper");
|
6
6
|
|
7
|
+
var _react = require("@testing-library/react");
|
8
|
+
|
7
9
|
var _enzyme = require("enzyme");
|
8
10
|
|
9
|
-
var
|
11
|
+
var _react2 = _interopRequireDefault(require("react"));
|
10
12
|
|
11
13
|
var _testUtils = require("react-dom/test-utils");
|
12
14
|
|
15
|
+
var _modal = require("../../../../modal/src/modal/modal.js");
|
16
|
+
|
13
17
|
var _index = require("../../index.js");
|
14
18
|
|
15
19
|
var _dropdownButton = require("../dropdown-button.js");
|
@@ -21,10 +25,10 @@ describe('<DropdownButton>', () => {
|
|
21
25
|
describe('open state', () => {
|
22
26
|
const onClick = jest.fn();
|
23
27
|
|
24
|
-
const Component = /*#__PURE__*/
|
28
|
+
const Component = /*#__PURE__*/_react2.default.createElement(_dropdownButton.DropdownButton, {
|
25
29
|
onClick: onClick,
|
26
30
|
open: true,
|
27
|
-
component: /*#__PURE__*/
|
31
|
+
component: /*#__PURE__*/_react2.default.createElement("span", null, "test")
|
28
32
|
});
|
29
33
|
|
30
34
|
it('shows the Popper when open is true', async () => {
|
@@ -47,13 +51,69 @@ describe('<DropdownButton>', () => {
|
|
47
51
|
open: false
|
48
52
|
}));
|
49
53
|
});
|
54
|
+
it('closes dropdown when escape key is pressed', async () => {
|
55
|
+
const componentText = 'Dropdown Content';
|
56
|
+
const {
|
57
|
+
getByTestId,
|
58
|
+
queryByText
|
59
|
+
} = (0, _react.render)( /*#__PURE__*/_react2.default.createElement(_dropdownButton.DropdownButton, {
|
60
|
+
component: componentText
|
61
|
+
}));
|
62
|
+
const toggleButton = getByTestId('dhis2-uicore-dropdownbutton-toggle');
|
63
|
+
|
64
|
+
_react.fireEvent.click(toggleButton);
|
65
|
+
|
66
|
+
await (0, _react.waitFor)(() => {
|
67
|
+
expect(queryByText(componentText)).toBeInTheDocument();
|
68
|
+
});
|
69
|
+
|
70
|
+
_react.fireEvent.keyDown(document, {
|
71
|
+
key: 'Escape'
|
72
|
+
});
|
73
|
+
|
74
|
+
await (0, _react.waitFor)(() => {
|
75
|
+
expect(queryByText(componentText)).not.toBeInTheDocument();
|
76
|
+
});
|
77
|
+
});
|
78
|
+
test('modal remains open when dropdown button is closed on escape click', async () => {
|
79
|
+
const dropdownButtonText = 'Dropdown Content';
|
80
|
+
const headingText = 'Heading Text';
|
81
|
+
|
82
|
+
const modalContent = /*#__PURE__*/_react2.default.createElement("div", null, /*#__PURE__*/_react2.default.createElement("h1", null, headingText), /*#__PURE__*/_react2.default.createElement(_dropdownButton.DropdownButton, {
|
83
|
+
component: dropdownButtonText
|
84
|
+
}));
|
85
|
+
|
86
|
+
const {
|
87
|
+
getByTestId,
|
88
|
+
queryByText
|
89
|
+
} = (0, _react.render)( /*#__PURE__*/_react2.default.createElement(_modal.Modal, {
|
90
|
+
hide: false,
|
91
|
+
onClose: () => {}
|
92
|
+
}, modalContent));
|
93
|
+
const toggleButton = getByTestId('dhis2-uicore-dropdownbutton-toggle');
|
94
|
+
|
95
|
+
_react.fireEvent.click(toggleButton);
|
96
|
+
|
97
|
+
await (0, _react.waitFor)(() => {
|
98
|
+
expect(queryByText(dropdownButtonText)).toBeInTheDocument();
|
99
|
+
});
|
100
|
+
|
101
|
+
_react.fireEvent.keyDown(document, {
|
102
|
+
key: 'Escape'
|
103
|
+
});
|
104
|
+
|
105
|
+
await (0, _react.waitFor)(() => {
|
106
|
+
expect(queryByText(dropdownButtonText)).not.toBeInTheDocument();
|
107
|
+
expect(queryByText(headingText)).toBeInTheDocument();
|
108
|
+
});
|
109
|
+
});
|
50
110
|
});
|
51
111
|
describe('closed state', () => {
|
52
112
|
const onClick = jest.fn();
|
53
|
-
const wrapper = (0, _enzyme.mount)( /*#__PURE__*/
|
113
|
+
const wrapper = (0, _enzyme.mount)( /*#__PURE__*/_react2.default.createElement(_dropdownButton.DropdownButton, {
|
54
114
|
onClick: onClick,
|
55
115
|
open: false,
|
56
|
-
component: /*#__PURE__*/
|
116
|
+
component: /*#__PURE__*/_react2.default.createElement("span", null, "test")
|
57
117
|
}));
|
58
118
|
it('it does not show the Popper when open is false', () => {
|
59
119
|
const popper = wrapper.find(_popper.Popper);
|
@@ -86,6 +86,17 @@ class DropdownButton extends _react.Component {
|
|
86
86
|
|
87
87
|
_defineProperty(this, "anchorRef", /*#__PURE__*/_react.default.createRef());
|
88
88
|
|
89
|
+
_defineProperty(this, "handleKeyDown", event => {
|
90
|
+
event.preventDefault();
|
91
|
+
|
92
|
+
if (event.key === 'Escape' && this.state.open) {
|
93
|
+
event.stopPropagation();
|
94
|
+
this.setState({
|
95
|
+
open: false
|
96
|
+
});
|
97
|
+
}
|
98
|
+
});
|
99
|
+
|
89
100
|
_defineProperty(this, "onClickHandler", (_ref3, event) => {
|
90
101
|
let {
|
91
102
|
name,
|
@@ -114,6 +125,14 @@ class DropdownButton extends _react.Component {
|
|
114
125
|
});
|
115
126
|
}
|
116
127
|
|
128
|
+
componentDidMount() {
|
129
|
+
document.addEventListener('keydown', this.handleKeyDown);
|
130
|
+
}
|
131
|
+
|
132
|
+
componentWillUnmount() {
|
133
|
+
document.removeEventListener('keydown', this.handleKeyDown);
|
134
|
+
}
|
135
|
+
|
117
136
|
render() {
|
118
137
|
const {
|
119
138
|
component,
|
@@ -153,7 +172,8 @@ class DropdownButton extends _react.Component {
|
|
153
172
|
value: value,
|
154
173
|
tabIndex: tabIndex,
|
155
174
|
type: type,
|
156
|
-
initialFocus: initialFocus
|
175
|
+
initialFocus: initialFocus,
|
176
|
+
"data-test": "dhis2-uicore-dropdownbutton-toggle"
|
157
177
|
}, children, /*#__PURE__*/_react.default.createElement(ArrowIconComponent, {
|
158
178
|
className: "jsx-3163060161" + " " + (arrow.className || "")
|
159
179
|
})), open && /*#__PURE__*/_react.default.createElement(_layer.Layer, {
|
@@ -0,0 +1,27 @@
|
|
1
|
+
"use strict";
|
2
|
+
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
4
|
+
value: true
|
5
|
+
});
|
6
|
+
exports.default = void 0;
|
7
|
+
|
8
|
+
var _d2I18n = _interopRequireDefault(require("@dhis2/d2-i18n"));
|
9
|
+
|
10
|
+
var _translations = _interopRequireDefault(require("./en/translations.json"));
|
11
|
+
|
12
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
13
|
+
|
14
|
+
//------------------------------------------------------------------------------
|
15
|
+
// <auto-generated>
|
16
|
+
// This code was generated by d2-i18n-generate.
|
17
|
+
//
|
18
|
+
// Changes to this file may cause incorrect behavior and will be lost if
|
19
|
+
// the code is regenerated.
|
20
|
+
// </auto-generated>
|
21
|
+
//------------------------------------------------------------------------------
|
22
|
+
const namespace = 'default';
|
23
|
+
|
24
|
+
_d2I18n.default.addResources('en', namespace, _translations.default);
|
25
|
+
|
26
|
+
var _default = _d2I18n.default;
|
27
|
+
exports.default = _default;
|
@@ -23,6 +23,8 @@ var _react = _interopRequireWildcard(require("react"));
|
|
23
23
|
|
24
24
|
var _index = require("../index.js");
|
25
25
|
|
26
|
+
var _index2 = _interopRequireDefault(require("../locales/index.js"));
|
27
|
+
|
26
28
|
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
|
27
29
|
|
28
30
|
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
|
@@ -48,6 +50,18 @@ class SplitButton extends _react.Component {
|
|
48
50
|
|
49
51
|
_defineProperty(this, "anchorRef", /*#__PURE__*/_react.default.createRef());
|
50
52
|
|
53
|
+
_defineProperty(this, "handleKeyDown", event => {
|
54
|
+
event.preventDefault();
|
55
|
+
|
56
|
+
if (event.key === 'Escape' && this.state.open) {
|
57
|
+
event.stopPropagation();
|
58
|
+
this.setState({
|
59
|
+
open: false
|
60
|
+
});
|
61
|
+
this.anchorRef.current && this.anchorRef.current.focus();
|
62
|
+
}
|
63
|
+
});
|
64
|
+
|
51
65
|
_defineProperty(this, "onClick", (payload, event) => {
|
52
66
|
if (this.props.onClick) {
|
53
67
|
this.props.onClick({
|
@@ -58,9 +72,19 @@ class SplitButton extends _react.Component {
|
|
58
72
|
}
|
59
73
|
});
|
60
74
|
|
61
|
-
_defineProperty(this, "onToggle", () =>
|
62
|
-
|
63
|
-
|
75
|
+
_defineProperty(this, "onToggle", () => {
|
76
|
+
this.setState(prevState => ({
|
77
|
+
open: !prevState.open
|
78
|
+
}));
|
79
|
+
});
|
80
|
+
}
|
81
|
+
|
82
|
+
componentDidMount() {
|
83
|
+
document.addEventListener('keydown', this.handleKeyDown);
|
84
|
+
}
|
85
|
+
|
86
|
+
componentWillUnmount() {
|
87
|
+
document.removeEventListener('keydown', this.handleKeyDown);
|
64
88
|
}
|
65
89
|
|
66
90
|
render() {
|
@@ -119,7 +143,9 @@ class SplitButton extends _react.Component {
|
|
119
143
|
type: type,
|
120
144
|
tabIndex: tabIndex,
|
121
145
|
className: (0, _classnames.default)(className, rightButton.className),
|
122
|
-
dataTest: "".concat(dataTest, "-toggle")
|
146
|
+
dataTest: "".concat(dataTest, "-toggle"),
|
147
|
+
title: _index2.default.t('Toggle dropdown'),
|
148
|
+
"aria-label": _index2.default.t('Toggle dropdown')
|
123
149
|
}, arrow), open && /*#__PURE__*/_react.default.createElement(_layer.Layer, {
|
124
150
|
onBackdropClick: this.onToggle,
|
125
151
|
transparent: true
|
@@ -0,0 +1,99 @@
|
|
1
|
+
"use strict";
|
2
|
+
|
3
|
+
var _react = require("@testing-library/react");
|
4
|
+
|
5
|
+
var _react2 = _interopRequireDefault(require("react"));
|
6
|
+
|
7
|
+
require("@testing-library/jest-dom/extend-expect");
|
8
|
+
|
9
|
+
var _splitButton = require("./split-button.js");
|
10
|
+
|
11
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
12
|
+
|
13
|
+
describe('SplitButton', () => {
|
14
|
+
afterEach(_react.cleanup);
|
15
|
+
it('renders button with children', () => {
|
16
|
+
const {
|
17
|
+
getByText
|
18
|
+
} = (0, _react.render)( /*#__PURE__*/_react2.default.createElement(_splitButton.SplitButton, null, "Click me"));
|
19
|
+
expect(getByText('Click me')).toBeInTheDocument();
|
20
|
+
});
|
21
|
+
it('toggles dropdown when left button is clicked', () => {
|
22
|
+
const {
|
23
|
+
getByTestId,
|
24
|
+
queryByTestId
|
25
|
+
} = (0, _react.render)( /*#__PURE__*/_react2.default.createElement(_splitButton.SplitButton, null));
|
26
|
+
const toggleButton = getByTestId('dhis2-uicore-splitbutton-toggle');
|
27
|
+
|
28
|
+
_react.fireEvent.click(toggleButton);
|
29
|
+
|
30
|
+
expect(queryByTestId('dhis2-uicore-splitbutton-menu')).toBeInTheDocument();
|
31
|
+
|
32
|
+
_react.fireEvent.click(toggleButton);
|
33
|
+
|
34
|
+
expect(queryByTestId('dhis2-uicore-splitbutton-menu')).not.toBeInTheDocument();
|
35
|
+
});
|
36
|
+
it('renders dropdown content when open is true', () => {
|
37
|
+
const {
|
38
|
+
getByTestId
|
39
|
+
} = (0, _react.render)( /*#__PURE__*/_react2.default.createElement(_splitButton.SplitButton, {
|
40
|
+
component: /*#__PURE__*/_react2.default.createElement("div", null, "Dropdown Content")
|
41
|
+
}));
|
42
|
+
const toggleButton = getByTestId('dhis2-uicore-splitbutton-toggle');
|
43
|
+
|
44
|
+
_react.fireEvent.click(toggleButton);
|
45
|
+
|
46
|
+
expect(getByTestId('dhis2-uicore-splitbutton-menu')).toBeInTheDocument();
|
47
|
+
});
|
48
|
+
it("does not close dropdown 'Enter' key is pressed", async () => {
|
49
|
+
const {
|
50
|
+
getByTestId
|
51
|
+
} = (0, _react.render)( /*#__PURE__*/_react2.default.createElement(_splitButton.SplitButton, {
|
52
|
+
component: /*#__PURE__*/_react2.default.createElement("div", null, "Dropdown Content")
|
53
|
+
}));
|
54
|
+
const toggleButton = getByTestId('dhis2-uicore-splitbutton-toggle');
|
55
|
+
|
56
|
+
_react.fireEvent.click(toggleButton);
|
57
|
+
|
58
|
+
expect(getByTestId('dhis2-uicore-splitbutton-menu')).toBeInTheDocument();
|
59
|
+
|
60
|
+
_react.fireEvent.keyDown(document, {
|
61
|
+
key: 'Enter'
|
62
|
+
}); // Use waitFor to wait for the DOM to update
|
63
|
+
|
64
|
+
|
65
|
+
await (0, _react.waitFor)(() => {
|
66
|
+
expect(getByTestId('dhis2-uicore-splitbutton-menu')).toBeInTheDocument();
|
67
|
+
});
|
68
|
+
});
|
69
|
+
it('closes dropdown when escape key is pressed', async () => {
|
70
|
+
const {
|
71
|
+
getByTestId,
|
72
|
+
queryByTestId
|
73
|
+
} = (0, _react.render)( /*#__PURE__*/_react2.default.createElement(_splitButton.SplitButton, {
|
74
|
+
component: /*#__PURE__*/_react2.default.createElement("div", null, "Dropdown Content")
|
75
|
+
}));
|
76
|
+
const toggleButton = getByTestId('dhis2-uicore-splitbutton-toggle');
|
77
|
+
|
78
|
+
_react.fireEvent.click(toggleButton);
|
79
|
+
|
80
|
+
expect(getByTestId('dhis2-uicore-splitbutton-menu')).toBeInTheDocument();
|
81
|
+
|
82
|
+
_react.fireEvent.keyDown(document, {
|
83
|
+
key: 'Escape'
|
84
|
+
}); // Use waitFor to wait for the DOM to update
|
85
|
+
|
86
|
+
|
87
|
+
await (0, _react.waitFor)(() => {
|
88
|
+
expect(queryByTestId('dhis2-uicore-splitbutton-menu')).not.toBeInTheDocument();
|
89
|
+
});
|
90
|
+
});
|
91
|
+
it('adds title and aria-label attributes to the right button', () => {
|
92
|
+
const {
|
93
|
+
getByTestId
|
94
|
+
} = (0, _react.render)( /*#__PURE__*/_react2.default.createElement(_splitButton.SplitButton, null));
|
95
|
+
const toggleButton = getByTestId('dhis2-uicore-splitbutton-toggle');
|
96
|
+
expect(toggleButton).toHaveAttribute('title', 'Toggle dropdown');
|
97
|
+
expect(toggleButton).toHaveAttribute('aria-label', 'Toggle dropdown');
|
98
|
+
});
|
99
|
+
});
|
@@ -3,6 +3,37 @@ import { mount } from 'enzyme';
|
|
3
3
|
import React from 'react';
|
4
4
|
import { Button } from '../button.js';
|
5
5
|
describe('<Button>', () => {
|
6
|
+
let consoleSpy;
|
7
|
+
beforeEach(() => {
|
8
|
+
consoleSpy = jest.spyOn(console, 'debug').mockImplementation();
|
9
|
+
});
|
10
|
+
afterEach(() => {
|
11
|
+
consoleSpy.mockRestore();
|
12
|
+
});
|
13
|
+
describe('warning for missing aria-label and title', () => {
|
14
|
+
it('No warning if children exist but aria-label and title is missing', () => {
|
15
|
+
render( /*#__PURE__*/React.createElement(Button, null, "Children content"));
|
16
|
+
expect(consoleSpy).not.toHaveBeenCalled();
|
17
|
+
});
|
18
|
+
it('does not warn if aria-label and title is present', () => {
|
19
|
+
render( /*#__PURE__*/React.createElement(Button, {
|
20
|
+
"aria-label": "Test",
|
21
|
+
title: "Test"
|
22
|
+
}, "Children content"));
|
23
|
+
expect(consoleSpy).not.toHaveBeenCalled();
|
24
|
+
});
|
25
|
+
it('warns if no children are present with no arial-label and title', () => {
|
26
|
+
render( /*#__PURE__*/React.createElement(Button, null));
|
27
|
+
expect(consoleSpy).toHaveBeenCalledWith('Button component has no children but is missing title and ariaLabel attribute.');
|
28
|
+
});
|
29
|
+
it('No warning if there are no children but arial label and title', () => {
|
30
|
+
render( /*#__PURE__*/React.createElement(Button, {
|
31
|
+
"aria-label": "Test",
|
32
|
+
title: "Test"
|
33
|
+
}));
|
34
|
+
expect(consoleSpy).not.toHaveBeenCalled();
|
35
|
+
});
|
36
|
+
});
|
6
37
|
it('renders a default data-test attribute', () => {
|
7
38
|
const dataTest = 'dhis2-uicore-button';
|
8
39
|
const wrapper = mount( /*#__PURE__*/React.createElement(Button, {
|
@@ -39,6 +39,14 @@ export const Button = _ref => {
|
|
39
39
|
ref.current.focus();
|
40
40
|
}
|
41
41
|
}, [initialFocus, ref.current]);
|
42
|
+
const {
|
43
|
+
'aria-label': ariaLabel,
|
44
|
+
title
|
45
|
+
} = otherProps;
|
46
|
+
|
47
|
+
if (!children && !title && !ariaLabel) {
|
48
|
+
console.debug('Button component has no children but is missing title and ariaLabel attribute.');
|
49
|
+
}
|
42
50
|
|
43
51
|
const handleClick = event => onClick && onClick({
|
44
52
|
value,
|
@@ -1,8 +1,10 @@
|
|
1
1
|
import { Layer } from '@dhis2-ui/layer';
|
2
2
|
import { Popper } from '@dhis2-ui/popper';
|
3
|
+
import { render, fireEvent, waitFor } from '@testing-library/react';
|
3
4
|
import { mount } from 'enzyme';
|
4
5
|
import React from 'react';
|
5
6
|
import { act } from 'react-dom/test-utils';
|
7
|
+
import { Modal } from '../../../../modal/src/modal/modal.js';
|
6
8
|
import { Button } from '../../index.js';
|
7
9
|
import { DropdownButton } from '../dropdown-button.js';
|
8
10
|
describe('<DropdownButton>', () => {
|
@@ -34,6 +36,52 @@ describe('<DropdownButton>', () => {
|
|
34
36
|
open: false
|
35
37
|
}));
|
36
38
|
});
|
39
|
+
it('closes dropdown when escape key is pressed', async () => {
|
40
|
+
const componentText = 'Dropdown Content';
|
41
|
+
const {
|
42
|
+
getByTestId,
|
43
|
+
queryByText
|
44
|
+
} = render( /*#__PURE__*/React.createElement(DropdownButton, {
|
45
|
+
component: componentText
|
46
|
+
}));
|
47
|
+
const toggleButton = getByTestId('dhis2-uicore-dropdownbutton-toggle');
|
48
|
+
fireEvent.click(toggleButton);
|
49
|
+
await waitFor(() => {
|
50
|
+
expect(queryByText(componentText)).toBeInTheDocument();
|
51
|
+
});
|
52
|
+
fireEvent.keyDown(document, {
|
53
|
+
key: 'Escape'
|
54
|
+
});
|
55
|
+
await waitFor(() => {
|
56
|
+
expect(queryByText(componentText)).not.toBeInTheDocument();
|
57
|
+
});
|
58
|
+
});
|
59
|
+
test('modal remains open when dropdown button is closed on escape click', async () => {
|
60
|
+
const dropdownButtonText = 'Dropdown Content';
|
61
|
+
const headingText = 'Heading Text';
|
62
|
+
const modalContent = /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("h1", null, headingText), /*#__PURE__*/React.createElement(DropdownButton, {
|
63
|
+
component: dropdownButtonText
|
64
|
+
}));
|
65
|
+
const {
|
66
|
+
getByTestId,
|
67
|
+
queryByText
|
68
|
+
} = render( /*#__PURE__*/React.createElement(Modal, {
|
69
|
+
hide: false,
|
70
|
+
onClose: () => {}
|
71
|
+
}, modalContent));
|
72
|
+
const toggleButton = getByTestId('dhis2-uicore-dropdownbutton-toggle');
|
73
|
+
fireEvent.click(toggleButton);
|
74
|
+
await waitFor(() => {
|
75
|
+
expect(queryByText(dropdownButtonText)).toBeInTheDocument();
|
76
|
+
});
|
77
|
+
fireEvent.keyDown(document, {
|
78
|
+
key: 'Escape'
|
79
|
+
});
|
80
|
+
await waitFor(() => {
|
81
|
+
expect(queryByText(dropdownButtonText)).not.toBeInTheDocument();
|
82
|
+
expect(queryByText(headingText)).toBeInTheDocument();
|
83
|
+
});
|
84
|
+
});
|
37
85
|
});
|
38
86
|
describe('closed state', () => {
|
39
87
|
const onClick = jest.fn();
|
@@ -67,6 +67,17 @@ class DropdownButton extends Component {
|
|
67
67
|
|
68
68
|
_defineProperty(this, "anchorRef", /*#__PURE__*/React.createRef());
|
69
69
|
|
70
|
+
_defineProperty(this, "handleKeyDown", event => {
|
71
|
+
event.preventDefault();
|
72
|
+
|
73
|
+
if (event.key === 'Escape' && this.state.open) {
|
74
|
+
event.stopPropagation();
|
75
|
+
this.setState({
|
76
|
+
open: false
|
77
|
+
});
|
78
|
+
}
|
79
|
+
});
|
80
|
+
|
70
81
|
_defineProperty(this, "onClickHandler", (_ref3, event) => {
|
71
82
|
let {
|
72
83
|
name,
|
@@ -95,6 +106,14 @@ class DropdownButton extends Component {
|
|
95
106
|
});
|
96
107
|
}
|
97
108
|
|
109
|
+
componentDidMount() {
|
110
|
+
document.addEventListener('keydown', this.handleKeyDown);
|
111
|
+
}
|
112
|
+
|
113
|
+
componentWillUnmount() {
|
114
|
+
document.removeEventListener('keydown', this.handleKeyDown);
|
115
|
+
}
|
116
|
+
|
98
117
|
render() {
|
99
118
|
const {
|
100
119
|
component,
|
@@ -134,7 +153,8 @@ class DropdownButton extends Component {
|
|
134
153
|
value: value,
|
135
154
|
tabIndex: tabIndex,
|
136
155
|
type: type,
|
137
|
-
initialFocus: initialFocus
|
156
|
+
initialFocus: initialFocus,
|
157
|
+
"data-test": "dhis2-uicore-dropdownbutton-toggle"
|
138
158
|
}, children, /*#__PURE__*/React.createElement(ArrowIconComponent, {
|
139
159
|
className: "jsx-3163060161" + " " + (arrow.className || "")
|
140
160
|
})), open && /*#__PURE__*/React.createElement(Layer, {
|
@@ -0,0 +1,13 @@
|
|
1
|
+
//------------------------------------------------------------------------------
|
2
|
+
// <auto-generated>
|
3
|
+
// This code was generated by d2-i18n-generate.
|
4
|
+
//
|
5
|
+
// Changes to this file may cause incorrect behavior and will be lost if
|
6
|
+
// the code is regenerated.
|
7
|
+
// </auto-generated>
|
8
|
+
//------------------------------------------------------------------------------
|
9
|
+
import i18n from '@dhis2/d2-i18n';
|
10
|
+
import enTranslations from './en/translations.json';
|
11
|
+
const namespace = 'default';
|
12
|
+
i18n.addResources('en', namespace, enTranslations);
|
13
|
+
export default i18n;
|
@@ -10,6 +10,7 @@ import cx from 'classnames';
|
|
10
10
|
import PropTypes from 'prop-types';
|
11
11
|
import React, { Component } from 'react';
|
12
12
|
import { Button } from '../index.js';
|
13
|
+
import i18n from '../locales/index.js';
|
13
14
|
const rightButton = {
|
14
15
|
styles: /*#__PURE__*/React.createElement(_JSXStyle, {
|
15
16
|
id: "2502148942"
|
@@ -27,6 +28,18 @@ class SplitButton extends Component {
|
|
27
28
|
|
28
29
|
_defineProperty(this, "anchorRef", /*#__PURE__*/React.createRef());
|
29
30
|
|
31
|
+
_defineProperty(this, "handleKeyDown", event => {
|
32
|
+
event.preventDefault();
|
33
|
+
|
34
|
+
if (event.key === 'Escape' && this.state.open) {
|
35
|
+
event.stopPropagation();
|
36
|
+
this.setState({
|
37
|
+
open: false
|
38
|
+
});
|
39
|
+
this.anchorRef.current && this.anchorRef.current.focus();
|
40
|
+
}
|
41
|
+
});
|
42
|
+
|
30
43
|
_defineProperty(this, "onClick", (payload, event) => {
|
31
44
|
if (this.props.onClick) {
|
32
45
|
this.props.onClick({
|
@@ -37,9 +50,19 @@ class SplitButton extends Component {
|
|
37
50
|
}
|
38
51
|
});
|
39
52
|
|
40
|
-
_defineProperty(this, "onToggle", () =>
|
41
|
-
|
42
|
-
|
53
|
+
_defineProperty(this, "onToggle", () => {
|
54
|
+
this.setState(prevState => ({
|
55
|
+
open: !prevState.open
|
56
|
+
}));
|
57
|
+
});
|
58
|
+
}
|
59
|
+
|
60
|
+
componentDidMount() {
|
61
|
+
document.addEventListener('keydown', this.handleKeyDown);
|
62
|
+
}
|
63
|
+
|
64
|
+
componentWillUnmount() {
|
65
|
+
document.removeEventListener('keydown', this.handleKeyDown);
|
43
66
|
}
|
44
67
|
|
45
68
|
render() {
|
@@ -98,7 +121,9 @@ class SplitButton extends Component {
|
|
98
121
|
type: type,
|
99
122
|
tabIndex: tabIndex,
|
100
123
|
className: cx(className, rightButton.className),
|
101
|
-
dataTest: "".concat(dataTest, "-toggle")
|
124
|
+
dataTest: "".concat(dataTest, "-toggle"),
|
125
|
+
title: i18n.t('Toggle dropdown'),
|
126
|
+
"aria-label": i18n.t('Toggle dropdown')
|
102
127
|
}, arrow), open && /*#__PURE__*/React.createElement(Layer, {
|
103
128
|
onBackdropClick: this.onToggle,
|
104
129
|
transparent: true
|
@@ -0,0 +1,77 @@
|
|
1
|
+
import { render, fireEvent, cleanup, waitFor } from '@testing-library/react';
|
2
|
+
import React from 'react';
|
3
|
+
import '@testing-library/jest-dom/extend-expect';
|
4
|
+
import { SplitButton } from './split-button.js';
|
5
|
+
describe('SplitButton', () => {
|
6
|
+
afterEach(cleanup);
|
7
|
+
it('renders button with children', () => {
|
8
|
+
const {
|
9
|
+
getByText
|
10
|
+
} = render( /*#__PURE__*/React.createElement(SplitButton, null, "Click me"));
|
11
|
+
expect(getByText('Click me')).toBeInTheDocument();
|
12
|
+
});
|
13
|
+
it('toggles dropdown when left button is clicked', () => {
|
14
|
+
const {
|
15
|
+
getByTestId,
|
16
|
+
queryByTestId
|
17
|
+
} = render( /*#__PURE__*/React.createElement(SplitButton, null));
|
18
|
+
const toggleButton = getByTestId('dhis2-uicore-splitbutton-toggle');
|
19
|
+
fireEvent.click(toggleButton);
|
20
|
+
expect(queryByTestId('dhis2-uicore-splitbutton-menu')).toBeInTheDocument();
|
21
|
+
fireEvent.click(toggleButton);
|
22
|
+
expect(queryByTestId('dhis2-uicore-splitbutton-menu')).not.toBeInTheDocument();
|
23
|
+
});
|
24
|
+
it('renders dropdown content when open is true', () => {
|
25
|
+
const {
|
26
|
+
getByTestId
|
27
|
+
} = render( /*#__PURE__*/React.createElement(SplitButton, {
|
28
|
+
component: /*#__PURE__*/React.createElement("div", null, "Dropdown Content")
|
29
|
+
}));
|
30
|
+
const toggleButton = getByTestId('dhis2-uicore-splitbutton-toggle');
|
31
|
+
fireEvent.click(toggleButton);
|
32
|
+
expect(getByTestId('dhis2-uicore-splitbutton-menu')).toBeInTheDocument();
|
33
|
+
});
|
34
|
+
it("does not close dropdown 'Enter' key is pressed", async () => {
|
35
|
+
const {
|
36
|
+
getByTestId
|
37
|
+
} = render( /*#__PURE__*/React.createElement(SplitButton, {
|
38
|
+
component: /*#__PURE__*/React.createElement("div", null, "Dropdown Content")
|
39
|
+
}));
|
40
|
+
const toggleButton = getByTestId('dhis2-uicore-splitbutton-toggle');
|
41
|
+
fireEvent.click(toggleButton);
|
42
|
+
expect(getByTestId('dhis2-uicore-splitbutton-menu')).toBeInTheDocument();
|
43
|
+
fireEvent.keyDown(document, {
|
44
|
+
key: 'Enter'
|
45
|
+
}); // Use waitFor to wait for the DOM to update
|
46
|
+
|
47
|
+
await waitFor(() => {
|
48
|
+
expect(getByTestId('dhis2-uicore-splitbutton-menu')).toBeInTheDocument();
|
49
|
+
});
|
50
|
+
});
|
51
|
+
it('closes dropdown when escape key is pressed', async () => {
|
52
|
+
const {
|
53
|
+
getByTestId,
|
54
|
+
queryByTestId
|
55
|
+
} = render( /*#__PURE__*/React.createElement(SplitButton, {
|
56
|
+
component: /*#__PURE__*/React.createElement("div", null, "Dropdown Content")
|
57
|
+
}));
|
58
|
+
const toggleButton = getByTestId('dhis2-uicore-splitbutton-toggle');
|
59
|
+
fireEvent.click(toggleButton);
|
60
|
+
expect(getByTestId('dhis2-uicore-splitbutton-menu')).toBeInTheDocument();
|
61
|
+
fireEvent.keyDown(document, {
|
62
|
+
key: 'Escape'
|
63
|
+
}); // Use waitFor to wait for the DOM to update
|
64
|
+
|
65
|
+
await waitFor(() => {
|
66
|
+
expect(queryByTestId('dhis2-uicore-splitbutton-menu')).not.toBeInTheDocument();
|
67
|
+
});
|
68
|
+
});
|
69
|
+
it('adds title and aria-label attributes to the right button', () => {
|
70
|
+
const {
|
71
|
+
getByTestId
|
72
|
+
} = render( /*#__PURE__*/React.createElement(SplitButton, null));
|
73
|
+
const toggleButton = getByTestId('dhis2-uicore-splitbutton-toggle');
|
74
|
+
expect(toggleButton).toHaveAttribute('title', 'Toggle dropdown');
|
75
|
+
expect(toggleButton).toHaveAttribute('aria-label', 'Toggle dropdown');
|
76
|
+
});
|
77
|
+
});
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@dhis2-ui/button",
|
3
|
-
"version": "9.
|
3
|
+
"version": "9.7.0",
|
4
4
|
"description": "UI Button",
|
5
5
|
"repository": {
|
6
6
|
"type": "git",
|
@@ -33,11 +33,11 @@
|
|
33
33
|
},
|
34
34
|
"dependencies": {
|
35
35
|
"@dhis2/prop-types": "^3.1.2",
|
36
|
-
"@dhis2-ui/layer": "9.
|
37
|
-
"@dhis2-ui/loader": "9.
|
38
|
-
"@dhis2-ui/popper": "9.
|
39
|
-
"@dhis2/ui-constants": "9.
|
40
|
-
"@dhis2/ui-icons": "9.
|
36
|
+
"@dhis2-ui/layer": "9.7.0",
|
37
|
+
"@dhis2-ui/loader": "9.7.0",
|
38
|
+
"@dhis2-ui/popper": "9.7.0",
|
39
|
+
"@dhis2/ui-constants": "9.7.0",
|
40
|
+
"@dhis2/ui-icons": "9.7.0",
|
41
41
|
"classnames": "^2.3.1",
|
42
42
|
"prop-types": "^15.7.2"
|
43
43
|
},
|