Unable to trigger click/onChange when interaction-testing a MUI-RadioButtonGroup

41 Views Asked by At

Context

  • I am mounting my component inside a Storybook
  • I am attempting to interact with this component using Storybook interactions
  • Storybook uses Testing-Library under the hood

I have the following component

import React from "react";
import PropTypes from "prop-types";
import cn from "classnames";
import FormControl from "@mui/material/FormControl";
import RadioGroup from "@mui/material/RadioGroup";
import FormLabel from "@mui/material/FormLabel";
import FormHelperText from "@mui/material/FormHelperText";
import FormControlLabel from "@mui/material/FormControlLabel";
import Radio from "@mui/material/Radio";

const MyRadioGroup = (props) => {
  const { value, input, label, meta, children, options, classes, ...custom } =
    props;

  const _value = value || input?.value;
  const _defaultValue = input?.defaultValue || meta?.initial;

  return (
    <FormControl
      sx={{ width: "100%" }}
      className={cn("radiogroup", classes.formControl)}
      key={`key-${input.name}`}
      error={Boolean(meta.touched && meta.error)}
    >
      <FormLabel className={classes.inputLabel} htmlFor={`id-${input.name}`}>
        {label}
      </FormLabel>
      <RadioGroup
        {...input}
        {...custom}
        data-testid={`radio-group-${input.name}`}
        className={cn(classes.select, {
          [classes.error]: Boolean(meta.touched && meta.error),
        })}
        variant="standard"
        value={_value}
        defaultValue={_defaultValue}
      >
        {children ||
          options?.map((option) => {
            const label =
              typeof option === "string"
                ? option
                : option.label || option.value;
            const value = typeof option === "string" ? option : option.value;

            return (
              <FormControlLabel
                data-testid={`radio-option-${value}`}
                key={value}
                value={value}
                control={<Radio />}
                label={label}
              />
            );
          })}
      </RadioGroup>
      {meta.touched && meta.error && (
        <FormHelperText sx={{ margin: 0 }}>
          {meta.touched && meta.error}
        </FormHelperText>
      )}
    </FormControl>
  );
};

MyRadioGroup.propTypes = {
  classes: PropTypes.shape({
    formControl: PropTypes.string,
    inputLabel: PropTypes.string,
    select: PropTypes.string,
    error: PropTypes.string,
  }),
  defaultValue: PropTypes.any,
  value: PropTypes.any,
  label: PropTypes.string,
  input: PropTypes.object,
  children: PropTypes.node,
  options: PropTypes.arrayOf(
    PropTypes.oneOfType([
      PropTypes.string,
      PropTypes.shape({
        label: PropTypes.string,
        value: PropTypes.any,
      }),
    ])
  ),
  meta: PropTypes.shape({
    initial: PropTypes.any,
    touched: PropTypes.bool,
    invalid: PropTypes.bool,
    error: PropTypes.any,
  }),
};

MyRadioGroup.defaultProps = {
  classes: {
    formControl: "",
    inputLabel: "",
    select: "",
  },
};

export default MyRadioGroup;

And the following story:

export const Automated: Story = {
  args: {
    row: true,
    label: "This is an Automated RadioGroup",
    options: [
      "option1",
      { value: "option2" },
      { value: "option3", label: "The Third One" },
    ],
  },
  play: async ({ canvasElement, step }) => {
    const { selectRadioButton } = Interact({ canvasElement, step });
    await selectRadioButton(".radiogroup", "option1");
  },
};

The problem is somewhere in the following function (I think).

I've tried selecting all sorts of components, firing all sorts of events, but I can't get the UI to show me a 'selected' RadioGroup in my Storybook

const selectRadioButton = async (selector: string, value: string) =>
  step(`selectRadioButton "${selector}": "${value}")`, async () => {
    const container = root?.querySelector(selector);
    const inContainer = within(container as HTMLElement);
    const radioGroup = inContainer.getByRole("radiogroup");
    const radios = inContainer.getAllByRole("radio");

    // I've tried selecting the RadioGroup many different ways using
    //      normal querySelectors, getByRole, getByTestId
    // I've also tried selecting the Radio option many different ways

    const inputWithValue = await container?.querySelector( `input[type=radio][value="${value}"]` );
    const closestSpan = inputWithValue?.closest("span");
    const nextSibling = closestSpan?.nextSibling as HTMLElement;
    const closestLabel = inputWithValue?.closest("label");

    //    I've tried triggering all sorts of events
    //        userEvent.click(...)
    //        userEvent.selectOptions(...) (throws a warning that it can't find an option with the selected value, even though I can see it in error output)
    //        fireEvent.click(...)
    //        fireEvent.click(..., { target: { value: value } })
    //        fireEvent.click(..., { target: { checked: true } })
    //        fireEvent(..., new MouseEvent('click', { bubbles: true }));
    //        fireEvent.mouseDown(...)
    //        fireEvent.mouseUp(...)
  });

I think the closest I've gotten is that the MUI Ripple effect will trigger but I can't get the RadioButton to actually be checked or the onChange event to fire

0

There are 0 best solutions below