diff --git a/src/components/Select/README.md b/src/components/Select/README.md
index ad66ff2cdd..8d7146ee18 100644
--- a/src/components/Select/README.md
+++ b/src/components/Select/README.md
@@ -241,6 +241,45 @@ LANDING_BLOCK-->
+### Counter
+
+You can add counter of the selected items to the component by using property `hasCounter`.
+
+
+
+
+
+```tsx
+
+```
+
+
+
## Filtering options
To enable filter section use the `filterable` property. Default to `false`.
@@ -1079,50 +1118,51 @@ LANDING_BLOCK-->
## Properties
-| Name | Description | Type | Default |
-| :-------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------- | :------------------------------------------------------- |
-| className | Control className | `string` | |
-| defaultValue | Default values that represent selected options in case of using uncontrolled state | `string[]` | |
-| disabled | Indicates that the user cannot interact with the control | `boolean` | `false` |
-| [filterable](#filtering-options) | Indicates that select popup have filter section | `boolean` | `false` |
-| filterOption | Used to compare option with filter | `function` | |
-| filterPlaceholder | Default filter input placeholder text | `string` | |
-| [getOptionHeight](#render-options-with-different-heights) | Used to set height of customized user options | `function` | |
-| getOptionGroupHeight | Used to set height of customized user option group | `function` | |
-| hasClear | Enable displaying icon for clear selected options | `boolean` | `false` |
-| id | HTML `id` attribute | `string` | |
-| label | Control label | `string` | |
-| loading | Add the loading item to the end of the options list. Works like persistant loading indicator while the options list is empty. | `boolean` | |
-| [multiple](#selecting-multiple-options) | Indicates that multiple options can be selected in the list | `boolean` | `false` |
-| name | Name of the control | `string` | |
-| onBlur | Handler that is called when the element loses focus. | `function` | |
-| onFilterChange | Fires every time after changing filter | `function` | |
-| onFocus | Handler that is called when the element receives focus. | `function` | |
-| onLoadMore | Fires when loading indicator gets visible. | `function` | |
-| onOpenChange | Fires every time after changing popup visibility | `function` | |
-| onUpdate | Fires when an alteration to the Select value is committed by the user | `function` | |
-| [options](#options) | Options to select | `(SelectOption \| SelectOptionGroup)[]` | |
-| pin | Control border view | `string` | `'round-round'` |
-| placeholder | Placeholder text | `string` | |
-| popupClassName | Popup with options list className | `string` | |
-| popupPlacement | `Popper.js` placement | `PopupPlacement` `Array` | `['bottom-start', 'bottom-end', 'top-start', 'top-end']` |
-| [popupWidth](#popup-width) | Popup width | `number \| 'fit' \| 'outfit'` | `'outfit'` |
-| qa | Test id attribute (`data-qa`) | `string` | |
-| [renderControl](#render-custom-control) | Used to render user control | `function` | |
-| renderEmptyOptions | Used to render node for an empty options list | `function` | |
-| [renderFilter](#render-custom-filter-section) | Used to render user filter section | `function` | |
-| [renderOption](#render-custom-options) | Used to render user options | `function` | |
-| renderOptionGroup | Used to render user option groups | `function` | |
-| [renderSelectedOption](#render-custom-selected-options) | Used to render user selected options | `function` | |
-| [renderPopup](#render-custom-popup) | Used to render user popup content | `function` | |
-| [size](#size) | Control / options size | `string` | `'m'` |
-| value | Values that represent selected options | `string[]` | |
-| view | Control view | `string` | `'normal'` |
-| [virtualizationThreshold](#virtualized-list) | The threshold of the options count after which virtualization is enabled | `number` | `50` |
-| [width](#control-width) | Control width | `string \| number` | `undefined` |
-| errorMessage | Error text | `string` | |
-| errorPlacement | Error placement | `outside` `inside` | `outside` |
-| validationState | Validation state | `"invalid"` | |
+| Name | Description | Type | Default |
+| :-------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------- | :------------------------------------------------------- |
+| className | Control className | `string` | |
+| defaultValue | Default values that represent selected options in case of using uncontrolled state | `string[]` | |
+| disabled | Indicates that the user cannot interact with the control | `boolean` | `false` |
+| [filterable](#filtering-options) | Indicates that select popup have filter section | `boolean` | `false` |
+| filterOption | Used to compare option with filter | `function` | |
+| filterPlaceholder | Default filter input placeholder text | `string` | |
+| [getOptionHeight](#render-options-with-different-heights) | Used to set height of customized user options | `function` | |
+| getOptionGroupHeight | Used to set height of customized user option group | `function` | |
+| hasClear | Enable displaying icon for clear selected options | `boolean` | `false` |
+| id | HTML `id` attribute | `string` | |
+| label | Control label | `string` | |
+| loading | Add the loading item to the end of the options list. Works like persistant loading indicator while the options list is empty. | `boolean` | |
+| [multiple](#selecting-multiple-options) | Indicates that multiple options can be selected in the list | `boolean` | `false` |
+| name | Name of the control | `string` | |
+| onBlur | Handler that is called when the element loses focus. | `function` | |
+| onFilterChange | Fires every time after changing filter | `function` | |
+| onFocus | Handler that is called when the element receives focus. | `function` | |
+| onLoadMore | Fires when loading indicator gets visible. | `function` | |
+| onOpenChange | Fires every time after changing popup visibility | `function` | |
+| onUpdate | Fires when an alteration to the Select value is committed by the user | `function` | |
+| [options](#options) | Options to select | `(SelectOption \| SelectOptionGroup)[]` | |
+| pin | Control border view | `string` | `'round-round'` |
+| placeholder | Placeholder text | `string` | |
+| popupClassName | Popup with options list className | `string` | |
+| popupPlacement | `Popper.js` placement | `PopupPlacement` `Array` | `['bottom-start', 'bottom-end', 'top-start', 'top-end']` |
+| [popupWidth](#popup-width) | Popup width | `number \| 'fit' \| 'outfit'` | `'outfit'` |
+| qa | Test id attribute (`data-qa`) | `string` | |
+| [renderControl](#render-custom-control) | Used to render user control | `function` | |
+| renderEmptyOptions | Used to render node for an empty options list | `function` | |
+| [renderFilter](#render-custom-filter-section) | Used to render user filter section | `function` | |
+| [renderOption](#render-custom-options) | Used to render user options | `function` | |
+| renderOptionGroup | Used to render user option groups | `function` | |
+| [renderSelectedOption](#render-custom-selected-options) | Used to render user selected options | `function` | |
+| [renderPopup](#render-custom-popup) | Used to render user popup content | `function` | |
+| [size](#size) | Control / options size | `string` | `'m'` |
+| value | Values that represent selected options | `string[]` | |
+| view | Control view | `string` | `'normal'` |
+| [virtualizationThreshold](#virtualized-list) | The threshold of the options count after which virtualization is enabled | `number` | `50` |
+| [width](#control-width) | Control width | `string \| number` | `undefined` |
+| errorMessage | Error text | `string` | |
+| errorPlacement | Error placement | `outside` `inside` | `outside` |
+| validationState | Validation state | `"invalid"` | |
+| [hasCounter](#counter) | Indicates count of the selected options. Counter appears only when [multiple](#selecting-multiple-options) selection enabled. state | `boolean` |
## CSS API
diff --git a/src/components/Select/Select.tsx b/src/components/Select/Select.tsx
index 5bd434b314..9ee92b4d17 100644
--- a/src/components/Select/Select.tsx
+++ b/src/components/Select/Select.tsx
@@ -87,6 +87,8 @@ export const Select = React.forwardRef(function
hasClear = false,
onClose,
id,
+ hasCounter,
+ renderCounter,
title,
} = props;
const mobile = useMobile();
@@ -339,6 +341,8 @@ export const Select = React.forwardRef(function
popupId={`select-popup-${selectId}`}
selectId={`select-${selectId}`}
activeIndex={activeIndex}
+ hasCounter={multiple && hasCounter}
+ renderCounter={renderCounter}
title={title}
/>
diff --git a/src/components/Select/components/SelectControl/SelectControl.tsx b/src/components/Select/components/SelectControl/SelectControl.tsx
index d510d07d07..09d361cf9b 100644
--- a/src/components/Select/components/SelectControl/SelectControl.tsx
+++ b/src/components/Select/components/SelectControl/SelectControl.tsx
@@ -14,14 +14,17 @@ import type {
SelectRenderClearArgs,
SelectRenderControl,
SelectRenderControlProps,
+ SelectRenderCounter,
} from '../../types';
import {SelectClear} from '../SelectClear/SelectClear';
+import {SelectCounter} from '../SelectCounter/SelectCounter';
import './SelectControl.scss';
type ControlProps = {
toggleOpen: () => void;
renderControl?: SelectRenderControl;
+ renderCounter?: SelectRenderCounter;
view: NonNullable;
size: NonNullable;
pin: NonNullable;
@@ -37,8 +40,9 @@ type ControlProps = {
value: SelectProps['value'];
clearValue: () => void;
hasClear?: boolean;
+ hasCounter?: boolean;
title?: string;
-} & Omit;
+} & Omit;
export const SelectControl = React.forwardRef((props, ref) => {
const {
@@ -64,6 +68,8 @@ export const SelectControl = React.forwardRef((
popupId,
selectId,
activeIndex,
+ renderCounter,
+ hasCounter,
title,
} = props;
const showOptionsText = Boolean(selectedOptionsContent);
@@ -105,6 +111,17 @@ export const SelectControl = React.forwardRef((
clearValue();
}, [clearValue]);
+ const renderCounterComponent = () => {
+ if (!hasCounter) {
+ return null;
+ }
+ const count = Number(value?.length) || 0;
+ const counterComponent = ;
+ return renderCounter
+ ? renderCounter(counterComponent, {count, size, disabled})
+ : counterComponent;
+ };
+
const renderClearIcon = (args: SelectRenderClearArgs) => {
const hideOnEmpty = !value?.[0];
if (!hasClear || !clearValue || hideOnEmpty || disabled) {
@@ -128,6 +145,7 @@ export const SelectControl = React.forwardRef((
onClear: clearValue,
onClick: toggleOpen,
renderClear: (arg) => renderClearIcon(arg),
+ renderCounter: renderCounterComponent,
ref,
open: Boolean(open),
popupId,
@@ -171,6 +189,7 @@ export const SelectControl = React.forwardRef((
)}
+ {renderCounterComponent()}
{renderClearIcon({})}
{errorMessage && (
diff --git a/src/components/Select/components/SelectCounter/SelectCounter.scss b/src/components/Select/components/SelectCounter/SelectCounter.scss
new file mode 100644
index 0000000000..fa092a5c22
--- /dev/null
+++ b/src/components/Select/components/SelectCounter/SelectCounter.scss
@@ -0,0 +1,48 @@
+@use '../../../variables';
+@use '../../variables.scss' as select-css-variables;
+
+$block: '.#{variables.$ns}select-counter';
+
+#{$block} {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ margin-inline: 4px;
+
+ background-color: var(--g-color-base-generic);
+
+ &__text {
+ margin-inline: 4px;
+ flex-grow: 1;
+ text-align: center;
+ }
+
+ &_size_xl &__text {
+ margin-inline: 6px;
+ }
+
+ &_size {
+ &_s {
+ border-radius: var(--g-border-radius-xs);
+ height: 20px;
+ min-width: 20px;
+ }
+ &_m {
+ border-radius: var(--g-border-radius-s);
+ height: 24px;
+ min-width: 24px;
+ }
+ &_l {
+ border-radius: var(--g-border-radius-m);
+ height: 28px;
+ min-width: 28px;
+ }
+
+ &_xl {
+ border-radius: var(--g-border-radius-l);
+ margin-inline: 4px;
+ height: 36px;
+ min-width: 36px;
+ }
+ }
+}
diff --git a/src/components/Select/components/SelectCounter/SelectCounter.tsx b/src/components/Select/components/SelectCounter/SelectCounter.tsx
new file mode 100644
index 0000000000..aa9ccfbe17
--- /dev/null
+++ b/src/components/Select/components/SelectCounter/SelectCounter.tsx
@@ -0,0 +1,26 @@
+import React from 'react';
+
+import {Text} from '../../../Text';
+import {block} from '../../../utils/cn';
+import type {SelectCounterProps} from '../../types';
+
+import './SelectCounter.scss';
+
+const b = block('select-counter');
+
+export const SelectCounter = React.forwardRef(function SelectCouner(
+ {count, size, disabled}: SelectCounterProps,
+ ref: React.ForwardedRef,
+) {
+ return (
+
+
+ {count}
+
+
+ );
+});
diff --git a/src/components/Select/types.ts b/src/components/Select/types.ts
index d1cdc7e18f..3aed92c62a 100644
--- a/src/components/Select/types.ts
+++ b/src/components/Select/types.ts
@@ -16,6 +16,7 @@ export type SelectRenderControlProps = {
onClick: () => void;
onKeyDown?: (e: React.KeyboardEvent) => void;
renderClear?: (args: SelectRenderClearArgs) => React.ReactNode;
+ renderCounter?: () => React.ReactNode;
ref: React.Ref;
open: boolean;
popupId: string;
@@ -51,6 +52,11 @@ export type SelectRenderPopup = (popupItems: {
export type SelectSize = InputControlSize;
+export type SelectRenderCounter = (
+ originalComponent: React.ReactElement,
+ counterProps: SelectCounterProps,
+) => React.ReactElement;
+
export type SelectProps = QAProps &
Pick &
UseOpenProps & {
@@ -69,6 +75,7 @@ export type SelectProps = QAProps &
renderSelectedOption?: (option: SelectOption, index: number) => React.ReactElement;
renderEmptyOptions?: ({filter}: {filter: string}) => React.ReactElement;
renderPopup?: SelectRenderPopup;
+ renderCounter?: SelectRenderCounter;
getOptionHeight?: (option: SelectOption, index: number) => number;
getOptionGroupHeight?: (option: SelectOptionGroup, index: number) => number;
filterOption?: (option: SelectOption, filter: string) => boolean;
@@ -112,6 +119,8 @@ export type SelectProps = QAProps &
| React.ReactElement, typeof OptionGroup>
| React.ReactElement, typeof OptionGroup>[];
id?: string;
+ /**Shows selected options count if multiple selection is avalable */
+ hasCounter?: boolean;
title?: string;
};
@@ -148,4 +157,13 @@ export type SelectClearProps = SelectClearIconProps & {
onMouseLeave: (e: React.MouseEvent) => void;
};
+export type SelectCounterProps = {
+ /** amount of selected elements to show */
+ count: number;
+ /** size of the parent element */
+ size: SelectSize;
+ /** disabled state of the parent element*/
+ disabled?: boolean;
+};
+
export type SelectOptions = NonNullable['options']>;