diff --git a/packages/grafana-ui/src/options/builder/stacking.tsx b/packages/grafana-ui/src/options/builder/stacking.tsx index 38667addb19..21c279448bc 100644 --- a/packages/grafana-ui/src/options/builder/stacking.tsx +++ b/packages/grafana-ui/src/options/builder/stacking.tsx @@ -5,7 +5,15 @@ import { identityOverrideProcessor, } from '@grafana/data'; import React from 'react'; -import { graphFieldOptions, HorizontalGroup, IconButton, Input, RadioButtonGroup, Tooltip } from '../..'; +import { + GraphFieldConfig, + graphFieldOptions, + HorizontalGroup, + IconButton, + Input, + RadioButtonGroup, + Tooltip, +} from '../..'; import { StackingConfig, StackingMode } from '@grafana/schema'; export const StackingEditor: React.FC> = ({ @@ -49,7 +57,7 @@ export const StackingEditor: React.FC, + builder: FieldConfigEditorBuilder, defaultConfig?: StackingConfig, category = ['Graph styles'] ) { diff --git a/public/app/core/services/ng_react.ts b/public/app/core/services/ng_react.ts index bb0e2b50d7b..3cf65f48708 100644 --- a/public/app/core/services/ng_react.ts +++ b/public/app/core/services/ng_react.ts @@ -10,16 +10,16 @@ // - reactDirective (factory for creating specific directives that correspond to reactComponent directives) import { kebabCase } from 'lodash'; -import React from 'react'; +import React, { ComponentType } from 'react'; import ReactDOM from 'react-dom'; import angular, { auto } from 'angular'; // get a react component from name (components can be an angular injectable e.g. value, factory or // available on window -function getReactComponent(name: string, $injector: auto.IInjectorService) { +function getReactComponent(name: string | Function, $injector: auto.IInjectorService): ComponentType { // if name is a function assume it is component and return it if (angular.isFunction(name)) { - return name; + return (name as unknown) as ComponentType; } // a React component name must be specified @@ -46,7 +46,7 @@ function getReactComponent(name: string, $injector: auto.IInjectorService) { throw Error('Cannot find react component ' + name); } - return reactComponent; + return (reactComponent as unknown) as ComponentType; } // wraps a function with scope.$apply, if already applied just return diff --git a/public/app/features/dashboard/components/ShareModal/ShareEmbed.tsx b/public/app/features/dashboard/components/ShareModal/ShareEmbed.tsx index cbbd35b11c7..92100d76a56 100644 --- a/public/app/features/dashboard/components/ShareModal/ShareEmbed.tsx +++ b/public/app/features/dashboard/components/ShareModal/ShareEmbed.tsx @@ -1,9 +1,9 @@ import React, { FormEvent, PureComponent } from 'react'; import { ClipboardButton, Field, Modal, RadioButtonGroup, Switch, TextArea } from '@grafana/ui'; import { AppEvents, SelectableValue } from '@grafana/data'; -import { DashboardModel, PanelModel } from 'app/features/dashboard/state'; import { appEvents } from 'app/core/core'; import { buildIframeHtml } from './utils'; +import { ShareModalTabProps } from './types'; const themeOptions: Array> = [ { label: 'Current', value: 'current' }, @@ -11,10 +11,7 @@ const themeOptions: Array> = [ { label: 'Light', value: 'light' }, ]; -interface Props { - dashboard: DashboardModel; - panel?: PanelModel; -} +interface Props extends ShareModalTabProps {} interface State { useCurrentTimeRange: boolean; diff --git a/public/app/features/dashboard/components/ShareModal/ShareExport.tsx b/public/app/features/dashboard/components/ShareModal/ShareExport.tsx index 47e0fbb0f73..c687a86f89a 100644 --- a/public/app/features/dashboard/components/ShareModal/ShareExport.tsx +++ b/public/app/features/dashboard/components/ShareModal/ShareExport.tsx @@ -2,18 +2,14 @@ import React, { PureComponent } from 'react'; import { saveAs } from 'file-saver'; import { getBackendSrv } from 'app/core/services/backend_srv'; import { Button, Field, Modal, Switch } from '@grafana/ui'; -import { DashboardModel, PanelModel } from 'app/features/dashboard/state'; import { DashboardExporter } from 'app/features/dashboard/components/DashExportModal'; import { appEvents } from 'app/core/core'; import { ShowModalReactEvent } from 'app/types/events'; import { ViewJsonModal } from './ViewJsonModal'; import { config } from '@grafana/runtime'; +import { ShareModalTabProps } from './types'; -interface Props { - dashboard: DashboardModel; - panel?: PanelModel; - onDismiss(): void; -} +interface Props extends ShareModalTabProps {} interface State { shareExternally: boolean; @@ -124,7 +120,7 @@ export class ShareExport extends PureComponent { }) ); - this.props.onDismiss(); + this.props.onDismiss?.(); }; render() { diff --git a/public/app/features/dashboard/components/ShareModal/ShareLibraryPanel.tsx b/public/app/features/dashboard/components/ShareModal/ShareLibraryPanel.tsx index 11c77d96438..a9b5ace06c9 100644 --- a/public/app/features/dashboard/components/ShareModal/ShareLibraryPanel.tsx +++ b/public/app/features/dashboard/components/ShareModal/ShareLibraryPanel.tsx @@ -1,10 +1,8 @@ import React from 'react'; -import { PanelModel } from 'app/features/dashboard/state'; import { AddLibraryPanelContents } from 'app/features/library-panels/components/AddLibraryPanelModal/AddLibraryPanelModal'; +import { ShareModalTabProps } from './types'; -interface Props { - onDismiss?: () => void; - panel?: PanelModel; +interface Props extends ShareModalTabProps { initialFolderId?: number; } diff --git a/public/app/features/dashboard/components/ShareModal/ShareLink.test.tsx b/public/app/features/dashboard/components/ShareModal/ShareLink.test.tsx index 4cf7c6512d9..70025821539 100644 --- a/public/app/features/dashboard/components/ShareModal/ShareLink.test.tsx +++ b/public/app/features/dashboard/components/ShareModal/ShareLink.test.tsx @@ -2,10 +2,11 @@ import React from 'react'; import { shallow, ShallowWrapper } from 'enzyme'; import { setTemplateSrv } from '@grafana/runtime'; import config from 'app/core/config'; -import { ShareLink, Props, State } from './ShareLink'; +import { Props, ShareLink, State } from './ShareLink'; import { initTemplateSrv } from '../../../../../test/helpers/initTemplateSrv'; import { variableAdapters } from '../../../variables/adapters'; import { createQueryVariableAdapter } from '../../../variables/query/adapter'; +import { PanelModel } from '../../state'; jest.mock('app/features/dashboard/services/TimeSrv', () => ({ getTimeSrv: () => ({ @@ -108,7 +109,7 @@ describe('ShareModal', () => { }, }; ctx.mount({ - panel: { id: 22, options: {}, fieldConfig: { defaults: {}, overrides: [] } }, + panel: new PanelModel({ id: 22, options: {}, fieldConfig: { defaults: {}, overrides: [] } }), }); }); @@ -121,7 +122,7 @@ describe('ShareModal', () => { it('should generate render url', async () => { mockLocationHref('http://dashboards.grafana.com/d/abcdefghi/my-dash'); ctx.mount({ - panel: { id: 22, options: {}, fieldConfig: { defaults: {}, overrides: [] } }, + panel: new PanelModel({ id: 22, options: {}, fieldConfig: { defaults: {}, overrides: [] } }), }); await ctx.wrapper?.instance().buildUrl(); @@ -134,7 +135,7 @@ describe('ShareModal', () => { it('should generate render url for scripted dashboard', async () => { mockLocationHref('http://dashboards.grafana.com/dashboard/script/my-dash.js'); ctx.mount({ - panel: { id: 22, options: {}, fieldConfig: { defaults: {}, overrides: [] } }, + panel: new PanelModel({ id: 22, options: {}, fieldConfig: { defaults: {}, overrides: [] } }), }); await ctx.wrapper?.instance().buildUrl(); @@ -166,7 +167,7 @@ describe('ShareModal', () => { it('should remove editPanel from image url when is first param in querystring', async () => { mockLocationHref('http://server/#!/test?editPanel=1'); ctx.mount({ - panel: { id: 1, options: {}, fieldConfig: { defaults: {}, overrides: [] } }, + panel: new PanelModel({ id: 1, options: {}, fieldConfig: { defaults: {}, overrides: [] } }), }); await ctx.wrapper?.instance().buildUrl(); diff --git a/public/app/features/dashboard/components/ShareModal/ShareLink.tsx b/public/app/features/dashboard/components/ShareModal/ShareLink.tsx index 38f9091d71f..32b93cd5423 100644 --- a/public/app/features/dashboard/components/ShareModal/ShareLink.tsx +++ b/public/app/features/dashboard/components/ShareModal/ShareLink.tsx @@ -1,11 +1,11 @@ import React, { PureComponent } from 'react'; import { selectors as e2eSelectors } from '@grafana/e2e-selectors'; -import { Field, RadioButtonGroup, Switch, ClipboardButton, Icon, Input, FieldSet, Alert } from '@grafana/ui'; -import { SelectableValue, PanelModel, AppEvents } from '@grafana/data'; -import { DashboardModel } from 'app/features/dashboard/state'; +import { Alert, ClipboardButton, Field, FieldSet, Icon, Input, RadioButtonGroup, Switch } from '@grafana/ui'; +import { AppEvents, SelectableValue } from '@grafana/data'; import { buildImageUrl, buildShareUrl } from './utils'; import { appEvents } from 'app/core/core'; import config from 'app/core/config'; +import { ShareModalTabProps } from './types'; const themeOptions: Array> = [ { label: 'Current', value: 'current' }, @@ -13,10 +13,7 @@ const themeOptions: Array> = [ { label: 'Light', value: 'light' }, ]; -export interface Props { - dashboard: DashboardModel; - panel?: PanelModel; -} +export interface Props extends ShareModalTabProps {} export interface State { useCurrentTimeRange: boolean; diff --git a/public/app/features/dashboard/components/ShareModal/ShareSnapshot.tsx b/public/app/features/dashboard/components/ShareModal/ShareSnapshot.tsx index 617c15dec03..9b1221fc19b 100644 --- a/public/app/features/dashboard/components/ShareModal/ShareSnapshot.tsx +++ b/public/app/features/dashboard/components/ShareModal/ShareSnapshot.tsx @@ -1,11 +1,12 @@ import React, { PureComponent } from 'react'; -import { Button, ClipboardButton, Icon, Spinner, Select, Input, LinkButton, Field, Modal } from '@grafana/ui'; +import { Button, ClipboardButton, Field, Icon, Input, LinkButton, Modal, Select, Spinner } from '@grafana/ui'; import { AppEvents, SelectableValue } from '@grafana/data'; import { getBackendSrv } from '@grafana/runtime'; import { DashboardModel, PanelModel } from 'app/features/dashboard/state'; import { getTimeSrv } from 'app/features/dashboard/services/TimeSrv'; import { appEvents } from 'app/core/core'; import { VariableRefresh } from '../../../variables/types'; +import { ShareModalTabProps } from './types'; const snapshotApiUrl = '/api/snapshots'; @@ -16,11 +17,7 @@ const expireOptions: Array> = [ { label: '7 Days', value: 60 * 60 * 24 * 7 }, ]; -interface Props { - dashboard: DashboardModel; - panel?: PanelModel; - onDismiss(): void; -} +interface Props extends ShareModalTabProps {} interface State { isLoading: boolean; diff --git a/public/app/features/dashboard/components/ShareModal/types.ts b/public/app/features/dashboard/components/ShareModal/types.ts index e7099062d44..af637f2f9f4 100644 --- a/public/app/features/dashboard/components/ShareModal/types.ts +++ b/public/app/features/dashboard/components/ShareModal/types.ts @@ -1,6 +1,5 @@ import React from 'react'; -import { PanelModel } from '@grafana/data'; -import { DashboardModel, PanelModel as InternalPanelModel } from 'app/features/dashboard/state'; +import { DashboardModel, PanelModel } from 'app/features/dashboard/state'; export interface ShareModalTabProps { dashboard: DashboardModel; @@ -8,13 +7,8 @@ export interface ShareModalTabProps { onDismiss?(): void; } -type ShareModalTabPropsWithInternalModel = ShareModalTabProps & { panel?: InternalPanelModel }; -export type ShareModalTab = - | React.ComponentType - | React.ComponentType; - export interface ShareModalTabModel { label: string; value: string; - component: ShareModalTab; + component: React.ComponentType; } diff --git a/public/app/features/dashboard/components/VersionHistory/utils.test.ts b/public/app/features/dashboard/components/VersionHistory/utils.test.ts index a0eb0979584..70f6896bbd4 100644 --- a/public/app/features/dashboard/components/VersionHistory/utils.test.ts +++ b/public/app/features/dashboard/components/VersionHistory/utils.test.ts @@ -1,4 +1,4 @@ -import { getDiffText, getDiffOperationText, jsonDiff, Diff } from './utils'; +import { Diff, getDiffOperationText, getDiffText, jsonDiff } from './utils'; describe('getDiffOperationText', () => { const cases = [ @@ -13,9 +13,13 @@ describe('getDiffOperationText', () => { }); }); +type DiffTextCase = [Partial, string]; describe('getDiffText', () => { - const addEmptyArray = [{ op: 'add', value: [], path: ['annotations', 'list'], startLineNumber: 24 }, 'added list']; - const addArrayNumericProp = [ + const addEmptyArray: DiffTextCase = [ + { op: 'add', value: [], path: ['annotations', 'list'], startLineNumber: 24 }, + 'added list', + ]; + const addArrayNumericProp: DiffTextCase = [ { op: 'add', value: ['tag'], @@ -23,7 +27,7 @@ describe('getDiffText', () => { }, 'added item 3', ]; - const addArrayProp = [ + const addArrayProp: DiffTextCase = [ { op: 'add', value: [{ name: 'dummy target 1' }, { name: 'dummy target 2' }], @@ -31,7 +35,7 @@ describe('getDiffText', () => { }, 'added 2 targets', ]; - const addValueNumericProp = [ + const addValueNumericProp: DiffTextCase = [ { op: 'add', value: 'foo', @@ -39,7 +43,7 @@ describe('getDiffText', () => { }, 'added item 3', ]; - const addValueProp = [ + const addValueProp: DiffTextCase = [ { op: 'add', value: 'foo', @@ -48,11 +52,11 @@ describe('getDiffText', () => { 'added targets', ]; - const removeEmptyArray = [ + const removeEmptyArray: DiffTextCase = [ { op: 'remove', originalValue: [], path: ['annotations', 'list'], startLineNumber: 24 }, 'deleted list', ]; - const removeArrayNumericProp = [ + const removeArrayNumericProp: DiffTextCase = [ { op: 'remove', originalValue: ['tag'], @@ -60,7 +64,7 @@ describe('getDiffText', () => { }, 'deleted item 3', ]; - const removeArrayProp = [ + const removeArrayProp: DiffTextCase = [ { op: 'remove', originalValue: [{ name: 'dummy target 1' }, { name: 'dummy target 2' }], @@ -68,7 +72,7 @@ describe('getDiffText', () => { }, 'deleted 2 targets', ]; - const removeValueNumericProp = [ + const removeValueNumericProp: DiffTextCase = [ { op: 'remove', originalValue: 'foo', @@ -76,7 +80,7 @@ describe('getDiffText', () => { }, 'deleted item 3', ]; - const removeValueProp = [ + const removeValueProp: DiffTextCase = [ { op: 'remove', originalValue: 'foo', @@ -84,7 +88,7 @@ describe('getDiffText', () => { }, 'deleted targets', ]; - const replaceValueNumericProp = [ + const replaceValueNumericProp: DiffTextCase = [ { op: 'replace', originalValue: 'foo', @@ -93,7 +97,7 @@ describe('getDiffText', () => { }, 'changed item 3', ]; - const replaceValueProp = [ + const replaceValueProp: DiffTextCase = [ { op: 'replace', originalValue: 'foo', @@ -120,8 +124,8 @@ describe('getDiffText', () => { test.each(cases)( 'returns a semantic message based on the type of diff, the values and the location of the change', - (diff: Diff, expected: string) => { - expect(getDiffText(diff)).toBe(expected); + (diff: Partial, expected: string) => { + expect(getDiffText((diff as unknown) as Diff)).toBe(expected); } ); }); diff --git a/public/app/features/expressions/ExpressionQueryEditor.tsx b/public/app/features/expressions/ExpressionQueryEditor.tsx index 8c3d2f5d73b..85783f48959 100644 --- a/public/app/features/expressions/ExpressionQueryEditor.tsx +++ b/public/app/features/expressions/ExpressionQueryEditor.tsx @@ -1,7 +1,6 @@ import React, { PureComponent } from 'react'; -import { SelectableValue, QueryEditorProps } from '@grafana/data'; +import { DataSourceApi, QueryEditorProps, SelectableValue } from '@grafana/data'; import { InlineField, Select } from '@grafana/ui'; -import { ExpressionDatasourceApi } from './ExpressionDatasource'; import { Resample } from './components/Resample'; import { Reduce } from './components/Reduce'; import { Math } from './components/Math'; @@ -9,7 +8,7 @@ import { ClassicConditions } from './components/ClassicConditions'; import { getDefaults } from './utils/expressionTypes'; import { ExpressionQuery, ExpressionQueryType, gelTypes } from './types'; -type Props = QueryEditorProps; +type Props = QueryEditorProps, ExpressionQuery>; const labelWidth = 14; export class ExpressionQueryEditor extends PureComponent { diff --git a/public/app/features/live/dashboard/dashboardWatcher.ts b/public/app/features/live/dashboard/dashboardWatcher.ts index 847cc6f80e1..507dccdd950 100644 --- a/public/app/features/live/dashboard/dashboardWatcher.ts +++ b/public/app/features/live/dashboard/dashboardWatcher.ts @@ -63,7 +63,7 @@ class DashboardWatcher { }; this.leave(); if (uid) { - this.subscription = live.getStream(this.channel).subscribe(this.observer); + this.subscription = live.getStream(this.channel).subscribe(this.observer); } this.uid = uid; } diff --git a/public/app/features/plugins/PluginPage.tsx b/public/app/features/plugins/PluginPage.tsx index 83cf0e260e1..841ab85920b 100644 --- a/public/app/features/plugins/PluginPage.tsx +++ b/public/app/features/plugins/PluginPage.tsx @@ -8,6 +8,7 @@ import { GrafanaTheme2, NavModel, NavModelItem, + PanelPluginMeta, PluginDependencies, PluginInclude, PluginIncludeType, @@ -17,10 +18,9 @@ import { PluginSignatureType, PluginType, UrlQueryMap, - PanelPluginMeta, } from '@grafana/data'; import { AppNotificationSeverity } from 'app/types'; -import { Alert, LinkButton, PluginSignatureBadge, Tooltip, Badge, useStyles2, Icon } from '@grafana/ui'; +import { Alert, Badge, Icon, LinkButton, PluginSignatureBadge, Tooltip, useStyles2 } from '@grafana/ui'; import Page from 'app/core/components/Page/Page'; import { getPluginSettings } from './PluginSettingsCache'; @@ -490,22 +490,29 @@ export function getLoadingNav(): NavModel { }; } -export function loadPlugin(pluginId: string): Promise { - return getPluginSettings(pluginId).then((info) => { - if (info.type === PluginType.app) { - return importAppPlugin(info); - } - if (info.type === PluginType.datasource) { - return importDataSourcePlugin(info); - } - if (info.type === PluginType.panel) { - return importPanelPluginFromMeta(info as PanelPluginMeta); - } - if (info.type === PluginType.renderer) { - return Promise.resolve({ meta: info } as GrafanaPlugin); - } - return Promise.reject('Unknown Plugin type: ' + info.type); - }); +export async function loadPlugin(pluginId: string): Promise { + const info = await getPluginSettings(pluginId); + let result: GrafanaPlugin | undefined; + + if (info.type === PluginType.app) { + result = await importAppPlugin(info); + } + if (info.type === PluginType.datasource) { + result = await importDataSourcePlugin(info); + } + if (info.type === PluginType.panel) { + const panelPlugin = await importPanelPluginFromMeta(info as PanelPluginMeta); + result = (panelPlugin as unknown) as GrafanaPlugin; + } + if (info.type === PluginType.renderer) { + result = { meta: info } as GrafanaPlugin; + } + + if (!result) { + throw new Error('Unknown Plugin type: ' + info.type); + } + + return result; } type PluginSignatureDetailsBadgeProps = { diff --git a/public/app/features/variables/guard.ts b/public/app/features/variables/guard.ts index 3ffca2a248c..bb42a3a1e42 100644 --- a/public/app/features/variables/guard.ts +++ b/public/app/features/variables/guard.ts @@ -8,7 +8,6 @@ import { DataSourceApi, DataSourceJsonData, MetricFindValue, - QueryEditorProps, StandardVariableQuery, StandardVariableSupport, VariableModel, @@ -86,7 +85,7 @@ interface DataSourceWithCustomVariableSupport< > extends DataSourceApi { variables: { getType(): VariableSupportType; - editor: ComponentType>; + editor: VariableQueryEditorType; query(request: DataQueryRequest): Observable; }; } @@ -184,7 +183,7 @@ export function isQueryEditor< >( component: VariableQueryEditorType, datasource: DataSourceApi -): component is ComponentType, TQuery, TOptions, any>> { +): component is VariableQueryEditorType { if (!component) { return false; } diff --git a/public/app/plugins/panel/graph/graph.ts b/public/app/plugins/panel/graph/graph.ts index 0ba97c2d0ed..bd1897557dc 100644 --- a/public/app/plugins/panel/graph/graph.ts +++ b/public/app/plugins/panel/graph/graph.ts @@ -9,7 +9,7 @@ import 'vendor/flot/jquery.flot.dashes'; import './jquery.flot.events'; import $ from 'jquery'; -import { min as _min, max as _max, clone, find, isUndefined, map, toNumber, sortBy as _sortBy, flatten } from 'lodash'; +import { clone, find, flatten, isUndefined, map, max as _max, min as _min, sortBy as _sortBy, toNumber } from 'lodash'; import { tickStep } from 'app/core/utils/ticks'; import { coreModule, updateLegendValues } from 'app/core/core'; import GraphTooltip from './graph_tooltip'; @@ -37,6 +37,7 @@ import { getTimeField, getValueFormat, hasLinks, + LegacyEventHandler, LegacyGraphHoverClearEvent, LegacyGraphHoverEvent, LinkModelSupplier, @@ -67,7 +68,13 @@ class GraphElement { timeRegionManager: TimeRegionManager; declare legendElem: HTMLElement; - constructor(private scope: any, private elem: JQuery, private timeSrv: TimeSrv) { + constructor( + private scope: any, + private elem: JQuery & { + bind(eventType: string, handler: (eventObject: JQueryEventObject, ...args: any[]) => any): JQuery; // need to extend with Plot + }, + private timeSrv: TimeSrv + ) { this.ctrl = scope.ctrl; this.contextMenu = scope.ctrl.contextMenuCtrl; this.dashboard = this.ctrl.dashboard; @@ -167,7 +174,7 @@ class GraphElement { ReactDOM.unmountComponentAtNode(this.legendElem); } - onGraphHoverClear(event: any, info: any) { + onGraphHoverClear(handler: LegacyEventHandler) { if (this.plot) { this.tooltip.clear(this.plot); } diff --git a/public/app/polyfills/old-mediaquerylist.ts b/public/app/polyfills/old-mediaquerylist.ts index 15f04a6eded..6fdd31397bf 100644 --- a/public/app/polyfills/old-mediaquerylist.ts +++ b/public/app/polyfills/old-mediaquerylist.ts @@ -8,9 +8,11 @@ window.matchMedia = (mediaQueryString) => { let mql = oMatchMedia(mediaQueryString); if (!mql.addEventListener) { + // @ts-ignore mql.addEventListener = (type: string, listener: MqlListener) => { mql.addListener(listener); }; + // @ts-ignore mql.removeEventListener = (type: string, listener: MqlListener) => { mql.removeListener(listener); }; diff --git a/scripts/ci-check-strict.sh b/scripts/ci-check-strict.sh index a588e22be28..1d55ae0a3b1 100755 --- a/scripts/ci-check-strict.sh +++ b/scripts/ci-check-strict.sh @@ -3,7 +3,7 @@ set -e echo -e "Collecting code stats (typescript errors & more)" -ERROR_COUNT_LIMIT=39 +ERROR_COUNT_LIMIT=24 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