diff --git a/public/app/core/components/PermissionList/AddPermission.tsx b/public/app/core/components/PermissionList/AddPermission.tsx index bcdcba9149d..2ba2cea1e61 100644 --- a/public/app/core/components/PermissionList/AddPermission.tsx +++ b/public/app/core/components/PermissionList/AddPermission.tsx @@ -2,10 +2,10 @@ import React, { Component } from 'react'; import { css } from '@emotion/css'; import config from 'app/core/config'; import { UserPicker } from 'app/core/components/Select/UserPicker'; -import { TeamPicker, Team } from 'app/core/components/Select/TeamPicker'; +import { TeamPicker } from 'app/core/components/Select/TeamPicker'; import { Button, Form, HorizontalGroup, Select, stylesFactory } from '@grafana/ui'; import { GrafanaTheme, SelectableValue } from '@grafana/data'; -import { User } from 'app/types'; +import { Team, User } from 'app/types'; import { dashboardPermissionLevels, dashboardAclTargets, @@ -62,8 +62,8 @@ class AddPermissions extends Component { this.setState({ userId: user && !Array.isArray(user) ? user.id : 0 }); }; - onTeamSelected = (team: Team) => { - this.setState({ teamId: team && !Array.isArray(team) ? team.id : 0 }); + onTeamSelected = (team: SelectableValue) => { + this.setState({ teamId: team.value?.id && !Array.isArray(team.value) ? team.value.id : 0 }); }; onPermissionChanged = (permission: SelectableValue) => { diff --git a/public/app/core/components/Select/OrgPicker.tsx b/public/app/core/components/Select/OrgPicker.tsx index 77299295d62..76007b6ea2d 100644 --- a/public/app/core/components/Select/OrgPicker.tsx +++ b/public/app/core/components/Select/OrgPicker.tsx @@ -4,12 +4,7 @@ import { getBackendSrv } from 'app/core/services/backend_srv'; import { Organization } from 'app/types'; import { SelectableValue } from '@grafana/data'; -export interface OrgSelectItem { - id: number; - value: number; - label: string; - name: string; -} +export type OrgSelectItem = SelectableValue; export interface Props { onSelected: (org: OrgSelectItem) => void; @@ -35,16 +30,14 @@ export class OrgPicker extends PureComponent { return orgs; } - getOrgOptions = async (query: string): Promise>> => { + getOrgOptions = async (query: string): Promise => { if (!this.orgs?.length) { await this.loadOrgs(); } return this.orgs.map( - (org: Organization): SelectableValue => ({ - id: org.id, - value: org.id, + (org: Organization): OrgSelectItem => ({ + value: { id: org.id, name: org.name }, label: org.name, - name: org.name, }) ); }; diff --git a/public/app/core/components/Select/TeamPicker.tsx b/public/app/core/components/Select/TeamPicker.tsx index dd8cd4407e5..20c9fb3a88a 100644 --- a/public/app/core/components/Select/TeamPicker.tsx +++ b/public/app/core/components/Select/TeamPicker.tsx @@ -1,17 +1,12 @@ import React, { Component } from 'react'; import { debounce, isNil } from 'lodash'; import { AsyncSelect } from '@grafana/ui'; +import { SelectableValue } from '@grafana/data'; import { getBackendSrv } from '@grafana/runtime'; - -export interface Team { - id: number; - label: string; - name: string; - avatarUrl: string; -} +import { Team } from 'app/types'; export interface Props { - onSelected: (team: Team) => void; + onSelected: (team: SelectableValue) => void; className?: string; } @@ -42,13 +37,11 @@ export class TeamPicker extends Component { return getBackendSrv() .get(`/api/teams/search?perpage=100&page=1&query=${query}`) - .then((result: any) => { - const teams = result.teams.map((team: any) => { + .then((result: { teams: Team[] }) => { + const teams: Array> = result.teams.map((team) => { return { - id: team.id, - value: team.id, + value: team, label: team.name, - name: team.name, imgUrl: team.avatarUrl, }; }); diff --git a/public/app/core/components/Signup/SignupPage.tsx b/public/app/core/components/Signup/SignupPage.tsx index 08de16051c1..31e52133c0d 100644 --- a/public/app/core/components/Signup/SignupPage.tsx +++ b/public/app/core/components/Signup/SignupPage.tsx @@ -1,5 +1,5 @@ import React, { FC } from 'react'; -import { Form, Field, Input, Button, HorizontalGroup, LinkButton } from '@grafana/ui'; +import { Form, Field, Input, Button, HorizontalGroup, LinkButton, FormAPI } from '@grafana/ui'; import { getConfig } from 'app/core/config'; import { getBackendSrv } from '@grafana/runtime'; import appEvents from 'app/core/app_events'; @@ -60,7 +60,7 @@ export const SignupPage: FC = (props) => {
- {({ errors, register, getValues }) => ( + {({ errors, register, getValues }: FormAPI) => ( <> diff --git a/public/app/core/navigation/parseKeyValue.ts b/public/app/core/navigation/parseKeyValue.ts index ec20bd650e0..440e0862acb 100644 --- a/public/app/core/navigation/parseKeyValue.ts +++ b/public/app/core/navigation/parseKeyValue.ts @@ -44,9 +44,7 @@ export function isObject(value: any) { // http://jsperf.com/isobject4 return value !== null && typeof value === 'object'; } -export function isDefined(value: any) { - return typeof value !== 'undefined'; -} + export function isWindow(obj: { window: any }) { return obj && obj.window === obj; } @@ -118,11 +116,12 @@ export function forEach(obj: any, iterator: any, context?: any) { } return obj; } -export function tryDecodeURIComponent(value: string): string | void { +export function tryDecodeURIComponent(value: string): string { try { return decodeURIComponent(value); } catch (e) { // Ignore any invalid uri component. + return ''; } } @@ -138,8 +137,8 @@ function parseKeyValue(keyValue: string | null) { val = keyValue.substring(splitPoint + 1); } key = tryDecodeURIComponent(key); - if (isDefined(key)) { - val = isDefined(val) ? tryDecodeURIComponent(val as string) : true; + if (key) { + val = val !== undefined ? tryDecodeURIComponent(val) : true; if (!hasOwnProperty.call(obj, key)) { // @ts-ignore obj[key] = val; @@ -156,9 +155,6 @@ function parseKeyValue(keyValue: string | null) { }); return obj; } -export function isUndefined(value: any) { - return typeof value === 'undefined'; -} export default parseKeyValue; diff --git a/public/app/features/admin/UserOrgs.tsx b/public/app/features/admin/UserOrgs.tsx index 083650ace92..adaee950425 100644 --- a/public/app/features/admin/UserOrgs.tsx +++ b/public/app/features/admin/UserOrgs.tsx @@ -84,7 +84,7 @@ const getOrgRowStyles = stylesFactory((theme: GrafanaTheme) => { interface OrgRowProps extends Themeable { org: UserOrg; onOrgRemove: (orgId: number) => void; - onOrgRoleChange: (orgId: number, newRole: string) => void; + onOrgRoleChange: (orgId: number, newRole: OrgRole) => void; } interface OrgRowState { @@ -203,7 +203,7 @@ export class AddToOrgModal extends PureComponent { - this.setState({ selectedOrg: { ...org } }); + this.setState({ selectedOrg: org.value! }); }; onOrgRoleChange = (newRole: OrgRole) => { diff --git a/public/app/features/dashboard/components/FolderPicker/FolderPickerCtrl.ts b/public/app/features/dashboard/components/FolderPicker/FolderPickerCtrl.ts index e389c9a8ce2..7ebf8343383 100644 --- a/public/app/features/dashboard/components/FolderPicker/FolderPickerCtrl.ts +++ b/public/app/features/dashboard/components/FolderPicker/FolderPickerCtrl.ts @@ -13,7 +13,7 @@ import { createFolder } from 'app/features/manage-dashboards/state/actions'; export class FolderPickerCtrl { declare initialTitle: string; initialFolderId?: number; - labelClass: string; + labelClass?: string; onChange: any; onLoad: any; onCreateFolder: any; @@ -35,7 +35,7 @@ export class FolderPickerCtrl { constructor(private validationSrv: ValidationSrv, private contextSrv: ContextSrv, private $scope: IScope) { this.isEditor = this.contextSrv.isEditor; - if (!this.labelClass) { + if (this.labelClass === undefined) { this.labelClass = 'width-7'; } diff --git a/public/app/features/explore/Logs.tsx b/public/app/features/explore/Logs.tsx index ce3437fa1d4..0e254fe65c8 100644 --- a/public/app/features/explore/Logs.tsx +++ b/public/app/features/explore/Logs.tsx @@ -82,8 +82,8 @@ interface State { } export class UnthemedLogs extends PureComponent { - flipOrderTimer: NodeJS.Timeout; - cancelFlippingTimer: NodeJS.Timeout; + flipOrderTimer?: number; + cancelFlippingTimer?: number; topLogsRef = createRef(); state: State = { @@ -99,14 +99,19 @@ export class UnthemedLogs extends PureComponent { }; componentWillUnmount() { - clearTimeout(this.flipOrderTimer); - clearTimeout(this.cancelFlippingTimer); + if (this.flipOrderTimer) { + window.clearTimeout(this.flipOrderTimer); + } + + if (this.cancelFlippingTimer) { + window.clearTimeout(this.cancelFlippingTimer); + } } onChangeLogsSortOrder = () => { this.setState({ isFlipping: true }); // we are using setTimeout here to make sure that disabled button is rendered before the rendering of reordered logs - this.flipOrderTimer = setTimeout(() => { + this.flipOrderTimer = window.setTimeout(() => { this.setState((prevState) => { if (prevState.logsSortOrder === null || prevState.logsSortOrder === LogsSortOrder.Descending) { return { logsSortOrder: LogsSortOrder.Ascending }; @@ -114,7 +119,7 @@ export class UnthemedLogs extends PureComponent { return { logsSortOrder: LogsSortOrder.Descending }; }); }, 0); - this.cancelFlippingTimer = setTimeout(() => this.setState({ isFlipping: false }), 1000); + this.cancelFlippingTimer = window.setTimeout(() => this.setState({ isFlipping: false }), 1000); }; onEscapeNewlines = () => { diff --git a/public/app/features/explore/QueryEditor.tsx b/public/app/features/explore/QueryEditor.tsx index f8176d41bc4..46c1a870c51 100644 --- a/public/app/features/explore/QueryEditor.tsx +++ b/public/app/features/explore/QueryEditor.tsx @@ -21,7 +21,7 @@ interface QueryEditorProps { export default class QueryEditor extends PureComponent { element: any; - component: AngularComponent; + component?: AngularComponent; angularScope: any; async componentDidMount() { diff --git a/public/app/features/panel/metrics_panel_ctrl.ts b/public/app/features/panel/metrics_panel_ctrl.ts index bc7903e9833..22c0471ea2e 100644 --- a/public/app/features/panel/metrics_panel_ctrl.ts +++ b/public/app/features/panel/metrics_panel_ctrl.ts @@ -19,12 +19,12 @@ import { PanelModel } from 'app/features/dashboard/state'; import { PanelQueryRunner } from '../query/state/PanelQueryRunner'; class MetricsPanelCtrl extends PanelCtrl { - datasource: DataSourceApi; + declare datasource: DataSourceApi; contextSrv: ContextSrv; datasourceSrv: any; timeSrv: any; templateSrv: any; - range: TimeRange; + declare range: TimeRange; interval: any; intervalMs: any; resolution: any; diff --git a/public/app/features/panel/query_editor_row.ts b/public/app/features/panel/query_editor_row.ts index 594a795a115..876047a9603 100644 --- a/public/app/features/panel/query_editor_row.ts +++ b/public/app/features/panel/query_editor_row.ts @@ -5,7 +5,7 @@ export class QueryRowCtrl { queryCtrl: any; panelCtrl: any; panel: any; - hasTextEditMode: boolean; + hasTextEditMode = false; $onInit() { this.panelCtrl = this.queryCtrl.panelCtrl; diff --git a/public/app/plugins/panel/graph/Legend/Legend.tsx b/public/app/plugins/panel/graph/Legend/Legend.tsx index 48e6302794c..ae28086d48e 100644 --- a/public/app/plugins/panel/graph/Legend/Legend.tsx +++ b/public/app/plugins/panel/graph/Legend/Legend.tsx @@ -2,9 +2,8 @@ import { sortBy as _sortBy } from 'lodash'; import React, { PureComponent } from 'react'; import { TimeSeries } from 'app/core/core'; import { CustomScrollbar, Icon } from '@grafana/ui'; -import { LegendItem, LEGEND_STATS } from './LegendSeriesItem'; +import { LegendStat, LegendItem, LEGEND_STATS } from './LegendSeriesItem'; -type Sort = 'min' | 'max' | 'avg' | 'current' | 'total'; interface LegendProps { seriesList: TimeSeries[]; optionalClass?: string; @@ -19,7 +18,7 @@ interface LegendEventHandlers { interface LegendComponentEventHandlers { onToggleSeries?: (series: TimeSeries, event: any) => void; - onToggleSort?: (sortBy: Sort | undefined, sortDesc: any) => void; + onToggleSort?: (sortBy: LegendStat | undefined, sortDesc: any) => void; onToggleAxis?: (series: TimeSeries) => void; onColorChange?: (series: TimeSeries, color: string) => void; } @@ -43,7 +42,7 @@ interface LegendValuesProps { } interface LegendSortProps { - sort?: Sort; + sort?: LegendStat; sortDesc?: boolean; } @@ -232,7 +231,7 @@ class LegendSeriesList extends PureComponent { } class LegendTable extends PureComponent> { - onToggleSort = (stat: Sort) => { + onToggleSort = (stat: LegendStat) => { if (!this.props.onToggleSort) { return; } @@ -306,8 +305,8 @@ class LegendTable extends PureComponent> { } interface LegendTableHeaderProps { - statName: string; - onClick?: (statName: string) => void; + statName: LegendStat; + onClick?: (statName: LegendStat) => void; } class LegendTableHeaderItem extends PureComponent { diff --git a/public/app/plugins/panel/graph/Legend/LegendSeriesItem.tsx b/public/app/plugins/panel/graph/Legend/LegendSeriesItem.tsx index b028cf88c63..d6fabd2db00 100644 --- a/public/app/plugins/panel/graph/Legend/LegendSeriesItem.tsx +++ b/public/app/plugins/panel/graph/Legend/LegendSeriesItem.tsx @@ -4,7 +4,8 @@ import { TimeSeries } from 'app/core/core'; import { SeriesColorPicker, SeriesIcon } from '@grafana/ui'; import { selectors } from '@grafana/e2e-selectors'; -export const LEGEND_STATS = ['min', 'max', 'avg', 'current', 'total']; +export const LEGEND_STATS = ['min', 'max', 'avg', 'current', 'total'] as const; +export type LegendStat = typeof LEGEND_STATS[number]; export interface LegendLabelProps { series: TimeSeries; diff --git a/public/app/plugins/panel/graph/graph.ts b/public/app/plugins/panel/graph/graph.ts index dbaee0433a8..148cf3a801e 100644 --- a/public/app/plugins/panel/graph/graph.ts +++ b/public/app/plugins/panel/graph/graph.ts @@ -481,7 +481,7 @@ class GraphElement { series.data = series.getFlotPairs(series.nullPointMode || this.panel.nullPointMode); if (series.transform === 'constant') { - series.data = getFlotPairsConstant(series.data, this.ctrl.range); + series.data = getFlotPairsConstant(series.data, this.ctrl.range!); } // if hidden remove points and disable stack @@ -666,8 +666,8 @@ class GraphElement { addTimeAxis(options: any) { const ticks = this.panelWidth / 100; - const min = isUndefined(this.ctrl.range.from) ? null : this.ctrl.range.from.valueOf(); - const max = isUndefined(this.ctrl.range.to) ? null : this.ctrl.range.to.valueOf(); + const min = isUndefined(this.ctrl.range!.from) ? null : this.ctrl.range!.from.valueOf(); + const max = isUndefined(this.ctrl.range!.to) ? null : this.ctrl.range!.to.valueOf(); options.xaxis = { timezone: this.dashboard.getTimezone(), diff --git a/public/app/plugins/panel/heatmap/heatmap_ctrl.ts b/public/app/plugins/panel/heatmap/heatmap_ctrl.ts index 631a93053c5..90673c52254 100644 --- a/public/app/plugins/panel/heatmap/heatmap_ctrl.ts +++ b/public/app/plugins/panel/heatmap/heatmap_ctrl.ts @@ -164,10 +164,6 @@ export class HeatmapCtrl extends MetricsPanelCtrl { } onRender() { - if (!this.range || !this.series) { - return; - } - if (this.panel.dataFormat === 'tsbuckets') { this.convertHistogramToHeatmapData(); } else { @@ -176,6 +172,10 @@ export class HeatmapCtrl extends MetricsPanelCtrl { } convertTimeSeriesToHeatmapData() { + if (!this.range || !this.series) { + return; + } + let xBucketSize, yBucketSize, bucketsData, heatmapStats; const logBase = this.panel.yAxis.logBase; @@ -235,6 +235,10 @@ export class HeatmapCtrl extends MetricsPanelCtrl { } convertHistogramToHeatmapData() { + if (!this.range || !this.series) { + return; + } + const panelDatasource = this.getPanelDataSourceType(); let xBucketSize, yBucketSize, bucketsData, tsBuckets; diff --git a/public/app/plugins/panel/heatmap/rendering.ts b/public/app/plugins/panel/heatmap/rendering.ts index 12849742cfc..e570f4163c1 100644 --- a/public/app/plugins/panel/heatmap/rendering.ts +++ b/public/app/plugins/panel/heatmap/rendering.ts @@ -161,7 +161,7 @@ export class HeatmapRenderer { const ticks = this.chartWidth / DEFAULT_X_TICK_SIZE_PX; const format = graphTimeFormat(ticks, this.timeRange.from.valueOf(), this.timeRange.to.valueOf()); const timeZone = this.ctrl.dashboard.getTimezone(); - const formatter = (date: Date) => + const formatter = (date: d3.AxisDomain) => dateTimeFormat(date.valueOf(), { format: format, timeZone: timeZone, @@ -340,8 +340,8 @@ export class HeatmapRenderer { this.ctrl.decimals = decimals; const tickValueFormatter = this.tickValueFormatter.bind(this); - function tickFormatter(valIndex: string) { - let valueFormatted = tsBuckets[valIndex]; + function tickFormatter(valIndex: d3.AxisDomain) { + let valueFormatted = tsBuckets[valIndex.valueOf()]; if (!isNaN(toNumber(valueFormatted)) && valueFormatted !== '') { // Try to format numeric tick labels valueFormatted = tickValueFormatter(decimals)(toNumber(valueFormatted));