@@ -4,15 +4,20 @@ import { connect } from 'react-redux';
44import { Map as ImmutableMap , Set as ImmutableSet } from 'immutable' ;
55import * as classNames from 'classnames' ;
66import { useTranslation } from 'react-i18next' ;
7-
87import { ResourceIcon } from './utils' ;
98import { K8sKind , K8sResourceKindReference , modelFor , referenceForModel } from '../module/k8s' ;
109import { DiscoveryResources } from '@console/dynamic-plugin-sdk/src/api/common-types' ;
1110import {
1211 Select as SelectDeprecated ,
12+ SelectGroup as SelectGroupDeprecated ,
1313 SelectOption as SelectOptionDeprecated ,
1414 SelectVariant as SelectVariantDeprecated ,
1515} from '@patternfly/react-core/deprecated' ;
16+ import { useUserSettings } from '@console/shared/src' ;
17+ import { Divider , Tooltip } from '@patternfly/react-core' ;
18+ import CloseButton from '@console/shared/src/components/close-button' ;
19+
20+ const RECENT_SEARCH_ITEMS = 5 ;
1621
1722// Blacklist known duplicate resources.
1823const blacklistGroups = ImmutableSet ( [
@@ -26,12 +31,37 @@ const blacklistResources = ImmutableSet([
2631] ) ;
2732
2833const ResourceListDropdown_ : React . SFC < ResourceListDropdownProps > = ( props ) => {
29- const { selected, onChange, allModels, groupToVersionMap, className } = props ;
34+ const { selected, onChange, recentList , allModels, groupToVersionMap, className } = props ;
3035 const { t } = useTranslation ( ) ;
3136
3237 const [ isOpen , setOpen ] = React . useState ( false ) ;
38+ const [ clearItems , setClearItems ] = React . useState ( false ) ;
39+
40+ const [ recentSelected , setRecentSelected ] = useUserSettings < string > (
41+ 'console.search.recentlySearched' ,
42+ '[]' ,
43+ true ,
44+ ) ;
45+
3346 const [ selectedOptions , setSelectedOptions ] = React . useState ( selected ) ;
3447
48+ const filterGroupVersionKind = ( resourceList : string [ ] ) : string [ ] => {
49+ return resourceList . filter ( ( resource ) => {
50+ const parts = resource . split ( '~' ) ;
51+ return parts . length === 3 && parts . every ( ( part ) => part . trim ( ) !== '' ) ;
52+ } ) ;
53+ } ;
54+
55+ const recentSelectedList = ( data : string [ ] | string ) : string [ ] => {
56+ return (
57+ ( data &&
58+ data !== '[]' &&
59+ data !== 'undefined' &&
60+ JSON . parse ( _ . isString ( data ) ? data : JSON . stringify ( data ) ) ) ??
61+ [ ]
62+ ) ;
63+ } ;
64+
3565 const resources = allModels
3666 . filter ( ( { apiGroup, apiVersion, kind, verbs } ) => {
3767 // Remove blacklisted items.
@@ -65,7 +95,24 @@ const ResourceListDropdown_: React.SFC<ResourceListDropdownProps> = (props) => {
6595
6696 React . useEffect ( ( ) => {
6797 setSelectedOptions ( selected ) ;
68- } , [ selected ] ) ;
98+ ! _ . isEmpty ( selected ) &&
99+ setRecentSelected (
100+ JSON . stringify (
101+ _ . union (
102+ ! clearItems ? filterGroupVersionKind ( selected . reverse ( ) ) : [ ] ,
103+ recentSelectedList ( recentSelected ) ,
104+ ) ,
105+ ) ,
106+ ) ;
107+ // eslint-disable-next-line react-hooks/exhaustive-deps
108+ setClearItems ( false ) ;
109+ } , [ selected , setRecentSelected ] ) ;
110+
111+ const onClear = ( ) => {
112+ setSelectedOptions ( [ ] ) ;
113+ setClearItems ( true ) ;
114+ setRecentSelected ( JSON . stringify ( [ ] ) ) ;
115+ } ;
69116
70117 const items = resources
71118 . map ( ( model : K8sKind ) => (
@@ -98,9 +145,78 @@ const ResourceListDropdown_: React.SFC<ResourceListDropdownProps> = (props) => {
98145 ) )
99146 . toArray ( ) ;
100147
148+ const recentSearches : JSX . Element [ ] =
149+ ! _ . isEmpty ( recentSelectedList ( recentSelected ) ) &&
150+ recentSelectedList ( recentSelected )
151+ . splice ( 0 , RECENT_SEARCH_ITEMS )
152+ . map ( ( modelRef : K8sResourceKindReference ) => {
153+ const model : K8sKind = resources . find ( ( m ) => referenceForModel ( m ) === modelRef ) ;
154+ if ( model ) {
155+ return (
156+ < SelectOptionDeprecated
157+ key = { modelRef }
158+ value = { modelRef }
159+ data-filter-text = { `${ model . abbr } ${ model . labelKey ? t ( model . labelKey ) : model . kind } ` }
160+ >
161+ < span className = "co-resource-item" >
162+ < span className = "co-resource-icon--fixed-width" >
163+ < ResourceIcon kind = { modelRef } />
164+ </ span >
165+ < span className = "co-resource-item__resource-name" >
166+ < span >
167+ { model . labelKey ? t ( model . labelKey ) : model . kind }
168+ { model . badge && model . badge === 'Tech Preview' && (
169+ < span className = "co-resource-item__tech-dev-preview" >
170+ { t ( 'public~Tech Preview' ) }
171+ </ span >
172+ ) }
173+ </ span >
174+ { isDup ( model . kind ) && (
175+ < div className = "co-resource-item__resource-api text-muted co-truncate co-nowrap small" >
176+ { model . apiGroup || 'core' } /{ model . apiVersion }
177+ </ div >
178+ ) }
179+ </ span >
180+ </ span >
181+ </ SelectOptionDeprecated >
182+ ) ;
183+ }
184+ return null ;
185+ } )
186+ . filter ( ( item ) => item !== null ) ;
187+
188+ const renderedOptions = ( ) => {
189+ const options : JSX . Element [ ] = [ ] ;
190+ if ( ! _ . isEmpty ( recentSelectedList ( recentSelected ) ) && ! ! recentList ) {
191+ options . push (
192+ < Tooltip position = "right" content = { t ( 'public~Clear history' ) } >
193+ < CloseButton
194+ additionalClassName = "co-select-group-close-button"
195+ dataTestID = "close-icon"
196+ onClick = { onClear }
197+ />
198+ </ Tooltip > ,
199+ ) ;
200+ options . push (
201+ < SelectGroupDeprecated
202+ label = { t ( 'public~Recently used' ) }
203+ className = "co-select-group-dismissible"
204+ >
205+ { recentSearches }
206+ </ SelectGroupDeprecated > ,
207+ ) ;
208+ options . push ( < Divider key = { 3 } className = "co-select-group-divider" /> ) ;
209+ }
210+ options . push ( < SelectGroupDeprecated > { items } </ SelectGroupDeprecated > ) ;
211+ return options ;
212+ } ;
213+
101214 const onCustomFilter = ( event : React . ChangeEvent < HTMLInputElement > ) => {
102215 const filterText = event ?. target . value . toLocaleLowerCase ( ) ;
103- if ( filterText === null || filterText === '' ) {
216+ if ( filterText === null || filterText === '' || filterText === undefined ) {
217+ if ( ! _ . isEmpty ( recentSelectedList ( recentSelected ) ) && ! ! recentList ) {
218+ return renderedOptions ( ) ;
219+ }
104220 return items ;
105221 }
106222 return items . filter ( ( item ) => {
@@ -130,8 +246,9 @@ const ResourceListDropdown_: React.SFC<ResourceListDropdownProps> = (props) => {
130246 customBadgeText = { selected . length }
131247 className = { classNames ( 'co-type-selector' , className ) }
132248 maxHeight = "60vh"
249+ isGrouped
133250 >
134- { items }
251+ { renderedOptions ( ) }
135252 </ SelectDeprecated >
136253 ) ;
137254} ;
@@ -148,6 +265,7 @@ export const ResourceListDropdown = connect<ResourceListDropdownStateToProps>(
148265export type ResourceListDropdownProps = ResourceListDropdownStateToProps & {
149266 selected : K8sResourceKindReference [ ] ;
150267 onChange : ( value : string ) => void ;
268+ recentList ?: boolean ;
151269 className ?: string ;
152270 id ?: string ;
153271} ;
0 commit comments