Skip to content

Commit c4d27e4

Browse files
committed
fix(search): wait for hydration before editing queries
1 parent 672122f commit c4d27e4

3 files changed

Lines changed: 41 additions & 13 deletions

File tree

‎apps/sim/app/o/[organizationId]/components/search-input-bar.tsx‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { InputToolbar } from '@/app/workspace/[workspaceId]/home/components/user
1616
interface SearchInputBarProps {
1717
inputRef: RefObject<HTMLTextAreaElement | null>
1818
value: string
19+
disabled?: boolean
1920
onChange: (value: string) => void
2021
onSubmit: () => void
2122
onPaste?: ClipboardEventHandler<HTMLTextAreaElement>
@@ -36,6 +37,7 @@ export function SearchInputBar({
3637
attachments,
3738
inputRef,
3839
value,
40+
disabled = false,
3941
onChange,
4042
onSubmit,
4143
onPaste,
@@ -106,6 +108,7 @@ export function SearchInputBar({
106108
inputRef={inputRef}
107109
scrollerRef={scrollerRef}
108110
value={value}
111+
disabled={disabled}
109112
compact={!hasAttachments && !expanded}
110113
className={hasAttachments ? 'min-h-[56px]' : undefined}
111114
maxHeight={200}

‎apps/sim/app/o/[organizationId]/search/search.tsx‎

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import { MicButton } from '@/app/workspace/[workspaceId]/home/components/user-in
2626
import { MicrophonePermissionHelp } from '@/app/workspace/[workspaceId]/home/components/user-input/components/microphone-permission-help/microphone-permission-help'
2727
import { HomeFallback } from '@/app/workspace/[workspaceId]/home/home-fallback'
2828
import { knowledgeKeys } from '@/hooks/queries/utils/knowledge-keys'
29+
import { useHydrated } from '@/hooks/use-hydrated'
2930
import { useVoiceInput } from '@/hooks/use-voice-input'
3031
import { useMothershipDraftsStore } from '@/stores/mothership-drafts/store'
3132

@@ -54,6 +55,7 @@ interface SearchFieldProps {
5455
/** Search commits a query on submit while retaining an independent editable draft. */
5556
function SearchField({ userId, initialValue, onSubmit }: SearchFieldProps) {
5657
const inputRef = useRef<HTMLTextAreaElement>(null)
58+
const hydrated = useHydrated()
5759
const { organization } = useOrganizationContext()
5860
const queryClient = useQueryClient()
5961
const queryKey = knowledgeKeys.searchQuery(
@@ -90,14 +92,17 @@ function SearchField({ userId, initialValue, onSubmit }: SearchFieldProps) {
9092
getValue: () => value,
9193
onChange: setValue,
9294
})
93-
const canSubmit = value.trim().length > 0
95+
const canSubmit = hydrated && value.trim().length > 0
9496

95-
useEffect(() => inputRef.current?.focus(), [])
97+
useEffect(() => {
98+
if (hydrated) inputRef.current?.focus()
99+
}, [hydrated])
96100

97101
const field = (
98102
<SearchInputBar
99103
inputRef={inputRef}
100104
value={value}
105+
disabled={!hydrated}
101106
onChange={setValue}
102107
onSubmit={() => submit()}
103108
floating={!initialValue.trim()}

‎apps/sim/scripts/test-organization-search-e2e.ts‎

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -715,17 +715,37 @@ try {
715715
await expect(page).toHaveURL(`${baseUrl.origin}${root}/search/chat/${assistantId}`)
716716
await expect(page.getByText(answer, { exact: true })).toBeVisible()
717717
})
718-
await check('New search clears its draft while Back restores the prior query draft', async () => {
719-
await page.goto(`${baseUrl.origin}${root}/search?q=Launch`)
720-
const input = page.getByRole('textbox', { name: 'Search your sources' })
721-
await input.fill('Edited launch query')
722-
await page.getByRole('button', { name: 'New search', exact: true }).click()
723-
await expect(page).toHaveURL(`${baseUrl.origin}${root}/search`)
724-
await expect(input).toHaveValue('')
725-
await page.goBack()
726-
await expect(input).toHaveValue('Edited launch query')
727-
assert.equal(new URL(page.url()).searchParams.get('q'), 'Launch')
728-
})
718+
await check(
719+
'Search protects edits during hydration and restores the prior draft on Back',
720+
async () => {
721+
const releaseScripts = createDeferred<void>()
722+
const scripts = '**/_next/static/**/*.js'
723+
await page.route(scripts, async (route) => {
724+
await releaseScripts.promise
725+
await route.continue()
726+
})
727+
const input = page.getByRole('textbox', { name: 'Search your sources' })
728+
try {
729+
await page.goto(`${baseUrl.origin}${root}/search?q=Launch`, { waitUntil: 'commit' })
730+
await expect(input).toBeVisible()
731+
await expect(input).toBeDisabled()
732+
await expect(page.getByLabel('Search', { exact: true })).toBeDisabled()
733+
} finally {
734+
releaseScripts.resolve()
735+
await page.unrouteAll({ behavior: 'wait' })
736+
await page.waitForLoadState()
737+
}
738+
await expect(input).toBeFocused()
739+
await input.fill('Edited launch query')
740+
await expect(input).toHaveValue('Edited launch query')
741+
await page.getByRole('button', { name: 'New search', exact: true }).click()
742+
await expect(page).toHaveURL(`${baseUrl.origin}${root}/search`)
743+
await expect(input).toHaveValue('')
744+
await page.goBack()
745+
await expect(input).toHaveValue('Edited launch query')
746+
assert.equal(new URL(page.url()).searchParams.get('q'), 'Launch')
747+
}
748+
)
729749
await check('Search URLs cannot change an agent conversation into an assistant', async () => {
730750
await page.goto(`${baseUrl.origin}${root}/search/chat/${agentId}`)
731751
await expect(page).toHaveURL(`${baseUrl.origin}${root}/chat/${agentId}`)

0 commit comments

Comments
 (0)