mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
[Discover] Add EUIDataGrid to surrounding documents (#99447)
* [Discover] migrate remaining context files from js to ts * [Discover] get rid of any types * [Discover] replace constants with enums, update imports * [Discover] use unknown instead of any, correct types * [Discover] skip any type for tests * [Discover] add euiDataGrid view * [Discover] add support dataGrid columns, provide ability to do not change sorting, highlight anchor doc, rename legacy variables * [Discover] update context_legacy test and types * [Discover] update unit tests, add context header * [Discover] update unit and functional tests * [Discover] remove docTable from context test which uses new data grid * [Discover] update EsHitRecord type, use it for context app. add no pagination support * [Discover] resolve type error in test * [Discover] add disabling control columns option, change loading feedback * [Discover] clean up, update functional tests * [Discover] remove invalid translations * [Discover] support both no results found and loading feedback * [Discover] provide loading status for discover * [Discover] fix functional test * [Discover] add useDataGridColumns test, update by comments * [Discover] fix types Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
1168e11639
commit
69883de634
40 changed files with 599 additions and 225 deletions
|
@ -7,7 +7,7 @@
|
|||
*/
|
||||
|
||||
import { IUiSettingsClient } from 'kibana/public';
|
||||
import { DEFAULT_COLUMNS_SETTING, SAMPLE_SIZE_SETTING } from '../../common';
|
||||
import { DEFAULT_COLUMNS_SETTING, DOC_TABLE_LEGACY, SAMPLE_SIZE_SETTING } from '../../common';
|
||||
|
||||
export const uiSettingsMock = ({
|
||||
get: (key: string) => {
|
||||
|
@ -15,6 +15,8 @@ export const uiSettingsMock = ({
|
|||
return 10;
|
||||
} else if (key === DEFAULT_COLUMNS_SETTING) {
|
||||
return ['default_column'];
|
||||
} else if (key === DOC_TABLE_LEGACY) {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
} as unknown) as IUiSettingsClient;
|
||||
|
|
|
@ -2,8 +2,9 @@
|
|||
anchor-id="contextAppRoute.anchorId"
|
||||
columns="contextAppRoute.state.columns"
|
||||
index-pattern="contextAppRoute.indexPattern"
|
||||
app-state="contextAppRoute.state"
|
||||
state-container="contextAppRoute.stateContainer"
|
||||
filters="contextAppRoute.filters"
|
||||
predecessor-count="contextAppRoute.state.predecessorCount"
|
||||
successor-count="contextAppRoute.state.successorCount"
|
||||
sort="contextAppRoute.state.sort"
|
||||
></context-app>
|
||||
sort="contextAppRoute.state.sort"></context-app>
|
|
@ -15,19 +15,12 @@ import { getState } from './context_state';
|
|||
import contextAppRouteTemplate from './context.html';
|
||||
import { getRootBreadcrumbs } from '../helpers/breadcrumbs';
|
||||
|
||||
const k7Breadcrumbs = ($route) => {
|
||||
const { indexPattern } = $route.current.locals;
|
||||
const { id } = $route.current.params;
|
||||
|
||||
const k7Breadcrumbs = () => {
|
||||
return [
|
||||
...getRootBreadcrumbs(),
|
||||
{
|
||||
text: i18n.translate('discover.context.breadcrumb', {
|
||||
defaultMessage: 'Context of {indexPatternTitle}#{docId}',
|
||||
values: {
|
||||
indexPatternTitle: indexPattern.title,
|
||||
docId: id,
|
||||
},
|
||||
defaultMessage: 'Surrounding documents',
|
||||
}),
|
||||
},
|
||||
];
|
||||
|
@ -51,6 +44,14 @@ getAngularModule().config(($routeProvider) => {
|
|||
function ContextAppRouteController($routeParams, $scope, $route) {
|
||||
const filterManager = getServices().filterManager;
|
||||
const indexPattern = $route.current.locals.indexPattern.ip;
|
||||
const stateContainer = getState({
|
||||
defaultStepSize: getServices().uiSettings.get(CONTEXT_DEFAULT_SIZE_SETTING),
|
||||
timeFieldName: indexPattern.timeFieldName,
|
||||
storeInSessionStorage: getServices().uiSettings.get('state:storeInSessionStorage'),
|
||||
history: getServices().history(),
|
||||
toasts: getServices().core.notifications.toasts,
|
||||
uiSettings: getServices().core.uiSettings,
|
||||
});
|
||||
const {
|
||||
startSync: startStateSync,
|
||||
stopSync: stopStateSync,
|
||||
|
@ -59,14 +60,8 @@ function ContextAppRouteController($routeParams, $scope, $route) {
|
|||
setFilters,
|
||||
setAppState,
|
||||
flushToUrl,
|
||||
} = getState({
|
||||
defaultStepSize: getServices().uiSettings.get(CONTEXT_DEFAULT_SIZE_SETTING),
|
||||
timeFieldName: indexPattern.timeFieldName,
|
||||
storeInSessionStorage: getServices().uiSettings.get('state:storeInSessionStorage'),
|
||||
history: getServices().history(),
|
||||
toasts: getServices().core.notifications.toasts,
|
||||
uiSettings: getServices().core.uiSettings,
|
||||
});
|
||||
} = stateContainer;
|
||||
this.stateContainer = stateContainer;
|
||||
this.state = { ...appState.getState() };
|
||||
this.anchorId = $routeParams.id;
|
||||
this.indexPattern = indexPattern;
|
||||
|
|
|
@ -8,22 +8,21 @@
|
|||
|
||||
import { EsQuerySortValue, SortDirection } from '../../../../../../data/public';
|
||||
import { createIndexPatternsStub, createSearchSourceStub } from './_stubs';
|
||||
import { AnchorHitRecord, fetchAnchorProvider } from './anchor';
|
||||
import { fetchAnchorProvider } from './anchor';
|
||||
import { EsHitRecord, EsHitRecordList } from './context';
|
||||
|
||||
describe('context app', function () {
|
||||
let fetchAnchor: (
|
||||
indexPatternId: string,
|
||||
anchorId: string,
|
||||
sort: EsQuerySortValue[]
|
||||
) => Promise<AnchorHitRecord>;
|
||||
) => Promise<EsHitRecord>;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
let searchSourceStub: any;
|
||||
|
||||
describe('function fetchAnchor', function () {
|
||||
beforeEach(() => {
|
||||
searchSourceStub = createSearchSourceStub([
|
||||
{ _id: 'hit1', fields: [], sort: [], _source: {} },
|
||||
]);
|
||||
searchSourceStub = createSearchSourceStub(([{ _id: 'hit1' }] as unknown) as EsHitRecordList);
|
||||
fetchAnchor = fetchAnchorProvider(createIndexPatternsStub(), searchSourceStub);
|
||||
});
|
||||
|
||||
|
@ -139,16 +138,14 @@ describe('context app', function () {
|
|||
{ _doc: SortDirection.desc },
|
||||
]).then((anchorDocument) => {
|
||||
expect(anchorDocument).toHaveProperty('property1', 'value1');
|
||||
expect(anchorDocument).toHaveProperty('$$_isAnchor', true);
|
||||
expect(anchorDocument).toHaveProperty('isAnchor', true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('useNewFields API', () => {
|
||||
beforeEach(() => {
|
||||
searchSourceStub = createSearchSourceStub([
|
||||
{ _id: 'hit1', fields: [], sort: [], _source: {} },
|
||||
]);
|
||||
searchSourceStub = createSearchSourceStub(([{ _id: 'hit1' }] as unknown) as EsHitRecordList);
|
||||
fetchAnchor = fetchAnchorProvider(createIndexPatternsStub(), searchSourceStub, true);
|
||||
});
|
||||
|
||||
|
|
|
@ -16,11 +16,6 @@ import {
|
|||
} from '../../../../../../data/public';
|
||||
import { EsHitRecord } from './context';
|
||||
|
||||
export interface AnchorHitRecord extends EsHitRecord {
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
$$_isAnchor: boolean;
|
||||
}
|
||||
|
||||
export function fetchAnchorProvider(
|
||||
indexPatterns: IndexPatternsContract,
|
||||
searchSource: ISearchSource,
|
||||
|
@ -30,7 +25,7 @@ export function fetchAnchorProvider(
|
|||
indexPatternId: string,
|
||||
anchorId: string,
|
||||
sort: EsQuerySortValue[]
|
||||
): Promise<AnchorHitRecord> {
|
||||
): Promise<EsHitRecord> {
|
||||
const indexPattern = await indexPatterns.get(indexPatternId);
|
||||
searchSource
|
||||
.setParent(undefined)
|
||||
|
@ -66,8 +61,7 @@ export function fetchAnchorProvider(
|
|||
|
||||
return {
|
||||
...get(response, ['hits', 'hits', 0]),
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
$$_isAnchor: true,
|
||||
} as AnchorHitRecord;
|
||||
isAnchor: true,
|
||||
} as EsHitRecord;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ import { get, last } from 'lodash';
|
|||
import { createIndexPatternsStub, createContextSearchSourceStub } from './_stubs';
|
||||
import { EsHitRecordList, fetchContextProvider } from './context';
|
||||
import { setServices, SortDirection } from '../../../../kibana_services';
|
||||
import { AnchorHitRecord } from './anchor';
|
||||
import { EsHitRecord } from './context';
|
||||
import { Query } from '../../../../../../data/public';
|
||||
import { DiscoverServices } from '../../../../build_services';
|
||||
|
||||
|
@ -75,7 +75,7 @@ describe('context app', function () {
|
|||
return fetchContextProvider(createIndexPatternsStub()).fetchSurroundingDocs(
|
||||
'predecessors',
|
||||
indexPatternId,
|
||||
anchor as AnchorHitRecord,
|
||||
anchor as EsHitRecord,
|
||||
timeField,
|
||||
tieBreakerField,
|
||||
sortDir,
|
||||
|
@ -267,7 +267,7 @@ describe('context app', function () {
|
|||
return fetchContextProvider(createIndexPatternsStub(), true).fetchSurroundingDocs(
|
||||
'predecessors',
|
||||
indexPatternId,
|
||||
anchor as AnchorHitRecord,
|
||||
anchor as EsHitRecord,
|
||||
timeField,
|
||||
tieBreakerField,
|
||||
sortDir,
|
||||
|
|
|
@ -13,7 +13,7 @@ import { createIndexPatternsStub, createContextSearchSourceStub } from './_stubs
|
|||
import { setServices, SortDirection } from '../../../../kibana_services';
|
||||
import { Query } from '../../../../../../data/public';
|
||||
import { EsHitRecordList, fetchContextProvider } from './context';
|
||||
import { AnchorHitRecord } from './anchor';
|
||||
import { EsHitRecord } from './context';
|
||||
import { DiscoverServices } from '../../../../build_services';
|
||||
|
||||
const MS_PER_DAY = 24 * 60 * 60 * 1000;
|
||||
|
@ -75,7 +75,7 @@ describe('context app', function () {
|
|||
return fetchContextProvider(createIndexPatternsStub()).fetchSurroundingDocs(
|
||||
'successors',
|
||||
indexPatternId,
|
||||
anchor as AnchorHitRecord,
|
||||
anchor as EsHitRecord,
|
||||
timeField,
|
||||
tieBreakerField,
|
||||
sortDir,
|
||||
|
@ -270,7 +270,7 @@ describe('context app', function () {
|
|||
return fetchContextProvider(createIndexPatternsStub(), true).fetchSurroundingDocs(
|
||||
'successors',
|
||||
indexPatternId,
|
||||
anchor as AnchorHitRecord,
|
||||
anchor as EsHitRecord,
|
||||
timeField,
|
||||
tieBreakerField,
|
||||
sortDir,
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import type { estypes } from '@elastic/elasticsearch';
|
||||
import { Filter, IndexPatternsContract, IndexPattern } from 'src/plugins/data/public';
|
||||
import { reverseSortDir, SortDirection } from './utils/sorting';
|
||||
import { extractNanos, convertIsoToMillis } from './utils/date_conversion';
|
||||
|
@ -14,17 +15,19 @@ import { generateIntervals } from './utils/generate_intervals';
|
|||
import { getEsQuerySearchAfter } from './utils/get_es_query_search_after';
|
||||
import { getEsQuerySort } from './utils/get_es_query_sort';
|
||||
import { getServices } from '../../../../kibana_services';
|
||||
import { AnchorHitRecord } from './anchor';
|
||||
|
||||
export type SurrDocType = 'successors' | 'predecessors';
|
||||
export interface EsHitRecord {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
fields: Record<string, any>;
|
||||
sort: number[];
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
_source: Record<string, any>;
|
||||
_id: string;
|
||||
}
|
||||
export type EsHitRecord = Required<
|
||||
Pick<
|
||||
estypes.SearchResponse['hits']['hits'][number],
|
||||
'_id' | 'fields' | 'sort' | '_index' | '_version'
|
||||
>
|
||||
> & {
|
||||
_source?: Record<string, unknown>;
|
||||
_score?: number;
|
||||
isAnchor?: boolean;
|
||||
};
|
||||
|
||||
export type EsHitRecordList = EsHitRecord[];
|
||||
|
||||
const DAY_MILLIS = 24 * 60 * 60 * 1000;
|
||||
|
@ -53,7 +56,7 @@ function fetchContextProvider(indexPatterns: IndexPatternsContract, useNewFields
|
|||
async function fetchSurroundingDocs(
|
||||
type: SurrDocType,
|
||||
indexPatternId: string,
|
||||
anchor: AnchorHitRecord,
|
||||
anchor: EsHitRecord,
|
||||
timeField: string,
|
||||
tieBreakerField: string,
|
||||
sortDir: SortDirection,
|
||||
|
@ -71,7 +74,7 @@ function fetchContextProvider(indexPatterns: IndexPatternsContract, useNewFields
|
|||
const timeValueMillis =
|
||||
nanos !== '' ? convertIsoToMillis(anchor.fields[timeField][0]) : anchor.sort[0];
|
||||
|
||||
const intervals = generateIntervals(LOOKUP_OFFSETS, timeValueMillis, type, sortDir);
|
||||
const intervals = generateIntervals(LOOKUP_OFFSETS, timeValueMillis as number, type, sortDir);
|
||||
let documents: EsHitRecordList = [];
|
||||
|
||||
for (const interval of intervals) {
|
||||
|
|
|
@ -28,23 +28,23 @@ export function getEsQuerySearchAfter(
|
|||
// already surrounding docs -> first or last record is used
|
||||
const afterTimeRecIdx = type === 'successors' && documents.length ? documents.length - 1 : 0;
|
||||
const afterTimeDoc = documents[afterTimeRecIdx];
|
||||
let afterTimeValue: string | number = afterTimeDoc.sort[0];
|
||||
let afterTimeValue = afterTimeDoc.sort[0] as string | number;
|
||||
if (nanoSeconds) {
|
||||
afterTimeValue = useNewFieldsApi
|
||||
? (afterTimeDoc.fields[timeFieldName] as Array<string | number>)[0]
|
||||
: (afterTimeDoc._source[timeFieldName] as string | number);
|
||||
? afterTimeDoc.fields[timeFieldName][0]
|
||||
: afterTimeDoc._source?.[timeFieldName];
|
||||
}
|
||||
return [afterTimeValue, afterTimeDoc.sort[1]];
|
||||
return [afterTimeValue, afterTimeDoc.sort[1] as string | number];
|
||||
}
|
||||
// if data_nanos adapt timestamp value for sorting, since numeric value was rounded by browser
|
||||
// ES search_after also works when number is provided as string
|
||||
const searchAfter = new Array(2) as EsQuerySearchAfter;
|
||||
searchAfter[0] = anchor.sort[0];
|
||||
searchAfter[0] = anchor.sort[0] as string | number;
|
||||
if (nanoSeconds) {
|
||||
searchAfter[0] = useNewFieldsApi
|
||||
? (anchor.fields[timeFieldName] as Array<string | number>)[0]
|
||||
: (anchor._source[timeFieldName] as string | number);
|
||||
? anchor.fields[timeFieldName][0]
|
||||
: anchor._source?.[timeFieldName];
|
||||
}
|
||||
searchAfter[1] = anchor.sort[1];
|
||||
searchAfter[1] = anchor.sort[1] as string | number;
|
||||
return searchAfter;
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ import { i18n } from '@kbn/i18n';
|
|||
import { getServices } from '../../../../kibana_services';
|
||||
import { SEARCH_FIELDS_FROM_SOURCE } from '../../../../../common';
|
||||
import { MarkdownSimple, toMountPoint } from '../../../../../../kibana_react/public';
|
||||
import { AnchorHitRecord, fetchAnchorProvider } from '../api/anchor';
|
||||
import { fetchAnchorProvider } from '../api/anchor';
|
||||
import { EsHitRecord, EsHitRecordList, fetchContextProvider, SurrDocType } from '../api/context';
|
||||
import { getQueryParameterActions } from '../query_parameters';
|
||||
import {
|
||||
|
@ -77,11 +77,12 @@ export function QueryActionsProvider(Promise: DiscoverPromise) {
|
|||
}
|
||||
|
||||
setLoadingStatus(state)('anchor');
|
||||
const [[, sortDir]] = sort;
|
||||
|
||||
return Promise.try(() =>
|
||||
fetchAnchor(indexPatternId, anchorId, [fromPairs([sort]), { [tieBreakerField]: sort[1] }])
|
||||
fetchAnchor(indexPatternId, anchorId, [fromPairs(sort), { [tieBreakerField]: sortDir }])
|
||||
).then(
|
||||
(anchorDocument: AnchorHitRecord) => {
|
||||
(anchorDocument: EsHitRecord) => {
|
||||
setLoadedStatus(state)('anchor');
|
||||
state.rows.anchor = anchorDocument;
|
||||
return anchorDocument;
|
||||
|
@ -120,7 +121,7 @@ export function QueryActionsProvider(Promise: DiscoverPromise) {
|
|||
}
|
||||
|
||||
setLoadingStatus(state)(type);
|
||||
const [sortField, sortDir] = sort;
|
||||
const [[sortField, sortDir]] = sort;
|
||||
|
||||
return Promise.try(() =>
|
||||
fetchSurroundingDocs(
|
||||
|
|
|
@ -10,6 +10,7 @@ import { getQueryParameterActions } from './actions';
|
|||
import { FilterManager, SortDirection } from '../../../../../../data/public';
|
||||
import { coreMock } from '../../../../../../../core/public/mocks';
|
||||
import { ContextAppState, LoadingStatus, QueryParameters } from '../../context_app_state';
|
||||
import { EsHitRecord } from '../api/context';
|
||||
const setupMock = coreMock.createSetup();
|
||||
|
||||
let state: ContextAppState;
|
||||
|
@ -29,7 +30,7 @@ beforeEach(() => {
|
|||
anchorId: '',
|
||||
columns: [],
|
||||
filters: [],
|
||||
sort: ['field', SortDirection.asc],
|
||||
sort: [['field', SortDirection.asc]],
|
||||
tieBreakerField: '',
|
||||
},
|
||||
loadingStatus: {
|
||||
|
@ -39,8 +40,7 @@ beforeEach(() => {
|
|||
},
|
||||
rows: {
|
||||
all: [],
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
anchor: { $$_isAnchor: true, fields: [], sort: [], _source: [], _id: '' },
|
||||
anchor: ({ isAnchor: true, fields: [], sort: [], _id: '' } as unknown) as EsHitRecord,
|
||||
predecessors: [],
|
||||
successors: [],
|
||||
},
|
||||
|
@ -129,7 +129,7 @@ describe('context query_parameter actions', function () {
|
|||
indexPatternId: 'INDEX_PATTERN',
|
||||
predecessorCount: 100,
|
||||
successorCount: 100,
|
||||
sort: ['field', SortDirection.asc],
|
||||
sort: [['field', SortDirection.asc]],
|
||||
tieBreakerField: '',
|
||||
});
|
||||
|
||||
|
@ -142,7 +142,7 @@ describe('context query_parameter actions', function () {
|
|||
indexPatternId: 'INDEX_PATTERN',
|
||||
predecessorCount: 100,
|
||||
successorCount: 100,
|
||||
sort: ['field', SortDirection.asc],
|
||||
sort: [['field', SortDirection.asc]],
|
||||
tieBreakerField: '',
|
||||
});
|
||||
});
|
||||
|
|
|
@ -3,11 +3,14 @@
|
|||
filter="contextApp.actions.addFilter"
|
||||
hits="contextApp.state.rows.all"
|
||||
index-pattern="contextApp.indexPattern"
|
||||
app-state="contextApp.appState"
|
||||
state-container="contextApp.stateContainer"
|
||||
sorting="contextApp.state.queryParameters.sort"
|
||||
columns="contextApp.state.queryParameters.columns"
|
||||
minimum-visible-rows="contextApp.state.rows.all.length"
|
||||
status="contextApp.state.loadingStatus.anchor.status"
|
||||
reason="contextApp.state.loadingStatus.anchor.reason"
|
||||
anchor-id="contextApp.anchorId"
|
||||
anchor-status="contextApp.state.loadingStatus.anchor.status"
|
||||
anchor-reason="contextApp.state.loadingStatus.anchor.reason"
|
||||
default-step-size="contextApp.state.queryParameters.defaultStepSize"
|
||||
predecessor-count="contextApp.state.queryParameters.predecessorCount"
|
||||
predecessor-available="contextApp.state.rows.predecessors.length"
|
||||
|
@ -18,5 +21,4 @@
|
|||
successor-status="contextApp.state.loadingStatus.successors.status"
|
||||
on-change-successor-count="contextApp.actions.fetchGivenSuccessorRows"
|
||||
use-new-fields-api="contextApp.state.useNewFieldsApi"
|
||||
top-nav-menu="contextApp.topNavMenu"
|
||||
></context-app-legacy>
|
||||
top-nav-menu="contextApp.topNavMenu"></context-app-legacy>
|
|
@ -34,6 +34,8 @@ getAngularModule().directive('contextApp', function ContextApp() {
|
|||
anchorId: '=',
|
||||
columns: '=',
|
||||
indexPattern: '=',
|
||||
appState: '=',
|
||||
stateContainer: '=',
|
||||
filters: '=',
|
||||
predecessorCount: '=',
|
||||
successorCount: '=',
|
||||
|
@ -55,7 +57,6 @@ function ContextAppController($scope, Private) {
|
|||
);
|
||||
this.state.useNewFieldsApi = useNewFieldsApi;
|
||||
this.topNavMenu = navigation.ui.TopNavMenu;
|
||||
|
||||
this.actions = _.mapValues(
|
||||
{
|
||||
...queryParameterActions,
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
*/
|
||||
|
||||
import { Filter } from '../../../../data/public';
|
||||
import { AnchorHitRecord } from './context/api/anchor';
|
||||
import { EsHitRecord } from './context/api/context';
|
||||
import { EsHitRecordList } from './context/api/context';
|
||||
import { SortDirection } from './context/api/utils/sorting';
|
||||
|
||||
|
@ -48,13 +48,13 @@ export interface QueryParameters {
|
|||
indexPatternId: string;
|
||||
predecessorCount: number;
|
||||
successorCount: number;
|
||||
sort: [string, SortDirection];
|
||||
sort: Array<[string, SortDirection]>;
|
||||
tieBreakerField: string;
|
||||
}
|
||||
|
||||
interface ContextRows {
|
||||
all: EsHitRecordList;
|
||||
anchor: AnchorHitRecord;
|
||||
anchor: EsHitRecord;
|
||||
predecessors: EsHitRecordList;
|
||||
successors: EsHitRecordList;
|
||||
}
|
||||
|
|
|
@ -45,8 +45,10 @@ describe('Test Discover Context State', () => {
|
|||
"filters": Array [],
|
||||
"predecessorCount": 4,
|
||||
"sort": Array [
|
||||
"time",
|
||||
"desc",
|
||||
Array [
|
||||
"time",
|
||||
"desc",
|
||||
],
|
||||
],
|
||||
"successorCount": 4,
|
||||
}
|
||||
|
@ -60,7 +62,7 @@ describe('Test Discover Context State', () => {
|
|||
state.setAppState({ predecessorCount: 10 });
|
||||
state.flushToUrl();
|
||||
expect(getCurrentUrl()).toMatchInlineSnapshot(
|
||||
`"/#?_a=(columns:!(_source),filters:!(),predecessorCount:10,sort:!(time,desc),successorCount:4)"`
|
||||
`"/#?_a=(columns:!(_source),filters:!(),predecessorCount:10,sort:!(!(time,desc)),successorCount:4)"`
|
||||
);
|
||||
});
|
||||
test('getState -> url to appState syncing', async () => {
|
||||
|
@ -183,7 +185,7 @@ describe('Test Discover Context State', () => {
|
|||
`);
|
||||
state.flushToUrl();
|
||||
expect(getCurrentUrl()).toMatchInlineSnapshot(
|
||||
`"/#?_g=(filters:!(('$state':(store:globalState),meta:(alias:!n,disabled:!f,index:'logstash-*',key:extension,negate:!f,params:(query:jpg),type:phrase),query:(match:(extension:(query:jpg,type:phrase))))))&_a=(columns:!(_source),filters:!(('$state':(store:appState),meta:(alias:!n,disabled:!f,index:'logstash-*',key:extension,negate:!t,params:(query:png),type:phrase),query:(match:(extension:(query:png,type:phrase))))),predecessorCount:4,sort:!(time,desc),successorCount:4)"`
|
||||
`"/#?_g=(filters:!(('$state':(store:globalState),meta:(alias:!n,disabled:!f,index:'logstash-*',key:extension,negate:!f,params:(query:jpg),type:phrase),query:(match:(extension:(query:jpg,type:phrase))))))&_a=(columns:!(_source),filters:!(('$state':(store:appState),meta:(alias:!n,disabled:!f,index:'logstash-*',key:extension,negate:!t,params:(query:png),type:phrase),query:(match:(extension:(query:png,type:phrase))))),predecessorCount:4,sort:!(!(time,desc)),successorCount:4)"`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -35,7 +35,7 @@ export interface AppState {
|
|||
/**
|
||||
* Sorting of the records to be fetched, assumed to be a legacy parameter
|
||||
*/
|
||||
sort: string[];
|
||||
sort: string[][];
|
||||
/**
|
||||
* Number of records to be fetched after the anchor records (older records)
|
||||
*/
|
||||
|
@ -50,7 +50,7 @@ interface GlobalState {
|
|||
filters: Filter[];
|
||||
}
|
||||
|
||||
interface GetStateParams {
|
||||
export interface GetStateParams {
|
||||
/**
|
||||
* Number of records to be fetched when 'Load' link/button is clicked
|
||||
*/
|
||||
|
@ -81,7 +81,7 @@ interface GetStateParams {
|
|||
uiSettings: IUiSettingsClient;
|
||||
}
|
||||
|
||||
interface GetStateReturn {
|
||||
export interface GetStateReturn {
|
||||
/**
|
||||
* Global state, the _g part of the URL
|
||||
*/
|
||||
|
@ -276,7 +276,7 @@ function createInitialAppState(
|
|||
columns: ['_source'],
|
||||
filters: [],
|
||||
predecessorCount: parseInt(defaultSize, 10),
|
||||
sort: [timeFieldName, 'desc'],
|
||||
sort: [[timeFieldName, 'desc']],
|
||||
successorCount: parseInt(defaultSize, 10),
|
||||
};
|
||||
if (typeof urlState !== 'object') {
|
||||
|
|
|
@ -10,13 +10,13 @@
|
|||
opts="opts"
|
||||
reset-query="resetQuery"
|
||||
result-state="resultState"
|
||||
fetch-status="fetchStatus"
|
||||
rows="rows"
|
||||
search-source="volatileSearchSource"
|
||||
state="state"
|
||||
top-nav-menu="topNavMenu"
|
||||
use-new-fields-api="useNewFieldsApi"
|
||||
unmapped-fields-config="unmappedFieldsConfig"
|
||||
refresh-app-state="refreshAppState"
|
||||
>
|
||||
refresh-app-state="refreshAppState">
|
||||
</discover>
|
||||
</discover-app>
|
||||
</discover-app>
|
|
@ -8,7 +8,14 @@
|
|||
import { Capabilities, IUiSettingsClient } from 'kibana/public';
|
||||
import { popularizeField } from '../../../helpers/popularize_field';
|
||||
import { IndexPattern, IndexPatternsContract } from '../../../../kibana_services';
|
||||
import { AppState } from '../../discover_state';
|
||||
import {
|
||||
AppState as DiscoverState,
|
||||
GetStateReturn as DiscoverGetStateReturn,
|
||||
} from '../../discover_state';
|
||||
import {
|
||||
AppState as ContextState,
|
||||
GetStateReturn as ContextGetStateReturn,
|
||||
} from '../../context_state';
|
||||
import { SORT_DEFAULT_ORDER_SETTING } from '../../../../../common';
|
||||
|
||||
/**
|
||||
|
@ -67,8 +74,8 @@ export function getStateColumnActions({
|
|||
indexPattern: IndexPattern;
|
||||
indexPatterns: IndexPatternsContract;
|
||||
useNewFieldsApi: boolean;
|
||||
setAppState: (state: Partial<AppState>) => void;
|
||||
state: AppState;
|
||||
setAppState: DiscoverGetStateReturn['setAppState'] | ContextGetStateReturn['setAppState'];
|
||||
state: DiscoverState | ContextState;
|
||||
}) {
|
||||
function onAddColumn(columnName: string) {
|
||||
if (capabilities.discover.save) {
|
||||
|
|
|
@ -95,8 +95,8 @@
|
|||
index-pattern="indexPattern"
|
||||
filter="filter"
|
||||
class="kbnDocTable__row"
|
||||
ng-class="{'kbnDocTable__row--highlight': row['$$_isAnchor']}"
|
||||
data-test-subj="docTableRow{{ row['$$_isAnchor'] ? ' docTableAnchorRow' : ''}}"
|
||||
ng-class="{'kbnDocTable__row--highlight': row['isAnchor']}"
|
||||
data-test-subj="docTableRow{{ row['isAnchor'] ? ' docTableAnchorRow' : ''}}"
|
||||
on-add-column="onAddColumn"
|
||||
on-remove-column="onRemoveColumn"
|
||||
use-new-fields-api="useNewFieldsApi"
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
@import '../../../../../../core/public/mixins';
|
||||
|
||||
.dscDocsPage {
|
||||
@include kibanaFullBodyHeight(54px); // action bar height
|
||||
}
|
||||
|
||||
.dscDocsContent {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.dscDocsGrid {
|
||||
flex: 1 1 100%;
|
||||
overflow: auto;
|
||||
|
||||
&__cell--highlight {
|
||||
background-color: tintOrShade($euiColorPrimary, 90%, 70%);
|
||||
}
|
||||
|
||||
.euiDataGridRowCell.euiDataGridRowCell--firstColumn {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
|
@ -7,14 +7,34 @@
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { ContextAppLegacy } from './context_app_legacy';
|
||||
import { IIndexPattern } from '../../../../../data/common/index_patterns';
|
||||
import { mountWithIntl } from '@kbn/test/jest';
|
||||
import { uiSettingsMock as mockUiSettings } from '../../../__mocks__/ui_settings';
|
||||
import { IndexPattern } from '../../../../../data/common/index_patterns';
|
||||
import { ContextAppLegacy } from './context_app_legacy';
|
||||
import { DocTableLegacy } from '../../angular/doc_table/create_doc_table_react';
|
||||
import { findTestSubject } from '@elastic/eui/lib/test';
|
||||
import { ActionBar } from '../../angular/context/components/action_bar/action_bar';
|
||||
import { ContextErrorMessage } from '../context_error_message';
|
||||
import { TopNavMenuMock } from './__mocks__/top_nav_menu';
|
||||
import { AppState, GetStateReturn } from '../../angular/context_state';
|
||||
import { SortDirection } from 'src/plugins/data/common';
|
||||
import { EsHitRecordList } from '../../angular/context/api/context';
|
||||
|
||||
jest.mock('../../../kibana_services', () => {
|
||||
return {
|
||||
getServices: () => ({
|
||||
metadata: {
|
||||
branch: 'test',
|
||||
},
|
||||
capabilities: {
|
||||
discover: {
|
||||
save: true,
|
||||
},
|
||||
},
|
||||
uiSettings: mockUiSettings,
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
describe('ContextAppLegacy test', () => {
|
||||
const hit = {
|
||||
|
@ -35,16 +55,19 @@ describe('ContextAppLegacy test', () => {
|
|||
};
|
||||
const indexPattern = {
|
||||
id: 'test_index_pattern',
|
||||
} as IIndexPattern;
|
||||
} as IndexPattern;
|
||||
const defaultProps = {
|
||||
columns: ['_source'],
|
||||
filter: () => {},
|
||||
hits: [hit],
|
||||
sorting: ['order_date', 'desc'],
|
||||
hits: ([hit] as unknown) as EsHitRecordList,
|
||||
sorting: [['order_date', 'desc']] as Array<[string, SortDirection]>,
|
||||
minimumVisibleRows: 5,
|
||||
indexPattern,
|
||||
status: 'loaded',
|
||||
reason: 'no reason',
|
||||
appState: ({} as unknown) as AppState,
|
||||
stateContainer: ({} as unknown) as GetStateReturn,
|
||||
anchorId: 'test_anchor_id',
|
||||
anchorStatus: 'loaded',
|
||||
anchorReason: 'no reason',
|
||||
defaultStepSize: 5,
|
||||
predecessorCount: 10,
|
||||
successorCount: 10,
|
||||
|
@ -55,6 +78,8 @@ describe('ContextAppLegacy test', () => {
|
|||
predecessorStatus: 'loaded',
|
||||
successorStatus: 'loaded',
|
||||
topNavMenu: TopNavMenuMock,
|
||||
useNewFieldsApi: false,
|
||||
isPaginationEnabled: false,
|
||||
};
|
||||
const topNavProps = {
|
||||
appName: 'context',
|
||||
|
@ -80,7 +105,7 @@ describe('ContextAppLegacy test', () => {
|
|||
|
||||
it('renders loading indicator', () => {
|
||||
const props = { ...defaultProps };
|
||||
props.status = 'loading';
|
||||
props.anchorStatus = 'loading';
|
||||
const component = mountWithIntl(<ContextAppLegacy {...props} />);
|
||||
expect(component.find(DocTableLegacy).length).toBe(0);
|
||||
const loadingIndicator = findTestSubject(component, 'contextApp_loadingIndicator');
|
||||
|
@ -91,8 +116,8 @@ describe('ContextAppLegacy test', () => {
|
|||
|
||||
it('renders error message', () => {
|
||||
const props = { ...defaultProps };
|
||||
props.status = 'failed';
|
||||
props.reason = 'something went wrong';
|
||||
props.anchorStatus = 'failed';
|
||||
props.anchorReason = 'something went wrong';
|
||||
const component = mountWithIntl(<ContextAppLegacy {...props} />);
|
||||
expect(component.find(DocTableLegacy).length).toBe(0);
|
||||
expect(component.find(TopNavMenuMock).length).toBe(0);
|
||||
|
|
|
@ -6,29 +6,43 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React, { useState, Fragment } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { FormattedMessage, I18nProvider } from '@kbn/i18n/react';
|
||||
import { EuiHorizontalRule, EuiText, EuiPageContent, EuiPage } from '@elastic/eui';
|
||||
import './context_app_legacy.scss';
|
||||
import { EuiHorizontalRule, EuiText, EuiPageContent, EuiPage, EuiSpacer } from '@elastic/eui';
|
||||
import { DOC_HIDE_TIME_COLUMN_SETTING, DOC_TABLE_LEGACY } from '../../../../common';
|
||||
import { ContextErrorMessage } from '../context_error_message';
|
||||
import {
|
||||
DocTableLegacy,
|
||||
DocTableLegacyProps,
|
||||
} from '../../angular/doc_table/create_doc_table_react';
|
||||
import { IIndexPattern, IndexPatternField } from '../../../../../data/common/index_patterns';
|
||||
import { IndexPattern } from '../../../../../data/common/index_patterns';
|
||||
import { LoadingStatus } from '../../angular/context_app_state';
|
||||
import { ActionBar, ActionBarProps } from '../../angular/context/components/action_bar/action_bar';
|
||||
import { TopNavMenuProps } from '../../../../../navigation/public';
|
||||
import { DiscoverGrid, DiscoverGridProps } from '../discover_grid/discover_grid';
|
||||
import { DocViewFilterFn } from '../../doc_views/doc_views_types';
|
||||
import { getServices, SortDirection } from '../../../kibana_services';
|
||||
import { GetStateReturn, AppState } from '../../angular/context_state';
|
||||
import { useDataGridColumns } from '../../helpers/use_data_grid_columns';
|
||||
import { EsHitRecord, EsHitRecordList } from '../../angular/context/api/context';
|
||||
|
||||
export interface ContextAppProps {
|
||||
topNavMenu: React.ComponentType<TopNavMenuProps>;
|
||||
columns: string[];
|
||||
hits: Array<Record<string, unknown>>;
|
||||
indexPattern: IIndexPattern;
|
||||
filter: (field: IndexPatternField | string, value: string, type: '+' | '-') => void;
|
||||
hits: EsHitRecordList;
|
||||
indexPattern: IndexPattern;
|
||||
appState: AppState;
|
||||
stateContainer: GetStateReturn;
|
||||
filter: DocViewFilterFn;
|
||||
minimumVisibleRows: number;
|
||||
sorting: string[];
|
||||
status: string;
|
||||
reason: string;
|
||||
sorting: Array<[string, SortDirection]>;
|
||||
anchorId: string;
|
||||
anchorStatus: string;
|
||||
anchorReason: string;
|
||||
predecessorStatus: string;
|
||||
successorStatus: string;
|
||||
defaultStepSize: number;
|
||||
predecessorCount: number;
|
||||
successorCount: number;
|
||||
|
@ -36,11 +50,10 @@ export interface ContextAppProps {
|
|||
successorAvailable: number;
|
||||
onChangePredecessorCount: (count: number) => void;
|
||||
onChangeSuccessorCount: (count: number) => void;
|
||||
predecessorStatus: string;
|
||||
successorStatus: string;
|
||||
useNewFieldsApi?: boolean;
|
||||
}
|
||||
|
||||
const DataGridMemoized = React.memo(DiscoverGrid);
|
||||
const PREDECESSOR_TYPE = 'predecessors';
|
||||
const SUCCESSOR_TYPE = 'successors';
|
||||
|
||||
|
@ -49,9 +62,36 @@ function isLoading(status: string) {
|
|||
}
|
||||
|
||||
export function ContextAppLegacy(renderProps: ContextAppProps) {
|
||||
const status = renderProps.status;
|
||||
const isLoaded = status === LoadingStatus.LOADED;
|
||||
const isFailed = status === LoadingStatus.FAILED;
|
||||
const services = getServices();
|
||||
const { uiSettings: config, capabilities, indexPatterns } = services;
|
||||
const {
|
||||
indexPattern,
|
||||
anchorId,
|
||||
anchorStatus,
|
||||
predecessorStatus,
|
||||
successorStatus,
|
||||
appState,
|
||||
stateContainer,
|
||||
hits: rows,
|
||||
sorting,
|
||||
filter,
|
||||
minimumVisibleRows,
|
||||
useNewFieldsApi,
|
||||
} = renderProps;
|
||||
const [expandedDoc, setExpandedDoc] = useState<EsHitRecord | undefined>(undefined);
|
||||
const isAnchorLoaded = anchorStatus === LoadingStatus.LOADED;
|
||||
const isFailed = anchorStatus === LoadingStatus.FAILED;
|
||||
const isLegacy = config.get(DOC_TABLE_LEGACY);
|
||||
|
||||
const { columns, onAddColumn, onRemoveColumn, onSetColumns } = useDataGridColumns({
|
||||
capabilities,
|
||||
config,
|
||||
indexPattern,
|
||||
indexPatterns,
|
||||
setAppState: stateContainer.setAppState,
|
||||
state: appState,
|
||||
useNewFieldsApi: !!useNewFieldsApi,
|
||||
});
|
||||
|
||||
const actionBarProps = (type: string) => {
|
||||
const {
|
||||
|
@ -60,8 +100,6 @@ export function ContextAppLegacy(renderProps: ContextAppProps) {
|
|||
predecessorCount,
|
||||
predecessorAvailable,
|
||||
successorAvailable,
|
||||
predecessorStatus,
|
||||
successorStatus,
|
||||
onChangePredecessorCount,
|
||||
onChangeSuccessorCount,
|
||||
} = renderProps;
|
||||
|
@ -73,27 +111,44 @@ export function ContextAppLegacy(renderProps: ContextAppProps) {
|
|||
onChangeCount: isPredecessorType ? onChangePredecessorCount : onChangeSuccessorCount,
|
||||
isLoading: isPredecessorType ? isLoading(predecessorStatus) : isLoading(successorStatus),
|
||||
type,
|
||||
isDisabled: !isLoaded,
|
||||
isDisabled: !isAnchorLoaded,
|
||||
} as ActionBarProps;
|
||||
};
|
||||
|
||||
const docTableProps = () => {
|
||||
const {
|
||||
hits,
|
||||
filter,
|
||||
sorting,
|
||||
return {
|
||||
ariaLabelledBy: 'surDocumentsAriaLabel',
|
||||
columns,
|
||||
rows,
|
||||
indexPattern,
|
||||
minimumVisibleRows,
|
||||
expandedDoc,
|
||||
isLoading: isLoading(anchorStatus),
|
||||
sampleSize: 0,
|
||||
sort: sorting,
|
||||
isSortEnabled: false,
|
||||
showTimeCol: !config.get(DOC_HIDE_TIME_COLUMN_SETTING, false) && !!indexPattern.timeFieldName,
|
||||
services,
|
||||
useNewFieldsApi,
|
||||
} = renderProps;
|
||||
isPaginationEnabled: false,
|
||||
controlColumnIds: ['openDetails'],
|
||||
setExpandedDoc,
|
||||
onFilter: filter,
|
||||
onAddColumn,
|
||||
onRemoveColumn,
|
||||
onSetColumns,
|
||||
} as DiscoverGridProps;
|
||||
};
|
||||
|
||||
const legacyDocTableProps = () => {
|
||||
// @ts-expect-error doesn't implement full DocTableLegacyProps interface
|
||||
return {
|
||||
columns,
|
||||
indexPattern,
|
||||
minimumVisibleRows,
|
||||
rows: hits,
|
||||
rows,
|
||||
onFilter: filter,
|
||||
onAddColumn,
|
||||
onRemoveColumn,
|
||||
sort: sorting.map((el) => [el]),
|
||||
useNewFieldsApi,
|
||||
} as DocTableLegacyProps;
|
||||
|
@ -114,7 +169,7 @@ export function ContextAppLegacy(renderProps: ContextAppProps) {
|
|||
};
|
||||
|
||||
const loadingFeedback = () => {
|
||||
if (status === LoadingStatus.UNINITIALIZED || status === LoadingStatus.LOADING) {
|
||||
if (anchorStatus === LoadingStatus.UNINITIALIZED || anchorStatus === LoadingStatus.LOADING) {
|
||||
return (
|
||||
<EuiText textAlign="center" data-test-subj="contextApp_loadingIndicator">
|
||||
<FormattedMessage id="discover.context.loadingDescription" defaultMessage="Loading..." />
|
||||
|
@ -127,25 +182,42 @@ export function ContextAppLegacy(renderProps: ContextAppProps) {
|
|||
return (
|
||||
<I18nProvider>
|
||||
{isFailed ? (
|
||||
<ContextErrorMessage status={status} reason={renderProps.reason} />
|
||||
<ContextErrorMessage status={anchorStatus} reason={renderProps.anchorReason} />
|
||||
) : (
|
||||
<div>
|
||||
<Fragment>
|
||||
<TopNavMenu {...getNavBarProps()} />
|
||||
<EuiPage>
|
||||
<EuiPageContent paddingSize="s" className="dscCxtAppContent">
|
||||
<EuiPage className={classNames({ dscDocsPage: !isLegacy })}>
|
||||
<EuiPageContent paddingSize="s" className="dscDocsContent">
|
||||
<EuiSpacer size="s" />
|
||||
<EuiText>
|
||||
<strong>
|
||||
<FormattedMessage
|
||||
id="discover.context.contextOfTitle"
|
||||
defaultMessage="Documents surrounding #{anchorId}"
|
||||
values={{ anchorId }}
|
||||
/>
|
||||
</strong>
|
||||
</EuiText>
|
||||
<EuiSpacer size="s" />
|
||||
<ActionBar {...actionBarProps(PREDECESSOR_TYPE)} />
|
||||
{loadingFeedback()}
|
||||
{isLegacy && loadingFeedback()}
|
||||
<EuiHorizontalRule margin="xs" />
|
||||
{isLoaded ? (
|
||||
<div className="discover-table">
|
||||
<DocTableLegacy {...docTableProps()} />
|
||||
{isLegacy ? (
|
||||
isAnchorLoaded && (
|
||||
<div className="discover-table">
|
||||
<DocTableLegacy {...legacyDocTableProps()} />
|
||||
</div>
|
||||
)
|
||||
) : (
|
||||
<div className="dscDocsGrid">
|
||||
<DataGridMemoized {...docTableProps()} />
|
||||
</div>
|
||||
) : null}
|
||||
)}
|
||||
<EuiHorizontalRule margin="xs" />
|
||||
<ActionBar {...actionBarProps(SUCCESSOR_TYPE)} />
|
||||
</EuiPageContent>
|
||||
</EuiPage>
|
||||
</div>
|
||||
</Fragment>
|
||||
)}
|
||||
</I18nProvider>
|
||||
);
|
||||
|
|
|
@ -14,11 +14,14 @@ export function createContextAppLegacy(reactDirective: any) {
|
|||
['filter', { watchDepth: 'reference' }],
|
||||
['hits', { watchDepth: 'reference' }],
|
||||
['indexPattern', { watchDepth: 'reference' }],
|
||||
['appState', { watchDepth: 'reference' }],
|
||||
['stateContainer', { watchDepth: 'reference' }],
|
||||
['sorting', { watchDepth: 'reference' }],
|
||||
['columns', { watchDepth: 'collection' }],
|
||||
['minimumVisibleRows', { watchDepth: 'reference' }],
|
||||
['status', { watchDepth: 'reference' }],
|
||||
['reason', { watchDepth: 'reference' }],
|
||||
['anchorId', { watchDepth: 'reference' }],
|
||||
['anchorStatus', { watchDepth: 'reference' }],
|
||||
['anchorReason', { watchDepth: 'reference' }],
|
||||
['defaultStepSize', { watchDepth: 'reference' }],
|
||||
['predecessorCount', { watchDepth: 'reference' }],
|
||||
['predecessorAvailable', { watchDepth: 'reference' }],
|
||||
|
|
|
@ -20,6 +20,7 @@ export function createDiscoverDirective(reactDirective: any) {
|
|||
['opts', { watchDepth: 'reference' }],
|
||||
['resetQuery', { watchDepth: 'reference' }],
|
||||
['resultState', { watchDepth: 'reference' }],
|
||||
['fetchStatus', { watchDepth: 'reference' }],
|
||||
['rows', { watchDepth: 'reference' }],
|
||||
['savedSearch', { watchDepth: 'reference' }],
|
||||
['searchSource', { watchDepth: 'reference' }],
|
||||
|
|
|
@ -34,9 +34,12 @@ import { esFilters, IndexPatternField, search } from '../../../../data/public';
|
|||
import { DiscoverSidebarResponsive } from './sidebar';
|
||||
import { DiscoverProps } from './types';
|
||||
import { SortPairArr } from '../angular/doc_table/lib/get_sort';
|
||||
import { SEARCH_FIELDS_FROM_SOURCE } from '../../../common';
|
||||
import {
|
||||
DOC_HIDE_TIME_COLUMN_SETTING,
|
||||
DOC_TABLE_LEGACY,
|
||||
SEARCH_FIELDS_FROM_SOURCE,
|
||||
} from '../../../common';
|
||||
import { popularizeField } from '../helpers/popularize_field';
|
||||
import { getStateColumnActions } from '../angular/doc_table/actions/columns';
|
||||
import { DocViewFilterFn } from '../doc_views/doc_views_types';
|
||||
import { DiscoverGrid } from './discover_grid/discover_grid';
|
||||
import { DiscoverTopNav } from './discover_topnav';
|
||||
|
@ -44,6 +47,7 @@ import { ElasticSearchHit } from '../doc_views/doc_views_types';
|
|||
import { setBreadcrumbsTitle } from '../helpers/breadcrumbs';
|
||||
import { addHelpMenuToAppChrome } from './help_menu/help_menu_util';
|
||||
import { InspectorSession } from '../../../../inspector/public';
|
||||
import { useDataGridColumns } from '../helpers/use_data_grid_columns';
|
||||
|
||||
const DocTableLegacyMemoized = React.memo(DocTableLegacy);
|
||||
const SidebarMemoized = React.memo(DiscoverSidebarResponsive);
|
||||
|
@ -96,7 +100,7 @@ export function Discover({
|
|||
}, [opts.chartAggConfigs]);
|
||||
|
||||
const contentCentered = resultState === 'uninitialized';
|
||||
const isLegacy = services.uiSettings.get('doc_table:legacy');
|
||||
const isLegacy = services.uiSettings.get(DOC_TABLE_LEGACY);
|
||||
const useNewFieldsApi = !services.uiSettings.get(SEARCH_FIELDS_FROM_SOURCE);
|
||||
const updateQuery = useCallback(
|
||||
(_payload, isUpdate?: boolean) => {
|
||||
|
@ -108,6 +112,16 @@ export function Discover({
|
|||
[opts]
|
||||
);
|
||||
|
||||
const { columns, onAddColumn, onRemoveColumn, onMoveColumn, onSetColumns } = useDataGridColumns({
|
||||
capabilities,
|
||||
config,
|
||||
indexPattern,
|
||||
indexPatterns,
|
||||
setAppState,
|
||||
state,
|
||||
useNewFieldsApi,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const pageTitleSuffix = savedSearch.id && savedSearch.title ? `: ${savedSearch.title}` : '';
|
||||
chrome.docTitle.change(`Discover${pageTitleSuffix}`);
|
||||
|
@ -116,20 +130,6 @@ export function Discover({
|
|||
addHelpMenuToAppChrome(chrome, docLinks);
|
||||
}, [savedSearch, chrome, docLinks]);
|
||||
|
||||
const { onAddColumn, onRemoveColumn, onMoveColumn, onSetColumns } = useMemo(
|
||||
() =>
|
||||
getStateColumnActions({
|
||||
capabilities,
|
||||
config,
|
||||
indexPattern,
|
||||
indexPatterns,
|
||||
setAppState,
|
||||
state,
|
||||
useNewFieldsApi,
|
||||
}),
|
||||
[capabilities, config, indexPattern, indexPatterns, setAppState, state, useNewFieldsApi]
|
||||
);
|
||||
|
||||
const onOpenInspector = useCallback(() => {
|
||||
// prevent overlapping
|
||||
setExpandedDoc(undefined);
|
||||
|
@ -225,12 +225,6 @@ export function Discover({
|
|||
}
|
||||
};
|
||||
|
||||
const columns = useMemo(() => {
|
||||
if (!state.columns) {
|
||||
return [];
|
||||
}
|
||||
return useNewFieldsApi ? state.columns.filter((col) => col !== '_source') : state.columns;
|
||||
}, [state, useNewFieldsApi]);
|
||||
return (
|
||||
<I18nProvider>
|
||||
<EuiPage className="dscPage" data-fetch-counter={fetchCounter}>
|
||||
|
@ -439,13 +433,13 @@ export function Discover({
|
|||
searchTitle={opts.savedSearch.lastSavedTitle}
|
||||
setExpandedDoc={setExpandedDoc}
|
||||
showTimeCol={
|
||||
!config.get('doc_table:hideTimeColumn', false) &&
|
||||
!config.get(DOC_HIDE_TIME_COLUMN_SETTING, false) &&
|
||||
!!indexPattern.timeFieldName
|
||||
}
|
||||
services={services}
|
||||
settings={state.grid}
|
||||
onAddColumn={onAddColumn}
|
||||
onFilter={onAddFilter as DocViewFilterFn}
|
||||
onAddColumn={onAddColumn}
|
||||
onRemoveColumn={onRemoveColumn}
|
||||
onSetColumns={onSetColumns}
|
||||
onSort={onSort}
|
||||
|
|
|
@ -66,6 +66,7 @@
|
|||
text-align: right;
|
||||
}
|
||||
|
||||
.euiDataGrid__loading,
|
||||
.euiDataGrid__noResults {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
|
|
@ -14,11 +14,12 @@ import {
|
|||
EuiDataGridStyle,
|
||||
EuiDataGridProps,
|
||||
EuiDataGrid,
|
||||
EuiIcon,
|
||||
EuiScreenReaderOnly,
|
||||
EuiSpacer,
|
||||
EuiText,
|
||||
htmlIdGenerator,
|
||||
EuiLoadingSpinner,
|
||||
EuiIcon,
|
||||
} from '@elastic/eui';
|
||||
import { IndexPattern } from '../../../kibana_services';
|
||||
import { DocViewFilterFn, ElasticSearchHit } from '../../doc_views/doc_views_types';
|
||||
|
@ -88,9 +89,9 @@ export interface DiscoverGridProps {
|
|||
*/
|
||||
onSetColumns: (columns: string[]) => void;
|
||||
/**
|
||||
* function to change sorting of the documents
|
||||
* function to change sorting of the documents, skipped when isSortEnabled is set to false
|
||||
*/
|
||||
onSort: (sort: string[][]) => void;
|
||||
onSort?: (sort: string[][]) => void;
|
||||
/**
|
||||
* Array of documents provided by Elasticsearch
|
||||
*/
|
||||
|
@ -123,6 +124,10 @@ export interface DiscoverGridProps {
|
|||
* Determines whether the time columns should be displayed (legacy settings)
|
||||
*/
|
||||
showTimeCol: boolean;
|
||||
/**
|
||||
* Manage user sorting control
|
||||
*/
|
||||
isSortEnabled?: boolean;
|
||||
/**
|
||||
* Current sort setting
|
||||
*/
|
||||
|
@ -131,6 +136,14 @@ export interface DiscoverGridProps {
|
|||
* How the data is fetched
|
||||
*/
|
||||
useNewFieldsApi: boolean;
|
||||
/**
|
||||
* Manage pagination control
|
||||
*/
|
||||
isPaginationEnabled?: boolean;
|
||||
/**
|
||||
* List of used control columns (available: 'openDetails', 'select')
|
||||
*/
|
||||
controlColumnIds?: string[];
|
||||
}
|
||||
|
||||
export const EuiDataGridMemoized = React.memo((props: EuiDataGridProps) => {
|
||||
|
@ -159,6 +172,9 @@ export const DiscoverGrid = ({
|
|||
showTimeCol,
|
||||
sort,
|
||||
useNewFieldsApi,
|
||||
isSortEnabled = true,
|
||||
isPaginationEnabled = true,
|
||||
controlColumnIds = ['openDetails', 'select'],
|
||||
}: DiscoverGridProps) => {
|
||||
const [selectedDocs, setSelectedDocs] = useState<string[]>([]);
|
||||
const [isFilterActive, setIsFilterActive] = useState(false);
|
||||
|
@ -210,14 +226,16 @@ export const DiscoverGrid = ({
|
|||
const onChangePage = (pageIndex: number) =>
|
||||
setPagination((paginationData) => ({ ...paginationData, pageIndex }));
|
||||
|
||||
return {
|
||||
onChangeItemsPerPage,
|
||||
onChangePage,
|
||||
pageIndex: pagination.pageIndex > pageCount - 1 ? 0 : pagination.pageIndex,
|
||||
pageSize: pagination.pageSize,
|
||||
pageSizeOptions: pageSizeArr,
|
||||
};
|
||||
}, [pagination, pageCount]);
|
||||
return isPaginationEnabled
|
||||
? {
|
||||
onChangeItemsPerPage,
|
||||
onChangePage,
|
||||
pageIndex: pagination.pageIndex > pageCount - 1 ? 0 : pagination.pageIndex,
|
||||
pageSize: pagination.pageSize,
|
||||
pageSizeOptions: pageSizeArr,
|
||||
}
|
||||
: undefined;
|
||||
}, [pagination, pageCount, isPaginationEnabled]);
|
||||
|
||||
/**
|
||||
* Sorting
|
||||
|
@ -226,9 +244,11 @@ export const DiscoverGrid = ({
|
|||
|
||||
const onTableSort = useCallback(
|
||||
(sortingColumnsData) => {
|
||||
onSort(sortingColumnsData.map(({ id, direction }: SortObj) => [id, direction]));
|
||||
if (isSortEnabled && onSort) {
|
||||
onSort(sortingColumnsData.map(({ id, direction }: SortObj) => [id, direction]));
|
||||
}
|
||||
},
|
||||
[onSort]
|
||||
[onSort, isSortEnabled]
|
||||
);
|
||||
|
||||
/**
|
||||
|
@ -253,8 +273,16 @@ export const DiscoverGrid = ({
|
|||
const randomId = useMemo(() => htmlIdGenerator()(), []);
|
||||
|
||||
const euiGridColumns = useMemo(
|
||||
() => getEuiGridColumns(displayedColumns, settings, indexPattern, showTimeCol, defaultColumns),
|
||||
[displayedColumns, indexPattern, showTimeCol, settings, defaultColumns]
|
||||
() =>
|
||||
getEuiGridColumns(
|
||||
displayedColumns,
|
||||
settings,
|
||||
indexPattern,
|
||||
showTimeCol,
|
||||
defaultColumns,
|
||||
isSortEnabled
|
||||
),
|
||||
[displayedColumns, indexPattern, showTimeCol, settings, defaultColumns, isSortEnabled]
|
||||
);
|
||||
const schemaDetectors = useMemo(() => getSchemaDetectors(), []);
|
||||
const columnsVisibility = useMemo(
|
||||
|
@ -266,11 +294,16 @@ export const DiscoverGrid = ({
|
|||
}),
|
||||
[displayedColumns, indexPattern, showTimeCol, onSetColumns]
|
||||
);
|
||||
const sorting = useMemo(() => ({ columns: sortingColumns, onSort: onTableSort }), [
|
||||
sortingColumns,
|
||||
onTableSort,
|
||||
]);
|
||||
const lead = useMemo(() => getLeadControlColumns(), []);
|
||||
const sorting = useMemo(() => {
|
||||
if (isSortEnabled) {
|
||||
return { columns: sortingColumns, onSort: onTableSort };
|
||||
}
|
||||
return { columns: sortingColumns, onSort: () => {} };
|
||||
}, [sortingColumns, onTableSort, isSortEnabled]);
|
||||
const lead = useMemo(
|
||||
() => getLeadControlColumns().filter(({ id }) => controlColumnIds.includes(id)),
|
||||
[controlColumnIds]
|
||||
);
|
||||
|
||||
const additionalControls = useMemo(
|
||||
() =>
|
||||
|
@ -286,6 +319,18 @@ export const DiscoverGrid = ({
|
|||
[usedSelectedDocs, isFilterActive, rows, setIsFilterActive]
|
||||
);
|
||||
|
||||
if (!rowCount && isLoading) {
|
||||
return (
|
||||
<div className="euiDataGrid__loading">
|
||||
<EuiText size="xs" color="subdued">
|
||||
<EuiLoadingSpinner />
|
||||
<EuiSpacer size="s" />
|
||||
<FormattedMessage id="discover.loadingResults" defaultMessage="Loading results" />
|
||||
</EuiText>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!rowCount) {
|
||||
return (
|
||||
<div className="euiDataGrid__noResults">
|
||||
|
@ -348,10 +393,12 @@ export const DiscoverGrid = ({
|
|||
? {
|
||||
...toolbarVisibility,
|
||||
showColumnSelector: false,
|
||||
showSortSelector: isSortEnabled,
|
||||
additionalControls,
|
||||
}
|
||||
: {
|
||||
...toolbarVisibility,
|
||||
showSortSelector: isSortEnabled,
|
||||
additionalControls,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,14 @@ import { indexPatternWithTimefieldMock } from '../../../__mocks__/index_pattern_
|
|||
|
||||
describe('Discover grid columns ', function () {
|
||||
it('returns eui grid columns without time column', async () => {
|
||||
const actual = getEuiGridColumns(['extension', 'message'], {}, indexPatternMock, false, false);
|
||||
const actual = getEuiGridColumns(
|
||||
['extension', 'message'],
|
||||
{},
|
||||
indexPatternMock,
|
||||
false,
|
||||
false,
|
||||
true
|
||||
);
|
||||
expect(actual).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Object {
|
||||
|
@ -54,6 +61,7 @@ describe('Discover grid columns ', function () {
|
|||
{},
|
||||
indexPatternWithTimefieldMock,
|
||||
false,
|
||||
true,
|
||||
true
|
||||
);
|
||||
expect(actual).toMatchInlineSnapshot(`
|
||||
|
@ -94,7 +102,8 @@ describe('Discover grid columns ', function () {
|
|||
{},
|
||||
indexPatternWithTimefieldMock,
|
||||
true,
|
||||
false
|
||||
false,
|
||||
true
|
||||
);
|
||||
expect(actual).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
|
|
|
@ -53,7 +53,8 @@ export function buildEuiGridColumn(
|
|||
columnName: string,
|
||||
columnWidth: number | undefined = 0,
|
||||
indexPattern: IndexPattern,
|
||||
defaultColumns: boolean
|
||||
defaultColumns: boolean,
|
||||
isSortEnabled: boolean
|
||||
) {
|
||||
const timeString = i18n.translate('discover.timeLabel', {
|
||||
defaultMessage: 'Time',
|
||||
|
@ -62,7 +63,7 @@ export function buildEuiGridColumn(
|
|||
const column: EuiDataGridColumn = {
|
||||
id: columnName,
|
||||
schema: getSchemaByKbnType(indexPatternField?.type),
|
||||
isSortable: indexPatternField?.sortable === true,
|
||||
isSortable: isSortEnabled && indexPatternField?.sortable === true,
|
||||
display:
|
||||
columnName === '_source'
|
||||
? i18n.translate('discover.grid.documentHeader', {
|
||||
|
@ -100,7 +101,8 @@ export function getEuiGridColumns(
|
|||
settings: DiscoverGridSettings | undefined,
|
||||
indexPattern: IndexPattern,
|
||||
showTimeCol: boolean,
|
||||
defaultColumns: boolean
|
||||
defaultColumns: boolean,
|
||||
isSortEnabled: boolean
|
||||
) {
|
||||
const timeFieldName = indexPattern.timeFieldName;
|
||||
const getColWidth = (column: string) => settings?.columns?.[column]?.width ?? 0;
|
||||
|
@ -108,12 +110,12 @@ export function getEuiGridColumns(
|
|||
if (showTimeCol && indexPattern.timeFieldName && !columns.find((col) => col === timeFieldName)) {
|
||||
const usedColumns = [indexPattern.timeFieldName, ...columns];
|
||||
return usedColumns.map((column) =>
|
||||
buildEuiGridColumn(column, getColWidth(column), indexPattern, defaultColumns)
|
||||
buildEuiGridColumn(column, getColWidth(column), indexPattern, defaultColumns, isSortEnabled)
|
||||
);
|
||||
}
|
||||
|
||||
return columns.map((column) =>
|
||||
buildEuiGridColumn(column, getColWidth(column), indexPattern, defaultColumns)
|
||||
buildEuiGridColumn(column, getColWidth(column), indexPattern, defaultColumns, isSortEnabled)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -51,7 +51,14 @@ describe('document selection', () => {
|
|||
|
||||
const component = mountWithIntl(
|
||||
<DiscoverGridContext.Provider value={contextMock}>
|
||||
<SelectButton rowIndex={0} />
|
||||
<SelectButton
|
||||
rowIndex={0}
|
||||
setCellProps={jest.fn()}
|
||||
columnId="test"
|
||||
isExpanded={false}
|
||||
isDetails={false}
|
||||
isExpandable={false}
|
||||
/>
|
||||
</DiscoverGridContext.Provider>
|
||||
);
|
||||
|
||||
|
@ -73,7 +80,14 @@ describe('document selection', () => {
|
|||
|
||||
const component = mountWithIntl(
|
||||
<DiscoverGridContext.Provider value={contextMock}>
|
||||
<SelectButton rowIndex={0} />
|
||||
<SelectButton
|
||||
rowIndex={0}
|
||||
setCellProps={jest.fn()}
|
||||
columnId="test"
|
||||
isExpanded={false}
|
||||
isDetails={false}
|
||||
isExpandable={false}
|
||||
/>
|
||||
</DiscoverGridContext.Provider>
|
||||
);
|
||||
|
||||
|
@ -95,7 +109,14 @@ describe('document selection', () => {
|
|||
|
||||
const component = mountWithIntl(
|
||||
<DiscoverGridContext.Provider value={contextMock}>
|
||||
<SelectButton rowIndex={0} />
|
||||
<SelectButton
|
||||
rowIndex={0}
|
||||
setCellProps={jest.fn()}
|
||||
columnId="test"
|
||||
isExpanded={false}
|
||||
isDetails={false}
|
||||
isExpandable={false}
|
||||
/>
|
||||
</DiscoverGridContext.Provider>
|
||||
);
|
||||
|
||||
|
@ -117,7 +138,14 @@ describe('document selection', () => {
|
|||
|
||||
const component = mountWithIntl(
|
||||
<DiscoverGridContext.Provider value={contextMock}>
|
||||
<SelectButton rowIndex={0} />
|
||||
<SelectButton
|
||||
rowIndex={0}
|
||||
setCellProps={jest.fn()}
|
||||
columnId="test"
|
||||
isExpanded={false}
|
||||
isDetails={false}
|
||||
isExpandable={false}
|
||||
/>
|
||||
</DiscoverGridContext.Provider>
|
||||
);
|
||||
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
import React, { useCallback, useState, useContext, useMemo } from 'react';
|
||||
import React, { useCallback, useState, useContext, useMemo, useEffect } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import {
|
||||
EuiButtonEmpty,
|
||||
EuiContextMenuItem,
|
||||
|
@ -13,9 +14,11 @@ import {
|
|||
EuiCopy,
|
||||
EuiPopover,
|
||||
EuiCheckbox,
|
||||
EuiDataGridCellValueElementProps,
|
||||
} from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import classNames from 'classnames';
|
||||
import themeDark from '@elastic/eui/dist/eui_theme_dark.json';
|
||||
import themeLight from '@elastic/eui/dist/eui_theme_light.json';
|
||||
import { ElasticSearchHit } from '../../doc_views/doc_views_types';
|
||||
import { DiscoverGridContext } from './discover_grid_context';
|
||||
|
||||
|
@ -27,11 +30,25 @@ export const getDocId = (doc: ElasticSearchHit & { _routing?: string }) => {
|
|||
const routing = doc._routing ? doc._routing : '';
|
||||
return [doc._index, doc._id, routing].join('::');
|
||||
};
|
||||
export const SelectButton = ({ rowIndex }: { rowIndex: number }) => {
|
||||
const ctx = useContext(DiscoverGridContext);
|
||||
const doc = useMemo(() => ctx.rows[rowIndex], [ctx.rows, rowIndex]);
|
||||
export const SelectButton = ({ rowIndex, setCellProps }: EuiDataGridCellValueElementProps) => {
|
||||
const { selectedDocs, expanded, rows, isDarkMode, setSelectedDocs } = useContext(
|
||||
DiscoverGridContext
|
||||
);
|
||||
const doc = useMemo(() => rows[rowIndex], [rows, rowIndex]);
|
||||
const id = useMemo(() => getDocId(doc), [doc]);
|
||||
const checked = useMemo(() => ctx.selectedDocs.includes(id), [ctx.selectedDocs, id]);
|
||||
const checked = useMemo(() => selectedDocs.includes(id), [selectedDocs, id]);
|
||||
|
||||
useEffect(() => {
|
||||
if (expanded && doc && expanded._id === doc._id) {
|
||||
setCellProps({
|
||||
style: {
|
||||
backgroundColor: isDarkMode ? themeDark.euiColorHighlight : themeLight.euiColorHighlight,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
setCellProps({ style: undefined });
|
||||
}
|
||||
}, [expanded, doc, setCellProps, isDarkMode]);
|
||||
|
||||
return (
|
||||
<EuiCheckbox
|
||||
|
@ -41,10 +58,10 @@ export const SelectButton = ({ rowIndex }: { rowIndex: number }) => {
|
|||
data-test-subj={`dscGridSelectDoc-${id}`}
|
||||
onChange={() => {
|
||||
if (checked) {
|
||||
const newSelection = ctx.selectedDocs.filter((docId) => docId !== id);
|
||||
ctx.setSelectedDocs(newSelection);
|
||||
const newSelection = selectedDocs.filter((docId) => docId !== id);
|
||||
setSelectedDocs(newSelection);
|
||||
} else {
|
||||
ctx.setSelectedDocs([...ctx.selectedDocs, id]);
|
||||
setSelectedDocs([...selectedDocs, id]);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
|
|
@ -12,6 +12,7 @@ import themeDark from '@elastic/eui/dist/eui_theme_dark.json';
|
|||
import themeLight from '@elastic/eui/dist/eui_theme_light.json';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { DiscoverGridContext } from './discover_grid_context';
|
||||
import { EsHitRecord } from '../../angular/context/api/context';
|
||||
/**
|
||||
* Button to expand a given row
|
||||
*/
|
||||
|
@ -19,7 +20,11 @@ export const ExpandButton = ({ rowIndex, setCellProps }: EuiDataGridCellValueEle
|
|||
const { expanded, setExpanded, rows, isDarkMode } = useContext(DiscoverGridContext);
|
||||
const current = rows[rowIndex];
|
||||
useEffect(() => {
|
||||
if (expanded && current && expanded._id === current._id) {
|
||||
if ((current as EsHitRecord).isAnchor) {
|
||||
setCellProps({
|
||||
className: 'dscDocsGrid__cell--highlight',
|
||||
});
|
||||
} else if (expanded && current && expanded._id === current._id) {
|
||||
setCellProps({
|
||||
style: {
|
||||
backgroundColor: isDarkMode ? themeDark.euiColorHighlight : themeLight.euiColorHighlight,
|
||||
|
|
|
@ -21,6 +21,7 @@ import { ElasticSearchHit } from '../../doc_views/doc_views_types';
|
|||
import { DiscoverGridContext } from './discover_grid_context';
|
||||
import { JsonCodeEditor } from '../json_code_editor/json_code_editor';
|
||||
import { defaultMonacoEditorWidth } from './constants';
|
||||
import { EsHitRecord } from '../../angular/context/api/context';
|
||||
|
||||
export const getRenderCellValueFn = (
|
||||
indexPattern: IndexPattern,
|
||||
|
@ -38,7 +39,11 @@ export const getRenderCellValueFn = (
|
|||
const ctx = useContext(DiscoverGridContext);
|
||||
|
||||
useEffect(() => {
|
||||
if (ctx.expanded && row && ctx.expanded._id === row._id) {
|
||||
if ((row as EsHitRecord).isAnchor) {
|
||||
setCellProps({
|
||||
className: 'dscDocsGrid__cell--highlight',
|
||||
});
|
||||
} else if (ctx.expanded && row && ctx.expanded._id === row._id) {
|
||||
setCellProps({
|
||||
style: {
|
||||
backgroundColor: ctx.isDarkMode
|
||||
|
|
|
@ -33,6 +33,7 @@ import { getServices, IndexPattern, ISearchSource } from '../../kibana_services'
|
|||
import { SEARCH_EMBEDDABLE_TYPE } from './constants';
|
||||
import { SavedSearch } from '../..';
|
||||
import {
|
||||
DOC_HIDE_TIME_COLUMN_SETTING,
|
||||
SAMPLE_SIZE_SETTING,
|
||||
SEARCH_FIELDS_FROM_SOURCE,
|
||||
SORT_DEFAULT_ORDER_SETTING,
|
||||
|
@ -256,7 +257,7 @@ export class SearchEmbeddable
|
|||
if (this.savedSearch.grid) {
|
||||
searchScope.settings = this.savedSearch.grid;
|
||||
}
|
||||
searchScope.showTimeCol = !this.services.uiSettings.get('doc_table:hideTimeColumn', false);
|
||||
searchScope.showTimeCol = !this.services.uiSettings.get(DOC_HIDE_TIME_COLUMN_SETTING, false);
|
||||
|
||||
searchScope.filter = async (field, value, operator) => {
|
||||
let filters = esFilters.generateFilters(
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* 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 } from '@testing-library/react-hooks';
|
||||
import { useDataGridColumns } from './use_data_grid_columns';
|
||||
import { indexPatternMock } from '../../__mocks__/index_pattern';
|
||||
import { configMock } from '../../__mocks__/config';
|
||||
import { indexPatternsMock } from '../../__mocks__/index_patterns';
|
||||
import { AppState } from '../angular/context_state';
|
||||
import { Capabilities } from '../../../../../core/types';
|
||||
|
||||
describe('useDataGridColumns', () => {
|
||||
const defaultProps = {
|
||||
capabilities: ({ discover: { save: true } } as unknown) as Capabilities,
|
||||
config: configMock,
|
||||
indexPattern: indexPatternMock,
|
||||
indexPatterns: indexPatternsMock,
|
||||
setAppState: () => {},
|
||||
state: {
|
||||
columns: ['Time', 'message'],
|
||||
} as AppState,
|
||||
useNewFieldsApi: false,
|
||||
};
|
||||
|
||||
test('should return valid result', () => {
|
||||
const { result } = renderHook(() => {
|
||||
return useDataGridColumns(defaultProps);
|
||||
});
|
||||
|
||||
expect(result.current.columns).toEqual(['Time', 'message']);
|
||||
expect(result.current.onAddColumn).toBeInstanceOf(Function);
|
||||
expect(result.current.onRemoveColumn).toBeInstanceOf(Function);
|
||||
expect(result.current.onMoveColumn).toBeInstanceOf(Function);
|
||||
expect(result.current.onSetColumns).toBeInstanceOf(Function);
|
||||
});
|
||||
|
||||
test('should skip _source column when useNewFieldsApi is set to true', () => {
|
||||
const { result } = renderHook(() => {
|
||||
return useDataGridColumns({
|
||||
...defaultProps,
|
||||
state: {
|
||||
columns: ['Time', '_source'],
|
||||
},
|
||||
useNewFieldsApi: true,
|
||||
});
|
||||
});
|
||||
|
||||
expect(result.current.columns).toEqual(['Time']);
|
||||
});
|
||||
|
||||
test('should return empty columns array', () => {
|
||||
const { result } = renderHook(() => {
|
||||
return useDataGridColumns({
|
||||
...defaultProps,
|
||||
state: {
|
||||
columns: [],
|
||||
},
|
||||
});
|
||||
});
|
||||
expect(result.current.columns).toEqual([]);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* 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 } from 'react';
|
||||
|
||||
import { Capabilities, IUiSettingsClient } from 'kibana/public';
|
||||
import { IndexPattern, IndexPatternsContract } from '../../kibana_services';
|
||||
import {
|
||||
AppState as DiscoverState,
|
||||
GetStateReturn as DiscoverGetStateReturn,
|
||||
} from '../angular/discover_state';
|
||||
import {
|
||||
AppState as ContextState,
|
||||
GetStateReturn as ContextGetStateReturn,
|
||||
} from '../angular/context_state';
|
||||
import { getStateColumnActions } from '../angular/doc_table/actions/columns';
|
||||
|
||||
interface UseDataGridColumnsProps {
|
||||
capabilities: Capabilities;
|
||||
config: IUiSettingsClient;
|
||||
indexPattern: IndexPattern;
|
||||
indexPatterns: IndexPatternsContract;
|
||||
useNewFieldsApi: boolean;
|
||||
setAppState: DiscoverGetStateReturn['setAppState'] | ContextGetStateReturn['setAppState'];
|
||||
state: DiscoverState | ContextState;
|
||||
}
|
||||
|
||||
export const useDataGridColumns = ({
|
||||
capabilities,
|
||||
config,
|
||||
indexPattern,
|
||||
indexPatterns,
|
||||
setAppState,
|
||||
state,
|
||||
useNewFieldsApi,
|
||||
}: UseDataGridColumnsProps) => {
|
||||
const { onAddColumn, onRemoveColumn, onSetColumns, onMoveColumn } = useMemo(
|
||||
() =>
|
||||
getStateColumnActions({
|
||||
capabilities,
|
||||
config,
|
||||
indexPattern,
|
||||
indexPatterns,
|
||||
setAppState,
|
||||
state,
|
||||
useNewFieldsApi,
|
||||
}),
|
||||
[capabilities, config, indexPattern, indexPatterns, setAppState, state, useNewFieldsApi]
|
||||
);
|
||||
|
||||
const columns = useMemo(() => {
|
||||
if (!state.columns) {
|
||||
return [];
|
||||
}
|
||||
return useNewFieldsApi ? state.columns.filter((col) => col !== '_source') : state.columns;
|
||||
}, [state, useNewFieldsApi]);
|
||||
|
||||
return {
|
||||
columns,
|
||||
onAddColumn,
|
||||
onRemoveColumn,
|
||||
onMoveColumn,
|
||||
onSetColumns,
|
||||
};
|
||||
};
|
|
@ -19,7 +19,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
const retry = getService('retry');
|
||||
const filterBar = getService('filterBar');
|
||||
const dataGrid = getService('dataGrid');
|
||||
const docTable = getService('docTable');
|
||||
const PageObjects = getPageObjects([
|
||||
'common',
|
||||
'discover',
|
||||
|
@ -67,16 +66,17 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
await dataGrid.clickRowToggle({ rowIndex: 0 });
|
||||
const rowActions = await dataGrid.getRowActions({ rowIndex: 0 });
|
||||
await rowActions[1].click();
|
||||
// entering the context view (contains the legacy type)
|
||||
const contextFields = await docTable.getFields();
|
||||
|
||||
const contextFields = await dataGrid.getFields();
|
||||
const anchorTimestamp = contextFields[0][0];
|
||||
|
||||
return anchorTimestamp === firstTimestamp;
|
||||
});
|
||||
});
|
||||
|
||||
it('should open the context view with the same columns', async () => {
|
||||
const columnNames = await docTable.getHeaderFields();
|
||||
expect(columnNames).to.eql(['Time', ...TEST_COLUMN_NAMES]);
|
||||
const columnNames = await dataGrid.getHeaderFields();
|
||||
expect(columnNames).to.eql(['Time (@timestamp)', ...TEST_COLUMN_NAMES]);
|
||||
});
|
||||
|
||||
it('should open the context view with the filters disabled', async () => {
|
||||
|
@ -111,7 +111,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
expect(await browser.getCurrentUrl()).to.contain('#/context');
|
||||
await PageObjects.header.waitUntilLoadingHasFinished();
|
||||
await retry.waitFor('document table has a length of 6', async () => {
|
||||
const nrOfDocs = (await docTable.getBodyRows()).length;
|
||||
const nrOfDocs = (await dataGrid.getBodyRows()).length;
|
||||
return nrOfDocs === 6;
|
||||
});
|
||||
});
|
||||
|
|
|
@ -135,6 +135,7 @@ export class DataGridService extends FtrService {
|
|||
if (!table) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const cells = await table.findAllByCssSelector('.euiDataGridRowCell');
|
||||
|
||||
const rows: WebElementWrapper[][] = [];
|
||||
|
@ -173,14 +174,13 @@ export class DataGridService extends FtrService {
|
|||
}
|
||||
|
||||
public async getHeaderFields(): Promise<string[]> {
|
||||
const result = await this.find.allByCssSelector('.euiDataGridHeaderCell__content');
|
||||
const result = await this.find.allByCssSelector(
|
||||
'.euiDataGridHeaderCell__button > .euiDataGridHeaderCell__content'
|
||||
);
|
||||
|
||||
const textArr = [];
|
||||
let idx = 0;
|
||||
for (const cell of result) {
|
||||
if (idx > 1) {
|
||||
textArr.push(await cell.getVisibleText());
|
||||
}
|
||||
idx++;
|
||||
textArr.push(await cell.getVisibleText());
|
||||
}
|
||||
return Promise.resolve(textArr);
|
||||
}
|
||||
|
|
|
@ -1553,7 +1553,6 @@
|
|||
"discover.bucketIntervalTooltip.tooLargeBucketsText": "大きすぎるバケット",
|
||||
"discover.bucketIntervalTooltip.tooManyBucketsText": "バケットが多すぎます",
|
||||
"discover.clearSelection": "選択した項目をクリア",
|
||||
"discover.context.breadcrumb": "{indexPatternTitle}#{docId} のコンテキスト",
|
||||
"discover.context.failedToLoadAnchorDocumentDescription": "アンカードキュメントの読み込みに失敗しました",
|
||||
"discover.context.failedToLoadAnchorDocumentErrorDescription": "アンカードキュメントの読み込みに失敗しました。",
|
||||
"discover.context.loadButtonLabel": "読み込み",
|
||||
|
|
|
@ -1562,7 +1562,6 @@
|
|||
"discover.bucketIntervalTooltip.tooLargeBucketsText": "存储桶过大",
|
||||
"discover.bucketIntervalTooltip.tooManyBucketsText": "存储桶过多",
|
||||
"discover.clearSelection": "清除所选内容",
|
||||
"discover.context.breadcrumb": "{indexPatternTitle}#{docId} 的上下文",
|
||||
"discover.context.failedToLoadAnchorDocumentDescription": "无法加载定位点文档",
|
||||
"discover.context.failedToLoadAnchorDocumentErrorDescription": "无法加载定位点文档。",
|
||||
"discover.context.loadButtonLabel": "加载",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue