Chore: Reduces strict errors (#33012)

* Chore: reduces strict error in OptionPicker tests

* Chore: reduces strict errors in FormDropdownCtrl

* Chore: reduces has no initializer and is not definitely assigned in the constructor errors

* Chore: reduces has no initializer and is not definitely assigned in the constructor errors

* Chore: lowers strict count limit

* Tests: updates snapshots

* Tests: updates snapshots

* Chore: updates after PR comments

* Refactor: removes throw and changes signature for DashboardSrv.getCurrent
This commit is contained in:
Hugo Häggmark 2021-04-15 14:21:06 +02:00 committed by GitHub
parent 345d9f93fe
commit 34b4f7c717
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
42 changed files with 286 additions and 131 deletions

View File

@ -21,7 +21,7 @@ export interface State {
} }
export class OrgPicker extends PureComponent<Props, State> { export class OrgPicker extends PureComponent<Props, State> {
orgs: Organization[]; orgs: Organization[] = [];
state: State = { state: State = {
isLoading: false, isLoading: false,

View File

@ -36,7 +36,7 @@ export class FormDropdownCtrl {
lookupText: boolean; lookupText: boolean;
placeholder: any; placeholder: any;
startOpen: any; startOpen: any;
debounce: number; debounce: boolean;
/** @ngInject */ /** @ngInject */
constructor(private $scope: any, $element: JQLite, private $sce: ISCEService, private templateSrv: any) { constructor(private $scope: any, $element: JQLite, private $sce: ISCEService, private templateSrv: any) {
@ -44,6 +44,9 @@ export class FormDropdownCtrl {
this.linkElement = $element.find('a').first(); this.linkElement = $element.find('a').first();
this.linkMode = true; this.linkMode = true;
this.cancelBlur = null; this.cancelBlur = null;
this.labelMode = false;
this.lookupText = false;
this.debounce = false;
// listen to model changes // listen to model changes
$scope.$watch('ctrl.model', this.modelChanged.bind(this)); $scope.$watch('ctrl.model', this.modelChanged.bind(this));

View File

@ -33,6 +33,7 @@ export class QueryPart {
part.params = part.params || _.clone(this.def.defaultParams); part.params = part.params || _.clone(this.def.defaultParams);
this.params = part.params; this.params = part.params;
this.text = '';
this.updateText(); this.updateText();
} }

View File

@ -35,7 +35,7 @@ export class SwitchCtrl {
checked: any; checked: any;
show: any; show: any;
id: any; id: any;
label: string; label?: string;
/** @ngInject */ /** @ngInject */
constructor($scope: any, private $timeout: any) { constructor($scope: any, private $timeout: any) {

View File

@ -1,10 +1,10 @@
import { GrafanaRootScope } from 'app/routes/GrafanaCtrl'; import { GrafanaRootScope } from 'app/routes/GrafanaCtrl';
export class Profiler { export class Profiler {
panelsRendered: number; panelsRendered = 0;
enabled: boolean; enabled?: boolean = undefined;
$rootScope: GrafanaRootScope; $rootScope?: GrafanaRootScope = undefined;
window: any; window?: any = undefined;
init(config: any, $rootScope: GrafanaRootScope) { init(config: any, $rootScope: GrafanaRootScope) {
this.$rootScope = $rootScope; this.$rootScope = $rootScope;
@ -18,7 +18,7 @@ export class Profiler {
renderingCompleted() { renderingCompleted() {
// add render counter to root scope // add render counter to root scope
// used by image renderer to know when panel has rendered // used by image renderer to know when panel has rendered
this.panelsRendered = (this.panelsRendered || 0) + 1; this.panelsRendered += 1;
// this window variable is used by backend rendering tools to know // this window variable is used by backend rendering tools to know
// all panels have completed rendering // all panels have completed rendering

View File

@ -54,6 +54,7 @@ export class BackendSrv implements BackendService {
}; };
} }
this.noBackendCache = false;
this.internalFetch = this.internalFetch.bind(this); this.internalFetch = this.internalFetch.bind(this);
this.fetchQueue = new FetchQueue(); this.fetchQueue = new FetchQueue();
this.responseQueue = new ResponseQueue(this.fetchQueue, this.internalFetch); this.responseQueue = new ResponseQueue(this.fetchQueue, this.internalFetch);

View File

@ -19,6 +19,19 @@ export class User {
email?: string; email?: string;
constructor() { constructor() {
this.id = 0;
this.isGrafanaAdmin = false;
this.isSignedIn = false;
this.orgRole = '';
this.orgId = 0;
this.orgName = '';
this.login = '';
this.orgCount = 0;
this.timezone = '';
this.helpFlags1 = 0;
this.lightTheme = false;
this.hasEditPermissionInFolders = false;
this.email = undefined;
if (config.bootData.user) { if (config.bootData.user) {
_.extend(this, config.bootData.user); _.extend(this, config.bootData.user);
} }

View File

@ -9,12 +9,12 @@ export function uiSegmentSrv(this: any, $sce: any, templateSrv: any) {
value: string; value: string;
html: any; html: any;
type: any; type: any;
expandable: boolean; expandable?: boolean;
text: string; text?: string;
cssClass: string; cssClass?: string;
fake: boolean; fake?: boolean;
custom: boolean; custom?: boolean;
selectMode: any; selectMode?: any;
constructor(options: any) { constructor(options: any) {
if (options === '*' || options.value === '*') { if (options === '*' || options.value === '*') {
@ -40,7 +40,6 @@ export function uiSegmentSrv(this: any, $sce: any, templateSrv: any) {
this.fake = options.fake; this.fake = options.fake;
this.value = options.value; this.value = options.value;
this.selectMode = options.selectMode; this.selectMode = options.selectMode;
this.type = options.type;
this.expandable = options.expandable; this.expandable = options.expandable;
this.html = options.html || $sce.trustAsHtml(templateSrv.highlightVariablesAsHtml(this.value)); this.html = options.html || $sce.trustAsHtml(templateSrv.highlightVariablesAsHtml(this.value));
} }

View File

@ -1,5 +1,5 @@
import _ from 'lodash'; import _ from 'lodash';
import { getValueFormat, ValueFormatter, stringToJsRegex, DecimalCount, formattedValueToString } from '@grafana/data'; import { DecimalCount, formattedValueToString, getValueFormat, stringToJsRegex, ValueFormatter } from '@grafana/data';
function matchSeriesOverride(aliasOrRegex: string, seriesAlias: string) { function matchSeriesOverride(aliasOrRegex: string, seriesAlias: string) {
if (!aliasOrRegex) { if (!aliasOrRegex) {
@ -72,15 +72,15 @@ export default class TimeSeries {
valueFormater: any; valueFormater: any;
stats: any; stats: any;
legend: boolean; legend: boolean;
hideTooltip: boolean; hideTooltip?: boolean;
allIsNull: boolean; allIsNull?: boolean;
allIsZero: boolean; allIsZero?: boolean;
decimals: DecimalCount; decimals: DecimalCount;
hasMsResolution: boolean; hasMsResolution: boolean;
isOutsideRange: boolean; isOutsideRange?: boolean;
lines: any; lines: any;
hiddenSeries: boolean; hiddenSeries?: boolean;
dashes: any; dashes: any;
bars: any; bars: any;
points: any; points: any;

View File

@ -1,6 +1,6 @@
export class Edge { export class Edge {
inputNode: Node; inputNode?: Node;
outputNode: Node; outputNode?: Node;
_linkTo(node: Node, direction: number) { _linkTo(node: Node, direction: number) {
if (direction <= 0) { if (direction <= 0) {
@ -82,10 +82,10 @@ export class Node {
} }
if (typeof from === 'object') { if (typeof from === 'object') {
return this.inputEdges.find((e) => e.inputNode.name === from.name); return this.inputEdges.find((e) => e.inputNode?.name === from.name);
} }
return this.inputEdges.find((e) => e.inputNode.name === from); return this.inputEdges.find((e) => e.inputNode?.name === from);
} }
getEdgeTo(to: string | Node): Edge | null | undefined { getEdgeTo(to: string | Node): Edge | null | undefined {
@ -94,19 +94,19 @@ export class Node {
} }
if (typeof to === 'object') { if (typeof to === 'object') {
return this.outputEdges.find((e) => e.outputNode.name === to.name); return this.outputEdges.find((e) => e.outputNode?.name === to.name);
} }
return this.outputEdges.find((e) => e.outputNode.name === to); return this.outputEdges.find((e) => e.outputNode?.name === to);
} }
getOptimizedInputEdges(): Edge[] { getOptimizedInputEdges(): Edge[] {
const toBeRemoved: any[] = []; const toBeRemoved: any[] = [];
this.inputEdges.forEach((e) => { this.inputEdges.forEach((e) => {
const inputEdgesNodes = e.inputNode.inputEdges.map((e) => e.inputNode); const inputEdgesNodes = e.inputNode?.inputEdges.map((e) => e.inputNode);
inputEdgesNodes.forEach((n) => { inputEdgesNodes?.forEach((n) => {
const edgeToRemove = n.getEdgeTo(this.name); const edgeToRemove = n?.getEdgeTo(this.name);
if (edgeToRemove) { if (edgeToRemove) {
toBeRemoved.push(edgeToRemove); toBeRemoved.push(edgeToRemove);
} }
@ -201,11 +201,11 @@ export class Graph {
export const printGraph = (g: Graph) => { export const printGraph = (g: Graph) => {
Object.keys(g.nodes).forEach((name) => { Object.keys(g.nodes).forEach((name) => {
const n = g.nodes[name]; const n = g.nodes[name];
let outputEdges = n.outputEdges.map((e: Edge) => e.outputNode.name).join(', '); let outputEdges = n.outputEdges.map((e: Edge) => e.outputNode?.name).join(', ');
if (!outputEdges) { if (!outputEdges) {
outputEdges = '<none>'; outputEdges = '<none>';
} }
let inputEdges = n.inputEdges.map((e: Edge) => e.inputNode.name).join(', '); let inputEdges = n.inputEdges.map((e: Edge) => e.inputNode?.name).join(', ');
if (!inputEdges) { if (!inputEdges) {
inputEdges = '<none>'; inputEdges = '<none>';
} }

View File

@ -9,6 +9,10 @@ export class SemVersion {
meta: string; meta: string;
constructor(version: string) { constructor(version: string) {
this.major = 0;
this.minor = 0;
this.patch = 0;
this.meta = '';
const match = versionPattern.exec(version); const match = versionPattern.exec(version);
if (match) { if (match) {
this.major = Number(match[1]); this.major = Number(match[1]);

View File

@ -1,9 +1,9 @@
import React, { PureComponent, FC } from 'react'; import React, { FC, PureComponent } from 'react';
import { UserDTO } from 'app/types'; import { UserDTO } from 'app/types';
import { cx, css } from '@emotion/css'; import { css, cx } from '@emotion/css';
import { config } from 'app/core/config'; import { config } from 'app/core/config';
import { GrafanaTheme } from '@grafana/data'; import { GrafanaTheme } from '@grafana/data';
import { ConfirmButton, ConfirmModal, LegacyInputStatus, Button, stylesFactory, Input } from '@grafana/ui'; import { Button, ConfirmButton, ConfirmModal, Input, LegacyInputStatus, stylesFactory } from '@grafana/ui';
interface Props { interface Props {
user: UserDTO; user: UserDTO;
@ -187,7 +187,7 @@ interface UserProfileRowState {
} }
export class UserProfileRow extends PureComponent<UserProfileRowProps, UserProfileRowState> { export class UserProfileRow extends PureComponent<UserProfileRowProps, UserProfileRowState> {
inputElem: HTMLInputElement; inputElem?: HTMLInputElement;
static defaultProps: Partial<UserProfileRowProps> = { static defaultProps: Partial<UserProfileRowProps> = {
value: '', value: '',

View File

@ -14,6 +14,14 @@ import { PanelModel } from '../dashboard/state/PanelModel';
import { TestRuleResult } from './TestRuleResult'; import { TestRuleResult } from './TestRuleResult';
import { AppNotificationSeverity, StoreState } from 'app/types'; import { AppNotificationSeverity, StoreState } from 'app/types';
import { PanelNotSupported } from '../dashboard/components/PanelEditor/PanelNotSupported'; import { PanelNotSupported } from '../dashboard/components/PanelEditor/PanelNotSupported';
import { AlertState } from '../../plugins/datasource/alertmanager/types';
interface AngularPanelController {
_enableAlert: () => void;
alertState: AlertState | null;
render: () => void;
refresh: () => void;
}
interface OwnProps { interface OwnProps {
dashboard: DashboardModel; dashboard: DashboardModel;
@ -36,9 +44,9 @@ interface State {
} }
class UnConnectedAlertTab extends PureComponent<Props, State> { class UnConnectedAlertTab extends PureComponent<Props, State> {
element: any; element?: HTMLDivElement | null;
component: AngularComponent; component?: AngularComponent;
panelCtrl: any; panelCtrl?: AngularPanelController;
state: State = { state: State = {
validationMessage: '', validationMessage: '',
@ -103,8 +111,8 @@ class UnConnectedAlertTab extends PureComponent<Props, State> {
} }
onAddAlert = () => { onAddAlert = () => {
this.panelCtrl._enableAlert(); this.panelCtrl?._enableAlert();
this.component.digest(); this.component?.digest();
this.forceUpdate(); this.forceUpdate();
}; };
@ -153,9 +161,11 @@ class UnConnectedAlertTab extends PureComponent<Props, State> {
onConfirm={() => { onConfirm={() => {
delete panel.alert; delete panel.alert;
panel.thresholds = []; panel.thresholds = [];
this.panelCtrl.alertState = null; if (this.panelCtrl) {
this.panelCtrl.render(); this.panelCtrl.alertState = null;
this.component.digest(); this.panelCtrl.render();
}
this.component?.digest();
onDismiss(); onDismiss();
}} }}
/> />
@ -175,7 +185,7 @@ class UnConnectedAlertTab extends PureComponent<Props, State> {
<StateHistory <StateHistory
dashboard={dashboard} dashboard={dashboard}
panelId={panel.editSourceId ?? panel.id} panelId={panel.editSourceId ?? panel.id}
onRefresh={() => this.panelCtrl.refresh()} onRefresh={() => this.panelCtrl?.refresh()}
/> />
</Modal> </Modal>
); );

View File

@ -29,7 +29,7 @@ export class AlertTabCtrl {
addNotificationSegment: any; addNotificationSegment: any;
notifications: any; notifications: any;
alertNotifications: any; alertNotifications: any;
error: string; error?: string;
appSubUrl: string; appSubUrl: string;
alertHistory: any; alertHistory: any;
newAlertRuleTag: any; newAlertRuleTag: any;
@ -96,7 +96,7 @@ export class AlertTabCtrl {
.get(`/api/annotations?dashboardId=${this.panelCtrl.dashboard.id}&panelId=${this.panel.id}&limit=50&type=alert`) .get(`/api/annotations?dashboardId=${this.panelCtrl.dashboard.id}&panelId=${this.panel.id}&limit=50&type=alert`)
.then((res: any) => { .then((res: any) => {
this.alertHistory = _.map(res, (ah) => { this.alertHistory = _.map(res, (ah) => {
ah.time = this.dashboardSrv.getCurrent().formatDate(ah.time, 'MMM D, YYYY HH:mm:ss'); ah.time = this.dashboardSrv.getCurrent()?.formatDate(ah.time, 'MMM D, YYYY HH:mm:ss');
ah.stateModel = alertDef.getStateDisplayModel(ah.newState); ah.stateModel = alertDef.getStateDisplayModel(ah.newState);
ah.info = alertDef.getAlertAnnotationInfo(ah); ah.info = alertDef.getAlertAnnotationInfo(ah);
return ah; return ah;

View File

@ -55,11 +55,11 @@ export function annotationTooltipDirective(
} }
header += ` header += `
<span class="graph-annotation__title ${titleStateClass}">${sanitizeString(title)}</span> <span class="graph-annotation__title ${titleStateClass}">${sanitizeString(title)}</span>
<span class="graph-annotation__time">${dashboard.formatDate(event.min)}</span> <span class="graph-annotation__time">${dashboard?.formatDate(event.min)}</span>
`; `;
// Show edit icon only for users with at least Editor role // Show edit icon only for users with at least Editor role
if (event.id && dashboard.canAddAnnotations()) { if (event.id && dashboard?.canAddAnnotations()) {
header += ` header += `
<span class="pointer graph-annotation__edit-icon" ng-click="onEdit()"> <span class="pointer graph-annotation__edit-icon" ng-click="onEdit()">
<i class="fa fa-pencil-square"></i> <i class="fa fa-pencil-square"></i>

View File

@ -1,11 +1,11 @@
import React, { PureComponent } from 'react'; import React, { PureComponent } from 'react';
import { AnnotationEventMappings, DataQuery, LoadingState, DataSourceApi, AnnotationQuery } from '@grafana/data'; import { AnnotationEventMappings, AnnotationQuery, DataQuery, DataSourceApi, LoadingState } from '@grafana/data';
import { Spinner, Icon, IconName, Button } from '@grafana/ui'; import { Button, Icon, IconName, Spinner } from '@grafana/ui';
import { getDashboardSrv } from 'app/features/dashboard/services/DashboardSrv'; import { getDashboardSrv } from 'app/features/dashboard/services/DashboardSrv';
import { getTimeSrv } from 'app/features/dashboard/services/TimeSrv'; import { getTimeSrv } from 'app/features/dashboard/services/TimeSrv';
import { cx, css } from '@emotion/css'; import { css, cx } from '@emotion/css';
import { standardAnnotationSupport } from '../standardAnnotationSupport'; import { standardAnnotationSupport } from '../standardAnnotationSupport';
import { executeAnnotationQuery } from '../annotations_srv'; import { executeAnnotationQuery } from '../annotations_srv';
import { PanelModel } from 'app/features/dashboard/state'; import { PanelModel } from 'app/features/dashboard/state';
@ -56,6 +56,11 @@ export default class StandardAnnotationQueryEditor extends PureComponent<Props,
onRunQuery = async () => { onRunQuery = async () => {
const { datasource, annotation } = this.props; const { datasource, annotation } = this.props;
const dashboard = getDashboardSrv().getCurrent();
if (!dashboard) {
return;
}
this.setState({ this.setState({
running: true, running: true,
}); });
@ -63,7 +68,7 @@ export default class StandardAnnotationQueryEditor extends PureComponent<Props,
{ {
range: getTimeSrv().timeRange(), range: getTimeSrv().timeRange(),
panel: {} as PanelModel, panel: {} as PanelModel,
dashboard: getDashboardSrv().getCurrent(), dashboard,
}, },
datasource, datasource,
annotation annotation

View File

@ -5,12 +5,14 @@ import { AnnotationsSrv } from './all';
import { MetricsPanelCtrl } from '../panel/metrics_panel_ctrl'; import { MetricsPanelCtrl } from '../panel/metrics_panel_ctrl';
export class EventEditorCtrl { export class EventEditorCtrl {
// @ts-ignore initialized through Angular not constructor
panelCtrl: MetricsPanelCtrl; panelCtrl: MetricsPanelCtrl;
// @ts-ignore initialized through Angular not constructor
event: AnnotationEvent; event: AnnotationEvent;
timeRange: { from: number; to: number }; timeRange?: { from: number; to: number };
form: any; form: any;
close: any; close: any;
timeFormated: string; timeFormated?: string;
/** @ngInject */ /** @ngInject */
constructor(private annotationsSrv: AnnotationsSrv) {} constructor(private annotationsSrv: AnnotationsSrv) {}

View File

@ -1,11 +1,11 @@
import _ from 'lodash'; import _ from 'lodash';
import tinycolor from 'tinycolor2'; import tinycolor from 'tinycolor2';
import { import {
OK_COLOR,
ALERTING_COLOR, ALERTING_COLOR,
NO_DATA_COLOR,
PENDING_COLOR,
DEFAULT_ANNOTATION_COLOR, DEFAULT_ANNOTATION_COLOR,
NO_DATA_COLOR,
OK_COLOR,
PENDING_COLOR,
REGION_FILL_ALPHA, REGION_FILL_ALPHA,
} from '@grafana/ui'; } from '@grafana/ui';
import { MetricsPanelCtrl } from '../panel/metrics_panel_ctrl'; import { MetricsPanelCtrl } from '../panel/metrics_panel_ctrl';
@ -13,8 +13,8 @@ import { MetricsPanelCtrl } from '../panel/metrics_panel_ctrl';
import { AnnotationEvent } from '@grafana/data'; import { AnnotationEvent } from '@grafana/data';
export class EventManager { export class EventManager {
event: AnnotationEvent | null; event: AnnotationEvent | null = null;
editorOpen: boolean; editorOpen = false;
constructor(private panelCtrl: MetricsPanelCtrl) {} constructor(private panelCtrl: MetricsPanelCtrl) {}

View File

@ -10,7 +10,7 @@ export interface Props {
export class AngularEditorLoader extends React.PureComponent<Props> { export class AngularEditorLoader extends React.PureComponent<Props> {
ref: HTMLDivElement | null = null; ref: HTMLDivElement | null = null;
angularComponent: AngularComponent; angularComponent?: AngularComponent;
componentWillUnmount() { componentWillUnmount() {
if (this.angularComponent) { if (this.angularComponent) {

View File

@ -11,7 +11,7 @@ import { promiseToDigest } from '../../../../core/utils/promiseToDigest';
import { createFolder } from 'app/features/manage-dashboards/state/actions'; import { createFolder } from 'app/features/manage-dashboards/state/actions';
export class FolderPickerCtrl { export class FolderPickerCtrl {
initialTitle: string; declare initialTitle: string;
initialFolderId?: number; initialFolderId?: number;
labelClass: string; labelClass: string;
onChange: any; onChange: any;
@ -19,14 +19,14 @@ export class FolderPickerCtrl {
onCreateFolder: any; onCreateFolder: any;
enterFolderCreation: any; enterFolderCreation: any;
exitFolderCreation: any; exitFolderCreation: any;
enableCreateNew: boolean; declare enableCreateNew: boolean;
enableReset: boolean; declare enableReset: boolean;
rootName = 'General'; rootName = 'General';
folder: any; folder: any;
createNewFolder: boolean; createNewFolder?: boolean;
newFolderName: string; newFolderName?: string;
newFolderNameTouched: boolean; newFolderNameTouched?: boolean;
hasValidationError: boolean; hasValidationError?: boolean;
validationError: any; validationError: any;
isEditor: boolean; isEditor: boolean;
dashboardId?: number; dashboardId?: number;

View File

@ -69,7 +69,11 @@ exports[`DashboardPage Dashboard init completed Should render dashboard grid 1`
"x": 0, "x": 0,
"y": 0, "y": 0,
}, },
"hasChanged": false,
"id": 1, "id": 1,
"isEditing": false,
"isInView": false,
"isViewing": false,
"options": Object {}, "options": Object {},
"replaceVariables": [Function], "replaceVariables": [Function],
"targets": Array [ "targets": Array [
@ -197,7 +201,11 @@ exports[`DashboardPage Dashboard init completed Should render dashboard grid 1`
"x": 0, "x": 0,
"y": 0, "y": 0,
}, },
"hasChanged": false,
"id": 1, "id": 1,
"isEditing": false,
"isInView": false,
"isViewing": false,
"options": Object {}, "options": Object {},
"replaceVariables": [Function], "replaceVariables": [Function],
"targets": Array [ "targets": Array [
@ -295,7 +303,11 @@ exports[`DashboardPage Dashboard init completed Should render dashboard grid 1`
"x": 0, "x": 0,
"y": 0, "y": 0,
}, },
"hasChanged": false,
"id": 1, "id": 1,
"isEditing": false,
"isInView": false,
"isViewing": false,
"options": Object {}, "options": Object {},
"replaceVariables": [Function], "replaceVariables": [Function],
"targets": Array [ "targets": Array [
@ -415,7 +427,11 @@ exports[`DashboardPage When dashboard has editview url state should render setti
"x": 0, "x": 0,
"y": 0, "y": 0,
}, },
"hasChanged": false,
"id": 1, "id": 1,
"isEditing": false,
"isInView": false,
"isViewing": false,
"options": Object {}, "options": Object {},
"replaceVariables": [Function], "replaceVariables": [Function],
"targets": Array [ "targets": Array [
@ -543,7 +559,11 @@ exports[`DashboardPage When dashboard has editview url state should render setti
"x": 0, "x": 0,
"y": 0, "y": 0,
}, },
"hasChanged": false,
"id": 1, "id": 1,
"isEditing": false,
"isInView": false,
"isViewing": false,
"options": Object {}, "options": Object {},
"replaceVariables": [Function], "replaceVariables": [Function],
"targets": Array [ "targets": Array [
@ -641,7 +661,11 @@ exports[`DashboardPage When dashboard has editview url state should render setti
"x": 0, "x": 0,
"y": 0, "y": 0,
}, },
"hasChanged": false,
"id": 1, "id": 1,
"isEditing": false,
"isInView": false,
"isViewing": false,
"options": Object {}, "options": Object {},
"replaceVariables": [Function], "replaceVariables": [Function],
"targets": Array [ "targets": Array [
@ -743,7 +767,11 @@ exports[`DashboardPage When dashboard has editview url state should render setti
"x": 0, "x": 0,
"y": 0, "y": 0,
}, },
"hasChanged": false,
"id": 1, "id": 1,
"isEditing": false,
"isInView": false,
"isViewing": false,
"options": Object {}, "options": Object {},
"replaceVariables": [Function], "replaceVariables": [Function],
"targets": Array [ "targets": Array [

View File

@ -16,9 +16,9 @@ interface State {
export class PanelResizer extends PureComponent<Props, State> { export class PanelResizer extends PureComponent<Props, State> {
initialHeight: number = Math.floor(document.documentElement.scrollHeight * 0.3); initialHeight: number = Math.floor(document.documentElement.scrollHeight * 0.3);
prevEditorHeight: number; prevEditorHeight?: number;
throttledChangeHeight: (height: number) => void; throttledChangeHeight: (height: number) => void;
throttledResizeDone: () => void; throttledResizeDone?: () => void;
noStyles: object = {}; noStyles: object = {};
constructor(props: Props) { constructor(props: Props) {

View File

@ -116,8 +116,11 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
"x": 0, "x": 0,
"y": 0, "y": 0,
}, },
"hasChanged": false,
"id": 1, "id": 1,
"isEditing": false,
"isInView": false, "isInView": false,
"isViewing": false,
"options": Object {}, "options": Object {},
"replaceVariables": [Function], "replaceVariables": [Function],
"targets": Array [ "targets": Array [
@ -144,8 +147,11 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
"x": 0, "x": 0,
"y": 10, "y": 10,
}, },
"hasChanged": false,
"id": 2, "id": 2,
"isEditing": false,
"isInView": false, "isInView": false,
"isViewing": false,
"options": Object {}, "options": Object {},
"replaceVariables": [Function], "replaceVariables": [Function],
"targets": Array [ "targets": Array [
@ -172,8 +178,11 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
"x": 0, "x": 0,
"y": 20, "y": 20,
}, },
"hasChanged": false,
"id": 3, "id": 3,
"isEditing": false,
"isInView": false, "isInView": false,
"isViewing": false,
"options": Object {}, "options": Object {},
"replaceVariables": [Function], "replaceVariables": [Function],
"targets": Array [ "targets": Array [
@ -200,8 +209,11 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
"x": 0, "x": 0,
"y": 120, "y": 120,
}, },
"hasChanged": false,
"id": 4, "id": 4,
"isEditing": false,
"isInView": false, "isInView": false,
"isViewing": false,
"options": Object {}, "options": Object {},
"replaceVariables": [Function], "replaceVariables": [Function],
"targets": Array [ "targets": Array [
@ -234,7 +246,9 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
"version": 0, "version": 0,
} }
} }
isEditing={false}
isInView={false} isInView={false}
isViewing={false}
panel={ panel={
PanelModel { PanelModel {
"cachedPluginOptions": Object {}, "cachedPluginOptions": Object {},
@ -251,8 +265,11 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
"x": 0, "x": 0,
"y": 0, "y": 0,
}, },
"hasChanged": false,
"id": 1, "id": 1,
"isEditing": false,
"isInView": false, "isInView": false,
"isViewing": false,
"options": Object {}, "options": Object {},
"replaceVariables": [Function], "replaceVariables": [Function],
"targets": Array [ "targets": Array [
@ -340,8 +357,11 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
"x": 0, "x": 0,
"y": 0, "y": 0,
}, },
"hasChanged": false,
"id": 1, "id": 1,
"isEditing": false,
"isInView": false, "isInView": false,
"isViewing": false,
"options": Object {}, "options": Object {},
"replaceVariables": [Function], "replaceVariables": [Function],
"targets": Array [ "targets": Array [
@ -368,8 +388,11 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
"x": 0, "x": 0,
"y": 10, "y": 10,
}, },
"hasChanged": false,
"id": 2, "id": 2,
"isEditing": false,
"isInView": false, "isInView": false,
"isViewing": false,
"options": Object {}, "options": Object {},
"replaceVariables": [Function], "replaceVariables": [Function],
"targets": Array [ "targets": Array [
@ -396,8 +419,11 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
"x": 0, "x": 0,
"y": 20, "y": 20,
}, },
"hasChanged": false,
"id": 3, "id": 3,
"isEditing": false,
"isInView": false, "isInView": false,
"isViewing": false,
"options": Object {}, "options": Object {},
"replaceVariables": [Function], "replaceVariables": [Function],
"targets": Array [ "targets": Array [
@ -424,8 +450,11 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
"x": 0, "x": 0,
"y": 120, "y": 120,
}, },
"hasChanged": false,
"id": 4, "id": 4,
"isEditing": false,
"isInView": false, "isInView": false,
"isViewing": false,
"options": Object {}, "options": Object {},
"replaceVariables": [Function], "replaceVariables": [Function],
"targets": Array [ "targets": Array [
@ -458,7 +487,9 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
"version": 0, "version": 0,
} }
} }
isEditing={false}
isInView={false} isInView={false}
isViewing={false}
panel={ panel={
PanelModel { PanelModel {
"cachedPluginOptions": Object {}, "cachedPluginOptions": Object {},
@ -475,8 +506,11 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
"x": 0, "x": 0,
"y": 10, "y": 10,
}, },
"hasChanged": false,
"id": 2, "id": 2,
"isEditing": false,
"isInView": false, "isInView": false,
"isViewing": false,
"options": Object {}, "options": Object {},
"replaceVariables": [Function], "replaceVariables": [Function],
"targets": Array [ "targets": Array [
@ -564,8 +598,11 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
"x": 0, "x": 0,
"y": 0, "y": 0,
}, },
"hasChanged": false,
"id": 1, "id": 1,
"isEditing": false,
"isInView": false, "isInView": false,
"isViewing": false,
"options": Object {}, "options": Object {},
"replaceVariables": [Function], "replaceVariables": [Function],
"targets": Array [ "targets": Array [
@ -592,8 +629,11 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
"x": 0, "x": 0,
"y": 10, "y": 10,
}, },
"hasChanged": false,
"id": 2, "id": 2,
"isEditing": false,
"isInView": false, "isInView": false,
"isViewing": false,
"options": Object {}, "options": Object {},
"replaceVariables": [Function], "replaceVariables": [Function],
"targets": Array [ "targets": Array [
@ -620,8 +660,11 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
"x": 0, "x": 0,
"y": 20, "y": 20,
}, },
"hasChanged": false,
"id": 3, "id": 3,
"isEditing": false,
"isInView": false, "isInView": false,
"isViewing": false,
"options": Object {}, "options": Object {},
"replaceVariables": [Function], "replaceVariables": [Function],
"targets": Array [ "targets": Array [
@ -648,8 +691,11 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
"x": 0, "x": 0,
"y": 120, "y": 120,
}, },
"hasChanged": false,
"id": 4, "id": 4,
"isEditing": false,
"isInView": false, "isInView": false,
"isViewing": false,
"options": Object {}, "options": Object {},
"replaceVariables": [Function], "replaceVariables": [Function],
"targets": Array [ "targets": Array [
@ -682,7 +728,9 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
"version": 0, "version": 0,
} }
} }
isEditing={false}
isInView={false} isInView={false}
isViewing={false}
panel={ panel={
PanelModel { PanelModel {
"cachedPluginOptions": Object {}, "cachedPluginOptions": Object {},
@ -699,8 +747,11 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
"x": 0, "x": 0,
"y": 20, "y": 20,
}, },
"hasChanged": false,
"id": 3, "id": 3,
"isEditing": false,
"isInView": false, "isInView": false,
"isViewing": false,
"options": Object {}, "options": Object {},
"replaceVariables": [Function], "replaceVariables": [Function],
"targets": Array [ "targets": Array [
@ -788,8 +839,11 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
"x": 0, "x": 0,
"y": 0, "y": 0,
}, },
"hasChanged": false,
"id": 1, "id": 1,
"isEditing": false,
"isInView": false, "isInView": false,
"isViewing": false,
"options": Object {}, "options": Object {},
"replaceVariables": [Function], "replaceVariables": [Function],
"targets": Array [ "targets": Array [
@ -816,8 +870,11 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
"x": 0, "x": 0,
"y": 10, "y": 10,
}, },
"hasChanged": false,
"id": 2, "id": 2,
"isEditing": false,
"isInView": false, "isInView": false,
"isViewing": false,
"options": Object {}, "options": Object {},
"replaceVariables": [Function], "replaceVariables": [Function],
"targets": Array [ "targets": Array [
@ -844,8 +901,11 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
"x": 0, "x": 0,
"y": 20, "y": 20,
}, },
"hasChanged": false,
"id": 3, "id": 3,
"isEditing": false,
"isInView": false, "isInView": false,
"isViewing": false,
"options": Object {}, "options": Object {},
"replaceVariables": [Function], "replaceVariables": [Function],
"targets": Array [ "targets": Array [
@ -872,8 +932,11 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
"x": 0, "x": 0,
"y": 120, "y": 120,
}, },
"hasChanged": false,
"id": 4, "id": 4,
"isEditing": false,
"isInView": false, "isInView": false,
"isViewing": false,
"options": Object {}, "options": Object {},
"replaceVariables": [Function], "replaceVariables": [Function],
"targets": Array [ "targets": Array [
@ -906,7 +969,9 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
"version": 0, "version": 0,
} }
} }
isEditing={false}
isInView={false} isInView={false}
isViewing={false}
panel={ panel={
PanelModel { PanelModel {
"cachedPluginOptions": Object {}, "cachedPluginOptions": Object {},
@ -923,8 +988,11 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
"x": 0, "x": 0,
"y": 120, "y": 120,
}, },
"hasChanged": false,
"id": 4, "id": 4,
"isEditing": false,
"isInView": false, "isInView": false,
"isViewing": false,
"options": Object {}, "options": Object {},
"replaceVariables": [Function], "replaceVariables": [Function],
"targets": Array [ "targets": Array [

View File

@ -10,7 +10,7 @@ import { saveDashboard } from 'app/features/manage-dashboards/state/actions';
import { RemovePanelEvent } from '../../../types/events'; import { RemovePanelEvent } from '../../../types/events';
export class DashboardSrv { export class DashboardSrv {
dashboard: DashboardModel; dashboard?: DashboardModel;
/** @ngInject */ /** @ngInject */
constructor(private $rootScope: GrafanaRootScope) { constructor(private $rootScope: GrafanaRootScope) {
@ -25,20 +25,25 @@ export class DashboardSrv {
this.dashboard = dashboard; this.dashboard = dashboard;
} }
getCurrent(): DashboardModel { getCurrent(): DashboardModel | undefined {
if (!this.dashboard) {
console.warn('Calling getDashboardSrv().getCurrent() without calling getDashboardSrv().setCurrent() first.');
}
return this.dashboard; return this.dashboard;
} }
onRemovePanel = (panelId: number) => { onRemovePanel = (panelId: number) => {
const dashboard = this.getCurrent(); const dashboard = this.getCurrent();
removePanel(dashboard, dashboard.getPanelById(panelId)!, true); if (dashboard) {
removePanel(dashboard, dashboard.getPanelById(panelId)!, true);
}
}; };
saveJSONDashboard(json: string) { saveJSONDashboard(json: string) {
const parsedJson = JSON.parse(json); const parsedJson = JSON.parse(json);
return saveDashboard({ return saveDashboard({
dashboard: parsedJson, dashboard: parsedJson,
folderId: this.dashboard.meta.folderId || parsedJson.folderId, folderId: this.dashboard?.meta.folderId || parsedJson.folderId,
}); });
} }

View File

@ -23,9 +23,9 @@ export class TimeSrv {
refreshTimer: any; refreshTimer: any;
refresh: any; refresh: any;
oldRefresh: string | null | undefined; oldRefresh: string | null | undefined;
dashboard: DashboardModel; dashboard?: DashboardModel;
timeAtLoad: any; timeAtLoad: any;
private autoRefreshBlocked: boolean; private autoRefreshBlocked?: boolean;
constructor(private contextSrv: ContextSrv) { constructor(private contextSrv: ContextSrv) {
// default time // default time
@ -141,7 +141,9 @@ export class TimeSrv {
// if absolute ignore refresh option saved to dashboard // if absolute ignore refresh option saved to dashboard
if (params.get('to') && params.get('to')!.indexOf('now') === -1) { if (params.get('to') && params.get('to')!.indexOf('now') === -1) {
this.refresh = false; this.refresh = false;
this.dashboard.refresh = false; if (this.dashboard) {
this.dashboard.refresh = false;
}
} }
let paramsJSON: Record<string, string> = {}; let paramsJSON: Record<string, string> = {};
@ -188,7 +190,10 @@ export class TimeSrv {
} }
setAutoRefresh(interval: any) { setAutoRefresh(interval: any) {
this.dashboard.refresh = interval; if (this.dashboard) {
this.dashboard.refresh = interval;
}
this.stopAutoRefresh(); this.stopAutoRefresh();
if (interval) { if (interval) {
@ -210,7 +215,7 @@ export class TimeSrv {
} }
refreshDashboard() { refreshDashboard() {
this.dashboard.timeRangeUpdated(this.timeRange()); this.dashboard?.timeRangeUpdated(this.timeRange());
} }
private startNextRefreshTimer(afterMs: number) { private startNextRefreshTimer(afterMs: number) {
@ -233,9 +238,9 @@ export class TimeSrv {
// disable refresh if zoom in or zoom out // disable refresh if zoom in or zoom out
if (isDateTime(time.to)) { if (isDateTime(time.to)) {
this.oldRefresh = this.dashboard.refresh || this.oldRefresh; this.oldRefresh = this.dashboard?.refresh || this.oldRefresh;
this.setAutoRefresh(false); this.setAutoRefresh(false);
} else if (this.oldRefresh && this.oldRefresh !== this.dashboard.refresh) { } else if (this.oldRefresh && this.oldRefresh !== this.dashboard?.refresh) {
this.setAutoRefresh(this.oldRefresh); this.setAutoRefresh(this.oldRefresh);
this.oldRefresh = null; this.oldRefresh = null;
} }

View File

@ -85,7 +85,7 @@ export class DashboardModel {
// repeat process cycles // repeat process cycles
iteration?: number; iteration?: number;
meta: DashboardMeta; declare meta: DashboardMeta;
events: EventBusExtended; events: EventBusExtended;
static nonPersistedProperties: { [str: string]: boolean } = { static nonPersistedProperties: { [str: string]: boolean } = {

View File

@ -135,9 +135,9 @@ export class PanelModel implements DataConfigSource {
collapsed?: boolean; collapsed?: boolean;
panels?: any; panels?: any;
targets: DataQuery[]; declare targets: DataQuery[];
transformations?: DataTransformerConfig[]; transformations?: DataTransformerConfig[];
datasource: string | null; datasource: string | null = null;
thresholds?: any; thresholds?: any;
pluginVersion?: string; pluginVersion?: string;
@ -145,29 +145,29 @@ export class PanelModel implements DataConfigSource {
timeFrom?: any; timeFrom?: any;
timeShift?: any; timeShift?: any;
hideTimeOverride?: any; hideTimeOverride?: any;
options: { declare options: {
[key: string]: any; [key: string]: any;
}; };
fieldConfig: FieldConfigSource; declare fieldConfig: FieldConfigSource;
maxDataPoints?: number | null; maxDataPoints?: number | null;
interval?: string | null; interval?: string | null;
description?: string; description?: string;
links?: DataLink[]; links?: DataLink[];
transparent: boolean; declare transparent: boolean;
libraryPanel?: { uid: undefined; name: string } | PanelModelLibraryPanel; libraryPanel?: { uid: undefined; name: string } | PanelModelLibraryPanel;
// non persisted // non persisted
isViewing: boolean; isViewing = false;
isEditing: boolean; isEditing = false;
isInView: boolean; isInView = false;
hasChanged: boolean; hasChanged = false;
hasRefreshed: boolean; hasRefreshed?: boolean;
events: EventBus; events: EventBus;
cacheTimeout?: any; cacheTimeout?: any;
cachedPluginOptions: Record<string, PanelOptionsCache>; declare cachedPluginOptions: Record<string, PanelOptionsCache>;
legend?: { show: boolean; sort?: string; sortDesc?: boolean }; legend?: { show: boolean; sort?: string; sortDesc?: boolean };
plugin?: PanelPlugin; plugin?: PanelPlugin;

View File

@ -1,14 +1,14 @@
import React, { PureComponent } from 'react'; import React, { PureComponent } from 'react';
import _ from 'lodash'; import _ from 'lodash';
import { import {
DataSourceSettings, DataQuery,
DataSourceApi,
DataSourceJsonData,
DataSourcePlugin, DataSourcePlugin,
DataSourcePluginMeta, DataSourcePluginMeta,
DataSourceApi, DataSourceSettings,
DataQuery,
DataSourceJsonData,
} from '@grafana/data'; } from '@grafana/data';
import { getAngularLoader, AngularComponent } from '@grafana/runtime'; import { AngularComponent, getAngularLoader } from '@grafana/runtime';
export type GenericDataSourcePlugin = DataSourcePlugin<DataSourceApi<DataQuery, DataSourceJsonData>>; export type GenericDataSourcePlugin = DataSourcePlugin<DataSourceApi<DataQuery, DataSourceJsonData>>;
@ -20,8 +20,8 @@ export interface Props {
} }
export class PluginSettings extends PureComponent<Props> { export class PluginSettings extends PureComponent<Props> {
element: any; element: HTMLDivElement | null = null;
component: AngularComponent; component?: AngularComponent;
scopeProps: { scopeProps: {
ctrl: { datasourceMeta: DataSourcePluginMeta; current: DataSourceSettings }; ctrl: { datasourceMeta: DataSourcePluginMeta; current: DataSourceSettings };
onModelChanged: (dataSource: DataSourceSettings) => void; onModelChanged: (dataSource: DataSourceSettings) => void;
@ -59,7 +59,7 @@ export class PluginSettings extends PureComponent<Props> {
if (!plugin.components.ConfigEditor && this.props.dataSource !== prevProps.dataSource) { if (!plugin.components.ConfigEditor && this.props.dataSource !== prevProps.dataSource) {
this.scopeProps.ctrl.current = _.cloneDeep(this.props.dataSource); this.scopeProps.ctrl.current = _.cloneDeep(this.props.dataSource);
this.component.digest(); this.component?.digest();
} }
} }

View File

@ -1,6 +1,6 @@
import React, { FC } from 'react'; import React, { FC } from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { Icon, Tooltip, ButtonSelect, ToolbarButton, ButtonGroup } from '@grafana/ui'; import { ButtonGroup, ButtonSelect, Icon, ToolbarButton, Tooltip } from '@grafana/ui';
import { DataQuery, urlUtil } from '@grafana/data'; import { DataQuery, urlUtil } from '@grafana/data';
import kbn from '../../core/utils/kbn'; import kbn from '../../core/utils/kbn';
@ -43,6 +43,10 @@ export const UnconnectedReturnToDashboardButton: FC<Props> = ({
const returnToPanel = async ({ withChanges = false } = {}) => { const returnToPanel = async ({ withChanges = false } = {}) => {
const dashboardSrv = getDashboardSrv(); const dashboardSrv = getDashboardSrv();
const dash = dashboardSrv.getCurrent(); const dash = dashboardSrv.getCurrent();
if (!dash) {
return;
}
const titleSlug = kbn.slugifyForUrl(dash.title); const titleSlug = kbn.slugifyForUrl(dash.title);
if (withChanges) { if (withChanges) {

View File

@ -84,7 +84,7 @@ export const PanelLibraryOptionsGroup: FC<Props> = ({ panel, searchQuery }) => {
<AddLibraryPanelModal <AddLibraryPanelModal
panel={panel} panel={panel}
onDismiss={() => setShowingAddPanelModal(false)} onDismiss={() => setShowingAddPanelModal(false)}
initialFolderId={dashboard.meta.folderId} initialFolderId={dashboard?.meta.folderId}
isOpen={showingAddPanelModal} isOpen={showingAddPanelModal}
/> />
)} )}

View File

@ -3,13 +3,13 @@ import { getDashboardSrv } from '../../dashboard/services/DashboardSrv';
import { appEvents } from 'app/core/core'; import { appEvents } from 'app/core/core';
import { import {
AppEvents, AppEvents,
isLiveChannelMessageEvent,
isLiveChannelStatusEvent,
LiveChannel, LiveChannel,
LiveChannelScope,
LiveChannelEvent,
LiveChannelConfig, LiveChannelConfig,
LiveChannelConnectionState, LiveChannelConnectionState,
isLiveChannelStatusEvent, LiveChannelEvent,
isLiveChannelMessageEvent, LiveChannelScope,
} from '@grafana/data'; } from '@grafana/data';
import { DashboardChangedModal } from './DashboardChangedModal'; import { DashboardChangedModal } from './DashboardChangedModal';
import { DashboardEvent, DashboardEventAction } from './types'; import { DashboardEvent, DashboardEventAction } from './types';
@ -111,7 +111,7 @@ class DashboardWatcher {
} }
const dash = getDashboardSrv().getCurrent(); const dash = getDashboardSrv().getCurrent();
if (dash.uid !== event.message.uid) { if (dash?.uid !== event.message.uid) {
console.log('dashboard event for different dashboard?', event, dash); console.log('dashboard event for different dashboard?', event, dash);
return; return;
} }

View File

@ -13,11 +13,11 @@ export class ValidationSrv {
return this.validate(folderId, name, 'A dashboard or a folder with the same name already exists'); return this.validate(folderId, name, 'A dashboard or a folder with the same name already exists');
} }
validateNewFolderName(name: string) { validateNewFolderName(name?: string) {
return this.validate(0, name, 'A folder or dashboard in the general folder with the same name already exists'); return this.validate(0, name, 'A folder or dashboard in the general folder with the same name already exists');
} }
private async validate(folderId: any, name: string, existingErrorMessage: string) { private async validate(folderId: any, name: string | undefined, existingErrorMessage: string) {
name = (name || '').trim(); name = (name || '').trim();
const nameLowerCased = name.toLowerCase(); const nameLowerCased = name.toLowerCase();

View File

@ -189,9 +189,9 @@ export const getParsedQuery = (query: DashboardQuery, queryParsing = false) => {
if (parseQuery(query.query).folder === 'current') { if (parseQuery(query.query).folder === 'current') {
try { try {
const { folderId } = getDashboardSrv().getCurrent()?.meta; const dash = getDashboardSrv().getCurrent();
if (folderId) { if (dash?.meta.folderId) {
folderIds = [folderId]; folderIds = [dash?.meta.folderId];
} }
} catch (e) { } catch (e) {
console.error(e); console.error(e);

View File

@ -6,7 +6,7 @@ import { selectors } from '@grafana/e2e-selectors';
import { LoadingState } from '@grafana/data'; import { LoadingState } from '@grafana/data';
import { VariablePickerProps } from '../types'; import { VariablePickerProps } from '../types';
import { QueryVariableModel } from '../../types'; import { QueryVariableModel, VariableWithMultiSupport, VariableWithOptions } from '../../types';
import { queryBuilder } from '../../shared/testing/builders'; import { queryBuilder } from '../../shared/testing/builders';
import { optionPickerFactory } from './OptionsPicker'; import { optionPickerFactory } from './OptionsPicker';
import { initialState, OptionsPickerState } from './reducer'; import { initialState, OptionsPickerState } from './reducer';
@ -30,7 +30,7 @@ function setupTestContext({ pickerState = {}, variable = {} }: Args = {}) {
...variable, ...variable,
}; };
const onVariableChange = jest.fn(); const onVariableChange = jest.fn();
const props: VariablePickerProps<QueryVariableModel> = { const props: VariablePickerProps<VariableWithMultiSupport | VariableWithOptions> = {
variable: v, variable: v,
onVariableChange, onVariableChange,
}; };

View File

@ -482,7 +482,7 @@ export const variableUpdated = (
let promises: Array<Promise<any>> = []; let promises: Array<Promise<any>> = [];
if (node) { if (node) {
promises = node.getOptimizedInputEdges().map((e) => { promises = node.getOptimizedInputEdges().map((e) => {
const variable = variables.find((v) => v.name === e.inputNode.name); const variable = variables.find((v) => v.name === e.inputNode?.name);
if (!variable) { if (!variable) {
return Promise.resolve(); return Promise.resolve();
} }

View File

@ -60,7 +60,7 @@ export class DashboardQueryEditor extends PureComponent<Props, State> {
const query = queries[0] as DashboardQuery; const query = queries[0] as DashboardQuery;
const defaultDS = await getDatasourceSrv().get(); const defaultDS = await getDatasourceSrv().get();
const dashboard = getDashboardSrv().getCurrent(); const dashboard = getDashboardSrv().getCurrent();
const panel = dashboard.getPanelById(query.panelId ?? -124134); const panel = dashboard?.getPanelById(query.panelId ?? -124134);
if (!panel) { if (!panel) {
this.setState({ defaultDatasource: defaultDS.name }); this.setState({ defaultDatasource: defaultDS.name });
@ -126,6 +126,10 @@ export class DashboardQueryEditor extends PureComponent<Props, State> {
render() { render() {
const dashboard = getDashboardSrv().getCurrent(); const dashboard = getDashboardSrv().getCurrent();
if (!dashboard) {
return null;
}
const query = this.getQuery(); const query = this.getQuery();
let selected: SelectableValue<number> | undefined; let selected: SelectableValue<number> | undefined;

View File

@ -33,7 +33,7 @@ export function runSharedRequest(options: QueryRunnerOptions): Observable<PanelD
return undefined; return undefined;
} }
const listenToPanel = dashboard.getPanelById(listenToPanelId); const listenToPanel = dashboard?.getPanelById(listenToPanelId);
if (!listenToPanel) { if (!listenToPanel) {
subscriber.next(getQueryError('Unknown Panel: ' + listenToPanelId)); subscriber.next(getQueryError('Unknown Panel: ' + listenToPanelId));

View File

@ -1,6 +1,6 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import sortBy from 'lodash/sortBy'; import sortBy from 'lodash/sortBy';
import { PanelProps, GrafanaTheme, dateMath, dateTime } from '@grafana/data'; import { dateMath, dateTime, GrafanaTheme, PanelProps } from '@grafana/data';
import { Card, CustomScrollbar, Icon, stylesFactory, useStyles } from '@grafana/ui'; import { Card, CustomScrollbar, Icon, stylesFactory, useStyles } from '@grafana/ui';
import { css, cx } from '@emotion/css'; import { css, cx } from '@emotion/css';
import { getBackendSrv, getTemplateSrv } from '@grafana/runtime'; import { getBackendSrv, getTemplateSrv } from '@grafana/runtime';
@ -21,7 +21,7 @@ export function AlertList(props: PanelProps<AlertListOptions>) {
const params: any = { const params: any = {
state: getStateFilter(props.options.stateFilter), state: getStateFilter(props.options.stateFilter),
}; };
const panel = getDashboardSrv().getCurrent().getPanelById(props.id)!; const panel = getDashboardSrv().getCurrent()?.getPanelById(props.id)!;
if (props.options.alertName) { if (props.options.alertName) {
params.query = getTemplateSrv().replace(props.options.alertName, panel.scopedVars); params.query = getTemplateSrv().replace(props.options.alertName, panel.scopedVars);
@ -36,7 +36,7 @@ export function AlertList(props: PanelProps<AlertListOptions>) {
} }
if (props.options.dashboardAlerts) { if (props.options.dashboardAlerts) {
params.dashboardId = getDashboardSrv().getCurrent().id; params.dashboardId = getDashboardSrv().getCurrent()?.id;
} }
if (props.options.tags) { if (props.options.tags) {
@ -93,11 +93,11 @@ export function AlertList(props: PanelProps<AlertListOptions>) {
const currentDashboard = getDashboardSrv().getCurrent(); const currentDashboard = getDashboardSrv().getCurrent();
if (props.options.dashboardAlerts) { if (props.options.dashboardAlerts) {
params.dashboardId = currentDashboard.id; params.dashboardId = currentDashboard?.id;
} }
params.from = dateMath.parse(currentDashboard.time.from)!.unix() * 1000; params.from = dateMath.parse(currentDashboard?.time.from)!.unix() * 1000;
params.to = dateMath.parse(currentDashboard.time.to)!.unix() * 1000; params.to = dateMath.parse(currentDashboard?.time.to)!.unix() * 1000;
const data: AnnotationItemDTO[] = await getBackendSrv().get( const data: AnnotationItemDTO[] = await getBackendSrv().get(
'/api/annotations', '/api/annotations',
@ -109,7 +109,7 @@ export function AlertList(props: PanelProps<AlertListOptions>) {
data.map((al) => { data.map((al) => {
return { return {
...al, ...al,
time: currentDashboard.formatDate(al.time, 'MMM D, YYYY HH:mm:ss'), time: currentDashboard?.formatDate(al.time, 'MMM D, YYYY HH:mm:ss'),
stateModel: alertDef.getStateDisplayModel(al.newState), stateModel: alertDef.getStateDisplayModel(al.newState),
info: alertDef.getAlertAnnotationInfo(al), info: alertDef.getAlertAnnotationInfo(al),
}; };

View File

@ -69,7 +69,7 @@ export class AnnoListPanel extends PureComponent<Props, State> {
}; };
if (options.onlyFromThisDashboard) { if (options.onlyFromThisDashboard) {
params.dashboardId = getDashboardSrv().getCurrent().id; params.dashboardId = getDashboardSrv().getCurrent()?.id;
} }
let timeInfo = ''; let timeInfo = '';
@ -120,7 +120,7 @@ export class AnnoListPanel extends PureComponent<Props, State> {
params.viewPanel = anno.panelId; params.viewPanel = anno.panelId;
} }
if (current.id === anno.dashboardId) { if (current?.id === anno.dashboardId) {
getLocationSrv().update({ getLocationSrv().update({
query: params, query: params,
partial: true, partial: true,
@ -188,6 +188,9 @@ export class AnnoListPanel extends PureComponent<Props, State> {
renderItem = (anno: AnnotationEvent, index: number): JSX.Element => { renderItem = (anno: AnnotationEvent, index: number): JSX.Element => {
const { options } = this.props; const { options } = this.props;
const dashboard = getDashboardSrv().getCurrent(); const dashboard = getDashboardSrv().getCurrent();
if (!dashboard) {
return <></>;
}
return ( return (
<AnnotationListItem <AnnotationListItem

View File

@ -65,9 +65,9 @@ export class GettingStarted extends PureComponent<PanelProps, State> {
dismiss = () => { dismiss = () => {
const { id } = this.props; const { id } = this.props;
const dashboard = getDashboardSrv().getCurrent(); const dashboard = getDashboardSrv().getCurrent();
const panel = dashboard.getPanelById(id); const panel = dashboard?.getPanelById(id);
dashboard.removePanel(panel!); dashboard?.removePanel(panel!);
backendSrv backendSrv
.request({ .request({

View File

@ -67,7 +67,7 @@ export class TablePanel extends Component<Props> {
onCellFilterAdded = (filter: FilterItem) => { onCellFilterAdded = (filter: FilterItem) => {
const { key, value, operator } = filter; const { key, value, operator } = filter;
const panelModel = getDashboardSrv().getCurrent().getPanelById(this.props.id); const panelModel = getDashboardSrv().getCurrent()?.getPanelById(this.props.id);
const datasource = panelModel?.datasource; const datasource = panelModel?.datasource;
if (!datasource) { if (!datasource) {

View File

@ -3,7 +3,7 @@ set -e
echo -e "Collecting code stats (typescript errors & more)" echo -e "Collecting code stats (typescript errors & more)"
ERROR_COUNT_LIMIT=340 ERROR_COUNT_LIMIT=255
ERROR_COUNT="$(./node_modules/.bin/tsc --project tsconfig.json --noEmit --strict true | grep -oP 'Found \K(\d+)')" ERROR_COUNT="$(./node_modules/.bin/tsc --project tsconfig.json --noEmit --strict true | grep -oP 'Found \K(\d+)')"
if [ "$ERROR_COUNT" -gt $ERROR_COUNT_LIMIT ]; then if [ "$ERROR_COUNT" -gt $ERROR_COUNT_LIMIT ]; then