@@ -168,8 +172,8 @@
class="btn btn-primary"
ng-if="user.isDisabled"
ng-click="disableUser($event)"
- bs-tooltip="user.authModule ? 'External user cannot be activated or deactivated' : ''"
- ng-class="{'disabled': user.authModule}"
+ bs-tooltip="user.isExternal ? 'External user cannot be enabled or disabled' : ''"
+ ng-class="{'disabled': user.isExternal}"
>
Enable
diff --git a/public/app/features/admin/partials/users.html b/public/app/features/admin/partials/users.html
index da33deeef0c..21346b92aa7 100644
--- a/public/app/features/admin/partials/users.html
+++ b/public/app/features/admin/partials/users.html
@@ -55,7 +55,9 @@
- LDAP
+
+ {{user.authLabel}}
+
|
Disabled
diff --git a/public/app/features/dashboard/components/ExportDataModal/ExportDataModalCtrl.ts b/public/app/features/dashboard/components/ExportDataModal/ExportDataModalCtrl.ts
index f87daa94ee7..023b60ac074 100644
--- a/public/app/features/dashboard/components/ExportDataModal/ExportDataModalCtrl.ts
+++ b/public/app/features/dashboard/components/ExportDataModal/ExportDataModalCtrl.ts
@@ -1,6 +1,7 @@
import angular from 'angular';
import * as fileExport from 'app/core/utils/file_export';
import appEvents from 'app/core/app_events';
+import { DashboardSrv } from 'app/features/dashboard/services/DashboardSrv';
export class ExportDataModalCtrl {
private data: any[];
@@ -9,14 +10,23 @@ export class ExportDataModalCtrl {
dateTimeFormat = 'YYYY-MM-DDTHH:mm:ssZ';
excel = false;
+ /** @ngInject */
+ constructor(private dashboardSrv: DashboardSrv) {}
+
export() {
+ const timezone = this.dashboardSrv.getCurrent().timezone;
+ const options = {
+ excel: this.excel,
+ dateTimeFormat: this.dateTimeFormat,
+ timezone,
+ };
if (this.panel === 'table') {
fileExport.exportTableDataToCsv(this.data, this.excel);
} else {
if (this.asRows) {
- fileExport.exportSeriesListToCsv(this.data, this.dateTimeFormat, this.excel);
+ fileExport.exportSeriesListToCsv(this.data, options);
} else {
- fileExport.exportSeriesListToCsvColumns(this.data, this.dateTimeFormat, this.excel);
+ fileExport.exportSeriesListToCsvColumns(this.data, options);
}
}
diff --git a/public/app/features/dashboard/panel_editor/QueryOptions.tsx b/public/app/features/dashboard/panel_editor/QueryOptions.tsx
index e47b12cdb55..a10ec31fae7 100644
--- a/public/app/features/dashboard/panel_editor/QueryOptions.tsx
+++ b/public/app/features/dashboard/panel_editor/QueryOptions.tsx
@@ -2,12 +2,19 @@
import React, { PureComponent, ChangeEvent, FocusEvent } from 'react';
// Utils
-import { isValidTimeSpan } from '@grafana/data/src/utils/rangeutil';
+import { rangeUtil } from '@grafana/data';
// Components
-import { DataSourceSelectItem, EventsWithValidation, Input, InputStatus, Switch, ValidationEvents } from '@grafana/ui';
+import {
+ DataSourceSelectItem,
+ EventsWithValidation,
+ Input,
+ InputStatus,
+ Switch,
+ ValidationEvents,
+ FormLabel,
+} from '@grafana/ui';
import { DataSourceOption } from './DataSourceOption';
-import { FormLabel } from '@grafana/ui';
// Types
import { PanelModel } from '../state';
@@ -19,7 +26,7 @@ const timeRangeValidationEvents: ValidationEvents = {
if (!value) {
return true;
}
- return isValidTimeSpan(value);
+ return rangeUtil.isValidTimeSpan(value);
},
errorMessage: 'Not a valid timespan',
},
diff --git a/public/app/features/plugins/built_in_plugins.ts b/public/app/features/plugins/built_in_plugins.ts
index 4429085fcff..913f8e9a36f 100644
--- a/public/app/features/plugins/built_in_plugins.ts
+++ b/public/app/features/plugins/built_in_plugins.ts
@@ -22,6 +22,7 @@ import * as graphPanel from 'app/plugins/panel/graph/module';
import * as dashListPanel from 'app/plugins/panel/dashlist/module';
import * as pluginsListPanel from 'app/plugins/panel/pluginlist/module';
import * as alertListPanel from 'app/plugins/panel/alertlist/module';
+import * as annoListPanel from 'app/plugins/panel/annolist/module';
import * as heatmapPanel from 'app/plugins/panel/heatmap/module';
import * as tablePanel from 'app/plugins/panel/table/module';
import * as table2Panel from 'app/plugins/panel/table2/module';
@@ -59,6 +60,7 @@ const builtInPlugins = {
'app/plugins/panel/dashlist/module': dashListPanel,
'app/plugins/panel/pluginlist/module': pluginsListPanel,
'app/plugins/panel/alertlist/module': alertListPanel,
+ 'app/plugins/panel/annolist/module': annoListPanel,
'app/plugins/panel/heatmap/module': heatmapPanel,
'app/plugins/panel/table/module': tablePanel,
'app/plugins/panel/table2/module': table2Panel,
diff --git a/public/app/partials/login.html b/public/app/partials/login.html
index 8acc0527fdb..78ff178c151 100644
--- a/public/app/partials/login.html
+++ b/public/app/partials/login.html
@@ -45,6 +45,10 @@
+
+
+ Sign in with SAML
+
diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/editor/KustoQueryField.tsx b/public/app/plugins/datasource/grafana-azure-monitor-datasource/editor/KustoQueryField.tsx
index fc4d1a44822..c49718056af 100644
--- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/editor/KustoQueryField.tsx
+++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/editor/KustoQueryField.tsx
@@ -4,7 +4,7 @@ import Plain from 'slate-plain-serializer';
import QueryField from './query_field';
import debounce from 'lodash/debounce';
-import { getNextCharacter } from 'app/features/explore/utils/dom';
+import { DOMUtil } from '@grafana/ui';
import { KEYWORDS, functionTokens, operatorTokens, grafanaMacros } from './kusto/kusto';
// import '../sass/editor.base.scss';
@@ -203,7 +203,7 @@ export default class KustoQueryField extends QueryField {
// Modify suggestion based on context
- const nextChar = getNextCharacter();
+ const nextChar = DOMUtil.getNextCharacter();
if (suggestion.type === 'function') {
if (!nextChar || nextChar !== '(') {
suggestionText += '(';
diff --git a/public/app/plugins/datasource/loki/components/LokiQueryFieldForm.tsx b/public/app/plugins/datasource/loki/components/LokiQueryFieldForm.tsx
index 8646a7c6849..95d1b3d4cb3 100644
--- a/public/app/plugins/datasource/loki/components/LokiQueryFieldForm.tsx
+++ b/public/app/plugins/datasource/loki/components/LokiQueryFieldForm.tsx
@@ -10,13 +10,12 @@ import QueryField, { TypeaheadInput, QueryFieldState } from 'app/features/explor
// Utils & Services
// dom also includes Element polyfills
-import { getNextCharacter, getPreviousCousin } from 'app/features/explore/utils/dom';
import BracesPlugin from 'app/features/explore/slate-plugins/braces';
// Types
import { LokiQuery } from '../types';
import { TypeaheadOutput, HistoryItem } from 'app/types/explore';
-import { DataSourceApi, ExploreQueryFieldProps, DataSourceStatus } from '@grafana/ui';
+import { DataSourceApi, ExploreQueryFieldProps, DataSourceStatus, DOMUtil } from '@grafana/ui';
import { AbsoluteTimeRange } from '@grafana/data';
function getChooserText(hasSyntax: boolean, hasLogLabels: boolean, datasourceStatus: DataSourceStatus) {
@@ -36,7 +35,7 @@ function willApplySuggestion(suggestion: string, { typeaheadContext, typeaheadTe
// Modify suggestion based on context
switch (typeaheadContext) {
case 'context-labels': {
- const nextChar = getNextCharacter();
+ const nextChar = DOMUtil.getNextCharacter();
if (!nextChar || nextChar === '}' || nextChar === ',') {
suggestion += '=';
}
@@ -48,7 +47,7 @@ function willApplySuggestion(suggestion: string, { typeaheadContext, typeaheadTe
if (!typeaheadText.match(/^(!?=~?"|")/)) {
suggestion = `"${suggestion}`;
}
- if (getNextCharacter() !== '"') {
+ if (DOMUtil.getNextCharacter() !== '"') {
suggestion = `${suggestion}"`;
}
break;
@@ -130,9 +129,9 @@ export class LokiQueryFieldForm extends React.PureComponent ({ label });
diff --git a/public/app/plugins/datasource/prometheus/components/PromQueryField.tsx b/public/app/plugins/datasource/prometheus/components/PromQueryField.tsx
index c50fdb5d39b..729d6a8a00e 100644
--- a/public/app/plugins/datasource/prometheus/components/PromQueryField.tsx
+++ b/public/app/plugins/datasource/prometheus/components/PromQueryField.tsx
@@ -10,12 +10,11 @@ import Prism from 'prismjs';
import { TypeaheadOutput, HistoryItem } from 'app/types/explore';
// dom also includes Element polyfills
-import { getNextCharacter, getPreviousCousin } from 'app/features/explore/utils/dom';
import BracesPlugin from 'app/features/explore/slate-plugins/braces';
import QueryField, { TypeaheadInput, QueryFieldState } from 'app/features/explore/QueryField';
import { PromQuery, PromContext, PromOptions } from '../types';
import { CancelablePromise, makePromiseCancelable } from 'app/core/utils/CancelablePromise';
-import { ExploreQueryFieldProps, DataSourceStatus, QueryHint } from '@grafana/ui';
+import { ExploreQueryFieldProps, DataSourceStatus, QueryHint, DOMUtil } from '@grafana/ui';
import { isDataFrame, toLegacyResponseData } from '@grafana/data';
import { PrometheusDatasource } from '../datasource';
@@ -73,7 +72,7 @@ export function willApplySuggestion(suggestion: string, { typeaheadContext, type
// Modify suggestion based on context
switch (typeaheadContext) {
case 'context-labels': {
- const nextChar = getNextCharacter();
+ const nextChar = DOMUtil.getNextCharacter();
if (!nextChar || nextChar === '}' || nextChar === ',') {
suggestion += '=';
}
@@ -85,7 +84,7 @@ export function willApplySuggestion(suggestion: string, { typeaheadContext, type
if (!typeaheadText.match(/^(!?=~?"|")/)) {
suggestion = `"${suggestion}`;
}
- if (getNextCharacter() !== '"') {
+ if (DOMUtil.getNextCharacter() !== '"') {
suggestion = `${suggestion}"`;
}
break;
@@ -282,9 +281,9 @@ class PromQueryField extends React.PureComponent, State> {
+ constructor(props: PanelEditorProps) {
+ super(props);
+
+ this.state = {
+ tag: '',
+ };
+ }
+
+ // Display
+ //-----------
+
+ onToggleShowUser = () =>
+ this.props.onOptionsChange({ ...this.props.options, showUser: !this.props.options.showUser });
+
+ onToggleShowTime = () =>
+ this.props.onOptionsChange({ ...this.props.options, showTime: !this.props.options.showTime });
+
+ onToggleShowTags = () =>
+ this.props.onOptionsChange({ ...this.props.options, showTags: !this.props.options.showTags });
+
+ // Navigate
+ //-----------
+
+ onNavigateBeforeChange = (event: ChangeEvent) => {
+ this.props.onOptionsChange({ ...this.props.options, navigateBefore: event.target.value });
+ };
+
+ onNavigateAfterChange = (event: ChangeEvent) => {
+ this.props.onOptionsChange({ ...this.props.options, navigateAfter: event.target.value });
+ };
+
+ onToggleNavigateToPanel = () =>
+ this.props.onOptionsChange({ ...this.props.options, navigateToPanel: !this.props.options.navigateToPanel });
+
+ // Search
+ //-----------
+ onLimitChange = (event: ChangeEvent) => {
+ const v = toIntegerOrUndefined(event.target.value);
+ this.props.onOptionsChange({ ...this.props.options, limit: v });
+ };
+
+ onToggleOnlyFromThisDashboard = () =>
+ this.props.onOptionsChange({
+ ...this.props.options,
+ onlyFromThisDashboard: !this.props.options.onlyFromThisDashboard,
+ });
+
+ onToggleOnlyInTimeRange = () =>
+ this.props.onOptionsChange({ ...this.props.options, onlyInTimeRange: !this.props.options.onlyInTimeRange });
+
+ // Tags
+ //-----------
+
+ onTagTextChange = (event: ChangeEvent) => {
+ this.setState({ tag: event.target.value });
+ };
+
+ onTagClick = (e: React.SyntheticEvent, tag: string) => {
+ e.stopPropagation();
+
+ const tags = this.props.options.tags.filter(item => item !== tag);
+ this.props.onOptionsChange({
+ ...this.props.options,
+ tags,
+ });
+ };
+
+ renderTags = (tags: string[]): JSX.Element => {
+ if (!tags || !tags.length) {
+ return null;
+ }
+ return (
+ <>
+ {tags.map(tag => {
+ return (
+ this.onTagClick(e, tag)} className="pointer">
+
+
+ );
+ })}
+ >
+ );
+ };
+
+ render() {
+ const { options } = this.props;
+ const labelWidth = 8;
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Tags
+ {this.renderTags(options.tags)}
+ {
+ if (this.state.tag && ev.key === 'Enter') {
+ const tags = [...options.tags, this.state.tag];
+ this.props.onOptionsChange({
+ ...this.props.options,
+ tags,
+ });
+ this.setState({ tag: '' });
+ ev.preventDefault();
+ }
+ }}
+ />
+
+
+
+
+
+ );
+ }
+}
diff --git a/public/app/plugins/panel/annolist/AnnoListPanel.tsx b/public/app/plugins/panel/annolist/AnnoListPanel.tsx
new file mode 100644
index 00000000000..b98645ddba0
--- /dev/null
+++ b/public/app/plugins/panel/annolist/AnnoListPanel.tsx
@@ -0,0 +1,304 @@
+// Libraries
+import React, { PureComponent } from 'react';
+
+// Types
+import { AnnoOptions } from './types';
+import { dateTime, DurationUnit, AnnotationEvent } from '@grafana/data';
+import { PanelProps, Tooltip } from '@grafana/ui';
+import { getBackendSrv } from 'app/core/services/backend_srv';
+import { AbstractList } from '@grafana/ui/src/components/List/AbstractList';
+import { TagBadge } from 'app/core/components/TagFilter/TagBadge';
+import { getDashboardSrv } from 'app/features/dashboard/services/DashboardSrv';
+import appEvents from 'app/core/app_events';
+
+import { updateLocation } from 'app/core/actions';
+import { store } from 'app/store/store';
+import { cx, css } from 'emotion';
+
+interface UserInfo {
+ id: number;
+ login: string;
+ email: string;
+}
+
+interface Props extends PanelProps {}
+interface State {
+ annotations: AnnotationEvent[];
+ timeInfo: string;
+ loaded: boolean;
+ queryUser?: UserInfo;
+ queryTags: string[];
+}
+
+export class AnnoListPanel extends PureComponent {
+ constructor(props: Props) {
+ super(props);
+
+ this.state = {
+ annotations: [],
+ timeInfo: '',
+ loaded: false,
+ queryTags: [],
+ };
+ }
+
+ componentDidMount() {
+ this.doSearch();
+ }
+
+ componentDidUpdate(prevProps: Props, prevState: State) {
+ const { options, timeRange } = this.props;
+ const needsQuery =
+ options !== prevProps.options ||
+ this.state.queryTags !== prevState.queryTags ||
+ this.state.queryUser !== prevState.queryUser ||
+ timeRange !== prevProps.timeRange;
+
+ if (needsQuery) {
+ this.doSearch();
+ }
+ }
+
+ async doSearch() {
+ // http://docs.grafana.org/http_api/annotations/
+ // https://github.com/grafana/grafana/blob/master/public/app/core/services/backend_srv.ts
+ // https://github.com/grafana/grafana/blob/master/public/app/features/annotations/annotations_srv.ts
+
+ const { options } = this.props;
+ const { queryUser, queryTags } = this.state;
+
+ const params: any = {
+ tags: options.tags,
+ limit: options.limit,
+ type: 'annotation', // Skip the Annotations that are really alerts. (Use the alerts panel!)
+ };
+
+ if (options.onlyFromThisDashboard) {
+ params.dashboardId = getDashboardSrv().getCurrent().id;
+ }
+
+ let timeInfo = '';
+ if (options.onlyInTimeRange) {
+ const { timeRange } = this.props;
+ params.from = timeRange.from.valueOf();
+ params.to = timeRange.to.valueOf();
+ } else {
+ timeInfo = 'All Time';
+ }
+
+ if (queryUser) {
+ params.userId = queryUser.id;
+ }
+
+ if (options.tags && options.tags.length) {
+ params.tags = options.tags;
+ }
+
+ if (queryTags.length) {
+ params.tags = params.tags ? [...params.tags, ...queryTags] : queryTags;
+ }
+
+ const annotations = await getBackendSrv().get('/api/annotations', params);
+ this.setState({
+ annotations,
+ timeInfo,
+ loaded: true,
+ });
+ }
+
+ onAnnoClick = (e: React.SyntheticEvent, anno: AnnotationEvent) => {
+ e.stopPropagation();
+ const { options } = this.props;
+ const dashboardSrv = getDashboardSrv();
+ const current = dashboardSrv.getCurrent();
+
+ const params: any = {
+ from: this._timeOffset(anno.time, options.navigateBefore, true),
+ to: this._timeOffset(anno.time, options.navigateAfter, false),
+ };
+
+ if (options.navigateToPanel) {
+ params.panelId = anno.panelId;
+ params.fullscreen = true;
+ }
+
+ if (current.id === anno.dashboardId) {
+ store.dispatch(
+ updateLocation({
+ query: params,
+ partial: true,
+ })
+ );
+ return;
+ }
+
+ getBackendSrv()
+ .get('/api/search', { dashboardIds: anno.dashboardId })
+ .then((res: any[]) => {
+ if (res && res.length && res[0].id === anno.dashboardId) {
+ const dash = res[0];
+ store.dispatch(
+ updateLocation({
+ query: params,
+ path: dash.url,
+ })
+ );
+ return;
+ }
+ appEvents.emit('alert-warning', ['Unknown Dashboard: ' + anno.dashboardId]);
+ });
+ };
+
+ _timeOffset(time: number, offset: string, subtract = false): number {
+ let incr = 5;
+ let unit = 'm';
+ const parts = /^(\d+)(\w)/.exec(offset);
+ if (parts && parts.length === 3) {
+ incr = parseInt(parts[1], 10);
+ unit = parts[2];
+ }
+
+ const t = dateTime(time);
+ if (subtract) {
+ incr *= -1;
+ }
+ return t.add(incr, unit as DurationUnit).valueOf();
+ }
+
+ onTagClick = (e: React.SyntheticEvent, tag: string, remove: boolean) => {
+ e.stopPropagation();
+ const queryTags = remove ? this.state.queryTags.filter(item => item !== tag) : [...this.state.queryTags, tag];
+
+ this.setState({ queryTags });
+ };
+
+ onUserClick = (e: React.SyntheticEvent, anno: AnnotationEvent) => {
+ e.stopPropagation();
+ this.setState({
+ queryUser: {
+ id: anno.userId,
+ login: anno.login,
+ email: anno.email,
+ },
+ });
+ };
+
+ onClearUser = () => {
+ this.setState({
+ queryUser: undefined,
+ });
+ };
+
+ renderTags = (tags: string[], remove: boolean): JSX.Element => {
+ if (!tags || !tags.length) {
+ return null;
+ }
+ return (
+ <>
+ {tags.map(tag => {
+ return (
+ this.onTagClick(e, tag, remove)} className="pointer">
+
+
+ );
+ })}
+ >
+ );
+ };
+
+ renderItem = (anno: AnnotationEvent, index: number): JSX.Element => {
+ const { options } = this.props;
+ const { showUser, showTags, showTime } = options;
+ const dashboard = getDashboardSrv().getCurrent();
+
+ return (
+
+ {
+ this.onAnnoClick(e, anno);
+ }}
+ >
+
+ {anno.text}
+
+
+
+ {anno.login && showUser && (
+
+
+ Created by:
+ {anno.email}
+
+ }
+ theme="info"
+ placement="top"
+ >
+ this.onUserClick(e, anno)} className="graph-annotation__user">
+
+
+
+
+ )}
+ {showTags && this.renderTags(anno.tags, false)}
+
+
+ {showTime && {dashboard.formatDate(anno.time)}}
+
+
+ );
+ };
+
+ render() {
+ const { height } = this.props;
+ const { loaded, annotations, queryUser, queryTags } = this.state;
+ if (!loaded) {
+ return loading... ;
+ }
+
+ // Previously we showed inidication that it covered all time
+ // { timeInfo && (
+ //
+ // {timeInfo}
+ //
+ // )}
+
+ const hasFilter = queryUser || queryTags.length > 0;
+
+ return (
+
+ {hasFilter && (
+
+ Filter:
+ {queryUser && (
+
+ {queryUser.email}
+
+ )}
+ {queryTags.length > 0 && this.renderTags(queryTags, true)}
+
+ )}
+
+ {annotations.length < 1 && No Annotations Found }
+
+ {
+ return item.id + '';
+ }}
+ className="dashlist"
+ />
+
+ );
+ }
+}
diff --git a/public/app/plugins/panel/annolist/README.md b/public/app/plugins/panel/annolist/README.md
new file mode 100644
index 00000000000..ef0e0d123aa
--- /dev/null
+++ b/public/app/plugins/panel/annolist/README.md
@@ -0,0 +1,4 @@
+# Annotation List Panel - Native Plugin
+
+This Annotations List panel is **included** with Grafana.
+
diff --git a/public/app/plugins/panel/annolist/img/icn-annolist-panel.svg b/public/app/plugins/panel/annolist/img/icn-annolist-panel.svg
new file mode 100644
index 00000000000..f584770dff0
--- /dev/null
+++ b/public/app/plugins/panel/annolist/img/icn-annolist-panel.svg
@@ -0,0 +1,119 @@
+
+
+
diff --git a/public/app/plugins/panel/annolist/module.ts b/public/app/plugins/panel/annolist/module.ts
new file mode 100644
index 00000000000..4f7156361af
--- /dev/null
+++ b/public/app/plugins/panel/annolist/module.ts
@@ -0,0 +1,16 @@
+import { AnnoListPanel } from './AnnoListPanel';
+import { AnnoOptions, defaults } from './types';
+import { AnnoListEditor } from './AnnoListEditor';
+import { PanelPlugin } from '@grafana/ui';
+
+export const plugin = new PanelPlugin(AnnoListPanel)
+ .setDefaults(defaults)
+ .setEditor(AnnoListEditor)
+
+ // TODO, we should support this directly in the plugin infrastructure
+ .setPanelChangeHandler((options: AnnoOptions, prevPluginId: string, prevOptions: any) => {
+ if (prevPluginId === 'ryantxu-annolist-panel') {
+ return prevOptions as AnnoOptions;
+ }
+ return options;
+ });
diff --git a/public/app/plugins/panel/annolist/plugin.json b/public/app/plugins/panel/annolist/plugin.json
new file mode 100644
index 00000000000..d21890a0cb3
--- /dev/null
+++ b/public/app/plugins/panel/annolist/plugin.json
@@ -0,0 +1,20 @@
+{
+ "type": "panel",
+ "name": "Annotations list (alpha)",
+ "id": "annolist",
+ "state": "alpha",
+
+ "skipDataQuery": true,
+
+ "info": {
+ "description": "List annotations",
+ "author": {
+ "name": "Grafana Project",
+ "url": "https://grafana.com"
+ },
+ "logos": {
+ "small": "img/icn-annolist-panel.svg",
+ "large": "img/icn-annolist-panel.svg"
+ }
+ }
+}
diff --git a/public/app/plugins/panel/annolist/types.ts b/public/app/plugins/panel/annolist/types.ts
new file mode 100644
index 00000000000..e78f562b214
--- /dev/null
+++ b/public/app/plugins/panel/annolist/types.ts
@@ -0,0 +1,29 @@
+export interface AnnoOptions {
+ limit: number;
+ tags: string[];
+ onlyFromThisDashboard: boolean;
+ onlyInTimeRange: boolean;
+
+ showTags: boolean;
+ showUser: boolean;
+ showTime: boolean;
+
+ navigateBefore: string;
+ navigateAfter: string;
+ navigateToPanel: boolean;
+}
+
+export const defaults: AnnoOptions = {
+ limit: 10,
+ tags: [],
+ onlyFromThisDashboard: false,
+ onlyInTimeRange: false,
+
+ showTags: true,
+ showUser: true,
+ showTime: true,
+
+ navigateBefore: '10m',
+ navigateAfter: '10m',
+ navigateToPanel: true,
+};
diff --git a/yarn.lock b/yarn.lock
index 113e9a0e954..44aab000579 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2283,10 +2283,10 @@
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.3.tgz#bdfd69d61e464dcc81b25159c270d75a73c1a636"
integrity sha512-Il2DtDVRGDcqjDtE+rF8iqg1CArehSK84HZJCT7AMITlyXRBpuPhqGLDQMowraqqu1coEaimg4ZOqggt6L6L+A==
-"@types/lodash@4.14.119", "@types/lodash@4.14.123":
- version "4.14.119"
- resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.119.tgz#be847e5f4bc3e35e46d041c394ead8b603ad8b39"
- integrity sha512-Z3TNyBL8Vd/M9D9Ms2S3LmFq2sSMzahodD6rCS9V2N44HUMINb75jNkSuwAx7eo2ufqTdfOdtGQpNbieUjPQmw==
+"@types/lodash@4.14.123":
+ version "4.14.123"
+ resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.123.tgz#39be5d211478c8dd3bdae98ee75bb7efe4abfe4d"
+ integrity sha512-pQvPkc4Nltyx7G1Ww45OjVqUsJP4UsZm+GWJpigXgkikZqJgRm4c48g027o6tdgubWHwFRF15iFd+Y4Pmqv6+Q==
"@types/marked@0.6.5":
version "0.6.5"
@@ -3432,7 +3432,7 @@ babel-plugin-add-react-displayname@^0.0.5:
version "0.0.5"
resolved "https://registry.yarnpkg.com/babel-plugin-add-react-displayname/-/babel-plugin-add-react-displayname-0.0.5.tgz#339d4cddb7b65fd62d1df9db9fe04de134122bd5"
-babel-plugin-angularjs-annotate@0.10.0:
+babel-plugin-angularjs-annotate@0.10.0, babel-plugin-angularjs-annotate@^0.10.0:
version "0.10.0"
resolved "https://registry.yarnpkg.com/babel-plugin-angularjs-annotate/-/babel-plugin-angularjs-annotate-0.10.0.tgz#4213b3aaae494a087aad0b8237c5d0716d22ca76"
dependencies:
@@ -4216,6 +4216,11 @@ caniuse-api@^3.0.0:
lodash.memoize "^4.1.2"
lodash.uniq "^4.5.0"
+caniuse-db@1.0.30000772:
+ version "1.0.30000772"
+ resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000772.tgz#51aae891768286eade4a3d8319ea76d6a01b512b"
+ integrity sha1-UarokXaChureSj2DGep21qAbUSs=
+
caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000929, caniuse-lite@^1.0.30000947, caniuse-lite@^1.0.30000957, caniuse-lite@^1.0.30000963:
version "1.0.30000966"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000966.tgz#f3c6fefacfbfbfb981df6dfa68f2aae7bff41b64"
@@ -10753,7 +10758,7 @@ ng-annotate-loader@0.6.1:
normalize-path "2.0.1"
source-map "0.5.6"
-ng-annotate-webpack-plugin@0.3.0, ng-annotate-webpack-plugin@^0.3.0:
+ng-annotate-webpack-plugin@0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/ng-annotate-webpack-plugin/-/ng-annotate-webpack-plugin-0.3.0.tgz#2e7f5e29c6a4ce26649edcb06c1213408b35b84a"
dependencies:
|