fbredius/storybook

View on GitHub
addons/a11y/src/components/Report/HighlightToggle.tsx

Summary

Maintainability
A
0 mins
Test Coverage
import React from 'react';
import { styled } from '@storybook/theming';

import { NodeResult } from 'axe-core';
import { useA11yContext } from '../A11yContext';

interface ToggleProps {
  elementsToHighlight: NodeResult[];
  toggleId?: string;
}

enum CheckBoxStates {
  CHECKED,
  UNCHECKED,
  INDETERMINATE,
}

const Checkbox = styled.input<{ disabled: boolean }>(({ disabled }) => ({
  cursor: disabled ? 'not-allowed' : 'pointer',
}));

function areAllRequiredElementsHighlighted(
  elementsToHighlight: NodeResult[],
  highlighted: string[]
): CheckBoxStates {
  const highlightedCount = elementsToHighlight.filter((item) =>
    highlighted.includes(item.target[0])
  ).length;

  // eslint-disable-next-line no-nested-ternary
  return highlightedCount === 0
    ? CheckBoxStates.UNCHECKED
    : highlightedCount === elementsToHighlight.length
    ? CheckBoxStates.CHECKED
    : CheckBoxStates.INDETERMINATE;
}

const HighlightToggle: React.FC<ToggleProps> = ({ toggleId, elementsToHighlight = [] }) => {
  const { toggleHighlight, highlighted } = useA11yContext();
  const checkBoxRef = React.useRef<HTMLInputElement>(null);
  const [checkBoxState, setChecked] = React.useState(
    areAllRequiredElementsHighlighted(elementsToHighlight, highlighted)
  );

  React.useEffect(() => {
    const newState = areAllRequiredElementsHighlighted(elementsToHighlight, highlighted);
    if (checkBoxRef.current) {
      checkBoxRef.current.indeterminate = newState === CheckBoxStates.INDETERMINATE;
    }
    setChecked(newState);
  }, [elementsToHighlight, highlighted]);

  const handleToggle = React.useCallback((): void => {
    toggleHighlight(
      elementsToHighlight.map((e) => e.target[0]),
      checkBoxState !== CheckBoxStates.CHECKED
    );
  }, [elementsToHighlight, checkBoxState, toggleHighlight]);

  return (
    <Checkbox
      ref={checkBoxRef}
      id={toggleId}
      type="checkbox"
      aria-label="Highlight result"
      disabled={!elementsToHighlight.length}
      onChange={handleToggle}
      checked={checkBoxState === CheckBoxStates.CHECKED}
    />
  );
};

export default HighlightToggle;