ycs77 / headlessui-float

Easily use Headless UI with Floating UI to position floating elements.
https://headlessui-float.vercel.app
MIT License
344 stars 11 forks source link

Incompatible with the Storybook? #35

Closed liliiakhr closed 1 year ago

liliiakhr commented 1 year ago

Use Version Use version when bugs appear:

Describe the bug When using headless-float with the Combobox and trying to display the Combobox in the Storybook, getting the following error. Removing <Float></Float> fixes the error.

Screenshot 2022-12-28 at 11 23 03
ycs77 commented 1 year ago

Hi @liliiakhr, Please provide a minimal reproducible example (like github repo, codesandbox, stackblitz...) so I can debug. otherwise, this issue will be closed. 😅

helmisatria commented 1 year ago

Experiencing the same issue with @liliiakhr, my workaround is to use render props like this:

import { Popover as HeadlessPopover } from '@headlessui/react'

{/* use the component using render props and don't forget to add `static` on the Panel component */}
<HeadlessPopover>
   {({open}) => (
      <Float show={open}>
         {/* ... */}
         <HeadlessPopover.Panel static />
      </Float>
   )}
</HeadlessPopover>
ycs77 commented 1 year ago

I follow the steps below to try to reproduce this bug, but I can't, the component is work:

create storybook react project:

npx degit chromaui/intro-storybook-react-template headlessui-float-story

Install dependencies:

yarn add @headlessui/react @headlessui-float/react @heroicons/react/24/outline

Follow https://storybook.js.org/recipes/tailwindcss to integrate Tailwind CSS in Storybook.

Combobox component:

// Combobox.jsx
import React, { useState, Fragment } from 'react';
import PropTypes from 'prop-types';
import { Combobox as HeadlessCombobox } from '@headlessui/react';
import { Float } from '@headlessui-float/react';
import { CheckIcon, ChevronUpDownIcon } from '@heroicons/react/24/outline';

export const Combobox = ({ people }) => {
  const [selected, setSelected] = useState(people[0]);
  const [query, setQuery] = useState('');

  const filteredPeople =
    query === ''
      ? people
      : people.filter(person =>
        person.name
          .toLowerCase()
          .replace(/\s+/g, '')
          .includes(query.toLowerCase().replace(/\s+/g, ''))
      );

  return (
    <HeadlessCombobox value={selected} onChange={setSelected}>
      <Float
        as="div"
        className="relative w-64"
        placement="bottom-start"
        offset={4}
        leave="transition ease-in duration-100"
        leaveFrom="opacity-100"
        leaveTo="opacity-0"
        floatingAs={Fragment}
        onHide={() => setQuery('')}
      >
        <div className="relative w-full text-left bg-white border border-gray-200 rounded-lg shadow-md cursor-default focus:outline-none sm:text-sm overflow-hidden">
          <HeadlessCombobox.Input
            className="w-full border-none py-2 pl-3 pr-10 text-sm leading-5 text-gray-900 focus:outline-none focus:ring-0"
            displayValue={person => person?.name}
            onChange={event => setQuery(event.target.value)}
          />

          <HeadlessCombobox.Button className="absolute inset-y-0 right-0 flex items-center pr-2">
            <ChevronUpDownIcon className="w-5 h-5 text-gray-400" aria-hidden="true" />
          </HeadlessCombobox.Button>
        </div>

        <HeadlessCombobox.Options className="absolute w-full py-1 overflow-auto text-base bg-white rounded-md shadow-lg max-h-60 ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm">
          {filteredPeople.length === 0 && query !== '' ? (
            <div className="relative py-2 px-4 text-gray-700 cursor-default select-none">
              Nothing found.
            </div>
          ) : (
            filteredPeople.map(person => (
              <HeadlessCombobox.Option
                key={person.id}
                className={({ active }) =>
                  `relative py-2 pl-10 pr-4 cursor-default select-none ${
                    active ? 'text-white bg-teal-600' : 'text-gray-900'
                  }`
                }
                value={person}
              >
                {({ selected, active }) => (
                  <>
                    <span className={`block truncate ${selected ? 'font-medium' : 'font-normal'}`}>
                      { person.name }
                    </span>
                    {selected && (
                      <span
                        className={`absolute inset-y-0 left-0 flex items-center pl-3 ${
                          active ? 'text-white' : 'text-teal-600'
                        }`}
                      >
                        <CheckIcon className="w-5 h-5" aria-hidden="true" />
                      </span>
                    )}
                  </>
                )}
              </HeadlessCombobox.Option>
            ))
          )}
        </HeadlessCombobox.Options>
      </Float>
    </HeadlessCombobox>
  );
};

Combobox.propTypes = {
  /**
   * People data
   */
  people: PropTypes.array.isRequired,
};

Combobox.defaultProps = {
  people: [],
};

storybook component for Combobox:

// Combobox.stories.jsx
import React from 'react';

import { Combobox } from './Combobox';

export default {
  title: 'Combobox',
  component: Combobox,
};

const Template = (args) => <Combobox {...args} />;

export const Default = Template.bind({});
Default.args = {
  people: [
    { id: 1, name: 'Wade Cooper', unavailable: false },
    { id: 2, name: 'Arlene Mccoy', unavailable: false },
    { id: 3, name: 'Devon Webb', unavailable: false },
    { id: 4, name: 'Tom Cook', unavailable: false },
    { id: 5, name: 'Tanya Fox', unavailable: true },
    { id: 6, name: 'Hellen Schmidt', unavailable: false },
  ],
};

@liliiakhr @helmisatria With the current information, it is difficult for me to judge where the problem is. Please provide a minimal reproducible example (like github repo, codesandbox, stackblitz...) that reproduces this bug so I can debug, otherwise, I will close this issue.