All files / atoms/RadioInput/mobile RadioInput.native.tsx

88.88% Statements 24/27
74.19% Branches 23/31
100% Functions 4/4
92.3% Lines 24/26

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 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140              2x                           6x     6x     6x   6x   6x 6x   1x   5x           6x     6x                       6x                                 6x 1x     1x         1x     6x 6x 5x                               1x     6x       6x                                         2x           2x  
import React, { useState } from 'react';
import { StyleProp, StyleSheet, Text, TouchableOpacity, View, ViewStyle } from 'react-native';
import { RadioInputNativeProps } from './RadioInput.native.types';
import { cn } from '@sb/libs';
import { useTheme } from '@sb/ui/components/Themes/ThemeProvider';
import { colors } from '@sb/styles/colors';
 
export const RadioInput: React.FC<RadioInputNativeProps> = ({
  id,
  classNames,
  value,
  labelClassNames,
  label,
  labelPosition = 'left',
  variant = 'secondary',
  disabled,
  checked = false,
  onChange,
  unCheckColor = '',
  containerClassNames,
}) => {
  const [internalChecked, setInternalChecked] = useState<boolean>(checked);
 
  // Determine if component is controlled by external checked prop
  const isControlled = checked !== undefined;
 
  // Use external checked prop when controlled, internal state when uncontrolled
  const isChecked = isControlled ? checked : internalChecked;
 
  const { theme } = useTheme();
 
  const getColor = () => {
    switch (variant) {
      case 'primary':
        return colors[theme].colorAccent;
      case 'secondary':
        return colors[theme].colorTertiary;
      default:
        return variant;
    }
  };
 
  const color = getColor();
 
  const uncheckIcon = (
    <View
      testID="uncheckedIcon"
      style={{
        borderWidth: 1,
        height: 21,
        width: 21,
        borderRadius: 30,
        borderColor: unCheckColor ? unCheckColor : colors[theme].borderWhite,
      }}
    ></View>
  );
  const checkIcon = (
    <View
      style={{
        borderRadius: 30,
        padding: 4,
        borderWidth: 1,
        height: 24,
        width: 24,
        borderColor: color,
        alignItems: 'center',
        justifyContent: 'center',
      }}
      testID="checkIcon"
    >
      <View style={{ backgroundColor: color, height: 15, width: 15, borderRadius: 30 }}></View>
    </View>
  );
 
  const handleOnPress = () => {
    Iif (disabled) return;
 
    // Only update internal state if not controlled
    Iif (!isControlled) {
      setInternalChecked(!isChecked);
    }
 
    // Always call onChange callback
    onChange?.(value, !isChecked);
  };
 
  const renderLabel = () => {
    if (typeof label === 'string') {
      return (
        <Text
          accessible
          accessibilityLabel={label}
          className={cn(
            'text-text-main',
            labelPosition === 'left' && 'mr-2',
            labelPosition === 'right' && 'ml-2',
            labelClassNames
          )}
        >
          {label}
        </Text>
      );
    }
 
    return label;
  };
 
  const radioInputStyle: StyleProp<ViewStyle> = {
    opacity: disabled ? 0.4 : 1,
  };
 
  return (
    <TouchableOpacity
      id={id}
      testID={id}
      className={classNames}
      style={radioInputStyle}
      onPress={handleOnPress}
      disabled={disabled}
      accessible
      accessibilityRole="radio"
      accessibilityState={{ selected: isChecked, disabled }}
    >
      <View style={styles.container} className={containerClassNames}>
        {labelPosition === 'left' && label && renderLabel()}
        {isChecked ? checkIcon : uncheckIcon}
        {labelPosition === 'right' && label && renderLabel()}
      </View>
    </TouchableOpacity>
  );
};
 
const styles = StyleSheet.create({
  container: {
    flexDirection: 'row',
  },
});
 
RadioInput.displayName = 'RadioInput';