Commit f12d47ef by Torkel Ödegaard Committed by GitHub

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

* Chore: Typescript no-implicit any fixes progress

* Fixed tests

* Updated snapshot
parent 813e3ffc
......@@ -55,11 +55,11 @@ export interface CommonProps<T> {
onCloseMenu?: () => void;
}
export interface SelectProps<T> {
export interface SelectProps<T> extends CommonProps<T> {
options: Array<SelectOptionItem<T>>;
}
interface AsyncProps<T> {
interface AsyncProps<T> extends CommonProps<T> {
defaultOptions: boolean;
loadOptions: (query: string) => Promise<Array<SelectOptionItem<T>>>;
loadingMessage?: () => string;
......@@ -95,9 +95,8 @@ export const MenuList = (props: any) => {
);
};
export class Select<T> extends PureComponent<CommonProps<T> & SelectProps<T>> {
static defaultProps = {
width: null,
export class Select<T> extends PureComponent<SelectProps<T>> {
static defaultProps: Partial<SelectProps<any>> = {
className: '',
isDisabled: false,
isSearchable: true,
......@@ -108,7 +107,7 @@ export class Select<T> extends PureComponent<CommonProps<T> & SelectProps<T>> {
isLoading: false,
backspaceRemovesValue: true,
maxMenuHeight: 300,
menuIsOpen: false,
isOpen: false,
components: {
Option: SelectOption,
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>> {
static defaultProps = {
width: null,
export class AsyncSelect<T> extends PureComponent<AsyncProps<T>> {
static defaultProps: Partial<AsyncProps<any>> = {
className: '',
components: {},
loadingMessage: () => 'Loading...',
......
......@@ -7,11 +7,13 @@ export interface NavModelItem {
id?: string;
active?: boolean;
hideFromTabs?: boolean;
hideFromMenu?: boolean;
divider?: boolean;
children?: NavModelItem[];
breadcrumbs?: NavModelBreadcrumb[];
target?: string;
parentItem?: NavModelItem;
showOrgSwitcher?: boolean;
}
export interface NavModel {
......
......@@ -4,7 +4,6 @@ import { AnnotationQueryEditor as StackdriverAnnotationQueryEditor } from 'app/p
import { PasswordStrength } from './components/PasswordStrength';
import PageHeader from './components/PageHeader/PageHeader';
import EmptyListCTA from './components/EmptyListCTA/EmptyListCTA';
import { SearchResult } from './components/search/SearchResult';
import { TagFilter } from './components/TagFilter/TagFilter';
import { SideMenu } from './components/sidemenu/SideMenu';
import { MetricSelect } from './components/Select/MetricSelect';
......@@ -20,7 +19,6 @@ export function registerAngularDirectives() {
react2AngularDirective('appNotificationsList', AppNotificationList, []);
react2AngularDirective('pageHeader', PageHeader, ['model', 'noTabs']);
react2AngularDirective('emptyListCta', EmptyListCTA, ['model']);
react2AngularDirective('searchResult', SearchResult, []);
react2AngularDirective('searchField', SearchField, [
'query',
'autoFocus',
......
......@@ -4,11 +4,11 @@ import { AlertBox } from '../AlertBox/AlertBox';
interface Props {
appNotification: AppNotification;
onClearNotification: (id) => void;
onClearNotification: (id: number) => void;
}
export default class AppNotificationItem extends Component<Props> {
shouldComponentUpdate(nextProps) {
shouldComponentUpdate(nextProps: Props) {
return this.props.appNotification.id !== nextProps.appNotification.id;
}
......
......@@ -20,12 +20,12 @@ export class AppNotificationList extends PureComponent<Props> {
componentDidMount() {
const { notifyApp } = this.props;
appEvents.on('alert-warning', options => notifyApp(createWarningNotification(options[0], options[1])));
appEvents.on('alert-success', options => notifyApp(createSuccessNotification(options[0], options[1])));
appEvents.on('alert-error', options => notifyApp(createErrorNotification(options[0], options[1])));
appEvents.on('alert-warning', (options: string[]) => notifyApp(createWarningNotification(options[0], options[1])));
appEvents.on('alert-success', (options: string[]) => notifyApp(createSuccessNotification(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);
};
......
......@@ -14,10 +14,10 @@ export interface Props {
export default class OrgActionBar extends PureComponent<Props> {
render() {
const { searchQuery, layoutMode, onSetLayoutMode, linkButton, setSearchQuery, target } = this.props;
const linkProps = { href: linkButton.href, target: undefined };
const linkProps = { href: linkButton.href };
if (target) {
linkProps.target = target;
(linkProps as any).target = target;
}
return (
......
import React from 'react';
import PageHeader from './PageHeader';
import { shallow } from 'enzyme';
import { shallow, ShallowWrapper } from 'enzyme';
describe('PageHeader', () => {
let wrapper;
let wrapper: ShallowWrapper<PageHeader>;
describe('when the nav tree has a node with a title', () => {
beforeAll(() => {
......
......@@ -22,7 +22,7 @@ class AddPermissions extends Component<Props, NewDashboardAclItem> {
showPermissionLevels: true,
};
constructor(props) {
constructor(props: Props) {
super(props);
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;
switch (type) {
......@@ -65,7 +65,7 @@ class AddPermissions extends Component<Props, NewDashboardAclItem> {
this.setState({ permission: permission.value });
};
onSubmit = async evt => {
onSubmit = async (evt: React.SyntheticEvent) => {
evt.preventDefault();
await this.props.onAddPermission(this.state);
this.setState(this.getCleanState());
......
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 { FolderInfo } from 'app/types';
const setClassNameHelper = inherited => {
const setClassNameHelper = (inherited: boolean) => {
return inherited ? 'gf-form-disabled' : '';
};
function ItemAvatar({ item }) {
function ItemAvatar({ item }: { item: DashboardAcl }) {
if (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" />;
}
function ItemDescription({ item }) {
function ItemDescription({ item }: { item: DashboardAcl }) {
if (item.userId) {
return <span className="filter-table__weak-italic">(User)</span>;
}
......@@ -39,8 +39,8 @@ interface Props {
}
export default class PermissionsListItem extends PureComponent<Props> {
onPermissionChanged = option => {
this.props.onPermissionChanged(this.props.item, option.value as PermissionLevel);
onPermissionChanged = (option: SelectOptionItem<PermissionLevel>) => {
this.props.onPermissionChanged(this.props.item, option.value);
};
onRemoveItem = () => {
......
import React, { PureComponent } from 'react';
// @ts-ignore
import Remarkable from 'remarkable';
import { getBackendSrv } from '../../services/backend_srv';
......@@ -37,7 +38,7 @@ export class PluginHelp extends PureComponent<Props, State> {
getBackendSrv()
.get(`/api/plugins/${plugin.id}/markdown/${type}`)
.then(response => {
.then((response: string) => {
const markdown = new Remarkable();
const helpHtml = markdown.render(response);
......
......@@ -19,13 +19,13 @@ interface State {
}
export class MetricSelect extends React.Component<Props, State> {
static defaultProps = {
static defaultProps: Partial<Props> = {
variables: [],
options: [],
isSearchable: true,
};
constructor(props) {
constructor(props: Props) {
super(props);
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);
}
buildOptions({ variables = [], options }) {
buildOptions({ variables = [], options }: Props) {
return variables.length > 0 ? [this.getVariablesGroup(), ...options] : options;
}
......
import React from 'react';
// @ts-ignore
import renderer from 'react-test-renderer';
import { TeamPicker } from './TeamPicker';
......
......@@ -23,7 +23,7 @@ export interface State {
export class TeamPicker extends Component<Props, State> {
debouncedSearch: any;
constructor(props) {
constructor(props: Props) {
super(props);
this.state = { isLoading: false };
this.search = this.search.bind(this);
......@@ -42,8 +42,8 @@ export class TeamPicker extends Component<Props, State> {
query = '';
}
return backendSrv.get(`/api/teams/search?perpage=10&page=1&query=${query}`).then(result => {
const teams = result.teams.map(team => {
return backendSrv.get(`/api/teams/search?perpage=10&page=1&query=${query}`).then((result: any) => {
const teams = result.teams.map((team: any) => {
return {
id: team.id,
value: team.id,
......
import React from 'react';
// @ts-ignore
import renderer from 'react-test-renderer';
import { UserPicker } from './UserPicker';
......
......@@ -24,7 +24,7 @@ export interface State {
export class UserPicker extends Component<Props, State> {
debouncedSearch: any;
constructor(props) {
constructor(props: Props) {
super(props);
this.state = { isLoading: false };
this.search = this.search.bind(this);
......@@ -45,8 +45,8 @@ export class UserPicker extends Component<Props, State> {
return backendSrv
.get(`/api/org/users?query=${query}&limit=10`)
.then(result => {
return result.map(user => ({
.then((result: any) => {
return result.map((user: any) => ({
id: user.userId,
value: user.userId,
label: user.login === user.email ? user.login : `${user.login} - ${user.email}`,
......
......@@ -27,7 +27,7 @@ const timezones = [
export class SharedPreferences extends PureComponent<Props, State> {
backendSrv: BackendSrv = getBackendSrv();
constructor(props) {
constructor(props: Props) {
super(props);
this.state = {
......@@ -72,7 +72,7 @@ export class SharedPreferences extends PureComponent<Props, State> {
});
}
onSubmitForm = async event => {
onSubmitForm = async (event: React.SyntheticEvent) => {
event.preventDefault();
const { homeDashboardId, theme, timezone } = this.state;
......
......@@ -55,7 +55,7 @@ const DEFAULT_SNIPPETS = true;
const editorTemplate = `<div></div>`;
function link(scope, elem, attrs) {
function link(scope: any, elem: any, attrs: any) {
// Options
const langMode = attrs.mode || DEFAULT_MODE;
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.
scope.$watch('content', (newValue, oldValue) => {
scope.$watch('content', (newValue: any, oldValue: any) => {
const editorValue = codeEditor.getValue();
if (newValue !== editorValue && newValue !== oldValue) {
scope.$$postDigest(() => {
......@@ -142,7 +142,7 @@ function link(scope, elem, attrs) {
},
});
function setLangMode(lang) {
function setLangMode(lang: string) {
ace.acequire('ace/ext/language_tools');
codeEditor.setOptions({
enableBasicAutocompletion: true,
......@@ -170,7 +170,7 @@ function link(scope, elem, attrs) {
codeEditor.setTheme(theme);
}
function setEditorContent(value) {
function setEditorContent(value: string) {
codeEditor.setValue(value);
codeEditor.clearSelection();
}
......
......@@ -13,9 +13,9 @@ export function spectrumPicker() {
scope: true,
replace: true,
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.onColorChange = color => {
scope.onColorChange = (color: string) => {
ngModel.$setViewValue(color);
};
},
......
<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>
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);
......@@ -40,7 +40,7 @@ export class QueryPart {
return this.def.renderer(this, innerExpr);
}
hasMultipleParamsInString(strValue, index) {
hasMultipleParamsInString(strValue: string, index: number) {
if (strValue.indexOf(',') === -1) {
return false;
}
......@@ -48,7 +48,7 @@ export class QueryPart {
return this.def.params[index + 1] && this.def.params[index + 1].optional;
}
updateParam(strValue, index) {
updateParam(strValue: string, index: number) {
// handle optional parameters
// if string contains ',' and next param is optional, split and update both
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 parameters = _.map(part.params, (value, index) => {
const paramType = part.def.params[index];
......@@ -105,14 +105,14 @@ export function functionRenderer(part, innerExpr) {
return str + parameters.join(', ') + ')';
}
export function suffixRenderer(part, innerExpr) {
export function suffixRenderer(part: QueryPartDef, innerExpr: string) {
return innerExpr + ' ' + part.params[0];
}
export function identityRenderer(part, innerExpr) {
export function identityRenderer(part: QueryPartDef, innerExpr: string) {
return part.params[0];
}
export function quotedIdentityRenderer(part, innerExpr) {
export function quotedIdentityRenderer(part: QueryPartDef, innerExpr: string) {
return '"' + part.params[0] + '"';
}
......@@ -14,7 +14,7 @@ const template = `
`;
/** @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>';
return {
......@@ -25,7 +25,7 @@ export function queryPartEditorDirective($compile, templateSrv) {
handleEvent: '&',
debounce: '@',
},
link: function postLink($scope, elem) {
link: function postLink($scope: any, elem: any) {
const part = $scope.part;
const partDef = part.def;
const $paramsContainer = elem.find('.query-part-parameters');
......@@ -33,7 +33,7 @@ export function queryPartEditorDirective($compile, templateSrv) {
$scope.partActions = [];
function clickFuncParam(this: any, paramIndex) {
function clickFuncParam(this: any, paramIndex: number) {
/*jshint validthis:true */
const $link = $(this);
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 */
const $input = $(this);
const $link = $input.prev();
......@@ -72,7 +72,7 @@ export function queryPartEditorDirective($compile, templateSrv) {
$link.show();
}
function inputKeyPress(this: any, paramIndex, e) {
function inputKeyPress(this: any, paramIndex: number, e: any) {
/*jshint validthis:true */
if (e.which === 13) {
inputBlur.call(this, paramIndex);
......@@ -84,12 +84,12 @@ export function queryPartEditorDirective($compile, templateSrv) {
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) {
return;
}
const typeaheadSource = (query, callback) => {
const typeaheadSource = (query: string, callback: any) => {
if (param.options) {
let options = param.options;
if (param.type === 'int') {
......@@ -101,7 +101,7 @@ export function queryPartEditorDirective($compile, templateSrv) {
}
$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 => {
return _.escape(op.value);
});
......@@ -116,7 +116,7 @@ export function queryPartEditorDirective($compile, templateSrv) {
source: typeaheadSource,
minLength: 0,
items: 1000,
updater: value => {
updater: (value: string) => {
value = _.unescape(value);
setTimeout(() => {
inputBlur.call($input[0], paramIndex);
......@@ -138,12 +138,12 @@ export function queryPartEditorDirective($compile, templateSrv) {
}
$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.triggerPartAction = action => {
$scope.triggerPartAction = (action: string) => {
$scope.handleEvent({ $event: { name: 'action', action: action } });
};
......
import $ from 'jquery';
// @ts-ignore
import baron from 'baron';
import coreModule from 'app/core/core_module';
......@@ -14,7 +15,7 @@ const scrollerClass = 'baron__scroller';
export function geminiScrollbar() {
return {
restrict: 'A',
link: (scope, elem, attrs) => {
link: (scope: any, elem: any, attrs: any) => {
let scrollRoot = elem.parent();
const scroller = elem;
......
import React, { useContext } from 'react';
// @ts-ignore
import tinycolor from 'tinycolor2';
import { SearchQuery } from './search';
import { css, cx } from 'emotion';
......
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>
);
}
}
......@@ -6,6 +6,7 @@ import { contextSrv } from 'app/core/services/context_srv';
import appEvents from 'app/core/app_events';
import { parse, SearchParserOptions, SearchParserResult } from 'search-query-parser';
import { getDashboardSrv } from 'app/features/dashboard/services/DashboardSrv';
export interface SearchQuery {
query: string;
parsedQuery: SearchParserResult;
......@@ -32,6 +33,11 @@ class SearchQueryParser {
}
}
interface SelectedIndicies {
dashboardIndex?: number;
folderIndex?: number;
}
export class SearchCtrl {
isOpen: boolean;
query: SearchQuery;
......@@ -49,7 +55,7 @@ export class SearchCtrl {
queryParser: SearchQueryParser;
/** @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('hide-dash-search', this.closeSearch.bind(this), $scope);
appEvents.on('search-query', debounce(this.search.bind(this), 500), $scope);
......@@ -88,7 +94,7 @@ export class SearchCtrl {
appEvents.emit('search-query');
}
openSearch(evt, payload) {
openSearch(evt: any, payload: any) {
if (this.isOpen) {
this.closeSearch();
return;
......@@ -166,7 +172,7 @@ export class SearchCtrl {
}, 100);
}
moveSelection(direction) {
moveSelection(direction: number) {
if (this.results.length === 0) {
return;
}
......@@ -252,14 +258,14 @@ export class SearchCtrl {
return query.query === '' && query.starred === false && query.tags.length === 0;
}
filterByTag(tag) {
filterByTag(tag: string) {
if (_.indexOf(this.query.tags, tag) === -1) {
this.query.tags.push(tag);
this.search();
}
}
removeTag(tag, evt) {
removeTag(tag: string, evt: any) {
this.query.tags = _.without(this.query.tags, tag);
this.search();
this.giveSearchFocus = true;
......@@ -298,14 +304,11 @@ export class SearchCtrl {
this.moveSelection(0);
}
private getFlattenedResultForNavigation(): Array<{
folderIndex: number;
dashboardIndex: number;
}> {
private getFlattenedResultForNavigation(): SelectedIndicies[] {
let folderIndex = 0;
return _.flatMap(this.results, s => {
let result = [];
return _.flatMap(this.results, (s: any) => {
let result: SelectedIndicies[] = [];
result.push({
folderIndex: folderIndex,
......
......@@ -10,15 +10,15 @@ export class SearchResultsCtrl {
editable: boolean;
/** @ngInject */
constructor(private $location) {}
constructor(private $location: any) {}
toggleFolderExpand(section) {
toggleFolderExpand(section: any) {
if (section.toggle) {
if (!section.expanded && this.onFolderExpanding) {
this.onFolderExpanding();
}
section.toggle(section).then(f => {
section.toggle(section).then((f: any) => {
if (this.editable && f.expanded) {
if (f.items) {
_.each(f.items, i => {
......@@ -34,7 +34,7 @@ export class SearchResultsCtrl {
}
}
navigateToFolder(section, evt) {
navigateToFolder(section: any, evt: any) {
this.$location.path(section.url);
if (evt) {
......@@ -43,7 +43,7 @@ export class SearchResultsCtrl {
}
}
toggleSelection(item, evt) {
toggleSelection(item: any, evt: any) {
item.checked = !item.checked;
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
if (this.$location.path().indexOf(item.url) > -1 || item.url.indexOf(this.$location.path()) > -1) {
appEvents.emit('hide-dash-search');
}
}
selectTag(tag, evt) {
selectTag(tag: any, evt: any) {
if (this.onTagSelected) {
this.onTagSelected({ $tag: tag });
}
......
......@@ -10,7 +10,9 @@ jest.mock('../../app_events', () => ({
const setup = (propOverrides?: object) => {
const props = Object.assign(
{
link: {},
link: {
text: 'Hello',
},
user: {
id: 1,
isGrafanaAdmin: false,
......@@ -87,9 +89,9 @@ describe('Functions', () => {
const wrapper = setup();
const mockEvent = { preventDefault: jest.fn() };
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;
instance.itemClicked(mockEvent, child);
instance.itemClicked(mockEvent as any, child);
expect(appEvents.emit).toHaveBeenCalledWith('show-modal', { templateHtml: '<help-modal></help-modal>' });
});
......
import React, { PureComponent } from 'react';
import appEvents from '../../app_events';
import { User } from '../../services/context_srv';
import { NavModelItem } from '@grafana/ui';
export interface Props {
link: any;
link: NavModelItem;
user: User;
}
class BottomNavLinks extends PureComponent<Props> {
itemClicked = (event, child) => {
itemClicked = (event: React.SyntheticEvent, child: NavModelItem) => {
if (child.url === '/shortcuts') {
event.preventDefault();
appEvents.emit('show-modal', {
......@@ -57,7 +58,7 @@ class BottomNavLinks extends PureComponent<Props> {
link.children.map((child, index) => {
if (!child.hideFromMenu) {
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)}>
{child.icon && <i className={child.icon} />}
{child.text}
......
......@@ -4,10 +4,11 @@ import SignIn from './SignIn';
import BottomNavLinks from './BottomNavLinks';
import { contextSrv } from 'app/core/services/context_srv';
import config from '../../config';
import { NavModelItem } from '@grafana/ui';
export default function BottomSection() {
const navTree: any = _.cloneDeep(config.bootData.navTree);
const bottomNav: any = _.filter(navTree, item => item.hideFromMenu);
const navTree: NavModelItem[] = _.cloneDeep(config.bootData.navTree);
const bottomNav: NavModelItem[] = _.filter(navTree, item => item.hideFromMenu);
const isSignedIn = contextSrv.isSignedIn;
const user = contextSrv.user;
......
import React, { FC } from 'react';
import DropDownChild from './DropDownChild';
import { NavModelItem } from '@grafana/ui';
interface Props {
link: any;
link: NavModelItem;
}
const SideMenuDropDown: FC<Props> = props => {
......
......@@ -67,7 +67,9 @@ exports[`Render should render component 1`] = `
>
<span
className="sidemenu-item-text"
/>
>
Hello
</span>
</li>
</ul>
</div>
......
......@@ -20,7 +20,6 @@ import { colors } from '@grafana/ui/';
import { searchDirective } from './components/search/search';
import { infoPopover } from './components/info_popover';
import { navbarDirective } from './components/navbar/navbar';
import { arrayJoin } from './directives/array_join';
import { liveSrv } from './live/live_srv';
import { Emitter } from './utils/emitter';
......@@ -56,7 +55,6 @@ export {
registerAngularDirectives,
arrayJoin,
coreModule,
navbarDirective,
searchDirective,
liveSrv,
layoutSelector,
......
......@@ -97,9 +97,9 @@ exports[`Render when feature toggle editorsCanAdmin is turned off should not ren
isDisabled={false}
isLoading={false}
isMulti={false}
isOpen={false}
isSearchable={false}
maxMenuHeight={300}
menuIsOpen={false}
onChange={[Function]}
openMenuOnFocus={false}
options={
......@@ -123,7 +123,6 @@ exports[`Render when feature toggle editorsCanAdmin is turned off should not ren
"value": 0,
}
}
width={null}
/>
</div>
</td>
......@@ -183,9 +182,9 @@ exports[`Render when feature toggle editorsCanAdmin is turned on should render p
isDisabled={false}
isLoading={false}
isMulti={false}
isOpen={false}
isSearchable={false}
maxMenuHeight={300}
menuIsOpen={false}
onChange={[Function]}
openMenuOnFocus={false}
options={
......@@ -209,7 +208,6 @@ exports[`Render when feature toggle editorsCanAdmin is turned on should render p
"value": 0,
}
}
width={null}
/>
</div>
</td>
......
......@@ -39,6 +39,8 @@ export interface DashboardAcl {
name?: string;
inherited?: boolean;
sortRank?: number;
userAvatarUrl?: string;
teamAvatarUrl?: string;
}
export interface DashboardPermissionInfo {
......
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