Skip to content

Commit 5e89280

Browse files
Bill LeoutsakosBill Leoutsakos
authored andcommitted
Refine manual tag ID editing
1 parent c03a1df commit 5e89280

2 files changed

Lines changed: 173 additions & 1 deletion

File tree

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
/**
2+
* @vitest-environment jsdom
3+
*/
4+
import { act, type ChangeEvent } from 'react'
5+
import { createRoot, type Root } from 'react-dom/client'
6+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
7+
8+
const { mockSetStoreValue } = vi.hoisted(() => ({
9+
mockSetStoreValue: vi.fn(),
10+
}))
11+
12+
const initialFilters = [
13+
{
14+
id: 'filter-1',
15+
tagName: '',
16+
tagId: 'tag-text',
17+
tagSlot: 'tag1',
18+
fieldType: 'text',
19+
operator: 'contains',
20+
tagValue: 'api',
21+
valueTo: 'secondary',
22+
collapsed: false,
23+
},
24+
]
25+
26+
vi.mock(
27+
'@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/workflow-search-highlight',
28+
() => ({
29+
getActiveWorkflowSearchHighlight: () => undefined,
30+
})
31+
)
32+
33+
vi.mock(
34+
'@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-depends-on-gate',
35+
() => ({
36+
useDependsOnGate: () => ({
37+
dependencyValues: { knowledgeBaseSelector: 'kb-1' },
38+
}),
39+
})
40+
)
41+
42+
vi.mock(
43+
'@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-sub-block-input',
44+
() => ({
45+
useSubBlockInput: () => ({
46+
fieldHelpers: {
47+
getFieldState: () => ({ showTags: false }),
48+
createFieldHandlers: (_key: string, _value: string, onChange: (value: string) => void) => ({
49+
onChange: (event: ChangeEvent<HTMLInputElement>) => onChange(event.target.value),
50+
onKeyDown: vi.fn(),
51+
onDrop: vi.fn(),
52+
onDragOver: vi.fn(),
53+
onFocus: vi.fn(),
54+
}),
55+
createTagSelectHandler: vi.fn(),
56+
hideFieldDropdowns: vi.fn(),
57+
},
58+
}),
59+
})
60+
)
61+
62+
vi.mock(
63+
'@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/providers/active-search-target-provider',
64+
() => ({
65+
useActiveSearchTarget: () => null,
66+
})
67+
)
68+
69+
vi.mock(
70+
'@/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-accessible-reference-prefixes',
71+
() => ({
72+
useAccessibleReferencePrefixes: () => null,
73+
})
74+
)
75+
76+
vi.mock('@/hooks/kb/use-knowledge-base-tag-definitions', () => ({
77+
useKnowledgeBaseTagDefinitions: () => ({
78+
tagDefinitions: [
79+
{ id: 'tag-text', tagSlot: 'tag1', displayName: 'category', fieldType: 'text' },
80+
{ id: 'tag-number', tagSlot: 'number1', displayName: 'score', fieldType: 'number' },
81+
],
82+
isLoading: false,
83+
}),
84+
}))
85+
86+
vi.mock('@/hooks/kb/use-tag-selection', () => ({
87+
useTagSelection: () => vi.fn(),
88+
}))
89+
90+
vi.mock(
91+
'@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-sub-block-value',
92+
() => ({
93+
useSubBlockValue: () => [JSON.stringify(initialFilters), mockSetStoreValue],
94+
})
95+
)
96+
97+
import { KnowledgeTagFilters } from './knowledge-tag-filters'
98+
99+
let container: HTMLDivElement
100+
let root: Root
101+
102+
describe('KnowledgeTagFilters Tag ID editing', () => {
103+
beforeEach(() => {
104+
container = document.createElement('div')
105+
document.body.appendChild(container)
106+
root = createRoot(container)
107+
mockSetStoreValue.mockClear()
108+
})
109+
110+
afterEach(() => {
111+
act(() => root.unmount())
112+
container.remove()
113+
vi.clearAllMocks()
114+
})
115+
116+
const renderTagFilters = async () => {
117+
await act(async () => {
118+
root.render(
119+
<KnowledgeTagFilters
120+
blockId='knowledge-1'
121+
subBlock={{
122+
id: 'manualTagFilters',
123+
title: 'Tag Filters',
124+
type: 'knowledge-tag-filters',
125+
mode: 'advanced',
126+
canonicalParamId: 'tagFilters',
127+
}}
128+
/>
129+
)
130+
})
131+
132+
return container.querySelector<HTMLInputElement>('input[placeholder="Enter tag ID"]')
133+
}
134+
135+
const changeTagId = async (input: HTMLInputElement, value: string) => {
136+
await act(async () => {
137+
const valueSetter = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value')?.set
138+
valueSetter?.call(input, value)
139+
input.dispatchEvent(new Event('input', { bubbles: true }))
140+
})
141+
142+
const serializedFilters = mockSetStoreValue.mock.lastCall?.[0] as string
143+
return JSON.parse(serializedFilters)[0]
144+
}
145+
146+
it('preserves values for unresolved edits and resets them once a different literal ID resolves', async () => {
147+
const input = await renderTagFilters()
148+
expect(input).not.toBeNull()
149+
150+
const unresolvedFilter = await changeTagId(input as HTMLInputElement, 'tag-numbe')
151+
expect(unresolvedFilter).toMatchObject({
152+
tagId: 'tag-numbe',
153+
operator: 'contains',
154+
tagValue: 'api',
155+
valueTo: 'secondary',
156+
})
157+
158+
const resolvedFilter = await changeTagId(input as HTMLInputElement, 'tag-number')
159+
expect(resolvedFilter).toMatchObject({
160+
tagId: 'tag-number',
161+
tagSlot: 'number1',
162+
fieldType: 'number',
163+
operator: 'eq',
164+
tagValue: '',
165+
})
166+
expect(resolvedFilter.valueTo).toBeUndefined()
167+
})
168+
})

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/knowledge-tag-filters/knowledge-tag-filters.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -232,8 +232,12 @@ export function KnowledgeTagFilters({
232232
if (field === 'tagId') {
233233
const tagDef = tagDefinitions.find((t) => t.id === value)
234234
updated.tagSlot = tagDef?.tagSlot
235-
if (tagDef) {
235+
if (tagDef && f.tagId !== value) {
236236
updated.fieldType = tagDef.fieldType as FilterFieldType
237+
const operators = getOperatorsForFieldType(updated.fieldType)
238+
updated.operator = operators[0]?.value || 'eq'
239+
updated.tagValue = ''
240+
updated.valueTo = undefined
237241
}
238242
}
239243

0 commit comments

Comments
 (0)