From 985c3db4fda7533416cf2162d4458778811afc8b Mon Sep 17 00:00:00 2001 From: JzoNg Date: Sun, 29 Mar 2026 18:02:27 +0800 Subject: [PATCH] feat(web): snippet input field panel layout --- .../snippets/__tests__/index.spec.tsx | 7 ++ .../__tests__/input-field-editor.spec.tsx | 80 +++++++++++++++++++ .../components/input-field-editor.tsx | 17 +++- .../snippets/components/snippet-children.tsx | 24 +++--- 4 files changed, 112 insertions(+), 16 deletions(-) create mode 100644 web/app/components/snippets/components/__tests__/input-field-editor.spec.tsx 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 && ( -
-
- -
-
- )} ) }