Commit 31181c99 by Dominik Prokop Committed by GitHub

Forms: introduce checkbox (#20701)

* introduce checkbox theme variables

* Add checkbox component

* Style tweaks

* Namespace form styles returned from getFormStyles

* Name fix
parent aa9d00d0
......@@ -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;
......
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} />
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"
/>
);
};
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>
);
};
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,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment