Part3: Unicons implementation (#23356)

* Create a new Icon component

* Update icons in main sidebar

* Update icons in Useful links and in react components on  main site

* Update icons in Useful links and in main top navigation

* Adjust sizing

* Update panel navigation and timepicker

* Update icons in Panel menu

* Update icons in add panel widget

* Resolve merge conflict

* Fix part of the test errors and type errors

* Fix storybook errors

* Update getAvailableIcons import in storybook knobs

* Fix import path

* Fix SyntaxError: Cannot use import statement outside a module in test environment error

* Remove dynamic imports

* Remove types as using @ts-ignore

* Update snapshot test

* Add @iconscout/react-unicons to the shouldExclude list as it is blundled with es2015 syntax

* Implment icons in Tabs

* Implement icons in search items and  empty  list

* Update buttons

* Update button-related snapshot tests

* Update icons in modals and page headers

* Create anfular wrapper and update all icons on search screen

* Update sizing, remove colors, update snapshot tests

* Remove color prop from icon, remove color implemetation in mono icons

* Remove color props from monochrome icons

* Complete update of icons for search screen

* Update icons for infor tooltips, playlist, permissions

* Support temporarly font awesome icons used in enterprise grafana

* Part1: Unicons implementation (#23197)

* Create a new Icon component

* Update icons in main sidebar

* Update icons in Useful links and in react components on  main site

* Update icons in Useful links and in main top navigation

* Adjust sizing

* Update panel navigation and timepicker

* Update icons in Panel menu

* NewPanelEditor: Fixed so that test alert rule works in new edit mode (#23179)

* Update icons in add panel widget

* Resolve merge conflict

* Fix part of the test errors and type errors

* Fix storybook errors

* Update getAvailableIcons import in storybook knobs

* Fix import path

* Fix SyntaxError: Cannot use import statement outside a module in test environment error

* Remove dynamic imports

* Remove types as using @ts-ignore

* Update snapshot test

* Add @iconscout/react-unicons to the shouldExclude list as it is blundled with es2015 syntax

* Remove color prop from icon, remove color implemetation in mono icons

* Update navbar styling

* Move toPascalCase to utils/string

Co-authored-by: Torkel Ödegaard <torkel@grafana.com>

* Update icons in Explore

* Update icons in alerting

* Update + and x buttons

* Update icons in configurations and settings

* Update close icons

* Update icons in rich history

* Update alert messages

* Add optional chaining to for isFontAwesome variable

* Remove icon mock, set up jest.config

* Fix navbar plus icon

* Fir enable-bacground to enableBackgournd

Co-authored-by: Torkel Ödegaard <torkel@grafana.com>
This commit is contained in:
Ivana Huckova 2020-04-07 16:25:42 +02:00 committed by GitHub
parent d2d61d9bc2
commit bc468e4b92
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
95 changed files with 899 additions and 504 deletions

View File

@ -1,8 +1,12 @@
const esModule = '@iconscout/react-unicons';
module.exports = { module.exports = {
verbose: false, verbose: false,
transform: { transform: {
'^.+\\.(ts|tsx|js|jsx)$': 'ts-jest', '^.+\\.(ts|tsx|js|jsx)$': 'ts-jest',
[`(${esModule}).+\\.js$`]: 'babel-jest',
}, },
transformIgnorePatterns: [`/node_modules/(?!${esModule})`],
moduleDirectories: ['node_modules', 'public'], moduleDirectories: ['node_modules', 'public'],
roots: ['<rootDir>/public/app', '<rootDir>/public/test', '<rootDir>/packages', '<rootDir>/scripts'], roots: ['<rootDir>/public/app', '<rootDir>/public/test', '<rootDir>/packages', '<rootDir>/scripts'],
testRegex: '(\\.|/)(test)\\.(jsx?|tsx?)$', testRegex: '(\\.|/)(test)\\.(jsx?|tsx?)$',

View File

@ -1,4 +1,5 @@
import React, { FC, ReactNode } from 'react'; import React, { FC, ReactNode } from 'react';
import { Icon, IconName } from '@grafana/ui';
import classNames from 'classnames'; import classNames from 'classnames';
export type AlertVariant = 'success' | 'warning' | 'error' | 'info'; export type AlertVariant = 'success' | 'warning' | 'error' | 'info';
@ -15,16 +16,16 @@ interface AlertProps {
function getIconFromSeverity(severity: AlertVariant): string { function getIconFromSeverity(severity: AlertVariant): string {
switch (severity) { switch (severity) {
case 'error': { case 'error': {
return 'fa fa-exclamation-triangle'; return 'exclamation-triangle';
} }
case 'warning': { case 'warning': {
return 'fa fa-exclamation-triangle'; return 'exclamation-triangle';
} }
case 'info': { case 'info': {
return 'fa fa-info-circle'; return 'info-circle';
} }
case 'success': { case 'success': {
return 'fa fa-check'; return 'check';
} }
default: default:
return ''; return '';
@ -37,7 +38,7 @@ export const Alert: FC<AlertProps> = ({ title, buttonText, onButtonClick, onRemo
<div className="alert-container"> <div className="alert-container">
<div className={alertClass}> <div className={alertClass}>
<div className="alert-icon"> <div className="alert-icon">
<i className={getIconFromSeverity(severity)} /> <Icon size="xl" name={getIconFromSeverity(severity) as IconName} />
</div> </div>
<div className="alert-body"> <div className="alert-body">
<div className="alert-title">{title}</div> <div className="alert-title">{title}</div>
@ -46,7 +47,7 @@ export const Alert: FC<AlertProps> = ({ title, buttonText, onButtonClick, onRemo
{/* If onRemove is specified , giving preference to onRemove */} {/* If onRemove is specified , giving preference to onRemove */}
{onRemove && ( {onRemove && (
<button type="button" className="alert-close" onClick={onRemove}> <button type="button" className="alert-close" onClick={onRemove}>
<i className="fa fa fa-remove" /> <Icon name="times" size="lg" />
</button> </button>
)} )}
{onButtonClick && ( {onButtonClick && (

View File

@ -12,7 +12,7 @@ CallToActionCardStories.add('default', () => {
const ctaElements: { [key: string]: JSX.Element } = { const ctaElements: { [key: string]: JSX.Element } = {
custom: <h1>This is just H1 tag, you can any component as CTA element</h1>, custom: <h1>This is just H1 tag, you can any component as CTA element</h1>,
button: ( button: (
<Button size="lg" icon="plus" onClick={action('cta button clicked')}> <Button size="lg" icon="plus-circle" onClick={action('cta button clicked')}>
Add datasource Add datasource
</Button> </Button>
), ),

View File

@ -73,7 +73,7 @@ export const DataLinksEditor: FC<DataLinksEditorProps> = React.memo(
)} )}
{(!value || (value && value.length < (maxLinks || Infinity))) && ( {(!value || (value && value.length < (maxLinks || Infinity))) && (
<Button variant="secondary" icon="plus" onClick={() => onAdd()}> <Button variant="secondary" icon="plus-circle" onClick={() => onAdd()}>
Add link Add link
</Button> </Button>
)} )}

View File

@ -100,7 +100,7 @@ export const DataLinksInlineEditor: React.FC<DataLinksInlineEditorProps> = ({ li
)} )}
<FullWidthButtonContainer> <FullWidthButtonContainer>
<Button size="sm" icon="plus" onClick={onDataLinkAdd} variant="secondary"> <Button size="sm" icon="plus-circle" onClick={onDataLinkAdd} variant="secondary">
Add link Add link
</Button> </Button>
</FullWidthButtonContainer> </FullWidthButtonContainer>

View File

@ -2,6 +2,7 @@ import React from 'react';
import { KeyValue } from '@grafana/data'; import { KeyValue } from '@grafana/data';
import { css, cx } from 'emotion'; import { css, cx } from 'emotion';
import { Tooltip } from '../Tooltip/Tooltip'; import { Tooltip } from '../Tooltip/Tooltip';
import { Icon } from '../Icon/Icon';
import { CertificationKey } from './CertificationKey'; import { CertificationKey } from './CertificationKey';
import { HttpSettingsBaseProps } from './types'; import { HttpSettingsBaseProps } from './types';
@ -47,7 +48,7 @@ export const TLSAuthSettings: React.FC<HttpSettingsBaseProps> = ({ dataSourceCon
theme="info" theme="info"
> >
<div className="gf-form-help-icon gf-form-help-icon--right-normal"> <div className="gf-form-help-icon gf-form-help-icon--right-normal">
<i className="fa fa-info-circle" /> <Icon name="info-circle" size="sm" />
</div> </div>
</Tooltip> </Tooltip>
</div> </div>

View File

@ -34,7 +34,7 @@ export const FormLabel: FunctionComponent<Props> = ({
{tooltip && ( {tooltip && (
<Tooltip placement="top" content={tooltip} theme={'info'}> <Tooltip placement="top" content={tooltip} theme={'info'}>
<div className="gf-form-help-icon gf-form-help-icon--right-normal"> <div className="gf-form-help-icon gf-form-help-icon--right-normal">
<Icon name="info-circle" /> <Icon name="info-circle" size="sm" style={{ marginBottom: 0 }} />
</div> </div>
</Tooltip> </Tooltip>
)} )}

View File

@ -136,7 +136,6 @@ $select-input-bg-disabled: $input-bg-disabled;
height: 100%; height: 100%;
right: 8px; right: 8px;
top: 1px; top: 1px;
margin-top: -1px;
display: inline-block; display: inline-block;
text-align: right; text-align: right;
} }

View File

@ -39,7 +39,7 @@ const getIconStyles = stylesFactory((theme: GrafanaTheme, type: IconType) => {
}); });
export const Icon = React.forwardRef<HTMLDivElement, IconProps>( export const Icon = React.forwardRef<HTMLDivElement, IconProps>(
({ size = 'md', type = 'default', title, name, className, style, ...divElementProps }, ref) => { ({ size = 'md', type = 'default', name, className, style, ...divElementProps }, ref) => {
const theme = useTheme(); const theme = useTheme();
const styles = getIconStyles(theme, type); const styles = getIconStyles(theme, type);
const svgSize = getSvgSize(size, theme); const svgSize = getSvgSize(size, theme);

View File

@ -5,7 +5,7 @@ export const Bell: FunctionComponent<SvgProps> = ({ size, ...rest }) => {
return ( return (
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
enable-background="new 0 0 24 24" enableBackground="new 0 0 24 24"
viewBox="0 0 24 24" viewBox="0 0 24 24"
width={size} width={size}
height={size} height={size}

View File

@ -5,7 +5,7 @@ export const FolderPlus: FunctionComponent<SvgProps> = ({ size, ...rest }) => {
return ( return (
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
enable-background="new 0 0 24 24" enableBackground="new 0 0 24 24"
viewBox="0 0 24 24" viewBox="0 0 24 24"
width={size} width={size}
height={size} height={size}

View File

@ -5,7 +5,7 @@ export const Import: FunctionComponent<SvgProps> = ({ size, ...rest }) => {
return ( return (
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
enable-background="new 0 0 24 24" enableBackground="new 0 0 24 24"
viewBox="0 0 24 24" viewBox="0 0 24 24"
width={size} width={size}
height={size} height={size}

View File

@ -5,7 +5,7 @@ export const PlusSquare: FunctionComponent<SvgProps> = ({ size, ...rest }) => {
return ( return (
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
enable-background="new 0 0 24 24" enableBackground="new 0 0 24 24"
viewBox="0 0 24 24" viewBox="0 0 24 24"
width={size} width={size}
height={size} height={size}

View File

@ -37,7 +37,7 @@ describe('LogDetails', () => {
describe('when labels are present', () => { describe('when labels are present', () => {
it('should render heading', () => { it('should render heading', () => {
const wrapper = setup(undefined, { labels: { key1: 'label1', key2: 'label2' } }); const wrapper = setup(undefined, { labels: { key1: 'label1', key2: 'label2' } });
expect(wrapper.find({ 'aria-label': 'Log Labels' })).toHaveLength(1); expect(wrapper.find({ 'aria-label': 'Log Labels' }).hostNodes()).toHaveLength(1);
}); });
it('should render labels', () => { it('should render labels', () => {
const wrapper = setup(undefined, { labels: { key1: 'label1', key2: 'label2' } }); const wrapper = setup(undefined, { labels: { key1: 'label1', key2: 'label2' } });
@ -47,7 +47,7 @@ describe('LogDetails', () => {
describe('when row entry has parsable fields', () => { describe('when row entry has parsable fields', () => {
it('should render heading ', () => { it('should render heading ', () => {
const wrapper = setup(undefined, { entry: 'test=successful' }); const wrapper = setup(undefined, { entry: 'test=successful' });
expect(wrapper.find({ title: 'Ad-hoc statistics' })).toHaveLength(1); expect(wrapper.find({ title: 'Ad-hoc statistics' }).hostNodes()).toHaveLength(1);
}); });
it('should render parsed fields', () => { it('should render parsed fields', () => {
const wrapper = setup(undefined, { entry: 'test=successful' }); const wrapper = setup(undefined, { entry: 'test=successful' });
@ -116,7 +116,7 @@ describe('LogDetails', () => {
expect(wrapper.find(LogDetailsRow).length).toBe(3); expect(wrapper.find(LogDetailsRow).length).toBe(3);
const traceIdRow = wrapper.find(LogDetailsRow).filter({ parsedKey: 'traceId' }); const traceIdRow = wrapper.find(LogDetailsRow).filter({ parsedKey: 'traceId' });
expect(traceIdRow.length).toBe(1); expect(traceIdRow.length).toBe(1);
expect(traceIdRow.find('a').length).toBe(1); expect(traceIdRow.find('a').hostNodes().length).toBe(1);
expect((traceIdRow.find('a').getDOMNode() as HTMLAnchorElement).href).toBe('localhost:3210/1234'); expect((traceIdRow.find('a').getDOMNode() as HTMLAnchorElement).href).toBe('localhost:3210/1234');
}); });
}); });

View File

@ -32,16 +32,16 @@ describe('LogDetailsRow', () => {
}); });
it('should render metrics button', () => { it('should render metrics button', () => {
const wrapper = setup(); const wrapper = setup();
expect(wrapper.find('i.fa-signal')).toHaveLength(1); expect(wrapper.find({ title: 'Ad-hoc statistics' }).hostNodes()).toHaveLength(1);
}); });
describe('if props is a label', () => { describe('if props is a label', () => {
it('should render filter label button', () => { it('should render filter label button', () => {
const wrapper = setup(); const wrapper = setup();
expect(wrapper.find('i.fa-search-plus')).toHaveLength(1); expect(wrapper.find({ title: 'Filter for value' }).hostNodes()).toHaveLength(1);
}); });
it('should render filter out label button', () => { it('should render filter out label button', () => {
const wrapper = setup(); const wrapper = setup();
expect(wrapper.find('i.fa-search-minus')).toHaveLength(1); expect(wrapper.find({ title: 'Filter out value' }).hostNodes()).toHaveLength(1);
}); });
}); });
@ -66,7 +66,10 @@ describe('LogDetailsRow', () => {
}); });
expect(wrapper.find(LogLabelStats).length).toBe(0); expect(wrapper.find(LogLabelStats).length).toBe(0);
wrapper.find({ title: 'Ad-hoc statistics' }).simulate('click'); wrapper
.find({ title: 'Ad-hoc statistics' })
.hostNodes()
.simulate('click');
expect(wrapper.find(LogLabelStats).length).toBe(1); expect(wrapper.find(LogLabelStats).length).toBe(1);
expect(wrapper.find(LogLabelStats).contains('another value')).toBeTruthy(); expect(wrapper.find(LogLabelStats).contains('another value')).toBeTruthy();
}); });

View File

@ -10,6 +10,7 @@ import { stylesFactory } from '../../themes/stylesFactory';
//Components //Components
import { LogLabelStats } from './LogLabelStats'; import { LogLabelStats } from './LogLabelStats';
import { LinkButton } from '../Button/Button'; import { LinkButton } from '../Button/Button';
import { Icon } from '../Icon/Icon';
export interface Props extends Themeable { export interface Props extends Themeable {
parsedValue: string; parsedValue: string;
@ -94,24 +95,16 @@ class UnThemedLogDetailsRow extends PureComponent<Props, State> {
<tr className={cx(style.logDetailsValue, { [styles.noHoverBackground]: showFieldsStats })}> <tr className={cx(style.logDetailsValue, { [styles.noHoverBackground]: showFieldsStats })}>
{/* Action buttons - show stats/filter results */} {/* Action buttons - show stats/filter results */}
<td className={style.logsDetailsIcon} colSpan={isLabel ? undefined : 3}> <td className={style.logsDetailsIcon} colSpan={isLabel ? undefined : 3}>
<i title="Ad-hoc statistics" className={`fa fa-signal ${styles.hoverCursor}`} onClick={this.showStats} /> <Icon name="signal" title={'Ad-hoc statistics'} onClick={this.showStats} />
</td> </td>
{isLabel && ( {isLabel && (
<> <>
<td className={style.logsDetailsIcon}> <td className={style.logsDetailsIcon}>
<i <Icon name="search-minus" title="Filter for value" onClick={this.filterLabel} />
title="Filter for value"
className={`fa fa-search-plus ${styles.hoverCursor}`}
onClick={this.filterLabel}
/>
</td> </td>
<td className={style.logsDetailsIcon}> <td className={style.logsDetailsIcon}>
<i <Icon name="search-plus" title="Filter out value" onClick={this.filterOutLabel} />
title="Filter out value"
className={`fa fa-search-minus ${styles.hoverCursor}`}
onClick={this.filterOutLabel}
/>
</td> </td>
</> </>
)} )}

View File

@ -1,5 +1,6 @@
import React, { PureComponent } from 'react'; import React, { PureComponent } from 'react';
import { Field, LinkModel, LogRowModel, TimeZone, DataQueryResponse, GrafanaTheme } from '@grafana/data'; import { Field, LinkModel, LogRowModel, TimeZone, DataQueryResponse, GrafanaTheme } from '@grafana/data';
import { Icon } from '@grafana/ui';
import { cx, css } from 'emotion'; import { cx, css } from 'emotion';
import { import {
@ -48,6 +49,8 @@ const getStyles = stylesFactory((theme: GrafanaTheme) => {
topVerticalAlign: css` topVerticalAlign: css`
label: topVerticalAlign; label: topVerticalAlign;
vertical-align: top; vertical-align: top;
margin-top: -${theme.spacing.xs};
margin-left: -${theme.spacing.xxs};
`, `,
hoverBackground: css` hoverBackground: css`
label: hoverBackground; label: hoverBackground;
@ -129,9 +132,6 @@ class UnThemedLogRow extends PureComponent<Props, State> {
const style = getLogRowStyles(theme, row.logLevel); const style = getLogRowStyles(theme, row.logLevel);
const styles = getStyles(theme); const styles = getStyles(theme);
const showUtc = timeZone === 'utc'; const showUtc = timeZone === 'utc';
const showDetailsClassName = showDetails
? cx(['fa fa-chevron-down', styles.topVerticalAlign])
: cx(['fa fa-chevron-right', styles.topVerticalAlign]);
const hoverBackground = cx(style.logsRow, { [styles.hoverBackground]: hasHoverBackground }); const hoverBackground = cx(style.logsRow, { [styles.hoverBackground]: hasHoverBackground });
return ( return (
@ -150,7 +150,7 @@ class UnThemedLogRow extends PureComponent<Props, State> {
<td className={style.logsRowLevel} /> <td className={style.logsRowLevel} />
{!allowDetails && ( {!allowDetails && (
<td title={showDetails ? 'Hide log details' : 'See log details'} className={style.logsRowToggleDetails}> <td title={showDetails ? 'Hide log details' : 'See log details'} className={style.logsRowToggleDetails}>
<i className={showDetailsClassName} /> <Icon className={styles.topVerticalAlign} name={showDetails ? 'angle-down' : 'angle-right'} />
</td> </td>
)} )}
{showTime && showUtc && ( {showTime && showUtc && (

View File

@ -158,8 +158,10 @@ export const getLogRowStyles = stylesFactory((theme: GrafanaTheme, logLevel?: Lo
logsDetailsIcon: css` logsDetailsIcon: css`
label: logs-row-details__icon; label: logs-row-details__icon;
position: relative; position: relative;
padding-right: ${theme.spacing.md};
color: ${theme.colors.gray3}; color: ${theme.colors.gray3};
svg {
margin-right: ${theme.spacing.md};
}
`, `,
logDetailsLabel: css` logDetailsLabel: css`
label: logs-row-details__label; label: logs-row-details__label;

View File

@ -6,6 +6,7 @@ import { IconName } from '../../types';
import { Themeable } from '../../types'; import { Themeable } from '../../types';
import { getModalStyles } from './getModalStyles'; import { getModalStyles } from './getModalStyles';
import { ModalHeader } from './ModalHeader'; import { ModalHeader } from './ModalHeader';
import { Icon } from '../Icon/Icon';
interface Props extends Themeable { interface Props extends Themeable {
icon?: IconName; icon?: IconName;
@ -50,7 +51,7 @@ export class UnthemedModal extends React.PureComponent<Props> {
<div className={styles.modalHeader}> <div className={styles.modalHeader}>
{typeof title === 'string' ? this.renderDefaultHeader(title) : title} {typeof title === 'string' ? this.renderDefaultHeader(title) : title}
<a className={styles.modalHeaderClose} onClick={this.onDismiss}> <a className={styles.modalHeaderClose} onClick={this.onDismiss}>
<i className="fa fa-remove" /> <Icon name="times" />
</a> </a>
</div> </div>
<div className={styles.modalContent}>{this.props.children}</div> <div className={styles.modalContent}>{this.props.children}</div>

View File

@ -1,5 +1,6 @@
// Libraries // Libraries
import React, { FunctionComponent } from 'react'; import React, { FunctionComponent } from 'react';
import { Icon } from '@grafana/ui';
interface Props { interface Props {
title?: string | JSX.Element; title?: string | JSX.Element;
@ -26,7 +27,7 @@ export const PanelOptionsGroup: FunctionComponent<Props> = props => {
<span className="panel-options-group__title">{props.title}</span> <span className="panel-options-group__title">{props.title}</span>
{props.onClose && ( {props.onClose && (
<button className="btn btn-link" onClick={props.onClose}> <button className="btn btn-link" onClick={props.onClose}>
<i className="fa fa-remove" /> <Icon name="times" />
</button> </button>
)} )}
</div> </div>

View File

@ -200,7 +200,7 @@ export class ThresholdsEditor extends PureComponent<Props, State> {
return ( return (
<div className={styles.wrapper}> <div className={styles.wrapper}>
<FullWidthButtonContainer className={styles.addButton}> <FullWidthButtonContainer className={styles.addButton}>
<Button size="sm" icon="plus" onClick={() => this.onAddThreshold()} variant="secondary"> <Button size="sm" icon="plus-circle" onClick={() => this.onAddThreshold()} variant="secondary">
Add threshold Add threshold
</Button> </Button>
</FullWidthButtonContainer> </FullWidthButtonContainer>

View File

@ -118,7 +118,7 @@ export class TransformationsEditor extends React.PureComponent<TransformationsEd
return ( return (
<> <>
{this.renderTransformationEditors()} {this.renderTransformationEditors()}
<Button variant="secondary" icon="plus" onClick={this.onTransformationAdd}> <Button variant="secondary" icon="plus-circle" onClick={this.onTransformationAdd}>
Add transformation Add transformation
</Button> </Button>
</> </>

View File

@ -98,7 +98,7 @@ export class LegacyValueMappingsEditor extends PureComponent<Props, State> {
removeValueMapping={() => this.onRemoveMapping(valueMapping.id)} removeValueMapping={() => this.onRemoveMapping(valueMapping.id)}
/> />
))} ))}
<Button variant="primary" icon="plus" onClick={this.onAddMapping}> <Button variant="primary" icon="plus-circle" onClick={this.onAddMapping}>
Add mapping Add mapping
</Button> </Button>
</div> </div>

View File

@ -68,7 +68,7 @@ export const ValueMappingsEditor: React.FC<Props> = ({ valueMappings, onChange,
<FullWidthButtonContainer> <FullWidthButtonContainer>
<Button <Button
size="sm" size="sm"
icon="plus" icon="plus-circle"
onClick={onAdd} onClick={onAdd}
aria-label="ValueMappingsEditor add mapping button" aria-label="ValueMappingsEditor add mapping button"
variant="secondary" variant="secondary"

View File

@ -35,7 +35,7 @@ exports[`Render should render component 1`] = `
} }
/> />
<Button <Button
icon="plus" icon="plus-circle"
onClick={[Function]} onClick={[Function]}
variant="primary" variant="primary"
> >

View File

@ -23,7 +23,7 @@ export function ValuePicker<T>({ label, icon, options, onChange, variant }: Valu
<> <>
{!isPicking && ( {!isPicking && (
<FullWidthButtonContainer> <FullWidthButtonContainer>
<Button size="sm" icon={icon || 'plus'} onClick={() => setIsPicking(true)} variant={variant}> <Button size="sm" icon={icon || 'plus-circle'} onClick={() => setIsPicking(true)} variant={variant}>
{label} {label}
</Button> </Button>
</FullWidthButtonContainer> </FullWidthButtonContainer>

View File

@ -3,10 +3,12 @@ export type IconType = 'mono' | 'default';
export type IconName = export type IconName =
| 'fa fa-fw fa-unlock' | 'fa fa-fw fa-unlock'
| 'fa fa-envelope' | 'fa fa-envelope'
| 'fa fa-spinner'
| 'question-circle' | 'question-circle'
| 'plus'
| 'angle-up' | 'angle-up'
| 'history'
| 'angle-down' | 'angle-down'
| 'filter'
| 'angle-left' | 'angle-left'
| 'angle-right' | 'angle-right'
| 'pen' | 'pen'
@ -34,6 +36,8 @@ export type IconName =
| 'panel-add' | 'panel-add'
| 'arrow-random' | 'arrow-random'
| 'arrow-down' | 'arrow-down'
| 'comment-alt'
| 'arrow-right'
| 'arrow-up' | 'arrow-up'
| 'arrow-from-right' | 'arrow-from-right'
| 'keyboard' | 'keyboard'
@ -50,9 +54,12 @@ export type IconName =
| 'folder-plus' | 'folder-plus'
| 'link' | 'link'
| 'upload' | 'upload'
| 'columns'
| 'home-alt' | 'home-alt'
| 'channel-add' | 'channel-add'
| 'calendar-alt' | 'calendar-alt'
| 'play'
| 'pause'
| 'calculator-alt' | 'calculator-alt'
| 'compass' | 'compass'
| 'sliders-v-alt' | 'sliders-v-alt'
@ -77,12 +84,20 @@ export type IconName =
| 'edit' | 'edit'
| 'shield' | 'shield'
| 'eye' | 'eye'
| 'eye-slash'
| 'filter' | 'filter'
| 'monitor' | 'monitor'
| 'plus-circle' | 'plus-circle'
| 'arrow-left' | 'arrow-left'
| 'repeat' | 'repeat'
| 'external-link-alt' | 'external-link-alt'
| 'minus'
| 'signal'
| 'search-plus'
| 'search-minus'
| 'table'
| 'plus'
| 'heart'
| 'favorite'; | 'favorite';
export const getAvailableIcons = (): IconName[] => [ export const getAvailableIcons = (): IconName[] => [
@ -102,6 +117,8 @@ export const getAvailableIcons = (): IconName[] => [
'repeat', 'repeat',
'external-link-alt', 'external-link-alt',
'power', 'power',
'play',
'pause',
'trash-alt', 'trash-alt',
'exclamation-triangle', 'exclamation-triangle',
'times', 'times',
@ -152,8 +169,10 @@ export const getAvailableIcons = (): IconName[] => [
'info-circle', 'info-circle',
'bug', 'bug',
'cube', 'cube',
'history',
'star', 'star',
'edit', 'edit',
'columns',
'eye', 'eye',
'channel-add', 'channel-add',
'monitor', 'monitor',
@ -164,6 +183,7 @@ export const getAvailableIcons = (): IconName[] => [
'folder-open', 'folder-open',
'file-copy-alt', 'file-copy-alt',
'arrow-down', 'arrow-down',
'filter',
'arrow-up', 'arrow-up',
'exchange-alt', 'exchange-alt',
]; ];

View File

@ -1,4 +1,5 @@
import React, { FC } from 'react'; import React, { FC } from 'react';
import { Icon } from '@grafana/ui';
export type LayoutMode = LayoutModes.Grid | LayoutModes.List; export type LayoutMode = LayoutModes.Grid | LayoutModes.List;
@ -22,7 +23,7 @@ const LayoutSelector: FC<Props> = props => {
}} }}
className={mode === LayoutModes.List ? 'active' : ''} className={mode === LayoutModes.List ? 'active' : ''}
> >
<i className="fa fa-list" /> <Icon name="list-ul" />
</button> </button>
<button <button
onClick={() => { onClick={() => {
@ -30,7 +31,7 @@ const LayoutSelector: FC<Props> = props => {
}} }}
className={mode === LayoutModes.Grid ? 'active' : ''} className={mode === LayoutModes.Grid ? 'active' : ''}
> >
<i className="fa fa-th" /> <Icon name="table" />
</button> </button>
</div> </div>
); );

View File

@ -1,7 +1,7 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import { UserPicker } from 'app/core/components/Select/UserPicker'; import { UserPicker } from 'app/core/components/Select/UserPicker';
import { TeamPicker, Team } from 'app/core/components/Select/TeamPicker'; import { TeamPicker, Team } from 'app/core/components/Select/TeamPicker';
import { LegacyForms } from '@grafana/ui'; import { LegacyForms, Icon } from '@grafana/ui';
import { SelectableValue } from '@grafana/data'; import { SelectableValue } from '@grafana/data';
import { User } from 'app/types'; import { User } from 'app/types';
import { import {
@ -91,7 +91,7 @@ class AddPermissions extends Component<Props, NewDashboardAclItem> {
return ( return (
<div className="gf-form-inline cta-form"> <div className="gf-form-inline cta-form">
<button className="cta-form__close btn btn-transparent" onClick={onCancel}> <button className="cta-form__close btn btn-transparent" onClick={onCancel}>
<i className="fa fa-close" /> <Icon name="times" />
</button> </button>
<form name="addPermission" onSubmit={this.onSubmit}> <form name="addPermission" onSubmit={this.onSubmit}>
<h5>Add Permission For</h5> <h5>Add Permission For</h5>

View File

@ -1,5 +1,5 @@
import React from 'react'; import React from 'react';
import { getTagColorsFromName } from '@grafana/ui'; import { getTagColorsFromName, Icon } from '@grafana/ui';
export interface Props { export interface Props {
label: string; label: string;
@ -24,7 +24,7 @@ export class TagBadge extends React.Component<Props, any> {
return ( return (
<span className={`label label-tag`} style={tagStyle}> <span className={`label label-tag`} style={tagStyle}>
{removeIcon && <i className="fa fa-remove" />} {removeIcon && <Icon name="times" />}
{label} {countLabel} {label} {countLabel}
</span> </span>
); );

View File

@ -1,5 +1,6 @@
import React from 'react'; import React from 'react';
import { appEvents } from 'app/core/core'; import { appEvents } from 'app/core/core';
import { Icon } from '@grafana/ui';
export class HelpModal extends React.PureComponent { export class HelpModal extends React.PureComponent {
static tabIndex = 0; static tabIndex = 0;
@ -52,11 +53,11 @@ export class HelpModal extends React.PureComponent {
<div className="modal-body"> <div className="modal-body">
<div className="modal-header"> <div className="modal-header">
<h2 className="modal-header-title"> <h2 className="modal-header-title">
<i className="fa fa-keyboard-o" /> <Icon name="keyboard" size="lg" />
<span className="p-l-1">Shortcuts</span> <span className="p-l-1">Shortcuts</span>
</h2> </h2>
<a className="modal-header-close" onClick={this.dismiss}> <a className="modal-header-close" onClick={this.dismiss}>
<i className="fa fa-remove" /> <Icon name="times" style={{ margin: '3px 0 0 0' }} />
</a> </a>
</div> </div>

View File

@ -9,7 +9,7 @@ const template = `
<i class="fa fa-list"></i> <i class="fa fa-list"></i>
</button> </button>
<button ng-click="ctrl.gridView()" ng-class="{active: ctrl.mode === 'grid'}"> <button ng-click="ctrl.gridView()" ng-class="{active: ctrl.mode === 'grid'}">
<i class="fa fa-th"></i> <icon name="table"></i>
</button> </button>
</div> </div>
`; `;

View File

@ -38,24 +38,25 @@
<div class="search-filter-box" ng-if="ctrl.isEditor || ctrl.hasEditPermissionInFolders"> <div class="search-filter-box" ng-if="ctrl.isEditor || ctrl.hasEditPermissionInFolders">
<a href="dashboard/new" class="search-filter-box-link"> <a href="dashboard/new" class="search-filter-box-link">
<icon name="'plus-square'" size="'xl'" style="margin-right: 8px;"></icon> New dashboard <icon name="'plus-square'" type="'mono'" size="'xl'" style="margin-right: 8px;"></icon> New dashboard
</a> </a>
<a href="dashboards/folder/new" class="search-filter-box-link" ng-if="ctrl.isEditor"> <a href="dashboards/folder/new" class="search-filter-box-link" ng-if="ctrl.isEditor">
<icon name="'folder-plus'" size="'xl'" style="margin-right: 8px;"></icon> New folder <icon name="'folder-plus'" type="'mono'" size="'xl'" style="margin-right: 8px;"></icon> New folder
</a> </a>
<a <a
href="dashboard/import" href="dashboard/import"
class="search-filter-box-link" class="search-filter-box-link"
ng-if="ctrl.isEditor || ctrl.hasEditPermissionInFolders" ng-if="ctrl.isEditor || ctrl.hasEditPermissionInFolders"
> >
<icon name="'import'" size="'xl'" style="margin-right: 8px;"></icon> Import dashboard <icon name="'import'" type="'mono'" size="'xl'" style="margin-right: 8px;"></icon> Import dashboard
</a> </a>
<a <a
class="search-filter-box-link" class="search-filter-box-link"
target="_blank" target="_blank"
href="https://grafana.com/dashboards?utm_source=grafana_search" href="https://grafana.com/dashboards?utm_source=grafana_search"
> >
<icon name="'apps'" size="'xl'" style="margin-right: 8px;"></icon> Find dashboards on Grafana.com <icon name="'apps'" type="'mono'" size="'xl'" style="margin-right: 8px;"></icon> Find dashboards on
Grafana.com
</a> </a>
</div> </div>
</div> </div>

View File

@ -70,7 +70,7 @@ const UserListAdminPageUnConnected: React.FC<Props> = props => {
<th> <th>
Seen&nbsp; Seen&nbsp;
<Tooltip placement="top" content="Time since user was seen using Grafana"> <Tooltip placement="top" content="Time since user was seen using Grafana">
<i className="fa fa-question-circle" /> <Icon name="question-circle" />
</Tooltip> </Tooltip>
</th> </th>
<th></th> <th></th>
@ -113,7 +113,7 @@ const renderUser = (user: UserDTO) => {
{user.isAdmin && ( {user.isAdmin && (
<a href={editUrl}> <a href={editUrl}>
<Tooltip placement="top" content="Grafana Admin"> <Tooltip placement="top" content="Grafana Admin">
<i className="fa fa-shield" /> <Icon name="shield" />
</Tooltip> </Tooltip>
</a> </a>
)} )}

View File

@ -52,7 +52,11 @@ exports[`ServerStats Should render table with stats 1`] = `
> >
<span <span
className="page-header__logo" className="page-header__logo"
/> >
<i
className="fa fa-fw fa-warning css-1lgsh82"
/>
</span>
<div <div
className="page-header__info-block" className="page-header__info-block"
> >
@ -99,6 +103,7 @@ exports[`ServerStats Should render table with stats 1`] = `
className="css-vohy86" className="css-vohy86"
onClick={[Function]} onClick={[Function]}
> >
<div />
Admin Admin
</li> </li>
</ul> </ul>

View File

@ -1,46 +1,52 @@
<page-header model="navModel"></page-header> <page-header model="navModel"></page-header>
<div class="page-container page-body"> <div class="page-container page-body">
<h3 class="page-sub-heading">Edit Organization</h3> <h3 class="page-sub-heading">Edit Organization</h3>
<form name="orgDetailsForm" class="gf-form-group"> <form name="orgDetailsForm" class="gf-form-group">
<div class="gf-form"> <div class="gf-form">
<span class="gf-form-label width-10">Name</span> <span class="gf-form-label width-10">Name</span>
<input type="text" required ng-model="org.name" class="gf-form-input max-width-14" > <input type="text" required ng-model="org.name" class="gf-form-input max-width-14" />
</div> </div>
<div class="gf-form-button-row"> <div class="gf-form-button-row">
<button type="submit" class="btn btn-primary" ng-click="update()" ng-show="!createMode">Update</button> <button type="submit" class="btn btn-primary" ng-click="update()" ng-show="!createMode">Update</button>
</div> </div>
</form> </form>
<h3 class="page-heading">Organization Users</h3> <h3 class="page-heading">Organization Users</h3>
<table class="filter-table"> <table class="filter-table">
<tr> <tr>
<th>Username</th> <th>Username</th>
<th>Email</th> <th>Email</th>
<th>Role</th> <th>Role</th>
<th></th> <th></th>
</tr> </tr>
<tr ng-repeat="orgUser in orgUsers"> <tr ng-repeat="orgUser in orgUsers">
<td>{{orgUser.login}}</td> <td>{{orgUser.login}}</td>
<td>{{orgUser.email}}</td> <td>{{orgUser.email}}</td>
<td> <td>
<div class="gf-form"> <div class="gf-form">
<span class="gf-form-select-wrapper"> <span class="gf-form-select-wrapper">
<select type="text" ng-model="orgUser.role" class="gf-form-input max-width-8" ng-options="f for f in ['Viewer', 'Editor', 'Admin']" ng-change="updateOrgUser(orgUser)"> <select
</select> type="text"
ng-model="orgUser.role"
class="gf-form-input max-width-8"
ng-options="f for f in ['Viewer', 'Editor', 'Admin']"
ng-change="updateOrgUser(orgUser)"
>
</select>
</span> </span>
</div> </div>
</td> </td>
<td style="width: 1%"> <td style="width: 1%">
<a ng-click="removeOrgUser(orgUser)" class="btn btn-danger btn-small"> <a ng-click="removeOrgUser(orgUser)" class="btn btn-danger btn-small">
<i class="fa fa-remove"></i> <icon name="'times'"></icon>
</a> </a>
</td> </td>
</tr> </tr>
</table> </table>
</div> </div>
<footer /> <footer />

View File

@ -108,7 +108,7 @@
</td> </td>
<td style="width: 1%"> <td style="width: 1%">
<a ng-click="removeOrgUser(org)" class="btn btn-danger btn-small"> <a ng-click="removeOrgUser(org)" class="btn btn-danger btn-small">
<i class="fa fa-remove"></i> <icon name="'times'"></icon>
</a> </a>
</td> </td>
</tr> </tr>
@ -138,7 +138,7 @@
<td>{{session.browser}} on {{session.os}} {{session.osVersion}}</td> <td>{{session.browser}} on {{session.os}} {{session.osVersion}}</td>
<td> <td>
<button class="btn btn-danger btn-small" ng-click="revokeUserSession(session.id)"> <button class="btn btn-danger btn-small" ng-click="revokeUserSession(session.id)">
<i class="fa fa-power-off"></i> <icon name="'power'" style="margin-top: -2px;"></icon>
</button> </button>
</td> </td>
</tr> </tr>
@ -152,7 +152,6 @@
</div> </div>
</div> </div>
<h3 class="page-heading">User status</h3> <h3 class="page-heading">User status</h3>
<div class="gf-form-group"> <div class="gf-form-group">
@ -177,7 +176,9 @@
> >
Enable Enable
</button> </button>
<button type="submit" class="btn btn-danger" ng-click="deleteUser(user)" ng-show="!createMode">Delete User</button> <button type="submit" class="btn btn-danger" ng-click="deleteUser(user)" ng-show="!createMode">
Delete User
</button>
</div> </div>
</div> </div>
</div> </div>

View File

@ -30,7 +30,7 @@
</td> </td>
<td class="text-right"> <td class="text-right">
<a ng-click="deleteOrg(org)" class="btn btn-danger btn-small"> <a ng-click="deleteOrg(org)" class="btn btn-danger btn-small">
<i class="fa fa-remove"></i> <icon name="'times'" size="sm"></icon>
</a> </a>
</td> </td>
</tr> </tr>

View File

@ -1,8 +1,8 @@
import React, { PureComponent } from 'react'; import React, { PureComponent } from 'react';
// @ts-ignore // @ts-ignore
import Highlighter from 'react-highlight-words'; import Highlighter from 'react-highlight-words';
import classNames from 'classnames';
import { AlertRule } from '../../types'; import { AlertRule } from '../../types';
import { Icon, IconName } from '@grafana/ui';
export interface Props { export interface Props {
rule: AlertRule; rule: AlertRule;
@ -24,19 +24,11 @@ class AlertRuleItem extends PureComponent<Props> {
render() { render() {
const { rule, onTogglePause } = this.props; const { rule, onTogglePause } = this.props;
const iconClassName = classNames({
fa: true,
'fa-play': rule.state === 'paused',
'fa-pause': rule.state !== 'paused',
});
const ruleUrl = `${rule.url}?panelId=${rule.panelId}&fullscreen&edit&tab=alert`; const ruleUrl = `${rule.url}?panelId=${rule.panelId}&fullscreen&edit&tab=alert`;
return ( return (
<li className="alert-rule-item"> <li className="alert-rule-item">
<span className={`alert-rule-item__icon ${rule.stateClass}`}> <Icon size="xl" name={rule.stateIcon as IconName} className={`alert-rule-item__icon ${rule.stateClass}`} />
<i className={rule.stateIcon} />
</span>
<div className="alert-rule-item__body"> <div className="alert-rule-item__body">
<div className="alert-rule-item__header"> <div className="alert-rule-item__header">
<div className="alert-rule-item__name"> <div className="alert-rule-item__name">
@ -56,10 +48,10 @@ class AlertRuleItem extends PureComponent<Props> {
title="Pausing an alert rule prevents it from executing" title="Pausing an alert rule prevents it from executing"
onClick={onTogglePause} onClick={onTogglePause}
> >
<i className={iconClassName} /> <Icon name={rule.state === 'paused' ? 'play' : 'pause'} />
</button> </button>
<a className="btn btn-small btn-inverse alert-list__btn width-2" href={ruleUrl} title="Edit alert rule"> <a className="btn btn-small btn-inverse alert-list__btn width-2" href={ruleUrl} title="Edit alert rule">
<i className="gicon gicon-cog" /> <Icon name="cog" />
</a> </a>
</div> </div>
</li> </li>

View File

@ -4,13 +4,11 @@ exports[`Render should render component 1`] = `
<li <li
className="alert-rule-item" className="alert-rule-item"
> >
<span <Icon
className="alert-rule-item__icon state class" className="alert-rule-item__icon state class"
> name="icon"
<i size="xl"
className="icon" />
/>
</span>
<div <div
className="alert-rule-item__body" className="alert-rule-item__body"
> >
@ -67,8 +65,8 @@ exports[`Render should render component 1`] = `
onClick={[MockFunction]} onClick={[MockFunction]}
title="Pausing an alert rule prevents it from executing" title="Pausing an alert rule prevents it from executing"
> >
<i <Icon
className="fa fa-pause" name="pause"
/> />
</button> </button>
<a <a
@ -76,8 +74,8 @@ exports[`Render should render component 1`] = `
href="https://something.something.darkside?panelId=1&fullscreen&edit&tab=alert" href="https://something.something.darkside?panelId=1&fullscreen&edit&tab=alert"
title="Edit alert rule" title="Edit alert rule"
> >
<i <Icon
className="gicon gicon-cog" name="cog"
/> />
</a> </a>
</div> </div>

View File

@ -6,7 +6,7 @@
</h2> </h2>
<a class="modal-header-close" ng-click="dismiss();"> <a class="modal-header-close" ng-click="dismiss();">
<i class="fa fa-remove"></i> <icon name="'times'"></icon>
</a> </a>
</div> </div>

View File

@ -1,7 +1,5 @@
<div ng-if="ctrl.panel.alert"> <div ng-if="ctrl.panel.alert">
<div class="alert alert-error m-b-2" ng-show="ctrl.error"> <div class="alert alert-error m-b-2" ng-show="ctrl.error"><i class="fa fa-warning"></i> {{ctrl.error}}</div>
<i class="fa fa-warning"></i> {{ctrl.error}}
</div>
<div class="panel-options-group"> <div class="panel-options-group">
<div class="panel-options-group__body"> <div class="panel-options-group__body">
<div class="gf-form-group"> <div class="gf-form-group">
@ -9,33 +7,35 @@
<div class="gf-form-inline"> <div class="gf-form-inline">
<div class="gf-form"> <div class="gf-form">
<span class="gf-form-label width-6">Name</span> <span class="gf-form-label width-6">Name</span>
<input type="text" class="gf-form-input width-20" ng-model="ctrl.alert.name"> <input type="text" class="gf-form-input width-20" ng-model="ctrl.alert.name" />
</div> </div>
<div class="gf-form"> <div class="gf-form">
<span class="gf-form-label width-9">Evaluate every</span> <span class="gf-form-label width-9">Evaluate every</span>
<input class="gf-form-input max-width-6" type="text" ng-model="ctrl.alert.frequency" ng-blur="ctrl.checkFrequency()"> <input
class="gf-form-input max-width-6"
type="text"
ng-model="ctrl.alert.frequency"
ng-blur="ctrl.checkFrequency()"
/>
</div> </div>
<div class="gf-form max-width-11"> <div class="gf-form max-width-11">
<label class="gf-form-label width-5">For</label> <label class="gf-form-label width-5">For</label>
<input type="text" class="gf-form-input max-width-6 gf-form-input--has-help-icon" ng-model="ctrl.alert.for" <input
spellcheck='false' placeholder="5m"> type="text"
class="gf-form-input max-width-6 gf-form-input--has-help-icon"
ng-model="ctrl.alert.for"
spellcheck="false"
placeholder="5m"
/>
<info-popover mode="right-absolute"> <info-popover mode="right-absolute">
If an alert rule has a configured For and the query violates the configured If an alert rule has a configured For and the query violates the configured threshold it will first go
threshold it from OK to Pending. Going from OK to Pending Grafana will not send any notifications. Once the alert rule
will first go from OK to Pending. has been firing for more than For duration, it will change to Alerting and send alert notifications.
Going from OK to Pending Grafana will not send any notifications. Once the alert
rule
has
been firing for more than For duration, it will change to Alerting and send alert
notifications.
</info-popover> </info-popover>
</div> </div>
</div> </div>
<div class="gf-form" ng-if="ctrl.frequencyWarning"> <div class="gf-form" ng-if="ctrl.frequencyWarning">
<label class="gf-form-label text-warning"> <label class="gf-form-label text-warning"> <i class="fa fa-warning"></i> {{ctrl.frequencyWarning}} </label>
<i
class="fa fa-warning"></i> {{ctrl.frequencyWarning}}
</label>
</div> </div>
</div> </div>
@ -43,37 +43,57 @@
<h4 class="section-heading">Conditions</h4> <h4 class="section-heading">Conditions</h4>
<div class="gf-form-inline" ng-repeat="conditionModel in ctrl.conditionModels"> <div class="gf-form-inline" ng-repeat="conditionModel in ctrl.conditionModels">
<div class="gf-form"> <div class="gf-form">
<metric-segment-model css-class="query-keyword width-5" ng-if="$index" <metric-segment-model
property="conditionModel.operator.type" options="ctrl.evalOperators" css-class="query-keyword width-5"
custom="false"></metric-segment-model> ng-if="$index"
property="conditionModel.operator.type"
options="ctrl.evalOperators"
custom="false"
></metric-segment-model>
<span class="gf-form-label query-keyword width-5" ng-if="$index===0">WHEN</span> <span class="gf-form-label query-keyword width-5" ng-if="$index===0">WHEN</span>
</div> </div>
<div class="gf-form"> <div class="gf-form">
<query-part-editor class="gf-form-label query-part width-9" <query-part-editor
part="conditionModel.reducerPart" class="gf-form-label query-part width-9"
handle-event="ctrl.handleReducerPartEvent(conditionModel, $event)"> part="conditionModel.reducerPart"
handle-event="ctrl.handleReducerPartEvent(conditionModel, $event)"
>
</query-part-editor> </query-part-editor>
<span class="gf-form-label query-keyword">OF</span> <span class="gf-form-label query-keyword">OF</span>
</div> </div>
<div class="gf-form"> <div class="gf-form">
<query-part-editor class="gf-form-label query-part" part="conditionModel.queryPart" <query-part-editor
handle-event="ctrl.handleQueryPartEvent(conditionModel, $event)"> class="gf-form-label query-part"
part="conditionModel.queryPart"
handle-event="ctrl.handleQueryPartEvent(conditionModel, $event)"
>
</query-part-editor> </query-part-editor>
</div> </div>
<div class="gf-form"> <div class="gf-form">
<metric-segment-model property="conditionModel.evaluator.type" options="ctrl.evalFunctions" <metric-segment-model
custom="false" css-class="query-keyword" property="conditionModel.evaluator.type"
on-change="ctrl.evaluatorTypeChanged(conditionModel.evaluator)"></metric-segment-model> options="ctrl.evalFunctions"
<input class="gf-form-input max-width-9" type="number" step="any" custom="false"
ng-hide="conditionModel.evaluator.params.length === 0" css-class="query-keyword"
ng-model="conditionModel.evaluator.params[0]" on-change="ctrl.evaluatorTypeChanged(conditionModel.evaluator)"
ng-change="ctrl.evaluatorParamsChanged()" /> ></metric-segment-model>
<label class="gf-form-label query-keyword" <input
ng-show="conditionModel.evaluator.params.length === 2">TO</label> class="gf-form-input max-width-9"
<input class="gf-form-input max-width-9" type="number" step="any" type="number"
ng-if="conditionModel.evaluator.params.length === 2" step="any"
ng-model="conditionModel.evaluator.params[1]" ng-hide="conditionModel.evaluator.params.length === 0"
ng-change="ctrl.evaluatorParamsChanged()" /> ng-model="conditionModel.evaluator.params[0]"
ng-change="ctrl.evaluatorParamsChanged()"
/>
<label class="gf-form-label query-keyword" ng-show="conditionModel.evaluator.params.length === 2">TO</label>
<input
class="gf-form-input max-width-9"
type="number"
step="any"
ng-if="conditionModel.evaluator.params.length === 2"
ng-model="conditionModel.evaluator.params[1]"
ng-change="ctrl.evaluatorParamsChanged()"
/>
</div> </div>
<div class="gf-form"> <div class="gf-form">
<label class="gf-form-label"> <label class="gf-form-label">
@ -107,8 +127,11 @@
<div class="gf-form"> <div class="gf-form">
<span class="gf-form-label query-keyword">SET STATE TO</span> <span class="gf-form-label query-keyword">SET STATE TO</span>
<div class="gf-form-select-wrapper"> <div class="gf-form-select-wrapper">
<select class="gf-form-input" ng-model="ctrl.alert.noDataState" <select
ng-options="f.value as f.text for f in ctrl.noDataModes"> class="gf-form-input"
ng-model="ctrl.alert.noDataState"
ng-options="f.value as f.text for f in ctrl.noDataModes"
>
</select> </select>
</div> </div>
</div> </div>
@ -121,8 +144,11 @@
<div class="gf-form"> <div class="gf-form">
<span class="gf-form-label query-keyword">SET STATE TO</span> <span class="gf-form-label query-keyword">SET STATE TO</span>
<div class="gf-form-select-wrapper"> <div class="gf-form-select-wrapper">
<select class="gf-form-input" ng-model="ctrl.alert.executionErrorState" <select
ng-options="f.value as f.text for f in ctrl.executionErrorModes"> class="gf-form-input"
ng-model="ctrl.alert.executionErrorState"
ng-options="f.value as f.text for f in ctrl.executionErrorModes"
>
</select> </select>
</div> </div>
</div> </div>
@ -142,27 +168,42 @@
<span class="gf-form-label"> <span class="gf-form-label">
<i class="{{nc.iconClass}}"></i> <i class="{{nc.iconClass}}"></i>
&nbsp;{{nc.name}}&nbsp;<span ng-if="nc.isDefault">(default)</span> &nbsp;{{nc.name}}&nbsp;<span ng-if="nc.isDefault">(default)</span>
<i class="fa fa-remove pointer muted" ng-click="ctrl.removeNotification(nc)" ng-if="nc.isDefault === false"></i> <icon
name="'times'"
class="pointer muted"
ng-click="ctrl.removeNotification(nc)"
ng-if="nc.isDefault === false"
></icon>
</span> </span>
</div> </div>
<div class="gf-form"> <div class="gf-form">
<metric-segment segment="ctrl.addNotificationSegment" <metric-segment
get-options="ctrl.getNotifications()" segment="ctrl.addNotificationSegment"
on-change="ctrl.notificationAdded()"></metric-segment> get-options="ctrl.getNotifications()"
on-change="ctrl.notificationAdded()"
></metric-segment>
</div> </div>
</div> </div>
<div class="gf-form gf-form--v-stretch"> <div class="gf-form gf-form--v-stretch">
<span class="gf-form-label width-8">Message</span> <span class="gf-form-label width-8">Message</span>
<textarea class="gf-form-input" rows="10" ng-model="ctrl.alert.message" <textarea
placeholder="Notification message details..."></textarea> class="gf-form-input"
rows="10"
ng-model="ctrl.alert.message"
placeholder="Notification message details..."
></textarea>
</div> </div>
<div class="gf-form"> <div class="gf-form">
<span class="gf-form-label width-8">Tags</span> <span class="gf-form-label width-8">Tags</span>
<div class="gf-form-group"> <div class="gf-form-group">
<div class="gf-form-inline" ng-repeat="(name, value) in ctrl.alert.alertRuleTags"> <div class="gf-form-inline" ng-repeat="(name, value) in ctrl.alert.alertRuleTags">
<label class="gf-form-label width-15">{{ name }}</label> <label class="gf-form-label width-15">{{ name }}</label>
<input class="gf-form-input width-15" placeholder="Tag value..." <input
ng-model="ctrl.alert.alertRuleTags[name]" type="text"/> class="gf-form-input width-15"
placeholder="Tag value..."
ng-model="ctrl.alert.alertRuleTags[name]"
type="text"
/>
<label class="gf-form-label"> <label class="gf-form-label">
<a class="pointer" tabindex="1" ng-click="ctrl.removeAlertRuleTag(name)"> <a class="pointer" tabindex="1" ng-click="ctrl.removeAlertRuleTag(name)">
<i class="fa fa-trash"></i> <i class="fa fa-trash"></i>
@ -172,10 +213,18 @@
<div class="gf-form-group"> <div class="gf-form-group">
<div class="gf-form-inline"> <div class="gf-form-inline">
<div class="gf-form"> <div class="gf-form">
<input class="gf-form-input width-15" placeholder="New tag name..." <input
ng-model="ctrl.newAlertRuleTag.name" type="text"> class="gf-form-input width-15"
<input class="gf-form-input width-15" placeholder="New tag value..." placeholder="New tag name..."
ng-model="ctrl.newAlertRuleTag.value" type="text"> ng-model="ctrl.newAlertRuleTag.name"
type="text"
/>
<input
class="gf-form-input width-15"
placeholder="New tag value..."
ng-model="ctrl.newAlertRuleTag.value"
type="text"
/>
</div> </div>
</div> </div>
<div class="gf-form"> <div class="gf-form">

View File

@ -35,7 +35,7 @@
default default
</span> </span>
<a ng-click="ctrl.deleteNotification(notification.id)" class="btn btn-danger btn-small"> <a ng-click="ctrl.deleteNotification(notification.id)" class="btn btn-danger btn-small">
<i class="fa fa-remove"></i> <icon name="'times'"></icon>
</a> </a>
</td> </td>
</tr> </tr>

View File

@ -75,42 +75,42 @@ function getStateDisplayModel(state: string) {
case 'ok': { case 'ok': {
return { return {
text: 'OK', text: 'OK',
iconClass: 'icon-gf icon-gf-online', iconClass: 'heart',
stateClass: 'alert-state-ok', stateClass: 'alert-state-ok',
}; };
} }
case 'alerting': { case 'alerting': {
return { return {
text: 'ALERTING', text: 'ALERTING',
iconClass: 'icon-gf icon-gf-critical', iconClass: 'heartbeat',
stateClass: 'alert-state-critical', stateClass: 'alert-state-critical',
}; };
} }
case 'no_data': { case 'no_data': {
return { return {
text: 'NO DATA', text: 'NO DATA',
iconClass: 'fa fa-question', iconClass: 'question-circle',
stateClass: 'alert-state-warning', stateClass: 'alert-state-warning',
}; };
} }
case 'paused': { case 'paused': {
return { return {
text: 'PAUSED', text: 'PAUSED',
iconClass: 'fa fa-pause', iconClass: 'pause',
stateClass: 'alert-state-paused', stateClass: 'alert-state-paused',
}; };
} }
case 'pending': { case 'pending': {
return { return {
text: 'PENDING', text: 'PENDING',
iconClass: 'fa fa-exclamation', iconClass: 'exclamation-triangle',
stateClass: 'alert-state-warning', stateClass: 'alert-state-warning',
}; };
} }
case 'unknown': { case 'unknown': {
return { return {
text: 'UNKNOWN', text: 'UNKNOWN',
iconClass: 'fa fa-question', iconClass: 'question-circle',
stateClass: 'alert-state-paused', stateClass: 'alert-state-paused',
}; };
} }

View File

@ -131,7 +131,7 @@ describe('Alert rules', () => {
state: 'alerting', state: 'alerting',
stateAge: newStateDateAge, stateAge: newStateDateAge,
stateClass: 'alert-state-critical', stateClass: 'alert-state-critical',
stateIcon: 'icon-gf icon-gf-critical', stateIcon: 'heartbeat',
stateText: 'ALERTING', stateText: 'ALERTING',
url: '/d/ggHbN42mk/alerting-with-testdata', url: '/d/ggHbN42mk/alerting-with-testdata',
}, },
@ -149,7 +149,7 @@ describe('Alert rules', () => {
state: 'ok', state: 'ok',
stateAge: newStateDateAge, stateAge: newStateDateAge,
stateClass: 'alert-state-ok', stateClass: 'alert-state-ok',
stateIcon: 'icon-gf icon-gf-online', stateIcon: 'heart',
stateText: 'OK', stateText: 'OK',
url: '/d/ggHbN42mk/alerting-with-testdata', url: '/d/ggHbN42mk/alerting-with-testdata',
}, },
@ -168,7 +168,7 @@ describe('Alert rules', () => {
state: 'ok', state: 'ok',
stateAge: newStateDateAge, stateAge: newStateDateAge,
stateClass: 'alert-state-ok', stateClass: 'alert-state-ok',
stateIcon: 'icon-gf icon-gf-online', stateIcon: 'heart',
stateText: 'OK', stateText: 'OK',
url: '/d/ggHbN42mk/alerting-with-testdata', url: '/d/ggHbN42mk/alerting-with-testdata',
}, },
@ -186,7 +186,7 @@ describe('Alert rules', () => {
state: 'paused', state: 'paused',
stateAge: newStateDateAge, stateAge: newStateDateAge,
stateClass: 'alert-state-paused', stateClass: 'alert-state-paused',
stateIcon: 'fa fa-pause', stateIcon: 'pause',
stateText: 'PAUSED', stateText: 'PAUSED',
url: '/d/ggHbN42mk/alerting-with-testdata', url: '/d/ggHbN42mk/alerting-with-testdata',
}, },
@ -207,7 +207,7 @@ describe('Alert rules', () => {
state: 'ok', state: 'ok',
stateAge: newStateDateAge, stateAge: newStateDateAge,
stateClass: 'alert-state-ok', stateClass: 'alert-state-ok',
stateIcon: 'icon-gf icon-gf-online', stateIcon: 'heart',
stateText: 'OK', stateText: 'OK',
url: '/d/ggHbN42mk/alerting-with-testdata', url: '/d/ggHbN42mk/alerting-with-testdata',
}, },

View File

@ -31,8 +31,7 @@
<tbody> <tbody>
<tr ng-repeat="annotation in ctrl.annotations track by annotation.name"> <tr ng-repeat="annotation in ctrl.annotations track by annotation.name">
<td style="width:90%" ng-hide="annotation.builtIn" class="pointer" ng-click="ctrl.edit(annotation)"> <td style="width:90%" ng-hide="annotation.builtIn" class="pointer" ng-click="ctrl.edit(annotation)">
<i class="fa fa-comment" style="color:{{ annotation.iconColor }}"></i> &nbsp; <i class="fa fa-comment" style="color:{{ annotation.iconColor }}"></i> &nbsp; {{ annotation.name }}
{{ annotation.name }}
</td> </td>
<td style="width:90%" ng-show="annotation.builtIn" class="pointer" ng-click="ctrl.edit(annotation)"> <td style="width:90%" ng-show="annotation.builtIn" class="pointer" ng-click="ctrl.edit(annotation)">
<i class="gicon gicon-annotation"></i> &nbsp; <i class="gicon gicon-annotation"></i> &nbsp;
@ -53,7 +52,7 @@
class="btn btn-danger btn-small" class="btn btn-danger btn-small"
ng-hide="annotation.builtIn" ng-hide="annotation.builtIn"
> >
<i class="fa fa-remove"></i> <icon name="'times'"></icon>
</a> </a>
</td> </td>
</tr> </tr>
@ -62,7 +61,14 @@
<!-- empty list cta, there is always one built in query --> <!-- empty list cta, there is always one built in query -->
<div ng-if="ctrl.annotations.length === 1" class="p-t-2"> <div ng-if="ctrl.annotations.length === 1" class="p-t-2">
<empty-list-cta title="ctrl.emptyListCta.title" buttonIcon="ctrl.emptyListCta.buttonIcon" buttonTitle="ctrl.emptyListCta.buttonTitle" infoBox="ctrl.emptyListCta.infoBox" infoBoxTitle="ctrl.emptyListCta.infoBoxTitle" on-click="ctrl.setupNew"/> <empty-list-cta
title="ctrl.emptyListCta.title"
buttonIcon="ctrl.emptyListCta.buttonIcon"
buttonTitle="ctrl.emptyListCta.buttonTitle"
infoBox="ctrl.emptyListCta.infoBox"
infoBoxTitle="ctrl.emptyListCta.infoBoxTitle"
on-click="ctrl.setupNew"
/>
</div> </div>
</div> </div>

View File

@ -1,4 +1,5 @@
import React from 'react'; import React from 'react';
import { Icon } from '@grafana/ui';
export interface Props { export interface Props {
apiKey: string; apiKey: string;
@ -10,12 +11,12 @@ export const ApiKeysAddedModal = (props: Props) => {
<div className="modal-body"> <div className="modal-body">
<div className="modal-header"> <div className="modal-header">
<h2 className="modal-header-title"> <h2 className="modal-header-title">
<i className="fa fa-key" /> <Icon name="key-skeleton-alt" />
<span className="p-l-1">API Key Created</span> <span className="p-l-1">API Key Created</span>
</h2> </h2>
<a className="modal-header-close" ng-click="dismiss();"> <a className="modal-header-close" ng-click="dismiss();">
<i className="fa fa-remove" /> <Icon name="times" />
</a> </a>
</div> </div>

View File

@ -13,7 +13,15 @@ import ApiKeysAddedModal from './ApiKeysAddedModal';
import config from 'app/core/config'; import config from 'app/core/config';
import appEvents from 'app/core/app_events'; import appEvents from 'app/core/app_events';
import EmptyListCTA from 'app/core/components/EmptyListCTA/EmptyListCTA'; import EmptyListCTA from 'app/core/components/EmptyListCTA/EmptyListCTA';
import { DeleteButton, EventsWithValidation, FormLabel, LegacyForms, Switch, ValidationEvents } from '@grafana/ui'; import {
DeleteButton,
EventsWithValidation,
FormLabel,
LegacyForms,
Switch,
ValidationEvents,
Icon,
} from '@grafana/ui';
const { Input } = LegacyForms; const { Input } = LegacyForms;
import { dateTime, isDateTime, NavModel } from '@grafana/data'; import { dateTime, isDateTime, NavModel } from '@grafana/data';
import { FilterInput } from 'app/core/components/FilterInput/FilterInput'; import { FilterInput } from 'app/core/components/FilterInput/FilterInput';
@ -181,7 +189,7 @@ export class ApiKeysPage extends PureComponent<Props, any> {
<SlideDown in={isAdding}> <SlideDown in={isAdding}>
<div className="cta-form"> <div className="cta-form">
<button className="cta-form__close btn btn-transparent" onClick={this.onToggleAdding}> <button className="cta-form__close btn btn-transparent" onClick={this.onToggleAdding}>
<i className="fa fa-close" /> <Icon name="times" />
</button> </button>
<h5>Add API Key</h5> <h5>Add API Key</h5>
<form className="gf-form-group" onSubmit={this.onAddApiKey}> <form className="gf-form-group" onSubmit={this.onAddApiKey}>

View File

@ -10,8 +10,8 @@ exports[`Render should render component 1`] = `
<h2 <h2
className="modal-header-title" className="modal-header-title"
> >
<i <Icon
className="fa fa-key" name="key-skeleton-alt"
/> />
<span <span
className="p-l-1" className="p-l-1"
@ -23,8 +23,8 @@ exports[`Render should render component 1`] = `
className="modal-header-close" className="modal-header-close"
ng-click="dismiss();" ng-click="dismiss();"
> >
<i <Icon
className="fa fa-remove" name="times"
/> />
</a> </a>
</div> </div>

View File

@ -53,8 +53,8 @@ exports[`Render should render CTA if there are no API keys 1`] = `
className="cta-form__close btn btn-transparent" className="cta-form__close btn btn-transparent"
onClick={[Function]} onClick={[Function]}
> >
<i <Icon
className="fa fa-close" name="times"
/> />
</button> </button>
<h5> <h5>

View File

@ -11,15 +11,22 @@
class="btn btn-primary" class="btn btn-primary"
ng-click="ctrl.setupNew()" ng-click="ctrl.setupNew()"
ng-if="ctrl.dashboard.links.length > 0" ng-if="ctrl.dashboard.links.length > 0"
ng-hide="ctrl.mode === 'edit' || ctrl.mode === 'new'"> ng-hide="ctrl.mode === 'edit' || ctrl.mode === 'new'"
New
</a
> >
New
</a>
</div> </div>
<div ng-if="ctrl.mode == 'list'"> <div ng-if="ctrl.mode == 'list'">
<div ng-if="ctrl.dashboard.links.length === 0"> <div ng-if="ctrl.dashboard.links.length === 0">
<empty-list-cta on-click="ctrl.setupNew" title="ctrl.emptyListCta.title" buttonIcon="ctrl.emptyListCta.buttonIcon" buttonTitle="ctrl.emptyListCta.buttonTitle" infoBox="ctrl.emptyListCta.infoBox" infoBoxTitle="ctrl.emptyListCta.infoBoxTitle"/> <empty-list-cta
on-click="ctrl.setupNew"
title="ctrl.emptyListCta.title"
buttonIcon="ctrl.emptyListCta.buttonIcon"
buttonTitle="ctrl.emptyListCta.buttonTitle"
infoBox="ctrl.emptyListCta.infoBox"
infoBoxTitle="ctrl.emptyListCta.infoBoxTitle"
/>
</div> </div>
<div ng-if="ctrl.dashboard.links.length > 0"> <div ng-if="ctrl.dashboard.links.length > 0">
@ -62,7 +69,7 @@
</td> </td>
<td style="width: 1%"> <td style="width: 1%">
<a ng-click="ctrl.deleteLink($index)" class="btn btn-danger btn-small" ng-hide="annotation.builtIn"> <a ng-click="ctrl.deleteLink($index)" class="btn btn-danger btn-small" ng-hide="annotation.builtIn">
<i class="fa fa-remove"></i> <icon name="'times'"></icon>
</a> </a>
</td> </td>
</tr> </tr>

View File

@ -112,7 +112,7 @@ export const OverrideFieldConfigEditor: React.FC<Props> = props => {
const renderAddOverride = () => { const renderAddOverride = () => {
return ( return (
<ValuePicker <ValuePicker
icon="plus" icon="plus-circle"
label="Add override" label="Add override"
variant="secondary" variant="secondary"
options={fieldMatchersUI options={fieldMatchersUI

View File

@ -115,7 +115,7 @@ export const OverrideEditor: React.FC<OverrideEditorProps> = ({
<div className={styles.propertyPickerWrapper}> <div className={styles.propertyPickerWrapper}>
<ValuePicker <ValuePicker
label="Set config property" label="Set config property"
icon="plus" icon="plus-circle"
options={configPropertiesOptions} options={configPropertiesOptions}
variant={'link'} variant={'link'}
onChange={o => { onChange={o => {

View File

@ -6,7 +6,7 @@
</h2> </h2>
<a class="modal-header-close" ng-click="ctrl.dismiss();"> <a class="modal-header-close" ng-click="ctrl.dismiss();">
<i class="fa fa-remove"></i> <icon name="'times'"></icon>
</a> </a>
</div> </div>

View File

@ -9,7 +9,7 @@ const template = `
</h2> </h2>
<a class="modal-header-close" ng-click="ctrl.dismiss();"> <a class="modal-header-close" ng-click="ctrl.dismiss();">
<i class="fa fa-remove"></i> <icon name="'times'"></icon>
</a> </a>
</div> </div>

View File

@ -7,7 +7,7 @@ import classNames from 'classnames';
import { css } from 'emotion'; import { css } from 'emotion';
import { ExploreId, ExploreItemState } from 'app/types/explore'; import { ExploreId, ExploreItemState } from 'app/types/explore';
import { ToggleButtonGroup, ToggleButton, Tooltip, LegacyForms, SetInterval } from '@grafana/ui'; import { ToggleButtonGroup, ToggleButton, Tooltip, LegacyForms, SetInterval, Icon } from '@grafana/ui';
const { ButtonSelect } = LegacyForms; const { ButtonSelect } = LegacyForms;
import { RawTimeRange, TimeZone, TimeRange, DataQuery, ExploreMode } from '@grafana/data'; import { RawTimeRange, TimeZone, TimeRange, DataQuery, ExploreMode } from '@grafana/data';
import { DataSourcePicker } from 'app/core/components/Select/DataSourcePicker'; import { DataSourcePicker } from 'app/core/components/Select/DataSourcePicker';
@ -202,14 +202,21 @@ export class UnConnectedExploreToolbar extends PureComponent<Props> {
<div className="explore-toolbar-header-title"> <div className="explore-toolbar-header-title">
{exploreId === 'left' && ( {exploreId === 'left' && (
<span className="navbar-page-btn"> <span className="navbar-page-btn">
<i className="gicon gicon-explore" /> <Icon
name="compass"
size="xl"
className={css`
margin-right: 6px;
margin-bottom: 3px;
`}
/>
Explore Explore
</span> </span>
)} )}
</div> </div>
{splitted && ( {splitted && (
<a className="explore-toolbar-header-close" onClick={() => closeSplit(exploreId)}> <a className="explore-toolbar-header-close" onClick={() => closeSplit(exploreId)}>
<i className="fa fa-times fa-fw" /> <Icon name="times" />
</a> </a>
)} )}
</div> </div>
@ -261,7 +268,7 @@ export class UnConnectedExploreToolbar extends PureComponent<Props> {
<div className="explore-toolbar-content-item"> <div className="explore-toolbar-content-item">
<Tooltip content={'Return to panel'} placement="bottom"> <Tooltip content={'Return to panel'} placement="bottom">
<button className={panelReturnClasses} onClick={() => this.returnToPanel()}> <button className={panelReturnClasses} onClick={() => this.returnToPanel()}>
<i className="fa fa-arrow-left" /> <Icon name="arrow-left" />
</button> </button>
</Tooltip> </Tooltip>
{originDashboardIsEditable && ( {originDashboardIsEditable && (
@ -281,7 +288,8 @@ export class UnConnectedExploreToolbar extends PureComponent<Props> {
splitted={splitted} splitted={splitted}
title="Split" title="Split"
onClick={split} onClick={split}
iconClassName="fa fa-fw fa-columns icon-margin-right" icon="columns"
iconClassName="icon-margin-right"
disabled={isLive} disabled={isLive}
/> />
</div> </div>
@ -307,7 +315,8 @@ export class UnConnectedExploreToolbar extends PureComponent<Props> {
splitted={splitted} splitted={splitted}
title="Clear All" title="Clear All"
onClick={this.onClearAll} onClick={this.onClearAll}
iconClassName="fa fa-fw fa-trash icon-margin-right" icon="trash-alt"
iconClassName="icon-margin-right"
/> />
</div> </div>
)} )}

View File

@ -128,11 +128,8 @@ export function LiveTailButton(props: LiveTailButtonProps) {
[styles.isLive]: isLive && !isPaused, [styles.isLive]: isLive && !isPaused,
[styles.isPaused]: isLive && isPaused, [styles.isPaused]: isLive && isPaused,
})} })}
iconClassName={classNames( icon={!isLive ? 'play' : 'pause'}
'fa', iconClassName={isLive && 'icon-brand-gradient'}
isPaused || !isLive ? 'fa-play' : 'fa-pause',
isLive && 'icon-brand-gradient'
)}
onClick={onClickMain} onClick={onClickMain}
title={'\xa0Live'} title={'\xa0Live'}
/> />

View File

@ -30,10 +30,10 @@ describe('QueryRowActions', () => {
}); });
it('should change icon to fa-eye-slash when query row result is hidden', () => { it('should change icon to fa-eye-slash when query row result is hidden', () => {
const wrapper = setup({ isDisabled: true }); const wrapper = setup({ isDisabled: true });
expect(wrapper.find('i.fa-eye-slash')).toHaveLength(1); expect(wrapper.find({ title: 'Enable query' })).toHaveLength(1);
}); });
it('should change icon to fa-eye when query row result is not hidden', () => { it('should change icon to fa-eye when query row result is not hidden', () => {
const wrapper = setup({ isDisabled: false }); const wrapper = setup({ isDisabled: false });
expect(wrapper.find('i.fa-eye')).toHaveLength(1); expect(wrapper.find({ title: 'Disable query' })).toHaveLength(1);
}); });
}); });

View File

@ -1,4 +1,5 @@
import React from 'react'; import React from 'react';
import { Icon } from '@grafana/ui';
function formatLatency(value: number) { function formatLatency(value: number) {
return `${(value / 1000).toFixed(1)}s`; return `${(value / 1000).toFixed(1)}s`;
@ -34,7 +35,7 @@ export function QueryRowActions(props: Props) {
className="gf-form-label gf-form-label--btn" className="gf-form-label gf-form-label--btn"
onClick={onClickToggleEditorMode} onClick={onClickToggleEditorMode}
> >
<i className="fa fa-pencil" /> <Icon name="pen" />
</button> </button>
</div> </div>
)} )}
@ -48,14 +49,14 @@ export function QueryRowActions(props: Props) {
disabled={isNotStarted} disabled={isNotStarted}
className="gf-form-label gf-form-label--btn" className="gf-form-label gf-form-label--btn"
onClick={onClickToggleDisabled} onClick={onClickToggleDisabled}
title="Disable/enable query" title={isDisabled ? 'Enable query' : 'Disable query'}
> >
<i className={isDisabled ? 'fa fa-eye-slash' : 'fa fa-eye'} /> <Icon name={isDisabled ? 'eye' : 'eye-slash'} />
</button> </button>
</div> </div>
<div className="gf-form"> <div className="gf-form">
<button className="gf-form-label gf-form-label--btn" onClick={onClickRemoveButton} title="Remove query"> <button className="gf-form-label gf-form-label--btn" onClick={onClickRemoveButton} title="Remove query">
<i className="fa fa-minus" /> <Icon name="minus" />
</button> </button>
</div> </div>
</div> </div>

View File

@ -1,4 +1,5 @@
import React, { forwardRef } from 'react'; import React, { forwardRef } from 'react';
import { IconName, Icon } from '@grafana/ui';
export enum IconSide { export enum IconSide {
left = 'left', left = 'left',
@ -10,6 +11,7 @@ interface Props extends React.HTMLAttributes<HTMLDivElement> {
title: string; title: string;
onClick: () => void; onClick: () => void;
buttonClassName?: string; buttonClassName?: string;
icon?: IconName;
iconClassName?: string; iconClassName?: string;
iconSide?: IconSide; iconSide?: IconSide;
disabled?: boolean; disabled?: boolean;
@ -25,7 +27,17 @@ export const ResponsiveButton = forwardRef<HTMLDivElement, Props>((props, ref) =
}; };
props = { ...defaultProps, ...props }; props = { ...defaultProps, ...props };
const { title, onClick, buttonClassName, iconClassName, splitted, iconSide, disabled, ...divElementProps } = props; const {
title,
onClick,
buttonClassName,
icon,
iconClassName,
splitted,
iconSide,
disabled,
...divElementProps
} = props;
return ( return (
<div ref={ref} {...divElementProps}> <div ref={ref} {...divElementProps}>
@ -34,9 +46,9 @@ export const ResponsiveButton = forwardRef<HTMLDivElement, Props>((props, ref) =
onClick={onClick} onClick={onClick}
disabled={disabled || false} disabled={disabled || false}
> >
{iconClassName && iconSide === IconSide.left ? <i className={`${iconClassName}`} /> : null} {icon && iconSide === IconSide.left ? <Icon name={icon} className={iconClassName} /> : null}
<span className="btn-title">{!splitted ? formatBtnTitle(title, iconSide) : ''}</span> <span className="btn-title">{!splitted ? formatBtnTitle(title, iconSide) : ''}</span>
{iconClassName && iconSide === IconSide.right ? <i className={`${iconClassName}`} /> : null} {icon && iconSide === IconSide.right ? <Icon name={icon} className={iconClassName} /> : null}
</button> </button>
</div> </div>
); );

View File

@ -10,7 +10,7 @@ import { stylesFactory, withTheme } from '@grafana/ui';
//Types //Types
import { RichHistoryQuery, ExploreId } from 'app/types/explore'; import { RichHistoryQuery, ExploreId } from 'app/types/explore';
import { SelectableValue, GrafanaTheme } from '@grafana/data'; import { SelectableValue, GrafanaTheme } from '@grafana/data';
import { TabsBar, Tab, TabContent, Themeable, CustomScrollbar, IconName } from '@grafana/ui'; import { TabsBar, Tab, TabContent, Themeable, CustomScrollbar, IconName, Icon } from '@grafana/ui';
//Components //Components
import { RichHistorySettings } from './RichHistorySettings'; import { RichHistorySettings } from './RichHistorySettings';
@ -169,7 +169,7 @@ class UnThemedRichHistory extends PureComponent<RichHistoryProps, RichHistorySta
height={height} height={height}
/> />
), ),
icon: 'fa fa-history', icon: 'history',
}; };
const StarredTab = { const StarredTab = {
@ -186,7 +186,7 @@ class UnThemedRichHistory extends PureComponent<RichHistoryProps, RichHistorySta
exploreId={exploreId} exploreId={exploreId}
/> />
), ),
icon: 'fa fa-star', icon: 'star',
}; };
const SettingsTab = { const SettingsTab = {
@ -203,7 +203,7 @@ class UnThemedRichHistory extends PureComponent<RichHistoryProps, RichHistorySta
deleteRichHistory={deleteRichHistory} deleteRichHistory={deleteRichHistory}
/> />
), ),
icon: 'gicon gicon-preferences', icon: 'sliders-v-alt',
}; };
let tabs = [QueriesTab, StarredTab, SettingsTab]; let tabs = [QueriesTab, StarredTab, SettingsTab];
@ -220,7 +220,7 @@ class UnThemedRichHistory extends PureComponent<RichHistoryProps, RichHistorySta
/> />
))} ))}
<div className={styles.close} onClick={onClose}> <div className={styles.close} onClick={onClose}>
<i className="fa fa-times" title="Close query history" /> <Icon name="times" title="Close query history" />
</div> </div>
</TabsBar> </TabsBar>
<CustomScrollbar <CustomScrollbar

View File

@ -79,37 +79,29 @@ describe('RichHistoryCard', () => {
describe('commenting', () => { describe('commenting', () => {
it('should render comment, if comment present', () => { it('should render comment, if comment present', () => {
const wrapper = setup({ query: starredQueryWithComment }); const wrapper = setup({ query: starredQueryWithComment });
expect(wrapper.find({ 'aria-label': 'Query comment' })).toHaveLength(1); expect(wrapper.find({ 'aria-label': 'Query comment' }).hostNodes()).toHaveLength(1);
expect(wrapper.find({ 'aria-label': 'Query comment' }).text()).toEqual('test comment'); expect(wrapper.find({ 'aria-label': 'Query comment' }).text()).toEqual('test comment');
}); });
it('should have title "Edit comment" at comment icon, if comment present', () => { it('should have title "Edit comment" at comment icon, if comment present', () => {
const wrapper = setup({ query: starredQueryWithComment }); const wrapper = setup({ query: starredQueryWithComment });
expect(wrapper.find({ title: 'Edit comment' })).toHaveLength(1); expect(wrapper.find({ title: 'Edit comment' }).hostNodes()).toHaveLength(1);
expect(wrapper.find({ title: 'Add comment' })).toHaveLength(0); expect(wrapper.find({ title: 'Add comment' }).hostNodes()).toHaveLength(0);
}); });
it('should have title "Add comment" at comment icon, if no comment present', () => { it('should have title "Add comment" at comment icon, if no comment present', () => {
const wrapper = setup(); const wrapper = setup();
expect(wrapper.find({ title: 'Add comment' })).toHaveLength(1); expect(wrapper.find({ title: 'Add comment' }).hostNodes()).toHaveLength(1);
expect(wrapper.find({ title: 'Edit comment' })).toHaveLength(0); expect(wrapper.find({ title: 'Edit comment' }).hostNodes()).toHaveLength(0);
}); });
}); });
describe('starring', () => { describe('starring', () => {
it('should have title "Star query", if not starred', () => { it('should have title "Star query", if not starred', () => {
const wrapper = setup(); const wrapper = setup();
expect(wrapper.find({ title: 'Star query' })).toHaveLength(1); expect(wrapper.find({ title: 'Star query' }).hostNodes()).toHaveLength(1);
});
it('should render fa-star-o icon, if not starred', () => {
const wrapper = setup();
expect(wrapper.find({ title: 'Star query' }).hasClass('fa-star-o')).toBe(true);
}); });
it('should have title "Unstar query", if not starred', () => { it('should have title "Unstar query", if not starred', () => {
const wrapper = setup({ query: starredQueryWithComment }); const wrapper = setup({ query: starredQueryWithComment });
expect(wrapper.find({ title: 'Unstar query' })).toHaveLength(1); expect(wrapper.find({ title: 'Unstar query' }).hostNodes()).toHaveLength(1);
});
it('should have fa-star icon, if not starred', () => {
const wrapper = setup({ query: starredQueryWithComment });
expect(wrapper.find({ title: 'Unstar query' }).hasClass('fa-star')).toBe(true);
}); });
}); });
}); });

View File

@ -2,7 +2,7 @@ import React, { useState } from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { hot } from 'react-hot-loader'; import { hot } from 'react-hot-loader';
import { css, cx } from 'emotion'; import { css, cx } from 'emotion';
import { stylesFactory, useTheme, Forms, Button } from '@grafana/ui'; import { stylesFactory, useTheme, Forms, Button, Icon } from '@grafana/ui';
import { GrafanaTheme, AppEvents, DataSourceApi } from '@grafana/data'; import { GrafanaTheme, AppEvents, DataSourceApi } from '@grafana/data';
import { RichHistoryQuery, ExploreId } from 'app/types/explore'; import { RichHistoryQuery, ExploreId } from 'app/types/explore';
import { copyStringToClipboard, createUrlFromRichHistory, createDataQuery } from 'app/core/utils/richHistory'; import { copyStringToClipboard, createUrlFromRichHistory, createDataQuery } from 'app/core/utils/richHistory';
@ -76,9 +76,8 @@ const getStyles = stylesFactory((theme: GrafanaTheme, isRemoved: boolean) => {
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;
font-size: ${theme.typography.size.base}; font-size: ${theme.typography.size.base};
i { svg {
margin: ${theme.spacing.xs}; margin-left: ${theme.spacing.sm};
cursor: pointer;
} }
`, `,
queryContainer: css` queryContainer: css`
@ -212,19 +211,20 @@ export function RichHistoryCard(props: Props) {
const queryActionButtons = ( const queryActionButtons = (
<div className={styles.queryActionButtons}> <div className={styles.queryActionButtons}>
<i <Icon
className="fa fa-fw fa-comment-o" name="comment-alt"
onClick={toggleActiveUpdateComment} onClick={toggleActiveUpdateComment}
title={query.comment?.length > 0 ? 'Edit comment' : 'Add comment'} title={query.comment?.length > 0 ? 'Edit comment' : 'Add comment'}
></i> />
<i className="fa fa-fw fa-copy" onClick={onCopyQuery} title="Copy query to clipboard"></i> <Icon name="copy" onClick={onCopyQuery} title="Copy query to clipboard" />
{!isRemoved && <i className="fa fa-fw fa-link" onClick={onCreateLink} title="Copy link to clipboard"></i>} {!isRemoved && <Icon name="link" onClick={onCreateLink} title="Copy link to clipboard" />}
<i className={'fa fa-trash'} title={'Delete query'} onClick={onDeleteQuery}></i> <Icon name="trash-alt" title={'Delete query'} onClick={onDeleteQuery} />
<i <Icon
className={cx('fa fa-fw', query.starred ? 'fa-star starred' : 'fa-star-o')} name={query.starred ? 'favorite' : 'star'}
type={query.starred ? 'mono' : 'default'}
onClick={onStarrQuery} onClick={onStarrQuery}
title={query.starred ? 'Unstar query' : 'Star query'} title={query.starred ? 'Unstar query' : 'Star query'}
></i> />
</div> </div>
); );

View File

@ -40,7 +40,8 @@ export function RunButton(props: Props) {
'navbar-button--danger': loading, 'navbar-button--danger': loading,
'btn--radius-right-0': showDropdown, 'btn--radius-right-0': showDropdown,
})} })}
iconClassName={loading ? 'fa fa-spinner fa-fw fa-spin run-icon' : 'fa fa-refresh fa-fw'} icon={loading ? 'fa fa-spinner' : 'sync'}
iconClassName={loading && ' fa-spin run-icon'}
/> />
); );

View File

@ -12,8 +12,8 @@ exports[`QueryRowActions should render component 1`] = `
className="gf-form-label gf-form-label--btn" className="gf-form-label gf-form-label--btn"
onClick={[Function]} onClick={[Function]}
> >
<i <Icon
className="fa fa-pencil" name="pen"
/> />
</button> </button>
</div> </div>
@ -35,10 +35,10 @@ exports[`QueryRowActions should render component 1`] = `
className="gf-form-label gf-form-label--btn" className="gf-form-label gf-form-label--btn"
disabled={true} disabled={true}
onClick={[Function]} onClick={[Function]}
title="Disable/enable query" title="Disable query"
> >
<i <Icon
className="fa fa-eye" name="eye-slash"
/> />
</button> </button>
</div> </div>
@ -50,8 +50,8 @@ exports[`QueryRowActions should render component 1`] = `
onClick={[Function]} onClick={[Function]}
title="Remove query" title="Remove query"
> >
<i <Icon
className="fa fa-minus" name="minus"
/> />
</button> </button>
</div> </div>

View File

@ -6,7 +6,7 @@
</h2> </h2>
<a class="modal-header-close" ng-click="ctrl.dismiss();"> <a class="modal-header-close" ng-click="ctrl.dismiss();">
<i class="fa fa-remove"></i> <icon name="'times'"></icon>
</a> </a>
</div> </div>

View File

@ -2,7 +2,7 @@
<span style="position: relative;"> <span style="position: relative;">
<input <input
type="text" type="text"
placeholder="Find dashboards by name" placeholder="Search dashboards by name"
tabindex="1" tabindex="1"
ng-keydown="ctrl.keyDown($event)" ng-keydown="ctrl.keyDown($event)"
ng-model="ctrl.query.query" ng-model="ctrl.query.query"

View File

@ -46,7 +46,7 @@
</td> </td>
<td class="text-right"> <td class="text-right">
<a ng-click="ctrl.removePlaylist(playlist)" class="btn btn-danger btn-small"> <a ng-click="ctrl.removePlaylist(playlist)" class="btn btn-danger btn-small">
<i class="fa fa-remove"></i> <icon name="'times'"></icon>
</a> </a>
</td> </td>
</tr> </tr>

View File

@ -6,7 +6,7 @@
</h2> </h2>
<a class="modal-header-close" ng-click="dismiss();"> <a class="modal-header-close" ng-click="dismiss();">
<i class="fa fa-remove"></i> <icon name="'times'"></icon>
</a> </a>
</div> </div>

View File

@ -24,7 +24,7 @@
<td>{{ session.browser }} on {{ session.os }} {{ session.osVersion }}</td> <td>{{ session.browser }} on {{ session.os }} {{ session.osVersion }}</td>
<td> <td>
<button class="btn btn-danger btn-small" ng-click="ctrl.revokeUserSession(session.id)"> <button class="btn btn-danger btn-small" ng-click="ctrl.revokeUserSession(session.id)">
<i class="fa fa-power-off"></i> <icon name="'power'" style="margin-top: -2px;"></icon>
</button> </button>
</td> </td>
</tr> </tr>

View File

@ -66,7 +66,7 @@ export class TeamGroupSync extends PureComponent<Props, State> {
<td>{group.groupId}</td> <td>{group.groupId}</td>
<td style={{ width: '1%' }}> <td style={{ width: '1%' }}>
<a className="btn btn-danger btn-small" onClick={() => this.onRemoveGroup(group)}> <a className="btn btn-danger btn-small" onClick={() => this.onRemoveGroup(group)}>
<i className="fa fa-remove" /> <Icon name="times" />
</a> </a>
</td> </td>
</tr> </tr>
@ -95,7 +95,7 @@ export class TeamGroupSync extends PureComponent<Props, State> {
<SlideDown in={isAdding}> <SlideDown in={isAdding}>
<div className="cta-form"> <div className="cta-form">
<button className="cta-form__close btn btn-transparent" onClick={this.onToggleAdding}> <button className="cta-form__close btn btn-transparent" onClick={this.onToggleAdding}>
<i className="fa fa-close" /> <Icon name="times" />
</button> </button>
<h5>Add External Group</h5> <h5>Add External Group</h5>
<form className="gf-form-inline" onSubmit={this.onAddGroup}> <form className="gf-form-inline" onSubmit={this.onAddGroup}>

View File

@ -1,5 +1,6 @@
import React, { PureComponent } from 'react'; import React, { PureComponent } from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { Icon } from '@grafana/ui';
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 { UserPicker } from 'app/core/components/Select/UserPicker';
import { TagBadge } from 'app/core/components/TagFilter/TagBadge'; import { TagBadge } from 'app/core/components/TagFilter/TagBadge';
@ -97,7 +98,7 @@ export class TeamMembers extends PureComponent<Props, State> {
<SlideDown in={isAdding}> <SlideDown in={isAdding}>
<div className="cta-form"> <div className="cta-form">
<button className="cta-form__close btn btn-transparent" onClick={this.onToggleAdding}> <button className="cta-form__close btn btn-transparent" onClick={this.onToggleAdding}>
<i className="fa fa-close" /> <Icon name="times" />
</button> </button>
<h5>Add team member</h5> <h5>Add team member</h5>
<div className="gf-form-inline"> <div className="gf-form-inline">

View File

@ -33,8 +33,8 @@ exports[`Render should render component 1`] = `
className="cta-form__close btn btn-transparent" className="cta-form__close btn btn-transparent"
onClick={[Function]} onClick={[Function]}
> >
<i <Icon
className="fa fa-close" name="times"
/> />
</button> </button>
<h5> <h5>
@ -124,8 +124,8 @@ exports[`Render should render groups table 1`] = `
className="cta-form__close btn btn-transparent" className="cta-form__close btn btn-transparent"
onClick={[Function]} onClick={[Function]}
> >
<i <Icon
className="fa fa-close" name="times"
/> />
</button> </button>
<h5> <h5>
@ -198,8 +198,8 @@ exports[`Render should render groups table 1`] = `
className="btn btn-danger btn-small" className="btn btn-danger btn-small"
onClick={[Function]} onClick={[Function]}
> >
<i <Icon
className="fa fa-remove" name="times"
/> />
</a> </a>
</td> </td>
@ -221,8 +221,8 @@ exports[`Render should render groups table 1`] = `
className="btn btn-danger btn-small" className="btn btn-danger btn-small"
onClick={[Function]} onClick={[Function]}
> >
<i <Icon
className="fa fa-remove" name="times"
/> />
</a> </a>
</td> </td>
@ -244,8 +244,8 @@ exports[`Render should render groups table 1`] = `
className="btn btn-danger btn-small" className="btn btn-danger btn-small"
onClick={[Function]} onClick={[Function]}
> >
<i <Icon
className="fa fa-remove" name="times"
/> />
</a> </a>
</td> </td>

View File

@ -37,8 +37,8 @@ exports[`Render should render component 1`] = `
className="cta-form__close btn btn-transparent" className="cta-form__close btn btn-transparent"
onClick={[Function]} onClick={[Function]}
> >
<i <Icon
className="fa fa-close" name="times"
/> />
</button> </button>
<h5> <h5>
@ -131,8 +131,8 @@ exports[`Render should render team members 1`] = `
className="cta-form__close btn btn-transparent" className="cta-form__close btn btn-transparent"
onClick={[Function]} onClick={[Function]}
> >
<i <Icon
className="fa fa-close" name="times"
/> />
</button> </button>
<h5> <h5>

View File

@ -94,7 +94,7 @@
class="btn btn-danger btn-small" class="btn btn-danger btn-small"
aria-label="{{::selectors.tableRowRemoveButtons(variable.name)}}" aria-label="{{::selectors.tableRowRemoveButtons(variable.name)}}"
> >
<i class="fa fa-remove"></i> <icon name="'times'"></icon>
</a> </a>
</td> </td>
</tr> </tr>

View File

@ -2,6 +2,7 @@ import React, { createRef, PureComponent } from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { Invitee } from 'app/types'; import { Invitee } from 'app/types';
import { revokeInvite } from './state/actions'; import { revokeInvite } from './state/actions';
import { Icon } from '@grafana/ui';
export interface Props { export interface Props {
invitee: Invitee; invitee: Invitee;
@ -40,7 +41,7 @@ class InviteeRow extends PureComponent<Props> {
</td> </td>
<td> <td>
<button className="btn btn-danger btn-small" onClick={() => revokeInvite(invitee.code)}> <button className="btn btn-danger btn-small" onClick={() => revokeInvite(invitee.code)}>
<i className="fa fa-remove" /> <Icon name="times" />
</button> </button>
</td> </td>
</tr> </tr>

View File

@ -1,5 +1,6 @@
import React, { FC } from 'react'; import React, { FC } from 'react';
import { OrgUser } from 'app/types'; import { OrgUser } from 'app/types';
import { Icon } from '@grafana/ui';
export interface Props { export interface Props {
users: OrgUser[]; users: OrgUser[];
@ -55,7 +56,7 @@ const UsersTable: FC<Props> = props => {
</td> </td>
<td> <td>
<div onClick={() => onRemoveUser(user)} className="btn btn-danger btn-small"> <div onClick={() => onRemoveUser(user)} className="btn btn-danger btn-small">
<i className="fa fa-remove" /> <Icon name="times" />
</div> </div>
</td> </td>
</tr> </tr>

View File

@ -127,8 +127,8 @@ exports[`Render should render users table 1`] = `
className="btn btn-danger btn-small" className="btn btn-danger btn-small"
onClick={[Function]} onClick={[Function]}
> >
<i <Icon
className="fa fa-remove" name="times"
/> />
</div> </div>
</td> </td>
@ -193,8 +193,8 @@ exports[`Render should render users table 1`] = `
className="btn btn-danger btn-small" className="btn btn-danger btn-small"
onClick={[Function]} onClick={[Function]}
> >
<i <Icon
className="fa fa-remove" name="times"
/> />
</div> </div>
</td> </td>
@ -259,8 +259,8 @@ exports[`Render should render users table 1`] = `
className="btn btn-danger btn-small" className="btn btn-danger btn-small"
onClick={[Function]} onClick={[Function]}
> >
<i <Icon
className="fa fa-remove" name="times"
/> />
</div> </div>
</td> </td>
@ -325,8 +325,8 @@ exports[`Render should render users table 1`] = `
className="btn btn-danger btn-small" className="btn btn-danger btn-small"
onClick={[Function]} onClick={[Function]}
> >
<i <Icon
className="fa fa-remove" name="times"
/> />
</div> </div>
</td> </td>
@ -391,8 +391,8 @@ exports[`Render should render users table 1`] = `
className="btn btn-danger btn-small" className="btn btn-danger btn-small"
onClick={[Function]} onClick={[Function]}
> >
<i <Icon
className="fa fa-remove" name="times"
/> />
</div> </div>
</td> </td>
@ -457,8 +457,8 @@ exports[`Render should render users table 1`] = `
className="btn btn-danger btn-small" className="btn btn-danger btn-small"
onClick={[Function]} onClick={[Function]}
> >
<i <Icon
className="fa fa-remove" name="times"
/> />
</div> </div>
</td> </td>

View File

@ -3,6 +3,7 @@ import { e2e } from '@grafana/e2e';
import EmptyListCTA from '../../../core/components/EmptyListCTA/EmptyListCTA'; import EmptyListCTA from '../../../core/components/EmptyListCTA/EmptyListCTA';
import { QueryVariableModel, VariableModel } from '../../templating/types'; import { QueryVariableModel, VariableModel } from '../../templating/types';
import { toVariableIdentifier, VariableIdentifier } from '../state/types'; import { toVariableIdentifier, VariableIdentifier } from '../state/types';
import { Icon } from '@grafana/ui';
export interface Props { export interface Props {
variables: VariableModel[]; variables: VariableModel[];
@ -148,7 +149,7 @@ export class VariableEditorList extends PureComponent<Props> {
variable.name variable.name
)} )}
> >
<i className="fa fa-remove" /> <Icon name="times" />
</a> </a>
</td> </td>
</tr> </tr>

View File

@ -1,34 +1,51 @@
<div class="modal-body" ng-cloak> <div class="modal-body" ng-cloak>
<div class="modal-header"> <div class="modal-header">
<h2 class="modal-header-title"> <h2 class="modal-header-title">
<i class="fa {{icon}}"></i> <i class="fa {{icon}}"></i>
<span class="p-l-1"> <span class="p-l-1">
{{title}} {{title}}
</span> </span>
</h2> </h2>
<a class="modal-header-close" ng-click="dismiss();"> <a class="modal-header-close" ng-click="dismiss();">
<i class="fa fa-remove"></i> <icon name="'times'"></icon>
</a> </a>
</div> </div>
<div class="modal-content text-center"> <div class="modal-content text-center">
<div class="confirm-modal-text">
<div class="confirm-modal-text"> {{text}}
{{text}} <div ng-if="text2 && text2htmlBind" class="confirm-modal-text2" ng-bind-html="text2"></div>
<div ng-if="text2 && text2htmlBind" class="confirm-modal-text2" ng-bind-html="text2"></div>
<div ng-if="text2 && !text2htmlBind" class="confirm-modal-text2">{{text2}}</div> <div ng-if="text2 && !text2htmlBind" class="confirm-modal-text2">{{text2}}</div>
</div> </div>
<div class="modal-content-confirm-text" ng-if="confirmText"> <div class="modal-content-confirm-text" ng-if="confirmText">
<input type="text" class="gf-form-input width-16" style="display: inline-block;" placeholder="Type {{confirmText}} to confirm" ng-model="confirmInput" ng-change="updateConfirmText(confirmInput)"> <input
</div> type="text"
class="gf-form-input width-16"
<div class="confirm-modal-buttons"> style="display: inline-block;"
<button ng-show="onAltAction" type="button" class="btn btn-primary" ng-click="dismiss();onAltAction();">{{altActionText}}</button> placeholder="Type {{confirmText}} to confirm"
<button ng-show="onConfirm" type="button" class="btn btn-danger" ng-click="onConfirm();dismiss();" ng-disabled="!confirmTextValid" give-focus="true" aria-label={{selectors.delete}}>{{yesText}}</button> ng-model="confirmInput"
<button type="button" class="btn btn-inverse" ng-click="dismiss()">{{noText}}</button> ng-change="updateConfirmText(confirmInput)"
</div> />
</div> </div>
<div class="confirm-modal-buttons">
<button ng-show="onAltAction" type="button" class="btn btn-primary" ng-click="dismiss();onAltAction();">
{{altActionText}}
</button>
<button
ng-show="onConfirm"
type="button"
class="btn btn-danger"
ng-click="onConfirm();dismiss();"
ng-disabled="!confirmTextValid"
give-focus="true"
aria-label="{{selectors.delete}}"
>
{{yesText}}
</button>
<button type="button" class="btn btn-inverse" ng-click="dismiss()">{{noText}}</button>
</div>
</div>
</div> </div>

View File

@ -5,7 +5,7 @@
</h2> </h2>
<button class="tabbed-view-close-btn" ng-click="dismiss()"> <button class="tabbed-view-close-btn" ng-click="dismiss()">
<i class="fa fa-remove"></i> <icon name="'times'"></icon>
</button> </button>
</div> </div>

View File

@ -67,7 +67,7 @@ export const DataLinks = (props: Props) => {
className={css` className={css`
margin-right: 10px; margin-right: 10px;
`} `}
icon="plus" icon="plus-circle"
onClick={event => { onClick={event => {
event.preventDefault(); event.preventDefault();
const newDataLinks = [...(value || []), { field: '', url: '' }]; const newDataLinks = [...(value || []), { field: '', url: '' }];

View File

@ -1,16 +1,33 @@
<query-editor-row query-ctrl="ctrl" can-collapse="false" has-text-edit-mode="ctrl.target.queryType === 'Application Insights'"> <query-editor-row
query-ctrl="ctrl"
can-collapse="false"
has-text-edit-mode="ctrl.target.queryType === 'Application Insights'"
>
<div class="gf-form-inline"> <div class="gf-form-inline">
<div class="gf-form"> <div class="gf-form">
<label class="gf-form-label query-keyword width-9">Service</label> <label class="gf-form-label query-keyword width-9">Service</label>
<div class="gf-form-select-wrapper gf-form-select-wrapper--caret-indent"> <div class="gf-form-select-wrapper gf-form-select-wrapper--caret-indent">
<select class="gf-form-input service-dropdown" ng-model="ctrl.target.queryType" ng-options="f as f for f in ['Application Insights', 'Azure Monitor', 'Azure Log Analytics']" <select
ng-change="ctrl.onQueryTypeChange()"></select> class="gf-form-input service-dropdown"
ng-model="ctrl.target.queryType"
ng-options="f as f for f in ['Application Insights', 'Azure Monitor', 'Azure Log Analytics']"
ng-change="ctrl.onQueryTypeChange()"
></select>
</div> </div>
</div> </div>
<div class="gf-form" ng-if="ctrl.target.queryType === 'Azure Monitor' || ctrl.target.queryType === 'Azure Log Analytics'"> <div
class="gf-form"
ng-if="ctrl.target.queryType === 'Azure Monitor' || ctrl.target.queryType === 'Azure Log Analytics'"
>
<label class="gf-form-label query-keyword width-9">Subscription</label> <label class="gf-form-label query-keyword width-9">Subscription</label>
<gf-form-dropdown model="ctrl.target.subscription" allow-custom="true" lookup-text="true" <gf-form-dropdown
get-options="ctrl.getSubscriptions()" on-change="ctrl.onSubscriptionChange()" css-class="min-width-12"> model="ctrl.target.subscription"
allow-custom="true"
lookup-text="true"
get-options="ctrl.getSubscriptions()"
on-change="ctrl.onSubscriptionChange()"
css-class="min-width-12"
>
</gf-form-dropdown> </gf-form-dropdown>
</div> </div>
<div class="gf-form gf-form--grow"> <div class="gf-form gf-form--grow">
@ -21,20 +38,38 @@
<div class="gf-form-inline"> <div class="gf-form-inline">
<div class="gf-form"> <div class="gf-form">
<label class="gf-form-label query-keyword width-9">Resource Group</label> <label class="gf-form-label query-keyword width-9">Resource Group</label>
<gf-form-dropdown model="ctrl.target.azureMonitor.resourceGroup" allow-custom="true" lookup-text="true" <gf-form-dropdown
get-options="ctrl.getResourceGroups($query)" on-change="ctrl.onResourceGroupChange()" css-class="min-width-12"> model="ctrl.target.azureMonitor.resourceGroup"
allow-custom="true"
lookup-text="true"
get-options="ctrl.getResourceGroups($query)"
on-change="ctrl.onResourceGroupChange()"
css-class="min-width-12"
>
</gf-form-dropdown> </gf-form-dropdown>
</div> </div>
<div class="gf-form"> <div class="gf-form">
<label class="gf-form-label query-keyword width-9">Namespace</label> <label class="gf-form-label query-keyword width-9">Namespace</label>
<gf-form-dropdown model="ctrl.target.azureMonitor.metricDefinition" allow-custom="true" lookup-text="true" <gf-form-dropdown
get-options="ctrl.getMetricDefinitions($query)" on-change="ctrl.onMetricDefinitionChange()" css-class="min-width-20"> model="ctrl.target.azureMonitor.metricDefinition"
allow-custom="true"
lookup-text="true"
get-options="ctrl.getMetricDefinitions($query)"
on-change="ctrl.onMetricDefinitionChange()"
css-class="min-width-20"
>
</gf-form-dropdown> </gf-form-dropdown>
</div> </div>
<div class="gf-form"> <div class="gf-form">
<label class="gf-form-label query-keyword width-9">Resource Name</label> <label class="gf-form-label query-keyword width-9">Resource Name</label>
<gf-form-dropdown model="ctrl.target.azureMonitor.resourceName" allow-custom="true" lookup-text="true" <gf-form-dropdown
get-options="ctrl.getResourceNames($query)" on-change="ctrl.onResourceNameChange()" css-class="min-width-12"> model="ctrl.target.azureMonitor.resourceName"
allow-custom="true"
lookup-text="true"
get-options="ctrl.getResourceNames($query)"
on-change="ctrl.onResourceNameChange()"
css-class="min-width-12"
>
</gf-form-dropdown> </gf-form-dropdown>
</div> </div>
<div class="gf-form gf-form--grow"> <div class="gf-form gf-form--grow">
@ -44,21 +79,37 @@
<div class="gf-form-inline"> <div class="gf-form-inline">
<div class="gf-form"> <div class="gf-form">
<label class="gf-form-label query-keyword width-9">Metric Namespace</label> <label class="gf-form-label query-keyword width-9">Metric Namespace</label>
<gf-form-dropdown model="ctrl.target.azureMonitor.metricNamespace" allow-custom="true" lookup-text="true" <gf-form-dropdown
get-options="ctrl.getMetricNamespaces($query)" on-change="ctrl.onMetricNamespacesChange()" css-class="min-width-12"> model="ctrl.target.azureMonitor.metricNamespace"
allow-custom="true"
lookup-text="true"
get-options="ctrl.getMetricNamespaces($query)"
on-change="ctrl.onMetricNamespacesChange()"
css-class="min-width-12"
>
</gf-form-dropdown> </gf-form-dropdown>
</div> </div>
<div class="gf-form"> <div class="gf-form">
<label class="gf-form-label query-keyword width-9">Metric</label> <label class="gf-form-label query-keyword width-9">Metric</label>
<gf-form-dropdown model="ctrl.target.azureMonitor.metricName" allow-custom="true" lookup-text="true" <gf-form-dropdown
get-options="ctrl.getMetricNames($query)" on-change="ctrl.onMetricNameChange()" css-class="min-width-12"> model="ctrl.target.azureMonitor.metricName"
allow-custom="true"
lookup-text="true"
get-options="ctrl.getMetricNames($query)"
on-change="ctrl.onMetricNameChange()"
css-class="min-width-12"
>
</gf-form-dropdown> </gf-form-dropdown>
</div> </div>
<div class="gf-form gf-form--grow aggregation-dropdown-wrapper"> <div class="gf-form gf-form--grow aggregation-dropdown-wrapper">
<label class="gf-form-label query-keyword width-9">Aggregation</label> <label class="gf-form-label query-keyword width-9">Aggregation</label>
<div class="gf-form-select-wrapper gf-form-select-wrapper--caret-indent"> <div class="gf-form-select-wrapper gf-form-select-wrapper--caret-indent">
<select class="gf-form-input width-11" ng-model="ctrl.target.azureMonitor.aggregation" ng-options="f as f for f in ctrl.target.azureMonitor.aggOptions" <select
ng-change="ctrl.refresh()"></select> class="gf-form-input width-11"
ng-model="ctrl.target.azureMonitor.aggregation"
ng-options="f as f for f in ctrl.target.azureMonitor.aggOptions"
ng-change="ctrl.refresh()"
></select>
</div> </div>
</div> </div>
</div> </div>
@ -66,8 +117,12 @@
<div class="gf-form"> <div class="gf-form">
<label class="gf-form-label query-keyword width-9">Time Grain</label> <label class="gf-form-label query-keyword width-9">Time Grain</label>
<div class="gf-form-select-wrapper gf-form-select-wrapper--caret-indent timegrainunit-dropdown-wrapper"> <div class="gf-form-select-wrapper gf-form-select-wrapper--caret-indent timegrainunit-dropdown-wrapper">
<select class="gf-form-input" ng-model="ctrl.target.azureMonitor.timeGrain" ng-options="f.value as f.text for f in ctrl.target.azureMonitor.timeGrains" <select
ng-change="ctrl.refresh()"></select> class="gf-form-input"
ng-model="ctrl.target.azureMonitor.timeGrain"
ng-options="f.value as f.text for f in ctrl.target.azureMonitor.timeGrains"
ng-change="ctrl.refresh()"
></select>
</div> </div>
</div> </div>
<div class="gf-form" ng-show="ctrl.target.azureMonitor.timeGrain.trim() === 'auto'"> <div class="gf-form" ng-show="ctrl.target.azureMonitor.timeGrain.trim() === 'auto'">
@ -82,19 +137,35 @@
<div class="gf-form"> <div class="gf-form">
<label class="gf-form-label query-keyword width-9">Dimension</label> <label class="gf-form-label query-keyword width-9">Dimension</label>
<div class="gf-form-select-wrapper gf-form-select-wrapper--caret-indent"> <div class="gf-form-select-wrapper gf-form-select-wrapper--caret-indent">
<select class="gf-form-input min-width-12" ng-model="ctrl.target.azureMonitor.dimension" ng-options="f.value as f.text for f in ctrl.target.azureMonitor.dimensions" <select
ng-change="ctrl.refresh()"></select> class="gf-form-input min-width-12"
ng-model="ctrl.target.azureMonitor.dimension"
ng-options="f.value as f.text for f in ctrl.target.azureMonitor.dimensions"
ng-change="ctrl.refresh()"
></select>
</div> </div>
</div> </div>
<div class="gf-form"> <div class="gf-form">
<label class="gf-form-label query-keyword width-3">eq</label> <label class="gf-form-label query-keyword width-3">eq</label>
<input type="text" class="gf-form-input width-17" ng-model="ctrl.target.azureMonitor.dimensionFilter" <input
spellcheck="false" placeholder="auto" ng-blur="ctrl.refresh()"> type="text"
class="gf-form-input width-17"
ng-model="ctrl.target.azureMonitor.dimensionFilter"
spellcheck="false"
placeholder="auto"
ng-blur="ctrl.refresh()"
/>
</div> </div>
<div class="gf-form"> <div class="gf-form">
<label class="gf-form-label query-keyword width-9">Top</label> <label class="gf-form-label query-keyword width-9">Top</label>
<input type="text" class="gf-form-input width-3" ng-model="ctrl.target.azureMonitor.top" <input
spellcheck="false" placeholder="10" ng-blur="ctrl.refresh()"> type="text"
class="gf-form-input width-3"
ng-model="ctrl.target.azureMonitor.top"
spellcheck="false"
placeholder="10"
ng-blur="ctrl.refresh()"
/>
</div> </div>
<div class="gf-form gf-form--grow"> <div class="gf-form gf-form--grow">
<div class="gf-form-label gf-form-label--grow"></div> <div class="gf-form-label gf-form-label--grow"></div>
@ -103,8 +174,14 @@
<div class="gf-form-inline"> <div class="gf-form-inline">
<div class="gf-form"> <div class="gf-form">
<label class="gf-form-label query-keyword width-9">Legend Format</label> <label class="gf-form-label query-keyword width-9">Legend Format</label>
<input type="text" class="gf-form-input width-30" ng-model="ctrl.target.azureMonitor.alias" spellcheck="false" <input
placeholder="alias patterns (see help for more info)" ng-blur="ctrl.refresh()"> type="text"
class="gf-form-input width-30"
ng-model="ctrl.target.azureMonitor.alias"
spellcheck="false"
placeholder="alias patterns (see help for more info)"
ng-blur="ctrl.refresh()"
/>
</div> </div>
<div class="gf-form gf-form--grow"> <div class="gf-form gf-form--grow">
@ -117,8 +194,14 @@
<div class="gf-form-inline"> <div class="gf-form-inline">
<div class="gf-form"> <div class="gf-form">
<label class="gf-form-label query-keyword width-9">Workspace</label> <label class="gf-form-label query-keyword width-9">Workspace</label>
<gf-form-dropdown model="ctrl.target.azureLogAnalytics.workspace" allow-custom="true" lookup-text="true" <gf-form-dropdown
get-options="ctrl.getWorkspaces()" on-change="ctrl.refresh()" css-class="min-width-12"> model="ctrl.target.azureLogAnalytics.workspace"
allow-custom="true"
lookup-text="true"
get-options="ctrl.getWorkspaces()"
on-change="ctrl.refresh()"
css-class="min-width-12"
>
</gf-form-dropdown> </gf-form-dropdown>
<div class="gf-form"> <div class="gf-form">
<div class="width-1"></div> <div class="width-1"></div>
@ -150,8 +233,12 @@
<div class="gf-form"> <div class="gf-form">
<label class="gf-form-label query-keyword width-7">Format As</label> <label class="gf-form-label query-keyword width-7">Format As</label>
<div class="gf-form-select-wrapper"> <div class="gf-form-select-wrapper">
<select class="gf-form-input gf-size-auto" ng-model="ctrl.target.azureLogAnalytics.resultFormat" ng-options="f.value as f.text for f in ctrl.resultFormats" <select
ng-change="ctrl.refresh()"></select> class="gf-form-input gf-size-auto"
ng-model="ctrl.target.azureLogAnalytics.resultFormat"
ng-options="f.value as f.text for f in ctrl.resultFormats"
ng-change="ctrl.refresh()"
></select>
</div> </div>
</div> </div>
<div class="gf-form"> <div class="gf-form">
@ -211,7 +298,6 @@
- | summarize count() by Category, bin(TimeGenerated, $__interval) - | summarize count() by Category, bin(TimeGenerated, $__interval)
</pre> </pre>
</div> </div>
</div> </div>
<div ng-if="ctrl.target.queryType === 'Application Insights'"> <div ng-if="ctrl.target.queryType === 'Application Insights'">
@ -219,16 +305,25 @@
<div class="gf-form-inline"> <div class="gf-form-inline">
<div class="gf-form"> <div class="gf-form">
<label class="gf-form-label query-keyword width-9">Metric</label> <label class="gf-form-label query-keyword width-9">Metric</label>
<gf-form-dropdown model="ctrl.target.appInsights.metricName" allow-custom="true" lookup-text="true" <gf-form-dropdown
get-options="ctrl.getAppInsightsMetricNames($query)" on-change="ctrl.onAppInsightsMetricNameChange()" model="ctrl.target.appInsights.metricName"
css-class="min-width-20"> allow-custom="true"
lookup-text="true"
get-options="ctrl.getAppInsightsMetricNames($query)"
on-change="ctrl.onAppInsightsMetricNameChange()"
css-class="min-width-20"
>
</gf-form-dropdown> </gf-form-dropdown>
</div> </div>
<div class="gf-form"> <div class="gf-form">
<label class="gf-form-label query-keyword width-9">Aggregation</label> <label class="gf-form-label query-keyword width-9">Aggregation</label>
<div class="gf-form-select-wrapper gf-form-select-wrapper--caret-indent"> <div class="gf-form-select-wrapper gf-form-select-wrapper--caret-indent">
<select class="gf-form-input" ng-model="ctrl.target.appInsights.aggregation" ng-options="f as f for f in ctrl.target.appInsights.aggOptions" <select
ng-change="ctrl.refresh()"></select> class="gf-form-input"
ng-model="ctrl.target.appInsights.aggregation"
ng-options="f as f for f in ctrl.target.appInsights.aggOptions"
ng-change="ctrl.refresh()"
></select>
</div> </div>
</div> </div>
<div class="gf-form gf-form--grow"> <div class="gf-form gf-form--grow">
@ -238,20 +333,35 @@
<div class="gf-form-inline"> <div class="gf-form-inline">
<div class="gf-form"> <div class="gf-form">
<label class="gf-form-label query-keyword width-9">Group By</label> <label class="gf-form-label query-keyword width-9">Group By</label>
<gf-form-dropdown allow-custom="true" ng-hide="ctrl.target.appInsights.dimension !== 'none'" model="ctrl.target.appInsights.dimension" <gf-form-dropdown
lookup-text="true" get-options="ctrl.getAppInsightsGroupBySegments($query)" on-change="ctrl.refresh()" allow-custom="true"
css-class="min-width-20"> ng-hide="ctrl.target.appInsights.dimension !== 'none'"
model="ctrl.target.appInsights.dimension"
lookup-text="true"
get-options="ctrl.getAppInsightsGroupBySegments($query)"
on-change="ctrl.refresh()"
css-class="min-width-20"
>
</gf-form-dropdown> </gf-form-dropdown>
<label class="gf-form-label min-width-20 pointer" ng-hide="ctrl.target.appInsights.dimension === 'none'" <label
ng-click="ctrl.resetAppInsightsGroupBy()">{{ctrl.target.appInsights.dimension}} class="gf-form-label min-width-20 pointer"
<i class="fa fa-remove"></i> ng-hide="ctrl.target.appInsights.dimension === 'none'"
ng-click="ctrl.resetAppInsightsGroupBy()"
>{{ctrl.target.appInsights.dimension}}
<icon name="'times'"></icon>
</label> </label>
</div> </div>
<div class="gf-form-inline"> <div class="gf-form-inline">
<div class="gf-form"> <div class="gf-form">
<label class="gf-form-label query-keyword width-9">Filter</label> <label class="gf-form-label query-keyword width-9">Filter</label>
<input type="text" class="gf-form-input width-17" ng-model="ctrl.target.appInsights.dimensionFilter" spellcheck="false" <input
placeholder="your/groupby eq 'a_value'" ng-blur="ctrl.refresh()"> type="text"
class="gf-form-input width-17"
ng-model="ctrl.target.appInsights.dimensionFilter"
spellcheck="false"
placeholder="your/groupby eq 'a_value'"
ng-blur="ctrl.refresh()"
/>
</div> </div>
</div> </div>
<div class="gf-form gf-form--grow"> <div class="gf-form gf-form--grow">
@ -262,22 +372,42 @@
<div class="gf-form"> <div class="gf-form">
<label class="gf-form-label query-keyword width-9">Time Grain</label> <label class="gf-form-label query-keyword width-9">Time Grain</label>
<div class="gf-form-select-wrapper gf-form-select-wrapper--caret-indent"> <div class="gf-form-select-wrapper gf-form-select-wrapper--caret-indent">
<select class="gf-form-input" ng-model="ctrl.target.appInsights.timeGrainType" ng-options="f as f for f in ['auto', 'none', 'specific']" <select
ng-change="ctrl.updateTimeGrainType()"></select> class="gf-form-input"
ng-model="ctrl.target.appInsights.timeGrainType"
ng-options="f as f for f in ['auto', 'none', 'specific']"
ng-change="ctrl.updateTimeGrainType()"
></select>
</div> </div>
</div> </div>
<div class="gf-form" ng-hide="ctrl.target.appInsights.timeGrainType === 'auto' || ctrl.target.appInsights.timeGrainType === 'none'"> <div
<input type="text" class="gf-form-input width-3" ng-model="ctrl.target.appInsights.timeGrainCount" spellcheck="false" class="gf-form"
placeholder="" ng-blur="ctrl.updateAppInsightsTimeGrain()"> ng-hide="ctrl.target.appInsights.timeGrainType === 'auto' || ctrl.target.appInsights.timeGrainType === 'none'"
>
<input
type="text"
class="gf-form-input width-3"
ng-model="ctrl.target.appInsights.timeGrainCount"
spellcheck="false"
placeholder=""
ng-blur="ctrl.updateAppInsightsTimeGrain()"
/>
</div> </div>
<div class="gf-form" ng-hide="ctrl.target.appInsights.timeGrainType === 'auto' || ctrl.target.appInsights.timeGrainType === 'none'"> <div
<div class="gf-form-select-wrapper gf-form-select-wrapper--caret-indent timegrainunit-dropdown-wrapper"> class="gf-form"
<select class="gf-form-input" ng-model="ctrl.target.appInsights.timeGrainUnit" ng-options="f as f for f in ['minute', 'hour', 'day', 'month', 'year']" ng-hide="ctrl.target.appInsights.timeGrainType === 'auto' || ctrl.target.appInsights.timeGrainType === 'none'"
ng-change="ctrl.updateAppInsightsTimeGrain()"></select> >
</div> <div class="gf-form-select-wrapper gf-form-select-wrapper--caret-indent timegrainunit-dropdown-wrapper">
<select
class="gf-form-input"
ng-model="ctrl.target.appInsights.timeGrainUnit"
ng-options="f as f for f in ['minute', 'hour', 'day', 'month', 'year']"
ng-change="ctrl.updateAppInsightsTimeGrain()"
></select>
</div> </div>
</div>
<div class="gf-form" ng-hide="ctrl.target.appInsights.timeGrainType !== 'auto'"> <div class="gf-form" ng-hide="ctrl.target.appInsights.timeGrainType !== 'auto'">
<label class="gf-form-label">Auto Interval</label> <label class="gf-form-label">Auto Interval</label>
<label class="gf-form-label">{{ctrl.getAppInsightsAutoInterval()}}</label> <label class="gf-form-label">{{ctrl.getAppInsightsAutoInterval()}}</label>
</div> </div>
<div class="gf-form gf-form--grow"> <div class="gf-form gf-form--grow">
@ -287,8 +417,14 @@
<div class="gf-form-inline"> <div class="gf-form-inline">
<div class="gf-form"> <div class="gf-form">
<label class="gf-form-label query-keyword width-9">Legend Format</label> <label class="gf-form-label query-keyword width-9">Legend Format</label>
<input type="text" class="gf-form-input width-30" ng-model="ctrl.target.appInsights.alias" spellcheck="false" <input
placeholder="alias patterns (see help for more info)" ng-blur="ctrl.refresh()"> type="text"
class="gf-form-input width-30"
ng-model="ctrl.target.appInsights.alias"
spellcheck="false"
placeholder="alias patterns (see help for more info)"
ng-blur="ctrl.refresh()"
/>
</div> </div>
</div> </div>
<div class="gf-form gf-form--grow"> <div class="gf-form gf-form--grow">
@ -314,14 +450,25 @@
<div class="gf-form-inline"> <div class="gf-form-inline">
<div class="gf-form"> <div class="gf-form">
<label class="gf-form-label query-keyword width-9">X-axis</label> <label class="gf-form-label query-keyword width-9">X-axis</label>
<gf-form-dropdown model="ctrl.target.appInsights.timeColumn" allow-custom="true" placeholder="eg. 'timestamp'" <gf-form-dropdown
get-options="ctrl.getAppInsightsColumns($query)" on-change="ctrl.onAppInsightsColumnChange()" css-class="min-width-20"> model="ctrl.target.appInsights.timeColumn"
allow-custom="true"
placeholder="eg. 'timestamp'"
get-options="ctrl.getAppInsightsColumns($query)"
on-change="ctrl.onAppInsightsColumnChange()"
css-class="min-width-20"
>
</gf-form-dropdown> </gf-form-dropdown>
</div> </div>
<div class="gf-form"> <div class="gf-form">
<label class="gf-form-label query-keyword width-9">Y-axis</label> <label class="gf-form-label query-keyword width-9">Y-axis</label>
<gf-form-dropdown model="ctrl.target.appInsights.valueColumn" allow-custom="true" get-options="ctrl.getAppInsightsColumns($query)" <gf-form-dropdown
on-change="ctrl.onAppInsightsColumnChange()" css-class="min-width-20"> model="ctrl.target.appInsights.valueColumn"
allow-custom="true"
get-options="ctrl.getAppInsightsColumns($query)"
on-change="ctrl.onAppInsightsColumnChange()"
css-class="min-width-20"
>
</gf-form-dropdown> </gf-form-dropdown>
</div> </div>
<div class="gf-form gf-form--grow"> <div class="gf-form gf-form--grow">
@ -331,8 +478,13 @@
<div class="gf-form-inline"> <div class="gf-form-inline">
<div class="gf-form"> <div class="gf-form">
<label class="gf-form-label query-keyword width-9">Split On</label> <label class="gf-form-label query-keyword width-9">Split On</label>
<gf-form-dropdown model="ctrl.target.appInsights.segmentColumn" allow-custom="true" get-options="ctrl.getAppInsightsColumns($query)" <gf-form-dropdown
on-change="ctrl.onAppInsightsColumnChange()" css-class="min-width-20"> model="ctrl.target.appInsights.segmentColumn"
allow-custom="true"
get-options="ctrl.getAppInsightsColumns($query)"
on-change="ctrl.onAppInsightsColumnChange()"
css-class="min-width-20"
>
</gf-form-dropdown> </gf-form-dropdown>
</div> </div>
<div class="gf-form gf-form--grow"> <div class="gf-form gf-form--grow">

View File

@ -1,4 +1,5 @@
import React from 'react'; import React from 'react';
import { Icon } from '@grafana/ui';
export interface FunctionDescriptor { export interface FunctionDescriptor {
text: string; text: string;
@ -21,12 +22,13 @@ export interface FunctionEditorControlsProps {
const FunctionHelpButton = (props: { description: string; name: string; onDescriptionShow: () => void }) => { const FunctionHelpButton = (props: { description: string; name: string; onDescriptionShow: () => void }) => {
if (props.description) { if (props.description) {
return <span className="pointer fa fa-question-circle" onClick={props.onDescriptionShow} />; return <Icon className="pointer" name="question-circle" onClick={props.onDescriptionShow} />;
} }
return ( return (
<span <Icon
className="pointer fa fa-question-circle" className="pointer"
name="question-circle"
onClick={() => { onClick={() => {
window.open( window.open(
'http://graphite.readthedocs.org/en/latest/functions.html#graphite.render.functions.' + props.name, 'http://graphite.readthedocs.org/en/latest/functions.html#graphite.render.functions.' + props.name,
@ -58,8 +60,8 @@ export const FunctionEditorControls = (
description={func.def.description} description={func.def.description}
onDescriptionShow={onDescriptionShow} onDescriptionShow={onDescriptionShow}
/> />
<span className="pointer fa fa-remove" onClick={() => onRemove(func)} /> <Icon name="times" onClick={() => onRemove(func)} />
<span className="pointer fa fa-arrow-right" onClick={() => onMoveRight(func)} /> <Icon name="arrow-right" onClick={() => onMoveRight(func)} />
</div> </div>
); );
}; };

View File

@ -135,7 +135,7 @@
<div class="gf-form"> <div class="gf-form">
<label class="gf-form-label query-keyword width-7">ORDER BY</label> <label class="gf-form-label query-keyword width-7">ORDER BY</label>
<label class="gf-form-label pointer" ng-click="ctrl.removeOrderByTime()" <label class="gf-form-label pointer" ng-click="ctrl.removeOrderByTime()"
>time <span class="query-keyword">DESC</span> <i class="fa fa-remove"></i >time <span class="query-keyword">DESC</span> <icon name="'times'"></icon
></label> ></label>
</div> </div>
<div class="gf-form gf-form--grow"> <div class="gf-form gf-form--grow">

View File

@ -70,7 +70,7 @@ export const DerivedFields = (props: Props) => {
className={css` className={css`
margin-right: 10px; margin-right: 10px;
`} `}
icon="plus" icon="plus-circle"
onClick={event => { onClick={event => {
event.preventDefault(); event.preventDefault();
const newDerivedFields = [...(value || []), { name: '', matcherRegex: '' }]; const newDerivedFields = [...(value || []), { name: '', matcherRegex: '' }];

View File

@ -107,7 +107,7 @@
<i class="fa fa-pencil"></i> <i class="fa fa-pencil"></i>
</a> </a>
<a ng-click="ctrl.removeFilter($index)"> <a ng-click="ctrl.removeFilter($index)">
<i class="fa fa-remove"></i> <icon name="'times'"></icon>
</a> </a>
</div> </div>
<label class="gf-form-label query-keyword" ng-hide="ctrl.addFilterMode"> <label class="gf-form-label query-keyword" ng-hide="ctrl.addFilterMode">
@ -154,7 +154,7 @@
<label class="gf-form-label"> <label class="gf-form-label">
<a ng-click="ctrl.addFilter()" ng-hide="ctrl.errors.filters">add filter</a> <a ng-click="ctrl.addFilter()" ng-hide="ctrl.errors.filters">add filter</a>
<a ng-click="ctrl.closeAddFilterMode()"> <a ng-click="ctrl.closeAddFilterMode()">
<i class="fa fa-remove"></i> <icon name="'times'"></icon>
</a> </a>
</label> </label>
</div> </div>
@ -183,7 +183,7 @@
<i class="fa fa-pencil"></i> <i class="fa fa-pencil"></i>
</a> </a>
<a ng-click="ctrl.removeTag(key)"> <a ng-click="ctrl.removeTag(key)">
<i class="fa fa-remove"></i> <icon name="'times'"></icon>
</a> </a>
</label> </label>
</div> </div>
@ -213,7 +213,7 @@
</label> </label>
<label class="gf-form-label" > <label class="gf-form-label" >
<a ng-click="ctrl.addTag()" ng-hide="ctrl.errors.tags">add tag</a> <a ng-click="ctrl.addTag()" ng-hide="ctrl.errors.tags">add tag</a>
<a ng-click="ctrl.closeAddTagMode()"><i class="fa fa-remove"></i></a> <a ng-click="ctrl.closeAddTagMode()"><icon name="'times'"></icon></a>
</label> </label>
</div> </div>

View File

@ -13,7 +13,7 @@ export interface Props {
} }
const removeText = '-- remove filter --'; const removeText = '-- remove filter --';
const removeOption: SelectableValue<string> = { label: removeText, value: removeText, icon: 'fa fa-remove' }; const removeOption: SelectableValue<string> = { label: removeText, value: removeText, icon: 'times' };
const operators = ['=', '!=', '=~', '!=~']; const operators = ['=', '!=', '=~', '!=~'];
export const LabelFilter: FunctionComponent<Props> = ({ export const LabelFilter: FunctionComponent<Props> = ({

View File

@ -2,6 +2,7 @@
import React, { PureComponent } from 'react'; import React, { PureComponent } from 'react';
import { PanelProps } from '@grafana/data'; import { PanelProps } from '@grafana/data';
import { Icon } from '@grafana/ui';
import { getDatasourceSrv } from 'app/features/plugins/datasource_srv'; import { getDatasourceSrv } from 'app/features/plugins/datasource_srv';
import { backendSrv } from 'app/core/services/backend_srv'; import { backendSrv } from 'app/core/services/backend_srv';
import { contextSrv } from 'app/core/core'; import { contextSrv } from 'app/core/core';
@ -144,7 +145,7 @@ export class GettingStarted extends PureComponent<PanelProps, State> {
return ( return (
<div className="progress-tracker-container"> <div className="progress-tracker-container">
<button className="progress-tracker-close-btn" onClick={this.dismiss}> <button className="progress-tracker-close-btn" onClick={this.dismiss}>
<i className="fa fa-remove" /> <Icon name="times" />
</button> </button>
<div className="progress-tracker"> <div className="progress-tracker">
{this.steps.map((step, index) => { {this.steps.map((step, index) => {

View File

@ -1,13 +1,17 @@
<div class="editor-row"> <div class="editor-row">
<div class="gf-form-group"> <div class="gf-form-group">
<div class="gf-form"> <div class="gf-form">
<span class="gf-form-label"> <span class="gf-form-label">
Type Type
</span> </span>
<div class="gf-form-select-wrapper"> <div class="gf-form-select-wrapper">
<select class="gf-form-input" ng-model="ctrl.panel.mappingType" <select
ng-options="f.value as f.name for f in ctrl.panel.mappingTypes" ng-change="ctrl.refresh()"></select> class="gf-form-input"
</div> ng-model="ctrl.panel.mappingType"
ng-options="f.value as f.name for f in ctrl.panel.mappingTypes"
ng-change="ctrl.refresh()"
></select>
</div>
</div> </div>
</div> </div>
</div> </div>
@ -16,13 +20,25 @@
<div class="gf-form-group"> <div class="gf-form-group">
<div class="gf-form" ng-repeat="map in ctrl.panel.valueMaps"> <div class="gf-form" ng-repeat="map in ctrl.panel.valueMaps">
<span class="gf-form-label"> <span class="gf-form-label">
<i class="fa fa-remove pointer" ng-click="ctrl.removeValueMap(map)"></i> <icon class="'times'" ng-click="ctrl.removeValueMap(map)"></icon>
</span> </span>
<input type="text" ng-model="map.value" placeholder="value" class="gf-form-input max-width-6" ng-blur="ctrl.refresh()"> <input
type="text"
ng-model="map.value"
placeholder="value"
class="gf-form-input max-width-6"
ng-blur="ctrl.refresh()"
/>
<span class="gf-form-label"> <span class="gf-form-label">
<i class="fa fa-arrow-right"></i> <icon name="'arrow-right'"></icon>
</span> </span>
<input type="text" placeholder="text" ng-model="map.text" class="gf-form-input max-width-8" ng-blur="ctrl.refresh()"> <input
type="text"
placeholder="text"
ng-model="map.text"
class="gf-form-input max-width-8"
ng-blur="ctrl.refresh()"
/>
</div> </div>
<div class="gf-form-button-row"> <div class="gf-form-button-row">
@ -37,15 +53,15 @@
<h5 class="section-heading">Set range mappings</h5> <h5 class="section-heading">Set range mappings</h5>
<div class="gf-form-group"> <div class="gf-form-group">
<div class="gf-form" ng-repeat="rangeMap in ctrl.panel.rangeMaps"> <div class="gf-form" ng-repeat="rangeMap in ctrl.panel.rangeMaps">
<span class="gf-form-label"> <span class="gf-form-label">
<i class="fa fa-remove pointer" ng-click="ctrl.removeRangeMap(rangeMap)"></i> <icon name="'times'" ng-click="ctrl.removeRangeMap(rangeMap)"></icon>
</span> </span>
<span class="gf-form-label">From</span> <span class="gf-form-label">From</span>
<input type="text" ng-model="rangeMap.from" class="gf-form-input max-width-6" ng-blur="ctrl.refresh()"> <input type="text" ng-model="rangeMap.from" class="gf-form-input max-width-6" ng-blur="ctrl.refresh()" />
<span class="gf-form-label">To</span> <span class="gf-form-label">To</span>
<input type="text" ng-model="rangeMap.to" class="gf-form-input max-width-6" ng-blur="ctrl.refresh()"> <input type="text" ng-model="rangeMap.to" class="gf-form-input max-width-6" ng-blur="ctrl.refresh()" />
<span class="gf-form-label">Text</span> <span class="gf-form-label">Text</span>
<input type="text" ng-model="rangeMap.text" class="gf-form-input max-width-8" ng-blur="ctrl.refresh()"> <input type="text" ng-model="rangeMap.text" class="gf-form-input max-width-8" ng-blur="ctrl.refresh()" />
</div> </div>
<div class="gf-form-button-row"> <div class="gf-form-button-row">

View File

@ -4,16 +4,39 @@
<div class="gf-form-inline"> <div class="gf-form-inline">
<div class="gf-form"> <div class="gf-form">
<label class="gf-form-label width-12">Apply to columns named</label> <label class="gf-form-label width-12">Apply to columns named</label>
<input type="text" placeholder="Name or regex" class="gf-form-input width-13" ng-model="style.pattern" bs-tooltip="'Specify regex using /my.*regex/ syntax'" <input
bs-typeahead="editor.getColumnNames" ng-blur="editor.render()" data-min-length=0 data-items=100 ng-model-onblur type="text"
data-placement="right"> placeholder="Name or regex"
class="gf-form-input width-13"
ng-model="style.pattern"
bs-tooltip="'Specify regex using /my.*regex/ syntax'"
bs-typeahead="editor.getColumnNames"
ng-blur="editor.render()"
data-min-length="0"
data-items="100"
ng-model-onblur
data-placement="right"
/>
</div> </div>
</div> </div>
<div class="gf-form" ng-if="style.type !== 'hidden'"> <div class="gf-form" ng-if="style.type !== 'hidden'">
<label class="gf-form-label width-12">Column Header</label> <label class="gf-form-label width-12">Column Header</label>
<input type="text" class="gf-form-input width-12" ng-model="style.alias" ng-change="editor.render()" ng-model-onblur placeholder="Override header label"> <input
type="text"
class="gf-form-input width-12"
ng-model="style.alias"
ng-change="editor.render()"
ng-model-onblur
placeholder="Override header label"
/>
</div> </div>
<gf-form-switch class="gf-form" label-class="width-12" label="Render value as link" checked="style.link" on-change="editor.render()"></gf-form-switch> <gf-form-switch
class="gf-form"
label-class="width-12"
label="Render value as link"
checked="style.link"
on-change="editor.render()"
></gf-form-switch>
</div> </div>
<div class="section gf-form-group"> <div class="section gf-form-group">
@ -22,37 +45,62 @@
<div class="gf-form"> <div class="gf-form">
<label class="gf-form-label width-10">Type</label> <label class="gf-form-label width-10">Type</label>
<div class="gf-form-select-wrapper width-16"> <div class="gf-form-select-wrapper width-16">
<select class="gf-form-input" ng-model="style.type" ng-options="c.value as c.text for c in editor.columnTypes" ng-change="editor.render()"></select> <select
class="gf-form-input"
ng-model="style.type"
ng-options="c.value as c.text for c in editor.columnTypes"
ng-change="editor.render()"
></select>
</div> </div>
</div> </div>
<div class="gf-form" ng-if="style.type === 'date'"> <div class="gf-form" ng-if="style.type === 'date'">
<label class="gf-form-label width-10">Date Format</label> <label class="gf-form-label width-10">Date Format</label>
<gf-form-dropdown model="style.dateFormat" css-class="gf-form-input width-16" lookup-text="true" <gf-form-dropdown
get-options="editor.dateFormats" on-change="editor.render()" allow-custom="true"> model="style.dateFormat"
css-class="gf-form-input width-16"
lookup-text="true"
get-options="editor.dateFormats"
on-change="editor.render()"
allow-custom="true"
>
</gf-form-dropdown> </gf-form-dropdown>
</div> </div>
<div ng-if="style.type === 'string'"> <div ng-if="style.type === 'string'">
<gf-form-switch class="gf-form" label-class="width-10" ng-if="style.type === 'string'" label="Sanitize HTML" checked="style.sanitize" <gf-form-switch
on-change="editor.render()"></gf-form-switch> class="gf-form"
label-class="width-10"
ng-if="style.type === 'string'"
label="Sanitize HTML"
checked="style.sanitize"
on-change="editor.render()"
></gf-form-switch>
</div> </div>
<div ng-if="style.type !== 'hidden'"> <div ng-if="style.type !== 'hidden'">
<div class="gf-form"> <div class="gf-form">
<label class="gf-form-label width-10">Align</label> <label class="gf-form-label width-10">Align</label>
<gf-form-dropdown model="style.align" <gf-form-dropdown
css-class="gf-form-input width-16" model="style.align"
lookup-text="true" css-class="gf-form-input width-16"
get-options="editor.alignTypes" lookup-text="true"
allow-custom="false" get-options="editor.alignTypes"
on-change="editor.render()" allow-custom="false"
allow-custom="false"/> on-change="editor.render()"
allow-custom="false"
/>
</div> </div>
</div> </div>
<div ng-if="style.type === 'string'"> <div ng-if="style.type === 'string'">
<gf-form-switch class="gf-form" label-class="width-10" ng-if="style.type === 'string'" label="Preserve Formatting" checked="style.preserveFormat" <gf-form-switch
on-change="editor.render()"></gf-form-switch> class="gf-form"
label-class="width-10"
ng-if="style.type === 'string'"
label="Preserve Formatting"
checked="style.preserveFormat"
on-change="editor.render()"
></gf-form-switch>
</div> </div>
<div ng-if="style.type === 'number'"> <div ng-if="style.type === 'number'">
@ -62,8 +110,14 @@
</div> </div>
<div class="gf-form"> <div class="gf-form">
<label class="gf-form-label width-10">Decimals</label> <label class="gf-form-label width-10">Decimals</label>
<input type="number" class="gf-form-input width-4" data-placement="right" ng-model="style.decimals" ng-change="editor.render()" <input
ng-model-onblur> type="number"
class="gf-form-input width-4"
data-placement="right"
ng-model="style.decimals"
ng-change="editor.render()"
ng-model-onblur
/>
</div> </div>
</div> </div>
</div> </div>
@ -77,20 +131,36 @@
Type Type
</span> </span>
<div class="gf-form-select-wrapper"> <div class="gf-form-select-wrapper">
<select class="gf-form-input" ng-model="style.mappingType" <select
ng-options="c.value as c.text for c in editor.mappingTypes" ng-change="editor.render()"></select> class="gf-form-input"
ng-model="style.mappingType"
ng-options="c.value as c.text for c in editor.mappingTypes"
ng-change="editor.render()"
></select>
</div> </div>
</div> </div>
<div class="gf-form-group" ng-if="style.mappingType==1"> <div class="gf-form-group" ng-if="style.mappingType==1">
<div class="gf-form" ng-repeat="map in style.valueMaps"> <div class="gf-form" ng-repeat="map in style.valueMaps">
<span class="gf-form-label"> <span class="gf-form-label">
<i class="fa fa-remove pointer" ng-click="editor.removeValueMap(style, $index)"></i> <icon name="'times'" ng-click="editor.removeValueMap(style, $index)"></icon>
</span> </span>
<input type="text" class="gf-form-input max-width-6" ng-model="map.value" placeholder="Value" ng-blur="editor.render()"> <input
type="text"
class="gf-form-input max-width-6"
ng-model="map.value"
placeholder="Value"
ng-blur="editor.render()"
/>
<label class="gf-form-label"> <label class="gf-form-label">
<i class="fa fa-arrow-right"></i> <icon name="'arrow-right'"></icon>
</label> </label>
<input type="text" class="gf-form-input max-width-8" ng-model="map.text" placeholder="Text" ng-blur="editor.render()"> <input
type="text"
class="gf-form-input max-width-8"
ng-model="map.text"
placeholder="Text"
ng-blur="editor.render()"
/>
</div> </div>
<div class="gf-form"> <div class="gf-form">
<label class="gf-form-label"> <label class="gf-form-label">
@ -101,18 +171,18 @@
<div class="gf-form-group" ng-if="style.mappingType==2"> <div class="gf-form-group" ng-if="style.mappingType==2">
<div class="gf-form" ng-repeat="rangeMap in style.rangeMaps"> <div class="gf-form" ng-repeat="rangeMap in style.rangeMaps">
<span class="gf-form-label"> <span class="gf-form-label">
<i class="fa fa-remove pointer" ng-click="editor.removeRangeMap(style, $index)"></i> <icon name="'times'" ng-click="editor.removeRangeMap(style, $index)"></icon>
</span> </span>
<span class="gf-form-label">From</span> <span class="gf-form-label">From</span>
<input type="text" ng-model="rangeMap.from" class="gf-form-input max-width-6" ng-blur="editor.render()"> <input type="text" ng-model="rangeMap.from" class="gf-form-input max-width-6" ng-blur="editor.render()" />
<span class="gf-form-label">To</span> <span class="gf-form-label">To</span>
<input type="text" ng-model="rangeMap.to" class="gf-form-input max-width-6" ng-blur="editor.render()"> <input type="text" ng-model="rangeMap.to" class="gf-form-input max-width-6" ng-blur="editor.render()" />
<span class="gf-form-label">Text</span> <span class="gf-form-label">Text</span>
<input type="text" ng-model="rangeMap.text" class="gf-form-input max-width-8" ng-blur="editor.render()"> <input type="text" ng-model="rangeMap.text" class="gf-form-input max-width-8" ng-blur="editor.render()" />
</div> </div>
<div class="gf-form"> <div class="gf-form">
<label class="gf-form-label"> <label class="gf-form-label">
<a class="pointer" ng-click="editor.addRangeMap(style)"><i class="fa fa-plus"></i></a> <a class="pointer" ng-click="editor.addRangeMap(style)"><icon name="'plus-circle'"></icon></a>
</label> </label>
</div> </div>
</div> </div>
@ -123,16 +193,28 @@
<div class="section gf-form-group" ng-if="['number', 'string'].indexOf(style.type) !== -1"> <div class="section gf-form-group" ng-if="['number', 'string'].indexOf(style.type) !== -1">
<h5 class="section-heading">Thresholds</h5> <h5 class="section-heading">Thresholds</h5>
<div class="gf-form"> <div class="gf-form">
<label class="gf-form-label width-8">Thresholds <label class="gf-form-label width-8"
>Thresholds
<tip>Comma separated values</tip> <tip>Comma separated values</tip>
</label> </label>
<input type="text" class="gf-form-input width-10" ng-model="style.thresholds" placeholder="50,80" ng-blur="editor.render()" <input
array-join> type="text"
class="gf-form-input width-10"
ng-model="style.thresholds"
placeholder="50,80"
ng-blur="editor.render()"
array-join
/>
</div> </div>
<div class="gf-form"> <div class="gf-form">
<label class="gf-form-label width-8">Color Mode</label> <label class="gf-form-label width-8">Color Mode</label>
<div class="gf-form-select-wrapper width-10"> <div class="gf-form-select-wrapper width-10">
<select class="gf-form-input" ng-model="style.colorMode" ng-options="c.value as c.text for c in editor.colorModes" ng-change="editor.render()"></select> <select
class="gf-form-input"
ng-model="style.colorMode"
ng-options="c.value as c.text for c in editor.colorModes"
ng-change="editor.render()"
></select>
</div> </div>
</div> </div>
<div class="gf-form"> <div class="gf-form">
@ -161,17 +243,25 @@
<p>Specify an URL (relative or absolute)</p> <p>Specify an URL (relative or absolute)</p>
<span> <span>
Use special variables to specify cell values: Use special variables to specify cell values:
<br> <br />
<em>${__cell}</em> refers to current cell value <em>${__cell}</em> refers to current cell value
<br> <br />
<em>${__cell_n}</em> refers to Nth column value in current row. Column indexes are started from 0. For instance, <em>${__cell_n}</em> refers to Nth column value in current row. Column indexes are started from 0. For
<em>${__cell_1}</em> refers to second column's value. instance, <em>${__cell_1}</em> refers to second column's value.
<br> <br />
<em>${__cell:raw}</em> disables all encoding. Useful if the value is a complete URL. By default values are URI encoded. <em>${__cell:raw}</em> disables all encoding. Useful if the value is a complete URL. By default values are
URI encoded.
</span> </span>
</info-popover> </info-popover>
</label> </label>
<input type="text" class="gf-form-input width-29" ng-model="style.linkUrl" ng-blur="editor.render()" ng-model-onblur data-placement="right"> <input
type="text"
class="gf-form-input width-29"
ng-model="style.linkUrl"
ng-blur="editor.render()"
ng-model-onblur
data-placement="right"
/>
</div> </div>
<div class="gf-form"> <div class="gf-form">
<label class="gf-form-label width-9"> <label class="gf-form-label width-9">
@ -179,14 +269,25 @@
<info-popover mode="right-normal"> <info-popover mode="right-normal">
<p>Specify text for link tooltip.</p> <p>Specify text for link tooltip.</p>
<span> <span>
This title appears when user hovers pointer over the cell with link. Use the same variables as for URL. This title appears when user hovers pointer over the cell with link. Use the same variables as for URL.
</span> </span>
</info-popover> </info-popover>
</label> </label>
<input type="text" class="gf-form-input width-29" ng-model="style.linkTooltip" ng-blur="editor.render()" ng-model-onblur <input
data-placement="right"> type="text"
class="gf-form-input width-29"
ng-model="style.linkTooltip"
ng-blur="editor.render()"
ng-model-onblur
data-placement="right"
/>
</div> </div>
<gf-form-switch class="gf-form" label-class="width-9" label="Open in new tab" checked="style.linkTargetBlank"></gf-form-switch> <gf-form-switch
class="gf-form"
label-class="width-9"
label="Open in new tab"
checked="style.linkTargetBlank"
></gf-form-switch>
</div> </div>
<div class="clearfix"></div> <div class="clearfix"></div>
@ -196,7 +297,7 @@
</button> </button>
</div> </div>
<hr> <hr />
</div> </div>
<div class="gf-form-button-row"> <div class="gf-form-button-row">
@ -204,4 +305,3 @@
<i class="fa fa-plus"></i>&nbsp;Add column style <i class="fa fa-plus"></i>&nbsp;Add column style
</button> </button>
</div> </div>

View File

@ -18,7 +18,7 @@
</div> </div>
<div class="gf-form" ng-repeat="column in editor.panel.columns"> <div class="gf-form" ng-repeat="column in editor.panel.columns">
<label class="gf-form-label"> <label class="gf-form-label">
<i class="pointer fa fa-remove" ng-click="editor.removeColumn(column)"></i> <icon name="'times'" ng-click="editor.removeColumn(column)"></icon>
<span>{{ column.text }}</span> <span>{{ column.text }}</span>
</label> </label>
</div> </div>

View File

@ -40,8 +40,7 @@ $input-border: 1px solid $input-border-color;
.gf-form-input-icon { .gf-form-input-icon {
position: absolute; position: absolute;
top: 50%; top: 10px;
margin-top: -8px;
font-size: $font-size-lg; font-size: $font-size-lg;
left: 10px; left: 10px;
color: $input-color-placeholder; color: $input-color-placeholder;
@ -370,7 +369,6 @@ $input-border: 1px solid $input-border-color;
.gf-form-help-icon { .gf-form-help-icon {
flex-grow: 0; flex-grow: 0;
padding-left: $spacer;
color: $text-color-weak; color: $text-color-weak;
&--bold { &--bold {

View File

@ -101,6 +101,7 @@
.alert-rule-item { .alert-rule-item {
display: flex; display: flex;
align-items: center;
width: 100%; width: 100%;
height: 100%; height: 100%;
background: $card-background; background: $card-background;

View File

@ -23,15 +23,6 @@ angular.module('grafana.routes', ['ngRoute']);
jest.mock('app/core/core', () => ({})); jest.mock('app/core/core', () => ({}));
jest.mock('app/features/plugins/plugin_loader', () => ({})); jest.mock('app/features/plugins/plugin_loader', () => ({}));
/* Temporary solution as Jest can't parse Unicons imports.
* Therefore we are mocking in for all tests. Needs to be fixed before merging to master.
*/
jest.mock('@grafana/ui/src/components/Icon/Icon', () => {
return {
Icon: () => null as any,
};
});
configure({ adapter: new Adapter() }); configure({ adapter: new Adapter() });
const localStorageMock = (() => { const localStorageMock = (() => {