Combo Box

UI: Combo Box(= Input + Dropdown)


요구 사항:

  • input 박스에서 입력은 못하고, 오른쪽 끝에 클릭하면 list를 보여줄 수 있는 아이콘 추가
  • 아이콘을 클릭하면, 선택할 수 있는 list 나열
  • list의 각 option을 클릭하면, 그 option은 input 박스에 bold 상태로 보임
  • 단, option 중 ‘직접 입력하기’를 클릭하면, input 박스에 focus가 되고, 값을 입력 가능


개발 접근법:

  • input 박스에 아이콘 추가(css)
  • list 중 선택한 option 값(e.target.innerText)를 input 박스의 value 값을 변경
  • ‘직접 입력하기’ option을 선택할 때는, input 박스에 focus를 적용(React의 ref 사용)


Code: React


Container.js


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
import React, { Component, Fragment } from 'react';
import Combobox from './components/Combobox'

class Container extends Component {
constructor(props) {
super(props);
this.childRef = React.createRef();
this.state = {
message: '',
placeholder: '',
showMessageList: false,
isDisabled: true,
options: []
}
}
componentDidMount () {
this.setState({
placeholder: '응원하세요.',
options: [
'직접 입력하기',
'여러분 힘내세요.',
'응원합니다.'
]
})
}
componentDidUpdate () {
if(this.state.placeholder === '직접 입력하기') {
this.childRef.current.focus();
}
}
onClick = () => {
this.setState({ showMessageList: true })
}
onClickMessage = (e) => {
const messageSelected = e.target.innerText;
if(messageSelected === '직접 입력하기') {
this.setState({
message: '',
placeholder: messageSelected,
isDisabled: false,
showMessageList: false
})
return;
}
this.setState({
message: e.target.innerText,
showMessageList: false,
placeholder: '응원 메세지 작성 혹은 선택해주세요.'
})
}
onChange = (e) => {
if(this.state.placeholder === '직접 입력하기') {
this.setState({
message: e.target.value,
showMessageList: false
})
}
}
onBlur = () => {
this.setState({ showMessageList: false })
}
render() {
const { message, placeholder, showMessageList, isDisabled, options } = this.state;
return (
<Fragment>
<Combobox
message={message}
placeholder={placeholder}
showMessageList={showMessageList}
isDisabled={isDisabled}
options={options}
ref={this.childRef}
onClick={this.onClick}
onClickMessage={this.onClickMessage}
onChange={this.onChange}
onBlur={this.onBlur}
/>
</Fragment>
);
}
}

export default Container;


Combobox.js


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import React from 'react';

const Combobox = React.forwardRef((props, ref) => {
const inputStyle = props.message ? { 'fontWeight': 'bold' } : {}
return (
<div>
<h2>combo box...</h2>
<div className="dropdown">
<input
ref={ref}
style={inputStyle}
onChange={props.onChange}
onBlur={props.onBlur}
value={props.message}
placeholder={props.placeholder}
disabled={props.isDisabled}
type="text"
name="message"
title="message input"
/>
<span
className="arrow down"
style={{"cursor":"pointer"}}
onClick={props.onClick}
>
</span>
{
props.showMessageList ?
<ul className="showMessageList">
{
props.options.map((el, key) => {
return <li key={key}><span onMouseDown={props.onClickMessage} style={{"cursor":"pointer"}}>{el}</span></li>
})
}
</ul>
:
null
}
</div>
</div>
)
})

export default Combobox;


index.css


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
.dropdown {
position: relative;
}
.dropdown input {
position: relative;
width: 300px;
height: 30px;
}
.dropdown .down {
position: absolute;
top: 12px;
left: 290px;
border: solid black;
border-width: 0 3px 3px 0;
display: inline-block;
padding: 3px;
transform: rotate(45deg);
}
.dropdown ul {
list-style-type: none;
/* border-collapse: collapse; */
}
.dropdown li {
margin: 10px;
}