Commit 1db9fff0 by Tobias Skarhed Committed by Torkel Ödegaard

strictNullChecks: First batch (#18390)

* First batch of strictNullChecks

* Remove undefined

* Check an alternative solution

* Fix strict nullChecks

* Low hanging strictNullChecks

* Fixing strict nulls issues and more

* Minor change

* fixed unit test

* Fix feedback

* Update public/vendor/ansicolor/ansicolor.ts

Co-Authored-By: Dominik Prokop <dominik.prokop@grafana.com>

* Update public/vendor/ansicolor/ansicolor.ts

Co-Authored-By: Dominik Prokop <dominik.prokop@grafana.com>

* Update public/vendor/ansicolor/ansicolor.ts

Co-Authored-By: Dominik Prokop <dominik.prokop@grafana.com>

* Fix build errors
parent 0b828cfa
......@@ -27,7 +27,6 @@ function getCurrentThresholds(editor: ThresholdsEditor) {
describe('Render', () => {
it('should render with base threshold', () => {
const { wrapper } = setup();
expect(wrapper).toMatchSnapshot();
});
});
......@@ -35,8 +34,7 @@ describe('Render', () => {
describe('Initialization', () => {
it('should add a base threshold if missing', () => {
const { instance } = setup();
expect(getCurrentThresholds(instance)).toEqual([{ value: -Infinity, color: colors[0] }]);
expect(getCurrentThresholds(instance)).toEqual([{ value: -Infinity, color: 'green' }]);
});
});
......@@ -47,8 +45,8 @@ describe('Add threshold', () => {
instance.onAddThresholdAfter(instance.state.thresholds[0]);
expect(getCurrentThresholds(instance)).toEqual([
{ value: -Infinity, color: colors[0] }, // 0
{ value: 50, color: colors[2] }, // 1
{ value: -Infinity, color: 'green' }, // 0
{ value: 50, color: colors[1] }, // 1
]);
});
......
......@@ -8,7 +8,7 @@ import { ColorPicker } from '../ColorPicker/ColorPicker';
import { PanelOptionsGroup } from '../PanelOptionsGroup/PanelOptionsGroup';
export interface Props {
thresholds: Threshold[];
thresholds?: Threshold[];
onChange: (thresholds: Threshold[]) => void;
}
......@@ -22,35 +22,28 @@ interface ThresholdWithKey extends Threshold {
let counter = 100;
function toThresholdsWithKey(thresholds?: Threshold[]): ThresholdWithKey[] {
if (!thresholds || thresholds.length === 0) {
thresholds = [{ value: -Infinity, color: 'green' }];
}
return thresholds.map(t => {
return {
color: t.color,
value: t.value === null ? -Infinity : t.value,
key: counter++,
};
});
}
export class ThresholdsEditor extends PureComponent<Props, State> {
constructor(props: Props) {
super(props);
const thresholds = props.thresholds
? props.thresholds.map(t => {
return {
color: t.color,
value: t.value === null ? -Infinity : t.value,
key: counter++,
};
})
: ([] as ThresholdWithKey[]);
let needsCallback = false;
if (!thresholds.length) {
thresholds.push({ value: -Infinity, color: colors[0], key: counter++ });
needsCallback = true;
} else {
// First value is always base
thresholds[0].value = -Infinity;
}
const thresholds = toThresholdsWithKey(props.thresholds);
thresholds[0].value = -Infinity;
// Update the state
this.state = { thresholds };
if (needsCallback) {
this.onChange();
}
}
onAddThresholdAfter = (threshold: ThresholdWithKey) => {
......
......@@ -2,26 +2,7 @@
exports[`Render should render with base threshold 1`] = `
<ThresholdsEditor
onChange={
[MockFunction] {
"calls": Array [
Array [
Array [
Object {
"color": "#7EB26D",
"value": -Infinity,
},
],
],
],
"results": Array [
Object {
"type": "return",
"value": undefined,
},
],
}
}
onChange={[MockFunction]}
thresholds={Array []}
>
<Component
......@@ -61,7 +42,7 @@ exports[`Render should render with base threshold 1`] = `
className="thresholds-row-color-indicator"
style={
Object {
"backgroundColor": "#7EB26D",
"backgroundColor": "#73BF69",
}
}
/>
......@@ -81,12 +62,12 @@ exports[`Render should render with base threshold 1`] = `
className="thresholds-row-input-inner-color-colorpicker"
>
<WithTheme(ColorPicker)
color="#7EB26D"
color="green"
enableNamedColors={true}
onChange={[Function]}
>
<ColorPicker
color="#7EB26D"
color="green"
enableNamedColors={true}
onChange={[Function]}
theme={
......@@ -247,7 +228,7 @@ exports[`Render should render with base threshold 1`] = `
<PopperController
content={
<ColorPickerPopover
color="#7EB26D"
color="green"
enableNamedColors={true}
onChange={[Function]}
theme={
......@@ -409,7 +390,7 @@ exports[`Render should render with base threshold 1`] = `
hideAfter={300}
>
<ForwardRef(ColorPickerTrigger)
color="#7EB26D"
color="#73BF69"
onClick={[Function]}
onMouseLeave={[Function]}
>
......@@ -445,7 +426,7 @@ exports[`Render should render with base threshold 1`] = `
<div
style={
Object {
"backgroundColor": "#7EB26D",
"backgroundColor": "#73BF69",
"bottom": 0,
"display": "block",
"left": 0,
......
......@@ -5,7 +5,7 @@ import { MappingType, ValueMapping } from '@grafana/data';
import { PanelOptionsGroup } from '../PanelOptionsGroup/PanelOptionsGroup';
export interface Props {
valueMappings: ValueMapping[];
valueMappings?: ValueMapping[];
onChange: (valueMappings: ValueMapping[]) => void;
}
......
......@@ -103,16 +103,19 @@ export function mergeTablesIntoModel(dst?: TableModel, ...tables: TableModel[]):
const columnNames: { [key: string]: any } = {};
// Union of all non-value columns
const columnsUnion = tables.slice().reduce((acc, series) => {
series.columns.forEach(col => {
const { text } = col;
if (columnNames[text] === undefined) {
columnNames[text] = acc.length;
acc.push(col);
}
});
return acc;
}, []);
const columnsUnion = tables.slice().reduce(
(acc, series) => {
series.columns.forEach(col => {
const { text } = col;
if (columnNames[text] === undefined) {
columnNames[text] = acc.length;
acc.push(col);
}
});
return acc;
},
[] as MutableColumn[]
);
// Map old column index to union index per series, e.g.,
// given columnNames {A: 0, B: 1} and
......@@ -120,51 +123,57 @@ export function mergeTablesIntoModel(dst?: TableModel, ...tables: TableModel[]):
const columnIndexMapper = tables.map(series => series.columns.map(col => columnNames[col.text]));
// Flatten rows of all series and adjust new column indexes
const flattenedRows = tables.reduce((acc, series, seriesIndex) => {
const mapper = columnIndexMapper[seriesIndex];
series.rows.forEach(row => {
const alteredRow: any[] = [];
// Shifting entries according to index mapper
mapper.forEach((to, from) => {
alteredRow[to] = row[from];
const flattenedRows = tables.reduce(
(acc, series, seriesIndex) => {
const mapper = columnIndexMapper[seriesIndex];
series.rows.forEach(row => {
const alteredRow: MutableColumn[] = [];
// Shifting entries according to index mapper
mapper.forEach((to, from) => {
alteredRow[to] = row[from];
});
acc.push(alteredRow);
});
acc.push(alteredRow);
});
return acc;
}, []);
return acc;
},
[] as MutableColumn[][]
);
// Merge rows that have same values for columns
const mergedRows: { [key: string]: any } = {};
const compactedRows = flattenedRows.reduce((acc, row, rowIndex) => {
if (!mergedRows[rowIndex]) {
// Look from current row onwards
let offset = rowIndex + 1;
// More than one row can be merged into current row
while (offset < flattenedRows.length) {
// Find next row that could be merged
const match = _.findIndex(flattenedRows, otherRow => areRowsMatching(columnsUnion, row, otherRow), offset);
if (match > -1) {
const matchedRow = flattenedRows[match];
// Merge values from match into current row if there is a gap in the current row
for (let columnIndex = 0; columnIndex < columnsUnion.length; columnIndex++) {
if (row[columnIndex] === undefined && matchedRow[columnIndex] !== undefined) {
row[columnIndex] = matchedRow[columnIndex];
const compactedRows = flattenedRows.reduce(
(acc, row, rowIndex) => {
if (!mergedRows[rowIndex]) {
// Look from current row onwards
let offset = rowIndex + 1;
// More than one row can be merged into current row
while (offset < flattenedRows.length) {
// Find next row that could be merged
const match = _.findIndex(flattenedRows, otherRow => areRowsMatching(columnsUnion, row, otherRow), offset);
if (match > -1) {
const matchedRow = flattenedRows[match];
// Merge values from match into current row if there is a gap in the current row
for (let columnIndex = 0; columnIndex < columnsUnion.length; columnIndex++) {
if (row[columnIndex] === undefined && matchedRow[columnIndex] !== undefined) {
row[columnIndex] = matchedRow[columnIndex];
}
}
// Don't visit this row again
mergedRows[match] = matchedRow;
// Keep looking for more rows to merge
offset = match + 1;
} else {
// No match found, stop looking
break;
}
// Don't visit this row again
mergedRows[match] = matchedRow;
// Keep looking for more rows to merge
offset = match + 1;
} else {
// No match found, stop looking
break;
}
acc.push(row);
}
acc.push(row);
}
return acc;
}, []);
return acc;
},
[] as MutableColumn[][]
);
model.columns = columnsUnion;
model.rows = compactedRows;
......
// Copyright (c) 2014, Hugh Kennedy
// Based on code from https://github.com/hughsk/flat/blob/master/index.js
//
export default function flatten(target: object, opts: { delimiter?: any; maxDepth?: any; safe?: any }): any {
export default function flatten(target: object, opts?: { delimiter?: any; maxDepth?: any; safe?: any }): any {
opts = opts || {};
const delimiter = opts.delimiter || '.';
......
......@@ -46,7 +46,7 @@ export const getDataLinksVariableSuggestions = (): VariableSuggestion[] => [
type LinkTarget = '_blank' | '_self';
interface LinkModel {
export interface LinkModel {
href: string;
title: string;
target: LinkTarget;
......
......@@ -24,8 +24,9 @@ describe('getQueryHints()', () => {
it('returns a rate hint for a monotonically increasing series', () => {
const series = [{ datapoints: [[23, 1000], [24, 1001]] }];
const hints = getQueryHints('metric', series);
expect(hints.length).toBe(1);
expect(hints[0]).toMatchObject({
expect(hints!.length).toBe(1);
expect(hints![0]).toMatchObject({
label: 'Time series is monotonically increasing.',
fix: {
action: {
......@@ -45,16 +46,16 @@ describe('getQueryHints()', () => {
it('returns a rate hint w/o action for a complex monotonically increasing series', () => {
const series = [{ datapoints: [[23, 1000], [24, 1001]] }];
const hints = getQueryHints('sum(metric)', series);
expect(hints.length).toBe(1);
expect(hints[0].label).toContain('rate()');
expect(hints[0].fix).toBeUndefined();
expect(hints!.length).toBe(1);
expect(hints![0].label).toContain('rate()');
expect(hints![0].fix).toBeUndefined();
});
it('returns a rate hint for a monotonically increasing series with missing data', () => {
const series = [{ datapoints: [[23, 1000], [null, 1001], [24, 1002]] }];
const hints = getQueryHints('metric', series);
expect(hints.length).toBe(1);
expect(hints[0]).toMatchObject({
expect(hints!.length).toBe(1);
expect(hints![0]).toMatchObject({
label: 'Time series is monotonically increasing.',
fix: {
action: {
......@@ -68,8 +69,8 @@ describe('getQueryHints()', () => {
it('returns a histogram hint for a bucket series', () => {
const series = [{ datapoints: [[23, 1000]] }];
const hints = getQueryHints('metric_bucket', series);
expect(hints.length).toBe(1);
expect(hints[0]).toMatchObject({
expect(hints!.length).toBe(1);
expect(hints![0]).toMatchObject({
label: 'Time series has buckets, you probably wanted a histogram.',
fix: {
action: {
......@@ -86,8 +87,8 @@ describe('getQueryHints()', () => {
datapoints: [[0, 0], [0, 0]],
}));
const hints = getQueryHints('metric', series);
expect(hints.length).toBe(1);
expect(hints[0]).toMatchObject({
expect(hints!.length).toBe(1);
expect(hints![0]).toMatchObject({
type: 'ADD_SUM',
label: 'Many time series results returned.',
fix: {
......
......@@ -3,7 +3,7 @@ import { TemplateSrvStub } from 'test/specs/helpers';
import { DefaultRemoveFilterValue, DefaultFilterValue } from '../filter_segments';
describe('StackdriverQueryFilterCtrl', () => {
let ctrl: Partial<StackdriverFilterCtrl>;
let ctrl: StackdriverFilterCtrl;
let result: any;
let groupByChangedMock: any;
......
......@@ -13,7 +13,7 @@ export class GraphContextMenuCtrl {
private source?: FlotDataPoint | null;
private scope?: any;
menuItems: ContextMenuItem[];
scrollContextElement: HTMLElement;
scrollContextElement: HTMLElement | null;
position: {
x: number;
y: number;
......@@ -58,7 +58,7 @@ export class GraphContextMenuCtrl {
// Sets element which is considered as a scroll context of given context menu.
// Having access to this element allows scroll event attachement for menu to be closed when user scrolls
setScrollContextElement = (el: HTMLElement) => {
setScrollContextElement = (el: HTMLElement | null) => {
this.scrollContextElement = el;
};
......
......@@ -9,9 +9,9 @@ export interface LegendLabelProps {
series: TimeSeries;
asTable?: boolean;
hidden?: boolean;
onLabelClick?: (series: any, event: any) => void;
onColorChange?: (series: any, color: string) => void;
onToggleAxis?: (series: any) => void;
onLabelClick: (series: any, event: any) => void;
onColorChange: (series: any, color: string) => void;
onToggleAxis: (series: any) => void;
}
export interface LegendValuesProps {
......
......@@ -135,9 +135,6 @@ class GraphElement {
}
onPanelTeardown() {
this.thresholdManager = null;
this.timeRegionManager = null;
if (this.plot) {
this.plot.destroy();
this.plot = null;
......@@ -587,19 +584,24 @@ class GraphElement {
}
addXHistogramAxis(options: any, bucketSize: number) {
let ticks, min, max;
let ticks: number | number[];
let min: number | undefined;
let max: number | undefined;
const defaultTicks = this.panelWidth / 50;
if (this.data.length && bucketSize) {
const tickValues = [];
for (const d of this.data) {
for (const point of d.data) {
tickValues[point[0]] = true;
}
}
ticks = Object.keys(tickValues).map(v => Number(v));
min = _.min(ticks);
max = _.max(ticks);
min = _.min(ticks)!;
max = _.max(ticks)!;
// Adjust tick step
let tickStep = bucketSize;
......@@ -819,7 +821,7 @@ class GraphElement {
};
}
time_format(ticks: number, min: number, max: number) {
time_format(ticks: number, min: number | null, max: number | null) {
if (min && max && ticks) {
const range = max - min;
const secPerTick = range / ticks / 1000;
......
......@@ -35,7 +35,7 @@ export class ThresholdManager {
const handleElem = $(evt.currentTarget).parents('.alert-handle-wrapper');
const handleIndex = $(evt.currentTarget).data('handleIndex');
let lastY: number = null;
let lastY: number | null = null;
let posTop: number;
const plot = this.plot;
const panelCtrl = this.panelCtrl;
......
......@@ -520,7 +520,7 @@ export class HeatmapRenderer {
const logBase = this.panel.yAxis.logBase;
const domain = this.yScale.domain();
const tickValues = this.logScaleTickValues(domain, logBase);
this.data.buckets = mergeZeroBuckets(this.data.buckets, _.min(tickValues));
this.data.buckets = mergeZeroBuckets(this.data.buckets, _.min(tickValues)!);
}
const cardsData = this.data.cards;
......
......@@ -12,7 +12,7 @@ import { MetricsPanelCtrl } from 'app/plugins/sdk';
import { isTableData } from '@grafana/data';
import { GrafanaThemeType, getValueFormat, getColorFromHexRgbOrName } from '@grafana/ui';
import { auto } from 'angular';
import { LinkSrv } from 'app/features/panel/panellinks/link_srv';
import { LinkSrv, LinkModel } from 'app/features/panel/panellinks/link_srv';
import TableModel from 'app/core/table_model';
const BASE_FONT_SIZE = 38;
......@@ -385,7 +385,8 @@ class SingleStatCtrl extends MetricsPanelCtrl {
const $sanitize = this.$sanitize;
const panel = ctrl.panel;
const templateSrv = this.templateSrv;
let data: any, linkInfo: { target: string; href: string; title: string };
let data: any;
let linkInfo: LinkModel | null = null;
const $panelContainer = elem.find('.panel-container');
elem = elem.find('.singlestat-panel');
......
......@@ -14,11 +14,6 @@ export interface Props {
onChange: (options: SingleStatOptions) => void;
}
// colorBackground?: boolean;
// colorValue?: boolean;
// colorPrefix?: boolean;
// colorPostfix?: boolean;
export class ColoringEditor extends PureComponent<Props> {
onToggleColorBackground = () =>
this.props.onChange({ ...this.props.options, colorBackground: !this.props.options.colorBackground });
......@@ -39,27 +34,27 @@ export class ColoringEditor extends PureComponent<Props> {
<Switch
label="Background"
labelClass={`width-${labelWidth}`}
checked={colorBackground}
checked={colorBackground!}
onChange={this.onToggleColorBackground}
/>
<Switch
label="Value"
labelClass={`width-${labelWidth}`}
checked={colorValue}
checked={colorValue!}
onChange={this.onToggleColorValue}
/>
<Switch
label="Prefix"
labelClass={`width-${labelWidth}`}
checked={colorPrefix}
checked={colorPrefix!}
onChange={this.onToggleColorPrefix}
/>
<Switch
label="Postfix"
labelClass={`width-${labelWidth}`}
checked={colorPostfix}
checked={colorPostfix!}
onChange={this.onToggleColorPostfix}
/>
</PanelOptionsGroup>
......
......@@ -55,7 +55,7 @@ export class TableRenderer {
}
getColorForValue(value: number, style: ColumnStyle) {
if (!style.thresholds) {
if (!style.thresholds || !style.colors) {
return null;
}
for (let i = style.thresholds.length; i > 0; i--) {
......
import { transformers, transformDataToTable } from '../transformers';
import { TableData } from '@grafana/data';
describe('when transforming time series table', () => {
let table: TableData;
let table: any;
describe('given 2 time series', () => {
const time = new Date().getTime();
......
......@@ -191,7 +191,7 @@ transformers['json'] = {
const maxDocs = Math.min(series.datapoints.length, 100);
for (let y = 0; y < maxDocs; y++) {
const doc = series.datapoints[y];
const flattened = flatten(doc, null);
const flattened = flatten(doc, {});
for (const propName in flattened) {
names[propName] = true;
}
......@@ -228,7 +228,7 @@ transformers['json'] = {
const values = [];
if (_.isObject(dp) && panel.columns.length > 0) {
const flattened = flatten(dp, null);
const flattened = flatten(dp);
for (z = 0; z < panel.columns.length; z++) {
values.push(flattened[panel.columns[z].value]);
}
......
......@@ -27,7 +27,6 @@ export const defaults: Options = {
alias: '',
decimals: 2,
colors: ['rgba(245, 54, 54, 0.9)', 'rgba(237, 129, 40, 0.89)', 'rgba(50, 172, 45, 0.97)'],
colorMode: null,
pattern: '/.*/',
thresholds: [],
},
......
......@@ -16,10 +16,10 @@ export class TextPanelEditor extends PureComponent<PanelEditorProps<TextOptions>
];
onModeChange = (item: SelectableValue<TextMode>) =>
this.props.onOptionsChange({ ...this.props.options, mode: item.value });
this.props.onOptionsChange({ ...this.props.options, mode: item.value! });
onContentChange = (evt: ChangeEvent<HTMLTextAreaElement>) => {
this.props.onOptionsChange({ ...this.props.options, content: (event.target as any).value });
this.props.onOptionsChange({ ...this.props.options, content: (evt.target as any).value });
};
render() {
......
......@@ -206,7 +206,7 @@ export function grafanaAppDirective(
}
$timeout(() => $location.search(search));
setViewModeBodyClass(body, search.kiosk);
setViewModeBodyClass(body, search.kiosk!);
});
// handle in active view state class
......
......@@ -49,9 +49,9 @@ const deepFreeze = <T>(obj: T): T => {
interface ReducerTester<State> extends Given<State>, When<State>, Then<State> {}
export const reducerTester = <State>(): Given<State> => {
let reducerUnderTest: Reducer<State, ActionOf<any>> = null;
let resultingState: State = null;
let initialState: State = null;
let reducerUnderTest: Reducer<State, ActionOf<any>>;
let resultingState: State;
let initialState: State;
const givenReducer = (reducer: Reducer<State, ActionOf<any>>, state: State): When<State> => {
reducerUnderTest = reducer;
......
......@@ -31,8 +31,10 @@ export function createNavModel(title: string, ...tabs: string[]): NavModel {
breadcrumbs: [],
};
const children = [];
for (const tab of tabs) {
node.children.push({
children.push({
id: tab,
icon: 'icon',
subTitle: 'subTitle',
......@@ -42,7 +44,9 @@ export function createNavModel(title: string, ...tabs: string[]): NavModel {
});
}
node.children[0].active = true;
children[0].active = true;
node.children = children;
return {
node: node,
......
......@@ -60,9 +60,9 @@ const clean = (obj: any) => {
/* ------------------------------------------------------------------------ */
class Color {
background: boolean;
name: string;
brightness: number;
background?: boolean;
name?: string;
brightness?: number;
constructor(background?: boolean, name?: string, brightness?: number) {
this.background = background;
......@@ -82,7 +82,7 @@ class Color {
});
}
defaultBrightness(value: number) {
defaultBrightness(value?: number) {
return new Color(this.background, this.name, this.brightness || value);
}
......@@ -351,7 +351,7 @@ export default class Colors {
get parsed() {
let styles: Set<string>;
let brightness: number;
let brightness: number | undefined;
let color: Color;
let bgColor: Color;
......
......@@ -19,6 +19,7 @@
"noEmitOnError": false,
"emitDecoratorMetadata": false,
"experimentalDecorators": true,
"strictNullChecks": false,
"noImplicitReturns": true,
"noImplicitThis": true,
"noImplicitUseStrict": false,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment