@dhis2-ui/button 9.5.0 → 9.7.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/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
|
},
|