Grafana UI: Add invalid state to Checkbox component (#68242)

This commit is contained in:
Laura Fernández 2023-05-26 10:11:48 +02:00 committed by GitHub
parent f515d2efac
commit 4cf77424fd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 30 additions and 13 deletions

View File

@ -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<typeof Checkbox> = (args) => {
@ -96,6 +98,8 @@ export const AllStates: StoryFn<typeof Checkbox> = (args) => {
<Checkbox value={true} label="Checked" />
<Checkbox value={false} label="Unchecked" />
<Checkbox value={false} indeterminate={true} label="Interdeterminate" />
<Checkbox value={false} invalid={true} label="Invalid and unchecked" />
<Checkbox value={true} invalid={true} label="Invalid and checked" />
</VerticalGroup>
</div>
);
@ -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;

View File

@ -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<HTMLProps<HTMLInputElement>, '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<HTMLInputElement, CheckboxProps>(
({ 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<HTMLInputElement>) => {
if (onChange) {
@ -31,7 +36,8 @@ export const Checkbox = React.forwardRef<HTMLInputElement, CheckboxProps>(
},
[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<HTMLInputElement, CheckboxProps>(
}
);
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';