mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
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
This commit is contained in:
parent
fc1ded5026
commit
24183ba390
@ -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",
|
||||
|
11
packages/grafana-ui/.storybook/tsconfig.json
Normal file
11
packages/grafana-ui/.storybook/tsconfig.json
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"extends": "../tsconfig.json",
|
||||
"include": ["../src/**/*.ts", "../src/**/*.tsx"],
|
||||
"exclude": ["../dist", "../node_modules"],
|
||||
"compilerOptions": {
|
||||
"noUnusedLocals": false,
|
||||
|
||||
"declarationDir": "dist",
|
||||
"outDir": "compiled"
|
||||
}
|
||||
}
|
@ -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 => {
|
||||
|
@ -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<HTMLAnchorElement> {
|
||||
disabled?: boolean;
|
||||
}
|
||||
export interface ButtonProps extends CommonButtonProps, ButtonHTMLAttributes<HTMLButtonElement> {}
|
||||
|
||||
interface AbstractButtonProps extends CommonButtonProps, Themeable {
|
||||
renderAs: React.ComponentType<CommonButtonProps> | 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<AbstractButtonProps> = ({
|
||||
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<CommonButtonProps> | string,
|
||||
children: ReactNode,
|
||||
size: ButtonSize,
|
||||
variant: ButtonVariant,
|
||||
icon?: string,
|
||||
className?: string,
|
||||
otherProps?: Partial<AbstractButtonProps>
|
||||
) => {
|
||||
const nonHtmlProps = {
|
||||
theme,
|
||||
size,
|
||||
variant,
|
||||
};
|
||||
|
||||
const finalClassName = cx(buttonStyles.button, className);
|
||||
const finalChildren = icon ? (
|
||||
<span className={buttonStyles.iconWrap}>
|
||||
@ -208,4 +184,19 @@ export const AbstractButton: React.FunctionComponent<AbstractButtonProps> = ({
|
||||
return React.createElement(renderAs, finalProps);
|
||||
};
|
||||
|
||||
export const AbstractButton: React.FunctionComponent<AbstractButtonProps> = ({
|
||||
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';
|
||||
|
@ -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 = {
|
||||
|
@ -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<ButtonProps> = props => {
|
||||
const theme = useContext(ThemeContext);
|
||||
|
39
packages/grafana-ui/src/components/Button/types.ts
Normal file
39
packages/grafana-ui/src/components/Button/types.ts
Normal file
@ -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<HTMLAnchorElement> {
|
||||
disabled?: boolean;
|
||||
}
|
||||
export interface ButtonProps extends CommonButtonProps, ButtonHTMLAttributes<HTMLButtonElement> {}
|
||||
|
||||
export interface AbstractButtonProps extends CommonButtonProps, Themeable {
|
||||
renderAs: ComponentType<CommonButtonProps> | string;
|
||||
}
|
63
packages/grafana-ui/src/components/Forms/Button.mdx
Normal file
63
packages/grafana-ui/src/components/Forms/Button.mdx
Normal file
@ -0,0 +1,63 @@
|
||||
import { Meta, Story, Preview, Props } from '@storybook/addon-docs/blocks';
|
||||
import { Button } from './Button';
|
||||
|
||||
<Meta title="MDX|Button" component={Button} />
|
||||
|
||||
# Button
|
||||
|
||||
## Primary
|
||||
|
||||
Used for "call to action".
|
||||
|
||||
<Preview>
|
||||
<div>
|
||||
<Button variant="primary" size="sm" renderAs="button" style={{ margin: '5px' }}>
|
||||
Small
|
||||
</Button>
|
||||
<Button variant="primary" size="md" renderAs="button" style={{ margin: '5px' }}>
|
||||
Medium
|
||||
</Button>
|
||||
<Button variant="primary" size="lg" renderAs="button" style={{ margin: '5px' }}>
|
||||
Large
|
||||
</Button>
|
||||
</div>
|
||||
</Preview>
|
||||
|
||||
## Secondary
|
||||
|
||||
The secondary button, used for "cancel" or aborting.
|
||||
|
||||
<Preview>
|
||||
<div>
|
||||
<Button variant="secondary" size="sm" renderAs="button" style={{ margin: '5px' }}>
|
||||
Small
|
||||
</Button>
|
||||
<Button variant="secondary" size="md" renderAs="button" style={{ margin: '5px' }}>
|
||||
Medium
|
||||
</Button>
|
||||
<Button variant="secondary" size="lg" renderAs="button" style={{ margin: '5px' }}>
|
||||
Large
|
||||
</Button>
|
||||
</div>
|
||||
</Preview>
|
||||
|
||||
## Destructive
|
||||
|
||||
Used for removing or deleting entities.
|
||||
|
||||
<Preview>
|
||||
<div>
|
||||
<Button variant="destructive" size="sm" renderAs="button" style={{ margin: '5px' }}>
|
||||
Small
|
||||
</Button>
|
||||
<Button variant="destructive" size="md" renderAs="button" style={{ margin: '5px' }}>
|
||||
Medium
|
||||
</Button>
|
||||
<Button variant="destructive" size="lg" renderAs="button" style={{ margin: '5px' }}>
|
||||
Large
|
||||
</Button>
|
||||
</div>
|
||||
</Preview>
|
||||
|
||||
|
||||
<Props of={Button} />
|
33
packages/grafana-ui/src/components/Forms/Button.story.tsx
Normal file
33
packages/grafana-ui/src/components/Forms/Button.story.tsx
Normal file
@ -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 (
|
||||
<Button variant={variant as ButtonVariant} size={size as ButtonSize} renderAs="button">
|
||||
{buttonText}
|
||||
</Button>
|
||||
);
|
||||
};
|
158
packages/grafana-ui/src/components/Forms/Button.tsx
Normal file
158
packages/grafana-ui/src/components/Forms/Button.tsx
Normal file
@ -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<Omit<AbstractButtonProps, 'theme'>> = ({
|
||||
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);
|
||||
};
|
@ -1,4 +1,5 @@
|
||||
import React from 'react';
|
||||
|
||||
import { text } from '@storybook/addon-knobs';
|
||||
|
||||
import { Label } from './Label';
|
||||
|
20
packages/grafana-ui/src/components/Forms/commonStyles.ts
Normal file
20
packages/grafana-ui/src/components/Forms/commonStyles.ts
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
`;
|
@ -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 }),
|
||||
};
|
||||
});
|
||||
|
@ -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,
|
||||
|
@ -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';
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user