(function
),
});
+ const _renderFilter = () => {
+ if (filterable) {
+ return (
+
+ );
+ }
+
+ return null;
+ };
+
+ const _renderList = () => {
+ if (filteredFlattenOptions.length || props.loading) {
+ return (
+
+ );
+ }
+
+ return ;
+ };
+
return (
(function
id={`select-popup-${selectId}`}
placement={popupPlacement}
>
- {filterable && (
-
- )}
- {filteredFlattenOptions.length || props.loading ? (
-
- ) : (
-
- )}
+ {renderPopup({renderFilter: _renderFilter, renderList: _renderList})}
{mode === Mode.VIEW ? (
-
+
+ {
+ return (
+
+ {'---- Before Filter ----'}
+ {renderFilter()}
+ {'---- After Filter, Before List ----'}
+ {renderList()}
+ {'---- After List ----'}
+
+ );
+ },
+ }}
+ >
+
+
+
+
+
);
};
diff --git a/src/components/Select/__tests__/Select.filter.test.tsx b/src/components/Select/__tests__/Select.filter.test.tsx
index da995d3a04..3ea9bc6cbc 100644
--- a/src/components/Select/__tests__/Select.filter.test.tsx
+++ b/src/components/Select/__tests__/Select.filter.test.tsx
@@ -4,7 +4,7 @@ import userEvent from '@testing-library/user-event';
import {cleanup} from '../../../../test-utils/utils';
import {TextInput} from '../../controls';
-import type {SelectOption, SelectProps} from '../types';
+import type {SelectOption, SelectProps, SelectRenderPopup} from '../types';
import {TEST_QA, generateOptions, generateOptionsGroups, setup} from './utils';
@@ -17,7 +17,7 @@ const onFilterChange = jest.fn();
const FILTER_PLACEHOLDER = 'Filter placeholder';
const EMPTY_OPTIONS_QA = 'empty-options';
-const renderCustomFilter: SelectProps['renderFilter'] = (props) => {
+const RENDER_CUSTOM_FILTER: SelectProps['renderFilter'] = (props) => {
const {value, ref, onChange, onKeyDown} = props;
return (
@@ -31,17 +31,28 @@ const renderCustomFilter: SelectProps['renderFilter'] = (props) => {
);
};
+const RENDER_POPUP: SelectRenderPopup = ({renderList, renderFilter}) => {
+ return (
+
+ {renderFilter()}
+ {renderList()}
+
+ );
+};
+
describe('Select filter', () => {
- test.each<[string, Partial]>([
- ['default', {renderFilter: undefined}],
- ['custom', {renderFilter: renderCustomFilter}],
- ])('base functional with %s filter section', async (_, {renderFilter}) => {
+ test.each([
+ ['default', undefined, undefined],
+ ['custom', RENDER_CUSTOM_FILTER, RENDER_POPUP],
+ ['custom', RENDER_CUSTOM_FILTER, undefined],
+ ])('base functional with %s filter section', async (_, renderFilter, renderPopup) => {
const {getByTestId, getByPlaceholderText, getAllByRole, queryAllByRole} = setup({
options: generateOptions(40),
filterPlaceholder: FILTER_PLACEHOLDER,
filterable: true,
onFilterChange,
renderFilter,
+ renderPopup,
});
const user = userEvent.setup();
const selectControl = getByTestId(TEST_QA);
diff --git a/src/components/Select/__tests__/Select.renderPopup.test.tsx b/src/components/Select/__tests__/Select.renderPopup.test.tsx
new file mode 100644
index 0000000000..482c8447a8
--- /dev/null
+++ b/src/components/Select/__tests__/Select.renderPopup.test.tsx
@@ -0,0 +1,57 @@
+import React from 'react';
+
+import userEvent from '@testing-library/user-event';
+
+import {SelectQa} from '../constants';
+
+import {DEFAULT_OPTIONS, TEST_QA, setup} from './utils';
+
+const QA = 'SELECT_RENDER_POPUP_TEST_QA';
+
+describe('Select renderPopup', () => {
+ test('default case', async () => {
+ const {getByTestId} = setup({
+ options: DEFAULT_OPTIONS,
+ filterable: true,
+ renderPopup: ({renderFilter, renderList}) => {
+ return (
+
+ {renderFilter()}
+
+ {renderList()}
+
+ );
+ },
+ });
+
+ const user = userEvent.setup();
+ const selectControl = getByTestId(TEST_QA);
+ // open select popup
+ await user.click(selectControl);
+
+ const filterInput = getByTestId(SelectQa.FILTER_INPUT);
+ expect(filterInput).toBeVisible();
+
+ const list = getByTestId(SelectQa.LIST);
+ expect(list).toBeVisible();
+
+ const customPopupDiv = getByTestId(QA);
+ expect(customPopupDiv).toBeVisible();
+ });
+
+ test('empty options', async () => {
+ const {getByTestId} = setup({
+ options: [],
+ renderEmptyOptions: () => ,
+ renderPopup: ({renderList}) => renderList(),
+ });
+
+ const user = userEvent.setup();
+ const selectControl = getByTestId(TEST_QA);
+ // open select popup
+ await user.click(selectControl);
+
+ const emptyContent = getByTestId(QA);
+ expect(emptyContent).toBeVisible();
+ });
+});
diff --git a/src/components/Select/__tests__/Select.single.test.tsx b/src/components/Select/__tests__/Select.single.test.tsx
index 04964bd784..9999d7cf7c 100644
--- a/src/components/Select/__tests__/Select.single.test.tsx
+++ b/src/components/Select/__tests__/Select.single.test.tsx
@@ -1,6 +1,7 @@
import userEvent from '@testing-library/user-event';
import {cleanup} from '../../../../test-utils/utils';
+import type {SelectRenderPopup} from '../types';
import {
DEFAULT_OPTIONS,
@@ -19,15 +20,18 @@ afterEach(() => {
const onUpdate = jest.fn();
const onOpenChange = jest.fn();
const SELECTED_OPTION = DEFAULT_OPTIONS[0];
+const RENDER_POPUP: SelectRenderPopup = ({renderList}) => renderList();
describe('Select single mode actions', () => {
describe('select option by click in', () => {
test.each([
- [OptionsListType.FLAT, SELECTED_OPTION],
- [OptionsListType.GROUPED, SELECTED_OPTION],
- ])('%s list', async (type, selectedOption) => {
+ [OptionsListType.FLAT, SELECTED_OPTION, RENDER_POPUP],
+ [OptionsListType.GROUPED, SELECTED_OPTION, RENDER_POPUP],
+ [OptionsListType.FLAT, SELECTED_OPTION, undefined],
+ [OptionsListType.GROUPED, SELECTED_OPTION, undefined],
+ ])('%s list', async (type, selectedOption, renderPopup) => {
const options = type === 'grouped' ? GROUPED_OPTIONS : DEFAULT_OPTIONS;
- const {getByTestId, getByText} = setup({options, onUpdate, onOpenChange});
+ const {getByTestId, getByText} = setup({options, onUpdate, onOpenChange, renderPopup});
const user = userEvent.setup();
const selectControl = getByTestId(TEST_QA);
// open select popup
@@ -43,13 +47,17 @@ describe('Select single mode actions', () => {
describe('select option by', () => {
test.each([
- ['Enter', OptionsListType.FLAT, SELECTED_OPTION],
- ['Enter', OptionsListType.GROUPED, SELECTED_OPTION],
- ['Space', OptionsListType.FLAT, SELECTED_OPTION],
- ['Space', OptionsListType.GROUPED, SELECTED_OPTION],
- ])('%s in %s list', async (key, type, selectedOption) => {
+ ['Enter', OptionsListType.FLAT, SELECTED_OPTION, RENDER_POPUP],
+ ['Enter', OptionsListType.GROUPED, SELECTED_OPTION, RENDER_POPUP],
+ ['Space', OptionsListType.FLAT, SELECTED_OPTION, RENDER_POPUP],
+ ['Space', OptionsListType.GROUPED, SELECTED_OPTION, RENDER_POPUP],
+ ['Enter', OptionsListType.FLAT, SELECTED_OPTION, undefined],
+ ['Enter', OptionsListType.GROUPED, SELECTED_OPTION, undefined],
+ ['Space', OptionsListType.FLAT, SELECTED_OPTION, undefined],
+ ['Space', OptionsListType.GROUPED, SELECTED_OPTION, undefined],
+ ])('%s in %s list', async (key, type, selectedOption, renderPopup) => {
const options = type === 'grouped' ? GROUPED_OPTIONS : DEFAULT_OPTIONS;
- const {getByTestId} = setup({options, onUpdate, onOpenChange});
+ const {getByTestId} = setup({options, onUpdate, onOpenChange, renderPopup});
const user = userEvent.setup();
const selectControl = getByTestId(TEST_QA);
await user.keyboard('[Tab]');
diff --git a/src/components/Select/components/SelectFilter/SelectFilter.tsx b/src/components/Select/components/SelectFilter/SelectFilter.tsx
index 74e2beb99a..6923ce0b77 100644
--- a/src/components/Select/components/SelectFilter/SelectFilter.tsx
+++ b/src/components/Select/components/SelectFilter/SelectFilter.tsx
@@ -2,6 +2,7 @@ import React from 'react';
import {TextInput} from '../../../controls';
import {block} from '../../../utils/cn';
+import {SelectQa} from '../../constants';
import type {SelectProps} from '../../types';
import type {SelectFilterRef} from '../../types-misc';
@@ -46,6 +47,7 @@ export const SelectFilter = React.forwardRef
placeholder={placeholder}
onUpdate={onChange}
onKeyDown={onKeyDown}
+ qa={SelectQa.FILTER_INPUT}
/>
);
diff --git a/src/components/Select/constants.ts b/src/components/Select/constants.ts
index 7da7c3f704..abb183aad8 100644
--- a/src/components/Select/constants.ts
+++ b/src/components/Select/constants.ts
@@ -36,4 +36,5 @@ export const SelectQa = {
POPUP: 'select-popup',
SHEET: 'select-sheet',
CLEAR: 'select-clear',
+ FILTER_INPUT: 'select-filter-input',
};
diff --git a/src/components/Select/types.ts b/src/components/Select/types.ts
index 85710fcf5e..e568b030c9 100644
--- a/src/components/Select/types.ts
+++ b/src/components/Select/types.ts
@@ -44,6 +44,11 @@ export type SelectRenderOptionGroup = (
options: SelectRenderOptionViewParams,
) => React.ReactElement;
+export type SelectRenderPopup = (popupItems: {
+ renderFilter: () => React.JSX.Element | null;
+ renderList: () => React.JSX.Element;
+}) => React.ReactElement;
+
export type SelectSize = InputControlSize;
export type SelectProps = QAProps &
@@ -63,6 +68,7 @@ export type SelectProps = QAProps &
renderOptionGroup?: SelectRenderOptionGroup;
renderSelectedOption?: (option: SelectOption, index: number) => React.ReactElement;
renderEmptyOptions?: ({filter}: {filter: string}) => React.ReactElement;
+ renderPopup?: SelectRenderPopup;
getOptionHeight?: (option: SelectOption, index: number) => number;
getOptionGroupHeight?: (option: SelectOptionGroup, index: number) => number;
filterOption?: (option: SelectOption, filter: string) => boolean;