11import * as React from 'react' ;
22import * as _ from 'lodash' ;
3+ import * as fuzzy from 'fuzzysearch' ;
34import { useField , useFormikContext , FormikValues } from 'formik' ;
45import { LoadingInline } from '@console/internal/components/utils' ;
5- import { FormGroup } from '@patternfly/react-core' ;
6- import { getFieldId } from '@console/shared' ;
6+ import {
7+ FormGroup ,
8+ EmptyState ,
9+ Title ,
10+ EmptyStatePrimary ,
11+ Button ,
12+ TextInput ,
13+ EmptyStateBody ,
14+ pluralize ,
15+ } from '@patternfly/react-core' ;
16+ import { getFieldId , useDebounceCallback } from '@console/shared' ;
717import SelectorCard from './SelectorCard' ;
818import './ItemSelectorField.scss' ;
919
@@ -26,6 +36,9 @@ interface ItemSelectorFieldProps {
2636 label ?: string ;
2737 autoSelect ?: boolean ;
2838 onSelect ?: ( name : string ) => void ;
39+ showIfSingle ?: boolean ;
40+ showFilter ?: boolean ;
41+ showCount ?: boolean ;
2942}
3043
3144const ItemSelectorField : React . FC < ItemSelectorFieldProps > = ( {
@@ -36,10 +49,15 @@ const ItemSelectorField: React.FC<ItemSelectorFieldProps> = ({
3649 onSelect,
3750 label,
3851 autoSelect,
52+ showIfSingle = false ,
53+ showFilter = false ,
54+ showCount = false ,
3955} ) => {
4056 const [ selected , { error : selectedError , touched : selectedTouched } ] = useField ( name ) ;
4157 const { setFieldValue, setFieldTouched, validateForm } = useFormikContext < FormikValues > ( ) ;
42- const itemCount = _ . keys ( itemList ) . length ;
58+ const [ filteredList , setFilteredList ] = React . useState ( itemList ) ;
59+ const [ filterText , setFilterText ] = React . useState ( '' ) ;
60+ const itemCount = _ . keys ( filteredList ) . length ;
4361
4462 const handleItemChange = React . useCallback (
4563 ( item : string ) => {
@@ -52,30 +70,57 @@ const ItemSelectorField: React.FC<ItemSelectorFieldProps> = ({
5270 ) ;
5371
5472 React . useEffect ( ( ) => {
55- if ( ! selected . value && itemCount === 1 ) {
56- const image = _ . find ( itemList ) ;
73+ if ( ! selected . value && _ . keys ( itemList ) . length === 1 ) {
74+ const image = _ . find ( filteredList ) ;
5775 handleItemChange ( image . name ) ;
5876 }
5977 if ( ! selected . value && recommended ) {
6078 handleItemChange ( recommended ) ;
6179 setFieldTouched ( name , false ) ;
6280 }
63- if ( ! selected . value && autoSelect && ! _ . isEmpty ( itemList ) ) {
64- const image = _ . get ( _ . keys ( itemList ) , 0 ) ;
65- handleItemChange ( itemList [ image ] ?. name ) ;
81+ if ( ! selected . value && autoSelect && ! _ . isEmpty ( filteredList ) ) {
82+ const image = _ . get ( _ . keys ( filteredList ) , 0 ) ;
83+ handleItemChange ( filteredList [ image ] ?. name ) ;
6684 }
6785 } , [
6886 autoSelect ,
69- itemCount ,
7087 itemList ,
88+ filteredList ,
7189 handleItemChange ,
7290 selected . value ,
7391 recommended ,
7492 name ,
7593 setFieldTouched ,
7694 ] ) ;
7795
78- if ( itemCount === 1 ) {
96+ const filterSources = React . useCallback (
97+ ( text : string ) => {
98+ const subList = _ . pickBy ( itemList , ( val ) =>
99+ fuzzy ( text . toLowerCase ( ) , val . title . toLowerCase ( ) ) ,
100+ ) ;
101+ if ( selected . value ) {
102+ subList [ selected . value ] = itemList [ selected . value ] ;
103+ }
104+ setFilteredList ( subList ) ;
105+ } ,
106+ [ itemList , selected . value ] ,
107+ ) ;
108+
109+ const debounceFilterText = useDebounceCallback < ( text : string ) => void > ( filterSources , [
110+ filterSources ,
111+ ] ) ;
112+
113+ const handleFilterChange = ( text : string ) => {
114+ setFilterText ( text ) ;
115+ debounceFilterText ( text ) ;
116+ } ;
117+
118+ const handleClearFilter = ( ) => {
119+ setFilterText ( '' ) ;
120+ filterSources ( '' ) ;
121+ } ;
122+
123+ if ( ! showIfSingle && itemCount === 1 ) {
79124 return null ;
80125 }
81126
@@ -94,20 +139,59 @@ const ItemSelectorField: React.FC<ItemSelectorFieldProps> = ({
94139 { loadingItems ? (
95140 < LoadingInline />
96141 ) : (
97- < div id = "item-selector-field" className = "odc-item-selector-field" >
98- { _ . values ( itemList ) . map ( ( item ) => (
99- < SelectorCard
100- key = { item . name }
101- title = { item . title }
102- iconUrl = { item . iconUrl }
103- name = { item . name }
104- displayName = { item . displayName }
105- selected = { selected . value === item . name }
106- recommended = { recommended === item . name }
107- onChange = { handleItemChange }
108- />
109- ) ) }
110- </ div >
142+ < >
143+ { showFilter && (
144+ < div className = "odc-item-selector-filter" >
145+ < TextInput
146+ className = "odc-item-selector-filter__input"
147+ onChange = { handleFilterChange }
148+ value = { filterText }
149+ placeholder = "Filter by type..."
150+ aria-label = "Filter by type"
151+ />
152+ { showCount && (
153+ < span className = "odc-item-selector-filter__count" >
154+ { pluralize ( itemCount , 'item' ) }
155+ </ span >
156+ ) }
157+ </ div >
158+ ) }
159+ { showFilter && itemCount === 0 ? (
160+ < EmptyState >
161+ < Title headingLevel = "h2" size = "lg" >
162+ No results match the filter criteria
163+ </ Title >
164+ < EmptyStateBody >
165+ No Event Source types are being shown due to the filters being applied.
166+ </ EmptyStateBody >
167+ < EmptyStatePrimary >
168+ < Button variant = "link" onClick = { handleClearFilter } >
169+ Clear filter
170+ </ Button >
171+ </ EmptyStatePrimary >
172+ </ EmptyState >
173+ ) : (
174+ < div
175+ id = "item-selector-field"
176+ className = { `odc-item-selector-field ${
177+ showFilter ? 'odc-item-selector-field__scrollbar' : ''
178+ } `}
179+ >
180+ { _ . values ( filteredList ) . map ( ( item ) => (
181+ < SelectorCard
182+ key = { item . name }
183+ title = { item . title }
184+ iconUrl = { item . iconUrl }
185+ name = { item . name }
186+ displayName = { item . displayName }
187+ selected = { selected . value === item . name }
188+ recommended = { recommended === item . name }
189+ onChange = { handleItemChange }
190+ />
191+ ) ) }
192+ </ div >
193+ ) }
194+ </ >
111195 ) }
112196 </ FormGroup >
113197 ) ;
0 commit comments