Skip to content

Commit 0753752

Browse files
fix(ui-react-ai): Addressing cross-browser inconsistencies in AIConversation IME input handling (#6429)
* Addressing cross-browser inconsistencies in AIConversation IME input handling (#6429) --------- Co-authored-by: Caleb Pollman <[email protected]>
1 parent b1ea48c commit 0753752

File tree

3 files changed

+84
-2
lines changed

3 files changed

+84
-2
lines changed

.changeset/fluffy-flowers-grab.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@aws-amplify/ui-react-ai': patch
3+
---
4+
5+
fix(ui-react-ai): Addressing cross-browser inconsistencies in AIConversation IME input handling

packages/react-ai/src/components/AIConversation/views/default/Form.tsx

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,21 @@ export const Form: Required<ControlsContextProps>['Form'] = ({
110110
value={input?.text ?? ''}
111111
testId="text-input"
112112
onCompositionStart={() => setComposing(true)}
113-
onCompositionEnd={() => setComposing(false)}
113+
onCompositionUpdate={(e) => {
114+
const composedText = e?.currentTarget?.value || '';
115+
setInput?.((prevValue) => ({
116+
...prevValue,
117+
text: composedText,
118+
}));
119+
}}
120+
onCompositionEnd={(e) => {
121+
setComposing(false);
122+
const composedText = e?.currentTarget?.value || '';
123+
setInput?.((prevValue) => ({
124+
...prevValue,
125+
text: composedText,
126+
}));
127+
}}
114128
onKeyDown={(e) => {
115129
// Submit on enter key if shift is not pressed also
116130
const shouldSubmit = !e.shiftKey && e.key === 'Enter' && !composing;

packages/react-ai/src/components/AIConversation/views/default/__tests__/Form.spec.tsx

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,31 @@
1+
/* eslint-disable no-console */
12
import React from 'react';
2-
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
3+
import {
4+
render,
5+
screen,
6+
fireEvent,
7+
waitFor,
8+
within,
9+
} from '@testing-library/react';
310
import { Form } from '../Form';
411

512
const setInput = jest.fn();
613
const input = {};
714
const handleSubmit = jest.fn();
815
const onValidate = jest.fn();
16+
const onCompositionStart = jest.fn();
17+
const onCompositionEnd = jest.fn();
18+
const onKeyDown = jest.fn();
919

1020
const defaultProps = {
1121
allowAttachments: true,
1222
setInput,
1323
input,
1424
handleSubmit,
1525
onValidate,
26+
onCompositionStart,
27+
onCompositionEnd,
28+
onKeyDown,
1629
};
1730

1831
describe('Form', () => {
@@ -57,4 +70,54 @@ describe('Form', () => {
5770
expect(fileInput.files).not.toBeNull();
5871
expect(fileInput.files![0]).toStrictEqual(testFile);
5972
});
73+
74+
it('updates IME input with composition completion', async () => {
75+
const input = { currentTarget: { value: '你' } };
76+
const updatedInput = { currentTarget: { value: '你好' } };
77+
78+
const result = render(<Form {...defaultProps} />);
79+
expect(result.container).toBeDefined();
80+
81+
const textFieldContainer = screen.getByTestId('text-input');
82+
83+
const textInput =
84+
textFieldContainer.querySelector('textarea') ??
85+
within(textFieldContainer).getByRole('textbox');
86+
87+
await waitFor(() => {
88+
fireEvent.compositionStart(textInput);
89+
fireEvent.compositionEnd(textInput, input);
90+
});
91+
92+
await waitFor(() => {
93+
fireEvent.compositionEnd(textInput, updatedInput);
94+
});
95+
96+
expect(setInput).toHaveBeenCalledTimes(2);
97+
});
98+
99+
it('updates IME input with composition update', async () => {
100+
const input = { currentTarget: { value: 'しあわせ' } };
101+
const updatedInput = { currentTarget: { value: '幸せならおkです' } };
102+
103+
const result = render(<Form {...defaultProps} />);
104+
expect(result.container).toBeDefined();
105+
106+
const textFieldContainer = screen.getByTestId('text-input');
107+
108+
const textInput =
109+
textFieldContainer.querySelector('textarea') ??
110+
within(textFieldContainer).getByRole('textbox');
111+
112+
await waitFor(() => {
113+
fireEvent.compositionStart(textInput);
114+
fireEvent.compositionUpdate(textInput, input);
115+
});
116+
117+
await waitFor(() => {
118+
fireEvent.compositionUpdate(textInput, updatedInput);
119+
});
120+
121+
expect(setInput).toHaveBeenCalledTimes(2);
122+
});
60123
});

0 commit comments

Comments
 (0)