mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
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>
This commit is contained in:
parent
3d98641a45
commit
8f78b0e7bc
@ -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) {
|
||||
timeField.display = getDisplayProcessor({
|
||||
field: timeField,
|
||||
timeZone,
|
||||
});
|
||||
}
|
||||
const timeField = fieldCache.getFirstFieldOfType(FieldType.time)!;
|
||||
timeField.display = getDisplayProcessor({
|
||||
field: timeField,
|
||||
timeZone,
|
||||
});
|
||||
|
||||
const valueField = fieldCache.getFirstFieldOfType(FieldType.number);
|
||||
if (valueField) {
|
||||
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 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,10 +266,12 @@ export class KeybindingSrv {
|
||||
datasourceSrv: this.datasourceSrv,
|
||||
timeSrv: this.timeSrv,
|
||||
});
|
||||
const urlWithoutBase = locationUtil.stripBaseFromUrl(url);
|
||||
|
||||
if (urlWithoutBase) {
|
||||
this.$timeout(() => this.$location.url(urlWithoutBase));
|
||||
if (url) {
|
||||
const urlWithoutBase = locationUtil.stripBaseFromUrl(url);
|
||||
if (urlWithoutBase) {
|
||||
this.$timeout(() => this.$location.url(urlWithoutBase));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -273,16 +279,20 @@ export class KeybindingSrv {
|
||||
|
||||
// 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 = () => {
|
||||
this.props.onDismiss();
|
||||
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[]) => {
|
||||
return activeString;
|
||||
}
|
||||
|
||||
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…
Reference in New Issue
Block a user