Skip to content

Commit 76031bf

Browse files
At mentioned from quick action respect cursor position (#9425) (#9477)
* At mentioned from quick action respect cursor position * Reverted the unnecessary changes (cherry picked from commit 20a70ae) Co-authored-by: Rajat Dabade <rajatdabade1997@gmail.com>
1 parent fc6eeec commit 76031bf

File tree

2 files changed

+365
-53
lines changed

2 files changed

+365
-53
lines changed
Lines changed: 324 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,337 @@
11
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
22
// See LICENSE.txt for license information.
33

4+
import {useKeyboardAnimationContext} from '@context/keyboard_animation';
45
import {fireEvent, renderWithIntlAndTheme} from '@test/intl-test-helper';
56

67
import InputQuickAction from '.';
78

9+
jest.mock('@context/keyboard_animation', () => ({
10+
useKeyboardAnimationContext: jest.fn(),
11+
}));
12+
13+
jest.mock('@hooks/useFocusAfterEmojiDismiss', () => ({
14+
useFocusAfterEmojiDismiss: jest.fn((inputRef, focusInput) => ({
15+
focus: focusInput,
16+
isDismissingEmojiPicker: {current: false},
17+
focusTimeoutRef: {current: null},
18+
isManuallyFocusingAfterEmojiDismiss: false,
19+
})),
20+
}));
21+
822
describe('InputQuickAction', () => {
9-
it('should add If theres existing text and it doesnt end with a space, add a space before @', () => {
10-
const updateValue = jest.fn();
11-
const testID = 'test-id';
12-
const inputType = 'at';
13-
const focus = jest.fn();
14-
15-
const {getByTestId} = renderWithIntlAndTheme(
16-
<InputQuickAction
17-
testID={testID}
18-
updateValue={updateValue}
19-
inputType={inputType}
20-
focus={focus}
21-
/>);
22-
23-
const icon = getByTestId('test-id');
24-
fireEvent.press(icon);
25-
26-
expect(updateValue).toHaveBeenCalledWith(expect.any(Function));
27-
const updateFunction = updateValue.mock.calls[0][0];
28-
expect(updateFunction('')).toBe('@');
29-
expect(focus).toHaveBeenCalled();
23+
const mockUseKeyboardAnimationContext = jest.mocked(useKeyboardAnimationContext);
24+
const mockUpdateCursorPosition = jest.fn();
25+
const mockInputRef = {current: undefined};
26+
27+
beforeEach(() => {
28+
jest.clearAllMocks();
3029
});
3130

32-
it('should add space before @ if there is existing text and it doesnt end with a space', () => {
33-
const updateValue = jest.fn();
34-
const testID = 'test-id';
35-
const inputType = 'at';
36-
const focus = jest.fn();
37-
38-
const {getByTestId} = renderWithIntlAndTheme(
39-
<InputQuickAction
40-
testID={testID}
41-
updateValue={updateValue}
42-
inputType={inputType}
43-
focus={focus}
44-
/>);
45-
46-
const icon = getByTestId('test-id');
47-
fireEvent.press(icon);
48-
49-
expect(updateValue).toHaveBeenCalledWith(expect.any(Function));
50-
const updateFunction = updateValue.mock.calls[0][0];
51-
expect(updateFunction('Hello')).toBe('Hello @');
52-
expect(focus).toHaveBeenCalled();
31+
describe('fallback behavior (no cursor position context)', () => {
32+
beforeEach(() => {
33+
mockUseKeyboardAnimationContext.mockReturnValue({
34+
inputRef: mockInputRef,
35+
cursorPositionRef: undefined,
36+
updateCursorPosition: undefined,
37+
} as unknown as ReturnType<typeof useKeyboardAnimationContext>);
38+
});
39+
40+
it('should add @ to empty string', () => {
41+
const updateValue = jest.fn();
42+
const testID = 'test-id';
43+
const inputType = 'at';
44+
const focus = jest.fn();
45+
46+
const {getByTestId} = renderWithIntlAndTheme(
47+
<InputQuickAction
48+
testID={testID}
49+
updateValue={updateValue}
50+
inputType={inputType}
51+
focus={focus}
52+
/>);
53+
54+
const icon = getByTestId('test-id');
55+
fireEvent.press(icon);
56+
57+
expect(updateValue).toHaveBeenCalledWith(expect.any(Function));
58+
const updateFunction = updateValue.mock.calls[0][0];
59+
expect(updateFunction('')).toBe('@');
60+
expect(focus).toHaveBeenCalled();
61+
});
62+
63+
it('should add space before @ if there is existing text and it doesnt end with a space', () => {
64+
const updateValue = jest.fn();
65+
const testID = 'test-id';
66+
const inputType = 'at';
67+
const focus = jest.fn();
68+
69+
const {getByTestId} = renderWithIntlAndTheme(
70+
<InputQuickAction
71+
testID={testID}
72+
updateValue={updateValue}
73+
inputType={inputType}
74+
focus={focus}
75+
/>);
76+
77+
const icon = getByTestId('test-id');
78+
fireEvent.press(icon);
79+
80+
expect(updateValue).toHaveBeenCalledWith(expect.any(Function));
81+
const updateFunction = updateValue.mock.calls[0][0];
82+
expect(updateFunction('Hello')).toBe('Hello @');
83+
expect(focus).toHaveBeenCalled();
84+
});
5385
});
5486

87+
describe('cursor position insertion', () => {
88+
let cursorPositionRef: {current: number};
89+
90+
beforeEach(() => {
91+
cursorPositionRef = {current: 0};
92+
mockUseKeyboardAnimationContext.mockReturnValue({
93+
inputRef: mockInputRef,
94+
cursorPositionRef,
95+
updateCursorPosition: mockUpdateCursorPosition,
96+
} as unknown as ReturnType<typeof useKeyboardAnimationContext>);
97+
});
98+
99+
describe('@ input type', () => {
100+
it('should insert @ at cursor position at the beginning', () => {
101+
const updateValue = jest.fn();
102+
const testID = 'test-id';
103+
const inputType = 'at';
104+
const focus = jest.fn();
105+
cursorPositionRef.current = 0;
106+
107+
const {getByTestId} = renderWithIntlAndTheme(
108+
<InputQuickAction
109+
testID={testID}
110+
updateValue={updateValue}
111+
inputType={inputType}
112+
focus={focus}
113+
/>);
114+
115+
const icon = getByTestId('test-id');
116+
fireEvent.press(icon);
117+
118+
expect(updateValue).toHaveBeenCalledWith(expect.any(Function));
119+
const updateFunction = updateValue.mock.calls[0][0];
120+
expect(updateFunction('Hello')).toBe('@Hello');
121+
expect(cursorPositionRef.current).toBe(1);
122+
expect(mockUpdateCursorPosition).toHaveBeenCalledWith(1);
123+
expect(focus).toHaveBeenCalled();
124+
});
125+
126+
it('should insert @ at cursor position in the middle', () => {
127+
const updateValue = jest.fn();
128+
const testID = 'test-id';
129+
const inputType = 'at';
130+
const focus = jest.fn();
131+
cursorPositionRef.current = 3;
132+
133+
const {getByTestId} = renderWithIntlAndTheme(
134+
<InputQuickAction
135+
testID={testID}
136+
updateValue={updateValue}
137+
inputType={inputType}
138+
focus={focus}
139+
/>);
140+
141+
const icon = getByTestId('test-id');
142+
fireEvent.press(icon);
143+
144+
expect(updateValue).toHaveBeenCalledWith(expect.any(Function));
145+
const updateFunction = updateValue.mock.calls[0][0];
146+
expect(updateFunction('Hello')).toBe('Hel@lo');
147+
expect(cursorPositionRef.current).toBe(4);
148+
expect(mockUpdateCursorPosition).toHaveBeenCalledWith(4);
149+
expect(focus).toHaveBeenCalled();
150+
});
151+
152+
it('should insert @ at cursor position at the end', () => {
153+
const updateValue = jest.fn();
154+
const testID = 'test-id';
155+
const inputType = 'at';
156+
const focus = jest.fn();
157+
cursorPositionRef.current = 5;
158+
159+
const {getByTestId} = renderWithIntlAndTheme(
160+
<InputQuickAction
161+
testID={testID}
162+
updateValue={updateValue}
163+
inputType={inputType}
164+
focus={focus}
165+
/>);
166+
167+
const icon = getByTestId('test-id');
168+
fireEvent.press(icon);
169+
170+
expect(updateValue).toHaveBeenCalledWith(expect.any(Function));
171+
const updateFunction = updateValue.mock.calls[0][0];
172+
expect(updateFunction('Hello')).toBe('Hello @');
173+
expect(cursorPositionRef.current).toBe(7);
174+
expect(mockUpdateCursorPosition).toHaveBeenCalledWith(7);
175+
expect(focus).toHaveBeenCalled();
176+
});
177+
178+
it('should add space before @ when cursor is at the end and previous char is not space', () => {
179+
const updateValue = jest.fn();
180+
const testID = 'test-id';
181+
const inputType = 'at';
182+
const focus = jest.fn();
183+
cursorPositionRef.current = 5;
184+
185+
const {getByTestId} = renderWithIntlAndTheme(
186+
<InputQuickAction
187+
testID={testID}
188+
updateValue={updateValue}
189+
inputType={inputType}
190+
focus={focus}
191+
/>);
192+
193+
const icon = getByTestId('test-id');
194+
fireEvent.press(icon);
195+
196+
expect(updateValue).toHaveBeenCalledWith(expect.any(Function));
197+
const updateFunction = updateValue.mock.calls[0][0];
198+
expect(updateFunction('Hello')).toBe('Hello @');
199+
expect(cursorPositionRef.current).toBe(7);
200+
expect(mockUpdateCursorPosition).toHaveBeenCalledWith(7);
201+
expect(focus).toHaveBeenCalled();
202+
});
203+
204+
it('should not add space before @ when cursor is at the end and previous char is space', () => {
205+
const updateValue = jest.fn();
206+
const testID = 'test-id';
207+
const inputType = 'at';
208+
const focus = jest.fn();
209+
cursorPositionRef.current = 6;
210+
211+
const {getByTestId} = renderWithIntlAndTheme(
212+
<InputQuickAction
213+
testID={testID}
214+
updateValue={updateValue}
215+
inputType={inputType}
216+
focus={focus}
217+
/>);
218+
219+
const icon = getByTestId('test-id');
220+
fireEvent.press(icon);
221+
222+
expect(updateValue).toHaveBeenCalledWith(expect.any(Function));
223+
const updateFunction = updateValue.mock.calls[0][0];
224+
expect(updateFunction('Hello ')).toBe('Hello @');
225+
expect(cursorPositionRef.current).toBe(7);
226+
expect(mockUpdateCursorPosition).toHaveBeenCalledWith(7);
227+
expect(focus).toHaveBeenCalled();
228+
});
229+
230+
it('should not add space before @ when cursor is at the beginning', () => {
231+
const updateValue = jest.fn();
232+
const testID = 'test-id';
233+
const inputType = 'at';
234+
const focus = jest.fn();
235+
cursorPositionRef.current = 0;
236+
237+
const {getByTestId} = renderWithIntlAndTheme(
238+
<InputQuickAction
239+
testID={testID}
240+
updateValue={updateValue}
241+
inputType={inputType}
242+
focus={focus}
243+
/>);
244+
245+
const icon = getByTestId('test-id');
246+
fireEvent.press(icon);
247+
248+
expect(updateValue).toHaveBeenCalledWith(expect.any(Function));
249+
const updateFunction = updateValue.mock.calls[0][0];
250+
expect(updateFunction('Hello')).toBe('@Hello');
251+
expect(cursorPositionRef.current).toBe(1);
252+
expect(mockUpdateCursorPosition).toHaveBeenCalledWith(1);
253+
expect(focus).toHaveBeenCalled();
254+
});
255+
});
256+
257+
describe('slash input type', () => {
258+
it('should insert / at cursor position at the beginning', () => {
259+
const updateValue = jest.fn();
260+
const testID = 'test-id';
261+
const inputType = 'slash';
262+
const focus = jest.fn();
263+
cursorPositionRef.current = 0;
264+
265+
const {getByTestId} = renderWithIntlAndTheme(
266+
<InputQuickAction
267+
testID={testID}
268+
updateValue={updateValue}
269+
inputType={inputType}
270+
focus={focus}
271+
/>);
272+
273+
const icon = getByTestId('test-id');
274+
fireEvent.press(icon);
275+
276+
expect(updateValue).toHaveBeenCalledWith(expect.any(Function));
277+
const updateFunction = updateValue.mock.calls[0][0];
278+
expect(updateFunction('Hello')).toBe('/Hello');
279+
expect(cursorPositionRef.current).toBe(1);
280+
expect(mockUpdateCursorPosition).toHaveBeenCalledWith(1);
281+
expect(focus).toHaveBeenCalled();
282+
});
283+
284+
it('should insert / at cursor position in the middle', () => {
285+
const updateValue = jest.fn();
286+
const testID = 'test-id';
287+
const inputType = 'slash';
288+
const focus = jest.fn();
289+
cursorPositionRef.current = 3;
290+
291+
const {getByTestId} = renderWithIntlAndTheme(
292+
<InputQuickAction
293+
testID={testID}
294+
updateValue={updateValue}
295+
inputType={inputType}
296+
focus={focus}
297+
/>);
298+
299+
const icon = getByTestId('test-id');
300+
fireEvent.press(icon);
301+
302+
expect(updateValue).toHaveBeenCalledWith(expect.any(Function));
303+
const updateFunction = updateValue.mock.calls[0][0];
304+
expect(updateFunction('Hello')).toBe('Hel/lo');
305+
expect(cursorPositionRef.current).toBe(4);
306+
expect(mockUpdateCursorPosition).toHaveBeenCalledWith(4);
307+
expect(focus).toHaveBeenCalled();
308+
});
309+
310+
it('should insert / at cursor position at the end', () => {
311+
const updateValue = jest.fn();
312+
const testID = 'test-id';
313+
const inputType = 'slash';
314+
const focus = jest.fn();
315+
cursorPositionRef.current = 5;
316+
317+
const {getByTestId} = renderWithIntlAndTheme(
318+
<InputQuickAction
319+
testID={testID}
320+
updateValue={updateValue}
321+
inputType={inputType}
322+
focus={focus}
323+
/>);
324+
325+
const icon = getByTestId('test-id');
326+
fireEvent.press(icon);
327+
328+
expect(updateValue).toHaveBeenCalledWith(expect.any(Function));
329+
const updateFunction = updateValue.mock.calls[0][0];
330+
expect(updateFunction('Hello')).toBe('Hello/');
331+
expect(cursorPositionRef.current).toBe(6);
332+
expect(mockUpdateCursorPosition).toHaveBeenCalledWith(6);
333+
expect(focus).toHaveBeenCalled();
334+
});
335+
});
336+
});
55337
});

0 commit comments

Comments
 (0)