mirror of
https://github.com/langgenius/dify.git
synced 2026-06-07 16:32:01 +08:00
fix(explore): render human input preview handles (#37086)
This commit is contained in:
parent
a1ad4be61e
commit
9da4d167fa
@ -1,4 +1,5 @@
|
||||
import { render } from '@testing-library/react'
|
||||
import { UserActionButtonType } from '@/app/components/workflow/nodes/human-input/types'
|
||||
import { BlockEnum } from '@/app/components/workflow/types'
|
||||
import CustomNode from '../index'
|
||||
|
||||
@ -41,4 +42,39 @@ describe('workflow preview custom node', () => {
|
||||
expect(getByText('Classifier node')).toBeInTheDocument()
|
||||
expect(container.querySelector('[data-handleid="class-a"]')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('renders human input output handles from the mapped node component', () => {
|
||||
const props: React.ComponentProps<typeof CustomNode> = {
|
||||
id: 'human-input-1',
|
||||
type: 'custom-node',
|
||||
selected: false,
|
||||
zIndex: 1,
|
||||
isConnectable: true,
|
||||
dragging: false,
|
||||
xPos: 0,
|
||||
yPos: 0,
|
||||
dragHandle: undefined,
|
||||
data: {
|
||||
type: BlockEnum.HumanInput,
|
||||
title: 'Human Input',
|
||||
desc: '',
|
||||
delivery_methods: [],
|
||||
form_content: '',
|
||||
inputs: [],
|
||||
user_actions: [
|
||||
{ id: 'approve', title: 'Approve', button_style: UserActionButtonType.Primary },
|
||||
],
|
||||
timeout: 1,
|
||||
timeout_unit: 'hour',
|
||||
} as never,
|
||||
}
|
||||
|
||||
const { container, getByText } = render(
|
||||
<CustomNode {...props} />,
|
||||
)
|
||||
|
||||
expect(getByText('Human Input')).toBeInTheDocument()
|
||||
expect(container.querySelector('[data-handleid="approve"]')).toBeInTheDocument()
|
||||
expect(container.querySelector('[data-handleid="__timeout"]')).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
||||
@ -1,13 +1,14 @@
|
||||
import { BlockEnum } from '@/app/components/workflow/types'
|
||||
import HumanInputNode from './human-input/node'
|
||||
import IfElseNode from './if-else/node'
|
||||
import IterationNode from './iteration/node'
|
||||
import LoopNode from './loop/node'
|
||||
import QuestionClassifierNode from './question-classifier/node'
|
||||
|
||||
// todo: add human-input node support
|
||||
export const NodeComponentMap: Record<string, any> = {
|
||||
[BlockEnum.QuestionClassifier]: QuestionClassifierNode,
|
||||
[BlockEnum.IfElse]: IfElseNode,
|
||||
[BlockEnum.HumanInput]: HumanInputNode,
|
||||
[BlockEnum.Iteration]: IterationNode,
|
||||
[BlockEnum.Loop]: LoopNode,
|
||||
}
|
||||
|
||||
@ -0,0 +1,54 @@
|
||||
import { render } from '@testing-library/react'
|
||||
import { UserActionButtonType } from '@/app/components/workflow/nodes/human-input/types'
|
||||
import { BlockEnum } from '@/app/components/workflow/types'
|
||||
import Node from '../node'
|
||||
|
||||
vi.mock('reactflow', () => ({
|
||||
Handle: (props: { id: string, type: string, className?: string }) => (
|
||||
<div data-testid="handle" data-handleid={props.id} data-type={props.type} className={props.className} />
|
||||
),
|
||||
Position: {
|
||||
Right: 'right',
|
||||
},
|
||||
}))
|
||||
|
||||
describe('workflow preview human input node', () => {
|
||||
it('renders one output handle per user action and timeout', () => {
|
||||
const props: React.ComponentProps<typeof Node> = {
|
||||
id: 'human-input-1',
|
||||
type: 'human-input-node',
|
||||
selected: false,
|
||||
zIndex: 1,
|
||||
isConnectable: true,
|
||||
dragging: false,
|
||||
xPos: 0,
|
||||
yPos: 0,
|
||||
dragHandle: undefined,
|
||||
data: {
|
||||
type: BlockEnum.HumanInput,
|
||||
title: 'Human Input',
|
||||
desc: '',
|
||||
delivery_methods: [],
|
||||
form_content: '',
|
||||
inputs: [],
|
||||
user_actions: [
|
||||
{ id: 'approve', title: 'Approve', button_style: UserActionButtonType.Primary },
|
||||
{ id: 'regenerate', title: 'Regenerate', button_style: UserActionButtonType.Default },
|
||||
],
|
||||
timeout: 1,
|
||||
timeout_unit: 'hour',
|
||||
} as never,
|
||||
}
|
||||
|
||||
const { container, getByText } = render(
|
||||
<Node {...props} />,
|
||||
)
|
||||
|
||||
expect(getByText('approve')).toBeInTheDocument()
|
||||
expect(getByText('regenerate')).toBeInTheDocument()
|
||||
expect(getByText('Timeout')).toBeInTheDocument()
|
||||
expect(container.querySelector('[data-handleid="approve"]')).toBeInTheDocument()
|
||||
expect(container.querySelector('[data-handleid="regenerate"]')).toBeInTheDocument()
|
||||
expect(container.querySelector('[data-handleid="__timeout"]')).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
@ -0,0 +1,33 @@
|
||||
import type { NodeProps } from 'reactflow'
|
||||
import type { HumanInputNodeType } from '@/app/components/workflow/nodes/human-input/types'
|
||||
import { memo } from 'react'
|
||||
import { NodeSourceHandle } from '../../node-handle'
|
||||
|
||||
function HumanInputNode(props: NodeProps<HumanInputNodeType>) {
|
||||
const { data } = props
|
||||
|
||||
return (
|
||||
<div className="space-y-0.5 px-3 py-1">
|
||||
{data.user_actions.map(userAction => (
|
||||
<div key={userAction.id} className="relative flex h-6 flex-row-reverse items-center px-1">
|
||||
<span className="truncate system-xs-semibold-uppercase text-text-secondary">{userAction.id}</span>
|
||||
<NodeSourceHandle
|
||||
{...props}
|
||||
handleId={userAction.id}
|
||||
handleClassName="top-1/2! -right-[21px]! -translate-y-1/2!"
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
<div className="relative flex h-6 flex-row-reverse items-center px-1">
|
||||
<span className="truncate system-xs-semibold-uppercase text-text-secondary">Timeout</span>
|
||||
<NodeSourceHandle
|
||||
{...props}
|
||||
handleId="__timeout"
|
||||
handleClassName="top-1/2! -right-[21px]! -translate-y-1/2!"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default memo(HumanInputNode)
|
||||
Loading…
Reference in New Issue
Block a user