[Maps] use EuiSuperDatePicker in QueryBar instead of Angular timepicker in Top Nav (#29235)
* Embed timepicker in query bar (#29130) * replace kbnTimepicker directive with EuiSuperDatePicker * remove kbnTimepicker directive * remove bootstrap datepicker * embed timepicker in query bar * flesh out date picker in query bar for maps app * wire up refresh config * fix bug with way update function called by watcher * get maps application functional tests working with new timepicker * remove some changes outside of scoped work * clean up typescript lint problems * fix query_bar I18n lint error * update query_bar jest test * grab some parts missing from one cherry-pick * pass dateRange to updateQueryAndDispatch when app state changes * use timefilter disable methods to hide timepicker from top naav * get selected refresh unit * use EuiSuperUpdate button * Fix responsive sizing of datepicker (#29) * set isDisabled on EuiSuperUpdateButton * review feedback * remove ts-ignore comment from defaultProps, fix query_bar snapshot * make new props optional * fighting with ts linter * do not include dateRangeFrom and dateRangeTo in isDirty when shoDateRange is not true * pull initial query from UI settings
This commit is contained in:
parent
434747b37c
commit
c1d6a1bd76
24 changed files with 607 additions and 219 deletions
|
@ -215,7 +215,7 @@ app.directive('dashboardApp', function ($injector) {
|
|||
dashboardStateManager.getPanels().find((panel) => panel.panelIndex === panelIndex);
|
||||
};
|
||||
|
||||
$scope.updateQueryAndFetch = function (query) {
|
||||
$scope.updateQueryAndFetch = function ({ query }) {
|
||||
const oldQuery = $scope.model.query;
|
||||
if (_.isEqual(oldQuery, query)) {
|
||||
// The user can still request a reload in the query bar, even if the
|
||||
|
@ -236,7 +236,9 @@ app.directive('dashboardApp', function ($injector) {
|
|||
$scope.indexPatterns = dashboardStateManager.getPanelIndexPatterns();
|
||||
};
|
||||
|
||||
$scope.$watch('model.query', $scope.updateQueryAndFetch);
|
||||
$scope.$watch('model.query', (query) => {
|
||||
$scope.updateQueryAndFetch({ query });
|
||||
});
|
||||
|
||||
$scope.$listenAndDigestAsync(timefilter, 'fetch', () => {
|
||||
dashboardStateManager.handleTimeChange(timefilter.getTime());
|
||||
|
|
|
@ -525,7 +525,9 @@ function discoverController(
|
|||
}
|
||||
});
|
||||
|
||||
$scope.$watch('state.query', $scope.updateQueryAndFetch);
|
||||
$scope.$watch('state.query', (query) => {
|
||||
$scope.updateQueryAndFetch({ query });
|
||||
});
|
||||
|
||||
$scope.$watchMulti([
|
||||
'rows',
|
||||
|
@ -643,7 +645,7 @@ function discoverController(
|
|||
.catch(notify.error);
|
||||
};
|
||||
|
||||
$scope.updateQueryAndFetch = function (query) {
|
||||
$scope.updateQueryAndFetch = function ({ query }) {
|
||||
$state.query = migrateLegacyQuery(query);
|
||||
$scope.fetch();
|
||||
};
|
||||
|
|
|
@ -311,7 +311,9 @@ function VisEditor(
|
|||
$appStatus.dirty = status.dirty || !savedVis.id;
|
||||
});
|
||||
|
||||
$scope.$watch('state.query', $scope.updateQueryAndFetch);
|
||||
$scope.$watch('state.query', (query) => {
|
||||
$scope.updateQueryAndFetch({ query });
|
||||
});
|
||||
|
||||
$state.replace();
|
||||
|
||||
|
@ -385,7 +387,7 @@ function VisEditor(
|
|||
}
|
||||
}
|
||||
|
||||
$scope.updateQueryAndFetch = function (query) {
|
||||
$scope.updateQueryAndFetch = function ({ query }) {
|
||||
$state.query = migrateLegacyQuery(query);
|
||||
$scope.fetch();
|
||||
};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SASSTODO: Formalize this color in Kibana's styling constants
|
||||
$typeaheadConjunctionColor: #7800A6;
|
||||
|
||||
@import 'components/typeahead/index';
|
||||
@import './components/index';
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
exports[`QueryBar Should disable autoFocus on EuiFieldText when disableAutoFocus prop is true 1`] = `
|
||||
<EuiFlexGroup
|
||||
alignItems="stretch"
|
||||
className="kbnQueryBar"
|
||||
component="div"
|
||||
direction="row"
|
||||
gutterSize="s"
|
||||
|
@ -90,21 +91,13 @@ exports[`QueryBar Should disable autoFocus on EuiFieldText when disableAutoFocus
|
|||
component="div"
|
||||
grow={false}
|
||||
>
|
||||
<EuiButton
|
||||
aria-label="Search"
|
||||
color="primary"
|
||||
<EuiSuperUpdateButton
|
||||
data-test-subj="querySubmitButton"
|
||||
fill={true}
|
||||
iconSide="left"
|
||||
isDisabled={false}
|
||||
isLoading={false}
|
||||
needsUpdate={false}
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Refresh"
|
||||
id="common.ui.queryBar.refreshButtonLabel"
|
||||
values={Object {}}
|
||||
/>
|
||||
</EuiButton>
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
`;
|
||||
|
@ -112,6 +105,7 @@ exports[`QueryBar Should disable autoFocus on EuiFieldText when disableAutoFocus
|
|||
exports[`QueryBar Should pass the query language to the language switcher 1`] = `
|
||||
<EuiFlexGroup
|
||||
alignItems="stretch"
|
||||
className="kbnQueryBar"
|
||||
component="div"
|
||||
direction="row"
|
||||
gutterSize="s"
|
||||
|
@ -199,21 +193,13 @@ exports[`QueryBar Should pass the query language to the language switcher 1`] =
|
|||
component="div"
|
||||
grow={false}
|
||||
>
|
||||
<EuiButton
|
||||
aria-label="Search"
|
||||
color="primary"
|
||||
<EuiSuperUpdateButton
|
||||
data-test-subj="querySubmitButton"
|
||||
fill={true}
|
||||
iconSide="left"
|
||||
isDisabled={false}
|
||||
isLoading={false}
|
||||
needsUpdate={false}
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Refresh"
|
||||
id="common.ui.queryBar.refreshButtonLabel"
|
||||
values={Object {}}
|
||||
/>
|
||||
</EuiButton>
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
`;
|
||||
|
@ -221,6 +207,7 @@ exports[`QueryBar Should pass the query language to the language switcher 1`] =
|
|||
exports[`QueryBar Should render the given query 1`] = `
|
||||
<EuiFlexGroup
|
||||
alignItems="stretch"
|
||||
className="kbnQueryBar"
|
||||
component="div"
|
||||
direction="row"
|
||||
gutterSize="s"
|
||||
|
@ -308,21 +295,13 @@ exports[`QueryBar Should render the given query 1`] = `
|
|||
component="div"
|
||||
grow={false}
|
||||
>
|
||||
<EuiButton
|
||||
aria-label="Search"
|
||||
color="primary"
|
||||
<EuiSuperUpdateButton
|
||||
data-test-subj="querySubmitButton"
|
||||
fill={true}
|
||||
iconSide="left"
|
||||
isDisabled={false}
|
||||
isLoading={false}
|
||||
needsUpdate={false}
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Refresh"
|
||||
id="common.ui.queryBar.refreshButtonLabel"
|
||||
values={Object {}}
|
||||
/>
|
||||
</EuiButton>
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
`;
|
||||
|
|
2
src/ui/public/query_bar/components/_index.scss
Normal file
2
src/ui/public/query_bar/components/_index.scss
Normal file
|
@ -0,0 +1,2 @@
|
|||
@import './query_bar';
|
||||
@import './typeahead/index';
|
14
src/ui/public/query_bar/components/_query_bar.scss
Normal file
14
src/ui/public/query_bar/components/_query_bar.scss
Normal file
|
@ -0,0 +1,14 @@
|
|||
@include euiBreakpoint('xs', 's') {
|
||||
.kbnQueryBar--withDatePicker {
|
||||
> :last-child {
|
||||
// EUI Flexbox adds too much margin between responded items, this just moves the last one up
|
||||
margin-top: -$euiSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include euiBreakpoint('m', 'l', 'xl') {
|
||||
.kbnQueryBar__datePickerWrapper {
|
||||
max-width: 40vw;
|
||||
}
|
||||
}
|
|
@ -207,8 +207,14 @@ describe('QueryBar', () => {
|
|||
component.find(QueryLanguageSwitcher).simulate('selectLanguage', 'lucene');
|
||||
expect(mockStorage.set).toHaveBeenCalledWith('kibana.userQueryLanguage', 'lucene');
|
||||
expect(mockCallback).toHaveBeenCalledWith({
|
||||
query: '',
|
||||
language: 'lucene',
|
||||
dateRange: {
|
||||
from: 'now-15m',
|
||||
to: 'now',
|
||||
},
|
||||
query: {
|
||||
query: '',
|
||||
language: 'lucene',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -235,8 +241,14 @@ describe('QueryBar', () => {
|
|||
|
||||
expect(mockCallback).toHaveBeenCalledTimes(1);
|
||||
expect(mockCallback).toHaveBeenCalledWith({
|
||||
query: 'extension:jpg',
|
||||
language: 'kuery',
|
||||
dateRange: {
|
||||
from: 'now-15m',
|
||||
to: 'now',
|
||||
},
|
||||
query: {
|
||||
query: 'extension:jpg',
|
||||
language: 'kuery',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -19,12 +19,15 @@
|
|||
|
||||
import { IndexPattern } from 'ui/index_patterns';
|
||||
|
||||
import { compact, debounce, isEqual } from 'lodash';
|
||||
import classNames from 'classnames';
|
||||
import _ from 'lodash';
|
||||
import { compact, debounce, get, isEqual } from 'lodash';
|
||||
import React, { Component } from 'react';
|
||||
import { getFromLegacyIndexPattern } from 'ui/index_patterns/static_utils';
|
||||
import { kfetch } from 'ui/kfetch';
|
||||
import { PersistedLog } from 'ui/persisted_log';
|
||||
import { Storage } from 'ui/storage';
|
||||
import { timeHistory } from 'ui/timefilter/time_history';
|
||||
import {
|
||||
AutocompleteSuggestion,
|
||||
AutocompleteSuggestionType,
|
||||
|
@ -36,15 +39,12 @@ import { matchPairs } from '../lib/match_pairs';
|
|||
import { QueryLanguageSwitcher } from './language_switcher';
|
||||
import { SuggestionsComponent } from './typeahead/suggestions_component';
|
||||
|
||||
import {
|
||||
EuiButton,
|
||||
EuiFieldText,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiOutsideClickDetector,
|
||||
} from '@elastic/eui';
|
||||
import { EuiFieldText, EuiFlexGroup, EuiFlexItem, EuiOutsideClickDetector } from '@elastic/eui';
|
||||
|
||||
import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n/react';
|
||||
// @ts-ignore
|
||||
import { EuiSuperDatePicker, EuiSuperUpdateButton } from '@elastic/eui';
|
||||
|
||||
import { InjectedIntl, injectI18n } from '@kbn/i18n/react';
|
||||
|
||||
const KEY_CODES = {
|
||||
LEFT: 37,
|
||||
|
@ -66,14 +66,25 @@ interface Query {
|
|||
language: string;
|
||||
}
|
||||
|
||||
interface DateRange {
|
||||
from: string;
|
||||
to: string;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
query: Query;
|
||||
onSubmit: (query: { query: string | object; language: string }) => void;
|
||||
onSubmit: (payload: { dateRange: DateRange; query: Query }) => void;
|
||||
disableAutoFocus?: boolean;
|
||||
appName: string;
|
||||
indexPatterns: IndexPattern[];
|
||||
store: Storage;
|
||||
intl: InjectedIntl;
|
||||
showDatePicker?: boolean;
|
||||
dateRangeFrom?: string;
|
||||
dateRangeTo?: string;
|
||||
isRefreshPaused?: boolean;
|
||||
refreshInterval?: number;
|
||||
onRefreshChange?: (isPaused: boolean, refreshInterval: number) => void;
|
||||
}
|
||||
|
||||
interface State {
|
||||
|
@ -84,6 +95,9 @@ interface State {
|
|||
suggestions: AutocompleteSuggestion[];
|
||||
suggestionLimit: number;
|
||||
currentProps?: Props;
|
||||
dateRangeFrom: string;
|
||||
dateRangeTo: string;
|
||||
isDateRangeInvalid: boolean;
|
||||
}
|
||||
|
||||
export class QueryBarUI extends Component<Props, State> {
|
||||
|
@ -92,25 +106,41 @@ export class QueryBarUI extends Component<Props, State> {
|
|||
return null;
|
||||
}
|
||||
|
||||
let nextQuery = null;
|
||||
if (nextProps.query.query !== prevState.query.query) {
|
||||
return {
|
||||
query: {
|
||||
query: toUser(nextProps.query.query),
|
||||
language: nextProps.query.language,
|
||||
},
|
||||
currentProps: nextProps,
|
||||
nextQuery = {
|
||||
query: toUser(nextProps.query.query),
|
||||
language: nextProps.query.language,
|
||||
};
|
||||
} else if (nextProps.query.language !== prevState.query.language) {
|
||||
return {
|
||||
query: {
|
||||
query: '',
|
||||
language: nextProps.query.language,
|
||||
},
|
||||
currentProps: nextProps,
|
||||
nextQuery = {
|
||||
query: '',
|
||||
language: nextProps.query.language,
|
||||
};
|
||||
}
|
||||
|
||||
return { currentProps: nextProps };
|
||||
let nextDateRange = null;
|
||||
if (
|
||||
nextProps.dateRangeFrom !== get(prevState, 'currentProps.dateRangeFrom') ||
|
||||
nextProps.dateRangeTo !== get(prevState, 'currentProps.dateRangeTo')
|
||||
) {
|
||||
nextDateRange = {
|
||||
dateRangeFrom: nextProps.dateRangeFrom,
|
||||
dateRangeTo: nextProps.dateRangeTo,
|
||||
};
|
||||
}
|
||||
|
||||
const nextState: any = {
|
||||
currentProps: nextProps,
|
||||
};
|
||||
if (nextQuery) {
|
||||
nextState.query = nextQuery;
|
||||
}
|
||||
if (nextDateRange) {
|
||||
nextState.dateRangeFrom = nextDateRange.dateRangeFrom;
|
||||
nextState.dateRangeTo = nextDateRange.dateRangeTo;
|
||||
}
|
||||
return nextState;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -136,6 +166,9 @@ export class QueryBarUI extends Component<Props, State> {
|
|||
index: null,
|
||||
suggestions: [],
|
||||
suggestionLimit: 50,
|
||||
dateRangeFrom: _.get(this.props, 'dateRangeFrom', 'now-15m'),
|
||||
dateRangeTo: _.get(this.props, 'dateRangeTo', 'now'),
|
||||
isDateRangeInvalid: false,
|
||||
};
|
||||
|
||||
public updateSuggestions = debounce(async () => {
|
||||
|
@ -151,7 +184,15 @@ export class QueryBarUI extends Component<Props, State> {
|
|||
private persistedLog: PersistedLog | null = null;
|
||||
|
||||
public isDirty = () => {
|
||||
return this.state.query.query !== this.props.query.query;
|
||||
if (!this.props.showDatePicker) {
|
||||
return this.state.query.query !== this.props.query.query;
|
||||
}
|
||||
|
||||
return (
|
||||
this.state.query.query !== this.props.query.query ||
|
||||
this.state.dateRangeFrom !== this.props.dateRangeFrom ||
|
||||
this.state.dateRangeTo !== this.props.dateRangeTo
|
||||
);
|
||||
};
|
||||
|
||||
public increaseLimit = () => {
|
||||
|
@ -322,6 +363,22 @@ export class QueryBarUI extends Component<Props, State> {
|
|||
this.onInputChange(event.target.value);
|
||||
};
|
||||
|
||||
public onTimeChange = ({
|
||||
start,
|
||||
end,
|
||||
isInvalid,
|
||||
}: {
|
||||
start: string;
|
||||
end: string;
|
||||
isInvalid: boolean;
|
||||
}) => {
|
||||
this.setState({
|
||||
dateRangeFrom: start,
|
||||
dateRangeTo: end,
|
||||
isDateRangeInvalid: isInvalid,
|
||||
});
|
||||
};
|
||||
|
||||
public onKeyUp = (event: React.KeyboardEvent<HTMLInputElement>) => {
|
||||
if ([KEY_CODES.LEFT, KEY_CODES.RIGHT, KEY_CODES.HOME, KEY_CODES.END].includes(event.keyCode)) {
|
||||
this.setState({ isSuggestionsVisible: true });
|
||||
|
@ -408,9 +465,20 @@ export class QueryBarUI extends Component<Props, State> {
|
|||
this.persistedLog.add(this.state.query.query);
|
||||
}
|
||||
|
||||
timeHistory.add({
|
||||
from: this.state.dateRangeFrom,
|
||||
to: this.state.dateRangeTo,
|
||||
});
|
||||
|
||||
this.props.onSubmit({
|
||||
query: fromUser(this.state.query.query),
|
||||
language: this.state.query.language,
|
||||
query: {
|
||||
query: fromUser(this.state.query.query),
|
||||
language: this.state.query.language,
|
||||
},
|
||||
dateRange: {
|
||||
from: this.state.dateRangeFrom,
|
||||
to: this.state.dateRangeTo,
|
||||
},
|
||||
});
|
||||
this.setState({ isSuggestionsVisible: false });
|
||||
};
|
||||
|
@ -427,8 +495,14 @@ export class QueryBarUI extends Component<Props, State> {
|
|||
|
||||
this.props.store.set('kibana.userQueryLanguage', language);
|
||||
this.props.onSubmit({
|
||||
query: '',
|
||||
language,
|
||||
query: {
|
||||
query: '',
|
||||
language,
|
||||
},
|
||||
dateRange: {
|
||||
from: this.state.dateRangeFrom,
|
||||
to: this.state.dateRangeTo,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -462,8 +536,16 @@ export class QueryBarUI extends Component<Props, State> {
|
|||
}
|
||||
|
||||
public render() {
|
||||
const classes = classNames('kbnQueryBar', {
|
||||
'kbnQueryBar--withDatePicker': this.props.showDatePicker,
|
||||
});
|
||||
|
||||
return (
|
||||
<EuiFlexGroup responsive={false} gutterSize="s">
|
||||
<EuiFlexGroup
|
||||
className={classes}
|
||||
responsive={this.props.showDatePicker ? true : false}
|
||||
gutterSize="s"
|
||||
>
|
||||
<EuiFlexItem>
|
||||
<EuiOutsideClickDetector onOutsideClick={this.onOutsideClick}>
|
||||
{/* position:relative required on container so the suggestions appear under the query bar*/}
|
||||
|
@ -533,30 +615,74 @@ export class QueryBarUI extends Component<Props, State> {
|
|||
</div>
|
||||
</EuiOutsideClickDetector>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButton
|
||||
aria-label={this.props.intl.formatMessage({
|
||||
id: 'common.ui.queryBar.searchButtonAriaLabel',
|
||||
defaultMessage: 'Search',
|
||||
})}
|
||||
data-test-subj="querySubmitButton"
|
||||
color={this.isDirty() ? 'secondary' : 'primary'}
|
||||
fill
|
||||
onClick={this.onClickSubmitButton}
|
||||
>
|
||||
{this.isDirty() ? (
|
||||
<FormattedMessage id="common.ui.queryBar.updateButtonLabel" defaultMessage="Update" />
|
||||
) : (
|
||||
<FormattedMessage
|
||||
id="common.ui.queryBar.refreshButtonLabel"
|
||||
defaultMessage="Refresh"
|
||||
/>
|
||||
)}
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>{this.renderUpdateButton()}</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
}
|
||||
|
||||
private renderUpdateButton() {
|
||||
const button = (
|
||||
<EuiSuperUpdateButton
|
||||
needsUpdate={this.isDirty()}
|
||||
isDisabled={this.state.isDateRangeInvalid}
|
||||
onClick={this.onClickSubmitButton}
|
||||
data-test-subj="querySubmitButton"
|
||||
/>
|
||||
);
|
||||
if (this.props.showDatePicker) {
|
||||
return (
|
||||
<EuiFlexGroup responsive={false} gutterSize="s">
|
||||
{this.renderDatePicker()}
|
||||
<EuiFlexItem grow={false}>{button}</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
} else {
|
||||
return button;
|
||||
}
|
||||
}
|
||||
|
||||
private renderDatePicker() {
|
||||
if (!this.props.showDatePicker) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const recentlyUsedRanges = timeHistory
|
||||
.get()
|
||||
.map(({ from, to }: { from: string; to: string }) => {
|
||||
return {
|
||||
start: from,
|
||||
end: to,
|
||||
};
|
||||
});
|
||||
|
||||
const commonlyUsedRanges = config
|
||||
.get('timepicker:quickRanges')
|
||||
.map(({ from, to, display }: { from: string; to: string; display: string }) => {
|
||||
return {
|
||||
start: from,
|
||||
end: to,
|
||||
label: display,
|
||||
};
|
||||
});
|
||||
|
||||
return (
|
||||
<EuiFlexItem className="kbnQueryBar__datePickerWrapper">
|
||||
<EuiSuperDatePicker
|
||||
start={this.state.dateRangeFrom}
|
||||
end={this.state.dateRangeTo}
|
||||
isPaused={this.props.isRefreshPaused}
|
||||
refreshInterval={this.props.refreshInterval}
|
||||
onTimeChange={this.onTimeChange}
|
||||
onRefreshChange={this.props.onRefreshChange}
|
||||
showUpdateButton={false}
|
||||
recentlyUsedRanges={recentlyUsedRanges}
|
||||
commonlyUsedRanges={commonlyUsedRanges}
|
||||
dateFormat={config.get('dateFormat')}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
export const QueryBar = injectI18n(QueryBarUI);
|
||||
|
|
31
src/ui/public/timefilter/time_history.d.ts
vendored
Normal file
31
src/ui/public/timefilter/time_history.d.ts
vendored
Normal file
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
interface TimeRange {
|
||||
from: string;
|
||||
to: string;
|
||||
mode?: string;
|
||||
}
|
||||
|
||||
export interface TimeHistory {
|
||||
add: (options: TimeRange) => void;
|
||||
get: () => TimeRange[];
|
||||
}
|
||||
|
||||
export const timeHistory: TimeHistory;
|
|
@ -33,6 +33,7 @@ import {
|
|||
VisualBuilderPageProvider,
|
||||
TimelionPageProvider,
|
||||
SharePageProvider,
|
||||
TimePickerPageProvider,
|
||||
} from './page_objects';
|
||||
|
||||
import {
|
||||
|
@ -94,6 +95,7 @@ export default async function ({ readConfigFile }) {
|
|||
visualBuilder: VisualBuilderPageProvider,
|
||||
timelion: TimelionPageProvider,
|
||||
share: SharePageProvider,
|
||||
timePicker: TimePickerPageProvider,
|
||||
},
|
||||
services: {
|
||||
es: commonConfig.get('services.es'),
|
||||
|
|
|
@ -32,3 +32,4 @@ export { PointSeriesPageProvider } from './point_series_page';
|
|||
export { VisualBuilderPageProvider } from './visual_builder_page';
|
||||
export { TimelionPageProvider } from './timelion_page';
|
||||
export { SharePageProvider } from './share_page';
|
||||
export { TimePickerPageProvider } from './time_picker';
|
||||
|
|
122
test/functional/page_objects/time_picker.js
Normal file
122
test/functional/page_objects/time_picker.js
Normal file
|
@ -0,0 +1,122 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
export function TimePickerPageProvider({ getService }) {
|
||||
const log = getService('log');
|
||||
const retry = getService('retry');
|
||||
const find = getService('find');
|
||||
const testSubjects = getService('testSubjects');
|
||||
|
||||
class TimePickerPage {
|
||||
|
||||
async isQuickSelectMenuOpen() {
|
||||
return await testSubjects.exists('superDatePickerQuickMenu');
|
||||
}
|
||||
|
||||
async openQuickSelectTimeMenu() {
|
||||
log.debug('openQuickSelectTimeMenu');
|
||||
const isMenuOpen = await this.isQuickSelectMenuOpen();
|
||||
if (!isMenuOpen) {
|
||||
log.debug('opening quick select menu');
|
||||
await retry.try(async () => {
|
||||
await testSubjects.click('superDatePickerToggleQuickMenuButton');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async closeQuickSelectTimeMenu() {
|
||||
log.debug('closeQuickSelectTimeMenu');
|
||||
const isMenuOpen = await this.isQuickSelectMenuOpen();
|
||||
if (isMenuOpen) {
|
||||
log.debug('closing quick select menu');
|
||||
await retry.try(async () => {
|
||||
await testSubjects.click('superDatePickerToggleQuickMenuButton');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async showStartEndTimes() {
|
||||
const isShowDatesButton = await testSubjects.exists('superDatePickerShowDatesButton');
|
||||
if (isShowDatesButton) {
|
||||
await testSubjects.click('superDatePickerShowDatesButton');
|
||||
}
|
||||
}
|
||||
|
||||
async getRefreshConfig(keepQuickSelectOpen = false) {
|
||||
await this.openQuickSelectTimeMenu();
|
||||
const interval = await testSubjects.getAttribute('superDatePickerRefreshIntervalInput', 'value');
|
||||
|
||||
let selectedUnit;
|
||||
const select = await testSubjects.find('superDatePickerRefreshIntervalUnitsSelect');
|
||||
const options = await find.allDescendantDisplayedByCssSelector('option', select);
|
||||
await Promise.all(options.map(async (optionElement) => {
|
||||
const isSelected = await optionElement.isSelected();
|
||||
if (isSelected) {
|
||||
selectedUnit = await optionElement.getVisibleText();
|
||||
}
|
||||
}));
|
||||
|
||||
const toggleButtonText = await testSubjects.getVisibleText('superDatePickerToggleRefreshButton');
|
||||
if (!keepQuickSelectOpen) {
|
||||
await this.closeQuickSelectTimeMenu();
|
||||
}
|
||||
|
||||
return {
|
||||
interval,
|
||||
units: selectedUnit,
|
||||
isPaused: toggleButtonText === 'Start' ? true : false
|
||||
};
|
||||
}
|
||||
|
||||
async getTimeConfig() {
|
||||
await this.showStartEndTimes();
|
||||
const start = await testSubjects.getVisibleText('superDatePickerstartDatePopoverButton');
|
||||
const end = await testSubjects.getVisibleText('superDatePickerendDatePopoverButton');
|
||||
return {
|
||||
start,
|
||||
end
|
||||
};
|
||||
}
|
||||
|
||||
async pauseAutoRefresh() {
|
||||
log.debug('pauseAutoRefresh');
|
||||
const refreshConfig = await this.getRefreshConfig(true);
|
||||
if (!refreshConfig.isPaused) {
|
||||
log.debug('pause auto refresh');
|
||||
await testSubjects.click('superDatePickerToggleRefreshButton');
|
||||
await this.closeQuickSelectTimeMenu();
|
||||
}
|
||||
|
||||
await this.closeQuickSelectTimeMenu();
|
||||
}
|
||||
|
||||
async resumeAutoRefresh() {
|
||||
log.debug('resumeAutoRefresh');
|
||||
const refreshConfig = await this.getRefreshConfig(true);
|
||||
if (refreshConfig.isPaused) {
|
||||
log.debug('resume auto refresh');
|
||||
await testSubjects.click('superDatePickerToggleRefreshButton');
|
||||
}
|
||||
|
||||
await this.closeQuickSelectTimeMenu();
|
||||
}
|
||||
}
|
||||
|
||||
return new TimePickerPage();
|
||||
}
|
|
@ -16,7 +16,6 @@ import {
|
|||
getMapReady,
|
||||
getWaitingForMapReadyLayerListRaw,
|
||||
} from '../selectors/map_selectors';
|
||||
import { timeService } from '../kibana_services';
|
||||
|
||||
export const SET_SELECTED_LAYER = 'SET_SELECTED_LAYER';
|
||||
export const UPDATE_LAYER_ORDER = 'UPDATE_LAYER_ORDER';
|
||||
|
@ -35,7 +34,6 @@ export const LAYER_DATA_LOAD_STARTED = 'LAYER_DATA_LOAD_STARTED';
|
|||
export const LAYER_DATA_LOAD_ENDED = 'LAYER_DATA_LOAD_ENDED';
|
||||
export const LAYER_DATA_LOAD_ERROR = 'LAYER_DATA_LOAD_ERROR';
|
||||
export const SET_JOINS = 'SET_JOINS';
|
||||
export const SET_TIME_FILTERS = 'SET_TIME_FILTERS';
|
||||
export const SET_QUERY = 'SET_QUERY';
|
||||
export const TRIGGER_REFRESH_TIMER = 'TRIGGER_REFRESH_TIMER';
|
||||
export const UPDATE_LAYER_PROP = 'UPDATE_LAYER_PROP';
|
||||
|
@ -418,43 +416,17 @@ export function removeLayer(id) {
|
|||
}
|
||||
|
||||
export function setMeta(metaJson) {
|
||||
return async dispatch => {
|
||||
dispatch({
|
||||
type: SET_META,
|
||||
meta: metaJson
|
||||
});
|
||||
return {
|
||||
type: SET_META,
|
||||
meta: metaJson
|
||||
};
|
||||
}
|
||||
|
||||
export function setTimeFiltersToKbnGlobalTime() {
|
||||
return (dispatch) => {
|
||||
dispatch(setTimeFilters(timeService.getTime()));
|
||||
};
|
||||
}
|
||||
|
||||
export function setTimeFilters({ from, to }) {
|
||||
return async (dispatch, getState) => {
|
||||
dispatch({
|
||||
type: SET_TIME_FILTERS,
|
||||
from,
|
||||
to,
|
||||
});
|
||||
|
||||
// Update Kibana global time
|
||||
const kbnTime = timeService.getTime();
|
||||
if ((to && to !== kbnTime.to) || (from && from !== kbnTime.from)) {
|
||||
timeService.setTime({ from, to });
|
||||
}
|
||||
|
||||
const dataFilters = getDataFilters(getState());
|
||||
await syncDataForAllLayers(getState, dispatch, dataFilters);
|
||||
};
|
||||
}
|
||||
|
||||
export function setQuery({ query }) {
|
||||
export function setQuery({ query, timeFilters }) {
|
||||
return async (dispatch, getState) => {
|
||||
dispatch({
|
||||
type: SET_QUERY,
|
||||
timeFilters,
|
||||
query: {
|
||||
...query,
|
||||
// ensure query changes to trigger re-fetch even when query is the same because "Refresh" clicked
|
||||
|
@ -468,21 +440,10 @@ export function setQuery({ query }) {
|
|||
}
|
||||
|
||||
export function setRefreshConfig({ isPaused, interval }) {
|
||||
return async (dispatch) => {
|
||||
dispatch({
|
||||
type: SET_REFRESH_CONFIG,
|
||||
isPaused,
|
||||
interval,
|
||||
});
|
||||
|
||||
// Update Kibana global refresh
|
||||
const kbnRefresh = timeService.getRefreshInterval();
|
||||
if (isPaused !== kbnRefresh.pause || interval !== kbnRefresh.value) {
|
||||
timeService.setRefreshInterval({
|
||||
pause: isPaused,
|
||||
value: interval,
|
||||
});
|
||||
}
|
||||
return {
|
||||
type: SET_REFRESH_CONFIG,
|
||||
isPaused,
|
||||
interval,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
32
x-pack/plugins/gis/public/angular/get_initial_query.js
vendored
Normal file
32
x-pack/plugins/gis/public/angular/get_initial_query.js
vendored
Normal file
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import chrome from 'ui/chrome';
|
||||
|
||||
const settings = chrome.getUiSettingsClient();
|
||||
|
||||
export function getInitialQuery({
|
||||
mapStateJSON,
|
||||
appState = {},
|
||||
userQueryLanguage,
|
||||
}) {
|
||||
|
||||
if (appState.query) {
|
||||
return appState.query;
|
||||
}
|
||||
|
||||
if (mapStateJSON) {
|
||||
const mapState = JSON.parse(mapStateJSON);
|
||||
if (mapState.query) {
|
||||
return mapState.query;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
query: '',
|
||||
language: userQueryLanguage || settings.get('search:queryLanguage')
|
||||
};
|
||||
}
|
28
x-pack/plugins/gis/public/angular/get_initial_refresh_config.js
vendored
Normal file
28
x-pack/plugins/gis/public/angular/get_initial_refresh_config.js
vendored
Normal file
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
import chrome from 'ui/chrome';
|
||||
|
||||
const uiSettings = chrome.getUiSettingsClient();
|
||||
|
||||
export function getInitialRefreshConfig({
|
||||
mapStateJSON,
|
||||
globalState = {},
|
||||
}) {
|
||||
|
||||
if (mapStateJSON) {
|
||||
const mapState = JSON.parse(mapStateJSON);
|
||||
if (mapState.refreshConfig) {
|
||||
return mapState.refreshConfig;
|
||||
}
|
||||
}
|
||||
|
||||
const defaultRefreshConfig = uiSettings.get('timepicker:refreshIntervalDefaults');
|
||||
const refreshInterval = { ...defaultRefreshConfig, ...globalState.refreshInterval };
|
||||
return {
|
||||
isPaused: refreshInterval.pause,
|
||||
interval: refreshInterval.value,
|
||||
};
|
||||
}
|
24
x-pack/plugins/gis/public/angular/get_initial_time_filters.js
vendored
Normal file
24
x-pack/plugins/gis/public/angular/get_initial_time_filters.js
vendored
Normal file
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
import chrome from 'ui/chrome';
|
||||
|
||||
const uiSettings = chrome.getUiSettingsClient();
|
||||
|
||||
export function getInitialTimeFilters({
|
||||
mapStateJSON,
|
||||
globalState = {},
|
||||
}) {
|
||||
|
||||
if (mapStateJSON) {
|
||||
const mapState = JSON.parse(mapStateJSON);
|
||||
if (mapState.timeFilters) {
|
||||
return mapState.timeFilters;
|
||||
}
|
||||
}
|
||||
|
||||
const defaultTime = uiSettings.get('timepicker:timeDefaults');
|
||||
return { ...defaultTime, ...globalState.time };
|
||||
}
|
|
@ -27,6 +27,12 @@
|
|||
app-name="'maps'"
|
||||
on-submit="updateQueryAndDispatch"
|
||||
index-patterns="indexPatterns"
|
||||
show-date-picker="showDatePicker"
|
||||
date-range-from="time.from"
|
||||
date-range-to="time.to"
|
||||
is-refresh-paused="refreshConfig.isPaused"
|
||||
refresh-interval="refreshConfig.interval"
|
||||
on-refresh-change="onRefreshChange"
|
||||
></query-bar>
|
||||
</div>
|
||||
|
||||
|
|
108
x-pack/plugins/gis/public/angular/map_controller.js
vendored
108
x-pack/plugins/gis/public/angular/map_controller.js
vendored
|
@ -14,7 +14,6 @@ import { getStore } from '../store/store';
|
|||
import { GisMap } from '../components/gis_map';
|
||||
import {
|
||||
setSelectedLayer,
|
||||
setTimeFilters,
|
||||
setRefreshConfig,
|
||||
setGotoWithCenter,
|
||||
replaceLayerList,
|
||||
|
@ -28,40 +27,95 @@ import { SavedObjectSaveModal } from 'ui/saved_objects/components/saved_object_s
|
|||
import { showSaveModal } from 'ui/saved_objects/show_saved_object_save_modal';
|
||||
import { toastNotifications } from 'ui/notify';
|
||||
import { getInitialLayers } from './get_initial_layers';
|
||||
import { getInitialQuery } from './get_initial_query';
|
||||
import { getInitialTimeFilters } from './get_initial_time_filters';
|
||||
import { getInitialRefreshConfig } from './get_initial_refresh_config';
|
||||
|
||||
const REACT_ANCHOR_DOM_ELEMENT_ID = 'react-gis-root';
|
||||
const DEFAULT_QUERY_LANGUAGE = 'kuery';
|
||||
|
||||
|
||||
const app = uiModules.get('app/gis', []);
|
||||
|
||||
app.controller('GisMapController', ($scope, $route, config, kbnUrl, localStorage, AppState) => {
|
||||
app.controller('GisMapController', ($scope, $route, config, kbnUrl, localStorage, AppState, globalState) => {
|
||||
|
||||
const savedMap = $scope.map = $route.current.locals.map;
|
||||
let unsubscribe;
|
||||
|
||||
inspectorAdapters.requests.reset();
|
||||
|
||||
$scope.$listen(globalState, 'fetch_with_changes', (diff) => {
|
||||
if (diff.includes('time')) {
|
||||
$scope.updateQueryAndDispatch({ query: $scope.query, dateRange: globalState.time });
|
||||
}
|
||||
if (diff.includes('refreshInterval')) {
|
||||
$scope.onRefreshChange({ isPaused: globalState.pause, refreshInterval: globalState.value });
|
||||
}
|
||||
});
|
||||
|
||||
const $state = new AppState();
|
||||
$scope.$listen($state, 'fetch_with_changes', function (diff) {
|
||||
if (diff.includes('query')) {
|
||||
$scope.updateQueryAndDispatch($state.query);
|
||||
$scope.updateQueryAndDispatch({ query: $state.query, dateRange: $scope.time });
|
||||
}
|
||||
});
|
||||
$scope.query = {};
|
||||
|
||||
function syncAppAndGlobalState() {
|
||||
$scope.$evalAsync(() => {
|
||||
$state.query = $scope.query;
|
||||
$state.save();
|
||||
globalState.time = $scope.time;
|
||||
globalState.refreshInterval = {
|
||||
pause: $scope.refreshConfig.isPaused,
|
||||
value: $scope.refreshConfig.interval,
|
||||
};
|
||||
globalState.save();
|
||||
});
|
||||
}
|
||||
|
||||
$scope.query = getInitialQuery({
|
||||
mapStateJSON: savedMap.mapStateJSON,
|
||||
appState: $state,
|
||||
userQueryLanguage: localStorage.get('kibana.userQueryLanguage')
|
||||
});
|
||||
$scope.time = getInitialTimeFilters({
|
||||
mapStateJSON: savedMap.mapStateJSON,
|
||||
globalState: globalState,
|
||||
});
|
||||
$scope.refreshConfig = getInitialRefreshConfig({
|
||||
mapStateJSON: savedMap.mapStateJSON,
|
||||
globalState: globalState,
|
||||
});
|
||||
syncAppAndGlobalState();
|
||||
|
||||
$scope.indexPatterns = [];
|
||||
$scope.updateQueryAndDispatch = function (newQuery) {
|
||||
$scope.query = newQuery;
|
||||
$scope.updateQueryAndDispatch = function ({ dateRange, query }) {
|
||||
$scope.query = query;
|
||||
$scope.time = dateRange;
|
||||
getStore().then(store => {
|
||||
// ignore outdated query
|
||||
if ($scope.query !== newQuery) {
|
||||
if ($scope.query !== query && $scope.time !== dateRange) {
|
||||
return;
|
||||
}
|
||||
|
||||
store.dispatch(setQuery({ query: $scope.query }));
|
||||
store.dispatch(setQuery({ query: $scope.query, timeFilters: $scope.time }));
|
||||
|
||||
// update appState
|
||||
$state.query = $scope.query;
|
||||
$state.save();
|
||||
syncAppAndGlobalState();
|
||||
});
|
||||
};
|
||||
$scope.onRefreshChange = function ({ isPaused, refreshInterval }) {
|
||||
$scope.refreshConfig = {
|
||||
isPaused,
|
||||
interval: refreshInterval ? refreshInterval : $scope.refreshConfig.interval
|
||||
};
|
||||
getStore().then(store => {
|
||||
// ignore outdated
|
||||
if ($scope.refreshConfig.isPaused !== isPaused && $scope.refreshConfig.interval !== refreshInterval) {
|
||||
return;
|
||||
}
|
||||
|
||||
store.dispatch(setRefreshConfig($scope.refreshConfig));
|
||||
|
||||
syncAppAndGlobalState();
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -76,36 +130,20 @@ app.controller('GisMapController', ($scope, $route, config, kbnUrl, localStorage
|
|||
});
|
||||
|
||||
// sync store with savedMap mapState
|
||||
let queryFromSavedObject;
|
||||
if (savedMap.mapStateJSON) {
|
||||
const mapState = JSON.parse(savedMap.mapStateJSON);
|
||||
queryFromSavedObject = mapState.query;
|
||||
const timeFilters = mapState.timeFilters ? mapState.timeFilters : timefilter.getTime();
|
||||
store.dispatch(setTimeFilters(timeFilters));
|
||||
store.dispatch(setGotoWithCenter({
|
||||
lat: mapState.center.lat,
|
||||
lon: mapState.center.lon,
|
||||
zoom: mapState.zoom,
|
||||
}));
|
||||
if (mapState.refreshConfig) {
|
||||
store.dispatch(setRefreshConfig(mapState.refreshConfig));
|
||||
}
|
||||
}
|
||||
|
||||
const layerList = getInitialLayers(savedMap.layerListJSON, getDataSources(store.getState()));
|
||||
store.dispatch(replaceLayerList(layerList));
|
||||
|
||||
// Initialize query, syncing appState and store
|
||||
if ($state.query) {
|
||||
$scope.updateQueryAndDispatch($state.query);
|
||||
} else if (queryFromSavedObject) {
|
||||
$scope.updateQueryAndDispatch(queryFromSavedObject);
|
||||
} else {
|
||||
$scope.updateQueryAndDispatch({
|
||||
query: '',
|
||||
language: localStorage.get('kibana.userQueryLanguage') || DEFAULT_QUERY_LANGUAGE
|
||||
});
|
||||
}
|
||||
store.dispatch(setRefreshConfig($scope.refreshConfig));
|
||||
store.dispatch(setQuery({ query: $scope.query, timeFilters: $scope.time }));
|
||||
|
||||
const root = document.getElementById(REACT_ANCHOR_DOM_ELEMENT_ID);
|
||||
render(
|
||||
|
@ -136,9 +174,7 @@ app.controller('GisMapController', ($scope, $route, config, kbnUrl, localStorage
|
|||
}
|
||||
|
||||
function handleStoreChanges(store) {
|
||||
const state = store.getState();
|
||||
|
||||
const nextIndexPatternIds = getUniqueIndexPatternIds(state);
|
||||
const nextIndexPatternIds = getUniqueIndexPatternIds(store.getState());
|
||||
if (nextIndexPatternIds !== prevIndexPatternIds) {
|
||||
prevIndexPatternIds = nextIndexPatternIds;
|
||||
updateIndexPatterns(nextIndexPatternIds);
|
||||
|
@ -197,6 +233,10 @@ app.controller('GisMapController', ($scope, $route, config, kbnUrl, localStorage
|
|||
return { id };
|
||||
}
|
||||
|
||||
// Hide angular timepicer/refresh UI from top nav
|
||||
timefilter.disableTimeRangeSelector();
|
||||
timefilter.disableAutoRefreshSelector();
|
||||
$scope.showDatePicker = true; // used by query-bar directive to enable timepikcer in query bar
|
||||
$scope.topNavMenu = [{
|
||||
key: 'inspect',
|
||||
description: 'Open Inspector',
|
||||
|
@ -238,6 +278,4 @@ app.controller('GisMapController', ($scope, $route, config, kbnUrl, localStorage
|
|||
showSaveModal(saveModal);
|
||||
}
|
||||
}];
|
||||
timefilter.enableTimeRangeSelector();
|
||||
timefilter.enableAutoRefreshSelector();
|
||||
});
|
||||
|
|
|
@ -7,26 +7,22 @@
|
|||
import { connect } from 'react-redux';
|
||||
import { GisMap } from './view';
|
||||
import { getFlyoutDisplay, FLYOUT_STATE } from '../../store/ui';
|
||||
import {
|
||||
setTimeFiltersToKbnGlobalTime,
|
||||
triggerRefreshTimer,
|
||||
setRefreshConfig
|
||||
} from '../../actions/store_actions';
|
||||
import { triggerRefreshTimer } from '../../actions/store_actions';
|
||||
import { getRefreshConfig } from '../../selectors/map_selectors';
|
||||
|
||||
function mapStateToProps(state = {}) {
|
||||
const flyoutDisplay = getFlyoutDisplay(state);
|
||||
return {
|
||||
layerDetailsVisible: flyoutDisplay === FLYOUT_STATE.LAYER_PANEL,
|
||||
addLayerVisible: flyoutDisplay === FLYOUT_STATE.ADD_LAYER_WIZARD,
|
||||
noFlyoutVisible: flyoutDisplay === FLYOUT_STATE.NONE
|
||||
noFlyoutVisible: flyoutDisplay === FLYOUT_STATE.NONE,
|
||||
refreshConfig: getRefreshConfig(state),
|
||||
};
|
||||
}
|
||||
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
setTimeFiltersToKbnGlobalTime: () => dispatch(setTimeFiltersToKbnGlobalTime()),
|
||||
triggerRefreshTimer: () => dispatch(triggerRefreshTimer()),
|
||||
setRefreshConfig: (({ isPaused, interval }) => dispatch(setRefreshConfig({ isPaused, interval }))),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -12,39 +12,41 @@ import { AddLayerPanel } from '../layer_addpanel/index';
|
|||
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
|
||||
import { Toasts } from '../toasts';
|
||||
|
||||
import { timeService } from '../../kibana_services';
|
||||
|
||||
export class GisMap extends Component {
|
||||
|
||||
componentDidMount() {
|
||||
timeService.on('timeUpdate', this.props.setTimeFiltersToKbnGlobalTime);
|
||||
timeService.on('refreshIntervalUpdate', this.setRefreshTimer);
|
||||
this.setRefreshTimer();
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
this.setRefreshTimer();
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
timeService.off('timeUpdate', this.props.setTimeFiltersToKbnGlobalTime);
|
||||
timeService.off('refreshIntervalUpdate', this.setRefreshTimer);
|
||||
this.clearRefreshTimer();
|
||||
}
|
||||
|
||||
setRefreshTimer = () => {
|
||||
const { isPaused, interval } = this.props.refreshConfig;
|
||||
|
||||
if (this.isPaused === isPaused && this.interval === interval) {
|
||||
// refreshConfig is the same, nothing to do
|
||||
return;
|
||||
}
|
||||
|
||||
this.isPaused = isPaused;
|
||||
this.interval = interval;
|
||||
|
||||
this.clearRefreshTimer();
|
||||
|
||||
const { value, pause } = timeService.getRefreshInterval();
|
||||
if (!pause && value > 0) {
|
||||
if (!isPaused && interval > 0) {
|
||||
this.refreshTimerId = setInterval(
|
||||
() => {
|
||||
this.props.triggerRefreshTimer();
|
||||
},
|
||||
value
|
||||
interval
|
||||
);
|
||||
}
|
||||
|
||||
this.props.setRefreshConfig({
|
||||
isPaused: pause,
|
||||
interval: value,
|
||||
});
|
||||
}
|
||||
|
||||
clearRefreshTimer = () => {
|
||||
|
|
|
@ -20,7 +20,6 @@ import {
|
|||
MAP_EXTENT_CHANGED,
|
||||
MAP_READY,
|
||||
MAP_DESTROYED,
|
||||
SET_TIME_FILTERS,
|
||||
SET_QUERY,
|
||||
UPDATE_LAYER_PROP,
|
||||
UPDATE_LAYER_STYLE_FOR_SELECTED_LAYER,
|
||||
|
@ -163,12 +162,16 @@ export function map(state = INITIAL_STATE, action) {
|
|||
buffer: action.mapState.buffer,
|
||||
};
|
||||
return { ...state, mapState: { ...state.mapState, ...newMapState } };
|
||||
case SET_TIME_FILTERS:
|
||||
const { from, to } = action;
|
||||
return { ...state, mapState: { ...state.mapState, timeFilters: { from, to } } };
|
||||
case SET_QUERY:
|
||||
const { query } = action;
|
||||
return { ...state, mapState: { ...state.mapState, query } };
|
||||
const { query, timeFilters } = action;
|
||||
return {
|
||||
...state,
|
||||
mapState: {
|
||||
...state.mapState,
|
||||
query,
|
||||
timeFilters,
|
||||
}
|
||||
};
|
||||
case SET_REFRESH_CONFIG:
|
||||
const { isPaused, interval } = action;
|
||||
return {
|
||||
|
|
|
@ -8,7 +8,7 @@ import expect from 'expect.js';
|
|||
|
||||
export default function ({ getPageObjects, getService }) {
|
||||
|
||||
const PageObjects = getPageObjects(['gis', 'header']);
|
||||
const PageObjects = getPageObjects(['gis', 'header', 'timePicker']);
|
||||
const queryBar = getService('queryBar');
|
||||
const browser = getService('browser');
|
||||
const inspector = getService('inspector');
|
||||
|
@ -25,13 +25,16 @@ export default function ({ getPageObjects, getService }) {
|
|||
});
|
||||
|
||||
it('should update global Kibana time to value stored with map', async () => {
|
||||
const kibanaTime = await PageObjects.header.getPrettyDuration();
|
||||
expect(kibanaTime).to.equal('Last 17m');
|
||||
const timeConfig = await PageObjects.timePicker.getTimeConfig();
|
||||
expect(timeConfig.start).to.equal('~ 17 minutes ago');
|
||||
expect(timeConfig.end).to.equal('now');
|
||||
});
|
||||
|
||||
it('should update global Kibana refresh config to value stored with map', async () => {
|
||||
const kibanaRefreshConfig = await PageObjects.header.getRefreshConfig();
|
||||
expect(kibanaRefreshConfig).to.equal('inactive 1 second');
|
||||
const kibanaRefreshConfig = await PageObjects.timePicker.getRefreshConfig();
|
||||
expect(kibanaRefreshConfig.interval).to.equal('0.02');
|
||||
expect(kibanaRefreshConfig.units).to.equal('minutes');
|
||||
expect(kibanaRefreshConfig.isPaused).to.equal(true);
|
||||
});
|
||||
|
||||
it('should set map location to value stored with map', async () => {
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
|
||||
export function GisPageProvider({ getService, getPageObjects }) {
|
||||
const PageObjects = getPageObjects(['common', 'header']);
|
||||
const PageObjects = getPageObjects(['common', 'header', 'timePicker']);
|
||||
|
||||
const log = getService('log');
|
||||
const testSubjects = getService('testSubjects');
|
||||
|
@ -204,10 +204,10 @@ export function GisPageProvider({ getService, getPageObjects }) {
|
|||
|
||||
async triggerSingleRefresh(refreshInterval) {
|
||||
log.debug(`triggerSingleRefresh, refreshInterval: ${refreshInterval}`);
|
||||
await PageObjects.header.resumeAutoRefresh();
|
||||
await PageObjects.timePicker.resumeAutoRefresh();
|
||||
log.debug('waiting to give time for refresh timer to fire');
|
||||
await PageObjects.common.sleep(refreshInterval + (refreshInterval / 2));
|
||||
await PageObjects.header.pauseAutoRefresh();
|
||||
await PageObjects.timePicker.pauseAutoRefresh();
|
||||
await PageObjects.header.waitUntilLoadingHasFinished();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue