From 0a7a9e4933104d9698e8926328a3ff73c80c7820 Mon Sep 17 00:00:00 2001 From: Matthias Wilhelm Date: Wed, 27 Jan 2021 17:58:42 +0100 Subject: [PATCH] [Discover] Merge discover.tsx and discover_legacy.tsx (#88465) --- .../public/application/angular/discover.js | 5 +- .../application/angular/discover_legacy.html | 4 +- .../doc_table/create_doc_table_react.tsx | 33 +- .../components/create_discover_directive.ts | 5 +- .../create_discover_legacy_directive.ts | 45 -- ...over_legacy.test.tsx => discover.test.tsx} | 16 +- .../application/components/discover.tsx | 125 +++-- .../discover_grid/discover_grid.tsx | 8 +- .../discover_grid/discover_grid_columns.tsx | 7 +- .../components/discover_legacy.tsx | 517 ------------------ .../public/application/components/types.ts | 179 ++++++ .../application/helpers/columns.test.ts | 46 ++ .../public/application/helpers/columns.ts | 22 + .../discover/public/get_inner_angular.ts | 2 - test/functional/apps/discover/_data_grid.ts | 4 +- .../apps/discover/_data_grid_field_data.ts | 2 +- 16 files changed, 387 insertions(+), 633 deletions(-) delete mode 100644 src/plugins/discover/public/application/components/create_discover_legacy_directive.ts rename src/plugins/discover/public/application/components/{discover_legacy.test.tsx => discover.test.tsx} (89%) delete mode 100644 src/plugins/discover/public/application/components/discover_legacy.tsx create mode 100644 src/plugins/discover/public/application/components/types.ts create mode 100644 src/plugins/discover/public/application/helpers/columns.test.ts create mode 100644 src/plugins/discover/public/application/helpers/columns.ts diff --git a/src/plugins/discover/public/application/angular/discover.js b/src/plugins/discover/public/application/angular/discover.js index 5c26680c7cc45..41c80a717ce75 100644 --- a/src/plugins/discover/public/application/angular/discover.js +++ b/src/plugins/discover/public/application/angular/discover.js @@ -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 { @@ -116,9 +115,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) { diff --git a/src/plugins/discover/public/application/angular/discover_legacy.html b/src/plugins/discover/public/application/angular/discover_legacy.html index 9383980fd9fd6..76e5c568ffde6 100644 --- a/src/plugins/discover/public/application/angular/discover_legacy.html +++ b/src/plugins/discover/public/application/angular/discover_legacy.html @@ -1,5 +1,5 @@ - - + diff --git a/src/plugins/discover/public/application/angular/doc_table/create_doc_table_react.tsx b/src/plugins/discover/public/application/angular/doc_table/create_doc_table_react.tsx index 06b6e504832e4..cbd93feb835a0 100644 --- a/src/plugins/discover/public/application/angular/doc_table/create_doc_table_react.tsx +++ b/src/plugins/discover/public/application/angular/doc_table/create_doc_table_react.tsx @@ -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
; + return ( +
+
+ {renderProps.rows.length === renderProps.sampleSize ? ( +
+ + + + +
+ ) : ( + + ​ + + )} +
+ ); } diff --git a/src/plugins/discover/public/application/components/create_discover_directive.ts b/src/plugins/discover/public/application/components/create_discover_directive.ts index 42b99b635a791..10439488f4bc7 100644 --- a/src/plugins/discover/public/application/components/create_discover_directive.ts +++ b/src/plugins/discover/public/application/components/create_discover_directive.ts @@ -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' }], diff --git a/src/plugins/discover/public/application/components/create_discover_legacy_directive.ts b/src/plugins/discover/public/application/components/create_discover_legacy_directive.ts deleted file mode 100644 index b2b9fd38f73b1..0000000000000 --- a/src/plugins/discover/public/application/components/create_discover_legacy_directive.ts +++ /dev/null @@ -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' }], - ]); -} diff --git a/src/plugins/discover/public/application/components/discover_legacy.test.tsx b/src/plugins/discover/public/application/components/discover.test.tsx similarity index 89% rename from src/plugins/discover/public/application/components/discover_legacy.test.tsx rename to src/plugins/discover/public/application/components/discover.test.tsx index 04f294912d49e..3088ca45f7941 100644 --- a/src/plugins/discover/public/application/components/discover_legacy.test.tsx +++ b/src/plugins/discover/public/application/components/discover.test.tsx @@ -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(); + const component = shallowWithIntl(); expect(component.find('[data-test-subj="discoverChartToggle"]').length).toBe(0); }); test('selected index pattern with time field displays chart toggle', () => { - const component = shallowWithIntl( - - ); + const component = shallowWithIntl(); expect(component.find('[data-test-subj="discoverChartToggle"]').length).toBe(1); }); }); diff --git a/src/plugins/discover/public/application/components/discover.tsx b/src/plugins/discover/public/application/components/discover.tsx index 704e7a9c02e1b..5653ef4f57435 100644 --- a/src/plugins/discover/public/application/components/discover.tsx +++ b/src/plugins/discover/public/application/components/discover.tsx @@ -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) => ( + +)); +const SidebarMemoized = React.memo((props: DiscoverSidebarResponsiveProps) => ( )); -export const DataGridMemoized = React.memo((props: DiscoverGridProps) => ( - -)); +const DataGridMemoized = React.memo((props: DiscoverGridProps) => ); 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(null); const collapseIcon = useRef(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 ( @@ -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} /> @@ -207,24 +210,28 @@ export function Discover({ /> )} - - { - toggleChart(!toggleOn); - }} - > - {toggleOn - ? i18n.translate('discover.hideChart', { - defaultMessage: 'Hide chart', - }) - : i18n.translate('discover.showChart', { - defaultMessage: 'Show chart', - })} - - + {opts.timefield && ( + + { + toggleChart(!toggleOn); + }} + data-test-subj="discoverChartToggle" + > + {toggleOn + ? i18n.translate('discover.hideChart', { + defaultMessage: 'Hide chart', + }) + : i18n.translate('discover.showChart', { + defaultMessage: 'Show chart', + })} + + + )} + {isLegacy && } {toggleOn && opts.timefield && ( @@ -238,7 +245,10 @@ export function Discover({ className="dscTimechart" > {opts.chartAggConfigs && histogramData && rows.length !== 0 && ( -
+
- {rows && rows.length && ( + {isLegacy && rows && rows.length && ( + { + 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 && (
{ export const DiscoverGrid = ({ ariaLabelledBy, columns, - defaultColumns, indexPattern, onAddColumn, onFilter, @@ -144,6 +137,7 @@ export const DiscoverGrid = ({ sort, }: DiscoverGridProps) => { const [expanded, setExpanded] = useState(undefined); + const defaultColumns = columns.includes('_source'); /** * Pagination diff --git a/src/plugins/discover/public/application/components/discover_grid/discover_grid_columns.tsx b/src/plugins/discover/public/application/components/discover_grid/discover_grid_columns.tsx index 481d308cf88a9..6a247ad951c9b 100644 --- a/src/plugins/discover/public/application/components/discover_grid/discover_grid_columns.tsx +++ b/src/plugins/discover/public/application/components/discover_grid/discover_grid_columns.tsx @@ -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 diff --git a/src/plugins/discover/public/application/components/discover_legacy.tsx b/src/plugins/discover/public/application/components/discover_legacy.tsx deleted file mode 100644 index 1b90b845a8fff..0000000000000 --- a/src/plugins/discover/public/application/components/discover_legacy.tsx +++ /dev/null @@ -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; - /** - * 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>; - /** - * 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) => 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) => ( - -)); -export const SidebarMemoized = React.memo((props: DiscoverSidebarResponsiveProps) => ( - -)); - -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(null); - const collapseIcon = useRef(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 ( - - - - -

- {savedSearch.title} -

- - - - - - - setIsSidebarClosed(!isSidebarClosed)} - data-test-subj="collapseSideBarButton" - aria-controls="discover-sidebar" - aria-expanded={isSidebarClosed ? 'false' : 'true'} - aria-label="Toggle sidebar" - buttonRef={collapseIcon} - /> - - - - - {resultState === 'none' && ( - - )} - {resultState === 'uninitialized' && } - {resultState === 'loading' && } - {resultState === 'ready' && ( - - - - - 0 ? hits : 0} - showResetButton={!!(savedSearch && savedSearch.id)} - onResetQuery={resetQuery} - /> - - {toggleOn && ( - - - - )} - {opts.timefield && ( - - { - toggleChart(!toggleOn); - }} - data-test-subj="discoverChartToggle" - > - {toggleOn - ? i18n.translate('discover.hideChart', { - defaultMessage: 'Hide chart', - }) - : i18n.translate('discover.showChart', { - defaultMessage: 'Show chart', - })} - - - )} - - - - {toggleOn && opts.timefield && ( - -
- {opts.chartAggConfigs && rows.length !== 0 && histogramData && ( -
- -
- )} -
-
- )} - - -
-

- -

- {rows && rows.length && ( -
- - {rows.length === opts.sampleSize ? ( -
- - - { - 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); - } - }} - > - - -
- ) : ( - - ​ - - )} -
- )} -
-
-
- )} -
-
-
-
-
-
- ); -} diff --git a/src/plugins/discover/public/application/components/types.ts b/src/plugins/discover/public/application/components/types.ts new file mode 100644 index 0000000000000..fe0d40f222f23 --- /dev/null +++ b/src/plugins/discover/public/application/components/types.ts @@ -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; + /** + * 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>; + /** + * 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) => 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; +} diff --git a/src/plugins/discover/public/application/helpers/columns.test.ts b/src/plugins/discover/public/application/helpers/columns.test.ts new file mode 100644 index 0000000000000..d455fd1f42c6d --- /dev/null +++ b/src/plugins/discover/public/application/helpers/columns.test.ts @@ -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", + ] + `); + }); +}); diff --git a/src/plugins/discover/public/application/helpers/columns.ts b/src/plugins/discover/public/application/helpers/columns.ts new file mode 100644 index 0000000000000..d2d47c932b7bd --- /dev/null +++ b/src/plugins/discover/public/application/helpers/columns.ts @@ -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']; +} diff --git a/src/plugins/discover/public/get_inner_angular.ts b/src/plugins/discover/public/get_inner_angular.ts index b27426a6c0621..4eda742d967f4 100644 --- a/src/plugins/discover/public/get_inner_angular.ts +++ b/src/plugins/discover/public/get_inner_angular.ts @@ -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); } diff --git a/test/functional/apps/discover/_data_grid.ts b/test/functional/apps/discover/_data_grid.ts index 3bac05c5b18fc..1329e7657ad9c 100644 --- a/test/functional/apps/discover/_data_grid.ts +++ b/test/functional/apps/discover/_data_grid.ts @@ -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'); }); }); } diff --git a/test/functional/apps/discover/_data_grid_field_data.ts b/test/functional/apps/discover/_data_grid_field_data.ts index bdbaacc33c1bd..3eec84ad3d7c0 100644 --- a/test/functional/apps/discover/_data_grid_field_data.ts +++ b/test/functional/apps/discover/_data_grid_field_data.ts @@ -57,7 +57,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); });