Skip to content

Commit 6467a35

Browse files
msujawsclaude
andcommitted
fix(url): only update URL when Apply Filters is clicked
Previously, the URL was updated via replaceState every time a filter value changed. Now the URL is only updated when the user explicitly clicks "Apply Filters" (or presses Enter in a filter field). Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 31d493f commit 6467a35

File tree

2 files changed

+79
-10
lines changed

2 files changed

+79
-10
lines changed

src/App.test.tsx

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
22
import { render, screen, waitFor } from '@testing-library/react'
3+
import userEvent from '@testing-library/user-event'
34
import App from './App'
45
import { useStore } from './store'
56

@@ -261,4 +262,78 @@ describe('App', () => {
261262
})
262263
})
263264
})
265+
266+
describe('URL sync behavior', () => {
267+
let replaceStateMock: ReturnType<typeof vi.fn>
268+
269+
beforeEach(() => {
270+
replaceStateMock = vi.fn()
271+
vi.spyOn(window.history, 'replaceState').mockImplementation(replaceStateMock)
272+
273+
useStore.setState({
274+
apiKey: 'test-api-key',
275+
isValid: true,
276+
isValidating: false,
277+
})
278+
})
279+
280+
it('should NOT update URL when filter input changes', async () => {
281+
const user = userEvent.setup()
282+
render(<App />)
283+
284+
// Clear any calls from initialization
285+
replaceStateMock.mockClear()
286+
287+
// Type into the whiteboard filter
288+
const whiteboardInput = screen.getByLabelText(/whiteboard/i)
289+
await user.clear(whiteboardInput)
290+
await user.type(whiteboardInput, '[test]')
291+
292+
// URL should NOT have been updated
293+
expect(replaceStateMock).not.toHaveBeenCalled()
294+
})
295+
296+
it('should update URL when Apply Filter button is clicked', async () => {
297+
const user = userEvent.setup()
298+
const fetchBugs = vi.fn().mockResolvedValue([])
299+
useStore.setState({
300+
fetchBugs,
301+
filters: {
302+
whiteboardTag: '[test]',
303+
component: '',
304+
excludeMetaBugs: false,
305+
sortOrder: 'priority',
306+
},
307+
})
308+
309+
render(<App />)
310+
311+
// Clear any calls from initialization
312+
replaceStateMock.mockClear()
313+
314+
// Click Apply Filters button
315+
const applyButton = screen.getByRole('button', { name: /apply filters/i })
316+
await user.click(applyButton)
317+
318+
// URL should have been updated with the filter
319+
expect(replaceStateMock).toHaveBeenCalled()
320+
const call = replaceStateMock.mock.calls[0]
321+
expect(call[2]).toContain('whiteboard=%5Btest%5D')
322+
})
323+
324+
it('should NOT update URL when sort order changes without clicking Apply', async () => {
325+
const user = userEvent.setup()
326+
render(<App />)
327+
328+
// Clear any calls from initialization
329+
replaceStateMock.mockClear()
330+
331+
// Change sort order by clicking the radio button
332+
const lastChangedRadio = screen.getByRole('radio', { name: /last changed/i })
333+
await user.click(lastChangedRadio)
334+
335+
// URL should NOT have been updated
336+
expect(replaceStateMock).not.toHaveBeenCalled()
337+
})
338+
})
264339
})

src/App.tsx

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -90,14 +90,6 @@ function App() {
9090
void fetchBugs(apiKey)
9191
}, [hasUrlFilters, apiKey, fetchBugs])
9292

93-
// Sync filters to URL when they change (after initialization)
94-
useEffect(() => {
95-
if (!hasInitializedFilters.current) {
96-
return
97-
}
98-
updateUrl(filters)
99-
}, [filters, updateUrl])
100-
10193
// Handle close modal
10294
const handleCloseModal = useCallback(() => {
10395
setShowApiKeyModal(false)
@@ -142,16 +134,18 @@ function App() {
142134
[setFilters],
143135
)
144136

145-
// Handle apply filters (fetch bugs)
137+
// Handle apply filters (fetch bugs and update URL)
146138
const handleApplyFilters = useCallback(() => {
147139
if (!apiKey) {
148140
addToast('error', 'Please enter your API key first! 🔑')
149141
return
150142
}
143+
// Update URL with current filters
144+
updateUrl(filters)
151145
fetchBugs(apiKey).catch(() => {
152146
// Error is handled in the store
153147
})
154-
}, [apiKey, fetchBugs, addToast])
148+
}, [apiKey, fetchBugs, addToast, updateUrl, filters])
155149

156150
// Handle bug move (drag and drop)
157151
const handleBugMove = useCallback(

0 commit comments

Comments
 (0)