diff --git a/src/components/Combobox/Combobox.spec.js b/src/components/Combobox/Combobox.spec.js
index 7e6ce4c53..9741ce7a7 100644
--- a/src/components/Combobox/Combobox.spec.js
+++ b/src/components/Combobox/Combobox.spec.js
@@ -1,5 +1,5 @@
import assert from 'assert';
-import { render, fireEvent, cleanup } from '@testing-library/react';
+import { render, fireEvent, cleanup, screen, within } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';
import sinon from 'sinon';
@@ -361,6 +361,21 @@ describe('', () => {
);
});
+ it('can render the dropdown menu inside a portal', async () => {
+ render();
+ const toggle = await screen.findByTestId('react-gears-combobox-button');
+ await userEvent.click(toggle);
+ const dropdown = await screen.findByTestId('react-gears-combobox-dropdownmenu');
+
+ expect(dropdown.parentElement).toEqual(document.body);
+
+ expect(
+ within(await screen.findByTestId('react-gears-combobox-dropdown')).queryByTestId(
+ 'react-gears-combobox-dropdownmenu'
+ )
+ ).toBeNull();
+ });
+
describe('default filterOptions ', () => {
it('should filter by input (case insensitive)', () => {
const combobox = render();
diff --git a/src/components/Combobox/Combobox.stories.js b/src/components/Combobox/Combobox.stories.js
index 7a3d17c60..c51e96b5d 100644
--- a/src/components/Combobox/Combobox.stories.js
+++ b/src/components/Combobox/Combobox.stories.js
@@ -203,3 +203,23 @@ export const CustomOptions = () => {
/>
);
};
+
+export const PortalElement = () => {
+ const [value, setValue] = useState();
+ return (
+ <>
+
value: {value}
+
+ >
+ );
+};
diff --git a/src/components/Combobox/Combobox.tsx b/src/components/Combobox/Combobox.tsx
index eceb05466..4a88e5c31 100644
--- a/src/components/Combobox/Combobox.tsx
+++ b/src/components/Combobox/Combobox.tsx
@@ -1,6 +1,6 @@
import equal from 'fast-deep-equal';
import React, { useEffect, useState, useRef, useMemo } from 'react';
-import { findDOMNode } from 'react-dom';
+import { createPortal, findDOMNode } from 'react-dom';
import { DropdownProps, InputProps } from 'reactstrap';
import Badge from '../Badge/Badge';
import Button from '../Button/Button';
@@ -37,6 +37,7 @@ interface ComboboxProps extends Omit {
renderOption?: (option: Option) => React.ReactNode;
menuMaxHeight?: string;
multi?: boolean;
+ portalEl?: HTMLElement;
}
const defaultProps = {
@@ -60,6 +61,7 @@ function Combobox({
menuMaxHeight,
multi,
noResultsLabel = defaultProps.noResultsLabel,
+ portalEl,
onChange = defaultProps.onChange,
onCreate,
isValidNewOption = defaultProps.isValidNewOption,
@@ -118,7 +120,7 @@ function Combobox({
if (open && !multi && selected && inputElement?.current) {
window.setTimeout(() => {
- inputElement!.current!.setSelectionRange(0, 0);
+ inputElement!.current?.setSelectionRange(0, 0);
}, 1);
}
}, [open, multi, selected]);
@@ -332,6 +334,25 @@ function Combobox({
return {noResultsLabel};
};
+ const menu = (
+
+ {grouped ? renderGroupedOptions(optionsProp as OptionGroup[]) : renderOptions(options)}
+ {noMatches && renderNoOptions()}
+
+ );
+
return (
<>
{multi && (selected as Option[]).length > 0 && (
@@ -442,22 +463,7 @@ function Combobox({
-
- {grouped ? renderGroupedOptions(optionsProp as OptionGroup[]) : renderOptions(options)}
- {noMatches && renderNoOptions()}
-
+ {portalEl ? {createPortal(menu, portalEl)}
: menu}
>
);