From 4cf77424fd6c70781ed42337ce1c465bf5ea29e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Laura=20Fern=C3=A1ndez?= Date: Fri, 26 May 2023 10:11:48 +0200 Subject: [PATCH] Grafana UI: Add invalid state to Checkbox component (#68242) --- .../src/components/Forms/Checkbox.story.tsx | 5 +++ .../src/components/Forms/Checkbox.tsx | 38 ++++++++++++------- 2 files changed, 30 insertions(+), 13 deletions(-) diff --git a/packages/grafana-ui/src/components/Forms/Checkbox.story.tsx b/packages/grafana-ui/src/components/Forms/Checkbox.story.tsx index 96d6bc182e9..cd9e7a3ebd5 100644 --- a/packages/grafana-ui/src/components/Forms/Checkbox.story.tsx +++ b/packages/grafana-ui/src/components/Forms/Checkbox.story.tsx @@ -38,6 +38,7 @@ Basic.args = { description: 'Set to true if you want to skip TLS cert validation', disabled: false, indeterminate: false, + invalid: false, }; export const StackedList = () => { @@ -80,6 +81,7 @@ InAField.args = { 'Annotation queries can be toggled on or of at the top of the dashboard. With this option checked this toggle will be hidden.', disabled: false, indeterminate: false, + invalid: false, }; export const AllStates: StoryFn = (args) => { @@ -96,6 +98,8 @@ export const AllStates: StoryFn = (args) => { + + ); @@ -106,6 +110,7 @@ AllStates.args = { description: 'Set to true if you want to skip TLS cert validation', disabled: false, indeterminate: false, + invalid: false, }; export default meta; diff --git a/packages/grafana-ui/src/components/Forms/Checkbox.tsx b/packages/grafana-ui/src/components/Forms/Checkbox.tsx index 498661949ab..79a599464a5 100644 --- a/packages/grafana-ui/src/components/Forms/Checkbox.tsx +++ b/packages/grafana-ui/src/components/Forms/Checkbox.tsx @@ -3,7 +3,7 @@ import React, { HTMLProps, useCallback } from 'react'; import { GrafanaTheme2 } from '@grafana/data'; -import { stylesFactory, useStyles2 } from '../../themes'; +import { useTheme2 } from '../../themes'; import { getFocusStyles, getMouseFocusStyles } from '../../themes/mixins'; import { getLabelStyles } from './Label'; @@ -19,10 +19,15 @@ export interface CheckboxProps extends Omit, 'value' htmlValue?: string | number; /** Sets the checkbox into a "mixed" state. This is only a visual change and does not affect the value. */ indeterminate?: boolean; + /** Show an invalid state around the input */ + invalid?: boolean; } export const Checkbox = React.forwardRef( - ({ label, description, value, htmlValue, onChange, disabled, className, indeterminate, ...inputProps }, ref) => { + ( + { label, description, value, htmlValue, onChange, disabled, className, indeterminate, invalid, ...inputProps }, + ref + ) => { const handleOnChange = useCallback( (e: React.ChangeEvent) => { if (onChange) { @@ -31,7 +36,8 @@ export const Checkbox = React.forwardRef( }, [onChange] ); - const styles = useStyles2(getCheckboxStyles); + const theme = useTheme2(); + const styles = getCheckboxStyles(theme, invalid); const ariaChecked = indeterminate ? 'mixed' : undefined; @@ -58,11 +64,15 @@ export const Checkbox = React.forwardRef( } ); -export const getCheckboxStyles = stylesFactory((theme: GrafanaTheme2) => { +export const getCheckboxStyles = (theme: GrafanaTheme2, invalid = false) => { const labelStyles = getLabelStyles(theme); const checkboxSize = 2; const labelPadding = 1; + const getBorderColor = (color: string) => { + return invalid ? theme.colors.error.border : color; + }; + return { wrapper: css` display: inline-grid; @@ -96,7 +106,7 @@ export const getCheckboxStyles = stylesFactory((theme: GrafanaTheme2) => { * */ &:checked + span { background: ${theme.colors.primary.main}; - border: none; + border: 1px solid ${getBorderColor(theme.colors.primary.main)}; &:hover { background: ${theme.colors.primary.shade}; @@ -106,8 +116,8 @@ export const getCheckboxStyles = stylesFactory((theme: GrafanaTheme2) => { content: ''; position: absolute; z-index: 2; - left: 5px; - top: 1px; + left: 4px; + top: 0px; width: 6px; height: 12px; border: solid ${theme.colors.primary.contrastText}; @@ -119,6 +129,7 @@ export const getCheckboxStyles = stylesFactory((theme: GrafanaTheme2) => { &:disabled + span { background-color: ${theme.colors.action.disabledBackground}; cursor: not-allowed; + border: 1px solid ${getBorderColor(theme.colors.action.disabledBackground)}; &:hover { background-color: ${theme.colors.action.disabledBackground}; @@ -132,7 +143,7 @@ export const getCheckboxStyles = stylesFactory((theme: GrafanaTheme2) => { inputIndeterminate: css` &[aria-checked='mixed'] + span { - border: none; + border: 1px solid ${getBorderColor(theme.colors.primary.main)}; background: ${theme.colors.primary.main}; &:hover { @@ -143,8 +154,8 @@ export const getCheckboxStyles = stylesFactory((theme: GrafanaTheme2) => { content: ''; position: absolute; z-index: 2; - left: 3px; - right: 3px; + left: 2px; + right: 2px; top: calc(50% - 1.5px); height: 3px; border: 1.5px solid ${theme.colors.primary.contrastText}; @@ -155,6 +166,7 @@ export const getCheckboxStyles = stylesFactory((theme: GrafanaTheme2) => { } &:disabled[aria-checked='mixed'] + span { background-color: ${theme.colors.action.disabledBackground}; + border: 1px solid ${getBorderColor(theme.colors.error.transparent)}; &:after { border-color: ${theme.colors.action.disabledText}; @@ -176,11 +188,11 @@ export const getCheckboxStyles = stylesFactory((theme: GrafanaTheme2) => { height: ${theme.spacing(checkboxSize)}; border-radius: ${theme.shape.borderRadius()}; background: ${theme.components.input.background}; - border: 1px solid ${theme.components.input.borderColor}; + border: 1px solid ${getBorderColor(theme.components.input.borderColor)}; &:hover { cursor: pointer; - border-color: ${theme.components.input.borderHover}; + border-color: ${getBorderColor(theme.components.input.borderHover)}; } `, label: cx( @@ -206,6 +218,6 @@ export const getCheckboxStyles = stylesFactory((theme: GrafanaTheme2) => { ` ), }; -}); +}; Checkbox.displayName = 'Checkbox';