Merge branch 'master' into fix/15691-refresh-issue-playlists

This commit is contained in:
Peter Holmberg 2019-03-20 09:17:57 +01:00
commit 81afd68752
34 changed files with 1141 additions and 1096 deletions

View File

@ -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'
```

View File

@ -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": [

View File

@ -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",

View File

@ -25,6 +25,7 @@ const model: OptionProps<any> = {
key: '',
onClick: jest.fn(),
onMouseOver: jest.fn(),
onMouseMove: jest.fn(),
tabIndex: 1,
},
label: 'Option label',

View File

@ -4,6 +4,7 @@ exports[`SelectOption renders correctly 1`] = `
<div
id=""
onClick={[MockFunction]}
onMouseMove={[MockFunction]}
onMouseOver={[MockFunction]}
tabIndex={1}
>

View File

@ -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}

View File

@ -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 },

View File

@ -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}

View File

@ -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();
}

View File

@ -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();
});
});

View File

@ -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 {

View File

@ -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],
});
}

View File

@ -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));
}

View File

@ -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', ','));
}

View File

@ -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);

View File

@ -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;

View File

@ -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);
}

View File

@ -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';

View File

@ -16,6 +16,7 @@ const mockSection = (overides?: object): Section => {
items: [],
checked: false,
expanded: false,
removable: false,
hideHeader: false,
icon: '',
score: 0,

View File

@ -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(() => {});

View File

@ -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';

View File

@ -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 {

View File

@ -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;

View File

@ -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';

View File

@ -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';

View File

@ -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';

View File

@ -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;

View File

@ -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>

View File

@ -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', () => {

View File

@ -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,

View File

@ -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;
}

View File

@ -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"]

1963
yarn.lock

File diff suppressed because it is too large Load Diff