From 31181c9981f4070d78ce4d1ad1478b59c20d6b09 Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Tue, 3 Dec 2019 09:29:44 +0100 Subject: [PATCH] Forms: introduce checkbox (#20701) * introduce checkbox theme variables * Add checkbox component * Style tweaks * Namespace form styles returned from getFormStyles * Name fix --- packages/grafana-data/src/types/theme.ts | 4 + .../src/components/Forms/Checkbox.mdx | 18 +++ .../src/components/Forms/Checkbox.story.tsx | 25 ++++ .../src/components/Forms/Checkbox.tsx | 129 ++++++++++++++++++ .../src/components/Forms/Form.story.tsx | 10 ++ .../grafana-ui/src/components/Forms/Label.tsx | 9 +- .../src/components/Forms/commonStyles.ts | 12 +- .../src/components/Forms/getFormStyles.ts | 14 +- packages/grafana-ui/src/themes/dark.ts | 4 + packages/grafana-ui/src/themes/light.ts | 4 + 10 files changed, 217 insertions(+), 12 deletions(-) create mode 100644 packages/grafana-ui/src/components/Forms/Checkbox.mdx create mode 100644 packages/grafana-ui/src/components/Forms/Checkbox.story.tsx create mode 100644 packages/grafana-ui/src/components/Forms/Checkbox.tsx diff --git a/packages/grafana-data/src/types/theme.ts b/packages/grafana-data/src/types/theme.ts index 6f3ad8ff742..2d7eb3229a7 100644 --- a/packages/grafana-data/src/types/theme.ts +++ b/packages/grafana-data/src/types/theme.ts @@ -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; diff --git a/packages/grafana-ui/src/components/Forms/Checkbox.mdx b/packages/grafana-ui/src/components/Forms/Checkbox.mdx new file mode 100644 index 00000000000..c74326611d4 --- /dev/null +++ b/packages/grafana-ui/src/components/Forms/Checkbox.mdx @@ -0,0 +1,18 @@ +import { Meta, Story, Preview, Props } from '@storybook/addon-docs/blocks'; +import { Checkbox } from './Checkbox'; + + + +# Checkbox + +### Usage + +```jsx +import { Forms } from '@grafana/ui'; + + +``` + +### Props + + diff --git a/packages/grafana-ui/src/components/Forms/Checkbox.story.tsx b/packages/grafana-ui/src/components/Forms/Checkbox.story.tsx new file mode 100644 index 00000000000..14f37f09bec --- /dev/null +++ b/packages/grafana-ui/src/components/Forms/Checkbox.story.tsx @@ -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 ( + + ); +}; diff --git a/packages/grafana-ui/src/components/Forms/Checkbox.tsx b/packages/grafana-ui/src/components/Forms/Checkbox.tsx new file mode 100644 index 00000000000..13f868ed18a --- /dev/null +++ b/packages/grafana-ui/src/components/Forms/Checkbox.tsx @@ -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, '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 = ({ + label, + description, + value, + onChange, + id, + disabled, + ...inputProps +}) => { + const theme = useTheme(); + const styles = getCheckboxStyles(theme); + + return ( + + ); +}; + +Checkbox.displayName = 'Checkbox'; diff --git a/packages/grafana-ui/src/components/Forms/Form.story.tsx b/packages/grafana-ui/src/components/Forms/Form.story.tsx index 8db650c24cc..8396fd60aa3 100644 --- a/packages/grafana-ui/src/components/Forms/Form.story.tsx +++ b/packages/grafana-ui/src/components/Forms/Form.story.tsx @@ -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 = () => { setDisabledUser(checked)} /> + + +
diff --git a/packages/grafana-ui/src/components/Forms/Label.tsx b/packages/grafana-ui/src/components/Forms/Label.tsx index ebeb48ec30c..208907f6c21 100644 --- a/packages/grafana-ui/src/components/Forms/Label.tsx +++ b/packages/grafana-ui/src/components/Forms/Label.tsx @@ -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 = ({ children, description, className, return (
- - {description &&
{description}
} +
); }; diff --git a/packages/grafana-ui/src/components/Forms/commonStyles.ts b/packages/grafana-ui/src/components/Forms/commonStyles.ts index 5720c312957..bce41bee55a 100644 --- a/packages/grafana-ui/src/components/Forms/commonStyles.ts +++ b/packages/grafana-ui/src/components/Forms/commonStyles.ts @@ -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)} } `; diff --git a/packages/grafana-ui/src/components/Forms/getFormStyles.ts b/packages/grafana-ui/src/components/Forms/getFormStyles.ts index 09523b0f3ba..b52956ea3c3 100644 --- a/packages/grafana-ui/src/components/Forms/getFormStyles.ts +++ b/packages/grafana-ui/src/components/Forms/getFormStyles.ts @@ -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), }; } ); diff --git a/packages/grafana-ui/src/themes/dark.ts b/packages/grafana-ui/src/themes/dark.ts index 0aed0d152e2..cec68fc0850 100644 --- a/packages/grafana-ui/src/themes/dark.ts +++ b/packages/grafana-ui/src/themes/dark.ts @@ -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, diff --git a/packages/grafana-ui/src/themes/light.ts b/packages/grafana-ui/src/themes/light.ts index 1d669a4cadc..730661a2cd7 100644 --- a/packages/grafana-ui/src/themes/light.ts +++ b/packages/grafana-ui/src/themes/light.ts @@ -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,