Chore: Fix all Typescript strict null errors (#26204)

* Chore: Fix typescript strict null errors

* Added new limit

* Fixed ts issue

* fixed tests

* trying to fix type inference

* Fixing more ts errors

* Revert tsconfig option

* Fix

* Fixed code

* More fixes

* fix tests

* Updated snapshot

* Chore: More ts strict null fixes

* More fixes in some really messed up azure config components

* More fixes, current count: 441

* 419

* More fixes

* Fixed invalid initial state in explore

* Fixing tests

* Fixed tests

* Explore fix

* More fixes

* Progress

* Sub 300

* Now at 218

* Progress

* Update

* Progress

* Updated tests

* at 159

* fixed tests

* Progress

* YAy blow 100! at 94

* 10,9,8,7,6,5,4,3,2,1... lift off

* Fixed tests

* Fixed more type errors

Co-authored-by: Ryan McKinley <ryantxu@gmail.com>
pull/26245/head
Torkel Ödegaard 5 years ago committed by GitHub
parent 3d98641a45
commit 8f78b0e7bc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 9
      packages/grafana-data/src/panel/PanelPlugin.ts
  2. 2
      packages/grafana-data/src/types/panel.ts
  3. 2
      packages/grafana-ui/src/components/Tabs/Tab.tsx
  4. 2
      public/app/core/components/QueryOperationRow/QueryOperationAction.test.tsx
  5. 2
      public/app/core/components/QueryOperationRow/QueryOperationAction.tsx
  6. 1
      public/app/core/components/QueryOperationRow/QueryOperationRow.tsx
  7. 2
      public/app/core/components/Select/MetricSelect.tsx
  8. 2
      public/app/core/components/form_dropdown/form_dropdown.ts
  9. 2
      public/app/core/components/query_part/query_part_editor.ts
  10. 2
      public/app/core/components/sidemenu/BottomSection.tsx
  11. 2
      public/app/core/components/sql_part/sql_part_editor.ts
  12. 2
      public/app/core/directives/metric_segment.ts
  13. 5
      public/app/core/directives/rebuild_on_change.ts
  14. 8
      public/app/core/jquery_extended.ts
  15. 40
      public/app/core/logs_model.ts
  16. 2
      public/app/core/nav_model_srv.ts
  17. 6
      public/app/core/reducers/navModel.ts
  18. 7
      public/app/core/reducers/processsAclItems.ts
  19. 1
      public/app/core/services/backend_srv.ts
  20. 2
      public/app/core/services/bridge_srv.test.ts
  21. 34
      public/app/core/services/keybindingSrv.ts
  22. 2
      public/app/core/time_series2.ts
  23. 7
      public/app/core/utils/acl.ts
  24. 8
      public/app/core/utils/browser.ts
  25. 7
      public/app/core/utils/dag.ts
  26. 1
      public/app/core/utils/explore.ts
  27. 2
      public/app/core/utils/richHistory.ts
  28. 9
      public/app/features/admin/UserCreatePage.tsx
  29. 2
      public/app/features/admin/UserLdapSyncInfo.tsx
  30. 8
      public/app/features/admin/UserOrgs.tsx
  31. 4
      public/app/features/admin/UserSyncInfo.tsx
  32. 2
      public/app/features/alerting/AlertTab.tsx
  33. 4
      public/app/features/alerting/AlertTabCtrl.ts
  34. 2
      public/app/features/alerting/TestRuleResult.tsx
  35. 4
      public/app/features/alerting/getAlertingValidationMessage.ts
  36. 2
      public/app/features/alerting/state/alertDef.ts
  37. 2
      public/app/features/alerting/state/selectors.ts
  38. 6
      public/app/features/annotations/event_editor.ts
  39. 2
      public/app/features/annotations/event_manager.ts
  40. 5
      public/app/features/api-keys/__mocks__/apiKeysMock.ts
  41. 2
      public/app/features/dashboard/components/DashNav/DashNav.tsx
  42. 2
      public/app/features/dashboard/components/DashboardRow/DashboardRow.tsx
  43. 6
      public/app/features/dashboard/components/DashboardSettings/DashboardSettings.tsx
  44. 4
      public/app/features/dashboard/components/DashboardSettings/SettingsCtrl.ts
  45. 3
      public/app/features/dashboard/components/FolderPicker/FolderPickerCtrl.ts
  46. 33
      public/app/features/dashboard/components/Inspector/InspectDataTab.tsx
  47. 16
      public/app/features/dashboard/components/Inspector/InspectJSONTab.tsx
  48. 8
      public/app/features/dashboard/components/Inspector/PanelInspector.tsx
  49. 2
      public/app/features/dashboard/components/Inspector/QueryInspector.tsx
  50. 6
      public/app/features/dashboard/components/PanelEditor/AngularPanelOptions.tsx
  51. 4
      public/app/features/dashboard/components/PanelEditor/FieldConfigEditor.tsx
  52. 10
      public/app/features/dashboard/components/PanelEditor/OptionsPaneContent.tsx
  53. 2
      public/app/features/dashboard/components/PanelEditor/OverrideEditor.tsx
  54. 2
      public/app/features/dashboard/components/PanelEditor/PanelEditorTabs.tsx
  55. 4
      public/app/features/dashboard/components/PanelEditor/PanelOptionsTab.tsx
  56. 2
      public/app/features/dashboard/components/PanelEditor/usePanelLatestData.ts
  57. 4
      public/app/features/dashboard/components/RepeatRowSelect/RepeatRowSelect.tsx
  58. 2
      public/app/features/dashboard/components/RowOptions/RowOptionsButton.tsx
  59. 6
      public/app/features/dashboard/components/RowOptions/RowOptionsForm.tsx
  60. 4
      public/app/features/dashboard/components/SaveDashboard/forms/SaveDashboardAsForm.tsx
  61. 4
      public/app/features/dashboard/components/SaveDashboard/forms/SaveDashboardForm.tsx
  62. 5
      public/app/features/dashboard/components/SaveDashboard/useDashboardSave.tsx
  63. 4
      public/app/features/dashboard/components/ShareModal/ShareModal.tsx
  64. 3
      public/app/features/dashboard/components/TransformationsEditor/TransformationOperationRow.tsx
  65. 2
      public/app/features/explore/state/actions.test.ts
  66. 10
      public/app/features/explore/state/reducers.test.ts
  67. 12
      public/app/features/plugins/__mocks__/pluginMocks.ts
  68. 2
      public/app/plugins/datasource/cloudwatch/datasource.ts
  69. 9
      public/app/plugins/datasource/elasticsearch/datasource.test.ts
  70. 2
      public/app/plugins/datasource/elasticsearch/specs/index_pattern.test.ts
  71. 1
      public/app/plugins/datasource/loki/datasource.test.ts
  72. 3
      tsconfig.json

@ -158,11 +158,14 @@ export class PanelPlugin<TOptions = any, TFieldConfigOptions extends object = an
return this._fieldConfigRegistry; return this._fieldConfigRegistry;
} }
get optionEditors() { get optionEditors(): PanelOptionEditorsRegistry {
if (!this._optionEditors && this.registerOptionEditors) { if (!this._optionEditors) {
const builder = new PanelOptionsEditorBuilder<TOptions>(); const builder = new PanelOptionsEditorBuilder<TOptions>();
this.registerOptionEditors(builder);
this._optionEditors = builder.getRegistry(); this._optionEditors = builder.getRegistry();
if (this.registerOptionEditors) {
this.registerOptionEditors(builder);
}
} }
return this._optionEditors; return this._optionEditors;

@ -81,7 +81,7 @@ export interface PanelEditorProps<T = any> {
callback?: () => void callback?: () => void
) => void; ) => void;
/** Result set of panel queries */ /** Result set of panel queries */
data: PanelData; data?: PanelData;
} }
export interface PanelModel<TOptions = any> { export interface PanelModel<TOptions = any> {

@ -16,7 +16,7 @@ export interface TabProps extends HTMLProps<HTMLLIElement> {
icon?: IconName; icon?: IconName;
onChangeTab: (event?: React.MouseEvent<HTMLLIElement>) => void; onChangeTab: (event?: React.MouseEvent<HTMLLIElement>) => void;
/** A number rendered next to the text. Usually used to display the number of items in a tab's view. */ /** A number rendered next to the text. Usually used to display the number of items in a tab's view. */
counter?: number; counter?: number | null;
} }
export const Tab = React.forwardRef<HTMLLIElement, TabProps>( export const Tab = React.forwardRef<HTMLLIElement, TabProps>(

@ -4,7 +4,7 @@ import { shallow } from 'enzyme';
describe('QueryOperationAction', () => { describe('QueryOperationAction', () => {
it('renders', () => { it('renders', () => {
expect(() => shallow(<QueryOperationAction icon="panel-add" onClick={() => {}} />)).not.toThrow(); expect(() => shallow(<QueryOperationAction title="test" icon="panel-add" onClick={() => {}} />)).not.toThrow();
}); });
describe('when disabled', () => { describe('when disabled', () => {
it('does not call onClick handler', () => { it('does not call onClick handler', () => {

@ -6,7 +6,7 @@ import { selectors } from '@grafana/e2e-selectors';
interface QueryOperationActionProps { interface QueryOperationActionProps {
icon: IconName; icon: IconName;
title?: string; title: string;
onClick: (e: React.MouseEvent) => void; onClick: (e: React.MouseEvent) => void;
disabled?: boolean; disabled?: boolean;
} }

@ -28,6 +28,7 @@ export const QueryOperationRow: React.FC<QueryOperationRowProps> = ({
const [isContentVisible, setIsContentVisible] = useState(isOpen !== undefined ? isOpen : true); const [isContentVisible, setIsContentVisible] = useState(isOpen !== undefined ? isOpen : true);
const theme = useTheme(); const theme = useTheme();
const styles = getQueryOperationRowStyles(theme); const styles = getQueryOperationRowStyles(theme);
useUpdateEffect(() => { useUpdateEffect(() => {
if (isContentVisible) { if (isContentVisible) {
if (onOpen) { if (onOpen) {

@ -7,7 +7,7 @@ import { Variable } from 'app/types/templates';
const { Select } = LegacyForms; const { Select } = LegacyForms;
export interface Props { export interface Props {
onChange: (value: string) => void; onChange: (value: string | undefined) => void;
options: Array<SelectableValue<string>>; options: Array<SelectableValue<string>>;
isSearchable: boolean; isSearchable: boolean;
value: string; value: string;

@ -218,7 +218,7 @@ export class FormDropdownCtrl {
} }
open() { open() {
this.inputElement.css('width', Math.max(this.linkElement.width(), 80) + 16 + 'px'); this.inputElement.css('width', Math.max(this.linkElement.width()!, 80) + 16 + 'px');
this.inputElement.show(); this.inputElement.show();
this.inputElement.focus(); this.inputElement.focus();

@ -39,7 +39,7 @@ export function queryPartEditorDirective(templateSrv: any) {
const $input = $link.next(); const $input = $link.next();
$input.val(part.params[paramIndex]); $input.val(part.params[paramIndex]);
$input.css('width', $link.width() + 16 + 'px'); $input.css('width', $link.width()! + 16 + 'px');
$link.hide(); $link.hide();
$input.show(); $input.show();

@ -8,7 +8,7 @@ import { NavModelItem } from '@grafana/data';
export default function BottomSection() { export default function BottomSection() {
const navTree: NavModelItem[] = _.cloneDeep(config.bootData.navTree); const navTree: NavModelItem[] = _.cloneDeep(config.bootData.navTree);
const bottomNav: NavModelItem[] = _.filter(navTree, item => item.hideFromMenu); const bottomNav: NavModelItem[] = navTree.filter(item => item.hideFromMenu);
const isSignedIn = contextSrv.isSignedIn; const isSignedIn = contextSrv.isSignedIn;
const user = contextSrv.user; const user = contextSrv.user;

@ -39,7 +39,7 @@ export function sqlPartEditorDirective(templateSrv: any) {
const $input = $link.next(); const $input = $link.next();
$input.val(part.params[paramIndex]); $input.val(part.params[paramIndex]);
$input.css('width', $link.width() + 16 + 'px'); $input.css('width', $link.width()! + 16 + 'px');
$link.hide(); $link.hide();
$input.show(); $input.show();

@ -164,7 +164,7 @@ export function metricSegment($compile: any, $sce: any, templateSrv: TemplateSrv
$button.click(() => { $button.click(() => {
options = null; options = null;
$input.css('width', Math.max($button.width(), 80) + 16 + 'px'); $input.css('width', Math.max($button.width()!, 80) + 16 + 'px');
$button.hide(); $button.hide();
$input.show(); $input.show();

@ -4,7 +4,7 @@ import coreModule from '../core_module';
function getBlockNodes(nodes: any[]) { function getBlockNodes(nodes: any[]) {
let node = nodes[0]; let node = nodes[0];
const endNode = nodes[nodes.length - 1]; const endNode = nodes[nodes.length - 1];
let blockNodes: any[]; let blockNodes: any[] | undefined;
node = node.nextSibling; node = node.nextSibling;
for (let i = 1; node !== endNode && node; i++) { for (let i = 1; node !== endNode && node; i++) {
@ -12,7 +12,8 @@ function getBlockNodes(nodes: any[]) {
if (!blockNodes) { if (!blockNodes) {
blockNodes = $([].slice.call(nodes, 0, i)) as any; blockNodes = $([].slice.call(nodes, 0, i)) as any;
} }
blockNodes.push(node);
blockNodes!.push(node);
} }
node = node.nextSibling; node = node.nextSibling;
} }

@ -39,11 +39,11 @@ $.fn.place_tt = (() => {
]); ]);
} }
width = $tooltip.outerWidth(true); width = $tooltip.outerWidth(true)!;
height = $tooltip.outerHeight(true); height = $tooltip.outerHeight(true)!;
const left = x + opts.offset + width > $win.width() ? x - opts.offset - width : x + opts.offset; const left = x + opts.offset + width > $win.width()! ? x - opts.offset - width : x + opts.offset;
const top = y + opts.offset + height > $win.height() ? y - opts.offset - height : y + opts.offset; const top = y + opts.offset + height > $win.height()! ? y - opts.offset - height : y + opts.offset;
$tooltip.css('left', left > 0 ? left : 0); $tooltip.css('left', left > 0 ? left : 0);
$tooltip.css('top', top > 0 ? top : 0); $tooltip.css('top', top > 0 ? top : 0);

@ -141,24 +141,21 @@ export function makeSeriesForLogs(sortedRows: LogRowModel[], bucketSize: number,
const data = toDataFrame(series); const data = toDataFrame(series);
const fieldCache = new FieldCache(data); const fieldCache = new FieldCache(data);
const timeField = fieldCache.getFirstFieldOfType(FieldType.time); const timeField = fieldCache.getFirstFieldOfType(FieldType.time)!;
if (timeField) { timeField.display = getDisplayProcessor({
timeField.display = getDisplayProcessor({ field: timeField,
field: timeField, timeZone,
timeZone, });
});
}
const valueField = fieldCache.getFirstFieldOfType(FieldType.number); const valueField = fieldCache.getFirstFieldOfType(FieldType.number)!;
if (valueField) { valueField.config = {
valueField.config = { ...valueField.config,
...valueField.config, color: series.color,
color: series.color, };
};
valueField.name = series.alias; valueField.name = series.alias;
const fieldDisplayProcessor = getDisplayProcessor({ field: valueField, timeZone }); const fieldDisplayProcessor = getDisplayProcessor({ field: valueField, timeZone });
valueField.display = (value: any) => ({ ...fieldDisplayProcessor(value), color: series.color }); valueField.display = (value: any) => ({ ...fieldDisplayProcessor(value), color: series.color });
}
const points = getFlotPairs({ const points = getFlotPairs({
xField: timeField, xField: timeField,
@ -415,20 +412,23 @@ export function logSeriesToLogsModel(logSeries: DataFrame[]): LogsModel | undefi
// Hack to print loki stats in Explore. Should be using proper stats display via drawer in Explore (rework in 7.1) // Hack to print loki stats in Explore. Should be using proper stats display via drawer in Explore (rework in 7.1)
let totalBytes = 0; let totalBytes = 0;
const queriesVisited: { [refId: string]: boolean } = {}; const queriesVisited: { [refId: string]: boolean } = {};
for (const series of logSeries) { for (const series of logSeries) {
const totalBytesKey = series.meta?.custom?.lokiQueryStatKey; const totalBytesKey = series.meta?.custom?.lokiQueryStatKey;
// Stats are per query, keeping track by refId const { refId } = series; // Stats are per query, keeping track by refId
const { refId } = series;
if (refId && !queriesVisited[refId]) { if (refId && !queriesVisited[refId]) {
if (totalBytesKey && series.meta?.stats) { if (totalBytesKey && series.meta?.stats) {
const byteStat = series.meta?.stats.find(stat => stat.displayName === totalBytesKey); const byteStat = series.meta.stats.find(stat => stat.displayName === totalBytesKey);
if (byteStat) { if (byteStat) {
totalBytes += byteStat.value; totalBytes += byteStat.value;
} }
} }
queriesVisited[refId] = true; queriesVisited[refId] = true;
} }
} }
if (totalBytes > 0) { if (totalBytes > 0) {
const { text, suffix } = decimalSIPrefix('B')(totalBytes); const { text, suffix } = decimalSIPrefix('B')(totalBytes);
meta.push({ meta.push({

@ -19,7 +19,7 @@ export class NavModelSrv {
let children = this.navItems; let children = this.navItems;
const nav = { const nav = {
breadcrumbs: [], breadcrumbs: [],
} as NavModel; } as any;
for (const id of args) { for (const id of args) {
// if its a number then it's the index to use for main // if its a number then it's the index to use for main

@ -12,7 +12,7 @@ export function buildInitialState(): NavIndex {
function buildNavIndex(navIndex: NavIndex, children: NavModelItem[], parentItem?: NavModelItem) { function buildNavIndex(navIndex: NavIndex, children: NavModelItem[], parentItem?: NavModelItem) {
for (const node of children) { for (const node of children) {
navIndex[node.id] = { navIndex[node.id!] = {
...node, ...node,
parentItem: parentItem, parentItem: parentItem,
}; };
@ -52,8 +52,8 @@ export const navIndexReducer = (state: NavIndex = initialState, action: AnyActio
const newPages: NavIndex = {}; const newPages: NavIndex = {};
const payload = action.payload; const payload = action.payload;
for (const node of payload.children) { for (const node of payload.children!) {
newPages[node.id] = { newPages[node.id!] = {
...node, ...node,
parentItem: payload, parentItem: payload,
}; };

@ -1,17 +1,18 @@
import { DashboardAcl, DashboardAclDTO } from 'app/types/acl'; import { DashboardAcl, DashboardAclDTO } from 'app/types/acl';
export function processAclItems(items: DashboardAclDTO[]): DashboardAcl[] { export function processAclItems(items: DashboardAclDTO[]): DashboardAcl[] {
return items.map(processAclItem).sort((a, b) => b.sortRank - a.sortRank || a.name.localeCompare(b.name)); return items.map(processAclItem).sort((a, b) => b.sortRank! - a.sortRank! || a.name!.localeCompare(b.name!));
} }
function processAclItem(dto: DashboardAclDTO): DashboardAcl { function processAclItem(dto: DashboardAclDTO): DashboardAcl {
const item = dto as DashboardAcl; const item = dto as DashboardAcl;
item.sortRank = 0; item.sortRank = 0;
if (item.userId > 0) {
if (item.userId! > 0) {
item.name = item.userLogin; item.name = item.userLogin;
item.sortRank = 10; item.sortRank = 10;
} else if (item.teamId > 0) { } else if (item.teamId! > 0) {
item.name = item.team; item.name = item.team;
item.sortRank = 20; item.sortRank = 20;
} else if (item.role) { } else if (item.role) {

@ -113,6 +113,7 @@ export class BackendSrv implements BackendService {
} }
if (this.noBackendCache) { if (this.noBackendCache) {
options.headers = options.headers ?? {};
options.headers['X-Grafana-NoCache'] = 'true'; options.headers['X-Grafana-NoCache'] = 'true';
} }
} }

@ -71,6 +71,6 @@ describe('when checking template variables', () => {
'var-test': 'asd', 'var-test': 'asd',
}; };
expect(findTemplateVarChanges(a, b)['var-test']).toEqual(['test']); expect(findTemplateVarChanges(a, b)!['var-test']).toEqual(['test']);
}); });
}); });

@ -228,6 +228,10 @@ export class KeybindingSrv {
// edit panel // edit panel
this.bind('e', () => { this.bind('e', () => {
if (!dashboard.meta.focusPanelId) {
return;
}
if (dashboard.canEditPanelById(dashboard.meta.focusPanelId)) { if (dashboard.canEditPanelById(dashboard.meta.focusPanelId)) {
const search = _.extend(this.$location.search(), { editPanel: dashboard.meta.focusPanelId }); const search = _.extend(this.$location.search(), { editPanel: dashboard.meta.focusPanelId });
this.$location.search(search); this.$location.search(search);
@ -253,7 +257,7 @@ export class KeybindingSrv {
if (this.contextSrv.hasAccessToExplore()) { if (this.contextSrv.hasAccessToExplore()) {
this.bind('x', async () => { this.bind('x', async () => {
if (dashboard.meta.focusPanelId) { if (dashboard.meta.focusPanelId) {
const panel = dashboard.getPanelById(dashboard.meta.focusPanelId); const panel = dashboard.getPanelById(dashboard.meta.focusPanelId)!;
const datasource = await this.datasourceSrv.get(panel.datasource); const datasource = await this.datasourceSrv.get(panel.datasource);
const url = await getExploreUrl({ const url = await getExploreUrl({
panel, panel,
@ -262,10 +266,12 @@ export class KeybindingSrv {
datasourceSrv: this.datasourceSrv, datasourceSrv: this.datasourceSrv,
timeSrv: this.timeSrv, timeSrv: this.timeSrv,
}); });
const urlWithoutBase = locationUtil.stripBaseFromUrl(url);
if (urlWithoutBase) { if (url) {
this.$timeout(() => this.$location.url(urlWithoutBase)); const urlWithoutBase = locationUtil.stripBaseFromUrl(url);
if (urlWithoutBase) {
this.$timeout(() => this.$location.url(urlWithoutBase));
}
} }
} }
}); });
@ -273,16 +279,20 @@ export class KeybindingSrv {
// delete panel // delete panel
this.bind('p r', () => { this.bind('p r', () => {
if (dashboard.canEditPanelById(dashboard.meta.focusPanelId)) { const panelId = dashboard.meta.focusPanelId;
appEvents.emit(CoreEvents.removePanel, dashboard.meta.focusPanelId);
if (panelId && dashboard.canEditPanelById(panelId)) {
appEvents.emit(CoreEvents.removePanel, panelId);
dashboard.meta.focusPanelId = 0; dashboard.meta.focusPanelId = 0;
} }
}); });
// duplicate panel // duplicate panel
this.bind('p d', () => { this.bind('p d', () => {
if (dashboard.canEditPanelById(dashboard.meta.focusPanelId)) { const panelId = dashboard.meta.focusPanelId;
const panelIndex = dashboard.getPanelInfoById(dashboard.meta.focusPanelId).index;
if (panelId && dashboard.canEditPanelById(panelId)) {
const panelIndex = dashboard.getPanelInfoById(panelId)!.index;
dashboard.duplicatePanel(dashboard.panels[panelIndex]); dashboard.duplicatePanel(dashboard.panels[panelIndex]);
} }
}); });
@ -305,11 +315,11 @@ export class KeybindingSrv {
// toggle panel legend // toggle panel legend
this.bind('p l', () => { this.bind('p l', () => {
if (dashboard.meta.focusPanelId) { if (dashboard.meta.focusPanelId) {
const panelInfo = dashboard.getPanelInfoById(dashboard.meta.focusPanelId); const panelInfo = dashboard.getPanelInfoById(dashboard.meta.focusPanelId)!;
if (panelInfo.panel.legend) { if (panelInfo.panel.legend) {
const panelRef = dashboard.getPanelById(dashboard.meta.focusPanelId); panelInfo.panel.legend.show = !panelInfo.panel.legend.show;
panelRef.legend.show = !panelRef.legend.show; panelInfo.panel.render();
panelRef.render();
} }
} }
}); });

@ -348,7 +348,7 @@ export default class TimeSeries {
this.scaledDecimals = scaledDecimals; this.scaledDecimals = scaledDecimals;
} }
formatValue(value: number) { formatValue(value: number | null) {
if (!_.isFinite(value)) { if (!_.isFinite(value)) {
value = null; // Prevent NaN formatting value = null; // Prevent NaN formatting
} }

@ -1,17 +1,18 @@
import { DashboardAcl, DashboardAclDTO } from 'app/types/acl'; import { DashboardAcl, DashboardAclDTO } from 'app/types/acl';
export function processAclItems(items: DashboardAclDTO[]): DashboardAcl[] { export function processAclItems(items: DashboardAclDTO[]): DashboardAcl[] {
return items.map(processAclItem).sort((a, b) => b.sortRank - a.sortRank || a.name.localeCompare(b.name)); return items.map(processAclItem).sort((a, b) => b.sortRank! - a.sortRank! || a.name!.localeCompare(b.name!));
} }
function processAclItem(dto: DashboardAclDTO): DashboardAcl { function processAclItem(dto: DashboardAclDTO): DashboardAcl {
const item = dto as DashboardAcl; const item = dto as DashboardAcl;
item.sortRank = 0; item.sortRank = 0;
if (item.userId > 0) {
if (item.userId! > 0) {
item.name = item.userLogin; item.name = item.userLogin;
item.sortRank = 10; item.sortRank = 10;
} else if (item.teamId > 0) { } else if (item.teamId! > 0) {
item.name = item.team; item.name = item.team;
item.sortRank = 20; item.sortRank = 20;
} else if (item.role) { } else if (item.role) {

@ -13,17 +13,17 @@ export function checkBrowserCompatibility() {
*/ */
const isEdgeVersion = /Edge\/([0-9.]+)/.exec(navigator.userAgent); const isEdgeVersion = /Edge\/([0-9.]+)/.exec(navigator.userAgent);
if (isIE && parseFloat(/Trident\/([0-9.]+)/.exec(navigator.userAgent)[1]) <= 7) { if (isIE && parseFloat(/Trident\/([0-9.]+)/.exec(navigator.userAgent)![1]) <= 7) {
return false; return false;
} else if ( } else if (
isEdge && isEdge &&
((isEdgeVersion && parseFloat(isEdgeVersion[1]) <= 16) || ((isEdgeVersion && parseFloat(isEdgeVersion[1]) <= 16) ||
parseFloat(/Edg\/([0-9.]+)/.exec(navigator.userAgent)[1]) <= 16) parseFloat(/Edg\/([0-9.]+)/.exec(navigator.userAgent)![1]) <= 16)
) { ) {
return false; return false;
} else if (isFirefox && parseFloat(/Firefox\/([0-9.]+)/.exec(navigator.userAgent)[1]) <= 64) { } else if (isFirefox && parseFloat(/Firefox\/([0-9.]+)/.exec(navigator.userAgent)![1]) <= 64) {
return false; return false;
} else if (isChrome && parseFloat(/Chrome\/([0-9.]+)/.exec(navigator.userAgent)[1]) <= 54) { } else if (isChrome && parseFloat(/Chrome\/([0-9.]+)/.exec(navigator.userAgent)![1]) <= 54) {
return false; return false;
} }

@ -60,9 +60,6 @@ export class Edge {
if (pos > -1) { if (pos > -1) {
onode.inputEdges.splice(pos, 1); onode.inputEdges.splice(pos, 1);
} }
this.inputNode = null;
this.outputNode = null;
} }
} }
@ -79,7 +76,7 @@ export class Node {
this.outputEdges = []; this.outputEdges = [];
} }
getEdgeFrom(from: string | Node): Edge { getEdgeFrom(from: string | Node): Edge | null | undefined {
if (!from) { if (!from) {
return null; return null;
} }
@ -91,7 +88,7 @@ export class Node {
return this.inputEdges.find(e => e.inputNode.name === from); return this.inputEdges.find(e => e.inputNode.name === from);
} }
getEdgeTo(to: string | Node): Edge { getEdgeTo(to: string | Node): Edge | null | undefined {
if (!to) { if (!to) {
return null; return null;
} }

@ -113,6 +113,7 @@ export async function getExploreUrl(args: GetExploreUrlArguments): Promise<strin
const exploreState = JSON.stringify({ ...state, originPanelId: panel.getSavedId() }); const exploreState = JSON.stringify({ ...state, originPanelId: panel.getSavedId() });
url = urlUtil.renderUrl('/explore', { left: exploreState }); url = urlUtil.renderUrl('/explore', { left: exploreState });
} }
return url; return url;
} }

@ -188,7 +188,7 @@ export const createUrlFromRichHistory = (query: RichHistoryQuery) => {
}; };
const serializedState = serializeStateToUrlParam(exploreState, true); const serializedState = serializeStateToUrlParam(exploreState, true);
const baseUrl = /.*(?=\/explore)/.exec(`${window.location.href}`)[0]; const baseUrl = /.*(?=\/explore)/.exec(`${window.location.href}`)![0];
const url = urlUtil.renderUrl(`${baseUrl}/explore`, { left: serializedState }); const url = urlUtil.renderUrl(`${baseUrl}/explore`, { left: serializedState });
return url; return url;
}; };

@ -36,7 +36,12 @@ const UserCreatePage: React.FC<UserCreatePageProps> = ({ navModel, updateLocatio
{({ register, errors }) => { {({ register, errors }) => {
return ( return (
<> <>
<Field label="Name" required invalid={!!errors.name} error={!!errors.name && 'Name is required'}> <Field
label="Name"
required
invalid={!!errors.name}
error={errors.name ? 'Name is required' : undefined}
>
<Input name="name" ref={register({ required: true })} /> <Input name="name" ref={register({ required: true })} />
</Field> </Field>
@ -51,7 +56,7 @@ const UserCreatePage: React.FC<UserCreatePageProps> = ({ navModel, updateLocatio
label="Password" label="Password"
required required
invalid={!!errors.password} invalid={!!errors.password}
error={!!errors.password && 'Password is required and must contain at least 4 characters'} error={errors.password ? 'Password is required and must contain at least 4 characters' : undefined}
> >
<Input <Input
type="password" type="password"

@ -24,7 +24,7 @@ export class UserLdapSyncInfo extends PureComponent<Props, State> {
const prevSyncSuccessful = ldapSyncInfo && ldapSyncInfo.prevSync; const prevSyncSuccessful = ldapSyncInfo && ldapSyncInfo.prevSync;
const nextSyncSuccessful = ldapSyncInfo && ldapSyncInfo.nextSync; const nextSyncSuccessful = ldapSyncInfo && ldapSyncInfo.nextSync;
const nextSyncTime = nextSyncSuccessful ? dateTimeFormat(ldapSyncInfo.nextSync, { format }) : ''; const nextSyncTime = nextSyncSuccessful ? dateTimeFormat(ldapSyncInfo.nextSync, { format }) : '';
const prevSyncTime = prevSyncSuccessful ? dateTimeFormat(ldapSyncInfo.prevSync.started, { format }) : ''; const prevSyncTime = prevSyncSuccessful ? dateTimeFormat(ldapSyncInfo.prevSync!.started, { format }) : '';
const debugLDAPMappingURL = `${debugLDAPMappingBaseURL}?user=${user && user.login}`; const debugLDAPMappingURL = `${debugLDAPMappingBaseURL}?user=${user && user.login}`;
return ( return (

@ -189,7 +189,7 @@ interface AddToOrgModalProps {
} }
interface AddToOrgModalState { interface AddToOrgModalState {
selectedOrg: Organization; selectedOrg: Organization | null;
role: OrgRole; role: OrgRole;
} }
@ -211,11 +211,13 @@ export class AddToOrgModal extends PureComponent<AddToOrgModalProps, AddToOrgMod
onAddUserToOrg = () => { onAddUserToOrg = () => {
const { selectedOrg, role } = this.state; const { selectedOrg, role } = this.state;
this.props.onOrgAdd(selectedOrg.id, role); this.props.onOrgAdd(selectedOrg!.id, role);
}; };
onCancel = () => { onCancel = () => {
this.props.onDismiss(); if (this.props.onDismiss) {
this.props.onDismiss();
}
}; };
render() { render() {

@ -36,9 +36,9 @@ export class UserSyncInfo extends PureComponent<Props, State> {
const { syncInfo, disableSync } = this.props; const { syncInfo, disableSync } = this.props;
const { isSyncing } = this.state; const { isSyncing } = this.state;
const nextSyncSuccessful = syncInfo && syncInfo.nextSync; const nextSyncSuccessful = syncInfo && syncInfo.nextSync;
const nextSyncTime = nextSyncSuccessful ? dateTimeFormat(syncInfo.nextSync, { format }) : ''; const nextSyncTime = nextSyncSuccessful ? dateTimeFormat(syncInfo.nextSync!, { format }) : '';
const prevSyncSuccessful = syncInfo && syncInfo.prevSync; const prevSyncSuccessful = syncInfo && syncInfo.prevSync;
const prevSyncTime = prevSyncSuccessful ? dateTimeFormat(syncInfo.prevSync, { format }) : ''; const prevSyncTime = prevSyncSuccessful ? dateTimeFormat(syncInfo.prevSync!, { format }) : '';
const isDisabled = isSyncing || disableSync; const isDisabled = isSyncing || disableSync;
return ( return (

@ -23,7 +23,7 @@ interface OwnProps {
} }
interface ConnectedProps { interface ConnectedProps {
angularPanelComponent: AngularComponent; angularPanelComponent?: AngularComponent | null;
} }
interface DispatchProps { interface DispatchProps {

@ -214,7 +214,7 @@ export class AlertTabCtrl {
memo.push(this.buildConditionModel(value)); memo.push(this.buildConditionModel(value));
return memo; return memo;
}, },
[] [] as string[]
); );
ThresholdMapper.alertToGraphThresholds(this.panel); ThresholdMapper.alertToGraphThresholds(this.panel);
@ -282,7 +282,7 @@ export class AlertTabCtrl {
} }
let firstTarget; let firstTarget;
let foundTarget: DataQuery = null; let foundTarget: DataQuery | null = null;
const promises: Array<Promise<any>> = []; const promises: Array<Promise<any>> = [];
for (const condition of this.alert.conditions) { for (const condition of this.alert.conditions) {

@ -14,7 +14,7 @@ export interface Props {
interface State { interface State {
isLoading: boolean; isLoading: boolean;
allNodesExpanded: boolean; allNodesExpanded: boolean | null;
testRuleResponse: {}; testRuleResponse: {};
} }

@ -11,10 +11,10 @@ export const getDefaultCondition = () => ({
}); });
export const getAlertingValidationMessage = async ( export const getAlertingValidationMessage = async (
transformations: DataTransformerConfig[], transformations: DataTransformerConfig[] | undefined,
targets: DataQuery[], targets: DataQuery[],
datasourceSrv: DataSourceSrv, datasourceSrv: DataSourceSrv,
datasourceName: string datasourceName: string | null
): Promise<string> => { ): Promise<string> => {
if (targets.length === 0) { if (targets.length === 0) {
return 'Could not find any metric queries'; return 'Could not find any metric queries';

@ -134,7 +134,7 @@ function joinEvalMatches(matches: any, separator: string) {
return res; return res;
}, },
[] [] as string[]
).join(separator); ).join(separator);
} }

@ -6,6 +6,6 @@ export const getAlertRuleItems = (state: AlertRulesState) => {
const regex = new RegExp(state.searchQuery, 'i'); const regex = new RegExp(state.searchQuery, 'i');
return state.items.filter(item => { return state.items.filter(item => {
return regex.test(item.name) || regex.test(item.stateText) || regex.test(item.info); return regex.test(item.name) || regex.test(item.stateText) || regex.test(item.info!);
}); });
}; };

@ -24,7 +24,7 @@ export class EventEditorCtrl {
this.event.timeEnd = tryEpochToMoment(this.event.timeEnd); this.event.timeEnd = tryEpochToMoment(this.event.timeEnd);
} }
this.timeFormated = this.panelCtrl.dashboard.formatDate(this.event.time); this.timeFormated = this.panelCtrl.dashboard.formatDate(this.event.time!);
} }
save() { save() {
@ -33,11 +33,11 @@ export class EventEditorCtrl {
} }
const saveModel = _.cloneDeep(this.event); const saveModel = _.cloneDeep(this.event);
saveModel.time = saveModel.time.valueOf(); saveModel.time = saveModel.time!.valueOf();
saveModel.timeEnd = 0; saveModel.timeEnd = 0;
if (saveModel.isRegion) { if (saveModel.isRegion) {
saveModel.timeEnd = this.event.timeEnd.valueOf(); saveModel.timeEnd = this.event.timeEnd!.valueOf();
if (saveModel.timeEnd < saveModel.time) { if (saveModel.timeEnd < saveModel.time) {
console.log('invalid time'); console.log('invalid time');

@ -13,7 +13,7 @@ import { MetricsPanelCtrl } from 'app/plugins/sdk';
import { AnnotationEvent } from '@grafana/data'; import { AnnotationEvent } from '@grafana/data';
export class EventManager { export class EventManager {
event: AnnotationEvent; event: AnnotationEvent | null;
editorOpen: boolean; editorOpen: boolean;
constructor(private panelCtrl: MetricsPanelCtrl) {} constructor(private panelCtrl: MetricsPanelCtrl) {}

@ -2,12 +2,13 @@
export const getMultipleMockKeys = (numberOfKeys: number): ApiKey[] => { export const getMultipleMockKeys = (numberOfKeys: number): ApiKey[] => {
const keys: ApiKey[] = []; const keys: ApiKey[] = [];
for (let i = 1; i <= numberOfKeys; i++) { for (let i = 1; i <= numberOfKeys; i++) {
keys.push({ keys.push({
id: i, id: i,
name: `test-${i}`, name: `test-${i}`,
role: OrgRole.Viewer, role: OrgRole.Viewer,
secondsToLive: null, secondsToLive: 100,
expiration: '2019-06-04', expiration: '2019-06-04',
}); });
} }
@ -20,7 +21,7 @@ export const getMockKey = (): ApiKey => {
id: 1, id: 1,
name: 'test', name: 'test',
role: OrgRole.Admin, role: OrgRole.Admin,
secondsToLive: null, secondsToLive: 200,
expiration: '2019-06-04', expiration: '2019-06-04',
}; };
}; };

@ -184,7 +184,7 @@ class DashNav extends PureComponent<Props> {
`; `;
const folderTitle = dashboard.meta.folderTitle; const folderTitle = dashboard.meta.folderTitle;
const haveFolder = dashboard.meta.folderId > 0; const haveFolder = (dashboard.meta.folderId ?? 0) > 0;
return ( return (
<> <>

@ -40,7 +40,7 @@ export class DashboardRow extends React.Component<DashboardRowProps, any> {
}); });
}; };
onUpdate = (title: string | null, repeat: string | null) => { onUpdate = (title: string, repeat: string | undefined) => {
this.props.panel['title'] = title; this.props.panel['title'] = title;
this.props.panel['repeat'] = repeat; this.props.panel['repeat'] = repeat;
this.props.panel.render(); this.props.panel.render();

@ -11,12 +11,12 @@ import { updateLocation } from 'app/core/actions';
import { CustomScrollbar } from '@grafana/ui'; import { CustomScrollbar } from '@grafana/ui';
export interface Props { export interface Props {
dashboard: DashboardModel | null; dashboard: DashboardModel;
updateLocation: typeof updateLocation; updateLocation: typeof updateLocation;
} }
export class DashboardSettings extends PureComponent<Props> { export class DashboardSettings extends PureComponent<Props> {
element: HTMLElement; element?: HTMLElement | null;
angularCmp: AngularComponent; angularCmp: AngularComponent;
componentDidMount() { componentDidMount() {
@ -44,7 +44,7 @@ export class DashboardSettings extends PureComponent<Props> {
render() { render() {
const { dashboard } = this.props; const { dashboard } = this.props;
const folderTitle = dashboard.meta.folderTitle; const folderTitle = dashboard.meta.folderTitle;
const haveFolder = dashboard.meta.folderId > 0; const haveFolder = (dashboard.meta.folderId ?? 0) > 0;
return ( return (
<div className="dashboard-settings"> <div className="dashboard-settings">

@ -18,8 +18,8 @@ export class SettingsCtrl {
json: string; json: string;
alertCount: number; alertCount: number;
canSaveAs: boolean; canSaveAs: boolean;
canSave: boolean; canSave?: boolean;
canDelete: boolean; canDelete?: boolean;
sections: any[]; sections: any[];
hasUnsavedFolderChange: boolean; hasUnsavedFolderChange: boolean;
selectors: typeof selectors.pages.Dashboard.Settings.General; selectors: typeof selectors.pages.Dashboard.Settings.General;

@ -146,7 +146,8 @@ export class FolderPickerCtrl {
const rootFolder: { text: string; value: any } = { text: this.rootName, value: 0 }; const rootFolder: { text: string; value: any } = { text: this.rootName, value: 0 };
this.getOptions('').then((result: any[]) => { this.getOptions('').then((result: any[]) => {
let folder: { text: string; value: any }; let folder: { text: string; value: any } | undefined;
if (this.initialFolderId) { if (this.initialFolderId) {
// @ts-ignore // @ts-ignore
folder = _.find(result, { value: this.initialFolderId }); folder = _.find(result, { value: this.initialFolderId });

@ -62,9 +62,10 @@ export class InspectDataTab extends PureComponent<Props, State> {
// Replace the time field with a formatted time // Replace the time field with a formatted time
const { timeIndex, timeField } = getTimeField(dataFrame); const { timeIndex, timeField } = getTimeField(dataFrame);
if (timeField) { if (timeField) {
// Use the configurd date or standandard time display // Use the configurd date or standandard time display
let processor: DisplayProcessor = timeField.display; let processor: DisplayProcessor | undefined = timeField.display;
if (!processor) { if (!processor) {
processor = getDisplayProcessor({ processor = getDisplayProcessor({
field: timeField, field: timeField,
@ -78,7 +79,8 @@ export class InspectDataTab extends PureComponent<Props, State> {
}; };
const fields = [...dataFrame.fields]; const fields = [...dataFrame.fields];
fields[timeIndex] = formattedDateField; fields[timeIndex!] = formattedDateField;
dataFrame = { dataFrame = {
...dataFrame, ...dataFrame,
fields, fields,
@ -100,8 +102,9 @@ export class InspectDataTab extends PureComponent<Props, State> {
transformId: transformId:
item.value === DataTransformerID.seriesToColumns ? DataTransformerID.seriesToColumns : DataTransformerID.noop, item.value === DataTransformerID.seriesToColumns ? DataTransformerID.seriesToColumns : DataTransformerID.noop,
dataFrameIndex: typeof item.value === 'number' ? item.value : 0, dataFrameIndex: typeof item.value === 'number' ? item.value : 0,
selectedDataFrame: item.value, selectedDataFrame: item.value!,
}); });
this.props.onOptionsChange({ this.props.onOptionsChange({
...this.props.options, ...this.props.options,
}); });
@ -127,6 +130,10 @@ export class InspectDataTab extends PureComponent<Props, State> {
const { options } = this.props; const { options } = this.props;
let data = this.props.data; let data = this.props.data;
if (!data) {
return [];
}
if (this.state.transformId !== DataTransformerID.noop) { if (this.state.transformId !== DataTransformerID.noop) {
data = this.getTransformedData(); data = this.getTransformedData();
} }
@ -152,16 +159,21 @@ export class InspectDataTab extends PureComponent<Props, State> {
}); });
} }
getActiveString = () => { getActiveString() {
const { selectedDataFrame } = this.state; const { selectedDataFrame } = this.state;
const { options, data } = this.props; const { options, data } = this.props;
let activeString = ''; let activeString = '';
if (!data) {
return activeString;
}
if (selectedDataFrame === DataTransformerID.seriesToColumns) { if (selectedDataFrame === DataTransformerID.seriesToColumns) {
activeString = 'series joined by time'; activeString = 'series joined by time';
} else { } else {
activeString = getFrameDisplayName(data[selectedDataFrame as number]); activeString = getFrameDisplayName(data[selectedDataFrame as number]);
} }
if (options.withTransforms || options.withFieldConfig) { if (options.withTransforms || options.withFieldConfig) {
activeString += ' - applied '; activeString += ' - applied ';
if (options.withTransforms) { if (options.withTransforms) {
@ -176,10 +188,11 @@ export class InspectDataTab extends PureComponent<Props, State> {
activeString += 'field configuration'; activeString += 'field configuration';
} }
} }
return activeString; return activeString;
}; }
renderDataOptions = (dataFrames: DataFrame[]) => { renderDataOptions(dataFrames: DataFrame[]) {
const { options, onOptionsChange, panel, data } = this.props; const { options, onOptionsChange, panel, data } = this.props;
const { transformId, transformationOptions, selectedDataFrame } = this.state; const { transformId, transformationOptions, selectedDataFrame } = this.state;
@ -193,7 +206,7 @@ export class InspectDataTab extends PureComponent<Props, State> {
let dataSelect = dataFrames; let dataSelect = dataFrames;
if (selectedDataFrame === DataTransformerID.seriesToColumns) { if (selectedDataFrame === DataTransformerID.seriesToColumns) {
dataSelect = data; dataSelect = data!;
} }
const choices = dataSelect.map((frame, index) => { const choices = dataSelect.map((frame, index) => {
@ -222,7 +235,7 @@ export class InspectDataTab extends PureComponent<Props, State> {
className={css` className={css`
margin-bottom: 0; margin-bottom: 0;
`} `}
disabled={!(data.length > 1)} disabled={data!.length < 2}
> >
<Select <Select
options={selectableOptions} options={selectableOptions}
@ -260,7 +273,7 @@ export class InspectDataTab extends PureComponent<Props, State> {
</div> </div>
</QueryOperationRow> </QueryOperationRow>
); );
}; }
render() { render() {
const { isLoading } = this.props; const { isLoading } = this.props;

@ -35,7 +35,7 @@ const options: Array<SelectableValue<ShowContent>> = [
interface Props { interface Props {
dashboard: DashboardModel; dashboard: DashboardModel;
panel: PanelModel; panel: PanelModel;
data: PanelData; data?: PanelData;
onClose: () => void; onClose: () => void;
} }
@ -54,9 +54,9 @@ export class InspectJSONTab extends PureComponent<Props, State> {
} }
onSelectChanged = (item: SelectableValue<ShowContent>) => { onSelectChanged = (item: SelectableValue<ShowContent>) => {
const show = this.getJSONObject(item.value); const show = this.getJSONObject(item.value!);
const text = getPrettyJSON(show); const text = getPrettyJSON(show);
this.setState({ text, show: item.value }); this.setState({ text, show: item.value! });
}; };
// Called onBlur // Called onBlur
@ -64,16 +64,17 @@ export class InspectJSONTab extends PureComponent<Props, State> {
this.setState({ text }); this.setState({ text });
}; };
getJSONObject = (show: ShowContent): any => { getJSONObject(show: ShowContent) {
if (show === ShowContent.PanelData) { if (show === ShowContent.PanelData) {
return this.props.data; return this.props.data;
} }
if (show === ShowContent.DataStructure) { if (show === ShowContent.DataStructure) {
const series = this.props.data?.series; const series = this.props.data?.series;
if (!series) { if (!series) {
return { note: 'Missing Response Data' }; return { note: 'Missing Response Data' };
} }
return this.props.data.series.map(frame => { return this.props.data!.series.map(frame => {
const { table, fields, ...rest } = frame as any; // remove 'table' from arrow response const { table, fields, ...rest } = frame as any; // remove 'table' from arrow response
return { return {
...rest, ...rest,
@ -87,12 +88,13 @@ export class InspectJSONTab extends PureComponent<Props, State> {
}; };
}); });
} }
if (show === ShowContent.PanelJSON) { if (show === ShowContent.PanelJSON) {
return this.props.panel.getSaveModel(); return this.props.panel.getSaveModel();
} }
return { note: `Unknown Object: ${show}` }; return { note: `Unknown Object: ${show}` };
}; }
onApplyPanelModel = () => { onApplyPanelModel = () => {
const { panel, dashboard, onClose } = this.props; const { panel, dashboard, onClose } = this.props;
@ -142,7 +144,7 @@ export class InspectJSONTab extends PureComponent<Props, State> {
height={height} height={height}
language="json" language="json"
showLineNumbers={true} showLineNumbers={true}
showMiniMap={text && text.length > 100} showMiniMap={(text && text.length) > 100}
value={text || ''} value={text || ''}
readOnly={!isPanelJSON} readOnly={!isPanelJSON}
onBlur={this.onTextChanged} onBlur={this.onTextChanged}

@ -25,6 +25,10 @@ export interface ConnectedProps {
export type Props = OwnProps & ConnectedProps; export type Props = OwnProps & ConnectedProps;
const PanelInspectorUnconnected: React.FC<Props> = ({ panel, dashboard, defaultTab, plugin }) => { const PanelInspectorUnconnected: React.FC<Props> = ({ panel, dashboard, defaultTab, plugin }) => {
if (!plugin) {
return null;
}
const dispatch = useDispatch(); const dispatch = useDispatch();
const [dataOptions, setDataOptions] = useState<GetDataOptions>({ const [dataOptions, setDataOptions] = useState<GetDataOptions>({
withTransforms: false, withTransforms: false,
@ -42,10 +46,6 @@ const PanelInspectorUnconnected: React.FC<Props> = ({ panel, dashboard, defaultT
); );
}, [updateLocation]); }, [updateLocation]);
if (!plugin) {
return null;
}
return ( return (
<InspectContent <InspectContent
dashboard={dashboard} dashboard={dashboard}

@ -31,7 +31,7 @@ interface Props {
} }
interface State { interface State {
allNodesExpanded: boolean; allNodesExpanded: boolean | null;
isMocking: boolean; isMocking: boolean;
mockedResponse: string; mockedResponse: string;
dsQuery: DsQuery; dsQuery: DsQuery;

@ -20,7 +20,7 @@ interface OwnProps {
} }
interface ConnectedProps { interface ConnectedProps {
angularPanelComponent: AngularComponent; angularPanelComponent?: AngularComponent | null;
} }
interface DispatchProps { interface DispatchProps {
@ -30,8 +30,8 @@ interface DispatchProps {
type Props = OwnProps & ConnectedProps & DispatchProps; type Props = OwnProps & ConnectedProps & DispatchProps;
export class AngularPanelOptionsUnconnected extends PureComponent<Props> { export class AngularPanelOptionsUnconnected extends PureComponent<Props> {
element?: HTMLElement; element?: HTMLElement | null;
angularOptions: AngularComponent; angularOptions?: AngularComponent | null;
constructor(props: Props) { constructor(props: Props) {
super(props); super(props);

@ -129,11 +129,11 @@ export const OverrideFieldConfigEditor: React.FC<Props> = props => {
export const DefaultFieldConfigEditor: React.FC<Props> = ({ data, onChange, config, plugin }) => { export const DefaultFieldConfigEditor: React.FC<Props> = ({ data, onChange, config, plugin }) => {
const setDefaultValue = useCallback( const setDefaultValue = useCallback(
(name: string, value: any, custom: boolean) => { (name: string, value: any, isCustom: boolean | undefined) => {
const defaults = { ...config.defaults }; const defaults = { ...config.defaults };
const remove = value === undefined || value === null || ''; const remove = value === undefined || value === null || '';
if (custom) { if (isCustom) {
if (defaults.custom) { if (defaults.custom) {
if (remove) { if (remove) {
defaults.custom = { ...defaults.custom }; defaults.custom = { ...defaults.custom };

@ -175,7 +175,7 @@ export const TabsBarContent: React.FC<{
// Show the appropriate tabs // Show the appropriate tabs
let tabs = tabSelections; let tabs = tabSelections;
let active = tabs.find(v => v.value === activeTab); let active = tabs.find(v => v.value === activeTab)!;
// If no field configs hide Fields & Override tab // If no field configs hide Fields & Override tab
if (plugin.fieldConfigRegistry.isEmpty()) { if (plugin.fieldConfigRegistry.isEmpty()) {
@ -191,7 +191,7 @@ export const TabsBarContent: React.FC<{
options={tabs} options={tabs}
value={active} value={active}
onChange={v => { onChange={v => {
setActiveTab(v.value); setActiveTab(v.value!);
}} }}
/> />
</div> </div>
@ -200,12 +200,12 @@ export const TabsBarContent: React.FC<{
{tabs.map(item => ( {tabs.map(item => (
<Tab <Tab
key={item.value} key={item.value}
label={item.label} label={item.label!}
counter={item.value === 'overrides' ? overridesCount : undefined} counter={item.value === 'overrides' ? overridesCount : undefined}
active={active.value === item.value} active={active.value === item.value}
onChangeTab={() => setActiveTab(item.value)} onChangeTab={() => setActiveTab(item.value!)}
title={item.tooltip} title={item.tooltip}
aria-label={selectors.components.PanelEditor.OptionsPane.tab(item.label)} aria-label={selectors.components.PanelEditor.OptionsPane.tab(item.label!)}
/> />
))} ))}
<div className="flex-grow-1" /> <div className="flex-grow-1" />

@ -170,7 +170,7 @@ export const OverrideEditor: React.FC<OverrideEditorProps> = ({
icon="plus" icon="plus"
options={configPropertiesOptions} options={configPropertiesOptions}
onChange={o => { onChange={o => {
onDynamicConfigValueAdd(o.value); onDynamicConfigValueAdd(o.value!);
}} }}
isFullWidth={false} isFullWidth={false}
/> />

@ -18,7 +18,7 @@ interface PanelEditorTabsProps {
export const PanelEditorTabs: React.FC<PanelEditorTabsProps> = ({ panel, dashboard, tabs, onChangeTab }) => { export const PanelEditorTabs: React.FC<PanelEditorTabsProps> = ({ panel, dashboard, tabs, onChangeTab }) => {
const styles = getPanelEditorTabsStyles(); const styles = getPanelEditorTabsStyles();
const activeTab = tabs.find(item => item.active); const activeTab = tabs.find(item => item.active)!;
const getCounter = useCallback( const getCounter = useCallback(
(tab: PanelEditorTab) => { (tab: PanelEditorTab) => {

@ -12,7 +12,7 @@ import { RepeatRowSelect } from '../RepeatRowSelect/RepeatRowSelect';
interface Props { interface Props {
panel: PanelModel; panel: PanelModel;
plugin: PanelPlugin; plugin: PanelPlugin;
data: PanelData; data?: PanelData;
dashboard: DashboardModel; dashboard: DashboardModel;
onPanelConfigChange: (configKey: string, value: any) => void; onPanelConfigChange: (configKey: string, value: any) => void;
onPanelOptionsChanged: (options: any) => void; onPanelOptionsChanged: (options: any) => void;
@ -26,7 +26,7 @@ export const PanelOptionsTab: FC<Props> = ({
onPanelConfigChange, onPanelConfigChange,
onPanelOptionsChanged, onPanelOptionsChanged,
}) => { }) => {
const visTabInputRef = useRef<HTMLInputElement>(); const visTabInputRef = useRef<HTMLInputElement>(null);
const linkVariablesSuggestions = useMemo(() => getPanelLinksVariableSuggestions(), []); const linkVariablesSuggestions = useMemo(() => getPanelLinksVariableSuggestions(), []);
const onRepeatRowSelectChange = useCallback((value: string | null) => onPanelConfigChange('repeat', value), [ const onRepeatRowSelectChange = useCallback((value: string | null) => onPanelConfigChange('repeat', value), [
onPanelConfigChange, onPanelConfigChange,

@ -15,7 +15,7 @@ interface UsePanelLatestData {
* Subscribes and returns latest panel data from PanelQueryRunner * Subscribes and returns latest panel data from PanelQueryRunner
*/ */
export const usePanelLatestData = (panel: PanelModel, options: GetDataOptions): UsePanelLatestData => { export const usePanelLatestData = (panel: PanelModel, options: GetDataOptions): UsePanelLatestData => {
const querySubscription = useRef<Unsubscribable>(null); const querySubscription = useRef<Unsubscribable>();
const [latestData, setLatestData] = useState<PanelData>(); const [latestData, setLatestData] = useState<PanelData>();
useEffect(() => { useEffect(() => {

@ -7,8 +7,8 @@ import { getVariables } from '../../../variables/state/selectors';
import { StoreState } from '../../../../types'; import { StoreState } from '../../../../types';
export interface Props { export interface Props {
repeat: string | undefined; repeat: string | undefined | null;
onChange: (name: string) => void; onChange: (name: string | null | undefined) => void;
} }
export const RepeatRowSelect: FC<Props> = ({ repeat, onChange }) => { export const RepeatRowSelect: FC<Props> = ({ repeat, onChange }) => {

@ -6,7 +6,7 @@ import { OnRowOptionsUpdate } from './RowOptionsForm';
export interface RowOptionsButtonProps { export interface RowOptionsButtonProps {
title: string | null; title: string | null;
repeat: string | null; repeat: string | null | undefined;
onUpdate: OnRowOptionsUpdate; onUpdate: OnRowOptionsUpdate;
} }

@ -3,17 +3,17 @@ import { Button, Field, Form, HorizontalGroup, Input } from '@grafana/ui';
import { RepeatRowSelect } from '../RepeatRowSelect/RepeatRowSelect'; import { RepeatRowSelect } from '../RepeatRowSelect/RepeatRowSelect';
export type OnRowOptionsUpdate = (title: string | null, repeat: string | null) => void; export type OnRowOptionsUpdate = (title: string | null, repeat: string | null | undefined) => void;
export interface Props { export interface Props {
title: string | null; title: string | null;
repeat: string | null; repeat?: string | null;
onUpdate: OnRowOptionsUpdate; onUpdate: OnRowOptionsUpdate;
onCancel: () => void; onCancel: () => void;
} }
export const RowOptionsForm: FC<Props> = ({ repeat, title, onUpdate, onCancel }) => { export const RowOptionsForm: FC<Props> = ({ repeat, title, onUpdate, onCancel }) => {
const [newRepeat, setNewRepeat] = useState<string | null>(repeat); const [newRepeat, setNewRepeat] = useState<string | null | undefined>(repeat);
const onChangeRepeat = useCallback((name: string) => setNewRepeat(name), [setNewRepeat]); const onChangeRepeat = useCallback((name: string) => setNewRepeat(name), [setNewRepeat]);
return ( return (

@ -65,6 +65,10 @@ export const SaveDashboardAsForm: React.FC<SaveDashboardFormProps & { isNew?: bo
<Form <Form
defaultValues={defaultValues} defaultValues={defaultValues}
onSubmit={async (data: SaveDashboardAsFormDTO) => { onSubmit={async (data: SaveDashboardAsFormDTO) => {
if (!onSubmit) {
return;
}
const clone = getSaveAsDashboardClone(dashboard); const clone = getSaveAsDashboardClone(dashboard);
clone.title = data.title; clone.title = data.title;
if (!data.copyTags) { if (!data.copyTags) {

@ -18,6 +18,10 @@ export const SaveDashboardForm: React.FC<SaveDashboardFormProps> = ({ dashboard,
return ( return (
<Form <Form
onSubmit={async (data: SaveDashboardFormDTO) => { onSubmit={async (data: SaveDashboardFormDTO) => {
if (!onSubmit) {
return;
}
const result = await onSubmit(dashboard.getSaveModelClone(data), data, dashboard); const result = await onSubmit(dashboard.getSaveModelClone(data), data, dashboard);
if (result.status === 'success') { if (result.status === 'success') {
if (data.saveVariables) { if (data.saveVariables) {

@ -10,7 +10,10 @@ import { DashboardModel } from 'app/features/dashboard/state';
import { saveDashboard as saveDashboardApiCall } from 'app/features/manage-dashboards/state/actions'; import { saveDashboard as saveDashboardApiCall } from 'app/features/manage-dashboards/state/actions';
const saveDashboard = async (saveModel: any, options: SaveDashboardOptions, dashboard: DashboardModel) => { const saveDashboard = async (saveModel: any, options: SaveDashboardOptions, dashboard: DashboardModel) => {
const folderId = options.folderId >= 0 ? options.folderId : dashboard.meta.folderId || saveModel.folderId; let folderId = options.folderId;
if (folderId === undefined) {
folderId = dashboard.meta.folderId || saveModel.folderId;
}
return await saveDashboardApiCall({ ...options, folderId, dashboard: saveModel }); return await saveDashboardApiCall({ ...options, folderId, dashboard: saveModel });
}; };

@ -89,7 +89,7 @@ export class ShareModal extends React.Component<Props, State> {
getActiveTab() { getActiveTab() {
const { tabs, activeTab } = this.state; const { tabs, activeTab } = this.state;
return tabs.find(t => t.value === activeTab); return tabs.find(t => t.value === activeTab)!;
} }
renderTitle() { renderTitle() {
@ -112,7 +112,7 @@ export class ShareModal extends React.Component<Props, State> {
render() { render() {
const { dashboard, panel } = this.props; const { dashboard, panel } = this.props;
const activeTabModel = this.getActiveTab(); const activeTabModel = this.getActiveTab();
const ActiveTab = activeTabModel?.component; const ActiveTab = activeTabModel.component;
return ( return (
<Modal isOpen={true} title={this.renderTitle()} onDismiss={this.onDismiss}> <Modal isOpen={true} title={this.renderTitle()} onDismiss={this.onDismiss}>

@ -25,6 +25,7 @@ export const TransformationOperationRow: React.FC<TransformationOperationRowProp
return ( return (
<HorizontalGroup align="center"> <HorizontalGroup align="center">
<QueryOperationAction <QueryOperationAction
title="Debug"
disabled={!isOpen} disabled={!isOpen}
icon="bug" icon="bug"
onClick={() => { onClick={() => {
@ -32,7 +33,7 @@ export const TransformationOperationRow: React.FC<TransformationOperationRowProp
}} }}
/> />
<QueryOperationAction icon="trash-alt" onClick={onRemove} /> <QueryOperationAction title="Remove" icon="trash-alt" onClick={onRemove} />
</HorizontalGroup> </HorizontalGroup>
); );
}; };

@ -273,7 +273,7 @@ const getNavigateToExploreContext = async (openInNewWindow?: (url: string) => vo
datasource: 'mocked datasource', datasource: 'mocked datasource',
targets: [{ refId: 'A' }], targets: [{ refId: 'A' }],
}; };
const datasource = new MockDataSourceApi(panel.datasource); const datasource = new MockDataSourceApi(panel.datasource!);
const get = jest.fn().mockResolvedValue(datasource); const get = jest.fn().mockResolvedValue(datasource);
const getDataSourceSrv = jest.fn().mockReturnValue({ get }); const getDataSourceSrv = jest.fn().mockReturnValue({ get });
const getTimeSrv = jest.fn(); const getTimeSrv = jest.fn();

@ -491,7 +491,7 @@ describe('Explore reducer', () => {
}, },
}, },
}; };
const stateWithDifferentDataSource = { const stateWithDifferentDataSource: any = {
...initialState, ...initialState,
left: { left: {
...initialState.left, ...initialState.left,
@ -529,7 +529,7 @@ describe('Explore reducer', () => {
}, },
}, },
}; };
const stateWithDifferentDataSource = { const stateWithDifferentDataSource: any = {
...initialState, ...initialState,
left: { left: {
...initialState.left, ...initialState.left,
@ -570,7 +570,7 @@ describe('Explore reducer', () => {
}, },
}, },
}; };
const stateWithDifferentDataSource = { const stateWithDifferentDataSource: any = {
...initialState, ...initialState,
left: { left: {
...initialState.left, ...initialState.left,
@ -608,14 +608,14 @@ describe('Explore reducer', () => {
}, },
}, },
}; };
const stateWithDifferentDataSource = { const stateWithDifferentDataSource: any = {
...initialState, ...initialState,
left: { left: {
...initialState.left, ...initialState.left,
urlState: { urlState: {
...initialState.left.urlState, ...initialState.left.urlState,
ui: { ui: {
...initialState.left.urlState.ui, ...initialState.left.urlState!.ui,
showingGraph: true, showingGraph: true,
}, },
}, },

@ -35,17 +35,17 @@ export const getMockPlugins = (amount: number): PluginMeta[] => {
return plugins as any; return plugins as any;
}; };
export const getPanelPlugin = ( export function getPanelPlugin(
options: Partial<PanelPluginMeta>, options: Partial<PanelPluginMeta>,
reactPanel?: ComponentType<PanelProps>, reactPanel?: ComponentType<PanelProps>,
angularPanel?: any angularPanel?: any
): PanelPlugin => { ): PanelPlugin {
const plugin = new PanelPlugin(reactPanel); const plugin = new PanelPlugin(reactPanel!);
plugin.angularPanelCtrl = angularPanel; plugin.angularPanelCtrl = angularPanel;
plugin.meta = { plugin.meta = {
id: options.id, id: options.id!,
type: PluginType.panel, type: PluginType.panel,
name: options.id, name: options.id!,
sort: options.sort || 1, sort: options.sort || 1,
info: { info: {
author: { author: {
@ -66,7 +66,7 @@ export const getPanelPlugin = (
baseUrl: '', baseUrl: '',
}; };
return plugin; return plugin;
}; }
export function getMockPlugin(overrides?: Partial<PluginMeta>): PluginMeta { export function getMockPlugin(overrides?: Partial<PluginMeta>): PluginMeta {
const defaults: PluginMeta = { const defaults: PluginMeta = {

@ -231,7 +231,7 @@ export class CloudWatchDatasource extends DataSourceApi<CloudWatchQuery, CloudWa
this.logQueries[param.refId] = { this.logQueries[param.refId] = {
id: param.queryId, id: param.queryId,
region: param.region, region: param.region,
statsQuery: param.statsGroups?.length > 0 ?? false, statsQuery: (param.statsGroups?.length ?? 0) > 0 ?? false,
}; };
}); });
let prevRecordsMatched: Record<string, number> = {}; let prevRecordsMatched: Record<string, number> = {};

@ -873,6 +873,7 @@ describe('enhanceDataFrame', () => {
}, },
], ],
}); });
enhanceDataFrame(df, [ enhanceDataFrame(df, [
{ {
field: 'urlField', field: 'urlField',
@ -885,13 +886,13 @@ describe('enhanceDataFrame', () => {
}, },
]); ]);
expect(df.fields[0].config.links?.length).toBe(1); expect(df.fields[0].config.links!.length).toBe(1);
expect(df.fields[0].config.links?.[0]).toEqual({ expect(df.fields[0].config.links![0]).toEqual({
title: '', title: '',
url: 'someUrl', url: 'someUrl',
}); });
expect(df.fields[1].config.links?.length).toBe(1); expect(df.fields[1].config.links!.length).toBe(1);
expect(df.fields[1].config.links?.[0]).toEqual({ expect(df.fields[1].config.links![0]).toEqual({
title: '', title: '',
url: '', url: '',
internal: { internal: {

@ -16,7 +16,7 @@ describe('IndexPattern', () => {
describe('when getting index list for time range', () => { describe('when getting index list for time range', () => {
describe('no interval', () => { describe('no interval', () => {
test('should return correct index', () => { test('should return correct index', () => {
const pattern = new IndexPattern('my-metrics', null); const pattern = new IndexPattern('my-metrics');
const from = new Date(2015, 4, 30, 1, 2, 3); const from = new Date(2015, 4, 30, 1, 2, 3);
const to = new Date(2015, 5, 1, 12, 5, 6); const to = new Date(2015, 5, 1, 12, 5, 6);
expect(pattern.getIndexList(from, to)).toEqual('my-metrics'); expect(pattern.getIndexList(from, to)).toEqual('my-metrics');

@ -11,6 +11,7 @@ import { CustomVariableModel } from '../../../features/variables/types';
import { initialCustomVariableModelState } from '../../../features/variables/custom/reducer'; // will use the version in __mocks__ import { initialCustomVariableModelState } from '../../../features/variables/custom/reducer'; // will use the version in __mocks__
jest.mock('@grafana/runtime', () => ({ jest.mock('@grafana/runtime', () => ({
//@ts-ignore
...jest.requireActual('@grafana/runtime'), ...jest.requireActual('@grafana/runtime'),
getBackendSrv: () => backendSrv, getBackendSrv: () => backendSrv,
})); }));

@ -10,7 +10,8 @@
}, },
"rootDirs": ["public/"], "rootDirs": ["public/"],
"typeRoots": ["node_modules/@types", "public/app/types"], "typeRoots": ["node_modules/@types", "public/app/types"],
"allowJs": true "allowJs": true,
"strictNullChecks": true
}, },
"extends": "@grafana/tsconfig/base.json", "extends": "@grafana/tsconfig/base.json",
"include": [ "include": [

Loading…
Cancel
Save