# All files / src/components chip.tsx

100% Statements 15/15
100% Branches 16/16
100% Functions 2/2
100% Lines 15/15

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

    9x

    9x
    9x

    9x

    1x
    1x

    9x
    9x
    9x
    3x
    3x
    1x
    1x

    9x
    9x

     import {
      type FC,
      useCallback,
      useRef,
      type ReactNode,
      type MouseEvent as ReactMouseEvent,
      type HTMLAttributes,
    } from 'react';
    import cn from 'classnames';

    import RemoveIcon from '../svg/times.svg';

    import '../styles/components/chip.scss';

    type ChipProps = {
      /**
       * What is displayed within the chip
       */
      children: string | ReactNode;
      /**
       * Call back which, if present, will display a remove icon and is fired when this is clicked
       */
      onRemove?: (event: ReactMouseEvent<SVGElement, MouseEvent>) => void;
      /**
       * If true will opacify the chip and prevent the remove from being clickable
       */
      disabled?: boolean;
      /**
       * Additional CSS classnames to apply (eg secondary, tertiary)
       */
      className?: string;
      /**
       * Compact styling for chip
       */
      compact?: boolean;
      /**
       * click event listener on the component (except on the close button if present)
       */
      onClick?: () => void;
      /**
       * key press event listener on the component
       */
      onKeyPress?: () => void;
      innerRef?: (node: HTMLElement | null) => void;
      asSpan?: boolean;
    };

    export const Chip: FC<
      ChipProps & HTMLAttributes<HTMLButtonElement | HTMLSpanElement>
    > = ({
      children,
      onRemove,
      className = '',
      disabled,
      compact = false,
      onClick,
      onKeyPress,
      innerRef,
      asSpan = false,
      ...rest
    }) => {
      const onRemoveRef = useRef(onRemove);
      onRemoveRef.current = onRemove;

      const handleRemove = useCallback(
        (event: ReactMouseEvent<SVGElement, MouseEvent>) => {
          event.stopPropagation();
          onRemoveRef.current?.(event);
        },
        []
      );
      const props = { ...rest };
      let element: 'button' | 'span' = 'button';
      if (onRemove || asSpan) {
        element = 'span';
        if (onClick || onKeyPress) {
          props.role = 'button';
          props.tabIndex = 0;
        }
      }

      const Element = element;
      return (
        <Element
          ref={innerRef}
          className={cn(
            'chip',
            { 'chip--disabled': disabled, 'chip--compact': compact },
            className
          )}
          type={element === 'button' ? 'button' : undefined}
          onKeyPress={onKeyPress}
          onClick={onClick}
          {...props}
        >
          {children}
          {onRemove && !disabled && (
            <RemoveIcon data-testid="remove-icon" onClick={handleRemove} />
          )}
        </Element>
      );
    };

    export default Chip;