Skip to content

Commit

Permalink
feat(Select): loading indicator (#845)
Browse files Browse the repository at this point in the history
Co-authored-by: Yevhenii Chernovol <[email protected]>
  • Loading branch information
imechoim and imechoim authored Jul 27, 2023
1 parent 8273d91 commit ee900a8
Show file tree
Hide file tree
Showing 6 changed files with 71 additions and 36 deletions.
65 changes: 33 additions & 32 deletions src/components/Select/README.md
Original file line number Diff line number Diff line change
@@ -1,35 +1,36 @@
| Property | Type | Default | Description |
| :------------------------------ | :-------------------------------------- | :-------------- | :--------------------------------------------------------------------------------------------------------- |
| onUpdate | `function` | `-` | Fires when an alteration to the Select value is committed by the user |
| onOpenChange | `function` | `-` | Fires every time after changing popup visibility |
| onFilterChange | `function` | `-` | Fires every time after changing filter |
| filterOption | `function` | `-` | Used to compare option with filter |
| [renderControl](#rendercontrol) | `function` | `-` | Used to render user control |
| [renderFilter](#renderfilter) | `function` | `-` | Used to render user filter section |
| renderOption | `function` | `-` | Used to render user options |
| renderSelectedOption | `function` | `-` | Used to render user selected options |
| renderEmptyOptions | `function` | `-` | Used to render node for an empty options list |
| getOptionHeight | `function` | `-` | Used to set height of customized user options |
| [options](#options) | `(SelectOption \| SelectOptionGroup)[]` | `-` | Options to select |
| view | `string` | `'normal'` | Control [view](https://github.com/gravity-ui/uikit/blob/main/src/components/TextInput/types.ts#L4) |
| size | `string` | `'m'` | Control/options [size](https://github.com/gravity-ui/uikit/blob/main/src/components/TextInput/types.ts#L6) |
| 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 |
| popupClassName | `string` | `-` | Popup with options list className |
| label | `string` | `-` | Control label |
| placeholder | `string` | `-` | Placeholder text |
| filterPlaceholder | `string` | `-` | Default filter input placeholder text |
| value | `string[]` | `-` | Values that represent selected options |
| defaultValue | `string[]` | `-` | Default values that represent selected options in case of using uncontrolled state |
| qa | `string` | `-` | Test id attribute (`data-qa`) |
| multiple | `boolean` | `false` | Indicates that multiple options can be selected in the list |
| filterable | `boolean` | `false` | Indicates that select popup have filter section |
| disabled | `boolean` | `false` | Indicates that the user cannot interact with the control |
| hasClear | `boolean` | `false` | Enable displaying icon for clear selected options |
| Property | Type | Default | Description |
| :------------------------------ | :-------------------------------------- | :-------------- | :---------------------------------------------------------------------------------------------------------------------------- |
| onUpdate | `function` | `-` | Fires when an alteration to the Select value is committed by the user |
| onOpenChange | `function` | `-` | Fires every time after changing popup visibility |
| onFilterChange | `function` | `-` | Fires every time after changing filter |
| filterOption | `function` | `-` | Used to compare option with filter |
| [renderControl](#rendercontrol) | `function` | `-` | Used to render user control |
| [renderFilter](#renderfilter) | `function` | `-` | Used to render user filter section |
| renderOption | `function` | `-` | Used to render user options |
| renderSelectedOption | `function` | `-` | Used to render user selected options |
| renderEmptyOptions | `function` | `-` | Used to render node for an empty options list |
| getOptionHeight | `function` | `-` | Used to set height of customized user options |
| [options](#options) | `(SelectOption \| SelectOptionGroup)[]` | `-` | Options to select |
| view | `string` | `'normal'` | Control [view](https://github.com/gravity-ui/uikit/blob/main/src/components/TextInput/types.ts#L4) |
| size | `string` | `'m'` | Control/options [size](https://github.com/gravity-ui/uikit/blob/main/src/components/TextInput/types.ts#L6) |
| 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 |
| popupClassName | `string` | `-` | Popup with options list className |
| label | `string` | `-` | Control label |
| placeholder | `string` | `-` | Placeholder text |
| filterPlaceholder | `string` | `-` | Default filter input placeholder text |
| value | `string[]` | `-` | Values that represent selected options |
| defaultValue | `string[]` | `-` | Default values that represent selected options in case of using uncontrolled state |
| qa | `string` | `-` | Test id attribute (`data-qa`) |
| multiple | `boolean` | `false` | Indicates that multiple options can be selected in the list |
| filterable | `boolean` | `false` | Indicates that select popup have filter section |
| disabled | `boolean` | `false` | Indicates that the user cannot interact with the control |
| hasClear | `boolean` | `false` | Enable displaying icon for clear selected options |
| loading | `boolean` | `-` | Add the loading item to the end of the options list. Works like persistant loading indicator while the options list is empty. |

---

Expand Down
3 changes: 2 additions & 1 deletion src/components/Select/Select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ export const Select = React.forwardRef<HTMLButtonElement, SelectProps>(function
renderFilter={renderFilter}
/>
)}
{filteredFlattenOptions.length ? (
{filteredFlattenOptions.length || props.loading ? (
<SelectList
ref={listRef}
size={size}
Expand All @@ -266,6 +266,7 @@ export const Select = React.forwardRef<HTMLButtonElement, SelectProps>(function
onOptionClick={handleOptionClick}
renderOption={renderOption}
getOptionHeight={getOptionHeight}
loading={props.loading}
/>
) : (
<EmptyOptions filter={filter} renderEmptyOptions={renderEmptyOptions} />
Expand Down
7 changes: 7 additions & 0 deletions src/components/Select/components/SelectList/SelectList.scss
Original file line number Diff line number Diff line change
Expand Up @@ -175,4 +175,11 @@ $xl-right-padding: '12px';
visibility: visible;
}
}

&__loading-indicator {
display: flex;
width: 100%;
align-items: center;
justify-content: center;
}
}
19 changes: 16 additions & 3 deletions src/components/Select/components/SelectList/SelectList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ import React from 'react';
import {List} from '../../../List';
import {SelectQa, selectListBlock} from '../../constants';
import type {SelectOption, SelectProps} from '../../types';
import {getOptionsHeight, getPopupItemHeight} from '../../utils';
import type {FlattenOption} from '../../utils';
import {getOptionsHeight, getPopupItemHeight} from '../../utils';

import {GroupLabel} from './GroupLabel';
import {OptionWrap} from './OptionWrap';
import {SelectLoadingIndicator} from './SelectLoadingIndicator';

import './SelectList.scss';

Expand All @@ -21,8 +22,11 @@ type SelectListProps = {
flattenOptions: FlattenOption[];
multiple?: boolean;
virtualized?: boolean;
loading?: boolean;
};

const loadingOption = {value: '__SELECT_LIST_ITEM_LOADING__', disabled: true};

export const SelectList = React.forwardRef<List<FlattenOption>, SelectListProps>((props, ref) => {
const {
onOptionClick,
Expand All @@ -34,9 +38,15 @@ export const SelectList = React.forwardRef<List<FlattenOption>, SelectListProps>
multiple,
virtualized,
mobile,
loading,
} = props;
const items = React.useMemo(
() => (loading ? [...flattenOptions, loadingOption] : flattenOptions),
[flattenOptions, loading],
);

const optionsHeight = getOptionsHeight({
options: flattenOptions,
options: items,
getOptionHeight,
size,
mobile,
Expand All @@ -54,6 +64,9 @@ export const SelectList = React.forwardRef<List<FlattenOption>, SelectListProps>
if ('label' in option) {
return <GroupLabel label={option.label} />;
}
if (option.value === loadingOption.value) {
return <SelectLoadingIndicator />;
}

const wrappedRenderOption = renderOption
? (option: SelectOption) => {
Expand Down Expand Up @@ -81,7 +94,7 @@ export const SelectList = React.forwardRef<List<FlattenOption>, SelectListProps>
itemClassName={selectListBlock('item')}
itemHeight={getItemHeight}
itemsHeight={virtualized ? optionsHeight : undefined}
items={flattenOptions}
items={items}
filterable={false}
virtualized={virtualized}
renderItem={renderItem}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import React from 'react';

import {Loader} from '../../../Loader/Loader';
import {selectListBlock} from '../../constants';

export const SelectLoadingIndicator = () => {
return (
<div className={selectListBlock('loading-indicator')}>
<Loader />
</div>
);
};
1 change: 1 addition & 0 deletions src/components/Select/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ export type SelectProps<T = any> = QAProps &
filterable?: boolean;
disablePortal?: boolean;
hasClear?: boolean;
loading?: boolean;
children?:
| React.ReactElement<SelectOption<T>, typeof Option>
| React.ReactElement<SelectOption<T>, typeof Option>[]
Expand Down

0 comments on commit ee900a8

Please sign in to comment.