diff --git a/src/components/select/CustomSelect.tsx b/src/components/select/CustomSelect.tsx index 5c9c643aa3..219b12ee1b 100644 --- a/src/components/select/CustomSelect.tsx +++ b/src/components/select/CustomSelect.tsx @@ -132,6 +132,7 @@ const CustomSelect: FC = ({ ); const handleSelect = (value: string) => { + document.getElementById(selectId)?.focus(); setIsOpen(false); onChange(value); }; @@ -197,6 +198,7 @@ const CustomSelect: FC = ({ document.getElementById(selectId)?.focus(); }} header={header} + toggleId={selectId} /> )} diff --git a/src/components/select/CustomSelectDropdown.tsx b/src/components/select/CustomSelectDropdown.tsx index 32904c2ac0..7601561084 100644 --- a/src/components/select/CustomSelectDropdown.tsx +++ b/src/components/select/CustomSelectDropdown.tsx @@ -12,6 +12,7 @@ import { import classnames from "classnames"; import { adjustDropdownHeight } from "util/customSelect"; import useEventListener from "@use-it/event-listener"; +import { getNearestParentsZIndex } from "util/zIndex"; export type CustomSelectOption = LiHTMLAttributes & { value: string; @@ -29,6 +30,7 @@ interface Props { onSelect: (value: string) => void; onClose: () => void; header?: ReactNode; + toggleId: string; } export const getOptionText = (option: CustomSelectOption): string => { @@ -62,6 +64,7 @@ const CustomSelectDropdown: FC = ({ onSelect, onClose, header, + toggleId, }) => { const [search, setSearch] = useState(""); // track selected option index for keyboard actions @@ -77,6 +80,24 @@ const CustomSelectDropdown: FC = ({ (searchable === "always" || (searchable === "auto" && options.length >= 5)); useEffect(() => { + if (dropdownRef.current) { + const toggle = document.getElementById(toggleId); + + // align width with wrapper toggle width + const toggleWidth = toggle?.getBoundingClientRect()?.width ?? 0; + dropdownRef.current.style.setProperty("min-width", `${toggleWidth}px`); + + // align z-index: when we are in a modal context, we want the dropdown to be above the modal + // apply the nearest parents z-index + 1 + const zIndex = getNearestParentsZIndex(toggle); + if (parseInt(zIndex) > 0) { + dropdownRef.current.parentElement?.style.setProperty( + "z-index", + zIndex + 1, + ); + } + } + setTimeout(() => { if (isSearchable) { searchRef.current?.focus(); @@ -210,6 +231,11 @@ const CustomSelectDropdown: FC = ({ // allow focus on the dropdown so that keyboard actions can be captured tabIndex={-1} ref={dropdownRef} + onMouseDown={(e) => { + // when custom select is used in a modal, which is a portal, a dropdown click + // should not close the modal itself, so we stop the event right here. + e.stopPropagation(); + }} > {isSearchable && (
diff --git a/src/sass/_custom_select.scss b/src/sass/_custom_select.scss index f113c2cd65..3254e1d4d9 100644 --- a/src/sass/_custom_select.scss +++ b/src/sass/_custom_select.scss @@ -13,6 +13,7 @@ center; background-repeat: no-repeat; background-size: map-get($icon-sizes, default); + border-top: none; box-shadow: none; min-height: map-get($line-heights, default-text); padding-right: calc($default-icon-size + 2 * $sph--small); @@ -74,7 +75,3 @@ width: 20rem; } } - -.p-custom-select__wrapper { - z-index: 400; -} diff --git a/src/util/zIndex.tsx b/src/util/zIndex.tsx index 1d2e5837ee..b64610c792 100644 --- a/src/util/zIndex.tsx +++ b/src/util/zIndex.tsx @@ -1 +1,20 @@ export const TOOLTIP_OVER_MODAL_ZINDEX = 150; + +// nearest parents z-index that is not 0 or auto +export const getNearestParentsZIndex = ( + element: HTMLElement | null, +): string => { + if (!document.defaultView || !element) { + return "0"; + } + const zIndex = document.defaultView + .getComputedStyle(element, null) + .getPropertyValue("z-index"); + if (!element.parentElement) { + return zIndex; + } + if (zIndex === "auto" || zIndex === "0" || zIndex === "") { + return getNearestParentsZIndex(element.parentElement); + } + return zIndex; +};