[Discover] Fix issue where Discover breadcrumb loses context after page refresh or when opening in a new tab (#136749)

This commit is contained in:
Davis McPhee 2022-07-22 16:17:15 -03:00 committed by GitHub
parent 20de3fb673
commit 90091baede
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 57 additions and 35 deletions

View file

@ -9,12 +9,7 @@
import React, { ReactElement } from 'react';
import { renderHook } from '@testing-library/react-hooks';
import { createFilterManagerMock } from '@kbn/data-plugin/public/query/filter_manager/filter_manager.mock';
import {
getContextHash,
HistoryState,
useNavigationProps,
UseNavigationProps,
} from './use_navigation_props';
import { getContextHash, useNavigationProps, UseNavigationProps } from './use_navigation_props';
import { Router } from 'react-router-dom';
import { createMemoryHistory } from 'history';
import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public';
@ -45,7 +40,7 @@ const getContextRoute = () => {
};
const render = (withRouter = true, props?: Partial<UseNavigationProps>) => {
const history = createMemoryHistory<HistoryState>({
const history = createMemoryHistory({
initialEntries: ['/' + getSearch()],
});
const wrapper = ({ children }: { children: ReactElement }) => (
@ -66,8 +61,9 @@ describe('useNavigationProps', () => {
// @ts-expect-error
result.current.singleDocProps.onClick();
expect(history.location.pathname).toEqual(getSingeDocRoute());
expect(history.location.search).toEqual(`?id=${defaultProps.rowId}`);
expect(history.location.state?.breadcrumb).toEqual(`#/${getSearch()}`);
expect(history.location.search).toEqual(
`?id=${defaultProps.rowId}&breadcrumb=${encodeURIComponent(`#/${getSearch()}`)}`
);
});
test('should provide valid breadcrumb for context page from main view', () => {
@ -77,9 +73,10 @@ describe('useNavigationProps', () => {
result.current.surrDocsProps.onClick();
expect(history.location.pathname).toEqual(getContextRoute());
expect(history.location.search).toEqual(
`?${getContextHash(defaultProps.columns, filterManager)}`
`?${getContextHash(defaultProps.columns, filterManager)}&breadcrumb=${encodeURIComponent(
`#/${getSearch()}`
)}`
);
expect(history.location.state?.breadcrumb).toEqual(`#/${getSearch()}`);
});
test('should create valid links to the context and single doc pages from embeddable', () => {

View file

@ -6,7 +6,7 @@
* Side Public License, v 1.
*/
import { MouseEventHandler, useMemo, useRef } from 'react';
import { MouseEventHandler, useMemo } from 'react';
import { useHistory, matchPath } from 'react-router-dom';
import type { Location } from 'history';
import { stringify } from 'query-string';
@ -27,8 +27,6 @@ export interface UseNavigationProps {
addBasePath: (url: string) => string;
}
export type HistoryState = { breadcrumb?: string } | undefined;
export const getContextHash = (columns: string[], filterManager: FilterManager) => {
const globalFilters = filterManager.getGlobalFilters();
const appFilters = filterManager.getAppFilters();
@ -64,16 +62,12 @@ const getCurrentBreadcrumbs = (
return isContextRoute ? prevBreadcrumb : '#' + currentLocation.pathname + currentLocation.search;
};
const getCurrentBreadcrumb = (search: string | undefined) =>
new URLSearchParams(search).get('breadcrumb') || undefined;
export const useMainRouteBreadcrumb = () => {
// useRef needed to retrieve initial breadcrumb link from the push state without updates
const breadcrumb = useRef<string>();
const history = useHistory<HistoryState>();
if (history.location.state?.breadcrumb) {
breadcrumb.current = history.location.state.breadcrumb;
}
return breadcrumb.current;
const history = useHistory();
return useMemo(() => getCurrentBreadcrumb(history.location.search), [history.location.search]);
};
export const useNavigationProps = ({
@ -84,10 +78,13 @@ export const useNavigationProps = ({
filterManager,
addBasePath,
}: UseNavigationProps) => {
const history = useHistory<HistoryState>();
const history = useHistory();
const currentLocation = useDiscoverServices().history().location;
const prevBreadcrumb = useRef(history?.location.state?.breadcrumb).current;
const prevBreadcrumb = useMemo(
() => getCurrentBreadcrumb(history?.location?.search),
[history?.location?.search]
);
const contextSearchHash = useMemo(
() => getContextHash(columns, filterManager),
[columns, filterManager]
@ -111,16 +108,16 @@ export const useNavigationProps = ({
path: '/context/:indexPatternId/:id',
exact: true,
});
const currentBreadcrumb = encodeURIComponent(
getCurrentBreadcrumbs(!!isContextRoute, currentLocation, prevBreadcrumb) ?? ''
);
const onOpenSingleDoc: MouseEventHandler<HTMLAnchorElement> = (event) => {
event?.preventDefault?.();
history.push({
pathname: `/doc/${indexPatternId}/${rowIndex}`,
search: `?id=${encodeURIComponent(rowId)}`,
state: {
breadcrumb: getCurrentBreadcrumbs(!!isContextRoute, currentLocation, prevBreadcrumb),
},
search: `?id=${encodeURIComponent(rowId)}&breadcrumb=${currentBreadcrumb}`,
});
};
@ -131,16 +128,19 @@ export const useNavigationProps = ({
pathname: `/context/${encodeURIComponent(indexPatternId)}/${encodeURIComponent(
String(rowId)
)}`,
search: `?${contextSearchHash}`,
state: {
breadcrumb: getCurrentBreadcrumbs(!!isContextRoute, currentLocation, prevBreadcrumb),
},
search: `?${contextSearchHash}&breadcrumb=${currentBreadcrumb}`,
});
};
return {
singleDocProps: { onClick: onOpenSingleDoc, href: singleDocHref },
surrDocsProps: { onClick: onOpenSurrDocs, href: surDocsHref },
singleDocProps: {
onClick: onOpenSingleDoc,
href: `${singleDocHref}&breadcrumb=${currentBreadcrumb}`,
},
surrDocsProps: {
onClick: onOpenSurrDocs,
href: `${surDocsHref}&breadcrumb=${currentBreadcrumb}`,
},
};
}

View file

@ -86,5 +86,30 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
}
);
});
it('should go back via breadcrumbs with preserved state after a page refresh', async function () {
await retry.waitFor(
'user navigating to context and returning to discover via breadcrumbs',
async () => {
await dataGrid.clickRowToggle({ rowIndex: 0 });
const rowActions = await dataGrid.getRowActions({ rowIndex: 0 });
await rowActions[1].click();
await PageObjects.context.waitUntilContextLoadingHasFinished();
await browser.refresh();
await PageObjects.context.waitUntilContextLoadingHasFinished();
await find.clickByCssSelector(`[data-test-subj="breadcrumb first"]`);
await PageObjects.discover.waitForDocTableLoadingComplete();
for (const [columnName, value] of TEST_FILTER_COLUMN_NAMES) {
expect(await filterBar.hasFilter(columnName, value)).to.eql(true);
}
expect(await PageObjects.timePicker.getTimeConfigAsAbsoluteTimes()).to.eql({
start: 'Sep 18, 2015 @ 06:31:44.000',
end: 'Sep 23, 2015 @ 18:31:44.000',
});
return true;
}
);
});
});
}