mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Refactor buttons (#17611)
This commit is contained in:
parent
8192fa538e
commit
6de75de755
@ -4,21 +4,9 @@ import { css, cx } from 'emotion';
|
|||||||
import { Themeable, GrafanaTheme } from '../../types';
|
import { Themeable, GrafanaTheme } from '../../types';
|
||||||
import { selectThemeVariant } from '../../themes/selectThemeVariant';
|
import { selectThemeVariant } from '../../themes/selectThemeVariant';
|
||||||
|
|
||||||
export enum ButtonVariant {
|
export type ButtonVariant = 'primary' | 'secondary' | 'danger' | 'inverse' | 'transparent';
|
||||||
Primary = 'primary',
|
|
||||||
Secondary = 'secondary',
|
|
||||||
Danger = 'danger',
|
|
||||||
Inverse = 'inverse',
|
|
||||||
Transparent = 'transparent',
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum ButtonSize {
|
export type ButtonSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl';
|
||||||
ExtraSmall = 'xs',
|
|
||||||
Small = 'sm',
|
|
||||||
Medium = 'md',
|
|
||||||
Large = 'lg',
|
|
||||||
ExtraLarge = 'xl',
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface CommonButtonProps {
|
export interface CommonButtonProps {
|
||||||
size?: ButtonSize;
|
size?: ButtonSize;
|
||||||
@ -69,19 +57,19 @@ const getButtonStyles = (theme: GrafanaTheme, size: ButtonSize, variant: ButtonV
|
|||||||
fontWeight = theme.typography.weight.semibold;
|
fontWeight = theme.typography.weight.semibold;
|
||||||
|
|
||||||
switch (size) {
|
switch (size) {
|
||||||
case ButtonSize.Small:
|
case 'sm':
|
||||||
padding = `${theme.spacing.xs} ${theme.spacing.sm}`;
|
padding = `${theme.spacing.xs} ${theme.spacing.sm}`;
|
||||||
fontSize = theme.typography.size.sm;
|
fontSize = theme.typography.size.sm;
|
||||||
iconDistance = theme.spacing.xs;
|
iconDistance = theme.spacing.xs;
|
||||||
height = theme.height.sm;
|
height = theme.height.sm;
|
||||||
break;
|
break;
|
||||||
case ButtonSize.Medium:
|
case 'md':
|
||||||
padding = `${theme.spacing.sm} ${theme.spacing.md}`;
|
padding = `${theme.spacing.sm} ${theme.spacing.md}`;
|
||||||
fontSize = theme.typography.size.md;
|
fontSize = theme.typography.size.md;
|
||||||
iconDistance = theme.spacing.sm;
|
iconDistance = theme.spacing.sm;
|
||||||
height = theme.height.md;
|
height = theme.height.md;
|
||||||
break;
|
break;
|
||||||
case ButtonSize.Large:
|
case 'lg':
|
||||||
padding = `${theme.spacing.md} ${theme.spacing.lg}`;
|
padding = `${theme.spacing.md} ${theme.spacing.lg}`;
|
||||||
fontSize = theme.typography.size.lg;
|
fontSize = theme.typography.size.lg;
|
||||||
fontWeight = theme.typography.weight.regular;
|
fontWeight = theme.typography.weight.regular;
|
||||||
@ -96,16 +84,16 @@ const getButtonStyles = (theme: GrafanaTheme, size: ButtonSize, variant: ButtonV
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch (variant) {
|
switch (variant) {
|
||||||
case ButtonVariant.Primary:
|
case 'primary':
|
||||||
background = buttonVariantStyles(theme.colors.greenBase, theme.colors.greenShade, theme.colors.white);
|
background = buttonVariantStyles(theme.colors.greenBase, theme.colors.greenShade, theme.colors.white);
|
||||||
break;
|
break;
|
||||||
case ButtonVariant.Secondary:
|
case 'secondary':
|
||||||
background = buttonVariantStyles(theme.colors.blueBase, theme.colors.blueShade, theme.colors.white);
|
background = buttonVariantStyles(theme.colors.blueBase, theme.colors.blueShade, theme.colors.white);
|
||||||
break;
|
break;
|
||||||
case ButtonVariant.Danger:
|
case 'danger':
|
||||||
background = buttonVariantStyles(theme.colors.redBase, theme.colors.redShade, theme.colors.white);
|
background = buttonVariantStyles(theme.colors.redBase, theme.colors.redShade, theme.colors.white);
|
||||||
break;
|
break;
|
||||||
case ButtonVariant.Inverse:
|
case 'inverse':
|
||||||
const from = selectThemeVariant({ light: theme.colors.gray5, dark: theme.colors.dark6 }, theme.type) as string;
|
const from = selectThemeVariant({ light: theme.colors.gray5, dark: theme.colors.dark6 }, theme.type) as string;
|
||||||
const to = selectThemeVariant(
|
const to = selectThemeVariant(
|
||||||
{
|
{
|
||||||
@ -121,7 +109,7 @@ const getButtonStyles = (theme: GrafanaTheme, size: ButtonSize, variant: ButtonV
|
|||||||
|
|
||||||
background = buttonVariantStyles(from, to, theme.colors.link, 'rgba(0, 0, 0, 0.1)', true);
|
background = buttonVariantStyles(from, to, theme.colors.link, 'rgba(0, 0, 0, 0.1)', true);
|
||||||
break;
|
break;
|
||||||
case ButtonVariant.Transparent:
|
case 'transparent':
|
||||||
background = css`
|
background = css`
|
||||||
${buttonVariantStyles('', '', theme.colors.link, 'rgba(0, 0, 0, 0.1)', true)};
|
${buttonVariantStyles('', '', theme.colors.link, 'rgba(0, 0, 0, 0.1)', true)};
|
||||||
background: transparent;
|
background: transparent;
|
||||||
@ -170,8 +158,8 @@ const getButtonStyles = (theme: GrafanaTheme, size: ButtonSize, variant: ButtonV
|
|||||||
export const AbstractButton: React.FunctionComponent<AbstractButtonProps> = ({
|
export const AbstractButton: React.FunctionComponent<AbstractButtonProps> = ({
|
||||||
renderAs,
|
renderAs,
|
||||||
theme,
|
theme,
|
||||||
size = ButtonSize.Medium,
|
size = 'md',
|
||||||
variant = ButtonVariant.Primary,
|
variant = 'primary',
|
||||||
className,
|
className,
|
||||||
icon,
|
icon,
|
||||||
children,
|
children,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { storiesOf } from '@storybook/react';
|
import { storiesOf } from '@storybook/react';
|
||||||
import { Button, LinkButton } from './Button';
|
import { Button, LinkButton } from './Button';
|
||||||
import { ButtonSize, ButtonVariant, CommonButtonProps } from './AbstractButton';
|
import { CommonButtonProps } from './AbstractButton';
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import withPropsCombinations from 'react-storybook-addon-props-combinations';
|
import withPropsCombinations from 'react-storybook-addon-props-combinations';
|
||||||
import { action } from '@storybook/addon-actions';
|
import { action } from '@storybook/addon-actions';
|
||||||
@ -15,14 +15,8 @@ const defaultProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const variants = {
|
const variants = {
|
||||||
size: [ButtonSize.ExtraSmall, ButtonSize.Small, ButtonSize.Medium, ButtonSize.Large, ButtonSize.ExtraLarge],
|
size: ['xs', 'sm', 'md', 'lg', 'xl'],
|
||||||
variant: [
|
variant: ['primary', 'secondary', 'danger', 'inverse', 'transparent'],
|
||||||
ButtonVariant.Primary,
|
|
||||||
ButtonVariant.Secondary,
|
|
||||||
ButtonVariant.Danger,
|
|
||||||
ButtonVariant.Inverse,
|
|
||||||
ButtonVariant.Transparent,
|
|
||||||
],
|
|
||||||
};
|
};
|
||||||
const combinationOptions = {
|
const combinationOptions = {
|
||||||
CombinationRenderer: ThemeableCombinationsRowRenderer,
|
CombinationRenderer: ThemeableCombinationsRowRenderer,
|
||||||
|
@ -1,36 +1,7 @@
|
|||||||
import React, { useContext } from 'react';
|
import React, { useContext } from 'react';
|
||||||
import { AbstractButton, ButtonProps, ButtonSize, LinkButtonProps } from './AbstractButton';
|
import { AbstractButton, ButtonProps, LinkButtonProps } from './AbstractButton';
|
||||||
import { ThemeContext } from '../../themes';
|
import { ThemeContext } from '../../themes';
|
||||||
|
|
||||||
const getSizeNameComponentSegment = (size: ButtonSize) => {
|
|
||||||
switch (size) {
|
|
||||||
case ButtonSize.ExtraSmall:
|
|
||||||
return 'ExtraSmall';
|
|
||||||
case ButtonSize.Small:
|
|
||||||
return 'Small';
|
|
||||||
case ButtonSize.Large:
|
|
||||||
return 'Large';
|
|
||||||
case ButtonSize.ExtraLarge:
|
|
||||||
return 'ExtraLarge';
|
|
||||||
default:
|
|
||||||
return 'Medium';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const buttonFactory: <T>(renderAs: string, size: ButtonSize, displayName: string) => React.ComponentType<T> = (
|
|
||||||
renderAs,
|
|
||||||
size,
|
|
||||||
displayName
|
|
||||||
) => {
|
|
||||||
const ButtonComponent: React.FunctionComponent<any> = props => {
|
|
||||||
const theme = useContext(ThemeContext);
|
|
||||||
return <AbstractButton {...props} size={size} renderAs={renderAs} theme={theme} />;
|
|
||||||
};
|
|
||||||
ButtonComponent.displayName = displayName;
|
|
||||||
|
|
||||||
return ButtonComponent;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const Button: React.FunctionComponent<ButtonProps> = props => {
|
export const Button: React.FunctionComponent<ButtonProps> = props => {
|
||||||
const theme = useContext(ThemeContext);
|
const theme = useContext(ThemeContext);
|
||||||
return <AbstractButton {...props} renderAs="button" theme={theme} />;
|
return <AbstractButton {...props} renderAs="button" theme={theme} />;
|
||||||
@ -42,45 +13,3 @@ export const LinkButton: React.FunctionComponent<LinkButtonProps> = props => {
|
|||||||
return <AbstractButton {...props} renderAs="a" theme={theme} />;
|
return <AbstractButton {...props} renderAs="a" theme={theme} />;
|
||||||
};
|
};
|
||||||
LinkButton.displayName = 'LinkButton';
|
LinkButton.displayName = 'LinkButton';
|
||||||
|
|
||||||
export const ExtraSmallButton = buttonFactory<ButtonProps>(
|
|
||||||
'button',
|
|
||||||
ButtonSize.ExtraSmall,
|
|
||||||
`${getSizeNameComponentSegment(ButtonSize.ExtraSmall)}Button`
|
|
||||||
);
|
|
||||||
export const SmallButton = buttonFactory<ButtonProps>(
|
|
||||||
'button',
|
|
||||||
ButtonSize.Small,
|
|
||||||
`${getSizeNameComponentSegment(ButtonSize.Small)}Button`
|
|
||||||
);
|
|
||||||
export const LargeButton = buttonFactory<ButtonProps>(
|
|
||||||
'button',
|
|
||||||
ButtonSize.Large,
|
|
||||||
`${getSizeNameComponentSegment(ButtonSize.Large)}Button`
|
|
||||||
);
|
|
||||||
export const ExtraLargeButton = buttonFactory<ButtonProps>(
|
|
||||||
'button',
|
|
||||||
ButtonSize.ExtraLarge,
|
|
||||||
`${getSizeNameComponentSegment(ButtonSize.ExtraLarge)}Button`
|
|
||||||
);
|
|
||||||
|
|
||||||
export const ExtraSmallLinkButton = buttonFactory<LinkButtonProps>(
|
|
||||||
'a',
|
|
||||||
ButtonSize.ExtraSmall,
|
|
||||||
`${getSizeNameComponentSegment(ButtonSize.ExtraSmall)}LinkButton`
|
|
||||||
);
|
|
||||||
export const SmallLinkButton = buttonFactory<LinkButtonProps>(
|
|
||||||
'a',
|
|
||||||
ButtonSize.Small,
|
|
||||||
`${getSizeNameComponentSegment(ButtonSize.Small)}LinkButton`
|
|
||||||
);
|
|
||||||
export const LargeLinkButton = buttonFactory<LinkButtonProps>(
|
|
||||||
'a',
|
|
||||||
ButtonSize.Large,
|
|
||||||
`${getSizeNameComponentSegment(ButtonSize.Large)}LinkButton`
|
|
||||||
);
|
|
||||||
export const ExtraLargeLinkButton = buttonFactory<LinkButtonProps>(
|
|
||||||
'a',
|
|
||||||
ButtonSize.ExtraLarge,
|
|
||||||
`${getSizeNameComponentSegment(ButtonSize.ExtraLarge)}LinkButton`
|
|
||||||
);
|
|
||||||
|
@ -3,7 +3,7 @@ import { storiesOf } from '@storybook/react';
|
|||||||
import { renderComponentWithTheme } from '../../utils/storybook/withTheme';
|
import { renderComponentWithTheme } from '../../utils/storybook/withTheme';
|
||||||
import { CallToActionCard } from './CallToActionCard';
|
import { CallToActionCard } from './CallToActionCard';
|
||||||
import { select, text } from '@storybook/addon-knobs';
|
import { select, text } from '@storybook/addon-knobs';
|
||||||
import { LargeButton } from '../Button/Button';
|
import { Button } from '../Button/Button';
|
||||||
import { action } from '@storybook/addon-actions';
|
import { action } from '@storybook/addon-actions';
|
||||||
|
|
||||||
const CallToActionCardStories = storiesOf('UI/CallToActionCard', module);
|
const CallToActionCardStories = storiesOf('UI/CallToActionCard', module);
|
||||||
@ -12,9 +12,9 @@ CallToActionCardStories.add('default', () => {
|
|||||||
const ctaElements: { [key: string]: JSX.Element } = {
|
const ctaElements: { [key: string]: JSX.Element } = {
|
||||||
custom: <h1>This is just H1 tag, you can any component as CTA element</h1>,
|
custom: <h1>This is just H1 tag, you can any component as CTA element</h1>,
|
||||||
button: (
|
button: (
|
||||||
<LargeButton icon="fa fa-plus" onClick={action('cta button clicked')}>
|
<Button size="lg" icon="fa fa-plus" onClick={action('cta button clicked')}>
|
||||||
Add datasource
|
Add datasource
|
||||||
</LargeButton>
|
</Button>
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
const ctaElement = select(
|
const ctaElement = select(
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import React, { useContext } from 'react';
|
import React, { useContext } from 'react';
|
||||||
import { CallToActionCard, LargeLinkButton, ThemeContext } from '@grafana/ui';
|
import { CallToActionCard, LinkButton, ThemeContext } from '@grafana/ui';
|
||||||
import { css } from 'emotion';
|
import { css } from 'emotion';
|
||||||
export interface Props {
|
export interface Props {
|
||||||
model: any;
|
model: any;
|
||||||
@ -37,9 +37,9 @@ const EmptyListCTA: React.FunctionComponent<Props> = props => {
|
|||||||
: '';
|
: '';
|
||||||
|
|
||||||
const ctaElement = (
|
const ctaElement = (
|
||||||
<LargeLinkButton onClick={onClick} href={buttonLink} icon={buttonIcon} className={ctaElementClassName}>
|
<LinkButton size="lg" onClick={onClick} href={buttonLink} icon={buttonIcon} className={ctaElementClassName}>
|
||||||
{buttonTitle}
|
{buttonTitle}
|
||||||
</LargeLinkButton>
|
</LinkButton>
|
||||||
);
|
);
|
||||||
|
|
||||||
return <CallToActionCard message={title} footer={footer} callToActionElement={ctaElement} theme={theme} />;
|
return <CallToActionCard message={title} footer={footer} callToActionElement={ctaElement} theme={theme} />;
|
||||||
|
@ -11,7 +11,6 @@ import {
|
|||||||
} from '@grafana/ui';
|
} from '@grafana/ui';
|
||||||
|
|
||||||
import ElapsedTime from './ElapsedTime';
|
import ElapsedTime from './ElapsedTime';
|
||||||
import { ButtonSize, ButtonVariant } from '@grafana/ui/src/components/Button/AbstractButton';
|
|
||||||
|
|
||||||
const getStyles = (theme: GrafanaTheme) => ({
|
const getStyles = (theme: GrafanaTheme) => ({
|
||||||
logsRowsLive: css`
|
logsRowsLive: css`
|
||||||
@ -110,8 +109,8 @@ class LiveLogs extends PureComponent<Props, State> {
|
|||||||
</span>
|
</span>
|
||||||
<LinkButton
|
<LinkButton
|
||||||
onClick={this.props.stopLive}
|
onClick={this.props.stopLive}
|
||||||
size={ButtonSize.Medium}
|
size="md"
|
||||||
variant={ButtonVariant.Transparent}
|
variant="transparent"
|
||||||
style={{ color: theme.colors.orange }}
|
style={{ color: theme.colors.orange }}
|
||||||
>
|
>
|
||||||
Stop Live
|
Stop Live
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import React, { useContext } from 'react';
|
import React, { useContext } from 'react';
|
||||||
import { css } from 'emotion';
|
import { css } from 'emotion';
|
||||||
import { ThemeContext, LargeLinkButton, CallToActionCard } from '@grafana/ui';
|
import { ThemeContext, LinkButton, CallToActionCard } from '@grafana/ui';
|
||||||
|
|
||||||
export const NoDataSourceCallToAction = () => {
|
export const NoDataSourceCallToAction = () => {
|
||||||
const theme = useContext(ThemeContext);
|
const theme = useContext(ThemeContext);
|
||||||
@ -22,9 +22,9 @@ export const NoDataSourceCallToAction = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const ctaElement = (
|
const ctaElement = (
|
||||||
<LargeLinkButton href="/datasources/new" icon="gicon gicon-datasources">
|
<LinkButton size="lg" href="/datasources/new" icon="gicon gicon-datasources">
|
||||||
Add data source
|
Add data source
|
||||||
</LargeLinkButton>
|
</LinkButton>
|
||||||
);
|
);
|
||||||
|
|
||||||
const cardClassName = css`
|
const cardClassName = css`
|
||||||
|
@ -7,7 +7,6 @@ import { PluginMeta, AppPlugin, Button } from '@grafana/ui';
|
|||||||
|
|
||||||
import { AngularComponent, getAngularLoader } from '@grafana/runtime';
|
import { AngularComponent, getAngularLoader } from '@grafana/runtime';
|
||||||
import { getBackendSrv } from 'app/core/services/backend_srv';
|
import { getBackendSrv } from 'app/core/services/backend_srv';
|
||||||
import { ButtonVariant } from '@grafana/ui/src/components/Button/AbstractButton';
|
|
||||||
import { css } from 'emotion';
|
import { css } from 'emotion';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@ -71,17 +70,17 @@ export class AppConfigCtrlWrapper extends PureComponent<Props, State> {
|
|||||||
{model && (
|
{model && (
|
||||||
<div className="gf-form">
|
<div className="gf-form">
|
||||||
{!model.enabled && (
|
{!model.enabled && (
|
||||||
<Button variant={ButtonVariant.Primary} onClick={this.enable} className={withRightMargin}>
|
<Button variant="primary" onClick={this.enable} className={withRightMargin}>
|
||||||
Enable
|
Enable
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
{model.enabled && (
|
{model.enabled && (
|
||||||
<Button variant={ButtonVariant.Primary} onClick={this.update} className={withRightMargin}>
|
<Button variant="primary" onClick={this.update} className={withRightMargin}>
|
||||||
Update
|
Update
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
{model.enabled && (
|
{model.enabled && (
|
||||||
<Button variant={ButtonVariant.Danger} onClick={this.disable} className={withRightMargin}>
|
<Button variant="danger" onClick={this.disable} className={withRightMargin}>
|
||||||
Disable
|
Disable
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
|
Loading…
Reference in New Issue
Block a user