mirror of
https://github.com/grafana/grafana.git
synced 2024-11-24 09:50:29 -06:00
Merge branch 'master' into fix/15691-refresh-issue-playlists
This commit is contained in:
commit
81afd68752
@ -110,7 +110,7 @@ Formats single & multi valued variables for use in URL parameters.
|
||||
|
||||
```bash
|
||||
servers = ['foo()bar BAZ', 'test2']
|
||||
String to interpolate: '${servers:lucene}'
|
||||
String to interpolate: '${servers:percentencode}'
|
||||
Interpolation result: 'foo%28%29bar%20BAZ%2Ctest2'
|
||||
```
|
||||
|
||||
|
15
package.json
15
package.json
@ -27,12 +27,13 @@
|
||||
"@types/jest": "^23.3.2",
|
||||
"@types/jquery": "^1.10.35",
|
||||
"@types/node": "^8.0.31",
|
||||
"@types/react": "^16.7.6",
|
||||
"@types/react-dom": "^16.0.9",
|
||||
"@types/react": "^16.8.8",
|
||||
"@types/react-dom": "^16.8.2",
|
||||
"@types/react-grid-layout": "^0.16.6",
|
||||
"@types/react-select": "^2.0.4",
|
||||
"@types/react-transition-group": "^2.0.15",
|
||||
"@types/react-virtualized": "^9.18.12",
|
||||
"@types/clipboard": "^2.0.1",
|
||||
"angular-mocks": "1.6.6",
|
||||
"autoprefixer": "^6.4.0",
|
||||
"axios": "^0.17.1",
|
||||
@ -107,7 +108,7 @@
|
||||
"systemjs-plugin-css": "^0.1.36",
|
||||
"ts-jest": "^23.10.4",
|
||||
"ts-loader": "^5.1.0",
|
||||
"ts-node": "^8.0.2",
|
||||
"ts-node": "8.0.2",
|
||||
"tslib": "^1.9.3",
|
||||
"tslint": "^5.8.0",
|
||||
"tslint-loader": "^3.5.3",
|
||||
@ -176,7 +177,7 @@
|
||||
"baron": "^3.0.3",
|
||||
"brace": "^0.10.0",
|
||||
"classnames": "^2.2.6",
|
||||
"clipboard": "^1.7.1",
|
||||
"clipboard": "^2.0.4",
|
||||
"d3": "^4.11.0",
|
||||
"d3-scale-chromatic": "^1.3.0",
|
||||
"eventemitter3": "^2.0.3",
|
||||
@ -191,8 +192,8 @@
|
||||
"prismjs": "^1.6.0",
|
||||
"prop-types": "^15.6.2",
|
||||
"rc-cascader": "^0.14.0",
|
||||
"react": "^16.6.3",
|
||||
"react-dom": "^16.6.3",
|
||||
"react": "^16.8.4",
|
||||
"react-dom": "^16.8.4",
|
||||
"react-grid-layout": "0.16.6",
|
||||
"react-highlight-words": "0.11.0",
|
||||
"react-popper": "^1.3.0",
|
||||
@ -219,7 +220,7 @@
|
||||
},
|
||||
"resolutions": {
|
||||
"caniuse-db": "1.0.30000772",
|
||||
"**/@types/react": "16.7.6"
|
||||
"**/@types/react": "16.8.8"
|
||||
},
|
||||
"workspaces": {
|
||||
"packages": [
|
||||
|
@ -25,10 +25,10 @@
|
||||
"lodash": "^4.17.10",
|
||||
"moment": "^2.22.2",
|
||||
"papaparse": "^4.6.3",
|
||||
"react": "^16.6.3",
|
||||
"react": "^16.8.4",
|
||||
"react-color": "^2.17.0",
|
||||
"react-custom-scrollbars": "^4.2.1",
|
||||
"react-dom": "^16.6.3",
|
||||
"react-dom": "^16.8.4",
|
||||
"react-highlight-words": "0.11.0",
|
||||
"react-popper": "^1.3.0",
|
||||
"react-transition-group": "^2.2.1",
|
||||
@ -48,7 +48,7 @@
|
||||
"@types/lodash": "^4.14.119",
|
||||
"@types/node": "^10.12.18",
|
||||
"@types/papaparse": "^4.5.9",
|
||||
"@types/react": "^16.7.6",
|
||||
"@types/react": "^16.8.8",
|
||||
"@types/react-custom-scrollbars": "^4.0.5",
|
||||
"@types/react-test-renderer": "^16.0.3",
|
||||
"@types/react-transition-group": "^2.0.15",
|
||||
|
@ -25,6 +25,7 @@ const model: OptionProps<any> = {
|
||||
key: '',
|
||||
onClick: jest.fn(),
|
||||
onMouseOver: jest.fn(),
|
||||
onMouseMove: jest.fn(),
|
||||
tabIndex: 1,
|
||||
},
|
||||
label: 'Option label',
|
||||
|
@ -4,6 +4,7 @@ exports[`SelectOption renders correctly 1`] = `
|
||||
<div
|
||||
id=""
|
||||
onClick={[MockFunction]}
|
||||
onMouseMove={[MockFunction]}
|
||||
onMouseOver={[MockFunction]}
|
||||
tabIndex={1}
|
||||
>
|
||||
|
@ -8,6 +8,7 @@ import {
|
||||
CellMeasurerCache,
|
||||
CellMeasurer,
|
||||
GridCellProps,
|
||||
Index,
|
||||
} from 'react-virtualized';
|
||||
import { Themeable } from '../../types/theme';
|
||||
|
||||
@ -26,6 +27,7 @@ import { stringToJsRegex } from '../../utils/index';
|
||||
export interface Props extends Themeable {
|
||||
data: TableData;
|
||||
|
||||
minColumnWidth: number;
|
||||
showHeader: boolean;
|
||||
fixedHeader: boolean;
|
||||
fixedColumns: number;
|
||||
@ -46,6 +48,7 @@ interface State {
|
||||
|
||||
interface ColumnRenderInfo {
|
||||
header: string;
|
||||
width: number;
|
||||
builder: TableCellBuilder;
|
||||
}
|
||||
|
||||
@ -64,6 +67,7 @@ export class Table extends Component<Props, State> {
|
||||
fixedHeader: true,
|
||||
fixedColumns: 0,
|
||||
rotate: false,
|
||||
minColumnWidth: 150,
|
||||
};
|
||||
|
||||
constructor(props: Props) {
|
||||
@ -76,7 +80,7 @@ export class Table extends Component<Props, State> {
|
||||
this.renderer = this.initColumns(props);
|
||||
this.measurer = new CellMeasurerCache({
|
||||
defaultHeight: 30,
|
||||
defaultWidth: 150,
|
||||
fixedWidth: true,
|
||||
});
|
||||
}
|
||||
|
||||
@ -110,7 +114,8 @@ export class Table extends Component<Props, State> {
|
||||
|
||||
/** Given the configuration, setup how each column gets rendered */
|
||||
initColumns(props: Props): ColumnRenderInfo[] {
|
||||
const { styles, data } = props;
|
||||
const { styles, data, width, minColumnWidth } = props;
|
||||
const columnWidth = Math.max(width / data.columns.length, minColumnWidth);
|
||||
|
||||
return data.columns.map((col, index) => {
|
||||
let title = col.text;
|
||||
@ -131,6 +136,7 @@ export class Table extends Component<Props, State> {
|
||||
|
||||
return {
|
||||
header: title,
|
||||
width: columnWidth,
|
||||
builder: getCellBuilder(col, style, this.props),
|
||||
};
|
||||
});
|
||||
@ -228,6 +234,10 @@ export class Table extends Component<Props, State> {
|
||||
);
|
||||
};
|
||||
|
||||
getColumnWidth = (col: Index): number => {
|
||||
return this.renderer[col.index].width;
|
||||
};
|
||||
|
||||
render() {
|
||||
const { showHeader, fixedHeader, fixedColumns, rotate, width, height } = this.props;
|
||||
const { data } = this.state;
|
||||
@ -269,7 +279,7 @@ export class Table extends Component<Props, State> {
|
||||
rowCount={rowCount}
|
||||
overscanColumnCount={8}
|
||||
overscanRowCount={8}
|
||||
columnWidth={this.measurer.columnWidth}
|
||||
columnWidth={this.getColumnWidth}
|
||||
deferredMeasurementCache={this.measurer}
|
||||
cellRenderer={this.cellRenderer}
|
||||
rowHeight={this.measurer.rowHeight}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { FC } from 'react';
|
||||
import React, { FC, CSSProperties } from 'react';
|
||||
import Transition, { ExitHandler } from 'react-transition-group/Transition';
|
||||
|
||||
interface Props {
|
||||
@ -10,12 +10,12 @@ interface Props {
|
||||
}
|
||||
|
||||
export const FadeIn: FC<Props> = props => {
|
||||
const defaultStyle = {
|
||||
const defaultStyle: CSSProperties = {
|
||||
transition: `opacity ${props.duration}ms linear`,
|
||||
opacity: 0,
|
||||
};
|
||||
|
||||
const transitionStyles = {
|
||||
const transitionStyles: { [str: string]: CSSProperties } = {
|
||||
exited: { opacity: 0, display: 'none' },
|
||||
entering: { opacity: 0 },
|
||||
entered: { opacity: 1 },
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React from 'react';
|
||||
import React, { CSSProperties, FC } from 'react';
|
||||
import Transition from 'react-transition-group/Transition';
|
||||
|
||||
interface Style {
|
||||
@ -16,11 +16,18 @@ export const defaultStyle: Style = {
|
||||
overflow: 'hidden',
|
||||
};
|
||||
|
||||
export default ({ children, in: inProp, maxHeight = defaultMaxHeight, style = defaultStyle }) => {
|
||||
export interface Props {
|
||||
children: React.ReactNode;
|
||||
in: boolean;
|
||||
maxHeight?: number;
|
||||
style?: CSSProperties;
|
||||
}
|
||||
|
||||
export const SlideDown: FC<Props> = ({ children, in: inProp, maxHeight = defaultMaxHeight, style = defaultStyle }) => {
|
||||
// There are 4 main states a Transition can be in:
|
||||
// ENTERING, ENTERED, EXITING, EXITED
|
||||
// https://reactcommunity.org/react-transition-group/
|
||||
const transitionStyles = {
|
||||
// https://reactcommunity.or[g/react-transition-group/
|
||||
const transitionStyles: { [str: string]: CSSProperties } = {
|
||||
exited: { maxHeight: 0 },
|
||||
entering: { maxHeight: maxHeight },
|
||||
entered: { maxHeight: 'unset', overflow: 'visible' },
|
||||
@ -34,6 +41,7 @@ export default ({ children, in: inProp, maxHeight = defaultMaxHeight, style = de
|
||||
style={{
|
||||
...style,
|
||||
...transitionStyles[state],
|
||||
inProp,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
@ -11,10 +11,10 @@ interface Props {
|
||||
}
|
||||
|
||||
export class CopyToClipboard extends PureComponent<Props> {
|
||||
clipboardjs: any;
|
||||
clipboardjs: ClipboardJS;
|
||||
myRef: any;
|
||||
|
||||
constructor(props) {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.myRef = React.createRef();
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import renderer from 'react-test-renderer';
|
||||
import { shallow } from 'enzyme';
|
||||
import EmptyListCTA from './EmptyListCTA';
|
||||
|
||||
const model = {
|
||||
@ -16,7 +16,7 @@ const model = {
|
||||
|
||||
describe('EmptyListCTA', () => {
|
||||
it('renders correctly', () => {
|
||||
const tree = renderer.create(<EmptyListCTA model={model} />).toJSON();
|
||||
const tree = shallow(<EmptyListCTA model={model} />);
|
||||
expect(tree).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
@ -1,5 +1,4 @@
|
||||
import React, { PureComponent, createRef } from 'react';
|
||||
// import JSONFormatterJS, { JSONFormatterConfiguration } from 'json-formatter-js';
|
||||
import { JsonExplorer } from 'app/core/core'; // We have made some monkey-patching of json-formatter-js so we can't switch right now
|
||||
|
||||
interface Props {
|
||||
|
@ -3,7 +3,7 @@ import React, { PureComponent } from 'react';
|
||||
import { FormLabel, Select } from '@grafana/ui';
|
||||
import { getBackendSrv, BackendSrv } from 'app/core/services/backend_srv';
|
||||
|
||||
import { DashboardSearchHit } from 'app/types';
|
||||
import { DashboardSearchHit, DashboardSearchHitType } from 'app/types';
|
||||
|
||||
export interface Props {
|
||||
resourceUri: string;
|
||||
@ -41,6 +41,21 @@ export class SharedPreferences extends PureComponent<Props, State> {
|
||||
async componentDidMount() {
|
||||
const prefs = await this.backendSrv.get(`/api/${this.props.resourceUri}/preferences`);
|
||||
const dashboards = await this.backendSrv.search({ starred: true });
|
||||
const defaultDashboardHit: DashboardSearchHit = {
|
||||
id: 0,
|
||||
title: 'Default',
|
||||
tags: [],
|
||||
type: '' as DashboardSearchHitType,
|
||||
uid: '',
|
||||
uri: '',
|
||||
url: '',
|
||||
folderId: 0,
|
||||
folderTitle: '',
|
||||
folderUid: '',
|
||||
folderUrl: '',
|
||||
isStarred: false,
|
||||
slug: '',
|
||||
};
|
||||
|
||||
if (prefs.homeDashboardId > 0 && !dashboards.find(d => d.id === prefs.homeDashboardId)) {
|
||||
const missing = await this.backendSrv.search({ dashboardIds: [prefs.homeDashboardId] });
|
||||
@ -53,7 +68,7 @@ export class SharedPreferences extends PureComponent<Props, State> {
|
||||
homeDashboardId: prefs.homeDashboardId,
|
||||
theme: prefs.theme,
|
||||
timezone: prefs.timezone,
|
||||
dashboards: [{ id: 0, title: 'Default', tags: [], type: '', uid: '', uri: '', url: '' }, ...dashboards],
|
||||
dashboards: [defaultDashboardHit, ...dashboards],
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,8 @@
|
||||
import _ from 'lodash';
|
||||
import coreModule from '../../core_module';
|
||||
import { ISCEService, IQService } from 'angular';
|
||||
|
||||
function typeaheadMatcher(this: any, item) {
|
||||
function typeaheadMatcher(this: any, item: string) {
|
||||
let str = this.query;
|
||||
if (str === '') {
|
||||
return true;
|
||||
@ -16,8 +17,8 @@ function typeaheadMatcher(this: any, item) {
|
||||
}
|
||||
|
||||
export class FormDropdownCtrl {
|
||||
inputElement: any;
|
||||
linkElement: any;
|
||||
inputElement: JQLite;
|
||||
linkElement: JQLite;
|
||||
model: any;
|
||||
display: any;
|
||||
text: any;
|
||||
@ -37,7 +38,13 @@ export class FormDropdownCtrl {
|
||||
debounce: number;
|
||||
|
||||
/** @ngInject */
|
||||
constructor(private $scope, $element, private $sce, private templateSrv, private $q) {
|
||||
constructor(
|
||||
private $scope: any,
|
||||
$element: JQLite,
|
||||
private $sce: ISCEService,
|
||||
private templateSrv: any,
|
||||
private $q: IQService
|
||||
) {
|
||||
this.inputElement = $element.find('input').first();
|
||||
this.linkElement = $element.find('a').first();
|
||||
this.linkMode = true;
|
||||
@ -99,7 +106,7 @@ export class FormDropdownCtrl {
|
||||
}
|
||||
}
|
||||
|
||||
getOptionsInternal(query) {
|
||||
getOptionsInternal(query: string) {
|
||||
const result = this.getOptions({ $query: query });
|
||||
if (this.isPromiseLike(result)) {
|
||||
return result;
|
||||
@ -107,7 +114,7 @@ export class FormDropdownCtrl {
|
||||
return this.$q.when(result);
|
||||
}
|
||||
|
||||
isPromiseLike(obj) {
|
||||
isPromiseLike(obj: any) {
|
||||
return obj && typeof obj.then === 'function';
|
||||
}
|
||||
|
||||
@ -117,7 +124,7 @@ export class FormDropdownCtrl {
|
||||
} else {
|
||||
// if we have text use it
|
||||
if (this.lookupText) {
|
||||
this.getOptionsInternal('').then(options => {
|
||||
this.getOptionsInternal('').then((options: any) => {
|
||||
const item = _.find(options, { value: this.model });
|
||||
this.updateDisplay(item ? item.text : this.model);
|
||||
});
|
||||
@ -127,12 +134,12 @@ export class FormDropdownCtrl {
|
||||
}
|
||||
}
|
||||
|
||||
typeaheadSource(query, callback) {
|
||||
this.getOptionsInternal(query).then(options => {
|
||||
typeaheadSource(query: string, callback: (res: any) => void) {
|
||||
this.getOptionsInternal(query).then((options: any) => {
|
||||
this.optionCache = options;
|
||||
|
||||
// extract texts
|
||||
const optionTexts = _.map(options, op => {
|
||||
const optionTexts = _.map(options, (op: any) => {
|
||||
return _.escape(op.text);
|
||||
});
|
||||
|
||||
@ -147,7 +154,7 @@ export class FormDropdownCtrl {
|
||||
});
|
||||
}
|
||||
|
||||
typeaheadUpdater(text) {
|
||||
typeaheadUpdater(text: string) {
|
||||
if (text === this.text) {
|
||||
clearTimeout(this.cancelBlur);
|
||||
this.inputElement.focus();
|
||||
@ -159,7 +166,7 @@ export class FormDropdownCtrl {
|
||||
return text;
|
||||
}
|
||||
|
||||
switchToLink(fromClick) {
|
||||
switchToLink(fromClick: boolean) {
|
||||
if (this.linkMode && !fromClick) {
|
||||
return;
|
||||
}
|
||||
@ -178,7 +185,7 @@ export class FormDropdownCtrl {
|
||||
this.cancelBlur = setTimeout(this.switchToLink.bind(this), 200);
|
||||
}
|
||||
|
||||
updateValue(text) {
|
||||
updateValue(text: string) {
|
||||
text = _.unescape(text);
|
||||
|
||||
if (text === '' || this.text === text) {
|
||||
@ -214,7 +221,7 @@ export class FormDropdownCtrl {
|
||||
});
|
||||
}
|
||||
|
||||
updateDisplay(text) {
|
||||
updateDisplay(text: string) {
|
||||
this.text = text;
|
||||
this.display = this.$sce.trustAsHtml(this.templateSrv.highlightVariablesAsHtml(text));
|
||||
}
|
||||
|
@ -232,7 +232,7 @@ export class JsonExplorer {
|
||||
|
||||
// some pretty handling of number arrays
|
||||
if (this.isNumberArray()) {
|
||||
this.json.forEach((val, index) => {
|
||||
this.json.forEach((val: any, index: number) => {
|
||||
if (index > 0) {
|
||||
arrayWrapperSpan.appendChild(createElement('span', 'array-comma', ','));
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ export class LayoutSelectorCtrl {
|
||||
mode: string;
|
||||
|
||||
/** @ngInject */
|
||||
constructor(private $rootScope) {
|
||||
constructor(private $rootScope: any) {
|
||||
this.mode = store.get('grafana.list.layout.mode') || 'grid';
|
||||
}
|
||||
|
||||
@ -46,18 +46,18 @@ export function layoutSelector() {
|
||||
}
|
||||
|
||||
/** @ngInject */
|
||||
export function layoutMode($rootScope) {
|
||||
export function layoutMode($rootScope: any) {
|
||||
return {
|
||||
restrict: 'A',
|
||||
scope: {},
|
||||
link: (scope, elem) => {
|
||||
link: (scope: any, elem: any) => {
|
||||
const layout = store.get('grafana.list.layout.mode') || 'grid';
|
||||
let className = 'card-list-layout-' + layout;
|
||||
elem.addClass(className);
|
||||
|
||||
$rootScope.onAppEvent(
|
||||
'layout-mode-changed',
|
||||
(evt, newLayout) => {
|
||||
(evt: any, newLayout: any) => {
|
||||
elem.removeClass(className);
|
||||
className = 'card-list-layout-' + newLayout;
|
||||
elem.addClass(className);
|
||||
|
@ -11,7 +11,8 @@ export interface Section {
|
||||
id: number;
|
||||
uid: string;
|
||||
title: string;
|
||||
expanded: false;
|
||||
expanded: boolean;
|
||||
removable: boolean;
|
||||
items: any[];
|
||||
url: string;
|
||||
icon: string;
|
||||
|
@ -3,6 +3,7 @@ import coreModule from 'app/core/core_module';
|
||||
import appEvents from 'app/core/app_events';
|
||||
import config from 'app/core/config';
|
||||
import { DashboardModel } from 'app/features/dashboard/state/DashboardModel';
|
||||
import { DashboardSearchHit } from 'app/types/search';
|
||||
|
||||
export class BackendSrv {
|
||||
private inFlightRequests = {};
|
||||
@ -237,7 +238,7 @@ export class BackendSrv {
|
||||
return this.request({ url: '/api/login/ping', method: 'GET', retry: 1 });
|
||||
}
|
||||
|
||||
search(query) {
|
||||
search(query): Promise<DashboardSearchHit[]> {
|
||||
return this.get('/api/search', query);
|
||||
}
|
||||
|
||||
|
@ -1,21 +1,32 @@
|
||||
// @ts-ignore
|
||||
import _ from 'lodash';
|
||||
// @ts-ignore
|
||||
import { IQService } from 'angular';
|
||||
|
||||
import coreModule from 'app/core/core_module';
|
||||
import impressionSrv from 'app/core/services/impression_srv';
|
||||
import store from 'app/core/store';
|
||||
import { contextSrv } from 'app/core/services/context_srv';
|
||||
import { BackendSrv } from './backend_srv';
|
||||
import { Section } from '../components/manage_dashboards/manage_dashboards';
|
||||
import { DashboardSearchHit } from 'app/types/search';
|
||||
|
||||
interface Sections {
|
||||
[key: string]: Partial<Section>;
|
||||
}
|
||||
|
||||
export class SearchSrv {
|
||||
recentIsOpen: boolean;
|
||||
starredIsOpen: boolean;
|
||||
|
||||
/** @ngInject */
|
||||
constructor(private backendSrv, private $q) {
|
||||
constructor(private backendSrv: BackendSrv, private $q: IQService) {
|
||||
this.recentIsOpen = store.getBool('search.sections.recent', true);
|
||||
this.starredIsOpen = store.getBool('search.sections.starred', true);
|
||||
}
|
||||
|
||||
private getRecentDashboards(sections) {
|
||||
return this.queryForRecentDashboards().then(result => {
|
||||
private getRecentDashboards(sections: Sections) {
|
||||
return this.queryForRecentDashboards().then((result: any[]) => {
|
||||
if (result.length > 0) {
|
||||
sections['recent'] = {
|
||||
title: 'Recent',
|
||||
@ -30,8 +41,8 @@ export class SearchSrv {
|
||||
});
|
||||
}
|
||||
|
||||
private queryForRecentDashboards() {
|
||||
const dashIds = _.take(impressionSrv.getDashboardOpened(), 30);
|
||||
private queryForRecentDashboards(): Promise<number[]> {
|
||||
const dashIds: number[] = _.take(impressionSrv.getDashboardOpened(), 30);
|
||||
if (dashIds.length === 0) {
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
@ -45,7 +56,7 @@ export class SearchSrv {
|
||||
});
|
||||
}
|
||||
|
||||
private toggleRecent(section) {
|
||||
private toggleRecent(section: Section) {
|
||||
this.recentIsOpen = section.expanded = !section.expanded;
|
||||
store.set('search.sections.recent', this.recentIsOpen);
|
||||
|
||||
@ -59,13 +70,13 @@ export class SearchSrv {
|
||||
});
|
||||
}
|
||||
|
||||
private toggleStarred(section) {
|
||||
private toggleStarred(section: Section) {
|
||||
this.starredIsOpen = section.expanded = !section.expanded;
|
||||
store.set('search.sections.starred', this.starredIsOpen);
|
||||
return Promise.resolve(section);
|
||||
}
|
||||
|
||||
private getStarred(sections) {
|
||||
private getStarred(sections: Sections) {
|
||||
if (!contextSrv.isSignedIn) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
@ -84,7 +95,7 @@ export class SearchSrv {
|
||||
});
|
||||
}
|
||||
|
||||
search(options) {
|
||||
search(options: any) {
|
||||
const sections: any = {};
|
||||
const promises = [];
|
||||
const query = _.clone(options);
|
||||
@ -118,7 +129,7 @@ export class SearchSrv {
|
||||
});
|
||||
}
|
||||
|
||||
private handleSearchResult(sections, results) {
|
||||
private handleSearchResult(sections: Sections, results: DashboardSearchHit[]): any {
|
||||
if (results.length === 0) {
|
||||
return sections;
|
||||
}
|
||||
@ -177,7 +188,7 @@ export class SearchSrv {
|
||||
}
|
||||
}
|
||||
|
||||
private toggleFolder(section) {
|
||||
private toggleFolder(section: Section) {
|
||||
section.expanded = !section.expanded;
|
||||
section.icon = section.expanded ? 'fa fa-folder-open' : 'fa fa-folder';
|
||||
|
||||
|
@ -16,6 +16,7 @@ const mockSection = (overides?: object): Section => {
|
||||
items: [],
|
||||
checked: false,
|
||||
expanded: false,
|
||||
removable: false,
|
||||
hideHeader: false,
|
||||
icon: '',
|
||||
score: 0,
|
||||
|
@ -1,8 +1,12 @@
|
||||
// @ts-ignore
|
||||
import { IQService } from 'angular';
|
||||
|
||||
import { SearchSrv } from 'app/core/services/search_srv';
|
||||
import { BackendSrvMock } from 'test/mocks/backend_srv';
|
||||
import impressionSrv from 'app/core/services/impression_srv';
|
||||
import { contextSrv } from 'app/core/services/context_srv';
|
||||
import { beforeEach } from 'test/lib/common';
|
||||
import { BackendSrv } from '../services/backend_srv';
|
||||
|
||||
jest.mock('app/core/store', () => {
|
||||
return {
|
||||
@ -18,18 +22,18 @@ jest.mock('app/core/services/impression_srv', () => {
|
||||
});
|
||||
|
||||
describe('SearchSrv', () => {
|
||||
let searchSrv, backendSrvMock;
|
||||
let searchSrv: SearchSrv, backendSrvMock: BackendSrvMock;
|
||||
|
||||
beforeEach(() => {
|
||||
backendSrvMock = new BackendSrvMock();
|
||||
searchSrv = new SearchSrv(backendSrvMock, Promise);
|
||||
searchSrv = new SearchSrv(backendSrvMock as BackendSrv, (Promise as any) as IQService);
|
||||
|
||||
contextSrv.isSignedIn = true;
|
||||
impressionSrv.getDashboardOpened = jest.fn().mockReturnValue([]);
|
||||
});
|
||||
|
||||
describe('With recent dashboards', () => {
|
||||
let results;
|
||||
let results: any;
|
||||
|
||||
beforeEach(() => {
|
||||
backendSrvMock.search = jest
|
||||
@ -56,7 +60,7 @@ describe('SearchSrv', () => {
|
||||
});
|
||||
|
||||
describe('and 3 recent dashboards removed in backend', () => {
|
||||
let results;
|
||||
let results: any;
|
||||
|
||||
beforeEach(() => {
|
||||
backendSrvMock.search = jest
|
||||
@ -80,7 +84,7 @@ describe('SearchSrv', () => {
|
||||
});
|
||||
|
||||
describe('With starred dashboards', () => {
|
||||
let results;
|
||||
let results: any;
|
||||
|
||||
beforeEach(() => {
|
||||
backendSrvMock.search = jest.fn().mockReturnValue(Promise.resolve([{ id: 1, title: 'starred' }]));
|
||||
@ -97,7 +101,7 @@ describe('SearchSrv', () => {
|
||||
});
|
||||
|
||||
describe('With starred dashboards and recent', () => {
|
||||
let results;
|
||||
let results: any;
|
||||
|
||||
beforeEach(() => {
|
||||
backendSrvMock.search = jest
|
||||
@ -125,7 +129,7 @@ describe('SearchSrv', () => {
|
||||
});
|
||||
|
||||
describe('with no query string and dashboards with folders returned', () => {
|
||||
let results;
|
||||
let results: any;
|
||||
|
||||
beforeEach(() => {
|
||||
backendSrvMock.search = jest
|
||||
@ -173,12 +177,10 @@ describe('SearchSrv', () => {
|
||||
});
|
||||
|
||||
describe('with query string and dashboards with folders returned', () => {
|
||||
let results;
|
||||
let results: any;
|
||||
|
||||
beforeEach(() => {
|
||||
backendSrvMock.search = jest.fn();
|
||||
|
||||
backendSrvMock.search.mockReturnValue(
|
||||
backendSrvMock.search = jest.fn().mockReturnValue(
|
||||
Promise.resolve([
|
||||
{
|
||||
id: 2,
|
||||
@ -249,8 +251,9 @@ describe('SearchSrv', () => {
|
||||
backendSrvMock.search = jest.fn();
|
||||
backendSrvMock.search.mockReturnValue(Promise.resolve([]));
|
||||
|
||||
searchSrv.getRecentDashboards = () => {
|
||||
searchSrv['getRecentDashboards'] = () => {
|
||||
getRecentDashboardsCalled = true;
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
return searchSrv.search({ skipRecent: true }).then(() => {});
|
||||
@ -269,8 +272,9 @@ describe('SearchSrv', () => {
|
||||
backendSrvMock.search.mockReturnValue(Promise.resolve([]));
|
||||
impressionSrv.getDashboardOpened = jest.fn().mockReturnValue([]);
|
||||
|
||||
searchSrv.getStarred = () => {
|
||||
searchSrv['getStarred'] = () => {
|
||||
getStarredCalled = true;
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
return searchSrv.search({ skipStarred: true }).then(() => {});
|
||||
|
@ -7,7 +7,7 @@ import { getNavModel } from 'app/core/selectors/navModel';
|
||||
import { getApiKeys, getApiKeysCount } from './state/selectors';
|
||||
import { loadApiKeys, deleteApiKey, setSearchQuery, addApiKey } from './state/actions';
|
||||
import Page from 'app/core/components/Page/Page';
|
||||
import SlideDown from 'app/core/components/Animations/SlideDown';
|
||||
import { SlideDown } from 'app/core/components/Animations/SlideDown';
|
||||
import ApiKeysAddedModal from './ApiKeysAddedModal';
|
||||
import config from 'app/core/config';
|
||||
import appEvents from 'app/core/app_events';
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import { Tooltip } from '@grafana/ui';
|
||||
import SlideDown from 'app/core/components/Animations/SlideDown';
|
||||
import { SlideDown } from 'app/core/components/Animations/SlideDown';
|
||||
import { StoreState, FolderInfo } from 'app/types';
|
||||
import { DashboardAcl, PermissionLevel, NewDashboardAclItem } from 'app/types/acl';
|
||||
import {
|
||||
|
@ -111,12 +111,12 @@ export class PanelModel {
|
||||
cachedPluginOptions?: any;
|
||||
legend?: { show: boolean };
|
||||
|
||||
constructor(model) {
|
||||
constructor(model: any) {
|
||||
this.events = new Emitter();
|
||||
|
||||
// copy properties from persisted model
|
||||
for (const property in model) {
|
||||
this[property] = model[property];
|
||||
(this as any)[property] = model[property];
|
||||
}
|
||||
|
||||
// defaults
|
||||
@ -150,7 +150,7 @@ export class PanelModel {
|
||||
}
|
||||
}
|
||||
|
||||
getOptions(panelDefaults) {
|
||||
getOptions(panelDefaults: any) {
|
||||
return _.defaultsDeep(this.options || {}, panelDefaults);
|
||||
}
|
||||
|
||||
@ -227,7 +227,7 @@ export class PanelModel {
|
||||
}
|
||||
return {
|
||||
...acc,
|
||||
[property]: this[property],
|
||||
[property]: (this as any)[property],
|
||||
};
|
||||
}, {});
|
||||
}
|
||||
@ -236,7 +236,7 @@ export class PanelModel {
|
||||
const prevOptions = this.cachedPluginOptions[pluginId] || {};
|
||||
|
||||
Object.keys(prevOptions).map(property => {
|
||||
this[property] = prevOptions[property];
|
||||
(this as any)[property] = prevOptions[property];
|
||||
});
|
||||
}
|
||||
|
||||
@ -252,7 +252,7 @@ export class PanelModel {
|
||||
continue;
|
||||
}
|
||||
|
||||
delete this[key];
|
||||
delete (this as any)[key];
|
||||
}
|
||||
|
||||
this.cachedPluginOptions[oldPluginId] = oldOptions;
|
||||
|
@ -3,7 +3,7 @@ import { hot } from 'react-hot-loader';
|
||||
import { connect } from 'react-redux';
|
||||
import Page from 'app/core/components/Page/Page';
|
||||
import { Tooltip } from '@grafana/ui';
|
||||
import SlideDown from 'app/core/components/Animations/SlideDown';
|
||||
import { SlideDown } from 'app/core/components/Animations/SlideDown';
|
||||
import { getNavModel } from 'app/core/selectors/navModel';
|
||||
import { NavModel, StoreState, FolderState } from 'app/types';
|
||||
import { DashboardAcl, PermissionLevel, NewDashboardAclItem } from 'app/types/acl';
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import SlideDown from 'app/core/components/Animations/SlideDown';
|
||||
import { SlideDown } from 'app/core/components/Animations/SlideDown';
|
||||
import { Tooltip } from '@grafana/ui';
|
||||
import { TeamGroup } from '../../types';
|
||||
import { addTeamGroup, loadTeamGroups, removeTeamGroup } from './state/actions';
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import SlideDown from 'app/core/components/Animations/SlideDown';
|
||||
import { SlideDown } from 'app/core/components/Animations/SlideDown';
|
||||
import { UserPicker } from 'app/core/components/Select/UserPicker';
|
||||
import { TagBadge } from 'app/core/components/TagFilter/TagBadge';
|
||||
import { TeamMember, User } from 'app/types';
|
||||
|
@ -216,12 +216,6 @@ export class ThresholdManager {
|
||||
break;
|
||||
}
|
||||
case 'custom': {
|
||||
if (!threshold.fillColor) {
|
||||
threshold.fillColor = 'rgba(255, 255, 255, 1)';
|
||||
}
|
||||
if (!threshold.lineColor) {
|
||||
threshold.lineColor = 'rgba(255, 255, 255, 0)';
|
||||
}
|
||||
fillColor = threshold.fillColor;
|
||||
lineColor = threshold.lineColor;
|
||||
break;
|
||||
|
@ -23,7 +23,7 @@
|
||||
<label class="gf-form-label">Color</label>
|
||||
<div class="gf-form-select-wrapper">
|
||||
<select class="gf-form-input" ng-model="threshold.colorMode"
|
||||
ng-options="f for f in ['custom', 'critical', 'warning', 'ok']" ng-change="ctrl.render()" ng-disabled="ctrl.disabled">
|
||||
ng-options="f for f in ['custom', 'critical', 'warning', 'ok']" ng-change="ctrl.onThresholdTypeChange($index)" ng-disabled="ctrl.disabled">
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
@ -73,4 +73,4 @@
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,5 +1,6 @@
|
||||
import coreModule from 'app/core/core_module';
|
||||
|
||||
import config from 'app/core/config';
|
||||
import tinycolor from 'tinycolor2';
|
||||
export class ThresholdFormCtrl {
|
||||
panelCtrl: any;
|
||||
panel: any;
|
||||
@ -56,6 +57,19 @@ export class ThresholdFormCtrl {
|
||||
this.render();
|
||||
};
|
||||
}
|
||||
|
||||
onThresholdTypeChange(index) {
|
||||
// Because of the ng-model binding, threshold's color mode is already set here
|
||||
if (this.panel.thresholds[index].colorMode === 'custom') {
|
||||
this.panel.thresholds[index].fillColor = tinycolor(config.theme.colors.blueBase)
|
||||
.setAlpha(0.2)
|
||||
.toRgbString();
|
||||
this.panel.thresholds[index].lineColor = tinycolor(config.theme.colors.blueShade)
|
||||
.setAlpha(0.6)
|
||||
.toRgbString();
|
||||
}
|
||||
this.panelCtrl.render();
|
||||
}
|
||||
}
|
||||
|
||||
coreModule.directive('graphThresholdForm', () => {
|
||||
|
@ -17,7 +17,6 @@ export const getSingleStatValues = (props: PanelProps<SingleStatBaseOptions>): D
|
||||
decimals: valueOptions.decimals,
|
||||
mappings: valueMappings,
|
||||
thresholds: options.thresholds,
|
||||
|
||||
prefix: replaceVariables(valueOptions.prefix),
|
||||
suffix: replaceVariables(valueOptions.suffix),
|
||||
theme: config.theme,
|
||||
|
@ -1,9 +1,20 @@
|
||||
export enum DashboardSearchHitType {
|
||||
DashHitDB = 'dash-db',
|
||||
DashHitHome = 'dash-home',
|
||||
DashHitFolder = 'dash-folder',
|
||||
}
|
||||
export interface DashboardSearchHit {
|
||||
id: number;
|
||||
tags: string[];
|
||||
title: string;
|
||||
type: string;
|
||||
uid: string;
|
||||
title: string;
|
||||
uri: string;
|
||||
url: string;
|
||||
slug: string;
|
||||
type: DashboardSearchHitType;
|
||||
tags: string[];
|
||||
isStarred: boolean;
|
||||
folderId: number;
|
||||
folderUid: string;
|
||||
folderTitle: string;
|
||||
folderUrl: string;
|
||||
}
|
||||
|
@ -27,7 +27,7 @@
|
||||
"noUnusedLocals": true,
|
||||
"baseUrl": "public",
|
||||
"pretty": true,
|
||||
"typeRoots": ["node_modules/@types", "types"],
|
||||
"typeRoots": ["node_modules/@types", "public/app/types"],
|
||||
"paths": {
|
||||
"app": ["app"],
|
||||
"sass": ["sass"]
|
||||
|
Loading…
Reference in New Issue
Block a user