This commit is contained in:
parent
747a5fda60
commit
bd8642ba76
|
@ -252,6 +252,12 @@ function discoverController($route, $scope, Promise) {
|
|||
(prop) => !_.isEqual(newStatePartial[prop], oldStatePartial[prop])
|
||||
);
|
||||
|
||||
if (oldStatePartial.hideChart && !newStatePartial.hideChart) {
|
||||
// in case the histogram is hidden, no data is requested
|
||||
// so when changing this state data needs to be fetched
|
||||
changes.push(true);
|
||||
}
|
||||
|
||||
if (changes.length) {
|
||||
refetch$.next();
|
||||
}
|
||||
|
@ -313,6 +319,8 @@ function discoverController($route, $scope, Promise) {
|
|||
setAppState,
|
||||
data,
|
||||
stateContainer,
|
||||
searchSessionManager,
|
||||
refetch$,
|
||||
};
|
||||
|
||||
const inspectorAdapters = ($scope.opts.inspectorAdapters = {
|
||||
|
@ -412,6 +420,9 @@ function discoverController($route, $scope, Promise) {
|
|||
if (savedSearch.grid) {
|
||||
defaultState.grid = savedSearch.grid;
|
||||
}
|
||||
if (savedSearch.hideChart) {
|
||||
defaultState.hideChart = savedSearch.hideChart;
|
||||
}
|
||||
|
||||
return defaultState;
|
||||
}
|
||||
|
@ -562,13 +573,6 @@ function discoverController($route, $scope, Promise) {
|
|||
});
|
||||
};
|
||||
|
||||
$scope.handleRefresh = function (_payload, isUpdate) {
|
||||
if (isUpdate === false) {
|
||||
searchSessionManager.removeSearchSessionIdFromURL({ replace: false });
|
||||
refetch$.next();
|
||||
}
|
||||
};
|
||||
|
||||
function getDimensions(aggs, timeRange) {
|
||||
const [metric, agg] = aggs;
|
||||
agg.params.timeRange = timeRange;
|
||||
|
@ -601,7 +605,7 @@ function discoverController($route, $scope, Promise) {
|
|||
function onResults(resp) {
|
||||
inspectorRequest.stats(getResponseInspectorStats(resp, $scope.searchSource)).ok({ json: resp });
|
||||
|
||||
if (getTimeField()) {
|
||||
if (getTimeField() && !$scope.state.hideChart) {
|
||||
const tabifiedData = tabifyAggResponse($scope.opts.chartAggConfigs, resp);
|
||||
$scope.searchSource.rawResponse = resp;
|
||||
$scope.histogramData = discoverResponseHandler(
|
||||
|
@ -704,7 +708,7 @@ function discoverController($route, $scope, Promise) {
|
|||
|
||||
async function setupVisualization() {
|
||||
// If no timefield has been specified we don't create a histogram of messages
|
||||
if (!getTimeField()) return;
|
||||
if (!getTimeField() || $scope.state.hideChart) return;
|
||||
const { interval: histogramInterval } = $scope.state;
|
||||
|
||||
const visStateAggs = [
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
state="state"
|
||||
time-range="timeRange"
|
||||
top-nav-menu="topNavMenu"
|
||||
update-query="handleRefresh"
|
||||
use-new-fields-api="useNewFieldsApi"
|
||||
unmapped-fields-config="unmappedFieldsConfig"
|
||||
>
|
||||
|
|
|
@ -44,6 +44,10 @@ export interface AppState {
|
|||
* Data Grid related state
|
||||
*/
|
||||
grid?: DiscoverGridSettings;
|
||||
/**
|
||||
* Hide chart
|
||||
*/
|
||||
hideChart?: boolean;
|
||||
/**
|
||||
* id of the used index pattern
|
||||
*/
|
||||
|
|
|
@ -25,6 +25,8 @@ import { indexPatternWithTimefieldMock } from '../../__mocks__/index_pattern_wit
|
|||
import { calcFieldCounts } from '../helpers/calc_field_counts';
|
||||
import { DiscoverProps } from './types';
|
||||
import { RequestAdapter } from '../../../../inspector/common';
|
||||
import { Subject } from 'rxjs';
|
||||
import { DiscoverSearchSessionManager } from '../angular/discover_search_session';
|
||||
|
||||
const mockNavigation = navigationPluginMock.createStartContract();
|
||||
|
||||
|
@ -73,8 +75,10 @@ function getProps(indexPattern: IndexPattern): DiscoverProps {
|
|||
indexPatternList: (indexPattern as unknown) as Array<SavedObject<IndexPatternAttributes>>,
|
||||
inspectorAdapters: { requests: {} as RequestAdapter },
|
||||
navigateTo: jest.fn(),
|
||||
refetch$: {} as Subject<undefined>,
|
||||
sampleSize: 10,
|
||||
savedSearch: savedSearchMock,
|
||||
searchSessionManager: {} as DiscoverSearchSessionManager,
|
||||
setHeaderActionMenu: jest.fn(),
|
||||
timefield: indexPattern.timeFieldName || '',
|
||||
setAppState: jest.fn(),
|
||||
|
@ -86,7 +90,6 @@ function getProps(indexPattern: IndexPattern): DiscoverProps {
|
|||
rows: esHits,
|
||||
searchSource: searchSourceMock,
|
||||
state: { columns: [] },
|
||||
updateQuery: jest.fn(),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
import './discover.scss';
|
||||
import React, { useCallback, useMemo, useRef, useState } from 'react';
|
||||
import React, { useState, useRef, useMemo, useCallback } from 'react';
|
||||
import {
|
||||
EuiButtonEmpty,
|
||||
EuiButtonIcon,
|
||||
|
@ -66,7 +66,6 @@ export function Discover({
|
|||
searchSource,
|
||||
state,
|
||||
timeRange,
|
||||
updateQuery,
|
||||
unmappedFieldsConfig,
|
||||
}: DiscoverProps) {
|
||||
const [expandedDoc, setExpandedDoc] = useState<ElasticSearchHit | undefined>(undefined);
|
||||
|
@ -76,8 +75,11 @@ export function Discover({
|
|||
// 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 toggleHideChart = useCallback(() => {
|
||||
const newState = { ...state, hideChart: !state.hideChart };
|
||||
opts.stateContainer.setAppState(newState);
|
||||
}, [state, opts]);
|
||||
const hideChart = useMemo(() => state.hideChart, [state]);
|
||||
const { savedSearch, indexPatternList, config, services, data, setAppState } = opts;
|
||||
const { trackUiMetric, capabilities, indexPatterns } = services;
|
||||
const [isSidebarClosed, setIsSidebarClosed] = useState(false);
|
||||
|
@ -89,6 +91,15 @@ export function Discover({
|
|||
const contentCentered = resultState === 'uninitialized';
|
||||
const isLegacy = services.uiSettings.get('doc_table:legacy');
|
||||
const useNewFieldsApi = !services.uiSettings.get(SEARCH_FIELDS_FROM_SOURCE);
|
||||
const updateQuery = useCallback(
|
||||
(_payload, isUpdate?: boolean) => {
|
||||
if (isUpdate === false) {
|
||||
opts.searchSessionManager.removeSearchSessionIdFromURL({ replace: false });
|
||||
opts.refetch$.next();
|
||||
}
|
||||
},
|
||||
[opts]
|
||||
);
|
||||
|
||||
const { onAddColumn, onRemoveColumn, onMoveColumn, onSetColumns } = useMemo(
|
||||
() =>
|
||||
|
@ -192,7 +203,8 @@ export function Discover({
|
|||
indexPattern={indexPattern}
|
||||
opts={opts}
|
||||
onOpenInspector={onOpenInspector}
|
||||
state={state}
|
||||
query={state.query}
|
||||
savedQuery={state.savedQuery}
|
||||
updateQuery={updateQuery}
|
||||
/>
|
||||
<EuiPageBody className="dscPageBody" aria-describedby="savedSearchTitle">
|
||||
|
@ -277,7 +289,7 @@ export function Discover({
|
|||
onResetQuery={resetQuery}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
{toggleOn && (
|
||||
{!hideChart && (
|
||||
<EuiFlexItem className="dscResultCount__actions">
|
||||
<TimechartHeader
|
||||
dateFormat={opts.config.get('dateFormat')}
|
||||
|
@ -293,13 +305,13 @@ export function Discover({
|
|||
<EuiFlexItem className="dscResultCount__toggle" grow={false}>
|
||||
<EuiButtonEmpty
|
||||
size="xs"
|
||||
iconType={toggleOn ? 'eyeClosed' : 'eye'}
|
||||
iconType={!hideChart ? 'eyeClosed' : 'eye'}
|
||||
onClick={() => {
|
||||
toggleChart(!toggleOn);
|
||||
toggleHideChart();
|
||||
}}
|
||||
data-test-subj="discoverChartToggle"
|
||||
>
|
||||
{toggleOn
|
||||
{!hideChart
|
||||
? i18n.translate('discover.hideChart', {
|
||||
defaultMessage: 'Hide chart',
|
||||
})
|
||||
|
@ -312,7 +324,7 @@ export function Discover({
|
|||
</EuiFlexGroup>
|
||||
{isLegacy && <SkipBottomButton onClick={onSkipBottomButtonClick} />}
|
||||
</EuiFlexItem>
|
||||
{toggleOn && opts.timefield && (
|
||||
{!hideChart && opts.timefield && (
|
||||
<EuiFlexItem grow={false}>
|
||||
<section
|
||||
aria-label={i18n.translate(
|
||||
|
|
|
@ -10,7 +10,7 @@ import React from 'react';
|
|||
import { mountWithIntl } from '@kbn/test/jest';
|
||||
import { indexPatternMock } from '../../__mocks__/index_pattern';
|
||||
import { DiscoverServices } from '../../build_services';
|
||||
import { AppState, GetStateReturn } from '../angular/discover_state';
|
||||
import { GetStateReturn } from '../angular/discover_state';
|
||||
import { savedSearchMock } from '../../__mocks__/saved_search';
|
||||
import { dataPluginMock } from '../../../../data/public/mocks';
|
||||
import { createFilterManagerMock } from '../../../../data/public/query/filter_manager/filter_manager.mock';
|
||||
|
@ -20,9 +20,11 @@ import { SavedObject } from '../../../../../core/types';
|
|||
import { DiscoverTopNav, DiscoverTopNavProps } from './discover_topnav';
|
||||
import { RequestAdapter } from '../../../../inspector/common/adapters/request';
|
||||
import { TopNavMenu } from '../../../../navigation/public';
|
||||
import { Query } from '../../../../data/common';
|
||||
import { DiscoverSearchSessionManager } from '../angular/discover_search_session';
|
||||
import { Subject } from 'rxjs';
|
||||
|
||||
function getProps(): DiscoverTopNavProps {
|
||||
const state = ({} as unknown) as AppState;
|
||||
const services = ({
|
||||
navigation: {
|
||||
ui: { TopNavMenu },
|
||||
|
@ -45,15 +47,18 @@ function getProps(): DiscoverTopNavProps {
|
|||
indexPatternList: (indexPattern as unknown) as Array<SavedObject<IndexPatternAttributes>>,
|
||||
inspectorAdapters: { requests: {} as RequestAdapter },
|
||||
navigateTo: jest.fn(),
|
||||
refetch$: {} as Subject<undefined>,
|
||||
sampleSize: 10,
|
||||
savedSearch: savedSearchMock,
|
||||
searchSessionManager: {} as DiscoverSearchSessionManager,
|
||||
services,
|
||||
setAppState: jest.fn(),
|
||||
setHeaderActionMenu: jest.fn(),
|
||||
stateContainer: {} as GetStateReturn,
|
||||
timefield: indexPattern.timeFieldName || '',
|
||||
},
|
||||
state,
|
||||
query: {} as Query,
|
||||
savedQuery: '',
|
||||
updateQuery: jest.fn(),
|
||||
onOpenInspector: jest.fn(),
|
||||
};
|
||||
|
|
|
@ -8,17 +8,21 @@
|
|||
import React, { useMemo } from 'react';
|
||||
import { DiscoverProps } from './types';
|
||||
import { getTopNavLinks } from './top_nav/get_top_nav_links';
|
||||
import { Query, TimeRange } from '../../../../data/common/query';
|
||||
|
||||
export type DiscoverTopNavProps = Pick<
|
||||
DiscoverProps,
|
||||
'indexPattern' | 'updateQuery' | 'state' | 'opts'
|
||||
> & { onOpenInspector: () => void };
|
||||
export type DiscoverTopNavProps = Pick<DiscoverProps, 'indexPattern' | 'opts'> & {
|
||||
onOpenInspector: () => void;
|
||||
query?: Query;
|
||||
savedQuery?: string;
|
||||
updateQuery: (payload: { dateRange: TimeRange; query?: Query }, isUpdate?: boolean) => void;
|
||||
};
|
||||
|
||||
export const DiscoverTopNav = ({
|
||||
indexPattern,
|
||||
opts,
|
||||
onOpenInspector,
|
||||
state,
|
||||
query,
|
||||
savedQuery,
|
||||
updateQuery,
|
||||
}: DiscoverTopNavProps) => {
|
||||
const showDatePicker = useMemo(() => indexPattern.isTimeBased(), [indexPattern]);
|
||||
|
@ -58,9 +62,9 @@ export const DiscoverTopNav = ({
|
|||
indexPatterns={[indexPattern]}
|
||||
onQuerySubmit={updateQuery}
|
||||
onSavedQueryIdChange={updateSavedQueryId}
|
||||
query={state.query}
|
||||
query={query}
|
||||
setMenuMountPoint={opts.setHeaderActionMenu}
|
||||
savedQueryId={state.savedQuery}
|
||||
savedQueryId={savedQuery}
|
||||
screenTitle={opts.savedSearch.title}
|
||||
showDatePicker={showDatePicker}
|
||||
showSaveQuery={!!opts.services.capabilities.discover.saveQuery}
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
*/
|
||||
|
||||
import { IUiSettingsClient, MountPoint, SavedObject } from 'kibana/public';
|
||||
import { Subject } from 'rxjs';
|
||||
import { Chart } from '../angular/helpers/point_series';
|
||||
import { IndexPattern } from '../../../../data/common/index_patterns/index_patterns';
|
||||
import { ElasticSearchHit } from '../doc_views/doc_views_types';
|
||||
|
@ -17,13 +18,12 @@ import {
|
|||
FilterManager,
|
||||
IndexPatternAttributes,
|
||||
ISearchSource,
|
||||
Query,
|
||||
TimeRange,
|
||||
} from '../../../../data/public';
|
||||
import { SavedSearch } from '../../saved_searches';
|
||||
import { AppState, GetStateReturn } from '../angular/discover_state';
|
||||
import { RequestAdapter } from '../../../../inspector/common';
|
||||
import { DiscoverServices } from '../../build_services';
|
||||
import { DiscoverSearchSessionManager } from '../angular/discover_search_session';
|
||||
|
||||
export interface DiscoverProps {
|
||||
/**
|
||||
|
@ -97,10 +97,18 @@ export interface DiscoverProps {
|
|||
* List of available index patterns
|
||||
*/
|
||||
indexPatternList: Array<SavedObject<IndexPatternAttributes>>;
|
||||
/**
|
||||
* Refetch observable
|
||||
*/
|
||||
refetch$: Subject<undefined>;
|
||||
/**
|
||||
* Kibana core services used by discover
|
||||
*/
|
||||
services: DiscoverServices;
|
||||
/**
|
||||
* Helps with state management of search session
|
||||
*/
|
||||
searchSessionManager: DiscoverSearchSessionManager;
|
||||
/**
|
||||
* The number of documents that can be displayed in the table/grid
|
||||
*/
|
||||
|
@ -113,10 +121,6 @@ export interface DiscoverProps {
|
|||
* Function to set the header menu
|
||||
*/
|
||||
setHeaderActionMenu: (menuMount: MountPoint | undefined) => void;
|
||||
/**
|
||||
* Functions for retrieving/mutating state
|
||||
*/
|
||||
stateContainer: GetStateReturn;
|
||||
/**
|
||||
* Timefield of the currently used index pattern
|
||||
*/
|
||||
|
@ -125,6 +129,10 @@ export interface DiscoverProps {
|
|||
* Function to set the current state
|
||||
*/
|
||||
setAppState: (state: Partial<AppState>) => void;
|
||||
/**
|
||||
* State container providing globalState, appState and functions
|
||||
*/
|
||||
stateContainer: GetStateReturn;
|
||||
};
|
||||
/**
|
||||
* Function to reset the current query
|
||||
|
@ -150,10 +158,6 @@ export interface DiscoverProps {
|
|||
* Currently selected time range
|
||||
*/
|
||||
timeRange?: { from: string; to: string };
|
||||
/**
|
||||
* Function to update the actual query
|
||||
*/
|
||||
updateQuery: (payload: { dateRange: TimeRange; query?: Query }, isUpdate?: boolean) => void;
|
||||
/**
|
||||
* An object containing properties for proper handling of unmapped fields in the UI
|
||||
*/
|
||||
|
|
|
@ -48,6 +48,9 @@ export async function persistSavedSearch(
|
|||
if (state.grid) {
|
||||
savedSearch.grid = state.grid;
|
||||
}
|
||||
if (state.hideChart) {
|
||||
savedSearch.hideChart = state.hideChart;
|
||||
}
|
||||
|
||||
try {
|
||||
const id = await savedSearch.save(saveOptions);
|
||||
|
|
|
@ -14,6 +14,7 @@ export function createSavedSearchClass(savedObjects: SavedObjectsStart) {
|
|||
public static mapping = {
|
||||
title: 'text',
|
||||
description: 'text',
|
||||
hideChart: 'boolean',
|
||||
hits: 'integer',
|
||||
columns: 'keyword',
|
||||
grid: 'object',
|
||||
|
@ -35,6 +36,7 @@ export function createSavedSearchClass(savedObjects: SavedObjectsStart) {
|
|||
mapping: {
|
||||
title: 'text',
|
||||
description: 'text',
|
||||
hideChart: 'boolean',
|
||||
hits: 'integer',
|
||||
columns: 'keyword',
|
||||
grid: 'object',
|
||||
|
|
|
@ -24,6 +24,7 @@ export interface SavedSearch {
|
|||
lastSavedTitle?: string;
|
||||
copyOnSave?: boolean;
|
||||
pre712?: boolean;
|
||||
hideChart?: boolean;
|
||||
}
|
||||
export interface SavedSearchLoader {
|
||||
get: (id: string) => Promise<SavedSearch>;
|
||||
|
|
|
@ -34,6 +34,7 @@ export const searchSavedObjectType: SavedObjectsType = {
|
|||
properties: {
|
||||
columns: { type: 'keyword', index: false, doc_values: false },
|
||||
description: { type: 'text' },
|
||||
hideChart: { type: 'boolean', index: false, doc_values: false },
|
||||
hits: { type: 'integer', index: false, doc_values: false },
|
||||
kibanaSavedObjectMeta: {
|
||||
properties: {
|
||||
|
|
|
@ -19,6 +19,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
defaultIndex: 'long-window-logstash-*',
|
||||
'dateFormat:tz': 'Europe/Berlin',
|
||||
};
|
||||
const testSubjects = getService('testSubjects');
|
||||
const browser = getService('browser');
|
||||
|
||||
describe('discover histogram', function describeIndexTests() {
|
||||
before(async () => {
|
||||
|
@ -35,11 +37,13 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
await security.testUser.restoreDefaults();
|
||||
});
|
||||
|
||||
async function prepareTest(fromTime: string, toTime: string, interval: string) {
|
||||
async function prepareTest(fromTime: string, toTime: string, interval?: string) {
|
||||
await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime);
|
||||
await PageObjects.discover.waitUntilSearchingHasFinished();
|
||||
await PageObjects.discover.setChartInterval(interval);
|
||||
await PageObjects.header.waitUntilLoadingHasFinished();
|
||||
if (interval) {
|
||||
await PageObjects.discover.setChartInterval(interval);
|
||||
await PageObjects.header.waitUntilLoadingHasFinished();
|
||||
}
|
||||
}
|
||||
|
||||
it('should visualize monthly data with different day intervals', async () => {
|
||||
|
@ -65,5 +69,43 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
const chartIntervalIconTip = await PageObjects.discover.getChartIntervalWarningIcon();
|
||||
expect(chartIntervalIconTip).to.be(true);
|
||||
});
|
||||
it('should allow hide/show histogram, persisted in url state', async () => {
|
||||
const fromTime = 'Jan 01, 2010 @ 00:00:00.000';
|
||||
const toTime = 'Mar 21, 2019 @ 00:00:00.000';
|
||||
await prepareTest(fromTime, toTime);
|
||||
let canvasExists = await elasticChart.canvasExists();
|
||||
expect(canvasExists).to.be(true);
|
||||
await testSubjects.click('discoverChartToggle');
|
||||
canvasExists = await elasticChart.canvasExists();
|
||||
expect(canvasExists).to.be(false);
|
||||
// histogram is hidden, when reloading the page it should remain hidden
|
||||
await browser.refresh();
|
||||
canvasExists = await elasticChart.canvasExists();
|
||||
expect(canvasExists).to.be(false);
|
||||
await testSubjects.click('discoverChartToggle');
|
||||
await PageObjects.header.waitUntilLoadingHasFinished();
|
||||
canvasExists = await elasticChart.canvasExists();
|
||||
expect(canvasExists).to.be(true);
|
||||
});
|
||||
it('should allow hiding the histogram, persisted in saved search', async () => {
|
||||
const fromTime = 'Jan 01, 2010 @ 00:00:00.000';
|
||||
const toTime = 'Mar 21, 2019 @ 00:00:00.000';
|
||||
const savedSearch = 'persisted hidden histogram';
|
||||
await prepareTest(fromTime, toTime);
|
||||
await testSubjects.click('discoverChartToggle');
|
||||
let canvasExists = await elasticChart.canvasExists();
|
||||
expect(canvasExists).to.be(false);
|
||||
await PageObjects.discover.saveSearch(savedSearch);
|
||||
await PageObjects.header.waitUntilLoadingHasFinished();
|
||||
canvasExists = await elasticChart.canvasExists();
|
||||
expect(canvasExists).to.be(false);
|
||||
await testSubjects.click('discoverChartToggle');
|
||||
canvasExists = await elasticChart.canvasExists();
|
||||
expect(canvasExists).to.be(true);
|
||||
await PageObjects.discover.clickResetSavedSearchButton();
|
||||
await PageObjects.header.waitUntilLoadingHasFinished();
|
||||
canvasExists = await elasticChart.canvasExists();
|
||||
expect(canvasExists).to.be(false);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue