4848 - Update Checkbox component (#4883)

# Summary
* Add hover state which defaults to **false**
* Add disable state


![chrome_KV2AltSmBK](https://github.com/twentyhq/twenty/assets/54629307/976fba28-b975-4acc-9d06-c14c4fe339d8)


closes #4848

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
This commit is contained in:
R894
2024-05-29 14:34:29 +03:00
committed by GitHub
parent ecff27f90c
commit df2b76ff6c
2 changed files with 78 additions and 24 deletions

View File

@ -22,39 +22,63 @@ export enum CheckboxSize {
type CheckboxProps = { type CheckboxProps = {
checked: boolean; checked: boolean;
indeterminate?: boolean; indeterminate?: boolean;
hoverable?: boolean;
onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void; onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void;
onCheckedChange?: (value: boolean) => void; onCheckedChange?: (value: boolean) => void;
variant?: CheckboxVariant; variant?: CheckboxVariant;
size?: CheckboxSize; size?: CheckboxSize;
shape?: CheckboxShape; shape?: CheckboxShape;
className?: string; className?: string;
disabled?: boolean;
}; };
const StyledInputContainer = styled.div`
align-items: center;
display: flex;
position: relative;
`;
type InputProps = { type InputProps = {
checkboxSize: CheckboxSize; checkboxSize: CheckboxSize;
variant: CheckboxVariant; variant: CheckboxVariant;
indeterminate?: boolean; indeterminate?: boolean;
hoverable?: boolean;
shape?: CheckboxShape; shape?: CheckboxShape;
isChecked?: boolean; isChecked?: boolean;
disabled?: boolean;
}; };
const StyledInputContainer = styled.div<InputProps>`
--size: ${({ checkboxSize }) =>
checkboxSize === CheckboxSize.Large ? '32px' : '24px'};
align-items: center;
border-radius: ${({ theme, shape }) =>
shape === CheckboxShape.Rounded
? theme.border.radius.rounded
: theme.border.radius.sm};
cursor: ${({ disabled }) => (disabled ? 'not-allowed' : 'pointer')};
display: flex;
padding: ${({ checkboxSize }) =>
checkboxSize === CheckboxSize.Large ? '6px' : '5px'};
position: relative;
${({ hoverable, isChecked, theme, indeterminate, disabled }) => {
if (!hoverable || disabled === true) return '';
return `&:hover{
background-color: ${
indeterminate || isChecked
? theme.color.blue10
: theme.background.transparent.light
};
}}
}`;
}}
`;
const StyledInput = styled.input<InputProps>` const StyledInput = styled.input<InputProps>`
cursor: pointer; cursor: ${({ disabled }) => (disabled ? 'not-allowed' : 'pointer')};
margin: 0; margin: 0;
opacity: 0; opacity: 0;
position: absolute; position: absolute;
z-index: 10; z-index: 10;
& + label { & + label {
--size: ${({ checkboxSize }) => --size: ${({ checkboxSize }) =>
checkboxSize === CheckboxSize.Large ? '18px' : '12px'}; checkboxSize === CheckboxSize.Large ? '18px' : '12px'};
cursor: pointer; cursor: ${({ disabled }) => (disabled ? 'not-allowed' : 'pointer')};
height: calc(var(--size) + 2px); height: calc(var(--size) + 2px);
padding: 0; padding: 0;
position: relative; position: relative;
@ -66,8 +90,16 @@ const StyledInput = styled.input<InputProps>`
checkboxSize === CheckboxSize.Large ? '18px' : '12px'}; checkboxSize === CheckboxSize.Large ? '18px' : '12px'};
background: ${({ theme, indeterminate, isChecked }) => background: ${({ theme, indeterminate, isChecked }) =>
indeterminate || isChecked ? theme.color.blue : 'transparent'}; indeterminate || isChecked ? theme.color.blue : 'transparent'};
border-color: ${({ theme, indeterminate, isChecked, variant }) => { border-color: ${({
theme,
indeterminate,
isChecked,
variant,
disabled,
}) => {
switch (true) { switch (true) {
case disabled:
return theme.background.transparent.medium;
case indeterminate || isChecked: case indeterminate || isChecked:
return theme.color.blue; return theme.color.blue;
case variant === CheckboxVariant.Primary: case variant === CheckboxVariant.Primary:
@ -83,21 +115,21 @@ const StyledInput = styled.input<InputProps>`
? theme.border.radius.rounded ? theme.border.radius.rounded
: theme.border.radius.sm}; : theme.border.radius.sm};
border-style: solid; border-style: solid;
border-width: ${({ variant }) => border-width: ${({ variant, checkboxSize }) =>
variant === CheckboxVariant.Tertiary ? '2px' : '1px'}; checkboxSize === CheckboxSize.Large ||
variant === CheckboxVariant.Tertiary
? '1.43px'
: '1px'};
content: ''; content: '';
cursor: pointer; cursor: ${({ disabled }) => (disabled ? 'not-allowed' : 'pointer')};
display: inline-block; display: inline-block;
height: var(--size); height: var(--size);
width: var(--size); width: var(--size);
} }
& + label > svg { & + label > svg {
--padding: ${({ checkboxSize, variant }) => --padding: ${({ checkboxSize }) =>
checkboxSize === CheckboxSize.Large || checkboxSize === CheckboxSize.Large ? '2px' : '1px'};
variant === CheckboxVariant.Tertiary
? '2px'
: '1px'};
--size: ${({ checkboxSize }) => --size: ${({ checkboxSize }) =>
checkboxSize === CheckboxSize.Large ? '16px' : '12px'}; checkboxSize === CheckboxSize.Large ? '16px' : '12px'};
height: var(--size); height: var(--size);
@ -117,7 +149,9 @@ export const Checkbox = ({
variant = CheckboxVariant.Primary, variant = CheckboxVariant.Primary,
size = CheckboxSize.Small, size = CheckboxSize.Small,
shape = CheckboxShape.Squared, shape = CheckboxShape.Squared,
hoverable = false,
className, className,
disabled = false,
}: CheckboxProps) => { }: CheckboxProps) => {
const [isInternalChecked, setIsInternalChecked] = const [isInternalChecked, setIsInternalChecked] =
React.useState<boolean>(false); React.useState<boolean>(false);
@ -135,7 +169,16 @@ export const Checkbox = ({
const checkboxId = 'checkbox' + v4(); const checkboxId = 'checkbox' + v4();
return ( return (
<StyledInputContainer className={className}> <StyledInputContainer
checkboxSize={size}
variant={variant}
shape={shape}
isChecked={isInternalChecked}
hoverable={hoverable}
indeterminate={indeterminate}
className={className}
disabled={disabled}
>
<StyledInput <StyledInput
autoComplete="off" autoComplete="off"
type="checkbox" type="checkbox"
@ -149,6 +192,7 @@ export const Checkbox = ({
shape={shape} shape={shape}
isChecked={isInternalChecked} isChecked={isInternalChecked}
onChange={handleChange} onChange={handleChange}
disabled={disabled}
/> />
<label htmlFor={checkboxId}> <label htmlFor={checkboxId}>
{indeterminate ? ( {indeterminate ? (

View File

@ -20,6 +20,8 @@ export const Default: Story = {
args: { args: {
checked: false, checked: false,
indeterminate: false, indeterminate: false,
hoverable: false,
disabled: false,
variant: CheckboxVariant.Primary, variant: CheckboxVariant.Primary,
size: CheckboxSize.Small, size: CheckboxSize.Small,
shape: CheckboxShape.Squared, shape: CheckboxShape.Squared,
@ -34,6 +36,7 @@ export const Catalog: CatalogStory<Story, typeof Checkbox> = {
size: { control: false }, size: { control: false },
indeterminate: { control: false }, indeterminate: { control: false },
checked: { control: false }, checked: { control: false },
hoverable: { control: false },
shape: { control: false }, shape: { control: false },
}, },
parameters: { parameters: {
@ -41,12 +44,14 @@ export const Catalog: CatalogStory<Story, typeof Checkbox> = {
dimensions: [ dimensions: [
{ {
name: 'state', name: 'state',
values: ['unchecked', 'checked', 'indeterminate'], values: ['disabled', 'unchecked', 'checked', 'indeterminate'],
props: (state: string) => { props: (state: string) => {
if (state === 'disabled') {
return { disabled: true };
}
if (state === 'checked') { if (state === 'checked') {
return { checked: true }; return { checked: true };
} }
if (state === 'indeterminate') { if (state === 'indeterminate') {
return { indeterminate: true }; return { indeterminate: true };
} }
@ -59,9 +64,14 @@ export const Catalog: CatalogStory<Story, typeof Checkbox> = {
props: (shape: CheckboxShape) => ({ shape }), props: (shape: CheckboxShape) => ({ shape }),
}, },
{ {
name: 'variant', name: 'isHoverable',
values: Object.values(CheckboxVariant), values: ['default', 'hoverable'],
props: (variant: CheckboxVariant) => ({ variant }), props: (isHoverable: string) => {
if (isHoverable === 'hoverable') {
return { hoverable: true };
}
return {};
},
}, },
{ {
name: 'size', name: 'size',