All files Checkbox.tsx

100% Statements 45/45
100% Branches 8/8
100% Functions 2/2
100% Lines 45/45

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 692x 2x 2x                           14x 14x 14x 14x 14x 14x 14x 14x 14x 14x 14x 14x 14x   14x   14x 14x 3x 3x 2x 2x 3x 3x 3x 3x   14x 14x 14x 14x 14x 14x 14x 14x 14x 14x 14x   14x 14x   14x 14x 14x         14x   1x  
import * as React from 'react';
import { CheckIcon } from 'lucide-react';
import { cn } from '../lib/utils';
 
interface CheckboxProps {
  id?: string;
  checked?: boolean;
  defaultChecked?: boolean;
  onChange?: (checked: boolean) => void;
  disabled?: boolean;
  className?: string;
  onClick?: React.MouseEventHandler<HTMLButtonElement>;
  onKeyDown?: React.KeyboardEventHandler<HTMLButtonElement>;
  tabIndex?: number;
}
 
function Checkbox({
  id,
  checked,
  defaultChecked,
  onChange,
  disabled,
  className,
  ...props
}: CheckboxProps) {
  // Determine if the component is controlled or uncontrolled
  const isControlled = checked !== undefined;
  const [internalChecked, setInternalChecked] = React.useState(
    defaultChecked ?? false
  );
  const currentChecked = isControlled ? checked : internalChecked;
 
  // Handle click event to toggle state and call onChange
  const handleClick = () => {
    const newChecked = !currentChecked;
    if (!isControlled) {
      setInternalChecked(newChecked);
    }
    if (onChange) {
      onChange(newChecked);
    }
  };
 
  return (
    <button
      id={id}
      type='button'
      role='checkbox'
      aria-checked={currentChecked}
      disabled={disabled}
      onClick={handleClick}
      className={cn(
        'peer border-input dark:bg-input/30 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground dark:data-[state=checked]:bg-primary data-[state=checked]:border-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive size-4 shrink-0 rounded-[4px] border shadow-xs transition-shadow outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50',
        className
      )}
      data-state={currentChecked ? 'checked' : 'unchecked'}
      {...props}
    >
      {currentChecked && (
        <span className='flex items-center justify-center text-current transition-none'>
          <CheckIcon className='size-3.5' data-testid='check-icon' />
        </span>
      )}
    </button>
  );
}
 
export { Checkbox };