mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
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>
This commit is contained in:
parent
3fae28be52
commit
d6292ac15b
@ -1,3 +1,4 @@
|
||||
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);
|
||||
};
|
||||
|
@ -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-color": "3.0.1",
|
||||
"@types/react-select": "3.0.8",
|
||||
|
@ -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>
|
||||
);
|
||||
|
@ -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>
|
||||
|
@ -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'),
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
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;
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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}
|
||||
|
@ -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 '../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';
|
||||
|
@ -3,7 +3,6 @@ import { GrafanaTheme } from '@grafana/data';
|
||||
import { css, cx } from 'emotion';
|
||||
import { getFocusStyle, inputSizes, sharedInputStyle } from '../commonStyles';
|
||||
import { stylesFactory, useTheme } from '../../../themes';
|
||||
import { Icon } from '../../Icon/Icon';
|
||||
import { useClientRect } from '../../../utils/useClientRect';
|
||||
import { FormInputSize } from '../types';
|
||||
|
||||
@ -246,7 +245,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 && <i className={cx('fa fa-spinner fa-spin', styles.loadingIndicator)} />}
|
||||
{suffix}
|
||||
</div>
|
||||
)}
|
||||
|
@ -1,28 +0,0 @@
|
||||
import { Meta, Story, Preview, Props } from '@storybook/addon-docs/blocks';
|
||||
import { Icon } from './Icon';
|
||||
|
||||
<Meta title="MDX|Icon" component={Icon} />
|
||||
|
||||
# Icon
|
||||
|
||||
Grafana's wrapper component over [Font Awesome](https://fontawesome.com/) icons
|
||||
|
||||
|
||||
### Changing icon size
|
||||
|
||||
By default `Icon` has width and height of `16px` and font-size of `14px`. Pass `className` to control icon's size:
|
||||
|
||||
```jsx
|
||||
import { css } from 'emotion';
|
||||
|
||||
const customIconSize = css`
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
font-size: 18px;
|
||||
`;
|
||||
|
||||
<Icon name="check" className={customIconSize} />
|
||||
```
|
||||
|
||||
|
||||
<Props of={Icon} />
|
@ -1,103 +0,0 @@
|
||||
import React, { ChangeEvent, useState } from 'react';
|
||||
import { css } from 'emotion';
|
||||
|
||||
import { Forms } from '../index';
|
||||
import { Icon } from './Icon';
|
||||
import { getAvailableIcons, IconType } from './types';
|
||||
import { withCenteredStory } from '../../utils/storybook/withCenteredStory';
|
||||
import { useTheme, selectThemeVariant } from '../../themes';
|
||||
import mdx from './Icon.mdx';
|
||||
|
||||
export default {
|
||||
title: 'General/Icon',
|
||||
component: Icon,
|
||||
decorators: [withCenteredStory],
|
||||
parameters: {
|
||||
docs: {
|
||||
page: mdx,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const IconWrapper: React.FC<{ name: IconType }> = ({ name }) => {
|
||||
const theme = useTheme();
|
||||
const borderColor = selectThemeVariant(
|
||||
{
|
||||
light: theme.colors.gray5,
|
||||
dark: theme.colors.dark6,
|
||||
},
|
||||
theme.type
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={css`
|
||||
width: 150px;
|
||||
padding: 12px;
|
||||
border: 1px solid ${borderColor};
|
||||
text-align: center;
|
||||
|
||||
&:hover {
|
||||
background: ${borderColor};
|
||||
}
|
||||
`}
|
||||
>
|
||||
<Icon
|
||||
name={name}
|
||||
className={css`
|
||||
font-size: 18px;
|
||||
`}
|
||||
/>
|
||||
<div
|
||||
className={css`
|
||||
padding-top: 16px;
|
||||
word-break: break-all;
|
||||
font-family: ${theme.typography.fontFamily.monospace};
|
||||
font-size: ${theme.typography.size.xs};
|
||||
`}
|
||||
>
|
||||
{name}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const icons = getAvailableIcons().sort((a, b) => a.localeCompare(b));
|
||||
|
||||
export const simple = () => {
|
||||
const [filter, setFilter] = useState('');
|
||||
|
||||
const searchIcon = (event: ChangeEvent<HTMLInputElement>) => {
|
||||
setFilter(event.target.value);
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className={css`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
`}
|
||||
>
|
||||
<Forms.Field
|
||||
className={css`
|
||||
width: 300px;
|
||||
`}
|
||||
>
|
||||
<Forms.Input onChange={searchIcon} placeholder="Search icons by name" />
|
||||
</Forms.Field>
|
||||
<div
|
||||
className={css`
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
`}
|
||||
>
|
||||
{icons
|
||||
.filter(val => val.includes(filter))
|
||||
.map(i => {
|
||||
return <IconWrapper name={i} key={i} />;
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
@ -1,36 +1,78 @@
|
||||
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 } 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?: ComponentSize | 'xl' | 'xxl';
|
||||
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, type: IconType) => {
|
||||
const defaultIconColor = type === 'default' ? 'currentColor' : theme.colors.orange;
|
||||
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: ${defaultIconColor};
|
||||
}
|
||||
`,
|
||||
};
|
||||
});
|
||||
|
||||
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', title, name, className, style, ...divElementProps }, ref) => {
|
||||
const theme = useTheme();
|
||||
const styles = getIconStyles(theme, type);
|
||||
const svgSize = getSvgSize(size, theme);
|
||||
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, 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;
|
||||
};
|
||||
|
13
packages/grafana-ui/src/components/Icon/assets/Apps.tsx
Normal file
13
packages/grafana-ui/src/components/Icon/assets/Apps.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
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>
|
||||
);
|
||||
};
|
14
packages/grafana-ui/src/components/Icon/assets/Cog.tsx
Normal file
14
packages/grafana-ui/src/components/Icon/assets/Cog.tsx
Normal file
@ -0,0 +1,14 @@
|
||||
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>
|
||||
);
|
||||
};
|
10
packages/grafana-ui/src/components/Icon/assets/Favorite.tsx
Normal file
10
packages/grafana-ui/src/components/Icon/assets/Favorite.tsx
Normal file
@ -0,0 +1,10 @@
|
||||
import React, { FunctionComponent } from 'react';
|
||||
import { SvgProps } from '../Icon';
|
||||
|
||||
export const Favorite: FunctionComponent<SvgProps> = ({ size, color, secondaryColor, ...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>
|
||||
);
|
||||
};
|
14
packages/grafana-ui/src/components/Icon/assets/Shield.tsx
Normal file
14
packages/grafana-ui/src/components/Icon/assets/Shield.tsx
Normal file
@ -0,0 +1,14 @@
|
||||
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>
|
||||
);
|
||||
};
|
4
packages/grafana-ui/src/components/Icon/assets/index.ts
Normal file
4
packages/grafana-ui/src/components/Icon/assets/index.ts
Normal file
@ -0,0 +1,4 @@
|
||||
export * from './Apps';
|
||||
export * from './Cog';
|
||||
export * from './Shield';
|
||||
export * from './Favorite';
|
File diff suppressed because it is too large
Load Diff
@ -2,13 +2,13 @@ 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';
|
||||
|
||||
interface Props extends Themeable {
|
||||
icon?: IconType;
|
||||
icon?: IconName;
|
||||
title: string | JSX.Element;
|
||||
className?: string;
|
||||
|
||||
|
@ -1,12 +1,12 @@
|
||||
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 }) => {
|
||||
|
@ -1,9 +1,9 @@
|
||||
import React from 'react';
|
||||
import { IconType } from '../Icon/types';
|
||||
import { IconName } from '../../types';
|
||||
import { Icon } from '../Icon/Icon';
|
||||
|
||||
interface Props {
|
||||
icon?: IconType;
|
||||
icon?: IconName;
|
||||
iconClass?: string;
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
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';
|
||||
@ -11,7 +11,7 @@ interface ModalTab {
|
||||
}
|
||||
|
||||
interface Props {
|
||||
icon: IconType;
|
||||
icon: IconName;
|
||||
title: string;
|
||||
tabs: ModalTab[];
|
||||
activeTab: string;
|
||||
|
@ -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" />
|
||||
</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>;
|
||||
|
@ -105,7 +105,7 @@ function renderHeaderCell(column: any, className: string, field?: Field) {
|
||||
return (
|
||||
<div className={className} {...headerProps}>
|
||||
{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>
|
||||
);
|
||||
}
|
||||
|
@ -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)} />
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -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)} />
|
||||
<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'} />}</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" />
|
||||
</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" />
|
||||
</button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
@ -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}>
|
||||
|
@ -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,9 @@ 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"
|
||||
/>
|
||||
<Memo()
|
||||
onChange={[Function]}
|
||||
@ -276,10 +277,10 @@ 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"
|
||||
/>
|
||||
</span>
|
||||
</button>
|
||||
@ -289,8 +290,8 @@ 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"
|
||||
/>
|
||||
</button>
|
||||
<Component
|
||||
@ -301,8 +302,8 @@ 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"
|
||||
/>
|
||||
</button>
|
||||
</Component>
|
||||
@ -321,8 +322,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 +349,9 @@ 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"
|
||||
/>
|
||||
<Memo()
|
||||
onChange={[Function]}
|
||||
@ -586,10 +588,10 @@ 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"
|
||||
/>
|
||||
</span>
|
||||
</button>
|
||||
@ -804,8 +806,8 @@ 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"
|
||||
/>
|
||||
</button>
|
||||
<Component
|
||||
@ -816,8 +818,8 @@ 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"
|
||||
/>
|
||||
</button>
|
||||
</Component>
|
||||
|
@ -1,5 +1,5 @@
|
||||
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;
|
||||
|
128
packages/grafana-ui/src/types/icon.ts
Normal file
128
packages/grafana-ui/src/types/icon.ts
Normal file
@ -0,0 +1,128 @@
|
||||
export type IconType = 'mono' | 'default';
|
||||
|
||||
export type IconName =
|
||||
| 'question-circle'
|
||||
| 'plus'
|
||||
| 'angle-up'
|
||||
| 'angle-down'
|
||||
| 'angle-left'
|
||||
| 'angle-right'
|
||||
| 'pen'
|
||||
| 'plane'
|
||||
| 'power'
|
||||
| 'trash-alt'
|
||||
| 'exclamation-triangle'
|
||||
| 'times'
|
||||
| 'step-backward'
|
||||
| 'square-shape'
|
||||
| 'share-alt'
|
||||
| 'forward'
|
||||
| 'check'
|
||||
| 'add-panel'
|
||||
| 'copy'
|
||||
| 'lock'
|
||||
| 'panel-add'
|
||||
| 'arrow-random'
|
||||
| 'arrow-from-right'
|
||||
| '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'
|
||||
| 'comments-alt'
|
||||
| 'document-info'
|
||||
| 'info-circle'
|
||||
| 'bug'
|
||||
| 'cube'
|
||||
| 'star'
|
||||
| 'edit'
|
||||
| 'shield'
|
||||
| 'eye'
|
||||
| 'monitor'
|
||||
| 'favorite';
|
||||
|
||||
export const getAvailableIcons = (): IconName[] => [
|
||||
'question-circle',
|
||||
'plane',
|
||||
'plus',
|
||||
'angle-up',
|
||||
'shield',
|
||||
'angle-down',
|
||||
'angle-left',
|
||||
'angle-right',
|
||||
'pen',
|
||||
'power',
|
||||
'trash-alt',
|
||||
'exclamation-triangle',
|
||||
'times',
|
||||
'step-backward',
|
||||
'square-shape',
|
||||
'share-alt',
|
||||
'forward',
|
||||
'check',
|
||||
'add-panel',
|
||||
'copy',
|
||||
'lock',
|
||||
'panel-add',
|
||||
'arrow-random',
|
||||
'arrow-from-right',
|
||||
'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',
|
||||
'comments-alt',
|
||||
'document-info',
|
||||
'info-circle',
|
||||
'bug',
|
||||
'cube',
|
||||
'star',
|
||||
'edit',
|
||||
'eye',
|
||||
'monitor',
|
||||
'favorite',
|
||||
];
|
@ -3,3 +3,4 @@ export * from './input';
|
||||
export * from './completion';
|
||||
export * from './storybook';
|
||||
export * from './forms';
|
||||
export * from './icon';
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { select } from '@storybook/addon-knobs';
|
||||
import { getAvailableIcons } from '../../components/Icon/types';
|
||||
import { getAvailableIcons } from '../../types';
|
||||
|
||||
const VISUAL_GROUP = 'Visual options';
|
||||
|
||||
|
@ -109,19 +109,19 @@ func (hs *HTTPServer) setIndexViewData(c *models.ReqContext) (*dtos.IndexViewDat
|
||||
|
||||
if hasEditPermissionInFoldersQuery.Result {
|
||||
children := []*dtos.NavLink{
|
||||
{Text: "Dashboard", Icon: "gicon gicon-dashboard-new", Url: setting.AppSubUrl + "/dashboard/new"},
|
||||
{Text: "Dashboard", Icon: "apps", Url: setting.AppSubUrl + "/dashboard/new"},
|
||||
}
|
||||
|
||||
if c.OrgRole == models.ROLE_ADMIN || c.OrgRole == models.ROLE_EDITOR {
|
||||
children = append(children, &dtos.NavLink{Text: "Folder", SubTitle: "Create a new folder to organize your dashboards", Id: "folder", Icon: "gicon gicon-folder-new", Url: setting.AppSubUrl + "/dashboards/folder/new"})
|
||||
children = append(children, &dtos.NavLink{Text: "Folder", SubTitle: "Create a new folder to organize your dashboards", Id: "folder", Icon: "folder-plus", Url: setting.AppSubUrl + "/dashboards/folder/new"})
|
||||
}
|
||||
|
||||
children = append(children, &dtos.NavLink{Text: "Import", SubTitle: "Import dashboard from file or Grafana.com", Id: "import", Icon: "gicon gicon-dashboard-import", Url: setting.AppSubUrl + "/dashboard/import"})
|
||||
children = append(children, &dtos.NavLink{Text: "Import", SubTitle: "Import dashboard from file or Grafana.com", Id: "import", Icon: "upload", Url: setting.AppSubUrl + "/dashboard/import"})
|
||||
|
||||
data.NavTree = append(data.NavTree, &dtos.NavLink{
|
||||
Text: "Create",
|
||||
Id: "create",
|
||||
Icon: "fa fa-fw fa-plus",
|
||||
Icon: "plus",
|
||||
Url: setting.AppSubUrl + "/dashboard/new",
|
||||
Children: children,
|
||||
SortWeight: dtos.WeightCreate,
|
||||
@ -129,18 +129,18 @@ func (hs *HTTPServer) setIndexViewData(c *models.ReqContext) (*dtos.IndexViewDat
|
||||
}
|
||||
|
||||
dashboardChildNavs := []*dtos.NavLink{
|
||||
{Text: "Home", Id: "home", Url: setting.AppSubUrl + "/", Icon: "gicon gicon-home", HideFromTabs: true},
|
||||
{Text: "Home", Id: "home", Url: setting.AppSubUrl + "/", Icon: "home-alt", HideFromTabs: true},
|
||||
{Text: "Divider", Divider: true, Id: "divider", HideFromTabs: true},
|
||||
{Text: "Manage", Id: "manage-dashboards", Url: setting.AppSubUrl + "/dashboards", Icon: "gicon gicon-manage"},
|
||||
{Text: "Playlists", Id: "playlists", Url: setting.AppSubUrl + "/playlists", Icon: "gicon gicon-playlists"},
|
||||
{Text: "Snapshots", Id: "snapshots", Url: setting.AppSubUrl + "/dashboard/snapshots", Icon: "gicon gicon-snapshots"},
|
||||
{Text: "Manage", Id: "manage-dashboards", Url: setting.AppSubUrl + "/dashboards", Icon: "sitemap"},
|
||||
{Text: "Playlists", Id: "playlists", Url: setting.AppSubUrl + "/playlists", Icon: "presentation-play"},
|
||||
{Text: "Snapshots", Id: "snapshots", Url: setting.AppSubUrl + "/dashboard/snapshots", Icon: "camera"},
|
||||
}
|
||||
|
||||
data.NavTree = append(data.NavTree, &dtos.NavLink{
|
||||
Text: "Dashboards",
|
||||
Id: "dashboards",
|
||||
SubTitle: "Manage dashboards & folders",
|
||||
Icon: "gicon gicon-dashboard",
|
||||
Icon: "apps",
|
||||
Url: setting.AppSubUrl + "/",
|
||||
SortWeight: dtos.WeightDashboard,
|
||||
Children: dashboardChildNavs,
|
||||
@ -151,7 +151,7 @@ func (hs *HTTPServer) setIndexViewData(c *models.ReqContext) (*dtos.IndexViewDat
|
||||
Text: "Explore",
|
||||
Id: "explore",
|
||||
SubTitle: "Explore your data",
|
||||
Icon: "gicon gicon-explore",
|
||||
Icon: "compass",
|
||||
SortWeight: dtos.WeightExplore,
|
||||
Url: setting.AppSubUrl + "/explore",
|
||||
})
|
||||
@ -172,8 +172,8 @@ func (hs *HTTPServer) setIndexViewData(c *models.ReqContext) (*dtos.IndexViewDat
|
||||
HideFromMenu: true,
|
||||
SortWeight: dtos.WeightProfile,
|
||||
Children: []*dtos.NavLink{
|
||||
{Text: "Preferences", Id: "profile-settings", Url: setting.AppSubUrl + "/profile", Icon: "gicon gicon-preferences"},
|
||||
{Text: "Change Password", Id: "change-password", Url: setting.AppSubUrl + "/profile/password", Icon: "fa fa-fw fa-lock", HideFromMenu: true},
|
||||
{Text: "Preferences", Id: "profile-settings", Url: setting.AppSubUrl + "/profile", Icon: "sliders-v-alt"},
|
||||
{Text: "Change Password", Id: "change-password", Url: setting.AppSubUrl + "/profile/password", Icon: "lock", HideFromMenu: true},
|
||||
},
|
||||
}
|
||||
|
||||
@ -183,7 +183,7 @@ func (hs *HTTPServer) setIndexViewData(c *models.ReqContext) (*dtos.IndexViewDat
|
||||
Text: "Sign out",
|
||||
Id: "sign-out",
|
||||
Url: setting.AppSubUrl + "/logout",
|
||||
Icon: "fa fa-fw fa-sign-out",
|
||||
Icon: "arrow-from-right",
|
||||
Target: "_self",
|
||||
HideFromTabs: true,
|
||||
})
|
||||
@ -194,15 +194,15 @@ func (hs *HTTPServer) setIndexViewData(c *models.ReqContext) (*dtos.IndexViewDat
|
||||
|
||||
if setting.AlertingEnabled && (c.OrgRole == models.ROLE_ADMIN || c.OrgRole == models.ROLE_EDITOR) {
|
||||
alertChildNavs := []*dtos.NavLink{
|
||||
{Text: "Alert Rules", Id: "alert-list", Url: setting.AppSubUrl + "/alerting/list", Icon: "gicon gicon-alert-rules"},
|
||||
{Text: "Notification channels", Id: "channels", Url: setting.AppSubUrl + "/alerting/notifications", Icon: "gicon gicon-alert-notification-channel"},
|
||||
{Text: "Alert Rules", Id: "alert-list", Url: setting.AppSubUrl + "/alerting/list", Icon: "list-ul"},
|
||||
{Text: "Notification channels", Id: "channels", Url: setting.AppSubUrl + "/alerting/notifications", Icon: "comment-alt-share"},
|
||||
}
|
||||
|
||||
data.NavTree = append(data.NavTree, &dtos.NavLink{
|
||||
Text: "Alerting",
|
||||
SubTitle: "Alert rules & notifications",
|
||||
Id: "alerting",
|
||||
Icon: "gicon gicon-alert",
|
||||
Icon: "bell",
|
||||
Url: setting.AppSubUrl + "/alerting/list",
|
||||
Children: alertChildNavs,
|
||||
SortWeight: dtos.WeightAlerting,
|
||||
@ -259,7 +259,7 @@ func (hs *HTTPServer) setIndexViewData(c *models.ReqContext) (*dtos.IndexViewDat
|
||||
|
||||
if len(appLink.Children) > 0 && c.OrgRole == models.ROLE_ADMIN {
|
||||
appLink.Children = append(appLink.Children, &dtos.NavLink{Divider: true})
|
||||
appLink.Children = append(appLink.Children, &dtos.NavLink{Text: "Plugin Config", Icon: "gicon gicon-cog", Url: setting.AppSubUrl + "/plugins/" + plugin.Id + "/"})
|
||||
appLink.Children = append(appLink.Children, &dtos.NavLink{Text: "Plugin Config", Icon: "cog", Url: setting.AppSubUrl + "/plugins/" + plugin.Id + "/"})
|
||||
}
|
||||
|
||||
if len(appLink.Children) > 0 {
|
||||
@ -273,7 +273,7 @@ func (hs *HTTPServer) setIndexViewData(c *models.ReqContext) (*dtos.IndexViewDat
|
||||
if c.OrgRole == models.ROLE_ADMIN {
|
||||
configNodes = append(configNodes, &dtos.NavLink{
|
||||
Text: "Data Sources",
|
||||
Icon: "gicon gicon-datasources",
|
||||
Icon: "database",
|
||||
Description: "Add and configure data sources",
|
||||
Id: "datasources",
|
||||
Url: setting.AppSubUrl + "/datasources",
|
||||
@ -282,7 +282,7 @@ func (hs *HTTPServer) setIndexViewData(c *models.ReqContext) (*dtos.IndexViewDat
|
||||
Text: "Users",
|
||||
Id: "users",
|
||||
Description: "Manage org members",
|
||||
Icon: "gicon gicon-user",
|
||||
Icon: "user",
|
||||
Url: setting.AppSubUrl + "/org/users",
|
||||
})
|
||||
}
|
||||
@ -292,7 +292,7 @@ func (hs *HTTPServer) setIndexViewData(c *models.ReqContext) (*dtos.IndexViewDat
|
||||
Text: "Teams",
|
||||
Id: "teams",
|
||||
Description: "Manage org groups",
|
||||
Icon: "gicon gicon-team",
|
||||
Icon: "users-alt",
|
||||
Url: setting.AppSubUrl + "/org/teams",
|
||||
})
|
||||
}
|
||||
@ -302,7 +302,7 @@ func (hs *HTTPServer) setIndexViewData(c *models.ReqContext) (*dtos.IndexViewDat
|
||||
Text: "Plugins",
|
||||
Id: "plugins",
|
||||
Description: "View and configure plugins",
|
||||
Icon: "gicon gicon-plugins",
|
||||
Icon: "plug",
|
||||
Url: setting.AppSubUrl + "/plugins",
|
||||
})
|
||||
|
||||
@ -310,14 +310,14 @@ func (hs *HTTPServer) setIndexViewData(c *models.ReqContext) (*dtos.IndexViewDat
|
||||
Text: "Preferences",
|
||||
Id: "org-settings",
|
||||
Description: "Organization preferences",
|
||||
Icon: "gicon gicon-preferences",
|
||||
Icon: "sliders-v-alt",
|
||||
Url: setting.AppSubUrl + "/org",
|
||||
})
|
||||
configNodes = append(configNodes, &dtos.NavLink{
|
||||
Text: "API Keys",
|
||||
Id: "apikeys",
|
||||
Description: "Create & manage API keys",
|
||||
Icon: "gicon gicon-apikeys",
|
||||
Icon: "key-skeleton-alt",
|
||||
Url: setting.AppSubUrl + "/org/apikeys",
|
||||
})
|
||||
}
|
||||
@ -327,7 +327,7 @@ func (hs *HTTPServer) setIndexViewData(c *models.ReqContext) (*dtos.IndexViewDat
|
||||
Id: "cfg",
|
||||
Text: "Configuration",
|
||||
SubTitle: "Organization: " + c.OrgName,
|
||||
Icon: "gicon gicon-cog",
|
||||
Icon: "cog",
|
||||
Url: configNodes[0].Url,
|
||||
SortWeight: dtos.WeightConfig,
|
||||
Children: configNodes,
|
||||
@ -336,15 +336,15 @@ func (hs *HTTPServer) setIndexViewData(c *models.ReqContext) (*dtos.IndexViewDat
|
||||
|
||||
if c.IsGrafanaAdmin {
|
||||
adminNavLinks := []*dtos.NavLink{
|
||||
{Text: "Users", Id: "global-users", Url: setting.AppSubUrl + "/admin/users", Icon: "gicon gicon-user"},
|
||||
{Text: "Orgs", Id: "global-orgs", Url: setting.AppSubUrl + "/admin/orgs", Icon: "gicon gicon-org"},
|
||||
{Text: "Settings", Id: "server-settings", Url: setting.AppSubUrl + "/admin/settings", Icon: "gicon gicon-preferences"},
|
||||
{Text: "Stats", Id: "server-stats", Url: setting.AppSubUrl + "/admin/stats", Icon: "fa fa-fw fa-bar-chart"},
|
||||
{Text: "Users", Id: "global-users", Url: setting.AppSubUrl + "/admin/users", Icon: "user"},
|
||||
{Text: "Orgs", Id: "global-orgs", Url: setting.AppSubUrl + "/admin/orgs", Icon: "building"},
|
||||
{Text: "Settings", Id: "server-settings", Url: setting.AppSubUrl + "/admin/settings", Icon: "sliders-v-alt"},
|
||||
{Text: "Stats", Id: "server-stats", Url: setting.AppSubUrl + "/admin/stats", Icon: "graph-bar"},
|
||||
}
|
||||
|
||||
if setting.LDAPEnabled {
|
||||
adminNavLinks = append(adminNavLinks, &dtos.NavLink{
|
||||
Text: "LDAP", Id: "ldap", Url: setting.AppSubUrl + "/admin/ldap", Icon: "fa fa-fw fa-address-book-o",
|
||||
Text: "LDAP", Id: "ldap", Url: setting.AppSubUrl + "/admin/ldap", Icon: "book",
|
||||
})
|
||||
}
|
||||
|
||||
@ -353,7 +353,7 @@ func (hs *HTTPServer) setIndexViewData(c *models.ReqContext) (*dtos.IndexViewDat
|
||||
SubTitle: "Manage all users & orgs",
|
||||
HideFromTabs: true,
|
||||
Id: "admin",
|
||||
Icon: "gicon gicon-shield",
|
||||
Icon: "shield",
|
||||
Url: setting.AppSubUrl + "/admin/users",
|
||||
SortWeight: dtos.WeightAdmin,
|
||||
Children: adminNavLinks,
|
||||
@ -365,7 +365,7 @@ func (hs *HTTPServer) setIndexViewData(c *models.ReqContext) (*dtos.IndexViewDat
|
||||
SubTitle: fmt.Sprintf(`%s v%s (%s)`, setting.ApplicationName, setting.BuildVersion, setting.BuildCommit),
|
||||
Id: "help",
|
||||
Url: "#",
|
||||
Icon: "gicon gicon-question",
|
||||
Icon: "question-circle",
|
||||
HideFromMenu: true,
|
||||
SortWeight: dtos.WeightHelp,
|
||||
Children: []*dtos.NavLink{},
|
||||
|
@ -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",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -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',
|
||||
},
|
||||
|
@ -47,7 +47,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>
|
||||
|
@ -1,7 +1,9 @@
|
||||
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;
|
||||
|
@ -1,4 +1,6 @@
|
||||
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" />
|
||||
Close
|
||||
</span>
|
||||
</div>,
|
||||
|
@ -1,5 +1,6 @@
|
||||
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>
|
||||
|
@ -13,8 +13,9 @@ exports[`Render should render icon if exists 1`] = `
|
||||
className=""
|
||||
>
|
||||
<a>
|
||||
<i
|
||||
className="icon-test"
|
||||
<Icon
|
||||
className="css-290ig"
|
||||
name="icon-test"
|
||||
/>
|
||||
</a>
|
||||
</li>
|
||||
|
@ -14,14 +14,14 @@ Array [
|
||||
key="hamburger"
|
||||
onClick={[Function]}
|
||||
>
|
||||
<i
|
||||
className="fa fa-bars"
|
||||
<Icon
|
||||
name="bars"
|
||||
/>
|
||||
<span
|
||||
className="sidemenu__close"
|
||||
>
|
||||
<i
|
||||
className="fa fa-times"
|
||||
<Icon
|
||||
name="times"
|
||||
/>
|
||||
Close
|
||||
</span>
|
||||
|
@ -10,7 +10,9 @@ exports[`Render should render component 1`] = `
|
||||
<span
|
||||
className="icon-circle sidemenu-icon"
|
||||
>
|
||||
<i />
|
||||
<Icon
|
||||
size="xl"
|
||||
/>
|
||||
</span>
|
||||
</a>
|
||||
<SideMenuDropDown
|
||||
|
@ -164,7 +164,7 @@ exports[`ServerStats Should render table with stats 1`] = `
|
||||
target="_blank"
|
||||
>
|
||||
<i
|
||||
className="fa fa-file-code-o"
|
||||
className="document-info"
|
||||
/>
|
||||
|
||||
Documentation
|
||||
@ -177,7 +177,7 @@ exports[`ServerStats Should render table with stats 1`] = `
|
||||
target="_blank"
|
||||
>
|
||||
<i
|
||||
className="fa fa-support"
|
||||
className="question-circle"
|
||||
/>
|
||||
|
||||
Support
|
||||
@ -190,7 +190,7 @@ exports[`ServerStats Should render table with stats 1`] = `
|
||||
target="_blank"
|
||||
>
|
||||
<i
|
||||
className="fa fa-comments-o"
|
||||
className="comments-alt"
|
||||
/>
|
||||
|
||||
Community
|
||||
|
@ -2,6 +2,7 @@
|
||||
import React from 'react';
|
||||
import _ from 'lodash';
|
||||
import { LocationUpdate } from '@grafana/runtime';
|
||||
import { Icon, IconName } from '@grafana/ui';
|
||||
import { e2e } from '@grafana/e2e';
|
||||
import { connect, MapDispatchToProps } from 'react-redux';
|
||||
// Utils
|
||||
@ -139,7 +140,7 @@ export class AddPanelWidgetUnconnected extends React.Component<Props, State> {
|
||||
dashboard.removePanel(this.props.panel);
|
||||
};
|
||||
|
||||
renderOptionLink = (icon: string, text: string, onClick: any) => {
|
||||
renderOptionLink = (icon: IconName, text: string, onClick: any) => {
|
||||
return (
|
||||
<div>
|
||||
<a
|
||||
@ -149,7 +150,7 @@ export class AddPanelWidgetUnconnected extends React.Component<Props, State> {
|
||||
aria-label={e2e.pages.AddDashboard.selectors.ctaButtons(text)}
|
||||
>
|
||||
<div className="add-panel-widget__icon">
|
||||
<i className={`gicon gicon-${icon}`} />
|
||||
<Icon name={icon} size="xl" />
|
||||
</div>
|
||||
<span>{text}</span>
|
||||
</a>
|
||||
@ -164,16 +165,16 @@ export class AddPanelWidgetUnconnected extends React.Component<Props, State> {
|
||||
<div className="panel-container add-panel-widget-container">
|
||||
<div className="add-panel-widget">
|
||||
<div className="add-panel-widget__header grid-drag-handle">
|
||||
<i className="gicon gicon-add-panel" />
|
||||
<Icon name="apps" type="mono" size="xl" style={{ margin: '4px', marginRight: '8px' }} />
|
||||
<span className="add-panel-widget__title">New Panel</span>
|
||||
<button className="add-panel-widget__close" onClick={this.handleCloseAddPanel}>
|
||||
<i className="fa fa-close" />
|
||||
<Icon name="times" />
|
||||
</button>
|
||||
</div>
|
||||
<div className="add-panel-widget__btn-container">
|
||||
<div className="add-panel-widget__create">
|
||||
{this.renderOptionLink('queries', 'Add Query', this.onCreateNewPanel)}
|
||||
{this.renderOptionLink('visualization', 'Choose Visualization', () =>
|
||||
{this.renderOptionLink('search', 'Add Query', this.onCreateNewPanel)}
|
||||
{this.renderOptionLink('chart-line', 'Choose Visualization', () =>
|
||||
this.onCreateNewPanel('visualization')
|
||||
)}
|
||||
</div>
|
||||
|
@ -10,8 +10,16 @@ exports[`Render should render component 1`] = `
|
||||
<div
|
||||
className="add-panel-widget__header grid-drag-handle"
|
||||
>
|
||||
<i
|
||||
className="gicon gicon-add-panel"
|
||||
<Icon
|
||||
name="apps"
|
||||
size="xl"
|
||||
style={
|
||||
Object {
|
||||
"margin": "4px",
|
||||
"marginRight": "8px",
|
||||
}
|
||||
}
|
||||
type="mono"
|
||||
/>
|
||||
<span
|
||||
className="add-panel-widget__title"
|
||||
@ -22,8 +30,8 @@ exports[`Render should render component 1`] = `
|
||||
className="add-panel-widget__close"
|
||||
onClick={[Function]}
|
||||
>
|
||||
<i
|
||||
className="fa fa-close"
|
||||
<Icon
|
||||
name="times"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
@ -43,8 +51,9 @@ exports[`Render should render component 1`] = `
|
||||
<div
|
||||
className="add-panel-widget__icon"
|
||||
>
|
||||
<i
|
||||
className="gicon gicon-queries"
|
||||
<Icon
|
||||
name="search"
|
||||
size="xl"
|
||||
/>
|
||||
</div>
|
||||
<span>
|
||||
@ -62,8 +71,9 @@ exports[`Render should render component 1`] = `
|
||||
<div
|
||||
className="add-panel-widget__icon"
|
||||
>
|
||||
<i
|
||||
className="gicon gicon-visualization"
|
||||
<Icon
|
||||
name="chart-line"
|
||||
size="xl"
|
||||
/>
|
||||
</div>
|
||||
<span>
|
||||
|
@ -1,6 +1,7 @@
|
||||
// Libaries
|
||||
import React, { PureComponent } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { css } from 'emotion';
|
||||
import { e2e } from '@grafana/e2e';
|
||||
// Utils & Services
|
||||
import { appEvents } from 'app/core/app_events';
|
||||
@ -8,7 +9,7 @@ import { PlaylistSrv } from 'app/features/playlist/playlist_srv';
|
||||
// Components
|
||||
import { DashNavButton } from './DashNavButton';
|
||||
import { DashNavTimeControls } from './DashNavTimeControls';
|
||||
import { ModalsController } from '@grafana/ui';
|
||||
import { ModalsController, Icon } from '@grafana/ui';
|
||||
import { BackButton } from 'app/core/components/BackButton/BackButton';
|
||||
// State
|
||||
import { updateLocation } from 'app/core/actions';
|
||||
@ -34,7 +35,7 @@ export interface StateProps {
|
||||
|
||||
type Props = StateProps & OwnProps;
|
||||
|
||||
export class DashNav extends PureComponent<Props> {
|
||||
class DashNav extends PureComponent<Props> {
|
||||
playlistSrv: PlaylistSrv;
|
||||
|
||||
constructor(props: Props) {
|
||||
@ -95,6 +96,10 @@ export class DashNav extends PureComponent<Props> {
|
||||
|
||||
renderDashboardTitleSearchButton() {
|
||||
const { dashboard, isFullscreen } = this.props;
|
||||
/* Hard-coded value so we don't have to wrap whole component in withTheme because of 1 variable */
|
||||
const iconClassName = css`
|
||||
margin-right: 8px;
|
||||
`;
|
||||
|
||||
const folderTitle = dashboard.meta.folderTitle;
|
||||
const haveFolder = dashboard.meta.folderId > 0;
|
||||
@ -103,17 +108,17 @@ export class DashNav extends PureComponent<Props> {
|
||||
<>
|
||||
<div>
|
||||
<div className="navbar-page-btn">
|
||||
{!isFullscreen && <i className="gicon gicon-dashboard" />}
|
||||
{!isFullscreen && <Icon name="apps" size="xl" className={iconClassName} />}
|
||||
{haveFolder && (
|
||||
<>
|
||||
<a className="navbar-page-btn__folder" onClick={this.onFolderNameClick}>
|
||||
{folderTitle}
|
||||
</a>
|
||||
<i className="fa fa-chevron-right navbar-page-btn__folder-icon" />
|
||||
<Icon name="angle-right" className={iconClassName} />
|
||||
</>
|
||||
)}
|
||||
<a onClick={this.onDahboardNameClick}>
|
||||
{dashboard.title} <i className="fa fa-caret-down navbar-page-btn__search" />
|
||||
{dashboard.title} <Icon name="angle-down" className={iconClassName} />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
@ -146,39 +151,33 @@ export class DashNav extends PureComponent<Props> {
|
||||
<DashNavButton
|
||||
tooltip="Go to previous dashboard"
|
||||
classSuffix="tight"
|
||||
icon="fa fa-step-backward"
|
||||
icon="step-backward"
|
||||
onClick={this.onPlaylistPrev}
|
||||
/>
|
||||
<DashNavButton
|
||||
tooltip="Stop playlist"
|
||||
classSuffix="tight"
|
||||
icon="fa fa-stop"
|
||||
icon="square-shape"
|
||||
onClick={this.onPlaylistStop}
|
||||
/>
|
||||
<DashNavButton
|
||||
tooltip="Go to next dashboard"
|
||||
classSuffix="tight"
|
||||
icon="fa fa-forward"
|
||||
icon="forward"
|
||||
onClick={this.onPlaylistNext}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="navbar-buttons navbar-buttons--actions">
|
||||
{canSave && (
|
||||
<DashNavButton
|
||||
tooltip="Add panel"
|
||||
classSuffix="add-panel"
|
||||
icon="gicon gicon-add-panel"
|
||||
onClick={onAddPanel}
|
||||
/>
|
||||
)}
|
||||
{canSave && <DashNavButton classSuffix="save" tooltip="Add panel" icon="panel-add" onClick={onAddPanel} />}
|
||||
|
||||
{canStar && (
|
||||
<DashNavButton
|
||||
tooltip="Mark as favorite"
|
||||
classSuffix="star"
|
||||
icon={`${isStarred ? 'fa fa-star' : 'fa fa-star-o'}`}
|
||||
icon={isStarred ? 'favorite' : 'star'}
|
||||
iconType={isStarred ? 'mono' : 'default'}
|
||||
onClick={this.onStarDashboard}
|
||||
/>
|
||||
)}
|
||||
@ -189,7 +188,7 @@ export class DashNav extends PureComponent<Props> {
|
||||
<DashNavButton
|
||||
tooltip="Share dashboard"
|
||||
classSuffix="share"
|
||||
icon="fa fa-share-square-o"
|
||||
icon="share-alt"
|
||||
onClick={() => {
|
||||
showModal(ShareModal, {
|
||||
dashboard,
|
||||
@ -207,7 +206,7 @@ export class DashNav extends PureComponent<Props> {
|
||||
<DashNavButton
|
||||
tooltip="Save dashboard"
|
||||
classSuffix="save"
|
||||
icon="fa fa-save"
|
||||
icon="save"
|
||||
onClick={() => {
|
||||
showModal(SaveDashboardModalProxy, {
|
||||
dashboard,
|
||||
@ -223,7 +222,7 @@ export class DashNav extends PureComponent<Props> {
|
||||
<DashNavButton
|
||||
tooltip="Open original dashboard"
|
||||
classSuffix="snapshot-origin"
|
||||
icon="gicon gicon-link"
|
||||
icon="link"
|
||||
href={sanitizeUrl(snapshotUrl)}
|
||||
/>
|
||||
)}
|
||||
@ -232,19 +231,14 @@ export class DashNav extends PureComponent<Props> {
|
||||
<DashNavButton
|
||||
tooltip="Dashboard settings"
|
||||
classSuffix="settings"
|
||||
icon="gicon gicon-cog"
|
||||
icon="cog"
|
||||
onClick={this.onOpenSettings}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="navbar-buttons navbar-buttons--tv">
|
||||
<DashNavButton
|
||||
tooltip="Cycle view mode"
|
||||
classSuffix="tv"
|
||||
icon="fa fa-desktop"
|
||||
onClick={this.onToggleTVMode}
|
||||
/>
|
||||
<DashNavButton tooltip="Cycle view mode" classSuffix="tv" icon="monitor" onClick={this.onToggleTVMode} />
|
||||
</div>
|
||||
|
||||
{!dashboard.timepicker.hidden && (
|
||||
|
@ -1,19 +1,28 @@
|
||||
// Libraries
|
||||
import React, { FunctionComponent } from 'react';
|
||||
// Components
|
||||
import { Tooltip } from '@grafana/ui';
|
||||
import { Tooltip, Icon, IconName, IconType } from '@grafana/ui';
|
||||
import { e2e } from '@grafana/e2e';
|
||||
|
||||
interface Props {
|
||||
icon?: string;
|
||||
icon?: IconName;
|
||||
tooltip: string;
|
||||
classSuffix?: string;
|
||||
onClick?: () => void;
|
||||
href?: string;
|
||||
children?: React.ReactNode;
|
||||
iconType?: IconType;
|
||||
}
|
||||
|
||||
export const DashNavButton: FunctionComponent<Props> = ({ icon, tooltip, classSuffix, onClick, href, children }) => {
|
||||
export const DashNavButton: FunctionComponent<Props> = ({
|
||||
icon,
|
||||
iconType,
|
||||
tooltip,
|
||||
classSuffix,
|
||||
onClick,
|
||||
href,
|
||||
children,
|
||||
}) => {
|
||||
if (onClick) {
|
||||
return (
|
||||
<Tooltip content={tooltip} placement="bottom">
|
||||
@ -22,7 +31,7 @@ export const DashNavButton: FunctionComponent<Props> = ({ icon, tooltip, classSu
|
||||
onClick={onClick}
|
||||
aria-label={e2e.pages.Dashboard.Toolbar.selectors.toolbarItems(tooltip)}
|
||||
>
|
||||
{icon && <i className={icon} />}
|
||||
{icon && <Icon name={icon} type={iconType} />}
|
||||
{children}
|
||||
</button>
|
||||
</Tooltip>
|
||||
@ -32,7 +41,7 @@ export const DashNavButton: FunctionComponent<Props> = ({ icon, tooltip, classSu
|
||||
return (
|
||||
<Tooltip content={tooltip} placement="bottom">
|
||||
<a className={`btn navbar-button navbar-button--${classSuffix}`} href={href}>
|
||||
{icon && <i className={icon} />}
|
||||
{icon && <Icon name={icon} type={iconType} />}
|
||||
{children}
|
||||
</a>
|
||||
</Tooltip>
|
||||
|
@ -33,7 +33,7 @@ export const InspectHeader: FC<Props> = ({
|
||||
<div className={styles.header}>
|
||||
<div className={styles.actions}>
|
||||
<div className={styles.iconWrapper} onClick={onToggleExpand}>
|
||||
<Icon name={isExpanded ? 'chevron-right' : 'chevron-left'} className={styles.icon} />
|
||||
<Icon name={isExpanded ? 'angle-right' : 'angle-left'} className={styles.icon} />
|
||||
</div>
|
||||
<div className={styles.iconWrapper} onClick={onClose}>
|
||||
<Icon name="times" className={styles.icon} />
|
||||
|
@ -17,7 +17,7 @@ export const OptionsGroup: FC<Props> = ({ title, children }) => {
|
||||
<div className={styles.header} onClick={() => toggleExpand(!isExpanded)}>
|
||||
{title}
|
||||
<div className={cx(styles.toggle, 'editor-options-group-toggle')}>
|
||||
<Icon name={isExpanded ? 'chevron-down' : 'chevron-left'} />
|
||||
<Icon name={isExpanded ? 'angle-down' : 'angle-left'} />
|
||||
</div>
|
||||
</div>
|
||||
{isExpanded && <div className={styles.body}>{children}</div>}
|
||||
|
@ -101,7 +101,7 @@ export class ShareModal extends React.Component<Props, State> {
|
||||
return (
|
||||
<ModalTabsHeader
|
||||
title={title}
|
||||
icon="share-square-o"
|
||||
icon="share-alt"
|
||||
tabs={tabs}
|
||||
activeTab={activeTab}
|
||||
onChangeTab={this.onSelectTab}
|
||||
|
@ -3,7 +3,7 @@ import classNames from 'classnames';
|
||||
import { isEqual } from 'lodash';
|
||||
import { DataLink, ScopedVars, PanelMenuItem, PanelData, LoadingState, QueryResultMetaNotice } from '@grafana/data';
|
||||
import { AngularComponent } from '@grafana/runtime';
|
||||
import { ClickOutsideWrapper, Tooltip } from '@grafana/ui';
|
||||
import { ClickOutsideWrapper, Tooltip, Icon } from '@grafana/ui';
|
||||
import { e2e } from '@grafana/e2e';
|
||||
|
||||
import PanelHeaderCorner from './PanelHeaderCorner';
|
||||
@ -109,11 +109,11 @@ export class PanelHeader extends Component<Props, State> {
|
||||
<Tooltip content={notice.text} key={notice.severity}>
|
||||
{notice.inspect ? (
|
||||
<div className="panel-info-notice" onClick={e => this.openInspect(e, notice.inspect!)}>
|
||||
<span className="fa fa-info-circle" style={{ marginRight: '8px', cursor: 'pointer' }} />
|
||||
<Icon name="info-circle" style={{ marginRight: '8px' }} />
|
||||
</div>
|
||||
) : (
|
||||
<a className="panel-info-notice" href={notice.url} target="_blank">
|
||||
<span className="fa fa-info-circle" style={{ marginRight: '8px', cursor: 'pointer' }} />
|
||||
<Icon name="info-circle" style={{ marginRight: '8px' }} />
|
||||
</a>
|
||||
)}
|
||||
</Tooltip>
|
||||
@ -163,7 +163,8 @@ export class PanelHeader extends Component<Props, State> {
|
||||
{Object.values(notices).map(this.renderNotice)}
|
||||
<span className="icon-gf panel-alert-icon" />
|
||||
<span className="panel-title-text">
|
||||
{title} <span className="fa fa-caret-down panel-menu-toggle" />
|
||||
{title}
|
||||
<Icon name="angle-down" className="panel-menu-toggle" />
|
||||
</span>
|
||||
{this.state.panelMenuOpen && (
|
||||
<ClickOutsideWrapper onClick={this.closeMenu}>
|
||||
@ -172,7 +173,7 @@ export class PanelHeader extends Component<Props, State> {
|
||||
)}
|
||||
{data.request && data.request.timeInfo && (
|
||||
<span className="panel-time-info">
|
||||
<i className="fa fa-clock-o" /> {data.request.timeInfo}
|
||||
<Icon name="clock-nine" /> {data.request.timeInfo}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
@ -1,5 +1,7 @@
|
||||
import React, { FC } from 'react';
|
||||
import { css } from 'emotion';
|
||||
import { PanelMenuItem } from '@grafana/data';
|
||||
import { Icon, IconName, useTheme } from '@grafana/ui';
|
||||
import { e2e } from '@grafana/e2e';
|
||||
|
||||
interface Props {
|
||||
@ -9,19 +11,37 @@ interface Props {
|
||||
export const PanelHeaderMenuItem: FC<Props & PanelMenuItem> = props => {
|
||||
const isSubMenu = props.type === 'submenu';
|
||||
const isDivider = props.type === 'divider';
|
||||
const theme = useTheme();
|
||||
const menuIconClassName = css`
|
||||
margin-right: ${theme.spacing.sm};
|
||||
a::after {
|
||||
display: none;
|
||||
}
|
||||
`;
|
||||
const shortcutIconClassName = css`
|
||||
position: absolute;
|
||||
margin-top: 3px;
|
||||
right: ${theme.spacing.xs};
|
||||
color: ${theme.colors.textWeak};
|
||||
`;
|
||||
return isDivider ? (
|
||||
<li className="divider" />
|
||||
) : (
|
||||
<li className={isSubMenu ? 'dropdown-submenu' : undefined}>
|
||||
<a onClick={props.onClick} href={props.href}>
|
||||
{props.iconClassName && <i className={props.iconClassName} />}
|
||||
{props.iconClassName && <Icon name={props.iconClassName as IconName} className={menuIconClassName} />}
|
||||
<span
|
||||
className="dropdown-item-text"
|
||||
aria-label={e2e.pages.Dashboard.Panels.Panel.selectors.headerItems(props.text)}
|
||||
>
|
||||
{props.text}
|
||||
{isSubMenu && <Icon name="angle-right" className={shortcutIconClassName} />}
|
||||
</span>
|
||||
{props.shortcut && <span className="dropdown-menu-item-shortcut">{props.shortcut}</span>}
|
||||
{props.shortcut && (
|
||||
<span className="dropdown-menu-item-shortcut">
|
||||
<Icon name="keyboard" className={menuIconClassName} /> {props.shortcut}
|
||||
</span>
|
||||
)}
|
||||
</a>
|
||||
{props.children}
|
||||
</li>
|
||||
|
@ -10,31 +10,31 @@ describe('getPanelMenu', () => {
|
||||
expect(menuItems).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Object {
|
||||
"iconClassName": "gicon gicon-viewer",
|
||||
"iconClassName": "eye",
|
||||
"onClick": [Function],
|
||||
"shortcut": "v",
|
||||
"text": "View",
|
||||
},
|
||||
Object {
|
||||
"iconClassName": "gicon gicon-editor",
|
||||
"iconClassName": "edit",
|
||||
"onClick": [Function],
|
||||
"shortcut": "e",
|
||||
"text": "Edit",
|
||||
},
|
||||
Object {
|
||||
"iconClassName": "fa fa-fw fa-share",
|
||||
"iconClassName": "share-alt",
|
||||
"onClick": [Function],
|
||||
"shortcut": "p s",
|
||||
"text": "Share",
|
||||
},
|
||||
Object {
|
||||
"iconClassName": "fa fa-fw fa-info-circle",
|
||||
"iconClassName": "info-circle",
|
||||
"onClick": [Function],
|
||||
"shortcut": "p i",
|
||||
"text": "Inspect",
|
||||
},
|
||||
Object {
|
||||
"iconClassName": "fa fa-fw fa-cube",
|
||||
"iconClassName": "cube",
|
||||
"onClick": [Function],
|
||||
"subMenu": Array [
|
||||
Object {
|
||||
@ -58,7 +58,7 @@ describe('getPanelMenu', () => {
|
||||
"type": "divider",
|
||||
},
|
||||
Object {
|
||||
"iconClassName": "fa fa-fw fa-trash",
|
||||
"iconClassName": "trash-alt",
|
||||
"onClick": [Function],
|
||||
"shortcut": "p r",
|
||||
"text": "Remove",
|
||||
|
@ -106,7 +106,7 @@ export function getPanelMenu(
|
||||
|
||||
menu.push({
|
||||
text: 'View',
|
||||
iconClassName: 'gicon gicon-viewer',
|
||||
iconClassName: 'eye',
|
||||
onClick: onViewPanel,
|
||||
shortcut: 'v',
|
||||
});
|
||||
@ -114,7 +114,7 @@ export function getPanelMenu(
|
||||
if (dashboard.canEditPanel(panel)) {
|
||||
menu.push({
|
||||
text: 'Edit',
|
||||
iconClassName: 'gicon gicon-editor',
|
||||
iconClassName: 'edit',
|
||||
onClick: onEditPanel,
|
||||
shortcut: 'e',
|
||||
});
|
||||
@ -122,7 +122,7 @@ export function getPanelMenu(
|
||||
|
||||
menu.push({
|
||||
text: 'Share',
|
||||
iconClassName: 'fa fa-fw fa-share',
|
||||
iconClassName: 'share-alt',
|
||||
onClick: onSharePanel,
|
||||
shortcut: 'p s',
|
||||
});
|
||||
@ -130,7 +130,7 @@ export function getPanelMenu(
|
||||
if (contextSrv.hasAccessToExplore() && !(panel.plugin && panel.plugin.meta.skipDataQuery)) {
|
||||
menu.push({
|
||||
text: 'Explore',
|
||||
iconClassName: 'gicon gicon-explore',
|
||||
iconClassName: 'compass',
|
||||
shortcut: 'x',
|
||||
onClick: onNavigateToExplore,
|
||||
});
|
||||
@ -138,7 +138,7 @@ export function getPanelMenu(
|
||||
|
||||
menu.push({
|
||||
text: 'Inspect',
|
||||
iconClassName: 'fa fa-fw fa-info-circle',
|
||||
iconClassName: 'info-circle',
|
||||
onClick: onInspectPanel,
|
||||
shortcut: 'p i',
|
||||
});
|
||||
@ -146,7 +146,7 @@ export function getPanelMenu(
|
||||
if (config.featureToggles.newEdit) {
|
||||
menu.push({
|
||||
text: 'New edit',
|
||||
iconClassName: 'gicon gicon-editor',
|
||||
iconClassName: 'edit',
|
||||
onClick: onNewEditPanel,
|
||||
shortcut: 'p i',
|
||||
});
|
||||
@ -198,7 +198,7 @@ export function getPanelMenu(
|
||||
menu.push({
|
||||
type: 'submenu',
|
||||
text: 'More...',
|
||||
iconClassName: 'fa fa-fw fa-cube',
|
||||
iconClassName: 'cube',
|
||||
subMenu: subMenu,
|
||||
onClick: onMore,
|
||||
});
|
||||
@ -208,7 +208,7 @@ export function getPanelMenu(
|
||||
|
||||
menu.push({
|
||||
text: 'Remove',
|
||||
iconClassName: 'fa fa-fw fa-trash',
|
||||
iconClassName: 'trash-alt',
|
||||
onClick: onRemovePanel,
|
||||
shortcut: 'p r',
|
||||
});
|
||||
|
@ -54,7 +54,7 @@ export const SearchItem: FC<Props> = ({ item, editable, onToggleSelection, onTag
|
||||
>
|
||||
<SearchCheckbox editable={editable} checked={item.checked} onClick={toggleItem} />
|
||||
<a href={item.url} className={styles.link}>
|
||||
<Icon className={styles.icon} name="th-large" />
|
||||
<Icon className={styles.icon} name="apps" />
|
||||
<div className={styles.body} onClick={onItemClick}>
|
||||
<span>{item.title}</span>
|
||||
<span className={styles.folderTitle}>{item.folderTitle}</span>
|
||||
|
@ -1,8 +1,7 @@
|
||||
import React, { FC } from 'react';
|
||||
import { css, cx } from 'emotion';
|
||||
import { GrafanaTheme } from '@grafana/data';
|
||||
import { Icon, stylesFactory, useTheme } from '@grafana/ui';
|
||||
import { IconType } from '@grafana/ui/src/components/Icon/types';
|
||||
import { Icon, IconName, stylesFactory, useTheme } from '@grafana/ui';
|
||||
import { DashboardSection, ItemClickWithEvent } from '../types';
|
||||
import { SearchItem } from './SearchItem';
|
||||
import { SearchCheckbox } from './SearchCheckbox';
|
||||
@ -99,7 +98,7 @@ const SectionHeader: FC<SectionHeaderProps> = ({ section, onSectionClick, onTogg
|
||||
checked={section.checked}
|
||||
onClick={(e: MouseEvent) => onToggleSelection(section, e)}
|
||||
/>
|
||||
<Icon className={styles.icon} name={section.icon as IconType} />
|
||||
<Icon className={styles.icon} name={section.icon as IconName} />
|
||||
|
||||
<span className={styles.text}>{section.title}</span>
|
||||
{section.url && (
|
||||
|
@ -17,7 +17,7 @@ export function PromExploreExtraField(props: PromExploreExtraFieldProps) {
|
||||
const { label, onChangeFunc, onKeyDownFunc, value, hasTooltip, tooltipContent } = props;
|
||||
|
||||
return (
|
||||
<div className="gf-form-inline explore-input--ml">
|
||||
<div className="gf-form-inline explore-input--ml" aria-label="Prometheus extra field">
|
||||
<div className="gf-form">
|
||||
<FormLabel width={5} tooltip={hasTooltip ? tooltipContent : null}>
|
||||
{label}
|
||||
|
@ -1,8 +1,7 @@
|
||||
import React from 'react';
|
||||
import { mount, shallow } from 'enzyme';
|
||||
import { shallow } from 'enzyme';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import PromExploreQueryEditor from './PromExploreQueryEditor';
|
||||
import { PromExploreExtraField } from './PromExploreExtraField';
|
||||
import { PrometheusDatasource } from '../datasource';
|
||||
import { PromQuery } from '../types';
|
||||
import { LoadingState, PanelData, toUtc } from '@grafana/data';
|
||||
@ -81,8 +80,8 @@ describe('PromExploreQueryEditor', () => {
|
||||
it('should render PromQueryField with ExtraFieldElement', async () => {
|
||||
// @ts-ignore strict null errpr TS2345: Argument of type '() => Promise<void>' is not assignable to parameter of type '() => void | undefined'.
|
||||
await act(async () => {
|
||||
const wrapper = setup(mount);
|
||||
expect(wrapper.find(PromExploreExtraField).length).toBe(1);
|
||||
const wrapper = setup(shallow);
|
||||
expect(wrapper.html()).toContain('aria-label="Prometheus extra field"');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
exports[`PrometheusExploreExtraField should render component 1`] = `
|
||||
<div
|
||||
aria-label="Prometheus extra field"
|
||||
className="gf-form-inline explore-input--ml"
|
||||
>
|
||||
<div
|
||||
|
@ -1,10 +1,10 @@
|
||||
// Libraries
|
||||
import React, { PureComponent, FC } from 'react';
|
||||
import { css, cx } from 'emotion';
|
||||
import { css } from 'emotion';
|
||||
|
||||
// Utils & Services
|
||||
import { PanelPlugin } from '@grafana/data';
|
||||
import { stylesFactory, styleMixins } from '@grafana/ui';
|
||||
import { stylesFactory, styleMixins, Icon, IconName } from '@grafana/ui';
|
||||
import config from 'app/core/config';
|
||||
|
||||
// Types
|
||||
@ -21,25 +21,25 @@ export class GrafanaLinksPanel extends PureComponent<Props> {
|
||||
<div className={styles.list}>
|
||||
<HomeLink
|
||||
title="Documentation"
|
||||
icon="fa fa-book"
|
||||
icon="book"
|
||||
url="https://grafana.com/docs/grafana/latest?utm_source=grafana_homelinks"
|
||||
target="_blank"
|
||||
/>
|
||||
<HomeLink
|
||||
title="Getting started"
|
||||
icon="fa fa-bolt"
|
||||
icon="bolt"
|
||||
url="https://grafana.com/docs/grafana/latest/guides/getting_started/?utm_source=grafana_homelinks"
|
||||
target="_blank"
|
||||
/>
|
||||
<HomeLink
|
||||
title="Community forum"
|
||||
icon="fa fa-comments"
|
||||
icon="comments-alt"
|
||||
url="https://community.grafana.com?utm_source=grafana_homelinks"
|
||||
target="_blank"
|
||||
/>
|
||||
<HomeLink
|
||||
title="Report a bug"
|
||||
icon="fa fa-bug"
|
||||
icon="bug"
|
||||
url="https://github.com/grafana/grafana/issues/new?template=1-bug_report.md"
|
||||
target="_blank"
|
||||
/>
|
||||
@ -54,7 +54,7 @@ interface HomeLinkProps {
|
||||
title: string;
|
||||
url: string;
|
||||
target?: string;
|
||||
icon: string;
|
||||
icon: IconName;
|
||||
}
|
||||
|
||||
export const HomeLink: FC<HomeLinkProps> = ({ title, url, target, icon }) => {
|
||||
@ -62,7 +62,7 @@ export const HomeLink: FC<HomeLinkProps> = ({ title, url, target, icon }) => {
|
||||
|
||||
return (
|
||||
<a className={styles.item} href={url} target={target}>
|
||||
<i className={cx(icon, styles.icon)} />
|
||||
<Icon name={icon} className={styles.icon} />
|
||||
{title}
|
||||
</a>
|
||||
);
|
||||
@ -89,7 +89,7 @@ export const getStyles = stylesFactory(() => {
|
||||
padding: ${theme.spacing.md};
|
||||
`,
|
||||
icon: css`
|
||||
padding-right: ${theme.spacing.sm};
|
||||
margin-right: ${theme.spacing.sm};
|
||||
`,
|
||||
footer: css`
|
||||
${styleMixins.listItem(theme)}
|
||||
|
@ -260,19 +260,17 @@
|
||||
margin-bottom: -2px;
|
||||
@include border-radius(5px 5px 5px 0);
|
||||
}
|
||||
|
||||
// Caret to indicate there is a submenu
|
||||
.dropdown-submenu > a::after {
|
||||
position: absolute;
|
||||
top: 35%;
|
||||
right: $space-sm;
|
||||
background-color: transparent;
|
||||
color: $text-color-weak;
|
||||
font: normal normal normal $font-size-sm/1 FontAwesome;
|
||||
content: '\f0da';
|
||||
pointer-events: none;
|
||||
font-size: 11px;
|
||||
}
|
||||
// .dropdown-submenu > a::after {
|
||||
// position: absolute;
|
||||
// top: 35%;
|
||||
// right: $space-sm;
|
||||
// background-color: transparent;
|
||||
// color: $text-color-weak;
|
||||
// font: normal normal normal $font-size-sm/1 FontAwesome;
|
||||
// content: '\f0da';
|
||||
// pointer-events: none;
|
||||
// font-size: 11px;
|
||||
// }
|
||||
.dropdown-submenu:hover > a::after {
|
||||
border-left-color: $dropdownLinkColorHover;
|
||||
}
|
||||
@ -310,14 +308,6 @@
|
||||
margin-left: $spacer;
|
||||
color: $text-muted;
|
||||
min-width: 47px;
|
||||
|
||||
&::before {
|
||||
font-family: FontAwesome;
|
||||
width: 28px;
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
content: '\f11c';
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-menu.dropdown-menu--new {
|
||||
|
@ -61,13 +61,9 @@ $panel-header-no-title-zindex: 1;
|
||||
.panel-menu-toggle {
|
||||
color: $text-color-weak;
|
||||
cursor: pointer;
|
||||
padding: 3px 5px;
|
||||
margin: 2px 0 0 2px;
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
left: 1px;
|
||||
top: 2px;
|
||||
|
||||
&:hover {
|
||||
color: $link-hover-color;
|
||||
|
@ -116,26 +116,9 @@ $mobile-menu-breakpoint: md;
|
||||
width: 35px;
|
||||
height: 35px;
|
||||
display: inline-block;
|
||||
|
||||
.fa,
|
||||
.icon-gf,
|
||||
.gicon {
|
||||
color: $side-menu-link-color;
|
||||
position: relative;
|
||||
opacity: 0.7;
|
||||
font-size: 130%;
|
||||
height: 22px;
|
||||
width: 22px;
|
||||
}
|
||||
|
||||
.fa {
|
||||
top: 2px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.icon-gf {
|
||||
top: 5px;
|
||||
}
|
||||
color: $side-menu-link-color;
|
||||
position: relative;
|
||||
opacity: 0.7;
|
||||
|
||||
img {
|
||||
position: relative;
|
||||
|
@ -23,6 +23,15 @@ angular.module('grafana.routes', ['ngRoute']);
|
||||
jest.mock('app/core/core', () => ({}));
|
||||
jest.mock('app/features/plugins/plugin_loader', () => ({}));
|
||||
|
||||
/* Temporary solution as Jest can't parse Unicons imports.
|
||||
* Therefore we are mocking in for all tests. Needs to be fixed before merging to master.
|
||||
*/
|
||||
jest.mock('@grafana/ui/src/components/Icon/Icon', () => {
|
||||
return {
|
||||
Icon: () => null as any,
|
||||
};
|
||||
});
|
||||
|
||||
configure({ adapter: new Adapter() });
|
||||
|
||||
const localStorageMock = (() => {
|
||||
|
@ -7,7 +7,15 @@ function shouldExclude(filename) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const packagesToProcessbyBabel = ['debug', 'lru-cache', 'yallist', 'apache-arrow', 'react-hook-form', 'rc-trigger'];
|
||||
const packagesToProcessbyBabel = [
|
||||
'debug',
|
||||
'lru-cache',
|
||||
'yallist',
|
||||
'apache-arrow',
|
||||
'react-hook-form',
|
||||
'rc-trigger',
|
||||
'@iconscout/react-unicons',
|
||||
];
|
||||
for (const package of packagesToProcessbyBabel) {
|
||||
if (filename.indexOf(`node_modules/${package}`) > 0) {
|
||||
return false;
|
||||
|
Loading…
Reference in New Issue
Block a user