Skip to content

Commit a60cdfe

Browse files
committed
Remove view defaults logic from useQueryState hook
1 parent 0a5108d commit a60cdfe

File tree

9 files changed

+25
-964
lines changed

9 files changed

+25
-964
lines changed

ui/packages/shared/components/src/hooks/URLState/index.test.tsx

Lines changed: 0 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -647,121 +647,4 @@ describe('URLState Hooks', () => {
647647
consoleSpy.mockRestore();
648648
});
649649
});
650-
651-
describe('mergeStrategy option', () => {
652-
it('should replace existing value by default', async () => {
653-
const {result} = renderHook(() => useURLState('param'), {wrapper: createWrapper()});
654-
const [, setParam] = result.current;
655-
656-
act(() => {
657-
setParam('initial');
658-
});
659-
await waitFor(() => {
660-
expect(result.current[0]).toBe('initial');
661-
});
662-
663-
act(() => {
664-
setParam('replaced');
665-
});
666-
await waitFor(() => {
667-
expect(result.current[0]).toBe('replaced');
668-
});
669-
});
670-
671-
it('should only set value when empty with preserve-existing strategy', async () => {
672-
const {result} = renderHook(
673-
() => useURLState('param', {mergeStrategy: 'preserve-existing'}),
674-
{wrapper: createWrapper()}
675-
);
676-
const [, setParam] = result.current;
677-
678-
// Set when undefined - should work
679-
act(() => {
680-
setParam('first');
681-
});
682-
await waitFor(() => {
683-
expect(result.current[0]).toBe('first');
684-
});
685-
686-
// Try to overwrite - should be ignored
687-
act(() => {
688-
setParam('second');
689-
});
690-
await waitFor(() => {
691-
expect(result.current[0]).toBe('first');
692-
});
693-
});
694-
695-
it('should merge arrays with deduplication using append strategy', async () => {
696-
const {result} = renderHook(
697-
() => useURLState<string | string[]>('param', {mergeStrategy: 'append'}),
698-
{wrapper: createWrapper()}
699-
);
700-
const [, setParam] = result.current;
701-
702-
// Set initial array
703-
act(() => {
704-
setParam(['a', 'b']);
705-
});
706-
await waitFor(() => {
707-
expect(result.current[0]).toEqual(['a', 'b']);
708-
});
709-
710-
// Append with overlap - should deduplicate
711-
act(() => {
712-
setParam(['b', 'c']);
713-
});
714-
await waitFor(() => {
715-
expect(result.current[0]).toEqual(['a', 'b', 'c']);
716-
});
717-
718-
// Append string to array
719-
act(() => {
720-
setParam('d');
721-
});
722-
await waitFor(() => {
723-
expect(result.current[0]).toEqual(['a', 'b', 'c', 'd']);
724-
});
725-
726-
// Append duplicate string - should be ignored
727-
act(() => {
728-
setParam('a');
729-
});
730-
await waitFor(() => {
731-
expect(result.current[0]).toEqual(['a', 'b', 'c', 'd']);
732-
});
733-
});
734-
735-
it('should return undefined and no-op setter when enabled is false', async () => {
736-
const {result} = renderHook(() => useURLState('param', {enabled: false}), {
737-
wrapper: createWrapper(),
738-
});
739-
740-
const [value, setParam] = result.current;
741-
expect(value).toBeUndefined();
742-
743-
act(() => {
744-
setParam('should-not-work');
745-
});
746-
await waitFor(() => {
747-
expect(mockNavigateTo).not.toHaveBeenCalled();
748-
});
749-
});
750-
751-
it('should handle compare mode with enabled option', async () => {
752-
const TestComponent = (): {
753-
groupByA: string | string[] | undefined;
754-
groupByB: string | string[] | undefined;
755-
} => {
756-
const [groupByA] = useURLState('group_by', {enabled: true, defaultValue: ['node']});
757-
const [groupByB] = useURLState('group_by', {enabled: false});
758-
return {groupByA, groupByB};
759-
};
760-
761-
const {result} = renderHook(() => TestComponent(), {wrapper: createWrapper()});
762-
763-
expect(result.current.groupByA).toEqual(['node']);
764-
expect(result.current.groupByB).toBeUndefined();
765-
});
766-
});
767650
});

ui/packages/shared/components/src/hooks/URLState/index.tsx

Lines changed: 3 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -210,8 +210,6 @@ interface Options {
210210
defaultValue?: string | string[];
211211
debugLog?: boolean;
212212
alwaysReturnArray?: boolean;
213-
mergeStrategy?: 'replace' | 'append' | 'preserve-existing';
214-
enabled?: boolean;
215213
}
216214

