@dhis2-ui/menu 10.0.4 → 10.1.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.
@@ -67,4 +67,39 @@ describe('Menu Component', () => {
67
67
  label: 'Test submenu child'
68
68
  }).exists()).toBe(false);
69
69
  });
70
+ describe('aria-label attribute', () => {
71
+ it('produces a log if the label prop is not a string', () => {
72
+ // Prep to test logs
73
+ const originalConsoleDebug = console.debug;
74
+ const consoleDebugMock = jest.fn();
75
+ console.debug = consoleDebugMock;
76
+ const menuItemDataTest = 'data-test-menu-item';
77
+ const wrapper = (0, _enzyme.mount)(/*#__PURE__*/_react.default.createElement(_menuItem.MenuItem, {
78
+ dataTest: menuItemDataTest,
79
+ label: /*#__PURE__*/_react.default.createElement("span", null, 'Node label')
80
+ }));
81
+ const menuItem = wrapper.find({
82
+ 'data-test': menuItemDataTest
83
+ });
84
+ expect(menuItem.childAt(0).prop('role')).toBe('menuitem');
85
+ expect(menuItem.childAt(0).prop('aria-label')).toBe(undefined);
86
+ expect(consoleDebugMock).toHaveBeenCalledWith('The label prop on MenuItem is not a string; a value for the ariaLabel prop should be provided');
87
+
88
+ // Teardown
89
+ console.debug = originalConsoleDebug;
90
+ });
91
+ it('uses the ariaLabel prop for aria-label if defined', () => {
92
+ const menuItemDataTest = 'data-test-menu-item';
93
+ const wrapper = (0, _enzyme.mount)(/*#__PURE__*/_react.default.createElement(_menuItem.MenuItem, {
94
+ dataTest: menuItemDataTest,
95
+ label: /*#__PURE__*/_react.default.createElement("span", null, 'Node label'),
96
+ ariaLabel: "Aria label"
97
+ }));
98
+ const menuItem = wrapper.find({
99
+ 'data-test': menuItemDataTest
100
+ });
101
+ expect(menuItem.childAt(0).prop('role')).toBe('menuitem');
102
+ expect(menuItem.childAt(0).prop('aria-label')).toBe('Aria label');
103
+ });
104
+ });
70
105
  });
@@ -52,6 +52,7 @@ const MenuItem = _ref2 => {
52
52
  chevron,
53
53
  value,
54
54
  label,
55
+ ariaLabel,
55
56
  showSubMenu,
56
57
  toggleSubMenu,
57
58
  suffix,
@@ -96,6 +97,12 @@ const MenuItem = _ref2 => {
96
97
  menuItem.removeEventListener('keydown', handleKeyDown);
97
98
  };
98
99
  }, [openSubMenus]);
