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. 18
      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. 30
      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. 6
      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;
}
get optionEditors() {
if (!this._optionEditors && this.registerOptionEditors) {
get optionEditors(): PanelOptionEditorsRegistry {
if (!this._optionEditors) {
const builder = new PanelOptionsEditorBuilder<TOptions>();
this.registerOptionEditors(builder);
this._optionEditors = builder.getRegistry();
if (this.registerOptionEditors) {
this.registerOptionEditors(builder);
}
}
return this._optionEditors;

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

@ -16,7 +16,7 @@ export interface TabProps extends HTMLProps<HTMLLIElement> {
icon?: IconName;
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. */
counter?: number;
counter?: number | null;
}
export const Tab = React.forwardRef<HTMLLIElement, TabProps>(

@ -4,7 +4,7 @@ import { shallow } from 'enzyme';
describe('QueryOperationAction', () => {
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', () => {
it('does not call onClick handler', () => {

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

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

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

@ -218,7 +218,7 @@ export class FormDropdownCtrl {
}
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.focus();

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

@ -8,7 +8,7 @@ import { NavModelItem } from '@grafana/data';
export default function BottomSection() {
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 user = contextSrv.user;

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

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

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

@ -39,11 +39,11 @@ $.fn.place_tt = (() => {
]);
}
width = $tooltip.outerWidth(true);
height = $tooltip.outerHeight(true);
width = $tooltip.outerWidth(true)!;
height = $tooltip.outerHeight(true)!;
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 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;
$tooltip.css('left', left > 0 ? left : 0);
$tooltip.css('top', top > 0 ? top : 0);

@ -141,24 +141,21 @@ export function makeSeriesForLogs(sortedRows: LogRowModel[], bucketSize: number,
const data = toDataFrame(series);
const fieldCache = new FieldCache(data);
const timeField = fieldCache.getFirstFieldOfType(FieldType.time);
if (timeField) {
const timeField = fieldCache.getFirstFieldOfType(FieldType.time)!;
timeField.display = getDisplayProcessor({
field: timeField,
timeZone,
});
}
const valueField = fieldCache.getFirstFieldOfType(FieldType.number);
if (valueField) {
const valueField = fieldCache.getFirstFieldOfType(FieldType.number)!;
valueField.config = {
...valueField.config,
color: series.color,
};
valueField.name = series.alias;
const fieldDisplayProcessor = getDisplayProcessor({ field: valueField, timeZone });
valueField.display = (value: any) => ({ ...fieldDisplayProcessor(value), color: series.color });
}
const points = getFlotPairs({
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)
let totalBytes = 0;
const queriesVisited: { [refId: string]: boolean } = {};
for (const series of logSeries) {
const totalBytesKey = series.meta?.custom?.lokiQueryStatKey;
// Stats are per query, keeping track by refId
const { refId } = series;
const { refId } = series; // Stats are per query, keeping track by refId
if (refId && !queriesVisited[refId]) {
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) {
totalBytes += byteStat.value;
}
}
queriesVisited[refId] = true;
}
}
if (totalBytes > 0) {
const { text, suffix } = decimalSIPrefix('B')(totalBytes);
meta.push({

@ -19,7 +19,7 @@ export class NavModelSrv {
let children = this.navItems;
const nav = {
breadcrumbs: [],
} as NavModel;
} as any;
for (const id of args) {
// 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) {
for (const node of children) {
navIndex[node.id] = {
navIndex[node.id!] = {
...node,
parentItem: parentItem,
};
@ -52,8 +52,8 @@ export const navIndexReducer = (state: NavIndex = initialState, action: AnyActio
const newPages: NavIndex = {};
const payload = action.payload;
for (const node of payload.children) {
newPages[node.id] = {
for (const node of payload.children!) {
newPages[node.id!] = {
...node,
parentItem: payload,
};

@ -1,17 +1,18 @@
import { DashboardAcl, DashboardAclDTO } from 'app/types/acl';
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 {
const item = dto as DashboardAcl;
item.sortRank = 0;
if (item.userId > 0) {
if (item.userId! > 0) {
item.name = item.userLogin;
item.sortRank = 10;
} else if (item.teamId > 0) {
} else if (item.teamId! > 0) {
item.name = item.team;
item.sortRank = 20;
} else if (item.role) {

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

@ -71,6 +71,6 @@ describe('when checking template variables', () => {
'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
this.bind('e', () => {
if (!dashboard.meta.focusPanelId) {
return;
}
if (dashboard.canEditPanelById(dashboard.meta.focusPanelId)) {
const search = _.extend(this.$location.search(), { editPanel: dashboard.meta.focusPanelId });
this.$location.search(search);
@ -253,7 +257,7 @@ export class KeybindingSrv {
if (this.contextSrv.hasAccessToExplore()) {
this.bind('x', async () => {
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 url = await getExploreUrl({
panel,
@ -262,27 +266,33 @@ export class KeybindingSrv {
datasourceSrv: this.datasourceSrv,
timeSrv: this.timeSrv,
});
const urlWithoutBase = locationUtil.stripBaseFromUrl(url);
if (url) {
const urlWithoutBase = locationUtil.stripBaseFromUrl(url);
if (urlWithoutBase) {
this.$timeout(() => this.$location.url(urlWithoutBase));
}
}
}
});
}
// delete panel
this.bind('p r', () => {
if (dashboard.canEditPanelById(dashboard.meta.focusPanelId)) {
appEvents.emit(CoreEvents.removePanel, dashboard.meta.focusPanelId);
const panelId = dashboard.meta.focusPanelId;
if (panelId && dashboard.canEditPanelById(panelId)) {
appEvents.emit(CoreEvents.removePanel, panelId);
dashboard.meta.focusPanelId = 0;
}
});
// duplicate panel
this.bind('p d', () => {
if (dashboard.canEditPanelById(dashboard.meta.focusPanelId)) {
const panelIndex = dashboard.getPanelInfoById(dashboard.meta.focusPanelId).index;
const panelId = dashboard.meta.focusPanelId;
if (panelId && dashboard.canEditPanelById(panelId)) {
const panelIndex = dashboard.getPanelInfoById(panelId)!.index;
dashboard.duplicatePanel(dashboard.panels[panelIndex]);
}
});
@ -305,11 +315,11 @@ export class KeybindingSrv {
// toggle panel legend
this.bind('p l', () => {
if (dashboard.meta.focusPanelId) {
const panelInfo = dashboard.getPanelInfoById(dashboard.meta.focusPanelId);
const panelInfo = dashboard.getPanelInfoById(dashboard.meta.focusPanelId)!;
if (panelInfo.panel.legend) {
const panelRef = dashboard.getPanelById(dashboard.meta.focusPanelId);
panelRef.legend.show = !panelRef.legend.show;
panelRef.render();
panelInfo.panel.legend.show = !panelInfo.panel.legend.show;
panelInfo.panel.render();
}
}
});

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

@ -1,17 +1,18 @@
import { DashboardAcl, DashboardAclDTO } from 'app/types/acl';
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 {
const item = dto as DashboardAcl;
item.sortRank = 0;
if (item.userId > 0) {
if (item.userId! > 0) {
item.name = item.userLogin;
item.sortRank = 10;
} else if (item.teamId > 0) {
} else if (item.teamId! > 0) {
item.name = item.team;
item.sortRank = 20;
} else if (item.role) {

@ -13,17 +13,17 @@ export function checkBrowserCompatibility() {
*/
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;
} else if (
isEdge &&
((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;
} 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;
} 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;
}

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

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

@ -188,7 +188,7 @@ export const createUrlFromRichHistory = (query: RichHistoryQuery) => {
};
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 });
return url;
};

@ -36,7 +36,12 @@ const UserCreatePage: React.FC<UserCreatePageProps> = ({ navModel, updateLocatio
{({ register, errors }) => {
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 })} />
</Field>
@ -51,7 +56,7 @@ const UserCreatePage: React.FC<UserCreatePageProps> = ({ navModel, updateLocatio
label="Password"
required
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
type="password"

@ -24,7 +24,7 @@ export class UserLdapSyncInfo extends PureComponent<Props, State> {
const prevSyncSuccessful = ldapSyncInfo && ldapSyncInfo.prevSync;
const nextSyncSuccessful = ldapSyncInfo && ldapSyncInfo.nextSync;
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}`;
return (

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

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

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

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

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

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

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

@ -6,6 +6,6 @@ export const getAlertRuleItems = (state: AlertRulesState) => {
const regex = new RegExp(state.searchQuery, 'i');
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.timeFormated = this.panelCtrl.dashboard.formatDate(this.event.time);
this.timeFormated = this.panelCtrl.dashboard.formatDate(this.event.time!);
}
save() {
@ -33,11 +33,11 @@ export class EventEditorCtrl {
}
const saveModel = _.cloneDeep(this.event);
saveModel.time = saveModel.time.valueOf();
saveModel.time = saveModel.time!.valueOf();
saveModel.timeEnd = 0;
if (saveModel.isRegion) {
saveModel.timeEnd = this.event.timeEnd.valueOf();
saveModel.timeEnd = this.event.timeEnd!.valueOf();
if (saveModel.timeEnd < saveModel.time) {
console.log('invalid time');

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

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

@ -184,7 +184,7 @@ class DashNav extends PureComponent<Props> {
`;
const folderTitle = dashboard.meta.folderTitle;
const haveFolder = dashboard.meta.folderId > 0;
const haveFolder = (dashboard.meta.folderId ?? 0) > 0;
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['repeat'] = repeat;
this.props.panel.render();

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

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

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

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

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

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

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

@ -175,7 +175,7 @@ export const TabsBarContent: React.FC<{
// Show the appropriate tabs
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 (plugin.fieldConfigRegistry.isEmpty()) {
@ -191,7 +191,7 @@ export const TabsBarContent: React.FC<{
options={tabs}
value={active}
onChange={v => {
setActiveTab(v.value);
setActiveTab(v.value!);
}}
/>
</div>
@ -200,12 +200,12 @@ export const TabsBarContent: React.FC<{
{tabs.map(item => (
<Tab
key={item.value}
label={item.label}
label={item.label!}
counter={item.value === 'overrides' ? overridesCount : undefined}
active={active.value === item.value}
onChangeTab={() => setActiveTab(item.value)}
onChangeTab={() => setActiveTab(item.value!)}
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" />

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

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

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

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

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

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

@ -3,17 +3,17 @@ import { Button, Field, Form, HorizontalGroup, Input } from '@grafana/ui';
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 {
title: string | null;
repeat: string | null;
repeat?: string | null;
onUpdate: OnRowOptionsUpdate;
onCancel: () => void;
}
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]);
return (

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

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

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

@ -25,6 +25,7 @@ export const TransformationOperationRow: React.FC<TransformationOperationRowProp
return (
<HorizontalGroup align="center">
<QueryOperationAction
title="Debug"
disabled={!isOpen}
icon="bug"
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>
);
};

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

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

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

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

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

@ -16,7 +16,7 @@ describe('IndexPattern', () => {
describe('when getting index list for time range', () => {
describe('no interval', () => {
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 to = new Date(2015, 5, 1, 12, 5, 6);
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__
jest.mock('@grafana/runtime', () => ({
//@ts-ignore
...jest.requireActual('@grafana/runtime'),
getBackendSrv: () => backendSrv,
}));

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

Loading…
Cancel
Save