[Security Solution] Fix missing hash in sync to url (#166847)

## Summary

This PR fixes the root cause for
https://github.com/elastic/kibana/issues/166686 and
https://github.com/elastic/kibana/issues/166774

@angorayc @machadoum 


### Checklist

Delete any items that are not applicable to this PR.

- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
This commit is contained in:
Luke G 2023-09-22 10:23:50 +02:00 committed by GitHub
parent 3ff82f2c17
commit d52c5a15fd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 45 additions and 10 deletions

View file

@ -23,6 +23,7 @@ describe('useSyncToUrl', () => {
window.location = {
...originalLocation,
search: '',
hash: '',
};
window.history = {
...originalHistory,
@ -65,9 +66,30 @@ describe('useSyncToUrl', () => {
);
});
it('should should not alter the location hash', () => {
const key = 'testKey';
const valueToSerialize = { test: 'value' };
window.location.hash = '#should_be_there';
const { result } = renderHook(() => useSyncToUrl(key, jest.fn()));
act(() => {
result.current(valueToSerialize);
});
expect(window.history.replaceState).toHaveBeenCalledWith(
{ path: expect.any(String) },
'',
'/#should_be_there?testKey=%28test%3Avalue%29'
);
});
it('should clear the value from the query string on unmount', () => {
const key = 'testKey';
// Location should have a key to clear
window.location.search = `?${key}=${encode({ test: 'value' })}`;
const { unmount } = renderHook(() => useSyncToUrl(key, jest.fn()));
act(() => {
@ -85,6 +107,9 @@ describe('useSyncToUrl', () => {
const key = 'testKey';
const restore = jest.fn();
// Location should have a key to clear
window.location.search = `?${key}=${encode({ test: 'value' })}`;
renderHook(() => useSyncToUrl(key, restore, true));
act(() => {
@ -92,10 +117,6 @@ describe('useSyncToUrl', () => {
});
expect(window.history.replaceState).toHaveBeenCalledTimes(1);
expect(window.history.replaceState).toHaveBeenCalledWith(
{ path: expect.any(String) },
'',
'/?'
);
expect(window.history.replaceState).toHaveBeenCalledWith({ path: expect.any(String) }, '', '/');
});
});

View file

@ -55,10 +55,15 @@ export const useSyncToUrl = <TValueToSerialize>(
searchParams.delete(key);
}
const newSearch = searchParams.toString();
const stringifiedSearchParams = searchParams.toString();
const newSearch = stringifiedSearchParams.length > 0 ? `?${stringifiedSearchParams}` : '';
if (window.location.search === newSearch) {
return;
}
// Update query string without unnecessary re-render
const newUrl = `${window.location.pathname}?${newSearch}`;
const newUrl = `${window.location.pathname}${window.location.hash}${newSearch}`;
window.history.replaceState({ path: newUrl }, '', newUrl);
},
[key]

View file

@ -22,9 +22,18 @@ type FlyoutState = Parameters<ExpandableFlyoutApi['openFlyout']>[0];
export const useSyncFlyoutStateWithUrl = () => {
const flyoutApi = useRef<ExpandableFlyoutApi>(null);
const syncStateToUrl = useSyncToUrl<FlyoutState>(FLYOUT_URL_PARAM, (data) => {
flyoutApi.current?.openFlyout(data);
});
const handleRestoreFlyout = useCallback(
(state?: FlyoutState) => {
if (!state) {
return;
}
flyoutApi.current?.openFlyout(state);
},
[flyoutApi]
);
const syncStateToUrl = useSyncToUrl<FlyoutState>(FLYOUT_URL_PARAM, handleRestoreFlyout);
// This should be bound to flyout changed and closed events.
// When flyout is closed, url state is cleared