All files / RadioGroup RadioGroup.tsx

100% Statements 57/57
100% Branches 36/36
100% Functions 7/7
100% Lines 44/44

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 1132x   2x 2x 2x 2x   2x       24x     2x 25x 25x 25x 25x 25x 25x 25x 25x   25x 25x 25x 25x 25x   25x 25x 25x   25x 7x 7x       25x       25x 2x 2x     25x                   25x 16x 16x 14x 60x 58x 58x 58x 58x       58x                                                 25x                             2x  
import React, { useState } from "react";
import { RadioGroupProps, RadioGroupStyleProps } from "./RadioGroup.types";
import Text from "../Text/Text";
import { getIncrementalGeneratedId } from "../../helpers/helpers";
import styled from "styled-components";
import colorTokens from "../../tokens/colors.json";
 
const StyledRadioGroup = styled.div<RadioGroupStyleProps>`
  display: flex;
  gap: 0.5rem;
  flex-direction: ${(props) =>
    props.$orientation === "vertical" ? "column" : "row"};
`;
 
const RadioGroup = ({
  name,
  value,
  size = "medium",
  color = colorTokens.default.primary.main,
  disabled,
  children,
  label,
  orientation = "vertical",
  // Events
  onChange,
  className,
  style,
  "data-testid": dataTestId,
  ...props
}: RadioGroupProps) => {
  const [finalName, setFinalName] = React.useState(name);
  const [selectedIndex, setSelectedIndex] = useState<number | null>(null);
  const [focusedIndex, setFocusedIndex] = useState<number>(selectedIndex ?? 0);
 
  React.useEffect(() => {
    if (!name) {
      setFinalName("radio-" + getIncrementalGeneratedId());
    }
  }, [name]);
 
  const [filteredChildren, setFilteredChildren] = useState<
    React.ReactNode | React.ReactNode[]
  >(null);
 
  const _getSelectedIndex = (index: number) => {
    setSelectedIndex(index);
    setFocusedIndex(index);
  };
 
  const Label = label ? (
    typeof label === "string" ? (
      <Text variant="label" size={size} color={color}>
        {label}
      </Text>
    ) : (
      label
    )
  ) : null;
 
  React.useEffect(() => {
    setFilteredChildren(() => {
      if (!children) return null;
      return React.Children.map(children, (child, index) => {
        if (!child) return null;
        if (React.isValidElement(child) && child.type) {
          const childElement = child as React.ReactElement;
          const childType = childElement.type;
          if (
            (childType as React.ComponentType<any>).name === "Radio" ||
            (childType as React.ComponentType<any>).name === "FormControlLabel"
          ) {
            return React.cloneElement(childElement, {
              name: finalName,
              index,
              selected: selectedIndex === index,
              ...(!childElement.props.size && size && { size }),
              ...(!childElement.props.color && color && { color }),
              ...(!childElement.props.disabled && disabled && { disabled }),
              focused: focusedIndex === index,
              getSelectedIndex: _getSelectedIndex,
            });
          }
        }
      });
    });
  }, [
    finalName,
    value,
    size,
    color,
    disabled,
    children,
    selectedIndex,
    focusedIndex,
  ]);
 
  return (
    <StyledRadioGroup
      $orientation={orientation}
      role="radiogroup"
      className={className}
      style={style}
      data-testid={dataTestId}
      {...props}
    >
      {Label}
      {filteredChildren}
    </StyledRadioGroup>
  );
};
 
export default RadioGroup;