This commit is contained in:
parent
34269bef14
commit
925f12a237
|
@ -24,7 +24,6 @@ import {
|
|||
import { getSortArray } from './doc_table';
|
||||
import * as columnActions from './doc_table/actions/columns';
|
||||
import indexTemplateLegacy from './discover_legacy.html';
|
||||
import indexTemplateGrid from './discover_datagrid.html';
|
||||
import { addHelpMenuToAppChrome } from '../components/help_menu/help_menu_util';
|
||||
import { discoverResponseHandler } from './response_handler';
|
||||
import {
|
||||
|
@ -112,9 +111,7 @@ app.config(($routeProvider) => {
|
|||
};
|
||||
const discoverRoute = {
|
||||
...defaults,
|
||||
template: getServices().uiSettings.get('doc_table:legacy', true)
|
||||
? indexTemplateLegacy
|
||||
: indexTemplateGrid,
|
||||
template: indexTemplateLegacy,
|
||||
reloadOnSearch: false,
|
||||
resolve: {
|
||||
savedObjects: function ($route, Promise) {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<discover-app>
|
||||
<discover-legacy
|
||||
<discover
|
||||
fetch="fetch"
|
||||
fetch-counter="fetchCounter"
|
||||
fetch-error="fetchError"
|
||||
|
@ -31,5 +31,5 @@
|
|||
update-saved-query-id="updateSavedQueryId"
|
||||
use-new-fields-api="useNewFieldsApi"
|
||||
>
|
||||
</discover-legacy>
|
||||
</discover>
|
||||
</discover-app>
|
||||
|
|
|
@ -9,8 +9,11 @@
|
|||
import angular, { auto, ICompileService, IScope } from 'angular';
|
||||
import { render } from 'react-dom';
|
||||
import React, { useRef, useEffect } from 'react';
|
||||
import { EuiButtonEmpty } from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import { getServices, IIndexPattern } from '../../../kibana_services';
|
||||
import { IndexPatternField } from '../../../../../data/common/index_patterns';
|
||||
|
||||
export type AngularScope = IScope;
|
||||
|
||||
export interface AngularDirective {
|
||||
|
@ -83,9 +86,11 @@ export interface DocTableLegacyProps {
|
|||
indexPattern: IIndexPattern;
|
||||
minimumVisibleRows: number;
|
||||
onAddColumn?: (column: string) => void;
|
||||
onBackToTop: () => void;
|
||||
onSort?: (sort: string[][]) => void;
|
||||
onMoveColumn?: (columns: string, newIdx: number) => void;
|
||||
onRemoveColumn?: (column: string) => void;
|
||||
sampleSize: number;
|
||||
sort?: string[][];
|
||||
useNewFieldsApi?: boolean;
|
||||
}
|
||||
|
@ -120,5 +125,31 @@ export function DocTableLegacy(renderProps: DocTableLegacyProps) {
|
|||
return renderFn(ref.current, renderProps);
|
||||
}
|
||||
}, [renderFn, renderProps]);
|
||||
return <div ref={ref} />;
|
||||
return (
|
||||
<div>
|
||||
<div ref={ref} />
|
||||
{renderProps.rows.length === renderProps.sampleSize ? (
|
||||
<div
|
||||
className="dscTable__footer"
|
||||
data-test-subj="discoverDocTableFooter"
|
||||
tabIndex={-1}
|
||||
id="discoverBottomMarker"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="discover.howToSeeOtherMatchingDocumentsDescription"
|
||||
defaultMessage="These are the first {sampleSize} documents matching
|
||||
your search, refine your search to see others."
|
||||
values={{ sampleSize: renderProps.sampleSize }}
|
||||
/>
|
||||
<EuiButtonEmpty onClick={renderProps.onBackToTop}>
|
||||
<FormattedMessage id="discover.backToTopLinkText" defaultMessage="Back to top." />
|
||||
</EuiButtonEmpty>
|
||||
</div>
|
||||
) : (
|
||||
<span tabIndex={-1} id="discoverBottomMarker">
|
||||
​
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -17,18 +17,21 @@ export function createDiscoverDirective(reactDirective: any) {
|
|||
['histogramData', { watchDepth: 'reference' }],
|
||||
['hits', { watchDepth: 'reference' }],
|
||||
['indexPattern', { watchDepth: 'reference' }],
|
||||
['minimumVisibleRows', { watchDepth: 'reference' }],
|
||||
['onAddColumn', { watchDepth: 'reference' }],
|
||||
['onAddFilter', { watchDepth: 'reference' }],
|
||||
['onChangeInterval', { watchDepth: 'reference' }],
|
||||
['onMoveColumn', { watchDepth: 'reference' }],
|
||||
['onRemoveColumn', { watchDepth: 'reference' }],
|
||||
['onSetColumns', { watchDepth: 'reference' }],
|
||||
['onSkipBottomButtonClick', { watchDepth: 'reference' }],
|
||||
['onSort', { watchDepth: 'reference' }],
|
||||
['opts', { watchDepth: 'reference' }],
|
||||
['resetQuery', { watchDepth: 'reference' }],
|
||||
['resultState', { watchDepth: 'reference' }],
|
||||
['rows', { watchDepth: 'reference' }],
|
||||
['savedSearch', { watchDepth: 'reference' }],
|
||||
['searchSource', { watchDepth: 'reference' }],
|
||||
['setColumns', { watchDepth: 'reference' }],
|
||||
['setIndexPattern', { watchDepth: 'reference' }],
|
||||
['showSaveQuery', { watchDepth: 'reference' }],
|
||||
['state', { watchDepth: 'reference' }],
|
||||
|
|
|
@ -1,45 +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
|
||||
* and the Server Side Public License, v 1; you may not use this file except in
|
||||
* compliance with, at your election, the Elastic License or the Server Side
|
||||
* Public License, v 1.
|
||||
*/
|
||||
|
||||
import { DiscoverLegacy } from './discover_legacy';
|
||||
|
||||
export function createDiscoverLegacyDirective(reactDirective: any) {
|
||||
return reactDirective(DiscoverLegacy, [
|
||||
['fetch', { watchDepth: 'reference' }],
|
||||
['fetchCounter', { watchDepth: 'reference' }],
|
||||
['fetchError', { watchDepth: 'reference' }],
|
||||
['fieldCounts', { watchDepth: 'reference' }],
|
||||
['histogramData', { watchDepth: 'reference' }],
|
||||
['hits', { watchDepth: 'reference' }],
|
||||
['indexPattern', { watchDepth: 'reference' }],
|
||||
['minimumVisibleRows', { watchDepth: 'reference' }],
|
||||
['onAddColumn', { watchDepth: 'reference' }],
|
||||
['onAddFilter', { watchDepth: 'reference' }],
|
||||
['onChangeInterval', { watchDepth: 'reference' }],
|
||||
['onMoveColumn', { watchDepth: 'reference' }],
|
||||
['onRemoveColumn', { watchDepth: 'reference' }],
|
||||
['onSetColumns', { watchDepth: 'reference' }],
|
||||
['onSkipBottomButtonClick', { watchDepth: 'reference' }],
|
||||
['onSort', { watchDepth: 'reference' }],
|
||||
['opts', { watchDepth: 'reference' }],
|
||||
['resetQuery', { watchDepth: 'reference' }],
|
||||
['resultState', { watchDepth: 'reference' }],
|
||||
['rows', { watchDepth: 'reference' }],
|
||||
['savedSearch', { watchDepth: 'reference' }],
|
||||
['searchSource', { watchDepth: 'reference' }],
|
||||
['setIndexPattern', { watchDepth: 'reference' }],
|
||||
['showSaveQuery', { watchDepth: 'reference' }],
|
||||
['state', { watchDepth: 'reference' }],
|
||||
['timefilterUpdateHandler', { watchDepth: 'reference' }],
|
||||
['timeRange', { watchDepth: 'reference' }],
|
||||
['topNavMenu', { watchDepth: 'reference' }],
|
||||
['updateQuery', { watchDepth: 'reference' }],
|
||||
['updateSavedQueryId', { watchDepth: 'reference' }],
|
||||
['useNewFieldsApi', { watchDepth: 'reference' }],
|
||||
]);
|
||||
}
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
import React from 'react';
|
||||
import { shallowWithIntl } from '@kbn/test/jest';
|
||||
import { DiscoverLegacy } from './discover_legacy';
|
||||
import { Discover } from './discover';
|
||||
import { inspectorPluginMock } from '../../../../inspector/public/mocks';
|
||||
import { esHits } from '../../__mocks__/es_hits';
|
||||
import { indexPatternMock } from '../../__mocks__/index_pattern';
|
||||
|
@ -19,7 +19,7 @@ import { savedSearchMock } from '../../__mocks__/saved_search';
|
|||
import { createSearchSourceMock } from '../../../../data/common/search/search_source/mocks';
|
||||
import { dataPluginMock } from '../../../../data/public/mocks';
|
||||
import { createFilterManagerMock } from '../../../../data/public/query/filter_manager/filter_manager.mock';
|
||||
import { uiSettingsMock } from '../../__mocks__/ui_settings';
|
||||
import { uiSettingsMock as mockUiSettings } from '../../__mocks__/ui_settings';
|
||||
import { IndexPattern, IndexPatternAttributes } from '../../../../data/common/index_patterns';
|
||||
import { SavedObject } from '../../../../../core/types';
|
||||
import { navigationPluginMock } from '../../../../navigation/public/mocks';
|
||||
|
@ -40,6 +40,7 @@ jest.mock('../../kibana_services', () => {
|
|||
},
|
||||
},
|
||||
navigation: mockNavigation,
|
||||
uiSettings: mockUiSettings,
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
@ -53,6 +54,7 @@ function getProps(indexPattern: IndexPattern) {
|
|||
save: true,
|
||||
},
|
||||
},
|
||||
uiSettings: mockUiSettings,
|
||||
} as unknown) as DiscoverServices;
|
||||
|
||||
return {
|
||||
|
@ -72,7 +74,7 @@ function getProps(indexPattern: IndexPattern) {
|
|||
onSkipBottomButtonClick: jest.fn(),
|
||||
onSort: jest.fn(),
|
||||
opts: {
|
||||
config: uiSettingsMock,
|
||||
config: mockUiSettings,
|
||||
data: dataPluginMock.createStartContract(),
|
||||
fixedScroll: jest.fn(),
|
||||
filterManager: createFilterManagerMock(),
|
||||
|
@ -105,15 +107,13 @@ function getProps(indexPattern: IndexPattern) {
|
|||
};
|
||||
}
|
||||
|
||||
describe('Descover legacy component', () => {
|
||||
describe('Discover component', () => {
|
||||
test('selected index pattern without time field displays no chart toggle', () => {
|
||||
const component = shallowWithIntl(<DiscoverLegacy {...getProps(indexPatternMock)} />);
|
||||
const component = shallowWithIntl(<Discover {...getProps(indexPatternMock)} />);
|
||||
expect(component.find('[data-test-subj="discoverChartToggle"]').length).toBe(0);
|
||||
});
|
||||
test('selected index pattern with time field displays chart toggle', () => {
|
||||
const component = shallowWithIntl(
|
||||
<DiscoverLegacy {...getProps(indexPatternWithTimefieldMock)} />
|
||||
);
|
||||
const component = shallowWithIntl(<Discover {...getProps(indexPatternWithTimefieldMock)} />);
|
||||
expect(component.find('[data-test-subj="discoverChartToggle"]').length).toBe(1);
|
||||
});
|
||||
});
|
|
@ -26,25 +26,30 @@ import classNames from 'classnames';
|
|||
import { HitsCounter } from './hits_counter';
|
||||
import { TimechartHeader } from './timechart_header';
|
||||
import { getServices } from '../../kibana_services';
|
||||
import { DiscoverUninitialized, DiscoverHistogram } from '../angular/directives';
|
||||
import { DiscoverHistogram, DiscoverUninitialized } from '../angular/directives';
|
||||
import { DiscoverNoResults } from './no_results';
|
||||
import { LoadingSpinner } from './loading_spinner/loading_spinner';
|
||||
import { DocTableLegacy, DocTableLegacyProps } from '../angular/doc_table/create_doc_table_react';
|
||||
import { SkipBottomButton } from './skip_bottom_button';
|
||||
import { search } from '../../../../data/public';
|
||||
import {
|
||||
DiscoverSidebarResponsive,
|
||||
DiscoverSidebarResponsiveProps,
|
||||
} from './sidebar/discover_sidebar_responsive';
|
||||
import { DiscoverProps } from './discover_legacy';
|
||||
import { DiscoverProps } from './types';
|
||||
import { getDisplayedColumns } from '../helpers/columns';
|
||||
import { SortPairArr } from '../angular/doc_table/lib/get_sort';
|
||||
import { DiscoverGrid, DiscoverGridProps } from './discover_grid/discover_grid';
|
||||
import { SEARCH_FIELDS_FROM_SOURCE } from '../../../common';
|
||||
|
||||
export const SidebarMemoized = React.memo((props: DiscoverSidebarResponsiveProps) => (
|
||||
const DocTableLegacyMemoized = React.memo((props: DocTableLegacyProps) => (
|
||||
<DocTableLegacy {...props} />
|
||||
));
|
||||
const SidebarMemoized = React.memo((props: DiscoverSidebarResponsiveProps) => (
|
||||
<DiscoverSidebarResponsive {...props} />
|
||||
));
|
||||
|
||||
export const DataGridMemoized = React.memo((props: DiscoverGridProps) => (
|
||||
<DiscoverGrid {...props} />
|
||||
));
|
||||
const DataGridMemoized = React.memo((props: DiscoverGridProps) => <DiscoverGrid {...props} />);
|
||||
|
||||
export function Discover({
|
||||
fetch,
|
||||
|
@ -54,11 +59,14 @@ export function Discover({
|
|||
histogramData,
|
||||
hits,
|
||||
indexPattern,
|
||||
minimumVisibleRows,
|
||||
onAddColumn,
|
||||
onAddFilter,
|
||||
onChangeInterval,
|
||||
onMoveColumn,
|
||||
onRemoveColumn,
|
||||
onSetColumns,
|
||||
onSkipBottomButtonClick,
|
||||
onSort,
|
||||
opts,
|
||||
resetQuery,
|
||||
|
@ -66,7 +74,6 @@ export function Discover({
|
|||
rows,
|
||||
searchSource,
|
||||
setIndexPattern,
|
||||
showSaveQuery,
|
||||
state,
|
||||
timefilterUpdateHandler,
|
||||
timeRange,
|
||||
|
@ -76,6 +83,11 @@ export function Discover({
|
|||
}: DiscoverProps) {
|
||||
const scrollableDesktop = useRef<HTMLDivElement>(null);
|
||||
const collapseIcon = useRef<HTMLButtonElement>(null);
|
||||
const isMobile = () => {
|
||||
// collapse icon isn't displayed in mobile view, use it to detect which view is displayed
|
||||
return collapseIcon && !collapseIcon.current;
|
||||
};
|
||||
|
||||
const [toggleOn, toggleChart] = useState(true);
|
||||
const [isSidebarClosed, setIsSidebarClosed] = useState(false);
|
||||
const services = getServices();
|
||||
|
@ -88,18 +100,8 @@ export function Discover({
|
|||
? bucketAggConfig.buckets?.getInterval()
|
||||
: undefined;
|
||||
const contentCentered = resultState === 'uninitialized';
|
||||
const showTimeCol = !config.get('doc_table:hideTimeColumn', false) && indexPattern.timeFieldName;
|
||||
const columns =
|
||||
state.columns &&
|
||||
state.columns.length > 0 &&
|
||||
// check if all columns where removed except the configured timeField (this can't be removed)
|
||||
!(state.columns.length === 1 && state.columns[0] === indexPattern.timeFieldName)
|
||||
? state.columns
|
||||
: ['_source'];
|
||||
// if columns include _source this is considered as default view, so you can't remove columns
|
||||
// until you add a column using Discover's sidebar
|
||||
const defaultColumns = columns.includes('_source');
|
||||
|
||||
const isLegacy = services.uiSettings.get('doc_table:legacy');
|
||||
const useNewFieldsApi = !services.uiSettings.get(SEARCH_FIELDS_FROM_SOURCE);
|
||||
return (
|
||||
<I18nProvider>
|
||||
<EuiPage className="dscPage" data-fetch-counter={fetchCounter}>
|
||||
|
@ -114,7 +116,7 @@ export function Discover({
|
|||
savedQueryId={state.savedQuery}
|
||||
screenTitle={savedSearch.title}
|
||||
showDatePicker={indexPattern.isTimeBased()}
|
||||
showSaveQuery={showSaveQuery}
|
||||
showSaveQuery={!!services.capabilities.discover.saveQuery}
|
||||
showSearchBar={true}
|
||||
useDefaultBehaviors={true}
|
||||
/>
|
||||
|
@ -137,6 +139,7 @@ export function Discover({
|
|||
setIndexPattern={setIndexPattern}
|
||||
isClosed={isSidebarClosed}
|
||||
trackUiMetric={trackUiMetric}
|
||||
useNewFieldsApi={useNewFieldsApi}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiHideFor sizes={['xs', 's']}>
|
||||
|
@ -207,24 +210,28 @@ export function Discover({
|
|||
/>
|
||||
</EuiFlexItem>
|
||||
)}
|
||||
<EuiFlexItem className="dscResultCount__toggle" grow={false}>
|
||||
<EuiButtonEmpty
|
||||
size="xs"
|
||||
iconType={toggleOn ? 'eyeClosed' : 'eye'}
|
||||
onClick={() => {
|
||||
toggleChart(!toggleOn);
|
||||
}}
|
||||
>
|
||||
{toggleOn
|
||||
? i18n.translate('discover.hideChart', {
|
||||
defaultMessage: 'Hide chart',
|
||||
})
|
||||
: i18n.translate('discover.showChart', {
|
||||
defaultMessage: 'Show chart',
|
||||
})}
|
||||
</EuiButtonEmpty>
|
||||
</EuiFlexItem>
|
||||
{opts.timefield && (
|
||||
<EuiFlexItem className="dscResultCount__toggle" grow={false}>
|
||||
<EuiButtonEmpty
|
||||
size="xs"
|
||||
iconType={toggleOn ? 'eyeClosed' : 'eye'}
|
||||
onClick={() => {
|
||||
toggleChart(!toggleOn);
|
||||
}}
|
||||
data-test-subj="discoverChartToggle"
|
||||
>
|
||||
{toggleOn
|
||||
? i18n.translate('discover.hideChart', {
|
||||
defaultMessage: 'Hide chart',
|
||||
})
|
||||
: i18n.translate('discover.showChart', {
|
||||
defaultMessage: 'Show chart',
|
||||
})}
|
||||
</EuiButtonEmpty>
|
||||
</EuiFlexItem>
|
||||
)}
|
||||
</EuiFlexGroup>
|
||||
{isLegacy && <SkipBottomButton onClick={onSkipBottomButtonClick} />}
|
||||
</EuiFlexItem>
|
||||
{toggleOn && opts.timefield && (
|
||||
<EuiFlexItem grow={false}>
|
||||
|
@ -238,7 +245,10 @@ export function Discover({
|
|||
className="dscTimechart"
|
||||
>
|
||||
{opts.chartAggConfigs && histogramData && rows.length !== 0 && (
|
||||
<div className="dscHistogramGrid" data-test-subj="discoverChart">
|
||||
<div
|
||||
className={isLegacy ? 'dscHistogram' : 'dscHistogramGrid'}
|
||||
data-test-subj="discoverChart"
|
||||
>
|
||||
<DiscoverHistogram
|
||||
chartData={histogramData}
|
||||
timefilterUpdateHandler={timefilterUpdateHandler}
|
||||
|
@ -265,19 +275,50 @@ export function Discover({
|
|||
defaultMessage="Documents"
|
||||
/>
|
||||
</h2>
|
||||
{rows && rows.length && (
|
||||
{isLegacy && rows && rows.length && (
|
||||
<DocTableLegacyMemoized
|
||||
columns={state.columns || []}
|
||||
indexPattern={indexPattern}
|
||||
minimumVisibleRows={minimumVisibleRows}
|
||||
rows={rows}
|
||||
sort={state.sort || []}
|
||||
searchDescription={opts.savedSearch.description}
|
||||
searchTitle={opts.savedSearch.lastSavedTitle}
|
||||
onAddColumn={onAddColumn}
|
||||
onBackToTop={() => {
|
||||
if (scrollableDesktop && scrollableDesktop.current) {
|
||||
scrollableDesktop.current.focus();
|
||||
}
|
||||
// Only the desktop one needs to target a specific container
|
||||
if (!isMobile() && scrollableDesktop.current) {
|
||||
scrollableDesktop.current.scrollTo(0, 0);
|
||||
} else if (window) {
|
||||
window.scrollTo(0, 0);
|
||||
}
|
||||
}}
|
||||
onFilter={onAddFilter}
|
||||
onMoveColumn={onMoveColumn}
|
||||
onRemoveColumn={onRemoveColumn}
|
||||
onSort={onSort}
|
||||
sampleSize={opts.sampleSize}
|
||||
useNewFieldsApi={useNewFieldsApi}
|
||||
/>
|
||||
)}
|
||||
{!isLegacy && rows && rows.length && (
|
||||
<div className="dscDiscoverGrid">
|
||||
<DataGridMemoized
|
||||
ariaLabelledBy="documentsAriaLabel"
|
||||
columns={columns}
|
||||
defaultColumns={defaultColumns}
|
||||
columns={getDisplayedColumns(state.columns, indexPattern)}
|
||||
indexPattern={indexPattern}
|
||||
rows={rows}
|
||||
sort={(state.sort as SortPairArr[]) || []}
|
||||
sampleSize={opts.sampleSize}
|
||||
searchDescription={opts.savedSearch.description}
|
||||
searchTitle={opts.savedSearch.lastSavedTitle}
|
||||
showTimeCol={Boolean(showTimeCol)}
|
||||
showTimeCol={
|
||||
!config.get('doc_table:hideTimeColumn', false) &&
|
||||
!!indexPattern.timeFieldName
|
||||
}
|
||||
services={services}
|
||||
settings={state.grid}
|
||||
onAddColumn={onAddColumn}
|
||||
|
|
|
@ -50,12 +50,6 @@ export interface DiscoverGridProps {
|
|||
* Determines which columns are displayed
|
||||
*/
|
||||
columns: string[];
|
||||
/**
|
||||
* Determines whether the given columns are the default ones, so parts of the document
|
||||
* are displayed (_source) with limited actions (cannor move, remove columns)
|
||||
* Implemented for matching with legacy behavior
|
||||
*/
|
||||
defaultColumns: boolean;
|
||||
/**
|
||||
* The used index pattern
|
||||
*/
|
||||
|
@ -126,7 +120,6 @@ export const EuiDataGridMemoized = React.memo((props: EuiDataGridProps) => {
|
|||
export const DiscoverGrid = ({
|
||||
ariaLabelledBy,
|
||||
columns,
|
||||
defaultColumns,
|
||||
indexPattern,
|
||||
onAddColumn,
|
||||
onFilter,
|
||||
|
@ -144,6 +137,7 @@ export const DiscoverGrid = ({
|
|||
sort,
|
||||
}: DiscoverGridProps) => {
|
||||
const [expanded, setExpanded] = useState<ElasticSearchHit | undefined>(undefined);
|
||||
const defaultColumns = columns.includes('_source');
|
||||
|
||||
/**
|
||||
* Pagination
|
||||
|
|
|
@ -48,7 +48,12 @@ export function buildEuiGridColumn(
|
|||
id: columnName,
|
||||
schema: getSchemaByKbnType(indexPatternField?.type),
|
||||
isSortable: indexPatternField?.sortable,
|
||||
display: indexPatternField?.displayName,
|
||||
display:
|
||||
columnName === '_source'
|
||||
? i18n.translate('discover.grid.documentHeader', {
|
||||
defaultMessage: 'Document',
|
||||
})
|
||||
: indexPatternField?.displayName,
|
||||
actions: {
|
||||
showHide:
|
||||
defaultColumns || columnName === indexPattern.timeFieldName
|
||||
|
|
|
@ -1,517 +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
|
||||
* and the Server Side Public License, v 1; you may not use this file except in
|
||||
* compliance with, at your election, the Elastic License or the Server Side
|
||||
* Public License, v 1.
|
||||
*/
|
||||
|
||||
import './discover.scss';
|
||||
|
||||
import React, { useState, useRef } from 'react';
|
||||
import {
|
||||
EuiButtonEmpty,
|
||||
EuiButtonIcon,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiHideFor,
|
||||
EuiPage,
|
||||
EuiPageBody,
|
||||
EuiPageContent,
|
||||
} from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage, I18nProvider } from '@kbn/i18n/react';
|
||||
import { IUiSettingsClient, MountPoint } from 'kibana/public';
|
||||
import classNames from 'classnames';
|
||||
import { HitsCounter } from './hits_counter';
|
||||
import { TimechartHeader } from './timechart_header';
|
||||
import { getServices, IndexPattern } from '../../kibana_services';
|
||||
import { DiscoverUninitialized, DiscoverHistogram } from '../angular/directives';
|
||||
import { DiscoverNoResults } from './no_results';
|
||||
import { LoadingSpinner } from './loading_spinner/loading_spinner';
|
||||
import { DocTableLegacy, DocTableLegacyProps } from '../angular/doc_table/create_doc_table_react';
|
||||
import { SkipBottomButton } from './skip_bottom_button';
|
||||
import {
|
||||
search,
|
||||
ISearchSource,
|
||||
TimeRange,
|
||||
Query,
|
||||
IndexPatternAttributes,
|
||||
DataPublicPluginStart,
|
||||
AggConfigs,
|
||||
FilterManager,
|
||||
} from '../../../../data/public';
|
||||
import { Chart } from '../angular/helpers/point_series';
|
||||
import { AppState } from '../angular/discover_state';
|
||||
import { SavedSearch } from '../../saved_searches';
|
||||
import { SavedObject } from '../../../../../core/types';
|
||||
import { TopNavMenuData } from '../../../../navigation/public';
|
||||
import {
|
||||
DiscoverSidebarResponsive,
|
||||
DiscoverSidebarResponsiveProps,
|
||||
} from './sidebar/discover_sidebar_responsive';
|
||||
import { DocViewFilterFn, ElasticSearchHit } from '../doc_views/doc_views_types';
|
||||
|
||||
export interface DiscoverProps {
|
||||
/**
|
||||
* Function to fetch documents from Elasticsearch
|
||||
*/
|
||||
fetch: () => void;
|
||||
/**
|
||||
* Counter how often data was fetched (used for testing)
|
||||
*/
|
||||
fetchCounter: number;
|
||||
/**
|
||||
* Error in case of a failing document fetch
|
||||
*/
|
||||
fetchError?: Error;
|
||||
/**
|
||||
* Statistics by fields calculated using the fetched documents
|
||||
*/
|
||||
fieldCounts: Record<string, number>;
|
||||
/**
|
||||
* Histogram aggregation data
|
||||
*/
|
||||
histogramData?: Chart;
|
||||
/**
|
||||
* Number of documents found by recent fetch
|
||||
*/
|
||||
hits: number;
|
||||
/**
|
||||
* Current IndexPattern
|
||||
*/
|
||||
indexPattern: IndexPattern;
|
||||
/**
|
||||
* Value needed for legacy "infinite" loading functionality
|
||||
* Determins how much records are rendered using the legacy table
|
||||
* Increased when scrolling down
|
||||
*/
|
||||
minimumVisibleRows: number;
|
||||
/**
|
||||
* Function to add a column to state
|
||||
*/
|
||||
onAddColumn: (column: string) => void;
|
||||
/**
|
||||
* Function to add a filter to state
|
||||
*/
|
||||
onAddFilter: DocViewFilterFn;
|
||||
/**
|
||||
* Function to change the used time interval of the date histogram
|
||||
*/
|
||||
onChangeInterval: (interval: string) => void;
|
||||
/**
|
||||
* Function to move a given column to a given index, used in legacy table
|
||||
*/
|
||||
onMoveColumn: (columns: string, newIdx: number) => void;
|
||||
/**
|
||||
* Function to remove a given column from state
|
||||
*/
|
||||
onRemoveColumn: (column: string) => void;
|
||||
/**
|
||||
* Function to replace columns in state
|
||||
*/
|
||||
onSetColumns: (columns: string[]) => void;
|
||||
/**
|
||||
* Function to scroll down the legacy table to the bottom
|
||||
*/
|
||||
onSkipBottomButtonClick: () => void;
|
||||
/**
|
||||
* Function to change sorting of the table, triggers a fetch
|
||||
*/
|
||||
onSort: (sort: string[][]) => void;
|
||||
opts: {
|
||||
/**
|
||||
* Date histogram aggregation config
|
||||
*/
|
||||
chartAggConfigs?: AggConfigs;
|
||||
/**
|
||||
* Client of uiSettings
|
||||
*/
|
||||
config: IUiSettingsClient;
|
||||
/**
|
||||
* Data plugin
|
||||
*/
|
||||
data: DataPublicPluginStart;
|
||||
/**
|
||||
* Data plugin filter manager
|
||||
*/
|
||||
filterManager: FilterManager;
|
||||
/**
|
||||
* List of available index patterns
|
||||
*/
|
||||
indexPatternList: Array<SavedObject<IndexPatternAttributes>>;
|
||||
/**
|
||||
* The number of documents that can be displayed in the table/grid
|
||||
*/
|
||||
sampleSize: number;
|
||||
/**
|
||||
* Current instance of SavedSearch
|
||||
*/
|
||||
savedSearch: SavedSearch;
|
||||
/**
|
||||
* Function to set the header menu
|
||||
*/
|
||||
setHeaderActionMenu: (menuMount: MountPoint | undefined) => void;
|
||||
/**
|
||||
* Timefield of the currently used index pattern
|
||||
*/
|
||||
timefield: string;
|
||||
/**
|
||||
* Function to set the current state
|
||||
*/
|
||||
setAppState: (state: Partial<AppState>) => void;
|
||||
};
|
||||
/**
|
||||
* Function to reset the current query
|
||||
*/
|
||||
resetQuery: () => void;
|
||||
/**
|
||||
* Current state of the actual query, one of 'uninitialized', 'loading' ,'ready', 'none'
|
||||
*/
|
||||
resultState: string;
|
||||
/**
|
||||
* Array of document of the recent successful search request
|
||||
*/
|
||||
rows: ElasticSearchHit[];
|
||||
/**
|
||||
* Instance of SearchSource, the high level search API
|
||||
*/
|
||||
searchSource: ISearchSource;
|
||||
/**
|
||||
* Function to change the current index pattern
|
||||
*/
|
||||
setIndexPattern: (id: string) => void;
|
||||
/**
|
||||
* Determines whether the user should be able to use the save query feature
|
||||
*/
|
||||
showSaveQuery: boolean;
|
||||
/**
|
||||
* Current app state of URL
|
||||
*/
|
||||
state: AppState;
|
||||
/**
|
||||
* Function to update the time filter
|
||||
*/
|
||||
timefilterUpdateHandler: (ranges: { from: number; to: number }) => void;
|
||||
/**
|
||||
* Currently selected time range
|
||||
*/
|
||||
timeRange?: { from: string; to: string };
|
||||
/**
|
||||
* Menu data of top navigation (New, save ...)
|
||||
*/
|
||||
topNavMenu: TopNavMenuData[];
|
||||
/**
|
||||
* Function to update the actual query
|
||||
*/
|
||||
updateQuery: (payload: { dateRange: TimeRange; query?: Query }, isUpdate?: boolean) => void;
|
||||
/**
|
||||
* Function to update the actual savedQuery id
|
||||
*/
|
||||
updateSavedQueryId: (savedQueryId?: string) => void;
|
||||
useNewFieldsApi?: boolean;
|
||||
}
|
||||
|
||||
export const DocTableLegacyMemoized = React.memo((props: DocTableLegacyProps) => (
|
||||
<DocTableLegacy {...props} />
|
||||
));
|
||||
export const SidebarMemoized = React.memo((props: DiscoverSidebarResponsiveProps) => (
|
||||
<DiscoverSidebarResponsive {...props} />
|
||||
));
|
||||
|
||||
export function DiscoverLegacy({
|
||||
fetch,
|
||||
fetchCounter,
|
||||
fieldCounts,
|
||||
fetchError,
|
||||
histogramData,
|
||||
hits,
|
||||
indexPattern,
|
||||
minimumVisibleRows,
|
||||
onAddColumn,
|
||||
onAddFilter,
|
||||
onChangeInterval,
|
||||
onMoveColumn,
|
||||
onRemoveColumn,
|
||||
onSkipBottomButtonClick,
|
||||
onSort,
|
||||
opts,
|
||||
resetQuery,
|
||||
resultState,
|
||||
rows,
|
||||
searchSource,
|
||||
setIndexPattern,
|
||||
showSaveQuery,
|
||||
state,
|
||||
timefilterUpdateHandler,
|
||||
timeRange,
|
||||
topNavMenu,
|
||||
updateQuery,
|
||||
updateSavedQueryId,
|
||||
useNewFieldsApi,
|
||||
}: DiscoverProps) {
|
||||
const scrollableDesktop = useRef<HTMLDivElement>(null);
|
||||
const collapseIcon = useRef<HTMLButtonElement>(null);
|
||||
const isMobile = () => {
|
||||
// collapse icon isn't displayed in mobile view, use it to detect which view is displayed
|
||||
return collapseIcon && !collapseIcon.current;
|
||||
};
|
||||
|
||||
const [toggleOn, toggleChart] = useState(true);
|
||||
const [isSidebarClosed, setIsSidebarClosed] = useState(false);
|
||||
const services = getServices();
|
||||
const { TopNavMenu } = services.navigation.ui;
|
||||
const { trackUiMetric } = services;
|
||||
const { savedSearch, indexPatternList } = opts;
|
||||
const bucketAggConfig = opts.chartAggConfigs?.aggs[1];
|
||||
const bucketInterval =
|
||||
bucketAggConfig && search.aggs.isDateHistogramBucketAggConfig(bucketAggConfig)
|
||||
? bucketAggConfig.buckets?.getInterval()
|
||||
: undefined;
|
||||
const contentCentered = resultState === 'uninitialized';
|
||||
|
||||
const getDisplayColumns = () => {
|
||||
if (!state.columns) {
|
||||
return [];
|
||||
}
|
||||
const columns = [...state.columns];
|
||||
if (useNewFieldsApi) {
|
||||
return columns.filter((column) => column !== '_source');
|
||||
}
|
||||
return columns.length === 0 ? ['_source'] : columns;
|
||||
};
|
||||
|
||||
return (
|
||||
<I18nProvider>
|
||||
<EuiPage className="dscPage" data-fetch-counter={fetchCounter}>
|
||||
<TopNavMenu
|
||||
appName="discover"
|
||||
config={topNavMenu}
|
||||
indexPatterns={[indexPattern]}
|
||||
onQuerySubmit={updateQuery}
|
||||
onSavedQueryIdChange={updateSavedQueryId}
|
||||
query={state.query}
|
||||
setMenuMountPoint={opts.setHeaderActionMenu}
|
||||
savedQueryId={state.savedQuery}
|
||||
screenTitle={savedSearch.title}
|
||||
showDatePicker={indexPattern.isTimeBased()}
|
||||
showSaveQuery={showSaveQuery}
|
||||
showSearchBar={true}
|
||||
useDefaultBehaviors={true}
|
||||
/>
|
||||
<EuiPageBody className="dscPageBody" aria-describedby="savedSearchTitle">
|
||||
<h1 id="savedSearchTitle" className="euiScreenReaderOnly">
|
||||
{savedSearch.title}
|
||||
</h1>
|
||||
<EuiFlexGroup className="dscPageBody__contents" gutterSize="none">
|
||||
<EuiFlexItem grow={false}>
|
||||
<SidebarMemoized
|
||||
columns={state.columns || []}
|
||||
fieldCounts={fieldCounts}
|
||||
hits={rows}
|
||||
indexPatternList={indexPatternList}
|
||||
onAddField={onAddColumn}
|
||||
onAddFilter={onAddFilter}
|
||||
onRemoveField={onRemoveColumn}
|
||||
selectedIndexPattern={searchSource && searchSource.getField('index')}
|
||||
services={services}
|
||||
setIndexPattern={setIndexPattern}
|
||||
isClosed={isSidebarClosed}
|
||||
trackUiMetric={trackUiMetric}
|
||||
useNewFieldsApi={useNewFieldsApi}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiHideFor sizes={['xs', 's']}>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButtonIcon
|
||||
iconType={isSidebarClosed ? 'menuRight' : 'menuLeft'}
|
||||
iconSize="m"
|
||||
size="s"
|
||||
onClick={() => setIsSidebarClosed(!isSidebarClosed)}
|
||||
data-test-subj="collapseSideBarButton"
|
||||
aria-controls="discover-sidebar"
|
||||
aria-expanded={isSidebarClosed ? 'false' : 'true'}
|
||||
aria-label="Toggle sidebar"
|
||||
buttonRef={collapseIcon}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiHideFor>
|
||||
<EuiFlexItem className="dscPageContent__wrapper">
|
||||
<EuiPageContent
|
||||
verticalPosition={contentCentered ? 'center' : undefined}
|
||||
horizontalPosition={contentCentered ? 'center' : undefined}
|
||||
paddingSize="none"
|
||||
className={classNames('dscPageContent', {
|
||||
'dscPageContent--centered': contentCentered,
|
||||
})}
|
||||
>
|
||||
{resultState === 'none' && (
|
||||
<DiscoverNoResults
|
||||
timeFieldName={opts.timefield}
|
||||
queryLanguage={state.query ? state.query.language : ''}
|
||||
data={opts.data}
|
||||
error={fetchError}
|
||||
/>
|
||||
)}
|
||||
{resultState === 'uninitialized' && <DiscoverUninitialized onRefresh={fetch} />}
|
||||
{resultState === 'loading' && <LoadingSpinner />}
|
||||
{resultState === 'ready' && (
|
||||
<EuiFlexGroup
|
||||
className="dscPageContent__inner"
|
||||
direction="column"
|
||||
alignItems="stretch"
|
||||
gutterSize="none"
|
||||
responsive={false}
|
||||
>
|
||||
<EuiFlexItem grow={false} className="dscResultCount">
|
||||
<EuiFlexGroup alignItems="center" justifyContent="spaceBetween">
|
||||
<EuiFlexItem
|
||||
grow={false}
|
||||
className="dscResuntCount__title eui-textTruncate eui-textNoWrap"
|
||||
>
|
||||
<HitsCounter
|
||||
hits={hits > 0 ? hits : 0}
|
||||
showResetButton={!!(savedSearch && savedSearch.id)}
|
||||
onResetQuery={resetQuery}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
{toggleOn && (
|
||||
<EuiFlexItem className="dscResultCount__actions">
|
||||
<TimechartHeader
|
||||
dateFormat={opts.config.get('dateFormat')}
|
||||
timeRange={timeRange}
|
||||
options={search.aggs.intervalOptions}
|
||||
onChangeInterval={onChangeInterval}
|
||||
stateInterval={state.interval || ''}
|
||||
bucketInterval={bucketInterval}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
)}
|
||||
{opts.timefield && (
|
||||
<EuiFlexItem className="dscResultCount__toggle" grow={false}>
|
||||
<EuiButtonEmpty
|
||||
size="xs"
|
||||
iconType={toggleOn ? 'eyeClosed' : 'eye'}
|
||||
onClick={() => {
|
||||
toggleChart(!toggleOn);
|
||||
}}
|
||||
data-test-subj="discoverChartToggle"
|
||||
>
|
||||
{toggleOn
|
||||
? i18n.translate('discover.hideChart', {
|
||||
defaultMessage: 'Hide chart',
|
||||
})
|
||||
: i18n.translate('discover.showChart', {
|
||||
defaultMessage: 'Show chart',
|
||||
})}
|
||||
</EuiButtonEmpty>
|
||||
</EuiFlexItem>
|
||||
)}
|
||||
</EuiFlexGroup>
|
||||
<SkipBottomButton onClick={onSkipBottomButtonClick} />
|
||||
</EuiFlexItem>
|
||||
{toggleOn && opts.timefield && (
|
||||
<EuiFlexItem grow={false}>
|
||||
<section
|
||||
aria-label={i18n.translate(
|
||||
'discover.histogramOfFoundDocumentsAriaLabel',
|
||||
{
|
||||
defaultMessage: 'Histogram of found documents',
|
||||
}
|
||||
)}
|
||||
className="dscTimechart"
|
||||
>
|
||||
{opts.chartAggConfigs && rows.length !== 0 && histogramData && (
|
||||
<div className="dscHistogram" data-test-subj="discoverChart">
|
||||
<DiscoverHistogram
|
||||
chartData={histogramData}
|
||||
timefilterUpdateHandler={timefilterUpdateHandler}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
</EuiFlexItem>
|
||||
)}
|
||||
|
||||
<EuiFlexItem className="eui-yScroll">
|
||||
<section
|
||||
className="dscTable eui-yScroll"
|
||||
aria-labelledby="documentsAriaLabel"
|
||||
ref={scrollableDesktop}
|
||||
tabIndex={-1}
|
||||
>
|
||||
<h2 className="euiScreenReaderOnly" id="documentsAriaLabel">
|
||||
<FormattedMessage
|
||||
id="discover.documentsAriaLabel"
|
||||
defaultMessage="Documents"
|
||||
/>
|
||||
</h2>
|
||||
{rows && rows.length && (
|
||||
<div>
|
||||
<DocTableLegacyMemoized
|
||||
columns={getDisplayColumns()}
|
||||
indexPattern={indexPattern}
|
||||
minimumVisibleRows={minimumVisibleRows}
|
||||
rows={rows}
|
||||
sort={state.sort || []}
|
||||
searchDescription={opts.savedSearch.description}
|
||||
searchTitle={opts.savedSearch.lastSavedTitle}
|
||||
onAddColumn={onAddColumn}
|
||||
onFilter={onAddFilter}
|
||||
onMoveColumn={onMoveColumn}
|
||||
onRemoveColumn={onRemoveColumn}
|
||||
onSort={onSort}
|
||||
useNewFieldsApi={useNewFieldsApi}
|
||||
/>
|
||||
{rows.length === opts.sampleSize ? (
|
||||
<div
|
||||
className="dscTable__footer"
|
||||
data-test-subj="discoverDocTableFooter"
|
||||
tabIndex={-1}
|
||||
id="discoverBottomMarker"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="discover.howToSeeOtherMatchingDocumentsDescription"
|
||||
defaultMessage="These are the first {sampleSize} documents matching
|
||||
your search, refine your search to see others."
|
||||
values={{ sampleSize: opts.sampleSize }}
|
||||
/>
|
||||
|
||||
<EuiButtonEmpty
|
||||
onClick={() => {
|
||||
if (scrollableDesktop && scrollableDesktop.current) {
|
||||
scrollableDesktop.current.focus();
|
||||
}
|
||||
// Only the desktop one needs to target a specific container
|
||||
if (!isMobile() && scrollableDesktop.current) {
|
||||
scrollableDesktop.current.scrollTo(0, 0);
|
||||
} else if (window) {
|
||||
window.scrollTo(0, 0);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="discover.backToTopLinkText"
|
||||
defaultMessage="Back to top."
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
</div>
|
||||
) : (
|
||||
<span tabIndex={-1} id="discoverBottomMarker">
|
||||
​
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
)}
|
||||
</EuiPageContent>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiPageBody>
|
||||
</EuiPage>
|
||||
</I18nProvider>
|
||||
);
|
||||
}
|
179
src/plugins/discover/public/application/components/types.ts
Normal file
179
src/plugins/discover/public/application/components/types.ts
Normal file
|
@ -0,0 +1,179 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* and the Server Side Public License, v 1; you may not use this file except in
|
||||
* compliance with, at your election, the Elastic License or the Server Side
|
||||
* Public License, v 1.
|
||||
*/
|
||||
import { IUiSettingsClient, MountPoint, SavedObject } from 'kibana/public';
|
||||
import { Chart } from '../angular/helpers/point_series';
|
||||
import { IndexPattern } from '../../../../data/common/index_patterns/index_patterns';
|
||||
import { DocViewFilterFn, ElasticSearchHit } from '../doc_views/doc_views_types';
|
||||
import { AggConfigs } from '../../../../data/common/search/aggs';
|
||||
|
||||
import {
|
||||
DataPublicPluginStart,
|
||||
FilterManager,
|
||||
IndexPatternAttributes,
|
||||
ISearchSource,
|
||||
Query,
|
||||
TimeRange,
|
||||
} from '../../../../data/public';
|
||||
import { SavedSearch } from '../../saved_searches';
|
||||
import { AppState } from '../angular/discover_state';
|
||||
import { TopNavMenuData } from '../../../../navigation/public';
|
||||
|
||||
export interface DiscoverProps {
|
||||
/**
|
||||
* Function to fetch documents from Elasticsearch
|
||||
*/
|
||||
fetch: () => void;
|
||||
/**
|
||||
* Counter how often data was fetched (used for testing)
|
||||
*/
|
||||
fetchCounter: number;
|
||||
/**
|
||||
* Error in case of a failing document fetch
|
||||
*/
|
||||
fetchError?: Error;
|
||||
/**
|
||||
* Statistics by fields calculated using the fetched documents
|
||||
*/
|
||||
fieldCounts: Record<string, number>;
|
||||
/**
|
||||
* Histogram aggregation data
|
||||
*/
|
||||
histogramData?: Chart;
|
||||
/**
|
||||
* Number of documents found by recent fetch
|
||||
*/
|
||||
hits: number;
|
||||
/**
|
||||
* Current IndexPattern
|
||||
*/
|
||||
indexPattern: IndexPattern;
|
||||
/**
|
||||
* Value needed for legacy "infinite" loading functionality
|
||||
* Determins how much records are rendered using the legacy table
|
||||
* Increased when scrolling down
|
||||
*/
|
||||
minimumVisibleRows: number;
|
||||
/**
|
||||
* Function to add a column to state
|
||||
*/
|
||||
onAddColumn: (column: string) => void;
|
||||
/**
|
||||
* Function to add a filter to state
|
||||
*/
|
||||
onAddFilter: DocViewFilterFn;
|
||||
/**
|
||||
* Function to change the used time interval of the date histogram
|
||||
*/
|
||||
onChangeInterval: (interval: string) => void;
|
||||
/**
|
||||
* Function to move a given column to a given index, used in legacy table
|
||||
*/
|
||||
onMoveColumn: (columns: string, newIdx: number) => void;
|
||||
/**
|
||||
* Function to remove a given column from state
|
||||
*/
|
||||
onRemoveColumn: (column: string) => void;
|
||||
/**
|
||||
* Function to replace columns in state
|
||||
*/
|
||||
onSetColumns: (columns: string[]) => void;
|
||||
/**
|
||||
* Function to scroll down the legacy table to the bottom
|
||||
*/
|
||||
onSkipBottomButtonClick: () => void;
|
||||
/**
|
||||
* Function to change sorting of the table, triggers a fetch
|
||||
*/
|
||||
onSort: (sort: string[][]) => void;
|
||||
opts: {
|
||||
/**
|
||||
* Date histogram aggregation config
|
||||
*/
|
||||
chartAggConfigs?: AggConfigs;
|
||||
/**
|
||||
* Client of uiSettings
|
||||
*/
|
||||
config: IUiSettingsClient;
|
||||
/**
|
||||
* Data plugin
|
||||
*/
|
||||
data: DataPublicPluginStart;
|
||||
/**
|
||||
* Data plugin filter manager
|
||||
*/
|
||||
filterManager: FilterManager;
|
||||
/**
|
||||
* List of available index patterns
|
||||
*/
|
||||
indexPatternList: Array<SavedObject<IndexPatternAttributes>>;
|
||||
/**
|
||||
* The number of documents that can be displayed in the table/grid
|
||||
*/
|
||||
sampleSize: number;
|
||||
/**
|
||||
* Current instance of SavedSearch
|
||||
*/
|
||||
savedSearch: SavedSearch;
|
||||
/**
|
||||
* Function to set the header menu
|
||||
*/
|
||||
setHeaderActionMenu: (menuMount: MountPoint | undefined) => void;
|
||||
/**
|
||||
* Timefield of the currently used index pattern
|
||||
*/
|
||||
timefield: string;
|
||||
/**
|
||||
* Function to set the current state
|
||||
*/
|
||||
setAppState: (state: Partial<AppState>) => void;
|
||||
};
|
||||
/**
|
||||
* Function to reset the current query
|
||||
*/
|
||||
resetQuery: () => void;
|
||||
/**
|
||||
* Current state of the actual query, one of 'uninitialized', 'loading' ,'ready', 'none'
|
||||
*/
|
||||
resultState: string;
|
||||
/**
|
||||
* Array of document of the recent successful search request
|
||||
*/
|
||||
rows: ElasticSearchHit[];
|
||||
/**
|
||||
* Instance of SearchSource, the high level search API
|
||||
*/
|
||||
searchSource: ISearchSource;
|
||||
/**
|
||||
* Function to change the current index pattern
|
||||
*/
|
||||
setIndexPattern: (id: string) => void;
|
||||
/**
|
||||
* Current app state of URL
|
||||
*/
|
||||
state: AppState;
|
||||
/**
|
||||
* Function to update the time filter
|
||||
*/
|
||||
timefilterUpdateHandler: (ranges: { from: number; to: number }) => void;
|
||||
/**
|
||||
* Currently selected time range
|
||||
*/
|
||||
timeRange?: { from: string; to: string };
|
||||
/**
|
||||
* Menu data of top navigation (New, save ...)
|
||||
*/
|
||||
topNavMenu: TopNavMenuData[];
|
||||
/**
|
||||
* Function to update the actual query
|
||||
*/
|
||||
updateQuery: (payload: { dateRange: TimeRange; query?: Query }, isUpdate?: boolean) => void;
|
||||
/**
|
||||
* Function to update the actual savedQuery id
|
||||
*/
|
||||
updateSavedQueryId: (savedQueryId?: string) => void;
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* and the Server Side Public License, v 1; you may not use this file except in
|
||||
* compliance with, at your election, the Elastic License or the Server Side
|
||||
* Public License, v 1.
|
||||
*/
|
||||
|
||||
import { getDisplayedColumns } from './columns';
|
||||
import { indexPatternWithTimefieldMock } from '../../__mocks__/index_pattern_with_timefield';
|
||||
import { indexPatternMock } from '../../__mocks__/index_pattern';
|
||||
|
||||
describe('getDisplayedColumns', () => {
|
||||
test('returns default columns given a index pattern without timefield', async () => {
|
||||
const result = getDisplayedColumns([], indexPatternMock);
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"_source",
|
||||
]
|
||||
`);
|
||||
});
|
||||
test('returns default columns given a index pattern with timefield', async () => {
|
||||
const result = getDisplayedColumns([], indexPatternWithTimefieldMock);
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"_source",
|
||||
]
|
||||
`);
|
||||
});
|
||||
test('returns default columns when just timefield is in state', async () => {
|
||||
const result = getDisplayedColumns(['timestamp'], indexPatternWithTimefieldMock);
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"_source",
|
||||
]
|
||||
`);
|
||||
});
|
||||
test('returns columns given by argument, no fallback ', async () => {
|
||||
const result = getDisplayedColumns(['test'], indexPatternWithTimefieldMock);
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"test",
|
||||
]
|
||||
`);
|
||||
});
|
||||
});
|
22
src/plugins/discover/public/application/helpers/columns.ts
Normal file
22
src/plugins/discover/public/application/helpers/columns.ts
Normal file
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* and the Server Side Public License, v 1; you may not use this file except in
|
||||
* compliance with, at your election, the Elastic License or the Server Side
|
||||
* Public License, v 1.
|
||||
*/
|
||||
import { IndexPattern } from '../../../../data/common';
|
||||
|
||||
/**
|
||||
* Function to provide fallback when
|
||||
* 1) no columns are given
|
||||
* 2) Just one column is given, which is the configured timefields
|
||||
*/
|
||||
export function getDisplayedColumns(stateColumns: string[] = [], indexPattern: IndexPattern) {
|
||||
return stateColumns &&
|
||||
stateColumns.length > 0 &&
|
||||
// check if all columns where removed except the configured timeField (this can't be removed)
|
||||
!(stateColumns.length === 1 && stateColumns[0] === indexPattern.timeFieldName)
|
||||
? stateColumns
|
||||
: ['_source'];
|
||||
}
|
|
@ -42,7 +42,6 @@ import {
|
|||
} from '../../kibana_legacy/public';
|
||||
import { DiscoverStartPlugins } from './plugin';
|
||||
import { getScopedHistory } from './kibana_services';
|
||||
import { createDiscoverLegacyDirective } from './application/components/create_discover_legacy_directive';
|
||||
import { createDiscoverDirective } from './application/components/create_discover_directive';
|
||||
|
||||
/**
|
||||
|
@ -124,7 +123,6 @@ export function initializeInnerAngularModule(
|
|||
.config(watchMultiDecorator)
|
||||
.run(registerListenEventListener)
|
||||
.directive('renderComplete', createRenderCompleteDirective)
|
||||
.directive('discoverLegacy', createDiscoverLegacyDirective)
|
||||
.directive('discover', createDiscoverDirective);
|
||||
}
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ export default function ({
|
|||
const getTitles = async () =>
|
||||
(await testSubjects.getVisibleText('dataGridHeader')).replace(/\s|\r?\n|\r/g, ' ');
|
||||
|
||||
expect(await getTitles()).to.be('Time (@timestamp) _source');
|
||||
expect(await getTitles()).to.be('Time (@timestamp) Document');
|
||||
|
||||
await PageObjects.discover.clickFieldListItemAdd('bytes');
|
||||
expect(await getTitles()).to.be('Time (@timestamp) bytes');
|
||||
|
@ -50,7 +50,7 @@ export default function ({
|
|||
expect(await getTitles()).to.be('Time (@timestamp) agent');
|
||||
|
||||
await PageObjects.discover.clickFieldListItemAdd('agent');
|
||||
expect(await getTitles()).to.be('Time (@timestamp) _source');
|
||||
expect(await getTitles()).to.be('Time (@timestamp) Document');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -61,7 +61,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
});
|
||||
|
||||
it('doc view should show Time and _source columns', async function () {
|
||||
const expectedHeader = 'Time (@timestamp) _source';
|
||||
const expectedHeader = 'Time (@timestamp) Document';
|
||||
const DocHeader = await dataGrid.getHeaderFields();
|
||||
expect(DocHeader.join(' ')).to.be(expectedHeader);
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue