Commit 431f454d by Ivana Huckova Committed by GitHub

@grafana/ui: Create Icon component and replace part of the icons (#23402)

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

* Resolve type errors resulted from merge

* Part2: Unicons implementation (#23266)

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

* Icons update

* Add optional chaining to for isFontAwesome variable

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

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

* Merge remote branch origin master to icons-unicons

* Revert "Merge remote branch origin master to icons-unicons"

This reverts commit 3f25d50a39a940883fefe73ce51219139c1ed37f.

* Size-up dashnav icons

* Fix alerting icons, panel headers, update tests

* Fix typecheck error

* Adjustments - add panel icon, spacing

* Set TerserPlugin sourceMap to false to prevent running out of memory when publishing storybook

Co-authored-by: Torkel Ödegaard <torkel@grafana.com>
parent 7799dd84
const esModule = '@iconscout/react-unicons';
module.exports = {
verbose: false,
transform: {
'^.+\\.(ts|tsx|js|jsx)$': 'ts-jest',
[`(${esModule}).+\\.js$`]: 'babel-jest',
},
transformIgnorePatterns: [`/node_modules/(?!${esModule})`],
moduleDirectories: ['node_modules', 'public'],
roots: ['<rootDir>/public/app', '<rootDir>/public/test', '<rootDir>/packages', '<rootDir>/scripts'],
testRegex: '(\\.|/)(test)\\.(jsx?|tsx?)$',
......
import { camelCase } from 'lodash';
const specialChars = ['(', '[', '{', '}', ']', ')', '|', '*', '+', '-', '.', '?', '<', '>', '#', '&', '^', '$'];
export const escapeStringForRegex = (value: string) => {
......@@ -89,3 +90,8 @@ export function toFloatOrUndefined(value: string): number | undefined {
const v = parseFloat(value);
return isNaN(v) ? undefined : v;
}
export const toPascalCase = (string: string) => {
const str = camelCase(string);
return str.charAt(0).toUpperCase() + str.substring(1);
};
......@@ -82,7 +82,7 @@ module.exports = ({ config, mode }) => {
new TerserPlugin({
cache: false,
parallel: false,
sourceMap: true,
sourceMap: false,
}),
new OptimizeCSSAssetsPlugin({}),
],
......
......@@ -31,6 +31,7 @@
"@grafana/data": "7.0.0-pre.0",
"@grafana/slate-react": "0.22.9-grafana",
"@grafana/tsconfig": "^1.0.0-rc1",
"@iconscout/react-unicons": "^1.0.0",
"@torkelo/react-select": "3.0.8",
"@types/react-beautiful-dnd": "12.1.2",
"@types/react-color": "3.0.1",
......
import React, { FC, ReactNode } from 'react';
import { Icon, IconName } from '@grafana/ui';
import classNames from 'classnames';
export type AlertVariant = 'success' | 'warning' | 'error' | 'info';
......@@ -15,16 +16,16 @@ interface AlertProps {
function getIconFromSeverity(severity: AlertVariant): string {
switch (severity) {
case 'error': {
return 'fa fa-exclamation-triangle';
return 'exclamation-triangle';
}
case 'warning': {
return 'fa fa-exclamation-triangle';
return 'exclamation-triangle';
}
case 'info': {
return 'fa fa-info-circle';
return 'info-circle';
}
case 'success': {
return 'fa fa-check';
return 'check';
}
default:
return '';
......@@ -37,7 +38,7 @@ export const Alert: FC<AlertProps> = ({ title, buttonText, onButtonClick, onRemo
<div className="alert-container">
<div className={alertClass}>
<div className="alert-icon">
<i className={getIconFromSeverity(severity)} />
<Icon size="xl" name={getIconFromSeverity(severity) as IconName} />
</div>
<div className="alert-body">
<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 */}
{onRemove && (
<button type="button" className="alert-close" onClick={onRemove}>
<i className="fa fa fa-remove" />
<Icon name="times" size="lg" />
</button>
)}
{onButtonClick && (
......
......@@ -2,6 +2,7 @@ import React, { AnchorHTMLAttributes, ButtonHTMLAttributes, useContext } from 'r
import { css, cx } from 'emotion';
import tinycolor from 'tinycolor2';
import { selectThemeVariant, stylesFactory, ThemeContext } from '../../themes';
import { IconName } from '../../types';
import { getFocusStyle, getPropertiesForButtonSize } from '../Forms/commonStyles';
import { GrafanaTheme } from '@grafana/data';
import { ButtonContent } from './ButtonContent';
......@@ -134,7 +135,7 @@ export type ButtonVariant = 'primary' | 'secondary' | 'destructive' | 'link';
type CommonProps = {
size?: ComponentSize;
variant?: ButtonVariant;
icon?: string;
icon?: IconName;
className?: string;
};
......@@ -151,7 +152,9 @@ export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
return (
<button className={cx(styles.button, className)} {...otherProps} ref={ref}>
<ButtonContent icon={icon}>{children}</ButtonContent>
<ButtonContent icon={icon} size={otherProps.size}>
{children}
</ButtonContent>
</button>
);
}
......@@ -171,7 +174,9 @@ export const LinkButton = React.forwardRef<HTMLAnchorElement, ButtonLinkProps>(
return (
<a className={cx(styles.button, className)} {...otherProps} ref={ref}>
<ButtonContent icon={icon}>{children}</ButtonContent>
<ButtonContent icon={icon} size={otherProps.size}>
{children}
</ButtonContent>
</a>
);
}
......
import React from 'react';
import { css } from 'emotion';
import { stylesFactory, useTheme } from '../../themes';
import { IconName } from '../../types';
import { Icon } from '../Icon/Icon';
import { ComponentSize } from '../../types/size';
import { GrafanaTheme } from '@grafana/data';
......@@ -14,8 +16,6 @@ const getStyles = stylesFactory((theme: GrafanaTheme) => ({
`,
icon: css`
position: relative;
top: 1px;
& + * {
margin-left: ${theme.spacing.sm};
}
......@@ -23,28 +23,24 @@ const getStyles = stylesFactory((theme: GrafanaTheme) => ({
}));
type Props = {
icon?: string;
icon?: IconName;
className?: string;
children: React.ReactNode;
size?: ComponentSize;
};
export function ButtonContent(props: Props) {
const { icon, children } = props;
const { icon, children, size } = props;
const theme = useTheme();
const styles = getStyles(theme);
if (!children) {
return (
<span className={styles.content}>
<i className={icon} />
</span>
);
return <span className={styles.content}>{icon && <Icon name={icon} size={size} />}</span>;
}
const iconElement = icon && (
<span className={styles.icon}>
<i className={icon} />
<Icon name={icon} size={size} />
</span>
);
......
......@@ -40,7 +40,7 @@ export const ButtonCascader: React.FC<ButtonCascaderProps> = props => {
expandIcon={null}
>
<button className="gf-form-label gf-form-label--btn" disabled={props.disabled}>
{props.children} <Icon name="caret-down" />
{props.children} <Icon name="angle-down" />
</button>
</RCCascader>
);
......
......@@ -12,7 +12,7 @@ CallToActionCardStories.add('default', () => {
const ctaElements: { [key: string]: JSX.Element } = {
custom: <h1>This is just H1 tag, you can any component as CTA element</h1>,
button: (
<Button size="lg" icon="fa fa-plus" onClick={action('cta button clicked')}>
<Button size="lg" icon="plus-circle" onClick={action('cta button clicked')}>
Add datasource
</Button>
),
......
......@@ -212,7 +212,7 @@ export class Cascader extends React.PureComponent<CascaderProps, CascaderState>
value={activeLabel}
onKeyDown={this.onInputKeyDown}
onChange={() => {}}
suffix={focusCascade ? <Icon name="caret-up" /> : <Icon name="caret-down" />}
suffix={focusCascade ? <Icon name="angle-up" /> : <Icon name="angle-down" />}
/>
</div>
</RCCascader>
......
......@@ -123,7 +123,7 @@ export const Collapse: FunctionComponent<Props> = ({ isOpen, label, loading, col
<div className={panelClass}>
<div className={headerClass} onClick={onClickToggle}>
<div className={headerButtonsClass}>
<Icon name={isOpen ? 'caret-up' : 'caret-down'} />
<Icon name={isOpen ? 'angle-up' : 'angle-down'} />
</div>
<div className={cx([style.headerLabel])}>{label}</div>
</div>
......
......@@ -65,7 +65,7 @@ storiesOf('General/ConfirmButton', module)
action('Saved')('save!');
}}
>
<Button size={size} variant="secondary" icon="fa fa-pencil">
<Button size={size} variant="secondary" icon="pen">
{buttonText}
</Button>
</ConfirmButton>
......
......@@ -18,7 +18,7 @@ export const DeleteButton: FC<Props> = ({ size, disabled, onConfirm }) => {
disabled={disabled}
onConfirm={onConfirm}
>
<Button variant="destructive" icon="fa fa-remove" size={size || 'sm'} />
<Button variant="destructive" icon="times" size={size || 'sm'} />
</ConfirmButton>
);
};
......@@ -11,7 +11,7 @@ const getKnobs = () => {
body: text('Body', 'Are you sure you want to delete this user?'),
confirm: text('Confirm', 'Delete'),
visible: boolean('Visible', true),
icon: select('Icon', ['exclamation-triangle', 'power-off', 'cog', 'lock'], 'exclamation-triangle'),
icon: select('Icon', ['exclamation-triangle', 'power', 'cog', 'lock'], 'exclamation-triangle'),
};
};
......
import React, { FC, useContext } from 'react';
import { css } from 'emotion';
import { Modal } from '../Modal/Modal';
import { IconType } from '../Icon/types';
import { IconName } from '../../types';
import { Button } from '../Button';
import { stylesFactory, ThemeContext } from '../../themes';
import { GrafanaTheme } from '@grafana/data';
......@@ -22,7 +22,7 @@ const getStyles = stylesFactory((theme: GrafanaTheme) => ({
`,
}));
const defaultIcon: IconType = 'exclamation-triangle';
const defaultIcon: IconName = 'exclamation-triangle';
interface Props {
isOpen: boolean;
......@@ -30,7 +30,7 @@ interface Props {
body: React.ReactNode;
confirmText: string;
dismissText?: string;
icon?: IconType;
icon?: IconName;
onConfirm(): void;
onDismiss(): void;
}
......
......@@ -73,7 +73,7 @@ export const DataLinksEditor: FC<DataLinksEditorProps> = React.memo(
)}
{(!value || (value && value.length < (maxLinks || Infinity))) && (
<Button variant="secondary" icon="fa fa-plus" onClick={() => onAdd()}>
<Button variant="secondary" icon="plus-circle" onClick={() => onAdd()}>
Add link
</Button>
)}
......
......@@ -100,7 +100,7 @@ export const DataLinksInlineEditor: React.FC<DataLinksInlineEditorProps> = ({ li
)}
<FullWidthButtonContainer>
<Button size="sm" icon="fa fa-plus" onClick={onDataLinkAdd} variant="secondary">
<Button size="sm" icon="plus-circle" onClick={onDataLinkAdd} variant="secondary">
Add link
</Button>
</FullWidthButtonContainer>
......
......@@ -42,10 +42,10 @@ export const DataLinksListItem: FC<DataLinksListItemProps> = ({
<HorizontalGroup>
<div onClick={onEdit} className={styles.action}>
<Icon name="pencil" />
<Icon name="pen" />
</div>
<div onClick={onRemove} className={cx(styles.action, styles.remove)}>
<Icon name="trash" />
<Icon name="trash-alt" />
</div>
</HorizontalGroup>
</HorizontalGroup>
......
......@@ -2,6 +2,7 @@ import React from 'react';
import { KeyValue } from '@grafana/data';
import { css, cx } from 'emotion';
import { Tooltip } from '../Tooltip/Tooltip';
import { Icon } from '../Icon/Icon';
import { CertificationKey } from './CertificationKey';
import { HttpSettingsBaseProps } from './types';
......@@ -47,7 +48,7 @@ export const TLSAuthSettings: React.FC<HttpSettingsBaseProps> = ({ dataSourceCon
theme="info"
>
<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>
</Tooltip>
</div>
......
......@@ -26,7 +26,7 @@ export const FieldConfigItemHeaderTitle: React.FC<FieldConfigItemHeaderTitleProp
<div className={styles.header}>
<Forms.Label description={description}>{title}</Forms.Label>
<div className={styles.remove} onClick={() => onRemove()} aria-label="FieldConfigItemHeaderTitle remove button">
<Icon name="trash" />
<Icon name="trash-alt" />
</div>
</div>
{children}
......
import React, { FunctionComponent, ReactNode } from 'react';
import classNames from 'classnames';
import { Tooltip, PopoverContent } from '../Tooltip/Tooltip';
import { Icon } from '../Icon/Icon';
interface Props {
children: ReactNode;
......@@ -33,7 +34,7 @@ export const FormLabel: FunctionComponent<Props> = ({
{tooltip && (
<Tooltip placement="top" content={tooltip} theme={'info'}>
<div className="gf-form-help-icon gf-form-help-icon--right-normal">
<i className="fa fa-info-circle" />
<Icon name="info-circle" size="sm" style={{ marginBottom: 0 }} />
</div>
</Tooltip>
)}
......
import React, { PureComponent, ReactElement } from 'react';
import Select from './Select';
import { PopoverContent } from '../../../Tooltip/Tooltip';
import { Icon } from '../../../Icon/Icon';
import { SelectableValue } from '@grafana/data';
interface ButtonComponentProps {
......@@ -23,8 +24,8 @@ const ButtonComponent = (buttonProps: ButtonComponentProps) => (props: any) => {
<div className="select-button">
{iconClass && <i className={`select-button-icon ${iconClass}`} />}
<span className="select-button-value">{label ? label : ''}</span>
{!props.menuIsOpen && <i className="fa fa-caret-down fa-fw" />}
{props.menuIsOpen && <i className="fa fa-caret-up fa-fw" />}
{!props.menuIsOpen && <Icon name="angle-down" style={{ marginBottom: 0 }} size="lg" />}
{props.menuIsOpen && <Icon name="angle-up" style={{ marginBottom: 0 }} size="lg" />}
</div>
</div>
);
......
......@@ -135,26 +135,11 @@ $select-input-bg-disabled: $input-bg-disabled;
position: absolute;
height: 100%;
right: 8px;
top: 1px;
display: inline-block;
text-align: right;
}
.gf-form-select-box__select-arrow {
border-color: $input-color-select-arrow transparent transparent;
border-style: solid;
border-width: 4px 4px 2.5px;
display: inline-block;
position: absolute;
top: 50%;
right: 2px;
margin-top: -2px;
&.gf-form-select-box__select-arrow--reversed {
border-color: transparent transparent $input-color-select-arrow;
border-width: 0 4px 4px;
}
}
.gf-form-input--form-dropdown {
padding: 0;
border: 0;
......
......@@ -5,7 +5,7 @@ import { Icon } from './Icon';
# Icon
Grafana's wrapper component over [Font Awesome](https://fontawesome.com/) icons
Grafana's wrapper component over [Font Awesome](https://fontawesome.com/) and Unicons icons
### Changing icon size
......@@ -15,13 +15,7 @@ By default `Icon` has width and height of `16px` and font-size of `14px`. Pass `
```jsx
import { css } from 'emotion';
const customIconSize = css`
width: 20px;
height: 20px;
font-size: 18px;
`;
<Icon name="check" className={customIconSize} />
<Icon name="check" />
```
......
......@@ -4,7 +4,7 @@ import { css } from 'emotion';
import { Input } from '../Input/Input';
import { Field } from '../Forms/Field';
import { Icon } from './Icon';
import { getAvailableIcons, IconType } from './types';
import { getAvailableIcons, IconName } from '../../types';
import { withCenteredStory } from '../../utils/storybook/withCenteredStory';
import { useTheme, selectThemeVariant } from '../../themes';
import mdx from './Icon.mdx';
......@@ -20,7 +20,7 @@ export default {
},
};
const IconWrapper: React.FC<{ name: IconType }> = ({ name }) => {
const IconWrapper: React.FC<{ name: IconName }> = ({ name }) => {
const theme = useTheme();
const borderColor = selectThemeVariant(
{
......@@ -43,12 +43,7 @@ const IconWrapper: React.FC<{ name: IconType }> = ({ name }) => {
}
`}
>
<Icon
name={name}
className={css`
font-size: 18px;
`}
/>
<Icon name={name} />
<div
className={css`
padding-top: 16px;
......
import React from 'react';
import { cx, css } from 'emotion';
import { css, cx } from 'emotion';
import { GrafanaTheme, toPascalCase } from '@grafana/data';
import { stylesFactory } from '../../themes';
import { IconType } from './types';
import { useTheme } from '../../themes/ThemeContext';
import { IconName, IconType, IconSize } from '../../types';
import { ComponentSize } from '../../types/size';
//@ts-ignore
import * as DefaultIcon from '@iconscout/react-unicons';
import * as MonoIcon from './assets';
export interface IconProps {
name: IconType;
interface IconProps extends React.HTMLAttributes<HTMLDivElement> {
name: IconName;
size?: IconSize;
type?: IconType;
}
export interface SvgProps extends React.HTMLAttributes<SVGElement> {
size: number;
secondaryColor?: string;
className?: string;
onClick?: () => void;
onMouseDown?: React.MouseEventHandler;
}
const getIconStyles = stylesFactory(() => {
const getIconStyles = stylesFactory((theme: GrafanaTheme) => {
return {
container: css`
display: inline-block;
`,
icon: css`
display: inline-flex;
width: 16px;
align-items: center;
justify-content: center;
height: 16px;
text-align: center;
font-size: 14px;
&:before {
vertical-align: middle;
}
vertical-align: middle;
display: inline-block;
margin-bottom: ${theme.spacing.xxs};
cursor: pointer;
fill: currentColor;
`,
orange: css`
fill: ${theme.colors.orange};
`,
};
});
export const Icon: React.FC<IconProps> = ({ name, className, onClick, onMouseDown }) => {
const styles = getIconStyles();
return <i className={cx(styles.icon, 'fa', `fa-${name}`, className)} onClick={onClick} onMouseDown={onMouseDown} />;
};
export const Icon = React.forwardRef<HTMLDivElement, IconProps>(
({ size = 'md', type = 'default', name, className, style, ...divElementProps }, ref) => {
const theme = useTheme();
const styles = getIconStyles(theme);
const svgSize = getSvgSize(size, theme);
/* Temporary solution to display also font awesome icons */
const isFontAwesome = name?.includes('fa-');
if (isFontAwesome) {
return <i className={cx(name, className)} {...divElementProps} style={style} />;
}
const iconName = type === 'default' ? `Uil${toPascalCase(name)}` : toPascalCase(name);
/* Unicons don't have type definitions */
//@ts-ignore
const Component = type === 'default' ? DefaultIcon[iconName] : MonoIcon[iconName];
if (!Component) {
return <div />;
}
return (
<div className={styles.container} {...divElementProps} ref={ref}>
{type === 'default' && <Component size={svgSize} className={cx(styles.icon, className)} style={style} />}
{type === 'mono' && (
<Component
size={svgSize}
className={cx(styles.icon, { [styles.orange]: name === 'favorite' }, className)}
style={style}
/>
)}
</div>
);
}
);
Icon.displayName = 'Icon';
/* Transform string with px to number and add 2 pxs as path in svg is 2px smaller */
const getSvgSize = (size: ComponentSize | 'xl' | 'xxl', theme: GrafanaTheme) => {
let svgSize;
if (size === 'xl') {
svgSize = Number(theme.typography.heading.h1.slice(0, -2));
} else if (size === 'xxl') {
svgSize = Number(theme.height.lg.slice(0, -2));
} else {
svgSize = Number(theme.typography.size[size].slice(0, -2)) + 2;
}
return svgSize;
};
import React, { FunctionComponent } from 'react';
import { SvgProps } from '../Icon';
export const Apps: FunctionComponent<SvgProps> = ({ size, ...rest }) => {
return (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width={size} height={size} {...rest}>
<rect width="9" height="9" x="2" y="2" rx="1" />
<rect width="9" height="9" x="2" y="13" rx="1" opacity="0.6" />
<rect width="9" height="9" x="13" y="2" rx="1" opacity="0.6" />
<rect width="9" height="9" x="13" y="13" rx="1" opacity="0.6" />
</svg>
);
};
import React, { FunctionComponent } from 'react';
import { SvgProps } from '../Icon';
export const Bell: FunctionComponent<SvgProps> = ({ size, ...rest }) => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
enableBackground="new 0 0 24 24"
viewBox="0 0 24 24"
width={size}
height={size}
{...rest}
>
<path
opacity="0.6"
d="M18,13.18463V10c0-3.31372-2.68628-6-6-6s-6,2.68628-6,6v3.18463C4.83832,13.59863,4.00146,14.69641,4,16v2c0,0.00037,0,0.00073,0,0.00116C4.00031,18.5531,4.44806,19.00031,5,19h14c0.00037,0,0.00073,0,0.00116,0C19.5531,18.99969,20.00031,18.55194,20,18v-2C19.99854,14.69641,19.16168,13.59863,18,13.18463z"
/>
<path d="M8.14233 19c.4472 1.72119 1.99689 2.99817 3.85767 3 1.86078-.00183 3.41046-1.27881 3.85767-3H8.14233zM12 4c.34149 0 .67413.03516 1 .08997V3c0-.55231-.44769-1-1-1s-1 .44769-1 1v1.08997C11.32587 4.03516 11.65851 4 12 4z" />
</svg>
);
};
import React, { FunctionComponent } from 'react';
import { SvgProps } from '../Icon';
export const Cog: FunctionComponent<SvgProps> = ({ size, ...rest }) => {
return (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width={size} height={size} {...rest}>
<path d="M21.31641,9.55176l-1.88672-.62891.88965-1.77832a.99983.99983,0,0,0-.1875-1.1543L18.01025,3.86816a.99981.99981,0,0,0-1.15429-.1875l-1.77832.88965-.62891-1.88672A1,1,0,0,0,13.5,2h-3a1,1,0,0,0-.94873.68359L8.92236,4.57031,7.144,3.68066a.99634.99634,0,0,0-1.15429.1875L3.86816,5.99023a.99983.99983,0,0,0-.1875,1.1543l.88965,1.77832-1.88672.62891A.9989.9989,0,0,0,2,10.5v3a.9989.9989,0,0,0,.68359.94824l1.88672.62891-.88965,1.77832a.99983.99983,0,0,0,.1875,1.1543l2.12159,2.12207a.99813.99813,0,0,0,1.15429.1875l1.77832-.88965.62891,1.88672A1,1,0,0,0,10.5,22h3a1,1,0,0,0,.94873-.68359l.62891-1.88672,1.77832.88965a.99994.99994,0,0,0,1.15429-.1875l2.12159-2.12207a.99983.99983,0,0,0,.1875-1.1543l-.88916-1.77832,1.88623-.62891A.9989.9989,0,0,0,22,13.5v-3A.9989.9989,0,0,0,21.31641,9.55176ZM12,15a3,3,0,1,1,3-3A3.00344,3.00344,0,0,1,12,15Z" />
<path
opacity="0.6"
d="M12,16a4,4,0,1,1,4-4A4.00427,4.00427,0,0,1,12,16Zm0-6a2,2,0,1,0,2,2A2.00229,2.00229,0,0,0,12,10Z"
/>
</svg>
);
};
import React, { FunctionComponent } from 'react';
import { SvgProps } from '../Icon';
export const Favorite: FunctionComponent<SvgProps> = ({ size, ...rest }) => {
return (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width={size} height={size} {...rest}>
<path d="M17.56249,21.55957a.99941.99941,0,0,1-.46581-.11523L12,18.76465,6.90332,21.44434a.9999.9999,0,0,1-1.45117-1.05372l.97363-5.67578-4.124-4.01953a.99965.99965,0,0,1,.55469-1.70508l5.69824-.82812,2.54883-5.16406a1.04012,1.04012,0,0,1,1.793,0l2.54883,5.16406,5.69824.82812a.99965.99965,0,0,1,.55469,1.70508l-4.124,4.01953.97363,5.67578a1.00024,1.00024,0,0,1-.98536,1.169Z" />
</svg>
);
};
import React, { FunctionComponent } from 'react';
import { SvgProps } from '../Icon';
export const Folder: FunctionComponent<SvgProps> = ({ size, ...rest }) => {
return (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width={size} height={size} {...rest}>
<path d="M19,21.5H5a3.00328,3.00328,0,0,1-3-3V5.5a3.00328,3.00328,0,0,1,3-3H9.55859A2.99629,2.99629,0,0,1,12.4043,4.55078L12.7207,5.5H19a3.00328,3.00328,0,0,1,3,3v10A3.00328,3.00328,0,0,1,19,21.5Z" />
</svg>
);
};
import React, { FunctionComponent } from 'react';
import { SvgProps } from '../Icon';
export const FolderPlus: FunctionComponent<SvgProps> = ({ size, ...rest }) => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
enableBackground="new 0 0 24 24"
viewBox="0 0 24 24"
width={size}
height={size}
{...rest}
>
<path
opacity="0.6"
d="M19,21.5H5a3.00328,3.00328,0,0,1-3-3V5.5a3.00328,3.00328,0,0,1,3-3H9.55859A2.99629,2.99629,0,0,1,12.4043,4.55078L12.7207,5.5H19a3.00328,3.00328,0,0,1,3,3v10A3.00328,3.00328,0,0,1,19,21.5Z"
/>
<path d="M14,12.5H13v-1a1,1,0,0,0-2,0v1H10a1,1,0,0,0,0,2h1v1a1,1,0,0,0,2,0v-1h1a1,1,0,0,0,0-2Z" />
</svg>
);
};
import React, { FunctionComponent } from 'react';
import { SvgProps } from '../Icon';
export const Grafana: FunctionComponent<SvgProps> = ({ size, ...rest }) => {
return (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 85.12 92.46" height={size} width={size} {...rest}>
<defs>
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="42.562" y1="113.2595" x2="42.562" y2="28.7828">
<stop offset="0" style={{ stopColor: '#FFF200' }} />
<stop offset="1" style={{ stopColor: '#F15A29' }} />
</linearGradient>
</defs>
<path
fill="url(#SVGID_1_)"
d="M85.01,40.8c-0.14-1.55-0.41-3.35-0.93-5.32c-0.51-1.97-1.28-4.13-2.39-6.37c-1.12-2.24-2.57-4.57-4.47-6.82
c-0.74-0.88-1.54-1.76-2.42-2.6c1.3-5.17-1.59-9.65-1.59-9.65c-4.98-0.31-8.14,1.54-9.31,2.39c-0.2-0.08-0.39-0.17-0.59-0.25
c-0.85-0.34-1.72-0.66-2.61-0.95c-0.89-0.28-1.81-0.54-2.74-0.76c-0.94-0.22-1.89-0.4-2.86-0.55c-0.17-0.03-0.34-0.05-0.51-0.07
C52.41,2.9,46.18,0,46.18,0c-6.95,4.41-8.27,10.57-8.27,10.57s-0.03,0.14-0.07,0.36c-0.38,0.11-0.77,0.22-1.15,0.34
c-0.53,0.16-1.06,0.36-1.59,0.55c-0.53,0.21-1.06,0.41-1.58,0.64c-1.05,0.45-2.09,0.96-3.1,1.53c-0.99,0.55-1.95,1.16-2.9,1.82
c-0.14-0.06-0.24-0.11-0.24-0.11c-9.62-3.68-18.17,0.75-18.17,0.75c-0.78,10.24,3.84,16.68,4.76,17.86
c-0.23,0.63-0.44,1.27-0.64,1.92c-0.71,2.32-1.24,4.7-1.57,7.16c-0.05,0.35-0.09,0.71-0.13,1.07C2.63,48.84,0,57.84,0,57.84
c7.42,8.53,16.07,9.06,16.07,9.06c0.01-0.01,0.02-0.01,0.02-0.02c1.1,1.96,2.37,3.83,3.8,5.57c0.6,0.73,1.23,1.43,1.88,2.11
c-2.71,7.74,0.38,14.18,0.38,14.18c8.26,0.31,13.69-3.61,14.83-4.52c0.82,0.28,1.66,0.53,2.5,0.74c2.54,0.65,5.14,1.04,7.74,1.15
c0.65,0.03,1.3,0.04,1.95,0.04l0.31,0l0.21-0.01l0.41-0.01l0.4-0.02l0.01,0.01c3.89,5.55,10.74,6.34,10.74,6.34
c4.87-5.13,5.15-10.22,5.15-11.33l0,0c0,0,0-0.04,0-0.07c0-0.09,0-0.15,0-0.15s0,0,0,0c0-0.08-0.01-0.15-0.01-0.23
c1.02-0.72,2-1.49,2.92-2.31c1.95-1.76,3.65-3.77,5.06-5.93c0.13-0.2,0.26-0.41,0.39-0.62c5.51,0.32,9.39-3.41,9.39-3.41
c-0.91-5.74-4.18-8.54-4.87-9.07l0,0c0,0-0.03-0.02-0.07-0.05c-0.04-0.03-0.06-0.05-0.06-0.05l0,0c-0.04-0.02-0.08-0.05-0.12-0.08
c0.03-0.35,0.06-0.69,0.08-1.04c0.04-0.62,0.06-1.24,0.06-1.85l0-0.46l0-0.23l0-0.12c0-0.16,0-0.1,0-0.16l-0.02-0.38l-0.03-0.52
c-0.01-0.18-0.02-0.34-0.04-0.5c-0.01-0.16-0.03-0.32-0.05-0.48l-0.06-0.48l-0.07-0.47c-0.09-0.63-0.21-1.26-0.36-1.88
c-0.58-2.47-1.54-4.82-2.82-6.93c-1.28-2.11-2.86-3.98-4.65-5.56c-1.79-1.58-3.79-2.85-5.9-3.79c-2.1-0.95-4.31-1.55-6.51-1.83
c-1.1-0.14-2.2-0.2-3.28-0.19l-0.41,0.01l-0.1,0c-0.03,0-0.15,0-0.14,0l-0.17,0.01l-0.4,0.03c-0.15,0.01-0.31,0.02-0.45,0.04
c-0.56,0.05-1.11,0.13-1.66,0.23c-2.18,0.41-4.24,1.2-6.06,2.28c-1.82,1.09-3.39,2.45-4.68,3.98c-1.28,1.54-2.28,3.24-2.96,5
c-0.69,1.76-1.07,3.58-1.18,5.35c-0.03,0.44-0.04,0.88-0.03,1.32c0,0.11,0,0.22,0.01,0.33l0.01,0.35c0.02,0.21,0.03,0.42,0.05,0.63
c0.09,0.9,0.25,1.75,0.49,2.58c0.48,1.66,1.25,3.15,2.2,4.43c0.95,1.28,2.08,2.33,3.28,3.15c1.2,0.82,2.49,1.41,3.76,1.79
c1.27,0.38,2.54,0.54,3.74,0.53c0.15,0,0.3,0,0.44-0.01c0.08,0,0.16-0.01,0.24-0.01c0.08,0,0.16-0.01,0.24-0.01
c0.13-0.01,0.25-0.03,0.38-0.04c0.03,0,0.07-0.01,0.11-0.01l0.12-0.02c0.08-0.01,0.15-0.02,0.23-0.03c0.16-0.02,0.29-0.05,0.43-0.08
c0.14-0.03,0.28-0.05,0.42-0.09c0.27-0.06,0.54-0.14,0.8-0.22c0.52-0.17,1.01-0.38,1.46-0.61c0.45-0.23,0.87-0.5,1.26-0.77
c0.11-0.08,0.22-0.16,0.33-0.25c0.42-0.33,0.48-0.94,0.15-1.35c-0.29-0.36-0.79-0.45-1.19-0.23c-0.1,0.05-0.2,0.11-0.3,0.16
c-0.35,0.17-0.71,0.32-1.09,0.45c-0.39,0.12-0.79,0.22-1.2,0.29c-0.21,0.03-0.42,0.06-0.63,0.08c-0.11,0.01-0.21,0.02-0.32,0.02
c-0.11,0-0.22,0.01-0.32,0.01c-0.1,0-0.21,0-0.31-0.01c-0.13-0.01-0.26-0.01-0.39-0.02c0,0-0.07,0-0.01,0l-0.04,0L51.4,61.6
c-0.06-0.01-0.12-0.01-0.17-0.02c-0.12-0.01-0.23-0.03-0.35-0.04c-0.93-0.13-1.88-0.4-2.79-0.82c-0.91-0.41-1.79-0.98-2.57-1.69
c-0.79-0.71-1.48-1.56-2.01-2.52c-0.54-0.96-0.92-2.03-1.09-3.16c-0.09-0.56-0.13-1.14-0.11-1.71c0.01-0.16,0.01-0.31,0.02-0.47
c0,0.04,0-0.02,0-0.03l0-0.06l0.01-0.12c0.01-0.08,0.01-0.15,0.02-0.23c0.03-0.31,0.08-0.62,0.13-0.92
c0.43-2.45,1.65-4.83,3.55-6.65c0.47-0.45,0.98-0.87,1.53-1.25c0.55-0.37,1.12-0.7,1.73-0.98c0.6-0.28,1.23-0.5,1.88-0.68
c0.65-0.17,1.31-0.29,1.98-0.35c0.34-0.03,0.67-0.04,1.01-0.04c0.09,0,0.16,0,0.23,0l0.27,0.01l0.17,0.01c0.07,0,0,0,0.03,0l0.07,0
l0.27,0.02c0.73,0.06,1.46,0.16,2.17,0.32c1.43,0.32,2.83,0.85,4.13,1.57c2.6,1.44,4.81,3.69,6.17,6.4c0.69,1.35,1.16,2.81,1.4,4.31
c0.06,0.38,0.1,0.76,0.13,1.14l0.02,0.29l0.01,0.29c0.01,0.1,0.01,0.19,0.01,0.29c0,0.09,0.01,0.2,0,0.27l0,0.25l-0.01,0.28
c-0.01,0.19-0.02,0.49-0.03,0.67c-0.03,0.42-0.07,0.83-0.12,1.24c-0.05,0.41-0.12,0.82-0.19,1.22c-0.08,0.4-0.17,0.81-0.27,1.21
c-0.2,0.8-0.46,1.59-0.76,2.36c-0.61,1.54-1.42,3-2.4,4.36c-1.96,2.7-4.64,4.9-7.69,6.29c-1.52,0.69-3.13,1.19-4.78,1.47
c-0.82,0.14-1.66,0.22-2.5,0.25l-0.15,0.01l-0.13,0l-0.27,0l-0.41,0l-0.21,0c0.11,0-0.02,0-0.01,0l-0.08,0
c-0.45-0.01-0.9-0.03-1.34-0.07c-1.79-0.13-3.55-0.45-5.27-0.95c-1.71-0.49-3.38-1.16-4.95-2c-3.14-1.68-5.95-3.98-8.15-6.76
c-1.11-1.38-2.07-2.87-2.87-4.43c-0.8-1.56-1.42-3.2-1.89-4.88c-0.46-1.68-0.75-3.39-0.86-5.12l-0.02-0.32l-0.01-0.08l0-0.07l0-0.14
l-0.01-0.28l0-0.07l0-0.1l0-0.2l-0.01-0.4l0-0.08c0,0.01,0,0.01,0-0.03l0-0.16c0-0.21,0.01-0.42,0.01-0.63
c0.03-0.85,0.1-1.73,0.21-2.61c0.11-0.88,0.26-1.76,0.44-2.63c0.18-0.87,0.39-1.74,0.64-2.59c0.49-1.71,1.1-3.36,1.82-4.92
c1.44-3.12,3.34-5.88,5.61-8.09c0.57-0.55,1.16-1.08,1.77-1.57c0.61-0.49,1.25-0.95,1.9-1.37c0.65-0.43,1.32-0.82,2.02-1.18
c0.34-0.19,0.7-0.35,1.05-0.52c0.18-0.08,0.36-0.16,0.53-0.24c0.18-0.08,0.36-0.16,0.54-0.23c0.72-0.3,1.46-0.56,2.21-0.8
c0.19-0.06,0.38-0.11,0.56-0.17c0.19-0.06,0.38-0.1,0.57-0.16c0.38-0.11,0.76-0.2,1.14-0.29c0.19-0.05,0.39-0.08,0.58-0.13
c0.19-0.04,0.38-0.08,0.58-0.12c0.19-0.04,0.39-0.07,0.58-0.11l0.29-0.05l0.29-0.04c0.2-0.03,0.39-0.06,0.59-0.09
c0.22-0.04,0.44-0.05,0.66-0.09c0.18-0.02,0.48-0.06,0.65-0.08c0.14-0.01,0.28-0.03,0.41-0.04l0.28-0.03l0.14-0.01l0.16-0.01
c0.22-0.01,0.44-0.03,0.66-0.04l0.33-0.02c0,0,0.12,0,0.02,0l0.07,0l0.14-0.01c0.19-0.01,0.38-0.02,0.56-0.03
c0.75-0.02,1.5-0.02,2.24,0c1.48,0.06,2.93,0.22,4.34,0.48c2.82,0.53,5.49,1.43,7.89,2.62c2.41,1.18,4.57,2.63,6.44,4.2
c0.12,0.1,0.23,0.2,0.35,0.3c0.11,0.1,0.23,0.2,0.34,0.3c0.23,0.2,0.44,0.41,0.66,0.61c0.22,0.2,0.43,0.41,0.64,0.62
c0.2,0.21,0.41,0.41,0.61,0.63c0.8,0.84,1.53,1.69,2.19,2.55c1.33,1.71,2.39,3.44,3.24,5.07c0.05,0.1,0.11,0.2,0.16,0.3
c0.05,0.1,0.1,0.2,0.15,0.3c0.1,0.2,0.2,0.4,0.29,0.6c0.09,0.2,0.19,0.39,0.27,0.59c0.09,0.2,0.17,0.39,0.25,0.58
c0.32,0.76,0.61,1.49,0.84,2.18c0.39,1.11,0.67,2.11,0.89,2.98c0.09,0.35,0.42,0.58,0.78,0.55c0.37-0.03,0.66-0.34,0.66-0.71
C85.14,43.15,85.11,42.05,85.01,40.8z"
/>
</svg>
);
};
import React, { FunctionComponent } from 'react';
import { SvgProps } from '../Icon';
export const Import: FunctionComponent<SvgProps> = ({ size, ...rest }) => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
enableBackground="new 0 0 24 24"
viewBox="0 0 24 24"
width={size}
height={size}
{...rest}
>
<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" viewBox="0 0 24 24">
<path d="M19,22H5c-1.65611-0.00181-2.99819-1.34389-3-3v-4c0-0.55229,0.44772-1,1-1s1,0.44771,1,1v4c0.00037,0.55213,0.44787,0.99963,1,1h14c0.55213-0.00037,0.99963-0.44787,1-1v-4c0-0.55229,0.44772-1,1-1s1,0.44771,1,1v4C21.99819,20.65611,20.65611,21.99819,19,22z" />
<path
opacity="0.6"
d="M16.707,10.293c-0.39027-0.39048-1.02319-0.39065-1.41368-0.00038c-0.00013,0.00013-0.00026,0.00026-0.00038,0.00038L13,12.58594V3c0-0.55228-0.44771-1-1-1s-1,0.44772-1,1v9.58594L8.707,10.293c-0.39402-0.38691-1.02709-0.38116-1.414,0.01286c-0.38195,0.38896-0.38195,1.01218,0,1.40114l4,4c0.39028,0.39048,1.02321,0.39065,1.41369,0.00037c0.00012-0.00012,0.00025-0.00025,0.00037-0.00037l4-4c0.39045-0.3903,0.39058-1.02322,0.00028-1.41367C16.70723,10.29322,16.70712,10.29311,16.707,10.293z"
/>
</svg>
</svg>
);
};
import React, { FunctionComponent } from 'react';
import { SvgProps } from '../Icon';
export const PanelAdd: FunctionComponent<SvgProps> = ({ size, ...rest }) => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
enableBackground="new 0 0 117.8 64"
viewBox="0 0 117.8 64"
xmlSpace="preserve"
width={size}
height={size}
{...rest}
>
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="96.4427" y1="83.7013" x2="96.4427" y2="-9.4831">
<stop offset="0" style={{ stopColor: '#FFF23A' }} />
<stop offset="4.010540e-02" style={{ stopColor: '#FEE62D' }} />
<stop offset="0.1171" style={{ stopColor: '#FED41A' }} />
<stop offset="0.1964" style={{ stopColor: '#FDC90F' }} />
<stop offset="0.2809" style={{ stopColor: '#FDC60B' }} />
<stop offset="0.6685" style={{ stopColor: '#F28F3F' }} />
<stop offset="0.8876" style={{ stopColor: '#ED693C' }} />
<stop offset="1" style={{ stopColor: '#E83E39' }} />
</linearGradient>
<path
d="M15.2,22.7H1.9c-1.1,0-1.9,0.9-1.9,1.9v37.5C0,63.2,0.9,64,1.9,64h13.3c1.1,0,1.9-0.9,1.9-1.9V24.6
C17.1,23.5,16.3,22.7,15.2,22.7z"
/>
<path
d="M36.3,10.2H23c-1.1,0-1.9,0.9-1.9,1.9v50c0,1.1,0.9,1.9,1.9,1.9h13.3c1.1,0,1.9-0.9,1.9-1.9v-50
C38.2,11.1,37.3,10.2,36.3,10.2z"
/>
<path
d="M57.3,32H44c-1.1,0-1.9,0.9-1.9,1.9v28.1c0,1.1,0.9,1.9,1.9,1.9h13.3c1.1,0,1.9-0.9,1.9-1.9V34
C59.2,32.9,58.4,32,57.3,32z"
/>
<path
d="M70.1,38V26.1c0-3.4,2.7-6.1,6.1-6.1h4.1V2c0-1.1-0.9-1.9-1.9-1.9H65.1C64,0,63.1,0.9,63.1,2v60.1
c0,1.1,0.9,1.9,1.9,1.9h13.3c1.1,0,1.9-0.9,1.9-1.9V44.1h-4.1C72.9,44.1,70.1,41.3,70.1,38z"
/>
<path
fill="url(#SVGID_1_)"
d="M116.7,24.9h-7.2h-0.5h-5.4V11.8c0-0.6-0.5-1.1-1.1-1.1H90.5c-0.6,0-1.1,0.5-1.1,1.1v13.1h-9.1h-4.1
c-0.6,0-1.1,0.5-1.1,1.1V38c0,0.6,0.5,1.1,1.1,1.1h4.1h9.1v4.6v1.9v6.7c0,0.6,0.5,1.1,1.1,1.1h11.9c0.6,0,1.1-0.5,1.1-1.1V39.1
h13.1c0.6,0,1.1-0.5,1.1-1.1V26.1C117.8,25.5,117.3,24.9,116.7,24.9z"
/>
</svg>
);
};
import React, { FunctionComponent } from 'react';
import { SvgProps } from '../Icon';
export const PlusSquare: FunctionComponent<SvgProps> = ({ size, ...rest }) => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
enableBackground="new 0 0 24 24"
viewBox="0 0 24 24"
width={size}
height={size}
{...rest}
>
<path d="M17,11H13V7a1,1,0,0,0-2,0v4H7a1,1,0,0,0,0,2h4v4a1,1,0,0,0,2,0V13h4a1,1,0,0,0,0-2Z" />
<path
opacity="0.6"
d="M21,2H3A.99974.99974,0,0,0,2,3V21a.99974.99974,0,0,0,1,1H21a.99974.99974,0,0,0,1-1V3A.99974.99974,0,0,0,21,2ZM17,13H13v4a1,1,0,0,1-2,0V13H7a1,1,0,0,1,0-2h4V7a1,1,0,0,1,2,0v4h4a1,1,0,0,1,0,2Z"
/>
</svg>
);
};
import React, { FunctionComponent } from 'react';
import { SvgProps } from '../Icon';
export const Shield: FunctionComponent<SvgProps> = ({ size, ...rest }) => {
return (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width={size} height={size} {...rest}>
<path d="M12,22a.9986.9986,0,0,1-.581-.18652l-3.6504-2.60743A9.01643,9.01643,0,0,1,4,11.88281v-7.457a1.00039,1.00039,0,0,1,1.20605-.97851,8.00088,8.00088,0,0,0,6.22168-1.26758.99888.99888,0,0,1,1.14454,0A7.9976,7.9976,0,0,0,18.794,3.44727,1.00039,1.00039,0,0,1,20,4.42578v7.457a9.01643,9.01643,0,0,1-3.76855,7.32324l-3.6504,2.60743A.9986.9986,0,0,1,12,22Z" />
<path
opacity="0.6"
d="M10.84961,14.7002h0a.99927.99927,0,0,1-.707-.293L8.543,12.80664A.99989.99989,0,0,1,9.957,11.39258l.89258.89355L13.543,9.59277A.99989.99989,0,1,1,14.957,11.00684l-3.40039,3.40039A.99928.99928,0,0,1,10.84961,14.7002Z"
/>
</svg>
);
};
export * from './Apps';
export * from './Cog';
export * from './Shield';
export * from './Favorite';
export * from './Grafana';
export * from './Bell';
export * from './PlusSquare';
export * from './FolderPlus';
export * from './Folder';
export * from './Import';
export * from './PanelAdd';
import React from 'react';
import { Tooltip, TooltipProps, PopoverContent } from '../Tooltip/Tooltip';
import { Icon } from '../Icon/Icon';
interface InfoTooltipProps extends Omit<TooltipProps, 'children' | 'content'> {
children: PopoverContent;
......@@ -8,7 +9,7 @@ interface InfoTooltipProps extends Omit<TooltipProps, 'children' | 'content'> {
export const InfoTooltip = ({ children, ...restProps }: InfoTooltipProps) => {
return (
<Tooltip content={children} {...restProps}>
<i className="fa fa-info-circle" />
<Icon name="info-circle" />
</Tooltip>
);
};
......@@ -4,7 +4,7 @@ import { withCenteredStory } from '../../utils/storybook/withCenteredStory';
import { Input } from './Input';
import { Button } from '../Button';
import mdx from './Input.mdx';
import { getAvailableIcons, IconType } from '../Icon/types';
import { getAvailableIcons, IconName } from '../../types';
import { KeyValue } from '@grafana/data';
import { Icon } from '../Icon/Icon';
import { Field } from '../Forms/Field';
......@@ -59,11 +59,11 @@ export const simple = () => {
const suffix = select('Suffix', prefixSuffixOpts, null, VISUAL_GROUP);
let prefixEl: any = prefix;
if (prefix && prefix.match(/icon-/g)) {
prefixEl = <Icon name={prefix.replace(/icon-/g, '') as IconType} />;
prefixEl = <Icon name={prefix.replace(/icon-/g, '') as IconName} />;
}
let suffixEl: any = suffix;
if (suffix && suffix.match(/icon-/g)) {
suffixEl = <Icon name={suffix.replace(/icon-/g, '') as IconType} />;
suffixEl = <Icon name={suffix.replace(/icon-/g, '') as IconName} />;
}
const CONTAINER_GROUP = 'Container options';
......
......@@ -246,7 +246,7 @@ export const Input = React.forwardRef<HTMLInputElement, Props>((props, ref) => {
{(suffix || loading) && (
<div className={styles.suffix} ref={suffixRef}>
{loading && <Icon name="spinner" className={cx('fa-spin', styles.loadingIndicator)} />}
{loading && <Icon name="fa fa-spinner" className={cx('fa-spin', styles.loadingIndicator)} />}
{suffix}
</div>
)}
......
......@@ -37,7 +37,7 @@ describe('LogDetails', () => {
describe('when labels are present', () => {
it('should render heading', () => {
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', () => {
const wrapper = setup(undefined, { labels: { key1: 'label1', key2: 'label2' } });
......@@ -47,7 +47,7 @@ describe('LogDetails', () => {
describe('when row entry has parsable fields', () => {
it('should render heading ', () => {
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', () => {
const wrapper = setup(undefined, { entry: 'test=successful' });
......@@ -116,7 +116,7 @@ describe('LogDetails', () => {
expect(wrapper.find(LogDetailsRow).length).toBe(3);
const traceIdRow = wrapper.find(LogDetailsRow).filter({ parsedKey: 'traceId' });
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');
});
});
......@@ -32,16 +32,16 @@ describe('LogDetailsRow', () => {
});
it('should render metrics button', () => {
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', () => {
it('should render filter label button', () => {
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', () => {
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);
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).contains('another value')).toBeTruthy();
});
......
......@@ -10,6 +10,7 @@ import { stylesFactory } from '../../themes/stylesFactory';
//Components
import { LogLabelStats } from './LogLabelStats';
import { LinkButton } from '../Button/Button';
import { Icon } from '../Icon/Icon';
export interface Props extends Themeable {
parsedValue: string;
......@@ -94,24 +95,16 @@ class UnThemedLogDetailsRow extends PureComponent<Props, State> {
<tr className={cx(style.logDetailsValue, { [styles.noHoverBackground]: showFieldsStats })}>
{/* Action buttons - show stats/filter results */}
<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>
{isLabel && (
<>
<td className={style.logsDetailsIcon}>
<i
title="Filter for value"
className={`fa fa-search-plus ${styles.hoverCursor}`}
onClick={this.filterLabel}
/>
<Icon name="search-minus" title="Filter for value" onClick={this.filterLabel} />
</td>
<td className={style.logsDetailsIcon}>
<i
title="Filter out value"
className={`fa fa-search-minus ${styles.hoverCursor}`}
onClick={this.filterOutLabel}
/>
<Icon name="search-plus" title="Filter out value" onClick={this.filterOutLabel} />
</td>
</>
)}
......@@ -129,7 +122,7 @@ class UnThemedLogDetailsRow extends PureComponent<Props, State> {
<LinkButton
variant="link"
size={'sm'}
icon={cx('fa', link.onClick ? 'fa-list' : 'fa-external-link')}
icon={link.onClick ? 'list-ul' : 'external-link-alt'}
href={link.href}
target={'_blank'}
onClick={
......
import React, { PureComponent } from 'react';
import { Field, LinkModel, LogRowModel, TimeZone, DataQueryResponse, GrafanaTheme } from '@grafana/data';
import { Icon } from '@grafana/ui';
import { cx, css } from 'emotion';
import {
......@@ -48,6 +49,8 @@ const getStyles = stylesFactory((theme: GrafanaTheme) => {
topVerticalAlign: css`
label: topVerticalAlign;
vertical-align: top;
margin-top: -${theme.spacing.xs};
margin-left: -${theme.spacing.xxs};
`,
hoverBackground: css`
label: hoverBackground;
......@@ -129,9 +132,6 @@ class UnThemedLogRow extends PureComponent<Props, State> {
const style = getLogRowStyles(theme, row.logLevel);
const styles = getStyles(theme);
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 });
return (
......@@ -150,7 +150,7 @@ class UnThemedLogRow extends PureComponent<Props, State> {
<td className={style.logsRowLevel} />
{!allowDetails && (
<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>
)}
{showTime && showUtc && (
......
......@@ -158,8 +158,10 @@ export const getLogRowStyles = stylesFactory((theme: GrafanaTheme, logLevel?: Lo
logsDetailsIcon: css`
label: logs-row-details__icon;
position: relative;
padding-right: ${theme.spacing.md};
color: ${theme.colors.gray3};
svg {
margin-right: ${theme.spacing.md};
}
`,
logDetailsLabel: css`
label: logs-row-details__label;
......
......@@ -2,13 +2,14 @@ import React from 'react';
import { Portal } from '../Portal/Portal';
import { cx } from 'emotion';
import { withTheme } from '../../themes';
import { IconType } from '../Icon/types';
import { IconName } from '../../types';
import { Themeable } from '../../types';
import { getModalStyles } from './getModalStyles';
import { ModalHeader } from './ModalHeader';
import { Icon } from '../Icon/Icon';
interface Props extends Themeable {
icon?: IconType;
icon?: IconName;
title: string | JSX.Element;
className?: string;
......@@ -50,7 +51,7 @@ export class UnthemedModal extends React.PureComponent<Props> {
<div className={styles.modalHeader}>
{typeof title === 'string' ? this.renderDefaultHeader(title) : title}
<a className={styles.modalHeaderClose} onClick={this.onDismiss}>
<i className="fa fa-remove" />
<Icon name="times" />
</a>
</div>
<div className={styles.modalContent}>{this.props.children}</div>
......
import React, { useContext } from 'react';
import { getModalStyles } from './getModalStyles';
import { IconType } from '../Icon/types';
import { IconName } from '../../types';
import { ThemeContext } from '../../themes';
import { Icon } from '../Icon/Icon';
interface Props {
title: string;
icon?: IconType;
icon?: IconName;
}
export const ModalHeader: React.FC<Props> = ({ icon, title, children }) => {
......@@ -16,7 +16,7 @@ export const ModalHeader: React.FC<Props> = ({ icon, title, children }) => {
return (
<>
<h2 className={styles.modalHeaderTitle}>
{icon && <Icon name={icon} className={styles.modalHeaderIcon} />}
{icon && <Icon name={icon} size="lg" className={styles.modalHeaderIcon} />}
{title}
</h2>
{children}
......
import React from 'react';
import { IconType } from '../Icon/types';
import { cx } from 'emotion';
import { IconName } from '../../types';
import { Icon } from '../Icon/Icon';
interface Props {
icon?: IconType;
icon?: IconName;
iconClass?: string;
}
export const ModalTabContent: React.FC<Props> = ({ icon, iconClass, children }) => {
let iconElem;
const showIcon = icon || iconClass;
if (iconClass) {
iconElem = <i className={iconClass}></i>;
}
if (icon) {
iconElem = <Icon name={icon} />;
}
return (
<div className="share-modal-body">
<div className="share-modal-header">
{showIcon && <div className="share-modal-big-icon">{iconElem}</div>}
{icon && <Icon name={icon} size="xxl" className={cx(iconClass, 'share-modal-big-icon')} />}
<div className="share-modal-content">{children}</div>
</div>
</div>
......
import React from 'react';
import { IconType } from '../Icon/types';
import { IconName } from '../../types';
import { TabsBar } from '../Tabs/TabsBar';
import { Tab } from '../Tabs/Tab';
import { ModalHeader } from './ModalHeader';
......@@ -7,11 +7,11 @@ import { ModalHeader } from './ModalHeader';
interface ModalTab {
value: string;
label: string;
icon?: string;
icon?: IconName;
}
interface Props {
icon: IconType;
icon: IconName;
title: string;
tabs: ModalTab[];
activeTab: string;
......
// Libraries
import React, { FunctionComponent } from 'react';
import { Icon } from '@grafana/ui';
interface Props {
title?: string | JSX.Element;
......@@ -26,7 +27,7 @@ export const PanelOptionsGroup: FunctionComponent<Props> = props => {
<span className="panel-options-group__title">{props.title}</span>
{props.onClose && (
<button className="btn btn-link" onClick={props.onClose}>
<i className="fa fa-remove" />
<Icon name="times" />
</button>
)}
</div>
......
......@@ -3,6 +3,7 @@ import classNames from 'classnames';
import { SelectableValue } from '@grafana/data';
import { css } from 'emotion';
import { Tooltip } from '../Tooltip/Tooltip';
import { Icon } from '../Icon/Icon';
import { ButtonSelect } from '../Forms/Legacy/Select/ButtonSelect';
import memoizeOne from 'memoize-one';
import { GrafanaTheme } from '@grafana/data';
......@@ -87,7 +88,7 @@ export class RefreshPickerBase extends PureComponent<Props> {
className="btn btn--radius-right-0 navbar-button navbar-button--border-right-0"
onClick={onRefresh!}
>
<i className="fa fa-refresh" />
<Icon name="sync" size="lg" />
</button>
</Tooltip>
)}
......
......@@ -8,16 +8,16 @@ import { SelectCommonProps, CustomControlProps } from './types';
import { SelectBase } from './SelectBase';
import { stylesFactory, useTheme } from '../../themes';
import { Icon } from '../Icon/Icon';
import { IconType } from '../Icon/types';
import { IconName } from '../../types';
interface ButtonSelectProps<T> extends Omit<SelectCommonProps<T>, 'renderControl' | 'size' | 'prefix'> {
icon?: IconType;
icon?: IconName;
variant?: ButtonVariant;
size?: ComponentSize;
}
interface SelectButtonProps extends Omit<ButtonProps, 'icon'> {
icon?: IconType;
icon?: IconName;
isOpen?: boolean;
}
......@@ -42,14 +42,12 @@ const SelectButton = React.forwardRef<HTMLButtonElement, SelectButtonProps>(
`,
}));
const styles = getStyles(useTheme());
const buttonIcon = `fa fa-${icon}`;
const caretIcon = isOpen ? 'caret-up' : 'caret-down';
return (
<Button {...buttonProps} ref={ref} icon={buttonIcon}>
<Button {...buttonProps} ref={ref} icon={icon}>
<span className={styles.wrapper}>
<span>{children}</span>
<span className={styles.caretWrap}>
<Icon name={caretIcon} />
<Icon name={isOpen ? 'angle-up' : 'angle-down'} />
</span>
</span>
</Button>
......
......@@ -6,6 +6,6 @@ interface DropdownIndicatorProps {
}
export const DropdownIndicator: React.FC<DropdownIndicatorProps> = ({ isOpen }) => {
const icon = isOpen ? 'caret-up' : 'caret-down';
const icon = isOpen ? 'angle-up' : 'angle-down';
return <Icon name={icon} />;
};
......@@ -2,7 +2,7 @@ import React, { useState } from 'react';
import { Select, AsyncSelect, MultiSelect, AsyncMultiSelect } from './Select';
import { withCenteredStory, withHorizontallyCenteredStory } from '../../utils/storybook/withCenteredStory';
import { SelectableValue } from '@grafana/data';
import { getAvailableIcons, IconType } from '../Icon/types';
import { getAvailableIcons, IconName } from '../../types';
import { select, boolean } from '@storybook/addon-knobs';
import { Icon } from '../Icon/Icon';
import { Button } from '../Button';
......@@ -46,7 +46,7 @@ const getKnobs = () => {
let prefixEl: any = prefix;
if (prefix && prefix.match(/icon-/g)) {
prefixEl = <Icon name={prefix.replace(/icon-/g, '') as IconType} />;
prefixEl = <Icon name={prefix.replace(/icon-/g, '') as IconName} />;
}
return {
......
......@@ -242,7 +242,7 @@ export function SelectBase<T>({
);
},
LoadingIndicator: (props: any) => {
return <Icon name="spinner" className="fa fa-spin" />;
return <i className="fa fa-spinner fa-spin" />;
},
LoadingMessage: (props: any) => {
return <div className={styles.loadingMessage}>{loadingMessage}</div>;
......
import React, { PureComponent } from 'react';
import uniqueId from 'lodash/uniqueId';
import { Tooltip } from '../Tooltip/Tooltip';
import { Icon } from '../Icon/Icon';
import * as PopperJS from 'popper.js';
export interface Props {
......@@ -54,7 +55,7 @@ export class Switch extends PureComponent<Props, State> {
{tooltip && (
<Tooltip placement={tooltipPlacement ? tooltipPlacement : 'auto'} content={tooltip} theme={'info'}>
<div className="gf-form-help-icon gf-form-help-icon--right-normal">
<i className="fa fa-info-circle" />
<Icon name="info-circle" />
</div>
</Tooltip>
)}
......
......@@ -132,7 +132,7 @@ function renderHeaderCell(column: any, tableStyles: TableStyles, field?: Field)
{column.canSort && (
<div {...column.getSortByToggleProps()}>
{column.render('Header')}
{column.isSorted && (column.isSortedDesc ? <Icon name="caret-down" /> : <Icon name="caret-up" />)}
{column.isSorted && (column.isSortedDesc ? <Icon name="angle-down" /> : <Icon name="angle-up" />)}
</div>
)}
{!column.canSort && <div>{column.render('Header')}</div>}
......
import React, { FC } from 'react';
import { css, cx } from 'emotion';
import { GrafanaTheme } from '@grafana/data';
import { Icon } from '../Icon/Icon';
import { IconName } from '../../types';
import { stylesFactory, useTheme } from '../../themes';
export interface TabProps {
label: string;
active?: boolean;
icon?: string;
icon?: IconName;
onChangeTab: () => void;
}
......@@ -26,15 +28,10 @@ const getTabStyles = stylesFactory((theme: GrafanaTheme) => {
color: ${colors.text};
cursor: pointer;
i {
svg {
margin-right: ${theme.spacing.sm};
}
.gicon {
position: relative;
top: -2px;
}
&:hover,
&:focus {
color: ${colors.linkHover};
......@@ -67,7 +64,7 @@ export const Tab: FC<TabProps> = ({ label, active, icon, onChangeTab }) => {
return (
<li className={cx(tabsStyles.tabItem, active && tabsStyles.activeStyle)} onClick={onChangeTab}>
{icon && <i className={icon} />}
{icon && <Icon name={icon} />}
{label}
</li>
);
......
......@@ -182,7 +182,9 @@ export class ThresholdsEditor extends PureComponent<Props, State> {
{isPercent && <div className={styles.percentIcon}>%</div>}
</div>
}
suffix={<Icon className={styles.trashIcon} name="trash" onClick={() => this.onRemoveThreshold(threshold)} />}
suffix={
<Icon className={styles.trashIcon} name="trash-alt" onClick={() => this.onRemoveThreshold(threshold)} />
}
/>
);
}
......@@ -198,7 +200,7 @@ export class ThresholdsEditor extends PureComponent<Props, State> {
return (
<div className={styles.wrapper}>
<FullWidthButtonContainer className={styles.addButton}>
<Button size="sm" icon="fa fa-plus" onClick={() => this.onAddThreshold()} variant="secondary">
<Button size="sm" icon="plus-circle" onClick={() => this.onAddThreshold()} variant="secondary">
Add threshold
</Button>
</FullWidthButtonContainer>
......
......@@ -111,7 +111,7 @@ interface CaretProps {
const Caret: FC<CaretProps> = ({ wrapperStyle = '' }) => {
return (
<div className={wrapperStyle}>
<Icon name="caret-down" />
<Icon name="angle-down" />
</div>
);
};
......@@ -4,6 +4,7 @@ import { css, cx } from 'emotion';
// Components
import { Tooltip } from '../Tooltip/Tooltip';
import { Icon } from '../Icon/Icon';
import { TimePickerContent } from './TimePickerContent/TimePickerContent';
import { ClickOutsideWrapper } from '../ClickOutsideWrapper/ClickOutsideWrapper';
......@@ -63,11 +64,10 @@ const getStyles = stylesFactory((theme: GrafanaTheme) => {
display: flex;
`,
caretIcon: css`
margin-left: 3px;
i {
font-size: ${theme.typography.size.md};
}
margin-left: ${theme.spacing.xs};
`,
clockIcon: css`
margin-right: ${theme.spacing.xs};
`,
noRightBorderStyle: css`
label: noRightBorderStyle;
......@@ -144,7 +144,7 @@ export class UnthemedTimePicker extends PureComponent<Props, State> {
const styles = getStyles(theme);
const hasAbsolute = isDateTime(value.raw.from) || isDateTime(value.raw.to);
const syncedTimePicker = timeSyncButton && isSynced;
const timePickerIconClass = cx('fa fa-clock-o fa-fw', { ['icon-brand-gradient']: syncedTimePicker });
const timePickerIconClass = cx({ ['icon-brand-gradient']: syncedTimePicker });
const timePickerButtonClass = cx('btn navbar-button navbar-button--tight', {
[`btn--radius-right-0 ${styles.noRightBorderStyle}`]: !!timeSyncButton,
[`explore-active-button`]: syncedTimePicker,
......@@ -155,17 +155,15 @@ export class UnthemedTimePicker extends PureComponent<Props, State> {
<div className={styles.buttons}>
{hasAbsolute && (
<button className="btn navbar-button navbar-button--tight" onClick={onMoveBackward}>
<i className="fa fa-chevron-left" />
<Icon name="angle-left" />
</button>
)}
<div>
<Tooltip content={<TimePickerTooltip timeRange={value} />} placement="bottom">
<button aria-label="TimePicker Open Button" className={timePickerButtonClass} onClick={this.onOpen}>
<i className={timePickerIconClass} />
<Icon name="clock-nine" className={cx(styles.clockIcon, timePickerIconClass)} size="lg" />
<TimePickerButtonLabel {...this.props} />
<span className={styles.caretIcon}>
{isOpen ? <i className="fa fa-caret-up fa-fw" /> : <i className="fa fa-caret-down fa-fw" />}
</span>
<span className={styles.caretIcon}>{<Icon name={isOpen ? 'angle-up' : 'angle-down'} size="lg" />}</span>
</button>
</Tooltip>
{isOpen && (
......@@ -186,13 +184,13 @@ export class UnthemedTimePicker extends PureComponent<Props, State> {
{hasAbsolute && (
<button className="btn navbar-button navbar-button--tight" onClick={onMoveForward}>
<i className="fa fa-chevron-right" />
<Icon name="angle-right" size="lg" />
</button>
)}
<Tooltip content={ZoomOutTooltip} placement="bottom">
<button className="btn navbar-button navbar-button--zoom" onClick={onZoom}>
<i className="fa fa-search-minus" />
<Icon name="search-minus" size="lg" />
</button>
</Tooltip>
</div>
......
......@@ -82,7 +82,7 @@ const getBodyStyles = stylesFactory((theme: GrafanaTheme) => {
return {
title: css`
color: ${theme.colors.text}
color: ${theme.colors.text};
background-color: ${colors.background};
font-size: ${theme.typography.size.md};
border: 1px solid transparent;
......
......@@ -3,6 +3,7 @@ import { useMedia } from 'react-use';
import { css } from 'emotion';
import { useTheme, stylesFactory } from '../../../themes';
import { GrafanaTheme, TimeOption, TimeRange, TimeZone, isDateTime } from '@grafana/data';
import { Icon } from '../../Icon/Icon';
import { TimePickerTitle } from './TimePickerTitle';
import { TimeRangeForm } from './TimeRangeForm';
import { CustomScrollbar } from '../../CustomScrollbar/CustomScrollbar';
......@@ -193,7 +194,7 @@ const NarrowScreenForm: React.FC<FormProps> = props => {
<>
<div className={styles.header} onClick={() => setCollapsed(!collapsed)}>
<TimePickerTitle>Absolute time range</TimePickerTitle>
{collapsed ? <i className="fa fa-caret-up" /> : <i className="fa fa-caret-down" />}
{<Icon name={collapsed ? 'angle-up' : 'angle-down'} />}
</div>
{collapsed && (
<div className={styles.body}>
......
......@@ -62,7 +62,7 @@ export const TimeRangeForm: React.FC<Props> = props => {
[timeZone]
);
const icon = isFullscreen ? null : <Button icon="fa fa-calendar" variant="secondary" onClick={onOpen} />;
const icon = isFullscreen ? null : <Button icon="calendar-alt" variant="secondary" onClick={onOpen} />;
return (
<>
......
......@@ -11,8 +11,8 @@ exports[`TimePicker renders buttons correctly 1`] = `
className="btn navbar-button navbar-button--tight"
onClick={[Function]}
>
<i
className="fa fa-chevron-left"
<Icon
name="angle-left"
/>
</button>
<div>
......@@ -38,8 +38,10 @@ exports[`TimePicker renders buttons correctly 1`] = `
className="btn navbar-button navbar-button--tight"
onClick={[Function]}
>
<i
className="fa fa-clock-o fa-fw"
<Icon
className="css-znfyx6"
name="clock-nine"
size="lg"
/>
<Memo()
onChange={[Function]}
......@@ -276,10 +278,11 @@ exports[`TimePicker renders buttons correctly 1`] = `
}
/>
<span
className="css-6x26ye"
className="css-132xth5"
>
<i
className="fa fa-caret-down fa-fw"
<Icon
name="angle-down"
size="lg"
/>
</span>
</button>
......@@ -289,8 +292,9 @@ exports[`TimePicker renders buttons correctly 1`] = `
className="btn navbar-button navbar-button--tight"
onClick={[Function]}
>
<i
className="fa fa-chevron-right"
<Icon
name="angle-right"
size="lg"
/>
</button>
<Component
......@@ -301,8 +305,9 @@ exports[`TimePicker renders buttons correctly 1`] = `
className="btn navbar-button navbar-button--zoom"
onClick={[Function]}
>
<i
className="fa fa-search-minus"
<Icon
name="search-minus"
size="lg"
/>
</button>
</Component>
......@@ -321,8 +326,8 @@ exports[`TimePicker renders content correctly after beeing open 1`] = `
className="btn navbar-button navbar-button--tight"
onClick={[Function]}
>
<i
className="fa fa-chevron-left"
<Icon
name="angle-left"
/>
</button>
<div>
......@@ -348,8 +353,10 @@ exports[`TimePicker renders content correctly after beeing open 1`] = `
className="btn navbar-button navbar-button--tight"
onClick={[Function]}
>
<i
className="fa fa-clock-o fa-fw"
<Icon
className="css-znfyx6"
name="clock-nine"
size="lg"
/>
<Memo()
onChange={[Function]}
......@@ -586,10 +593,11 @@ exports[`TimePicker renders content correctly after beeing open 1`] = `
}
/>
<span
className="css-6x26ye"
className="css-132xth5"
>
<i
className="fa fa-caret-up fa-fw"
<Icon
name="angle-up"
size="lg"
/>
</span>
</button>
......@@ -804,8 +812,9 @@ exports[`TimePicker renders content correctly after beeing open 1`] = `
className="btn navbar-button navbar-button--tight"
onClick={[Function]}
>
<i
className="fa fa-chevron-right"
<Icon
name="angle-right"
size="lg"
/>
</button>
<Component
......@@ -816,8 +825,9 @@ exports[`TimePicker renders content correctly after beeing open 1`] = `
className="btn navbar-button navbar-button--zoom"
onClick={[Function]}
>
<i
className="fa fa-search-minus"
<Icon
name="search-minus"
size="lg"
/>
</button>
</Component>
......
......@@ -126,7 +126,7 @@ const DraggableFieldName: React.FC<DraggableFieldProps> = ({
className={styles.toggle}
variant="link"
size="md"
icon={visible ? 'fa fa-eye' : 'fa fa-eye-slash'}
icon={visible ? 'eye' : 'eye-slash'}
onClick={() => onToggleVisibility(fieldName, visible)}
/>
<span className={styles.name}>{fieldName}</span>
......
......@@ -98,7 +98,7 @@ export class LegacyValueMappingsEditor extends PureComponent<Props, State> {
removeValueMapping={() => this.onRemoveMapping(valueMapping.id)}
/>
))}
<Button variant="primary" icon="fa fa-plus" onClick={this.onAddMapping}>
<Button variant="primary" icon="plus-circle" onClick={this.onAddMapping}>
Add mapping
</Button>
</div>
......
......@@ -68,7 +68,7 @@ export const ValueMappingsEditor: React.FC<Props> = ({ valueMappings, onChange,
<FullWidthButtonContainer>
<Button
size="sm"
icon="fa fa-plus"
icon="plus-circle"
onClick={onAdd}
aria-label="ValueMappingsEditor add mapping button"
variant="secondary"
......
......@@ -35,7 +35,7 @@ exports[`Render should render component 1`] = `
}
/>
<Button
icon="fa fa-plus"
icon="plus-circle"
onClick={[Function]}
variant="primary"
>
......
import React, { useState } from 'react';
import { IconType } from '../Icon/types';
import { IconName } from '../../types';
import { SelectableValue } from '@grafana/data';
import { Button, ButtonVariant } from '../Button';
import { Select } from '../Select/Select';
......@@ -9,7 +9,7 @@ interface ValuePickerProps<T> {
/** Label to display on the picker button */
label: string;
/** Icon to display on the picker button */
icon?: IconType;
icon?: IconName;
/** ValuePicker options */
options: Array<SelectableValue<T>>;
onChange: (value: SelectableValue<T>) => void;
......@@ -23,7 +23,7 @@ export function ValuePicker<T>({ label, icon, options, onChange, variant }: Valu
<>
{!isPicking && (
<FullWidthButtonContainer>
<Button size="sm" icon={`fa fa-${icon || 'plus'}`} onClick={() => setIsPicking(true)} variant={variant}>
<Button size="sm" icon={icon || 'plus-circle'} onClick={() => setIsPicking(true)} variant={variant}>
{label}
</Button>
</FullWidthButtonContainer>
......
import { ComponentSize } from './size';
export type IconType = 'mono' | 'default';
export type IconSize = ComponentSize | 'xl' | 'xxl';
export type IconName =
| 'fa fa-fw fa-unlock'
| 'fa fa-envelope'
| 'fa fa-spinner'
| 'question-circle'
| 'angle-up'
| 'history'
| 'angle-down'
| 'filter'
| 'angle-left'
| 'angle-right'
| 'pen'
| 'plane'
| 'power'
| 'trash-alt'
| 'plus-square'
| 'folder-plus'
| 'folder-open'
| 'file-copy-alt'
| 'exchange-alt'
| 'import'
| 'exclamation-triangle'
| 'times'
| 'cloud-upload'
| 'step-backward'
| 'square-shape'
| 'share-alt'
| 'tag-alt'
| 'forward'
| 'check'
| 'add-panel'
| 'copy'
| 'lock'
| 'panel-add'
| 'arrow-random'
| 'arrow-down'
| 'comment-alt'
| 'arrow-right'
| 'arrow-up'
| 'arrow-from-right'
| 'keyboard'
| 'search'
| 'chart-line'
| 'search-minus'
| 'clock-nine'
| 'sync'
| 'signin'
| 'cog'
| 'bars'
| 'save'
| 'apps'
| 'folder-plus'
| 'link'
| 'upload'
| 'columns'
| 'home-alt'
| 'channel-add'
| 'calendar-alt'
| 'play'
| 'pause'
| 'calculator-alt'
| 'compass'
| 'sliders-v-alt'
| 'bell'
| 'database'
| 'user'
| 'camera'
| 'plug'
| 'shield'
| 'key-skeleton-alt'
| 'users-alt'
| 'graph-bar'
| 'book'
| 'bolt'
| 'comments-alt'
| 'document-info'
| 'info-circle'
| 'bug'
| 'cube'
| 'star'
| 'list-ul'
| 'edit'
| 'shield'
| 'eye'
| 'eye-slash'
| 'filter'
| 'monitor'
| 'plus-circle'
| 'arrow-left'
| 'repeat'
| 'external-link-alt'
| 'minus'
| 'signal'
| 'search-plus'
| 'search-minus'
| 'table'
| 'plus'
| 'heart'
| 'favorite';
export const getAvailableIcons = (): IconName[] => [
'question-circle',
'plane',
'plus',
'plus-circle',
'angle-up',
'shield',
'angle-down',
'angle-left',
'angle-right',
'calendar-alt',
'tag-alt',
'calculator-alt',
'pen',
'repeat',
'external-link-alt',
'power',
'play',
'pause',
'trash-alt',
'exclamation-triangle',
'times',
'step-backward',
'square-shape',
'share-alt',
'camera',
'forward',
'check',
'add-panel',
'copy',
'lock',
'panel-add',
'arrow-random',
'arrow-from-right',
'arrow-left',
'keyboard',
'search',
'chart-line',
'search-minus',
'clock-nine',
'sync',
'signin',
'cog',
'bars',
'save',
'apps',
'folder-plus',
'link',
'upload',
'home-alt',
'compass',
'sliders-v-alt',
'bell',
'database',
'user',
'plug',
'shield',
'key-skeleton-alt',
'users-alt',
'graph-bar',
'book',
'bolt',
'cloud-upload',
'comments-alt',
'list-ul',
'document-info',
'info-circle',
'bug',
'cube',
'history',
'star',
'edit',
'columns',
'eye',
'channel-add',
'monitor',
'favorite',
'folder-plus',
'plus-square',
'import',
'folder-open',
'file-copy-alt',
'arrow-down',
'filter',
'arrow-up',
'exchange-alt',
];
......@@ -3,3 +3,4 @@ export * from './input';
export * from './completion';
export * from './storybook';
export * from './forms';
export * from './icon';
import { select } from '@storybook/addon-knobs';
import { getAvailableIcons } from '../../components/Icon/types';
import { getAvailableIcons } from '../../types';
const VISUAL_GROUP = 'Visual options';
......
......@@ -44,7 +44,7 @@ func (l *OSSLicensingService) Init() error {
Text: "Upgrade",
Id: "upgrading",
Url: l.LicenseURL(req.SignedInUser),
Icon: "fa fa-fw fa-unlock-alt",
Icon: "unlock",
})
}
}
......
......@@ -16,6 +16,7 @@ import {
SecretFormField,
SeriesColorPickerPopoverWithTheme,
UnitPicker,
Icon,
} from '@grafana/ui';
import { FunctionEditor } from 'app/plugins/datasource/graphite/FunctionEditor';
import ReactProfileWrapper from 'app/features/profile/ReactProfileWrapper';
......@@ -32,6 +33,13 @@ import { SearchField, SearchResults, SearchResultsFilter } from '../features/sea
export function registerAngularDirectives() {
react2AngularDirective('footer', Footer, []);
react2AngularDirective('icon', Icon, [
'color',
'name',
'size',
'type',
['onClick', { watchDepth: 'reference', wrapApply: true }],
]);
react2AngularDirective('helpModal', HelpModal, []);
react2AngularDirective('sidemenu', SideMenu, []);
react2AngularDirective('functionEditor', FunctionEditor, ['func', 'onRemove', 'onMoveLeft', 'onMoveRight']);
......
......@@ -20,7 +20,7 @@ export const ErrorLoadingChunk: FunctionComponent<Props> = ({ error }) => (
<h2 className="page-heading">Grafana has likely been updated. Please try reloading the page.</h2>
<br />
<div className="gf-form-group">
<Button size="md" variant="secondary" icon="fa fa-repeat" onClick={() => window.location.reload()}>
<Button size="md" variant="secondary" icon="repeat" onClick={() => window.location.reload()}>
Reload
</Button>
</div>
......
import React, { MouseEvent, useContext } from 'react';
import { CallToActionCard, LinkButton, ThemeContext } from '@grafana/ui';
import { CallToActionCard, LinkButton, ThemeContext, Icon, IconName } from '@grafana/ui';
import { css } from 'emotion';
export interface Props {
title: string;
buttonIcon: string;
buttonIcon: IconName;
buttonLink?: string;
buttonTitle: string;
onClick?: (event: MouseEvent) => void;
......@@ -45,7 +45,7 @@ const EmptyListCTA: React.FunctionComponent<Props> = ({
<>
{proTip ? (
<span key="proTipFooter">
<i className="fa fa-rocket" />
<Icon name="plane" />
<> ProTip: {proTip} </>
<a href={proTipLink} target={proTipTarget} className="text-link">
{proTipLinkTitle}
......
......@@ -12,19 +12,19 @@ export let getFooterLinks = (): FooterLink[] => {
return [
{
text: 'Documentation',
icon: 'fa fa-file-code-o',
icon: 'document-info',
url: 'https://grafana.com/docs/grafana/latest/?utm_source=grafana_footer',
target: '_blank',
},
{
text: 'Support',
icon: 'fa fa-support',
icon: 'question-circle',
url: 'https://grafana.com/products/enterprise/?utm_source=grafana_footer',
target: '_blank',
},
{
text: 'Community',
icon: 'fa fa-comments-o',
icon: 'comments-alt',
url: 'https://community.grafana.com/?utm_source=grafana_footer',
target: '_blank',
},
......
import React, { FC } from 'react';
import { Icon } from '@grafana/ui';
export type LayoutMode = LayoutModes.Grid | LayoutModes.List;
......@@ -22,7 +23,7 @@ const LayoutSelector: FC<Props> = props => {
}}
className={mode === LayoutModes.List ? 'active' : ''}
>
<i className="fa fa-list" />
<Icon name="list-ul" />
</button>
<button
onClick={() => {
......@@ -30,7 +31,7 @@ const LayoutSelector: FC<Props> = props => {
}}
className={mode === LayoutModes.Grid ? 'active' : ''}
>
<i className="fa fa-th" />
<Icon name="table" />
</button>
</div>
);
......
......@@ -45,7 +45,7 @@ export class OrgSwitcher extends React.PureComponent<Props, State> {
const currentOrgId = contextSrv.user.orgId;
return (
<Modal title="Switch Organization" icon="random" onDismiss={onDismiss} isOpen={true}>
<Modal title="Switch Organization" icon="arrow-random" onDismiss={onDismiss} isOpen={true}>
<table className="filter-table form-inline">
<thead>
<tr>
......
import React, { FormEvent } from 'react';
import { Tab, TabsBar } from '@grafana/ui';
import { css } from 'emotion';
import { Tab, TabsBar, Icon, IconName } from '@grafana/ui';
import appEvents from 'app/core/app_events';
import { NavModel, NavModelItem, NavModelBreadcrumb } from '@grafana/data';
import { CoreEvents } from 'app/types';
......@@ -65,7 +66,7 @@ const Navigation = ({ main }: { main: NavModelItem }) => {
label={child.text}
active={child.active}
key={`${child.url}-${index}`}
icon={child.icon}
icon={child.icon as IconName}
onChangeTab={() => goToUrl(index)}
/>
)
......@@ -113,10 +114,19 @@ export default class PageHeader extends React.Component<Props, any> {
}
renderHeaderTitle(main: NavModelItem) {
const iconClassName =
main.icon === 'grafana'
? css`
margin-top: 12px;
`
: css`
margin-top: 14px;
`;
return (
<div className="page-header__inner">
<span className="page-header__logo">
{main.icon && <i className={`page-header__icon ${main.icon}`} />}
{main.icon && <Icon name={main.icon as IconName} size="xxl" className={iconClassName} />}
{main.img && <img className="page-header__img" src={main.img} />}
</span>
......
import React, { Component } from 'react';
import { UserPicker } from 'app/core/components/Select/UserPicker';
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 { User } from 'app/types';
import {
......@@ -91,7 +91,7 @@ class AddPermissions extends Component<Props, NewDashboardAclItem> {
return (
<div className="gf-form-inline cta-form">
<button className="cta-form__close btn btn-transparent" onClick={onCancel}>
<i className="fa fa-close" />
<Icon name="times" />
</button>
<form name="addPermission" onSubmit={this.onSubmit}>
<h5>Add Permission For</h5>
......
import React, { Component } from 'react';
import { LegacyForms } from '@grafana/ui';
import { LegacyForms, Icon } from '@grafana/ui';
import { dashboardPermissionLevels } from 'app/types/acl';
const { Select } = LegacyForms;
......@@ -15,7 +15,7 @@ export default class DisabledPermissionListItem extends Component<Props, any> {
return (
<tr className="gf-form-disabled">
<td style={{ width: '1%' }}>
<i style={{ width: '25px', height: '25px' }} className="gicon gicon-shield" />
<Icon size="lg" name="shield" />
</td>
<td style={{ width: '90%' }}>
{item.name}
......@@ -36,7 +36,7 @@ export default class DisabledPermissionListItem extends Component<Props, any> {
</td>
<td>
<button className="btn btn-inverse btn-small">
<i className="fa fa-lock" />
<Icon name="lock" />
</button>
</td>
</tr>
......
import React, { PureComponent } from 'react';
import { LegacyForms } from '@grafana/ui';
import { LegacyForms, Icon } from '@grafana/ui';
import { SelectableValue } from '@grafana/data';
import { dashboardPermissionLevels, DashboardAcl, PermissionLevel } from 'app/types/acl';
import { FolderInfo } from 'app/types';
......@@ -17,10 +17,10 @@ function ItemAvatar({ item }: { item: DashboardAcl }) {
return <img className="filter-table__avatar" src={item.teamAvatarUrl} />;
}
if (item.role === 'Editor') {
return <i style={{ width: '25px', height: '25px' }} className="gicon gicon-editor" />;
return <Icon size="lg" name="edit" />;
}
return <i style={{ width: '25px', height: '25px' }} className="gicon gicon-viewer" />;
return <Icon size="lg" name="eye" />;
}
function ItemDescription({ item }: { item: DashboardAcl }) {
......@@ -89,11 +89,11 @@ export default class PermissionsListItem extends PureComponent<Props> {
<td>
{!item.inherited ? (
<a className="btn btn-danger btn-small" onClick={this.onRemoveItem}>
<i className="fa fa-remove" />
<Icon name="times" />
</a>
) : (
<button className="btn btn-inverse btn-small">
<i className="fa fa-lock" />
<Icon name="lock" />
</button>
)}
</td>
......
import React from 'react';
import { getTagColorsFromName } from '@grafana/ui';
import { getTagColorsFromName, Icon } from '@grafana/ui';
export interface Props {
label: string;
......@@ -24,7 +24,7 @@ export class TagBadge extends React.Component<Props, any> {
return (
<span className={`label label-tag`} style={tagStyle}>
{removeIcon && <i className="fa fa-remove" />}
{removeIcon && <Icon name="times" />}
{label} {countLabel}
</span>
);
......
......@@ -8,7 +8,7 @@ import { escapeStringForRegex } from '@grafana/data';
// Components
import { TagOption } from './TagOption';
import { TagBadge } from './TagBadge';
import { resetSelectStyles, LegacyForms } from '@grafana/ui';
import { resetSelectStyles, LegacyForms, Icon } from '@grafana/ui';
const { IndicatorsContainer, NoOptionsMessage } = LegacyForms;
export interface TermCount {
......@@ -90,7 +90,7 @@ export class TagFilter extends React.Component<Props, any> {
<div className="tag-filter">
<AsyncSelect {...selectOptions} />
</div>
<i className="gf-form-input-icon fa fa-tag" />
<Icon className="gf-form-input-icon" name="tag-alt" />
</div>
);
}
......
import React from 'react';
import { appEvents } from 'app/core/core';
import { Icon } from '@grafana/ui';
export class HelpModal extends React.PureComponent {
static tabIndex = 0;
......@@ -52,11 +53,11 @@ export class HelpModal extends React.PureComponent {
<div className="modal-body">
<div className="modal-header">
<h2 className="modal-header-title">
<i className="fa fa-keyboard-o" />
<Icon name="keyboard" size="lg" />
<span className="p-l-1">Shortcuts</span>
</h2>
<a className="modal-header-close" onClick={this.dismiss}>
<i className="fa fa-remove" />
<Icon name="times" style={{ margin: '3px 0 0 0' }} />
</a>
</div>
......
......@@ -6,7 +6,7 @@ import Drop from 'tether-drop';
export function infoPopover() {
return {
restrict: 'E',
template: '<i class="fa fa-info-circle"></i>',
template: `<icon name="'info-circle'" style="margin-left: 10px;" size="'xs'"></icon>`,
transclude: true,
link: (scope: any, elem: any, attrs: any, ctrl: any, transclude: any) => {
const offset = attrs.offset || '0 -10px';
......
......@@ -9,7 +9,7 @@ const template = `
<i class="fa fa-list"></i>
</button>
<button ng-click="ctrl.gridView()" ng-class="{active: ctrl.mode === 'grid'}">
<i class="fa fa-th"></i>
<icon name="table"></i>
</button>
</div>
`;
......
<div class="dashboard-list">
<div class="page-action-bar page-action-bar--narrow" ng-hide="ctrl.folderId && !ctrl.hasFilters && ctrl.sections.length === 0">
<div
class="page-action-bar page-action-bar--narrow"
ng-hide="ctrl.folderId && !ctrl.hasFilters && ctrl.sections.length === 0"
>
<label class="gf-form gf-form--grow gf-form--has-input-icon">
<input type="text" class="gf-form-input max-width-30" placeholder="Search dashboards by name" tabindex="1" give-focus="true" ng-model="ctrl.query.query" ng-model-options="{ debounce: 500 }" spellcheck='false' ng-change="ctrl.onQueryChange()" />
<i class="gf-form-input-icon fa fa-search"></i>
<input
type="text"
class="gf-form-input max-width-30"
placeholder="Search dashboards by name"
tabindex="1"
give-focus="true"
ng-model="ctrl.query.query"
ng-model-options="{ debounce: 500 }"
spellcheck="false"
ng-change="ctrl.onQueryChange()"
/>
<icon class="gf-form-input-icon" name="'search'"></icon>
</label>
<div class="page-action-bar__spacer"></div>
<a class="btn btn-primary" ng-href="{{ctrl.createDashboardUrl()}}" ng-if="ctrl.hasEditPermissionInFolders || ctrl.canSave">
<a
class="btn btn-primary"
ng-href="{{ctrl.createDashboardUrl()}}"
ng-if="ctrl.hasEditPermissionInFolders || ctrl.canSave"
>
New Dashboard
</a>
<a class="btn btn-primary" href="dashboards/folder/new" ng-if="!ctrl.folderId && ctrl.isEditor">
New Folder
</a>
<a class="btn btn-primary" href="{{ctrl.importDashboardUrl()}}" ng-if="ctrl.hasEditPermissionInFolders || ctrl.canSave">
<a
class="btn btn-primary"
href="{{ctrl.importDashboardUrl()}}"
ng-if="ctrl.hasEditPermissionInFolders || ctrl.canSave"
>
Import
</a>
</div>
......@@ -25,22 +46,20 @@
<div class="gf-form-input gf-form-input--plaintext" ng-show="ctrl.query.tag.length > 0">
<span ng-repeat="tagName in ctrl.query.tag">
<a ng-click="ctrl.removeTag(tagName, $event)" tag-color-from-name="tagName" class="tag label label-tag">
<i class="fa fa-remove"></i>&nbsp;{{tagName}}
<icon name="'times'"></icon>&nbsp;{{tagName}}
</a>
</span>
</div>
</div>
<div class="gf-form" ng-show="ctrl.query.starred">
<label class="gf-form-label">
<a class="pointer" ng-click="ctrl.removeStarred()">
<i class="fa fa-fw fa-check"></i> Starred
</a>
<a class="pointer" ng-click="ctrl.removeStarred()"> <icon name="'check'"></icon> Starred </a>
</label>
</div>
<div class="gf-form">
<label class="gf-form-label">
<a class="pointer" ng-click="ctrl.clearFilters()" bs-tooltip="'Clear current search query and filters'">
<i class="fa fa-remove"></i>&nbsp;Clear
<icon name="'times'"></icon>&nbsp;Clear
</a>
</label>
</div>
......@@ -53,25 +72,25 @@
</em>
</div>
<div class="search-results" ng-show="!ctrl.folderId && !ctrl.hasFilters && ctrl.sections.length === 0">
<div class="search-results" ng-show="!ctrl.folderId && !ctrl.hasFilters && ctrl.sections.length === 0">
<em class="muted">
No dashboards found.
No dashboards found.
</em>
</div>
<div class="search-results" ng-show="ctrl.sections.length > 0">
<search-filters
on-select-all-changed="ctrl.onSelectAllChanged"
all-checked="ctrl.selectAllChecked"
can-move="ctrl.canMove"
can-delete="ctrl.canDelete"
move-to="ctrl.moveTo"
delete-item="ctrl.delete"
tag-filter-options="ctrl.tagFilterOptions"
selected-starred-filter="ctrl.selectedStarredFilter"
on-starred-filter-change="ctrl.onStarredFilterChange"
selected-tag-filter="ctrl.selectedTagFilter"
on-tagfilter-change="ctrl.onTagFilterChange"
on-select-all-changed="ctrl.onSelectAllChanged"
all-checked="ctrl.selectAllChecked"
can-move="ctrl.canMove"
can-delete="ctrl.canDelete"
move-to="ctrl.moveTo"
delete-item="ctrl.delete"
tag-filter-options="ctrl.tagFilterOptions"
selected-starred-filter="ctrl.selectedStarredFilter"
on-starred-filter-change="ctrl.onStarredFilterChange"
selected-tag-filter="ctrl.selectedTagFilter"
on-tagfilter-change="ctrl.onTagFilterChange"
/>
<div class="search-results-container">
<search-results
......@@ -83,13 +102,12 @@
/>
</div>
</div>
</div>
<div ng-if="ctrl.canSave && ctrl.folderId && !ctrl.hasFilters && ctrl.sections.length === 0">
<empty-list-cta
title="'This folder doesn\'t have any dashboards yet'"
buttonIcon="'gicon gicon-dashboard-new'"
buttonIcon="'plus-circle'"
buttonLink="'dashboard/new?folderId={{ctrl.folderId}}'"
buttonTitle="'Create Dashboard'"
proTip="'Add/move dashboards to your folder at ->'"
......
<div class="search-backdrop" ng-if="ctrl.isOpen"></div>
<div class="search-container" ng-if="ctrl.isOpen">
<search-field
query="ctrl.query"
autoFocus="ctrl.giveSearchFocus"
......@@ -10,8 +8,7 @@
on-key-down="ctrl.onKeyDown"
/>
<div class="search-dropdown">
<div class="search-dropdown">
<div class="search-dropdown__col_1">
<div class="search-results-scroller">
<div class="search-results-container" grafana-scrollbar>
......@@ -29,32 +26,38 @@
<div class="search-dropdown__col_2">
<div class="search-filter-box" ng-click="ctrl.onFilterboxClick()">
<div class="search-filter-box__header">
<i class="fa fa-filter"></i>
<icon name="'filter'"></icon>
Filter by:
<a class="pointer pull-right small" ng-click="ctrl.clearSearchFilter()">
<i class="fa fa-remove"></i> Clear
<icon name="'times'" size="'sm'"></icon> Clear
</a>
</div>
<tag-filter tags="ctrl.query.tags" tagOptions="ctrl.getTags" on-change="ctrl.onTagFiltersChanged">
</tag-filter>
<tag-filter tags="ctrl.query.tags" tagOptions="ctrl.getTags" on-change="ctrl.onTagFiltersChanged"> </tag-filter>
</div>
<div class="search-filter-box" ng-if="ctrl.isEditor || ctrl.hasEditPermissionInFolders">
<a href="dashboard/new" class="search-filter-box-link">
<i class="gicon gicon-dashboard-new"></i> New dashboard
<icon name="'plus-square'" size="'xl'" style="margin-right: 8px;"></icon> New dashboard
</a>
<a href="dashboards/folder/new" class="search-filter-box-link" ng-if="ctrl.isEditor">
<i class="gicon gicon-folder-new"></i> New folder
<icon name="'folder-plus'" size="'xl'" style="margin-right: 8px;"></icon> New folder
</a>
<a href="dashboard/import" class="search-filter-box-link" ng-if="ctrl.isEditor || ctrl.hasEditPermissionInFolders">
<i class="gicon gicon-dashboard-import"></i> Import dashboard
<a
href="dashboard/import"
class="search-filter-box-link"
ng-if="ctrl.isEditor || ctrl.hasEditPermissionInFolders"
>
<icon name="'import'" size="'xl'" style="margin-right: 8px;"></icon> Import dashboard
</a>
<a class="search-filter-box-link" target="_blank" href="https://grafana.com/dashboards?utm_source=grafana_search">
<img src="public/img/icn-dashboard-tiny.svg" width="20" /> Find dashboards on Grafana.com
<a
class="search-filter-box-link"
target="_blank"
href="https://grafana.com/dashboards?utm_source=grafana_search"
>
<icon name="'apps'" size="'xl'" style="margin-right: 8px;"></icon> Find dashboards on Grafana.com
</a>
</div>
</div>
</div>
</div>
<div ng-repeat="section in ctrl.results" class="search-section">
<div class="search-section__header pointer" ng-hide="section.hideHeader" ng-class="{'selected': section.selected}" ng-click="ctrl.toggleFolderExpand(section)">
<div
class="search-section__header pointer"
ng-hide="section.hideHeader"
ng-class="{'selected': section.selected}"
ng-click="ctrl.toggleFolderExpand(section)"
>
<div ng-click="ctrl.toggleSelection(section, $event)" class="center-vh">
<gf-form-checkbox
ng-show="ctrl.editable"
on-change="ctrl.selectionChanged($event)"
checked="section.checked"
switch-class="gf-form-checkbox--transparent">
ng-show="ctrl.editable"
on-change="ctrl.selectionChanged($event)"
checked="section.checked"
switch-class="gf-form-checkbox--transparent"
>
</gf-form-checkbox>
</div>
<i class="search-section__header__icon" ng-class="section.icon"></i>
<span class="search-section__header__text">{{::section.title}}</span>
<a ng-show="section.url" href="{{section.url}}" class="search-section__header__link">
<i class="gicon gicon-cog"></i>
<icon name="'cog'"></icon>
</a>
<i class="fa fa-angle-down search-section__header__toggle" ng-show="section.expanded"></i>
<i class="fa fa-angle-right search-section__header__toggle" ng-hide="section.expanded"></i>
<icon class="search-section__header__toggle" name="'angle-down'" ng-show="section.expanded"></icon>
<icon class="search-section__header__toggle" name="'angle-right'" ng-hide="section.expanded"></icon>
</div>
<div class="search-section__header" ng-show="section.hideHeader"></div>
<div ng-if="section.expanded">
<a ng-repeat="item in section.items" class="search-item search-item--indent" ng-class="{'selected': item.selected}" ng-href="{{::item.url}}" aria-label={{ctrl.selectors.dashboards(item.title)}}>
<a
ng-repeat="item in section.items"
class="search-item search-item--indent"
ng-class="{'selected': item.selected}"
ng-href="{{::item.url}}"
aria-label="{{ctrl.selectors.dashboards(item.title)}}"
>
<div ng-click="ctrl.toggleSelection(item, $event)" class="center-vh">
<gf-form-checkbox
ng-show="ctrl.editable"
on-change="ctrl.selectionChanged()"
checked="item.checked"
switch-class="gf-form-checkbox--transparent">
ng-show="ctrl.editable"
on-change="ctrl.selectionChanged()"
checked="item.checked"
switch-class="gf-form-checkbox--transparent"
>
</gf-form-checkbox>
</div>
<span class="search-item__icon">
......@@ -37,11 +50,15 @@
<span class="search-item__body-folder-title">{{::item.folderTitle}}</span>
</span>
<span class="search-item__tags">
<span ng-click="ctrl.selectTag(tag, $event)" ng-repeat="tag in item.tags" tag-color-from-name="tag" class="label label-tag">
<span
ng-click="ctrl.selectTag(tag, $event)"
ng-repeat="tag in item.tags"
tag-color-from-name="tag"
class="label label-tag"
>
{{tag}}
</span>
</span>
</a>
</div>
</div>
import React, { PureComponent } from 'react';
import { css } from 'emotion';
import appEvents from '../../app_events';
import { User } from '../../services/context_srv';
import { NavModelItem } from '@grafana/data';
import { Icon, IconName } from '@grafana/ui';
import { CoreEvents } from 'app/types';
import { OrgSwitcher } from '../OrgSwitcher';
import { getFooterLinks } from '../Footer/Footer';
......@@ -15,7 +17,7 @@ interface State {
showSwitcherModal: boolean;
}
class BottomNavLinks extends PureComponent<Props, State> {
export default class BottomNavLinks extends PureComponent<Props, State> {
state: State = {
showSwitcherModal: false,
};
......@@ -35,6 +37,9 @@ class BottomNavLinks extends PureComponent<Props, State> {
render() {
const { link, user } = this.props;
const { showSwitcherModal } = this.state;
const subMenuIconClassName = css`
margin-right: 8px;
`;
let children = link.children || [];
......@@ -46,7 +51,7 @@ class BottomNavLinks extends PureComponent<Props, State> {
<div className="sidemenu-item dropdown dropup">
<a href={link.url} className="sidemenu-link" target={link.target}>
<span className="icon-circle sidemenu-icon">
{link.icon && <i className={link.icon} />}
{link.icon && <Icon name={link.icon as IconName} size="xl" />}
{link.img && <img src={link.img} />}
</span>
</a>
......@@ -64,7 +69,7 @@ class BottomNavLinks extends PureComponent<Props, State> {
<div className="sidemenu-org-switcher__org-name">{user.orgName}</div>
</div>
<div className="sidemenu-org-switcher__switch">
<i className="fa fa-fw fa-random" />
<Icon name="arrow-random" className={subMenuIconClassName} />
Switch
</div>
</a>
......@@ -77,7 +82,7 @@ class BottomNavLinks extends PureComponent<Props, State> {
return (
<li key={`${child.text}-${index}`}>
<a href={child.url} target={child.target} rel="noopener">
{child.icon && <i className={child.icon} />}
{child.icon && <Icon name={child.icon as IconName} className={subMenuIconClassName} />}
{child.text}
</a>
</li>
......@@ -87,7 +92,7 @@ class BottomNavLinks extends PureComponent<Props, State> {
{link.id === 'help' && (
<li key="keyboard-shortcuts">
<a onClick={() => this.onOpenShortcuts()}>
<i className="fa fa-keyboard-o" /> Keyboard shortcuts
<Icon name="keyboard" className={subMenuIconClassName} /> Keyboard shortcuts
</a>
</li>
)}
......@@ -100,5 +105,3 @@ class BottomNavLinks extends PureComponent<Props, State> {
);
}
}
export default BottomNavLinks;
import React, { FC } from 'react';
import { css } from 'emotion';
import { Icon, IconName, useTheme } from '@grafana/ui';
export interface Props {
child: any;
......@@ -7,11 +9,15 @@ export interface Props {
const DropDownChild: FC<Props> = props => {
const { child } = props;
const listItemClassName = child.divider ? 'divider' : '';
const theme = useTheme();
const iconClassName = css`
margin-right: ${theme.spacing.sm};
`;
return (
<li className={listItemClassName}>
<a href={child.url}>
{child.icon && <i className={child.icon} />}
{child.icon && <Icon name={child.icon as IconName} className={iconClassName} />}
{child.text}
</a>
</li>
......
......@@ -5,6 +5,7 @@ import BottomSection from './BottomSection';
import config from 'app/core/config';
import { CoreEvents } from 'app/types';
import { Branding } from 'app/core/components/Branding/Branding';
import { Icon } from '@grafana/ui';
const homeUrl = config.appSubUrl || '/';
......@@ -19,9 +20,9 @@ export class SideMenu extends PureComponent {
<Branding.MenuLogo />
</a>,
<div className="sidemenu__logo_small_breakpoint" onClick={this.toggleSideMenuSmallBreakpoint} key="hamburger">
<i className="fa fa-bars" />
<Icon name="bars" />
<span className="sidemenu__close">
<i className="fa fa-times" />
<Icon name="times" />
&nbsp;Close
</span>
</div>,
......
import React, { FC } from 'react';
import SideMenuDropDown from './SideMenuDropDown';
import { Icon } from '@grafana/ui';
export interface Props {
link: any;
......@@ -11,7 +12,7 @@ const TopSectionItem: FC<Props> = props => {
<div className="sidemenu-item dropdown">
<a className="sidemenu-link" href={link.url} target={link.target}>
<span className="icon-circle sidemenu-icon">
<i className={link.icon} />
<Icon name={link.icon} size="xl" />
{link.img && <img src={link.img} />}
</span>
</a>
......
......@@ -118,8 +118,9 @@ exports[`Render should render organization switcher 1`] = `
<div
className="sidemenu-org-switcher__switch"
>
<i
className="fa fa-fw fa-random"
<Icon
className="css-f8is2k"
name="arrow-random"
/>
Switch
</div>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment