mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Buttons: replace usage of .btn classnames (#33226)
* refactor(loginpage): migrate custom button styles to use Button component * refactor(certificationkey): prefer grafana-ui form elements over html elements and classnames * refactor(axisselector): prefer grafana-ui Button component over html button element * refactor(input-datasource): replace use of btn class with grafana-ui components * chore(grafana-ui): delete deprecated ToggleButtonGroup component * refactor: replace btn and cta-form__close class usage with IconButton * chore(closebutton): post master merge use v2 theme * refactor(permissionlist): remove usage of .btn classname * Wip * docs(styling): update styling and theme docs import paths * refactor(alerting): remote btn classnames from TestRuleResult * refactor(apikeys): prefer grafana-ui Button components over btn classNames * refactor(folders): prefer grafana-ui Button components over btn classNames * refactor(teams): prefer grafana-ui Button components over btn classNames * refactor(datasources): prefer grafana-ui Button components over btn classNames * refactor: prefer grafana-ui Button components over btn classNames * Minor style tweak to service buttons * test: update snapshots related to button changes * chore(input-datasource): remove unused import declaration * refactor(loginservicebuttons): rename theme.palette to theme.colors Co-authored-by: Torkel Ödegaard <torkel@grafana.com>
This commit is contained in:
parent
6034bf37c6
commit
c809d63065
@ -10,7 +10,7 @@ For styling components, use [Emotion's `css` function](https://emotion.sh/docs/e
|
|||||||
|
|
||||||
```tsx
|
```tsx
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { css } from 'emotion';
|
import { css } from '@emotion/css';
|
||||||
|
|
||||||
const ComponentA = () => (
|
const ComponentA = () => (
|
||||||
<div
|
<div
|
||||||
@ -33,14 +33,13 @@ To access the theme in your styles, use the `useStyles` hook. It provides basic
|
|||||||
import React, { FC } from 'react';
|
import React, { FC } from 'react';
|
||||||
import { GrafanaTheme } from '@grafana/data';
|
import { GrafanaTheme } from '@grafana/data';
|
||||||
import { useStyles } from '@grafana/ui';
|
import { useStyles } from '@grafana/ui';
|
||||||
import { css } from 'emotion';
|
import { css } from '@emotion/css';
|
||||||
|
|
||||||
const Foo: FC<FooProps> = () => {
|
const Foo: FC<FooProps> = () => {
|
||||||
const styles = useStyles(getStyles);
|
const styles = useStyles(getStyles);
|
||||||
|
|
||||||
// Use styles with classNames
|
// Use styles with classNames
|
||||||
return <div className={styles}>...</div>
|
return <div className={styles}>...</div>;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const getStyles = (theme: GrafanaTheme) => css`
|
const getStyles = (theme: GrafanaTheme) => css`
|
||||||
@ -56,15 +55,15 @@ Let's say you need to style a component that has a different background dependin
|
|||||||
|
|
||||||
```tsx
|
```tsx
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { css } from 'emotion';
|
import { css } from '@emotion/css';
|
||||||
import { GrafanaTheme } from '@grafana/data';
|
import { GrafanaTheme } from '@grafana/data';
|
||||||
import { selectThemeVariant, stylesFactory, useTheme } from '@grafana/ui';
|
import { selectThemeVariant, stylesFactory, useTheme } from '@grafana/ui';
|
||||||
|
|
||||||
interface ComponentAProps {
|
interface ComponentAProps {
|
||||||
isActive: boolean
|
isActive: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ComponentA: React.FC<ComponentAProps> = ({isActive}) => {
|
const ComponentA: React.FC<ComponentAProps> = ({ isActive }) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const styles = getStyles(theme, isActive);
|
const styles = getStyles(theme, isActive);
|
||||||
|
|
||||||
@ -76,7 +75,6 @@ const ComponentA: React.FC<ComponentAProps> = ({isActive}) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// Mind, that you can pass multiple arguments, theme included
|
// Mind, that you can pass multiple arguments, theme included
|
||||||
const getStyles = stylesFactory((theme: GrafanaTheme, isActive: boolean) => {
|
const getStyles = stylesFactory((theme: GrafanaTheme, isActive: boolean) => {
|
||||||
const backgroundColor = isActive ? theme.colors.red : theme.colors.blue;
|
const backgroundColor = isActive ? theme.colors.red : theme.colors.blue;
|
||||||
@ -100,7 +98,7 @@ For class composition, use [Emotion's `cx` function](https://emotion.sh/docs/emo
|
|||||||
|
|
||||||
```tsx
|
```tsx
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { css, cx } from 'emotion';
|
import { css, cx } from '@emotion/css';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
className?: string;
|
className?: string;
|
||||||
|
@ -26,7 +26,7 @@ Here's how to use Grafana themes in React components.
|
|||||||
import React, { FC } from 'react';
|
import React, { FC } from 'react';
|
||||||
import { GrafanaTheme } from '@grafana/data';
|
import { GrafanaTheme } from '@grafana/data';
|
||||||
import { useStyles } from '@grafana/ui';
|
import { useStyles } from '@grafana/ui';
|
||||||
import { css } from 'emotion';
|
import { css } from '@emotion/css';
|
||||||
|
|
||||||
const getComponentStyles = (theme: GrafanaTheme) => css`
|
const getComponentStyles = (theme: GrafanaTheme) => css`
|
||||||
padding: ${theme.spacing.md};
|
padding: ${theme.spacing.md};
|
||||||
@ -57,7 +57,7 @@ const Foo: FC<FooProps> = () => {
|
|||||||
```tsx
|
```tsx
|
||||||
import { ThemeContext } from '@grafana/ui';
|
import { ThemeContext } from '@grafana/ui';
|
||||||
|
|
||||||
<ThemeContext.Consumer>{theme => <Foo theme={theme} />}</ThemeContext.Consumer>;
|
<ThemeContext.Consumer>{(theme) => <Foo theme={theme} />}</ThemeContext.Consumer>;
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Using `withTheme` higher-order component (HOC)
|
#### Using `withTheme` higher-order component (HOC)
|
||||||
@ -97,9 +97,8 @@ describe('MyComponent', () => {
|
|||||||
restoreThemeContext();
|
restoreThemeContext();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('renders correctly', () => {
|
it('renders correctly', () => {
|
||||||
const wrapper = mount(<MyComponent />)
|
const wrapper = mount(<MyComponent />);
|
||||||
expect(wrapper).toMatchSnapshot();
|
expect(wrapper).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -5,6 +5,7 @@ import { PopoverContentProps } from '../Tooltip/Tooltip';
|
|||||||
import { Switch } from '../Forms/Legacy/Switch/Switch';
|
import { Switch } from '../Forms/Legacy/Switch/Switch';
|
||||||
import { css } from '@emotion/css';
|
import { css } from '@emotion/css';
|
||||||
import { withTheme, useStyles } from '../../themes';
|
import { withTheme, useStyles } from '../../themes';
|
||||||
|
import { Button } from '../Button';
|
||||||
|
|
||||||
export interface SeriesColorPickerPopoverProps extends ColorPickerProps, PopoverContentProps {
|
export interface SeriesColorPickerPopoverProps extends ColorPickerProps, PopoverContentProps {
|
||||||
yaxis?: number;
|
yaxis?: number;
|
||||||
@ -70,18 +71,18 @@ export class AxisSelector extends React.PureComponent<AxisSelectorProps, AxisSel
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const leftButtonClass = this.state.yaxis === 1 ? 'btn-primary' : 'btn-inverse';
|
const leftButtonVariant = this.state.yaxis === 1 ? 'primary' : 'secondary';
|
||||||
const rightButtonClass = this.state.yaxis === 2 ? 'btn-primary' : 'btn-inverse';
|
const rightButtonVariant = this.state.yaxis === 2 ? 'primary' : 'secondary';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="p-b-1">
|
<div className="p-b-1">
|
||||||
<label className="small p-r-1">Y Axis:</label>
|
<label className="small p-r-1">Y Axis:</label>
|
||||||
<button onClick={this.onToggleAxis} className={'btn btn-small ' + leftButtonClass}>
|
<Button onClick={this.onToggleAxis} size="sm" variant={leftButtonVariant}>
|
||||||
Left
|
Left
|
||||||
</button>
|
</Button>
|
||||||
<button onClick={this.onToggleAxis} className={'btn btn-small ' + rightButtonClass}>
|
<Button onClick={this.onToggleAxis} size="sm" variant={rightButtonVariant}>
|
||||||
Right
|
Right
|
||||||
</button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,8 @@
|
|||||||
import React, { ChangeEvent, MouseEvent, FC } from 'react';
|
import React, { ChangeEvent, MouseEvent, FC } from 'react';
|
||||||
|
import { Input } from '../Input/Input';
|
||||||
|
import { Button } from '../Button';
|
||||||
|
import { TextArea } from '../TextArea/TextArea';
|
||||||
|
import { InlineField } from '../Forms/InlineField';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
label: string;
|
label: string;
|
||||||
@ -6,35 +10,22 @@ interface Props {
|
|||||||
placeholder: string;
|
placeholder: string;
|
||||||
|
|
||||||
onChange: (event: ChangeEvent<HTMLTextAreaElement>) => void;
|
onChange: (event: ChangeEvent<HTMLTextAreaElement>) => void;
|
||||||
onClick: (event: MouseEvent<HTMLAnchorElement>) => void;
|
onClick: (event: MouseEvent<HTMLButtonElement>) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const CertificationKey: FC<Props> = ({ hasCert, label, onChange, onClick, placeholder }) => {
|
export const CertificationKey: FC<Props> = ({ hasCert, label, onChange, onClick, placeholder }) => {
|
||||||
return (
|
return (
|
||||||
<div className="gf-form-inline">
|
<InlineField label={label} labelWidth={14}>
|
||||||
<div className="gf-form gf-form--v-stretch">
|
{hasCert ? (
|
||||||
<label className="gf-form-label width-7">{label}</label>
|
<>
|
||||||
</div>
|
<Input type="text" disabled value="configured" width={24} />
|
||||||
{!hasCert && (
|
<Button variant="secondary" onClick={onClick} style={{ marginLeft: 4 }}>
|
||||||
<div className="gf-form gf-form--grow">
|
Reset
|
||||||
<textarea
|
</Button>
|
||||||
rows={7}
|
</>
|
||||||
className="gf-form-input gf-form-textarea"
|
) : (
|
||||||
onChange={onChange}
|
<TextArea rows={7} onChange={onChange} placeholder={placeholder} required />
|
||||||
placeholder={placeholder}
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
|
</InlineField>
|
||||||
{hasCert && (
|
|
||||||
<div className="gf-form">
|
|
||||||
<input type="text" className="gf-form-input max-width-12" disabled value="configured" />
|
|
||||||
<a className="btn btn-secondary gf-form-btn" onClick={onClick}>
|
|
||||||
reset
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -13,7 +13,7 @@ export const TLSAuthSettings: React.FC<HttpSettingsBaseProps> = ({ dataSourceCon
|
|||||||
const hasTLSClientKey = dataSourceConfig.secureJsonFields && dataSourceConfig.secureJsonFields.tlsClientKey;
|
const hasTLSClientKey = dataSourceConfig.secureJsonFields && dataSourceConfig.secureJsonFields.tlsClientKey;
|
||||||
const hasServerName = dataSourceConfig.jsonData && dataSourceConfig.jsonData.serverName;
|
const hasServerName = dataSourceConfig.jsonData && dataSourceConfig.jsonData.serverName;
|
||||||
|
|
||||||
const onResetClickFactory = (field: string) => (event: React.MouseEvent<HTMLAnchorElement>) => {
|
const onResetClickFactory = (field: string) => (event: React.MouseEvent<HTMLButtonElement>) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const newSecureJsonFields: KeyValue<boolean> = { ...dataSourceConfig.secureJsonFields };
|
const newSecureJsonFields: KeyValue<boolean> = { ...dataSourceConfig.secureJsonFields };
|
||||||
newSecureJsonFields[field] = false;
|
newSecureJsonFields[field] = false;
|
||||||
|
@ -1,50 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { action } from '@storybook/addon-actions';
|
|
||||||
|
|
||||||
import { ToggleButton, ToggleButtonGroup } from './ToggleButtonGroup';
|
|
||||||
import { UseState } from '../../utils/storybook/UseState';
|
|
||||||
import { withCenteredStory } from '../../utils/storybook/withCenteredStory';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
title: 'Forms/Legacy/ToggleButtonGroup',
|
|
||||||
component: ToggleButtonGroup,
|
|
||||||
decorators: [withCenteredStory],
|
|
||||||
};
|
|
||||||
|
|
||||||
const options = [
|
|
||||||
{ value: 'first', label: 'First' },
|
|
||||||
{ value: 'second', label: 'Second' },
|
|
||||||
{ value: 'third', label: 'Third' },
|
|
||||||
];
|
|
||||||
|
|
||||||
export const basic = () => {
|
|
||||||
return (
|
|
||||||
<UseState
|
|
||||||
initialState={{
|
|
||||||
value: 'first',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{(value, updateValue) => {
|
|
||||||
return (
|
|
||||||
<ToggleButtonGroup label="Options">
|
|
||||||
{options.map((option, index) => {
|
|
||||||
return (
|
|
||||||
<ToggleButton
|
|
||||||
key={`${option.value}-${index}`}
|
|
||||||
value={option.value}
|
|
||||||
onChange={(newValue) => {
|
|
||||||
action('on change')(newValue);
|
|
||||||
updateValue({ value: newValue });
|
|
||||||
}}
|
|
||||||
selected={value.value === option.value}
|
|
||||||
>
|
|
||||||
{option.label}
|
|
||||||
</ToggleButton>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</ToggleButtonGroup>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
</UseState>
|
|
||||||
);
|
|
||||||
};
|
|
@ -1,77 +0,0 @@
|
|||||||
import React, { FC, ReactNode, PureComponent } from 'react';
|
|
||||||
import classNames from 'classnames';
|
|
||||||
import { Tooltip } from '../Tooltip/Tooltip';
|
|
||||||
import { deprecationWarning } from '@grafana/data';
|
|
||||||
|
|
||||||
interface ToggleButtonGroupProps {
|
|
||||||
label?: string;
|
|
||||||
children: JSX.Element[];
|
|
||||||
transparent?: boolean;
|
|
||||||
width?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @deprecated */
|
|
||||||
export class ToggleButtonGroup extends PureComponent<ToggleButtonGroupProps> {
|
|
||||||
render() {
|
|
||||||
const { children, label, transparent, width } = this.props;
|
|
||||||
const labelClasses = classNames('gf-form-label', {
|
|
||||||
'gf-form-label--transparent': transparent,
|
|
||||||
[`width-${width}`]: width,
|
|
||||||
});
|
|
||||||
const buttonGroupClasses = classNames('toggle-button-group', {
|
|
||||||
'toggle-button-group--transparent': transparent,
|
|
||||||
'toggle-button-group--padded': width, // Add extra padding to compensate for buttons border
|
|
||||||
});
|
|
||||||
|
|
||||||
deprecationWarning('ToggleButtonGroup', 'ToggleButtonGroup', 'RadioButtonGroup');
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="gf-form gf-form--align-center">
|
|
||||||
{label && <label className={labelClasses}>{label}</label>}
|
|
||||||
<div className={buttonGroupClasses}>{children}</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ToggleButtonProps {
|
|
||||||
onChange?: (value: any) => void;
|
|
||||||
selected?: boolean;
|
|
||||||
value: any;
|
|
||||||
className?: string;
|
|
||||||
children: ReactNode;
|
|
||||||
tooltip?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const ToggleButton: FC<ToggleButtonProps> = ({
|
|
||||||
children,
|
|
||||||
selected,
|
|
||||||
className = '',
|
|
||||||
value = null,
|
|
||||||
tooltip,
|
|
||||||
onChange,
|
|
||||||
}) => {
|
|
||||||
const onClick = (event: React.SyntheticEvent) => {
|
|
||||||
event.stopPropagation();
|
|
||||||
if (!selected && onChange) {
|
|
||||||
onChange(value);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const btnClassName = `btn ${className}${selected ? ' active' : ''}`;
|
|
||||||
const button = (
|
|
||||||
<button className={btnClassName} onClick={onClick}>
|
|
||||||
<span>{children}</span>
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
|
|
||||||
if (tooltip) {
|
|
||||||
return (
|
|
||||||
<Tooltip content={tooltip} placement="bottom">
|
|
||||||
{button}
|
|
||||||
</Tooltip>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return button;
|
|
||||||
}
|
|
||||||
};
|
|
@ -110,7 +110,6 @@ export { LogRows } from './Logs/LogRows';
|
|||||||
export { getLogRowStyles } from './Logs/getLogRowStyles';
|
export { getLogRowStyles } from './Logs/getLogRowStyles';
|
||||||
export { DataLinkButton } from './DataLinks/DataLinkButton';
|
export { DataLinkButton } from './DataLinks/DataLinkButton';
|
||||||
export { FieldLinkList } from './DataLinks/FieldLinkList';
|
export { FieldLinkList } from './DataLinks/FieldLinkList';
|
||||||
export { ToggleButtonGroup, ToggleButton } from './ToggleButtonGroup/ToggleButtonGroup';
|
|
||||||
// Panel editors
|
// Panel editors
|
||||||
export { FullWidthButtonContainer } from './Button/FullWidthButtonContainer';
|
export { FullWidthButtonContainer } from './Button/FullWidthButtonContainer';
|
||||||
export { ClickOutsideWrapper } from './ClickOutsideWrapper/ClickOutsideWrapper';
|
export { ClickOutsideWrapper } from './ClickOutsideWrapper/ClickOutsideWrapper';
|
||||||
|
@ -76,8 +76,11 @@ export type IconName =
|
|||||||
| 'gf-interpolation-step-after'
|
| 'gf-interpolation-step-after'
|
||||||
| 'gf-interpolation-step-before'
|
| 'gf-interpolation-step-before'
|
||||||
| 'gf-logs'
|
| 'gf-logs'
|
||||||
|
| 'github'
|
||||||
|
| 'gitlab'
|
||||||
| 'grafana'
|
| 'grafana'
|
||||||
| 'graph-bar'
|
| 'graph-bar'
|
||||||
|
| 'google'
|
||||||
| 'heart-break'
|
| 'heart-break'
|
||||||
| 'heart'
|
| 'heart'
|
||||||
| 'history'
|
| 'history'
|
||||||
@ -90,10 +93,12 @@ export type IconName =
|
|||||||
| 'link'
|
| 'link'
|
||||||
| 'list-ul'
|
| 'list-ul'
|
||||||
| 'lock'
|
| 'lock'
|
||||||
|
| 'microsoft'
|
||||||
| 'minus-circle'
|
| 'minus-circle'
|
||||||
| 'minus'
|
| 'minus'
|
||||||
| 'mobile-android'
|
| 'mobile-android'
|
||||||
| 'monitor'
|
| 'monitor'
|
||||||
|
| 'okta'
|
||||||
| 'palette'
|
| 'palette'
|
||||||
| 'panel-add'
|
| 'panel-add'
|
||||||
| 'pause'
|
| 'pause'
|
||||||
|
@ -5,8 +5,7 @@ import React, { PureComponent } from 'react';
|
|||||||
import { InputDatasource, describeDataFrame } from './InputDatasource';
|
import { InputDatasource, describeDataFrame } from './InputDatasource';
|
||||||
import { InputQuery, InputOptions } from './types';
|
import { InputQuery, InputOptions } from './types';
|
||||||
|
|
||||||
import { InlineFormLabel, LegacyForms, TableInputCSV, Icon } from '@grafana/ui';
|
import { Select, TableInputCSV, LinkButton, Icon, InlineField } from '@grafana/ui';
|
||||||
const { Select } = LegacyForms;
|
|
||||||
import { DataFrame, toCSV, SelectableValue, MutableDataFrame, QueryEditorProps } from '@grafana/data';
|
import { DataFrame, toCSV, SelectableValue, MutableDataFrame, QueryEditorProps } from '@grafana/data';
|
||||||
|
|
||||||
import { dataFrameToCSV } from './utils';
|
import { dataFrameToCSV } from './utils';
|
||||||
@ -68,21 +67,19 @@ export class InputQueryEditor extends PureComponent<Props, State> {
|
|||||||
const selected = query.data ? options[0] : options[1];
|
const selected = query.data ? options[0] : options[1];
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className="gf-form">
|
<InlineField label="Data" labelWidth={8}>
|
||||||
<InlineFormLabel width={4}>Data</InlineFormLabel>
|
<>
|
||||||
<Select width={6} options={options} value={selected} onChange={this.onSourceChange} />
|
<Select width={20} options={options} value={selected} onChange={this.onSourceChange} />
|
||||||
|
|
||||||
<div className="btn btn-link">
|
|
||||||
{query.data ? (
|
{query.data ? (
|
||||||
describeDataFrame(query.data)
|
<div style={{ alignSelf: 'center' }}>{describeDataFrame(query.data)}</div>
|
||||||
) : (
|
) : (
|
||||||
<a href={`datasources/edit/${id}/`}>
|
<LinkButton variant="link" href={`datasources/edit/${id}/`}>
|
||||||
{name}: {describeDataFrame(datasource.data)}
|
{name}: {describeDataFrame(datasource.data)}
|
||||||
<Icon name="pen" />
|
<Icon name="pen" />
|
||||||
</a>
|
</LinkButton>
|
||||||
)}
|
)}
|
||||||
</div>
|
</>
|
||||||
</div>
|
</InlineField>
|
||||||
{query.data && <TableInputCSV text={text} onSeriesParsed={this.onSeriesParsed} width={'100%'} height={200} />}
|
{query.data && <TableInputCSV text={text} onSeriesParsed={this.onSeriesParsed} width={'100%'} height={200} />}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
20
public/app/core/components/CloseButton/CloseButton.tsx
Normal file
20
public/app/core/components/CloseButton/CloseButton.tsx
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { css } from '@emotion/css';
|
||||||
|
import { IconButton, useStyles2 } from '@grafana/ui';
|
||||||
|
import { GrafanaThemeV2 } from '@grafana/data';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
onClick: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const CloseButton: React.FC<Props> = ({ onClick }) => {
|
||||||
|
const styles = useStyles2(getStyles);
|
||||||
|
return <IconButton className={styles} name="times" onClick={onClick} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getStyles = (theme: GrafanaThemeV2) =>
|
||||||
|
css`
|
||||||
|
position: absolute;
|
||||||
|
right: ${theme.spacing(0.5)};
|
||||||
|
top: ${theme.spacing(1)};
|
||||||
|
`;
|
@ -40,28 +40,26 @@ export const LoginPage: FC = () => {
|
|||||||
{!isChangingPassword && (
|
{!isChangingPassword && (
|
||||||
<InnerBox>
|
<InnerBox>
|
||||||
{!disableLoginForm && (
|
{!disableLoginForm && (
|
||||||
<>
|
<LoginForm
|
||||||
<LoginForm
|
onSubmit={login}
|
||||||
onSubmit={login}
|
loginHint={loginHint}
|
||||||
loginHint={loginHint}
|
passwordHint={passwordHint}
|
||||||
passwordHint={passwordHint}
|
isLoggingIn={isLoggingIn}
|
||||||
isLoggingIn={isLoggingIn}
|
>
|
||||||
>
|
{!(ldapEnabled || authProxyEnabled) ? (
|
||||||
{!(ldapEnabled || authProxyEnabled) ? (
|
<HorizontalGroup justify="flex-end">
|
||||||
<HorizontalGroup justify="flex-end">
|
<LinkButton
|
||||||
<LinkButton
|
className={forgottenPasswordStyles}
|
||||||
className={forgottenPasswordStyles}
|
variant="link"
|
||||||
variant="link"
|
href={`${config.appSubUrl}/user/password/send-reset-email`}
|
||||||
href={`${config.appSubUrl}/user/password/send-reset-email`}
|
>
|
||||||
>
|
Forgot your password?
|
||||||
Forgot your password?
|
</LinkButton>
|
||||||
</LinkButton>
|
</HorizontalGroup>
|
||||||
</HorizontalGroup>
|
) : (
|
||||||
) : (
|
<></>
|
||||||
<></>
|
)}
|
||||||
)}
|
</LoginForm>
|
||||||
</LoginForm>
|
|
||||||
</>
|
|
||||||
)}
|
)}
|
||||||
<LoginServiceButtons />
|
<LoginServiceButtons />
|
||||||
{!disableUserSignUp && <UserSignup />}
|
{!disableUserSignUp && <UserSignup />}
|
||||||
|
@ -1,89 +1,99 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import config from 'app/core/config';
|
import config from 'app/core/config';
|
||||||
import { css, cx } from '@emotion/css';
|
import { css, cx } from '@emotion/css';
|
||||||
import { useStyles } from '@grafana/ui';
|
import { Icon, IconName, LinkButton, useStyles, useTheme2, VerticalGroup } from '@grafana/ui';
|
||||||
import { GrafanaTheme } from '@grafana/data';
|
import { GrafanaTheme, GrafanaThemeV2 } from '@grafana/data';
|
||||||
|
import { pickBy } from 'lodash';
|
||||||
const loginServices: () => LoginServices = () => {
|
|
||||||
const oauthEnabled = !!config.oauth;
|
|
||||||
|
|
||||||
return {
|
|
||||||
saml: {
|
|
||||||
enabled: config.samlEnabled,
|
|
||||||
name: 'SAML',
|
|
||||||
className: 'github',
|
|
||||||
icon: 'key',
|
|
||||||
},
|
|
||||||
google: {
|
|
||||||
enabled: oauthEnabled && config.oauth.google,
|
|
||||||
name: 'Google',
|
|
||||||
},
|
|
||||||
azuread: {
|
|
||||||
enabled: oauthEnabled && config.oauth.azuread,
|
|
||||||
name: 'Microsoft',
|
|
||||||
},
|
|
||||||
github: {
|
|
||||||
enabled: oauthEnabled && config.oauth.github,
|
|
||||||
name: 'GitHub',
|
|
||||||
},
|
|
||||||
gitlab: {
|
|
||||||
enabled: oauthEnabled && config.oauth.gitlab,
|
|
||||||
name: 'GitLab',
|
|
||||||
},
|
|
||||||
grafanacom: {
|
|
||||||
enabled: oauthEnabled && config.oauth.grafana_com,
|
|
||||||
name: 'Grafana.com',
|
|
||||||
hrefName: 'grafana_com',
|
|
||||||
icon: 'grafana_com',
|
|
||||||
},
|
|
||||||
okta: {
|
|
||||||
enabled: oauthEnabled && config.oauth.okta,
|
|
||||||
name: 'Okta',
|
|
||||||
},
|
|
||||||
oauth: {
|
|
||||||
enabled: oauthEnabled && config.oauth.generic_oauth,
|
|
||||||
name: oauthEnabled && config.oauth.generic_oauth ? config.oauth.generic_oauth.name : 'OAuth',
|
|
||||||
icon: 'sign-in',
|
|
||||||
hrefName: 'generic_oauth',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export interface LoginService {
|
export interface LoginService {
|
||||||
|
bgColor: string;
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
name: string;
|
name: string;
|
||||||
hrefName?: string;
|
hrefName?: string;
|
||||||
icon?: string;
|
icon: IconName;
|
||||||
className?: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LoginServices {
|
export interface LoginServices {
|
||||||
[key: string]: LoginService;
|
[key: string]: LoginService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const loginServices: () => LoginServices = () => {
|
||||||
|
const oauthEnabled = !!config.oauth;
|
||||||
|
|
||||||
|
return {
|
||||||
|
saml: {
|
||||||
|
bgColor: '#464646',
|
||||||
|
enabled: config.samlEnabled,
|
||||||
|
name: 'SAML',
|
||||||
|
icon: 'key-skeleton-alt',
|
||||||
|
},
|
||||||
|
google: {
|
||||||
|
bgColor: '#e84d3c',
|
||||||
|
enabled: oauthEnabled && config.oauth.google,
|
||||||
|
name: 'Google',
|
||||||
|
icon: 'google',
|
||||||
|
},
|
||||||
|
azuread: {
|
||||||
|
bgColor: '#2f2f2f',
|
||||||
|
enabled: oauthEnabled && config.oauth.azuread,
|
||||||
|
name: 'Microsoft',
|
||||||
|
icon: 'microsoft',
|
||||||
|
},
|
||||||
|
github: {
|
||||||
|
bgColor: '#464646',
|
||||||
|
enabled: oauthEnabled && config.oauth.github,
|
||||||
|
name: 'GitHub',
|
||||||
|
icon: 'github',
|
||||||
|
},
|
||||||
|
gitlab: {
|
||||||
|
bgColor: '#fc6d26',
|
||||||
|
enabled: oauthEnabled && config.oauth.gitlab,
|
||||||
|
name: 'GitLab',
|
||||||
|
icon: 'gitlab',
|
||||||
|
},
|
||||||
|
grafanacom: {
|
||||||
|
bgColor: '#262628',
|
||||||
|
enabled: oauthEnabled && config.oauth.grafana_com,
|
||||||
|
name: 'Grafana.com',
|
||||||
|
hrefName: 'grafana_com',
|
||||||
|
icon: 'grafana',
|
||||||
|
},
|
||||||
|
okta: {
|
||||||
|
bgColor: '#2f2f2f',
|
||||||
|
enabled: oauthEnabled && config.oauth.okta,
|
||||||
|
name: 'Okta',
|
||||||
|
icon: 'okta',
|
||||||
|
},
|
||||||
|
oauth: {
|
||||||
|
bgColor: '#262628',
|
||||||
|
enabled: oauthEnabled && config.oauth.generic_oauth,
|
||||||
|
name: oauthEnabled && config.oauth.generic_oauth ? config.oauth.generic_oauth.name : 'OAuth',
|
||||||
|
icon: 'signin',
|
||||||
|
hrefName: 'generic_oauth',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
const getServiceStyles = (theme: GrafanaTheme) => {
|
const getServiceStyles = (theme: GrafanaTheme) => {
|
||||||
return {
|
return {
|
||||||
container: css`
|
|
||||||
width: 100%;
|
|
||||||
text-align: center;
|
|
||||||
`,
|
|
||||||
button: css`
|
button: css`
|
||||||
color: #d8d9da;
|
color: #d8d9da;
|
||||||
margin: 0 0 ${theme.spacing.md};
|
position: relative;
|
||||||
width: 100%;
|
`,
|
||||||
&:hover {
|
buttonIcon: css`
|
||||||
color: #fff;
|
position: absolute;
|
||||||
}
|
left: ${theme.spacing.sm};
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
`,
|
`,
|
||||||
divider: {
|
divider: {
|
||||||
base: css`
|
base: css`
|
||||||
float: left;
|
color: ${theme.colors.text};
|
||||||
width: 100%;
|
|
||||||
margin: 0 25% ${theme.spacing.md} 25%;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
|
margin-bottom: ${theme.spacing.sm};
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
color: ${theme.colors.text};
|
width: 100%;
|
||||||
`,
|
`,
|
||||||
line: css`
|
line: css`
|
||||||
width: 100px;
|
width: 100px;
|
||||||
@ -114,38 +124,46 @@ const LoginDivider = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const LoginServiceButtons = () => {
|
function getButtonStyleFor(service: LoginService, styles: ReturnType<typeof getServiceStyles>, theme: GrafanaThemeV2) {
|
||||||
const styles = useStyles(getServiceStyles);
|
return cx(
|
||||||
const keyNames = Object.keys(loginServices());
|
styles.button,
|
||||||
const serviceElementsEnabled = keyNames.filter((key) => {
|
css`
|
||||||
const service: LoginService = loginServices()[key];
|
background-color: ${service.bgColor};
|
||||||
return service.enabled;
|
color: ${theme.colors.getContrastText(service.bgColor)};
|
||||||
});
|
|
||||||
|
|
||||||
if (serviceElementsEnabled.length === 0) {
|
&:hover {
|
||||||
return null;
|
background-color: ${theme.colors.emphasize(service.bgColor, 0.15)};
|
||||||
|
box-shadow: ${theme.shadows.z1};
|
||||||
|
}
|
||||||
|
`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const LoginServiceButtons = () => {
|
||||||
|
const enabledServices = pickBy(loginServices(), (service) => service.enabled);
|
||||||
|
const hasServices = Object.keys(enabledServices).length > 0;
|
||||||
|
const theme = useTheme2();
|
||||||
|
const styles = useStyles(getServiceStyles);
|
||||||
|
|
||||||
|
if (hasServices) {
|
||||||
|
return (
|
||||||
|
<VerticalGroup>
|
||||||
|
<LoginDivider />
|
||||||
|
{Object.entries(enabledServices).map(([key, service]) => (
|
||||||
|
<LinkButton
|
||||||
|
key={key}
|
||||||
|
className={getButtonStyleFor(service, styles, theme)}
|
||||||
|
href={`login/${service.hrefName ? service.hrefName : key}`}
|
||||||
|
target="_self"
|
||||||
|
fullWidth
|
||||||
|
>
|
||||||
|
<Icon className={styles.buttonIcon} name={service.icon} />
|
||||||
|
Sign in with {service.name}
|
||||||
|
</LinkButton>
|
||||||
|
))}
|
||||||
|
</VerticalGroup>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const serviceElements = serviceElementsEnabled.map((key) => {
|
return null;
|
||||||
const service: LoginService = loginServices()[key];
|
|
||||||
return (
|
|
||||||
<a
|
|
||||||
key={key}
|
|
||||||
className={cx(`btn btn-medium btn-service btn-service--${service.className || key}`, styles.button)}
|
|
||||||
href={`login/${service.hrefName ? service.hrefName : key}`}
|
|
||||||
target="_self"
|
|
||||||
>
|
|
||||||
<i className={`btn-service-icon fa fa-${service.icon ? service.icon : key}`} />
|
|
||||||
Sign in with {service.name}
|
|
||||||
</a>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
const divider = LoginDivider();
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{divider}
|
|
||||||
<div className={styles.container}>{serviceElements}</div>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
@ -8,7 +8,7 @@ export const UserSignup: FC<{}> = () => {
|
|||||||
return (
|
return (
|
||||||
<VerticalGroup
|
<VerticalGroup
|
||||||
className={css`
|
className={css`
|
||||||
margin-top: 8px;
|
margin-top: 16px;
|
||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
<span>New to Grafana?</span>
|
<span>New to Grafana?</span>
|
||||||
|
@ -3,7 +3,7 @@ import { css } from '@emotion/css';
|
|||||||
import config from 'app/core/config';
|
import config from 'app/core/config';
|
||||||
import { UserPicker } from 'app/core/components/Select/UserPicker';
|
import { UserPicker } from 'app/core/components/Select/UserPicker';
|
||||||
import { TeamPicker, Team } from 'app/core/components/Select/TeamPicker';
|
import { TeamPicker, Team } from 'app/core/components/Select/TeamPicker';
|
||||||
import { Button, Form, HorizontalGroup, Icon, Select, stylesFactory } from '@grafana/ui';
|
import { Button, Form, HorizontalGroup, Select, stylesFactory } from '@grafana/ui';
|
||||||
import { GrafanaTheme, SelectableValue } from '@grafana/data';
|
import { GrafanaTheme, SelectableValue } from '@grafana/data';
|
||||||
import { User } from 'app/types';
|
import { User } from 'app/types';
|
||||||
import {
|
import {
|
||||||
@ -14,6 +14,7 @@ import {
|
|||||||
NewDashboardAclItem,
|
NewDashboardAclItem,
|
||||||
OrgRole,
|
OrgRole,
|
||||||
} from 'app/types/acl';
|
} from 'app/types/acl';
|
||||||
|
import { CloseButton } from '../CloseButton/CloseButton';
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
onAddPermission: (item: NewDashboardAclItem) => void;
|
onAddPermission: (item: NewDashboardAclItem) => void;
|
||||||
@ -93,9 +94,7 @@ class AddPermissions extends Component<Props, NewDashboardAclItem> {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="cta-form">
|
<div className="cta-form">
|
||||||
<button className="cta-form__close btn btn-transparent" onClick={onCancel}>
|
<CloseButton onClick={onCancel} />
|
||||||
<Icon name="times" />
|
|
||||||
</button>
|
|
||||||
<h5>Add Permission For</h5>
|
<h5>Add Permission For</h5>
|
||||||
<Form maxWidth="none" onSubmit={this.onSubmit}>
|
<Form maxWidth="none" onSubmit={this.onSubmit}>
|
||||||
{() => (
|
{() => (
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import { Select, Icon } from '@grafana/ui';
|
import { Select, Icon, Button } from '@grafana/ui';
|
||||||
import { dashboardPermissionLevels } from 'app/types/acl';
|
import { dashboardPermissionLevels } from 'app/types/acl';
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
@ -33,9 +33,7 @@ export default class DisabledPermissionListItem extends Component<Props, any> {
|
|||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<button className="btn btn-inverse btn-small">
|
<Button size="sm" disabled icon="lock" />
|
||||||
<Icon name="lock" />
|
|
||||||
</button>
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
);
|
);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import React, { PureComponent } from 'react';
|
import React, { PureComponent } from 'react';
|
||||||
import { Select, Icon } from '@grafana/ui';
|
import { Select, Icon, Button } from '@grafana/ui';
|
||||||
import { SelectableValue } from '@grafana/data';
|
import { SelectableValue } from '@grafana/data';
|
||||||
import { dashboardPermissionLevels, DashboardAcl, PermissionLevel } from 'app/types/acl';
|
import { dashboardPermissionLevels, DashboardAcl, PermissionLevel } from 'app/types/acl';
|
||||||
import { FolderInfo } from 'app/types';
|
import { FolderInfo } from 'app/types';
|
||||||
@ -85,13 +85,9 @@ export default class PermissionsListItem extends PureComponent<Props> {
|
|||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{!item.inherited ? (
|
{!item.inherited ? (
|
||||||
<a className="btn btn-danger btn-small" onClick={this.onRemoveItem}>
|
<Button size="sm" variant="destructive" icon="times" onClick={this.onRemoveItem} />
|
||||||
<Icon name="times" style={{ marginBottom: 0 }} />
|
|
||||||
</a>
|
|
||||||
) : (
|
) : (
|
||||||
<button className="btn btn-inverse btn-small">
|
<Button size="sm" disabled icon="times" />
|
||||||
<Icon name="lock" style={{ marginBottom: '3px' }} />
|
|
||||||
</button>
|
|
||||||
)}
|
)}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -2,7 +2,7 @@ import React, { PureComponent } from 'react';
|
|||||||
import { hot } from 'react-hot-loader';
|
import { hot } from 'react-hot-loader';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { NavModel } from '@grafana/data';
|
import { NavModel } from '@grafana/data';
|
||||||
import { Alert, LegacyForms } from '@grafana/ui';
|
import { Alert, Button, LegacyForms } from '@grafana/ui';
|
||||||
const { FormField } = LegacyForms;
|
const { FormField } = LegacyForms;
|
||||||
import { getNavModel } from 'app/core/selectors/navModel';
|
import { getNavModel } from 'app/core/selectors/navModel';
|
||||||
import config from 'app/core/config';
|
import config from 'app/core/config';
|
||||||
@ -122,9 +122,7 @@ export class LdapPage extends PureComponent<Props, State> {
|
|||||||
name="username"
|
name="username"
|
||||||
defaultValue={queryParams.username}
|
defaultValue={queryParams.username}
|
||||||
/>
|
/>
|
||||||
<button type="submit" className="btn btn-primary">
|
<Button type="submit">Run</Button>
|
||||||
Run
|
|
||||||
</button>
|
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
{userError && userError.title && (
|
{userError && userError.title && (
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import React, { PureComponent } from 'react';
|
import React, { PureComponent } from 'react';
|
||||||
import { dateTimeFormat } from '@grafana/data';
|
import { dateTimeFormat } from '@grafana/data';
|
||||||
import { Spinner } from '@grafana/ui';
|
import { Button, Spinner } from '@grafana/ui';
|
||||||
import { SyncInfo } from 'app/types';
|
import { SyncInfo } from 'app/types';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@ -31,10 +31,10 @@ export class LdapSyncInfo extends PureComponent<Props, State> {
|
|||||||
<>
|
<>
|
||||||
<h3 className="page-heading">
|
<h3 className="page-heading">
|
||||||
LDAP Synchronisation
|
LDAP Synchronisation
|
||||||
<button className={`btn btn-secondary pull-right`} onClick={this.handleSyncClick} hidden={true}>
|
<Button className="pull-right" onClick={this.handleSyncClick} hidden>
|
||||||
<span className="btn-title">Bulk-sync now</span>
|
<span className="btn-title">Bulk-sync now</span>
|
||||||
{isSyncing && <Spinner inline={true} />}
|
{isSyncing && <Spinner inline={true} />}
|
||||||
</button>
|
</Button>
|
||||||
</h3>
|
</h3>
|
||||||
<div className="gf-form-group">
|
<div className="gf-form-group">
|
||||||
<div className="gf-form">
|
<div className="gf-form">
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import React, { PureComponent } from 'react';
|
import React, { PureComponent } from 'react';
|
||||||
import { LoadingPlaceholder, JSONFormatter, Icon } from '@grafana/ui';
|
import { LoadingPlaceholder, JSONFormatter, Icon, HorizontalGroup } from '@grafana/ui';
|
||||||
|
|
||||||
import appEvents from 'app/core/app_events';
|
import appEvents from 'app/core/app_events';
|
||||||
import { CopyToClipboard } from 'app/core/components/CopyToClipboard/CopyToClipboard';
|
import { CopyToClipboard } from 'app/core/components/CopyToClipboard/CopyToClipboard';
|
||||||
import { DashboardModel, PanelModel } from '../dashboard/state';
|
import { DashboardModel, PanelModel } from '../dashboard/state';
|
||||||
@ -106,16 +105,12 @@ export class TestRuleResult extends PureComponent<Props, State> {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="pull-right">
|
<div className="pull-right">
|
||||||
<button className="btn btn-transparent btn-p-x-0 m-r-1" onClick={this.onToggleExpand}>
|
<HorizontalGroup spacing="md">
|
||||||
{this.renderExpandCollapse()}
|
<div onClick={this.onToggleExpand}>{this.renderExpandCollapse()}</div>
|
||||||
</button>
|
<CopyToClipboard elType="div" text={this.getTextForClipboard} onSuccess={this.onClipboardSuccess}>
|
||||||
<CopyToClipboard
|
<Icon name="copy" /> Copy to Clipboard
|
||||||
className="btn btn-transparent btn-p-x-0"
|
</CopyToClipboard>
|
||||||
text={this.getTextForClipboard}
|
</HorizontalGroup>
|
||||||
onSuccess={this.onClipboardSuccess}
|
|
||||||
>
|
|
||||||
<Icon name="copy" /> Copy to Clipboard
|
|
||||||
</CopyToClipboard>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<JSONFormatter json={testRuleResponse} open={openNodes} onDidRender={this.setFormattedJson} />
|
<JSONFormatter json={testRuleResponse} open={openNodes} onDidRender={this.setFormattedJson} />
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import React, { FC } from 'react';
|
import React, { FC } from 'react';
|
||||||
|
import { Button } from '@grafana/ui';
|
||||||
import { FilterInput } from '../../core/components/FilterInput/FilterInput';
|
import { FilterInput } from '../../core/components/FilterInput/FilterInput';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@ -17,9 +17,9 @@ export const ApiKeysActionBar: FC<Props> = ({ searchQuery, disabled, onAddClick,
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="page-action-bar__spacer" />
|
<div className="page-action-bar__spacer" />
|
||||||
<button className="btn btn-primary pull-right" onClick={onAddClick} disabled={disabled}>
|
<Button className="pull-right" onClick={onAddClick} disabled={disabled}>
|
||||||
Add API key
|
Add API key
|
||||||
</button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import React, { ChangeEvent, FC, FormEvent, useEffect, useState } from 'react';
|
import React, { ChangeEvent, FC, FormEvent, useEffect, useState } from 'react';
|
||||||
import { EventsWithValidation, Icon, InlineFormLabel, LegacyForms, ValidationEvents } from '@grafana/ui';
|
import { EventsWithValidation, InlineFormLabel, LegacyForms, ValidationEvents, Button } from '@grafana/ui';
|
||||||
import { NewApiKey, OrgRole } from '../../types';
|
import { NewApiKey, OrgRole } from '../../types';
|
||||||
import { rangeUtil } from '@grafana/data';
|
import { rangeUtil } from '@grafana/data';
|
||||||
import { SlideDown } from '../../core/components/Animations/SlideDown';
|
import { SlideDown } from '../../core/components/Animations/SlideDown';
|
||||||
|
import { CloseButton } from 'app/core/components/CloseButton/CloseButton';
|
||||||
|
|
||||||
const { Input } = LegacyForms;
|
const { Input } = LegacyForms;
|
||||||
|
|
||||||
@ -65,9 +66,7 @@ export const ApiKeysForm: FC<Props> = ({ show, onClose, onKeyAdded }) => {
|
|||||||
return (
|
return (
|
||||||
<SlideDown in={show}>
|
<SlideDown in={show}>
|
||||||
<div className="gf-form-inline cta-form">
|
<div className="gf-form-inline cta-form">
|
||||||
<button className="cta-form__close btn btn-transparent" onClick={onClose}>
|
<CloseButton onClick={onClose} />
|
||||||
<Icon name="times" />
|
|
||||||
</button>
|
|
||||||
<form className="gf-form-group" onSubmit={onSubmit}>
|
<form className="gf-form-group" onSubmit={onSubmit}>
|
||||||
<h5>Add API Key</h5>
|
<h5>Add API Key</h5>
|
||||||
<div className="gf-form-inline">
|
<div className="gf-form-inline">
|
||||||
@ -101,7 +100,7 @@ export const ApiKeysForm: FC<Props> = ({ show, onClose, onKeyAdded }) => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="gf-form">
|
<div className="gf-form">
|
||||||
<button className="btn gf-form-btn btn-primary">Add</button>
|
<Button>Add</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import React, { FC } from 'react';
|
import React, { FC } from 'react';
|
||||||
import { PluginDashboard } from '../../types';
|
import { PluginDashboard } from '../../types';
|
||||||
import { Icon } from '@grafana/ui';
|
import { Button, Icon } from '@grafana/ui';
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
dashboards: PluginDashboard[];
|
dashboards: PluginDashboard[];
|
||||||
@ -31,18 +31,16 @@ const DashboardsTable: FC<Props> = ({ dashboards, onImport, onRemove }) => {
|
|||||||
</td>
|
</td>
|
||||||
<td style={{ textAlign: 'right' }}>
|
<td style={{ textAlign: 'right' }}>
|
||||||
{!dashboard.imported ? (
|
{!dashboard.imported ? (
|
||||||
<button className="btn btn-secondary btn-small" onClick={() => onImport(dashboard, false)}>
|
<Button variant="secondary" size="sm" onClick={() => onImport(dashboard, false)}>
|
||||||
Import
|
Import
|
||||||
</button>
|
</Button>
|
||||||
) : (
|
) : (
|
||||||
<button className="btn btn-secondary btn-small" onClick={() => onImport(dashboard, true)}>
|
<Button variant="secondary" size="sm" onClick={() => onImport(dashboard, true)}>
|
||||||
{buttonText(dashboard)}
|
{buttonText(dashboard)}
|
||||||
</button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
{dashboard.imported && (
|
{dashboard.imported && (
|
||||||
<button className="btn btn-danger btn-small" onClick={() => onRemove(dashboard)}>
|
<Button icon="trash-alt" variant="destructive" size="sm" onClick={() => onRemove(dashboard)} />
|
||||||
<Icon name="trash-alt" />
|
|
||||||
</button>
|
|
||||||
)}
|
)}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -35,12 +35,13 @@ exports[`Render should render table 1`] = `
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<button
|
<Button
|
||||||
className="btn btn-secondary btn-small"
|
|
||||||
onClick={[Function]}
|
onClick={[Function]}
|
||||||
|
size="sm"
|
||||||
|
variant="secondary"
|
||||||
>
|
>
|
||||||
Import
|
Import
|
||||||
</button>
|
</Button>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr
|
<tr
|
||||||
@ -67,20 +68,19 @@ exports[`Render should render table 1`] = `
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<button
|
<Button
|
||||||
className="btn btn-secondary btn-small"
|
|
||||||
onClick={[Function]}
|
onClick={[Function]}
|
||||||
|
size="sm"
|
||||||
|
variant="secondary"
|
||||||
>
|
>
|
||||||
Update
|
Update
|
||||||
</button>
|
</Button>
|
||||||
<button
|
<Button
|
||||||
className="btn btn-danger btn-small"
|
icon="trash-alt"
|
||||||
onClick={[Function]}
|
onClick={[Function]}
|
||||||
>
|
size="sm"
|
||||||
<Icon
|
variant="destructive"
|
||||||
name="trash-alt"
|
/>
|
||||||
/>
|
|
||||||
</button>
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
|
@ -21,7 +21,7 @@ import { getNavModel } from 'app/core/selectors/navModel';
|
|||||||
// Types
|
// Types
|
||||||
import { StoreState } from 'app/types/';
|
import { StoreState } from 'app/types/';
|
||||||
import { DataSourceSettings } from '@grafana/data';
|
import { DataSourceSettings } from '@grafana/data';
|
||||||
import { Alert, InfoBox } from '@grafana/ui';
|
import { Alert, Button, InfoBox, LinkButton } from '@grafana/ui';
|
||||||
import { getDataSourceLoadingNav } from '../state/navModel';
|
import { getDataSourceLoadingNav } from '../state/navModel';
|
||||||
import PluginStateinfo from 'app/features/plugins/PluginStateInfo';
|
import PluginStateinfo from 'app/features/plugins/PluginStateInfo';
|
||||||
import { dataSourceLoaded, setDataSourceName, setIsDefault } from '../state/reducers';
|
import { dataSourceLoaded, setDataSourceName, setIsDefault } from '../state/reducers';
|
||||||
@ -170,13 +170,13 @@ export class DataSourceSettingsPage extends PureComponent<Props> {
|
|||||||
<div>
|
<div>
|
||||||
<div className="gf-form-button-row">
|
<div className="gf-form-button-row">
|
||||||
{showDelete && (
|
{showDelete && (
|
||||||
<button type="submit" className="btn btn-danger" onClick={this.onDelete}>
|
<Button type="submit" variant="destructive" onClick={this.onDelete}>
|
||||||
Delete
|
Delete
|
||||||
</button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
<a className="btn btn-inverse" href="datasources">
|
<LinkButton variant="link" href="datasources">
|
||||||
Back
|
Back
|
||||||
</a>
|
</LinkButton>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Page.Contents>
|
</Page.Contents>
|
||||||
|
@ -2,7 +2,7 @@ import React, { PureComponent } from 'react';
|
|||||||
import { css, cx } from '@emotion/css';
|
import { css, cx } from '@emotion/css';
|
||||||
import tinycolor from 'tinycolor2';
|
import tinycolor from 'tinycolor2';
|
||||||
|
|
||||||
import { LogMessageAnsi, Themeable, withTheme, getLogRowStyles, Icon } from '@grafana/ui';
|
import { LogMessageAnsi, Themeable, withTheme, getLogRowStyles, Icon, Button } from '@grafana/ui';
|
||||||
import { GrafanaTheme, LogRowModel, TimeZone, dateTimeFormat } from '@grafana/data';
|
import { GrafanaTheme, LogRowModel, TimeZone, dateTimeFormat } from '@grafana/data';
|
||||||
|
|
||||||
import { ElapsedTime } from './ElapsedTime';
|
import { ElapsedTime } from './ElapsedTime';
|
||||||
@ -142,16 +142,16 @@ class LiveLogs extends PureComponent<Props, State> {
|
|||||||
/>
|
/>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<div className={cx([styles.logsRowsIndicator])}>
|
<div className={styles.logsRowsIndicator}>
|
||||||
<button onClick={isPaused ? onResume : onPause} className={cx('btn btn-secondary', styles.button)}>
|
<Button variant="secondary" onClick={isPaused ? onResume : onPause} className={styles.button}>
|
||||||
<Icon name={isPaused ? 'play' : 'pause'} />
|
<Icon name={isPaused ? 'play' : 'pause'} />
|
||||||
|
|
||||||
{isPaused ? 'Resume' : 'Pause'}
|
{isPaused ? 'Resume' : 'Pause'}
|
||||||
</button>
|
</Button>
|
||||||
<button onClick={this.props.stopLive} className={cx('btn btn-inverse', styles.button)}>
|
<Button variant="secondary" onClick={this.props.stopLive} className={styles.button}>
|
||||||
<Icon name="square-shape" size="lg" type="mono" />
|
<Icon name="square-shape" size="lg" type="mono" />
|
||||||
Exit live mode
|
Exit live mode
|
||||||
</button>
|
</Button>
|
||||||
{isPaused || (
|
{isPaused || (
|
||||||
<span>
|
<span>
|
||||||
Last line received: <ElapsedTime resetKey={this.props.logRows} humanize={true} /> ago
|
Last line received: <ElapsedTime resetKey={this.props.logRows} humanize={true} /> ago
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React, { PureComponent } from 'react';
|
import React, { PureComponent } from 'react';
|
||||||
import { connect, ConnectedProps } from 'react-redux';
|
import { connect, ConnectedProps } from 'react-redux';
|
||||||
import Page from 'app/core/components/Page/Page';
|
import Page from 'app/core/components/Page/Page';
|
||||||
import { Tooltip, Icon } from '@grafana/ui';
|
import { Tooltip, Icon, Button } from '@grafana/ui';
|
||||||
import { SlideDown } from 'app/core/components/Animations/SlideDown';
|
import { SlideDown } from 'app/core/components/Animations/SlideDown';
|
||||||
import { getNavModel } from 'app/core/selectors/navModel';
|
import { getNavModel } from 'app/core/selectors/navModel';
|
||||||
import { StoreState } from 'app/types';
|
import { StoreState } from 'app/types';
|
||||||
@ -105,9 +105,9 @@ export class FolderPermissions extends PureComponent<Props, State> {
|
|||||||
<Icon className="icon--has-hover page-sub-heading-icon" name="question-circle" />
|
<Icon className="icon--has-hover page-sub-heading-icon" name="question-circle" />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<div className="page-action-bar__spacer" />
|
<div className="page-action-bar__spacer" />
|
||||||
<button className="btn btn-primary pull-right" onClick={this.onOpenAddPermissions} disabled={isAdding}>
|
<Button className="pull-right" onClick={this.onOpenAddPermissions} disabled={isAdding}>
|
||||||
Add Permission
|
Add Permission
|
||||||
</button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<SlideDown in={isAdding}>
|
<SlideDown in={isAdding}>
|
||||||
<AddPermission onAddPermission={this.onAddPermission} onCancel={this.onCancelAddPermission} />
|
<AddPermission onAddPermission={this.onAddPermission} onCancel={this.onCancelAddPermission} />
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import React, { PureComponent } from 'react';
|
import React, { PureComponent } from 'react';
|
||||||
import { connect, ConnectedProps } from 'react-redux';
|
import { connect, ConnectedProps } from 'react-redux';
|
||||||
import { LegacyForms } from '@grafana/ui';
|
import { Button, LegacyForms } from '@grafana/ui';
|
||||||
const { Input } = LegacyForms;
|
const { Input } = LegacyForms;
|
||||||
import Page from 'app/core/components/Page/Page';
|
import Page from 'app/core/components/Page/Page';
|
||||||
import appEvents from 'app/core/app_events';
|
import appEvents from 'app/core/app_events';
|
||||||
@ -99,12 +99,12 @@ export class FolderSettingsPage extends PureComponent<Props, State> {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="gf-form-button-row">
|
<div className="gf-form-button-row">
|
||||||
<button type="submit" className="btn btn-primary" disabled={!folder.canSave || !folder.hasChanged}>
|
<Button type="submit" disabled={!folder.canSave || !folder.hasChanged}>
|
||||||
Save
|
Save
|
||||||
</button>
|
</Button>
|
||||||
<button className="btn btn-danger" onClick={this.onDelete} disabled={!folder.canSave}>
|
<Button variant="destructive" onClick={this.onDelete} disabled={!folder.canSave}>
|
||||||
Delete
|
Delete
|
||||||
</button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
@ -37,20 +37,19 @@ exports[`Render should enable save button 1`] = `
|
|||||||
<div
|
<div
|
||||||
className="gf-form-button-row"
|
className="gf-form-button-row"
|
||||||
>
|
>
|
||||||
<button
|
<Button
|
||||||
className="btn btn-primary"
|
|
||||||
disabled={false}
|
disabled={false}
|
||||||
type="submit"
|
type="submit"
|
||||||
>
|
>
|
||||||
Save
|
Save
|
||||||
</button>
|
</Button>
|
||||||
<button
|
<Button
|
||||||
className="btn btn-danger"
|
|
||||||
disabled={false}
|
disabled={false}
|
||||||
onClick={[Function]}
|
onClick={[Function]}
|
||||||
|
variant="destructive"
|
||||||
>
|
>
|
||||||
Delete
|
Delete
|
||||||
</button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
@ -95,20 +94,19 @@ exports[`Render should render component 1`] = `
|
|||||||
<div
|
<div
|
||||||
className="gf-form-button-row"
|
className="gf-form-button-row"
|
||||||
>
|
>
|
||||||
<button
|
<Button
|
||||||
className="btn btn-primary"
|
|
||||||
disabled={true}
|
disabled={true}
|
||||||
type="submit"
|
type="submit"
|
||||||
>
|
>
|
||||||
Save
|
Save
|
||||||
</button>
|
</Button>
|
||||||
<button
|
<Button
|
||||||
className="btn btn-danger"
|
|
||||||
disabled={false}
|
disabled={false}
|
||||||
onClick={[Function]}
|
onClick={[Function]}
|
||||||
|
variant="destructive"
|
||||||
>
|
>
|
||||||
Delete
|
Delete
|
||||||
</button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
@ -44,7 +44,9 @@ export class UserOrganizations extends PureComponent<Props> {
|
|||||||
<td>{org.role}</td>
|
<td>{org.role}</td>
|
||||||
<td className="text-right">
|
<td className="text-right">
|
||||||
{org.orgId === user.orgId ? (
|
{org.orgId === user.orgId ? (
|
||||||
<span className="btn btn-primary btn-small">Current</span>
|
<Button variant="secondary" size="sm" disabled>
|
||||||
|
Current
|
||||||
|
</Button>
|
||||||
) : (
|
) : (
|
||||||
<Button
|
<Button
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
|
@ -2,13 +2,14 @@ import React, { PureComponent } from 'react';
|
|||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
import { SlideDown } from 'app/core/components/Animations/SlideDown';
|
import { SlideDown } from 'app/core/components/Animations/SlideDown';
|
||||||
import { LegacyForms, Tooltip, Icon } from '@grafana/ui';
|
import { LegacyForms, Tooltip, Icon, Button } from '@grafana/ui';
|
||||||
const { Input } = LegacyForms;
|
const { Input } = LegacyForms;
|
||||||
|
|
||||||
import { TeamGroup } from '../../types';
|
import { TeamGroup } from '../../types';
|
||||||
import { addTeamGroup, loadTeamGroups, removeTeamGroup } from './state/actions';
|
import { addTeamGroup, loadTeamGroups, removeTeamGroup } from './state/actions';
|
||||||
import { getTeamGroups } from './state/selectors';
|
import { getTeamGroups } from './state/selectors';
|
||||||
import EmptyListCTA from 'app/core/components/EmptyListCTA/EmptyListCTA';
|
import EmptyListCTA from 'app/core/components/EmptyListCTA/EmptyListCTA';
|
||||||
|
import { CloseButton } from 'app/core/components/CloseButton/CloseButton';
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
groups: TeamGroup[];
|
groups: TeamGroup[];
|
||||||
@ -65,9 +66,9 @@ export class TeamGroupSync extends PureComponent<Props, State> {
|
|||||||
<tr key={group.groupId}>
|
<tr key={group.groupId}>
|
||||||
<td>{group.groupId}</td>
|
<td>{group.groupId}</td>
|
||||||
<td style={{ width: '1%' }}>
|
<td style={{ width: '1%' }}>
|
||||||
<a className="btn btn-danger btn-small" onClick={() => this.onRemoveGroup(group)}>
|
<Button size="sm" variant="destructive" onClick={() => this.onRemoveGroup(group)}>
|
||||||
<Icon name="times" style={{ marginBottom: 0 }} />
|
<Icon name="times" />
|
||||||
</a>
|
</Button>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
);
|
);
|
||||||
@ -86,17 +87,15 @@ export class TeamGroupSync extends PureComponent<Props, State> {
|
|||||||
</Tooltip>
|
</Tooltip>
|
||||||
<div className="page-action-bar__spacer" />
|
<div className="page-action-bar__spacer" />
|
||||||
{groups.length > 0 && (
|
{groups.length > 0 && (
|
||||||
<button className="btn btn-primary pull-right" onClick={this.onToggleAdding}>
|
<Button className="pull-right" onClick={this.onToggleAdding}>
|
||||||
<Icon name="plus" /> Add group
|
<Icon name="plus" /> Add group
|
||||||
</button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<SlideDown in={isAdding}>
|
<SlideDown in={isAdding}>
|
||||||
<div className="cta-form">
|
<div className="cta-form">
|
||||||
<button className="cta-form__close btn btn-transparent" onClick={this.onToggleAdding}>
|
<CloseButton onClick={this.onToggleAdding} />
|
||||||
<Icon name="times" />
|
|
||||||
</button>
|
|
||||||
<h5>Add External Group</h5>
|
<h5>Add External Group</h5>
|
||||||
<form className="gf-form-inline" onSubmit={this.onAddGroup}>
|
<form className="gf-form-inline" onSubmit={this.onAddGroup}>
|
||||||
<div className="gf-form">
|
<div className="gf-form">
|
||||||
@ -110,9 +109,9 @@ export class TeamGroupSync extends PureComponent<Props, State> {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="gf-form">
|
<div className="gf-form">
|
||||||
<button className="btn btn-primary gf-form-btn" type="submit" disabled={!this.isNewGroupValid()}>
|
<Button type="submit" disabled={!this.isNewGroupValid()}>
|
||||||
Add group
|
Add group
|
||||||
</button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import React, { PureComponent } from 'react';
|
import React, { PureComponent } from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { Icon } from '@grafana/ui';
|
|
||||||
import { SlideDown } from 'app/core/components/Animations/SlideDown';
|
import { SlideDown } from 'app/core/components/Animations/SlideDown';
|
||||||
import { UserPicker } from 'app/core/components/Select/UserPicker';
|
import { UserPicker } from 'app/core/components/Select/UserPicker';
|
||||||
import { TagBadge } from 'app/core/components/TagFilter/TagBadge';
|
import { TagBadge } from 'app/core/components/TagFilter/TagBadge';
|
||||||
@ -13,6 +12,8 @@ import { config } from 'app/core/config';
|
|||||||
import { contextSrv, User as SignedInUser } from 'app/core/services/context_srv';
|
import { contextSrv, User as SignedInUser } from 'app/core/services/context_srv';
|
||||||
import TeamMemberRow from './TeamMemberRow';
|
import TeamMemberRow from './TeamMemberRow';
|
||||||
import { setSearchMemberQuery } from './state/reducers';
|
import { setSearchMemberQuery } from './state/reducers';
|
||||||
|
import { CloseButton } from 'app/core/components/CloseButton/CloseButton';
|
||||||
|
import { Button } from '@grafana/ui';
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
members: TeamMember[];
|
members: TeamMember[];
|
||||||
@ -79,28 +80,21 @@ export class TeamMembers extends PureComponent<Props, State> {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="page-action-bar__spacer" />
|
<div className="page-action-bar__spacer" />
|
||||||
|
<Button className="pull-right" onClick={this.onToggleAdding} disabled={isAdding || !isTeamAdmin}>
|
||||||
<button
|
|
||||||
className="btn btn-primary pull-right"
|
|
||||||
onClick={this.onToggleAdding}
|
|
||||||
disabled={isAdding || !isTeamAdmin}
|
|
||||||
>
|
|
||||||
Add member
|
Add member
|
||||||
</button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<SlideDown in={isAdding}>
|
<SlideDown in={isAdding}>
|
||||||
<div className="cta-form">
|
<div className="cta-form">
|
||||||
<button className="cta-form__close btn btn-transparent" onClick={this.onToggleAdding}>
|
<CloseButton onClick={this.onToggleAdding} />
|
||||||
<Icon name="times" />
|
|
||||||
</button>
|
|
||||||
<h5>Add team member</h5>
|
<h5>Add team member</h5>
|
||||||
<div className="gf-form-inline">
|
<div className="gf-form-inline">
|
||||||
<UserPicker onSelected={this.onUserSelected} className="min-width-30" />
|
<UserPicker onSelected={this.onUserSelected} className="min-width-30" />
|
||||||
{this.state.newTeamMember && (
|
{this.state.newTeamMember && (
|
||||||
<button className="btn btn-primary gf-form-btn" type="submit" onClick={this.onAddUserToTeam}>
|
<Button type="submit" onClick={this.onAddUserToTeam}>
|
||||||
Add to team
|
Add to team
|
||||||
</button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -29,14 +29,9 @@ exports[`Render should render component 1`] = `
|
|||||||
<div
|
<div
|
||||||
className="cta-form"
|
className="cta-form"
|
||||||
>
|
>
|
||||||
<button
|
<CloseButton
|
||||||
className="cta-form__close btn btn-transparent"
|
|
||||||
onClick={[Function]}
|
onClick={[Function]}
|
||||||
>
|
/>
|
||||||
<Icon
|
|
||||||
name="times"
|
|
||||||
/>
|
|
||||||
</button>
|
|
||||||
<h5>
|
<h5>
|
||||||
Add External Group
|
Add External Group
|
||||||
</h5>
|
</h5>
|
||||||
@ -58,13 +53,12 @@ exports[`Render should render component 1`] = `
|
|||||||
<div
|
<div
|
||||||
className="gf-form"
|
className="gf-form"
|
||||||
>
|
>
|
||||||
<button
|
<Button
|
||||||
className="btn btn-primary gf-form-btn"
|
|
||||||
disabled={true}
|
disabled={true}
|
||||||
type="submit"
|
type="submit"
|
||||||
>
|
>
|
||||||
Add group
|
Add group
|
||||||
</button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
@ -104,15 +98,15 @@ exports[`Render should render groups table 1`] = `
|
|||||||
<div
|
<div
|
||||||
className="page-action-bar__spacer"
|
className="page-action-bar__spacer"
|
||||||
/>
|
/>
|
||||||
<button
|
<Button
|
||||||
className="btn btn-primary pull-right"
|
className="pull-right"
|
||||||
onClick={[Function]}
|
onClick={[Function]}
|
||||||
>
|
>
|
||||||
<Icon
|
<Icon
|
||||||
name="plus"
|
name="plus"
|
||||||
/>
|
/>
|
||||||
Add group
|
Add group
|
||||||
</button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<SlideDown
|
<SlideDown
|
||||||
in={false}
|
in={false}
|
||||||
@ -120,14 +114,9 @@ exports[`Render should render groups table 1`] = `
|
|||||||
<div
|
<div
|
||||||
className="cta-form"
|
className="cta-form"
|
||||||
>
|
>
|
||||||
<button
|
<CloseButton
|
||||||
className="cta-form__close btn btn-transparent"
|
|
||||||
onClick={[Function]}
|
onClick={[Function]}
|
||||||
>
|
/>
|
||||||
<Icon
|
|
||||||
name="times"
|
|
||||||
/>
|
|
||||||
</button>
|
|
||||||
<h5>
|
<h5>
|
||||||
Add External Group
|
Add External Group
|
||||||
</h5>
|
</h5>
|
||||||
@ -149,13 +138,12 @@ exports[`Render should render groups table 1`] = `
|
|||||||
<div
|
<div
|
||||||
className="gf-form"
|
className="gf-form"
|
||||||
>
|
>
|
||||||
<button
|
<Button
|
||||||
className="btn btn-primary gf-form-btn"
|
|
||||||
disabled={true}
|
disabled={true}
|
||||||
type="submit"
|
type="submit"
|
||||||
>
|
>
|
||||||
Add group
|
Add group
|
||||||
</button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
@ -194,19 +182,15 @@ exports[`Render should render groups table 1`] = `
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<a
|
<Button
|
||||||
className="btn btn-danger btn-small"
|
|
||||||
onClick={[Function]}
|
onClick={[Function]}
|
||||||
|
size="sm"
|
||||||
|
variant="destructive"
|
||||||
>
|
>
|
||||||
<Icon
|
<Icon
|
||||||
name="times"
|
name="times"
|
||||||
style={
|
|
||||||
Object {
|
|
||||||
"marginBottom": 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
</a>
|
</Button>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr
|
<tr
|
||||||
@ -222,19 +206,15 @@ exports[`Render should render groups table 1`] = `
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<a
|
<Button
|
||||||
className="btn btn-danger btn-small"
|
|
||||||
onClick={[Function]}
|
onClick={[Function]}
|
||||||
|
size="sm"
|
||||||
|
variant="destructive"
|
||||||
>
|
>
|
||||||
<Icon
|
<Icon
|
||||||
name="times"
|
name="times"
|
||||||
style={
|
|
||||||
Object {
|
|
||||||
"marginBottom": 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
</a>
|
</Button>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr
|
<tr
|
||||||
@ -250,19 +230,15 @@ exports[`Render should render groups table 1`] = `
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<a
|
<Button
|
||||||
className="btn btn-danger btn-small"
|
|
||||||
onClick={[Function]}
|
onClick={[Function]}
|
||||||
|
size="sm"
|
||||||
|
variant="destructive"
|
||||||
>
|
>
|
||||||
<Icon
|
<Icon
|
||||||
name="times"
|
name="times"
|
||||||
style={
|
|
||||||
Object {
|
|
||||||
"marginBottom": 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
</a>
|
</Button>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
|
@ -17,13 +17,13 @@ exports[`Render should render component 1`] = `
|
|||||||
<div
|
<div
|
||||||
className="page-action-bar__spacer"
|
className="page-action-bar__spacer"
|
||||||
/>
|
/>
|
||||||
<button
|
<Button
|
||||||
className="btn btn-primary pull-right"
|
className="pull-right"
|
||||||
disabled={false}
|
disabled={false}
|
||||||
onClick={[Function]}
|
onClick={[Function]}
|
||||||
>
|
>
|
||||||
Add member
|
Add member
|
||||||
</button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<SlideDown
|
<SlideDown
|
||||||
in={false}
|
in={false}
|
||||||
@ -31,14 +31,9 @@ exports[`Render should render component 1`] = `
|
|||||||
<div
|
<div
|
||||||
className="cta-form"
|
className="cta-form"
|
||||||
>
|
>
|
||||||
<button
|
<CloseButton
|
||||||
className="cta-form__close btn btn-transparent"
|
|
||||||
onClick={[Function]}
|
onClick={[Function]}
|
||||||
>
|
/>
|
||||||
<Icon
|
|
||||||
name="times"
|
|
||||||
/>
|
|
||||||
</button>
|
|
||||||
<h5>
|
<h5>
|
||||||
Add team member
|
Add team member
|
||||||
</h5>
|
</h5>
|
||||||
@ -109,13 +104,13 @@ exports[`Render should render team members 1`] = `
|
|||||||
<div
|
<div
|
||||||
className="page-action-bar__spacer"
|
className="page-action-bar__spacer"
|
||||||
/>
|
/>
|
||||||
<button
|
<Button
|
||||||
className="btn btn-primary pull-right"
|
className="pull-right"
|
||||||
disabled={false}
|
disabled={false}
|
||||||
onClick={[Function]}
|
onClick={[Function]}
|
||||||
>
|
>
|
||||||
Add member
|
Add member
|
||||||
</button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<SlideDown
|
<SlideDown
|
||||||
in={false}
|
in={false}
|
||||||
@ -123,14 +118,9 @@ exports[`Render should render team members 1`] = `
|
|||||||
<div
|
<div
|
||||||
className="cta-form"
|
className="cta-form"
|
||||||
>
|
>
|
||||||
<button
|
<CloseButton
|
||||||
className="cta-form__close btn btn-transparent"
|
|
||||||
onClick={[Function]}
|
onClick={[Function]}
|
||||||
>
|
/>
|
||||||
<Icon
|
|
||||||
name="times"
|
|
||||||
/>
|
|
||||||
</button>
|
|
||||||
<h5>
|
<h5>
|
||||||
Add team member
|
Add team member
|
||||||
</h5>
|
</h5>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import React, { MouseEvent, PureComponent } from 'react';
|
import React, { MouseEvent, PureComponent } from 'react';
|
||||||
import { Icon } from '@grafana/ui';
|
import { Icon, LinkButton } from '@grafana/ui';
|
||||||
import { selectors } from '@grafana/e2e-selectors';
|
import { selectors } from '@grafana/e2e-selectors';
|
||||||
|
|
||||||
import { toVariableIdentifier, toVariablePayload, VariableIdentifier } from '../state/types';
|
import { toVariableIdentifier, toVariablePayload, VariableIdentifier } from '../state/types';
|
||||||
@ -98,14 +98,13 @@ class VariableEditorContainerUnconnected extends PureComponent<Props> {
|
|||||||
{this.props.variables.length > 0 && variableToEdit === null && (
|
{this.props.variables.length > 0 && variableToEdit === null && (
|
||||||
<>
|
<>
|
||||||
<VariablesDependenciesButton variables={this.props.variables} />
|
<VariablesDependenciesButton variables={this.props.variables} />
|
||||||
<a
|
<LinkButton
|
||||||
type="button"
|
type="button"
|
||||||
className="btn btn-primary"
|
|
||||||
onClick={this.onNewVariable}
|
onClick={this.onNewVariable}
|
||||||
aria-label={selectors.pages.Dashboard.Settings.Variables.List.newButton}
|
aria-label={selectors.pages.Dashboard.Settings.Variables.List.newButton}
|
||||||
>
|
>
|
||||||
New
|
New
|
||||||
</a>
|
</LinkButton>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
// Libraries
|
// Libraries
|
||||||
import React, { PureComponent } from 'react';
|
import React, { PureComponent } from 'react';
|
||||||
|
import { LinkButton } from '@grafana/ui';
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
import { PluginConfigPageProps, DataSourcePluginMeta, DataSourceJsonData } from '@grafana/data';
|
import { PluginConfigPageProps, DataSourcePluginMeta, DataSourceJsonData } from '@grafana/data';
|
||||||
@ -17,14 +18,14 @@ export class TestInfoTab extends PureComponent<Props> {
|
|||||||
See github for more information about setting up a reproducible test environment.
|
See github for more information about setting up a reproducible test environment.
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
<a
|
<LinkButton
|
||||||
className="btn btn-inverse"
|
variant="secondary"
|
||||||
href="https://github.com/grafana/grafana/tree/master/devenv"
|
href="https://github.com/grafana/grafana/tree/master/devenv"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
>
|
>
|
||||||
GitHub
|
GitHub
|
||||||
</a>
|
</LinkButton>
|
||||||
<br />
|
<br />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
Loading…
Reference in New Issue
Block a user