mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Form Migrations: Button (#23019)
* Update legacy exports and fix Type errors * Remove Button and LinkButton from Forms namespace * Fix errors * Update snapshots * Move Legacy button * Migrate more Buttons * Remove legacy button dependency * Move button up * Remove legacy button * Update Snapshots * Fix ComponentSize issues * Switch primary button * Switch primary * Add classNames and fix some angular directive issues * Fix failing build and remove log Co-authored-by: Torkel Ödegaard <torkel@grafana.com>
This commit is contained in:
@@ -59,5 +59,22 @@ Used for removing or deleting entities.
|
||||
</div>
|
||||
</Preview>
|
||||
|
||||
## Link
|
||||
|
||||
Used for for hyperlinks.
|
||||
|
||||
<Preview>
|
||||
<div>
|
||||
<Button href="/" variant="link" size="sm" renderAs="button" style={{ margin: '5px' }}>
|
||||
Small
|
||||
</Button>
|
||||
<Button href="/" variant="link" size="md" renderAs="button" style={{ margin: '5px' }}>
|
||||
Medium
|
||||
</Button>
|
||||
<Button href="/" variant="link" size="lg" renderAs="button" style={{ margin: '5px' }}>
|
||||
Large
|
||||
</Button>
|
||||
</div>
|
||||
</Preview>
|
||||
|
||||
<Props of={Button} />
|
@@ -1,45 +1,35 @@
|
||||
import { storiesOf } from '@storybook/react';
|
||||
import { Button, LinkButton } from './Button';
|
||||
// @ts-ignore
|
||||
import withPropsCombinations from 'react-storybook-addon-props-combinations';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import { ThemeableCombinationsRowRenderer } from '../../utils/storybook/CombinationsRowRenderer';
|
||||
import { boolean } from '@storybook/addon-knobs';
|
||||
import React from 'react';
|
||||
import { select, text } from '@storybook/addon-knobs';
|
||||
import { Button, ButtonVariant } from './Button';
|
||||
import { withCenteredStory, withHorizontallyCenteredStory } from '../../utils/storybook/withCenteredStory';
|
||||
import { getIconKnob } from '../../utils/storybook/knobs';
|
||||
import mdx from './Button.mdx';
|
||||
import { ComponentSize } from '../../types/size';
|
||||
|
||||
const ButtonStories = storiesOf('General/Button', module);
|
||||
|
||||
const defaultProps = {
|
||||
onClick: [action('Button clicked')],
|
||||
children: ['Click click!'],
|
||||
export default {
|
||||
title: 'Forms/Button',
|
||||
component: Button,
|
||||
decorators: [withCenteredStory, withHorizontallyCenteredStory],
|
||||
parameters: {
|
||||
docs: {
|
||||
page: mdx,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const variants = {
|
||||
size: ['xs', 'sm', 'md', 'lg'],
|
||||
variant: ['primary', 'secondary', 'danger', 'inverse', 'transparent', 'link'],
|
||||
};
|
||||
const combinationOptions = {
|
||||
CombinationRenderer: ThemeableCombinationsRowRenderer,
|
||||
};
|
||||
const variants = ['primary', 'secondary', 'destructive', 'link'];
|
||||
|
||||
const renderButtonStory = (buttonComponent: typeof Button | typeof LinkButton) => {
|
||||
const isDisabled = boolean('Disable button', false);
|
||||
return withPropsCombinations(
|
||||
buttonComponent,
|
||||
{ ...variants, ...defaultProps, disabled: [isDisabled] },
|
||||
combinationOptions
|
||||
)();
|
||||
};
|
||||
const sizes = ['sm', 'md', 'lg'];
|
||||
|
||||
ButtonStories.add('as button element', () => renderButtonStory(Button));
|
||||
|
||||
ButtonStories.add('as link element', () => renderButtonStory(LinkButton));
|
||||
|
||||
ButtonStories.add('with icon', () => {
|
||||
export const simple = () => {
|
||||
const variant = select('Variant', variants, 'primary');
|
||||
const size = select('Size', sizes, 'md');
|
||||
const buttonText = text('text', 'Button');
|
||||
const icon = getIconKnob();
|
||||
return withPropsCombinations(
|
||||
Button,
|
||||
{ ...variants, ...defaultProps, icon: [icon && `fa fa-${icon}`] },
|
||||
combinationOptions
|
||||
)();
|
||||
});
|
||||
|
||||
return (
|
||||
<Button variant={variant as ButtonVariant} size={size as ComponentSize} icon={icon && `fa fa-${icon}`}>
|
||||
{buttonText}
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
|
@@ -1,26 +0,0 @@
|
||||
import React from 'react';
|
||||
import { Button, LinkButton } from './Button';
|
||||
import { mount } from 'enzyme';
|
||||
|
||||
describe('Button', () => {
|
||||
it('renders correct html', () => {
|
||||
const wrapper = mount(<Button icon={'fa fa-plus'}>Click me</Button>);
|
||||
expect(wrapper.html()).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe('LinkButton', () => {
|
||||
it('renders correct html', () => {
|
||||
const wrapper = mount(<LinkButton icon={'fa fa-plus'}>Click me</LinkButton>);
|
||||
expect(wrapper.html()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('allows a disable state on link button', () => {
|
||||
const wrapper = mount(
|
||||
<LinkButton disabled icon={'fa fa-plus'}>
|
||||
Click me
|
||||
</LinkButton>
|
||||
);
|
||||
expect(wrapper.find('a[disabled]').length).toBe(1);
|
||||
});
|
||||
});
|
@@ -1,72 +1,178 @@
|
||||
import React, { AnchorHTMLAttributes, ButtonHTMLAttributes, useContext } from 'react';
|
||||
import { ThemeContext } from '../../themes';
|
||||
import { getButtonStyles } from './styles';
|
||||
import { css, cx } from 'emotion';
|
||||
import tinycolor from 'tinycolor2';
|
||||
import { selectThemeVariant, stylesFactory, ThemeContext } from '../../themes';
|
||||
import { getFocusStyle, getPropertiesForButtonSize } from '../Forms/commonStyles';
|
||||
import { GrafanaTheme } from '@grafana/data';
|
||||
import { ButtonContent } from './ButtonContent';
|
||||
import { ComponentSize } from '../../types/size';
|
||||
import { ButtonStyles, ButtonVariant } from './types';
|
||||
import { cx } from 'emotion';
|
||||
|
||||
const buttonVariantStyles = (from: string, to: string, textColor: string) => css`
|
||||
background: linear-gradient(180deg, ${from} 0%, ${to} 100%);
|
||||
color: ${textColor};
|
||||
&:hover {
|
||||
background: ${from};
|
||||
color: ${textColor};
|
||||
}
|
||||
|
||||
&:focus {
|
||||
background: ${from};
|
||||
outline: none;
|
||||
}
|
||||
`;
|
||||
|
||||
const getPropertiesForVariant = (theme: GrafanaTheme, variant: ButtonVariant) => {
|
||||
switch (variant) {
|
||||
case 'secondary':
|
||||
const from = selectThemeVariant({ light: theme.colors.gray7, dark: theme.colors.gray15 }, theme.type) as string;
|
||||
const to = selectThemeVariant(
|
||||
{
|
||||
light: tinycolor(from)
|
||||
.darken(5)
|
||||
.toString(),
|
||||
dark: tinycolor(from)
|
||||
.lighten(4)
|
||||
.toString(),
|
||||
},
|
||||
theme.type
|
||||
) as string;
|
||||
|
||||
return {
|
||||
borderColor: selectThemeVariant({ light: theme.colors.gray85, dark: theme.colors.gray25 }, theme.type),
|
||||
background: buttonVariantStyles(
|
||||
from,
|
||||
to,
|
||||
selectThemeVariant({ light: theme.colors.gray25, dark: theme.colors.gray4 }, theme.type) as string
|
||||
),
|
||||
};
|
||||
|
||||
case 'destructive':
|
||||
return {
|
||||
borderColor: theme.colors.redShade,
|
||||
background: buttonVariantStyles(theme.colors.redBase, theme.colors.redShade, theme.colors.white),
|
||||
};
|
||||
|
||||
case 'link':
|
||||
return {
|
||||
borderColor: 'transparent',
|
||||
background: buttonVariantStyles('transparent', 'transparent', theme.colors.linkExternal),
|
||||
variantStyles: css`
|
||||
&:focus {
|
||||
outline: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
`,
|
||||
};
|
||||
case 'primary':
|
||||
default:
|
||||
return {
|
||||
borderColor: theme.colors.blueShade,
|
||||
background: buttonVariantStyles(theme.colors.blueBase, theme.colors.blueShade, theme.colors.white),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
export interface StyleProps {
|
||||
theme: GrafanaTheme;
|
||||
size: ComponentSize;
|
||||
variant: ButtonVariant;
|
||||
textAndIcon?: boolean;
|
||||
}
|
||||
|
||||
export const getButtonStyles = stylesFactory(({ theme, size, variant }: StyleProps) => {
|
||||
const { padding, fontSize, height } = getPropertiesForButtonSize(theme, size);
|
||||
const { background, borderColor, variantStyles } = getPropertiesForVariant(theme, variant);
|
||||
|
||||
return {
|
||||
button: cx(
|
||||
css`
|
||||
label: button;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
font-weight: ${theme.typography.weight.semibold};
|
||||
font-family: ${theme.typography.fontFamily.sansSerif};
|
||||
font-size: ${fontSize};
|
||||
padding: ${padding};
|
||||
height: ${height};
|
||||
vertical-align: middle;
|
||||
cursor: pointer;
|
||||
border: 1px solid ${borderColor};
|
||||
border-radius: ${theme.border.radius.sm};
|
||||
${background};
|
||||
|
||||
&[disabled],
|
||||
&:disabled {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.65;
|
||||
box-shadow: none;
|
||||
}
|
||||
`,
|
||||
getFocusStyle(theme),
|
||||
css`
|
||||
${variantStyles}
|
||||
`
|
||||
),
|
||||
buttonWithIcon: css`
|
||||
padding-left: ${theme.spacing.sm};
|
||||
`,
|
||||
// used for buttons with icon only
|
||||
iconButton: css`
|
||||
padding-right: 0;
|
||||
`,
|
||||
iconWrap: css`
|
||||
label: button-icon-wrap;
|
||||
& + * {
|
||||
margin-left: ${theme.spacing.sm};
|
||||
}
|
||||
`,
|
||||
};
|
||||
});
|
||||
|
||||
export type ButtonVariant = 'primary' | 'secondary' | 'destructive' | 'link';
|
||||
|
||||
type CommonProps = {
|
||||
size?: ComponentSize;
|
||||
variant?: ButtonVariant;
|
||||
/**
|
||||
* icon prop is a temporary solution. It accepts legacy icon class names for the icon to be rendered.
|
||||
* TODO: migrate to a component when we are going to migrate icons to @grafana/ui
|
||||
*/
|
||||
icon?: string;
|
||||
className?: string;
|
||||
styles?: ButtonStyles;
|
||||
};
|
||||
|
||||
export type ButtonProps = CommonProps & ButtonHTMLAttributes<HTMLButtonElement>;
|
||||
export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>((props, ref) => {
|
||||
const theme = useContext(ThemeContext);
|
||||
const { size, variant, icon, children, className, styles: stylesProp, ...buttonProps } = props;
|
||||
|
||||
// Default this to 'button', otherwise html defaults to 'submit' which then submits any form it is in.
|
||||
buttonProps.type = buttonProps.type || 'button';
|
||||
|
||||
const styles: ButtonStyles =
|
||||
stylesProp ||
|
||||
getButtonStyles({
|
||||
export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
({ variant, icon, children, className, ...otherProps }, ref) => {
|
||||
const theme = useContext(ThemeContext);
|
||||
const styles = getButtonStyles({
|
||||
theme,
|
||||
size: size || 'md',
|
||||
size: otherProps.size || 'md',
|
||||
variant: variant || 'primary',
|
||||
textAndIcon: !!(children && icon),
|
||||
});
|
||||
|
||||
return (
|
||||
<button className={cx(styles.button, className)} {...buttonProps} ref={ref}>
|
||||
<ButtonContent icon={icon}>{children}</ButtonContent>
|
||||
</button>
|
||||
);
|
||||
});
|
||||
return (
|
||||
<button className={cx(styles.button, className)} {...otherProps} ref={ref}>
|
||||
<ButtonContent icon={icon}>{children}</ButtonContent>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
Button.displayName = 'Button';
|
||||
|
||||
export type LinkButtonProps = CommonProps &
|
||||
AnchorHTMLAttributes<HTMLAnchorElement> & {
|
||||
// We allow disabled here even though it is not standard for a link. We use it as a selector to style it as
|
||||
// disabled.
|
||||
disabled?: boolean;
|
||||
};
|
||||
|
||||
export const LinkButton = React.forwardRef<HTMLAnchorElement, LinkButtonProps>((props, ref) => {
|
||||
const theme = useContext(ThemeContext);
|
||||
const { size, variant, icon, children, className, styles: stylesProp, ...anchorProps } = props;
|
||||
const styles: ButtonStyles =
|
||||
stylesProp ||
|
||||
getButtonStyles({
|
||||
type ButtonLinkProps = CommonProps & AnchorHTMLAttributes<HTMLAnchorElement>;
|
||||
export const LinkButton = React.forwardRef<HTMLAnchorElement, ButtonLinkProps>(
|
||||
({ variant, icon, children, className, ...otherProps }, ref) => {
|
||||
const theme = useContext(ThemeContext);
|
||||
const styles = getButtonStyles({
|
||||
theme,
|
||||
size: size || 'md',
|
||||
size: otherProps.size || 'md',
|
||||
variant: variant || 'primary',
|
||||
textAndIcon: !!(children && icon),
|
||||
});
|
||||
|
||||
return (
|
||||
<a className={cx(styles.button, className)} {...anchorProps} ref={ref}>
|
||||
<ButtonContent icon={icon}>{children}</ButtonContent>
|
||||
</a>
|
||||
);
|
||||
});
|
||||
return (
|
||||
<a className={cx(styles.button, className)} {...otherProps} ref={ref}>
|
||||
<ButtonContent icon={icon}>{children}</ButtonContent>
|
||||
</a>
|
||||
);
|
||||
}
|
||||
);
|
||||
LinkButton.displayName = 'LinkButton';
|
||||
|
@@ -1,5 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Button renders correct html 1`] = `"<button class=\\"css-12s5hlm-button\\" type=\\"button\\"><span class=\\"css-1beih13\\"><span class=\\"css-1rgbe4\\"><i class=\\"fa fa-plus\\"></i></span><span>Click me</span></span></button>"`;
|
||||
|
||||
exports[`LinkButton renders correct html 1`] = `"<a class=\\"css-12s5hlm-button\\"><span class=\\"css-1beih13\\"><span class=\\"css-1rgbe4\\"><i class=\\"fa fa-plus\\"></i></span><span>Click me</span></span></a>"`;
|
1
packages/grafana-ui/src/components/Button/index.ts
Normal file
1
packages/grafana-ui/src/components/Button/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './Button';
|
@@ -1,164 +0,0 @@
|
||||
import tinycolor from 'tinycolor2';
|
||||
import { css } from 'emotion';
|
||||
import { selectThemeVariant, stylesFactory } from '../../themes';
|
||||
import { ComponentSize } from '../../types/size';
|
||||
import { StyleDeps } from './types';
|
||||
import { GrafanaTheme } from '@grafana/data';
|
||||
|
||||
const buttonVariantStyles = (
|
||||
from: string,
|
||||
to: string,
|
||||
textColor: string,
|
||||
textShadowColor = 'rgba(0, 0, 0, 0.1)',
|
||||
invert = false
|
||||
) => css`
|
||||
background: linear-gradient(to bottom, ${from}, ${to});
|
||||
color: ${textColor};
|
||||
text-shadow: 0 ${invert ? '1px' : '-1px'} ${textShadowColor};
|
||||
&:hover {
|
||||
background: ${from};
|
||||
color: ${textColor};
|
||||
}
|
||||
|
||||
&:focus {
|
||||
background: ${from};
|
||||
outline: none;
|
||||
}
|
||||
`;
|
||||
|
||||
export const getButtonStyles = stylesFactory(({ theme, size, variant, textAndIcon }: StyleDeps) => {
|
||||
const borderRadius = theme.border.radius.sm;
|
||||
const { padding, fontSize, height, fontWeight } = calculateMeasures(theme, size, !!textAndIcon);
|
||||
|
||||
let background;
|
||||
|
||||
switch (variant) {
|
||||
case 'primary':
|
||||
background = buttonVariantStyles(theme.colors.greenBase, theme.colors.greenShade, theme.colors.white);
|
||||
break;
|
||||
|
||||
case 'secondary':
|
||||
background = buttonVariantStyles(theme.colors.blueBase, theme.colors.blueShade, theme.colors.white);
|
||||
break;
|
||||
|
||||
case 'danger':
|
||||
background = buttonVariantStyles(theme.colors.redBase, theme.colors.redShade, theme.colors.white);
|
||||
break;
|
||||
|
||||
case 'inverse':
|
||||
const from = selectThemeVariant({ light: theme.colors.gray5, dark: theme.colors.dark6 }, theme.type) as string;
|
||||
const to = selectThemeVariant(
|
||||
{
|
||||
light: tinycolor(from)
|
||||
.darken(5)
|
||||
.toString(),
|
||||
dark: tinycolor(from)
|
||||
.lighten(4)
|
||||
.toString(),
|
||||
},
|
||||
theme.type
|
||||
) as string;
|
||||
|
||||
background = buttonVariantStyles(from, to, theme.colors.link, 'rgba(0, 0, 0, 0.1)', true);
|
||||
break;
|
||||
|
||||
case 'transparent':
|
||||
background = css`
|
||||
${buttonVariantStyles('', '', theme.colors.link, 'rgba(0, 0, 0, 0.1)', true)};
|
||||
background: transparent;
|
||||
`;
|
||||
break;
|
||||
|
||||
case 'link':
|
||||
background = css`
|
||||
${buttonVariantStyles('', '', theme.colors.linkExternal, 'rgba(0, 0, 0, 0.1)', true)};
|
||||
background: transparent;
|
||||
`;
|
||||
break;
|
||||
}
|
||||
|
||||
return {
|
||||
button: css`
|
||||
label: button;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
font-weight: ${fontWeight};
|
||||
font-size: ${fontSize};
|
||||
font-family: ${theme.typography.fontFamily.sansSerif};
|
||||
line-height: ${theme.typography.lineHeight.md};
|
||||
padding: ${padding};
|
||||
vertical-align: middle;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
height: ${height};
|
||||
border-radius: ${borderRadius};
|
||||
${background};
|
||||
|
||||
&[disabled],
|
||||
&:disabled {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.65;
|
||||
box-shadow: none;
|
||||
}
|
||||
`,
|
||||
iconWrap: css`
|
||||
label: button-icon-wrap;
|
||||
& + * {
|
||||
margin-left: ${theme.spacing.sm};
|
||||
}
|
||||
`,
|
||||
};
|
||||
});
|
||||
|
||||
type ButtonMeasures = {
|
||||
padding: string;
|
||||
fontSize: string;
|
||||
height: string;
|
||||
fontWeight: number;
|
||||
};
|
||||
|
||||
const calculateMeasures = (theme: GrafanaTheme, size: ComponentSize, textAndIcon: boolean): ButtonMeasures => {
|
||||
switch (size) {
|
||||
case 'sm': {
|
||||
return {
|
||||
padding: `0 ${theme.spacing.sm}`,
|
||||
fontSize: theme.typography.size.sm,
|
||||
height: theme.height.sm,
|
||||
fontWeight: theme.typography.weight.semibold,
|
||||
};
|
||||
}
|
||||
|
||||
case 'md': {
|
||||
const leftPadding = textAndIcon ? theme.spacing.sm : theme.spacing.md;
|
||||
|
||||
return {
|
||||
padding: `0 ${theme.spacing.md} 0 ${leftPadding}`,
|
||||
fontSize: theme.typography.size.md,
|
||||
height: theme.height.md,
|
||||
fontWeight: theme.typography.weight.semibold,
|
||||
};
|
||||
}
|
||||
|
||||
case 'lg': {
|
||||
const leftPadding = textAndIcon ? theme.spacing.md : theme.spacing.lg;
|
||||
|
||||
return {
|
||||
padding: `0 ${theme.spacing.lg} 0 ${leftPadding}`,
|
||||
fontSize: theme.typography.size.lg,
|
||||
height: theme.height.lg,
|
||||
fontWeight: theme.typography.weight.regular,
|
||||
};
|
||||
}
|
||||
|
||||
default: {
|
||||
const leftPadding = textAndIcon ? theme.spacing.sm : theme.spacing.md;
|
||||
|
||||
return {
|
||||
padding: `0 ${theme.spacing.md} 0 ${leftPadding}`,
|
||||
fontSize: theme.typography.size.base,
|
||||
height: theme.height.md,
|
||||
fontWeight: theme.typography.weight.regular,
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
@@ -1,17 +0,0 @@
|
||||
import { GrafanaTheme } from '@grafana/data';
|
||||
import { ComponentSize } from '../../types/size';
|
||||
|
||||
export type ButtonVariant = 'primary' | 'secondary' | 'danger' | 'inverse' | 'transparent' | 'destructive' | 'link';
|
||||
|
||||
export interface StyleDeps {
|
||||
theme: GrafanaTheme;
|
||||
size: ComponentSize;
|
||||
variant: ButtonVariant;
|
||||
textAndIcon?: boolean;
|
||||
}
|
||||
|
||||
export interface ButtonStyles {
|
||||
button: string;
|
||||
iconWrap: string;
|
||||
icon?: string;
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import Clipboard from 'clipboard';
|
||||
import { Button, ButtonProps } from '../Button/Button';
|
||||
import { Button, ButtonProps } from '../Button';
|
||||
|
||||
interface Props extends ButtonProps {
|
||||
getText(): string;
|
||||
|
@@ -1,10 +1,11 @@
|
||||
export { ClipboardButton } from '../ClipboardButton/ClipboardButton';
|
||||
import React from 'react';
|
||||
import { storiesOf } from '@storybook/react';
|
||||
import { text, boolean, select } from '@storybook/addon-knobs';
|
||||
import { ConfirmButton } from './ConfirmButton';
|
||||
import { withCenteredStory } from '../../utils/storybook/withCenteredStory';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import { Button } from '../Button/Button';
|
||||
import { Button } from '../Button';
|
||||
|
||||
const getKnobs = () => {
|
||||
return {
|
||||
@@ -16,9 +17,8 @@ const getKnobs = () => {
|
||||
{
|
||||
primary: 'primary',
|
||||
secondary: 'secondary',
|
||||
danger: 'danger',
|
||||
inverse: 'inverse',
|
||||
transparent: 'transparent',
|
||||
destructive: 'destructive',
|
||||
link: 'link',
|
||||
},
|
||||
'primary'
|
||||
),
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import { ConfirmButton } from './ConfirmButton';
|
||||
import { mount, ShallowWrapper } from 'enzyme';
|
||||
import { Button } from '../Button/Button';
|
||||
import { Button } from '../Button';
|
||||
|
||||
describe('ConfirmButton', () => {
|
||||
let wrapper: any;
|
||||
|
@@ -4,9 +4,7 @@ import { stylesFactory, withTheme } from '../../themes';
|
||||
import { GrafanaTheme } from '@grafana/data';
|
||||
import { Themeable } from '../../types';
|
||||
import { ComponentSize } from '../../types/size';
|
||||
import { Button } from '../Button/Button';
|
||||
import Forms from '../Forms';
|
||||
import { ButtonVariant } from '../Button/types';
|
||||
import { Button, ButtonVariant } from '../Button';
|
||||
|
||||
const getStyles = stylesFactory((theme: GrafanaTheme) => {
|
||||
return {
|
||||
@@ -135,9 +133,9 @@ class UnThemedConfirmButton extends PureComponent<Props, State> {
|
||||
<span className={styles.buttonContainer}>
|
||||
{typeof children === 'string' ? (
|
||||
<span className={buttonClass}>
|
||||
<Forms.Button size={size} variant="link" onClick={onClick}>
|
||||
<Button size={size} variant="link" onClick={onClick}>
|
||||
{children}
|
||||
</Forms.Button>
|
||||
</Button>
|
||||
</span>
|
||||
) : (
|
||||
<span className={buttonClass} onClick={onClick}>
|
||||
@@ -146,7 +144,7 @@ class UnThemedConfirmButton extends PureComponent<Props, State> {
|
||||
)}
|
||||
<span className={styles.confirmButtonContainer}>
|
||||
<span className={confirmButtonClass}>
|
||||
<Button size={size} variant="transparent" onClick={this.onClickCancel}>
|
||||
<Button size={size} variant="secondary" onClick={this.onClickCancel}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button size={size} variant={confirmButtonVariant} onClick={onConfirm}>
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import React, { FC } from 'react';
|
||||
import { ConfirmButton } from './ConfirmButton';
|
||||
import { Button } from '../Button/Button';
|
||||
import { ComponentSize } from '../../types/size';
|
||||
import { Button } from '../Button';
|
||||
|
||||
interface Props {
|
||||
size?: ComponentSize;
|
||||
@@ -13,12 +13,12 @@ export const DeleteButton: FC<Props> = ({ size, disabled, onConfirm }) => {
|
||||
return (
|
||||
<ConfirmButton
|
||||
confirmText="Delete"
|
||||
confirmVariant="danger"
|
||||
confirmVariant="destructive"
|
||||
size={size || 'md'}
|
||||
disabled={disabled}
|
||||
onConfirm={onConfirm}
|
||||
>
|
||||
<Button variant="danger" icon="fa fa-remove" size={size || 'sm'} />
|
||||
<Button variant="destructive" icon="fa fa-remove" size={size || 'sm'} />
|
||||
</ConfirmButton>
|
||||
);
|
||||
};
|
||||
|
@@ -2,7 +2,7 @@ import React, { FC, useContext } from 'react';
|
||||
import { css } from 'emotion';
|
||||
import { Modal } from '../Modal/Modal';
|
||||
import { IconType } from '../Icon/types';
|
||||
import { Button } from '../Button/Button';
|
||||
import { Button } from '../Button';
|
||||
import { stylesFactory, ThemeContext } from '../../themes';
|
||||
import { GrafanaTheme } from '@grafana/data';
|
||||
import { HorizontalGroup } from '..';
|
||||
@@ -53,10 +53,10 @@ export const ConfirmModal: FC<Props> = ({
|
||||
<div className={styles.modalContent}>
|
||||
<div className={styles.modalText}>{body}</div>
|
||||
<HorizontalGroup justify="center">
|
||||
<Button variant="danger" onClick={onConfirm}>
|
||||
<Button variant="destructive" onClick={onConfirm}>
|
||||
{confirmText}
|
||||
</Button>
|
||||
<Button variant="inverse" onClick={onDismiss}>
|
||||
<Button variant="secondary" onClick={onDismiss}>
|
||||
{dismissText}
|
||||
</Button>
|
||||
</HorizontalGroup>
|
||||
|
@@ -73,7 +73,7 @@ export const DataLinksEditor: FC<DataLinksEditorProps> = React.memo(
|
||||
)}
|
||||
|
||||
{(!value || (value && value.length < (maxLinks || Infinity))) && (
|
||||
<Button variant="inverse" icon="fa fa-plus" onClick={() => onAdd()}>
|
||||
<Button variant="secondary" icon="fa fa-plus" onClick={() => onAdd()}>
|
||||
Add link
|
||||
</Button>
|
||||
)}
|
||||
|
@@ -2,7 +2,7 @@ import { DataFrame, DataLink, VariableSuggestion } from '@grafana/data';
|
||||
import React, { FC, useState } from 'react';
|
||||
import { DataLinkEditor } from '../DataLinkEditor';
|
||||
import { HorizontalGroup } from '../../Layout/Layout';
|
||||
import Forms from '../../Forms';
|
||||
import { Button } from '../../Button';
|
||||
|
||||
interface DataLinkEditorModalContentProps {
|
||||
link: DataLink;
|
||||
@@ -34,17 +34,17 @@ export const DataLinkEditorModalContent: FC<DataLinkEditorModalContentProps> = (
|
||||
onRemove={() => {}}
|
||||
/>
|
||||
<HorizontalGroup>
|
||||
<Forms.Button
|
||||
<Button
|
||||
onClick={() => {
|
||||
onChange(index, dirtyLink);
|
||||
onClose();
|
||||
}}
|
||||
>
|
||||
Save
|
||||
</Forms.Button>
|
||||
<Forms.Button variant="secondary" onClick={() => onClose()}>
|
||||
</Button>
|
||||
<Button variant="secondary" onClick={() => onClose()}>
|
||||
Cancel
|
||||
</Forms.Button>
|
||||
</Button>
|
||||
</HorizontalGroup>
|
||||
</>
|
||||
);
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import { DataFrame, DataLink, GrafanaTheme, VariableSuggestion } from '@grafana/data';
|
||||
import React, { useState } from 'react';
|
||||
import { css } from 'emotion';
|
||||
import Forms from '../../Forms';
|
||||
import { Button } from '../../Button/Button';
|
||||
import cloneDeep from 'lodash/cloneDeep';
|
||||
import { Modal } from '../../Modal/Modal';
|
||||
import { FullWidthButtonContainer } from '../../Button/FullWidthButtonContainer';
|
||||
@@ -100,9 +100,9 @@ export const DataLinksInlineEditor: React.FC<DataLinksInlineEditorProps> = ({ li
|
||||
)}
|
||||
|
||||
<FullWidthButtonContainer>
|
||||
<Forms.Button size="sm" icon="fa fa-plus" onClick={onDataLinkAdd}>
|
||||
<Button size="sm" icon="fa fa-plus" onClick={onDataLinkAdd}>
|
||||
Add link
|
||||
</Forms.Button>
|
||||
</Button>
|
||||
</FullWidthButtonContainer>
|
||||
</>
|
||||
);
|
||||
|
@@ -57,7 +57,7 @@ describe('Render', () => {
|
||||
},
|
||||
},
|
||||
});
|
||||
const removeButton = wrapper.find('Button').find({ variant: 'transparent' });
|
||||
const removeButton = wrapper.find('Button').find({ variant: 'destructive' });
|
||||
removeButton.simulate('click', { preventDefault: () => {} });
|
||||
expect(wrapper.find('FormField').exists()).toBeFalsy();
|
||||
expect(wrapper.find('SecretFormField').exists()).toBeFalsy();
|
||||
|
@@ -2,7 +2,7 @@ import React, { PureComponent } from 'react';
|
||||
import { css } from 'emotion';
|
||||
import uniqueId from 'lodash/uniqueId';
|
||||
import { DataSourceSettings } from '@grafana/data';
|
||||
import { Button } from '../Button/Button';
|
||||
import { Button } from '../Button';
|
||||
import { FormField } from '../FormField/FormField';
|
||||
import { SecretFormField } from '../SecretFormFied/SecretFormField';
|
||||
import { stylesFactory } from '../../themes';
|
||||
@@ -76,7 +76,7 @@ const CustomHeaderRow: React.FC<CustomHeaderRowProps> = ({ header, onBlur, onCha
|
||||
onChange={e => onChange({ ...header, value: e.target.value })}
|
||||
onBlur={onBlur}
|
||||
/>
|
||||
<Button variant="transparent" size="xs" onClick={_e => onRemove(header.id)}>
|
||||
<Button variant="destructive" size="xs" onClick={_e => onRemove(header.id)}>
|
||||
<i className="fa fa-trash" />
|
||||
</Button>
|
||||
</div>
|
||||
@@ -202,7 +202,7 @@ export class CustomHeadersSettings extends PureComponent<Props, State> {
|
||||
</div>
|
||||
<div className="gf-form">
|
||||
<Button
|
||||
variant="inverse"
|
||||
variant="secondary"
|
||||
size="xs"
|
||||
onClick={e => {
|
||||
this.onHeaderAdd();
|
||||
|
@@ -1,35 +0,0 @@
|
||||
import React from 'react';
|
||||
import { select, text } from '@storybook/addon-knobs';
|
||||
import { Button, ButtonVariant } from './Button';
|
||||
import { withCenteredStory, withHorizontallyCenteredStory } from '../../utils/storybook/withCenteredStory';
|
||||
import { getIconKnob } from '../../utils/storybook/knobs';
|
||||
import { ComponentSize } from '../../types/size';
|
||||
import mdx from './Button.mdx';
|
||||
|
||||
export default {
|
||||
title: 'Forms/Button',
|
||||
component: Button,
|
||||
decorators: [withCenteredStory, withHorizontallyCenteredStory],
|
||||
parameters: {
|
||||
docs: {
|
||||
page: mdx,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const variants = ['primary', 'secondary', 'destructive', 'link'];
|
||||
|
||||
const sizes = ['sm', 'md', 'lg'];
|
||||
|
||||
export const simple = () => {
|
||||
const variant = select('Variant', variants, 'primary');
|
||||
const size = select('Size', sizes, 'md');
|
||||
const buttonText = text('text', 'Button');
|
||||
const icon = getIconKnob();
|
||||
|
||||
return (
|
||||
<Button variant={variant as ButtonVariant} size={size as ComponentSize} icon={icon && `fa fa-${icon}`}>
|
||||
{buttonText}
|
||||
</Button>
|
||||
);
|
||||
};
|
@@ -1,161 +0,0 @@
|
||||
import React, { AnchorHTMLAttributes, ButtonHTMLAttributes, useContext } from 'react';
|
||||
import { css, cx } from 'emotion';
|
||||
import tinycolor from 'tinycolor2';
|
||||
import { selectThemeVariant, stylesFactory, ThemeContext } from '../../themes';
|
||||
import { Button as DefaultButton, LinkButton as DefaultLinkButton } from '../Button/Button';
|
||||
import { getFocusStyle, getPropertiesForButtonSize } from './commonStyles';
|
||||
import { ComponentSize } from '../../types/size';
|
||||
import { StyleDeps } from '../Button/types';
|
||||
import { GrafanaTheme } from '@grafana/data';
|
||||
|
||||
const buttonVariantStyles = (from: string, to: string, textColor: string) => css`
|
||||
background: linear-gradient(180deg, ${from} 0%, ${to} 100%);
|
||||
color: ${textColor};
|
||||
&:hover {
|
||||
background: ${from};
|
||||
color: ${textColor};
|
||||
}
|
||||
|
||||
&:focus {
|
||||
background: ${from};
|
||||
outline: none;
|
||||
}
|
||||
`;
|
||||
|
||||
const getPropertiesForVariant = (theme: GrafanaTheme, variant: ButtonVariant) => {
|
||||
switch (variant) {
|
||||
case 'secondary':
|
||||
const from = selectThemeVariant({ light: theme.colors.gray7, dark: theme.colors.gray15 }, theme.type) as string;
|
||||
const to = selectThemeVariant(
|
||||
{
|
||||
light: tinycolor(from)
|
||||
.darken(5)
|
||||
.toString(),
|
||||
dark: tinycolor(from)
|
||||
.lighten(4)
|
||||
.toString(),
|
||||
},
|
||||
theme.type
|
||||
) as string;
|
||||
|
||||
return {
|
||||
borderColor: selectThemeVariant({ light: theme.colors.gray85, dark: theme.colors.gray25 }, theme.type),
|
||||
background: buttonVariantStyles(
|
||||
from,
|
||||
to,
|
||||
selectThemeVariant({ light: theme.colors.gray25, dark: theme.colors.gray4 }, theme.type) as string
|
||||
),
|
||||
};
|
||||
|
||||
case 'destructive':
|
||||
return {
|
||||
borderColor: theme.colors.redShade,
|
||||
background: buttonVariantStyles(theme.colors.redBase, theme.colors.redShade, theme.colors.white),
|
||||
};
|
||||
|
||||
case 'link':
|
||||
return {
|
||||
borderColor: 'transparent',
|
||||
background: buttonVariantStyles('transparent', 'transparent', theme.colors.linkExternal),
|
||||
variantStyles: css`
|
||||
&:focus {
|
||||
outline: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
`,
|
||||
};
|
||||
case 'primary':
|
||||
default:
|
||||
return {
|
||||
borderColor: theme.colors.blueShade,
|
||||
background: buttonVariantStyles(theme.colors.blueBase, theme.colors.blueShade, theme.colors.white),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
// Need to do this because of mismatch between variants in standard buttons and here
|
||||
type StyleProps = Omit<StyleDeps, 'variant'> & { variant: ButtonVariant };
|
||||
|
||||
export const getButtonStyles = stylesFactory(({ theme, size, variant }: StyleProps) => {
|
||||
const { padding, fontSize, height } = getPropertiesForButtonSize(theme, size);
|
||||
const { background, borderColor, variantStyles } = getPropertiesForVariant(theme, variant);
|
||||
|
||||
return {
|
||||
button: cx(
|
||||
css`
|
||||
label: button;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
font-weight: ${theme.typography.weight.semibold};
|
||||
font-family: ${theme.typography.fontFamily.sansSerif};
|
||||
line-height: ${theme.typography.lineHeight.md};
|
||||
font-size: ${fontSize};
|
||||
padding: ${padding};
|
||||
height: ${height};
|
||||
vertical-align: middle;
|
||||
cursor: pointer;
|
||||
border: 1px solid ${borderColor};
|
||||
border-radius: ${theme.border.radius.sm};
|
||||
${background};
|
||||
|
||||
&[disabled],
|
||||
&:disabled {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.65;
|
||||
box-shadow: none;
|
||||
}
|
||||
`,
|
||||
getFocusStyle(theme),
|
||||
css`
|
||||
${variantStyles}
|
||||
`
|
||||
),
|
||||
buttonWithIcon: css`
|
||||
padding-left: ${theme.spacing.sm};
|
||||
`,
|
||||
// used for buttons with icon only
|
||||
iconButton: css`
|
||||
padding-right: 0;
|
||||
`,
|
||||
iconWrap: css`
|
||||
label: button-icon-wrap;
|
||||
& + * {
|
||||
margin-left: ${theme.spacing.sm};
|
||||
}
|
||||
`,
|
||||
};
|
||||
});
|
||||
|
||||
// These are different from the standard Button where there are more variants.
|
||||
export type ButtonVariant = 'primary' | 'secondary' | 'destructive' | 'link';
|
||||
|
||||
// These also needs to be different because the ButtonVariant is different
|
||||
type CommonProps = {
|
||||
size?: ComponentSize;
|
||||
variant?: ButtonVariant;
|
||||
icon?: string;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
export type ButtonProps = CommonProps & ButtonHTMLAttributes<HTMLButtonElement>;
|
||||
|
||||
export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(({ variant, ...otherProps }, ref) => {
|
||||
const theme = useContext(ThemeContext);
|
||||
const styles = getButtonStyles({
|
||||
theme,
|
||||
size: otherProps.size || 'md',
|
||||
variant: variant || 'primary',
|
||||
});
|
||||
return <DefaultButton {...otherProps} variant={variant} styles={styles} ref={ref} />;
|
||||
});
|
||||
|
||||
type ButtonLinkProps = CommonProps & AnchorHTMLAttributes<HTMLAnchorElement>;
|
||||
export const LinkButton = React.forwardRef<HTMLAnchorElement, ButtonLinkProps>(({ variant, ...otherProps }, ref) => {
|
||||
const theme = useContext(ThemeContext);
|
||||
const styles = getButtonStyles({
|
||||
theme,
|
||||
size: otherProps.size || 'md',
|
||||
variant: variant || 'primary',
|
||||
});
|
||||
return <DefaultLinkButton {...otherProps} variant={variant} styles={styles} ref={ref} />;
|
||||
});
|
@@ -5,7 +5,7 @@ import { withCenteredStory } from '../../utils/storybook/withCenteredStory';
|
||||
import { withStoryContainer } from '../../utils/storybook/withStoryContainer';
|
||||
import { Field } from './Field';
|
||||
import { Input } from './Input/Input';
|
||||
import { Button } from './Button';
|
||||
import { Button } from '../Button';
|
||||
import { Form } from './Form';
|
||||
import { Switch } from './Switch';
|
||||
import { Checkbox } from './Checkbox';
|
||||
|
@@ -2,7 +2,7 @@ import React from 'react';
|
||||
import { boolean, text, select, number } from '@storybook/addon-knobs';
|
||||
import { withCenteredStory } from '../../../utils/storybook/withCenteredStory';
|
||||
import { Input } from './Input';
|
||||
import { Button } from '../Button';
|
||||
import { Button } from '../../Button';
|
||||
import mdx from './Input.mdx';
|
||||
import { getAvailableIcons, IconType } from '../../Icon/types';
|
||||
import { KeyValue } from '@grafana/data';
|
||||
|
@@ -2,7 +2,7 @@ import React from 'react';
|
||||
import { css } from 'emotion';
|
||||
import { GrafanaTheme } from '@grafana/data';
|
||||
|
||||
import { Button, ButtonVariant, ButtonProps } from '../Button';
|
||||
import { Button, ButtonVariant, ButtonProps } from '../../Button';
|
||||
import { ComponentSize } from '../../../types/size';
|
||||
import { SelectCommonProps, CustomControlProps } from './types';
|
||||
import { SelectBase } from './SelectBase';
|
||||
|
@@ -5,7 +5,7 @@ import { SelectableValue } from '@grafana/data';
|
||||
import { getAvailableIcons, IconType } from '../../Icon/types';
|
||||
import { select, boolean } from '@storybook/addon-knobs';
|
||||
import { Icon } from '../../Icon/Icon';
|
||||
import { Button } from '../Button';
|
||||
import { Button } from '../../Button';
|
||||
import { ButtonSelect } from './ButtonSelect';
|
||||
import { getIconKnob } from '../../../utils/storybook/knobs';
|
||||
import kebabCase from 'lodash/kebabCase';
|
||||
|
@@ -3,7 +3,7 @@ import { GrafanaTheme } from '@grafana/data';
|
||||
import { getLabelStyles } from './Label';
|
||||
import { getLegendStyles } from './Legend';
|
||||
import { getFieldValidationMessageStyles } from './FieldValidationMessage';
|
||||
import { getButtonStyles, ButtonVariant } from './Button';
|
||||
import { getButtonStyles, ButtonVariant } from '../Button';
|
||||
import { ComponentSize } from '../../types/size';
|
||||
import { getInputStyles } from './Input/Input';
|
||||
import { getSwitchStyles } from './Switch';
|
||||
|
@@ -7,21 +7,22 @@ import { RadioButtonGroup } from './RadioButtonGroup/RadioButtonGroup';
|
||||
import { AsyncSelect, Select } from './Select/Select';
|
||||
import { Form } from './Form';
|
||||
import { Field } from './Field';
|
||||
import { Button, LinkButton } from './Button';
|
||||
import { Switch } from './Switch';
|
||||
import { TextArea } from './TextArea/TextArea';
|
||||
import { Checkbox } from './Checkbox';
|
||||
//Will be removed after Enterprise changes have been merged
|
||||
import { Button, LinkButton } from '../Button';
|
||||
|
||||
const Forms = {
|
||||
RadioButtonGroup,
|
||||
Button,
|
||||
LinkButton,
|
||||
Switch,
|
||||
getFormStyles,
|
||||
Label,
|
||||
Input,
|
||||
Form,
|
||||
Field,
|
||||
Button,
|
||||
LinkButton,
|
||||
Select,
|
||||
ButtonSelect,
|
||||
InputControl,
|
||||
@@ -30,5 +31,4 @@ const Forms = {
|
||||
Checkbox,
|
||||
};
|
||||
|
||||
export { ButtonVariant } from './Button';
|
||||
export default Forms;
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import { withCenteredStory, withHorizontallyCenteredStory } from '../../utils/storybook/withCenteredStory';
|
||||
import { VerticalGroup, HorizontalGroup, Layout } from './Layout';
|
||||
import { Button } from '../Forms/Button';
|
||||
import { Button } from '../Button';
|
||||
import { withStoryContainer } from '../../utils/storybook/withStoryContainer';
|
||||
import { select } from '@storybook/addon-knobs';
|
||||
|
||||
@@ -11,7 +11,7 @@ export default {
|
||||
decorators: [withStoryContainer, withCenteredStory, withHorizontallyCenteredStory],
|
||||
};
|
||||
|
||||
const justifyVariants = ['flex-start', 'flex-end', 'space-between'];
|
||||
const justifyVariants = ['flex-start', 'flex-end', 'space-betw een'];
|
||||
|
||||
const spacingVariants = ['xs', 'sm', 'md', 'lg'];
|
||||
|
||||
|
@@ -127,7 +127,7 @@ class UnThemedLogDetailsRow extends PureComponent<Props, State> {
|
||||
<>
|
||||
|
||||
<LinkButton
|
||||
variant={'transparent'}
|
||||
variant="link"
|
||||
size={'sm'}
|
||||
icon={cx('fa', link.onClick ? 'fa-list' : 'fa-external-link')}
|
||||
href={link.href}
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import { css } from 'emotion';
|
||||
import { stylesFactory } from '../../themes';
|
||||
import { Button, ButtonVariant } from '../Forms/Button';
|
||||
import { Button, ButtonVariant } from '../Button';
|
||||
|
||||
interface Props {
|
||||
currentPage: number;
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import React, { ChangeEvent, KeyboardEvent, PureComponent } from 'react';
|
||||
import { css, cx } from 'emotion';
|
||||
import { stylesFactory } from '../../themes/stylesFactory';
|
||||
import { Button } from '../Button/Button';
|
||||
import { Button } from '../Button';
|
||||
import { Input } from '../Input/Input';
|
||||
import { TagItem } from './TagItem';
|
||||
|
||||
@@ -106,7 +106,7 @@ export class TagsInput extends PureComponent<Props, State> {
|
||||
)}
|
||||
>
|
||||
<Input placeholder="Add Name" onChange={this.onNameChange} value={newTag} onKeyUp={this.onKeyboardAdd} />
|
||||
<Button className={getStyles().addButtonStyle} onClick={this.onAdd} variant="secondary" size="md">
|
||||
<Button className={getStyles().addButtonStyle} onClick={this.onAdd} variant="primary" size="md">
|
||||
Add
|
||||
</Button>
|
||||
</div>
|
||||
|
@@ -16,7 +16,7 @@ import { stylesFactory } from '../../themes';
|
||||
import { Icon } from '../Icon/Icon';
|
||||
import { RadioButtonGroup } from '../Forms/RadioButtonGroup/RadioButtonGroup';
|
||||
import { Field } from '../Forms/Field';
|
||||
import { Button } from '../Forms/Button';
|
||||
import { Button } from '../Button';
|
||||
import { FullWidthButtonContainer } from '../Button/FullWidthButtonContainer';
|
||||
|
||||
const modes: Array<SelectableValue<ThresholdsMode>> = [
|
||||
|
@@ -5,7 +5,7 @@ import { GrafanaTheme, dateTime, TIME_FORMAT } from '@grafana/data';
|
||||
import { stringToDateTimeType } from '../time';
|
||||
import { useTheme, stylesFactory } from '../../../themes';
|
||||
import { TimePickerTitle } from './TimePickerTitle';
|
||||
import Forms from '../../Forms';
|
||||
import { Button } from '../../Button';
|
||||
import { Portal } from '../../Portal/Portal';
|
||||
import { getThemeColors } from './colors';
|
||||
import { ClickOutsideWrapper } from '../../ClickOutsideWrapper/ClickOutsideWrapper';
|
||||
@@ -281,12 +281,12 @@ const Footer = memo<Props>(({ onClose, onApply }) => {
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<Forms.Button className={styles.apply} onClick={onApply}>
|
||||
<Button className={styles.apply} onClick={onApply}>
|
||||
Apply time range
|
||||
</Forms.Button>
|
||||
<Forms.Button variant="secondary" onClick={onClose}>
|
||||
</Button>
|
||||
<Button variant="secondary" onClick={onClose}>
|
||||
Cancel
|
||||
</Forms.Button>
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
@@ -4,6 +4,7 @@ import { stringToDateTimeType, isValidTimeString } from '../time';
|
||||
import { mapStringsToTimeRange } from './mapper';
|
||||
import { TimePickerCalendar } from './TimePickerCalendar';
|
||||
import Forms from '../../Forms';
|
||||
import { Button } from '../../Button';
|
||||
|
||||
interface Props {
|
||||
isFullscreen: boolean;
|
||||
@@ -60,7 +61,7 @@ export const TimeRangeForm: React.FC<Props> = props => {
|
||||
[timeZone]
|
||||
);
|
||||
|
||||
const icon = isFullscreen ? null : <Forms.Button icon="fa fa-calendar" variant="secondary" onClick={onOpen} />;
|
||||
const icon = isFullscreen ? null : <Button icon="fa fa-calendar" variant="secondary" onClick={onOpen} />;
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -82,7 +83,7 @@ export const TimeRangeForm: React.FC<Props> = props => {
|
||||
value={to.value}
|
||||
/>
|
||||
</Forms.Field>
|
||||
<Forms.Button onClick={onApply}>Apply time range</Forms.Button>
|
||||
<Button onClick={onApply}>Apply time range</Button>
|
||||
|
||||
<TimePickerCalendar
|
||||
isFullscreen={isFullscreen}
|
||||
|
@@ -3,7 +3,7 @@ import { Select } from '../Select/Select';
|
||||
import { transformersUIRegistry } from './transformers';
|
||||
import React from 'react';
|
||||
import { TransformationRow } from './TransformationRow';
|
||||
import { Button } from '../Button/Button';
|
||||
import { Button } from '../Button';
|
||||
import { css } from 'emotion';
|
||||
|
||||
interface TransformationsEditorState {
|
||||
@@ -118,7 +118,7 @@ export class TransformationsEditor extends React.PureComponent<TransformationsEd
|
||||
return (
|
||||
<>
|
||||
{this.renderTransformationEditors()}
|
||||
<Button variant="inverse" icon="fa fa-plus" onClick={this.onTransformationAdd}>
|
||||
<Button variant="secondary" icon="fa fa-plus" onClick={this.onTransformationAdd}>
|
||||
Add transformation
|
||||
</Button>
|
||||
</>
|
||||
|
@@ -2,7 +2,7 @@ import React, { PureComponent } from 'react';
|
||||
|
||||
import LegacyMappingRow from './LegacyMappingRow';
|
||||
import { MappingType, ValueMapping } from '@grafana/data';
|
||||
import { Button } from '../Button/Button';
|
||||
import { Button } from '../Button';
|
||||
import { PanelOptionsGroup } from '../PanelOptionsGroup/PanelOptionsGroup';
|
||||
|
||||
export interface Props {
|
||||
@@ -98,7 +98,7 @@ export class LegacyValueMappingsEditor extends PureComponent<Props, State> {
|
||||
removeValueMapping={() => this.onRemoveMapping(valueMapping.id)}
|
||||
/>
|
||||
))}
|
||||
<Button variant="inverse" icon="fa fa-plus" onClick={this.onAddMapping}>
|
||||
<Button variant="primary" icon="fa fa-plus" onClick={this.onAddMapping}>
|
||||
Add mapping
|
||||
</Button>
|
||||
</div>
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import { MappingType, ValueMapping } from '@grafana/data';
|
||||
import Forms from '../Forms';
|
||||
import { Button } from '../Button/Button';
|
||||
import { FullWidthButtonContainer } from '../Button/FullWidthButtonContainer';
|
||||
import { MappingRow } from './MappingRow';
|
||||
|
||||
@@ -66,9 +66,9 @@ export const ValueMappingsEditor: React.FC<Props> = ({ valueMappings, onChange,
|
||||
</>
|
||||
)}
|
||||
<FullWidthButtonContainer>
|
||||
<Forms.Button size="sm" icon="fa fa-plus" onClick={onAdd} aria-label="ValueMappingsEditor add mapping button">
|
||||
<Button size="sm" icon="fa fa-plus" onClick={onAdd} aria-label="ValueMappingsEditor add mapping button">
|
||||
Add mapping
|
||||
</Forms.Button>
|
||||
</Button>
|
||||
</FullWidthButtonContainer>
|
||||
</>
|
||||
);
|
||||
|
@@ -37,7 +37,7 @@ exports[`Render should render component 1`] = `
|
||||
<Button
|
||||
icon="fa fa-plus"
|
||||
onClick={[Function]}
|
||||
variant="inverse"
|
||||
variant="primary"
|
||||
>
|
||||
Add mapping
|
||||
</Button>
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import React, { useState } from 'react';
|
||||
import { IconType } from '../Icon/types';
|
||||
import { SelectableValue } from '@grafana/data';
|
||||
import { Button, ButtonVariant } from '../Forms/Button';
|
||||
import { Button, ButtonVariant } from '../Button';
|
||||
import { Select } from '../Forms/Select/Select';
|
||||
import { FullWidthButtonContainer } from '../Button/FullWidthButtonContainer';
|
||||
|
||||
|
@@ -6,7 +6,6 @@ export { Popover } from './Tooltip/Popover';
|
||||
export { Portal } from './Portal/Portal';
|
||||
export { CustomScrollbar } from './CustomScrollbar/CustomScrollbar';
|
||||
|
||||
export * from './Button/Button';
|
||||
export { ClipboardButton } from './ClipboardButton/ClipboardButton';
|
||||
|
||||
// Select
|
||||
@@ -99,7 +98,7 @@ export { LogLabels } from './Logs/LogLabels';
|
||||
export { LogRows } from './Logs/LogRows';
|
||||
export { getLogRowStyles } from './Logs/getLogRowStyles';
|
||||
export { ToggleButtonGroup, ToggleButton } from './ToggleButtonGroup/ToggleButtonGroup';
|
||||
// Panel editors
|
||||
// Panel editors./Forms/Legacy/Button/FullWidthButtonContainer
|
||||
export { FullWidthButtonContainer } from './Button/FullWidthButtonContainer';
|
||||
export { ThresholdsEditor } from './ThresholdsEditor/ThresholdsEditor';
|
||||
export { ClickOutsideWrapper } from './ClickOutsideWrapper/ClickOutsideWrapper';
|
||||
@@ -151,7 +150,8 @@ export {
|
||||
export { FieldConfigItemHeaderTitle } from './FieldConfigs/FieldConfigItemHeaderTitle';
|
||||
|
||||
// Next-gen forms
|
||||
export { default as Forms, ButtonVariant } from './Forms';
|
||||
export { default as Forms } from './Forms';
|
||||
export * from './Button';
|
||||
export { ValuePicker } from './ValuePicker/ValuePicker';
|
||||
export { fieldMatchersUI } from './MatchersUI/fieldMatchersUI';
|
||||
export { getStandardFieldConfigs } from './FieldConfigs/standardFieldConfigEditors';
|
||||
|
@@ -168,11 +168,11 @@ $table-bg-hover: $dark-6;
|
||||
|
||||
// Buttons
|
||||
// -------------------------
|
||||
$btn-secondary-bg: $blue-base;
|
||||
$btn-secondary-bg-hl: $blue-shade;
|
||||
$btn-primary-bg: $blue-base;
|
||||
$btn-primary-bg-hl: $blue-shade;
|
||||
|
||||
$btn-primary-bg: $green-base;
|
||||
$btn-primary-bg-hl: $green-shade;
|
||||
$btn-secondary-bg: $dark-6;
|
||||
$btn-secondary-bg-hl: lighten($dark-6, 4%);
|
||||
|
||||
$btn-success-bg: $green-base;
|
||||
$btn-success-bg-hl: $green-shade;
|
||||
|
@@ -160,11 +160,11 @@ $table-bg-hover: $gray-5;
|
||||
|
||||
// Buttons
|
||||
// -------------------------
|
||||
$btn-primary-bg: $green-base;
|
||||
$btn-primary-bg-hl: $green-shade;
|
||||
$btn-secondary-bg: $gray-5;
|
||||
$btn-secondary-bg-hl: $gray-4;
|
||||
|
||||
$btn-secondary-bg: $blue-base;
|
||||
$btn-secondary-bg-hl: $blue-shade;
|
||||
$btn-primary-bg: $blue-base;
|
||||
$btn-primary-bg-hl: $blue-shade;
|
||||
|
||||
$btn-success-bg: $green-base;
|
||||
$btn-success-bg-hl: $green-shade;
|
||||
@@ -173,7 +173,6 @@ $btn-danger-bg: $red-base;
|
||||
$btn-danger-bg-hl: $red-shade;
|
||||
|
||||
$btn-inverse-bg: $gray-5;
|
||||
$btn-inverse-bg-hl: darken($gray-5, 5%);
|
||||
$btn-inverse-bg-hl: $gray-4;
|
||||
$btn-inverse-text-color: $gray-1;
|
||||
$btn-inverse-text-shadow: 0 1px 0 rgba(255, 255, 255, 0.4);
|
||||
|
@@ -173,7 +173,6 @@ export function registerAngularDirectives() {
|
||||
]);
|
||||
react2AngularDirective('saveDashboardAsButton', SaveDashboardAsButtonConnected, [
|
||||
'variant',
|
||||
'useNewForms',
|
||||
['getDashboard', { watchDepth: 'reference', wrapApply: true }],
|
||||
['onSaveSuccess', { watchDepth: 'reference', wrapApply: true }],
|
||||
]);
|
||||
|
@@ -65,7 +65,7 @@ export class OrgSwitcher extends React.PureComponent<Props, State> {
|
||||
{org.orgId === currentOrgId ? (
|
||||
<Button size="sm">Current</Button>
|
||||
) : (
|
||||
<Button variant="inverse" size="sm" onClick={() => this.setCurrentOrg(org)}>
|
||||
<Button variant="secondary" size="sm" onClick={() => this.setCurrentOrg(org)}>
|
||||
Switch to
|
||||
</Button>
|
||||
)}
|
||||
|
@@ -3,7 +3,7 @@ import { css } from 'emotion';
|
||||
import { NavModel } from '@grafana/data';
|
||||
import Page from '../../core/components/Page/Page';
|
||||
import { LicenseChrome } from './LicenseChrome';
|
||||
import { Forms } from '@grafana/ui';
|
||||
import { LinkButton } from '@grafana/ui';
|
||||
import { hot } from 'react-hot-loader';
|
||||
import { StoreState } from '../../types';
|
||||
import { getNavModel } from '../../core/selectors/navModel';
|
||||
@@ -69,13 +69,13 @@ const GetEnterprise: React.FC = () => {
|
||||
|
||||
const CallToAction: React.FC = () => {
|
||||
return (
|
||||
<Forms.LinkButton
|
||||
<LinkButton
|
||||
variant="primary"
|
||||
size="lg"
|
||||
href="https://grafana.com/contact?about=grafana-enterprise&utm_source=grafana-upgrade-page"
|
||||
>
|
||||
Contact us and get a free trial
|
||||
</Forms.LinkButton>
|
||||
</LinkButton>
|
||||
);
|
||||
};
|
||||
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import { hot } from 'react-hot-loader';
|
||||
import { connect } from 'react-redux';
|
||||
import { Forms } from '@grafana/ui';
|
||||
import { Forms, Button } from '@grafana/ui';
|
||||
import { NavModel } from '@grafana/data';
|
||||
import { getBackendSrv } from '@grafana/runtime';
|
||||
import { StoreState } from '../../types';
|
||||
@@ -62,7 +62,7 @@ const UserCreatePage: React.FC<UserCreatePageProps> = ({ navModel, updateLocatio
|
||||
})}
|
||||
/>
|
||||
</Forms.Field>
|
||||
<Forms.Button type="submit">Create user</Forms.Button>
|
||||
<Button type="submit">Create user</Button>
|
||||
</>
|
||||
);
|
||||
}}
|
||||
|
@@ -71,7 +71,7 @@ export class UserLdapSyncInfo extends PureComponent<Props, State> {
|
||||
<Button variant="secondary" onClick={this.onUserSync}>
|
||||
Sync user
|
||||
</Button>
|
||||
<LinkButton variant="inverse" href={debugLDAPMappingURL}>
|
||||
<LinkButton variant="secondary" href={debugLDAPMappingURL}>
|
||||
Debug LDAP Mapping
|
||||
</LinkButton>
|
||||
</div>
|
||||
|
@@ -3,7 +3,7 @@ import { css, cx } from 'emotion';
|
||||
import { hot } from 'react-hot-loader';
|
||||
import { connect, MapDispatchToProps, MapStateToProps } from 'react-redux';
|
||||
import { NavModel } from '@grafana/data';
|
||||
import { Pagination, Forms, Tooltip, HorizontalGroup, stylesFactory } from '@grafana/ui';
|
||||
import { Pagination, Forms, Tooltip, HorizontalGroup, stylesFactory, LinkButton } from '@grafana/ui';
|
||||
import { StoreState, UserDTO } from '../../types';
|
||||
import Page from 'app/core/components/Page/Page';
|
||||
import { getNavModel } from '../../core/selectors/navModel';
|
||||
@@ -53,9 +53,9 @@ const UserListAdminPageUnConnected: React.FC<Props> = props => {
|
||||
onChange={event => props.changeQuery(event.currentTarget.value)}
|
||||
prefix={<i className="fa fa-search" />}
|
||||
/>
|
||||
<Forms.LinkButton href="admin/users/create" variant="primary">
|
||||
<LinkButton href="admin/users/create" variant="primary">
|
||||
New user
|
||||
</Forms.LinkButton>
|
||||
</LinkButton>
|
||||
</HorizontalGroup>
|
||||
</div>
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import { css, cx } from 'emotion';
|
||||
import { Modal, Themeable, stylesFactory, withTheme, ConfirmButton, Forms } from '@grafana/ui';
|
||||
import { Modal, Themeable, stylesFactory, withTheme, ConfirmButton, Button } from '@grafana/ui';
|
||||
import { GrafanaTheme } from '@grafana/data';
|
||||
import { UserOrg, Organization } from 'app/types';
|
||||
import { OrgPicker, OrgSelectItem } from 'app/core/components/Select/OrgPicker';
|
||||
@@ -52,9 +52,9 @@ export class UserOrgs extends PureComponent<Props, State> {
|
||||
</table>
|
||||
</div>
|
||||
<div className={addToOrgContainerClass}>
|
||||
<Forms.Button variant="secondary" onClick={this.showOrgAddModal(true)}>
|
||||
<Button variant="secondary" onClick={this.showOrgAddModal(true)}>
|
||||
Add user to organization
|
||||
</Forms.Button>
|
||||
</Button>
|
||||
</div>
|
||||
<AddToOrgModal isOpen={showAddOrgModal} onOrgAdd={onOrgAdd} onDismiss={this.showOrgAddModal(false)} />
|
||||
</div>
|
||||
@@ -169,7 +169,7 @@ class UnThemedOrgRow extends PureComponent<OrgRowProps, OrgRowState> {
|
||||
<div className="pull-right">
|
||||
<ConfirmButton
|
||||
confirmText="Confirm removal"
|
||||
confirmVariant="danger"
|
||||
confirmVariant="destructive"
|
||||
onClick={this.onOrgRemoveClick}
|
||||
onCancel={this.onCancelClick}
|
||||
onConfirm={this.onOrgRemove}
|
||||
@@ -258,12 +258,12 @@ export class AddToOrgModal extends PureComponent<AddToOrgModalProps, AddToOrgMod
|
||||
</div>
|
||||
</div>
|
||||
<div className={buttonRowClass}>
|
||||
<Forms.Button variant="primary" onClick={this.onAddUserToOrg}>
|
||||
<Button variant="primary" onClick={this.onAddUserToOrg}>
|
||||
Add to organization
|
||||
</Forms.Button>
|
||||
<Forms.Button variant="secondary" onClick={this.onCancel}>
|
||||
</Button>
|
||||
<Button variant="secondary" onClick={this.onCancel}>
|
||||
Cancel
|
||||
</Forms.Button>
|
||||
</Button>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
|
@@ -3,7 +3,7 @@ import { UserDTO } from 'app/types';
|
||||
import { cx, css } from 'emotion';
|
||||
import { config } from 'app/core/config';
|
||||
import { GrafanaTheme } from '@grafana/data';
|
||||
import { ConfirmButton, Input, ConfirmModal, InputStatus, Forms, stylesFactory } from '@grafana/ui';
|
||||
import { ConfirmButton, Input, ConfirmModal, InputStatus, Button, stylesFactory } from '@grafana/ui';
|
||||
|
||||
interface Props {
|
||||
user: UserDTO;
|
||||
@@ -125,9 +125,9 @@ export class UserProfile extends PureComponent<Props, State> {
|
||||
</table>
|
||||
</div>
|
||||
<div className={styles.buttonRow}>
|
||||
<Forms.Button variant="destructive" onClick={this.showDeleteUserModal(true)}>
|
||||
<Button variant="destructive" onClick={this.showDeleteUserModal(true)}>
|
||||
Delete User
|
||||
</Forms.Button>
|
||||
</Button>
|
||||
<ConfirmModal
|
||||
isOpen={showDeleteModal}
|
||||
title="Delete user"
|
||||
@@ -137,13 +137,13 @@ export class UserProfile extends PureComponent<Props, State> {
|
||||
onDismiss={this.showDeleteUserModal(false)}
|
||||
/>
|
||||
{user.isDisabled ? (
|
||||
<Forms.Button variant="secondary" onClick={this.onUserEnable}>
|
||||
<Button variant="secondary" onClick={this.onUserEnable}>
|
||||
Enable User
|
||||
</Forms.Button>
|
||||
</Button>
|
||||
) : (
|
||||
<Forms.Button variant="secondary" onClick={this.showDisableUserModal(true)}>
|
||||
<Button variant="secondary" onClick={this.showDisableUserModal(true)}>
|
||||
Disable User
|
||||
</Forms.Button>
|
||||
</Button>
|
||||
)}
|
||||
<ConfirmModal
|
||||
isOpen={showDisableModal}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import { css } from 'emotion';
|
||||
import { ConfirmButton, ConfirmModal, Forms } from '@grafana/ui';
|
||||
import { ConfirmButton, ConfirmModal, Button } from '@grafana/ui';
|
||||
import { UserSession } from 'app/types';
|
||||
|
||||
interface Props {
|
||||
@@ -68,7 +68,7 @@ export class UserSessions extends PureComponent<Props, State> {
|
||||
<div className="pull-right">
|
||||
<ConfirmButton
|
||||
confirmText="Confirm logout"
|
||||
confirmVariant="danger"
|
||||
confirmVariant="destructive"
|
||||
onConfirm={this.onSessionRevoke(session.id)}
|
||||
>
|
||||
Force logout
|
||||
@@ -82,9 +82,9 @@ export class UserSessions extends PureComponent<Props, State> {
|
||||
</div>
|
||||
<div className={logoutFromAllDevicesClass}>
|
||||
{sessions.length > 0 && (
|
||||
<Forms.Button variant="secondary" onClick={this.showLogoutConfirmationModal(true)}>
|
||||
<Button variant="secondary" onClick={this.showLogoutConfirmationModal(true)}>
|
||||
Force logout from all devices
|
||||
</Forms.Button>
|
||||
</Button>
|
||||
)}
|
||||
<ConfirmModal
|
||||
isOpen={showLogoutModal}
|
||||
|
@@ -13,7 +13,7 @@
|
||||
<save-dashboard-button getDashboard="ctrl.getDashboard" aria-label="Dashboard settings aside actions Save button" />
|
||||
</div>
|
||||
<div ng-show="ctrl.canSaveAs">
|
||||
<save-dashboard-as-button getDashboard="ctrl.getDashboard" aria-label="Dashboard settings aside actions Save as button" variant="'secondary'" useNewForms="true"/>
|
||||
<save-dashboard-as-button getDashboard="ctrl.getDashboard" aria-label="Dashboard settings aside actions Save as button" variant="'secondary'" />
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
|
@@ -6,7 +6,7 @@ import { css } from 'emotion';
|
||||
import { InspectHeader } from './InspectHeader';
|
||||
|
||||
import { DashboardModel, PanelModel } from 'app/features/dashboard/state';
|
||||
import { JSONFormatter, Drawer, Select, Table, TabContent, Forms, stylesFactory, CustomScrollbar } from '@grafana/ui';
|
||||
import { JSONFormatter, Drawer, Select, Table, TabContent, stylesFactory, CustomScrollbar, Button } from '@grafana/ui';
|
||||
import { getLocationSrv, getDataSourceSrv } from '@grafana/runtime';
|
||||
import {
|
||||
DataFrame,
|
||||
@@ -189,9 +189,9 @@ export class PanelInspector extends PureComponent<Props, State> {
|
||||
</div>
|
||||
)}
|
||||
<div className={styles.downloadCsv}>
|
||||
<Forms.Button variant="primary" onClick={() => this.exportCsv(processed[selected])}>
|
||||
<Button variant="primary" onClick={() => this.exportCsv(processed[selected])}>
|
||||
Download CSV
|
||||
</Forms.Button>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ flexGrow: 1 }}>
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import { FieldConfigSource, GrafanaTheme, PanelData, PanelPlugin, SelectableValue } from '@grafana/data';
|
||||
import { Forms, stylesFactory } from '@grafana/ui';
|
||||
import { Forms, stylesFactory, Button } from '@grafana/ui';
|
||||
import { css, cx } from 'emotion';
|
||||
import config from 'app/core/config';
|
||||
import AutoSizer from 'react-virtualized-auto-sizer';
|
||||
@@ -198,9 +198,9 @@ export class PanelEditorUnconnected extends PureComponent<Props> {
|
||||
</div>
|
||||
<div className={styles.toolbarLeft}>
|
||||
<div className={styles.toolbarItem}>
|
||||
<Forms.Button className={styles.toolbarItem} variant="secondary" onClick={this.onDiscard}>
|
||||
<Button className={styles.toolbarItem} variant="secondary" onClick={this.onDiscard}>
|
||||
Discard changes
|
||||
</Forms.Button>
|
||||
</Button>
|
||||
</div>
|
||||
<div className={styles.toolbarItem}>
|
||||
<Forms.Select
|
||||
@@ -213,7 +213,7 @@ export class PanelEditorUnconnected extends PureComponent<Props> {
|
||||
<DashNavTimeControls dashboard={dashboard} location={location} updateLocation={updateLocation} />
|
||||
</div>
|
||||
<div className={styles.toolbarItem}>
|
||||
<Forms.Button
|
||||
<Button
|
||||
className={styles.toolbarItem}
|
||||
icon="fa fa-sliders"
|
||||
variant="secondary"
|
||||
|
@@ -1,6 +1,5 @@
|
||||
import React from 'react';
|
||||
import { css } from 'emotion';
|
||||
import { Button, Forms, ModalsController } from '@grafana/ui';
|
||||
import { Button, ButtonVariant, ModalsController, FullWidthButtonContainer } from '@grafana/ui';
|
||||
import { DashboardModel } from 'app/features/dashboard/state';
|
||||
import { connectWithProvider } from 'app/core/utils/connectWithReduxStore';
|
||||
import { provideModalsContext } from 'app/routes/ReactContainer';
|
||||
@@ -24,12 +23,11 @@ export const SaveDashboardButton: React.FC<SaveDashboardButtonProps> = ({
|
||||
getDashboard,
|
||||
useNewForms,
|
||||
}) => {
|
||||
const ButtonComponent = useNewForms ? Forms.Button : Button;
|
||||
return (
|
||||
<ModalsController>
|
||||
{({ showModal, hideModal }) => {
|
||||
return (
|
||||
<ButtonComponent
|
||||
<Button
|
||||
onClick={() => {
|
||||
showModal(SaveDashboardModalProxy, {
|
||||
// TODO[angular-migrations]: Remove tenary op when we migrate Dashboard Settings view to React
|
||||
@@ -40,46 +38,41 @@ export const SaveDashboardButton: React.FC<SaveDashboardButtonProps> = ({
|
||||
}}
|
||||
>
|
||||
Save dashboard
|
||||
</ButtonComponent>
|
||||
</Button>
|
||||
);
|
||||
}}
|
||||
</ModalsController>
|
||||
);
|
||||
};
|
||||
|
||||
export const SaveDashboardAsButton: React.FC<SaveDashboardButtonProps & { variant?: string }> = ({
|
||||
export const SaveDashboardAsButton: React.FC<SaveDashboardButtonProps & { variant?: ButtonVariant }> = ({
|
||||
dashboard,
|
||||
onSaveSuccess,
|
||||
getDashboard,
|
||||
useNewForms,
|
||||
variant,
|
||||
}) => {
|
||||
const ButtonComponent = useNewForms ? Forms.Button : Button;
|
||||
return (
|
||||
<ModalsController>
|
||||
{({ showModal, hideModal }) => {
|
||||
return (
|
||||
<ButtonComponent
|
||||
/* Styles applied here are specific to dashboard settings view */
|
||||
className={css`
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
`}
|
||||
onClick={() => {
|
||||
showModal(SaveDashboardAsModal, {
|
||||
// TODO[angular-migrations]: Remove tenary op when we migrate Dashboard Settings view to React
|
||||
dashboard: getDashboard ? getDashboard() : dashboard,
|
||||
onSaveSuccess,
|
||||
onDismiss: hideModal,
|
||||
});
|
||||
}}
|
||||
// TODO[angular-migrations]: Hacking the different variants for this single button
|
||||
// In Dashboard Settings in sidebar we need to use new form but with inverse variant to make it look like it should
|
||||
// Everywhere else we use old button component :(
|
||||
variant={variant as any}
|
||||
>
|
||||
Save As...
|
||||
</ButtonComponent>
|
||||
<FullWidthButtonContainer>
|
||||
<Button
|
||||
onClick={() => {
|
||||
showModal(SaveDashboardAsModal, {
|
||||
// TODO[angular-migrations]: Remove tenary op when we migrate Dashboard Settings view to React
|
||||
dashboard: getDashboard ? getDashboard() : dashboard,
|
||||
onSaveSuccess,
|
||||
onDismiss: hideModal,
|
||||
});
|
||||
}}
|
||||
// TODO[angular-migrations]: Hacking the different variants for this single button
|
||||
// In Dashboard Settings in sidebar we need to use new form but with inverse variant to make it look like it should
|
||||
// Everywhere else we use old button component :(
|
||||
variant={variant as ButtonVariant}
|
||||
>
|
||||
Save As...
|
||||
</Button>
|
||||
</FullWidthButtonContainer>
|
||||
);
|
||||
}}
|
||||
</ModalsController>
|
||||
|
@@ -89,7 +89,7 @@ const ConfirmPluginDashboardSaveModal: React.FC<SaveDashboardModalProps> = ({ on
|
||||
<HorizontalGroup justify="center">
|
||||
<SaveDashboardAsButton dashboard={dashboard} onSaveSuccess={onDismiss} />
|
||||
<Button
|
||||
variant="danger"
|
||||
variant="destructive"
|
||||
onClick={async () => {
|
||||
await onDashboardSave(dashboard.getSaveModelClone(), { overwrite: true }, dashboard);
|
||||
onDismiss();
|
||||
@@ -97,7 +97,7 @@ const ConfirmPluginDashboardSaveModal: React.FC<SaveDashboardModalProps> = ({ on
|
||||
>
|
||||
Overwrite
|
||||
</Button>
|
||||
<Button variant="inverse" onClick={onDismiss}>
|
||||
<Button variant="secondary" onClick={onDismiss}>
|
||||
Cancel
|
||||
</Button>
|
||||
</HorizontalGroup>
|
||||
|
@@ -101,9 +101,9 @@ export const SaveDashboardAsForm: React.FC<SaveDashboardFormProps & { isNew?: bo
|
||||
<Button type="submit" aria-label="Save dashboard button">
|
||||
Save
|
||||
</Button>
|
||||
<Forms.Button variant="secondary" onClick={onCancel}>
|
||||
<Button variant="secondary" onClick={onCancel}>
|
||||
Cancel
|
||||
</Forms.Button>
|
||||
</Button>
|
||||
</HorizontalGroup>
|
||||
</>
|
||||
)}
|
||||
|
@@ -62,9 +62,9 @@ export const SaveDashboardForm: React.FC<SaveDashboardFormProps> = ({ dashboard,
|
||||
<Button type="submit" aria-label={e2e.pages.SaveDashboardModal.selectors.save}>
|
||||
Save
|
||||
</Button>
|
||||
<Forms.Button variant="secondary" onClick={onCancel}>
|
||||
<Button variant="secondary" onClick={onCancel}>
|
||||
Cancel
|
||||
</Forms.Button>
|
||||
</Button>
|
||||
</HorizontalGroup>
|
||||
</>
|
||||
)}
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import { css } from 'emotion';
|
||||
import { saveAs } from 'file-saver';
|
||||
import { CustomScrollbar, Forms, Button, HorizontalGroup, JSONFormatter, VerticalGroup } from '@grafana/ui';
|
||||
import { CustomScrollbar, Button, HorizontalGroup, JSONFormatter, VerticalGroup } from '@grafana/ui';
|
||||
import { CopyToClipboard } from 'app/core/components/CopyToClipboard/CopyToClipboard';
|
||||
import { SaveDashboardFormProps } from '../types';
|
||||
|
||||
@@ -61,9 +61,9 @@ export const SaveProvisionedDashboardForm: React.FC<SaveDashboardFormProps> = ({
|
||||
Copy JSON to clipboard
|
||||
</CopyToClipboard>
|
||||
<Button onClick={saveToFile}>Save JSON to file</Button>
|
||||
<Forms.Button variant="secondary" onClick={onCancel}>
|
||||
<Button variant="secondary" onClick={onCancel}>
|
||||
Cancel
|
||||
</Forms.Button>
|
||||
</Button>
|
||||
</HorizontalGroup>
|
||||
</VerticalGroup>
|
||||
</>
|
||||
|
@@ -108,7 +108,7 @@ export class ShareExport extends PureComponent<Props, State> {
|
||||
<Button variant="secondary" onClick={this.onViewJson}>
|
||||
View JSON
|
||||
</Button>
|
||||
<Button variant="inverse" onClick={onDismiss}>
|
||||
<Button variant="secondary" onClick={onDismiss}>
|
||||
Cancel
|
||||
</Button>
|
||||
</div>
|
||||
|
@@ -121,7 +121,7 @@ export class ShareLink extends PureComponent<Props, State> {
|
||||
<input type="text" className="gf-form-input" defaultValue={shareUrl} />
|
||||
</div>
|
||||
<div className="gf-form">
|
||||
<ClipboardButton variant="inverse" getText={this.getShareUrl} onClipboardCopy={this.onShareUrlCopy}>
|
||||
<ClipboardButton variant="primary" getText={this.getShareUrl} onClipboardCopy={this.onShareUrlCopy}>
|
||||
Copy
|
||||
</ClipboardButton>
|
||||
</div>
|
||||
|
@@ -249,7 +249,7 @@ export class ShareSnapshot extends PureComponent<Props, State> {
|
||||
{sharingButtonText}
|
||||
</Button>
|
||||
)}
|
||||
<Button variant="inverse" onClick={onDismiss}>
|
||||
<Button variant="secondary" onClick={onDismiss}>
|
||||
Cancel
|
||||
</Button>
|
||||
</div>
|
||||
@@ -268,7 +268,7 @@ export class ShareSnapshot extends PureComponent<Props, State> {
|
||||
<i className="fa fa-external-link-square"></i> {snapshotUrl}
|
||||
</a>
|
||||
<br />
|
||||
<ClipboardButton variant="inverse" getText={this.getSnapshotUrl} onClipboardCopy={this.onSnapshotUrlCopy}>
|
||||
<ClipboardButton variant="secondary" getText={this.getSnapshotUrl} onClipboardCopy={this.onSnapshotUrlCopy}>
|
||||
Copy Link
|
||||
</ClipboardButton>
|
||||
</div>
|
||||
|
@@ -2,7 +2,7 @@ import React, { useState } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { hot } from 'react-hot-loader';
|
||||
import { css, cx } from 'emotion';
|
||||
import { stylesFactory, useTheme, Forms } from '@grafana/ui';
|
||||
import { stylesFactory, useTheme, Forms, Button } from '@grafana/ui';
|
||||
import { GrafanaTheme, AppEvents, DataSourceApi } from '@grafana/data';
|
||||
import { RichHistoryQuery, ExploreId } from 'app/types/explore';
|
||||
import { copyStringToClipboard, createUrlFromRichHistory, createDataQuery } from 'app/core/utils/richHistory';
|
||||
@@ -202,10 +202,10 @@ export function RichHistoryCard(props: Props) {
|
||||
className={styles.textArea}
|
||||
/>
|
||||
<div className={styles.commentButtonRow}>
|
||||
<Forms.Button onClick={onUpdateComment}>Save comment</Forms.Button>
|
||||
<Forms.Button variant="secondary" onClick={onCancelUpdateComment}>
|
||||
<Button onClick={onUpdateComment}>Save comment</Button>
|
||||
<Button variant="secondary" onClick={onCancelUpdateComment}>
|
||||
Cancel
|
||||
</Forms.Button>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@@ -257,9 +257,9 @@ export function RichHistoryCard(props: Props) {
|
||||
</div>
|
||||
{!activeUpdateComment && (
|
||||
<div className={styles.runButton}>
|
||||
<Forms.Button variant="secondary" onClick={onRunQuery} disabled={isRemoved}>
|
||||
<Button variant="secondary" onClick={onRunQuery} disabled={isRemoved}>
|
||||
{datasourceInstance?.name === query.datasourceName ? 'Run query' : 'Switch data source and run query'}
|
||||
</Forms.Button>
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import { css } from 'emotion';
|
||||
import { stylesFactory, useTheme, Forms } from '@grafana/ui';
|
||||
import { stylesFactory, useTheme, Forms, Button } from '@grafana/ui';
|
||||
import { GrafanaTheme, AppEvents } from '@grafana/data';
|
||||
import appEvents from 'app/core/app_events';
|
||||
import { CoreEvents } from 'app/types';
|
||||
@@ -112,9 +112,9 @@ export function RichHistorySettings(props: RichHistorySettingsProps) {
|
||||
>
|
||||
Delete all of your query history, permanently.
|
||||
</div>
|
||||
<Forms.Button variant="destructive" onClick={onDelete}>
|
||||
<Button variant="destructive" onClick={onDelete}>
|
||||
Clear query history
|
||||
</Forms.Button>
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@@ -36,7 +36,7 @@ export function RunButton(props: Props) {
|
||||
title={loading ? 'Cancel' : 'Run Query'}
|
||||
onClick={() => onRun(loading)}
|
||||
buttonClassName={classNames({
|
||||
'navbar-button--secondary': !loading,
|
||||
'navbar-button--primary': !loading,
|
||||
'navbar-button--danger': loading,
|
||||
'btn--radius-right-0': showDropdown,
|
||||
})}
|
||||
@@ -49,7 +49,7 @@ export function RunButton(props: Props) {
|
||||
<RefreshPicker
|
||||
onIntervalChanged={onChangeRefreshInterval}
|
||||
value={refreshInterval}
|
||||
buttonSelectClassName={`${loading ? 'navbar-button--danger' : 'navbar-button--secondary'} ${
|
||||
buttonSelectClassName={`${loading ? 'navbar-button--danger' : 'navbar-button--primary'} ${
|
||||
styles.selectButtonOverride
|
||||
}`}
|
||||
refreshButton={runButton}
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import { connect, MapDispatchToProps, MapStateToProps } from 'react-redux';
|
||||
import { NavModel } from '@grafana/data';
|
||||
import { Forms } from '@grafana/ui';
|
||||
import { Forms, Button } from '@grafana/ui';
|
||||
import Page from 'app/core/components/Page/Page';
|
||||
import { createNewFolder } from '../state/actions';
|
||||
import { getNavModel } from 'app/core/selectors/navModel';
|
||||
@@ -63,7 +63,7 @@ export class NewDashboardsFolder extends PureComponent<Props> {
|
||||
})}
|
||||
/>
|
||||
</Forms.Field>
|
||||
<Forms.Button type="submit">Create</Forms.Button>
|
||||
<Button type="submit">Create</Button>
|
||||
</>
|
||||
)}
|
||||
</Forms.Form>
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import React, { FC } from 'react';
|
||||
import { getBackendSrv } from '@grafana/runtime';
|
||||
import Page from 'app/core/components/Page/Page';
|
||||
import { Forms } from '@grafana/ui';
|
||||
import { Forms, Button } from '@grafana/ui';
|
||||
import { getConfig } from 'app/core/config';
|
||||
import { StoreState } from 'app/types';
|
||||
import { hot } from 'react-hot-loader';
|
||||
@@ -68,7 +68,7 @@ export const NewOrgPage: FC<PropsWithState> = ({ navModel }) => {
|
||||
})}
|
||||
/>
|
||||
</Forms.Field>
|
||||
<Forms.Button type="submit">Create</Forms.Button>
|
||||
<Button type="submit">Create</Button>
|
||||
</>
|
||||
);
|
||||
}}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import React, { FC } from 'react';
|
||||
import { Forms, HorizontalGroup } from '@grafana/ui';
|
||||
import { Forms, HorizontalGroup, Button, LinkButton } from '@grafana/ui';
|
||||
import { getConfig } from 'app/core/config';
|
||||
import { OrgRole } from 'app/types';
|
||||
import { getBackendSrv } from '@grafana/runtime';
|
||||
@@ -71,10 +71,10 @@ export const UserInviteForm: FC<Props> = ({ updateLocation }) => {
|
||||
<Forms.Switch name="sendEmail" ref={register} />
|
||||
</Forms.Field>
|
||||
<HorizontalGroup>
|
||||
<Forms.Button type="submit">Submit</Forms.Button>
|
||||
<Forms.LinkButton href={assureBaseUrl(getConfig().appSubUrl + '/org/users')} variant="secondary">
|
||||
<Button type="submit">Submit</Button>
|
||||
<LinkButton href={assureBaseUrl(getConfig().appSubUrl + '/org/users')} variant="secondary">
|
||||
Back
|
||||
</Forms.LinkButton>
|
||||
</LinkButton>
|
||||
</HorizontalGroup>
|
||||
</>
|
||||
);
|
||||
|
@@ -80,7 +80,7 @@ export class AppConfigCtrlWrapper extends PureComponent<Props, State> {
|
||||
</Button>
|
||||
)}
|
||||
{model.enabled && (
|
||||
<Button variant="danger" onClick={this.disable} className={withRightMargin}>
|
||||
<Button variant="destructive" onClick={this.disable} className={withRightMargin}>
|
||||
Disable
|
||||
</Button>
|
||||
)}
|
||||
|
@@ -63,7 +63,7 @@ export class ChangePasswordForm extends PureComponent<Props, State> {
|
||||
<Button variant="primary" onClick={this.onSubmitChangePassword} disabled={isSaving}>
|
||||
Change Password
|
||||
</Button>
|
||||
<LinkButton variant="transparent" href={`${config.appSubUrl}/profile`}>
|
||||
<LinkButton variant="secondary" href={`${config.appSubUrl}/profile`}>
|
||||
Cancel
|
||||
</LinkButton>
|
||||
</div>
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import React, { FC } from 'react';
|
||||
import { Forms } from '@grafana/ui';
|
||||
import { Forms, Button, LinkButton } from '@grafana/ui';
|
||||
import { css } from 'emotion';
|
||||
|
||||
import { getConfig } from 'app/core/config';
|
||||
@@ -106,11 +106,11 @@ export const SignupForm: FC<Props> = props => {
|
||||
/>
|
||||
</Forms.Field>
|
||||
|
||||
<Forms.Button type="submit">Submit</Forms.Button>
|
||||
<Button type="submit">Submit</Button>
|
||||
<span className={buttonSpacing}>
|
||||
<Forms.LinkButton href={getConfig().appSubUrl + '/login'} variant="secondary">
|
||||
<LinkButton href={getConfig().appSubUrl + '/login'} variant="secondary">
|
||||
Back
|
||||
</Forms.LinkButton>
|
||||
</LinkButton>
|
||||
</span>
|
||||
</>
|
||||
);
|
||||
|
@@ -48,7 +48,7 @@ export class UserOrganizations extends PureComponent<Props> {
|
||||
<span className="btn btn-primary btn-small">Current</span>
|
||||
) : (
|
||||
<Button
|
||||
variant="inverse"
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
this.props.setUserOrg(org);
|
||||
|
@@ -4,7 +4,7 @@ import { connect, MapStateToProps, MapDispatchToProps } from 'react-redux';
|
||||
import { StoreState } from 'app/types';
|
||||
import { updateLocation } from 'app/core/actions';
|
||||
import { UrlQueryValue, getBackendSrv } from '@grafana/runtime';
|
||||
import { Forms } from '@grafana/ui';
|
||||
import { Forms, Button } from '@grafana/ui';
|
||||
import { useAsync } from 'react-use';
|
||||
import Page from 'app/core/components/Page/Page';
|
||||
import { contextSrv } from 'app/core/core';
|
||||
@@ -115,7 +115,7 @@ const SingupInvitedPageUnconnected: FC<DispatchProps & ConnectedProps> = ({ code
|
||||
/>
|
||||
</Forms.Field>
|
||||
|
||||
<Forms.Button type="submit">Sign Up</Forms.Button>
|
||||
<Button type="submit">Sign Up</Button>
|
||||
</>
|
||||
)}
|
||||
</Forms.Form>
|
||||
|
@@ -49,7 +49,7 @@ export const DataLink = (props: Props) => {
|
||||
onChange={handleChange('field')}
|
||||
/>
|
||||
<Button
|
||||
variant={'inverse'}
|
||||
variant={'destructive'}
|
||||
title="Remove field"
|
||||
icon={'fa fa-times'}
|
||||
onClick={event => {
|
||||
|
@@ -63,7 +63,7 @@ export const DataLinks = (props: Props) => {
|
||||
})}
|
||||
<div>
|
||||
<Button
|
||||
variant={'inverse'}
|
||||
variant={'secondary'}
|
||||
className={css`
|
||||
margin-right: 10px;
|
||||
`}
|
||||
|
@@ -66,7 +66,7 @@ export const DerivedField = (props: Props) => {
|
||||
}
|
||||
/>
|
||||
<Button
|
||||
variant={'inverse'}
|
||||
variant="destructive"
|
||||
title="Remove field"
|
||||
icon={'fa fa-times'}
|
||||
onClick={event => {
|
||||
|
@@ -66,7 +66,7 @@ export const DerivedFields = (props: Props) => {
|
||||
})}
|
||||
<div>
|
||||
<Button
|
||||
variant={'inverse'}
|
||||
variant="primary"
|
||||
className={css`
|
||||
margin-right: 10px;
|
||||
`}
|
||||
@@ -81,7 +81,7 @@ export const DerivedFields = (props: Props) => {
|
||||
</Button>
|
||||
|
||||
{value && value.length > 0 && (
|
||||
<Button variant="inverse" onClick={() => setShowDebug(!showDebug)}>
|
||||
<Button variant="secondary" onClick={() => setShowDebug(!showDebug)}>
|
||||
{showDebug ? 'Hide example log message' : 'Show example log message'}
|
||||
</Button>
|
||||
)}
|
||||
|
@@ -58,7 +58,7 @@ export class NewsPanelEditor extends PureComponent<PanelEditorProps<NewsOptions>
|
||||
<div>
|
||||
<br />
|
||||
<div>If the feed is unable to connect, consider a CORS proxy</div>
|
||||
<Button variant="inverse" onClick={this.onSetProxyPrefix}>
|
||||
<Button variant="secondary" onClick={this.onSetProxyPrefix}>
|
||||
Use Proxy
|
||||
</Button>
|
||||
</div>
|
||||
|
@@ -171,11 +171,11 @@ $table-bg-hover: $dark-6;
|
||||
|
||||
// Buttons
|
||||
// -------------------------
|
||||
$btn-secondary-bg: $blue-base;
|
||||
$btn-secondary-bg-hl: $blue-shade;
|
||||
$btn-primary-bg: $blue-base;
|
||||
$btn-primary-bg-hl: $blue-shade;
|
||||
|
||||
$btn-primary-bg: $green-base;
|
||||
$btn-primary-bg-hl: $green-shade;
|
||||
$btn-secondary-bg: $dark-6;
|
||||
$btn-secondary-bg-hl: lighten($dark-6, 4%);
|
||||
|
||||
$btn-success-bg: $green-base;
|
||||
$btn-success-bg-hl: $green-shade;
|
||||
|
@@ -163,11 +163,11 @@ $table-bg-hover: $gray-5;
|
||||
|
||||
// Buttons
|
||||
// -------------------------
|
||||
$btn-primary-bg: $green-base;
|
||||
$btn-primary-bg-hl: $green-shade;
|
||||
$btn-secondary-bg: $gray-5;
|
||||
$btn-secondary-bg-hl: $gray-4;
|
||||
|
||||
$btn-secondary-bg: $blue-base;
|
||||
$btn-secondary-bg-hl: $blue-shade;
|
||||
$btn-primary-bg: $blue-base;
|
||||
$btn-primary-bg-hl: $blue-shade;
|
||||
|
||||
$btn-success-bg: $green-base;
|
||||
$btn-success-bg-hl: $green-shade;
|
||||
@@ -176,7 +176,6 @@ $btn-danger-bg: $red-base;
|
||||
$btn-danger-bg-hl: $red-shade;
|
||||
|
||||
$btn-inverse-bg: $gray-5;
|
||||
$btn-inverse-bg-hl: darken($gray-5, 5%);
|
||||
$btn-inverse-bg-hl: $gray-4;
|
||||
$btn-inverse-text-color: $gray-1;
|
||||
$btn-inverse-text-shadow: 0 1px 0 rgba(255, 255, 255, 0.4);
|
||||
|
@@ -171,8 +171,8 @@ i.navbar-page-btn__search {
|
||||
}
|
||||
}
|
||||
|
||||
&--secondary {
|
||||
@include buttonBackground($btn-secondary-bg, $btn-secondary-bg-hl);
|
||||
&--primary {
|
||||
@include buttonBackground($btn-primary-bg, $btn-primary-bg-hl);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
|
Reference in New Issue
Block a user