From 5309b562252668f63459bd9a5ab117fb21b2c537 Mon Sep 17 00:00:00 2001 From: JzoNg Date: Wed, 22 Apr 2026 08:10:06 +0800 Subject: [PATCH] Use shared renderer for human input content --- .../(humanInputLayout)/form/[token]/form.tsx | 5 ++-- .../app/text-generate/item/index.tsx | 3 +- .../app/text-generate/item/workflow-body.tsx | 3 +- .../__tests__/content-item.spec.tsx | 28 +++++++++++++------ .../human-input-content/content-item.tsx | 15 ++++------ .../human-input-content/human-input-form.tsx | 5 ++-- .../chat/answer/human-input-content/type.ts | 9 +++--- .../chat/answer/human-input-content/utils.ts | 3 +- .../chat/answer/human-input-form-list.tsx | 3 +- .../components/single-run-form.tsx | 5 ++-- web/service/share.ts | 2 +- web/service/workflow.ts | 8 +++--- 12 files changed, 52 insertions(+), 37 deletions(-) diff --git a/web/app/(humanInputLayout)/form/[token]/form.tsx b/web/app/(humanInputLayout)/form/[token]/form.tsx index d5d177616f..57c988a364 100644 --- a/web/app/(humanInputLayout)/form/[token]/form.tsx +++ b/web/app/(humanInputLayout)/form/[token]/form.tsx @@ -1,5 +1,6 @@ 'use client' import type { ButtonProps } from '@langgenius/dify-ui/button' +import type { HumanInputFieldValue } from '@/app/components/base/chat/chat/answer/human-input-content/field-renderer' import type { FormInputItem, UserAction } from '@/app/components/workflow/nodes/human-input/types' import type { SiteInfo } from '@/models/share' import type { HumanInputFormError } from '@/service/use-share' @@ -81,9 +82,9 @@ const FormContent = () => { }, [formData?.inputs, formData?.resolved_default_values]) // use immer - const handleInputsChange = (name: string, value: string) => { + const handleInputsChange = (name: string, value: HumanInputFieldValue) => { const newInputs = produce(inputs, (draft) => { - draft[name] = value + draft[name] = typeof value === 'string' ? value : '' }) setInputs(newInputs) } diff --git a/web/app/components/app/text-generate/item/index.tsx b/web/app/components/app/text-generate/item/index.tsx index 78ab1b5b32..caa2f624c8 100644 --- a/web/app/components/app/text-generate/item/index.tsx +++ b/web/app/components/app/text-generate/item/index.tsx @@ -1,5 +1,6 @@ 'use client' import type { FC } from 'react' +import type { HumanInputFieldValue } from '@/app/components/base/chat/chat/answer/human-input-content/field-renderer' import type { FeedbackType } from '@/app/components/base/chat/chat/type' import type { WorkflowProcess } from '@/app/components/base/chat/types' import type { SiteInfo } from '@/models/share' @@ -178,7 +179,7 @@ const GenerationItem: FC = ({ // eslint-disable-next-line react/set-state-in-effect setCurrentTab(getDefaultGenerationTab(workflowProcessData)) }, [workflowProcessData]) - const handleSubmitHumanInputForm = useCallback(async (formToken: string, formData: { inputs: Record, action: string }) => { + const handleSubmitHumanInputForm = useCallback(async (formToken: string, formData: { inputs: Record, action: string }) => { if (appSourceType === AppSourceType.installedApp) await submitHumanInputFormService(formToken, formData) else diff --git a/web/app/components/app/text-generate/item/workflow-body.tsx b/web/app/components/app/text-generate/item/workflow-body.tsx index d5a076ace9..d8cde0306e 100644 --- a/web/app/components/app/text-generate/item/workflow-body.tsx +++ b/web/app/components/app/text-generate/item/workflow-body.tsx @@ -1,5 +1,6 @@ 'use client' import type { FC } from 'react' +import type { HumanInputFieldValue } from '@/app/components/base/chat/chat/answer/human-input-content/field-renderer' import type { WorkflowProcess } from '@/app/components/base/chat/types' import type { SiteInfo } from '@/models/share' import { cn } from '@langgenius/dify-ui/cn' @@ -17,7 +18,7 @@ type WorkflowBodyProps = { depth: number hideProcessDetail?: boolean isError: boolean - onSubmitHumanInputForm: (formToken: string, formData: { inputs: Record, action: string }) => Promise + onSubmitHumanInputForm: (formToken: string, formData: { inputs: Record, action: string }) => Promise onSwitchTab: (tab: string) => void showResultTabs: boolean siteInfo: SiteInfo | null diff --git a/web/app/components/base/chat/chat/answer/human-input-content/__tests__/content-item.spec.tsx b/web/app/components/base/chat/chat/answer/human-input-content/__tests__/content-item.spec.tsx index c313a422c2..6e99f3b62b 100644 --- a/web/app/components/base/chat/chat/answer/human-input-content/__tests__/content-item.spec.tsx +++ b/web/app/components/base/chat/chat/answer/human-input-content/__tests__/content-item.spec.tsx @@ -8,6 +8,15 @@ vi.mock('@/app/components/base/markdown', () => ({ Markdown: ({ content }: { content: string }) =>
{content}
, })) +vi.mock('../field-renderer', () => ({ + __esModule: true, + default: ({ field, onChange }: { field: FormInputItem, onChange: (value: unknown) => void }) => ( + + ), +})) + describe('ContentItem', () => { const mockOnInputChange = vi.fn() const mockFormInputFields: FormInputItem[] = [ @@ -49,9 +58,8 @@ describe('ContentItem', () => { />, ) - const textarea = screen.getByTestId('content-item-textarea') + const textarea = screen.getByTestId('renderer-paragraph') expect(textarea).toBeInTheDocument() - expect(textarea).toHaveValue('Initial bio') expect(screen.queryByTestId('mock-markdown')).not.toBeInTheDocument() }) @@ -66,10 +74,9 @@ describe('ContentItem', () => { />, ) - const textarea = screen.getByTestId('content-item-textarea') - await user.type(textarea, 'x') + await user.click(screen.getByTestId('renderer-paragraph')) - expect(mockOnInputChange).toHaveBeenCalledWith('user_bio', 'Initial biox') + expect(mockOnInputChange).toHaveBeenCalledWith('user_bio', 'updated value') }) it('should render nothing if field name is valid but not found in formInputFields', () => { @@ -85,8 +92,10 @@ describe('ContentItem', () => { expect(container.firstChild).toBeNull() }) - it('should render nothing if input type is not supported', () => { - const { container } = render( + it('should delegate select fields to the shared renderer', async () => { + const user = userEvent.setup() + + render( { />, ) - expect(container.querySelector('[data-testid="content-item-textarea"]')).not.toBeInTheDocument() - expect(container.querySelector('.py-3')?.textContent).toBe('') + await user.click(screen.getByTestId('renderer-select')) + + expect(mockOnInputChange).toHaveBeenCalledWith('user_bio', 'select') }) }) diff --git a/web/app/components/base/chat/chat/answer/human-input-content/content-item.tsx b/web/app/components/base/chat/chat/answer/human-input-content/content-item.tsx index b958b48a17..a1c3e26111 100644 --- a/web/app/components/base/chat/chat/answer/human-input-content/content-item.tsx +++ b/web/app/components/base/chat/chat/answer/human-input-content/content-item.tsx @@ -2,7 +2,7 @@ import type { ContentItemProps } from './type' import * as React from 'react' import { useMemo } from 'react' import { Markdown } from '@/app/components/base/markdown' -import Textarea from '@/app/components/base/textarea' +import HumanInputFieldRenderer from './field-renderer' const ContentItem = ({ content, @@ -40,14 +40,11 @@ const ContentItem = ({ return (
- {formInputField.type === 'paragraph' && ( -