From da745613cb1b20bd4ee9bfa82ad7e06b5a6482a4 Mon Sep 17 00:00:00 2001 From: Vova Kulikov Date: Tue, 17 Jan 2023 22:07:16 -0300 Subject: [PATCH] Batch Changes: Replace MultiSelect with wildcard MultiCombobox UI (#46450) * Add first version of MultiCombobox UI * Fix problem with value set in combobox input * Replace multiselect with MultiCombobox UI on the batch changes list page * Remove react-select package * Add custom styles to batch changes list filter UI * Bring Input and Select types back * Fix no state option * Fix by review comments * Clear out the search input after you picked option --- .../settings/temporary/TemporarySettings.ts | 12 +- .../list/BatchChangeListFilter.module.scss | 8 + .../batches/list/BatchChangeListFilters.tsx | 115 +++++++++---- .../batches/list/BatchChangeListPage.tsx | 10 +- .../batches/list/useBatchChangeListFilters.ts | 83 ++++++---- .../Combobox/MultiCombobox.module.scss | 7 + .../src/components/Combobox/MultiCombobox.tsx | 15 +- .../wildcard/src/components/Combobox/index.ts | 1 + .../Form/MultiSelect/ClearIndicator.tsx | 19 --- .../Form/MultiSelect/DropdownIndicator.tsx | 24 --- .../Form/MultiSelect/MultiSelect.module.scss | 62 ------- .../Form/MultiSelect/MultiSelect.story.tsx | 95 ----------- .../Form/MultiSelect/MultiSelect.tsx | 76 --------- .../Form/MultiSelect/MultiValueContainer.tsx | 16 -- .../Form/MultiSelect/MultiValueLabel.tsx | 16 -- .../Form/MultiSelect/MultiValueRemove.tsx | 19 --- .../src/components/Form/MultiSelect/index.ts | 2 - .../src/components/Form/MultiSelect/styles.ts | 129 --------------- .../src/components/Form/MultiSelect/theme.ts | 52 ------ .../src/components/Form/MultiSelect/types.ts | 31 ---- client/wildcard/src/components/Form/index.ts | 1 - client/wildcard/src/components/index.ts | 4 +- package.json | 1 - pnpm-lock.yaml | 153 +----------------- 24 files changed, 187 insertions(+), 764 deletions(-) create mode 100644 client/web/src/enterprise/batches/list/BatchChangeListFilter.module.scss delete mode 100644 client/wildcard/src/components/Form/MultiSelect/ClearIndicator.tsx delete mode 100644 client/wildcard/src/components/Form/MultiSelect/DropdownIndicator.tsx delete mode 100644 client/wildcard/src/components/Form/MultiSelect/MultiSelect.module.scss delete mode 100644 client/wildcard/src/components/Form/MultiSelect/MultiSelect.story.tsx delete mode 100644 client/wildcard/src/components/Form/MultiSelect/MultiSelect.tsx delete mode 100644 client/wildcard/src/components/Form/MultiSelect/MultiValueContainer.tsx delete mode 100644 client/wildcard/src/components/Form/MultiSelect/MultiValueLabel.tsx delete mode 100644 client/wildcard/src/components/Form/MultiSelect/MultiValueRemove.tsx delete mode 100644 client/wildcard/src/components/Form/MultiSelect/index.ts delete mode 100644 client/wildcard/src/components/Form/MultiSelect/styles.ts delete mode 100644 client/wildcard/src/components/Form/MultiSelect/theme.ts delete mode 100644 client/wildcard/src/components/Form/MultiSelect/types.ts diff --git a/client/shared/src/settings/temporary/TemporarySettings.ts b/client/shared/src/settings/temporary/TemporarySettings.ts index a7422a9ccc5..e0939414e38 100644 --- a/client/shared/src/settings/temporary/TemporarySettings.ts +++ b/client/shared/src/settings/temporary/TemporarySettings.ts @@ -1,7 +1,5 @@ import { Optional } from 'utility-types' -import { MultiSelectState } from '@sourcegraph/wildcard' - import { BatchChangeState } from '../../graphql-operations' import { DiffMode } from './diffMode' @@ -9,6 +7,14 @@ import { RecentSearch } from './recentSearches' import { SectionID, NoResultsSectionID } from './searchSidebar' import { TourListState } from './tourState' +// Prior to this type we store in settings list of MultiSelectState +// we no longer use MultiSelect UI but for backward compatibility we still +// have to store and parse the old version of batch changes filters +export interface LegacyBatchChangesFilter { + label: string + value: BatchChangeState +} + /** * Schema for temporary settings. */ @@ -31,7 +37,7 @@ export interface TemporarySettingsSchema { 'user.themePreference': string 'signup.finishedWelcomeFlow': boolean 'homepage.userInvites.tab': number - 'batches.defaultListFilters': MultiSelectState + 'batches.defaultListFilters': LegacyBatchChangesFilter[] 'batches.downloadSpecModalDismissed': boolean 'codeintel.badge.used': boolean 'codeintel.referencePanel.redesign.ctaDismissed': boolean diff --git a/client/web/src/enterprise/batches/list/BatchChangeListFilter.module.scss b/client/web/src/enterprise/batches/list/BatchChangeListFilter.module.scss new file mode 100644 index 00000000000..47f413f7c6b --- /dev/null +++ b/client/web/src/enterprise/batches/list/BatchChangeListFilter.module.scss @@ -0,0 +1,8 @@ +.root { + display: flex; + align-items: center; + gap: 0.5rem; + + // Normalize font-weight within Label element + font-weight: normal; +} diff --git a/client/web/src/enterprise/batches/list/BatchChangeListFilters.tsx b/client/web/src/enterprise/batches/list/BatchChangeListFilters.tsx index 246ca992592..5e529e3b018 100644 --- a/client/web/src/enterprise/batches/list/BatchChangeListFilters.tsx +++ b/client/web/src/enterprise/batches/list/BatchChangeListFilters.tsx @@ -1,37 +1,96 @@ -import React from 'react' +import { FC, useCallback, useId, useState } from 'react' -import { H3, H4, MultiSelect, MultiSelectOption, MultiSelectProps } from '@sourcegraph/wildcard' +import classNames from 'classnames' +import { upperFirst } from 'lodash' + +import { + H3, + H4, + Label, + MultiCombobox, + MultiComboboxInput, + MultiComboboxPopover, + MultiComboboxList, + MultiComboboxEmptyList, + MultiComboboxOption, +} from '@sourcegraph/wildcard' import { BatchChangeState } from '../../../graphql-operations' -export const OPEN_STATUS: MultiSelectOption = { label: 'Open', value: BatchChangeState.OPEN } -export const DRAFT_STATUS: MultiSelectOption = { label: 'Draft', value: BatchChangeState.DRAFT } -export const CLOSED_STATUS: MultiSelectOption = { label: 'Closed', value: BatchChangeState.CLOSED } +import styles from './BatchChangeListFilter.module.scss' -export const STATUS_OPTIONS: MultiSelectOption[] = [OPEN_STATUS, DRAFT_STATUS, CLOSED_STATUS] -// Drafts are a new feature of severside execution that for now should not be shown if -// execution is not enabled. -const STATUS_OPTIONS_NO_DRAFTS: MultiSelectOption[] = [OPEN_STATUS, CLOSED_STATUS] +/** Returns string with capitalized first letter */ +const format = (filter: BatchChangeState): string => upperFirst(filter.toLowerCase()) -interface BatchChangeListFiltersProps - extends Required>, 'onChange' | 'value'>> { +interface BatchChangeListFiltersProps { + filters: BatchChangeState[] + selectedFilters: BatchChangeState[] + onFiltersChange: (filters: BatchChangeState[]) => void className?: string - isExecutionEnabled: boolean } -export const BatchChangeListFilters: React.FunctionComponent> = ({ - isExecutionEnabled, - ...props -}) => ( - <> - {/* TODO: This should be a proper label. MultiSelect currently doesn't support that being inline though, so this is for later. */} -

- Status -

- - -) +export const BatchChangeListFilters: FC = props => { + const { filters, selectedFilters, onFiltersChange, className } = props + + const id = useId() + const [searchTerm, setSearchTerm] = useState('') + + const handleFilterChange = useCallback( + (newFilters: BatchChangeState[]) => { + if (newFilters.length > selectedFilters.length) { + // Reset value when we add new filter + // see https://github.com/sourcegraph/sourcegraph/pull/46450#discussion_r1070840089 + setSearchTerm('') + } + + onFiltersChange(newFilters) + }, + [selectedFilters, onFiltersChange] + ) + + // Render only non-selected filters and filters that match with search term value + const suggestions = filters.filter( + filter => !selectedFilters.includes(filter) && filter.toLowerCase().includes(searchTerm.toLowerCase()) + ) + + return ( + + ) +} diff --git a/client/web/src/enterprise/batches/list/BatchChangeListPage.tsx b/client/web/src/enterprise/batches/list/BatchChangeListPage.tsx index df217ab9f70..17add774b65 100644 --- a/client/web/src/enterprise/batches/list/BatchChangeListPage.tsx +++ b/client/web/src/enterprise/batches/list/BatchChangeListPage.tsx @@ -83,7 +83,7 @@ export const BatchChangeListPage: React.FunctionComponent( openTab ?? (isSourcegraphDotCom ? 'gettingStarted' : 'batchChanges') ) @@ -118,7 +118,7 @@ export const BatchChangeListPage: React.FunctionComponent {error && } diff --git a/client/web/src/enterprise/batches/list/useBatchChangeListFilters.ts b/client/web/src/enterprise/batches/list/useBatchChangeListFilters.ts index 54abc2126d5..4e0afdc6f95 100644 --- a/client/web/src/enterprise/batches/list/useBatchChangeListFilters.ts +++ b/client/web/src/enterprise/batches/list/useBatchChangeListFilters.ts @@ -1,64 +1,85 @@ -import { useCallback, useEffect, useMemo, useState } from 'react' +import { useCallback, useEffect, useState } from 'react' -import { lowerCase } from 'lodash' import { useHistory } from 'react-router' +import { LegacyBatchChangesFilter } from '@sourcegraph/shared/src/settings/temporary/TemporarySettings' import { useTemporarySetting } from '@sourcegraph/shared/src/settings/temporary/useTemporarySetting' -import { MultiSelectState } from '@sourcegraph/wildcard' import { BatchChangeState } from '../../../graphql-operations' -import { STATUS_OPTIONS } from './BatchChangeListFilters' +const STATUS_OPTIONS = [BatchChangeState.OPEN, BatchChangeState.DRAFT, BatchChangeState.CLOSED] -const statesToFilters = (states: string[]): MultiSelectState => - STATUS_OPTIONS.filter(option => states.map(lowerCase).includes(option.value.toLowerCase())) +// Drafts are a new feature of serverside execution that for now should not be shown if +// execution is not enabled. +const STATUS_OPTIONS_NO_DRAFTS: BatchChangeState[] = [BatchChangeState.OPEN, BatchChangeState.CLOSED] -const filtersToStates = (filters: MultiSelectState): BatchChangeState[] => - filters.map(filter => filter.value) +const fromLegacyFilters = (legacyFilters: LegacyBatchChangesFilter[]): BatchChangeState[] => + legacyFilters.map(legacyFilter => legacyFilter.value) + +const toLegacyFilters = (filters: BatchChangeState[]): LegacyBatchChangesFilter[] => + filters.map(filter => ({ label: filter.toString(), value: filter })) + +interface UseBatchChangeListFiltersProps { + isExecutionEnabled: boolean +} interface UseBatchChangeListFiltersResult { /** State representing the different filters selected in the `MultiSelect` UI. */ - selectedFilters: MultiSelectState + selectedFilters: BatchChangeState[] + /** Method to set the filters selected in the `MultiSelect`. */ - setSelectedFilters: (filters: MultiSelectState) => void + setSelectedFilters: (filters: BatchChangeState[]) => void + /** - * Array of raw `BatchChangeState`s corresponding to `selectedFilters`, i.e. for - * passing in GraphQL connection query parameters. + * List of available batch changes filters, it may be different based on + * {@link isExecutionEnabled} prop */ - selectedStates: BatchChangeState[] + availableFilters: BatchChangeState[] } /** - * Custom hook for managing, persisting, and transforming the state options selected from - * the `MultiSelect` UI to filter a list of batch changes. + * Custom hook for managing and persisting filter options selected from + * the MultiCombobox UI to filter a list of batch changes. */ -export const useBatchChangeListFilters = (): UseBatchChangeListFiltersResult => { +export const useBatchChangeListFilters = (props: UseBatchChangeListFiltersProps): UseBatchChangeListFiltersResult => { + const { isExecutionEnabled } = props + const history = useHistory() + const availableFilters = isExecutionEnabled ? STATUS_OPTIONS : STATUS_OPTIONS_NO_DRAFTS // NOTE: Fetching this setting is an async operation, so we can't use it as the // initial value for `useState`. Instead, we will set the value of the filter state in // a `useEffect` hook once we've loaded it. const [defaultFilters, setDefaultFilters] = useTemporarySetting('batches.defaultListFilters', []) - const [selectedFilters, setSelectedFiltersRaw] = useState>(() => { + const [hasModifiedFilters, setHasModifiedFilters] = useState(false) + const [selectedFilters, setSelectedFiltersRaw] = useState(() => { const searchParameters = new URLSearchParams(history.location.search).get('states') + if (searchParameters) { - return statesToFilters(searchParameters.split(',')) + const loweredCaseFilters = new Set(availableFilters.map(filter => filter.toLowerCase())) + const urlFilters = searchParameters.split(',').map(option => option.toLowerCase()) + + return urlFilters + .filter(urlFilter => loweredCaseFilters.has(urlFilter)) + .map(urlFilters => urlFilters.toUpperCase() as BatchChangeState) } + return [] }) - const [hasModifiedFilters, setHasModifiedFilters] = useState(false) - const setSelectedFilters = useCallback( - (filters: MultiSelectState) => { - setHasModifiedFilters(true) - setSelectedFiltersRaw(filters) - setDefaultFilters(filters) - + (filters: BatchChangeState[]) => { const searchParameters = new URLSearchParams(history.location.search) + if (filters.length > 0) { - searchParameters.set('states', filtersToStates(filters).join(',').toLowerCase()) + searchParameters.set( + 'states', + filters + .map(filter => filter.toLowerCase()) + .join(',') + .toLowerCase() + ) } else { searchParameters.delete('states') } @@ -66,6 +87,10 @@ export const useBatchChangeListFilters = (): UseBatchChangeListFiltersResult => if (history.location.search !== searchParameters.toString()) { history.replace({ ...history.location, search: searchParameters.toString() }) } + + setHasModifiedFilters(true) + setSelectedFiltersRaw(filters) + setDefaultFilters(toLegacyFilters(filters)) }, [setDefaultFilters, history] ) @@ -77,15 +102,13 @@ export const useBatchChangeListFilters = (): UseBatchChangeListFiltersResult => const searchParameters = new URLSearchParams(history.location.search).get('states') if (defaultFilters && !hasModifiedFilters && !searchParameters) { - setSelectedFiltersRaw(defaultFilters) + setSelectedFiltersRaw(fromLegacyFilters(defaultFilters)) } }, [defaultFilters, hasModifiedFilters, history.location.search]) - const selectedStates = useMemo(() => filtersToStates(selectedFilters), [selectedFilters]) - return { selectedFilters, setSelectedFilters, - selectedStates, + availableFilters, } } diff --git a/client/wildcard/src/components/Combobox/MultiCombobox.module.scss b/client/wildcard/src/components/Combobox/MultiCombobox.module.scss index 2520eabdbcd..63e20e8292b 100644 --- a/client/wildcard/src/components/Combobox/MultiCombobox.module.scss +++ b/client/wildcard/src/components/Combobox/MultiCombobox.module.scss @@ -93,3 +93,10 @@ display: none; } } + +.zero-state { + color: var(--text-muted); + font-size: 0.75rem; + padding: 0.5rem; + display: block; +} diff --git a/client/wildcard/src/components/Combobox/MultiCombobox.tsx b/client/wildcard/src/components/Combobox/MultiCombobox.tsx index 7b47d120f42..4b7924bac4b 100644 --- a/client/wildcard/src/components/Combobox/MultiCombobox.tsx +++ b/client/wildcard/src/components/Combobox/MultiCombobox.tsx @@ -172,6 +172,7 @@ export const MultiComboboxInput = forwardRef ) @@ -179,12 +180,13 @@ export const MultiComboboxInput = forwardRef { status?: InputStatus | `${InputStatus}` + byPassValue: string } // Forward ref doesn't support function components with generic, // so we have to cast a proper FC types with generic props const MultiValueInput = forwardRef((props: MultiValueInputProps, ref: Ref) => { - const { onKeyDown, onFocus, onBlur, value, ...attributes } = props + const { onKeyDown, onFocus, onBlur, byPassValue, value, ...attributes } = props const { setInputElement, @@ -201,7 +203,7 @@ const MultiValueInput = forwardRef((props: MultiValueInputProps, ref: Ref([setInputElement]) const handleKeyDown = (event: KeyboardEvent): void => { - if (value === '' && event.key === Key.Backspace) { + if (byPassValue === '' && event.key === Key.Backspace) { onSelectedItemsChange(selectedItems.slice(0, -1)) // Prevent any single combobox UI state machine updates @@ -265,6 +267,7 @@ const MultiValueInput = forwardRef((props: MultiValueInputProps, ref: Ref(props: MultiComboboxListProps): ReactEle ) } +interface MultiComboboxEmptyListProps extends HTMLAttributes {} + +export function MultiComboboxEmptyList(props: MultiComboboxEmptyListProps): ReactElement { + const { className, ...attributes } = props + + return +} + interface MultiComboboxOptionProps extends ComboboxOptionProps { className?: string } diff --git a/client/wildcard/src/components/Combobox/index.ts b/client/wildcard/src/components/Combobox/index.ts index 8a8e9ba7f48..468da233085 100644 --- a/client/wildcard/src/components/Combobox/index.ts +++ b/client/wildcard/src/components/Combobox/index.ts @@ -13,6 +13,7 @@ export { MultiComboboxInput, MultiComboboxPopover, MultiComboboxList, + MultiComboboxEmptyList, MultiComboboxOptionGroup, MultiComboboxOption, MultiComboboxOptionText, diff --git a/client/wildcard/src/components/Form/MultiSelect/ClearIndicator.tsx b/client/wildcard/src/components/Form/MultiSelect/ClearIndicator.tsx deleted file mode 100644 index 7c4755ea5f9..00000000000 --- a/client/wildcard/src/components/Form/MultiSelect/ClearIndicator.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { ReactElement } from 'react' - -import { mdiClose } from '@mdi/js' -import { components, ClearIndicatorProps } from 'react-select' - -import { Icon } from '../../Icon' - -import { MultiSelectOption } from './types' - -import styles from './MultiSelect.module.scss' - -// Overwrite the clear indicator with `CloseIcon` -export const ClearIndicator = ( - props: ClearIndicatorProps, true> -): ReactElement => ( - - - -) diff --git a/client/wildcard/src/components/Form/MultiSelect/DropdownIndicator.tsx b/client/wildcard/src/components/Form/MultiSelect/DropdownIndicator.tsx deleted file mode 100644 index eea671d36a3..00000000000 --- a/client/wildcard/src/components/Form/MultiSelect/DropdownIndicator.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import { ReactElement } from 'react' - -import { mdiChevronDown } from '@mdi/js' -import { components, DropdownIndicatorProps } from 'react-select' - -import { Icon } from '../../Icon' - -import { MultiSelectOption } from './types' - -import styles from './MultiSelect.module.scss' - -// Overwrite the dropdown indicator with `ChevronDownIcon` -export const DropdownIndicator = ( - props: DropdownIndicatorProps, true> -): ReactElement => ( - - - -) diff --git a/client/wildcard/src/components/Form/MultiSelect/MultiSelect.module.scss b/client/wildcard/src/components/Form/MultiSelect/MultiSelect.module.scss deleted file mode 100644 index 8334ebafe31..00000000000 --- a/client/wildcard/src/components/Form/MultiSelect/MultiSelect.module.scss +++ /dev/null @@ -1,62 +0,0 @@ -.multi-select { - min-width: 6.5rem; - - :global(.theme-light) & { - --react-select-neutral0: var(--white); - --react-select-neutral5: var(--gray-01); - --react-select-neutral10: var(--gray-01); - --react-select-neutral20: var(--gray-02); - --react-select-neutral30: var(--gray-03); - --react-select-neutral40: var(--gray-04); - --react-select-neutral50: var(--gray-05); - --react-select-neutral60: var(--gray-06); - --react-select-neutral70: var(--gray-07); - --react-select-neutral80: var(--gray-08); - --react-select-neutral90: var(--gray-09); - --select-button-border-color: var(--secondary-3); - } - - :global(.theme-dark) & { - --react-select-neutral0: var(--black); - --react-select-neutral5: var(--gray-09); - --react-select-neutral10: var(--gray-08); - --react-select-neutral20: var(--gray-07); - --react-select-neutral30: var(--gray-06); - --react-select-neutral40: var(--gray-05); - --react-select-neutral50: var(--gray-04); - --react-select-neutral60: var(--gray-03); - --react-select-neutral70: var(--gray-02); - --react-select-neutral80: var(--gray-01); - --react-select-neutral90: var(--white); - --select-button-border-color: var(--secondary); - } - - input:focus-within { - box-shadow: none; - } -} - -.clear-icon { - color: var(--icon-color); - height: 1rem; -} - -.dropdown-icon { - color: var(--icon-color); - height: 1rem; -} - -.dropdown-icon-disabled { - color: var(--text-disabled); - height: 1rem; -} - -.remove-icon { - width: 0.75rem; - height: 0.75rem; -} - -.multi-value-label { - overflow: hidden; - text-overflow: ellipsis; -} diff --git a/client/wildcard/src/components/Form/MultiSelect/MultiSelect.story.tsx b/client/wildcard/src/components/Form/MultiSelect/MultiSelect.story.tsx deleted file mode 100644 index 76bb420bdc9..00000000000 --- a/client/wildcard/src/components/Form/MultiSelect/MultiSelect.story.tsx +++ /dev/null @@ -1,95 +0,0 @@ -import { useState } from 'react' - -import { Meta, Story } from '@storybook/react' - -import { H1, H2 } from '../..' -import { BrandedStory } from '../../../stories/BrandedStory' -import { Grid } from '../../Grid/Grid' - -import { MultiSelect, MultiSelectProps, MultiSelectState, MultiSelectOption } from '.' - -const config: Meta = { - title: 'wildcard/MultiSelect', - - decorators: [story => {() =>
{story()}
}
], -} - -export default config - -type OptionValue = 'chocolate' | 'strawberry' | 'vanilla' | 'green tea' | 'rocky road' | 'really long' - -const OPTIONS: MultiSelectOption[] = [ - { value: 'chocolate', label: 'Chocolate' }, - { value: 'strawberry', label: 'Strawberry' }, - { value: 'vanilla', label: 'Vanilla' }, - { value: 'green tea', label: 'Green Tea' }, - { value: 'rocky road', label: 'Rocky Road' }, - { value: 'really long', label: 'A really really really REALLY long ice cream flavor' }, -] - -const BaseSelect = (props: Partial>) => { - const [selectedOptions, setSelectedOptions] = useState>([]) - - return ( - - ) -} - -const SelectWithValues = () => { - const [selectedOptions, setSelectedOptions] = useState>([OPTIONS[5], OPTIONS[1]]) - - return ( - - ) -} - -export const MultiSelectExamples: Story = () => ( - <> -

Multi Select

- -
-

Standard

- -
-
-

Valid

- -
-
-

Invalid

- -
-
-

Disabled

- -
-
- -

Pre-selected values (300px wide container)

-
- -
- -) - -MultiSelectExamples.parameters = { - chromatic: { - enableDarkMode: true, - disableSnapshot: false, - }, -} diff --git a/client/wildcard/src/components/Form/MultiSelect/MultiSelect.tsx b/client/wildcard/src/components/Form/MultiSelect/MultiSelect.tsx deleted file mode 100644 index 5a7118586ed..00000000000 --- a/client/wildcard/src/components/Form/MultiSelect/MultiSelect.tsx +++ /dev/null @@ -1,76 +0,0 @@ -import { ReactElement } from 'react' - -import classNames from 'classnames' -import Select, { Props as SelectProps, StylesConfig, GroupBase } from 'react-select' - -import { AccessibleFieldProps } from '../internal/AccessibleFieldType' -import { FormFieldLabel } from '../internal/FormFieldLabel' -import { FormFieldMessage } from '../internal/FormFieldMessage' -import { getValidStyle } from '../internal/utils' - -import { ClearIndicator } from './ClearIndicator' -import { DropdownIndicator } from './DropdownIndicator' -import { MultiValueContainer } from './MultiValueContainer' -import { MultiValueLabel } from './MultiValueLabel' -import { MultiValueRemove } from './MultiValueRemove' -import { STYLES } from './styles' -import { THEME } from './theme' -import { MultiSelectOption } from './types' - -import selectStyles from '../Select/Select.module.scss' -import styles from './MultiSelect.module.scss' - -export type MultiSelectProps