Theo's Typesafe CultTTC
Theo's Typesafe Cult13mo ago
2 replies
BlueBeka

combobox popover component

I'm playing around with the html popover and css anchor positioning apis to create a dropdown menu.

I was just wondering if anyone could have a look at this stripped down code (almost all styling has been removed) and let me know how you could improve it (not looking for adding customization, just semantics, performance, accessibility type stuff).

One thing that doesn't quite seem right to be is the use of a
button
for each option in the drop down, but I don't want to just use a div with click and key-press events on it.

I'm pretty sure I don't need to add
aria-controls
and
aria-expanded
as the popover api controls that.

import { useId, useRef, useState } from "react";

type Option = { name: string; value: string };

export const Dropdown = ({ name, value, options }: { name: string; value: string; options: Option[] }) => {
  const [selectedOption, setSelectedOption] = useState<Option | null>(null);
  const popoverId = useId();
  const anchorName = `--anchor-${popoverId.replaceAll(":", "-")}`;

  const popoverRef = useRef<HTMLDivElement>(null);

  return (
    <div>
      <input type="hidden" name={name} value={value} />

      <button
        role="combobox"
        type="button"
        popoverTarget={popoverId}
        popoverTargetAction="toggle"
        style={{
          anchorName,
        }}
      >
        {selectedOption?.name}
      </button>

      <div
        role="group"
        popover="auto"
        id={popoverId}
        ref={popoverRef}
        style={{
          positionAnchor: anchorName,
        }}
      >
        <div role="listbox">
          {options.map((option) => (
<div key={option.value} role="listitem">
  <button
    role="option"
    aria-selected={selectedItem === item}
    onClick={() => {
      setSelectedOption(option);
      popoverRef.current!.hidePopover();
    }}
  >
    <div>{option.name}</div>
  </button>
</div>
          ))}
        </div>
      </div>
    </div>
  );
};
Was this page helpful?