From 24183ba390ab224abddaea679bba822d82811366 Mon Sep 17 00:00:00 2001 From: Peter Holmberg Date: Fri, 25 Oct 2019 14:35:29 +0200 Subject: [PATCH] Forms: Introduce new Primary, Secondary and Destructive buttons (#19973) * Implement Label component * Expose next-gen form components from grafana-ui under Forms namespace * adding knobs to story, setting new variants and sizes * handle next gen button in their own component * removing duplication * fix Thresholds test * removing blank lines * moving noUnusedLocals * new button should not need theme * remove not used export * pseudo classes for focus state * remove not used things * use correct border radius * extracting focus styles to commonStyles for reuse * tidying up getButtonStyles * Adding a few examples to the doc * Adding props table --- package.json | 2 +- packages/grafana-ui/.storybook/tsconfig.json | 11 ++ .../grafana-ui/.storybook/webpack.config.js | 4 +- .../src/components/Button/AbstractButton.tsx | 83 ++++----- .../src/components/Button/Button.story.tsx | 4 +- .../src/components/Button/Button.tsx | 3 +- .../grafana-ui/src/components/Button/types.ts | 39 +++++ .../src/components/Forms/Button.mdx | 63 +++++++ .../src/components/Forms/Button.story.tsx | 33 ++++ .../src/components/Forms/Button.tsx | 158 ++++++++++++++++++ .../src/components/Forms/Label.story.tsx | 1 + .../src/components/Forms/commonStyles.ts | 20 +++ .../src/components/Forms/getFormStyles.ts | 4 +- .../ThresholdsEditor.test.tsx.snap | 4 + packages/grafana-ui/src/components/index.ts | 1 - packages/grafana-ui/src/themes/default.ts | 2 + packages/grafana-ui/src/types/theme.ts | 2 + 17 files changed, 380 insertions(+), 54 deletions(-) create mode 100644 packages/grafana-ui/.storybook/tsconfig.json create mode 100644 packages/grafana-ui/src/components/Button/types.ts create mode 100644 packages/grafana-ui/src/components/Forms/Button.mdx create mode 100644 packages/grafana-ui/src/components/Forms/Button.story.tsx create mode 100644 packages/grafana-ui/src/components/Forms/Button.tsx create mode 100644 packages/grafana-ui/src/components/Forms/commonStyles.ts diff --git a/package.json b/package.json index 2220f0f076b..61fb9e1c0e6 100644 --- a/package.json +++ b/package.json @@ -161,7 +161,7 @@ "jest": "jest --notify --watch", "e2e-tests": "jest --runInBand --config=jest.config.e2e.js", "api-tests": "jest --notify --watch --config=devenv/e2e-api-tests/jest.js", - "storybook": "cd packages/grafana-ui && yarn storybook", + "storybook": "cd packages/grafana-ui && yarn storybook --ci", "storybook:build": "cd packages/grafana-ui && yarn storybook:build", "prettier:check": "prettier --list-different \"**/*.{ts,tsx,scss}\"", "prettier:write": "prettier --list-different \"**/*.{ts,tsx,scss}\" --write", diff --git a/packages/grafana-ui/.storybook/tsconfig.json b/packages/grafana-ui/.storybook/tsconfig.json new file mode 100644 index 00000000000..200f5b97a30 --- /dev/null +++ b/packages/grafana-ui/.storybook/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../tsconfig.json", + "include": ["../src/**/*.ts", "../src/**/*.tsx"], + "exclude": ["../dist", "../node_modules"], + "compilerOptions": { + "noUnusedLocals": false, + + "declarationDir": "dist", + "outDir": "compiled" + } +} diff --git a/packages/grafana-ui/.storybook/webpack.config.js b/packages/grafana-ui/.storybook/webpack.config.js index 2b0967ccee0..df725a7f0d0 100644 --- a/packages/grafana-ui/.storybook/webpack.config.js +++ b/packages/grafana-ui/.storybook/webpack.config.js @@ -9,13 +9,13 @@ module.exports = ({ config, mode }) => { loader: require.resolve('ts-loader'), options: { // transpileOnly: true, - configFile: path.resolve(__dirname, '../tsconfig.json'), + configFile: path.resolve(__dirname, 'tsconfig.json'), }, }, { loader: require.resolve('react-docgen-typescript-loader'), options: { - tsconfigPath: path.resolve(__dirname, '../tsconfig.json'), + tsconfigPath: path.resolve(__dirname, 'tsconfig.json'), // https://github.com/styleguidist/react-docgen-typescript#parseroptions // @ts-ignore propFilter: prop => { diff --git a/packages/grafana-ui/src/components/Button/AbstractButton.tsx b/packages/grafana-ui/src/components/Button/AbstractButton.tsx index 9d401077569..8a8467174e3 100644 --- a/packages/grafana-ui/src/components/Button/AbstractButton.tsx +++ b/packages/grafana-ui/src/components/Button/AbstractButton.tsx @@ -1,33 +1,9 @@ -import React, { AnchorHTMLAttributes, ButtonHTMLAttributes } from 'react'; +import React, { ComponentType, ReactNode } from 'react'; import tinycolor from 'tinycolor2'; import { css, cx } from 'emotion'; -import { Themeable, GrafanaTheme } from '../../types'; -import { selectThemeVariant } from '../../themes/selectThemeVariant'; -import { stylesFactory } from '../../themes/stylesFactory'; - -export type ButtonVariant = 'primary' | 'secondary' | 'danger' | 'inverse' | 'transparent'; - -export type ButtonSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl'; - -export interface CommonButtonProps { - size?: ButtonSize; - variant?: ButtonVariant; - /** - * icon prop is a temporary solution. It accepts lefacy 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; -} - -export interface LinkButtonProps extends CommonButtonProps, AnchorHTMLAttributes { - disabled?: boolean; -} -export interface ButtonProps extends CommonButtonProps, ButtonHTMLAttributes {} - -interface AbstractButtonProps extends CommonButtonProps, Themeable { - renderAs: React.ComponentType | string; -} +import { selectThemeVariant, stylesFactory } from '../../themes'; +import { AbstractButtonProps, ButtonSize, ButtonStyles, ButtonVariant, CommonButtonProps, StyleDeps } from './types'; +import { GrafanaTheme } from '../../types'; const buttonVariantStyles = ( from: string, @@ -50,12 +26,6 @@ const buttonVariantStyles = ( } `; -interface StyleDeps { - theme: GrafanaTheme; - size: ButtonSize; - variant: ButtonVariant; - withIcon: boolean; -} const getButtonStyles = stylesFactory(({ theme, size, variant, withIcon }: StyleDeps) => { const borderRadius = theme.border.radius.sm; let padding, @@ -72,12 +42,14 @@ const getButtonStyles = stylesFactory(({ theme, size, variant, withIcon }: Style iconDistance = theme.spacing.xs; height = theme.height.sm; break; + case 'md': padding = `${theme.spacing.sm} ${theme.spacing.md}`; fontSize = theme.typography.size.md; iconDistance = theme.spacing.sm; height = theme.height.md; break; + case 'lg': padding = `${theme.spacing.md} ${theme.spacing.lg}`; fontSize = theme.typography.size.lg; @@ -85,6 +57,7 @@ const getButtonStyles = stylesFactory(({ theme, size, variant, withIcon }: Style iconDistance = theme.spacing.sm; height = theme.height.lg; break; + default: padding = `${theme.spacing.sm} ${theme.spacing.md}`; iconDistance = theme.spacing.sm; @@ -96,12 +69,15 @@ const getButtonStyles = stylesFactory(({ theme, size, variant, withIcon }: Style 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( @@ -118,6 +94,7 @@ const getButtonStyles = stylesFactory(({ theme, size, variant, withIcon }: Style 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)}; @@ -164,23 +141,22 @@ const getButtonStyles = stylesFactory(({ theme, size, variant, withIcon }: Style }; }); -export const AbstractButton: React.FunctionComponent = ({ - renderAs, - theme, - size = 'md', - variant = 'primary', - className, - icon, - children, - ...otherProps -}) => { - const buttonStyles = getButtonStyles({ theme, size, variant, withIcon: !!icon }); +export const renderButton = ( + theme: GrafanaTheme, + buttonStyles: ButtonStyles, + renderAs: ComponentType | string, + children: ReactNode, + size: ButtonSize, + variant: ButtonVariant, + icon?: string, + className?: string, + otherProps?: Partial +) => { const nonHtmlProps = { theme, size, variant, }; - const finalClassName = cx(buttonStyles.button, className); const finalChildren = icon ? ( @@ -208,4 +184,19 @@ export const AbstractButton: React.FunctionComponent = ({ return React.createElement(renderAs, finalProps); }; +export const AbstractButton: React.FunctionComponent = ({ + renderAs, + theme, + size = 'md', + variant = 'primary', + className, + icon, + children, + ...otherProps +}) => { + const buttonStyles = getButtonStyles({ theme, size, variant, withIcon: !!icon }); + + return renderButton(theme, buttonStyles, renderAs, children, size, variant, icon, className, otherProps); +}; + AbstractButton.displayName = 'AbstractButton'; diff --git a/packages/grafana-ui/src/components/Button/Button.story.tsx b/packages/grafana-ui/src/components/Button/Button.story.tsx index fcdee1336ad..3e528e8dc0f 100644 --- a/packages/grafana-ui/src/components/Button/Button.story.tsx +++ b/packages/grafana-ui/src/components/Button/Button.story.tsx @@ -1,11 +1,11 @@ import { storiesOf } from '@storybook/react'; import { Button, LinkButton } from './Button'; -import { CommonButtonProps } from './AbstractButton'; // @ts-ignore import withPropsCombinations from 'react-storybook-addon-props-combinations'; import { action } from '@storybook/addon-actions'; import { ThemeableCombinationsRowRenderer } from '../../utils/storybook/CombinationsRowRenderer'; import { select, boolean } from '@storybook/addon-knobs'; +import { CommonButtonProps } from './types'; const ButtonStories = storiesOf('UI/Button', module); @@ -15,7 +15,7 @@ const defaultProps = { }; const variants = { - size: ['xs', 'sm', 'md', 'lg', 'xl'], + size: ['xs', 'sm', 'md', 'lg'], variant: ['primary', 'secondary', 'danger', 'inverse', 'transparent'], }; const combinationOptions = { diff --git a/packages/grafana-ui/src/components/Button/Button.tsx b/packages/grafana-ui/src/components/Button/Button.tsx index 64f0672b39d..b9db2ed1447 100644 --- a/packages/grafana-ui/src/components/Button/Button.tsx +++ b/packages/grafana-ui/src/components/Button/Button.tsx @@ -1,6 +1,7 @@ import React, { useContext } from 'react'; -import { AbstractButton, ButtonProps, LinkButtonProps } from './AbstractButton'; +import { AbstractButton } from './AbstractButton'; import { ThemeContext } from '../../themes'; +import { ButtonProps, LinkButtonProps } from './types'; export const Button: React.FunctionComponent = props => { const theme = useContext(ThemeContext); diff --git a/packages/grafana-ui/src/components/Button/types.ts b/packages/grafana-ui/src/components/Button/types.ts new file mode 100644 index 00000000000..5e719c5bad2 --- /dev/null +++ b/packages/grafana-ui/src/components/Button/types.ts @@ -0,0 +1,39 @@ +import { AnchorHTMLAttributes, ButtonHTMLAttributes, ComponentType } from 'react'; +import { GrafanaTheme, Themeable } from '../../types'; + +export type ButtonVariant = 'primary' | 'secondary' | 'danger' | 'inverse' | 'transparent' | 'destructive'; + +export type ButtonSize = 'xs' | 'sm' | 'md' | 'lg'; + +export interface StyleDeps { + theme: GrafanaTheme; + size: ButtonSize; + variant: ButtonVariant; + withIcon: boolean; +} + +export interface ButtonStyles { + button: string; + iconWrap: string; + icon: string; +} + +export interface CommonButtonProps { + size?: ButtonSize; + 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; +} + +export interface LinkButtonProps extends CommonButtonProps, AnchorHTMLAttributes { + disabled?: boolean; +} +export interface ButtonProps extends CommonButtonProps, ButtonHTMLAttributes {} + +export interface AbstractButtonProps extends CommonButtonProps, Themeable { + renderAs: ComponentType | string; +} diff --git a/packages/grafana-ui/src/components/Forms/Button.mdx b/packages/grafana-ui/src/components/Forms/Button.mdx new file mode 100644 index 00000000000..9a1a508ed6c --- /dev/null +++ b/packages/grafana-ui/src/components/Forms/Button.mdx @@ -0,0 +1,63 @@ +import { Meta, Story, Preview, Props } from '@storybook/addon-docs/blocks'; +import { Button } from './Button'; + + + +# Button + +## Primary + +Used for "call to action". + + +
+ + + +
+
+ +## Secondary + +The secondary button, used for "cancel" or aborting. + + +
+ + + +
+
+ +## Destructive + +Used for removing or deleting entities. + + +
+ + + +
+
+ + + diff --git a/packages/grafana-ui/src/components/Forms/Button.story.tsx b/packages/grafana-ui/src/components/Forms/Button.story.tsx new file mode 100644 index 00000000000..6ca661448d1 --- /dev/null +++ b/packages/grafana-ui/src/components/Forms/Button.story.tsx @@ -0,0 +1,33 @@ +import React from 'react'; +import { Button } from './Button'; +import { withCenteredStory, withHorizontallyCenteredStory } from '../../utils/storybook/withCenteredStory'; +import { select, text } from '@storybook/addon-knobs'; +import { ButtonSize, ButtonVariant } from '../Button/types'; +import mdx from './Button.mdx'; + +export default { + title: 'UI/Forms/Button', + component: Button, + decorators: [withCenteredStory, withHorizontallyCenteredStory], + parameters: { + docs: { + page: mdx, + }, + }, +}; + +const variants = ['primary', 'secondary', 'destructive']; + +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'); + + return ( + + ); +}; diff --git a/packages/grafana-ui/src/components/Forms/Button.tsx b/packages/grafana-ui/src/components/Forms/Button.tsx new file mode 100644 index 00000000000..6dcbdac2e3b --- /dev/null +++ b/packages/grafana-ui/src/components/Forms/Button.tsx @@ -0,0 +1,158 @@ +import { FC } from 'react'; +import { css, cx } from 'emotion'; +import tinycolor from 'tinycolor2'; +import { selectThemeVariant, stylesFactory, useTheme } from '../../themes'; +import { renderButton } from '../Button/AbstractButton'; +import { getFocusStyle } from './commonStyles'; +import { AbstractButtonProps, ButtonSize, ButtonVariant, StyleDeps } from '../Button/types'; +import { GrafanaTheme } from '../../types'; + +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 getPropertiesForSize = (theme: GrafanaTheme, size: ButtonSize) => { + switch (size) { + case 'sm': + return { + padding: `0 ${theme.spacing.sm}`, + fontSize: theme.typography.size.sm, + iconDistance: theme.spacing.xs, + height: theme.height.sm, + }; + + case 'md': + return { + padding: `0 ${theme.spacing.md}`, + fontSize: theme.typography.size.md, + iconDistance: theme.spacing.sm, + height: `${theme.spacing.formButtonHeight}px`, + }; + + case 'lg': + return { + padding: `0 ${theme.spacing.lg}`, + fontSize: theme.typography.size.lg, + iconDistance: theme.spacing.sm, + height: theme.height.lg, + }; + + default: + return { + padding: `0 ${theme.spacing.md}`, + iconDistance: theme.spacing.sm, + fontSize: theme.typography.size.base, + height: theme.height.md, + }; + } +}; + +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.gray70, dark: theme.colors.gray33 }, 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 'primary': + default: + return { + borderColor: theme.colors.blueShade, + background: buttonVariantStyles(theme.colors.blueBase, theme.colors.blueShade, theme.colors.white), + }; + } +}; + +export const getButtonStyles = stylesFactory(({ theme, size, variant, withIcon }: StyleDeps) => { + const { padding, fontSize, iconDistance, height } = getPropertiesForSize(theme, size); + const { background, borderColor } = getPropertiesForVariant(theme, variant); + + return { + button: cx( + css` + position: relative; + label: button; + display: inline-flex; + align-items: center; + font-weight: ${theme.typography.weight.semibold}; + font-size: ${fontSize}; + font-family: ${theme.typography.fontFamily.sansSerif}; + line-height: ${theme.typography.lineHeight.sm}; + padding: ${padding}; + text-align: ${withIcon ? 'left' : 'center'}; + vertical-align: middle; + cursor: pointer; + border: 1px solid ${borderColor}; + height: ${height}; + border-radius: ${theme.border.radius.sm}; + ${background}; + + &[disabled], + &:disabled { + cursor: not-allowed; + opacity: 0.65; + box-shadow: none; + } + `, + getFocusStyle(theme) + ), + iconWrap: css` + label: button-icon-wrap; + display: flex; + align-items: center; + `, + icon: css` + label: button-icon; + margin-right: ${iconDistance}; + filter: brightness(100); + `, + }; +}); + +export const Button: FC> = ({ + renderAs, + size = 'md', + variant = 'primary', + className, + icon, + children, + ...otherProps +}) => { + const theme = useTheme(); + const buttonStyles = getButtonStyles({ theme, size, variant, withIcon: !!icon }); + + return renderButton(theme, buttonStyles, renderAs, children, size, variant, icon, className, otherProps); +}; diff --git a/packages/grafana-ui/src/components/Forms/Label.story.tsx b/packages/grafana-ui/src/components/Forms/Label.story.tsx index 1386c24cd55..9f73da0029f 100644 --- a/packages/grafana-ui/src/components/Forms/Label.story.tsx +++ b/packages/grafana-ui/src/components/Forms/Label.story.tsx @@ -1,4 +1,5 @@ import React from 'react'; + import { text } from '@storybook/addon-knobs'; import { Label } from './Label'; diff --git a/packages/grafana-ui/src/components/Forms/commonStyles.ts b/packages/grafana-ui/src/components/Forms/commonStyles.ts new file mode 100644 index 00000000000..746d1a099e4 --- /dev/null +++ b/packages/grafana-ui/src/components/Forms/commonStyles.ts @@ -0,0 +1,20 @@ +import { css } from 'emotion'; +import { GrafanaTheme } from '../../types'; + +export const getFocusStyle = (theme: GrafanaTheme) => css` + &[focus], + &:focus { + &:before { + content: ''; + position: absolute; + border: 2px solid ${theme.colors.blueLight}; + border-radius: ${theme.border.radius.lg}; + background-color: ${theme.colors.bodyBg}; + height: calc(100% + 8px); + width: calc(100% + 8px); + top: -4px; + left: -4px; + z-index: -1; + } + } +`; diff --git a/packages/grafana-ui/src/components/Forms/getFormStyles.ts b/packages/grafana-ui/src/components/Forms/getFormStyles.ts index 41e9baa9da5..9df8b9df080 100644 --- a/packages/grafana-ui/src/components/Forms/getFormStyles.ts +++ b/packages/grafana-ui/src/components/Forms/getFormStyles.ts @@ -3,11 +3,13 @@ import { GrafanaTheme } from '../../types'; import { getLabelStyles } from './Label'; import { getLegendStyles } from './Legend'; import { getFieldValidationMessageStyles } from './FieldValidationMessage'; +import { getButtonStyles } from './Button'; -export const getFormStyles = stylesFactory((theme: GrafanaTheme) => { +export const getFormStyles = stylesFactory((theme: GrafanaTheme, options?: any) => { return { ...getLabelStyles(theme), ...getLegendStyles(theme), ...getFieldValidationMessageStyles(theme), + ...getButtonStyles({ theme, variant: options.variant, size: options.size, withIcon: options.withIcon }), }; }); diff --git a/packages/grafana-ui/src/components/ThresholdsEditor/__snapshots__/ThresholdsEditor.test.tsx.snap b/packages/grafana-ui/src/components/ThresholdsEditor/__snapshots__/ThresholdsEditor.test.tsx.snap index 284e5e33910..63ce15319c8 100644 --- a/packages/grafana-ui/src/components/ThresholdsEditor/__snapshots__/ThresholdsEditor.test.tsx.snap +++ b/packages/grafana-ui/src/components/ThresholdsEditor/__snapshots__/ThresholdsEditor.test.tsx.snap @@ -202,6 +202,7 @@ exports[`Render should render with base threshold 1`] = ` }, "spacing": Object { "d": "14px", + "formButtonHeight": 32, "formFieldsetMargin": "16px", "formInputAffixPaddingHorizontal": "4px", "formInputHeight": "32px", @@ -255,6 +256,7 @@ exports[`Render should render with base threshold 1`] = ` "xs": "10px", }, "weight": Object { + "bold": 600, "light": 300, "regular": 400, "semibold": 500, @@ -410,6 +412,7 @@ exports[`Render should render with base threshold 1`] = ` }, "spacing": Object { "d": "14px", + "formButtonHeight": 32, "formFieldsetMargin": "16px", "formInputAffixPaddingHorizontal": "4px", "formInputHeight": "32px", @@ -463,6 +466,7 @@ exports[`Render should render with base threshold 1`] = ` "xs": "10px", }, "weight": Object { + "bold": 600, "light": 300, "regular": 400, "semibold": 500, diff --git a/packages/grafana-ui/src/components/index.ts b/packages/grafana-ui/src/components/index.ts index 97d3f80668a..e529cf7ecc5 100644 --- a/packages/grafana-ui/src/components/index.ts +++ b/packages/grafana-ui/src/components/index.ts @@ -6,7 +6,6 @@ export { Portal } from './Portal/Portal'; export { CustomScrollbar } from './CustomScrollbar/CustomScrollbar'; export * from './Button/Button'; -export { ButtonVariant } from './Button/AbstractButton'; // Select export { Select, AsyncSelect } from './Select/Select'; diff --git a/packages/grafana-ui/src/themes/default.ts b/packages/grafana-ui/src/themes/default.ts index 22578ff0ac9..9db36f2ad17 100644 --- a/packages/grafana-ui/src/themes/default.ts +++ b/packages/grafana-ui/src/themes/default.ts @@ -50,6 +50,7 @@ const theme: GrafanaThemeCommons = { light: 300, regular: 400, semibold: 500, + bold: 600, }, lineHeight: { xs: 1, @@ -87,6 +88,7 @@ const theme: GrafanaThemeCommons = { formFieldsetMargin: `${SPACING_BASE * 2}px`, formLegendMargin: `0 0 ${SPACING_BASE * 2}px 0`, formInputHeight: `${SPACING_BASE * 4}px`, + formButtonHeight: SPACING_BASE * 4, formInputPaddingHorizontal: `${SPACING_BASE}px`, // Used for icons do define spacing between icon and input field diff --git a/packages/grafana-ui/src/types/theme.ts b/packages/grafana-ui/src/types/theme.ts index 03c0c24ac3e..e28338ffa85 100644 --- a/packages/grafana-ui/src/types/theme.ts +++ b/packages/grafana-ui/src/types/theme.ts @@ -30,6 +30,7 @@ export interface GrafanaThemeCommons { light: number; regular: number; semibold: number; + bold: number; }; lineHeight: { xs: number; //1 @@ -69,6 +70,7 @@ export interface GrafanaThemeCommons { formFieldsetMargin: string; formLegendMargin: string; formInputHeight: string; + formButtonHeight: number; formInputPaddingHorizontal: string; // Used for icons do define spacing between icon and input field // Applied on the right(prefix) or left(suffix)