diff --git a/packages/react-core/src/components/SearchInput/SearchInput.tsx b/packages/react-core/src/components/SearchInput/SearchInput.tsx index e74ccb2f560..e49601376b5 100644 --- a/packages/react-core/src/components/SearchInput/SearchInput.tsx +++ b/packages/react-core/src/components/SearchInput/SearchInput.tsx @@ -1,6 +1,5 @@ import * as React from 'react'; import { css } from '@patternfly/react-styles'; -import styles from '@patternfly/react-styles/css/components/SearchInput/search-input'; import { Button, ButtonVariant } from '../Button'; import { Badge } from '../Badge'; import AngleDownIcon from '@patternfly/react-icons/dist/esm/icons/angle-down-icon'; @@ -12,6 +11,7 @@ import ArrowRightIcon from '@patternfly/react-icons/dist/esm/icons/arrow-right-i import { AdvancedSearchMenu } from './AdvancedSearchMenu'; import { TextInputGroup, TextInputGroupMain, TextInputGroupUtilities } from '../TextInputGroup'; import { InputGroup } from '../InputGroup'; +import { Popper } from '../../helpers'; export interface SearchAttribute { /** The search attribute's value to be provided in the search input's query string. @@ -83,6 +83,13 @@ export interface SearchInputProps extends Omit, /** Delimiter in the query string for pairing attributes with search values. * Required whenever attributes are passed as props */ advancedSearchDelimiter?: string; + /** The container to append the menu to. + * If your menu is being cut off you can append it to an element higher up the DOM tree. + * Some examples: + * appendTo={() => document.body} + * appendTo={document.getElementById('target')} + */ + appendTo?: HTMLElement | (() => HTMLElement) | 'inline'; } const SearchInputBase: React.FunctionComponent = ({ @@ -112,6 +119,7 @@ const SearchInputBase: React.FunctionComponent = ({ nextNavigationButtonAriaLabel = 'Next', submitSearchButtonLabel = 'Search', isDisabled = false, + appendTo, ...props }: SearchInputProps) => { const [isSearchMenuOpen, setIsSearchMenuOpen] = React.useState(false); @@ -277,11 +285,9 @@ const SearchInputBase: React.FunctionComponent = ({ if (!!onSearch || attributes.length > 0 || !!onToggleAdvancedSearch) { if (attributes.length > 0) { - return ( -
- {buildSearchTextInputGroupWithExtraButtons()} + const AdvancedSearch = ( + = ({ getAttrValueMap={getAttrValueMap} isSearchMenuOpen={isSearchMenuOpen} /> + + ); + + const AdvancedSearchWithPopper = ( +
+ appendTo || searchInputRef.current} + />
); + + const AdvancedSearchInline = ( +
+ {buildSearchTextInputGroupWithExtraButtons()} + {AdvancedSearch} +
+ ); + + return appendTo !== 'inline' ? AdvancedSearchWithPopper : AdvancedSearchInline; } return buildSearchTextInputGroupWithExtraButtons({ ...searchInputProps }); diff --git a/packages/react-core/src/components/SearchInput/__tests__/SearchInput.test.tsx b/packages/react-core/src/components/SearchInput/__tests__/SearchInput.test.tsx index a85ab0bf115..b25f1583085 100644 --- a/packages/react-core/src/components/SearchInput/__tests__/SearchInput.test.tsx +++ b/packages/react-core/src/components/SearchInput/__tests__/SearchInput.test.tsx @@ -81,6 +81,7 @@ describe('SearchInput', () => { test('advanced search with custom attributes', () => { const { asFragment } = render( { userEvent.click(screen.getByRole('button', { name: 'Search' })); + userEvent.click(screen.getByRole('button', { name: 'Open advanced search' })); + expect(screen.getByTestId('test-id')).toContainElement(screen.getByText('First name')) + expect(props.onSearch).toHaveBeenCalled(); expect(asFragment()).toMatchSnapshot(); }); + + test('advanced search with custom attributes and appendTo="inline', () => { + const {container} = render( + + ); + + userEvent.click(screen.getByRole('button', { name: 'Open advanced search' })); + + expect(screen.getByTestId('test-id')).toContainElement(screen.getByText('test')) + }); + + test('advanced search with custom attributes and appendTo external DOM element', () => { + const {container} = render( + + ); + + userEvent.click(screen.getByRole('button', { name: 'Open advanced search' })); + + expect(screen.getByTestId('test-id')).not.toContainElement(screen.getByText('test')) + expect(document.body).toContainElement(screen.getByText('test')) + }); }); diff --git a/packages/react-core/src/components/SearchInput/__tests__/__snapshots__/SearchInput.test.tsx.snap b/packages/react-core/src/components/SearchInput/__tests__/__snapshots__/SearchInput.test.tsx.snap index 37a92b570dc..0c2789a4311 100644 --- a/packages/react-core/src/components/SearchInput/__tests__/__snapshots__/SearchInput.test.tsx.snap +++ b/packages/react-core/src/components/SearchInput/__tests__/__snapshots__/SearchInput.test.tsx.snap @@ -3,7 +3,7 @@ exports[`SearchInput advanced search 1`] = `
+ +
+
+
+
+
+
+ + +
+
+ +
+
+
+
+ + +
+
+ +
+
+
+
+ + +
+
+ +
+
+
+
+ +
+
+
+
+
+ + +
+
+
+
+
+
+
+
`;