diff --git a/web/app/components/workflow/nodes/human-input/components/__tests__/variable-in-markdown.spec.tsx b/web/app/components/workflow/nodes/human-input/components/__tests__/variable-in-markdown.spec.tsx index 2dbc75f1f8..bbb47288bb 100644 --- a/web/app/components/workflow/nodes/human-input/components/__tests__/variable-in-markdown.spec.tsx +++ b/web/app/components/workflow/nodes/human-input/components/__tests__/variable-in-markdown.spec.tsx @@ -1,5 +1,7 @@ import { render, screen } from '@testing-library/react' +import userEvent from '@testing-library/user-event' import { InputVarType } from '@/app/components/workflow/types' +import { TransferMethod } from '@/types/app' import { Note, rehypeNotes, rehypeVariable, Variable } from '../variable-in-markdown' describe('variable-in-markdown', () => { @@ -119,5 +121,67 @@ describe('variable-in-markdown', () => { expect(screen.getByText('Plain value')).toBeInTheDocument() }) + + it('should render a select preview control for select inputs', () => { + render( + nodeId} + />, + ) + + expect(screen.getByTestId('human-input-note-select-preview')).toBeInTheDocument() + expect(screen.getByText('Approved')).toBeInTheDocument() + }) + + it('should open the select preview and show option items', async () => { + const user = userEvent.setup() + + render( + nodeId} + />, + ) + + await user.click(screen.getByRole('combobox', { name: 'human-input-note-select' })) + + expect(await screen.findByRole('option', { name: 'Rejected' })).toBeInTheDocument() + }) + + it('should render upload placeholders for file inputs', () => { + render( + nodeId} + />, + ) + + expect(screen.getByTestId('human-input-note-file-preview')).toBeInTheDocument() + expect(screen.getByText('common.fileUploader.uploadFromComputer')).toBeInTheDocument() + expect(screen.getByText('common.fileUploader.pasteFileLink')).toBeInTheDocument() + }) }) }) diff --git a/web/app/components/workflow/nodes/human-input/components/variable-in-markdown.tsx b/web/app/components/workflow/nodes/human-input/components/variable-in-markdown.tsx index acdb54f924..2a3367ffd7 100644 --- a/web/app/components/workflow/nodes/human-input/components/variable-in-markdown.tsx +++ b/web/app/components/workflow/nodes/human-input/components/variable-in-markdown.tsx @@ -1,6 +1,10 @@ -import type * as React from 'react' +/* eslint-disable react-refresh/only-export-components */ import type { FormInputItem } from '../types' +import { cn } from '@langgenius/dify-ui/cn' +import { Select, SelectContent, SelectItem, SelectItemIndicator, SelectItemText, SelectTrigger } from '@langgenius/dify-ui/select' +import * as React from 'react' import { useTranslation } from 'react-i18next' +import { TransferMethod } from '@/types/app' import { isFileFormInput, isFileListFormInput, isSelectFormInput } from '../types' const variableRegex = /\{\{#(.+?)#\}\}/g @@ -134,38 +138,83 @@ export const Variable: React.FC<{ path: string }> = ({ path }) => { ) } +const SelectPreview: React.FC<{ label: string, options: string[] }> = ({ label, options }) => { + const [value, setValue] = React.useState(options[0] || label) + + return ( +
+ +
+ ) +} + +const FileUploadPreview: React.FC<{ methods: TransferMethod[], t: (key: string, options?: Record) => string }> = ({ methods, t }) => { + const normalizedMethods = methods.length + ? methods + : [TransferMethod.local_file, TransferMethod.remote_url] + const actions = [ + normalizedMethods.includes(TransferMethod.local_file) && { + iconClassName: 'i-ri-upload-cloud-2-line', + label: t('fileUploader.uploadFromComputer', { ns: 'common' }), + }, + normalizedMethods.includes(TransferMethod.remote_url) && { + iconClassName: 'i-ri-link', + label: t('fileUploader.pasteFileLink', { ns: 'common' }), + }, + ].filter(Boolean) as Array<{ iconClassName: string, label: string }> + + return ( +
1 ? 'grid-cols-2' : 'grid-cols-1', + )} + > + {actions.map(action => ( +
+ + + {action.label} + +
+ ))} +
+ ) +} + export const Note: React.FC<{ input: FormInputItem, nodeName: (nodeId: string) => string }> = ({ input, nodeName }) => { const { t } = useTranslation() if (isSelectFormInput(input)) { const isVariable = input.option_source.type === 'variable' - const path = `{{#${input.option_source.selector.join('.')}#}}` - const newPath = path ? replaceNodeIdsWithNames(path, nodeName) : path - return ( -
- {isVariable ? : {input.option_source.value.join(', ') || input.type}} -
- ) + const label = isVariable + ? t('nodes.humanInput.insertInputField.variable', { ns: 'workflow' }) + : input.option_source.value[0] || t('variableConfig.select', { ns: 'appDebug' }) + const options = isVariable ? [label] : input.option_source.value + return } if (isFileFormInput(input)) { - return ( -
- {input.allowed_file_types.join(', ') || input.type} -
- ) + return } if (isFileListFormInput(input)) { - const summary = [ - input.allowed_file_types.join(', '), - input.max_upload_count ? `${t('nodes.humanInput.insertInputField.maxUploads', { ns: 'workflow' })}: ${input.max_upload_count}` : null, - ].filter(Boolean).join(' ยท ') - - return ( -
- {summary || input.type} -
- ) + return } const isVariable = input.default.type === 'variable'