@instructure/ui-menu 10.10.1-snapshot-9 → 10.10.1-snapshot-14
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/CHANGELOG.md +2 -1
- package/es/Menu/MenuItem/index.js +0 -2
- package/es/Menu/MenuItemGroup/__new-tests__/MenuItemGroup.test.js +7 -28
- package/es/Menu/MenuItemGroup/index.js +4 -13
- package/es/Menu/MenuItemSeparator/index.js +2 -0
- package/es/Menu/index.js +8 -16
- package/lib/Menu/MenuItem/index.js +0 -2
- package/lib/Menu/MenuItemGroup/__new-tests__/MenuItemGroup.test.js +7 -28
- package/lib/Menu/MenuItemGroup/index.js +4 -13
- package/lib/Menu/MenuItemSeparator/index.js +2 -0
- package/lib/Menu/index.js +8 -16
- package/package.json +19 -19
- package/src/Menu/MenuItem/index.tsx +0 -2
- package/src/Menu/MenuItemGroup/__new-tests__/MenuItemGroup.test.tsx +3 -38
- package/src/Menu/MenuItemGroup/index.tsx +15 -26
- package/src/Menu/MenuItemSeparator/index.tsx +2 -0
- package/src/Menu/index.tsx +51 -63
- package/src/Menu/props.ts +3 -3
- package/tsconfig.build.tsbuildinfo +1 -1
- package/types/Menu/MenuItem/index.d.ts +1 -1
- package/types/Menu/MenuItem/index.d.ts.map +1 -1
- package/types/Menu/MenuItemGroup/index.d.ts +5 -2
- package/types/Menu/MenuItemGroup/index.d.ts.map +1 -1
- package/types/Menu/MenuItemSeparator/index.d.ts.map +1 -1
- package/types/Menu/index.d.ts +16 -10
- package/types/Menu/index.d.ts.map +1 -1
- package/types/Menu/props.d.ts +3 -3
- package/types/Menu/props.d.ts.map +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,12 +3,13 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
-
## [10.10.1-snapshot-
|
|
6
|
+
## [10.10.1-snapshot-14](https://github.com/instructure/instructure-ui/compare/v10.10.0...v10.10.1-snapshot-14) (2025-02-03)
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
### Bug Fixes
|
|
10
10
|
|
|
11
11
|
* **ui-menu:** make Menu.Item apply target prop ([6c85b31](https://github.com/instructure/instructure-ui/commit/6c85b312212edf9f08317d0d6aeb7c28fe1eb3b3))
|
|
12
|
+
* **ui-menu:** screenreaders should read the correct number of menu items ([0670648](https://github.com/instructure/instructure-ui/commit/06706488cd4b550594f7a5b2b52ea674c79b0530))
|
|
12
13
|
|
|
13
14
|
|
|
14
15
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var _MenuItemGroup, _MenuItemGroup2, _MenuItemGroup3, _MenuItemGroup4,
|
|
1
|
+
var _MenuItemGroup, _MenuItemGroup2, _MenuItemGroup3, _MenuItemGroup4, _MenuItem, _MenuItem2, _MenuItemSeparator, _MenuItem3, _MenuItem4, _MenuItemSeparator2, _MenuItem5, _MenuItem6, _MenuItem7, _MenuItem8, _MenuItemSeparator3, _MenuItem9, _MenuItem10, _MenuItemSeparator4;
|
|
2
2
|
/*
|
|
3
3
|
* The MIT License (MIT)
|
|
4
4
|
*
|
|
@@ -36,40 +36,21 @@ describe('<MenuItemGroup />', () => {
|
|
|
36
36
|
label: "Menu Label"
|
|
37
37
|
}, /*#__PURE__*/React.createElement(MenuItem, null, "Item Text 1"), /*#__PURE__*/React.createElement(MenuItem, null, "Item Text 2"), /*#__PURE__*/React.createElement(MenuItemSeparator, null)))),
|
|
38
38
|
container = _render.container;
|
|
39
|
-
const group = container.querySelector("[
|
|
40
|
-
const groupMenu = screen.getByRole('menu');
|
|
39
|
+
const group = container.querySelector("[class*='menuItemGroup']");
|
|
41
40
|
expect(group).toBeInTheDocument();
|
|
42
41
|
expect(group).toHaveTextContent('Menu Label');
|
|
43
|
-
expect(
|
|
44
|
-
expect(
|
|
45
|
-
expect(groupMenu).toHaveTextContent('Item Text 2');
|
|
46
|
-
});
|
|
47
|
-
it('should set the role to "menu"', () => {
|
|
48
|
-
const _render2 = render(_MenuItemGroup2 || (_MenuItemGroup2 = /*#__PURE__*/React.createElement(MenuItemGroup, {
|
|
49
|
-
label: "Select one"
|
|
50
|
-
}, /*#__PURE__*/React.createElement(MenuItem, null, "Foo"), /*#__PURE__*/React.createElement(MenuItem, null, "Bar"), /*#__PURE__*/React.createElement(MenuItemSeparator, null)))),
|
|
51
|
-
container = _render2.container;
|
|
52
|
-
const menuItemGroup = container.querySelector("[class*='menuItemGroup__items']");
|
|
53
|
-
expect(menuItemGroup).toHaveAttribute('role', 'menu');
|
|
54
|
-
});
|
|
55
|
-
it('should set the list item role to "none"', () => {
|
|
56
|
-
render(_MenuItemGroup3 || (_MenuItemGroup3 = /*#__PURE__*/React.createElement(MenuItemGroup, {
|
|
57
|
-
label: "Select one"
|
|
58
|
-
}, /*#__PURE__*/React.createElement(MenuItem, null, "Food"), /*#__PURE__*/React.createElement(MenuItem, null, "Bar"))));
|
|
59
|
-
const menu = screen.getByRole('menu');
|
|
60
|
-
const menuListItem = menu.firstChild;
|
|
61
|
-
expect(menuListItem.tagName).toBe('LI');
|
|
62
|
-
expect(menuListItem).toHaveAttribute('role', 'none');
|
|
42
|
+
expect(group).toHaveTextContent('Item Text 1');
|
|
43
|
+
expect(group).toHaveTextContent('Item Text 2');
|
|
63
44
|
});
|
|
64
45
|
it('should default to children with type "radio"', () => {
|
|
65
|
-
render(
|
|
46
|
+
render(_MenuItemGroup2 || (_MenuItemGroup2 = /*#__PURE__*/React.createElement(MenuItemGroup, {
|
|
66
47
|
label: "Select one"
|
|
67
48
|
}, /*#__PURE__*/React.createElement(MenuItem, null, "Foo"), /*#__PURE__*/React.createElement(MenuItem, null, "Bar"), /*#__PURE__*/React.createElement(MenuItemSeparator, null))));
|
|
68
49
|
const menuItems = screen.getAllByRole('menuitemradio');
|
|
69
50
|
expect(menuItems).toHaveLength(2);
|
|
70
51
|
});
|
|
71
52
|
it('should render children with type "checkbox" if allowMultiple is true', () => {
|
|
72
|
-
render(
|
|
53
|
+
render(_MenuItemGroup3 || (_MenuItemGroup3 = /*#__PURE__*/React.createElement(MenuItemGroup, {
|
|
73
54
|
label: "Select a few",
|
|
74
55
|
allowMultiple: true
|
|
75
56
|
}, /*#__PURE__*/React.createElement(MenuItem, null, "Foo"), /*#__PURE__*/React.createElement(MenuItem, null, "Bar"), /*#__PURE__*/React.createElement(MenuItemSeparator, null))));
|
|
@@ -77,13 +58,11 @@ describe('<MenuItemGroup />', () => {
|
|
|
77
58
|
expect(menuItems).toHaveLength(2);
|
|
78
59
|
});
|
|
79
60
|
it('should set aria-disabled', () => {
|
|
80
|
-
render(
|
|
61
|
+
render(_MenuItemGroup4 || (_MenuItemGroup4 = /*#__PURE__*/React.createElement(MenuItemGroup, {
|
|
81
62
|
label: "Select one",
|
|
82
63
|
disabled: true
|
|
83
64
|
}, /*#__PURE__*/React.createElement(MenuItem, null, "Foo"), /*#__PURE__*/React.createElement(MenuItem, null, "Bar"), /*#__PURE__*/React.createElement(MenuItemSeparator, null))));
|
|
84
|
-
const menu = screen.getByRole('menu');
|
|
85
65
|
const menuItems = screen.getAllByRole('menuitemradio');
|
|
86
|
-
expect(menu).toHaveAttribute('aria-disabled', 'true');
|
|
87
66
|
expect(menuItems).toHaveLength(2);
|
|
88
67
|
expect(menuItems[0]).toHaveAttribute('aria-disabled', 'true');
|
|
89
68
|
expect(menuItems[1]).toHaveAttribute('aria-disabled', 'true');
|
|
@@ -42,7 +42,6 @@ id: Menu.Group
|
|
|
42
42
|
let MenuItemGroup = (_dec = withDeterministicId(), _dec2 = withStyle(generateStyle, generateComponentTheme), _dec3 = testable(), _dec(_class = _dec2(_class = _dec3(_class = (_MenuItemGroup = class MenuItemGroup extends Component {
|
|
43
43
|
constructor(props) {
|
|
44
44
|
super(props);
|
|
45
|
-
this._labelId = void 0;
|
|
46
45
|
this.ref = null;
|
|
47
46
|
this.handleRef = el => {
|
|
48
47
|
this.ref = el;
|
|
@@ -84,7 +83,6 @@ let MenuItemGroup = (_dec = withDeterministicId(), _dec2 = withStyle(generateSty
|
|
|
84
83
|
selected: this.selectedFromChildren(props) || props.defaultSelected
|
|
85
84
|
};
|
|
86
85
|
}
|
|
87
|
-
this._labelId = props.deterministicId('MenuItemGroup');
|
|
88
86
|
}
|
|
89
87
|
componentDidMount() {
|
|
90
88
|
var _this$props$makeStyle, _this$props;
|
|
@@ -135,9 +133,7 @@ let MenuItemGroup = (_dec = withDeterministicId(), _dec2 = withStyle(generateSty
|
|
|
135
133
|
if (matchComponentTypes(child, [MenuItem])) {
|
|
136
134
|
++index;
|
|
137
135
|
const value = child.props.value || index;
|
|
138
|
-
return
|
|
139
|
-
role: "none"
|
|
140
|
-
}, ' ', safeCloneElement(child, {
|
|
136
|
+
return safeCloneElement(child, {
|
|
141
137
|
tabIndex: isTabbable && index === 0 ? 0 : -1,
|
|
142
138
|
controls,
|
|
143
139
|
value,
|
|
@@ -148,7 +144,7 @@ let MenuItemGroup = (_dec = withDeterministicId(), _dec2 = withStyle(generateSty
|
|
|
148
144
|
selected: this.selected.indexOf(value) > -1,
|
|
149
145
|
onSelect: this.handleSelect,
|
|
150
146
|
onMouseOver
|
|
151
|
-
})
|
|
147
|
+
});
|
|
152
148
|
} else {
|
|
153
149
|
return child;
|
|
154
150
|
}
|
|
@@ -159,15 +155,10 @@ let MenuItemGroup = (_dec = withDeterministicId(), _dec2 = withStyle(generateSty
|
|
|
159
155
|
const props = omitProps(this.props, MenuItemGroup.allowedProps);
|
|
160
156
|
return jsx("span", Object.assign({}, props, {
|
|
161
157
|
css: (_this$props$styles2 = this.props.styles) === null || _this$props$styles2 === void 0 ? void 0 : _this$props$styles2.menuItemGroup,
|
|
162
|
-
role: "presentation",
|
|
163
158
|
ref: this.handleRef
|
|
164
|
-
}), jsx("
|
|
165
|
-
id: this._labelId
|
|
166
|
-
}, this.renderLabel()), jsx("ul", {
|
|
167
|
-
role: "menu",
|
|
159
|
+
}), this.renderLabel(), jsx("div", {
|
|
168
160
|
css: (_this$props$styles3 = this.props.styles) === null || _this$props$styles3 === void 0 ? void 0 : _this$props$styles3.items,
|
|
169
|
-
"aria-disabled": this.props.disabled ? 'true' : void 0
|
|
170
|
-
"aria-labelledby": this._labelId
|
|
161
|
+
"aria-disabled": this.props.disabled ? 'true' : void 0
|
|
171
162
|
}, this.renderChildren()));
|
|
172
163
|
}
|
|
173
164
|
}, _MenuItemGroup.displayName = "MenuItemGroup", _MenuItemGroup.componentId = 'Menu.Group', _MenuItemGroup.propTypes = propTypes, _MenuItemGroup.allowedProps = allowedProps, _MenuItemGroup.defaultProps = {
|
|
@@ -57,6 +57,8 @@ let MenuItemSeparator = (_dec = withStyle(generateStyle, generateComponentTheme)
|
|
|
57
57
|
render() {
|
|
58
58
|
var _this$props$styles;
|
|
59
59
|
const props = omitProps(this.props, MenuItemSeparator.allowedProps);
|
|
60
|
+
// role="separator" would fit better here, but it causes NVDA to stop the
|
|
61
|
+
// MenuItem count after it
|
|
60
62
|
return jsx("div", Object.assign({}, props, {
|
|
61
63
|
role: "presentation",
|
|
62
64
|
css: (_this$props$styles = this.props.styles) === null || _this$props$styles === void 0 ? void 0 : _this$props$styles.menuItemSeparator,
|
package/es/Menu/index.js
CHANGED
|
@@ -235,16 +235,12 @@ let Menu = (_dec = withDeterministicId(), _dec2 = withStyle(generateStyle, gener
|
|
|
235
235
|
count += 1;
|
|
236
236
|
const isTabbable = !this.state.hasFocus && count === 1;
|
|
237
237
|
if (matchComponentTypes(child, ['MenuItemSeparator'])) {
|
|
238
|
-
return
|
|
239
|
-
role: "none"
|
|
240
|
-
}, child);
|
|
238
|
+
return child;
|
|
241
239
|
}
|
|
242
240
|
const menuItemChild = child;
|
|
243
241
|
const controls = menuItemChild.props['aria-controls'] || menuItemChild.props.controls || this.props['aria-controls'] || this.props.controls;
|
|
244
242
|
if (matchComponentTypes(child, ['MenuItem'])) {
|
|
245
|
-
return
|
|
246
|
-
role: "none"
|
|
247
|
-
}, safeCloneElement(child, {
|
|
243
|
+
return safeCloneElement(child, {
|
|
248
244
|
controls,
|
|
249
245
|
children: child.props.children,
|
|
250
246
|
disabled: disabled || child.props.disabled,
|
|
@@ -253,12 +249,10 @@ let Menu = (_dec = withDeterministicId(), _dec2 = withStyle(generateStyle, gener
|
|
|
253
249
|
onSelect: this.handleMenuItemSelect,
|
|
254
250
|
onMouseOver: this.handleMenuItemMouseOver,
|
|
255
251
|
tabIndex: isTabbable ? 0 : -1
|
|
256
|
-
})
|
|
252
|
+
});
|
|
257
253
|
}
|
|
258
254
|
if (matchComponentTypes(child, ['MenuItemGroup'])) {
|
|
259
|
-
return
|
|
260
|
-
role: "none"
|
|
261
|
-
}, safeCloneElement(child, {
|
|
255
|
+
return safeCloneElement(child, {
|
|
262
256
|
label: child.props.label,
|
|
263
257
|
controls,
|
|
264
258
|
disabled: disabled || child.props.disabled,
|
|
@@ -267,13 +261,11 @@ let Menu = (_dec = withDeterministicId(), _dec2 = withStyle(generateStyle, gener
|
|
|
267
261
|
onSelect: this.handleMenuItemSelect,
|
|
268
262
|
onMouseOver: this.handleMenuItemMouseOver,
|
|
269
263
|
isTabbable
|
|
270
|
-
})
|
|
264
|
+
});
|
|
271
265
|
}
|
|
272
266
|
if (matchComponentTypes(child, ['Menu'])) {
|
|
273
267
|
const submenuDisabled = disabled || child.props.disabled;
|
|
274
|
-
return
|
|
275
|
-
role: "none"
|
|
276
|
-
}, safeCloneElement(child, {
|
|
268
|
+
return safeCloneElement(child, {
|
|
277
269
|
type: 'flyout',
|
|
278
270
|
controls,
|
|
279
271
|
disabled: submenuDisabled,
|
|
@@ -293,7 +285,7 @@ let Menu = (_dec = withDeterministicId(), _dec2 = withStyle(generateStyle, gener
|
|
|
293
285
|
disabled: submenuDisabled,
|
|
294
286
|
renderLabelInfo: child.props.renderLabelInfo
|
|
295
287
|
}, child.props.title || child.props.label)
|
|
296
|
-
})
|
|
288
|
+
});
|
|
297
289
|
}
|
|
298
290
|
return;
|
|
299
291
|
});
|
|
@@ -312,7 +304,7 @@ let Menu = (_dec = withDeterministicId(), _dec2 = withStyle(generateStyle, gener
|
|
|
312
304
|
removeMenuItem: this.removeMenuItem,
|
|
313
305
|
registerMenuItem: this.registerMenuItem
|
|
314
306
|
}
|
|
315
|
-
}, jsx("
|
|
307
|
+
}, jsx("div", {
|
|
316
308
|
role: "menu",
|
|
317
309
|
"aria-label": label,
|
|
318
310
|
tabIndex: 0,
|
|
@@ -8,7 +8,7 @@ require("@testing-library/jest-dom");
|
|
|
8
8
|
var _MenuItem11 = require("../../MenuItem");
|
|
9
9
|
var _MenuItemSeparator5 = require("../../MenuItemSeparator");
|
|
10
10
|
var _index = require("../index");
|
|
11
|
-
var _MenuItemGroup, _MenuItemGroup2, _MenuItemGroup3, _MenuItemGroup4,
|
|
11
|
+
var _MenuItemGroup, _MenuItemGroup2, _MenuItemGroup3, _MenuItemGroup4, _MenuItem, _MenuItem2, _MenuItemSeparator, _MenuItem3, _MenuItem4, _MenuItemSeparator2, _MenuItem5, _MenuItem6, _MenuItem7, _MenuItem8, _MenuItemSeparator3, _MenuItem9, _MenuItem10, _MenuItemSeparator4;
|
|
12
12
|
/*
|
|
13
13
|
* The MIT License (MIT)
|
|
14
14
|
*
|
|
@@ -38,40 +38,21 @@ describe('<MenuItemGroup />', () => {
|
|
|
38
38
|
label: "Menu Label"
|
|
39
39
|
}, /*#__PURE__*/_react.default.createElement(_MenuItem11.MenuItem, null, "Item Text 1"), /*#__PURE__*/_react.default.createElement(_MenuItem11.MenuItem, null, "Item Text 2"), /*#__PURE__*/_react.default.createElement(_MenuItemSeparator5.MenuItemSeparator, null)))),
|
|
40
40
|
container = _render.container;
|
|
41
|
-
const group = container.querySelector("[
|
|
42
|
-
const groupMenu = _react2.screen.getByRole('menu');
|
|
41
|
+
const group = container.querySelector("[class*='menuItemGroup']");
|
|
43
42
|
expect(group).toBeInTheDocument();
|
|
44
43
|
expect(group).toHaveTextContent('Menu Label');
|
|
45
|
-
expect(
|
|
46
|
-
expect(
|
|
47
|
-
expect(groupMenu).toHaveTextContent('Item Text 2');
|
|
48
|
-
});
|
|
49
|
-
it('should set the role to "menu"', () => {
|
|
50
|
-
const _render2 = (0, _react2.render)(_MenuItemGroup2 || (_MenuItemGroup2 = /*#__PURE__*/_react.default.createElement(_index.MenuItemGroup, {
|
|
51
|
-
label: "Select one"
|
|
52
|
-
}, /*#__PURE__*/_react.default.createElement(_MenuItem11.MenuItem, null, "Foo"), /*#__PURE__*/_react.default.createElement(_MenuItem11.MenuItem, null, "Bar"), /*#__PURE__*/_react.default.createElement(_MenuItemSeparator5.MenuItemSeparator, null)))),
|
|
53
|
-
container = _render2.container;
|
|
54
|
-
const menuItemGroup = container.querySelector("[class*='menuItemGroup__items']");
|
|
55
|
-
expect(menuItemGroup).toHaveAttribute('role', 'menu');
|
|
56
|
-
});
|
|
57
|
-
it('should set the list item role to "none"', () => {
|
|
58
|
-
(0, _react2.render)(_MenuItemGroup3 || (_MenuItemGroup3 = /*#__PURE__*/_react.default.createElement(_index.MenuItemGroup, {
|
|
59
|
-
label: "Select one"
|
|
60
|
-
}, /*#__PURE__*/_react.default.createElement(_MenuItem11.MenuItem, null, "Food"), /*#__PURE__*/_react.default.createElement(_MenuItem11.MenuItem, null, "Bar"))));
|
|
61
|
-
const menu = _react2.screen.getByRole('menu');
|
|
62
|
-
const menuListItem = menu.firstChild;
|
|
63
|
-
expect(menuListItem.tagName).toBe('LI');
|
|
64
|
-
expect(menuListItem).toHaveAttribute('role', 'none');
|
|
44
|
+
expect(group).toHaveTextContent('Item Text 1');
|
|
45
|
+
expect(group).toHaveTextContent('Item Text 2');
|
|
65
46
|
});
|
|
66
47
|
it('should default to children with type "radio"', () => {
|
|
67
|
-
(0, _react2.render)(
|
|
48
|
+
(0, _react2.render)(_MenuItemGroup2 || (_MenuItemGroup2 = /*#__PURE__*/_react.default.createElement(_index.MenuItemGroup, {
|
|
68
49
|
label: "Select one"
|
|
69
50
|
}, /*#__PURE__*/_react.default.createElement(_MenuItem11.MenuItem, null, "Foo"), /*#__PURE__*/_react.default.createElement(_MenuItem11.MenuItem, null, "Bar"), /*#__PURE__*/_react.default.createElement(_MenuItemSeparator5.MenuItemSeparator, null))));
|
|
70
51
|
const menuItems = _react2.screen.getAllByRole('menuitemradio');
|
|
71
52
|
expect(menuItems).toHaveLength(2);
|
|
72
53
|
});
|
|
73
54
|
it('should render children with type "checkbox" if allowMultiple is true', () => {
|
|
74
|
-
(0, _react2.render)(
|
|
55
|
+
(0, _react2.render)(_MenuItemGroup3 || (_MenuItemGroup3 = /*#__PURE__*/_react.default.createElement(_index.MenuItemGroup, {
|
|
75
56
|
label: "Select a few",
|
|
76
57
|
allowMultiple: true
|
|
77
58
|
}, /*#__PURE__*/_react.default.createElement(_MenuItem11.MenuItem, null, "Foo"), /*#__PURE__*/_react.default.createElement(_MenuItem11.MenuItem, null, "Bar"), /*#__PURE__*/_react.default.createElement(_MenuItemSeparator5.MenuItemSeparator, null))));
|
|
@@ -79,13 +60,11 @@ describe('<MenuItemGroup />', () => {
|
|
|
79
60
|
expect(menuItems).toHaveLength(2);
|
|
80
61
|
});
|
|
81
62
|
it('should set aria-disabled', () => {
|
|
82
|
-
(0, _react2.render)(
|
|
63
|
+
(0, _react2.render)(_MenuItemGroup4 || (_MenuItemGroup4 = /*#__PURE__*/_react.default.createElement(_index.MenuItemGroup, {
|
|
83
64
|
label: "Select one",
|
|
84
65
|
disabled: true
|
|
85
66
|
}, /*#__PURE__*/_react.default.createElement(_MenuItem11.MenuItem, null, "Foo"), /*#__PURE__*/_react.default.createElement(_MenuItem11.MenuItem, null, "Bar"), /*#__PURE__*/_react.default.createElement(_MenuItemSeparator5.MenuItemSeparator, null))));
|
|
86
|
-
const menu = _react2.screen.getByRole('menu');
|
|
87
67
|
const menuItems = _react2.screen.getAllByRole('menuitemradio');
|
|
88
|
-
expect(menu).toHaveAttribute('aria-disabled', 'true');
|
|
89
68
|
expect(menuItems).toHaveLength(2);
|
|
90
69
|
expect(menuItems[0]).toHaveAttribute('aria-disabled', 'true');
|
|
91
70
|
expect(menuItems[1]).toHaveAttribute('aria-disabled', 'true');
|
|
@@ -52,7 +52,6 @@ id: Menu.Group
|
|
|
52
52
|
let MenuItemGroup = exports.MenuItemGroup = (_dec = (0, _withDeterministicId.withDeterministicId)(), _dec2 = (0, _emotion.withStyle)(_styles.default, _theme.default), _dec3 = (0, _testable.testable)(), _dec(_class = _dec2(_class = _dec3(_class = (_MenuItemGroup = class MenuItemGroup extends _react.Component {
|
|
53
53
|
constructor(props) {
|
|
54
54
|
super(props);
|
|
55
|
-
this._labelId = void 0;
|
|
56
55
|
this.ref = null;
|
|
57
56
|
this.handleRef = el => {
|
|
58
57
|
this.ref = el;
|
|
@@ -94,7 +93,6 @@ let MenuItemGroup = exports.MenuItemGroup = (_dec = (0, _withDeterministicId.wit
|
|
|
94
93
|
selected: this.selectedFromChildren(props) || props.defaultSelected
|
|
95
94
|
};
|
|
96
95
|
}
|
|
97
|
-
this._labelId = props.deterministicId('MenuItemGroup');
|
|
98
96
|
}
|
|
99
97
|
componentDidMount() {
|
|
100
98
|
var _this$props$makeStyle, _this$props;
|
|
@@ -145,9 +143,7 @@ let MenuItemGroup = exports.MenuItemGroup = (_dec = (0, _withDeterministicId.wit
|
|
|
145
143
|
if ((0, _matchComponentTypes.matchComponentTypes)(child, [_MenuItem.MenuItem])) {
|
|
146
144
|
++index;
|
|
147
145
|
const value = child.props.value || index;
|
|
148
|
-
return (0,
|
|
149
|
-
role: "none"
|
|
150
|
-
}, ' ', (0, _safeCloneElement.safeCloneElement)(child, {
|
|
146
|
+
return (0, _safeCloneElement.safeCloneElement)(child, {
|
|
151
147
|
tabIndex: isTabbable && index === 0 ? 0 : -1,
|
|
152
148
|
controls,
|
|
153
149
|
value,
|
|
@@ -158,7 +154,7 @@ let MenuItemGroup = exports.MenuItemGroup = (_dec = (0, _withDeterministicId.wit
|
|
|
158
154
|
selected: this.selected.indexOf(value) > -1,
|
|
159
155
|
onSelect: this.handleSelect,
|
|
160
156
|
onMouseOver
|
|
161
|
-
})
|
|
157
|
+
});
|
|
162
158
|
} else {
|
|
163
159
|
return child;
|
|
164
160
|
}
|
|
@@ -169,15 +165,10 @@ let MenuItemGroup = exports.MenuItemGroup = (_dec = (0, _withDeterministicId.wit
|
|
|
169
165
|
const props = (0, _omitProps.omitProps)(this.props, MenuItemGroup.allowedProps);
|
|
170
166
|
return (0, _emotion.jsx)("span", Object.assign({}, props, {
|
|
171
167
|
css: (_this$props$styles2 = this.props.styles) === null || _this$props$styles2 === void 0 ? void 0 : _this$props$styles2.menuItemGroup,
|
|
172
|
-
role: "presentation",
|
|
173
168
|
ref: this.handleRef
|
|
174
|
-
}), (0, _emotion.jsx)("
|
|
175
|
-
id: this._labelId
|
|
176
|
-
}, this.renderLabel()), (0, _emotion.jsx)("ul", {
|
|
177
|
-
role: "menu",
|
|
169
|
+
}), this.renderLabel(), (0, _emotion.jsx)("div", {
|
|
178
170
|
css: (_this$props$styles3 = this.props.styles) === null || _this$props$styles3 === void 0 ? void 0 : _this$props$styles3.items,
|
|
179
|
-
"aria-disabled": this.props.disabled ? 'true' : void 0
|
|
180
|
-
"aria-labelledby": this._labelId
|
|
171
|
+
"aria-disabled": this.props.disabled ? 'true' : void 0
|
|
181
172
|
}, this.renderChildren()));
|
|
182
173
|
}
|
|
183
174
|
}, _MenuItemGroup.displayName = "MenuItemGroup", _MenuItemGroup.componentId = 'Menu.Group', _MenuItemGroup.propTypes = _props.propTypes, _MenuItemGroup.allowedProps = _props.allowedProps, _MenuItemGroup.defaultProps = {
|
|
@@ -63,6 +63,8 @@ let MenuItemSeparator = exports.MenuItemSeparator = (_dec = (0, _emotion.withSty
|
|
|
63
63
|
render() {
|
|
64
64
|
var _this$props$styles;
|
|
65
65
|
const props = (0, _omitProps.omitProps)(this.props, MenuItemSeparator.allowedProps);
|
|
66
|
+
// role="separator" would fit better here, but it causes NVDA to stop the
|
|
67
|
+
// MenuItem count after it
|
|
66
68
|
return (0, _emotion.jsx)("div", Object.assign({}, props, {
|
|
67
69
|
role: "presentation",
|
|
68
70
|
css: (_this$props$styles = this.props.styles) === null || _this$props$styles === void 0 ? void 0 : _this$props$styles.menuItemSeparator,
|
package/lib/Menu/index.js
CHANGED
|
@@ -264,16 +264,12 @@ let Menu = exports.Menu = (_dec = (0, _withDeterministicId.withDeterministicId)(
|
|
|
264
264
|
count += 1;
|
|
265
265
|
const isTabbable = !this.state.hasFocus && count === 1;
|
|
266
266
|
if ((0, _matchComponentTypes.matchComponentTypes)(child, ['MenuItemSeparator'])) {
|
|
267
|
-
return
|
|
268
|
-
role: "none"
|
|
269
|
-
}, child);
|
|
267
|
+
return child;
|
|
270
268
|
}
|
|
271
269
|
const menuItemChild = child;
|
|
272
270
|
const controls = menuItemChild.props['aria-controls'] || menuItemChild.props.controls || this.props['aria-controls'] || this.props.controls;
|
|
273
271
|
if ((0, _matchComponentTypes.matchComponentTypes)(child, ['MenuItem'])) {
|
|
274
|
-
return (0,
|
|
275
|
-
role: "none"
|
|
276
|
-
}, (0, _safeCloneElement.safeCloneElement)(child, {
|
|
272
|
+
return (0, _safeCloneElement.safeCloneElement)(child, {
|
|
277
273
|
controls,
|
|
278
274
|
children: child.props.children,
|
|
279
275
|
disabled: disabled || child.props.disabled,
|
|
@@ -282,12 +278,10 @@ let Menu = exports.Menu = (_dec = (0, _withDeterministicId.withDeterministicId)(
|
|
|
282
278
|
onSelect: this.handleMenuItemSelect,
|
|
283
279
|
onMouseOver: this.handleMenuItemMouseOver,
|
|
284
280
|
tabIndex: isTabbable ? 0 : -1
|
|
285
|
-
})
|
|
281
|
+
});
|
|
286
282
|
}
|
|
287
283
|
if ((0, _matchComponentTypes.matchComponentTypes)(child, ['MenuItemGroup'])) {
|
|
288
|
-
return (0,
|
|
289
|
-
role: "none"
|
|
290
|
-
}, (0, _safeCloneElement.safeCloneElement)(child, {
|
|
284
|
+
return (0, _safeCloneElement.safeCloneElement)(child, {
|
|
291
285
|
label: child.props.label,
|
|
292
286
|
controls,
|
|
293
287
|
disabled: disabled || child.props.disabled,
|
|
@@ -296,13 +290,11 @@ let Menu = exports.Menu = (_dec = (0, _withDeterministicId.withDeterministicId)(
|
|
|
296
290
|
onSelect: this.handleMenuItemSelect,
|
|
297
291
|
onMouseOver: this.handleMenuItemMouseOver,
|
|
298
292
|
isTabbable
|
|
299
|
-
})
|
|
293
|
+
});
|
|
300
294
|
}
|
|
301
295
|
if ((0, _matchComponentTypes.matchComponentTypes)(child, ['Menu'])) {
|
|
302
296
|
const submenuDisabled = disabled || child.props.disabled;
|
|
303
|
-
return (0,
|
|
304
|
-
role: "none"
|
|
305
|
-
}, (0, _safeCloneElement.safeCloneElement)(child, {
|
|
297
|
+
return (0, _safeCloneElement.safeCloneElement)(child, {
|
|
306
298
|
type: 'flyout',
|
|
307
299
|
controls,
|
|
308
300
|
disabled: submenuDisabled,
|
|
@@ -322,7 +314,7 @@ let Menu = exports.Menu = (_dec = (0, _withDeterministicId.withDeterministicId)(
|
|
|
322
314
|
disabled: submenuDisabled,
|
|
323
315
|
renderLabelInfo: child.props.renderLabelInfo
|
|
324
316
|
}, child.props.title || child.props.label)
|
|
325
|
-
})
|
|
317
|
+
});
|
|
326
318
|
}
|
|
327
319
|
return;
|
|
328
320
|
});
|
|
@@ -341,7 +333,7 @@ let Menu = exports.Menu = (_dec = (0, _withDeterministicId.withDeterministicId)(
|
|
|
341
333
|
removeMenuItem: this.removeMenuItem,
|
|
342
334
|
registerMenuItem: this.registerMenuItem
|
|
343
335
|
}
|
|
344
|
-
}, (0, _emotion.jsx)("
|
|
336
|
+
}, (0, _emotion.jsx)("div", {
|
|
345
337
|
role: "menu",
|
|
346
338
|
"aria-label": label,
|
|
347
339
|
tabIndex: 0,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@instructure/ui-menu",
|
|
3
|
-
"version": "10.10.1-snapshot-
|
|
3
|
+
"version": "10.10.1-snapshot-14",
|
|
4
4
|
"description": "A dropdown menu component",
|
|
5
5
|
"author": "Instructure, Inc. Engineering and Product Design",
|
|
6
6
|
"module": "./es/index.js",
|
|
@@ -23,11 +23,11 @@
|
|
|
23
23
|
},
|
|
24
24
|
"license": "MIT",
|
|
25
25
|
"devDependencies": {
|
|
26
|
-
"@instructure/ui-axe-check": "10.10.1-snapshot-
|
|
27
|
-
"@instructure/ui-babel-preset": "10.10.1-snapshot-
|
|
28
|
-
"@instructure/ui-color-utils": "10.10.1-snapshot-
|
|
29
|
-
"@instructure/ui-test-utils": "10.10.1-snapshot-
|
|
30
|
-
"@instructure/ui-themes": "10.10.1-snapshot-
|
|
26
|
+
"@instructure/ui-axe-check": "10.10.1-snapshot-14",
|
|
27
|
+
"@instructure/ui-babel-preset": "10.10.1-snapshot-14",
|
|
28
|
+
"@instructure/ui-color-utils": "10.10.1-snapshot-14",
|
|
29
|
+
"@instructure/ui-test-utils": "10.10.1-snapshot-14",
|
|
30
|
+
"@instructure/ui-themes": "10.10.1-snapshot-14",
|
|
31
31
|
"@testing-library/jest-dom": "^6.6.3",
|
|
32
32
|
"@testing-library/react": "^16.0.1",
|
|
33
33
|
"@testing-library/user-event": "^14.5.2",
|
|
@@ -35,19 +35,19 @@
|
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
37
|
"@babel/runtime": "^7.26.0",
|
|
38
|
-
"@instructure/console": "10.10.1-snapshot-
|
|
39
|
-
"@instructure/emotion": "10.10.1-snapshot-
|
|
40
|
-
"@instructure/shared-types": "10.10.1-snapshot-
|
|
41
|
-
"@instructure/ui-a11y-utils": "10.10.1-snapshot-
|
|
42
|
-
"@instructure/ui-dom-utils": "10.10.1-snapshot-
|
|
43
|
-
"@instructure/ui-icons": "10.10.1-snapshot-
|
|
44
|
-
"@instructure/ui-popover": "10.10.1-snapshot-
|
|
45
|
-
"@instructure/ui-position": "10.10.1-snapshot-
|
|
46
|
-
"@instructure/ui-prop-types": "10.10.1-snapshot-
|
|
47
|
-
"@instructure/ui-react-utils": "10.10.1-snapshot-
|
|
48
|
-
"@instructure/ui-testable": "10.10.1-snapshot-
|
|
49
|
-
"@instructure/ui-utils": "10.10.1-snapshot-
|
|
50
|
-
"@instructure/ui-view": "10.10.1-snapshot-
|
|
38
|
+
"@instructure/console": "10.10.1-snapshot-14",
|
|
39
|
+
"@instructure/emotion": "10.10.1-snapshot-14",
|
|
40
|
+
"@instructure/shared-types": "10.10.1-snapshot-14",
|
|
41
|
+
"@instructure/ui-a11y-utils": "10.10.1-snapshot-14",
|
|
42
|
+
"@instructure/ui-dom-utils": "10.10.1-snapshot-14",
|
|
43
|
+
"@instructure/ui-icons": "10.10.1-snapshot-14",
|
|
44
|
+
"@instructure/ui-popover": "10.10.1-snapshot-14",
|
|
45
|
+
"@instructure/ui-position": "10.10.1-snapshot-14",
|
|
46
|
+
"@instructure/ui-prop-types": "10.10.1-snapshot-14",
|
|
47
|
+
"@instructure/ui-react-utils": "10.10.1-snapshot-14",
|
|
48
|
+
"@instructure/ui-testable": "10.10.1-snapshot-14",
|
|
49
|
+
"@instructure/ui-utils": "10.10.1-snapshot-14",
|
|
50
|
+
"@instructure/ui-view": "10.10.1-snapshot-14",
|
|
51
51
|
"keycode": "^2",
|
|
52
52
|
"prop-types": "^15.8.1"
|
|
53
53
|
},
|
|
@@ -40,44 +40,11 @@ describe('<MenuItemGroup />', () => {
|
|
|
40
40
|
<MenuItemSeparator />
|
|
41
41
|
</MenuItemGroup>
|
|
42
42
|
)
|
|
43
|
-
const group = container.querySelector("[
|
|
44
|
-
const groupMenu = screen.getByRole('menu')
|
|
45
|
-
|
|
43
|
+
const group = container.querySelector("[class*='menuItemGroup']")
|
|
46
44
|
expect(group).toBeInTheDocument()
|
|
47
45
|
expect(group).toHaveTextContent('Menu Label')
|
|
48
|
-
|
|
49
|
-
expect(
|
|
50
|
-
expect(groupMenu).toHaveTextContent('Item Text 1')
|
|
51
|
-
expect(groupMenu).toHaveTextContent('Item Text 2')
|
|
52
|
-
})
|
|
53
|
-
|
|
54
|
-
it('should set the role to "menu"', () => {
|
|
55
|
-
const { container } = render(
|
|
56
|
-
<MenuItemGroup label="Select one">
|
|
57
|
-
<MenuItem>Foo</MenuItem>
|
|
58
|
-
<MenuItem>Bar</MenuItem>
|
|
59
|
-
<MenuItemSeparator />
|
|
60
|
-
</MenuItemGroup>
|
|
61
|
-
)
|
|
62
|
-
const menuItemGroup = container.querySelector(
|
|
63
|
-
"[class*='menuItemGroup__items']"
|
|
64
|
-
)
|
|
65
|
-
|
|
66
|
-
expect(menuItemGroup).toHaveAttribute('role', 'menu')
|
|
67
|
-
})
|
|
68
|
-
|
|
69
|
-
it('should set the list item role to "none"', () => {
|
|
70
|
-
render(
|
|
71
|
-
<MenuItemGroup label="Select one">
|
|
72
|
-
<MenuItem>Food</MenuItem>
|
|
73
|
-
<MenuItem>Bar</MenuItem>
|
|
74
|
-
</MenuItemGroup>
|
|
75
|
-
)
|
|
76
|
-
const menu = screen.getByRole('menu')
|
|
77
|
-
const menuListItem = menu.firstChild as HTMLElement
|
|
78
|
-
|
|
79
|
-
expect(menuListItem.tagName).toBe('LI')
|
|
80
|
-
expect(menuListItem).toHaveAttribute('role', 'none')
|
|
46
|
+
expect(group).toHaveTextContent('Item Text 1')
|
|
47
|
+
expect(group).toHaveTextContent('Item Text 2')
|
|
81
48
|
})
|
|
82
49
|
|
|
83
50
|
it('should default to children with type "radio"', () => {
|
|
@@ -114,10 +81,8 @@ describe('<MenuItemGroup />', () => {
|
|
|
114
81
|
<MenuItemSeparator />
|
|
115
82
|
</MenuItemGroup>
|
|
116
83
|
)
|
|
117
|
-
const menu = screen.getByRole('menu')
|
|
118
84
|
const menuItems = screen.getAllByRole('menuitemradio')
|
|
119
85
|
|
|
120
|
-
expect(menu).toHaveAttribute('aria-disabled', 'true')
|
|
121
86
|
expect(menuItems).toHaveLength(2)
|
|
122
87
|
expect(menuItems[0]).toHaveAttribute('aria-disabled', 'true')
|
|
123
88
|
expect(menuItems[1]).toHaveAttribute('aria-disabled', 'true')
|
|
@@ -82,11 +82,8 @@ class MenuItemGroup extends Component<MenuGroupProps, MenuGroupState> {
|
|
|
82
82
|
selected: this.selectedFromChildren(props) || props.defaultSelected!
|
|
83
83
|
}
|
|
84
84
|
}
|
|
85
|
-
|
|
86
|
-
this._labelId = props.deterministicId!('MenuItemGroup')
|
|
87
85
|
}
|
|
88
86
|
|
|
89
|
-
private _labelId: string
|
|
90
87
|
ref: Element | null = null
|
|
91
88
|
|
|
92
89
|
handleRef = (el: Element | null) => {
|
|
@@ -210,23 +207,18 @@ class MenuItemGroup extends Component<MenuGroupProps, MenuGroupState> {
|
|
|
210
207
|
++index
|
|
211
208
|
const value = child.props.value || index
|
|
212
209
|
|
|
213
|
-
return (
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
onSelect: this.handleSelect,
|
|
226
|
-
onMouseOver
|
|
227
|
-
})}{' '}
|
|
228
|
-
</li>
|
|
229
|
-
)
|
|
210
|
+
return safeCloneElement(child, {
|
|
211
|
+
tabIndex: isTabbable && index === 0 ? 0 : -1,
|
|
212
|
+
controls,
|
|
213
|
+
value,
|
|
214
|
+
children: child.props.children,
|
|
215
|
+
type: allowMultiple ? 'checkbox' : 'radio',
|
|
216
|
+
ref: this.props.itemRef,
|
|
217
|
+
disabled: disabled || child.props.disabled,
|
|
218
|
+
selected: this.selected.indexOf(value) > -1,
|
|
219
|
+
onSelect: this.handleSelect,
|
|
220
|
+
onMouseOver
|
|
221
|
+
})
|
|
230
222
|
} else {
|
|
231
223
|
return child
|
|
232
224
|
}
|
|
@@ -239,18 +231,15 @@ class MenuItemGroup extends Component<MenuGroupProps, MenuGroupState> {
|
|
|
239
231
|
<span
|
|
240
232
|
{...props}
|
|
241
233
|
css={this.props.styles?.menuItemGroup}
|
|
242
|
-
role="presentation"
|
|
243
234
|
ref={this.handleRef}
|
|
244
235
|
>
|
|
245
|
-
|
|
246
|
-
<
|
|
247
|
-
role="menu"
|
|
236
|
+
{this.renderLabel()}
|
|
237
|
+
<div
|
|
248
238
|
css={this.props.styles?.items}
|
|
249
239
|
aria-disabled={this.props.disabled ? 'true' : undefined}
|
|
250
|
-
aria-labelledby={this._labelId}
|
|
251
240
|
>
|
|
252
241
|
{this.renderChildren()}
|
|
253
|
-
</
|
|
242
|
+
</div>
|
|
254
243
|
</span>
|
|
255
244
|
)
|
|
256
245
|
}
|
|
@@ -66,6 +66,8 @@ class MenuItemSeparator extends Component<MenuSeparatorProps> {
|
|
|
66
66
|
|
|
67
67
|
render() {
|
|
68
68
|
const props = omitProps(this.props, MenuItemSeparator.allowedProps)
|
|
69
|
+
// role="separator" would fit better here, but it causes NVDA to stop the
|
|
70
|
+
// MenuItem count after it
|
|
69
71
|
return (
|
|
70
72
|
<div
|
|
71
73
|
{...props}
|