1- /* eslint-disable react/jsx-no-bind */
2- import {
3- CSSProperties ,
4- FC ,
5- useState ,
6- } from 'react'
7- import { components , ControlProps , GroupBase ,
8- MultiValue , Options , SingleValue , StylesConfig } from 'react-select'
9- import AsyncSelect from 'react-select/async'
10- import PropTypes from 'prop-types'
1+ import { FC , useMemo , useState } from 'react'
112
12- import { SearchIcon } from '@heroicons/react/outline '
13- import { ContentLayout } from '~/libs/ui '
3+ import { ContentLayout , IconOutline , InputMultiselectOption } from '~/libs/ui '
4+ import { EmsiSkill , EmsiSkillSources , InputSkillSelector } from '~/libs/shared '
145import { Skill } from '@talentSearch/lib/models/'
15- import MatcherService from '@talentSearch/lib/services/MatcherService'
166
177import SkillPill from './components/SkillPill'
188import styles from './TalentSearch.module.scss'
199
20- function search ( skills :Options < Skill > ) : void {
21- alert ( JSON . stringify ( skills ) )
22- }
23-
24- // eslint-disable-next-line react/destructuring-assignment, @typescript-eslint/typedef
25- const Control : React . FC < ControlProps < Skill , boolean , GroupBase < Skill > > > = ( { children, ...props } ) => (
26- < components . Control { ...props } >
27- { children }
28- < span
29- onClick = { ( ) => search ( props . getValue ( ) ) }
30- className = { styles . searchIconSpan }
31- >
32- < SearchIcon className = { styles . searchIcon } />
33- </ span >
34- </ components . Control >
35- )
36-
37- Control . propTypes = {
38- children : PropTypes . node . isRequired ,
39- getValue : PropTypes . func . isRequired ,
40- }
10+ // TODO: Make this configurable, or read from a service. We need to discuss
11+ // how we want to handle this.
12+ const popularSkills :Skill [ ] [ ] = [
13+ [ { emsiId : 'KS441LF7187KS0CV4B6Y' , name : 'Typescript' } ,
14+ { emsiId : 'KS1244K6176NLVWV02B6' , name : 'Front-End Engineering' } ,
15+ { emsiId : 'KS1214R5XG4X4PY7LGY6' , name : 'Bootstrap (Front-End Framework)' } ] ,
16+ [ { emsiId : 'KS121F45VPV8C9W3QFYH' , name : 'Cascading Style Sheets (CSS)' } ,
17+ { emsiId : 'KS1200771D9CR9LB4MWW' , name : 'JavaScript (Programming Language)' } ] ,
18+ [ { emsiId : 'KS1200578T5QCYT0Z98G' , name : 'HyperText Markup Language (HTML)' } ,
19+ { emsiId : 'ES86A20379CD2AD061F3' , name : 'IOS Development' } ,
20+ { emsiId : 'KS127296VDYS7ZFWVC46' , name : 'Node.js' } ] ,
21+ [ { emsiId : 'ES50D03AC9CFC1A0BC93' , name : '.NET Development' } ,
22+ { emsiId : 'KS1219W70LY1GXZDSKW5' , name : 'C++ (Programming Language)' } ,
23+ { emsiId : 'KS127SZ60YZR8B5CQKV1' , name : 'PHP Development' } ] ,
24+ [ { emsiId : 'KS1206V6K46N1SDVJGBD' , name : 'Adobe Illustrator' } ,
25+ { emsiId : 'ESD07FEE22E7EC094EB8' , name : 'Ruby (Programming Language)' } ,
26+ { emsiId : 'KS120076FGP5WGWYMP0F' , name : 'Java (Programming Language)' } ] ,
27+ [ { emsiId : 'KSPSGF5MXB6568UIQ4BK' , name : 'React Native' } ,
28+ { emsiId : 'KS441PL6JPXW200W0GRQ' , name : 'User Experience (UX)' } ] ,
29+ ]
4130
4231export const TalentSearch : FC = ( ) => {
4332 const [ skillsFilter , setSkillsFilter ] = useState < Array < Skill > > ( [ ] )
33+ const emsiSkills : EmsiSkill [ ] = useMemo ( ( ) => skillsFilter . map ( s => ( {
34+ name : s . name ,
35+ skillId : s . emsiId ,
36+ skillSources : [ EmsiSkillSources . selfPicked ] ,
37+ } ) ) , [ skillsFilter ] )
4438
4539 function toggleSkill ( skill :Skill ) : void {
4640 let newFilter : Array < Skill > = [ ]
@@ -63,12 +57,12 @@ export const TalentSearch: FC = () => {
6357 }
6458 }
6559
66- function onChange ( options : MultiValue < Skill > | SingleValue < Skill > ) : void {
67- if ( Array . isArray ( options ) ) {
68- setSkillsFilter ( options )
69- } else {
70- setSkillsFilter ( [ ] )
71- }
60+ function onChange ( ev : any ) : void {
61+ const options = ( ev . target . value as unknown ) as InputMultiselectOption [ ]
62+ setSkillsFilter ( options . map ( v => ( {
63+ emsiId : v . value ,
64+ name : v . label as string ,
65+ } ) ) )
7266 }
7367
7468 function filteringSkill ( skill :Skill ) : boolean {
@@ -82,105 +76,6 @@ export const TalentSearch: FC = () => {
8276 return result
8377 }
8478
85- // TODO: Make this configurable, or read from a service. We need to discuss
86- // how we want to handle this.
87- const popularSkills :Skill [ ] [ ] = [
88- [ { emsiId : 'KS441LF7187KS0CV4B6Y' , name : 'Typescript' } ,
89- { emsiId : 'KS1244K6176NLVWV02B6' , name : 'Front-End Engineering' } ,
90- { emsiId : 'KS1214R5XG4X4PY7LGY6' , name : 'Bootstrap (Front-End Framework)' } ] ,
91- [ { emsiId : 'KS121F45VPV8C9W3QFYH' , name : 'Cascading Style Sheets (CSS)' } ,
92- { emsiId : 'KS1200771D9CR9LB4MWW' , name : 'JavaScript (Programming Language)' } ] ,
93- [ { emsiId : 'KS1200578T5QCYT0Z98G' , name : 'HyperText Markup Language (HTML)' } ,
94- { emsiId : 'ES86A20379CD2AD061F3' , name : 'IOS Development' } ,
95- { emsiId : 'KS127296VDYS7ZFWVC46' , name : 'Node.js' } ] ,
96- [ { emsiId : 'ES50D03AC9CFC1A0BC93' , name : '.NET Development' } ,
97- { emsiId : 'KS1219W70LY1GXZDSKW5' , name : 'C++ (Programming Language)' } ,
98- { emsiId : 'KS127SZ60YZR8B5CQKV1' , name : 'PHP Development' } ] ,
99- [ { emsiId : 'KS1206V6K46N1SDVJGBD' , name : 'Adobe Illustrator' } ,
100- { emsiId : 'ESD07FEE22E7EC094EB8' , name : 'Ruby (Programming Language)' } ,
101- { emsiId : 'KS120076FGP5WGWYMP0F' , name : 'Java (Programming Language)' } ] ,
102- [ { emsiId : 'KSPSGF5MXB6568UIQ4BK' , name : 'React Native' } ,
103- { emsiId : 'KS441PL6JPXW200W0GRQ' , name : 'User Experience (UX)' } ] ,
104- ]
105-
106- const controlStyle : CSSProperties = {
107- borderColor : 'black' ,
108- paddingBottom : '10px' ,
109- paddingTop : '10px' ,
110- }
111-
112- const placeholderStyle : CSSProperties = {
113- color : '#2A2A2A' ,
114- fontFamily : 'Roboto' ,
115- fontSize : '16' ,
116- fontWeight : 400 ,
117- height : '36px' ,
118- paddingTop : '4px' ,
119- }
120-
121- const multiValueStyle : CSSProperties = {
122- backgroundColor : 'white' ,
123- border : '1px solid #d4d4d4' ,
124- borderRadius : '24px' ,
125- color : '#333' ,
126- fontFamily : 'Roboto' ,
127- fontSize : '14' ,
128- fontWeight : '400' ,
129- height : '32px' ,
130- marginRight : '10px' ,
131- paddingLeft : '8px' ,
132- paddingRight : '8px' ,
133- }
134-
135- const multiValueRemoveStyle : CSSProperties = {
136- backgroundColor : '#d9d9d9' ,
137- border : '1px solid #d4d4d4' ,
138- borderRadius : '11px' ,
139- color : '#333' ,
140- fontSize : '12' ,
141- height : '12px' ,
142- marginBottom : 'auto' ,
143- marginLeft : '5px' ,
144- marginRight : '5px' ,
145- marginTop : 'auto' ,
146- padding : '0px' ,
147- width : '12px' ,
148- }
149-
150- const hiddenStyle : CSSProperties = {
151- display : 'none' ,
152- }
153-
154- const selectStyle : StylesConfig < Skill > = {
155- clearIndicator : provided => ( {
156- ...provided ,
157- ...hiddenStyle ,
158- } ) ,
159- control : provided => ( {
160- ...provided ,
161- ...controlStyle ,
162- } ) ,
163- dropdownIndicator : provided => ( {
164- ...provided ,
165- ...hiddenStyle ,
166- } ) ,
167- indicatorSeparator : provided => ( {
168- ...provided ,
169- ...hiddenStyle ,
170- } ) ,
171- multiValue : provided => ( {
172- ...provided ,
173- ...multiValueStyle ,
174- } ) ,
175- multiValueRemove : provided => ( {
176- ...provided ,
177- ...multiValueRemoveStyle ,
178- } ) ,
179- placeholder : provided => ( {
180- ...provided ,
181- ...placeholderStyle ,
182- } ) ,
183- }
18479 return (
18580 < ContentLayout
18681 contentClass = { styles . contentLayout }
@@ -197,42 +92,31 @@ export const TalentSearch: FC = () => {
19792 </ div >
19893 < div className = { styles . searchOptions } >
19994 < span className = { styles . searchPrompt } > Search by skills</ span >
200- < AsyncSelect
201- isMulti
202- cacheOptions
203- autoFocus
204- defaultOptions
95+ < InputSkillSelector
20596 placeholder = 'Enter skills you are searching for...'
206- loadOptions = { MatcherService . autoCompleteSkills }
207- name = 'skills'
208- styles = { selectStyle }
209- className = { styles . searchSelect }
210- getOptionLabel = { ( skill : Skill ) => skill . name }
211- getOptionValue = { ( skill : Skill ) => skill . emsiId }
212- components = { { Control } }
213- openMenuOnClick = { false }
214- value = { skillsFilter }
215- onChange = { (
216- newValue : MultiValue < Skill > | SingleValue < Skill > ,
217- ) => onChange ( newValue ) }
97+ useWrapper = { false }
98+ theme = 'clear'
99+ dropdownIcon = { < IconOutline . SearchIcon className = { styles . searchIcon } /> }
100+ value = { emsiSkills }
101+ onChange = { onChange }
218102 />
219103 </ div >
220104 < div className = { styles . popularSkillsContainer } >
221105 < span className = { styles . popularSkillsTitle } > Popular Skills</ span >
222106
223- { popularSkills . map (
224- row => (
225- < div className = { styles . pillRow } >
226- { row . map ( skill => (
227- < SkillPill
228- skill = { skill }
229- selected = { filteringSkill ( skill ) }
230- onClick = { toggleSkill }
231- />
232- ) ) }
233- </ div >
234- ) ,
235- ) }
107+ { popularSkills . map ( ( row , i ) => (
108+ // eslint-disable-next-line react/no-array-index-key
109+ < div className = { styles . pillRow } key = { i } >
110+ { row . map ( skill => (
111+ < SkillPill
112+ key = { skill . emsiId }
113+ skill = { skill }
114+ selected = { filteringSkill ( skill ) }
115+ onClick = { toggleSkill }
116+ />
117+ ) ) }
118+ </ div >
119+ ) ) }
236120 </ div >
237121 </ ContentLayout >
238122 )
0 commit comments