Replace angular timepicker with EuiSuperDatePicker (#29204)

* replace kbnTimepicker directive with EuiSuperDatePicker

* remove kbnTimepicker directive

* remove bootstrap datepicker

* 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

* update setAbsoluteRange for EuiSuperDatePicker

* replace setQuickTime with calls to setAbsoluteTime

* remove open time picker button in discover empty results view

* pass config values to super-date-picker directive

* remove getPrettyDuration

* clean up typescript lint problems

* some functional test fixes

* try something else to fix I18n problems

* fix some more functional tests

* update query_bar jest test

* remove unused method in kbn_global_timepicker

* do not import removed timepicker styles

* remove mode from time state

* remove mode from time_history interface

* fix problem with ui_settings_defaults

* fix failing TSVB functional test

* another round to test fixes

* more functional test changes

* fixes for failing tests

* add retry block to flaky tsvb test

* styles/bootstrap_dark.less

* fix functional tests

* call fetch event even when times are the same

* add retry around flaky tsvb test

* fix timefilter jest test, attempt to fix another flaky functional test

* revert emitting fetch outside of areTimePickerValsDifferent check

* clean up time mode code that is no longer needed in dashboard

* fix timefilter tests changed by timefilter emit revert
This commit is contained in:
Nathan Reese 2019-02-05 20:45:31 -07:00 committed by GitHub
parent ebe69ee4e5
commit 6ad036b187
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
116 changed files with 405 additions and 4022 deletions

View file

@ -125,7 +125,7 @@ app.directive('dashboardApp', function ($injector) {
// The 'previouslyStored' check is so we only update the time filter on dashboard open, not during
// normal cross app navigation.
if (dashboardStateManager.getIsTimeSavedWithDashboard() && !getAppState.previouslyStored()) {
dashboardStateManager.syncTimefilterWithDashboard(timefilter, config.get('timepicker:quickRanges'));
dashboardStateManager.syncTimefilterWithDashboard(timefilter);
}
const updateState = () => {
@ -307,7 +307,7 @@ app.directive('dashboardApp', function ($injector) {
// it does on 'open' because it's been saved to the url and the getAppState.previouslyStored() check on
// reload will cause it not to sync.
if (dashboardStateManager.getIsTimeSavedWithDashboard()) {
dashboardStateManager.syncTimefilterWithDashboard(timefilter, config.get('timepicker:quickRanges'));
dashboardStateManager.syncTimefilterWithDashboard(timefilter);
}
}

View file

@ -33,7 +33,6 @@ describe('DashboardState', function () {
time: {},
setTime: function (time) { this.time = time; },
};
const mockQuickTimeRanges = [{ from: 'now/w', to: 'now/w', display: 'This week', section: 0 }];
const mockIndexPattern = { id: 'index1' };
function initDashboardState() {
@ -52,12 +51,10 @@ describe('DashboardState', function () {
mockTimefilter.time.from = '2015-09-19 06:31:44.000';
mockTimefilter.time.to = '2015-09-29 06:31:44.000';
mockTimefilter.time.mode = 'absolute';
initDashboardState();
dashboardState.syncTimefilterWithDashboard(mockTimefilter, mockQuickTimeRanges);
dashboardState.syncTimefilterWithDashboard(mockTimefilter);
expect(mockTimefilter.time.mode).toBe('quick');
expect(mockTimefilter.time.to).toBe('now/w');
expect(mockTimefilter.time.from).toBe('now/w');
});
@ -69,12 +66,10 @@ describe('DashboardState', function () {
mockTimefilter.time.from = '2015-09-19 06:31:44.000';
mockTimefilter.time.to = '2015-09-29 06:31:44.000';
mockTimefilter.time.mode = 'absolute';
initDashboardState();
dashboardState.syncTimefilterWithDashboard(mockTimefilter, mockQuickTimeRanges);
dashboardState.syncTimefilterWithDashboard(mockTimefilter);
expect(mockTimefilter.time.mode).toBe('relative');
expect(mockTimefilter.time.to).toBe('now');
expect(mockTimefilter.time.from).toBe('now-13d');
});
@ -86,12 +81,10 @@ describe('DashboardState', function () {
mockTimefilter.time.from = 'now/w';
mockTimefilter.time.to = 'now/w';
mockTimefilter.time.mode = 'quick';
initDashboardState();
dashboardState.syncTimefilterWithDashboard(mockTimefilter, mockQuickTimeRanges);
dashboardState.syncTimefilterWithDashboard(mockTimefilter);
expect(mockTimefilter.time.mode).toBe('absolute');
expect(mockTimefilter.time.to).toBe(savedDashboard.timeTo);
expect(mockTimefilter.time.from).toBe(savedDashboard.timeFrom);
});

View file

@ -19,7 +19,6 @@
import { i18n } from '@kbn/i18n';
import _ from 'lodash';
import moment from 'moment';
import { DashboardViewMode } from './dashboard_view_mode';
import { FilterUtils } from './lib/filter_utils';
@ -144,13 +143,11 @@ export class DashboardStateManager {
* or a relative time (now-15m), or a moment object
* @param {String|Object} newTimeFilter.from - either a string representing an absolute or a relative time, or a
* moment object
* @param {String} newTimeFilter.mode
*/
handleTimeChange(newTimeFilter) {
store.dispatch(updateTimeRange({
from: FilterUtils.convertTimeToUTCString(newTimeFilter.from),
to: FilterUtils.convertTimeToUTCString(newTimeFilter.to),
mode: newTimeFilter.mode,
}));
}
@ -543,30 +540,17 @@ export class DashboardStateManager {
* @param {Object} timeFilter
* @param {func} timeFilter.setTime
* @param {func} timeFilter.setRefreshInterval
* @param quickTimeRanges
*/
syncTimefilterWithDashboard(timeFilter, quickTimeRanges) {
syncTimefilterWithDashboard(timeFilter) {
if (!this.getIsTimeSavedWithDashboard()) {
throw new Error(i18n.translate('kbn.dashboard.stateManager.timeNotSavedWithDashboardErrorMessage', {
defaultMessage: 'The time is not saved with this dashboard so should not be synced.',
}));
}
let mode;
const isMoment = moment(this.savedDashboard.timeTo).isValid();
if (isMoment) {
mode = 'absolute';
} else {
const quickTime = _.find(
quickTimeRanges,
(timeRange) => timeRange.from === this.savedDashboard.timeFrom && timeRange.to === this.savedDashboard.timeTo);
mode = quickTime ? 'quick' : 'relative';
}
timeFilter.setTime({
from: this.savedDashboard.timeFrom,
to: this.savedDashboard.timeTo,
mode
});
if (this.savedDashboard.refreshInterval) {

View file

@ -425,20 +425,13 @@ Array [
<div
class="euiText euiText--medium"
>
<h3>
<h3
data-test-subj="discoverNoResultsTimefilter"
>
Expand your time range
</h3>
<p>
One or more of the indices youre looking at contains a date field. Your query may not match anything in the current time range, or there may not be any data at all in the currently selected time range. You can try
<button
aria-expanded="false"
class="euiLink euiLink--primary"
data-test-subj="discoverNoResultsTimefilter"
type="button"
>
opening the time picker
</button>
and changing the time range to one which contains data.
One or more of the indices youre looking at contains a date field. Your query may not match anything in the current time range, or there may not be any data at all in the currently selected time range. You can try changing the time range to one which contains data.
</p>
</div>
</div>

View file

@ -33,18 +33,13 @@ import {
EuiText,
} from '@elastic/eui';
// eslint-disable-next-line react/prefer-stateless-function
export class DiscoverNoResults extends Component {
static propTypes = {
shardFailures: PropTypes.array,
timeFieldName: PropTypes.string,
queryLanguage: PropTypes.string,
isTimePickerOpen: PropTypes.bool.isRequired,
getDocLink: PropTypes.func.isRequired,
topNavToggle: PropTypes.func.isRequired,
};
onClickTimePickerButton = () => {
this.props.topNavToggle('filter');
};
render() {
@ -53,7 +48,6 @@ export class DiscoverNoResults extends Component {
timeFieldName,
queryLanguage,
getDocLink,
isTimePickerOpen,
} = this.props;
let shardFailuresMessage;
@ -125,7 +119,7 @@ export class DiscoverNoResults extends Component {
<EuiSpacer size="xl" />
<EuiText>
<h3>
<h3 data-test-subj="discoverNoResultsTimefilter">
<FormattedMessage
id="kbn.discover.noResults.expandYourTimeRangeTitle"
defaultMessage="Expand your time range"
@ -137,21 +131,7 @@ export class DiscoverNoResults extends Component {
id="kbn.discover.noResults.queryMayNotMatchTitle"
defaultMessage="One or more of the indices you&rsquo;re looking at contains a date field. Your query may
not match anything in the current time range, or there may not be any data at all in
the currently selected time range. You can try {timepickerLink} and changing the time range to one which contains data."
values={{
timepickerLink: (
<EuiLink
data-test-subj="discoverNoResultsTimefilter"
onClick={this.onClickTimePickerButton}
aria-expanded={isTimePickerOpen}
>
<FormattedMessage
id="kbn.discover.noResults.openingTimepickerLinkText"
defaultMessage="opening the time picker"
/>
</EuiLink>
)
}}
the currently selected time range. You can try changing the time range to one which contains data."
/>
</p>

View file

@ -18,9 +18,7 @@
*/
import React from 'react';
import { renderWithIntl, mountWithIntl } from 'test_utils/enzyme_helpers';
import sinon from 'sinon';
import { findTestSubject } from '@elastic/eui/lib/test';
import { renderWithIntl } from 'test_utils/enzyme_helpers';
import {
DiscoverNoResults,
@ -43,8 +41,6 @@ describe('DiscoverNoResults', () => {
const component = renderWithIntl(
<DiscoverNoResults
shardFailures={shardFailures}
isTimePickerOpen={false}
topNavToggle={() => {}}
getDocLink={() => ''}
/>
);
@ -58,8 +54,6 @@ describe('DiscoverNoResults', () => {
const component = renderWithIntl(
<DiscoverNoResults
shardFailures={shardFailures}
isTimePickerOpen={false}
topNavToggle={() => {}}
getDocLink={() => ''}
/>
);
@ -68,45 +62,11 @@ describe('DiscoverNoResults', () => {
});
});
describe('isTimePickerOpen', () => {
test('false is reflected in the aria-expanded state of the button', () => {
const component = renderWithIntl(
<DiscoverNoResults
timeFieldName="awesome_time_field"
isTimePickerOpen={false}
topNavToggle={() => {}}
getDocLink={() => ''}
/>
);
expect(
component.find('[data-test-subj="discoverNoResultsTimefilter"]')[0].attribs['aria-expanded']
).toBe('false');
});
test('true is reflected in the aria-expanded state of the button', () => {
const component = renderWithIntl(
<DiscoverNoResults
timeFieldName="awesome_time_field"
isTimePickerOpen={true}
topNavToggle={() => {}}
getDocLink={() => ''}
/>
);
expect(
component.find('[data-test-subj="discoverNoResultsTimefilter"]')[0].attribs['aria-expanded']
).toBe('true');
});
});
describe('timeFieldName', () => {
test('renders time range feedback', () => {
const component = renderWithIntl(
<DiscoverNoResults
timeFieldName="awesome_time_field"
isTimePickerOpen={false}
topNavToggle={() => {}}
getDocLink={() => ''}
/>
);
@ -120,8 +80,6 @@ describe('DiscoverNoResults', () => {
const component = renderWithIntl(
<DiscoverNoResults
queryLanguage="lucene"
isTimePickerOpen={false}
topNavToggle={() => {}}
getDocLink={() => 'documentation-link'}
/>
);
@ -129,22 +87,5 @@ describe('DiscoverNoResults', () => {
expect(component).toMatchSnapshot();
});
});
describe('topNavToggle', () => {
test('is called whe time picker button is clicked', () => {
const topNavToggleSpy = sinon.stub();
const component = mountWithIntl(
<DiscoverNoResults
timeFieldName="awesome_time_field"
isTimePickerOpen={false}
topNavToggle={topNavToggleSpy}
getDocLink={() => ''}
/>
);
findTestSubject(component, 'discoverNoResultsTimefilter').simulate('click');
sinon.assert.calledOnce(topNavToggleSpy);
});
});
});
});

View file

@ -71,8 +71,6 @@
<discover-no-results
ng-show="resultState === 'none'"
top-nav-toggle="kbnTopNav.toggle"
is-time-picker-open="kbnTopNav.isCurrent('filter')"
shard-failures="failures"
time-field-name="opts.timefield"
query-language="state.query.language"

View file

@ -107,7 +107,7 @@ export function getUiSettingDefaults() {
name: i18n.translate('kbn.advancedSettings.dateFormatTitle', {
defaultMessage: 'Date format',
}),
value: 'MMMM Do YYYY, HH:mm:ss.SSS',
value: 'MMM D, YYYY @ HH:mm:ss.SSS',
description: i18n.translate('kbn.advancedSettings.dateFormatText', {
defaultMessage: 'When displaying a pretty formatted date, use this {formatLink}',
description: 'Part of composite text: kbn.advancedSettings.dateFormatText + ' +
@ -740,8 +740,7 @@ export function getUiSettingDefaults() {
value:
`{
"from": "now-15m",
"to": "now",
"mode": "quick"
"to": "now"
}`,
type: 'json',
description: i18n.translate('kbn.advancedSettings.timepicker.timeDefaultsText', {
@ -767,53 +766,46 @@ export function getUiSettingDefaults() {
defaultMessage: 'Time picker quick ranges',
}),
value: JSON.stringify([
{ from: 'now/d', to: 'now/d',
display: i18n.translate('kbn.advancedSettings.timepicker.today', { defaultMessage: 'Today' }), section: 0 },
{ from: 'now/w', to: 'now/w',
display: i18n.translate('kbn.advancedSettings.timepicker.thisWeek', { defaultMessage: 'This week' }), section: 0 },
{ from: 'now/M', to: 'now/M',
display: i18n.translate('kbn.advancedSettings.timepicker.thisMonth', { defaultMessage: 'This month' }), section: 0 },
{ from: 'now/y', to: 'now/y',
display: i18n.translate('kbn.advancedSettings.timepicker.thisYear', { defaultMessage: 'This year' }), section: 0 },
{ from: 'now/d', to: 'now',
display: i18n.translate('kbn.advancedSettings.timepicker.todaySoFar', { defaultMessage: 'Today so far' }), section: 0 },
{ from: 'now/w', to: 'now',
display: i18n.translate('kbn.advancedSettings.timepicker.weekToDate', { defaultMessage: 'Week to date' }), section: 0 },
{ from: 'now/M', to: 'now',
display: i18n.translate('kbn.advancedSettings.timepicker.monthToDate', { defaultMessage: 'Month to date' }), section: 0 },
{ from: 'now/y', to: 'now',
display: i18n.translate('kbn.advancedSettings.timepicker.yearToDate', { defaultMessage: 'Year to date' }), section: 0 },
{ from: 'now-15m', to: 'now',
display: i18n.translate('kbn.advancedSettings.timepicker.last15Minutes', { defaultMessage: 'Last 15 minutes' }), section: 1 },
{ from: 'now-30m', to: 'now',
display: i18n.translate('kbn.advancedSettings.timepicker.last30Minutes', { defaultMessage: 'Last 30 minutes' }), section: 1 },
{ from: 'now-1h', to: 'now',
display: i18n.translate('kbn.advancedSettings.timepicker.last1Hour', { defaultMessage: 'Last 1 hour' }), section: 1 },
{ from: 'now-4h', to: 'now',
display: i18n.translate('kbn.advancedSettings.timepicker.last4Hours', { defaultMessage: 'Last 4 hours' }), section: 1 },
{ from: 'now-12h', to: 'now',
display: i18n.translate('kbn.advancedSettings.timepicker.last12Hours', { defaultMessage: 'Last 12 hours' }), section: 1 },
{ from: 'now-24h', to: 'now',
display: i18n.translate('kbn.advancedSettings.timepicker.last24Hours', { defaultMessage: 'Last 24 hours' }), section: 1 },
{ from: 'now-7d', to: 'now',
display: i18n.translate('kbn.advancedSettings.timepicker.last7Days', { defaultMessage: 'Last 7 days' }), section: 1 },
{ from: 'now-30d', to: 'now',
display: i18n.translate('kbn.advancedSettings.timepicker.last30Days', { defaultMessage: 'Last 30 days' }), section: 2 },
{ from: 'now-60d', to: 'now',
display: i18n.translate('kbn.advancedSettings.timepicker.last60Days', { defaultMessage: 'Last 60 days' }), section: 2 },
{ from: 'now-90d', to: 'now',
display: i18n.translate('kbn.advancedSettings.timepicker.last90Days', { defaultMessage: 'Last 90 days' }), section: 2 },
{ from: 'now-6M', to: 'now',
display: i18n.translate('kbn.advancedSettings.timepicker.last6Months', { defaultMessage: 'Last 6 months' }), section: 2 },
{ from: 'now-1y', to: 'now',
display: i18n.translate('kbn.advancedSettings.timepicker.last1Year', { defaultMessage: 'Last 1 year' }), section: 2 },
{ from: 'now-2y', to: 'now',
display: i18n.translate('kbn.advancedSettings.timepicker.last2Years', { defaultMessage: 'Last 2 years' }), section: 2 },
{ from: 'now-5y', to: 'now',
display: i18n.translate('kbn.advancedSettings.timepicker.last5Years', { defaultMessage: 'Last 5 years' }), section: 2 },
{
from: 'now/d',
to: 'now/d',
display: i18n.translate('kbn.advancedSettings.timepicker.today', { defaultMessage: 'Today' })
},
{
from: 'now/w',
to: 'now/w',
display: i18n.translate('kbn.advancedSettings.timepicker.thisWeek', { defaultMessage: 'This week' })
},
{
from: 'now/M',
to: 'now/M',
display: i18n.translate('kbn.advancedSettings.timepicker.thisMonth', { defaultMessage: 'This month' })
},
{
from: 'now/y',
to: 'now/y',
display: i18n.translate('kbn.advancedSettings.timepicker.thisYear', { defaultMessage: 'This year' })
},
{
from: 'now/d',
to: 'now',
display: i18n.translate('kbn.advancedSettings.timepicker.todaySoFar', { defaultMessage: 'Today so far' })
},
{
from: 'now/w',
to: 'now',
display: i18n.translate('kbn.advancedSettings.timepicker.weekToDate', { defaultMessage: 'Week to date' })
},
{
from: 'now/M',
to: 'now',
display: i18n.translate('kbn.advancedSettings.timepicker.monthToDate', { defaultMessage: 'Month to date' })
},
{
from: 'now/y',
to: 'now',
display: i18n.translate('kbn.advancedSettings.timepicker.yearToDate', { defaultMessage: 'Year to date' })
},
], null, 2),
type: 'json',
description: i18n.translate('kbn.advancedSettings.timepicker.quickRangesText', {

View file

@ -194,10 +194,10 @@ describe('AggTable Directive', function () {
$scope.table = response.tables[0];
$scope.showTotal = true;
$scope.totalFunc = totalFunc;
const $el = $(`<kbn-agg-table
table="table"
show-total="showTotal"
total-func="totalFunc"
const $el = $(`<kbn-agg-table
table="table"
show-total="showTotal"
total-func="totalFunc"
dimensions="dimensions"></kbn-agg-table>`);
$compile($el)($scope);
$scope.$digest();
@ -224,7 +224,7 @@ describe('AggTable Directive', function () {
'',
'2014-09-28',
'9,283',
'September 28th 2014, 00:00:00.000',
'Sep 28, 2014 @ 00:00:00.000',
'1',
'11'
]);
@ -234,7 +234,7 @@ describe('AggTable Directive', function () {
'',
'2014-10-03',
'220,943',
'October 3rd 2014, 00:00:00.000',
'Oct 3, 2014 @ 00:00:00.000',
'239',
'837'
]);

View file

@ -1,5 +0,0 @@
<div ng-switch="datepickerMode" role="application">
<daypicker ng-switch-when="day" tabindex="0"></daypicker>
<monthpicker ng-switch-when="month" tabindex="0"></monthpicker>
<yearpicker ng-switch-when="year" tabindex="0"></yearpicker>
</div>

View file

@ -1,625 +0,0 @@
import { i18n } from '@kbn/i18n';
angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootstrap.position'])
.constant('datepickerConfig', {
formatDay: 'dd',
formatMonth: 'MMMM',
formatYear: 'yyyy',
formatDayHeader: 'EEE',
formatDayTitle: 'MMMM yyyy',
formatMonthTitle: 'yyyy',
datepickerMode: 'day',
minMode: 'day',
maxMode: 'year',
showWeeks: true,
startingDay: 0,
yearRange: 20,
minDate: null,
maxDate: null
})
.controller('DatepickerController', ['$scope', '$attrs', '$parse', '$interpolate', '$timeout', '$log', 'dateFilter', 'datepickerConfig', function($scope, $attrs, $parse, $interpolate, $timeout, $log, dateFilter, datepickerConfig) {
var self = this,
ngModelCtrl = { $setViewValue: angular.noop }; // nullModelCtrl;
// Modes chain
this.modes = ['day', 'month', 'year'];
// Configuration attributes
angular.forEach(['formatDay', 'formatMonth', 'formatYear', 'formatDayHeader', 'formatDayTitle', 'formatMonthTitle',
'minMode', 'maxMode', 'showWeeks', 'startingDay', 'yearRange'], function( key, index ) {
self[key] = angular.isDefined($attrs[key]) ? (index < 8 ? $interpolate($attrs[key])($scope.$parent) : $scope.$parent.$eval($attrs[key])) : datepickerConfig[key];
});
// Watchable date attributes
angular.forEach(['minDate', 'maxDate'], function( key ) {
if ( $attrs[key] ) {
$scope.$parent.$watch($parse($attrs[key]), function(value) {
self[key] = value ? new Date(value) : null;
self.refreshView();
});
} else {
self[key] = datepickerConfig[key] ? new Date(datepickerConfig[key]) : null;
}
});
$scope.datepickerMode = $scope.datepickerMode || datepickerConfig.datepickerMode;
$scope.uniqueId = 'datepicker-' + $scope.$id + '-' + Math.floor(Math.random() * 10000);
this.activeDate = angular.isDefined($attrs.initDate) ? $scope.$parent.$eval($attrs.initDate) : new Date();
$scope.isActive = function(dateObject) {
if (self.compare(dateObject.date, self.activeDate) === 0) {
$scope.activeDateId = dateObject.uid;
return true;
}
return false;
};
this.init = function( ngModelCtrl_ ) {
ngModelCtrl = ngModelCtrl_;
ngModelCtrl.$render = function() {
self.render();
};
};
this.render = function() {
if ( ngModelCtrl.$modelValue ) {
var date = new Date( ngModelCtrl.$modelValue ),
isValid = !isNaN(date);
if ( isValid ) {
this.activeDate = date;
} else {
$log.error('Datepicker directive: "ng-model" value must be a Date object, a number of milliseconds since 01.01.1970 or a string representing an RFC2822 or ISO 8601 date.');
}
ngModelCtrl.$setValidity('date', isValid);
}
this.refreshView();
};
this.refreshView = function() {
if ( this.element ) {
this._refreshView();
var date = ngModelCtrl.$modelValue ? new Date(ngModelCtrl.$modelValue) : null;
ngModelCtrl.$setValidity('date-disabled', !date || (this.element && !this.isDisabled(date)));
}
};
this.createDateObject = function(date, format) {
var model = ngModelCtrl.$modelValue ? new Date(ngModelCtrl.$modelValue) : null;
return {
date: date,
label: dateFilter(date, format),
selected: model && this.compare(date, model) === 0,
disabled: this.isDisabled(date),
current: this.compare(date, new Date()) === 0
};
};
this.isDisabled = function( date ) {
return ((this.minDate && this.compare(date, this.minDate) < 0) || (this.maxDate && this.compare(date, this.maxDate) > 0) || ($attrs.dateDisabled && $scope.dateDisabled({date: date, mode: $scope.datepickerMode})));
};
// Split array into smaller arrays
this.split = function(arr, size) {
var arrays = [];
while (arr.length > 0) {
arrays.push(arr.splice(0, size));
}
return arrays;
};
$scope.select = function( date ) {
if ( $scope.datepickerMode === self.minMode ) {
var dt = ngModelCtrl.$modelValue ? new Date( ngModelCtrl.$modelValue ) : new Date(0, 0, 0, 0, 0, 0, 0);
dt.setFullYear( date.getFullYear(), date.getMonth(), date.getDate() );
ngModelCtrl.$setViewValue( dt );
ngModelCtrl.$render();
} else {
self.activeDate = date;
$scope.datepickerMode = self.modes[ self.modes.indexOf( $scope.datepickerMode ) - 1 ];
focusElement();
}
};
$scope.move = function( direction ) {
var year = self.activeDate.getFullYear() + direction * (self.step.years || 0),
month = self.activeDate.getMonth() + direction * (self.step.months || 0);
self.activeDate.setFullYear(year, month, 1);
self.refreshView();
};
$scope.toggleMode = function( direction ) {
direction = direction || 1;
if (($scope.datepickerMode === self.maxMode && direction === 1) || ($scope.datepickerMode === self.minMode && direction === -1)) {
return;
}
$scope.datepickerMode = self.modes[ self.modes.indexOf( $scope.datepickerMode ) + direction ];
focusElement();
};
// Key event mapper
$scope.keys = { 13:'enter', 32:'space', 33:'pageup', 34:'pagedown', 35:'end', 36:'home', 37:'left', 38:'up', 39:'right', 40:'down' };
var focusElement = function() {
$timeout(function() {
self.element[0].focus();
}, 0 , false);
};
// Listen for focus requests from popup directive
$scope.$on('datepicker.focus', focusElement);
}])
.directive( 'datepicker', function () {
return {
restrict: 'EA',
replace: true,
templateUrl: 'template/datepicker/datepicker.html',
scope: {
datepickerMode: '=?',
dateDisabled: '&'
},
require: ['datepicker', '?^ngModel'],
controller: 'DatepickerController',
link: function(scope, element, attrs, ctrls) {
var datepickerCtrl = ctrls[0], ngModelCtrl = ctrls[1];
if ( ngModelCtrl ) {
datepickerCtrl.init( ngModelCtrl );
}
}
};
})
.directive('daypicker', ['dateFilter', function (dateFilter) {
return {
restrict: 'EA',
replace: true,
templateUrl: 'template/datepicker/day.html',
require: '^datepicker',
link: function(scope, element, attrs, ctrl) {
scope.showWeeks = ctrl.showWeeks;
ctrl.step = { months: 1 };
ctrl.element = element;
var DAYS_IN_MONTH = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
function getDaysInMonth( year, month ) {
return ((month === 1) && (year % 4 === 0) && ((year % 100 !== 0) || (year % 400 === 0))) ? 29 : DAYS_IN_MONTH[month];
}
function getDates(startDate, n) {
var dates = new Array(n), current = new Date(startDate), i = 0;
current.setHours(12); // Prevent repeated dates because of timezone bug
while ( i < n ) {
dates[i++] = new Date(current);
current.setDate( current.getDate() + 1 );
}
return dates;
}
ctrl._refreshView = function() {
var year = ctrl.activeDate.getFullYear(),
month = ctrl.activeDate.getMonth(),
firstDayOfMonth = new Date(year, month, 1),
difference = ctrl.startingDay - firstDayOfMonth.getDay(),
numDisplayedFromPreviousMonth = (difference > 0) ? 7 - difference : - difference,
firstDate = new Date(firstDayOfMonth);
if ( numDisplayedFromPreviousMonth > 0 ) {
firstDate.setDate( - numDisplayedFromPreviousMonth + 1 );
}
// 42 is the number of days on a six-month calendar
var days = getDates(firstDate, 42);
for (var i = 0; i < 42; i ++) {
days[i] = angular.extend(ctrl.createDateObject(days[i], ctrl.formatDay), {
secondary: days[i].getMonth() !== month,
uid: scope.uniqueId + '-' + i
});
}
scope.labels = new Array(7);
for (var j = 0; j < 7; j++) {
scope.labels[j] = {
abbr: dateFilter(days[j].date, ctrl.formatDayHeader),
full: dateFilter(days[j].date, 'EEEE')
};
}
scope.title = dateFilter(ctrl.activeDate, ctrl.formatDayTitle);
scope.rows = ctrl.split(days, 7);
if ( scope.showWeeks ) {
scope.weekNumbers = [];
var weekNumber = getISO8601WeekNumber( scope.rows[0][0].date ),
numWeeks = scope.rows.length;
while( scope.weekNumbers.push(weekNumber++) < numWeeks ) {}
}
};
ctrl.compare = function(date1, date2) {
return (new Date( date1.getFullYear(), date1.getMonth(), date1.getDate() ) - new Date( date2.getFullYear(), date2.getMonth(), date2.getDate() ) );
};
function getISO8601WeekNumber(date) {
var checkDate = new Date(date);
checkDate.setDate(checkDate.getDate() + 4 - (checkDate.getDay() || 7)); // Thursday
var time = checkDate.getTime();
checkDate.setMonth(0); // Compare with Jan 1
checkDate.setDate(1);
return Math.floor(Math.round((time - checkDate) / 86400000) / 7) + 1;
}
ctrl.handleKeyDown = function( key, evt ) {
var date = ctrl.activeDate.getDate();
if (key === 'left') {
date = date - 1; // up
} else if (key === 'up') {
date = date - 7; // down
} else if (key === 'right') {
date = date + 1; // down
} else if (key === 'down') {
date = date + 7;
} else if (key === 'pageup' || key === 'pagedown') {
var month = ctrl.activeDate.getMonth() + (key === 'pageup' ? - 1 : 1);
ctrl.activeDate.setMonth(month, 1);
date = Math.min(getDaysInMonth(ctrl.activeDate.getFullYear(), ctrl.activeDate.getMonth()), date);
} else if (key === 'home') {
date = 1;
} else if (key === 'end') {
date = getDaysInMonth(ctrl.activeDate.getFullYear(), ctrl.activeDate.getMonth());
}
ctrl.activeDate.setDate(date);
};
ctrl.refreshView();
}
};
}])
.directive('monthpicker', ['dateFilter', function (dateFilter) {
return {
restrict: 'EA',
replace: true,
templateUrl: 'template/datepicker/month.html',
require: '^datepicker',
link: function(scope, element, attrs, ctrl) {
ctrl.step = { years: 1 };
ctrl.element = element;
ctrl._refreshView = function() {
var months = new Array(12),
year = ctrl.activeDate.getFullYear();
for ( var i = 0; i < 12; i++ ) {
months[i] = angular.extend(ctrl.createDateObject(new Date(year, i, 1), ctrl.formatMonth), {
uid: scope.uniqueId + '-' + i
});
}
scope.title = dateFilter(ctrl.activeDate, ctrl.formatMonthTitle);
scope.rows = ctrl.split(months, 3);
};
ctrl.compare = function(date1, date2) {
return new Date( date1.getFullYear(), date1.getMonth() ) - new Date( date2.getFullYear(), date2.getMonth() );
};
ctrl.handleKeyDown = function( key, evt ) {
var date = ctrl.activeDate.getMonth();
if (key === 'left') {
date = date - 1; // up
} else if (key === 'up') {
date = date - 3; // down
} else if (key === 'right') {
date = date + 1; // down
} else if (key === 'down') {
date = date + 3;
} else if (key === 'pageup' || key === 'pagedown') {
var year = ctrl.activeDate.getFullYear() + (key === 'pageup' ? - 1 : 1);
ctrl.activeDate.setFullYear(year);
} else if (key === 'home') {
date = 0;
} else if (key === 'end') {
date = 11;
}
ctrl.activeDate.setMonth(date);
};
ctrl.refreshView();
}
};
}])
.directive('yearpicker', ['dateFilter', function (dateFilter) {
return {
restrict: 'EA',
replace: true,
templateUrl: 'template/datepicker/year.html',
require: '^datepicker',
link: function(scope, element, attrs, ctrl) {
var range = ctrl.yearRange;
ctrl.step = { years: range };
ctrl.element = element;
function getStartingYear( year ) {
return parseInt((year - 1) / range, 10) * range + 1;
}
ctrl._refreshView = function() {
var years = new Array(range);
for ( var i = 0, start = getStartingYear(ctrl.activeDate.getFullYear()); i < range; i++ ) {
years[i] = angular.extend(ctrl.createDateObject(new Date(start + i, 0, 1), ctrl.formatYear), {
uid: scope.uniqueId + '-' + i
});
}
scope.title = [years[0].label, years[range - 1].label].join(' - ');
scope.rows = ctrl.split(years, 5);
scope.yearRange = ctrl.yearRange;
};
ctrl.compare = function(date1, date2) {
return date1.getFullYear() - date2.getFullYear();
};
ctrl.handleKeyDown = function( key, evt ) {
var date = ctrl.activeDate.getFullYear();
if (key === 'left') {
date = date - 1; // up
} else if (key === 'up') {
date = date - 5; // down
} else if (key === 'right') {
date = date + 1; // down
} else if (key === 'down') {
date = date + 5;
} else if (key === 'pageup' || key === 'pagedown') {
date += (key === 'pageup' ? - 1 : 1) * ctrl.step.years;
} else if (key === 'home') {
date = getStartingYear( ctrl.activeDate.getFullYear() );
} else if (key === 'end') {
date = getStartingYear( ctrl.activeDate.getFullYear() ) + range - 1;
}
ctrl.activeDate.setFullYear(date);
};
ctrl.refreshView();
}
};
}])
.constant('datepickerPopupConfig', {
datepickerPopup: 'yyyy-MM-dd',
currentText: i18n.translate('common.ui.angularBootstrap.datepicker.datepickerPopupConfig.todayLabel', {
defaultMessage: 'Today'
}),
clearText: i18n.translate('common.ui.angularBootstrap.datepicker.datepickerPopupConfig.clearLabel', {
defaultMessage: 'Clear'
}),
closeText: i18n.translate('common.ui.angularBootstrap.datepicker.datepickerPopupConfig.doneLabel', {
defaultMessage: 'Done'
}),
closeOnDateSelection: true,
appendToBody: false,
showButtonBar: true
})
.directive('datepickerPopup', ['$compile', '$parse', '$document', '$position', 'dateFilter', 'dateParser', 'datepickerPopupConfig',
function ($compile, $parse, $document, $position, dateFilter, dateParser, datepickerPopupConfig) {
return {
restrict: 'EA',
require: 'ngModel',
scope: {
isOpen: '=?',
currentText: '@',
clearText: '@',
closeText: '@',
dateDisabled: '&'
},
link: function(scope, element, attrs, ngModel) {
var dateFormat,
closeOnDateSelection = angular.isDefined(attrs.closeOnDateSelection) ? scope.$parent.$eval(attrs.closeOnDateSelection) : datepickerPopupConfig.closeOnDateSelection,
appendToBody = angular.isDefined(attrs.datepickerAppendToBody) ? scope.$parent.$eval(attrs.datepickerAppendToBody) : datepickerPopupConfig.appendToBody;
scope.showButtonBar = angular.isDefined(attrs.showButtonBar) ? scope.$parent.$eval(attrs.showButtonBar) : datepickerPopupConfig.showButtonBar;
scope.getText = function( key ) {
return scope[key + 'Text'] || datepickerPopupConfig[key + 'Text'];
};
attrs.$observe('datepickerPopup', function(value) {
dateFormat = value || datepickerPopupConfig.datepickerPopup;
ngModel.$render();
});
// popup element used to display calendar
var popupEl = angular.element('<div datepicker-popup-wrap><div datepicker></div></div>');
popupEl.attr({
'ng-model': 'date',
'ng-change': 'dateSelection()'
});
function cameltoDash( string ){
return string.replace(/([A-Z])/g, function($1) { return '-' + $1.toLowerCase(); });
}
// datepicker element
var datepickerEl = angular.element(popupEl.children()[0]);
if ( attrs.datepickerOptions ) {
angular.forEach(scope.$parent.$eval(attrs.datepickerOptions), function( value, option ) {
datepickerEl.attr( cameltoDash(option), value );
});
}
scope.watchData = {};
angular.forEach(['minDate', 'maxDate', 'datepickerMode'], function( key ) {
if ( attrs[key] ) {
var getAttribute = $parse(attrs[key]);
scope.$parent.$watch(getAttribute, function(value){
scope.watchData[key] = value;
});
datepickerEl.attr(cameltoDash(key), 'watchData.' + key);
// Propagate changes from datepicker to outside
if ( key === 'datepickerMode' ) {
var setAttribute = getAttribute.assign;
scope.$watch('watchData.' + key, function(value, oldvalue) {
if ( value !== oldvalue ) {
setAttribute(scope.$parent, value);
}
});
}
}
});
if (attrs.dateDisabled) {
datepickerEl.attr('date-disabled', 'dateDisabled({ date: date, mode: mode })');
}
function parseDate(viewValue) {
if (!viewValue) {
ngModel.$setValidity('date', true);
return null;
} else if (angular.isDate(viewValue) && !isNaN(viewValue)) {
ngModel.$setValidity('date', true);
return viewValue;
} else if (angular.isString(viewValue)) {
var date = dateParser.parse(viewValue, dateFormat) || new Date(viewValue);
if (isNaN(date)) {
ngModel.$setValidity('date', false);
return undefined;
} else {
ngModel.$setValidity('date', true);
return date;
}
} else {
ngModel.$setValidity('date', false);
return undefined;
}
}
ngModel.$parsers.unshift(parseDate);
// Inner change
scope.dateSelection = function(dt) {
if (angular.isDefined(dt)) {
scope.date = dt;
}
ngModel.$setViewValue(scope.date);
ngModel.$render();
if ( closeOnDateSelection ) {
scope.isOpen = false;
element[0].focus();
}
};
element.bind('input change keyup', function() {
scope.$apply(function() {
scope.date = ngModel.$modelValue;
});
});
// Outer change
ngModel.$render = function() {
var date = ngModel.$viewValue ? dateFilter(ngModel.$viewValue, dateFormat) : '';
element.val(date);
scope.date = parseDate( ngModel.$modelValue );
};
var documentClickBind = function(event) {
if (scope.isOpen && event.target !== element[0]) {
scope.$apply(function() {
scope.isOpen = false;
});
}
};
var keydown = function(evt, noApply) {
scope.keydown(evt);
};
element.bind('keydown', keydown);
scope.keydown = function(evt) {
if (evt.which === 27) {
evt.preventDefault();
evt.stopPropagation();
scope.close();
} else if (evt.which === 40 && !scope.isOpen) {
scope.isOpen = true;
}
};
scope.$watch('isOpen', function(value) {
if (value) {
scope.$broadcast('datepicker.focus');
scope.position = appendToBody ? $position.offset(element) : $position.position(element);
scope.position.top = scope.position.top + element.prop('offsetHeight');
$document.bind('click', documentClickBind);
} else {
$document.unbind('click', documentClickBind);
}
});
scope.select = function( date ) {
if (date === 'today') {
var today = new Date();
if (angular.isDate(ngModel.$modelValue)) {
date = new Date(ngModel.$modelValue);
date.setFullYear(today.getFullYear(), today.getMonth(), today.getDate());
} else {
date = new Date(today.setHours(0, 0, 0, 0));
}
}
scope.dateSelection( date );
};
scope.close = function() {
scope.isOpen = false;
element[0].focus();
};
var $popup = $compile(popupEl)(scope);
// Prevent jQuery cache memory leak (template is now redundant after linking)
popupEl.remove();
if ( appendToBody ) {
$document.find('body').append($popup);
} else {
element.after($popup);
}
scope.$on('$destroy', function() {
$popup.remove();
element.unbind('keydown', keydown);
$document.unbind('click', documentClickBind);
});
}
};
}])
.directive('datepickerPopupWrap', function() {
return {
restrict:'EA',
replace: true,
transclude: true,
templateUrl: 'template/datepicker/popup.html',
link:function (scope, element, attrs) {
element.bind('click', function(event) {
event.preventDefault();
event.stopPropagation();
});
}
};
});

View file

@ -1,81 +0,0 @@
<table
class="kuiDatePicker fullWidth"
role="grid"
aria-labelledby="{{uniqueId}}-title"
aria-activedescendant="{{activeDateId}}"
>
<thead>
<tr>
<th
class="kuiDatePickerNavigationCell"
colspan="{{7 + showWeeks}}"
>
<div class="kuiDatePickerNavigation">
<button
type="button"
class="kuiDatePickerNavigationButton"
ng-click="move(-1)"
aria-label="{{ ::'common.ui.angularBootstrap.datepicker.previousMonthButtonAriaLabel' | i18n: {defaultMessage: 'Previous month'} }}"
>
<span class="kuiIcon fa-chevron-left"></span>
</button>
<button
id="{{uniqueId}}-title"
role="heading"
aria-live="assertive"
aria-atomic="true"
type="button"
class="kuiDatePickerNavigationButton"
ng-click="toggleMode()"
>
<strong>{{title}}</strong>
</button>
<button
type="button"
class="kuiDatePickerNavigationButton"
ng-click="move(1)"
aria-label="{{ ::'common.ui.angularBootstrap.datepicker.nextMonthButtonAriaLabel' | i18n: {defaultMessage: 'Next month'} }}"
>
<span class="kuiIcon fa-chevron-right"></span>
</button>
</div>
</th>
</tr>
<tr>
<th ng-show="showWeeks" class="text-center"></th>
<th
ng-repeat="label in labels track by $index"
class="kuiDatePickerHeaderCell"
>
<small aria-label="{{label.full}}">{{label.abbr}}</small>
</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="row in rows track by $index">
<td ng-show="showWeeks">
<em>{{ weekNumbers[$index] }}</em>
</td>
<td
ng-repeat="dt in row track by dt.date"
class="kuiDatePickerRowCell"
ng-class="{'kuiDatePickerRowCell-isBlocked': dt.disabled && !dt.secondary}"
id="{{dt.uid}}"
aria-disabled="{{!!dt.disabled}}"
>
<button
type="button"
class="kuiDatePickerRowCellContent"
ng-class="{'kuiDatePickerRowCellContent-isSelected': dt.selected, 'kuiDatePickerRowCellContent-isCurrent': dt.current, 'kuiDatePickerRowCellContent-isOtherMonth': dt.secondary}"
ng-click="select(dt.date)"
ng-disabled="dt.disabled"
>
<span>{{dt.label}}</span>
</button>
</td>
</tr>
</tbody>
</table>

View file

@ -1,68 +0,0 @@
<table
class="kuiDatePicker fullWidth"
role="grid"
aria-labelledby="{{uniqueId}}-title"
aria-activedescendant="{{activeDateId}}"
>
<thead>
<tr>
<th
class="kuiDatePickerNavigationCell"
colspan="3"
>
<div class="kuiDatePickerNavigation">
<button
type="button"
class="kuiDatePickerNavigationButton"
ng-click="move(-1)"
aria-label="{{ ::'common.ui.angularBootstrap.datepicker.previousYearButtonAriaLabel' | i18n: {defaultMessage: 'Previous year'} }}"
>
<span class="kuiIcon fa-chevron-left"></span>
</button>
<button
id="{{uniqueId}}-title"
role="heading"
aria-live="assertive"
aria-atomic="true"
type="button"
class="kuiDatePickerNavigationButton"
ng-click="toggleMode()"
>
<strong>{{title}}</strong>
</button>
<button
type="button"
class="kuiDatePickerNavigationButton"
ng-click="move(1)"
aria-label="{{ ::'common.ui.angularBootstrap.datepicker.nextYearButtonAriaLabel' | i18n: {defaultMessage: 'Next year'} }}"
>
<span class="kuiIcon fa-chevron-right"></span>
</button>
</div>
</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="row in rows track by $index">
<td
ng-repeat="dt in row track by dt.date"
class="kuiDatePickerRowCell"
ng-class="{'kuiDatePickerRowCell-isBlocked': dt.disabled && !dt.secondary}"
id="{{dt.uid}}"
aria-disabled="{{!!dt.disabled}}"
>
<button
type="button"
class="kuiDatePickerRowCellContent"
ng-class="{'kuiDatePickerRowCellContent-isSelected': dt.selected, 'kuiDatePickerRowCellContent-isCurrent': dt.current}"
ng-click="select(dt.date)"
ng-disabled="dt.disabled"
>
<span>{{dt.label}}</span>
</button>
</td>
</tr>
</tbody>
</table>

View file

@ -1,10 +0,0 @@
<ul class="dropdown-menu" ng-style="{display: (isOpen && 'block') || 'none', top: position.top+'px', left: position.left+'px'}" ng-keydown="keydown($event)">
<li ng-transclude></li>
<li ng-if="showButtonBar" style="padding:10px 9px 2px">
<span class="kuiButtonGroup pull-left">
<button type="button" class="kuiButton kuiButton--small kuiButton--primary" ng-click="select('today')">{{ getText('current') }}</button>
<button type="button" class="kuiButton kuiButton--small kuiButton--danger" ng-click="select(null)">{{ getText('clear') }}</button>
</span>
<button type="button" class="kuiButton kuiButton--small kuiButton--primary pull-right" ng-click="close()">{{ getText('close') }}</button>
</li>
</ul>

View file

@ -1,74 +0,0 @@
<table
class="kuiDatePicker fullWidth"
role="grid"
aria-labelledby="{{uniqueId}}-title"
aria-activedescendant="{{activeDateId}}"
>
<thead>
<tr>
<th
class="kuiDatePickerNavigationCell"
colspan="5"
>
<div class="kuiDatePickerNavigation">
<button
type="button"
class="kuiDatePickerNavigationButton"
ng-click="move(-1)"
aria-label="{{ 'common.ui.angularBootstrap.datepicker.previousYearsRangeButtonAriaLabel' | i18n: {
defaultMessage: 'Previous {yearRange} years',
values: {yearRange}
} }}"
>
<span class="kuiIcon fa-chevron-left"></span>
</button>
<button
id="{{uniqueId}}-title"
role="heading"
aria-live="assertive"
aria-atomic="true"
type="button"
class="kuiDatePickerNavigationButton"
ng-click="toggleMode()"
>
<strong>{{title}}</strong>
</button>
<button
type="button"
class="kuiDatePickerNavigationButton"
ng-click="move(1)"
aria-label="{{ 'common.ui.angularBootstrap.datepicker.nextYearsRangeButtonAriaLabel' | i18n: {
defaultMessage: 'Next {yearRange} years',
values: {yearRange}
} }}"
>
<span class="kuiIcon fa-chevron-right"></span>
</button>
</div>
</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="row in rows track by $index">
<td
ng-repeat="dt in row track by dt.date"
class="kuiDatePickerRowCell"
ng-class="{'kuiDatePickerRowCell-isBlocked': dt.disabled && !dt.secondary}"
id="{{dt.uid}}"
aria-disabled="{{!!dt.disabled}}"
>
<button
type="button"
class="kuiDatePickerRowCellContent"
ng-class="{'kuiDatePickerRowCellContent-isSelected': dt.selected, 'kuiDatePickerRowCellContent-isCurrent': dt.current}"
ng-click="select(dt.date)"
ng-disabled="dt.disabled"
>
<span>{{dt.label}}</span>
</button>
</td>
</tr>
</tbody>
</table>

View file

@ -21,7 +21,6 @@ angular.module('ui.bootstrap', [
'ui.bootstrap.buttons',
'ui.bootstrap.dateparser',
'ui.bootstrap.position',
'ui.bootstrap.datepicker',
'ui.bootstrap.dropdown',
'ui.bootstrap.modal',
'ui.bootstrap.pagination',
@ -36,11 +35,6 @@ angular.module('ui.bootstrap', [
angular.module('ui.bootstrap.tpls', [
'template/alert/alert.html',
'template/datepicker/datepicker.html',
'template/datepicker/day.html',
'template/datepicker/month.html',
'template/datepicker/popup.html',
'template/datepicker/year.html',
'template/modal/backdrop.html',
'template/modal/window.html',
'template/pagination/pager.html',
@ -65,7 +59,6 @@ import './bindHtml/bindHtml';
import './buttons/buttons';
import './collapse/collapse';
import './dateparser/dateparser';
import './datepicker/datepicker';
import './dropdown/dropdown';
import './modal/modal';
import './pagination/pagination';
@ -85,36 +78,6 @@ angular.module('template/alert/alert.html', []).run(['$templateCache', function(
$templateCache.put('template/alert/alert.html', alert);
}]);
import datepicker from './datepicker/datepicker.html';
angular.module('template/datepicker/datepicker.html', []).run(['$templateCache', function($templateCache) {
$templateCache.put('template/datepicker/datepicker.html', datepicker);
}]);
import day from './datepicker/day.html';
angular.module('template/datepicker/day.html', []).run(['$templateCache', function($templateCache) {
$templateCache.put('template/datepicker/day.html', day);
}]);
import month from './datepicker/month.html';
angular.module('template/datepicker/month.html', []).run(['$templateCache', function($templateCache) {
$templateCache.put('template/datepicker/month.html', month);
}]);
import popup from './datepicker/popup.html';
angular.module('template/datepicker/popup.html', []).run(['$templateCache', function($templateCache) {
$templateCache.put('template/datepicker/popup.html', popup);
}]);
import year from './datepicker/year.html';
angular.module('template/datepicker/year.html', []).run(['$templateCache', function($templateCache) {
$templateCache.put('template/datepicker/year.html', year);
}]);
import backdrop from './modal/backdrop.html';
angular.module('template/modal/backdrop.html', []).run(['$templateCache', function($templateCache) {

View file

@ -1,9 +0,0 @@
<kbn-timepicker
from="timefilterValues.time.from"
to="timefilterValues.time.to"
mode="timefilterValues.time.mode"
active-tab="'filter'"
interval="timefilterValues.refreshInterval"
on-filter-select="updateFilter(from, to, mode)"
on-interval-select="updateInterval(interval)">
</kbn-timepicker>

View file

@ -1,9 +0,0 @@
<kbn-timepicker
from="timefilterValues.time.from"
to="timefilterValues.time.to"
mode="timefilterValues.time.mode"
active-tab="'interval'"
interval="timefilterValues.refreshInterval"
on-filter-select="updateFilter(from, to, mode)"
on-interval-select="updateInterval(interval)">
</kbn-timepicker>

View file

@ -1,454 +0,0 @@
/*
* 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.
*/
import angular from 'angular';
import moment from 'moment';
import expect from 'expect.js';
import _ from 'lodash';
import sinon from 'sinon';
import ngMock from 'ng_mock';
import 'plugins/kibana/visualize/index';
import 'plugins/kibana/dashboard/index';
import 'plugins/kibana/discover/index';
// TODO: This should not be needed, timefilter is only included here, it should move
let $parentScope;
let $scope;
let $elem;
const anchor = '2014-01-01T06:06:06.666Z';
const init = function () {
// Load the application
ngMock.module('kibana');
// Create the scope
ngMock.inject(function ($rootScope, $compile) {
// Give us a scope
$parentScope = $rootScope;
// Add some parameters to it
const timefilter = {
time: {
from: moment().subtract(15, 'minutes'),
to: moment(),
mode: undefined
},
refreshInterval: {
value: 0,
display: 'Off'
}
};
$parentScope.timefilter = timefilter;
$parentScope.updateInterval = sinon.spy();
$parentScope.updateFilter = sinon.spy();
// Create the element
$elem = angular.element(
'<kbn-timepicker' +
' from="timefilter.time.from"' +
' to="timefilter.time.to"' +
' mode="timefilter.time.mode"' +
' active-tab="timefilter.timepickerActiveTab"' +
' interval="timefilter.refreshInterval"' +
' on-interval-select="updateInterval(interval)"' +
' on-filter-select="updateFilter(from, to)">' +
'</kbn-timepicker>'
);
// And compile it
$compile($elem)($parentScope);
// Fire a digest cycle
$elem.scope().$digest();
// Grab the isolate scope so we can test it
$scope = $elem.isolateScope();
});
};
describe('timepicker directive', function () {
const sandbox = sinon.createSandbox();
beforeEach(function () {
// Stub out the clock so 'now' doesn't move
sandbox.useFakeTimers(moment(anchor).valueOf());
init();
});
afterEach(function () {
sandbox.restore();
});
describe('tabs', function () {
it('should contain two tabs', function () {
expect($elem.find('.tab-pane').length).to.be(2);
});
});
describe('refresh interval', function () {
beforeEach(function () {
ngMock.inject(function ($rootScope) {
$rootScope.$apply();
});
});
it('should contain a list of options', function () {
expect($elem.find('.kbn-refresh-section').length).to.be.greaterThan(0);
});
it('should have a $scope.setRefreshInterval() that calls handler', function () {
$scope.setRefreshInterval({ value: 10000 });
sinon.assert.calledOnce($parentScope.updateInterval);
expect($parentScope.updateInterval.firstCall.args[0]).to.have.property('value', 10000);
});
it('should unpause when setRefreshInterval is called without pause:true', function () {
$scope.setRefreshInterval({ value: 1000, pause: true });
expect($parentScope.updateInterval.getCall(0).args[0]).to.have.property('pause', true);
$scope.setRefreshInterval({ value: 1000, pause: false });
expect($parentScope.updateInterval.getCall(1).args[0]).to.have.property('pause', false);
$scope.setRefreshInterval({ value: 1000 });
expect($parentScope.updateInterval.getCall(2).args[0]).to.have.property('pause', false);
});
it('should highlight the current active interval', function () {
$scope.interval = { value: 300000 };
$elem.scope().$digest();
expect($elem.find('.refresh-interval-active').length).to.be(1);
expect($elem.find('.refresh-interval-active').text().trim()).to.be('5 minutes');
});
});
describe('mode setting', function () {
it('should be in quick mode by default', function () {
expect($scope.mode).to.be('quick');
});
it('should highlight the right mode', function () {
expect($elem.find('.kbn-timepicker-modes .euiTab-isSelected').text().trim()).to.be('Quick');
// Each of the 3 modes
const modes = ['absolute', 'relative', 'quick'];
_.each(modes, function (mode) {
$scope.setMode(mode);
$scope.$digest();
expect($elem.find('.kbn-timepicker-modes .euiTab-isSelected').text().trim().toLowerCase()).to.be(mode);
});
});
});
describe('quick mode', function () {
beforeEach(function () {
$scope.setMode('quick');
$scope.$digest();
});
it('should contain 3 lists of options', function () {
expect($elem.find('.kbn-timepicker-section .list-unstyled').length).to.be(3);
});
it('should have a $scope.setQuick() that calls handler', function () {
$scope.setQuick('now', 'now');
sinon.assert.calledOnce($parentScope.updateFilter);
expect($parentScope.updateFilter.firstCall.args[0]).to.be('now');
expect($parentScope.updateFilter.firstCall.args[1]).to.be('now');
});
});
describe('relative mode', function () {
beforeEach(function () {
$scope.setMode('relative');
$scope.$digest();
});
it('has a preview of the "from" input', function () {
const preview = $elem.find('.kbn-timepicker-section span[ng-show="relative.from.preview"]');
expect(preview.text().trim()).to.be(moment().subtract(15, 'minutes').format($scope.format));
});
it('has a disabled "to" field that contains "Now"', function () {
expect($elem.find('.kbn-timepicker-section span[ng-show="relative.to.preview"]').text().trim()).to.be('Now');
});
it('has a submit handler', function () {
const form = $elem.find('form[ng-submit="applyRelative()"]');
expect(form.length).to.be(1);
});
it('disables the submit button if the form is invalid', function () {
let button;
button = $elem.find('button[disabled]');
expect(button.length).to.be(0);
// Make the form invalid
$scope.relative.from.count = -3;
$scope.formatRelative('from');
$scope.$digest();
button = $elem.find('button[disabled]');
expect(button.length).to.be(1);
});
it('has a dropdown bound to relative.from.unit that contains all of the intervals', function () {
const select = $elem.find('.kbn-timepicker-section select[ng-model="relative.from.unit"]');
expect(select.length).to.be(1);
expect(select.find('option').length).to.be(14);
// Check each relative option, make sure it is in the list
_.each($scope.relativeOptions, function (unit, i) {
expect(select.find('option')[i].text).to.be(unit.text);
});
});
it('has a checkbox that is checked when rounding is enabled', function () {
const checkbox = $elem.find('.kbn-timepicker-section input[ng-model="relative.from.round"]');
expect(checkbox.length).to.be(1);
// Rounding is disabled by default
expect(checkbox.prop('checked')).to.be(false);
// Enable rounding
$scope.relative.from.round = true;
$scope.$digest();
expect(checkbox.prop('checked')).to.be(true);
});
it('rounds the preview down to the unit when rounding is enabled', function () {
// Enable rounding
$scope.relative.from.round = true;
$scope.relative.from.count = 0;
_.each($scope.units, function (longUnit, shortUnit) {
$scope.relative.from.count = 0;
$scope.relative.from.unit = shortUnit;
$scope.formatRelative('from');
// The preview should match the start of the unit e.g., the start of the minute
expect($scope.relative.from.preview).to.be(moment().startOf(longUnit).format($scope.format));
});
});
it('does not round the preview down to the unit when rounding is disable', function () {
// Disable rounding
$scope.relative.from.round = false;
$scope.relative.from.count = 1;
_.each($scope.units, function (longUnit, shortUnit) {
$scope.relative.from.unit = shortUnit;
$scope.formatRelative('from');
const matches = shortUnit.match(/([smhdwMy])(\+)?/);
let unit;
let opp = '-';
if (matches) {
unit = matches[1];
opp = matches[2];
}
const fn = opp === '+' ? 'add' : 'subtract';
// The preview should match the start of the unit e.g., the start of the minute
expect($scope.relative.from.preview).to.be(moment()[fn](1, unit).format($scope.format));
});
});
it('has a $scope.applyRelative() that sets from and to based on relative.round, relative.count and relative.unit', function () {
// Disable rounding
$scope.relative.from.round = false;
$scope.relative.from.count = 1;
$scope.relative.from.unit = 's';
$scope.applyRelative();
sinon.assert.calledOnce($parentScope.updateFilter);
expect($parentScope.updateFilter.getCall(0).args[0]).to.be('now-1s');
$scope.relative.from.count = 2;
$scope.relative.from.unit = 'm';
$scope.applyRelative();
expect($parentScope.updateFilter.getCall(1).args[0]).to.be('now-2m');
$scope.relative.from.count = 3;
$scope.relative.from.unit = 'h';
$scope.applyRelative();
expect($parentScope.updateFilter.getCall(2).args[0]).to.be('now-3h');
// Enable rounding
$scope.relative.from.round = true;
$scope.relative.from.count = 7;
$scope.relative.from.unit = 'd';
$scope.applyRelative();
expect($parentScope.updateFilter.getCall(3).args[0]).to.be('now-7d/d');
});
it('updates the input fields when the scope variables are changed', function () {
const input = $elem.find('.kbn-timepicker-section input[ng-model="relative.from.count"]');
const select = $elem.find('.kbn-timepicker-section select[ng-model="relative.from.unit"]');
$scope.relative.from.count = 5;
$scope.$digest();
expect(input.val()).to.be('5');
// Should update the selected option
_.each($scope.units, function (longUnit, shortUnit) {
$scope.relative.from.unit = shortUnit;
$scope.$digest();
expect(select.val().split(':')[1]).to.be(shortUnit);
});
});
});
describe('absolute mode', function () {
let inputs;
beforeEach(function () {
$scope.setMode('absolute');
$scope.$digest();
inputs = {
fromInput: $elem.find('.kbn-timepicker-section input[ng-model="absolute.from"]'),
toInput: $elem.find('.kbn-timepicker-section input[ng-model="absolute.to"]'),
fromCalendar: $elem.find('.kbn-timepicker-section div[ng-model="pickFromDate"] '),
toCalendar: $elem.find('.kbn-timepicker-section div[ng-model="pickToDate"] '),
};
});
it('should have input boxes for absolute.from and absolute.to', function () {
expect(inputs.fromInput.length).to.be(1);
expect(inputs.toInput.length).to.be(1);
});
it('should have tables that contain calendars bound to absolute.from and absolute.to', function () {
expect(inputs.fromCalendar.length).to.be(1);
expect(inputs.toCalendar.length).to.be(1);
});
it('should present a timeframe of 15 minutes ago to now if scope.from and scope.to are not set', function () {
delete $scope.from;
delete $scope.to;
$scope.setMode('absolute');
$scope.$digest();
expect($scope.absolute.from.valueOf()).to.be(moment().subtract(15, 'minutes').valueOf());
expect($scope.absolute.to.valueOf()).to.be(moment().valueOf());
});
it('should update its own variables if timefilter time is updated', function () {
$scope.setMode('absolute');
$scope.$digest();
const startDate = moment('1980-01-01T00:11:02.001Z');
const endDate = moment('1983-10-11T00:03:32.051Z');
$parentScope.timefilter.time.from = startDate;
$parentScope.timefilter.time.to = endDate;
$parentScope.$digest();
expect($scope.absolute.from.valueOf()).to.be(startDate.valueOf());
expect($scope.absolute.to.valueOf()).to.be(endDate.valueOf());
});
it('should disable the "Go" button if from > to', function () {
$scope.absolute.from = moment('2012-02-01');
$scope.absolute.to = moment('2012-02-11');
$scope.$digest();
expect($elem.find('[data-test-subj="timepickerGoButton"]').attr('disabled')).to.be(undefined);
$scope.absolute.from = moment('2012-02-12');
$scope.absolute.to = moment('2012-02-11');
$scope.$digest();
expect($elem.find('[data-test-subj="timepickerGoButton"]').attr('disabled')).to.be('disabled');
});
it('should only copy its input to scope.from and scope.to when scope.applyAbsolute() is called', function () {
$scope.from = 'now-30m';
$scope.to = 'now';
$scope.absolute.from = moment('2012-02-01');
$scope.absolute.to = moment('2012-02-11');
expect($scope.from).to.be('now-30m');
expect($scope.to).to.be('now');
$scope.applyAbsolute();
expect($parentScope.updateFilter.firstCall.args[0]).to.eql(moment('2012-02-01'));
expect($parentScope.updateFilter.firstCall.args[1]).to.eql(moment('2012-02-11'));
$scope.$digest();
});
it('should set from/to to start/end of day if set from datepicker', function () {
$scope.pickFromDate(new Date('2012-02-01 12:00'));
$scope.pickToDate(new Date('2012-02-11 12:00'));
$scope.applyAbsolute();
expect($parentScope.updateFilter.firstCall.args[0].valueOf()).to.be(moment('2012-02-01 00:00:00.000').valueOf());
expect($parentScope.updateFilter.firstCall.args[1].valueOf()).to.be(moment('2012-02-11 23:59:59.999').valueOf());
});
it('should allow setting hour/minute/second after setting from datepicker', function () {
$scope.pickFromDate(new Date('2012-02-01 12:00'));
$scope.pickToDate(new Date('2012-02-11 12:00'));
$scope.applyAbsolute();
expect($parentScope.updateFilter.firstCall.args[0].valueOf()).to.be(moment('2012-02-01 00:00:00.000').valueOf());
expect($parentScope.updateFilter.firstCall.args[1].valueOf()).to.be(moment('2012-02-11 23:59:59.999').valueOf());
$scope.absolute.from = moment('2012-02-01 01:23:45');
$scope.absolute.to = moment('2012-02-11 12:34:56');
$scope.applyAbsolute();
expect($parentScope.updateFilter.secondCall.args[0].valueOf()).to.be(moment('2012-02-01 01:23:45').valueOf());
expect($parentScope.updateFilter.secondCall.args[1].valueOf()).to.be(moment('2012-02-11 12:34:56').valueOf());
});
describe('datepicker timezone issue', function () {
it('should ignore the timezone from the datepicker because it does not respect dateFormat:tz advanced setting', function () {
moment.tz.setDefault('UTC');
$scope.pickFromDate(new Date('2012-02-01 12:00-05:00'));
$scope.pickToDate(new Date('2012-02-11 12:00-05:00'));
$scope.$digest();
const formattedFrom = inputs.fromInput.val();
const formattedTo = inputs.toInput.val();
expect(formattedFrom).to.be('2012-02-01 00:00:00.000');
expect(formattedTo).to.be('2012-02-11 23:59:59.999');
});
after(function () {
moment.tz.setDefault('Browser');
});
});
});
});

View file

@ -25,7 +25,7 @@ jest.mock('ui/chrome',
get: (key) => {
switch(key) {
case 'timepicker:timeDefaults':
return { from: 'now-15m', to: 'now', mode: 'quick' };
return { from: 'now-15m', to: 'now' };
case 'timepicker:refreshIntervalDefaults':
return { display: 'Off', pause: false, value: 0 };
default:
@ -47,8 +47,7 @@ describe('changeTimeFilter()', function () {
test('should change the timefilter to match the range gt/lt', function () {
const filter = { range: { '@timestamp': { gt, lt } } };
changeTimeFilter(filter);
const { mode, to, from } = timefilter.getTime();
expect(mode).to.be('absolute');
const { to, from } = timefilter.getTime();
expect(to).to.be(new Date(lt).toISOString());
expect(from).to.be(new Date(gt).toISOString());
});
@ -56,8 +55,7 @@ describe('changeTimeFilter()', function () {
test('should change the timefilter to match the range gte/lte', function () {
const filter = { range: { '@timestamp': { gte: gt, lte: lt } } };
changeTimeFilter(filter);
const { mode, to, from } = timefilter.getTime();
expect(mode).to.be('absolute');
const { to, from } = timefilter.getTime();
expect(to).to.be(new Date(lt).toISOString());
expect(from).to.be(new Date(gt).toISOString());
});

View file

@ -57,11 +57,11 @@ describe('moment formatting filter', function () {
// MMMM Do YYYY, HH:mm:ss.SSS
it('should format moments', function () {
expect(filter(moment())).to.be('January 1st 2014, 06:06:06.666');
expect(filter(moment())).to.be('Jan 1, 2014 @ 06:06:06.666');
});
it('should format dates', function () {
expect(filter(new Date())).to.be('January 1st 2014, 06:06:06.666');
expect(filter(new Date())).to.be('Jan 1, 2014 @ 06:06:06.666');
});
it('should return the original value if passed anything other than a moment or Date', function () {

View file

@ -38,10 +38,10 @@
/**
* kbnTopNav directive
*
* The top section that shows the timepicker, load, share and save dialogues.
* The top section that optionally shows the timepicker and menu items.
*
* ```
* <kbn-top-nav name="current-app-for-extensions" config="path.to.menuItems"></kbn-top-nav>
* <kbn-top-nav name="current-app-for-extensions" config="path.to.menuItems" ></kbn-top-nav>
* ```
*
* Menu items/templates are passed to the kbnTopNav via the config attribute
@ -149,6 +149,10 @@ module.directive('kbnTopNav', function (Private) {
initTopNav(topNavConfig, null);
if (!_.has($scope, 'showTimepickerInTopNav')) {
$scope.showTimepickerInTopNav = true;
}
return $scope.kbnTopNav;
},

View file

@ -20,8 +20,6 @@
import { capitalize, isArray, isFunction, get } from 'lodash';
import chrome from '../chrome';
import filterTemplate from '../chrome/config/filter.html';
import intervalTemplate from '../chrome/config/interval.html';
import { i18n } from '@kbn/i18n';
export function KbnTopNavControllerProvider($compile) {
@ -34,10 +32,7 @@ export function KbnTopNavControllerProvider($compile) {
this.opts = [];
this.menuItems = [];
this.currentKey = null;
this.templates = {
interval: intervalTemplate,
filter: filterTemplate,
};
this.templates = {};
this.locals = new Map();
this.addItems(opts);

View file

@ -29,6 +29,7 @@ import {
EuiColorPicker,
EuiIconTip,
EuiCallOut,
EuiSuperDatePicker,
} from '@elastic/eui';
import { uiModules } from './modules';
@ -46,3 +47,19 @@ app.directive('colorPicker', reactDirective => reactDirective(EuiColorPicker));
app.directive('iconTip', reactDirective => reactDirective(EuiIconTip, ['content', 'type', 'position', 'title', 'color']));
app.directive('callOut', reactDirective => reactDirective(EuiCallOut, ['title', 'color', 'size', 'iconType', 'children']));
app.directive('superDatePicker', reactDirective => reactDirective(EuiSuperDatePicker, [
'start',
'end',
'isPaused',
'refreshInterval',
'commonlyUsedRanges',
'dateFormat',
'recentlyUsedRanges',
'onTimeChange',
'onRefreshChange',
'isAutoRefreshOnly',
'commonlyUsedRanges',
'dateFormat',
'recentlyUsedRanges',
]));

View file

@ -62,7 +62,7 @@
@import "responsive-utilities.less"; // Minimal usage
// Decent usage in multiple areas
@import "dropdowns.less"; // Used in console, datepicker, watcher
@import "dropdowns.less"; // Used in console, watcher
@import "input-groups.less"; // Used in ML, typeahead, reporting, graph
@import "pagination.less";
@import "pager.less";

View file

@ -2,6 +2,3 @@
// COMPILER FOR BOOTSTRAP
@import "~ui/styles/bootstrap/bootstrap_dark";
// Components -- waiting on EUI conversion
@import "~ui/timepicker/timepicker";

View file

@ -5,4 +5,3 @@
// Components -- waiting on EUI conversion
@import "~ui/filter_bar/filter_bar";
@import "~ui/timepicker/timepicker";

View file

@ -20,7 +20,6 @@
interface TimeRange {
from: string;
to: string;
mode?: string;
}
export interface TimeHistory {

View file

@ -19,13 +19,15 @@
import moment from 'moment';
import { PersistedLog } from '../persisted_log';
import { TIME_MODES } from '../timepicker/modes';
class TimeHistory {
constructor() {
const historyOptions = {
maxLength: 10,
filterDuplicates: true
filterDuplicates: true,
isDuplicate: (oldItem, newItem) => {
return oldItem.from === newItem.from && oldItem.to === newItem.to;
}
};
this.history = new PersistedLog('kibana.timepicker.timeHistory', historyOptions);
}
@ -38,7 +40,6 @@ class TimeHistory {
// time from/to can be strings or moment objects - convert to strings so always dealing with same types
const justStringsTime = {
from: moment.isMoment(time.from) ? time.from.toISOString() : time.from,
mode: TIME_MODES.RECENT,
to: moment.isMoment(time.to) ? time.to.toISOString() : time.to
};
this.history.add(justStringsTime);

View file

@ -51,7 +51,6 @@ class Timefilter extends SimpleEmitter {
* @param {Object} time
* @property {string|moment} time.from
* @property {string|moment} time.to
* @property {string} time.mode (quick | relative | absolute)
*/
setTime = (time) => {
// Object.assign used for partially composed updates
@ -60,7 +59,6 @@ class Timefilter extends SimpleEmitter {
this._time = {
from: newTime.from,
to: newTime.to,
mode: newTime.mode
};
timeHistory.add(this._time);
this.emit('timeUpdate');

View file

@ -25,7 +25,7 @@ jest.mock('ui/chrome',
get: (key) => {
switch (key) {
case 'timepicker:timeDefaults':
return { from: 'now-15m', to: 'now', mode: 'quick' };
return { from: 'now-15m', to: 'now' };
case 'timepicker:refreshIntervalDefaults':
return { pause: false, value: 0 };
default:
@ -69,15 +69,14 @@ describe('setTime', () => {
timefilter.setTime({
from: 0,
to: 1,
mode: 'absolute'
});
timefilter.on('timeUpdate', update);
timefilter.on('fetch', fetch);
});
test('should update time', () => {
timefilter.setTime({ from: 5, to: 10, mode: 'absolute' });
expect(timefilter.getTime()).to.eql({ from: 5, to: 10, mode: 'absolute' });
timefilter.setTime({ from: 5, to: 10 });
expect(timefilter.getTime()).to.eql({ from: 5, to: 10 });
});
test('should not add unexpected object keys to time state', () => {
@ -88,7 +87,7 @@ describe('setTime', () => {
test('should allow partial updates to time', () => {
timefilter.setTime({ from: 5, to: 10 });
expect(timefilter.getTime()).to.eql({ from: 5, to: 10, mode: 'absolute' });
expect(timefilter.getTime()).to.eql({ from: 5, to: 10 });
});
test('not emit anything if the time has not changed', () => {
@ -106,11 +105,10 @@ describe('setTime', () => {
test('should return strings and not moment objects', () => {
const from = moment().subtract(15, 'minutes');
const to = moment();
timefilter.setTime({ to, from, mode: 'absolute' });
timefilter.setTime({ to, from });
expect(timefilter.getTime()).to.eql({
from: from.toISOString(),
to: to.toISOString(),
mode: 'absolute'
});
});
});

View file

@ -1,124 +0,0 @@
/*
* 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.
*/
import { parseRelativeString, parseRelativeParts } from '../parse_relative_parts';
import expect from 'expect.js';
import moment from 'moment';
describe('parseRelativeParts(from, to, relativeOptions)', () => {
it('should parse relative string', () => {
const results = parseRelativeString('now-2h');
expect(results).to.have.property('count', 2);
expect(results).to.have.property('unit', 'h');
expect(results).to.have.property('round', false);
});
it('should parse now', () => {
const results = parseRelativeString('now');
expect(results).to.have.property('count', 0);
expect(results).to.have.property('unit', 's');
expect(results).to.have.property('round', false);
});
it('should parse set round options', () => {
const results = parseRelativeString('now-2h/h');
expect(results).to.have.property('round', true);
});
it('should parse now-2h to now-10m/m', () => {
expect(parseRelativeParts('now-2h', 'now-10m/m')).to.eql({
from: {
count: 2,
unit: 'h',
round: false
},
to: {
count: 10,
unit: 'm',
round: true
}
});
});
it('should parse now-2h to now+10m/m', () => {
expect(parseRelativeParts('now-2h', 'now+10m/m')).to.eql({
from: {
count: 2,
unit: 'h',
round: false
},
to: {
count: 10,
unit: 'm+',
round: true
}
});
});
it('should parse 3 months ago to now', () => {
expect(parseRelativeParts(moment().subtract(3, 'M'), moment())).to.eql({
from: {
count: 3,
unit: 'M',
round: false
},
to: {
count: 0,
unit: 's',
round: false
}
});
});
it('should parse 3 months ago to 15 minutes ago', () => {
const from = moment().subtract(3, 'M');
const to = moment().subtract(15, 'm');
expect(parseRelativeParts(from, to)).to.eql({
from: {
count: 3,
unit: 'M',
round: false
},
to: {
count: 15,
unit: 'm',
round: false
}
});
});
it('should parse 3 months ago to 2 hours from now', () => {
const from = moment().subtract(3, 'M');
const to = moment().add(2, 'h');
expect(parseRelativeParts(from, to)).to.eql({
from: {
count: 3,
unit: 'M',
round: false
},
to: {
count: 2,
unit: 'h+',
round: false
}
});
});
});

View file

@ -1,61 +0,0 @@
/*
* 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.
*/
import expect from 'expect.js';
import moment from 'moment';
import { timeNavigation } from '../time_navigation';
describe('timeNavigation', () => {
let bounds;
beforeEach(() => {
bounds = {
min: moment('2016-01-01T00:00:00.000Z'),
max: moment('2016-01-01T00:15:00.000Z')
};
});
it('should step forward by the amount of the duration', () => {
const { from, to, mode } = timeNavigation.stepForward(bounds);
expect(from).to.be('2016-01-01T00:15:00.001Z');
expect(to).to.be('2016-01-01T00:30:00.001Z');
expect(mode).to.be('absolute');
});
it('should step backward by the amount of the duration', () => {
const { from, to, mode } = timeNavigation.stepBackward(bounds);
expect(from).to.be('2015-12-31T23:44:59.999Z');
expect(to).to.be('2015-12-31T23:59:59.999Z');
expect(mode).to.be('absolute');
});
it('should zoom out to be double the original duration', () => {
const { from, to, mode } = timeNavigation.zoomOut(bounds);
expect(from).to.be('2015-12-31T23:52:30.000Z');
expect(to).to.be('2016-01-01T00:22:30.000Z');
expect(mode).to.be('absolute');
});
it('should zoom in to be half the original duration', () => {
const { from, to, mode } = timeNavigation.zoomIn(bounds);
expect(from).to.be('2016-01-01T00:03:45.000Z');
expect(to).to.be('2016-01-01T00:11:15.000Z');
expect(mode).to.be('absolute');
});
});

View file

@ -1,101 +0,0 @@
/*
* 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.
*/
import expect from 'expect.js';
import ngMock from 'ng_mock';
import $ from 'jquery';
import { timefilter } from 'ui/timefilter';
describe('kbnGlobalTimepicker', function () {
let compile;
let scope;
beforeEach(() => {
ngMock.module('kibana');
ngMock.inject(($compile, $rootScope) => {
scope = $rootScope.$new();
compile = () => {
const $el = $('<kbn-global-timepicker></kbn-global-timepicker>');
$el.data('$kbnTopNavController', {}); // Mock the kbnTopNav
$compile($el)(scope);
scope.$apply();
return $el;
};
});
});
it('injects the timepicker into the DOM', () => {
const $el = compile();
expect($el.attr('data-test-subj')).to.be('globalTimepicker');
});
it('sets data-shared-timefilter-* using the timefilter when auto-refresh selector is enabled', function () {
const minString = '2000-01-01T00:00:00Z';
const maxString = '2001-01-01T00:00:00Z';
timefilter.enableAutoRefreshSelector();
timefilter.disableTimeRangeSelector();
timefilter.setTime({
from: minString,
to: maxString,
mode: 'absolute'
});
const $el = compile();
expect($el.attr('data-shared-timefilter-from')).to.eql(minString);
expect($el.attr('data-shared-timefilter-to')).to.eql(maxString);
});
it('sets data-shared-timefilter-* using the timefilter when time range selector is enabled', function () {
const minString = '2000-01-01T00:00:00Z';
const maxString = '2001-01-01T00:00:00Z';
timefilter.disableAutoRefreshSelector();
timefilter.enableTimeRangeSelector();
timefilter.setTime({
from: minString,
to: maxString,
mode: 'absolute'
});
const $el = compile();
expect($el.attr('data-shared-timefilter-from')).to.eql(minString);
expect($el.attr('data-shared-timefilter-to')).to.eql(maxString);
});
it(`doesn't set data-shared-timefilter-* when auto-refresh and time range selectors are both disabled`, function () {
const minString = '2000-01-01T00:00:00Z';
const maxString = '2001-01-01T00:00:00Z';
timefilter.disableAutoRefreshSelector();
timefilter.disableTimeRangeSelector();
timefilter.setTime({
from: minString,
to: maxString,
mode: 'absolute'
});
const $el = compile();
expect($el.attr('data-shared-timefilter-from')).to.eql('');
expect($el.attr('data-shared-timefilter-to')).to.eql('');
});
});

View file

@ -1,20 +0,0 @@
/*
* 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.
*/
import './kbn_timepicker_absolute_panel';

View file

@ -1,110 +0,0 @@
<form name="absoluteTime" ng-submit="applyAbsolute()">
<div class="kbn-timepicker-body">
<div class="kbn-timepicker-section kbn-timepicker-body-column">
<div class="kuiLocalDropdownHeader kbn-timepicker-form-header">
<label class="kuiLocalDropdownHeader__label"
i18n-id="common.ui.timepicker.from"
i18n-default-message="From"
>
</label>
<div class="kuiLocalDropdownHeader__actions">
<a
class="kuiLocalDropdownHeader__action"
ng-click="setToNow({key:'from'})"
kbn-accessible-click
>
<span
i18n-id="common.ui.timepicker.setToNow"
i18n-default-message="Set To Now"
></span>
</a>
</div>
</div>
<input
type="text"
required
class="kuiTextInput fullWidth"
input-datetime="{{format}}"
ng-model="absolute.from"
>
<div ng-show="!absolute.from">
<em
i18n-id="common.ui.timepicker.invalidDate"
i18n-default-message="Invalid Date"
>
</em>
</div>
<div>
<datepicker ng-model="pickFromDate" ng-model-options="{ getterSetter: true }" max-date="pickToDate()" show-weeks="false"></datepicker>
</div>
</div>
<div class="kbn-timepicker-section kbn-timepicker-body-column">
<div class="kuiLocalDropdownHeader kbn-timepicker-form-header">
<label class="kuiLocalDropdownHeader__label"
i18n-id="common.ui.timepicker.to"
i18n-default-message="To"
>
</label>
<div class="kuiLocalDropdownHeader__actions">
<a
class="kuiLocalDropdownHeader__action"
ng-click="setToNow({key:'to'})"
kbn-accessible-click
>
<span
i18n-id="common.ui.timepicker.setToNow"
i18n-default-message="Set To Now"
></span>
</a>
</div>
</div>
<span ng-show="!absolute.to">
<em
i18n-id="common.ui.timepicker.invalidDate"
i18n-default-message="Invalid Date"
>
</em>
</span>
<input
type="text"
required
class="kuiTextInput fullWidth"
input-datetime="{{format}}"
ng-model="absolute.to"
>
<div>
<datepicker ng-model="pickToDate" ng-model-options="{ getterSetter: true }" min-date="pickFromDate()" show-weeks="false"></datepicker>
</div>
</div>
</div>
<div class="kbn-timepicker-actions kuiVerticalRhythm">
<span
class="kbn-timepicker-action-item kbn-timepicker-error"
ng-show="absolute.from > absolute.to"
i18n-id="common.ui.timepicker.fromToError"
i18n-default-message="{strongStr}From{strongEnd} must occur before {strongStr}To{strongEnd}"
i18n-values="{
html_strongStr: '<strong>',
html_strongEnd: '</strong>'
}"
>
</span>
<button
type="submit"
class="kuiButton kuiButton--primary kbn-timepicker-submit-button"
ng-disabled="absolute.from > absolute.to || !absolute.from || !absolute.to"
data-test-subj="timepickerGoButton"
i18n-id="common.ui.timepicker.go"
i18n-default-message="Go"
>
</button>
</div>
</form>

View file

@ -1,41 +0,0 @@
/*
* 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.
*/
import template from './kbn_timepicker_absolute_panel.html';
import { uiModules } from '../../modules';
const module = uiModules.get('ui/timepicker');
module.directive('kbnTimepickerAbsolutePanel', function () {
return {
restrict: 'E',
replace: true,
scope: {
absolute: '=',
applyAbsolute: '&',
format: '=',
pickFromDate: '=',
pickToDate: '=',
setToNow: '&'
},
template,
controller: function () {
}
};
});

View file

@ -17,4 +17,4 @@
* under the License.
*/
import './timepicker';
import './kbn_global_timepicker';

View file

@ -1,90 +1,20 @@
<div
ng-show="timefilterValues.isAutoRefreshSelectorEnabled || timefilterValues.isTimeRangeSelectorEnabled"
ng-if="timefilterValues.isAutoRefreshSelectorEnabled || timefilterValues.isTimeRangeSelectorEnabled"
data-shared-timefilter-from="{{ getSharedTimeFilterFromDate() }}"
data-shared-timefilter-to="{{ getSharedTimeFilterToDate() }}"
class="kuiLocalMenu"
data-test-subj="globalTimepicker"
>
<button
class="kuiLocalMenuItem"
aria-label="{{ timefilterValues.refreshInterval.pause ? 'Resume refreshing data' : 'Pause refreshing data' }}"
ng-click="toggleRefresh()"
ng-show="timefilterValues.isAutoRefreshSelectorEnabled && (timefilterValues.refreshInterval.value > 0)"
data-test-subj="globalTimepickerAutoRefreshButton"
data-test-subj-state="{{ timefilterValues.refreshInterval.pause ? 'inactive' : 'active' }}"
>
<span
class="kuiIcon"
aria-hidden="true"
ng-class="timefilterValues.refreshInterval.pause ? 'fa-play' : 'fa-pause'"
></span>
</button>
<button
class="kuiLocalMenuItem navbar-timepicker-auto-refresh-desc"
ng-class="{'kuiLocalMenuItem-isSelected': kbnTopNav.isCurrent('interval') }"
ng-show="timefilterValues.isAutoRefreshSelectorEnabled"
ng-click="kbnTopNav.toggle('interval')"
data-test-subj="globalRefreshButton"
>
<span ng-show="timefilterValues.refreshInterval.value === 0">
<span aria-hidden="true" class="kuiIcon fa-repeat"></span>
<span
i18n-id="common.ui.timepicker.autoRefresh"
i18n-default-message="Auto-refresh"
>
</span>
</span>
<span
ng-show="timefilterValues.refreshInterval.value > 0"
aria-label="Data will refresh every {{timefilterValues.display.refreshInterval}}"
>
{{ timefilterValues.display.refreshInterval }}
</span>
</button>
<button
ng-show="timefilterValues.isTimeRangeSelectorEnabled"
class="kuiLocalMenuItem"
ng-click="back()"
aria-label="{{ ::'common.ui.timepicker.moveBackardInTime' | i18n: { defaultMessage: 'Move backward in time' } }}"
>
<span
class="kuiIcon fa-chevron-left"
aria-hidden="true"
tooltip="{{ ::'common.ui.timepicker.moveBackardInTime' | i18n: { defaultMessage: 'Move backward in time' } }}"
></span>
</button>
<button
ng-show="timefilterValues.isTimeRangeSelectorEnabled"
data-test-subj="globalTimepickerButton"
class="kuiLocalMenuItem navbar-timepicker-time-desc"
ng-class="{'kuiLocalMenuItem-isSelected': kbnTopNav.isCurrent('filter')}"
ng-click="kbnTopNav.toggle('filter')"
aria-label="Open time range picker"
aria-haspopup="true"
>
<span aria-hidden="true" class="kuiIcon fa-clock-o"></span>
<span
aria-label="Current time range is {{timefilterValues.display.time}}"
data-test-subj="globalTimepickerRange"
>
{{timefilterValues.display.time}}
</span>
</button>
<button
ng-show="timefilterValues.isTimeRangeSelectorEnabled"
class="kuiLocalMenuItem"
ng-click="forward()"
aria-label="{{ ::'common.ui.timepicker.moveForwardInTime' | i18n: { defaultMessage: 'Move forward in time' } }}"
>
<span
aria-hidden="true"
class="kuiIcon fa-chevron-right"
tooltip="{{ ::'common.ui.timepicker.moveForwardInTime' | i18n: { defaultMessage: 'Move forward in time' } }}"
></span>
</button>
<super-date-picker
start="timefilterValues.time.from"
end="timefilterValues.time.to"
is-paused="timefilterValues.refreshInterval.pause"
refresh-interval="timefilterValues.refreshInterval.value"
on-time-change="updateFilter"
on-refresh-change="updateInterval"
is-auto-refresh-only="!timefilterValues.isTimeRangeSelectorEnabled"
commonly-used-ranges="commonlyUsedRanges"
date-format="dateFormat"
recently-used-ranges="recentlyUsedRanges"
/>
</div>

View file

@ -20,16 +20,12 @@
import { uiModules } from '../modules';
import toggleHtml from './kbn_global_timepicker.html';
import { timeNavigation } from './time_navigation';
import { timefilter } from 'ui/timefilter';
import { prettyDuration } from './pretty_duration';
import { prettyInterval } from './pretty_interval';
import { timeHistory } from 'ui/timefilter/time_history';
uiModules
.get('kibana')
.directive('kbnGlobalTimepicker', (globalState, config) => {
const getConfig = (...args) => config.get(...args);
const listenForUpdates = ($scope) => {
$scope.$listenAndDigestAsync(timefilter, 'refreshIntervalUpdate', () => {
setTimefilterValues($scope);
@ -48,44 +44,51 @@ uiModules
$scope.timefilterValues = {
refreshInterval: refreshInterval,
time: time,
display: {
time: prettyDuration(time.from, time.to, getConfig),
refreshInterval: prettyInterval(refreshInterval.value),
},
isAutoRefreshSelectorEnabled: timefilter.isAutoRefreshSelectorEnabled,
isTimeRangeSelectorEnabled: timefilter.isTimeRangeSelectorEnabled,
};
$scope.recentlyUsedRanges = timeHistory.get().map(({ from, to }) => {
return {
start: from,
end: to,
};
});
}
return {
template: toggleHtml,
replace: true,
require: '^kbnTopNav',
link: ($scope, element, attributes, kbnTopNav) => {
link: ($scope) => {
listenForUpdates($scope);
setTimefilterValues($scope);
$scope.toggleRefresh = () => {
timefilter.toggleRefresh();
config.watch('timepicker:quickRanges', (quickRanges) => {
// quickRanges is null when timepicker:quickRanges is set to default value
const ranges = quickRanges ? quickRanges : config.get('timepicker:quickRanges');
$scope.commonlyUsedRanges = ranges.map(({ from, to, display }) => {
return {
start: from,
end: to,
label: display,
};
});
});
config.watch('dateFormat', (dateFormat) => {
// dateFormat is null when dateFormat is set to default value
$scope.dateFormat = dateFormat ? dateFormat : config.get('dateFormat');
});
$scope.updateFilter = function ({ start, end }) {
timefilter.setTime({ from: start, to: end });
};
$scope.forward = function () {
timefilter.setTime(timeNavigation.stepForward(timefilter.getBounds()));
};
$scope.back = function () {
timefilter.setTime(timeNavigation.stepBackward(timefilter.getBounds()));
};
$scope.updateFilter = function (from, to, mode) {
timefilter.setTime({ from, to, mode });
kbnTopNav.close('filter');
};
$scope.updateInterval = function (interval) {
timefilter.setRefreshInterval(interval);
kbnTopNav.close('interval');
$scope.updateInterval = function ({ isPaused, refreshInterval }) {
timefilter.setRefreshInterval({
pause: isPaused,
value: refreshInterval ? refreshInterval : $scope.timefilterValues.refreshInterval.value
});
};
$scope.getSharedTimeFilterFromDate = function () {

View file

@ -1,25 +0,0 @@
/*
* 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 const TIME_MODES = {
ABSOLUTE: 'absolute',
QUICK: 'quick',
RECENT: 'recent',
RELATIVE: 'relative',
};

View file

@ -1,73 +0,0 @@
/*
* 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.
*/
import dateMath from '@elastic/datemath';
import moment from 'moment';
import _ from 'lodash';
import { relativeOptions } from './relative_options';
export function parseRelativeString(part) {
let results = {};
const matches = _.isString(part) && part.match(/now(([\-\+])([0-9]+)([smhdwMy])(\/[smhdwMy])?)?/);
const isNow = matches && !matches[1];
const operator = matches && matches[2];
const count = matches && matches[3];
const unit = matches && matches[4];
const roundBy = matches && matches[5];
if (isNow) {
return { count: 0, unit: 's', round: false };
}
if (count && unit) {
results.count = parseInt(count, 10);
results.unit = unit;
if (operator === '+') results.unit += '+';
results.round = roundBy ? true : false;
return results;
} else {
results = { count: 0, unit: 's', round: false };
const duration = moment.duration(moment().diff(dateMath.parse(part)));
const units = _.pluck(_.clone(relativeOptions).reverse(), 'value')
.filter(s => /^[smhdwMy]$/.test(s));
let unitOp = '';
for (let i = 0; i < units.length; i++) {
const as = duration.as(units[i]);
if (as < 0) unitOp = '+';
if (Math.abs(as) > 1) {
results.count = Math.round(Math.abs(as));
results.unit = units[i] + unitOp;
results.round = false;
break;
}
}
return results;
}
}
export function parseRelativeParts(from, to) {
const results = {};
results.from = parseRelativeString(from);
results.to = parseRelativeString(to);
if (results.from && results.to) return results;
}

View file

@ -1,94 +0,0 @@
/*
* 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.
*/
import dateMath from '@elastic/datemath';
import moment from 'moment';
import { timeUnits } from './time_units';
import { i18n } from '@kbn/i18n';
const TIME_NOW = 'now';
function cantLookup(timeFrom, timeTo, dateFormat) {
const displayFrom = formatTimeString(timeFrom, dateFormat);
const displayTo = formatTimeString(timeTo, dateFormat, true);
return i18n.translate('common.ui.timepicker.fullTimeRange', {
defaultMessage: '{displayFrom} to {displayTo}',
values: {
displayFrom,
displayTo
}
});
}
function formatTimeString(timeString, dateFormat, roundUp = false) {
if (moment(timeString).isValid()) {
return moment(timeString).format(dateFormat);
} else {
if (timeString === TIME_NOW) {
return i18n.translate('common.ui.timepicker.timeNow', { defaultMessage: 'now' });
} else {
const tryParse = dateMath.parse(timeString, { roundUp: roundUp });
return moment.isMoment(tryParse) ? '~ ' + tryParse.fromNow() : timeString;
}
}
}
function getDateLookupKey(startDate, endDate) {
return startDate + ' to ' + endDate;
}
export function prettyDuration(timeFrom, timeTo, getConfig) {
const quickRanges = getConfig('timepicker:quickRanges');
const dateFormat = getConfig('dateFormat');
const lookupByRange = {};
quickRanges.forEach((frame) => {
lookupByRange[getDateLookupKey(frame.from, frame.to)] = frame;
});
// If both parts are date math, try to look up a reasonable string
if (timeFrom && timeTo && !moment.isMoment(timeFrom) && !moment.isMoment(timeTo)) {
const tryLookup = lookupByRange[getDateLookupKey(timeFrom, timeTo)];
if (tryLookup) {
return tryLookup.display;
} else {
const [start, end] = timeFrom.toString().split('-');
if (timeTo.toString() === TIME_NOW && start === TIME_NOW && end) {
const [amount, unitId] = end.split('/');
let text = i18n.translate('common.ui.timepicker.timeUntilNowStr', {
defaultMessage: 'Last {amount}',
values: { amount }
});
if (unitId) {
text = text + ' ' + i18n.translate('common.ui.timepicker.roundedTo', {
defaultMessage: 'rounded to the {unit}',
values: { unit: timeUnits[unitId] }
});
}
return text;
} else {
return cantLookup(timeFrom, timeTo, dateFormat);
}
}
}
// If at least one part is a moment, try to make pretty strings by parsing date math
return cantLookup(timeFrom, timeTo, dateFormat);
}

View file

@ -1,70 +0,0 @@
/*
* 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.
*/
import expect from 'expect.js';
import moment from 'moment-timezone';
import { prettyDuration } from './pretty_duration';
const config = {};
moment.tz.setDefault('UTC');
config.dateFormat = 'MMMM Do YYYY, HH:mm:ss.SSS';
config['timepicker:quickRanges'] = [
{
from: 'now-15m',
to: 'now',
display: 'quick range 15 minutes custom display',
}
];
const getConfig = (key) => config[key];
test('quick range', () => {
const timeFrom = 'now-15m';
const timeTo = 'now';
expect(prettyDuration(timeFrom, timeTo, getConfig)).to.be('quick range 15 minutes custom display');
});
describe('relative range', () => {
test('from = now', () => {
const timeFrom = 'now-1M';
const timeTo = 'now';
expect(prettyDuration(timeFrom, timeTo, getConfig)).to.be('Last 1M');
});
test('from is in past', () => {
const timeFrom = 'now-17m';
const timeTo = 'now-15m';
expect(prettyDuration(timeFrom, timeTo, getConfig)).to.be('~ 17 minutes ago to ~ 15 minutes ago');
});
});
describe('absolute range', () => {
test('dates in string format)', () => {
const timeFrom = '2018-01-17T18:57:57.149Z';
const timeTo = '2018-01-17T20:00:00.000Z';
expect(prettyDuration(timeFrom, timeTo, getConfig)).to.be('January 17th 2018, 18:57:57.149 to January 17th 2018, 20:00:00.000');
});
test('dates as moment objects', () => {
const timeFrom = moment('2018-01-17T18:57:57.149Z');
const timeTo = moment('2018-01-17T20:00:00.000Z');
expect(prettyDuration(timeFrom, timeTo, getConfig)).to.be('January 17th 2018, 18:57:57.149 to January 17th 2018, 20:00:00.000');
});
});

View file

@ -1,68 +0,0 @@
/*
* 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.
*/
import { i18n } from '@kbn/i18n';
const MS_IN_SECOND = 1000;
const MS_IN_MINUTE = 60 * MS_IN_SECOND;
const MS_IN_HOUR = 60 * MS_IN_MINUTE;
const MS_IN_DAY = 24 * MS_IN_HOUR;
export function prettyInterval(intervalInMs) {
let interval;
if (intervalInMs === 0) {
return i18n.translate('common.ui.timepicker.off', { defaultMessage: 'Off' });
} else if (intervalInMs < MS_IN_MINUTE) {
interval = Math.round(intervalInMs / MS_IN_SECOND);
return i18n.translate('common.ui.timepicker.totalSeconds', {
defaultMessage: `{interval} {interval, plural,
one {second}
other {seconds}
}`,
values: { interval },
});
} else if (intervalInMs < MS_IN_HOUR) {
interval = Math.round(intervalInMs / MS_IN_MINUTE);
return i18n.translate('common.ui.timepicker.totalMinutes', {
defaultMessage: `{interval} {interval, plural,
one {minute}
other {minutes}
}`,
values: { interval },
});
} else if (intervalInMs < MS_IN_DAY) {
interval = Math.round(intervalInMs / MS_IN_HOUR);
return i18n.translate('common.ui.timepicker.totalHours', {
defaultMessage: `{interval} {interval, plural,
one {hour}
other {hours}
}`,
values: { interval },
});
} else {
interval = Math.round(intervalInMs / MS_IN_DAY);
return i18n.translate('common.ui.timepicker.totalDays', {
defaultMessage: `{interval} {interval, plural,
one {day}
other {days}
}`,
values: { interval },
});
}
}

View file

@ -1,45 +0,0 @@
/*
* 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.
*/
import expect from 'expect.js';
import { prettyInterval } from './pretty_interval';
test('Off', () => {
expect(prettyInterval(0)).to.be('Off');
});
test('seconds', () => {
expect(prettyInterval(1000)).to.be('1 second');
expect(prettyInterval(15000)).to.be('15 seconds');
});
test('minutes', () => {
expect(prettyInterval(60000)).to.be('1 minute');
expect(prettyInterval(1800000)).to.be('30 minutes');
});
test('hours', () => {
expect(prettyInterval(3600000)).to.be('1 hour');
expect(prettyInterval(43200000)).to.be('12 hours');
});
test('days', () => {
expect(prettyInterval(86400000)).to.be('1 day');
expect(prettyInterval(86400000 * 2)).to.be('2 days');
});

View file

@ -1,20 +0,0 @@
/*
* 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.
*/
import './kbn_timepicker_quick_panel';

View file

@ -1,14 +0,0 @@
<div class="kbn-timepicker-body">
<div ng-repeat="list in quickLists" class="kbn-timepicker-section kbn-timepicker-quick-section">
<ul class="list-unstyled">
<li ng-repeat="option in list">
<a
class="kuiLink"
ng-click="setQuick({from: option.from, to: option.to})"
ng-bind="::option.display"
kbn-accessible-click
></a>
</li>
</ul>
</div>
</div>

View file

@ -1,39 +0,0 @@
/*
* 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.
*/
import _ from 'lodash';
import template from './kbn_timepicker_quick_panel.html';
import { uiModules } from '../../modules';
const module = uiModules.get('ui/timepicker');
module.directive('kbnTimepickerQuickPanel', function (config) {
return {
restrict: 'E',
replace: true,
scope: {
setQuick: '&'
},
template,
controller: function ($scope) {
const quickRanges = config.get('timepicker:quickRanges');
$scope.quickLists = _(quickRanges).groupBy('section').values().value();
}
};
});

View file

@ -1,20 +0,0 @@
/*
* 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.
*/
import './kbn_timepicker_recent_panel';

View file

@ -1,21 +0,0 @@
<div class="kbn-timepicker-body">
<div ng-show="recent.length === 0" class="euiText">
<p>
No time history. Your most recent time ranges show up here, so you can easily view them later.
</p>
</div>
<div class="kbn-timepicker-section">
<ul class="list-unstyled">
<li ng-repeat="option in recent">
<a
class="kuiLink"
ng-click="setQuick({from: option.from, to: option.to})"
ng-bind="::option.display"
kbn-accessible-click
></a>
</li>
</ul>
</div>
</div>

View file

@ -1,43 +0,0 @@
/*
* 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.
*/
import template from './kbn_timepicker_recent_panel.html';
import { uiModules } from '../../modules';
import { timeHistory } from '../../timefilter/time_history';
import { prettyDuration } from '../pretty_duration';
const module = uiModules.get('ui/timepicker');
module.directive('kbnTimepickerRecentPanel', function () {
return {
restrict: 'E',
replace: true,
scope: {
setQuick: '&'
},
template,
controller: function ($scope, config) {
const getConfig = (...args) => config.get(...args);
$scope.recent = timeHistory.get().map(time => {
time.display = prettyDuration(time.from, time.to, getConfig);
return time;
});
}
};
});

View file

@ -1,41 +0,0 @@
/*
* 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.
*/
import { uiModules } from '../modules';
const module = uiModules.get('kibana');
module.constant('refreshIntervals', [
{ value: 0, section: 0 },
{ value: 5000, section: 1 },
{ value: 10000, section: 1 },
{ value: 30000, section: 1 },
{ value: 45000, section: 1 },
{ value: 60000, section: 2 },
{ value: 300000, section: 2 },
{ value: 900000, section: 2 },
{ value: 1800000, section: 2 },
{ value: 3600000, section: 3 },
{ value: 7200000, section: 3 },
{ value: 43200000, section: 3 },
{ value: 86400000, section: 3 }
]);

View file

@ -1,20 +0,0 @@
/*
* 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.
*/
import './kbn_timepicker_relative_panel';

View file

@ -1,191 +0,0 @@
<form role="form" ng-submit="applyRelative()" class="form-inline" name="relativeTime">
<div class="kbn-timepicker-body kuiVerticalRhythm">
<div class="kbn-timepicker-section kbn-timepicker-body-column">
<div class="kuiLocalDropdownHeader kbn-timepicker-form-header">
<label class="kuiLocalDropdownHeader__label"
i18n-id="common.ui.timepicker.from"
i18n-default-message="From"
>
</label>
<div class="kuiLocalDropdownHeader__actions">
<a
class="kuiLocalDropdownHeader__action"
ng-click="setRelativeToNow({key:'from'})"
kbn-accessible-click
>
<span
i18n-id="common.ui.timepicker.setToNow"
i18n-default-message="Set To Now"
></span>
</a>
</div>
</div>
<div class="kuiLocalDropdownFormNote kuiVerticalRhythmSmall">
<span ng-show="relative.from.preview">
{{relative.from.preview}}
</span>
<span ng-hide="relative.from.preview">
<em
i18n-id="common.ui.timepicker.relative.invalidExpression"
i18n-default-message="Invalid Expression"
>
</em>
</span>
</div>
<div class="kuiFieldGroup kuiVerticalRhythmSmall">
<div class="kuiFieldGroupSection kuiFieldGroupSection--wide">
<input
required
ng-model="relative.from.count"
ng-change="formatRelative({key:'from'})"
greater-than="-1"
min="0"
type="number"
ng-class="{ 'kuiTextInput-isInvalid' : checkRelative() }"
class="kuiTextInput fullWidth"
>
</div>
<div class="kuiFieldGroupSection">
<select
ng-model="relative.from.unit"
ng-options="opt.value as opt.text for opt in relativeOptions"
ng-change="formatRelative({key:'from'})"
ng-class="{ 'kuiSelect-isInvalid': checkRelative() }"
class="kuiSelect fullWidth"
>
</select>
</div>
</div>
<div class="kuiVerticalRhythmSmall">
<label class="kuiCheckBoxLabel">
<input
class="kuiCheckBox"
type="checkbox"
ng-model="relative.from.round"
ng-checked="relative.from.round"
ng-change="formatRelative({key:'from'})"
>
<span class="kuiCheckBoxLabel__text"
i18n-id="common.ui.timepicker.roundTo"
i18n-default-message="round to the {unit}"
i18n-description="{unit} will be a translated time unit, e.g. \"hour\", \"minute\""
i18n-values="{
unit: units[relative.to.unit.substring(0,1)]
}"
>
</span>
</label>
</div>
</div>
<div class="kbn-timepicker-section kbn-timepicker-body-column">
<div class="kuiLocalDropdownHeader kbn-timepicker-form-header">
<label class="kuiLocalDropdownHeader__label"
i18n-id="common.ui.timepicker.to"
i18n-default-message="To"
>
</label>
<div class="kuiLocalDropdownHeader__actions">
<a
class="kuiLocalDropdownHeader__action"
ng-click="setRelativeToNow({key:'to'})"
kbn-accessible-click
>
<span
i18n-id="common.ui.timepicker.setToNow"
i18n-default-message="Set To Now"
></span>
</a>
</div>
</div>
<div class="kuiLocalDropdownFormNote kuiVerticalRhythmSmall">
<span ng-show="relative.to.preview">
{{relative.to.preview}}
</span>
<span ng-hide="relative.to.preview">
<em
i18n-id="common.ui.timepicker.invalidExpression"
i18n-default-message="Invalid Expression"
>
</em>
</span>
</div>
<div class="kuiFieldGroup kuiVerticalRhythmSmall">
<div class="kuiFieldGroupSection kuiFieldGroupSection--wide">
<input
required
ng-model="relative.to.count"
ng-change="formatRelative({key:'to'})"
greater-than="-1"
min="0"
type="number"
ng-class="{ 'kuiTextInput-isInvalid': checkRelative() }"
class="kuiTextInput fullWidth"
>
</div>
<div class="kuiFieldGroupSection">
<select
ng-model="relative.to.unit"
ng-options="opt.value as opt.text for opt in relativeOptions"
ng-change="formatRelative({key:'to'})"
ng-class="{ 'kuiSelect-isInvalid': checkRelative() }"
class="kuiSelect fullWidth"
>
</select>
</div>
</div>
<div class="kuiVerticalRhythmSmall">
<label class="kuiCheckBoxLabel">
<input
class="kuiCheckBox"
type="checkbox"
ng-model="relative.to.round"
ng-checked="relative.to.round"
ng-change="formatRelative({key:'to'})"
>
<span class="kuiCheckBoxLabel__text"
i18n-id="common.ui.timepicker.roundTo"
i18n-default-message="round to the {unit}"
i18n-description="{unit} will be a translated time unit, e.g. \"hour\", \"minute\""
i18n-values="{
unit: units[relative.to.unit.substring(0,1)]
}">
</span>
</label>
</div>
</div>
</div>
<div class="kbn-timepicker-actions kuiVerticalRhythm">
<span
class="kbn-timepicker-action-item kbn-timepicker-error"
ng-show="checkRelative()"
i18n-id="common.ui.timepicker.fromToError"
i18n-default-message="{strongStr}From{strongEnd} must occur before {strongStr}To{strongEnd}"
i18n-values="{
html_strongStr: '<strong>',
html_strongEnd: '</strong>'
}">
</span>
<button
type="submit"
class="kuiButton kuiButton--primary kbn-timepicker-submit-button"
ng-disabled="!(relative.from.preview && relative.to.preview) || checkRelative()"
data-test-subj="timepickerGoButton"
i18n-id="common.ui.timepicker.go"
i18n-default-message="Go">
</button>
</div>
</form>

View file

@ -1,42 +0,0 @@
/*
* 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.
*/
import template from './kbn_timepicker_relative_panel.html';
import { uiModules } from '../../modules';
const module = uiModules.get('ui/timepicker');
module.directive('kbnTimepickerRelativePanel', function () {
return {
restrict: 'E',
replace: true,
scope: {
applyRelative: '&',
checkRelative: '&',
formatRelative: '&',
relative: '=',
relativeOptions: '=',
setRelativeToNow: '&',
units: '='
},
template,
controller: function () {
}
};
});

View file

@ -1,62 +0,0 @@
/*
* 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.
*/
import moment from 'moment';
export const timeNavigation = {
// travel forward in time based on the interval between from and to
stepForward({ min, max }) {
const diff = max.diff(min);
return {
from: moment(max).add(1, 'ms').toISOString(),
to: moment(max).add(diff + 1, 'ms').toISOString(),
mode: 'absolute'
};
},
// travel backwards in time based on the interval between from and to
stepBackward({ min, max }) {
const diff = max.diff(min);
return {
from: moment(min).subtract(diff + 1, 'ms').toISOString(),
to: moment(min).subtract(1, 'ms').toISOString(),
mode: 'absolute'
};
},
// zoom out, doubling the difference between start and end, keeping the same time range center
zoomOut({ min, max }) {
const diff = max.diff(min);
return {
from: moment(min).subtract(diff / 2, 'ms').toISOString(),
to: moment(max).add(diff / 2, 'ms').toISOString(),
mode: 'absolute'
};
},
// zoom in, halving the difference between start and end, keeping the same time range center
zoomIn({ min, max }) {
const diff = max.diff(min);
return {
from: moment(min).add(diff / 4, 'ms').toISOString(),
to: moment(max).subtract(diff / 4, 'ms').toISOString(),
mode: 'absolute'
};
}
};

View file

@ -1,29 +0,0 @@
/*
* 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 const timeUnits = {
s: 'second',
m: 'minute',
h: 'hour',
d: 'day',
w: 'week',
M: 'month',
y: 'year'
};

View file

@ -1,141 +0,0 @@
<div class="kbn-timepicker" data-test-subj="timePicker">
<div class="tab-content kbn-timepicker-content">
<!-- Filters -->
<div ng-show="activeTab === 'filter'" role="tabpanel" class="tab-pane active">
<div class="kbn-timepicker-title">
<h2 class="kuiLocalDropdownTitle kbn-timepicker-title__text"
i18n-id="common.ui.timepicker.timerange"
i18n-default-message="Time Range"
>
</h2>
</div>
<div class="kuiVerticalRhythmSmall">
<div role="tablist" class="euiTabs euiTabs--small kbn-timepicker-modes kbn-timepicker-title__section">
<button
id="timepickerQuick"
role="tab"
class="euiTab"
ng-class="{'euiTab-isSelected': mode === 'quick' }"
ng-click="setMode('quick')"
aria-selected="{{mode === 'quick'}}"
data-test-subj="timepicker-quick-button"
i18n-id="common.ui.timepicker.categoryquick"
i18n-default-message="Quick"
>
</button>
<button
id="timepickerRelative"
role="tab"
class="euiTab"
ng-class="{'euiTab-isSelected': mode === 'relative' }"
ng-click="setMode('relative')"
aria-selected="{{mode === 'relative'}}"
data-test-subj="timepicker-relative-button"
i18n-id="common.ui.timepicker.categoryrelative"
i18n-default-message="Relative"
>
</button>
<button
id="timepickerAbsolute"
role="tab"
class="euiTab"
ng-class="{'euiTab-isSelected': mode === 'absolute' }"
ng-click="setMode('absolute')"
aria-selected="{{mode === 'absolute'}}"
data-test-subj="timepicker-absolute-button"
i18n-id="common.ui.timepicker.categoryabsolute"
i18n-default-message="Absolute"
>
</button>
<button
id="timepickerRecent"
role="tab"
class="euiTab"
ng-class="{'euiTab-isSelected': mode === 'recent' }"
ng-click="setMode('recent')"
aria-selected="{{mode === 'recent'}}"
data-test-subj="timepicker-recent-button"
i18n-id="common.ui.timepicker.categoryrecent"
i18n-default-message="Recent"
>
</button>
</div>
</div>
<div
class="kuiVerticalRhythmSmall"
ng-switch
on="mode"
>
<kbn-timepicker-quick-panel
ng-switch-when="quick"
set-quick="setQuick(from, to)"
role="tabpanel"
aria-labelledby="timepickerQuick"
></kbn-timepicker-quick-panel>
<kbn-timepicker-recent-panel
ng-switch-when="recent"
set-quick="setQuick(from, to)"
role="tabpanel"
aria-labelledby="timepickerRecent"
></kbn-timepicker-recent-panel>
<kbn-timepicker-relative-panel
ng-switch-when="relative"
apply-relative="applyRelative()"
check-relative="checkRelative()"
format-relative="formatRelative(key)"
relative="relative"
relative-options="relativeOptions"
set-relative-to-now="setRelativeToNow(key)"
units="units"
role="tabpanel"
aria-labelledby="timepickerRelative"
></kbn-timepicker-relative-panel>
<kbn-timepicker-absolute-panel
ng-switch-when="absolute"
absolute="absolute"
apply-absolute="applyAbsolute()"
format="format"
pick-from-date="pickFromDate"
pick-to-date="pickToDate"
set-to-now="setToNow(key)"
role="tabpanel"
aria-labelledby="timepickerAbsolute"
></kbn-timepicker-absolute-panel>
</div>
</div>
<!-- Refresh Intervals -->
<div ng-show="activeTab === 'interval'" role="tabpanel" class="tab-pane active">
<h2
class="kuiLocalDropdownTitle"
i18n-id="common.ui.timepicker.refreshInterval"
i18n-default-message="Refresh Interval">
</h2>
<div ng-repeat="list in refreshLists" class="kbn-refresh-section">
<ul class="list-unstyled">
<li ng-repeat="inter in list">
<a
class="refresh-interval"
ng-class="{ 'refresh-interval-active': interval.value === inter.value }"
ng-click="setRefreshInterval(inter)"
kbn-accessible-click
>
{{prettyInterval(inter)}}
</a>
</li>
</ul>
</div>
</div>
</div>
</div>

View file

@ -1,257 +0,0 @@
/*
* 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.
*/
import html from './timepicker.html';
import './quick_panel';
import './recent_panel';
import './relative_panel';
import './absolute_panel';
import _ from 'lodash';
import { relativeOptions } from './relative_options';
import { parseRelativeParts } from './parse_relative_parts';
import dateMath from '@elastic/datemath';
import moment from 'moment';
import '../directives/input_datetime';
import '../directives/inequality';
import './refresh_intervals';
import './kbn_global_timepicker';
import { uiModules } from '../modules';
import { TIME_MODES } from './modes';
import { timeUnits } from './time_units';
import { prettyInterval } from './pretty_interval';
import { i18n } from '@kbn/i18n';
const module = uiModules.get('ui/timepicker');
module.directive('kbnTimepicker', function (refreshIntervals) {
return {
restrict: 'E',
scope: {
from: '=',
to: '=',
mode: '=',
interval: '=',
activeTab: '=',
onFilterSelect: '&',
onIntervalSelect: '&'
},
template: html,
controller: function ($scope) {
$scope.format = 'MMMM Do YYYY, HH:mm:ss.SSS';
$scope.modes = Object.values(TIME_MODES);
$scope.activeTab = $scope.activeTab || 'filter';
if (_.isUndefined($scope.mode)) $scope.mode = TIME_MODES.QUICK;
$scope.refreshLists = _(refreshIntervals).groupBy('section').values().value();
$scope.relative = {
from: {
count: 1,
unit: 'm',
preview: undefined,
round: false
},
to: {
count: 0,
unit: 's',
preview: undefined,
round: false
}
};
$scope.absolute = {
from: moment(),
to: moment()
};
$scope.units = timeUnits;
$scope.relativeOptions = relativeOptions;
$scope.$watch('from', function (date) {
if (moment.isMoment(date) && $scope.mode === TIME_MODES.ABSOLUTE) {
$scope.absolute.from = date;
}
});
$scope.$watch('to', function (date) {
if (moment.isMoment(date) && $scope.mode === TIME_MODES.ABSOLUTE) {
$scope.absolute.to = date;
}
});
// If we always return a new object from the getters below (pickFromDate and pickToDate) we'll create an
// infinite digest loop, so we maintain these copies to return instead.
$scope.$watch('absolute.from', function (newDate) {
if (!newDate) {
return;
}
_.set($scope, 'browserAbsolute.from', new Date(newDate.year(), newDate.month(), newDate.date()));
});
$scope.$watch('absolute.to', function (newDate) {
if (!newDate) {
return;
}
_.set($scope, 'browserAbsolute.to', new Date(newDate.year(), newDate.month(), newDate.date()));
});
// The datepicker directive uses native JavaScript Dates, ignoring moment's default timezone. This causes
// the datepicker and the text input above it to get out of sync if the user changed the `dateFormat:tz` config
// in advanced settings. The text input will show the date in the user selected timezone, the datepicker will
// show the date in the local browser timezone. Since we really just want a day, month, year from the datepicker
// instead of a moment in time, we grab those individual values from the native date.
$scope.pickFromDate = function (date) {
if (!date) return _.get($scope, 'browserAbsolute.from');
const defaultTimeZoneDate = moment({
year: date.getFullYear(),
month: date.getMonth(),
day: date.getDate(),
hour: 0,
minute: 0,
second: 0,
millisecond: 0,
});
return $scope.absolute.from = defaultTimeZoneDate;
};
$scope.pickToDate = function (date) {
if (!date) return _.get($scope, 'browserAbsolute.to');
const defaultTimeZoneDate = moment({
year: date.getFullYear(),
month: date.getMonth(),
day: date.getDate(),
hour: 23,
minute: 59,
second: 59,
millisecond: 999,
});
return $scope.absolute.to = defaultTimeZoneDate;
};
$scope.setMode = function (thisMode) {
switch (thisMode) {
case TIME_MODES.QUICK:
break;
case TIME_MODES.RECENT:
break;
case TIME_MODES.RELATIVE:
$scope.relative = parseRelativeParts($scope.from, $scope.to);
$scope.formatRelative('from');
$scope.formatRelative('to');
break;
case TIME_MODES.ABSOLUTE:
$scope.absolute.from = dateMath.parse($scope.from || moment().subtract(15, 'minutes'));
$scope.absolute.to = dateMath.parse($scope.to || moment(), { roundUp: true });
break;
}
$scope.mode = thisMode;
};
$scope.setQuick = function (from, to) {
$scope.onFilterSelect({ from, to, mode: TIME_MODES.QUICK });
};
$scope.setToNow = function (key) {
$scope.absolute[key] = moment();
};
$scope.setRelativeToNow = function (key) {
$scope.relative[key].count = 0;
$scope.relative[key].round = false;
$scope.formatRelative(key);
};
$scope.checkRelative = function () {
if ($scope.relative.from.count != null && $scope.relative.to.count != null) {
const from = dateMath.parse(getRelativeString('from'));
const to = dateMath.parse(getRelativeString('to'), { roundUp: true });
if (to && from) return to.isBefore(from);
return true;
}
};
$scope.formatRelative = function (key) {
const relativeString = getRelativeString(key);
const parsed = dateMath.parse(relativeString, { roundUp: key === 'to' });
let preview;
if (relativeString === 'now') {
preview = i18n.translate('common.ui.timepicker.now', { defaultMessage: 'Now' });
} else {
preview = parsed ? parsed.format($scope.format) : undefined;
}
_.set($scope, `relative.${key}.preview`, preview);
return parsed;
};
$scope.applyRelative = function () {
$scope.onFilterSelect({
from: getRelativeString('from'),
to: getRelativeString('to'),
mode: TIME_MODES.RELATIVE,
});
};
function getRelativeString(key) {
const count = _.get($scope, `relative.${key}.count`, 0);
const round = _.get($scope, `relative.${key}.round`, false);
const matches = _.get($scope, `relative.${key}.unit`, 's').match(/([smhdwMy])(\+)?/);
let unit;
let operator = '-';
if (matches && matches[1]) unit = matches[1];
if (matches && matches[2]) operator = matches[2];
if (count === 0 && !round) return 'now';
let result = `now${operator}${count}${unit}`;
result += (round ? '/' + unit : '');
return result;
}
$scope.applyAbsolute = function () {
$scope.onFilterSelect({
from: moment($scope.absolute.from),
to: moment($scope.absolute.to),
mode: TIME_MODES.ABSOLUTE,
});
};
$scope.prettyInterval = function (interval) {
return prettyInterval(interval.value);
};
$scope.setRefreshInterval = function (interval) {
interval = _.clone(interval || {});
interval.pause = (interval.pause == null || interval.pause === false) ? false : true;
$scope.onIntervalSelect({
interval: {
value: interval.value,
pause: interval.pause,
}
});
};
$scope.setMode($scope.mode);
}
};
});

View file

@ -1,98 +0,0 @@
.kbn-timepicker {
display: flex;
justify-content: flex-end;
.kbn-timepicker-content {
width: 100%;
max-width: 600px;
}
[kbn-time-input] {
text-align: center;
}
.kbn-timepicker-modes {
text-transform: capitalize;
}
.kbn-timepicker-section {
float: left;
& + .kbn-timepicker-section {
padding-left: 15px;
}
}
.kbn-timepicker-quick-section {
width: 25%;
}
.kbn-timepicker-body {
display: flex;
align-items: top;
justify-content: stretch;
width: 100%;
@media (max-width: 660px) {
flex-direction: column;
}
}
.kbn-timepicker-body-column {
width: 100%;
}
.kbn-timepicker-form-header {
margin-bottom: 0 !important;
}
.kbn-timepicker-actions {
display: flex;
justify-content: flex-end;
align-items: baseline;
}
.kbn-timepicker-action-item,
.kbn-timepicker-submit-button {
margin-left: 10px;
}
.kbn-timepicker-submit-button {
min-width: 100px;
}
.kbn-refresh-section {
float: left;
padding: 0px 15px;
}
.kbn-timepicket-alert {
width: 100px;
}
.kbn-timepicker-error {
color: @brand-danger;
}
.kbn-timepicker-title {
display: flex;
justify-content: space-between;
}
/**
* 1. Override kuiLocalNav styles.
*/
.kbn-timepicker-title__text {
margin-bottom: 0 !important; /* 1 */
}
.kbn-timepicker-title__section {
display: flex;
align-items: center;
}
/**
* Avoid last nav overlapping collapse button
*/
.nav:last-child {
margin-right: 24px;
}
.refresh-interval {
padding: 0.2em 0.4em;
border-radius: @border-radius-small;
}
.refresh-interval-active {
background-color: @brand-info;
color: @white;
}
}
.navbar-timepicker-time-desc > .fa-clock-o {
padding-right: 5px;
}

View file

@ -25,7 +25,7 @@ jest.mock('ui/chrome',
get: (key) => {
switch (key) {
case 'timepicker:timeDefaults':
return { from: 'now-15m', to: 'now', mode: 'quick' };
return { from: 'now-15m', to: 'now' };
case 'timepicker:refreshIntervalDefaults':
return { display: 'Off', pause: false, value: 0 };
default:
@ -126,8 +126,7 @@ describe('brushEvent', () => {
const event = _.cloneDeep(dateEvent);
event.range = [JAN_01_2014, JAN_01_2014 + DAY_IN_MS];
onBrushEvent(event, $state);
const { mode, from, to } = timefilter.getTime();
expect(mode).to.be('absolute');
const { from, to } = timefilter.getTime();
// Set to a baseline timezone for comparison.
expect(from).to.be(new Date(JAN_01_2014).toISOString());
// Set to a baseline timezone for comparison.

View file

@ -28,12 +28,12 @@ export default function ({ getService, getPageObjects }) {
const retry = getService('retry');
const docTable = getService('docTable');
const filterBar = getService('filterBar');
const PageObjects = getPageObjects(['common', 'header', 'discover']);
const PageObjects = getPageObjects(['common', 'discover', 'timePicker']);
describe('context link in discover', function contextSize() {
before(async function () {
await PageObjects.common.navigateToApp('discover');
await PageObjects.header.setAbsoluteRange(TEST_DISCOVER_START_TIME, TEST_DISCOVER_END_TIME);
await PageObjects.timePicker.setAbsoluteRange(TEST_DISCOVER_START_TIME, TEST_DISCOVER_END_TIME);
await Promise.all(TEST_COLUMN_NAMES.map((columnName) => (
PageObjects.discover.clickFieldListItemAdd(columnName)
)));

View file

@ -25,7 +25,7 @@ const fromTime = '2015-09-19 06:31:44.000';
const toTime = '2015-09-23 18:31:44.000';
export default function ({ getPageObjects, getService }) {
const PageObjects = getPageObjects(['dashboard', 'header']);
const PageObjects = getPageObjects(['dashboard', 'header', 'timePicker']);
const browser = getService('browser');
describe('dashboard time', () => {
@ -46,48 +46,31 @@ export default function ({ getPageObjects, getService }) {
});
it('Does not set the time picker on open', async () => {
await PageObjects.header.setAbsoluteRange(fromTime, toTime);
await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime);
await PageObjects.dashboard.loadSavedDashboard(dashboardName);
const fromTimeNext = await PageObjects.header.getFromTime();
const toTimeNext = await PageObjects.header.getToTime();
expect(fromTimeNext).to.equal(fromTime);
expect(toTimeNext).to.equal(toTime);
const time = await PageObjects.timePicker.getTimeConfig();
expect(time.start).to.equal('Sep 19, 2015 @ 06:31:44.000');
expect(time.end).to.equal('Sep 23, 2015 @ 18:31:44.000');
});
});
describe('dashboard with stored timed', async function () {
it('is saved with quick time', async function () {
it('is saved with time', async function () {
await PageObjects.dashboard.switchToEditMode();
await PageObjects.header.setQuickTime('Today');
await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime);
await PageObjects.dashboard.saveDashboard(dashboardName, { storeTimeWithDashboard: true });
});
it('sets quick time on open', async function () {
await PageObjects.header.setAbsoluteRange(fromTime, toTime);
it('sets time on open', async function () {
await PageObjects.timePicker.setAbsoluteRange('2019-01-01 00:00:00.000', '2019-01-02 00:00:00.000');
await PageObjects.dashboard.loadSavedDashboard(dashboardName);
const prettyPrint = await PageObjects.header.getPrettyDuration();
expect(prettyPrint).to.equal('Today');
});
it('is saved with absolute time', async function () {
await PageObjects.dashboard.switchToEditMode();
await PageObjects.header.setAbsoluteRange(fromTime, toTime);
await PageObjects.dashboard.saveDashboard(dashboardName, { storeTimeWithDashboard: true });
});
it('sets absolute time on open', async function () {
await PageObjects.header.setQuickTime('Today');
await PageObjects.dashboard.loadSavedDashboard(dashboardName);
const fromTimeNext = await PageObjects.header.getFromTime();
const toTimeNext = await PageObjects.header.getToTime();
expect(fromTimeNext).to.equal(fromTime);
expect(toTimeNext).to.equal(toTime);
const time = await PageObjects.timePicker.getTimeConfig();
expect(time.start).to.equal('Sep 19, 2015 @ 06:31:44.000');
expect(time.end).to.equal('Sep 23, 2015 @ 18:31:44.000');
});
// If time is stored with a dashboard, it's supposed to override the current time settings when opened.
@ -100,10 +83,11 @@ export default function ({ getPageObjects, getService }) {
await PageObjects.dashboard.gotoDashboardLandingPage();
const urlWithGlobalTime = `${kibanaBaseUrl}#/dashboard/${id}?_g=(time:(from:now-1h,mode:quick,to:now))`;
const urlWithGlobalTime = `${kibanaBaseUrl}#/dashboard/${id}?_g=(time:(from:now-1h,to:now))`;
await browser.get(urlWithGlobalTime, false);
const prettyPrint = await PageObjects.header.getPrettyDuration();
expect(prettyPrint).to.equal('Last 1 hour');
const time = await PageObjects.timePicker.getTimeConfig();
expect(time.start).to.equal('~ an hour ago');
expect(time.end).to.equal('now');
});
});
@ -115,12 +99,13 @@ export default function ({ getPageObjects, getService }) {
it('preserved during navigation', async function () {
await PageObjects.dashboard.loadSavedDashboard(dashboardName);
await PageObjects.header.setQuickTime('Today');
await PageObjects.timePicker.setAbsoluteRange('2019-01-01 00:00:00.000', '2019-01-02 00:00:00.000');
await PageObjects.header.clickVisualize();
await PageObjects.header.clickDashboard();
const prettyPrint = await PageObjects.header.getPrettyDuration();
expect(prettyPrint).to.equal('Today');
const time = await PageObjects.timePicker.getTimeConfig();
expect(time.start).to.equal('Jan 1, 2019 @ 00:00:00.000');
expect(time.end).to.equal('Jan 2, 2019 @ 00:00:00.000');
});
});
});

View file

@ -23,7 +23,7 @@ export default function ({ getService, getPageObjects }) {
const dashboardExpect = getService('dashboardExpect');
const pieChart = getService('pieChart');
const dashboardVisualizations = getService('dashboardVisualizations');
const PageObjects = getPageObjects(['dashboard', 'header', 'visualize']);
const PageObjects = getPageObjects(['dashboard', 'header', 'visualize', 'timePicker']);
describe('dashboard time picker', function describeIndexTests() {
before(async function () {
@ -54,7 +54,8 @@ export default function ({ getService, getPageObjects }) {
await dashboardVisualizations.createAndAddSavedSearch({ name: 'saved search', fields: ['bytes', 'agent'] });
await dashboardExpect.docTableFieldCount(150);
await PageObjects.header.setQuickTime('Today');
// Set to time range with no data
await PageObjects.timePicker.setAbsoluteRange('2000-01-01 00:00:00.000', '2000-01-01 01:00:00.000');
await dashboardExpect.docTableFieldCount(0);
});
});

View file

@ -33,7 +33,7 @@ export default function ({ getService, getPageObjects }) {
const pieChart = getService('pieChart');
const dashboardExpect = getService('dashboardExpect');
const dashboardAddPanel = getService('dashboardAddPanel');
const PageObjects = getPageObjects(['common', 'dashboard', 'header', 'visualize', 'discover']);
const PageObjects = getPageObjects(['common', 'dashboard', 'header', 'visualize', 'discover', 'timePicker']);
const expectAllDataRenders = async () => {
await pieChart.expectPieSliceCount(16);
@ -96,7 +96,7 @@ export default function ({ getService, getPageObjects }) {
const fromTime = '2018-01-01 00:00:00.000';
const toTime = '2018-04-13 00:00:00.000';
await PageObjects.header.setAbsoluteRange(fromTime, toTime);
await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime);
});
after(async () => {
@ -139,7 +139,7 @@ export default function ({ getService, getPageObjects }) {
// Change the time to make sure that it's updated when re-opened from the listing page.
const fromTime = '2018-05-10 00:00:00.000';
const toTime = '2018-05-11 00:00:00.000';
await PageObjects.header.setAbsoluteRange(fromTime, toTime);
await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime);
await PageObjects.dashboard.loadSavedDashboard('embeddable rendering test');
await PageObjects.dashboard.waitForRenderComplete();
await expectAllDataRenders();
@ -156,8 +156,7 @@ export default function ({ getService, getPageObjects }) {
it('panels are updated when time changes outside of data', async () => {
const fromTime = '2018-05-11 00:00:00.000';
const toTime = '2018-05-12 00:00:00.000';
await PageObjects.header.setAbsoluteRange(fromTime, toTime);
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime);
await PageObjects.dashboard.waitForRenderComplete();
await expectNoDataRenders();
});
@ -165,8 +164,7 @@ export default function ({ getService, getPageObjects }) {
it('panels are updated when time changes inside of data', async () => {
const fromTime = '2018-01-01 00:00:00.000';
const toTime = '2018-04-13 00:00:00.000';
await PageObjects.header.setAbsoluteRange(fromTime, toTime);
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime);
await PageObjects.dashboard.waitForRenderComplete();
await expectAllDataRenders();
});

View file

@ -22,7 +22,7 @@ import expect from 'expect.js';
export default function ({ getService, getPageObjects }) {
const pieChart = getService('pieChart');
const PageObjects = getPageObjects(['dashboard', 'header', 'settings', 'common']);
const PageObjects = getPageObjects(['dashboard', 'timePicker', 'settings', 'common']);
describe('dashboard time zones', () => {
before(async () => {
@ -41,8 +41,9 @@ export default function ({ getService, getPageObjects }) {
});
it('Exported dashboard adjusts EST time to UTC', async () => {
const timeRange = await PageObjects.header.getPrettyDuration();
expect(timeRange).to.be('April 10th 2018, 03:00:00.000 to April 10th 2018, 04:00:00.000');
const time = await PageObjects.timePicker.getTimeConfigAsAbsoluteTimes();
expect(time.start).to.be('2018-04-10 03:00:00.000');
expect(time.end).to.be('2018-04-10 04:00:00.000');
await pieChart.expectPieSliceCount(4);
});
@ -52,8 +53,9 @@ export default function ({ getService, getPageObjects }) {
await PageObjects.settings.setAdvancedSettingsSelect('dateFormat:tz', 'EST');
await PageObjects.common.navigateToApp('dashboard');
await PageObjects.dashboard.loadSavedDashboard('time zone test');
const timeRange = await PageObjects.header.getPrettyDuration();
expect(timeRange).to.be('April 9th 2018, 22:00:00.000 to April 9th 2018, 23:00:00.000');
const time = await PageObjects.timePicker.getTimeConfigAsAbsoluteTimes();
expect(time.start).to.be('2018-04-09 22:00:00.000');
expect(time.end).to.be('2018-04-09 23:00:00.000');
await pieChart.expectPieSliceCount(4);
});
});

View file

@ -22,7 +22,7 @@ import expect from 'expect.js';
export default function ({ getService, getPageObjects }) {
const queryBar = getService('queryBar');
const dashboardAddPanel = getService('dashboardAddPanel');
const PageObjects = getPageObjects(['dashboard', 'header', 'common', 'visualize']);
const PageObjects = getPageObjects(['dashboard', 'header', 'common', 'visualize', 'timePicker']);
const dashboardName = 'dashboard with filter';
const filterBar = getService('filterBar');
@ -62,23 +62,21 @@ export default function ({ getService, getPageObjects }) {
it('when time changed is stored with dashboard', async function () {
await PageObjects.dashboard.setTimepickerInDataRange();
const originalFromTime = await PageObjects.header.getFromTime();
const originalToTime = await PageObjects.header.getToTime();
const originalTime = await PageObjects.timePicker.getTimeConfig();
await PageObjects.dashboard.saveDashboard(dashboardName, { storeTimeWithDashboard: true });
await PageObjects.dashboard.switchToEditMode();
await PageObjects.header.setAbsoluteRange('2013-09-19 06:31:44.000', '2013-09-19 06:31:44.000');
await PageObjects.timePicker.setAbsoluteRange('2013-09-19 06:31:44.000', '2013-09-19 06:31:44.000');
await PageObjects.dashboard.clickCancelOutOfEditMode();
// confirm lose changes
await PageObjects.common.clickConfirmOnModal();
const newFromTime = await PageObjects.header.getFromTime();
const newToTime = await PageObjects.header.getToTime();
const newTime = await PageObjects.timePicker.getTimeConfig();
expect(newFromTime).to.equal(originalFromTime);
expect(newToTime).to.equal(originalToTime);
expect(newTime.start).to.equal(originalTime.start);
expect(newTime.end).to.equal(originalTime.end);
});
it('when the query is edited and applied', async function () {
@ -153,12 +151,10 @@ export default function ({ getService, getPageObjects }) {
describe('and preserves edits on cancel', function () {
it('when time changed is stored with dashboard', async function () {
await PageObjects.dashboard.gotoDashboardEditMode(dashboardName);
const newFromTime = '2015-09-19 06:31:44.000';
const newToTime = '2015-09-19 06:31:44.000';
await PageObjects.header.setAbsoluteRange('2013-09-19 06:31:44.000', '2013-09-19 06:31:44.000');
await PageObjects.timePicker.setAbsoluteRange('2013-09-19 06:31:44.000', '2013-09-19 06:31:44.000');
await PageObjects.dashboard.saveDashboard(dashboardName, true);
await PageObjects.dashboard.switchToEditMode();
await PageObjects.header.setAbsoluteRange(newToTime, newToTime);
await PageObjects.timePicker.setAbsoluteRange('2015-09-19 06:31:44.000', '2015-09-19 06:31:44.000');
await PageObjects.dashboard.clickCancelOutOfEditMode();
await PageObjects.common.clickCancelOnModal();
@ -166,11 +162,10 @@ export default function ({ getService, getPageObjects }) {
await PageObjects.dashboard.loadSavedDashboard(dashboardName);
const fromTime = await PageObjects.header.getFromTime();
const toTime = await PageObjects.header.getToTime();
const time = await PageObjects.timePicker.getTimeConfig();
expect(fromTime).to.equal(newFromTime);
expect(toTime).to.equal(newToTime);
expect(time.start).to.equal('Sep 19, 2015 @ 06:31:44.000');
expect(time.end).to.equal('Sep 19, 2015 @ 06:31:44.000');
});
});
});
@ -181,10 +176,8 @@ export default function ({ getService, getPageObjects }) {
await PageObjects.dashboard.setTimepickerInDataRange();
await PageObjects.dashboard.saveDashboard(dashboardName, true);
await PageObjects.dashboard.switchToEditMode();
await PageObjects.header.setAbsoluteRange('2013-09-19 06:31:44.000', '2013-09-19 06:31:44.000');
const newFromTime = await PageObjects.header.getFromTime();
const newToTime = await PageObjects.header.getToTime();
await PageObjects.timePicker.setAbsoluteRange('2013-09-19 06:31:44.000', '2013-09-19 06:31:44.000');
const newTime = await PageObjects.timePicker.getTimeConfig();
await PageObjects.dashboard.clickCancelOutOfEditMode();
@ -193,11 +186,10 @@ export default function ({ getService, getPageObjects }) {
await PageObjects.dashboard.loadSavedDashboard(dashboardName);
const fromTime = await PageObjects.header.getFromTime();
const toTime = await PageObjects.header.getToTime();
const time = await PageObjects.timePicker.getTimeConfig();
expect(fromTime).to.equal(newFromTime);
expect(toTime).to.equal(newToTime);
expect(time.start).to.equal(newTime.start);
expect(time.end).to.equal(newTime.end);
});
});
@ -206,7 +198,7 @@ export default function ({ getService, getPageObjects }) {
await PageObjects.dashboard.gotoDashboardEditMode(dashboardName);
await PageObjects.dashboard.saveDashboard(dashboardName, { storeTimeWithDashboard: false });
await PageObjects.dashboard.switchToEditMode();
await PageObjects.header.setAbsoluteRange('2014-10-19 06:31:44.000', '2014-12-19 06:31:44.000');
await PageObjects.timePicker.setAbsoluteRange('2014-10-19 06:31:44.000', '2014-12-19 06:31:44.000');
await PageObjects.dashboard.clickCancelOutOfEditMode();
await PageObjects.common.expectConfirmModalOpenState(false);

View file

@ -18,7 +18,6 @@
*/
import expect from 'expect.js';
import moment from 'moment';
export default function ({ getService, getPageObjects }) {
const log = getService('log');
@ -27,7 +26,7 @@ export default function ({ getService, getPageObjects }) {
const browser = getService('browser');
const kibanaServer = getService('kibanaServer');
const filterBar = getService('filterBar');
const PageObjects = getPageObjects(['common', 'discover', 'header', 'visualize']);
const PageObjects = getPageObjects(['common', 'discover', 'header', 'visualize', 'timePicker']);
const defaultSettings = {
'dateFormat:tz': 'UTC',
defaultIndex: 'logstash-*',
@ -35,9 +34,7 @@ export default function ({ getService, getPageObjects }) {
describe('discover app', function describeIndexTests() {
const fromTime = '2015-09-19 06:31:44.000';
const fromTimeString = 'September 19th 2015, 06:31:44.000';
const toTime = '2015-09-23 18:31:44.000';
const toTimeString = 'September 23rd 2015, 18:31:44.000';
before(async function () {
// delete .kibana index and update configDoc
@ -50,18 +47,16 @@ export default function ({ getService, getPageObjects }) {
await esArchiver.loadIfNeeded('logstash_functional');
log.debug('discover');
await PageObjects.common.navigateToApp('discover');
log.debug('setAbsoluteRange');
await PageObjects.header.setAbsoluteRange(fromTime, toTime);
await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime);
});
describe('query', function () {
const queryName1 = 'Query # 1';
it('should show correct time range string by timepicker', async function () {
const actualTimeString = await PageObjects.header.getPrettyDuration();
const expectedTimeString = `${fromTimeString} to ${toTimeString}`;
expect(actualTimeString).to.be(expectedTimeString);
const time = await PageObjects.timePicker.getTimeConfig();
expect(time.start).to.be('Sep 19, 2015 @ 06:31:44.000');
expect(time.end).to.be('Sep 23, 2015 @ 18:31:44.000');
});
it('save query should show toast message and display query name', async function () {
@ -121,8 +116,7 @@ export default function ({ getService, getPageObjects }) {
it('should show correct time range string in chart', async function () {
const actualTimeString = await PageObjects.discover.getChartTimespan();
const expectedTimeString = `${fromTimeString} - ${toTimeString}`;
const expectedTimeString = `Sep 19, 2015 @ 06:31:44.000 - Sep 23, 2015 @ 18:31:44.000`;
expect(actualTimeString).to.be(expectedTimeString);
});
@ -146,30 +140,29 @@ export default function ({ getService, getPageObjects }) {
});
it('should modify the time range when a bar is clicked', async function () {
await PageObjects.header.setAbsoluteRange(fromTime, toTime);
await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime);
await PageObjects.visualize.waitForVisualization();
await PageObjects.discover.clickHistogramBar(0);
await PageObjects.visualize.waitForVisualization();
const actualTimeString = await PageObjects.header.getPrettyDuration();
expect(actualTimeString).to.be('September 20th 2015, 00:00:00.000 to September 20th 2015, 03:00:00.000');
const time = await PageObjects.timePicker.getTimeConfig();
expect(time.start).to.be('Sep 20, 2015 @ 00:00:00.000');
expect(time.end).to.be('Sep 20, 2015 @ 03:00:00.000');
});
it('should modify the time range when the histogram is brushed', async function () {
await PageObjects.header.setAbsoluteRange(fromTime, toTime);
await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime);
await PageObjects.visualize.waitForVisualization();
await PageObjects.discover.brushHistogram(0, 1);
await PageObjects.visualize.waitForVisualization();
const newFromTime = await PageObjects.header.getFromTime();
const newToTime = await PageObjects.header.getToTime();
const newDurationHours = moment.duration(moment(newToTime) - moment(newFromTime)).asHours();
const newDurationHours = await PageObjects.timePicker.getTimeDurationInHours();
if (newDurationHours < 1 || newDurationHours >= 5) {
throw new Error(`expected new duration of ${newDurationHours} hours to be between 1 and 5 hours`);
}
});
it('should show correct initial chart interval of Auto', async function () {
await PageObjects.header.setAbsoluteRange(fromTime, toTime);
await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime);
const actualInterval = await PageObjects.discover.getChartInterval();
const expectedInterval = 'Auto';
@ -177,6 +170,7 @@ export default function ({ getService, getPageObjects }) {
});
it('should show correct data for chart interval Hourly', async function () {
await PageObjects.header.awaitGlobalLoadingIndicatorHidden();
await PageObjects.discover.setChartInterval('Hourly');
const expectedBarChartData = [
@ -391,7 +385,7 @@ export default function ({ getService, getPageObjects }) {
before(() => {
log.debug('setAbsoluteRangeForAnotherQuery');
return PageObjects.header.setAbsoluteRange(fromTime, toTime);
return PageObjects.timePicker.setAbsoluteRange(fromTime, toTime);
});
it('should show "no results"', async () => {
@ -403,17 +397,6 @@ export default function ({ getService, getPageObjects }) {
const isVisible = await PageObjects.discover.hasNoResultsTimepicker();
expect(isVisible).to.be(true);
});
it('should have a link that opens and closes the time picker', async function () {
const noResultsTimepickerLink = await PageObjects.discover.getNoResultsTimepicker();
expect(await PageObjects.header.isTimepickerOpen()).to.be(false);
await noResultsTimepickerLink.click();
expect(await PageObjects.header.isTimepickerOpen()).to.be(true);
await noResultsTimepickerLink.click();
expect(await PageObjects.header.isTimepickerOpen()).to.be(false);
});
});
describe('filter editor', function () {
@ -453,7 +436,7 @@ export default function ({ getService, getPageObjects }) {
it('should show bars in the correct time zone after switching', async function () {
await kibanaServer.uiSettings.replace({ 'dateFormat:tz': 'America/Phoenix' });
await browser.refresh();
await PageObjects.header.setAbsoluteRange(fromTime, toTime);
await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime);
const maxTicks = [
'2015-09-19 17:00',

View file

@ -24,7 +24,7 @@ export default function ({ getService, getPageObjects }) {
const esArchiver = getService('esArchiver');
const kibanaServer = getService('kibanaServer');
const queryBar = getService('queryBar');
const PageObjects = getPageObjects(['common', 'header', 'discover', 'visualize']);
const PageObjects = getPageObjects(['common', 'header', 'discover', 'visualize', 'timePicker']);
describe('discover tab', function describeIndexTests() {
before(async function () {
@ -40,16 +40,15 @@ export default function ({ getService, getPageObjects }) {
});
await PageObjects.common.navigateToApp('discover');
await PageObjects.header.setAbsoluteRange(fromTime, toTime);
await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime);
});
describe('field data', function () {
it('search php should show the correct hit count', async function () {
const expectedHitCount = '445';
await queryBar.setQuery('php');
await queryBar.submitQuery();
await retry.try(async function tryingForTime() {
await retry.try(async function () {
await queryBar.setQuery('php');
await queryBar.submitQuery();
const hitCount = await PageObjects.discover.getHitCount();
expect(hitCount).to.be(expectedHitCount);
});
@ -80,7 +79,7 @@ export default function ({ getService, getPageObjects }) {
});
it('doc view should sort ascending', async function () {
const expectedTimeStamp = 'September 20th 2015, 00:00:00.000';
const expectedTimeStamp = 'Sep 20, 2015 @ 00:00:00.000';
await PageObjects.discover.clickDocSortDown();
// we don't technically need this sleep here because the tryForTime will retry and the

View file

@ -20,7 +20,7 @@
import expect from 'expect.js';
export default function ({ getService, getPageObjects }) {
const PageObjects = getPageObjects(['common', 'header', 'visualize']);
const PageObjects = getPageObjects(['common', 'visualize', 'timePicker']);
const esArchiver = getService('esArchiver');
const kibanaServer = getService('kibanaServer');
const inspector = getService('inspector');
@ -59,7 +59,7 @@ export default function ({ getService, getPageObjects }) {
});
it('should display request stats with results', async () => {
await PageObjects.header.setAbsoluteRange('2015-09-19 06:31:44.000', '2015-09-23 18:31:44.000');
await PageObjects.timePicker.setAbsoluteRange('2015-09-19 06:31:44.000', '2015-09-23 18:31:44.000');
await inspector.open();
const requestStats = await inspector.getTableData();

View file

@ -24,7 +24,7 @@ export default function ({ getService, getPageObjects }) {
const log = getService('log');
const esArchiver = getService('esArchiver');
const kibanaServer = getService('kibanaServer');
const PageObjects = getPageObjects(['common', 'discover', 'header', 'share']);
const PageObjects = getPageObjects(['common', 'discover', 'share', 'timePicker']);
describe('shared links', function describeIndexTests() {
let baseUrl;
@ -54,8 +54,7 @@ export default function ({ getService, getPageObjects }) {
log.debug('discover');
await PageObjects.common.navigateToApp('discover');
log.debug('setAbsoluteRange');
await PageObjects.header.setAbsoluteRange(fromTime, toTime);
await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime);
//After hiding the time picker, we need to wait for
//the refresh button to hide before clicking the share button
@ -70,7 +69,7 @@ export default function ({ getService, getPageObjects }) {
baseUrl +
'/app/kibana?_t=1453775307251#' +
'/discover?_g=(refreshInterval:(pause:!t,value:0),time' +
':(from:\'2015-09-19T06:31:44.000Z\',mode:absolute,to:\'2015-09' +
':(from:\'2015-09-19T06:31:44.000Z\',to:\'2015-09' +
'-23T18:31:44.000Z\'))&_a=(columns:!(_source),index:\'logstash-' +
'*\',interval:auto,query:(language:lucene,query:\'\')' +
',sort:!(\'@timestamp\',desc))';
@ -97,7 +96,7 @@ export default function ({ getService, getPageObjects }) {
'/discover/ab12e3c0-f231-11e6-9486-733b1ac9221a' +
'?_g=(refreshInterval%3A(pause%3A!t%2Cvalue%3A0)' +
'%2Ctime%3A(from%3A\'2015-09-19T06%3A31%3A44.000Z\'%2C' +
'mode%3Aabsolute%2Cto%3A\'2015-09-23T18%3A31%3A44.000Z\'))';
'to%3A\'2015-09-23T18%3A31%3A44.000Z\'))';
await PageObjects.discover.loadSavedSearch('A Saved Search');
await PageObjects.share.clickShareTopNavButton();
await PageObjects.share.exportAsSavedObject();

View file

@ -23,7 +23,7 @@ export default function ({ getService, getPageObjects }) {
const log = getService('log');
const esArchiver = getService('esArchiver');
const kibanaServer = getService('kibanaServer');
const PageObjects = getPageObjects(['common', 'discover', 'header']);
const PageObjects = getPageObjects(['common', 'discover', 'timePicker']);
describe('discover sidebar', function describeIndexTests() {
before(async function () {
@ -45,8 +45,7 @@ export default function ({ getService, getPageObjects }) {
log.debug('discover');
await PageObjects.common.navigateToApp('discover');
log.debug('setAbsoluteRange');
await PageObjects.header.setAbsoluteRange(fromTime, toTime);
await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime);
});
describe('field filtering', function () {

View file

@ -23,7 +23,7 @@ export default function ({ getService, getPageObjects }) {
const log = getService('log');
const esArchiver = getService('esArchiver');
const kibanaServer = getService('kibanaServer');
const PageObjects = getPageObjects(['common', 'header', 'discover']);
const PageObjects = getPageObjects(['common', 'timePicker', 'discover']);
describe('source filters', function describeIndexTests() {
before(async function () {
@ -45,8 +45,7 @@ export default function ({ getService, getPageObjects }) {
log.debug('discover');
await PageObjects.common.navigateToApp('discover');
log.debug('setAbsoluteRange');
await PageObjects.header.setAbsoluteRange(fromTime, toTime);
await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime);
//After hiding the time picker, we need to wait for
//the refresh button to hide before clicking the share button

View file

@ -24,7 +24,7 @@ export default function ({ getService, getPageObjects }) {
const find = getService('find');
const pieChart = getService('pieChart');
const dashboardExpect = getService('dashboardExpect');
const PageObjects = getPageObjects(['common', 'header', 'home', 'dashboard']);
const PageObjects = getPageObjects(['common', 'header', 'home', 'dashboard', 'timePicker']);
describe('sample data', function describeIndexTests() {
@ -85,7 +85,7 @@ export default function ({ getService, getPageObjects }) {
const todayYearMonthDay = today.toISOString().substring(0, 10);
const fromTime = `${todayYearMonthDay} 00:00:00.000`;
const toTime = `${todayYearMonthDay} 23:59:59.999`;
await PageObjects.header.setAbsoluteRange(fromTime, toTime);
await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime);
const panelCount = await PageObjects.dashboard.getPanelCount();
expect(panelCount).to.be(19);
});
@ -123,7 +123,7 @@ export default function ({ getService, getPageObjects }) {
const todayYearMonthDay = today.toISOString().substring(0, 10);
const fromTime = `${todayYearMonthDay} 00:00:00.000`;
const toTime = `${todayYearMonthDay} 23:59:59.999`;
await PageObjects.header.setAbsoluteRange(fromTime, toTime);
await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime);
const panelCount = await PageObjects.dashboard.getPanelCount();
expect(panelCount).to.be(11);
});
@ -135,7 +135,7 @@ export default function ({ getService, getPageObjects }) {
const todayYearMonthDay = today.toISOString().substring(0, 10);
const fromTime = `${todayYearMonthDay} 00:00:00.000`;
const toTime = `${todayYearMonthDay} 23:59:59.999`;
await PageObjects.header.setAbsoluteRange(fromTime, toTime);
await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime);
const panelCount = await PageObjects.dashboard.getPanelCount();
expect(panelCount).to.be(12);
});

View file

@ -23,7 +23,7 @@ export default function ({ getService, getPageObjects }) {
const esArchiver = getService('esArchiver');
const es = getService('es');
const retry = getService('retry');
const PageObjects = getPageObjects(['common', 'home', 'settings', 'discover', 'header']);
const PageObjects = getPageObjects(['common', 'home', 'settings', 'discover', 'timePicker']);
describe('Index patterns on aliases', function () {
before(async function () {
@ -77,7 +77,7 @@ export default function ({ getService, getPageObjects }) {
await PageObjects.common.navigateToApp('discover');
await PageObjects.discover.selectIndexPattern('alias2');
await PageObjects.header.setAbsoluteRange(fromTime, toTime);
await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime);
await retry.try(async function () {
expect(await PageObjects.discover.getHitCount()).to.be(expectedHitCount);

View file

@ -22,7 +22,7 @@ import expect from 'expect.js';
export default function ({ getService, getPageObjects }) {
const kibanaServer = getService('kibanaServer');
const browser = getService('browser');
const PageObjects = getPageObjects(['settings', 'common', 'dashboard', 'header']);
const PageObjects = getPageObjects(['settings', 'common', 'dashboard', 'timePicker']);
describe('kibana settings', function describeIndexTests() {
before(async function () {
@ -57,7 +57,7 @@ export default function ({ getService, getPageObjects }) {
it('when false, dashboard state is unhashed', async function () {
await PageObjects.common.navigateToApp('dashboard');
await PageObjects.dashboard.clickNewDashboard();
await PageObjects.header.setAbsoluteRange('2015-09-19 06:31:44.000', '2015-09-23 18:31:44.000');
await PageObjects.timePicker.setAbsoluteRange('2015-09-19 06:31:44.000', '2015-09-23 18:31:44.000');
const currentUrl = await browser.getCurrentUrl();
const urlPieces = currentUrl.match(/(.*)?_g=(.*)&_a=(.*)/);
const globalState = urlPieces[2];
@ -80,7 +80,7 @@ export default function ({ getService, getPageObjects }) {
it('when true, dashboard state is hashed', async function () {
await PageObjects.common.navigateToApp('dashboard');
await PageObjects.dashboard.clickNewDashboard();
await PageObjects.header.setAbsoluteRange('2015-09-19 06:31:44.000', '2015-09-23 18:31:44.000');
await PageObjects.timePicker.setAbsoluteRange('2015-09-19 06:31:44.000', '2015-09-23 18:31:44.000');
const currentUrl = await browser.getCurrentUrl();
const urlPieces = currentUrl.match(/(.*)?_g=(.*)&_a=(.*)/);
const globalState = urlPieces[2];

View file

@ -39,7 +39,7 @@ export default function ({ getService, getPageObjects }) {
const inspector = getService('inspector');
const testSubjects = getService('testSubjects');
const filterBar = getService('filterBar');
const PageObjects = getPageObjects(['common', 'header', 'settings', 'visualize', 'discover']);
const PageObjects = getPageObjects(['common', 'header', 'settings', 'visualize', 'discover', 'timePicker']);
describe('scripted fields', () => {
@ -97,9 +97,7 @@ export default function ({ getService, getPageObjects }) {
const fromTime = '2015-09-17 06:31:44.000';
const toTime = '2015-09-18 18:31:44.000';
await PageObjects.common.navigateToApp('discover');
await log.debug('setAbsoluteRange (' + fromTime + ') to (' + toTime + ')');
await PageObjects.header.setAbsoluteRange(fromTime, toTime);
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime);
await PageObjects.visualize.waitForVisualization();
await PageObjects.discover.clickFieldListItem(scriptedPainlessFieldName);
await retry.try(async function () {
@ -109,7 +107,7 @@ export default function ({ getService, getPageObjects }) {
await PageObjects.visualize.waitForVisualization();
await retry.try(async function () {
const rowData = await PageObjects.discover.getDocTableIndex(1);
expect(rowData).to.be('September 18th 2015, 18:20:57.916\n18');
expect(rowData).to.be('Sep 18, 2015 @ 18:20:57.916\n18');
});
});
@ -160,9 +158,7 @@ export default function ({ getService, getPageObjects }) {
const fromTime = '2015-09-17 06:31:44.000';
const toTime = '2015-09-18 18:31:44.000';
await PageObjects.common.navigateToApp('discover');
await log.debug('setAbsoluteRange (' + fromTime + ') to (' + toTime + ')');
await PageObjects.header.setAbsoluteRange(fromTime, toTime);
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime);
await PageObjects.visualize.waitForVisualization();
await PageObjects.discover.clickFieldListItem(scriptedPainlessFieldName2);
await retry.try(async function () {
@ -172,7 +168,7 @@ export default function ({ getService, getPageObjects }) {
await PageObjects.visualize.waitForVisualization();
await retry.try(async function () {
const rowData = await PageObjects.discover.getDocTableIndex(1);
expect(rowData).to.be('September 18th 2015, 18:20:57.916\ngood');
expect(rowData).to.be('Sep 18, 2015 @ 18:20:57.916\ngood');
});
});
@ -222,9 +218,7 @@ export default function ({ getService, getPageObjects }) {
const fromTime = '2015-09-17 06:31:44.000';
const toTime = '2015-09-18 18:31:44.000';
await PageObjects.common.navigateToApp('discover');
await log.debug('setAbsoluteRange (' + fromTime + ') to (' + toTime + ')');
await PageObjects.header.setAbsoluteRange(fromTime, toTime);
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime);
await PageObjects.visualize.waitForVisualization();
await PageObjects.discover.clickFieldListItem(scriptedPainlessFieldName2);
await retry.try(async function () {
@ -234,7 +228,7 @@ export default function ({ getService, getPageObjects }) {
await PageObjects.visualize.waitForVisualization();
await retry.try(async function () {
const rowData = await PageObjects.discover.getDocTableIndex(1);
expect(rowData).to.be('September 18th 2015, 18:20:57.916\ntrue');
expect(rowData).to.be('Sep 18, 2015 @ 18:20:57.916\ntrue');
});
});
@ -285,9 +279,7 @@ export default function ({ getService, getPageObjects }) {
const fromTime = '2015-09-17 19:22:00.000';
const toTime = '2015-09-18 07:00:00.000';
await PageObjects.common.navigateToApp('discover');
await log.debug('setAbsoluteRange (' + fromTime + ') to (' + toTime + ')');
await PageObjects.header.setAbsoluteRange(fromTime, toTime);
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime);
await PageObjects.visualize.waitForVisualization();
await PageObjects.discover.clickFieldListItem(scriptedPainlessFieldName2);
await retry.try(async function () {
@ -297,7 +289,7 @@ export default function ({ getService, getPageObjects }) {
await PageObjects.visualize.waitForVisualization();
await retry.try(async function () {
const rowData = await PageObjects.discover.getDocTableIndex(1);
expect(rowData).to.be('September 18th 2015, 06:52:55.953\n2015-09-18 07:00');
expect(rowData).to.be('Sep 18, 2015 @ 06:52:55.953\n2015-09-18 07:00');
});
});

View file

@ -20,7 +20,7 @@
import expect from 'expect.js';
export default function ({ getPageObjects }) {
const PageObjects = getPageObjects(['common', 'timelion', 'header', 'settings']);
const PageObjects = getPageObjects(['common', 'timelion', 'settings', 'timePicker']);
describe('expression typeahead', () => {
before(async () => {
@ -28,9 +28,7 @@ export default function ({ getPageObjects }) {
const toTime = '2015-09-23 18:31:44.000';
await PageObjects.timelion.initTests();
await PageObjects.header.setAbsoluteRange(fromTime, toTime);
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime);
});
it('should display function suggestions filtered by function name', async () => {

View file

@ -24,7 +24,7 @@ export default function ({ getService, getPageObjects }) {
const inspector = getService('inspector');
const browser = getService('browser');
const retry = getService('retry');
const PageObjects = getPageObjects(['common', 'visualize', 'header', 'settings']);
const PageObjects = getPageObjects(['common', 'visualize', 'header', 'settings', 'timePicker']);
describe('area charts', function indexPatternCreation() {
const vizName1 = 'Visualization AreaChart Name Test';
@ -39,8 +39,7 @@ export default function ({ getService, getPageObjects }) {
await PageObjects.visualize.clickAreaChart();
log.debug('clickNewSearch');
await PageObjects.visualize.clickNewSearch();
log.debug('Set absolute time range from \"' + fromTime + '\" to \"' + toTime + '\"');
await PageObjects.header.setAbsoluteRange(fromTime, toTime);
await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime);
log.debug('Click X-Axis');
await PageObjects.visualize.clickBucket('X-Axis');
log.debug('Click Date Histogram');

View file

@ -25,7 +25,7 @@ export default function ({ getService, getPageObjects }) {
const retry = getService('retry');
const filterBar = getService('filterBar');
const renderable = getService('renderable');
const PageObjects = getPageObjects(['common', 'visualize', 'header']);
const PageObjects = getPageObjects(['common', 'visualize', 'header', 'timePicker']);
const fromTime = '2015-09-19 06:31:44.000';
const toTime = '2015-09-23 18:31:44.000';
@ -40,8 +40,7 @@ export default function ({ getService, getPageObjects }) {
await PageObjects.visualize.clickDataTable();
log.debug('clickNewSearch');
await PageObjects.visualize.clickNewSearch();
log.debug('Set absolute time range from \"' + fromTime + '\" to \"' + toTime + '\"');
await PageObjects.header.setAbsoluteRange(fromTime, toTime);
await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime);
log.debug('Bucket = Split Rows');
await PageObjects.visualize.clickBucket('Split Rows');
log.debug('Aggregation = Histogram');
@ -103,7 +102,7 @@ export default function ({ getService, getPageObjects }) {
await PageObjects.visualize.navigateToNewVisualization();
await PageObjects.visualize.clickDataTable();
await PageObjects.visualize.clickNewSearch();
await PageObjects.header.setAbsoluteRange(fromTime, toTime);
await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime);
await PageObjects.visualize.clickAddMetric();
await PageObjects.visualize.clickBucket('Metric', 'metric');
await PageObjects.visualize.selectAggregation('Average Bucket', 'metrics');
@ -119,7 +118,7 @@ export default function ({ getService, getPageObjects }) {
await PageObjects.visualize.navigateToNewVisualization();
await PageObjects.visualize.clickDataTable();
await PageObjects.visualize.clickNewSearch();
await PageObjects.header.setAbsoluteRange(fromTime, toTime);
await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime);
await PageObjects.visualize.clickBucket('Split Rows');
await PageObjects.visualize.selectAggregation('Date Histogram');
await PageObjects.visualize.selectField('@timestamp');
@ -139,7 +138,7 @@ export default function ({ getService, getPageObjects }) {
await PageObjects.visualize.navigateToNewVisualization();
await PageObjects.visualize.clickDataTable();
await PageObjects.visualize.clickNewSearch();
await PageObjects.header.setAbsoluteRange(fromTime, toTime);
await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime);
await PageObjects.visualize.clickBucket('Split Rows');
await PageObjects.visualize.selectAggregation('Date Histogram');
await PageObjects.visualize.selectField('@timestamp');
@ -178,7 +177,7 @@ export default function ({ getService, getPageObjects }) {
await PageObjects.visualize.navigateToNewVisualization();
await PageObjects.visualize.clickDataTable();
await PageObjects.visualize.clickNewSearch();
await PageObjects.header.setAbsoluteRange(fromTime, toTime);
await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime);
await PageObjects.visualize.clickMetricEditor();
await PageObjects.visualize.selectAggregation('Top Hit', 'metrics');
await PageObjects.visualize.selectField('agent.raw', 'metrics');
@ -192,7 +191,7 @@ export default function ({ getService, getPageObjects }) {
await PageObjects.visualize.navigateToNewVisualization();
await PageObjects.visualize.clickDataTable();
await PageObjects.visualize.clickNewSearch();
await PageObjects.header.setAbsoluteRange(fromTime, toTime);
await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime);
await PageObjects.visualize.clickBucket('Split Rows');
await PageObjects.visualize.selectAggregation('Range');
await PageObjects.visualize.selectField('bytes');
@ -210,7 +209,7 @@ export default function ({ getService, getPageObjects }) {
await PageObjects.visualize.navigateToNewVisualization();
await PageObjects.visualize.clickDataTable();
await PageObjects.visualize.clickNewSearch();
await PageObjects.header.setAbsoluteRange(fromTime, toTime);
await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime);
await PageObjects.visualize.clickBucket('Split Rows');
await PageObjects.visualize.selectAggregation('Terms');
await PageObjects.visualize.selectField('extension.raw');
@ -248,7 +247,7 @@ export default function ({ getService, getPageObjects }) {
await PageObjects.visualize.navigateToNewVisualization();
await PageObjects.visualize.clickDataTable();
await PageObjects.visualize.clickNewSearch();
await PageObjects.header.setAbsoluteRange(fromTime, toTime);
await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime);
await PageObjects.visualize.clickBucket('Split Rows');
await PageObjects.visualize.selectAggregation('Terms');
await PageObjects.visualize.selectField('extension.raw');
@ -326,7 +325,7 @@ export default function ({ getService, getPageObjects }) {
await PageObjects.visualize.navigateToNewVisualization();
await PageObjects.visualize.clickDataTable();
await PageObjects.visualize.clickNewSearch();
await PageObjects.header.setAbsoluteRange(fromTime, toTime);
await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime);
await PageObjects.visualize.clickBucket('Split Table');
await PageObjects.visualize.selectAggregation('Terms');
await PageObjects.visualize.selectField('extension.raw');

View file

@ -24,7 +24,7 @@ export default function ({ getService, getPageObjects }) {
const log = getService('log');
const renderable = getService('renderable');
const embedding = getService('embedding');
const PageObjects = getPageObjects(['common', 'visualize', 'header']);
const PageObjects = getPageObjects(['common', 'visualize', 'header', 'timePicker']);
const fromTime = '2015-09-19 06:31:44.000';
const toTime = '2015-09-23 18:31:44.000';
@ -36,7 +36,7 @@ export default function ({ getService, getPageObjects }) {
await PageObjects.visualize.navigateToNewVisualization();
await PageObjects.visualize.clickDataTable();
await PageObjects.visualize.clickNewSearch();
await PageObjects.header.setAbsoluteRange(fromTime, toTime);
await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime);
await PageObjects.visualize.clickBucket('Split Rows');
await PageObjects.visualize.selectAggregation('Date Histogram');
await PageObjects.visualize.selectField('@timestamp');

View file

@ -23,7 +23,7 @@ export default function ({ getService, getPageObjects }) {
const log = getService('log');
const retry = getService('retry');
const inspector = getService('inspector');
const PageObjects = getPageObjects(['common', 'visualize', 'header']);
const PageObjects = getPageObjects(['common', 'visualize', 'timePicker']);
describe('gauge chart', function indexPatternCreation() {
const fromTime = '2015-09-19 06:31:44.000';
@ -35,8 +35,7 @@ export default function ({ getService, getPageObjects }) {
log.debug('clickGauge');
await PageObjects.visualize.clickGauge();
await PageObjects.visualize.clickNewSearch();
log.debug('Set absolute time range from \"' + fromTime + '\" to \"' + toTime + '\"');
await PageObjects.header.setAbsoluteRange(fromTime, toTime);
await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime);
});

View file

@ -22,7 +22,7 @@ import expect from 'expect.js';
export default function ({ getService, getPageObjects }) {
const log = getService('log');
const inspector = getService('inspector');
const PageObjects = getPageObjects(['common', 'visualize', 'header']);
const PageObjects = getPageObjects(['common', 'visualize', 'timePicker']);
describe('heatmap chart', function indexPatternCreation() {
const vizName1 = 'Visualization HeatmapChart';
@ -35,8 +35,7 @@ export default function ({ getService, getPageObjects }) {
log.debug('clickHeatmapChart');
await PageObjects.visualize.clickHeatmapChart();
await PageObjects.visualize.clickNewSearch();
log.debug('Set absolute time range from \"' + fromTime + '\" to \"' + toTime + '\"');
await PageObjects.header.setAbsoluteRange(fromTime, toTime);
await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime);
log.debug('Bucket = X-Axis');
await PageObjects.visualize.clickBucket('X-Axis');
log.debug('Aggregation = Date Histogram');

View file

@ -22,7 +22,7 @@ import expect from 'expect.js';
export default function ({ getService, getPageObjects }) {
const log = getService('log');
const retry = getService('retry');
const PageObjects = getPageObjects(['common', 'visualize', 'header']);
const PageObjects = getPageObjects(['common', 'visualize', 'timePicker']);
describe('histogram agg onSearchRequestStart', function () {
before(async function () {
@ -34,8 +34,7 @@ export default function ({ getService, getPageObjects }) {
log.debug('clickDataTable');
await PageObjects.visualize.clickDataTable();
await PageObjects.visualize.clickNewSearch();
log.debug('Set absolute time range from \"' + fromTime + '\" to \"' + toTime + '\"');
await PageObjects.header.setAbsoluteRange(fromTime, toTime);
await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime);
log.debug('Bucket = Split Rows');
await PageObjects.visualize.clickBucket('Split Rows');
log.debug('Aggregation = Histogram');

View file

@ -21,7 +21,7 @@ import expect from 'expect.js';
export default function ({ getService, getPageObjects }) {
const filterBar = getService('filterBar');
const PageObjects = getPageObjects(['common', 'visualize', 'header']);
const PageObjects = getPageObjects(['common', 'visualize', 'header', 'timePicker']);
const testSubjects = getService('testSubjects');
const inspector = getService('inspector');
const find = getService('find');
@ -35,7 +35,7 @@ export default function ({ getService, getPageObjects }) {
await PageObjects.visualize.navigateToNewVisualization();
await PageObjects.visualize.clickInputControlVis();
// set time range to time with no documents - input controls do not use time filter be default
await PageObjects.header.setAbsoluteRange('2017-01-01', '2017-01-02');
await PageObjects.timePicker.setAbsoluteRange('2017-01-01 00:00:00.000', '2017-01-02 00:00:00.000');
await PageObjects.visualize.clickVisEditorTab('controls');
await PageObjects.visualize.addInputControl();
await comboBox.set('indexPatternSelect-0', 'logstash- ');
@ -168,8 +168,7 @@ export default function ({ getService, getPageObjects }) {
});
it('should re-create control when global time filter is updated', async () => {
await PageObjects.header.setAbsoluteRange('2015-01-01', '2016-01-01');
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.timePicker.setAbsoluteRange('2015-01-01 00:00:00.000', '2016-01-01 00:00:00.000');
// Expect control to have values for selected time filter
const menu = await comboBox.getOptionsList('listControlSelect0');

View file

@ -22,7 +22,7 @@ export default function ({ getService, getPageObjects }) {
const log = getService('log');
const inspector = getService('inspector');
const filterBar = getService('filterBar');
const PageObjects = getPageObjects(['common', 'visualize', 'header']);
const PageObjects = getPageObjects(['common', 'visualize', 'timePicker']);
describe('inspector', function describeIndexTests() {
before(async function () {
@ -33,8 +33,7 @@ export default function ({ getService, getPageObjects }) {
await PageObjects.visualize.clickVerticalBarChart();
await PageObjects.visualize.clickNewSearch();
log.debug('Set absolute time range from \"' + fromTime + '\" to \"' + toTime + '\"');
await PageObjects.header.setAbsoluteRange(fromTime, toTime);
await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime);
});
describe('inspector table', function indexPatternCreation() {

View file

@ -23,7 +23,7 @@ export default function ({ getService, getPageObjects }) {
const log = getService('log');
const inspector = getService('inspector');
const retry = getService('retry');
const PageObjects = getPageObjects(['common', 'visualize', 'header']);
const PageObjects = getPageObjects(['common', 'visualize', 'timePicker']);
describe('line charts', function () {
const vizName1 = 'Visualization LineChart';
@ -37,8 +37,7 @@ export default function ({ getService, getPageObjects }) {
log.debug('clickLineChart');
await PageObjects.visualize.clickLineChart();
await PageObjects.visualize.clickNewSearch();
log.debug('Set absolute time range from \"' + fromTime + '\" to \"' + toTime + '\"');
await PageObjects.header.setAbsoluteRange(fromTime, toTime);
await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime);
log.debug('Bucket = Split Chart');
await PageObjects.visualize.clickBucket('Split Chart');
log.debug('Aggregation = Terms');

View file

@ -22,7 +22,7 @@ import expect from 'expect.js';
export default function ({ getService, getPageObjects }) {
const filterBar = getService('filterBar');
const retry = getService('retry');
const PageObjects = getPageObjects(['common', 'discover', 'visualize', 'header']);
const PageObjects = getPageObjects(['common', 'discover', 'visualize', 'header', 'timePicker']);
describe('visualize app', function describeIndexTests() {
const fromTime = '2015-09-19 06:31:44.000';
@ -45,8 +45,7 @@ export default function ({ getService, getPageObjects }) {
await PageObjects.visualize.navigateToNewVisualization();
await PageObjects.visualize.clickDataTable();
await PageObjects.visualize.clickSavedSearch(savedSearchName);
await PageObjects.header.setAbsoluteRange(fromTime, toTime);
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime);
await retry.waitFor('wait for count to equal 9,109', async () => {
const data = await PageObjects.visualize.getTableVisData();
return data.trim() === '9,109';
@ -54,8 +53,7 @@ export default function ({ getService, getPageObjects }) {
});
it('should respect the time filter when linked to a saved search', async () => {
await PageObjects.header.setAbsoluteRange('2015-09-19 06:31:44.000', '2015-09-21 10:00:00.000');
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.timePicker.setAbsoluteRange('2015-09-19 06:31:44.000', '2015-09-21 10:00:00.000');
await retry.waitFor('wait for count to equal 3,950', async () => {
const data = await PageObjects.visualize.getTableVisData();
return data.trim() === '3,950';

View file

@ -23,7 +23,7 @@ export default function ({ getService, getPageObjects }) {
const log = getService('log');
const retry = getService('retry');
const inspector = getService('inspector');
const PageObjects = getPageObjects(['common', 'visualize', 'header']);
const PageObjects = getPageObjects(['common', 'visualize', 'timePicker']);
describe('metric chart', function () {
const fromTime = '2015-09-19 06:31:44.000';
@ -35,8 +35,7 @@ export default function ({ getService, getPageObjects }) {
log.debug('clickMetric');
await PageObjects.visualize.clickMetric();
await PageObjects.visualize.clickNewSearch();
log.debug('Set absolute time range from \"' + fromTime + '\" to \"' + toTime + '\"');
await PageObjects.header.setAbsoluteRange(fromTime, toTime);
await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime);
});
it('should have inspector enabled', async function () {

View file

@ -24,7 +24,7 @@ export default function ({ getService, getPageObjects }) {
const filterBar = getService('filterBar');
const pieChart = getService('pieChart');
const inspector = getService('inspector');
const PageObjects = getPageObjects(['common', 'visualize', 'header', 'settings']);
const PageObjects = getPageObjects(['common', 'visualize', 'header', 'settings', 'timePicker']);
const fromTime = '2015-09-19 06:31:44.000';
const toTime = '2015-09-23 18:31:44.000';
@ -36,8 +36,7 @@ export default function ({ getService, getPageObjects }) {
log.debug('clickPieChart');
await PageObjects.visualize.clickPieChart();
await PageObjects.visualize.clickNewSearch();
log.debug('Set absolute time range from \"' + fromTime + '\" to \"' + toTime + '\"');
await PageObjects.header.setAbsoluteRange(fromTime, toTime);
await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime);
log.debug('select bucket Split Slices');
await PageObjects.visualize.clickBucket('Split Slices');
log.debug('Click aggregation Histogram');
@ -86,8 +85,7 @@ export default function ({ getService, getPageObjects }) {
log.debug('clickPieChart');
await PageObjects.visualize.clickPieChart();
await PageObjects.visualize.clickNewSearch();
log.debug(`Set absolute time range from "${fromTime}" to "${toTime}"`);
await PageObjects.header.setAbsoluteRange(fromTime, toTime);
await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime);
log.debug('select bucket Split Slices');
await PageObjects.visualize.clickBucket('Split Slices');
log.debug('Click aggregation Terms');
@ -191,8 +189,7 @@ export default function ({ getService, getPageObjects }) {
log.debug('clickPieChart');
await PageObjects.visualize.clickPieChart();
await PageObjects.visualize.clickNewSearch();
log.debug('Set absolute time range from \"' + fromTime + '\" to \"' + toTime + '\"');
await PageObjects.header.setAbsoluteRange(fromTime, toTime);
await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime);
log.debug('select bucket Split Slices');
await PageObjects.visualize.clickBucket('Split Slices');
log.debug('Click aggregation Filters');
@ -207,7 +204,7 @@ export default function ({ getService, getPageObjects }) {
const emptyFromTime = '2016-09-19 06:31:44.000';
const emptyToTime = '2016-09-23 18:31:44.000';
log.debug('Switch to a different time range from \"' + emptyFromTime + '\" to \"' + emptyToTime + '\"');
await PageObjects.header.setAbsoluteRange(emptyFromTime, emptyToTime);
await PageObjects.timePicker.setAbsoluteRange(emptyFromTime, emptyToTime);
await PageObjects.visualize.waitForVisualization();
await PageObjects.visualize.expectError();
});
@ -220,7 +217,7 @@ export default function ({ getService, getPageObjects }) {
await PageObjects.visualize.clickPieChart();
await PageObjects.visualize.clickNewSearch();
log.debug('Set absolute time range from \"' + fromTime + '\" to \"' + toTime + '\"');
await PageObjects.header.setAbsoluteRange(fromTime, toTime);
await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime);
log.debug('select bucket Split Slices');
await PageObjects.visualize.clickBucket('Split Slices');
log.debug('Click aggregation Histogram');
@ -281,8 +278,7 @@ export default function ({ getService, getPageObjects }) {
log.debug('clickPieChart');
await PageObjects.visualize.clickPieChart();
await PageObjects.visualize.clickNewSearch();
log.debug('Set absolute time range from \"' + fromTime + '\" to \"' + toTime + '\"');
await PageObjects.header.setAbsoluteRange(fromTime, toTime);
await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime);
log.debug('select bucket Split Slices');
await PageObjects.visualize.clickBucket('Split Slices');
log.debug('Click aggregation Filters');
@ -311,8 +307,7 @@ export default function ({ getService, getPageObjects }) {
await PageObjects.visualize.navigateToNewVisualization();
await PageObjects.visualize.clickPieChart();
await PageObjects.visualize.clickNewSearch();
log.debug('Set absolute time range from \"' + fromTime + '\" to \"' + toTime + '\"');
await PageObjects.header.setAbsoluteRange(fromTime, toTime);
await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime);
log.debug('select bucket Split Slices');
await PageObjects.visualize.clickBucket('Split Chart');
await PageObjects.visualize.selectAggregation('Terms');

View file

@ -22,7 +22,7 @@ import expect from 'expect.js';
export default function ({ getService, getPageObjects }) {
const log = getService('log');
const retry = getService('retry');
const PageObjects = getPageObjects(['common', 'visualize', 'header', 'pointSeries']);
const PageObjects = getPageObjects(['common', 'visualize', 'header', 'pointSeries', 'timePicker']);
const pointSeriesVis = PageObjects.pointSeries;
describe('point series', function describeIndexTests() {
@ -35,8 +35,7 @@ export default function ({ getService, getPageObjects }) {
log.debug('clickLineChart');
await PageObjects.visualize.clickLineChart();
await PageObjects.visualize.clickNewSearch();
log.debug('Set absolute time range from \"' + fromTime + '\" to \"' + toTime + '\"');
await PageObjects.header.setAbsoluteRange(fromTime, toTime);
await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime);
log.debug('Bucket = X-Axis');
await PageObjects.visualize.clickBucket('X-Axis');
log.debug('Aggregation = Date Histogram');

View file

@ -29,7 +29,7 @@ export default function ({ getService, getPageObjects }) {
const inspector = getService('inspector');
const log = getService('log');
const find = getService('find');
const PageObjects = getPageObjects(['common', 'visualize', 'header', 'settings']);
const PageObjects = getPageObjects(['common', 'visualize', 'timePicker', 'settings']);
before(async function () {
@ -38,8 +38,7 @@ export default function ({ getService, getPageObjects }) {
log.debug('clickRegionMap');
await PageObjects.visualize.clickRegionMap();
await PageObjects.visualize.clickNewSearch();
log.debug('Set absolute time range from \"' + fromTime + '\" to \"' + toTime + '\"');
await PageObjects.header.setAbsoluteRange(fromTime, toTime);
await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime);
log.debug('Bucket = shape field');
await PageObjects.visualize.clickBucket('shape field');
log.debug('Aggregation = Terms');

View file

@ -26,7 +26,7 @@ export default function ({ getService, getPageObjects }) {
const browser = getService('browser');
const retry = getService('retry');
const find = getService('find');
const PageObjects = getPageObjects(['common', 'visualize', 'header', 'settings']);
const PageObjects = getPageObjects(['common', 'visualize', 'header', 'settings', 'timePicker']);
describe('tag cloud chart', function () {
const vizName1 = 'Visualization tagCloud';
@ -40,8 +40,7 @@ export default function ({ getService, getPageObjects }) {
log.debug('clickTagCloud');
await PageObjects.visualize.clickTagCloud();
await PageObjects.visualize.clickNewSearch();
log.debug('Set absolute time range from \"' + fromTime + '\" to \"' + toTime + '\"');
await PageObjects.header.setAbsoluteRange(fromTime, toTime);
await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime);
log.debug('select Tags');
await PageObjects.visualize.clickBucket('Tags');
log.debug('Click aggregation Terms');
@ -140,7 +139,7 @@ export default function ({ getService, getPageObjects }) {
await PageObjects.common.navigateToApp('visualize');
await PageObjects.visualize.loadSavedVisualization(vizName1, { navigateToVisualize: false });
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.header.setAbsoluteRange(fromTime, toTime);
await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime);
await PageObjects.visualize.waitForVisualization();
});

View file

@ -26,7 +26,7 @@ export default function ({ getService, getPageObjects }) {
const find = getService('find');
const testSubjects = getService('testSubjects');
const browser = getService('browser');
const PageObjects = getPageObjects(['common', 'visualize', 'header', 'settings']);
const PageObjects = getPageObjects(['common', 'visualize', 'timePicker', 'settings']);
describe('tile map visualize app', function () {
@ -44,8 +44,7 @@ export default function ({ getService, getPageObjects }) {
log.debug('clickTileMap');
await PageObjects.visualize.clickTileMap();
await PageObjects.visualize.clickNewSearch();
log.debug('Set absolute time range from \"' + fromTime + '\" to \"' + toTime + '\"');
await PageObjects.header.setAbsoluteRange(fromTime, toTime);
await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime);
//do not configure aggs
});
@ -70,8 +69,7 @@ export default function ({ getService, getPageObjects }) {
log.debug('clickTileMap');
await PageObjects.visualize.clickTileMap();
await PageObjects.visualize.clickNewSearch();
log.debug('Set absolute time range from \"' + fromTime + '\" to \"' + toTime + '\"');
await PageObjects.header.setAbsoluteRange(fromTime, toTime);
await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime);
log.debug('select bucket Geo Coordinates');
await PageObjects.visualize.clickBucket('Geo Coordinates');
log.debug('Click aggregation Geohash');

View file

@ -26,7 +26,7 @@ export default function ({ getService, getPageObjects }) {
const retry = getService('retry');
const kibanaServer = getService('kibanaServer');
const testSubjects = getService('testSubjects');
const PageObjects = getPageObjects(['common', 'visualize', 'header', 'settings', 'visualBuilder']);
const PageObjects = getPageObjects(['common', 'visualize', 'header', 'settings', 'visualBuilder', 'timePicker']);
describe('visual builder', function describeIndexTests() {
@ -36,8 +36,11 @@ export default function ({ getService, getPageObjects }) {
});
it('should show the correct count in the legend', async function () {
const actualCount = await PageObjects.visualBuilder.getRhythmChartLegendValue();
expect(actualCount).to.be('156');
await retry.try(async () => {
await PageObjects.header.waitUntilLoadingHasFinished();
const actualCount = await PageObjects.visualBuilder.getRhythmChartLegendValue();
expect(actualCount).to.be('156');
});
});
it('should show the correct count in the legend with 2h offset', async function () {
@ -134,11 +137,13 @@ export default function ({ getService, getPageObjects }) {
});
it('should verify topN label and count display', async function () {
await PageObjects.visualize.waitForVisualization();
const labelString = await PageObjects.visualBuilder.getTopNLabel();
expect(labelString).to.be('Count');
const gaugeCount = await PageObjects.visualBuilder.getTopNCount();
expect(gaugeCount).to.be('156');
await retry.try(async () => {
await PageObjects.visualize.waitForVisualization();
const labelString = await PageObjects.visualBuilder.getTopNLabel();
expect(labelString).to.be('Count');
const gaugeCount = await PageObjects.visualBuilder.getTopNCount();
expect(gaugeCount).to.be('156');
});
});
});
@ -149,13 +154,15 @@ export default function ({ getService, getPageObjects }) {
before(async () => {
await PageObjects.visualBuilder.resetPage();
await PageObjects.visualBuilder.clickMarkdown();
await PageObjects.header.setAbsoluteRange('2015-09-22 06:00:00.000', '2015-09-22 11:00:00.000');
await PageObjects.timePicker.setAbsoluteRange('2015-09-22 06:00:00.000', '2015-09-22 11:00:00.000');
});
it('should allow printing raw timestamp of data', async () => {
await PageObjects.visualBuilder.enterMarkdown('{{ count.data.raw.[0].[0] }}');
const text = await PageObjects.visualBuilder.getMarkdownText();
expect(text).to.be('1442901600000');
await retry.try(async () => {
await PageObjects.visualBuilder.enterMarkdown('{{ count.data.raw.[0].[0] }}');
const text = await PageObjects.visualBuilder.getMarkdownText();
expect(text).to.be('1442901600000');
});
});
it('should allow printing raw value of data', async () => {
@ -196,7 +203,7 @@ export default function ({ getService, getPageObjects }) {
before(async () => {
await PageObjects.visualBuilder.resetPage();
await PageObjects.visualBuilder.clickTable();
await PageObjects.header.setAbsoluteRange('2015-09-22 06:00:00.000', '2015-09-22 11:00:00.000');
await PageObjects.timePicker.setAbsoluteRange('2015-09-22 06:00:00.000', '2015-09-22 11:00:00.000');
log.debug('clicked on Table');
});
@ -233,8 +240,7 @@ export default function ({ getService, getPageObjects }) {
await PageObjects.visualBuilder.clickMetricPanelOptions();
const fromTime = '2018-10-22 00:00:00.000';
const toTime = '2018-10-28 23:59:59.999';
log.debug('Set absolute time range from \"' + fromTime + '\" to \"' + toTime + '\"');
await PageObjects.header.setAbsoluteRange(fromTime, toTime);
await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime);
await PageObjects.visualBuilder.setIndexPatternValue('kibana_sample_data_flights');
await PageObjects.visualBuilder.selectIndexPatternTimeField('timestamp');
const newValue = await PageObjects.visualBuilder.getMetricValue();

View file

@ -24,7 +24,7 @@ export default function ({ getService, getPageObjects }) {
const retry = getService('retry');
const inspector = getService('inspector');
const filterBar = getService('filterBar');
const PageObjects = getPageObjects(['common', 'visualize', 'header']);
const PageObjects = getPageObjects(['common', 'visualize', 'header', 'timePicker']);
describe('vertical bar chart', function () {
const fromTime = '2015-09-19 06:31:44.000';
@ -37,8 +37,7 @@ export default function ({ getService, getPageObjects }) {
log.debug('clickVerticalBarChart');
await PageObjects.visualize.clickVerticalBarChart();
await PageObjects.visualize.clickNewSearch();
log.debug('Set absolute time range from \"' + fromTime + '\" to \"' + toTime + '\"');
await PageObjects.header.setAbsoluteRange(fromTime, toTime);
await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime);
log.debug('Bucket = X-Axis');
await PageObjects.visualize.clickBucket('X-Axis');
log.debug('Aggregation = Date Histogram');
@ -113,7 +112,7 @@ export default function ({ getService, getPageObjects }) {
const fromTime = '2015-09-20 06:31:44.000';
const toTime = '2015-09-22 18:31:44.000';
await PageObjects.header.setAbsoluteRange(fromTime, toTime);
await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime);
let expectedChartValues = [
82, 218, 341, 440, 480, 517, 522, 446, 403, 321, 258, 172, 95, 55, 38, 24, 3, 4,

View file

@ -35,7 +35,7 @@ export function DashboardPageProvider({ getService, getPageObjects }) {
const testSubjects = getService('testSubjects');
const dashboardAddPanel = getService('dashboardAddPanel');
const renderable = getService('renderable');
const PageObjects = getPageObjects(['common', 'header', 'settings', 'visualize']);
const PageObjects = getPageObjects(['common', 'header', 'settings', 'visualize', 'timePicker']);
const defaultFindTimeout = config.get('timeouts.find');
@ -495,19 +495,19 @@ export function DashboardPageProvider({ getService, getPageObjects }) {
async setTimepickerInHistoricalDataRange() {
const fromTime = '2015-09-19 06:31:44.000';
const toTime = '2015-09-23 18:31:44.000';
await PageObjects.header.setAbsoluteRange(fromTime, toTime);
await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime);
}
async setTimepickerInDataRange() {
const fromTime = '2018-01-01 00:00:00.000';
const toTime = '2018-04-13 00:00:00.000';
await PageObjects.header.setAbsoluteRange(fromTime, toTime);
await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime);
}
async setTimepickerInLogstashDataRange() {
const fromTime = '2018-04-09 00:00:00.000';
const toTime = '2018-04-13 00:00:00.000';
await PageObjects.header.setAbsoluteRange(fromTime, toTime);
await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime);
}
async setSaveAsNewCheckBox(checked) {

View file

@ -218,15 +218,8 @@ export function DiscoverPageProvider({ getService, getPageObjects }) {
return await testSubjects.exists('discoverNoResults');
}
async getNoResultsTimepicker() {
return await testSubjects.find('discoverNoResultsTimefilter');
}
hasNoResultsTimepicker() {
return this
.getNoResultsTimepicker()
.then(() => true)
.catch(() => false);
async hasNoResultsTimepicker() {
return await testSubjects.exists('discoverNoResultsTimefilter');
}
async clickFieldListItem(field) {

View file

@ -65,145 +65,6 @@ export function HeaderPageProvider({ getService, getPageObjects }) {
await this.awaitGlobalLoadingIndicatorHidden();
}
async clickTimepicker() {
await testSubjects.click('globalTimepickerButton');
}
async clickQuickButton() {
await retry.try(async () => {
await testSubjects.click('timepicker-quick-button');
});
}
async isTimepickerOpen() {
return await testSubjects.exists('timePicker');
}
async isAbsoluteSectionShowing() {
log.debug('isAbsoluteSectionShowing');
return await find.existsByCssSelector('input[ng-model=\'absolute.from\']');
}
async showAbsoluteSection() {
log.debug('showAbsoluteSection');
const isAbsoluteSectionShowing = await this.isAbsoluteSectionShowing();
if (!isAbsoluteSectionShowing) {
await retry.try(async () => {
await testSubjects.click('timepicker-absolute-button');
// Check to make sure one of the elements on the absolute section is showing.
await this.getFromTime();
});
}
}
async getFromTime() {
log.debug('getFromTime');
return await retry.try(async () => {
await this.ensureTimePickerIsOpen();
await this.showAbsoluteSection();
const element = await find.byCssSelector('input[ng-model=\'absolute.from\']');
return await element.getProperty('value');
});
}
async getToTime() {
log.debug('getToTime');
return await retry.try(async () => {
await this.ensureTimePickerIsOpen();
await this.showAbsoluteSection();
const element = await find.byCssSelector('input[ng-model=\'absolute.to\']');
return await element.getProperty('value');
});
}
async setFromTime(timeString) {
log.debug(`setFromTime: ${timeString}`);
await retry.try(async () => {
await this.ensureTimePickerIsOpen();
await this.showAbsoluteSection();
await find.setValue('input[ng-model=\'absolute.from\']', timeString);
});
}
async setToTime(timeString) {
log.debug(`setToTime: ${timeString}`);
await retry.try(async () => {
await this.ensureTimePickerIsOpen();
await this.showAbsoluteSection();
await find.setValue('input[ng-model=\'absolute.to\']', timeString);
});
}
async clickGoButton() {
log.debug('clickGoButton');
await retry.try(async () => {
await testSubjects.click('timepickerGoButton');
await this.waitUntilLoadingHasFinished();
});
}
async ensureTimePickerIsOpen() {
log.debug('ensureTimePickerIsOpen');
const isOpen = await this.isTimepickerOpen();
if (!isOpen) {
await retry.try(async () => {
await this.clickTimepicker();
const isOpen = await this.isTimepickerOpen();
if (!isOpen) throw new Error('Time picker still not open, try again.');
});
}
}
async setAbsoluteRange(fromTime, toTime) {
log.debug(`Setting absolute range to ${fromTime} to ${toTime}`);
await this.ensureTimePickerIsOpen();
log.debug('--Clicking Absolute button');
await this.showAbsoluteSection();
log.debug('--Setting From Time : ' + fromTime);
await this.setFromTime(fromTime);
log.debug('--Setting To Time : ' + toTime);
await this.setToTime(toTime);
await this.clickGoButton();
await this.awaitGlobalLoadingIndicatorHidden();
}
async setQuickTime(quickTime) {
await this.ensureTimePickerIsOpen();
log.debug('--Clicking Quick button');
await this.clickQuickButton();
await find.clickByLinkText(quickTime);
}
async getAutoRefreshState() {
return testSubjects.getAttribute('globalTimepickerAutoRefreshButton', 'data-test-subj-state');
}
async getRefreshConfig() {
const refreshState = await testSubjects.getAttribute('globalTimepickerAutoRefreshButton', 'data-test-subj-state');
const refreshConfig = await testSubjects.getVisibleText('globalRefreshButton');
return `${refreshState} ${refreshConfig}`;
}
// check if the auto refresh state is active and to pause it
async pauseAutoRefresh() {
let result = false;
if ((await this.getAutoRefreshState()) === 'active') {
await testSubjects.click('globalTimepickerAutoRefreshButton');
result = true;
}
return result;
}
// check if the auto refresh state is inactive and to resume it
async resumeAutoRefresh() {
let result = false;
if ((await this.getAutoRefreshState()) === 'inactive') {
await testSubjects.click('globalTimepickerAutoRefreshButton');
result = true;
}
return result;
}
async getToastMessage(findTimeout = defaultFindTimeout) {
const toastMessage = await find.displayedByCssSelector(
'kbn-truncated.kbnToast__message',
@ -249,13 +110,6 @@ export function HeaderPageProvider({ getService, getPageObjects }) {
await testSubjects.find('kibanaChrome', defaultFindTimeout * 10);
}
async getPrettyDuration() {
return await testSubjects.getVisibleText('globalTimepickerRange');
}
async isSharedTimefilterEnabled() {
return await find.existsByCssSelector('[shared-timefilter=true]');
}
}
return new HeaderPage();

Some files were not shown because too many files have changed in this diff Show more