18F/identity-idp

View on GitHub
app/javascript/packages/spinner-button/spinner-button.tsx

Summary

Maintainability
A
0 mins
Test Coverage
import { useRef, useImperativeHandle, forwardRef } from 'react';
import type { HTMLAttributes, RefAttributes, ForwardedRef } from 'react';
import { Button } from '@18f/identity-components';
import type { ButtonProps } from '@18f/identity-components';
import type { SpinnerButtonElement } from './spinner-button-element';
import './spinner-button-element';

declare global {
  namespace JSX {
    interface IntrinsicElements {
      'lg-spinner-button': HTMLAttributes<SpinnerButtonElement> &
        RefAttributes<SpinnerButtonElement> & { class?: string };
    }
  }
}

interface SpinnerButtonProps extends ButtonProps {
  /**
   * Whether to start spinner automatically on click.
   */
  spinOnClick?: boolean;

  /**
   * Text to show after long delay in processing.
   */
  actionMessage?: string;

  /**
   * Time after which to show action message, in milliseconds.
   */
  longWaitDurationMs?: number;
}

export type SpinnerButtonRefHandle = SpinnerButtonElement;

function SpinnerButton(
  {
    spinOnClick = true,
    actionMessage,
    longWaitDurationMs,
    isOutline,
    children,
    ...buttonProps
  }: SpinnerButtonProps,
  ref: ForwardedRef<SpinnerButtonElement | null>,
) {
  const elementRef = useRef<SpinnerButtonRefHandle>(null);
  useImperativeHandle(ref, () => elementRef.current!);

  const classes = isOutline ? 'spinner-button--outline' : undefined;

  return (
    <lg-spinner-button
      spin-on-click={spinOnClick}
      long-wait-duration-ms={longWaitDurationMs}
      ref={elementRef}
      class={classes}
    >
      <Button isOutline={isOutline} {...buttonProps}>
        <div className="spinner-button__content">{children}</div>
        <span className="spinner-dots spinner-dots--centered" aria-hidden="true">
          <span className="spinner-dots__dot" />
          <span className="spinner-dots__dot" />
          <span className="spinner-dots__dot" />
        </span>
      </Button>
      {actionMessage && (
        <div
          role="status"
          data-message={actionMessage}
          className="spinner-button__action-message usa-sr-only"
        />
      )}
    </lg-spinner-button>
  );
}

export default forwardRef(SpinnerButton);