diff --git a/web/app/components/snippets/__tests__/index.spec.tsx b/web/app/components/snippets/__tests__/index.spec.tsx
index 42fe83704a..041783b987 100644
--- a/web/app/components/snippets/__tests__/index.spec.tsx
+++ b/web/app/components/snippets/__tests__/index.spec.tsx
@@ -79,6 +79,13 @@ vi.mock('@/hooks/use-breakpoints', () => ({
MediaType: { mobile: 'mobile', desktop: 'desktop' },
}))
+vi.mock('@/app/components/rag-pipeline/components/panel/input-field/hooks', () => ({
+ useFloatingRight: () => ({
+ floatingRight: false,
+ floatingRightWidth: 400,
+ }),
+}))
+
vi.mock('@/app/components/workflow', () => ({
default: ({ children }: { children: React.ReactNode }) => (
{children}
diff --git a/web/app/components/snippets/components/__tests__/input-field-editor.spec.tsx b/web/app/components/snippets/components/__tests__/input-field-editor.spec.tsx
new file mode 100644
index 0000000000..de46e23164
--- /dev/null
+++ b/web/app/components/snippets/components/__tests__/input-field-editor.spec.tsx
@@ -0,0 +1,80 @@
+import type { InputVar } from '@/models/pipeline'
+import { render, screen } from '@testing-library/react'
+import { PipelineInputVarType } from '@/models/pipeline'
+import SnippetInputFieldEditor from '../input-field-editor'
+
+const mockUseFloatingRight = vi.fn()
+
+vi.mock('@/app/components/rag-pipeline/components/panel/input-field/hooks', () => ({
+ useFloatingRight: (...args: unknown[]) => mockUseFloatingRight(...args),
+}))
+
+vi.mock('@/app/components/rag-pipeline/components/panel/input-field/editor/form', () => ({
+ default: ({ isEditMode }: { isEditMode: boolean }) => (
+ {isEditMode ? 'edit' : 'create'}
+ ),
+}))
+
+const createField = (overrides: Partial = {}): InputVar => ({
+ type: PipelineInputVarType.textInput,
+ label: 'Blog URL',
+ variable: 'blog_url',
+ required: true,
+ options: [],
+ placeholder: 'Paste a source article URL',
+ max_length: 256,
+ ...overrides,
+})
+
+describe('SnippetInputFieldEditor', () => {
+ beforeEach(() => {
+ vi.clearAllMocks()
+ mockUseFloatingRight.mockReturnValue({
+ floatingRight: false,
+ floatingRightWidth: 400,
+ })
+ })
+
+ // Verifies the default desktop layout keeps the editor inline with the panel.
+ describe('Rendering', () => {
+ it('should render the add title without floating positioning by default', () => {
+ render(
+ ,
+ )
+
+ const title = screen.getByText('datasetPipeline.inputFieldPanel.addInputField')
+ const editor = title.parentElement
+
+ expect(title).toBeInTheDocument()
+ expect(editor).not.toHaveClass('absolute')
+ expect(editor).toHaveStyle({ width: 'min(400px, calc(100vw - 24px))' })
+ expect(mockUseFloatingRight).toHaveBeenCalledWith(400)
+ })
+
+ it('should float over the panel when there is not enough room', () => {
+ mockUseFloatingRight.mockReturnValue({
+ floatingRight: true,
+ floatingRightWidth: 320,
+ })
+
+ render(
+ ,
+ )
+
+ const title = screen.getByText('datasetPipeline.inputFieldPanel.editInputField')
+ const editor = title.parentElement
+
+ expect(title).toBeInTheDocument()
+ expect(editor).toHaveClass('absolute', 'right-0', 'z-[100]')
+ expect(editor).toHaveStyle({ width: 'min(320px, calc(100vw - 24px))' })
+ expect(screen.getByTestId('snippet-input-field-form')).toHaveTextContent('edit')
+ })
+ })
+})
diff --git a/web/app/components/snippets/components/input-field-editor.tsx b/web/app/components/snippets/components/input-field-editor.tsx
index 99362857e0..dcc6b8b72c 100644
--- a/web/app/components/snippets/components/input-field-editor.tsx
+++ b/web/app/components/snippets/components/input-field-editor.tsx
@@ -2,10 +2,13 @@
import type { FormData } from '@/app/components/rag-pipeline/components/panel/input-field/editor/form/types'
import type { SnippetInputField } from '@/models/snippet'
+import { RiCloseLine } from '@remixicon/react'
import { useCallback, useMemo } from 'react'
import { useTranslation } from 'react-i18next'
import InputFieldForm from '@/app/components/rag-pipeline/components/panel/input-field/editor/form'
import { convertFormDataToINputField, convertToInputFieldFormData } from '@/app/components/rag-pipeline/components/panel/input-field/editor/utils'
+import { useFloatingRight } from '@/app/components/rag-pipeline/components/panel/input-field/hooks'
+import { cn } from '@/utils/classnames'
type SnippetInputFieldEditorProps = {
field?: SnippetInputField | null
@@ -19,6 +22,7 @@ const SnippetInputFieldEditor = ({
onSubmit,
}: SnippetInputFieldEditorProps) => {
const { t } = useTranslation()
+ const { floatingRight, floatingRightWidth } = useFloatingRight(400)
const initialData = useMemo(() => {
return convertToInputFieldFormData(field || undefined)
@@ -29,7 +33,16 @@ const SnippetInputFieldEditor = ({
}, [onSubmit])
return (
-
+
{field ? t('inputFieldPanel.editInputField', { ns: 'datasetPipeline' }) : t('inputFieldPanel.addInputField', { ns: 'datasetPipeline' })}
@@ -38,7 +51,7 @@ const SnippetInputFieldEditor = ({
className="absolute right-2.5 top-2.5 flex h-8 w-8 items-center justify-center"
onClick={onClose}
>
-
+
- {isInputPanelOpen && (
-
-
+ {(isInputPanelOpen || isEditorOpen) && (
+
+
+ {isEditorOpen && (
+
+ )}
)}
- {isEditorOpen && (
-
- )}
>
)
}