100
+ const resolvedAriaLabel = (0, _react.useMemo)(() => {
101
+ if (typeof label !== 'string' && ariaLabel === undefined) {
102
+ console.debug('The label prop on MenuItem is not a string; a value for the ariaLabel prop should be provided');
103
+ }
104
+ return ariaLabel !== null && ariaLabel !== void 0 ? ariaLabel : typeof label === 'string' ? label : undefined;
105
+ }, [ariaLabel, label]);
99
106
  return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement("li", {
100
107
  ref: menuItemRef,
101
108
  "data-test": dataTest,
@@ -123,7 +130,7 @@ const MenuItem = _ref2 => {
123
130
  "aria-disabled": disabled,
124
131
  "aria-haspopup": children && 'menu',
125
132
  "aria-expanded": showSubMenu,
126
- "aria-label": label,
133
+ "aria-label": resolvedAriaLabel,
127
134
  className: `jsx-${_menuItemStyles.default.__hash}`
128
135
  }, icon && /*#__PURE__*/_react.default.createElement("span", {
129
136
  className: `jsx-${_menuItemStyles.default.__hash}` + " " + "icon"
@@ -145,6 +152,12 @@ const MenuItem = _ref2 => {
145
152
  exports.MenuItem = MenuItem;
146
153
  MenuItem.propTypes = {
147
154
  active: _propTypes.default.bool,
155
+ /**
156
+ * By default, the label prop is used for the aria-label attribute on the
157
+ * underlying HTML element. If this prop is defined, it will be used as the
158
+ * aria-label instead
159
+ */
160
+ ariaLabel: _propTypes.default.string,
148
161
  checkbox: _propTypes.default.bool,
149
162
  checked: _propTypes.default.bool,
150
163
  chevron: _propTypes.default.bool,
@@ -162,7 +175,7 @@ MenuItem.propTypes = {
162
175
  href: _propTypes.default.string,
163
176
  /** An icon for the left side of the menu item */
164
177
  icon: _propTypes.default.node,
165
- /** Text in the menu item */
178
+ /** Text in the menu item. If it's a string, will be used as aria-label */
166
179
  label: _propTypes.default.node,
167
180
  /** When true, nested menu items are shown in a Popper */
168
181
  showSubMenu: _propTypes.default.bool,
@@ -64,4 +64,39 @@ describe('Menu Component', () => {
64
64
  label: 'Test submenu child'
65
65
  }).exists()).toBe(false);
66
66
  });
67
+ describe('aria-label attribute', () => {
68
+ it('produces a log if the label prop is not a string', () => {
69
+ // Prep to test logs
70
+ const originalConsoleDebug = console.debug;
71
+ const consoleDebugMock = jest.fn();
72
+ console.debug = consoleDebugMock;
73
+ const menuItemDataTest = 'data-test-menu-item';
74
+ const wrapper = mount(/*#__PURE__*/React.createElement(MenuItem, {
75
+ dataTest: menuItemDataTest,
76
+ label: /*#__PURE__*/React.createElement("span", null, 'Node label')
77
+ }));
78
+ const menuItem = wrapper.find({
79
+ 'data-test': menuItemDataTest
80
+ });
81
+ expect(menuItem.childAt(0).prop('role')).toBe('menuitem');
82
+ expect(menuItem.childAt(0).prop('aria-label')).toBe(undefined);
83
+ expect(consoleDebugMock).toHaveBeenCalledWith('The label prop on MenuItem is not a string; a value for the ariaLabel prop should be provided');
84
+
85
+ // Teardown
86
+ console.debug = originalConsoleDebug;
87
+ });
88
+ it('uses the ariaLabel prop for aria-label if defined', () => {
89
+ const menuItemDataTest = 'data-test-menu-item';
90
+ const wrapper = mount(/*#__PURE__*/React.createElement(MenuItem, {
91
+ dataTest: menuItemDataTest,
92
+ label: /*#__PURE__*/React.createElement("span", null, 'Node label'),
93
+ ariaLabel: "Aria label"
94
+ }));
95
+ const menuItem = wrapper.find({
96
+ 'data-test': menuItemDataTest
97
+ });
98
+ expect(menuItem.childAt(0).prop('role')).toBe('menuitem');
99
+ expect(menuItem.childAt(0).prop('aria-label')).toBe('Aria label');
100
+ });
101
+ });
67
102
  });
@@ -4,7 +4,7 @@ import { Popper } from '@dhis2-ui/popper';
4
4
  import { Portal } from '@dhis2-ui/portal';
5
5
  import cx from 'classnames';
6
6
  import PropTypes from 'prop-types';
7
- import React, { useEffect, useRef, useState } from 'react';
7
+ import React, { useEffect, useRef, useState, useMemo } from 'react';
8
8
  import { FlyoutMenu } from '../flyout-menu/index.js';
9
9
  import styles from './menu-item.styles.js';
10
10
  const isModifiedEvent = evt => evt.metaKey || evt.altKey || evt.ctrlKey || evt.shiftKey;
@@ -43,6 +43,7 @@ const MenuItem = _ref2 => {
43
43
  chevron,
44
44
  value,
45
45
  label,
46
+ ariaLabel,
46
47
  showSubMenu,
47
48
  toggleSubMenu,
48
49
  suffix,
@@ -87,6 +88,12 @@ const MenuItem = _ref2 => {
87
88
  menuItem.removeEventListener('keydown', handleKeyDown);
88
89
  };
89
90
  }, [openSubMenus]);
91
+ const resolvedAriaLabel = useMemo(() => {
92
+ if (typeof label !== 'string' && ariaLabel === undefined) {
93
+ console.debug('The label prop on MenuItem is not a string; a value for the ariaLabel prop should be provided');
94
+ }
95
+ return ariaLabel !== null && ariaLabel !== void 0 ? ariaLabel : typeof label === 'string' ? label : undefined;
96
+ }, [ariaLabel, label]);
90
97
  return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("li", {
91
98
  ref: menuItemRef,
92
99
  "data-test": dataTest,
@@ -114,7 +121,7 @@ const MenuItem = _ref2 => {
114
121
  "aria-disabled": disabled,
115
122
  "aria-haspopup": children && 'menu',
116
123
  "aria-expanded": showSubMenu,
117
- "aria-label": label,
124
+ "aria-label": resolvedAriaLabel,
118
125
  className: `jsx-${styles.__hash}`
119
126
  }, icon && /*#__PURE__*/React.createElement("span", {
120
127
  className: `jsx-${styles.__hash}` + " " + "icon"
@@ -135,6 +142,12 @@ const MenuItem = _ref2 => {
135
142
  };
136
143
  MenuItem.propTypes = {
137
144
  active: PropTypes.bool,
145
+ /**
146
+ * By default, the label prop is used for the aria-label attribute on the
147
+ * underlying HTML element. If this prop is defined, it will be used as the
148
+ * aria-label instead
149
+ */
150
+ ariaLabel: PropTypes.string,
138
151
  checkbox: PropTypes.bool,
139
152
  checked: PropTypes.bool,
140
153
  chevron: PropTypes.bool,
@@ -152,7 +165,7 @@ MenuItem.propTypes = {
152
165
  href: PropTypes.string,
153
166
  /** An icon for the left side of the menu item */
154
167
  icon: PropTypes.node,
155
- /** Text in the menu item */
168
+ /** Text in the menu item. If it's a string, will be used as aria-label */
156
169
  label: PropTypes.node,
157
170
  /** When true, nested menu items are shown in a Popper */
158
171
  showSubMenu: PropTypes.bool,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dhis2-ui/menu",
3
- "version": "10.0.4",
3
+ "version": "10.1.0",
4
4
  "description": "UI Menu",
5
5
  "repository": {
6
6
  "type": "git",
@@ -33,13 +33,13 @@
33
33
  },
34
34
  "dependencies": {
35
35
  "@dhis2/prop-types": "^3.1.2",
36
- "@dhis2-ui/card": "10.0.4",
37
- "@dhis2-ui/divider": "10.0.4",
38
- "@dhis2-ui/layer": "10.0.4",
39
- "@dhis2-ui/popper": "10.0.4",
40
- "@dhis2-ui/portal": "10.0.4",
41
- "@dhis2/ui-constants": "10.0.4",
42
- "@dhis2/ui-icons": "10.0.4",
36
+ "@dhis2-ui/card": "10.1.0",
37
+ "@dhis2-ui/divider": "10.1.0",
38
+ "@dhis2-ui/layer": "10.1.0",
39
+ "@dhis2-ui/popper": "10.1.0",
40
+ "@dhis2-ui/portal": "10.1.0",
41
+ "@dhis2/ui-constants": "10.1.0",
42
+ "@dhis2/ui-icons": "10.1.0",
43
43
  "classnames": "^2.3.1",
44
44
  "prop-types": "^15.7.2"
45
45
  },