grafana/packages/grafana-ui/src/components/Button/Button.tsx

276 lines
7.9 KiB
TypeScript
Raw Normal View History

import React, { AnchorHTMLAttributes, ButtonHTMLAttributes } from 'react';
import { css, CSSObject, cx } from '@emotion/css';
import { useTheme2 } from '../../themes';
import { IconName } from '../../types/icon';
import { getPropertiesForButtonSize } from '../Forms/commonStyles';
import { colorManipulator, GrafanaTheme2, ThemeRichColor } from '@grafana/data';
import { ComponentSize } from '../../types/size';
import { getFocusStyles, getMouseFocusStyles } from '../../themes/mixins';
import { Icon } from '../Icon/Icon';
export type ButtonVariant = 'primary' | 'secondary' | 'destructive' | 'link';
export const allButtonVariants: ButtonVariant[] = ['primary', 'secondary', 'destructive'];
export type ButtonFill = 'solid' | 'outline' | 'text';
export const allButtonFills: ButtonFill[] = ['solid', 'outline', 'text'];
type CommonProps = {
size?: ComponentSize;
variant?: ButtonVariant;
fill?: ButtonFill;
@grafana/ui: Create Icon component and replace part of the icons (#23402) * Part1: Unicons implementation (#23197) * Create a new Icon component * Update icons in main sidebar * Update icons in Useful links and in react components on main site * Update icons in Useful links and in main top navigation * Adjust sizing * Update panel navigation and timepicker * Update icons in Panel menu * NewPanelEditor: Fixed so that test alert rule works in new edit mode (#23179) * Update icons in add panel widget * Resolve merge conflict * Fix part of the test errors and type errors * Fix storybook errors * Update getAvailableIcons import in storybook knobs * Fix import path * Fix SyntaxError: Cannot use import statement outside a module in test environment error * Remove dynamic imports * Remove types as using @ts-ignore * Update snapshot test * Add @iconscout/react-unicons to the shouldExclude list as it is blundled with es2015 syntax * Remove color prop from icon, remove color implemetation in mono icons * Update navbar styling * Move toPascalCase to utils/string Co-authored-by: Torkel Ödegaard <torkel@grafana.com> * Resolve type errors resulted from merge * Part2: Unicons implementation (#23266) * Create a new Icon component * Update icons in main sidebar * Update icons in Useful links and in react components on main site * Update icons in Useful links and in main top navigation * Adjust sizing * Update panel navigation and timepicker * Update icons in Panel menu * Update icons in add panel widget * Resolve merge conflict * Fix part of the test errors and type errors * Fix storybook errors * Update getAvailableIcons import in storybook knobs * Fix import path * Fix SyntaxError: Cannot use import statement outside a module in test environment error * Remove dynamic imports * Remove types as using @ts-ignore * Update snapshot test * Add @iconscout/react-unicons to the shouldExclude list as it is blundled with es2015 syntax * Implment icons in Tabs * Implement icons in search items and empty list * Update buttons * Update button-related snapshot tests * Update icons in modals and page headers * Create anfular wrapper and update all icons on search screen * Update sizing, remove colors, update snapshot tests * Remove color prop from icon, remove color implemetation in mono icons * Remove color props from monochrome icons * Complete update of icons for search screen * Update icons for infor tooltips, playlist, permissions * Support temporarly font awesome icons used in enterprise grafana * Part1: Unicons implementation (#23197) * Create a new Icon component * Update icons in main sidebar * Update icons in Useful links and in react components on main site * Update icons in Useful links and in main top navigation * Adjust sizing * Update panel navigation and timepicker * Update icons in Panel menu * NewPanelEditor: Fixed so that test alert rule works in new edit mode (#23179) * Update icons in add panel widget * Resolve merge conflict * Fix part of the test errors and type errors * Fix storybook errors * Update getAvailableIcons import in storybook knobs * Fix import path * Fix SyntaxError: Cannot use import statement outside a module in test environment error * Remove dynamic imports * Remove types as using @ts-ignore * Update snapshot test * Add @iconscout/react-unicons to the shouldExclude list as it is blundled with es2015 syntax * Remove color prop from icon, remove color implemetation in mono icons * Update navbar styling * Move toPascalCase to utils/string Co-authored-by: Torkel Ödegaard <torkel@grafana.com> * Icons update * Add optional chaining to for isFontAwesome variable Co-authored-by: Torkel Ödegaard <torkel@grafana.com> * Part3: Unicons implementation (#23356) * Create a new Icon component * Update icons in main sidebar * Update icons in Useful links and in react components on main site * Update icons in Useful links and in main top navigation * Adjust sizing * Update panel navigation and timepicker * Update icons in Panel menu * Update icons in add panel widget * Resolve merge conflict * Fix part of the test errors and type errors * Fix storybook errors * Update getAvailableIcons import in storybook knobs * Fix import path * Fix SyntaxError: Cannot use import statement outside a module in test environment error * Remove dynamic imports * Remove types as using @ts-ignore * Update snapshot test * Add @iconscout/react-unicons to the shouldExclude list as it is blundled with es2015 syntax * Implment icons in Tabs * Implement icons in search items and empty list * Update buttons * Update button-related snapshot tests * Update icons in modals and page headers * Create anfular wrapper and update all icons on search screen * Update sizing, remove colors, update snapshot tests * Remove color prop from icon, remove color implemetation in mono icons * Remove color props from monochrome icons * Complete update of icons for search screen * Update icons for infor tooltips, playlist, permissions * Support temporarly font awesome icons used in enterprise grafana * Part1: Unicons implementation (#23197) * Create a new Icon component * Update icons in main sidebar * Update icons in Useful links and in react components on main site * Update icons in Useful links and in main top navigation * Adjust sizing * Update panel navigation and timepicker * Update icons in Panel menu * NewPanelEditor: Fixed so that test alert rule works in new edit mode (#23179) * Update icons in add panel widget * Resolve merge conflict * Fix part of the test errors and type errors * Fix storybook errors * Update getAvailableIcons import in storybook knobs * Fix import path * Fix SyntaxError: Cannot use import statement outside a module in test environment error * Remove dynamic imports * Remove types as using @ts-ignore * Update snapshot test * Add @iconscout/react-unicons to the shouldExclude list as it is blundled with es2015 syntax * Remove color prop from icon, remove color implemetation in mono icons * Update navbar styling * Move toPascalCase to utils/string Co-authored-by: Torkel Ödegaard <torkel@grafana.com> * Update icons in Explore * Update icons in alerting * Update + and x buttons * Update icons in configurations and settings * Update close icons * Update icons in rich history * Update alert messages * Add optional chaining to for isFontAwesome variable * Remove icon mock, set up jest.config * Fix navbar plus icon * Fir enable-bacground to enableBackgournd Co-authored-by: Torkel Ödegaard <torkel@grafana.com> * Merge remote branch origin master to icons-unicons * Revert "Merge remote branch origin master to icons-unicons" This reverts commit 3f25d50a39a940883fefe73ce51219139c1ed37f. * Size-up dashnav icons * Fix alerting icons, panel headers, update tests * Fix typecheck error * Adjustments - add panel icon, spacing * Set TerserPlugin sourceMap to false to prevent running out of memory when publishing storybook Co-authored-by: Torkel Ödegaard <torkel@grafana.com>
2020-04-08 20:33:31 +08:00
icon?: IconName;
className?: string;
children?: React.ReactNode;
fullWidth?: boolean;
};
export type ButtonProps = CommonProps & ButtonHTMLAttributes<HTMLButtonElement>;
2020-01-07 16:20:06 +08:00
export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ variant = 'primary', size = 'md', fill = 'solid', icon, fullWidth, children, className, ...otherProps }, ref) => {
const theme = useTheme2();
const styles = getButtonStyles({
theme,
size,
variant,
fill,
fullWidth,
iconOnly: !children,
});
deprecatedPropWarning(
variant === 'link',
`${Button.displayName}: Prop variant="link" is deprecated. Please use fill="text".`
);
return (
<button className={cx(styles.button, className)} {...otherProps} ref={ref}>
{icon && <Icon name={icon} size={size} className={styles.icon} />}
{children && <span className={styles.content}>{children}</span>}
</button>
);
}
);
2020-01-07 16:20:06 +08:00
Button.displayName = 'Button';
type ButtonLinkProps = CommonProps & ButtonHTMLAttributes<HTMLButtonElement> & AnchorHTMLAttributes<HTMLAnchorElement>;
export const LinkButton = React.forwardRef<HTMLAnchorElement, ButtonLinkProps>(
(
{
variant = 'primary',
size = 'md',
fill = 'solid',
icon,
fullWidth,
children,
className,
onBlur,
onFocus,
disabled,
...otherProps
},
ref
) => {
const theme = useTheme2();
const styles = getButtonStyles({
theme,
fullWidth,
size,
variant,
fill,
iconOnly: !children,
});
const linkButtonStyles = cx(styles.button, { [styles.disabled]: disabled }, className);
deprecatedPropWarning(
variant === 'link',
`${LinkButton.displayName}: Prop variant="link" is deprecated. Please use fill="text".`
);
return (
<a className={linkButtonStyles} {...otherProps} tabIndex={disabled ? -1 : 0} ref={ref}>
{icon && <Icon name={icon} size={size} className={styles.icon} />}
{children && <span className={styles.content}>{children}</span>}
</a>
);
}
);
LinkButton.displayName = 'LinkButton';
export interface StyleProps {
size: ComponentSize;
variant: ButtonVariant;
fill?: ButtonFill;
iconOnly?: boolean;
theme: GrafanaTheme2;
fullWidth?: boolean;
narrow?: boolean;
}
export const getButtonStyles = (props: StyleProps) => {
const { theme, variant, fill = 'solid', size, iconOnly, fullWidth } = props;
const { height, padding, fontSize } = getPropertiesForButtonSize(size, theme);
const variantStyles = getPropertiesForVariant(theme, variant, fill);
const disabledStyles = getPropertiesForDisabled(theme, variant, fill);
const focusStyle = getFocusStyles(theme);
return {
button: css({
label: 'button',
display: 'inline-flex',
alignItems: 'center',
fontSize: fontSize,
fontWeight: theme.typography.fontWeightMedium,
fontFamily: theme.typography.fontFamily,
padding: theme.spacing(0, padding),
height: theme.spacing(height),
// Deduct border from line-height for perfect vertical centering on windows and linux
lineHeight: `${theme.spacing.gridSize * height - 2}px`,
verticalAlign: 'middle',
cursor: 'pointer',
borderRadius: theme.shape.borderRadius(1),
'&:focus': focusStyle,
'&:focus-visible': focusStyle,
'&:focus:not(:focus-visible)': getMouseFocusStyles(theme),
...(fullWidth && {
flexGrow: 1,
justifyContent: 'center',
}),
...variantStyles,
':disabled': disabledStyles,
'&[disabled]': disabledStyles,
}),
disabled: css(disabledStyles),
img: css`
width: 16px;
height: 16px;
margin: ${theme.spacing(0, 1, 0, 0.5)};
`,
icon: css`
ValueMapping: Support for mapping text to color, boolean values, NaN and Null. Improved UI for value mapping. (#33820) * alternative mapping editor * alternative mapping editor * values updating * UI updates * remove empty operators * fix types * horizontal * New value mapping model and migration * DataSource: show the uid in edit url, not the local id (#33818) * update mapping model object * Update to UI * fixing ts issues * Editing starting to work * adding missing thing * Update display processor to use color from value mapping * Range maps now work * Working on unit tests for modal editor * Updated * Adding new NullToText mapping type * Added null to text UI * add color from old threshold config * Added migration for overrides, added Type column * Added compact view model with color edit capability * [Alerting]: store encrypted receiver secure settings (#33832) * [Alerting]: Store secure settings encrypted * Move encryption to the API handler * CloudMonitoring: Migrate config editor from angular to react (#33645) * fix broken config ctrl * replace angular config with react config editor * remove not used code * add extra linebreak * add noopener to link * only test jwt props that we actually need * Elasticsearch: automatically set date_histogram field based on data source configuration (#33840) * Docs: delete from high availability docs references to removed configurations related to session storage (#33827) * docs: delete from high availability docs references to removed configurations related to session storage * docs: remove session storage mention and focus on the auth token implementation * fix postgres to have precision of ms (#33853) * Use ids for enterprise nav model items (#33854) * Alerting: Disable dash alerting if NG enabled (#33794) * Scuemata: Add grafana-cli cue schema validation to CI (#33798) * Add scuemata validation in CI * Fixes according to reviewer's comments * Ensure http client has no timeout (#33856) * Redact sensitive values before logging them (#33829) * use a common way to redact sensitive values before logging them * fix panic on missing testCase.err, simplify require checks * fix a silly typo * combine readConfig and buildConnectionString methods, as they are closely related * Tempo: Search for Traces by querying Loki directly from Tempo (#33308) * Loki query from Tempo UI - add query type selector to tempo - introduce linkedDatasource concept that runs queries on behalf of another datasource - Tempo uses Loki's query field and Loki's derived fields to find a trace matcher - Tempo uses the trace-to-logs mechanism to determine which dataource is linked Loki data loads successfully via tempo Extracted result transformers Skip null values Show trace on list id click Query type selector Use linked field trace regexp * Review feedback * Add isolation level db configuration parameter (#33830) * add isolation level db configuration parameter * add isolation_level to default.ini and sample.ini * add note that only mysql supports isolation levels for now * mention isolation_level in the documentation * Update docs/sources/administration/configuration.md Co-authored-by: achatterjee-grafana <70489351+achatterjee-grafana@users.noreply.github.com> Co-authored-by: achatterjee-grafana <70489351+achatterjee-grafana@users.noreply.github.com> * Drawer: fixes title overflowing its container (#33857) * Timeline: move grafana/ui elements to the panel folder (#33803) * revendor loki with new Tripperware (#33858) * live: move connection endpoint to api scope, fixes #33861 (#33863) * OAuth: Add support for empty scopes (#32129) * add parameter empty_scopes to override scope parameter with empty value and thus be able to authenticate against IdPs without scopes. Issue #27503 Update docs/sources/auth/generic-oauth.md Co-authored-by: achatterjee-grafana <70489351+achatterjee-grafana@users.noreply.github.com> * updated check according to feedback * Update generic-oauth.md Co-authored-by: achatterjee-grafana <70489351+achatterjee-grafana@users.noreply.github.com> * Prometheus: Fix exemplars hover disappearing and broken link (#33866) * Revert "Tooltip: eliminate flickering when repaint can't keep up (#33609)" This reverts commit e159985aa2907e2c2889853f9295183edc1032ac. * Fix exemplar linking Co-authored-by: David Kaltschmidt <david.kaltschmidt@gmail.com> * Removed content as per MarcusE's suggestion in https://github.com/grafana/grafana/issues/33822. (#33870) * Fixed grammar usage. (#33871) * Explore: Wrap each panel in separate error boundary (#33868) * New Panel: Histogram (#33752) * Sanitize PromLink button (#33874) * Refactor and unify option creation between new visualizations (#33867) * Refactor and unify option creation between new visualizations * move to grafana/ui * move to grafana/ui * resolve duplicate scale config * more imports Co-authored-by: Ryan McKinley <ryantxu@gmail.com> * Live: do not show connection warning when on the login page (#33865) * enforce receivers align with backend type when posting AM config (#33877) * special values * merge fix * Document `hide_version` flag (#33670) Unauthenticated users can be barred from being shown the current Grafana server version since https://github.com/grafana/grafana/pull/24919 * GraphNG: always use "x" as scaleKey for x axis (#33884) * Timeline: add support for strings & booleans (#33882) * Chore(deps): Bump hosted-git-info from 2.8.5 to 2.8.9 (#33886) Bumps [hosted-git-info](https://github.com/npm/hosted-git-info) from 2.8.5 to 2.8.9. - [Release notes](https://github.com/npm/hosted-git-info/releases) - [Changelog](https://github.com/npm/hosted-git-info/blob/v2.8.9/CHANGELOG.md) - [Commits](https://github.com/npm/hosted-git-info/compare/v2.8.5...v2.8.9) Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * merge with torkel * add empty special character * Fixed centered text in special value match select * fixed unit tests * Updated snapshot * Update dashboard page * updated snapshot * Fix more unit tests * Fixed test * Updates * Added back tests * Fixed doc issue Co-authored-by: Ryan McKinley <ryantxu@gmail.com> Co-authored-by: Dominik Prokop <dominik.prokop@grafana.com> Co-authored-by: Sofia Papagiannaki <papagian@users.noreply.github.com> Co-authored-by: Erik Sundell <erik.sundell@grafana.com> Co-authored-by: Giordano Ricci <me@giordanoricci.com> Co-authored-by: Daniel dos Santos Pereira <danield1591998@gmail.com> Co-authored-by: ying-jeanne <74549700+ying-jeanne@users.noreply.github.com> Co-authored-by: Alex Khomenko <Clarity-89@users.noreply.github.com> Co-authored-by: Kyle Brandt <kyle@grafana.com> Co-authored-by: Dimitris Sotirakis <dimitrios.sotirakis@grafana.com> Co-authored-by: Will Browne <wbrowne@users.noreply.github.com> Co-authored-by: Serge Zaitsev <serge.zaitsev@grafana.com> Co-authored-by: David <david.kaltschmidt@gmail.com> Co-authored-by: achatterjee-grafana <70489351+achatterjee-grafana@users.noreply.github.com> Co-authored-by: Uchechukwu Obasi <obasiuche62@gmail.com> Co-authored-by: Owen Diehl <ow.diehl@gmail.com> Co-authored-by: Alexander Emelin <frvzmb@gmail.com> Co-authored-by: jvoeller <48791711+jvoeller@users.noreply.github.com> Co-authored-by: Zoltán Bedi <zoltan.bedi@gmail.com> Co-authored-by: Andrej Ocenas <mr.ocenas@gmail.com> Co-authored-by: Leon Sorokin <leeoniya@gmail.com> Co-authored-by: Ivana Huckova <30407135+ivanahuckova@users.noreply.github.com> Co-authored-by: Oscar Kilhed <oscar.kilhed@grafana.com> Co-authored-by: Tristan Deloche <tde@hey.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-11 23:10:23 +08:00
margin-right: ${theme.spacing((iconOnly ? -padding : padding) / 2)};
margin-left: ${theme.spacing(-padding / 2)};
`,
content: css`
display: flex;
flex-direction: row;
align-items: center;
white-space: nowrap;
height: 100%;
`,
};
};
function getButtonVariantStyles(theme: GrafanaTheme2, color: ThemeRichColor, fill: ButtonFill): CSSObject {
if (fill === 'outline') {
return {
background: 'transparent',
color: color.text,
border: `1px solid ${color.border}`,
transition: theme.transitions.create(['background-color', 'border-color', 'color'], {
duration: theme.transitions.duration.short,
}),
'&:hover': {
background: colorManipulator.alpha(color.main, theme.colors.action.hoverOpacity),
borderColor: theme.colors.emphasize(color.border, 0.25),
color: color.text,
},
};
}
if (fill === 'text') {
return {
background: 'transparent',
color: color.text,
border: '1px solid transparent',
transition: theme.transitions.create(['background-color', 'color'], {
duration: theme.transitions.duration.short,
}),
'&:focus': {
outline: 'none',
textDecoration: 'none',
},
'&:hover': {
background: colorManipulator.alpha(color.shade, theme.colors.action.hoverOpacity),
textDecoration: 'none',
},
};
}
return {
background: color.main,
color: color.contrastText,
border: `1px solid transparent`,
transition: theme.transitions.create(['background-color', 'box-shadow', 'border-color', 'color'], {
duration: theme.transitions.duration.short,
}),
'&:hover': {
background: color.shade,
color: color.contrastText,
boxShadow: theme.shadows.z1,
},
};
}
function getPropertiesForDisabled(theme: GrafanaTheme2, variant: ButtonVariant, fill: ButtonFill) {
const disabledStyles: CSSObject = {
cursor: 'not-allowed',
boxShadow: 'none',
pointerEvents: 'none',
color: theme.colors.text.disabled,
transition: 'none',
};
if (fill === 'text' || variant === 'link') {
return {
...disabledStyles,
background: 'transparent',
border: `1px solid transparent`,
};
}
if (fill === 'outline') {
return {
...disabledStyles,
background: 'transparent',
border: `1px solid ${theme.colors.action.disabledText}`,
};
}
return {
...disabledStyles,
background: theme.colors.action.disabledBackground,
border: `1px solid transparent`,
};
}
export function getPropertiesForVariant(theme: GrafanaTheme2, variant: ButtonVariant, fill: ButtonFill) {
const buttonVariant = variant === 'link' ? 'primary' : variant;
const buttonFill = variant === 'link' ? 'text' : fill;
switch (buttonVariant) {
case 'secondary':
return getButtonVariantStyles(theme, theme.colors.secondary, buttonFill);
case 'destructive':
return getButtonVariantStyles(theme, theme.colors.error, buttonFill);
case 'primary':
default:
return getButtonVariantStyles(theme, theme.colors.primary, buttonFill);
}
}
function deprecatedPropWarning(test: boolean, message: string) {
if (process.env.NODE_ENV === 'development' && test) {
console.warn(`@grafana/ui ${message}`);
}
}