Absolute positioned dropdown causing scrollbar to appear instead of appearing above other divs

Viewed 32

I'm trying to create the below component using react and tailwind css:

The behavior I am trying to create is how to display the dropdown options on top of the next section items ("Add product images and description") instead of making a scrollbar to appear.

product page

It is vertical 2 step process where the active tab expands, allowing users to fill details.

Each of the 2 steps are placed inside a react component called StepContainer.jsx whose code is mentioned below:

import { useState, useEffect } from 'react';
import { IoIosArrowDown } from 'react-icons/io';

function StepContainer({ children, stepCount, step, setStep, title }) {
  // console.log('step', step);
  const [isOpen, setIsOpen] = useState(false);
  useEffect(() => {
    if (step[stepCount]?.active) {
      setIsOpen(true);
    }
  }, [step, stepCount]);

  const handleExpandButtonClick = () => {
    setIsOpen(!isOpen);
    setStep((prevState) => {
      return {
        ...prevState,
        [stepCount]: {
          ...prevState[stepCount],
          active: !isOpen,
        },
      };
    });
  };

  return (
    <div className='bg-white w-full border p-4 relative overflow-hidden'>
      {/* heading section */}
      <div className='flex items-center justify-between'>
        <div className='flex items-center gap-4'>
          {/* step counter */}
          <div
            className={`flex items-center justify-center h-6 w-6 rounded-full ${
              step[stepCount]?.completed
                ? 'bg-emerald-600 text-white'
                : step[stepCount]?.active
                ? 'bg-orange-500 text-white'
                : 'bg-gray-300'
            }`}
          >
            <span className='text-sm'>{step[stepCount]?.step}</span>
          </div>
          <h3 className='m-0'>{title}</h3>
        </div>
        <button
          className='border rounded-full w-6 h-6 flex items-center justify-center'
          onClick={handleExpandButtonClick}
        >
          <IoIosArrowDown
            className={`transition-all duration-300 ${
              isOpen ? 'transform rotate-180' : ''
            }`}
          />
        </button>
      </div>
      {/* content section */}
      <div
        className={`bg-white transition-all duration-300 max-h-0 ${
          isOpen ? 'max-h-[30rem] overflow-y-auto' : 'overflow-hidden'
        }`}
      >
        <div className='py-4'>{children}</div>
      </div>
    </div>
  );
}

export default StepContainer;

The dropdown is handled by a separate component called: DropdownAddWithCheck

import { useEffect, useRef, useState } from 'react';
import { AiFillCaretDown } from 'react-icons/ai';
import { IoAddCircleOutline } from 'react-icons/io5';

const DropdownAddWithCheck = ({
  list,
  placeholder,
  value,
  selectedItems,
  setSelectedItem,
  className,
  inputClassname,
  borderColor,
  borderSelectColor,
}) => {
  const [open, setOpen] = useState(false);
  const [searchText, setSearchText] = useState('');
  const [filteredList, setFilteredList] = useState([]);

  // Step 1: populate filtered list with provided list
  useEffect(() => {
    if (list) {
      setFilteredList(list);
    }
  }, [list]);

  // Step 2: Close dropdown on click outside using useRef
  const wrapperRef = useRef(null);

  useEffect(() => {
    const handleClickOutside = (event) => {
      if (wrapperRef.current && !wrapperRef.current.contains(event.target)) {
        setOpen(false);
      }
    };
    document.addEventListener('click', handleClickOutside, true);
    return () => {
      document.removeEventListener('click', handleClickOutside, true);
    };
  }, [wrapperRef]);

  // Step 3: Filter list based on search text
  const handleSearchTextChange = (event) => {
    setSearchText(event.target.value);
    let newList = [];
    if (searchText.length > 0) {
      newList = list.filter((item) =>
        item.name.toLowerCase().includes(event.target.value.toLowerCase())
      );
    } else {
      newList = list;
    }
    setFilteredList(newList);
  };

  // Step 4: Set selected item on click
  const handleListItemClick = (item) => {
    setSelectedItem(item);
    setSearchText(item.name);
    setOpen(false);
  };

  return (
    <div className='w-full flex items-center gap-2 z-20'>
      <div ref={wrapperRef} className={`${className} relative`}>
        {/* input search box */}
        <div
          className={`flex ${
            open
              ? borderSelectColor
                ? borderSelectColor
                : 'border-purple-500'
              : borderColor
              ? borderColor
              : 'border-gray-300'
          }`}
        >
          <input
            onFocus={() => setOpen(true)}
            type='text'
            placeholder={placeholder}
            value={searchText ? searchText : value ? value : ''}
            onChange={handleSearchTextChange}
            className={`outline-none w-full text-sm py-0.5 ${inputClassname}`}
          />
          <button onClick={() => setOpen(!open)} type='button'>
            <AiFillCaretDown
              className={`text-gray-600 transition-all duration-200 ${
                open ? 'transform rotate-180' : ''
              }`}
            />
          </button>
        </div>

        <ul
          className={`absolute top-9 bg-white w-full shadow-lg rounded max-h-0 transition-all ease-in-out 
                  duration-200 divide-y ${
                    open
                      ? 'max-h-40 border border-purple-500 overflow-y-scroll'
                      : 'overflow-hidden'
                  }`}
        >
          {filteredList &&
            filteredList.map((item, index) => (
              <li key={index}>
                <button
                  type='button'
                  onClick={() => handleListItemClick(item)}
                  className='w-full hover:bg-blue-50 text-left px-2 py-1 flex items-center gap-2'
                >
                  <input
                    type='checkbox'
                    checked={selectedItems?.includes(item) || false}
                    readOnly
                  />
                  <span className='text-sm'>{item.name}</span>
                </button>
              </li>
            ))}
        </ul>
      </div>
      <button className='border border-green-500 text-green-600 hover:bg-green-100 rounded-lg px-1 py-1 tracking-tighter text-sm whitespace-nowrap flex items-center gap-1'>
        <IoAddCircleOutline className='scale-125' />
        Add
      </button>
    </div>
  );
};

export default DropdownAddWithCheck;
0 Answers
Related