Skip to content

Commit b59fb97

Browse files
[Security Solution] Update use_url_state to work with new side nav (#132518)
* Fix landing pages browser tab title * Fix new navigation url state * Fix unit tests Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
1 parent 693b3e8 commit b59fb97

3 files changed

Lines changed: 197 additions & 6 deletions

File tree

x-pack/plugins/security_solution/public/common/components/url_state/index.test.tsx

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@ import { UrlStateContainerPropTypes } from './types';
2525
import { useUrlStateHooks } from './use_url_state';
2626
import { waitFor } from '@testing-library/react';
2727
import { useLocation } from 'react-router-dom';
28+
import { updateAppLinks } from '../../links';
29+
import { getAppLinks } from '../../links/app_links';
30+
import { StartPlugins } from '../../../types';
31+
import { allowedExperimentalValues } from '../../../../common/experimental_features';
32+
import { coreMock } from '@kbn/core/public/mocks';
2833

2934
let mockProps: UrlStateContainerPropTypes;
3035

@@ -78,10 +83,36 @@ jest.mock('react-router-dom', () => {
7883
};
7984
});
8085

86+
const mockedUseIsGroupedNavigationEnabled = jest.fn();
87+
jest.mock('../navigation/helpers', () => ({
88+
useIsGroupedNavigationEnabled: () => mockedUseIsGroupedNavigationEnabled(),
89+
}));
90+
8191
describe('UrlStateContainer', () => {
92+
beforeAll(async () => {
93+
mockedUseIsGroupedNavigationEnabled.mockReturnValue(false);
94+
95+
const appLinks = await getAppLinks(coreMock.createStart(), {} as StartPlugins);
96+
updateAppLinks(appLinks, {
97+
experimentalFeatures: allowedExperimentalValues,
98+
capabilities: {
99+
navLinks: {},
100+
management: {},
101+
catalogue: {},
102+
actions: { show: true, crud: true },
103+
siem: {
104+
show: true,
105+
crud: true,
106+
},
107+
},
108+
});
109+
});
110+
82111
afterEach(() => {
83112
jest.clearAllMocks();
113+
mockedUseIsGroupedNavigationEnabled.mockReturnValue(false);
84114
});
115+
85116
describe('handleInitialize', () => {
86117
describe('URL state updates redux', () => {
87118
describe('relative timerange actions are called with correct data on component mount', () => {
@@ -226,6 +257,44 @@ describe('UrlStateContainer', () => {
226257
expect(mockHistory.replace).not.toHaveBeenCalled();
227258
});
228259

260+
it("it doesn't update URL state when on admin page and grouped nav disabled", () => {
261+
mockedUseIsGroupedNavigationEnabled.mockReturnValue(false);
262+
mockProps = getMockPropsObj({
263+
page: CONSTANTS.unknown,
264+
examplePath: '/administration',
265+
namespaceLower: 'administration',
266+
pageName: SecurityPageName.administration,
267+
detailName: undefined,
268+
}).noSearch.undefinedQuery;
269+
270+
(useLocation as jest.Mock).mockReturnValue({
271+
pathname: mockProps.pathName,
272+
});
273+
274+
mount(<HookWrapper hookProps={mockProps} hook={(args) => useUrlStateHooks(args)} />);
275+
276+
expect(mockHistory.replace.mock.calls[0][0].search).toBe('?');
277+
});
278+
279+
it("it doesn't update URL state when on admin page and grouped nav enabled", () => {
280+
mockedUseIsGroupedNavigationEnabled.mockReturnValue(true);
281+
mockProps = getMockPropsObj({
282+
page: CONSTANTS.unknown,
283+
examplePath: '/dashboards',
284+
namespaceLower: 'dashboards',
285+
pageName: SecurityPageName.dashboardsLanding,
286+
detailName: undefined,
287+
}).noSearch.undefinedQuery;
288+
289+
(useLocation as jest.Mock).mockReturnValue({
290+
pathname: mockProps.pathName,
291+
});
292+
293+
mount(<HookWrapper hookProps={mockProps} hook={(args) => useUrlStateHooks(args)} />);
294+
295+
expect(mockHistory.replace.mock.calls[0][0].search).toBe('?');
296+
});
297+
229298
it('it removes empty AppQuery state from URL', () => {
230299
mockProps = {
231300
...getMockProps(

x-pack/plugins/security_solution/public/common/components/url_state/index_mocked.test.tsx

Lines changed: 109 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,12 @@ import { getFilterQuery, getMockPropsObj, mockHistory, testCases } from './test_
1616
import { UrlStateContainerPropTypes } from './types';
1717
import { useUrlStateHooks } from './use_url_state';
1818
import { useLocation } from 'react-router-dom';
19-
import { MANAGEMENT_PATH } from '../../../../common/constants';
19+
import { DASHBOARDS_PATH, MANAGEMENT_PATH } from '../../../../common/constants';
20+
import { getAppLinks } from '../../links/app_links';
21+
import { StartPlugins } from '../../../types';
22+
import { updateAppLinks } from '../../links';
23+
import { allowedExperimentalValues } from '../../../../common/experimental_features';
24+
import { coreMock } from '@kbn/core/public/mocks';
2025

2126
let mockProps: UrlStateContainerPropTypes;
2227

@@ -45,7 +50,31 @@ jest.mock('react-redux', () => {
4550
};
4651
});
4752

53+
const mockedUseIsGroupedNavigationEnabled = jest.fn();
54+
jest.mock('../navigation/helpers', () => ({
55+
useIsGroupedNavigationEnabled: () => mockedUseIsGroupedNavigationEnabled(),
56+
}));
57+
4858
describe('UrlStateContainer - lodash.throttle mocked to test update url', () => {
59+
beforeAll(async () => {
60+
mockedUseIsGroupedNavigationEnabled.mockReturnValue(false);
61+
62+
const appLinks = await getAppLinks(coreMock.createStart(), {} as StartPlugins);
63+
updateAppLinks(appLinks, {
64+
experimentalFeatures: allowedExperimentalValues,
65+
capabilities: {
66+
navLinks: {},
67+
management: {},
68+
catalogue: {},
69+
actions: { show: true, crud: true },
70+
siem: {
71+
show: true,
72+
crud: true,
73+
},
74+
},
75+
});
76+
});
77+
4978
afterEach(() => {
5079
jest.clearAllMocks();
5180
jest.resetAllMocks();
@@ -210,7 +239,8 @@ describe('UrlStateContainer - lodash.throttle mocked to test update url', () =>
210239
});
211240
});
212241

213-
test("administration page doesn't has query string", () => {
242+
test("administration page doesn't has query string when grouped nav disabled", () => {
243+
mockedUseIsGroupedNavigationEnabled.mockReturnValue(false);
214244
mockProps = getMockPropsObj({
215245
page: CONSTANTS.networkPage,
216246
examplePath: '/network',
@@ -285,6 +315,83 @@ describe('UrlStateContainer - lodash.throttle mocked to test update url', () =>
285315
state: '',
286316
});
287317
});
318+
319+
test("dashboards page doesn't has query string when grouped nav enabled", () => {
320+
mockedUseIsGroupedNavigationEnabled.mockReturnValue(true);
321+
mockProps = getMockPropsObj({
322+
page: CONSTANTS.networkPage,
323+
examplePath: '/network',
324+
namespaceLower: 'network',
325+
pageName: SecurityPageName.network,
326+
detailName: undefined,
327+
}).noSearch.definedQuery;
328+
329+
const urlState = {
330+
...mockProps.urlState,
331+
[CONSTANTS.appQuery]: getFilterQuery(),
332+
[CONSTANTS.timerange]: {
333+
global: {
334+
[CONSTANTS.timerange]: {
335+
from: '2020-07-07T08:20:18.966Z',
336+
fromStr: 'now-24h',
337+
kind: 'relative',
338+
to: '2020-07-08T08:20:18.966Z',
339+
toStr: 'now',
340+
},
341+
linkTo: ['timeline'],
342+
},
343+
timeline: {
344+
[CONSTANTS.timerange]: {
345+
from: '2020-07-07T08:20:18.966Z',
346+
fromStr: 'now-24h',
347+
kind: 'relative',
348+
to: '2020-07-08T08:20:18.966Z',
349+
toStr: 'now',
350+
},
351+
linkTo: ['global'],
352+
},
353+
},
354+
};
355+
356+
const updatedMockProps = {
357+
...getMockPropsObj({
358+
...mockProps,
359+
page: CONSTANTS.unknown,
360+
examplePath: DASHBOARDS_PATH,
361+
namespaceLower: 'dashboards',
362+
pageName: SecurityPageName.dashboardsLanding,
363+
detailName: undefined,
364+
}).noSearch.definedQuery,
365+
urlState,
366+
};
367+
368+
(useLocation as jest.Mock).mockReturnValue({
369+
pathname: mockProps.pathName,
370+
});
371+
372+
const wrapper = mount(
373+
<HookWrapper
374+
hookProps={{ ...mockProps, urlState }}
375+
hook={(args) => useUrlStateHooks(args)}
376+
/>
377+
);
378+
379+
(useLocation as jest.Mock).mockReturnValue({
380+
pathname: updatedMockProps.pathName,
381+
});
382+
383+
wrapper.setProps({
384+
hookProps: updatedMockProps,
385+
});
386+
387+
wrapper.update();
388+
expect(mockHistory.replace.mock.calls[1][0]).toStrictEqual({
389+
hash: '',
390+
pathname: DASHBOARDS_PATH,
391+
search: '?',
392+
state: '',
393+
});
394+
});
288395
});
289396

290397
describe('handleInitialize', () => {

x-pack/plugins/security_solution/public/common/components/url_state/use_url_state.tsx

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ import {
4040
import { TimelineUrl } from '../../../timelines/store/timeline/model';
4141
import { UrlInputsModel } from '../../store/inputs/model';
4242
import { queryTimelineByIdOnUrlChange } from './query_timeline_by_id_on_url_change';
43+
import { getLinkInfo } from '../../links';
44+
import { SecurityPageName } from '../../../app/types';
45+
import { useIsGroupedNavigationEnabled } from '../navigation/helpers';
4346

4447
function usePrevious(value: PreviousLocationUrlState) {
4548
const ref = useRef<PreviousLocationUrlState>(value);
@@ -62,17 +65,20 @@ export const useUrlStateHooks = ({
6265
const { filterManager, savedQueries } = useKibana().services.data.query;
6366
const { pathname: browserPathName } = useLocation();
6467
const prevProps = usePrevious({ pathName, pageName, urlState, search });
68+
const isGroupedNavEnabled = useIsGroupedNavigationEnabled();
6569

70+
const linkInfo = pageName ? getLinkInfo(pageName as SecurityPageName) : undefined;
6671
const { setInitialStateFromUrl, updateTimeline, updateTimelineIsLoading } =
6772
useSetInitialStateFromUrl();
6873

6974
const handleInitialize = useCallback(
7075
(type: UrlStateType) => {
7176
const urlStateUpdatesToStore: UrlStateToRedux[] = [];
7277
const urlStateUpdatesToLocation: ReplaceStateInLocation[] = [];
78+
const skipUrlState = isGroupedNavEnabled ? linkInfo?.skipUrlState : isAdministration(type);
7379

7480
// Delete all query strings from URL when the page is security/administration (Manage menu group)
75-
if (isAdministration(type)) {
81+
if (skipUrlState) {
7682
ALL_URL_STATE_KEYS.forEach((urlKey: KeyUrlState) => {
7783
urlStateUpdatesToLocation.push({
7884
urlStateToReplace: '',
@@ -146,6 +152,8 @@ export const useUrlStateHooks = ({
146152
setInitialStateFromUrl,
147153
urlState,
148154
isFirstPageLoad,
155+
isGroupedNavEnabled,
156+
linkInfo?.skipUrlState,
149157
]
150158
);
151159

@@ -159,8 +167,9 @@ export const useUrlStateHooks = ({
159167
if (browserPathName !== pathName) return;
160168

161169
const type: UrlStateType = getUrlType(pageName);
170+
const skipUrlState = isGroupedNavEnabled ? linkInfo?.skipUrlState : isAdministration(type);
162171

163-
if (!deepEqual(urlState, prevProps.urlState) && !isFirstPageLoad && !isAdministration(type)) {
172+
if (!deepEqual(urlState, prevProps.urlState) && !isFirstPageLoad && !skipUrlState) {
164173
const urlStateUpdatesToLocation: ReplaceStateInLocation[] = ALL_URL_STATE_KEYS.map(
165174
(urlKey: KeyUrlState) => ({
166175
urlStateToReplace: getUrlStateKeyValue(urlState, urlKey),
@@ -186,11 +195,17 @@ export const useUrlStateHooks = ({
186195
browserPathName,
187196
handleInitialize,
188197
search,
198+
isGroupedNavEnabled,
199+
linkInfo?.skipUrlState,
189200
]);
190201

191202
useEffect(() => {
192-
document.title = `${getTitle(pageName, navTabs)} - Kibana`;
193-
}, [pageName, navTabs]);
203+
if (!isGroupedNavEnabled) {
204+
document.title = `${getTitle(pageName, navTabs)} - Kibana`;
205+
} else {
206+
document.title = `${linkInfo?.title ?? ''} - Kibana`;
207+
}
208+
}, [pageName, navTabs, isGroupedNavEnabled, linkInfo]);
194209

195210
useEffect(() => {
196211
queryTimelineByIdOnUrlChange({

0 commit comments

Comments
 (0)