IconButton: introduce variant for red and blue icon buttons (#33479)

* feat(iconbutton): introduce variant for red and blue icon buttons

* refactor(iconbutton): give variants breathing space in story

* docs(iconbutton): add docblock comment for variant prop

* refactor(iconbutton): prefer secondary to undefined variant prop and add default

* refactor(iconbutton): use icon color for hover
This commit is contained in:
Jack Westbrook 2021-04-29 10:47:06 +02:00 committed by GitHub
parent 440f4182b9
commit 0836e7bde8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 40 additions and 16 deletions

View File

@ -1,10 +1,11 @@
import React from 'react';
import { css } from '@emotion/css';
import { IconButton } from '@grafana/ui';
import { IconButton, IconButtonVariant } from './IconButton';
import { withCenteredStory } from '../../utils/storybook/withCenteredStory';
import { useTheme2 } from '../../themes';
import { IconSize, IconName } from '../../types';
import mdx from './IconButton.mdx';
import { VerticalGroup } from '../Layout/Layout';
export default {
title: 'Buttons/IconButton',
@ -35,6 +36,7 @@ const RenderScenario = ({ background }: ScenarioProps) => {
const theme = useTheme2();
const sizes: IconSize[] = ['sm', 'md', 'lg', 'xl', 'xxl'];
const icons: IconName[] = ['search', 'trash-alt', 'arrow-left', 'times'];
const variants: IconButtonVariant[] = ['secondary', 'primary', 'destructive'];
return (
<div
@ -48,14 +50,22 @@ const RenderScenario = ({ background }: ScenarioProps) => {
}
`}
>
<div>{background}</div>
{icons.map((icon) => {
return sizes.map((size) => (
<span key={icon + size}>
<IconButton name={icon} size={size} />
</span>
));
})}
<VerticalGroup spacing="md">
<div>{background}</div>
{variants.map((variant) => {
return (
<div key={variant}>
{icons.map((icon) => {
return sizes.map((size) => (
<span key={icon + size}>
<IconButton name={icon} size={size} variant={variant} />
</span>
));
})}
</div>
);
})}
</VerticalGroup>
</div>
);
};

View File

@ -4,11 +4,13 @@ import { IconName, IconSize, IconType } from '../../types/icon';
import { stylesFactory } from '../../themes/stylesFactory';
import { css, cx } from '@emotion/css';
import { useTheme2 } from '../../themes/ThemeContext';
import { GrafanaThemeV2 } from '@grafana/data';
import { GrafanaThemeV2, colorManipulator } from '@grafana/data';
import { Tooltip } from '../Tooltip/Tooltip';
import { TooltipPlacement } from '../Tooltip/PopoverController';
import { getFocusStyles, getMouseFocusStyles } from '../../themes/mixins';
export type IconButtonVariant = 'primary' | 'secondary' | 'destructive';
export interface Props extends React.ButtonHTMLAttributes<HTMLButtonElement> {
/** Name of the icon **/
name: IconName;
@ -22,14 +24,16 @@ export interface Props extends React.ButtonHTMLAttributes<HTMLButtonElement> {
tooltip?: string;
/** Position of the tooltip */
tooltipPlacement?: TooltipPlacement;
/** Variant to change the color of the Icon */
variant?: IconButtonVariant;
}
type SurfaceType = 'dashboard' | 'panel' | 'header';
export const IconButton = React.forwardRef<HTMLButtonElement, Props>(
({ name, size = 'md', iconType, tooltip, tooltipPlacement, className, ...restProps }, ref) => {
({ name, size = 'md', iconType, tooltip, tooltipPlacement, className, variant = 'secondary', ...restProps }, ref) => {
const theme = useTheme2();
const styles = getStyles(theme, size);
const styles = getStyles(theme, size, variant);
const button = (
<button ref={ref} {...restProps} className={cx(styles.button, className)}>
@ -51,10 +55,16 @@ export const IconButton = React.forwardRef<HTMLButtonElement, Props>(
IconButton.displayName = 'IconButton';
const getStyles = stylesFactory((theme: GrafanaThemeV2, size: IconSize) => {
const hoverColor = theme.colors.action.hover;
const getStyles = stylesFactory((theme: GrafanaThemeV2, size: IconSize, variant: IconButtonVariant) => {
const pixelSize = getSvgSize(size);
const hoverSize = Math.max(pixelSize / 3, 8);
let iconColor = theme.colors.text.primary;
if (variant === 'primary') {
iconColor = theme.colors.primary.main;
} else if (variant === 'destructive') {
iconColor = theme.colors.error.main;
}
return {
button: css`
@ -62,6 +72,7 @@ const getStyles = stylesFactory((theme: GrafanaThemeV2, size: IconSize) => {
height: ${pixelSize}px;
background: transparent;
border: none;
color: ${iconColor};
padding: 0;
margin: 0;
outline: none;
@ -77,6 +88,7 @@ const getStyles = stylesFactory((theme: GrafanaThemeV2, size: IconSize) => {
&[disabled],
&:disabled {
cursor: not-allowed;
color: ${theme.colors.action.disabledText};
opacity: 0.65;
box-shadow: none;
}
@ -110,10 +122,12 @@ const getStyles = stylesFactory((theme: GrafanaThemeV2, size: IconSize) => {
}
&:hover {
color: ${theme.colors.text.primary};
color: ${iconColor};
&:before {
background-color: ${hoverColor};
background-color: ${variant === 'secondary'
? theme.colors.action.hover
: colorManipulator.alpha(iconColor, 0.12)};
border: none;
box-shadow: none;
opacity: 1;