diff --git a/web/app/components/datasets/create/step-two/components/__tests__/inputs.spec.tsx b/web/app/components/datasets/create/step-two/components/__tests__/inputs.spec.tsx
index 28c640cdbe..2c0480e508 100644
--- a/web/app/components/datasets/create/step-two/components/__tests__/inputs.spec.tsx
+++ b/web/app/components/datasets/create/step-two/components/__tests__/inputs.spec.tsx
@@ -33,6 +33,22 @@ describe('DelimiterInput', () => {
// Tooltip triggers render; component mounts without error
expect(screen.getByText(`${ns}.stepTwo.separator`)).toBeInTheDocument()
})
+
+ it('should suppress onChange during IME composition', () => {
+ const onChange = vi.fn()
+ const finalValue = 'wu'
+ render()
+ const input = screen.getByPlaceholderText(`${ns}.stepTwo.separatorPlaceholder`)
+
+ fireEvent.compositionStart(input)
+ fireEvent.change(input, { target: { value: 'w' } })
+ fireEvent.change(input, { target: { value: finalValue } })
+ expect(onChange).not.toHaveBeenCalled()
+
+ fireEvent.compositionEnd(input)
+ expect(onChange).toHaveBeenCalledTimes(1)
+ expect(onChange.mock.calls[0][0].target.value).toBe(finalValue)
+ })
})
describe('MaxLengthInput', () => {
diff --git a/web/app/components/datasets/create/step-two/components/inputs.tsx b/web/app/components/datasets/create/step-two/components/inputs.tsx
index 9d40f511f9..4733852d19 100644
--- a/web/app/components/datasets/create/step-two/components/inputs.tsx
+++ b/web/app/components/datasets/create/step-two/components/inputs.tsx
@@ -1,6 +1,7 @@
import type { FC, PropsWithChildren, ReactNode } from 'react'
import type { InputProps } from '@/app/components/base/input'
import type { NumberFieldInputProps, NumberFieldRootProps, NumberFieldSize } from '@/app/components/base/ui/number-field'
+import { useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import Input from '@/app/components/base/input'
import Tooltip from '@/app/components/base/tooltip'
@@ -16,7 +17,7 @@ import {
import { env } from '@/env'
const TextLabel: FC = (props) => {
- return
+ return
}
const FormField: FC> = (props) => {
@@ -28,8 +29,11 @@ const FormField: FC> = (props) => {
)
}
-export const DelimiterInput: FC = (props) => {
+export const DelimiterInput: FC = ({ tooltip, onChange, value, ...rest }) => {
const { t } = useTranslation()
+ const isComposing = useRef(false)
+ const [compositionValue, setCompositionValue] = useState('')
+
return (
@@ -37,7 +41,7 @@ export const DelimiterInput: FC = (props) =>
- {props.tooltip || t('stepTwo.separatorTip', { ns: 'datasetCreation' })}
+ {tooltip || t('stepTwo.separatorTip', { ns: 'datasetCreation' })}
)}
/>
@@ -48,7 +52,24 @@ export const DelimiterInput: FC = (props) =>
type="text"
className="h-9"
placeholder={t('stepTwo.separatorPlaceholder', { ns: 'datasetCreation' })!}
- {...props}
+ value={isComposing.current ? compositionValue : value}
+ onChange={(e) => {
+ if (isComposing.current)
+ setCompositionValue(e.target.value)
+ else
+ onChange?.(e)
+ }}
+ onCompositionStart={() => {
+ isComposing.current = true
+ setCompositionValue(String(value ?? ''))
+ }}
+ onCompositionEnd={(e) => {
+ const committed = e.currentTarget.value
+ isComposing.current = false
+ setCompositionValue('')
+ onChange?.({ ...e, target: { ...e.target, value: committed } } as unknown as React.ChangeEvent)
+ }}
+ {...rest}
/>
)
diff --git a/web/eslint-suppressions.json b/web/eslint-suppressions.json
index 7180bd677a..1077b4c1fd 100644
--- a/web/eslint-suppressions.json
+++ b/web/eslint-suppressions.json
@@ -4291,9 +4291,6 @@
"app/components/datasets/create/step-two/components/inputs.tsx": {
"no-restricted-imports": {
"count": 1
- },
- "tailwindcss/enforce-consistent-class-order": {
- "count": 1
}
},
"app/components/datasets/create/step-two/components/option-card.tsx": {