@dhis2-ui/button 9.4.2 → 9.5.0-alpha.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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,
@@ -0,0 +1,3 @@
1
+ {
2
+ "Toggle dropdown": "Toggle dropdown"
3
+ }
@@ -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", () => this.setState({
62
- open: !this.state.open
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,
@@ -0,0 +1,3 @@
1
+ {
2
+ "Toggle dropdown": "Toggle dropdown"
3
+ }
@@ -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", () => this.setState({
41
- open: !this.state.open
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.4.2",
3
+ "version": "9.5.0-alpha.1",
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.4.2",
37
- "@dhis2-ui/loader": "9.4.2",
38
- "@dhis2-ui/popper": "9.4.2",
39
- "@dhis2/ui-constants": "9.4.2",
40
- "@dhis2/ui-icons": "9.4.2",
36
+ "@dhis2-ui/layer": "9.5.0-alpha.1",
37
+ "@dhis2-ui/loader": "9.5.0-alpha.1",
38
+ "@dhis2-ui/popper": "9.5.0-alpha.1",
39
+ "@dhis2/ui-constants": "9.5.0-alpha.1",
40
+ "@dhis2/ui-icons": "9.5.0-alpha.1",
41
41
  "classnames": "^2.3.1",
42
42
  "prop-types": "^15.7.2"
43
43
  },