Skip to content

Commit

Permalink
feat(Select): add virtualizationThreshold property
Browse files Browse the repository at this point in the history
  • Loading branch information
korvin89 committed Dec 20, 2022
1 parent 5f96f62 commit 33dc1a0
Show file tree
Hide file tree
Showing 7 changed files with 54 additions and 10 deletions.
1 change: 1 addition & 0 deletions src/components/Select/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
| pin | `string` | `'round-round'` | Control [border view](https://github.com/gravity-ui/uikit/blob/main/src/components/TextInput/types.ts#L8) |
| width | `string \| number` | `undefined` | Control width |
| popupWidth | `number` | `-` | Popup width |
| virtualizationThreshold | `number` | `50` | The threshold of the options count after which virtualization is enabled |
| name | `string` | `-` | Name of the control |
| className | `string` | `-` | Control className |
| label | `string` | `-` | Control label |
Expand Down
5 changes: 3 additions & 2 deletions src/components/Select/Select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import {
} from './utils';
import {SelectControl, SelectPopup, SelectList, SelectFilter, EmptyOptions} from './components';
import {Option, OptionGroup} from './tech-components';
import {VIRTUALIZE_THRESHOLD} from './constants';
import {DEFAULT_VIRTUALIZATION_THRESHOLD} from './constants';

type SelectComponent = React.ForwardRefExoticComponent<
SelectProps & React.RefAttributes<HTMLButtonElement>
Expand Down Expand Up @@ -53,6 +53,7 @@ export const Select = React.forwardRef<HTMLButtonElement, SelectProps>(function
filterPlaceholder,
width,
popupWidth,
virtualizationThreshold = DEFAULT_VIRTUALIZATION_THRESHOLD,
view = 'normal',
size = 'm',
pin = 'round-round',
Expand Down Expand Up @@ -82,7 +83,7 @@ export const Select = React.forwardRef<HTMLButtonElement, SelectProps>(function
})
: flattenOptions;
const optionsText = getOptionsText(flattenOptions, value);
const virtualized = filteredFlattenOptions.length >= VIRTUALIZE_THRESHOLD;
const virtualized = filteredFlattenOptions.length >= virtualizationThreshold;
const listHeight = getListHeight({
options: filteredFlattenOptions,
getOptionHeight,
Expand Down
37 changes: 36 additions & 1 deletion src/components/Select/__tests__/Select.base-actions.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,17 @@ import React from 'react';
import userEvent from '@testing-library/user-event';
import {ListQa} from '../../List';
import {SelectQa} from '..';
import {QUICK_SEARCH_TIMEOUT} from '../constants';
import {QUICK_SEARCH_TIMEOUT, DEFAULT_VIRTUALIZATION_THRESHOLD} from '../constants';
import {
TEST_QA,
DEFAULT_OPTIONS,
QUICK_SEARCH_OPTIONS,
GROUPED_OPTIONS,
GROUPED_QUICK_SEARCH_OPTIONS,
SELECT_LIST_VIRTUALIZED_CLASS,
setup,
timeout,
generateOptions,
} from './utils';

describe('Select base actions', () => {
Expand Down Expand Up @@ -216,4 +218,37 @@ describe('Select base actions', () => {
// active item didn`t changed
expect(selectedItem.textContent).toBe('Value 3');
});

test.each<[number, number | undefined]>([
[DEFAULT_VIRTUALIZATION_THRESHOLD - 1, undefined],
[DEFAULT_VIRTUALIZATION_THRESHOLD, DEFAULT_VIRTUALIZATION_THRESHOLD + 1],
])(
'select list shouldn`t have virtualization',
async (optionsCount, virtualizationThreshold) => {
const {getByTestId} = setup({
options: generateOptions(optionsCount),
virtualizationThreshold,
});
const user = userEvent.setup();
const selectControl = getByTestId(TEST_QA);
await user.click(selectControl);
const selectList = getByTestId(SelectQa.LIST);
expect(selectList).not.toHaveClass(SELECT_LIST_VIRTUALIZED_CLASS);
},
);

test.each<[number, number | undefined]>([
[DEFAULT_VIRTUALIZATION_THRESHOLD, undefined],
[DEFAULT_VIRTUALIZATION_THRESHOLD - 1, DEFAULT_VIRTUALIZATION_THRESHOLD - 2],
])('select list should have virtualization', async (optionsCount, virtualizationThreshold) => {
const {getByTestId} = setup({
options: generateOptions(optionsCount),
virtualizationThreshold,
});
const user = userEvent.setup();
const selectControl = getByTestId(TEST_QA);
await user.click(selectControl);
const selectList = getByTestId(SelectQa.LIST);
expect(selectList).toHaveClass(SELECT_LIST_VIRTUALIZED_CLASS);
});
});
3 changes: 2 additions & 1 deletion src/components/Select/__tests__/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@ import React from 'react';
import {act, render} from '@testing-library/react';
import {range} from 'lodash';
import {Select, SelectProps, SelectOption, SelectOptionGroup} from '..';
import {selectBlock} from '../constants';
import {selectBlock, selectListBlock} from '../constants';

export const OptionsListType = {
FLAT: 'flat',
GROUPED: 'grouped',
} as const;
export const TEST_QA = 'select-test-qa';
export const SELECT_CONTROL_OPEN_CLASS = selectBlock({open: true});
export const SELECT_LIST_VIRTUALIZED_CLASS = selectListBlock({virtualized: true});
export const DEFAULT_OPTIONS = generateOptions([
['js', 'JavaScript'],
['python', 'Python'],
Expand Down
12 changes: 7 additions & 5 deletions src/components/Select/components/SelectList/SelectList.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
import React from 'react';
import {block} from '../../../utils/cn';
import {List} from '../../../List';
import {SelectProps} from '../../types';
import {FlattenOption, getPopupItemHeight} from '../../utils';
import {selectListBlock, SelectQa} from '../../constants';
import {GroupLabel} from './GroupLabel';
import {OptionWrap} from './OptionWrap';

import './SelectList.scss';

const b = block('select-list');

type SelectListProps = {
onOptionClick: (option: FlattenOption) => void;
renderOption?: SelectProps['renderOption'];
Expand Down Expand Up @@ -63,10 +61,14 @@ export const SelectList = React.forwardRef<List<FlattenOption>, SelectListProps>
);

return (
<div className={b({size})} style={{maxHeight: `calc(90vh - ${filterHeight}px)`}}>
<div
className={selectListBlock({size, virtualized})}
style={{maxHeight: `calc(90vh - ${filterHeight}px)`}}
data-qa={SelectQa.LIST}
>
<List
ref={ref}
itemClassName={b('item')}
itemClassName={selectListBlock('item')}
itemHeight={getItemHeight}
itemsHeight={virtualized ? listHeight : undefined}
items={flattenOptions}
Expand Down
5 changes: 4 additions & 1 deletion src/components/Select/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import {SelectProps} from './types';

export const selectBlock = block('select');

export const selectListBlock = block('select-list');

export const SIZE_TO_ITEM_HEIGHT: Record<NonNullable<SelectProps['size']>, number> = {
s: 28,
m: 28,
Expand All @@ -20,8 +22,9 @@ export const POPUP_MIN_WIDTH_IN_VIRTUALIZE_CASE = 100;

export const QUICK_SEARCH_TIMEOUT = 2000;

export const VIRTUALIZE_THRESHOLD = 50;
export const DEFAULT_VIRTUALIZATION_THRESHOLD = 50;

export const SelectQa = {
LIST: 'select-list',
POPUP: 'select-popup',
};
1 change: 1 addition & 0 deletions src/components/Select/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export type SelectProps = QAProps &
pin?: TextInputPin;
width?: 'auto' | 'max' | number;
popupWidth?: number;
virtualizationThreshold?: number;
className?: string;
label?: string;
placeholder?: React.ReactNode;
Expand Down

0 comments on commit 33dc1a0

Please sign in to comment.