Skip to content

Commit 422a4d2

Browse files
authored
[Fix] Allow focus on items wrapped in disabled tooltips (#7604)
1 parent a29aacd commit 422a4d2

File tree

2 files changed

+64
-2
lines changed

2 files changed

+64
-2
lines changed

packages/core/src/components/menu/menuItem.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -248,15 +248,17 @@ export const MenuItem: React.FC<MenuItemProps> = forwardRef<HTMLLIElement, MenuI
248248
</span>
249249
);
250250

251+
const htmlPropsOnly = removeNonHTMLProps(htmlProps);
252+
251253
const target = createElement(
252254
tagName,
253255
{
254256
// for menuitems, onClick when enter key pressed doesn't take effect like it does for a button-- fix this
255257
onKeyDown: clickElementOnKeyPress(["Enter", " "]),
258+
...htmlPropsOnly,
256259
// if hasSubmenu, must apply correct role and tabIndex to the outer popover target <span> instead of this target element
257260
role: hasSubmenu ? "none" : targetRole,
258-
tabIndex: hasSubmenu ? -1 : 0,
259-
...removeNonHTMLProps(htmlProps),
261+
tabIndex: hasSubmenu ? -1 : htmlPropsOnly.tabIndex != null ? htmlPropsOnly.tabIndex : 0,
260262
...(disabled ? DISABLED_PROPS : {}),
261263
className: anchorClasses,
262264
},

packages/core/test/menu/menuItemTests.tsx

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
* limitations under the License.
1515
*/
1616

17+
import { render } from "@testing-library/react";
1718
import { assert } from "chai";
1819
import { mount, type ReactWrapper, shallow, type ShallowWrapper } from "enzyme";
1920
import { spy } from "sinon";
@@ -197,6 +198,65 @@ describe("MenuItem", () => {
197198
assert.match(label.text(), /^label text/);
198199
assert.strictEqual(label.find("article").text(), "label element");
199200
});
201+
202+
describe("tabIndex behavior", () => {
203+
it("MenuItem without submenu has tabIndex={0} when enabled", () => {
204+
const { container } = render(<MenuItem text="Item" />);
205+
const anchor = container.querySelector("a");
206+
assert.strictEqual(anchor?.getAttribute("tabindex"), "0");
207+
});
208+
209+
it("MenuItem without submenu has tabIndex={-1} when disabled", () => {
210+
const { container } = render(<MenuItem text="Item" disabled={true} />);
211+
const anchor = container.querySelector("a");
212+
assert.strictEqual(anchor?.getAttribute("tabindex"), "-1");
213+
});
214+
215+
it("MenuItem with submenu has focusable Popover target when enabled", () => {
216+
const { container } = render(
217+
<MenuItem text="Parent">
218+
<MenuItem text="Child" />
219+
</MenuItem>,
220+
);
221+
// The Popover target wrapper should be focusable
222+
const popoverTarget = container.querySelector(`.${Classes.POPOVER_TARGET}`);
223+
assert.strictEqual(popoverTarget?.getAttribute("tabindex"), "0");
224+
});
225+
226+
it("MenuItem with submenu has tabIndex={-1} on inner anchor element", () => {
227+
const { getByText } = render(
228+
<MenuItem text="Parent">
229+
<MenuItem text="Child" />
230+
</MenuItem>,
231+
);
232+
// The inner anchor should NOT be focusable when there's a submenu
233+
const textElement = getByText("Parent");
234+
const anchor = textElement.closest("a");
235+
assert.strictEqual(anchor?.getAttribute("tabindex"), "-1");
236+
});
237+
238+
it("MenuItem with disabled submenu is not focusable", () => {
239+
const { container, getByText } = render(
240+
<MenuItem text="Parent" disabled={true}>
241+
<MenuItem text="Child" />
242+
</MenuItem>,
243+
);
244+
const parentElement = getByText("Parent");
245+
const parentAnchor = parentElement.closest("a");
246+
assert.strictEqual(parentAnchor?.getAttribute("tabindex"), "-1");
247+
248+
// When disabled, the Popover target should not be in the tab order
249+
const popoverTarget = container.querySelector(`.${Classes.POPOVER_TARGET}`);
250+
// The target exists but disabled state is handled by the Popover component
251+
assert.isNotNull(popoverTarget);
252+
});
253+
254+
it("MenuItem without submenu preserves custom tabIndex", () => {
255+
const { container } = render(<MenuItem text="Item" tabIndex={3} />);
256+
const anchor = container.querySelector("a");
257+
assert.strictEqual(anchor?.getAttribute("tabindex"), "3");
258+
});
259+
});
200260
});
201261

202262
function findSubmenu(wrapper: ShallowWrapper<any, any>) {

0 commit comments

Comments
 (0)