mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Forms: introduce checkbox (#20701)
* introduce checkbox theme variables * Add checkbox component * Style tweaks * Namespace form styles returned from getFormStyles * Name fix
This commit is contained in:
parent
aa9d00d019
commit
31181c9981
@ -237,6 +237,10 @@ export interface GrafanaTheme extends GrafanaThemeCommons {
|
||||
formSwitchBgHover: string;
|
||||
formSwitchBgDisabled: string;
|
||||
formSwitchDot: string;
|
||||
formCheckboxBg: string;
|
||||
formCheckboxBgChecked: string;
|
||||
formCheckboxBgCheckedHover: string;
|
||||
formCheckboxCheckmark: string;
|
||||
};
|
||||
shadow: {
|
||||
pageHeader: string;
|
||||
|
18
packages/grafana-ui/src/components/Forms/Checkbox.mdx
Normal file
18
packages/grafana-ui/src/components/Forms/Checkbox.mdx
Normal file
@ -0,0 +1,18 @@
|
||||
import { Meta, Story, Preview, Props } from '@storybook/addon-docs/blocks';
|
||||
import { Checkbox } from './Checkbox';
|
||||
|
||||
<Meta title="MDX|Checkbox" component={Checkbox} />
|
||||
|
||||
# Checkbox
|
||||
|
||||
### Usage
|
||||
|
||||
```jsx
|
||||
import { Forms } from '@grafana/ui';
|
||||
|
||||
<Forms.Checkbox value={true|false} label={...} description={...} onChange={...} />
|
||||
```
|
||||
|
||||
### Props
|
||||
<Props of={Checkbox} />
|
||||
|
25
packages/grafana-ui/src/components/Forms/Checkbox.story.tsx
Normal file
25
packages/grafana-ui/src/components/Forms/Checkbox.story.tsx
Normal file
@ -0,0 +1,25 @@
|
||||
import React, { useState } from 'react';
|
||||
import mdx from './Checkbox.mdx';
|
||||
import { Checkbox } from './Checkbox';
|
||||
|
||||
export default {
|
||||
title: 'UI/Forms/Checkbox',
|
||||
component: Checkbox,
|
||||
parameters: {
|
||||
docs: {
|
||||
page: mdx,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const simple = () => {
|
||||
const [checked, setChecked] = useState(false);
|
||||
return (
|
||||
<Checkbox
|
||||
value={checked}
|
||||
onChange={setChecked}
|
||||
label="Skip SLL cert validation"
|
||||
description="Set to true if you want to skip sll cert validation"
|
||||
/>
|
||||
);
|
||||
};
|
129
packages/grafana-ui/src/components/Forms/Checkbox.tsx
Normal file
129
packages/grafana-ui/src/components/Forms/Checkbox.tsx
Normal file
@ -0,0 +1,129 @@
|
||||
import React, { HTMLProps } from 'react';
|
||||
import { GrafanaTheme } from '@grafana/data';
|
||||
import { getLabelStyles } from './Label';
|
||||
import { useTheme, stylesFactory } from '../../themes';
|
||||
import { css, cx } from 'emotion';
|
||||
import { getFocusCss } from './commonStyles';
|
||||
|
||||
export interface CheckboxProps extends Omit<HTMLProps<HTMLInputElement>, 'onChange' | 'value'> {
|
||||
label?: string;
|
||||
description?: string;
|
||||
value: boolean;
|
||||
onChange?: (checked: boolean) => void;
|
||||
}
|
||||
|
||||
export const getCheckboxStyles = stylesFactory((theme: GrafanaTheme) => {
|
||||
const labelStyles = getLabelStyles(theme);
|
||||
const checkboxSize = '16px';
|
||||
return {
|
||||
label: cx(
|
||||
labelStyles.label,
|
||||
css`
|
||||
padding-left: ${theme.spacing.formSpacingBase}px;
|
||||
`
|
||||
),
|
||||
description: cx(
|
||||
labelStyles.description,
|
||||
css`
|
||||
padding-left: ${theme.spacing.formSpacingBase}px;
|
||||
`
|
||||
),
|
||||
wrapper: css`
|
||||
position: relative;
|
||||
padding-left: ${checkboxSize};
|
||||
`,
|
||||
input: css`
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
opacity: 0;
|
||||
&:focus + span {
|
||||
${getFocusCss(theme)}
|
||||
}
|
||||
|
||||
/**
|
||||
* Using adjacent sibling selector to style checked state.
|
||||
* Primarily to limit the classes necessary to use when these classes will be used
|
||||
* for angular components styling
|
||||
* */
|
||||
&:checked + span {
|
||||
background: blue;
|
||||
background: ${theme.colors.formCheckboxBgChecked};
|
||||
border: none;
|
||||
&:hover {
|
||||
background: ${theme.colors.formCheckboxBgCheckedHover};
|
||||
}
|
||||
&:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 5px;
|
||||
top: 1px;
|
||||
width: 6px;
|
||||
height: 12px;
|
||||
border: solid ${theme.colors.formCheckboxCheckmark};
|
||||
border-width: 0 3px 3px 0;
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
}
|
||||
`,
|
||||
checkmark: css`
|
||||
display: inline-block;
|
||||
width: ${checkboxSize};
|
||||
height: ${checkboxSize};
|
||||
border-radius: ${theme.border.radius.sm};
|
||||
margin-right: ${theme.spacing.formSpacingBase}px;
|
||||
background: ${theme.colors.formCheckboxBg};
|
||||
border: 1px solid ${theme.colors.formInputBorder};
|
||||
position: absolute;
|
||||
top: 1px;
|
||||
left: 0;
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
border-color: ${theme.colors.formInputBorderHover};
|
||||
}
|
||||
`,
|
||||
};
|
||||
});
|
||||
|
||||
export const Checkbox: React.FC<CheckboxProps> = ({
|
||||
label,
|
||||
description,
|
||||
value,
|
||||
onChange,
|
||||
id,
|
||||
disabled,
|
||||
...inputProps
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
const styles = getCheckboxStyles(theme);
|
||||
|
||||
return (
|
||||
<label className={styles.wrapper}>
|
||||
<input
|
||||
type="checkbox"
|
||||
className={styles.input}
|
||||
id={id}
|
||||
checked={value}
|
||||
disabled={disabled}
|
||||
onChange={event => {
|
||||
if (onChange) {
|
||||
onChange(event.target.checked);
|
||||
}
|
||||
}}
|
||||
{...inputProps}
|
||||
/>
|
||||
<span className={styles.checkmark} />
|
||||
{label && <span className={styles.label}>{label}</span>}
|
||||
{description && (
|
||||
<>
|
||||
<br />
|
||||
<span className={styles.description}>{description}</span>
|
||||
</>
|
||||
)}
|
||||
</label>
|
||||
);
|
||||
};
|
||||
|
||||
Checkbox.displayName = 'Checkbox';
|
@ -9,6 +9,7 @@ import { Button } from './Button';
|
||||
import { Form } from './Form';
|
||||
import { Switch } from './Switch';
|
||||
import { Icon } from '../Icon/Icon';
|
||||
import { Checkbox } from './Checkbox';
|
||||
import { TextArea } from './TextArea/TextArea';
|
||||
|
||||
export default {
|
||||
@ -22,6 +23,7 @@ export const users = () => {
|
||||
const [username, setUsername] = useState();
|
||||
const [password, setPassword] = useState();
|
||||
const [disabledUser, setDisabledUser] = useState(false);
|
||||
const [checked, setChecked] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -58,6 +60,14 @@ export const users = () => {
|
||||
<Field label="Disable" description="Added for testing purposes">
|
||||
<Switch checked={disabledUser} onChange={(_e, checked) => setDisabledUser(checked)} />
|
||||
</Field>
|
||||
<Field>
|
||||
<Checkbox
|
||||
label="Skip SLL cert validation"
|
||||
description="Set to true if you want to skip sll cert validation"
|
||||
value={checked}
|
||||
onChange={setChecked}
|
||||
/>
|
||||
</Field>
|
||||
<Button>Update</Button>
|
||||
</Form>
|
||||
<Form>
|
||||
|
@ -20,7 +20,10 @@ export const getLabelStyles = stylesFactory((theme: GrafanaTheme) => {
|
||||
max-width: 480px;
|
||||
`,
|
||||
description: css`
|
||||
color: ${theme.colors.formLabel};
|
||||
font-size: ${theme.typography.size.sm};
|
||||
font-weight: ${theme.typography.weight.regular};
|
||||
display: block;
|
||||
`,
|
||||
};
|
||||
});
|
||||
@ -31,8 +34,10 @@ export const Label: React.FC<LabelProps> = ({ children, description, className,
|
||||
|
||||
return (
|
||||
<div className={cx(styles.label, className)}>
|
||||
<label {...labelProps}>{children}</label>
|
||||
{description && <div className={styles.description}>{description}</div>}
|
||||
<label {...labelProps}>
|
||||
{children}
|
||||
{description && <span className={styles.description}>{description}</span>}
|
||||
</label>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -1,12 +1,16 @@
|
||||
import { css } from 'emotion';
|
||||
import { GrafanaTheme } from '@grafana/data';
|
||||
|
||||
export const getFocusCss = (theme: GrafanaTheme) => `
|
||||
outline: 2px dotted transparent;
|
||||
outline-offset: 2px;
|
||||
box-shadow: 0 0 0 2px ${theme.colors.pageBg}, 0 0 0px 4px ${theme.colors.formFocusOutline};
|
||||
transition: all 0.2s cubic-bezier(0.19, 1, 0.22, 1);
|
||||
`;
|
||||
|
||||
export const getFocusStyle = (theme: GrafanaTheme) => css`
|
||||
&:focus {
|
||||
outline: 2px dotted transparent;
|
||||
outline-offset: 2px;
|
||||
box-shadow: 0 0 0 2px ${theme.colors.pageBg}, 0 0 0px 4px ${theme.colors.formFocusOutline};
|
||||
transition: all 0.2s cubic-bezier(0.19, 1, 0.22, 1);
|
||||
${getFocusCss(theme)}
|
||||
}
|
||||
`;
|
||||
|
||||
|
@ -7,20 +7,22 @@ import { getButtonStyles, ButtonVariant } from './Button';
|
||||
import { ButtonSize } from '../Button/types';
|
||||
import { getInputStyles } from './Input/Input';
|
||||
import { getSwitchStyles } from './Switch';
|
||||
import { getCheckboxStyles } from './Checkbox';
|
||||
|
||||
export const getFormStyles = stylesFactory(
|
||||
(theme: GrafanaTheme, options: { variant: ButtonVariant; size: ButtonSize; invalid: boolean }) => {
|
||||
return {
|
||||
...getLabelStyles(theme),
|
||||
...getLegendStyles(theme),
|
||||
...getFieldValidationMessageStyles(theme),
|
||||
...getButtonStyles({
|
||||
label: getLabelStyles(theme),
|
||||
legend: getLegendStyles(theme),
|
||||
fieldValidationMessage: getFieldValidationMessageStyles(theme),
|
||||
button: getButtonStyles({
|
||||
theme,
|
||||
variant: options.variant,
|
||||
size: options.size,
|
||||
}),
|
||||
...getInputStyles({ theme, invalid: options.invalid }),
|
||||
...getSwitchStyles(theme),
|
||||
input: getInputStyles({ theme, invalid: options.invalid }),
|
||||
switch: getSwitchStyles(theme),
|
||||
checkbox: getCheckboxStyles(theme),
|
||||
};
|
||||
}
|
||||
);
|
||||
|
@ -99,6 +99,10 @@ const darkTheme: GrafanaTheme = {
|
||||
formSwitchBgActiveHover: basicColors.blueBase,
|
||||
formSwitchBgDisabled: basicColors.gray25,
|
||||
formSwitchDot: basicColors.gray15,
|
||||
formCheckboxBg: basicColors.dark5,
|
||||
formCheckboxBgChecked: basicColors.blueLight,
|
||||
formCheckboxBgCheckedHover: basicColors.blueBase,
|
||||
formCheckboxCheckmark: basicColors.gray25,
|
||||
},
|
||||
background: {
|
||||
dropdown: basicColors.dark3,
|
||||
|
@ -100,6 +100,10 @@ const lightTheme: GrafanaTheme = {
|
||||
formSwitchBgActiveHover: basicColors.blueBase,
|
||||
formSwitchBgDisabled: basicColors.gray4,
|
||||
formSwitchDot: basicColors.white,
|
||||
formCheckboxBg: basicColors.white,
|
||||
formCheckboxBgChecked: basicColors.blueShade,
|
||||
formCheckboxBgCheckedHover: basicColors.blueBase,
|
||||
formCheckboxCheckmark: basicColors.white,
|
||||
},
|
||||
background: {
|
||||
dropdown: basicColors.white,
|
||||
|
Loading…
Reference in New Issue
Block a user