217215
export const useURLState = <T extends ParamValue>(
@@ -223,13 +221,10 @@ export const useURLState = <T extends ParamValue>(
223221
throw new Error('useURLState must be used within a URLStateProvider');
224222
}
225223

226-
const {debugLog, defaultValue, alwaysReturnArray, mergeStrategy, enabled} = _options ?? {};
224+
const {debugLog, defaultValue, alwaysReturnArray} = _options ?? {};
227225

228226
const {state, setState} = context;
229227

230-
// Create no-op setter unconditionally to satisfy hooks rules
231-
const noOpSetter = useCallback(() => {}, []);
232-
233228
const setParam: ParamValueSetter = useCallback(
234229
(val: ParamValue) => {
235230
if (debugLog === true) {
@@ -238,57 +233,13 @@ export const useURLState = <T extends ParamValue>(
238233

239234
// Just update state - Provider handles URL sync automatically!
240235
setState(currentState => {
241-
const currentValue = currentState[param];
242-
let newValue: ParamValue;
243-
244-
if (mergeStrategy === undefined || mergeStrategy === 'replace') {
245-
newValue = val;
246-
} else if (mergeStrategy === 'preserve-existing') {
247-
// Only set if current is empty (including empty string)
248-
if (
249-
currentValue === undefined ||
250-
currentValue === null ||
251-
currentValue === '' ||
252-
(Array.isArray(currentValue) && currentValue.length === 0)
253-
) {
254-
newValue = val;
255-
} else {
256-
newValue = currentValue; // Keep existing
257-
}
258-
} else if (mergeStrategy === 'append') {
259-
// Ignore undefined/null new values - keep current state
260-
if (val === undefined || val === null) {
261-
newValue = currentValue;
262-
} else if (currentValue === undefined || currentValue === null || currentValue === '') {
263-
// Current is empty, use new value
264-
newValue = val;
265-
} else if (Array.isArray(currentValue) && Array.isArray(val)) {
266-
// Merge arrays and deduplicate
267-
newValue = Array.from(new Set([...currentValue, ...val]));
268-
} else if (Array.isArray(currentValue) && typeof val === 'string') {
269-
// Add string to array if not present (deduplication)
270-
newValue = currentValue.includes(val) ? currentValue : [...currentValue, val];
271-
} else if (typeof currentValue === 'string' && Array.isArray(val)) {
272-
// Merge string with array and deduplicate
273-
newValue = Array.from(new Set([currentValue, ...val]));
274-
} else if (typeof currentValue === 'string' && typeof val === 'string') {
275-
// Create array from both strings (deduplicate)
276-
newValue = currentValue === val ? currentValue : [currentValue, val];
277-
} else {
278-
// Fallback to replace for other cases
279-
newValue = val;
280-
}
281-
} else {
282-
newValue = val;
283-
}
284-
285236
return {
286237
...currentState,
287-
[param]: newValue,
238+
[param]: val,
288239
};
289240
});
290241
},
291-
[param, setState, debugLog, mergeStrategy]
242+
[param, setState, debugLog]
292243
);
293244

294245
if (debugLog === true) {
@@ -344,11 +295,6 @@ export const useURLState = <T extends ParamValue>(
344295
}
345296
}
346297

347-
// Return early if hook is disabled (after all hooks have been called)
348-
if (enabled === false) {
349-
return [undefined as T, noOpSetter];
350-
}
351-
352298
return [(value ?? defaultValue) as T, setParam];
353299
};
354300

ui/packages/shared/profile/src/ProfileSelector/index.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -124,9 +124,7 @@ const ProfileSelector = ({
124124
const batchUpdates = useURLStateBatch();
125125

126126
const profileFilterDefaults = viewComponent?.profileFilterDefaults as ProfileFilter[] | undefined;
127-
const {forceApplyFilters} = useProfileFilters({
128-
viewDefaults: profileFilterDefaults,
129-
});
127+
const {forceApplyFilters} = useProfileFilters();
130128

131129
const handleProfileTypeChange = useCallback(() => {
132130
if (profileFilterDefaults != null && profileFilterDefaults.length > 0) {

ui/packages/shared/profile/src/ProfileView/components/ProfileFilters/useProfileFilters.ts

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -225,13 +225,7 @@ export const convertToProtoFilters = (profileFilters: ProfileFilter[]): Filter[]
225225
});
226226
};
227227

228-
interface UseProfileFiltersOptions {
229-
viewDefaults?: ProfileFilter[];
230-
}
231-
232-
export const useProfileFilters = (
233-
options: UseProfileFiltersOptions = {}
234-
): {
228+
export const useProfileFilters = (): {
235229
localFilters: ProfileFilter[];
236230
appliedFilters: ProfileFilter[];
237231
protoFilters: Filter[];
@@ -243,14 +237,10 @@ export const useProfileFilters = (
243237
removeFilter: (id: string) => void;
244238
updateFilter: (id: string, updates: Partial<ProfileFilter>) => void;
245239
resetFilters: () => void;
246-
applyViewDefaults: () => void;
240+
setAppliedFilters: (filters: ProfileFilter[]) => void;
247241
forceApplyFilters: (filters: ProfileFilter[]) => void;
248242
} => {
249-
const {viewDefaults} = options;
250-
const {appliedFilters, setAppliedFilters, applyViewDefaults, forceApplyFilters} =
251-
useProfileFiltersUrlState({
252-
viewDefaults,
253-
});
243+
const {appliedFilters, setAppliedFilters, forceApplyFilters} = useProfileFiltersUrlState();
254244
const resetFlameGraphState = useResetFlameGraphState();
255245

256246
const [localFilters, setLocalFilters] = useState<ProfileFilter[]>(appliedFilters ?? []);
@@ -434,7 +424,7 @@ export const useProfileFilters = (
434424
removeFilter,
435425
updateFilter,
436426
resetFilters,
437-
applyViewDefaults,
427+
setAppliedFilters,
438428
forceApplyFilters,
439429
};
440430
};

0 commit comments

Comments
 (0)