Skip to content

Issue clicking buttons that get their size from an inner image #617

@Paul-Hebert

Description

@Paul-Hebert

I'm running into issues writing a test for a "Media Selector" component. It looks like this:

image

It's a set of buttons, with each button containing an image.

Here's the component markup:

import { CheckMark } from '../../icons/check-mark';
import { MediaSelectorItem } from './media-selector-item';
import './media-selector.scss';

export function MediaSelector({
  selectedItem,
  items,
  onChange,
}: {
  selectedItem: MediaSelectorItem;
  items: MediaSelectorItem[];
  onChange: (value: MediaSelectorItem) => void;
}) {
  return (
    <div className="api-x_c-media-selector">
      {items.map((item: MediaSelectorItem) => {
        // If a custom aspect ratio has been passed in, apply it
        const imageStyles = item.aspectRatio
          ? { '--api-x_media-aspect-ratio': item.aspectRatio }
          : {};

        return (
          <button
            type="button"
            className="api-x_c-media-selector__button"
            aria-pressed={(item.value === selectedItem.value).toString()}
            data-value={item.value}
            key={item.value}
            onClick={() => onChange(item)}
          >
            <CheckMark svgClass="api-x_c-media-selector__check" />
            <img
              style={imageStyles}
              className="api-x_c-media-selector__image"
              src={item.src}
              alt={item.alt}
            />
          </button>
        );
      })}
    </div>
  );
}

And here's my test:

import { withBrowser, PleasantestUtils } from 'pleasantest';

async function setup(utils: PleasantestUtils) {
  await utils.runJS(`
      import { render } from 'preact';
      import { useState } from 'preact/hooks';
      import { Root } from '../root/root';
      import { MediaSelector } from './media-selector';

      // Preact's render function doesn't clear this properly
      document.body.innerHTML = '';

      function MediaSelectorExample() {
        const items = [
          {
            src: 'https://cloudinary-marketing-res.cloudinary.com/image/upload/h_160/sneakers-wide.jpg',
            alt: 'sneakers',
            value: 'sneakers-wide'
          },
          {
            src: 'https://cloudinary-marketing-res.cloudinary.com/image/upload/h_160/car.jpg',
            alt: 'car',
            value: 'car'
          }
        ];
        const [option, setOption] = useState(items[0]);
        return (
          <Root>
            <MediaSelector onChange={setOption} selectedItem={option} items={items} />
          </Root>
        );
      }

      render((
        <MediaSelectorExample />
      ), document.body);
    `);
}

test(
  'Confirm the buttons are pressed correctly',
  withBrowser(async ({ utils, screen, user }) => {
    await setup(utils);

    const firstItem = await screen.getByRole('button', {
      name: 'sneakers',
    });
    const secondItem = await screen.getByRole('button', {
      name: 'car',
    });

    // We set the first item as selected
    await expect(firstItem).toHaveAttribute('aria-pressed', 'true');
    await expect(secondItem).toHaveAttribute('aria-pressed', 'false');

    // I'm not sure why this is necessary.
    // I'm opening a Pleasantest issue to discuss.
    await new Promise((resolve) => setTimeout(resolve, 500));

    // Clicking the first item should do nothing. It's already selected
    await user.click(firstItem, { force: true, targetSize: false });

    // The first item should still be pressed
    await expect(firstItem).toHaveAttribute('aria-pressed', 'true');
    await expect(secondItem).toHaveAttribute('aria-pressed', 'false');

    // Clicking the second item should select it
    await user.click(secondItem, { force: true, targetSize: false });

    // The user clicked the second item. It should be selected
    await expect(secondItem).toHaveAttribute('aria-pressed', 'true');
    await expect(firstItem).toHaveAttribute('aria-pressed', 'false');
  })
);

This test is passing, but only because I've put a 500ms timeout in the middle of it. If I remove this, then I can't click the buttons.

I ran into a few different issues:

  • First off it said the buttons were too small or not visibly.
    • I figured this was because the image hadn't loaded and the button had no width. I added an aspect ratio to the image CSS but it still didn't work
    • Then I added { force: true, targetSize: false }. At this point it worked sometimes but failed other times with a different error:
  • Then it said the buttons are "either not clickable or not an HTMLElement"
    • At this point I added the timeout and it fixed it.

I'm not sure what's going on. This is a private repo but you should have access. Here's the PR: https://github.com/cloudfour/cld-api-explorer/pull/9

Let me know if you need more info or would like to troubleshoot together.

Thanks!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions