Skip to content

Commit 5b6c361

Browse files
jin19980928WB0167625018220059148
authored
Fix:Mentions options does not automatically scroll when navigating through options using the keyboard if the options list is long (#286)
* fix:MetionsScroll * fix:MetionsScroll * fix:MetionsScroll * fix:MetionsScroll * fix:MetionsScroll * fix:MetionsScroll * fix:MetionsScroll * fix:metionsScroll * feat:test * feat:test * fix:test * fix:test * fix:test * fix:test * feat:test * feat:test * feat:test * feat:test * fix:test * fix:test --------- Co-authored-by: WB01676250 <jl01676250@antgroup.com> Co-authored-by: jinle <986530572@qq.com>
1 parent b489daa commit 5b6c361

File tree

3 files changed

+74
-3
lines changed

3 files changed

+74
-3
lines changed

src/DropdownMenu.tsx

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
import Menu, { MenuItem } from '@rc-component/menu';
2-
import * as React from 'react';
1+
import Menu, { MenuItem, MenuRef } from '@rc-component/menu';
2+
import React, { useEffect, useRef } from 'react';
33
import MentionsContext from './MentionsContext';
44
import type { DataDrivenOptionProps } from './Mentions';
5-
interface DropdownMenuProps {
5+
export interface DropdownMenuProps {
66
prefixCls?: string;
77
options: DataDrivenOptionProps[];
88
}
@@ -24,9 +24,26 @@ function DropdownMenu(props: DropdownMenuProps) {
2424

2525
const { prefixCls, options } = props;
2626
const activeOption = options[activeIndex] || {};
27+
const menuRef = useRef<MenuRef>(null);
28+
29+
// Monitor the changes in ActiveIndex and scroll to the visible area if there are any changes
30+
useEffect(() => {
31+
if (activeIndex === -1 || !menuRef.current) {
32+
return;
33+
}
34+
35+
const activeItem = menuRef.current?.findItem?.({ key: activeOption.key });
36+
if (activeItem) {
37+
activeItem.scrollIntoView({
38+
block: 'nearest',
39+
inline: 'nearest',
40+
});
41+
}
42+
}, [activeIndex, activeOption.key]);
2743

2844
return (
2945
<Menu
46+
ref={menuRef}
3047
prefixCls={`${prefixCls}-menu`}
3148
activeKey={activeOption.key}
3249
onSelect={({ key }) => {

tests/DropdownMenu.spec.tsx

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import React from 'react';
2+
import { render, fireEvent } from '@testing-library/react';
3+
import Mentions, { UnstableContext } from '../src';
4+
import { expectMeasuring } from './util';
5+
6+
describe('DropdownMenu', () => {
7+
// Generate 20 options for testing scrolling behavior
8+
const generateOptions = Array.from({ length: 20 }).map((_, index) => ({
9+
value: `item-${index}`,
10+
label: `item-${index}`,
11+
}));
12+
13+
// Setup component with UnstableContext for testing dropdown behavior
14+
const { container } = render(
15+
<UnstableContext.Provider value={{ open: true }}>
16+
<Mentions defaultValue="@" options={generateOptions} />
17+
</UnstableContext.Provider>,
18+
);
19+
20+
it('should scroll into view when navigating with keyboard', () => {
21+
// Mock scrollIntoView since it's not implemented in JSDOM
22+
const scrollIntoViewMock = jest
23+
.spyOn(HTMLElement.prototype, 'scrollIntoView')
24+
.mockImplementation(jest.fn());
25+
26+
// Press ArrowDown multiple times to make options overflow the visible area
27+
for (let i = 0; i < 10; i++) {
28+
fireEvent.keyDown(document.activeElement!, { key: 'ArrowDown' });
29+
}
30+
31+
// Verify if scrollIntoView was called
32+
expect(scrollIntoViewMock).toHaveBeenCalledWith({
33+
block: 'nearest',
34+
inline: 'nearest',
35+
});
36+
37+
// Press ArrowUp to verify scrolling up
38+
for (let i = 0; i < 5; i++) {
39+
fireEvent.keyDown(document.activeElement!, { key: 'ArrowUp' });
40+
}
41+
42+
// Verify if scrollIntoView was called again
43+
expect(scrollIntoViewMock).toHaveBeenCalledWith({
44+
block: 'nearest',
45+
inline: 'nearest',
46+
});
47+
48+
expectMeasuring(container);
49+
50+
scrollIntoViewMock.mockRestore();
51+
});
52+
});

tests/setup.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,5 @@ global.ResizeObserver = class ResizeObserver {
44
unobserve() {}
55
disconnect() {}
66
};
7+
8+
window.HTMLElement.prototype.scrollIntoView = jest.fn();

0 commit comments

Comments
 (0)