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> {
orgs: Organization[];
orgs: Organization[] = [];
state: State = {
isLoading: false,

View File

@ -36,7 +36,7 @@ export class FormDropdownCtrl {
lookupText: boolean;
placeholder: any;
startOpen: any;
debounce: number;
debounce: boolean;
/** @ngInject */
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.linkMode = true;
this.cancelBlur = null;
this.labelMode = false;
this.lookupText = false;
this.debounce = false;
// listen to model changes
$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);
this.params = part.params;
this.text = '';
this.updateText();
}

View File

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

View File

@ -1,10 +1,10 @@
import { GrafanaRootScope } from 'app/routes/GrafanaCtrl';
export class Profiler {
panelsRendered: number;
enabled: boolean;
$rootScope: GrafanaRootScope;
window: any;
panelsRendered = 0;
enabled?: boolean = undefined;
$rootScope?: GrafanaRootScope = undefined;
window?: any = undefined;
init(config: any, $rootScope: GrafanaRootScope) {
this.$rootScope = $rootScope;
@ -18,7 +18,7 @@ export class Profiler {
renderingCompleted() {
// add render counter to root scope
// 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
// 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.fetchQueue = new FetchQueue();
this.responseQueue = new ResponseQueue(this.fetchQueue, this.internalFetch);

View File

@ -19,6 +19,19 @@ export class User {
email?: string;
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) {
_.extend(this, config.bootData.user);
}

View File

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

View File

@ -1,5 +1,5 @@
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) {
if (!aliasOrRegex) {
@ -72,15 +72,15 @@ export default class TimeSeries {
valueFormater: any;
stats: any;
legend: boolean;
hideTooltip: boolean;
allIsNull: boolean;
allIsZero: boolean;
hideTooltip?: boolean;
allIsNull?: boolean;
allIsZero?: boolean;
decimals: DecimalCount;
hasMsResolution: boolean;
isOutsideRange: boolean;
isOutsideRange?: boolean;
lines: any;
hiddenSeries: boolean;
hiddenSeries?: boolean;
dashes: any;
bars: any;
points: any;

View File

@ -1,6 +1,6 @@
export class Edge {
inputNode: Node;
outputNode: Node;
inputNode?: Node;
outputNode?: Node;
_linkTo(node: Node, direction: number) {
if (direction <= 0) {
@ -82,10 +82,10 @@ export class Node {
}
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 {
@ -94,19 +94,19 @@ export class Node {
}
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[] {
const toBeRemoved: any[] = [];
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) => {
const edgeToRemove = n.getEdgeTo(this.name);
inputEdgesNodes?.forEach((n) => {
const edgeToRemove = n?.getEdgeTo(this.name);
if (edgeToRemove) {
toBeRemoved.push(edgeToRemove);
}
@ -201,11 +201,11 @@ export class Graph {
export const printGraph = (g: Graph) => {
Object.keys(g.nodes).forEach((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) {
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) {
inputEdges = '<none>';
}

View File

@ -9,6 +9,10 @@ export class SemVersion {
meta: string;
constructor(version: string) {
this.major = 0;
this.minor = 0;
this.patch = 0;
this.meta = '';
const match = versionPattern.exec(version);
if (match) {
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 { cx, css } from '@emotion/css';
import { css, cx } from '@emotion/css';
import { config } from 'app/core/config';
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 {
user: UserDTO;
@ -187,7 +187,7 @@ interface UserProfileRowState {
}
export class UserProfileRow extends PureComponent<UserProfileRowProps, UserProfileRowState> {
inputElem: HTMLInputElement;
inputElem?: HTMLInputElement;
static defaultProps: Partial<UserProfileRowProps> = {
value: '',

View File

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

View File

@ -29,7 +29,7 @@ export class AlertTabCtrl {
addNotificationSegment: any;
notifications: any;
alertNotifications: any;
error: string;
error?: string;
appSubUrl: string;
alertHistory: 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`)
.then((res: any) => {
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.info = alertDef.getAlertAnnotationInfo(ah);
return ah;

View File

@ -55,11 +55,11 @@ export function annotationTooltipDirective(
}
header += `
<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
if (event.id && dashboard.canAddAnnotations()) {
if (event.id && dashboard?.canAddAnnotations()) {
header += `
<span class="pointer graph-annotation__edit-icon" ng-click="onEdit()">
<i class="fa fa-pencil-square"></i>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -10,7 +10,7 @@ import { saveDashboard } from 'app/features/manage-dashboards/state/actions';
import { RemovePanelEvent } from '../../../types/events';
export class DashboardSrv {
dashboard: DashboardModel;
dashboard?: DashboardModel;
/** @ngInject */
constructor(private $rootScope: GrafanaRootScope) {
@ -25,20 +25,25 @@ export class DashboardSrv {
this.dashboard = dashboard;
}
getCurrent(): DashboardModel {
getCurrent(): DashboardModel | undefined {
if (!this.dashboard) {
console.warn('Calling getDashboardSrv().getCurrent() without calling getDashboardSrv().setCurrent() first.');
}
return this.dashboard;
}
onRemovePanel = (panelId: number) => {
const dashboard = this.getCurrent();
removePanel(dashboard, dashboard.getPanelById(panelId)!, true);
if (dashboard) {
removePanel(dashboard, dashboard.getPanelById(panelId)!, true);
}
};
saveJSONDashboard(json: string) {
const parsedJson = JSON.parse(json);
return saveDashboard({
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;
refresh: any;
oldRefresh: string | null | undefined;
dashboard: DashboardModel;
dashboard?: DashboardModel;
timeAtLoad: any;
private autoRefreshBlocked: boolean;
private autoRefreshBlocked?: boolean;
constructor(private contextSrv: ContextSrv) {
// default time
@ -141,7 +141,9 @@ export class TimeSrv {
// if absolute ignore refresh option saved to dashboard
if (params.get('to') && params.get('to')!.indexOf('now') === -1) {
this.refresh = false;
this.dashboard.refresh = false;
if (this.dashboard) {
this.dashboard.refresh = false;
}
}
let paramsJSON: Record<string, string> = {};
@ -188,7 +190,10 @@ export class TimeSrv {
}
setAutoRefresh(interval: any) {
this.dashboard.refresh = interval;
if (this.dashboard) {
this.dashboard.refresh = interval;
}
this.stopAutoRefresh();
if (interval) {
@ -210,7 +215,7 @@ export class TimeSrv {
}
refreshDashboard() {
this.dashboard.timeRangeUpdated(this.timeRange());
this.dashboard?.timeRangeUpdated(this.timeRange());
}
private startNextRefreshTimer(afterMs: number) {
@ -233,9 +238,9 @@ export class TimeSrv {
// disable refresh if zoom in or zoom out
if (isDateTime(time.to)) {
this.oldRefresh = this.dashboard.refresh || this.oldRefresh;
this.oldRefresh = this.dashboard?.refresh || this.oldRefresh;
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.oldRefresh = null;
}

View File

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

View File

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

View File

@ -1,14 +1,14 @@
import React, { PureComponent } from 'react';
import _ from 'lodash';
import {
DataSourceSettings,
DataQuery,
DataSourceApi,
DataSourceJsonData,
DataSourcePlugin,
DataSourcePluginMeta,
DataSourceApi,
DataQuery,
DataSourceJsonData,
DataSourceSettings,
} from '@grafana/data';
import { getAngularLoader, AngularComponent } from '@grafana/runtime';
import { AngularComponent, getAngularLoader } from '@grafana/runtime';
export type GenericDataSourcePlugin = DataSourcePlugin<DataSourceApi<DataQuery, DataSourceJsonData>>;
@ -20,8 +20,8 @@ export interface Props {
}
export class PluginSettings extends PureComponent<Props> {
element: any;
component: AngularComponent;
element: HTMLDivElement | null = null;
component?: AngularComponent;
scopeProps: {
ctrl: { datasourceMeta: DataSourcePluginMeta; current: DataSourceSettings };
onModelChanged: (dataSource: DataSourceSettings) => void;
@ -59,7 +59,7 @@ export class PluginSettings extends PureComponent<Props> {
if (!plugin.components.ConfigEditor && this.props.dataSource !== prevProps.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 { 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 kbn from '../../core/utils/kbn';
@ -43,6 +43,10 @@ export const UnconnectedReturnToDashboardButton: FC<Props> = ({
const returnToPanel = async ({ withChanges = false } = {}) => {
const dashboardSrv = getDashboardSrv();
const dash = dashboardSrv.getCurrent();
if (!dash) {
return;
}
const titleSlug = kbn.slugifyForUrl(dash.title);
if (withChanges) {

View File

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

View File

@ -3,13 +3,13 @@ import { getDashboardSrv } from '../../dashboard/services/DashboardSrv';
import { appEvents } from 'app/core/core';
import {
AppEvents,
isLiveChannelMessageEvent,
isLiveChannelStatusEvent,
LiveChannel,
LiveChannelScope,
LiveChannelEvent,
LiveChannelConfig,
LiveChannelConnectionState,
isLiveChannelStatusEvent,
isLiveChannelMessageEvent,
LiveChannelEvent,
LiveChannelScope,
} from '@grafana/data';
import { DashboardChangedModal } from './DashboardChangedModal';
import { DashboardEvent, DashboardEventAction } from './types';
@ -111,7 +111,7 @@ class DashboardWatcher {
}
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);
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');
}
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');
}
private async validate(folderId: any, name: string, existingErrorMessage: string) {
private async validate(folderId: any, name: string | undefined, existingErrorMessage: string) {
name = (name || '').trim();
const nameLowerCased = name.toLowerCase();

View File

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

View File

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

View File

@ -482,7 +482,7 @@ export const variableUpdated = (
let promises: Array<Promise<any>> = [];
if (node) {
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) {
return Promise.resolve();
}

View File

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

View File

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

View File

@ -1,6 +1,6 @@
import React, { useState } from 'react';
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 { css, cx } from '@emotion/css';
import { getBackendSrv, getTemplateSrv } from '@grafana/runtime';
@ -21,7 +21,7 @@ export function AlertList(props: PanelProps<AlertListOptions>) {
const params: any = {
state: getStateFilter(props.options.stateFilter),
};
const panel = getDashboardSrv().getCurrent().getPanelById(props.id)!;
const panel = getDashboardSrv().getCurrent()?.getPanelById(props.id)!;
if (props.options.alertName) {
params.query = getTemplateSrv().replace(props.options.alertName, panel.scopedVars);
@ -36,7 +36,7 @@ export function AlertList(props: PanelProps<AlertListOptions>) {
}
if (props.options.dashboardAlerts) {
params.dashboardId = getDashboardSrv().getCurrent().id;
params.dashboardId = getDashboardSrv().getCurrent()?.id;
}
if (props.options.tags) {
@ -93,11 +93,11 @@ export function AlertList(props: PanelProps<AlertListOptions>) {
const currentDashboard = getDashboardSrv().getCurrent();
if (props.options.dashboardAlerts) {
params.dashboardId = currentDashboard.id;
params.dashboardId = currentDashboard?.id;
}
params.from = dateMath.parse(currentDashboard.time.from)!.unix() * 1000;
params.to = dateMath.parse(currentDashboard.time.to)!.unix() * 1000;
params.from = dateMath.parse(currentDashboard?.time.from)!.unix() * 1000;
params.to = dateMath.parse(currentDashboard?.time.to)!.unix() * 1000;
const data: AnnotationItemDTO[] = await getBackendSrv().get(
'/api/annotations',
@ -109,7 +109,7 @@ export function AlertList(props: PanelProps<AlertListOptions>) {
data.map((al) => {
return {
...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),
info: alertDef.getAlertAnnotationInfo(al),
};

View File

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

View File

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

View File

@ -67,7 +67,7 @@ export class TablePanel extends Component<Props> {
onCellFilterAdded = (filter: FilterItem) => {
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;
if (!datasource) {

View File

@ -3,7 +3,7 @@ set -e
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+)')"
if [ "$ERROR_COUNT" -gt $ERROR_COUNT_LIMIT ]; then