Skip to content

Commit ce7a383

Browse files
authored
fix: open trigger force scroll (#304)
* fix: not trigger scroll when init * test: update test case
1 parent 1d31bf2 commit ce7a383

File tree

3 files changed

+23
-44
lines changed

3 files changed

+23
-44
lines changed

src/DropdownMenu.tsx

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
import Menu, { MenuItem, MenuRef } from '@rc-component/menu';
1+
import Menu, { MenuItem, type MenuRef } from '@rc-component/menu';
22
import React, { useEffect, useRef } from 'react';
33
import MentionsContext from './MentionsContext';
44
import type { DataDrivenOptionProps } from './Mentions';
55

66
export interface DropdownMenuProps {
77
prefixCls?: string;
88
options: DataDrivenOptionProps[];
9+
opened: boolean;
910
}
1011

1112
/**
@@ -23,24 +24,25 @@ function DropdownMenu(props: DropdownMenuProps) {
2324
onScroll,
2425
} = React.useContext(MentionsContext);
2526

26-
const { prefixCls, options } = props;
27+
const { prefixCls, options, opened } = props;
2728
const activeOption = options[activeIndex] || {};
2829
const menuRef = useRef<MenuRef>(null);
2930

3031
// Monitor the changes in ActiveIndex and scroll to the visible area if there are any changes
3132
useEffect(() => {
32-
if (activeIndex === -1 || !menuRef.current) {
33+
if (activeIndex === -1 || !menuRef.current || !opened) {
3334
return;
3435
}
3536

3637
const activeItem = menuRef.current?.findItem?.({ key: activeOption.key });
38+
3739
if (activeItem) {
3840
activeItem.scrollIntoView({
3941
block: 'nearest',
4042
inline: 'nearest',
4143
});
4244
}
43-
}, [activeIndex, activeOption.key]);
45+
}, [activeIndex, activeOption.key, opened]);
4446

4547
return (
4648
<Menu

src/KeywordTrigger.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,14 @@ const KeywordTrigger: FC<KeywordTriggerProps> = props => {
6969
} = props;
7070

7171
const dropdownPrefix = `${prefixCls}-dropdown`;
72+
const [opened, setOpened] = React.useState(false);
7273

7374
const dropdownElement = (
74-
<DropdownMenu prefixCls={dropdownPrefix} options={options} />
75+
<DropdownMenu
76+
prefixCls={dropdownPrefix}
77+
options={options}
78+
opened={opened}
79+
/>
7580
);
7681

7782
const dropdownPlacement = useMemo(() => {
@@ -95,6 +100,7 @@ const KeywordTrigger: FC<KeywordTriggerProps> = props => {
95100
getPopupContainer={getPopupContainer}
96101
popupClassName={popupClassName}
97102
popupStyle={popupStyle}
103+
afterOpenChange={setOpened}
98104
>
99105
{children}
100106
</Trigger>

tests/DropdownMenu.spec.tsx

Lines changed: 10 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ describe('DropdownMenu', () => {
1919
jest.useRealTimers();
2020
});
2121

22-
it('should scroll into view when navigating with keyboard', () => {
22+
it('should scroll into view when navigating with keyboard', async () => {
2323
// Setup component with UnstableContext for testing dropdown behavior
2424
const { container } = render(
2525
<UnstableContext.Provider value={{ open: true }}>
@@ -32,51 +32,22 @@ describe('DropdownMenu', () => {
3232
.spyOn(HTMLElement.prototype, 'scrollIntoView')
3333
.mockImplementation(jest.fn());
3434

35-
const textarea = container.querySelector('textarea')!;
35+
// Trigger should not scroll
36+
simulateInput(container, '@');
37+
expect(scrollIntoViewMock).not.toHaveBeenCalled();
3638

37-
act(() => {
38-
// First trigger the measuring state by typing @
39-
simulateInput(container, '@');
40-
jest.runAllTimers();
41-
});
42-
43-
// Verify we're in measuring state
44-
expectMeasuring(container, true);
45-
46-
act(() => {
47-
// Press ArrowDown multiple times to make options overflow the visible area
48-
for (let i = 0; i < 10; i++) {
49-
fireEvent.keyDown(textarea, {
50-
keyCode: KeyCode.DOWN,
51-
which: KeyCode.DOWN,
52-
});
53-
}
54-
jest.runAllTimers();
55-
});
39+
for (let i = 0; i < 10; i++) {
40+
await act(async () => {
41+
jest.advanceTimersByTime(1000);
42+
await Promise.resolve();
43+
});
44+
}
5645

5746
// Verify if scrollIntoView was called
5847
expect(scrollIntoViewMock).toHaveBeenCalledWith({
5948
block: 'nearest',
6049
inline: 'nearest',
6150
});
62-
scrollIntoViewMock.mockClear();
63-
64-
act(() => {
65-
// Press ArrowUp to verify scrolling up
66-
for (let i = 0; i < 5; i++) {
67-
fireEvent.keyDown(textarea, {
68-
keyCode: KeyCode.UP,
69-
which: KeyCode.UP,
70-
});
71-
}
72-
jest.runAllTimers();
73-
});
74-
75-
// Verify if scrollIntoView was called again
76-
expect(scrollIntoViewMock).toHaveBeenCalledWith({
77-
block: 'nearest',
78-
inline: 'nearest',
79-
});
8051

8152
scrollIntoViewMock.mockRestore();
8253
});

0 commit comments

Comments
 (0)