Chore: Typescript no-implicit any fixes progress (#17018)

* Chore: Typescript no-implicit any fixes progress

* Fixed tests

* Updated snapshot
This commit is contained in:
Torkel Ödegaard 2019-05-12 14:15:23 +02:00 committed by GitHub
parent 813e3ffc15
commit f12d47ef52
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
35 changed files with 111 additions and 233 deletions

View File

@ -55,11 +55,11 @@ export interface CommonProps<T> {
onCloseMenu?: () => void; onCloseMenu?: () => void;
} }
export interface SelectProps<T> { export interface SelectProps<T> extends CommonProps<T> {
options: Array<SelectOptionItem<T>>; options: Array<SelectOptionItem<T>>;
} }
interface AsyncProps<T> { interface AsyncProps<T> extends CommonProps<T> {
defaultOptions: boolean; defaultOptions: boolean;
loadOptions: (query: string) => Promise<Array<SelectOptionItem<T>>>; loadOptions: (query: string) => Promise<Array<SelectOptionItem<T>>>;
loadingMessage?: () => string; loadingMessage?: () => string;
@ -95,9 +95,8 @@ export const MenuList = (props: any) => {
); );
}; };
export class Select<T> extends PureComponent<CommonProps<T> & SelectProps<T>> { export class Select<T> extends PureComponent<SelectProps<T>> {
static defaultProps = { static defaultProps: Partial<SelectProps<any>> = {
width: null,
className: '', className: '',
isDisabled: false, isDisabled: false,
isSearchable: true, isSearchable: true,
@ -108,7 +107,7 @@ export class Select<T> extends PureComponent<CommonProps<T> & SelectProps<T>> {
isLoading: false, isLoading: false,
backspaceRemovesValue: true, backspaceRemovesValue: true,
maxMenuHeight: 300, maxMenuHeight: 300,
menuIsOpen: false, isOpen: false,
components: { components: {
Option: SelectOption, Option: SelectOption,
SingleValue, SingleValue,
@ -201,9 +200,8 @@ export class Select<T> extends PureComponent<CommonProps<T> & SelectProps<T>> {
} }
} }
export class AsyncSelect<T> extends PureComponent<CommonProps<T> & AsyncProps<T>> { export class AsyncSelect<T> extends PureComponent<AsyncProps<T>> {
static defaultProps = { static defaultProps: Partial<AsyncProps<any>> = {
width: null,
className: '', className: '',
components: {}, components: {},
loadingMessage: () => 'Loading...', loadingMessage: () => 'Loading...',

View File

@ -7,11 +7,13 @@ export interface NavModelItem {
id?: string; id?: string;
active?: boolean; active?: boolean;
hideFromTabs?: boolean; hideFromTabs?: boolean;
hideFromMenu?: boolean;
divider?: boolean; divider?: boolean;
children?: NavModelItem[]; children?: NavModelItem[];
breadcrumbs?: NavModelBreadcrumb[]; breadcrumbs?: NavModelBreadcrumb[];
target?: string; target?: string;
parentItem?: NavModelItem; parentItem?: NavModelItem;
showOrgSwitcher?: boolean;
} }
export interface NavModel { export interface NavModel {

View File

@ -4,7 +4,6 @@ import { AnnotationQueryEditor as StackdriverAnnotationQueryEditor } from 'app/p
import { PasswordStrength } from './components/PasswordStrength'; import { PasswordStrength } from './components/PasswordStrength';
import PageHeader from './components/PageHeader/PageHeader'; import PageHeader from './components/PageHeader/PageHeader';
import EmptyListCTA from './components/EmptyListCTA/EmptyListCTA'; import EmptyListCTA from './components/EmptyListCTA/EmptyListCTA';
import { SearchResult } from './components/search/SearchResult';
import { TagFilter } from './components/TagFilter/TagFilter'; import { TagFilter } from './components/TagFilter/TagFilter';
import { SideMenu } from './components/sidemenu/SideMenu'; import { SideMenu } from './components/sidemenu/SideMenu';
import { MetricSelect } from './components/Select/MetricSelect'; import { MetricSelect } from './components/Select/MetricSelect';
@ -20,7 +19,6 @@ export function registerAngularDirectives() {
react2AngularDirective('appNotificationsList', AppNotificationList, []); react2AngularDirective('appNotificationsList', AppNotificationList, []);
react2AngularDirective('pageHeader', PageHeader, ['model', 'noTabs']); react2AngularDirective('pageHeader', PageHeader, ['model', 'noTabs']);
react2AngularDirective('emptyListCta', EmptyListCTA, ['model']); react2AngularDirective('emptyListCta', EmptyListCTA, ['model']);
react2AngularDirective('searchResult', SearchResult, []);
react2AngularDirective('searchField', SearchField, [ react2AngularDirective('searchField', SearchField, [
'query', 'query',
'autoFocus', 'autoFocus',

View File

@ -4,11 +4,11 @@ import { AlertBox } from '../AlertBox/AlertBox';
interface Props { interface Props {
appNotification: AppNotification; appNotification: AppNotification;
onClearNotification: (id) => void; onClearNotification: (id: number) => void;
} }
export default class AppNotificationItem extends Component<Props> { export default class AppNotificationItem extends Component<Props> {
shouldComponentUpdate(nextProps) { shouldComponentUpdate(nextProps: Props) {
return this.props.appNotification.id !== nextProps.appNotification.id; return this.props.appNotification.id !== nextProps.appNotification.id;
} }

View File

@ -20,12 +20,12 @@ export class AppNotificationList extends PureComponent<Props> {
componentDidMount() { componentDidMount() {
const { notifyApp } = this.props; const { notifyApp } = this.props;
appEvents.on('alert-warning', options => notifyApp(createWarningNotification(options[0], options[1]))); appEvents.on('alert-warning', (options: string[]) => notifyApp(createWarningNotification(options[0], options[1])));
appEvents.on('alert-success', options => notifyApp(createSuccessNotification(options[0], options[1]))); appEvents.on('alert-success', (options: string[]) => notifyApp(createSuccessNotification(options[0], options[1])));
appEvents.on('alert-error', options => notifyApp(createErrorNotification(options[0], options[1]))); appEvents.on('alert-error', (options: string[]) => notifyApp(createErrorNotification(options[0], options[1])));
} }
onClearAppNotification = id => { onClearAppNotification = (id: number) => {
this.props.clearAppNotification(id); this.props.clearAppNotification(id);
}; };

View File

@ -14,10 +14,10 @@ export interface Props {
export default class OrgActionBar extends PureComponent<Props> { export default class OrgActionBar extends PureComponent<Props> {
render() { render() {
const { searchQuery, layoutMode, onSetLayoutMode, linkButton, setSearchQuery, target } = this.props; const { searchQuery, layoutMode, onSetLayoutMode, linkButton, setSearchQuery, target } = this.props;
const linkProps = { href: linkButton.href, target: undefined }; const linkProps = { href: linkButton.href };
if (target) { if (target) {
linkProps.target = target; (linkProps as any).target = target;
} }
return ( return (

View File

@ -1,9 +1,9 @@
import React from 'react'; import React from 'react';
import PageHeader from './PageHeader'; import PageHeader from './PageHeader';
import { shallow } from 'enzyme'; import { shallow, ShallowWrapper } from 'enzyme';
describe('PageHeader', () => { describe('PageHeader', () => {
let wrapper; let wrapper: ShallowWrapper<PageHeader>;
describe('when the nav tree has a node with a title', () => { describe('when the nav tree has a node with a title', () => {
beforeAll(() => { beforeAll(() => {

View File

@ -22,7 +22,7 @@ class AddPermissions extends Component<Props, NewDashboardAclItem> {
showPermissionLevels: true, showPermissionLevels: true,
}; };
constructor(props) { constructor(props: Props) {
super(props); super(props);
this.state = this.getCleanState(); this.state = this.getCleanState();
} }
@ -36,7 +36,7 @@ class AddPermissions extends Component<Props, NewDashboardAclItem> {
}; };
} }
onTypeChanged = evt => { onTypeChanged = (evt: any) => {
const type = evt.target.value as AclTarget; const type = evt.target.value as AclTarget;
switch (type) { switch (type) {
@ -65,7 +65,7 @@ class AddPermissions extends Component<Props, NewDashboardAclItem> {
this.setState({ permission: permission.value }); this.setState({ permission: permission.value });
}; };
onSubmit = async evt => { onSubmit = async (evt: React.SyntheticEvent) => {
evt.preventDefault(); evt.preventDefault();
await this.props.onAddPermission(this.state); await this.props.onAddPermission(this.state);
this.setState(this.getCleanState()); this.setState(this.getCleanState());

View File

@ -1,13 +1,13 @@
import React, { PureComponent } from 'react'; import React, { PureComponent } from 'react';
import { Select } from '@grafana/ui'; import { Select, SelectOptionItem } from '@grafana/ui';
import { dashboardPermissionLevels, DashboardAcl, PermissionLevel } from 'app/types/acl'; import { dashboardPermissionLevels, DashboardAcl, PermissionLevel } from 'app/types/acl';
import { FolderInfo } from 'app/types'; import { FolderInfo } from 'app/types';
const setClassNameHelper = inherited => { const setClassNameHelper = (inherited: boolean) => {
return inherited ? 'gf-form-disabled' : ''; return inherited ? 'gf-form-disabled' : '';
}; };
function ItemAvatar({ item }) { function ItemAvatar({ item }: { item: DashboardAcl }) {
if (item.userAvatarUrl) { if (item.userAvatarUrl) {
return <img className="filter-table__avatar" src={item.userAvatarUrl} />; return <img className="filter-table__avatar" src={item.userAvatarUrl} />;
} }
@ -21,7 +21,7 @@ function ItemAvatar({ item }) {
return <i style={{ width: '25px', height: '25px' }} className="gicon gicon-viewer" />; return <i style={{ width: '25px', height: '25px' }} className="gicon gicon-viewer" />;
} }
function ItemDescription({ item }) { function ItemDescription({ item }: { item: DashboardAcl }) {
if (item.userId) { if (item.userId) {
return <span className="filter-table__weak-italic">(User)</span>; return <span className="filter-table__weak-italic">(User)</span>;
} }
@ -39,8 +39,8 @@ interface Props {
} }
export default class PermissionsListItem extends PureComponent<Props> { export default class PermissionsListItem extends PureComponent<Props> {
onPermissionChanged = option => { onPermissionChanged = (option: SelectOptionItem<PermissionLevel>) => {
this.props.onPermissionChanged(this.props.item, option.value as PermissionLevel); this.props.onPermissionChanged(this.props.item, option.value);
}; };
onRemoveItem = () => { onRemoveItem = () => {

View File

@ -1,4 +1,5 @@
import React, { PureComponent } from 'react'; import React, { PureComponent } from 'react';
// @ts-ignore
import Remarkable from 'remarkable'; import Remarkable from 'remarkable';
import { getBackendSrv } from '../../services/backend_srv'; import { getBackendSrv } from '../../services/backend_srv';
@ -37,7 +38,7 @@ export class PluginHelp extends PureComponent<Props, State> {
getBackendSrv() getBackendSrv()
.get(`/api/plugins/${plugin.id}/markdown/${type}`) .get(`/api/plugins/${plugin.id}/markdown/${type}`)
.then(response => { .then((response: string) => {
const markdown = new Remarkable(); const markdown = new Remarkable();
const helpHtml = markdown.render(response); const helpHtml = markdown.render(response);

View File

@ -19,13 +19,13 @@ interface State {
} }
export class MetricSelect extends React.Component<Props, State> { export class MetricSelect extends React.Component<Props, State> {
static defaultProps = { static defaultProps: Partial<Props> = {
variables: [], variables: [],
options: [], options: [],
isSearchable: true, isSearchable: true,
}; };
constructor(props) { constructor(props: Props) {
super(props); super(props);
this.state = { options: [] }; this.state = { options: [] };
} }
@ -45,7 +45,7 @@ export class MetricSelect extends React.Component<Props, State> {
return nextProps.value !== this.props.value || !_.isEqual(nextOptions, this.state.options); return nextProps.value !== this.props.value || !_.isEqual(nextOptions, this.state.options);
} }
buildOptions({ variables = [], options }) { buildOptions({ variables = [], options }: Props) {
return variables.length > 0 ? [this.getVariablesGroup(), ...options] : options; return variables.length > 0 ? [this.getVariablesGroup(), ...options] : options;
} }

View File

@ -1,4 +1,5 @@
import React from 'react'; import React from 'react';
// @ts-ignore
import renderer from 'react-test-renderer'; import renderer from 'react-test-renderer';
import { TeamPicker } from './TeamPicker'; import { TeamPicker } from './TeamPicker';

View File

@ -23,7 +23,7 @@ export interface State {
export class TeamPicker extends Component<Props, State> { export class TeamPicker extends Component<Props, State> {
debouncedSearch: any; debouncedSearch: any;
constructor(props) { constructor(props: Props) {
super(props); super(props);
this.state = { isLoading: false }; this.state = { isLoading: false };
this.search = this.search.bind(this); this.search = this.search.bind(this);
@ -42,8 +42,8 @@ export class TeamPicker extends Component<Props, State> {
query = ''; query = '';
} }
return backendSrv.get(`/api/teams/search?perpage=10&page=1&query=${query}`).then(result => { return backendSrv.get(`/api/teams/search?perpage=10&page=1&query=${query}`).then((result: any) => {
const teams = result.teams.map(team => { const teams = result.teams.map((team: any) => {
return { return {
id: team.id, id: team.id,
value: team.id, value: team.id,

View File

@ -1,4 +1,5 @@
import React from 'react'; import React from 'react';
// @ts-ignore
import renderer from 'react-test-renderer'; import renderer from 'react-test-renderer';
import { UserPicker } from './UserPicker'; import { UserPicker } from './UserPicker';

View File

@ -24,7 +24,7 @@ export interface State {
export class UserPicker extends Component<Props, State> { export class UserPicker extends Component<Props, State> {
debouncedSearch: any; debouncedSearch: any;
constructor(props) { constructor(props: Props) {
super(props); super(props);
this.state = { isLoading: false }; this.state = { isLoading: false };
this.search = this.search.bind(this); this.search = this.search.bind(this);
@ -45,8 +45,8 @@ export class UserPicker extends Component<Props, State> {
return backendSrv return backendSrv
.get(`/api/org/users?query=${query}&limit=10`) .get(`/api/org/users?query=${query}&limit=10`)
.then(result => { .then((result: any) => {
return result.map(user => ({ return result.map((user: any) => ({
id: user.userId, id: user.userId,
value: user.userId, value: user.userId,
label: user.login === user.email ? user.login : `${user.login} - ${user.email}`, label: user.login === user.email ? user.login : `${user.login} - ${user.email}`,

View File

@ -27,7 +27,7 @@ const timezones = [
export class SharedPreferences extends PureComponent<Props, State> { export class SharedPreferences extends PureComponent<Props, State> {
backendSrv: BackendSrv = getBackendSrv(); backendSrv: BackendSrv = getBackendSrv();
constructor(props) { constructor(props: Props) {
super(props); super(props);
this.state = { this.state = {
@ -72,7 +72,7 @@ export class SharedPreferences extends PureComponent<Props, State> {
}); });
} }
onSubmitForm = async event => { onSubmitForm = async (event: React.SyntheticEvent) => {
event.preventDefault(); event.preventDefault();
const { homeDashboardId, theme, timezone } = this.state; const { homeDashboardId, theme, timezone } = this.state;

View File

@ -55,7 +55,7 @@ const DEFAULT_SNIPPETS = true;
const editorTemplate = `<div></div>`; const editorTemplate = `<div></div>`;
function link(scope, elem, attrs) { function link(scope: any, elem: any, attrs: any) {
// Options // Options
const langMode = attrs.mode || DEFAULT_MODE; const langMode = attrs.mode || DEFAULT_MODE;
const maxLines = attrs.maxLines || DEFAULT_MAX_LINES; const maxLines = attrs.maxLines || DEFAULT_MAX_LINES;
@ -116,7 +116,7 @@ function link(scope, elem, attrs) {
}); });
// Sync with outer scope - update editor content if model has been changed from outside of directive. // Sync with outer scope - update editor content if model has been changed from outside of directive.
scope.$watch('content', (newValue, oldValue) => { scope.$watch('content', (newValue: any, oldValue: any) => {
const editorValue = codeEditor.getValue(); const editorValue = codeEditor.getValue();
if (newValue !== editorValue && newValue !== oldValue) { if (newValue !== editorValue && newValue !== oldValue) {
scope.$$postDigest(() => { scope.$$postDigest(() => {
@ -142,7 +142,7 @@ function link(scope, elem, attrs) {
}, },
}); });
function setLangMode(lang) { function setLangMode(lang: string) {
ace.acequire('ace/ext/language_tools'); ace.acequire('ace/ext/language_tools');
codeEditor.setOptions({ codeEditor.setOptions({
enableBasicAutocompletion: true, enableBasicAutocompletion: true,
@ -170,7 +170,7 @@ function link(scope, elem, attrs) {
codeEditor.setTheme(theme); codeEditor.setTheme(theme);
} }
function setEditorContent(value) { function setEditorContent(value: string) {
codeEditor.setValue(value); codeEditor.setValue(value);
codeEditor.clearSelection(); codeEditor.clearSelection();
} }

View File

@ -13,9 +13,9 @@ export function spectrumPicker() {
scope: true, scope: true,
replace: true, replace: true,
template: '<color-picker color="ngModel.$viewValue" onChange="onColorChange"></color-picker>', template: '<color-picker color="ngModel.$viewValue" onChange="onColorChange"></color-picker>',
link: (scope, element, attrs, ngModel) => { link: (scope: any, element: any, attrs: any, ngModel: any) => {
scope.ngModel = ngModel; scope.ngModel = ngModel;
scope.onColorChange = color => { scope.onColorChange = (color: string) => {
ngModel.$setViewValue(color); ngModel.$setViewValue(color);
}; };
}, },

View File

@ -1,12 +0,0 @@
<div class="page-nav">
<div class="page-breadcrumbs">
<a class="breadcrumb-item active" href="/">
<i class="fa fa-home"></i>
</a>
<a class="breadcrumb-item" ng-href="{{::item.url}}" ng-repeat="item in ctrl.model.breadcrumbs">
{{::item.text}}
</a>
</div>
</div>
<dashboard-search></dashboard-search>

View File

@ -1,54 +0,0 @@
import coreModule from '../../core_module';
import appEvents from 'app/core/app_events';
import { NavModel } from '@grafana/ui';
export class NavbarCtrl {
model: NavModel;
/** @ngInject */
constructor() {}
showSearch() {
appEvents.emit('show-dash-search');
}
navItemClicked(navItem, evt) {
if (navItem.clickHandler) {
navItem.clickHandler();
evt.preventDefault();
}
}
}
export function navbarDirective() {
return {
restrict: 'E',
templateUrl: 'public/app/core/components/navbar/navbar.html',
controller: NavbarCtrl,
bindToController: true,
controllerAs: 'ctrl',
scope: {
model: '=',
},
link: (scope, elem) => {},
};
}
export function pageH1() {
return {
restrict: 'E',
template: `
<h1 class="page-header__title">
<i class="page-header__icon {{::model.header.icon}}" ng-if="::model.header.icon"></i>
<img class="page-header__img" ng-src="{{::model.header.img}}" ng-if="::model.header.img"></i>
{{model.header.text}}
</h1>
`,
scope: {
model: '=',
},
};
}
coreModule.directive('pageH1', pageH1);
coreModule.directive('navbar', navbarDirective);

View File

@ -40,7 +40,7 @@ export class QueryPart {
return this.def.renderer(this, innerExpr); return this.def.renderer(this, innerExpr);
} }
hasMultipleParamsInString(strValue, index) { hasMultipleParamsInString(strValue: string, index: number) {
if (strValue.indexOf(',') === -1) { if (strValue.indexOf(',') === -1) {
return false; return false;
} }
@ -48,7 +48,7 @@ export class QueryPart {
return this.def.params[index + 1] && this.def.params[index + 1].optional; return this.def.params[index + 1] && this.def.params[index + 1].optional;
} }
updateParam(strValue, index) { updateParam(strValue: string, index: number) {
// handle optional parameters // handle optional parameters
// if string contains ',' and next param is optional, split and update both // if string contains ',' and next param is optional, split and update both
if (this.hasMultipleParamsInString(strValue, index)) { if (this.hasMultipleParamsInString(strValue, index)) {
@ -81,7 +81,7 @@ export class QueryPart {
} }
} }
export function functionRenderer(part, innerExpr) { export function functionRenderer(part: any, innerExpr: string) {
const str = part.def.type + '('; const str = part.def.type + '(';
const parameters = _.map(part.params, (value, index) => { const parameters = _.map(part.params, (value, index) => {
const paramType = part.def.params[index]; const paramType = part.def.params[index];
@ -105,14 +105,14 @@ export function functionRenderer(part, innerExpr) {
return str + parameters.join(', ') + ')'; return str + parameters.join(', ') + ')';
} }
export function suffixRenderer(part, innerExpr) { export function suffixRenderer(part: QueryPartDef, innerExpr: string) {
return innerExpr + ' ' + part.params[0]; return innerExpr + ' ' + part.params[0];
} }
export function identityRenderer(part, innerExpr) { export function identityRenderer(part: QueryPartDef, innerExpr: string) {
return part.params[0]; return part.params[0];
} }
export function quotedIdentityRenderer(part, innerExpr) { export function quotedIdentityRenderer(part: QueryPartDef, innerExpr: string) {
return '"' + part.params[0] + '"'; return '"' + part.params[0] + '"';
} }

View File

@ -14,7 +14,7 @@ const template = `
`; `;
/** @ngInject */ /** @ngInject */
export function queryPartEditorDirective($compile, templateSrv) { export function queryPartEditorDirective(templateSrv: any) {
const paramTemplate = '<input type="text" class="hide input-mini tight-form-func-param"></input>'; const paramTemplate = '<input type="text" class="hide input-mini tight-form-func-param"></input>';
return { return {
@ -25,7 +25,7 @@ export function queryPartEditorDirective($compile, templateSrv) {
handleEvent: '&', handleEvent: '&',
debounce: '@', debounce: '@',
}, },
link: function postLink($scope, elem) { link: function postLink($scope: any, elem: any) {
const part = $scope.part; const part = $scope.part;
const partDef = part.def; const partDef = part.def;
const $paramsContainer = elem.find('.query-part-parameters'); const $paramsContainer = elem.find('.query-part-parameters');
@ -33,7 +33,7 @@ export function queryPartEditorDirective($compile, templateSrv) {
$scope.partActions = []; $scope.partActions = [];
function clickFuncParam(this: any, paramIndex) { function clickFuncParam(this: any, paramIndex: number) {
/*jshint validthis:true */ /*jshint validthis:true */
const $link = $(this); const $link = $(this);
const $input = $link.next(); const $input = $link.next();
@ -53,7 +53,7 @@ export function queryPartEditorDirective($compile, templateSrv) {
} }
} }
function inputBlur(this: any, paramIndex) { function inputBlur(this: any, paramIndex: number) {
/*jshint validthis:true */ /*jshint validthis:true */
const $input = $(this); const $input = $(this);
const $link = $input.prev(); const $link = $input.prev();
@ -72,7 +72,7 @@ export function queryPartEditorDirective($compile, templateSrv) {
$link.show(); $link.show();
} }
function inputKeyPress(this: any, paramIndex, e) { function inputKeyPress(this: any, paramIndex: number, e: any) {
/*jshint validthis:true */ /*jshint validthis:true */
if (e.which === 13) { if (e.which === 13) {
inputBlur.call(this, paramIndex); inputBlur.call(this, paramIndex);
@ -84,12 +84,12 @@ export function queryPartEditorDirective($compile, templateSrv) {
this.style.width = (3 + this.value.length) * 8 + 'px'; this.style.width = (3 + this.value.length) * 8 + 'px';
} }
function addTypeahead($input, param, paramIndex) { function addTypeahead($input: JQuery, param: any, paramIndex: number) {
if (!param.options && !param.dynamicLookup) { if (!param.options && !param.dynamicLookup) {
return; return;
} }
const typeaheadSource = (query, callback) => { const typeaheadSource = (query: string, callback: any) => {
if (param.options) { if (param.options) {
let options = param.options; let options = param.options;
if (param.type === 'int') { if (param.type === 'int') {
@ -101,7 +101,7 @@ export function queryPartEditorDirective($compile, templateSrv) {
} }
$scope.$apply(() => { $scope.$apply(() => {
$scope.handleEvent({ $event: { name: 'get-param-options' } }).then(result => { $scope.handleEvent({ $event: { name: 'get-param-options' } }).then((result: any) => {
const dynamicOptions = _.map(result, op => { const dynamicOptions = _.map(result, op => {
return _.escape(op.value); return _.escape(op.value);
}); });
@ -116,7 +116,7 @@ export function queryPartEditorDirective($compile, templateSrv) {
source: typeaheadSource, source: typeaheadSource,
minLength: 0, minLength: 0,
items: 1000, items: 1000,
updater: value => { updater: (value: string) => {
value = _.unescape(value); value = _.unescape(value);
setTimeout(() => { setTimeout(() => {
inputBlur.call($input[0], paramIndex); inputBlur.call($input[0], paramIndex);
@ -138,12 +138,12 @@ export function queryPartEditorDirective($compile, templateSrv) {
} }
$scope.showActionsMenu = () => { $scope.showActionsMenu = () => {
$scope.handleEvent({ $event: { name: 'get-part-actions' } }).then(res => { $scope.handleEvent({ $event: { name: 'get-part-actions' } }).then((res: any) => {
$scope.partActions = res; $scope.partActions = res;
}); });
}; };
$scope.triggerPartAction = action => { $scope.triggerPartAction = (action: string) => {
$scope.handleEvent({ $event: { name: 'action', action: action } }); $scope.handleEvent({ $event: { name: 'action', action: action } });
}; };

View File

@ -1,4 +1,5 @@
import $ from 'jquery'; import $ from 'jquery';
// @ts-ignore
import baron from 'baron'; import baron from 'baron';
import coreModule from 'app/core/core_module'; import coreModule from 'app/core/core_module';
@ -14,7 +15,7 @@ const scrollerClass = 'baron__scroller';
export function geminiScrollbar() { export function geminiScrollbar() {
return { return {
restrict: 'A', restrict: 'A',
link: (scope, elem, attrs) => { link: (scope: any, elem: any, attrs: any) => {
let scrollRoot = elem.parent(); let scrollRoot = elem.parent();
const scroller = elem; const scroller = elem;

View File

@ -1,4 +1,5 @@
import React, { useContext } from 'react'; import React, { useContext } from 'react';
// @ts-ignore
import tinycolor from 'tinycolor2'; import tinycolor from 'tinycolor2';
import { SearchQuery } from './search'; import { SearchQuery } from './search';
import { css, cx } from 'emotion'; import { css, cx } from 'emotion';

View File

@ -1,67 +0,0 @@
import React from 'react';
import classNames from 'classnames';
export class SearchResult extends React.Component<any, any> {
constructor(props) {
super(props);
this.state = {
search: '',
};
}
render() {
return this.state.search.sections.map(section => {
return <SearchResultSection section={section} key={section.id} />;
});
}
}
export interface SectionProps {
section: any;
}
export class SearchResultSection extends React.Component<SectionProps, any> {
constructor(props) {
super(props);
}
renderItem(item) {
return (
<a className="search-item" href={item.url} key={item.id}>
<span className="search-item__icon">
<i className="fa fa-th-large" />
</span>
<span className="search-item__body">
<div className="search-item__body-title">{item.title}</div>
</span>
</a>
);
}
toggleSection = () => {
this.props.section.toggle();
};
render() {
const collapseClassNames = classNames({
fa: true,
'fa-plus': !this.props.section.expanded,
'fa-minus': this.props.section.expanded,
'search-section__header__toggle': true,
});
return (
<div className="search-section" key={this.props.section.id}>
<div className="search-section__header">
<i className={classNames('search-section__header__icon', this.props.section.icon)} />
<span className="search-section__header__text">{this.props.section.title}</span>
<i className={collapseClassNames} onClick={this.toggleSection} />
</div>
{this.props.section.expanded && (
<div className="search-section__items">{this.props.section.items.map(this.renderItem)}</div>
)}
</div>
);
}
}

View File

@ -6,6 +6,7 @@ import { contextSrv } from 'app/core/services/context_srv';
import appEvents from 'app/core/app_events'; import appEvents from 'app/core/app_events';
import { parse, SearchParserOptions, SearchParserResult } from 'search-query-parser'; import { parse, SearchParserOptions, SearchParserResult } from 'search-query-parser';
import { getDashboardSrv } from 'app/features/dashboard/services/DashboardSrv'; import { getDashboardSrv } from 'app/features/dashboard/services/DashboardSrv';
export interface SearchQuery { export interface SearchQuery {
query: string; query: string;
parsedQuery: SearchParserResult; parsedQuery: SearchParserResult;
@ -32,6 +33,11 @@ class SearchQueryParser {
} }
} }
interface SelectedIndicies {
dashboardIndex?: number;
folderIndex?: number;
}
export class SearchCtrl { export class SearchCtrl {
isOpen: boolean; isOpen: boolean;
query: SearchQuery; query: SearchQuery;
@ -49,7 +55,7 @@ export class SearchCtrl {
queryParser: SearchQueryParser; queryParser: SearchQueryParser;
/** @ngInject */ /** @ngInject */
constructor($scope, private $location, private $timeout, private searchSrv: SearchSrv) { constructor($scope: any, private $location: any, private $timeout: any, private searchSrv: SearchSrv) {
appEvents.on('show-dash-search', this.openSearch.bind(this), $scope); appEvents.on('show-dash-search', this.openSearch.bind(this), $scope);
appEvents.on('hide-dash-search', this.closeSearch.bind(this), $scope); appEvents.on('hide-dash-search', this.closeSearch.bind(this), $scope);
appEvents.on('search-query', debounce(this.search.bind(this), 500), $scope); appEvents.on('search-query', debounce(this.search.bind(this), 500), $scope);
@ -88,7 +94,7 @@ export class SearchCtrl {
appEvents.emit('search-query'); appEvents.emit('search-query');
} }
openSearch(evt, payload) { openSearch(evt: any, payload: any) {
if (this.isOpen) { if (this.isOpen) {
this.closeSearch(); this.closeSearch();
return; return;
@ -166,7 +172,7 @@ export class SearchCtrl {
}, 100); }, 100);
} }
moveSelection(direction) { moveSelection(direction: number) {
if (this.results.length === 0) { if (this.results.length === 0) {
return; return;
} }
@ -252,14 +258,14 @@ export class SearchCtrl {
return query.query === '' && query.starred === false && query.tags.length === 0; return query.query === '' && query.starred === false && query.tags.length === 0;
} }
filterByTag(tag) { filterByTag(tag: string) {
if (_.indexOf(this.query.tags, tag) === -1) { if (_.indexOf(this.query.tags, tag) === -1) {
this.query.tags.push(tag); this.query.tags.push(tag);
this.search(); this.search();
} }
} }
removeTag(tag, evt) { removeTag(tag: string, evt: any) {
this.query.tags = _.without(this.query.tags, tag); this.query.tags = _.without(this.query.tags, tag);
this.search(); this.search();
this.giveSearchFocus = true; this.giveSearchFocus = true;
@ -298,14 +304,11 @@ export class SearchCtrl {
this.moveSelection(0); this.moveSelection(0);
} }
private getFlattenedResultForNavigation(): Array<{ private getFlattenedResultForNavigation(): SelectedIndicies[] {
folderIndex: number;
dashboardIndex: number;
}> {
let folderIndex = 0; let folderIndex = 0;
return _.flatMap(this.results, s => { return _.flatMap(this.results, (s: any) => {
let result = []; let result: SelectedIndicies[] = [];
result.push({ result.push({
folderIndex: folderIndex, folderIndex: folderIndex,

View File

@ -10,15 +10,15 @@ export class SearchResultsCtrl {
editable: boolean; editable: boolean;
/** @ngInject */ /** @ngInject */
constructor(private $location) {} constructor(private $location: any) {}
toggleFolderExpand(section) { toggleFolderExpand(section: any) {
if (section.toggle) { if (section.toggle) {
if (!section.expanded && this.onFolderExpanding) { if (!section.expanded && this.onFolderExpanding) {
this.onFolderExpanding(); this.onFolderExpanding();
} }
section.toggle(section).then(f => { section.toggle(section).then((f: any) => {
if (this.editable && f.expanded) { if (this.editable && f.expanded) {
if (f.items) { if (f.items) {
_.each(f.items, i => { _.each(f.items, i => {
@ -34,7 +34,7 @@ export class SearchResultsCtrl {
} }
} }
navigateToFolder(section, evt) { navigateToFolder(section: any, evt: any) {
this.$location.path(section.url); this.$location.path(section.url);
if (evt) { if (evt) {
@ -43,7 +43,7 @@ export class SearchResultsCtrl {
} }
} }
toggleSelection(item, evt) { toggleSelection(item: any, evt: any) {
item.checked = !item.checked; item.checked = !item.checked;
if (item.items) { if (item.items) {
@ -62,14 +62,14 @@ export class SearchResultsCtrl {
} }
} }
onItemClick(item) { onItemClick(item: any) {
//Check if one string can be found in the other //Check if one string can be found in the other
if (this.$location.path().indexOf(item.url) > -1 || item.url.indexOf(this.$location.path()) > -1) { if (this.$location.path().indexOf(item.url) > -1 || item.url.indexOf(this.$location.path()) > -1) {
appEvents.emit('hide-dash-search'); appEvents.emit('hide-dash-search');
} }
} }
selectTag(tag, evt) { selectTag(tag: any, evt: any) {
if (this.onTagSelected) { if (this.onTagSelected) {
this.onTagSelected({ $tag: tag }); this.onTagSelected({ $tag: tag });
} }

View File

@ -10,7 +10,9 @@ jest.mock('../../app_events', () => ({
const setup = (propOverrides?: object) => { const setup = (propOverrides?: object) => {
const props = Object.assign( const props = Object.assign(
{ {
link: {}, link: {
text: 'Hello',
},
user: { user: {
id: 1, id: 1,
isGrafanaAdmin: false, isGrafanaAdmin: false,
@ -87,9 +89,9 @@ describe('Functions', () => {
const wrapper = setup(); const wrapper = setup();
const mockEvent = { preventDefault: jest.fn() }; const mockEvent = { preventDefault: jest.fn() };
it('should emit show modal event if url matches shortcut', () => { it('should emit show modal event if url matches shortcut', () => {
const child = { url: '/shortcuts' }; const child = { url: '/shortcuts', text: 'hello' };
const instance = wrapper.instance() as BottomNavLinks; const instance = wrapper.instance() as BottomNavLinks;
instance.itemClicked(mockEvent, child); instance.itemClicked(mockEvent as any, child);
expect(appEvents.emit).toHaveBeenCalledWith('show-modal', { templateHtml: '<help-modal></help-modal>' }); expect(appEvents.emit).toHaveBeenCalledWith('show-modal', { templateHtml: '<help-modal></help-modal>' });
}); });

View File

@ -1,14 +1,15 @@
import React, { PureComponent } from 'react'; import React, { PureComponent } from 'react';
import appEvents from '../../app_events'; import appEvents from '../../app_events';
import { User } from '../../services/context_srv'; import { User } from '../../services/context_srv';
import { NavModelItem } from '@grafana/ui';
export interface Props { export interface Props {
link: any; link: NavModelItem;
user: User; user: User;
} }
class BottomNavLinks extends PureComponent<Props> { class BottomNavLinks extends PureComponent<Props> {
itemClicked = (event, child) => { itemClicked = (event: React.SyntheticEvent, child: NavModelItem) => {
if (child.url === '/shortcuts') { if (child.url === '/shortcuts') {
event.preventDefault(); event.preventDefault();
appEvents.emit('show-modal', { appEvents.emit('show-modal', {
@ -57,7 +58,7 @@ class BottomNavLinks extends PureComponent<Props> {
link.children.map((child, index) => { link.children.map((child, index) => {
if (!child.hideFromMenu) { if (!child.hideFromMenu) {
return ( return (
<li className={child.divider} key={`${child.text}-${index}`}> <li key={`${child.text}-${index}`}>
<a href={child.url} target={child.target} onClick={event => this.itemClicked(event, child)}> <a href={child.url} target={child.target} onClick={event => this.itemClicked(event, child)}>
{child.icon && <i className={child.icon} />} {child.icon && <i className={child.icon} />}
{child.text} {child.text}

View File

@ -4,10 +4,11 @@ import SignIn from './SignIn';
import BottomNavLinks from './BottomNavLinks'; import BottomNavLinks from './BottomNavLinks';
import { contextSrv } from 'app/core/services/context_srv'; import { contextSrv } from 'app/core/services/context_srv';
import config from '../../config'; import config from '../../config';
import { NavModelItem } from '@grafana/ui';
export default function BottomSection() { export default function BottomSection() {
const navTree: any = _.cloneDeep(config.bootData.navTree); const navTree: NavModelItem[] = _.cloneDeep(config.bootData.navTree);
const bottomNav: any = _.filter(navTree, item => item.hideFromMenu); const bottomNav: NavModelItem[] = _.filter(navTree, item => item.hideFromMenu);
const isSignedIn = contextSrv.isSignedIn; const isSignedIn = contextSrv.isSignedIn;
const user = contextSrv.user; const user = contextSrv.user;

View File

@ -1,8 +1,9 @@
import React, { FC } from 'react'; import React, { FC } from 'react';
import DropDownChild from './DropDownChild'; import DropDownChild from './DropDownChild';
import { NavModelItem } from '@grafana/ui';
interface Props { interface Props {
link: any; link: NavModelItem;
} }
const SideMenuDropDown: FC<Props> = props => { const SideMenuDropDown: FC<Props> = props => {

View File

@ -67,7 +67,9 @@ exports[`Render should render component 1`] = `
> >
<span <span
className="sidemenu-item-text" className="sidemenu-item-text"
/> >
Hello
</span>
</li> </li>
</ul> </ul>
</div> </div>

View File

@ -20,7 +20,6 @@ import { colors } from '@grafana/ui/';
import { searchDirective } from './components/search/search'; import { searchDirective } from './components/search/search';
import { infoPopover } from './components/info_popover'; import { infoPopover } from './components/info_popover';
import { navbarDirective } from './components/navbar/navbar';
import { arrayJoin } from './directives/array_join'; import { arrayJoin } from './directives/array_join';
import { liveSrv } from './live/live_srv'; import { liveSrv } from './live/live_srv';
import { Emitter } from './utils/emitter'; import { Emitter } from './utils/emitter';
@ -56,7 +55,6 @@ export {
registerAngularDirectives, registerAngularDirectives,
arrayJoin, arrayJoin,
coreModule, coreModule,
navbarDirective,
searchDirective, searchDirective,
liveSrv, liveSrv,
layoutSelector, layoutSelector,

View File

@ -97,9 +97,9 @@ exports[`Render when feature toggle editorsCanAdmin is turned off should not ren
isDisabled={false} isDisabled={false}
isLoading={false} isLoading={false}
isMulti={false} isMulti={false}
isOpen={false}
isSearchable={false} isSearchable={false}
maxMenuHeight={300} maxMenuHeight={300}
menuIsOpen={false}
onChange={[Function]} onChange={[Function]}
openMenuOnFocus={false} openMenuOnFocus={false}
options={ options={
@ -123,7 +123,6 @@ exports[`Render when feature toggle editorsCanAdmin is turned off should not ren
"value": 0, "value": 0,
} }
} }
width={null}
/> />
</div> </div>
</td> </td>
@ -183,9 +182,9 @@ exports[`Render when feature toggle editorsCanAdmin is turned on should render p
isDisabled={false} isDisabled={false}
isLoading={false} isLoading={false}
isMulti={false} isMulti={false}
isOpen={false}
isSearchable={false} isSearchable={false}
maxMenuHeight={300} maxMenuHeight={300}
menuIsOpen={false}
onChange={[Function]} onChange={[Function]}
openMenuOnFocus={false} openMenuOnFocus={false}
options={ options={
@ -209,7 +208,6 @@ exports[`Render when feature toggle editorsCanAdmin is turned on should render p
"value": 0, "value": 0,
} }
} }
width={null}
/> />
</div> </div>
</td> </td>

View File

@ -39,6 +39,8 @@ export interface DashboardAcl {
name?: string; name?: string;
inherited?: boolean; inherited?: boolean;
sortRank?: number; sortRank?: number;
userAvatarUrl?: string;
teamAvatarUrl?: string;
} }
export interface DashboardPermissionInfo { export interface DashboardPermissionInfo {