mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[Security Solution] Remove kbn-url-state package (#176122)
## Summary This PR removes unused kbn url state package. It was only used in the flyout and is now replaced with a shared utility, used by several other modules. --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
d3a2221ae4
commit
81ccdc2ac5
11 changed files with 0 additions and 300 deletions
1
.github/CODEOWNERS
vendored
1
.github/CODEOWNERS
vendored
|
@ -844,7 +844,6 @@ x-pack/plugins/upgrade_assistant @elastic/platform-deployment-management
|
|||
x-pack/plugins/uptime @elastic/obs-ux-infra_services-team
|
||||
x-pack/plugins/drilldowns/url_drilldown @elastic/appex-sharedux
|
||||
src/plugins/url_forwarding @elastic/kibana-visualizations
|
||||
packages/kbn-url-state @elastic/security-threat-hunting-investigations
|
||||
src/plugins/usage_collection @elastic/kibana-core
|
||||
test/plugin_functional/plugins/usage_collection @elastic/kibana-core
|
||||
packages/kbn-use-tracked-promise @elastic/obs-ux-logs-team
|
||||
|
|
|
@ -832,7 +832,6 @@
|
|||
"@kbn/uptime-plugin": "link:x-pack/plugins/uptime",
|
||||
"@kbn/url-drilldown-plugin": "link:x-pack/plugins/drilldowns/url_drilldown",
|
||||
"@kbn/url-forwarding-plugin": "link:src/plugins/url_forwarding",
|
||||
"@kbn/url-state": "link:packages/kbn-url-state",
|
||||
"@kbn/usage-collection-plugin": "link:src/plugins/usage_collection",
|
||||
"@kbn/usage-collection-test-plugin": "link:test/plugin_functional/plugins/usage_collection",
|
||||
"@kbn/use-tracked-promise": "link:packages/kbn-use-tracked-promise",
|
||||
|
|
|
@ -1,33 +0,0 @@
|
|||
# @kbn/url-state - utils for syncing state to URL
|
||||
|
||||
This package provides a React hook called `useUrlState` that can be used to synchronize state to the URL. This can be useful when you want to make a portion of state shareable.
|
||||
|
||||
The state is grouped under a namespace, to avoid collisions. See the example url below for how it would look like.
|
||||
|
||||
### Example usage:
|
||||
|
||||
```
|
||||
import React, { useState } from 'react';
|
||||
import { useUrlState } from '@kbn/url-state';
|
||||
|
||||
function MyComponent() {
|
||||
const [name, setName] = useUrlState<number>('namespace','name');
|
||||
|
||||
const handleClick = () => {
|
||||
setName('John Doe')
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<p>Name: {name}</p>
|
||||
<button onClick={handleClick}>Set name</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
The resulting URL will look like this:
|
||||
|
||||
```
|
||||
http://localhost:5601/?namespace=(name:John%20Doe)
|
||||
```
|
|
@ -1,113 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { renderHook, act } from '@testing-library/react-hooks';
|
||||
import { useUrlState } from '.';
|
||||
|
||||
describe('useSyncToUrl', () => {
|
||||
let originalLocation: Location;
|
||||
let originalHistory: History;
|
||||
|
||||
beforeEach(() => {
|
||||
originalLocation = window.location;
|
||||
originalHistory = window.history;
|
||||
delete (window as any).location;
|
||||
delete (window as any).history;
|
||||
|
||||
window.location = {
|
||||
...originalLocation,
|
||||
search: '',
|
||||
hash: '',
|
||||
};
|
||||
window.history = {
|
||||
...originalHistory,
|
||||
replaceState: jest.fn(),
|
||||
pushState: jest.fn(),
|
||||
};
|
||||
|
||||
jest.useFakeTimers();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
window.location = originalLocation;
|
||||
window.history = originalHistory;
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
it('should update the URL when the state changes', () => {
|
||||
window.location.hash = '#should_be_there';
|
||||
|
||||
const { result } = renderHook(() => useUrlState('namespace', 'test'));
|
||||
|
||||
act(() => {
|
||||
result.current[1]('foo');
|
||||
jest.runAllTimers();
|
||||
});
|
||||
|
||||
expect(window.history.pushState).toHaveBeenCalledWith(
|
||||
{},
|
||||
'',
|
||||
'#should_be_there?namespace=(test%3Afoo)'
|
||||
);
|
||||
});
|
||||
|
||||
it('should escape values correctly', () => {
|
||||
window.location.hash = '#should_be_there';
|
||||
|
||||
const { result } = renderHook(() => useUrlState('namespace', 'test'));
|
||||
|
||||
act(() => {
|
||||
result.current[1]('foo#bar');
|
||||
jest.runAllTimers();
|
||||
});
|
||||
|
||||
expect(window.history.pushState).toHaveBeenCalledWith(
|
||||
{},
|
||||
'',
|
||||
'#should_be_there?namespace=(test%3Afoo%23bar)'
|
||||
);
|
||||
});
|
||||
|
||||
it('should remove the key from the namespace after undefined is passed (state clear mechanism)', () => {
|
||||
window.location.hash = '#should_be_there';
|
||||
|
||||
const { result } = renderHook(() => useUrlState('namespace', 'test'));
|
||||
|
||||
act(() => {
|
||||
result.current[1](undefined);
|
||||
jest.runAllTimers();
|
||||
});
|
||||
|
||||
expect(window.history.pushState).toHaveBeenCalledWith({}, '', '#should_be_there?namespace=()');
|
||||
});
|
||||
|
||||
it('should restore the value from the query string on mount', () => {
|
||||
window.location.search = `?namespace=(test:foo)`;
|
||||
|
||||
const {
|
||||
result: { current: state },
|
||||
} = renderHook(() => useUrlState('namespace', 'test'));
|
||||
|
||||
expect(state[0]).toEqual('foo');
|
||||
});
|
||||
|
||||
it('should return updated state on browser navigation', () => {
|
||||
window.location.search = '?namespace=(test:foo)';
|
||||
|
||||
const { result } = renderHook(() => useUrlState('namespace', 'test'));
|
||||
|
||||
expect(result.current[0]).toEqual('foo');
|
||||
|
||||
act(() => {
|
||||
window.location.search = '?namespace=(test:bar)';
|
||||
window.dispatchEvent(new CustomEvent('popstate'));
|
||||
});
|
||||
|
||||
expect(result.current[0]).toEqual('bar');
|
||||
});
|
||||
});
|
|
@ -1,101 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { encode, decode, RisonValue } from '@kbn/rison';
|
||||
import { stringify, parse } from 'query-string';
|
||||
import { merge } from 'lodash';
|
||||
|
||||
const CUSTOM_URL_EVENT = 'url:update' as const;
|
||||
|
||||
// This is a list of events that can trigger a render.
|
||||
const URL_CHANGE_EVENTS: string[] = ['popstate', CUSTOM_URL_EVENT];
|
||||
|
||||
/**
|
||||
* This hook stores state in the URL, but with a namespace to avoid collisions with other values in the URL.
|
||||
* It also batches updates to the URL to avoid excessive history entries.
|
||||
* With it, you can store state in the URL and have it persist across page refreshes.
|
||||
* The state is stored in the URL as a Rison encoded object.
|
||||
*
|
||||
* Example: when called like this `const [value, setValue] = useUrlState<boolean>('myNamespace', 'myKey');`
|
||||
* the state will be stored in the URL like this: `?myNamespace=(myKey:!n)`
|
||||
*
|
||||
* State is not cleared from the URL when the hook is unmounted and this is by design.
|
||||
* If you want it to be cleared, you can do it manually by calling `setValue(undefined)`.
|
||||
*
|
||||
* @param urlNamespace actual top level query param key
|
||||
* @param key sub key of the query param
|
||||
*/
|
||||
export const useUrlState = <T = unknown>(urlNamespace: string, key: string) => {
|
||||
const [internalValue, setInternalValue] = useState<T | undefined>(undefined);
|
||||
|
||||
useEffect(() => {
|
||||
// This listener is called on browser navigation or on custom event.
|
||||
// It updates the LOCAL state, allowing dependent components to re-render.
|
||||
const listener = () => {
|
||||
const searchParams = new URLSearchParams(window.location.search);
|
||||
const param = searchParams.get(urlNamespace);
|
||||
|
||||
const decodedState = param ? decode(param) : ({} as Record<string, RisonValue>);
|
||||
const decodedValue = (decodedState as Record<string, RisonValue> | undefined)?.[key];
|
||||
setInternalValue(decodedValue as unknown as T);
|
||||
};
|
||||
|
||||
listener();
|
||||
|
||||
URL_CHANGE_EVENTS.forEach((event) => window.addEventListener(event, listener));
|
||||
|
||||
return () => URL_CHANGE_EVENTS.forEach((event) => window.removeEventListener(event, listener));
|
||||
}, [key, urlNamespace]);
|
||||
|
||||
const setValue = useCallback(
|
||||
(newValue: T | undefined) => {
|
||||
const queryParams = parse(location.search) as any;
|
||||
const currentNsValue = (
|
||||
queryParams?.[urlNamespace] ? decode(queryParams?.[urlNamespace]) : {}
|
||||
) as any;
|
||||
|
||||
const currentValue = currentNsValue?.[key];
|
||||
|
||||
const canSpread =
|
||||
typeof newValue === 'object' &&
|
||||
typeof currentValue === 'object' &&
|
||||
!Array.isArray(newValue) &&
|
||||
!Array.isArray(currentValue);
|
||||
|
||||
const upatedValueToStoreAtKey = canSpread
|
||||
? (merge(currentValue, newValue) as unknown as T)
|
||||
: (newValue as unknown as T);
|
||||
|
||||
if (upatedValueToStoreAtKey) {
|
||||
currentNsValue[key] = upatedValueToStoreAtKey;
|
||||
} else {
|
||||
delete currentNsValue[key];
|
||||
}
|
||||
|
||||
queryParams[urlNamespace] = encodeURIComponent(encode(currentNsValue));
|
||||
|
||||
// NOTE: don't re-encode the entire url params string
|
||||
const newSearch = stringify(queryParams, { encode: false });
|
||||
|
||||
if (window.location.search === newSearch) {
|
||||
return;
|
||||
}
|
||||
|
||||
const newUrl = `${window.location.hash}?${newSearch}`;
|
||||
|
||||
window.history.pushState({}, '', newUrl);
|
||||
// This custom event is used to notify other instances
|
||||
// of this hook that the URL has changed.
|
||||
window.dispatchEvent(new Event(CUSTOM_URL_EVENT));
|
||||
},
|
||||
[key, urlNamespace]
|
||||
);
|
||||
|
||||
return [internalValue, setValue] as const;
|
||||
};
|
|
@ -1,13 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
preset: '@kbn/test',
|
||||
rootDir: '../..',
|
||||
roots: ['<rootDir>/packages/kbn-url-state'],
|
||||
};
|
|
@ -1,5 +0,0 @@
|
|||
{
|
||||
"type": "shared-common",
|
||||
"id": "@kbn/url-state",
|
||||
"owner": "@elastic/security-threat-hunting-investigations"
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
{
|
||||
"name": "@kbn/url-state",
|
||||
"private": true,
|
||||
"version": "1.0.0",
|
||||
"license": "SSPL-1.0 OR Elastic License 2.0"
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "target/types",
|
||||
"types": [
|
||||
"jest",
|
||||
"node",
|
||||
"react"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
],
|
||||
"exclude": [
|
||||
"target/**/*"
|
||||
],
|
||||
"kbn_references": [
|
||||
"@kbn/rison",
|
||||
]
|
||||
}
|
|
@ -1682,8 +1682,6 @@
|
|||
"@kbn/url-drilldown-plugin/*": ["x-pack/plugins/drilldowns/url_drilldown/*"],
|
||||
"@kbn/url-forwarding-plugin": ["src/plugins/url_forwarding"],
|
||||
"@kbn/url-forwarding-plugin/*": ["src/plugins/url_forwarding/*"],
|
||||
"@kbn/url-state": ["packages/kbn-url-state"],
|
||||
"@kbn/url-state/*": ["packages/kbn-url-state/*"],
|
||||
"@kbn/usage-collection-plugin": ["src/plugins/usage_collection"],
|
||||
"@kbn/usage-collection-plugin/*": ["src/plugins/usage_collection/*"],
|
||||
"@kbn/usage-collection-test-plugin": ["test/plugin_functional/plugins/usage_collection"],
|
||||
|
|
|
@ -6428,10 +6428,6 @@
|
|||
version "0.0.0"
|
||||
uid ""
|
||||
|
||||
"@kbn/url-state@link:packages/kbn-url-state":
|
||||
version "0.0.0"
|
||||
uid ""
|
||||
|
||||
"@kbn/usage-collection-plugin@link:src/plugins/usage_collection":
|
||||
version "0.0.0"
|
||||
uid ""
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue