mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[Discover] Preserve discover main route state in breadcrumb links (#119838)
* [Discover] preserve discover main state in breadcrumb links * [Discover] change imports * [Discover] fix unit tests * [Discover] fix the case when doc was expanded and then main discover state changed * [Discover] change naming, add unit tests * [Discover] fix linting * [Discover] add functional test * [Discover] fix tests * [Discover] try fix tests * [Discover] use css selector manually * [Discover] fix functional * [Discover] apply backticks suggestion * [Discover] return before statement, try to run separately * [Discover] unskip test Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
b8189ed107
commit
ad6c22dfc3
13 changed files with 303 additions and 135 deletions
|
@ -97,4 +97,5 @@ export const discoverServiceMock = {
|
|||
storage: {
|
||||
get: jest.fn(),
|
||||
},
|
||||
addBasePath: jest.fn(),
|
||||
} as unknown as DiscoverServices;
|
||||
|
|
|
@ -15,6 +15,7 @@ import { ContextApp } from './context_app';
|
|||
import { getRootBreadcrumbs } from '../../utils/breadcrumbs';
|
||||
import { LoadingIndicator } from '../../components/common/loading_indicator';
|
||||
import { useIndexPattern } from '../../utils/use_index_pattern';
|
||||
import { useMainRouteBreadcrumb } from '../../utils/use_navigation_props';
|
||||
|
||||
export interface ContextAppProps {
|
||||
/**
|
||||
|
@ -33,17 +34,18 @@ export function ContextAppRoute(props: ContextAppProps) {
|
|||
const { chrome } = services;
|
||||
|
||||
const { indexPatternId, id } = useParams<ContextUrlParams>();
|
||||
const breadcrumb = useMainRouteBreadcrumb();
|
||||
|
||||
useEffect(() => {
|
||||
chrome.setBreadcrumbs([
|
||||
...getRootBreadcrumbs(),
|
||||
...getRootBreadcrumbs(breadcrumb),
|
||||
{
|
||||
text: i18n.translate('discover.context.breadcrumb', {
|
||||
defaultMessage: 'Surrounding documents',
|
||||
}),
|
||||
},
|
||||
]);
|
||||
}, [chrome]);
|
||||
}, [chrome, breadcrumb]);
|
||||
|
||||
const { indexPattern, error } = useIndexPattern(services.indexPatterns, indexPatternId);
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ import { getRootBreadcrumbs } from '../../utils/breadcrumbs';
|
|||
import { Doc } from './components/doc';
|
||||
import { LoadingIndicator } from '../../components/common/loading_indicator';
|
||||
import { useIndexPattern } from '../../utils/use_index_pattern';
|
||||
import { useMainRouteBreadcrumb } from '../../utils/use_navigation_props';
|
||||
|
||||
export interface SingleDocRouteProps {
|
||||
/**
|
||||
|
@ -36,18 +37,19 @@ export function SingleDocRoute(props: SingleDocRouteProps) {
|
|||
const { chrome, timefilter } = services;
|
||||
|
||||
const { indexPatternId, index } = useParams<DocUrlParams>();
|
||||
const breadcrumb = useMainRouteBreadcrumb();
|
||||
|
||||
const query = useQuery();
|
||||
const docId = query.get('id') || '';
|
||||
|
||||
useEffect(() => {
|
||||
chrome.setBreadcrumbs([
|
||||
...getRootBreadcrumbs(),
|
||||
...getRootBreadcrumbs(breadcrumb),
|
||||
{
|
||||
text: `${index}#${docId}`,
|
||||
},
|
||||
]);
|
||||
}, [chrome, index, docId]);
|
||||
}, [chrome, index, docId, breadcrumb]);
|
||||
|
||||
useEffect(() => {
|
||||
timefilter.disableAutoRefreshSelector();
|
||||
|
|
|
@ -27,8 +27,7 @@ import {
|
|||
import { DocViewer } from '../../services/doc_views/components/doc_viewer/doc_viewer';
|
||||
import { DocViewFilterFn } from '../../services/doc_views/doc_views_types';
|
||||
import { DiscoverServices } from '../../build_services';
|
||||
import { getContextUrl } from '../../utils/get_context_url';
|
||||
import { getSingleDocUrl } from '../../utils/get_single_doc_url';
|
||||
import { useNavigationProps } from '../../utils/use_navigation_props';
|
||||
import { ElasticSearchHit } from '../../types';
|
||||
|
||||
interface Props {
|
||||
|
@ -103,6 +102,15 @@ export function DiscoverGridFlyout({
|
|||
[activePage, setPage]
|
||||
);
|
||||
|
||||
const { singleDocProps, surrDocsProps } = useNavigationProps({
|
||||
indexPatternId: indexPattern.id!,
|
||||
rowIndex: hit._index,
|
||||
rowId: hit._id,
|
||||
filterManager: services.filterManager,
|
||||
addBasePath: services.addBasePath,
|
||||
columns,
|
||||
});
|
||||
|
||||
return (
|
||||
<EuiPortal>
|
||||
<EuiFlyout
|
||||
|
@ -143,8 +151,8 @@ export function DiscoverGridFlyout({
|
|||
size="xs"
|
||||
iconType="document"
|
||||
flush="left"
|
||||
href={services.addBasePath(getSingleDocUrl(indexPattern.id!, hit._index, hit._id))}
|
||||
data-test-subj="docTableRowAction"
|
||||
{...singleDocProps}
|
||||
>
|
||||
{i18n.translate('discover.grid.tableRow.viewSingleDocumentLinkTextSimple', {
|
||||
defaultMessage: 'Single document',
|
||||
|
@ -157,13 +165,7 @@ export function DiscoverGridFlyout({
|
|||
size="xs"
|
||||
iconType="documents"
|
||||
flush="left"
|
||||
href={getContextUrl(
|
||||
String(hit._id),
|
||||
indexPattern.id,
|
||||
columns,
|
||||
services.filterManager,
|
||||
services.addBasePath
|
||||
)}
|
||||
{...surrDocsProps}
|
||||
data-test-subj="docTableRowAction"
|
||||
>
|
||||
{i18n.translate('discover.grid.tableRow.viewSurroundingDocumentsLinkTextSimple', {
|
||||
|
|
|
@ -15,12 +15,11 @@ import { flattenHit } from '../../../../../data/common';
|
|||
import { DocViewer } from '../../../services/doc_views/components/doc_viewer/doc_viewer';
|
||||
import { FilterManager, IndexPattern } from '../../../../../data/public';
|
||||
import { TableCell } from './table_row/table_cell';
|
||||
import { DocViewFilterFn } from '../../../services/doc_views/doc_views_types';
|
||||
import { getContextUrl } from '../../../utils/get_context_url';
|
||||
import { getSingleDocUrl } from '../../../utils/get_single_doc_url';
|
||||
import { TableRowDetails } from './table_row_details';
|
||||
import { formatRow, formatTopLevelObject } from '../lib/row_formatter';
|
||||
import { useNavigationProps } from '../../../utils/use_navigation_props';
|
||||
import { DocViewFilterFn } from '../../../services/doc_views/doc_views_types';
|
||||
import { ElasticSearchHit } from '../../../types';
|
||||
import { TableRowDetails } from './table_row_details';
|
||||
|
||||
export type DocTableRow = ElasticSearchHit & {
|
||||
isAnchor?: boolean;
|
||||
|
@ -100,13 +99,14 @@ export const TableRow = ({
|
|||
[filter, flattenedRow, indexPattern.fields]
|
||||
);
|
||||
|
||||
const getContextAppHref = () => {
|
||||
return getContextUrl(row._id, indexPattern.id!, columns, filterManager, addBasePath);
|
||||
};
|
||||
|
||||
const getSingleDocHref = () => {
|
||||
return addBasePath(getSingleDocUrl(indexPattern.id!, row._index, row._id));
|
||||
};
|
||||
const { singleDocProps, surrDocsProps } = useNavigationProps({
|
||||
indexPatternId: indexPattern.id!,
|
||||
rowIndex: row._index,
|
||||
rowId: row._id,
|
||||
filterManager,
|
||||
addBasePath,
|
||||
columns,
|
||||
});
|
||||
|
||||
const rowCells = [
|
||||
<td className="kbnDocTableCell__toggleDetails" key="toggleDetailsCell">
|
||||
|
@ -208,8 +208,8 @@ export const TableRow = ({
|
|||
open={open}
|
||||
colLength={(columns.length || 1) + 2}
|
||||
isTimeBased={indexPattern.isTimeBased()}
|
||||
getContextAppHref={getContextAppHref}
|
||||
getSingleDocHref={getSingleDocHref}
|
||||
singleDocProps={singleDocProps}
|
||||
surrDocsProps={surrDocsProps}
|
||||
>
|
||||
<DocViewer
|
||||
columns={columns}
|
||||
|
|
|
@ -9,12 +9,13 @@
|
|||
import React from 'react';
|
||||
import { EuiFlexGroup, EuiFlexItem, EuiIcon, EuiLink, EuiTitle } from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { DiscoverNavigationProps } from '../../../utils/use_navigation_props';
|
||||
interface TableRowDetailsProps {
|
||||
open: boolean;
|
||||
colLength: number;
|
||||
isTimeBased: boolean;
|
||||
getContextAppHref: () => string;
|
||||
getSingleDocHref: () => string;
|
||||
singleDocProps: DiscoverNavigationProps;
|
||||
surrDocsProps: DiscoverNavigationProps;
|
||||
children: JSX.Element;
|
||||
}
|
||||
|
||||
|
@ -22,8 +23,8 @@ export const TableRowDetails = ({
|
|||
open,
|
||||
colLength,
|
||||
isTimeBased,
|
||||
getContextAppHref,
|
||||
getSingleDocHref,
|
||||
singleDocProps,
|
||||
surrDocsProps,
|
||||
children,
|
||||
}: TableRowDetailsProps) => {
|
||||
if (!open) {
|
||||
|
@ -54,7 +55,7 @@ export const TableRowDetails = ({
|
|||
<EuiFlexGroup gutterSize="l" alignItems="center" responsive={false}>
|
||||
<EuiFlexItem grow={false}>
|
||||
{isTimeBased && (
|
||||
<EuiLink data-test-subj="docTableRowAction" href={getContextAppHref()}>
|
||||
<EuiLink data-test-subj="docTableRowAction" {...surrDocsProps}>
|
||||
<FormattedMessage
|
||||
id="discover.docTable.tableRow.viewSurroundingDocumentsLinkText"
|
||||
defaultMessage="View surrounding documents"
|
||||
|
@ -63,7 +64,7 @@ export const TableRowDetails = ({
|
|||
)}
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiLink data-test-subj="docTableRowAction" href={getSingleDocHref()}>
|
||||
<EuiLink data-test-subj="docTableRowAction" {...singleDocProps}>
|
||||
<FormattedMessage
|
||||
id="discover.docTable.tableRow.viewSingleDocumentLinkText"
|
||||
defaultMessage="View single document"
|
||||
|
|
|
@ -10,13 +10,13 @@ import { ChromeStart } from 'kibana/public';
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import { SavedSearch } from '../services/saved_searches';
|
||||
|
||||
export function getRootBreadcrumbs() {
|
||||
export function getRootBreadcrumbs(breadcrumb?: string) {
|
||||
return [
|
||||
{
|
||||
text: i18n.translate('discover.rootBreadcrumb', {
|
||||
defaultMessage: 'Discover',
|
||||
}),
|
||||
href: '#/',
|
||||
href: breadcrumb || '#/',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
|
|
@ -1,43 +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 { getContextUrl } from './get_context_url';
|
||||
import { FilterManager } from '../../../data/public/query/filter_manager';
|
||||
const filterManager = {
|
||||
getGlobalFilters: () => [],
|
||||
getAppFilters: () => [],
|
||||
} as unknown as FilterManager;
|
||||
const addBasePath = (path: string) => `/base${path}`;
|
||||
|
||||
describe('Get context url', () => {
|
||||
test('returning a valid context url', async () => {
|
||||
const url = await getContextUrl(
|
||||
'docId',
|
||||
'ipId',
|
||||
['test1', 'test2'],
|
||||
filterManager,
|
||||
addBasePath
|
||||
);
|
||||
expect(url).toMatchInlineSnapshot(
|
||||
`"/base/app/discover#/context/ipId/docId?_g=(filters:!())&_a=(columns:!(test1,test2),filters:!())"`
|
||||
);
|
||||
});
|
||||
|
||||
test('returning a valid context url when docId contains whitespace', async () => {
|
||||
const url = await getContextUrl(
|
||||
'doc Id',
|
||||
'ipId',
|
||||
['test1', 'test2'],
|
||||
filterManager,
|
||||
addBasePath
|
||||
);
|
||||
expect(url).toMatchInlineSnapshot(
|
||||
`"/base/app/discover#/context/ipId/doc%20Id?_g=(filters:!())&_a=(columns:!(test1,test2),filters:!())"`
|
||||
);
|
||||
});
|
||||
});
|
|
@ -1,46 +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 { stringify } from 'query-string';
|
||||
import rison from 'rison-node';
|
||||
import { url } from '../../../kibana_utils/common';
|
||||
import { esFilters, FilterManager } from '../../../data/public';
|
||||
import { DiscoverServices } from '../build_services';
|
||||
|
||||
/**
|
||||
* Helper function to generate an URL to a document in Discover's context view
|
||||
*/
|
||||
export function getContextUrl(
|
||||
documentId: string,
|
||||
indexPatternId: string,
|
||||
columns: string[],
|
||||
filterManager: FilterManager,
|
||||
addBasePath: DiscoverServices['addBasePath']
|
||||
) {
|
||||
const globalFilters = filterManager.getGlobalFilters();
|
||||
const appFilters = filterManager.getAppFilters();
|
||||
|
||||
const hash = stringify(
|
||||
url.encodeQuery({
|
||||
_g: rison.encode({
|
||||
filters: globalFilters || [],
|
||||
}),
|
||||
_a: rison.encode({
|
||||
columns,
|
||||
filters: (appFilters || []).map(esFilters.disableFilter),
|
||||
}),
|
||||
}),
|
||||
{ encode: false, sort: false }
|
||||
);
|
||||
|
||||
return addBasePath(
|
||||
`/app/discover#/context/${encodeURIComponent(indexPatternId)}/${encodeURIComponent(
|
||||
documentId
|
||||
)}?${hash}`
|
||||
);
|
||||
}
|
|
@ -1,11 +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.
|
||||
*/
|
||||
|
||||
export const getSingleDocUrl = (indexPatternId: string, rowIndex: string, rowId: string) => {
|
||||
return `/app/discover#/doc/${indexPatternId}/${rowIndex}?id=${encodeURIComponent(rowId)}`;
|
||||
};
|
101
src/plugins/discover/public/utils/use_navigation_props.test.tsx
Normal file
101
src/plugins/discover/public/utils/use_navigation_props.test.tsx
Normal file
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
* 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 React, { ReactElement } from 'react';
|
||||
import { renderHook } from '@testing-library/react-hooks';
|
||||
import { createFilterManagerMock } from '../../../data/public/query/filter_manager/filter_manager.mock';
|
||||
import {
|
||||
getContextHash,
|
||||
HistoryState,
|
||||
useNavigationProps,
|
||||
UseNavigationProps,
|
||||
} from './use_navigation_props';
|
||||
import { Router } from 'react-router-dom';
|
||||
import { createMemoryHistory } from 'history';
|
||||
import { setServices } from '../kibana_services';
|
||||
import { DiscoverServices } from '../build_services';
|
||||
|
||||
const filterManager = createFilterManagerMock();
|
||||
const defaultProps = {
|
||||
indexPatternId: 'ff959d40-b880-11e8-a6d9-e546fe2bba5f',
|
||||
rowIndex: 'kibana_sample_data_ecommerce',
|
||||
rowId: 'QmsYdX0BQ6gV8MTfoPYE',
|
||||
columns: ['customer_first_name', 'products.manufacturer'],
|
||||
filterManager,
|
||||
addBasePath: jest.fn(),
|
||||
} as UseNavigationProps;
|
||||
const basePathPrefix = 'localhost:5601/xqj';
|
||||
|
||||
const getSearch = () => {
|
||||
return `?_g=(filters:!(),refreshInterval:(pause:!t,value:0),time:(from:now-15m,to:now))
|
||||
&_a=(columns:!(${defaultProps.columns.join()}),filters:!(),index:${defaultProps.indexPatternId}
|
||||
,interval:auto,query:(language:kuery,query:''),sort:!(!(order_date,desc)))`;
|
||||
};
|
||||
|
||||
const getSingeDocRoute = () => {
|
||||
return `/doc/${defaultProps.indexPatternId}/${defaultProps.rowIndex}`;
|
||||
};
|
||||
|
||||
const getContextRoute = () => {
|
||||
return `/context/${defaultProps.indexPatternId}/${defaultProps.rowId}`;
|
||||
};
|
||||
|
||||
const render = () => {
|
||||
const history = createMemoryHistory<HistoryState>({
|
||||
initialEntries: ['/' + getSearch()],
|
||||
});
|
||||
setServices({ history: () => history } as unknown as DiscoverServices);
|
||||
const wrapper = ({ children }: { children: ReactElement }) => (
|
||||
<Router history={history}>{children}</Router>
|
||||
);
|
||||
return {
|
||||
result: renderHook(() => useNavigationProps(defaultProps), { wrapper }).result,
|
||||
history,
|
||||
};
|
||||
};
|
||||
|
||||
describe('useNavigationProps', () => {
|
||||
test('should provide valid breadcrumb for single doc page from main view', () => {
|
||||
const { result, history } = render();
|
||||
|
||||
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()}`);
|
||||
});
|
||||
|
||||
test('should provide valid breadcrumb for context page from main view', () => {
|
||||
const { result, history } = render();
|
||||
|
||||
result.current.surrDocsProps.onClick?.();
|
||||
expect(history.location.pathname).toEqual(getContextRoute());
|
||||
expect(history.location.search).toEqual(
|
||||
`?${getContextHash(defaultProps.columns, filterManager)}`
|
||||
);
|
||||
expect(history.location.state?.breadcrumb).toEqual(`#/${getSearch()}`);
|
||||
});
|
||||
|
||||
test('should create valid links to the context and single doc pages from embeddable', () => {
|
||||
const { result } = renderHook(() =>
|
||||
useNavigationProps({
|
||||
...defaultProps,
|
||||
addBasePath: (val: string) => `${basePathPrefix}${val}`,
|
||||
})
|
||||
);
|
||||
|
||||
expect(result.current.singleDocProps.href!).toEqual(
|
||||
`${basePathPrefix}/app/discover#${getSingeDocRoute()}?id=${defaultProps.rowId}`
|
||||
);
|
||||
expect(result.current.surrDocsProps.href!).toEqual(
|
||||
`${basePathPrefix}/app/discover#${getContextRoute()}?${getContextHash(
|
||||
defaultProps.columns,
|
||||
filterManager
|
||||
)}`
|
||||
);
|
||||
});
|
||||
});
|
132
src/plugins/discover/public/utils/use_navigation_props.tsx
Normal file
132
src/plugins/discover/public/utils/use_navigation_props.tsx
Normal file
|
@ -0,0 +1,132 @@
|
|||
/*
|
||||
* 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 { useMemo, useRef } from 'react';
|
||||
import { useHistory, matchPath } from 'react-router-dom';
|
||||
import { stringify } from 'query-string';
|
||||
import rison from 'rison-node';
|
||||
import { esFilters, FilterManager } from '../../../data/public';
|
||||
import { url } from '../../../kibana_utils/common';
|
||||
import { getServices } from '../kibana_services';
|
||||
|
||||
export type DiscoverNavigationProps = { onClick: () => void } | { href: string };
|
||||
|
||||
export interface UseNavigationProps {
|
||||
indexPatternId: string;
|
||||
rowIndex: string;
|
||||
rowId: string;
|
||||
columns: string[];
|
||||
filterManager: FilterManager;
|
||||
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();
|
||||
|
||||
const hash = stringify(
|
||||
url.encodeQuery({
|
||||
_g: rison.encode({
|
||||
filters: globalFilters || [],
|
||||
}),
|
||||
_a: rison.encode({
|
||||
columns,
|
||||
filters: (appFilters || []).map(esFilters.disableFilter),
|
||||
}),
|
||||
}),
|
||||
{ encode: false, sort: false }
|
||||
);
|
||||
|
||||
return hash;
|
||||
};
|
||||
|
||||
/**
|
||||
* When it's context route, breadcrumb link should point to the main discover page anyway.
|
||||
* Otherwise, we are on main page and should create breadcrumb link from it.
|
||||
* Current history object should be used in callback, since url state might be changed
|
||||
* after expanded document opened.
|
||||
*/
|
||||
const getCurrentBreadcrumbs = (isContextRoute: boolean, prevBreadcrumb?: string) => {
|
||||
const { history: getHistory } = getServices();
|
||||
const currentHistory = getHistory();
|
||||
return isContextRoute
|
||||
? prevBreadcrumb
|
||||
: '#' + currentHistory?.location.pathname + currentHistory?.location.search;
|
||||
};
|
||||
|
||||
export const useMainRouteBreadcrumb = () => {
|
||||
// useRef needed to retrieve initial breadcrumb link from the push state without updates
|
||||
return useRef(useHistory<HistoryState>().location.state?.breadcrumb).current;
|
||||
};
|
||||
|
||||
export const useNavigationProps = ({
|
||||
indexPatternId,
|
||||
rowIndex,
|
||||
rowId,
|
||||
columns,
|
||||
filterManager,
|
||||
addBasePath,
|
||||
}: UseNavigationProps) => {
|
||||
const history = useHistory<HistoryState>();
|
||||
const prevBreadcrumb = useRef(history?.location.state?.breadcrumb).current;
|
||||
const contextSearchHash = useMemo(
|
||||
() => getContextHash(columns, filterManager),
|
||||
[columns, filterManager]
|
||||
);
|
||||
|
||||
/**
|
||||
* When history can be accessed via hooks,
|
||||
* it is discover main or context route.
|
||||
*/
|
||||
if (!!history) {
|
||||
const isContextRoute = matchPath(history.location.pathname, {
|
||||
path: '/context/:indexPatternId/:id',
|
||||
exact: true,
|
||||
});
|
||||
|
||||
const onOpenSingleDoc = () => {
|
||||
history.push({
|
||||
pathname: `/doc/${indexPatternId}/${rowIndex}`,
|
||||
search: `?id=${encodeURIComponent(rowId)}`,
|
||||
state: { breadcrumb: getCurrentBreadcrumbs(!!isContextRoute, prevBreadcrumb) },
|
||||
});
|
||||
};
|
||||
|
||||
const onOpenSurrDocs = () =>
|
||||
history.push({
|
||||
pathname: `/context/${encodeURIComponent(indexPatternId)}/${encodeURIComponent(
|
||||
String(rowId)
|
||||
)}`,
|
||||
search: `?${contextSearchHash}`,
|
||||
state: { breadcrumb: getCurrentBreadcrumbs(!!isContextRoute, prevBreadcrumb) },
|
||||
});
|
||||
|
||||
return {
|
||||
singleDocProps: { onClick: onOpenSingleDoc },
|
||||
surrDocsProps: { onClick: onOpenSurrDocs },
|
||||
};
|
||||
}
|
||||
|
||||
// for embeddable absolute href should be kept
|
||||
return {
|
||||
singleDocProps: {
|
||||
href: addBasePath(
|
||||
`/app/discover#/doc/${indexPatternId}/${rowIndex}?id=${encodeURIComponent(rowId)}`
|
||||
),
|
||||
},
|
||||
surrDocsProps: {
|
||||
href: addBasePath(
|
||||
`/app/discover#/context/${encodeURIComponent(indexPatternId)}/${encodeURIComponent(
|
||||
rowId
|
||||
)}?${contextSearchHash}`
|
||||
),
|
||||
},
|
||||
};
|
||||
};
|
|
@ -6,6 +6,7 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import expect from '@kbn/expect';
|
||||
import { FtrProviderContext } from '../../ftr_provider_context';
|
||||
|
||||
const TEST_FILTER_COLUMN_NAMES = [
|
||||
|
@ -22,6 +23,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
const docTable = getService('docTable');
|
||||
const PageObjects = getPageObjects(['common', 'context', 'discover', 'timePicker']);
|
||||
const kibanaServer = getService('kibanaServer');
|
||||
const filterBar = getService('filterBar');
|
||||
const find = getService('find');
|
||||
|
||||
describe('discover - context - back navigation', function contextSize() {
|
||||
before(async function () {
|
||||
|
@ -56,5 +59,29 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
return initialHitCount === hitCount;
|
||||
});
|
||||
});
|
||||
|
||||
it('should go back via breadcrumbs with preserved state', async function () {
|
||||
await retry.waitFor(
|
||||
'user navigating to context and returning to discover via breadcrumbs',
|
||||
async () => {
|
||||
await docTable.clickRowToggle({ rowIndex: 0 });
|
||||
const rowActions = await docTable.getRowActions({ rowIndex: 0 });
|
||||
await rowActions[0].click();
|
||||
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;
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue