diff --git a/docs/sources/auth/generic-oauth.md b/docs/sources/auth/generic-oauth.md index ac5801dbe1d..1a83432b8f7 100644 --- a/docs/sources/auth/generic-oauth.md +++ b/docs/sources/auth/generic-oauth.md @@ -113,7 +113,7 @@ allowed_organizations = allowed_organizations = ``` -### Set up OAuth2 with Auth0 +## Set up OAuth2 with Auth0 1. Create a new Client in Auth0 - Name: Grafana @@ -139,7 +139,7 @@ allowed_organizations = api_url = https:///userinfo ``` -### Set up OAuth2 with Azure Active Directory +## Set up OAuth2 with Azure Active Directory 1. Log in to portal.azure.com and click "Azure Active Directory" in the side menu, then click the "Properties" sub-menu item. diff --git a/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx b/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx index 27371d5515f..321323ac58b 100644 --- a/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx @@ -1,6 +1,6 @@ import React, { Component, createRef } from 'react'; -import PopperController from '../Tooltip/PopperController'; -import Popper from '../Tooltip/Popper'; +import { PopperController } from '../Tooltip/PopperController'; +import { Popper } from '../Tooltip/Popper'; import { ColorPickerPopover } from './ColorPickerPopover'; import { Themeable } from '../../types'; import { getColorFromHexRgbOrName } from '../../utils/namedColorsPalette'; diff --git a/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.tsx b/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.tsx index d3d70175c4b..ee879f94762 100644 --- a/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.tsx @@ -4,7 +4,7 @@ import { getColorName, getColorFromHexRgbOrName } from '../../utils/namedColorsP import { ColorPickerProps, warnAboutColorPickerPropsDeprecation } from './ColorPicker'; import { PopperContentProps } from '../Tooltip/PopperController'; import SpectrumPalette from './SpectrumPalette'; -import { GrafanaThemeType } from '@grafana/ui'; +import { GrafanaThemeType } from '../../types/theme'; export interface Props extends ColorPickerProps, PopperContentProps { customPickers?: T; diff --git a/packages/grafana-ui/src/components/CustomScrollbar/CustomScrollbar.tsx b/packages/grafana-ui/src/components/CustomScrollbar/CustomScrollbar.tsx index 40f6c6c3c37..39bae62e28b 100644 --- a/packages/grafana-ui/src/components/CustomScrollbar/CustomScrollbar.tsx +++ b/packages/grafana-ui/src/components/CustomScrollbar/CustomScrollbar.tsx @@ -1,9 +1,10 @@ import React, { PureComponent } from 'react'; import _ from 'lodash'; +import classNames from 'classnames'; import Scrollbars from 'react-custom-scrollbars'; interface Props { - customClassName?: string; + className?: string; autoHide?: boolean; autoHideTimeout?: number; autoHideDuration?: number; @@ -21,7 +22,6 @@ interface Props { */ export class CustomScrollbar extends PureComponent { static defaultProps: Partial = { - customClassName: 'custom-scrollbars', autoHide: false, autoHideTimeout: 200, autoHideDuration: 200, @@ -60,7 +60,7 @@ export class CustomScrollbar extends PureComponent { render() { const { - customClassName, + className, children, autoHeightMax, autoHeightMin, @@ -75,7 +75,7 @@ export class CustomScrollbar extends PureComponent { return ( = ({ children }) => { return ( diff --git a/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.tsx b/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.tsx index d613a911b03..b3d82982e6c 100644 --- a/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.tsx +++ b/packages/grafana-ui/src/components/ThresholdsEditor/ThresholdsEditor.tsx @@ -3,7 +3,8 @@ import { Threshold } from '../../types'; import { ColorPicker } from '..'; import { PanelOptionsGroup } from '..'; import { colors } from '../../utils'; -import { getColorFromHexRgbOrName, ThemeContext } from '@grafana/ui'; +import { ThemeContext } from '../../themes/ThemeContext'; +import { getColorFromHexRgbOrName } from '../../utils/namedColorsPalette'; export interface Props { thresholds: Threshold[]; diff --git a/packages/grafana-ui/src/components/Tooltip/Popper.tsx b/packages/grafana-ui/src/components/Tooltip/Popper.tsx index d43f37ecfcc..e439ecf0437 100644 --- a/packages/grafana-ui/src/components/Tooltip/Popper.tsx +++ b/packages/grafana-ui/src/components/Tooltip/Popper.tsx @@ -1,7 +1,7 @@ import React, { PureComponent } from 'react'; import * as PopperJS from 'popper.js'; import { Manager, Popper as ReactPopper, PopperArrowProps } from 'react-popper'; -import { Portal } from '@grafana/ui'; +import { Portal } from '../Portal/Portal'; import Transition from 'react-transition-group/Transition'; import { PopperContent } from './PopperController'; @@ -53,7 +53,7 @@ class Popper extends PureComponent { // TODO: move modifiers config to popper controller modifiers={{ preventOverflow: { enabled: true, boundariesElement: 'window' } }} > - {({ ref, style, placement, arrowProps }) => { + {({ ref, style, placement, arrowProps, scheduleUpdate }) => { return (
{ className={`${wrapperClassName}`} >
- {typeof content === 'string' ? content : React.cloneElement(content)} + {typeof content === 'string' && content} + {React.isValidElement(content) && React.cloneElement(content)} + {typeof content === 'function' && + content({ + updatePopperPosition: scheduleUpdate, + })} {renderArrow && renderArrow({ arrowProps, @@ -88,4 +93,4 @@ class Popper extends PureComponent { } } -export default Popper; +export { Popper }; diff --git a/packages/grafana-ui/src/components/Tooltip/PopperController.tsx b/packages/grafana-ui/src/components/Tooltip/PopperController.tsx index 770d1ce9f37..ed422104701 100644 --- a/packages/grafana-ui/src/components/Tooltip/PopperController.tsx +++ b/packages/grafana-ui/src/components/Tooltip/PopperController.tsx @@ -7,7 +7,7 @@ export interface PopperContentProps { updatePopperPosition?: () => void; } -export type PopperContent = string | React.ReactElement; +export type PopperContent = string | React.ReactElement | ((props: T) => JSX.Element); export interface UsingPopperProps { show?: boolean; @@ -101,4 +101,4 @@ class PopperController extends React.Component { } } -export default PopperController; +export { PopperController }; diff --git a/packages/grafana-ui/src/components/Tooltip/Tooltip.tsx b/packages/grafana-ui/src/components/Tooltip/Tooltip.tsx index 28987f272cc..81b33e44156 100644 --- a/packages/grafana-ui/src/components/Tooltip/Tooltip.tsx +++ b/packages/grafana-ui/src/components/Tooltip/Tooltip.tsx @@ -1,7 +1,7 @@ import React, { createRef } from 'react'; import * as PopperJS from 'popper.js'; -import Popper from './Popper'; -import PopperController, { UsingPopperProps } from './PopperController'; +import { Popper } from './Popper'; +import { PopperController, UsingPopperProps } from './PopperController'; interface TooltipProps extends UsingPopperProps { theme?: 'info' | 'error'; diff --git a/packages/grafana-ui/src/components/index.ts b/packages/grafana-ui/src/components/index.ts index 86ce9347dad..ca8899bd928 100644 --- a/packages/grafana-ui/src/components/index.ts +++ b/packages/grafana-ui/src/components/index.ts @@ -1,5 +1,7 @@ export { DeleteButton } from './DeleteButton/DeleteButton'; export { Tooltip } from './Tooltip/Tooltip'; +export { PopperController } from './Tooltip/PopperController'; +export { Popper } from './Tooltip/Popper'; export { Portal } from './Portal/Portal'; export { CustomScrollbar } from './CustomScrollbar/CustomScrollbar'; diff --git a/packages/grafana-ui/src/themes/_variables.dark.scss.tmpl.ts b/packages/grafana-ui/src/themes/_variables.dark.scss.tmpl.ts index c8f389b88d6..aa1297fe056 100644 --- a/packages/grafana-ui/src/themes/_variables.dark.scss.tmpl.ts +++ b/packages/grafana-ui/src/themes/_variables.dark.scss.tmpl.ts @@ -150,9 +150,9 @@ $list-item-shadow: $card-shadow; $empty-list-cta-bg: $gray-blue; // Scrollbars -$scrollbarBackground: $dark-9; -$scrollbarBackground2: $dark-9; -$scrollbarBorder: $dark-10; +$scrollbarBackground: #404357; +$scrollbarBackground2: $dark-10; +$scrollbarBorder: black; // Tables // ------------------------- diff --git a/packages/grafana-ui/src/themes/_variables.light.scss.tmpl.ts b/packages/grafana-ui/src/themes/_variables.light.scss.tmpl.ts index 585e705feac..566e5be31f4 100644 --- a/packages/grafana-ui/src/themes/_variables.light.scss.tmpl.ts +++ b/packages/grafana-ui/src/themes/_variables.light.scss.tmpl.ts @@ -138,7 +138,7 @@ $empty-list-cta-bg: $gray-6; // Scrollbars $scrollbarBackground: $gray-4; $scrollbarBackground2: $gray-4; -$scrollbarBorder: $gray-3; +$scrollbarBorder: $gray-7; // Tables // ------------------------- diff --git a/public/app/core/angular_wrappers.ts b/public/app/core/angular_wrappers.ts index 6db442e7470..9105de82d53 100644 --- a/public/app/core/angular_wrappers.ts +++ b/public/app/core/angular_wrappers.ts @@ -10,10 +10,12 @@ import { SideMenu } from './components/sidemenu/SideMenu'; import { MetricSelect } from './components/Select/MetricSelect'; import AppNotificationList from './components/AppNotifications/AppNotificationList'; import { ColorPicker, SeriesColorPickerPopoverWithTheme } from '@grafana/ui'; +import { FunctionEditor } from 'app/plugins/datasource/graphite/FunctionEditor'; export function registerAngularDirectives() { react2AngularDirective('passwordStrength', PasswordStrength, ['password']); react2AngularDirective('sidemenu', SideMenu, []); + react2AngularDirective('functionEditor', FunctionEditor, ['func', 'onRemove', 'onMoveLeft', 'onMoveRight']); react2AngularDirective('appNotificationsList', AppNotificationList, []); react2AngularDirective('pageHeader', PageHeader, ['model', 'noTabs']); react2AngularDirective('emptyListCta', EmptyListCTA, ['model']); diff --git a/public/app/core/components/Page/Page.tsx b/public/app/core/components/Page/Page.tsx index fe518d8f891..1803317a8da 100644 --- a/public/app/core/components/Page/Page.tsx +++ b/public/app/core/components/Page/Page.tsx @@ -48,7 +48,7 @@ class Page extends Component { const { buildInfo } = config; return (
- +
{this.props.children} diff --git a/public/app/features/explore/Wrapper.tsx b/public/app/features/explore/Wrapper.tsx index 35d09cfd9bb..add2ffed235 100644 --- a/public/app/features/explore/Wrapper.tsx +++ b/public/app/features/explore/Wrapper.tsx @@ -53,7 +53,7 @@ export class Wrapper extends Component { return (
- +
diff --git a/public/app/plugins/datasource/graphite/FunctionEditor.tsx b/public/app/plugins/datasource/graphite/FunctionEditor.tsx new file mode 100644 index 00000000000..f8a684529c6 --- /dev/null +++ b/public/app/plugins/datasource/graphite/FunctionEditor.tsx @@ -0,0 +1,110 @@ +import React from 'react'; +import { PopperController, Popper } from '@grafana/ui'; +import rst2html from 'rst2html'; +import { FunctionDescriptor, FunctionEditorControlsProps, FunctionEditorControls } from './FunctionEditorControls'; + +interface FunctionEditorProps extends FunctionEditorControlsProps { + func: FunctionDescriptor; +} + +interface FunctionEditorState { + showingDescription: boolean; +} + +class FunctionEditor extends React.PureComponent { + private triggerRef = React.createRef(); + + constructor(props: FunctionEditorProps) { + super(props); + + this.state = { + showingDescription: false, + }; + } + + renderContent = ({ updatePopperPosition }) => { + const { + onMoveLeft, + onMoveRight, + func: { + def: { name, description }, + }, + } = this.props; + const { showingDescription } = this.state; + + if (showingDescription) { + return ( +
+

{name}

+
+
+ ); + } + + return ( + { + onMoveLeft(this.props.func); + updatePopperPosition(); + }} + onMoveRight={() => { + onMoveRight(this.props.func); + updatePopperPosition(); + }} + onDescriptionShow={() => { + this.setState({ showingDescription: true }, () => { + updatePopperPosition(); + }); + }} + /> + ); + }; + + render() { + return ( + + {(showPopper, hidePopper, popperProps) => { + return ( + <> + {this.triggerRef && ( + { + this.setState({ showingDescription: false }); + hidePopper(); + }} + onMouseEnter={showPopper} + renderArrow={({ arrowProps, placement }) => ( +
+ )} + /> + )} + + { + hidePopper(); + this.setState({ showingDescription: false }); + }} + style={{ cursor: 'pointer' }} + > + {this.props.func.def.name} + + + ); + }} + + ); + } +} + +export { FunctionEditor }; diff --git a/public/app/plugins/datasource/graphite/FunctionEditorControls.tsx b/public/app/plugins/datasource/graphite/FunctionEditorControls.tsx new file mode 100644 index 00000000000..9862cc4f038 --- /dev/null +++ b/public/app/plugins/datasource/graphite/FunctionEditorControls.tsx @@ -0,0 +1,65 @@ +import React from 'react'; + +export interface FunctionDescriptor { + text: string; + params: string[]; + def: { + category: string; + defaultParams: string[]; + description?: string; + fake: boolean; + name: string; + params: string[]; + }; +} + +export interface FunctionEditorControlsProps { + onMoveLeft: (func: FunctionDescriptor) => void; + onMoveRight: (func: FunctionDescriptor) => void; + onRemove: (func: FunctionDescriptor) => void; +} + +const FunctionHelpButton = (props: { description: string; name: string; onDescriptionShow: () => void }) => { + if (props.description) { + return ; + } + + return ( + { + window.open( + 'http://graphite.readthedocs.org/en/latest/functions.html#graphite.render.functions.' + props.name, + '_blank' + ); + }} + /> + ); +}; + +export const FunctionEditorControls = ( + props: FunctionEditorControlsProps & { + func: FunctionDescriptor; + onDescriptionShow: () => void; + } +) => { + const { func, onMoveLeft, onMoveRight, onRemove, onDescriptionShow } = props; + return ( +
+ onMoveLeft(func)} /> + + onRemove(func)} /> + onMoveRight(func)} /> +
+ ); +}; diff --git a/public/app/plugins/datasource/graphite/func_editor.ts b/public/app/plugins/datasource/graphite/func_editor.ts index 9e19083a9c3..f2522f2ab18 100644 --- a/public/app/plugins/datasource/graphite/func_editor.ts +++ b/public/app/plugins/datasource/graphite/func_editor.ts @@ -1,33 +1,42 @@ import _ from 'lodash'; import $ from 'jquery'; -import rst2html from 'rst2html'; import coreModule from 'app/core/core_module'; /** @ngInject */ export function graphiteFuncEditor($compile, templateSrv, popoverSrv) { - const funcSpanTemplate = '{{func.def.name}}('; + const funcSpanTemplate = ` + ( + `; const paramTemplate = ''; - const funcControlsTemplate = ` -
- - - - -
`; - return { restrict: 'A', link: function postLink($scope, elem) { const $funcLink = $(funcSpanTemplate); - const $funcControls = $(funcControlsTemplate); const ctrl = $scope.ctrl; const func = $scope.func; let scheduledRelink = false; let paramCountAtLink = 0; let cancelBlur = null; + ctrl.handleRemoveFunction = func => { + ctrl.removeFunction(func); + }; + + ctrl.handleMoveLeft = func => { + ctrl.moveFunction(func, -1); + }; + + ctrl.handleMoveRight = func => { + ctrl.moveFunction(func, 1); + }; + function clickFuncParam(this: any, paramIndex) { /*jshint validthis:true */ @@ -158,24 +167,7 @@ export function graphiteFuncEditor($compile, templateSrv, popoverSrv) { }; } - function toggleFuncControls() { - const targetDiv = elem.closest('.tight-form'); - - if (elem.hasClass('show-function-controls')) { - elem.removeClass('show-function-controls'); - targetDiv.removeClass('has-open-function'); - $funcControls.hide(); - return; - } - - elem.addClass('show-function-controls'); - targetDiv.addClass('has-open-function'); - - $funcControls.show(); - } - function addElementsAndCompile() { - $funcControls.appendTo(elem); $funcLink.appendTo(elem); const defParams = _.clone(func.def.params); @@ -245,69 +237,10 @@ export function graphiteFuncEditor($compile, templateSrv, popoverSrv) { } } - function registerFuncControlsToggle() { - $funcLink.click(toggleFuncControls); - } - - function registerFuncControlsActions() { - $funcControls.click(e => { - const $target = $(e.target); - if ($target.hasClass('fa-remove')) { - toggleFuncControls(); - $scope.$apply(() => { - ctrl.removeFunction($scope.func); - }); - return; - } - - if ($target.hasClass('fa-arrow-left')) { - $scope.$apply(() => { - _.move(ctrl.queryModel.functions, $scope.$index, $scope.$index - 1); - ctrl.targetChanged(); - }); - return; - } - - if ($target.hasClass('fa-arrow-right')) { - $scope.$apply(() => { - _.move(ctrl.queryModel.functions, $scope.$index, $scope.$index + 1); - ctrl.targetChanged(); - }); - return; - } - - if ($target.hasClass('fa-question-circle')) { - const funcDef = ctrl.datasource.getFuncDef(func.def.name); - if (funcDef && funcDef.description) { - popoverSrv.show({ - element: e.target, - position: 'bottom left', - classNames: 'drop-popover drop-function-def', - template: ` -
-

${funcDef.name}

- ${rst2html(funcDef.description)} -
`, - openOn: 'click', - }); - } else { - window.open( - 'http://graphite.readthedocs.org/en/latest/functions.html#graphite.render.functions.' + func.def.name, - '_blank' - ); - } - return; - } - }); - } - function relink() { elem.children().remove(); - addElementsAndCompile(); ifJustAddedFocusFirstParam(); - registerFuncControlsToggle(); - registerFuncControlsActions(); } relink(); diff --git a/public/app/plugins/datasource/graphite/graphite_query.ts b/public/app/plugins/datasource/graphite/graphite_query.ts index ab137a6a299..40a6b1d8543 100644 --- a/public/app/plugins/datasource/graphite/graphite_query.ts +++ b/public/app/plugins/datasource/graphite/graphite_query.ts @@ -154,6 +154,11 @@ export default class GraphiteQuery { this.functions = _.without(this.functions, func); } + moveFunction(func, offset) { + const index = this.functions.indexOf(func); + _.move(this.functions, index, index + offset); + } + updateModelTarget(targets) { // render query if (!this.target.textEditor) { diff --git a/public/app/plugins/datasource/graphite/query_ctrl.ts b/public/app/plugins/datasource/graphite/query_ctrl.ts index b89e84d23a7..80ddc0f5e07 100644 --- a/public/app/plugins/datasource/graphite/query_ctrl.ts +++ b/public/app/plugins/datasource/graphite/query_ctrl.ts @@ -272,6 +272,11 @@ export class GraphiteQueryCtrl extends QueryCtrl { this.targetChanged(); } + moveFunction(func, offset) { + this.queryModel.moveFunction(func, offset); + this.targetChanged(); + } + addSeriesByTagFunc(tag) { const newFunc = this.datasource.createFuncInstance('seriesByTag', { withDefaultParams: false, diff --git a/public/app/plugins/datasource/mysql/mysql_query.ts b/public/app/plugins/datasource/mysql/mysql_query.ts index e636d66e6fd..9494d867fad 100644 --- a/public/app/plugins/datasource/mysql/mysql_query.ts +++ b/public/app/plugins/datasource/mysql/mysql_query.ts @@ -52,7 +52,7 @@ export default class MysqlQuery { } escapeLiteral(value) { - return value.replace(/'/g, "''"); + return String(value).replace(/'/g, "''"); } hasTimeGroup() { diff --git a/public/sass/_variables.dark.generated.scss b/public/sass/_variables.dark.generated.scss index 5eeb58b5828..79dd52915df 100644 --- a/public/sass/_variables.dark.generated.scss +++ b/public/sass/_variables.dark.generated.scss @@ -153,9 +153,9 @@ $list-item-shadow: $card-shadow; $empty-list-cta-bg: $gray-blue; // Scrollbars -$scrollbarBackground: $dark-9; -$scrollbarBackground2: $dark-9; -$scrollbarBorder: $dark-10; +$scrollbarBackground: #404357; +$scrollbarBackground2: $dark-10; +$scrollbarBorder: black; // Tables // ------------------------- diff --git a/public/sass/_variables.light.generated.scss b/public/sass/_variables.light.generated.scss index 2d80e111499..5be8cfb7987 100644 --- a/public/sass/_variables.light.generated.scss +++ b/public/sass/_variables.light.generated.scss @@ -141,7 +141,7 @@ $empty-list-cta-bg: $gray-6; // Scrollbars $scrollbarBackground: $gray-4; $scrollbarBackground2: $gray-4; -$scrollbarBorder: $gray-3; +$scrollbarBorder: $gray-7; // Tables // ------------------------- diff --git a/public/sass/components/_footer.scss b/public/sass/components/_footer.scss index b5617f4e387..88e5b1521f3 100644 --- a/public/sass/components/_footer.scss +++ b/public/sass/components/_footer.scss @@ -42,7 +42,7 @@ display: none; } -.is-react .custom-scrollbars .footer { +.is-react .custom-scrollbar .footer { display: block; } diff --git a/public/sass/components/_query_editor.scss b/public/sass/components/_query_editor.scss index 71332541b2e..676b3cd5407 100644 --- a/public/sass/components/_query_editor.scss +++ b/public/sass/components/_query_editor.scss @@ -50,7 +50,6 @@ input[type='text'].tight-form-func-param { } .tight-form-func-controls { - display: none; text-align: center; .fa-arrow-left {