Skip to content

Commit 853dd67

Browse files
committed
[Feat/#8] 에러 케이스 및 라벨 추가
1 parent 00106e8 commit 853dd67

File tree

2 files changed

+113
-75
lines changed

2 files changed

+113
-75
lines changed

app/src/main/java/com/poti/android/core/designsystem/component/field/PotiDropdownField.kt

Lines changed: 58 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ import androidx.compose.foundation.BorderStroke
66
import androidx.compose.foundation.ScrollState
77
import androidx.compose.foundation.clickable
88
import androidx.compose.foundation.interaction.MutableInteractionSource
9+
import androidx.compose.foundation.layout.Arrangement
910
import androidx.compose.foundation.layout.Box
11+
import androidx.compose.foundation.layout.Column
1012
import androidx.compose.foundation.layout.fillMaxWidth
1113
import androidx.compose.foundation.layout.height
1214
import androidx.compose.foundation.layout.padding
@@ -49,6 +51,8 @@ import com.poti.android.core.designsystem.theme.White
4951
* @param menuItems 메뉴에 노출되는 아이템 데이터 리스트입니다.
5052
* @param selectedIds 메뉴 아이템의 selected 상태 표시에 쓰이며, 외부에서 제어합니다. 선택된 아이템 객체의 id 프로퍼티를 넣어줍니다.
5153
* @param modifier
54+
* @param label 필드 상단에 표시됩니다.
55+
* @param error emptyString이 아닌 경우 필드 하단에 에러 메시지를 노출하고, borderColor를 변경합니다.
5256
* @param initialOpenState 메뉴의 초기 열림 상태를 설정합니다. 기본값 false로, 열린 상태를 초기값으로 하고 싶을 때에만 true로 설정합니다.
5357
* @param closeOnItemClick 메뉴 아이템 클릭 시 메뉴를 닫는 옵션입니다. 기본값 true로, 다중 선택 필요하다면 false로 설정합니다.
5458
* @param maxHeight 메뉴 최대 높이를 제한합니다. 기본값 422dp입니다.
@@ -68,6 +72,8 @@ fun PotiDropdownField(
6872
menuItems: List<FieldMenuItem>,
6973
selectedIds: Set<String>,
7074
modifier: Modifier = Modifier,
75+
label: String = "",
76+
error: String = "",
7177
initialOpenState: Boolean = false,
7278
closeOnItemClick: Boolean = true,
7379
maxHeight: Dp? = 422.dp,
@@ -85,51 +91,63 @@ fun PotiDropdownField(
8591
}
8692
}
8793

94+
val borderColor = when {
95+
error.isNotEmpty() -> PotiTheme.colors.sementicRed
96+
expandedState.currentState || expandedState.targetState -> PotiTheme.colors.gray700
97+
else -> PotiTheme.colors.gray300
98+
}
99+
88100
Box(
89101
modifier = modifier
90102
.fillMaxWidth(),
91103
) {
92-
PotiBasicField(
93-
value = value,
94-
onValueChaged = {},
95-
placeholder = placeholder,
96-
modifier = Modifier
97-
.fillMaxWidth()
98-
.height(52.dp)
99-
.onGloballyPositioned { coordinates ->
100-
parentWidth = coordinates.size.width
101-
}
102-
.onFocusChanged { focusState ->
103-
if (!focusState.isFocused && expandedState.currentState) {
104-
expandedState.targetState = false
104+
Column(
105+
verticalArrangement = Arrangement.spacedBy(8.dp)
106+
) {
107+
FieldLabel(label)
108+
109+
PotiBasicField(
110+
value = value,
111+
onValueChaged = {},
112+
placeholder = placeholder,
113+
modifier = Modifier
114+
.fillMaxWidth()
115+
.height(52.dp)
116+
.onGloballyPositioned { coordinates ->
117+
parentWidth = coordinates.size.width
118+
}
119+
.onFocusChanged { focusState ->
120+
if (!focusState.isFocused && expandedState.currentState) {
121+
expandedState.targetState = false
122+
}
123+
}
124+
.clickable(
125+
indication = null,
126+
interactionSource = remember { MutableInteractionSource() },
127+
) {
128+
expandedState.targetState = !expandedState.currentState
129+
},
130+
borderColor = borderColor,
131+
backgroundColor = White,
132+
trailingIcon = {
133+
Crossfade(
134+
targetState = expandedState.targetState,
135+
) { opened ->
136+
Icon(
137+
imageVector = ImageVector.vectorResource(if (opened) R.drawable.ic_arrow_up_lg else R.drawable.ic_arrow_down_lg),
138+
contentDescription = null,
139+
modifier = Modifier
140+
.size(24.dp),
141+
tint = PotiTheme.colors.gray700,
142+
)
105143
}
106-
}
107-
.clickable(
108-
indication = null,
109-
interactionSource = remember { MutableInteractionSource() },
110-
) {
111-
expandedState.targetState = !expandedState.currentState
112144
},
113-
borderColor = when {
114-
expandedState.currentState || expandedState.targetState -> PotiTheme.colors.gray700
115-
else -> PotiTheme.colors.gray300
116-
},
117-
backgroundColor = White,
118-
trailingIcon = {
119-
Crossfade(
120-
targetState = expandedState.targetState,
121-
) { opened ->
122-
Icon(
123-
imageVector = ImageVector.vectorResource(if (opened) R.drawable.ic_arrow_up_lg else R.drawable.ic_arrow_down_lg),
124-
contentDescription = null,
125-
modifier = Modifier
126-
.size(24.dp),
127-
tint = PotiTheme.colors.gray700,
128-
)
129-
}
130-
},
131-
enabled = false,
132-
)
145+
enabled = false,
146+
)
147+
148+
// TODO: [도연] Display>errorMessage로 대체
149+
FieldErrorMessage(error)
150+
}
133151

134152
PotiDropdownMenu(
135153
expandedState = expandedState,
@@ -241,6 +259,7 @@ private fun PotiDropdownFieldWithPriceWithMutlipleSelectPreveiw() {
241259
modifier = Modifier
242260
.padding(horizontal = 20.dp)
243261
.padding(top = 40.dp),
262+
error = "에러 메시지",
244263
closeOnItemClick = false,
245264
)
246265
}

app/src/main/java/com/poti/android/core/designsystem/component/field/PotiSearchField.kt

Lines changed: 55 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ import androidx.compose.foundation.BorderStroke
55
import androidx.compose.foundation.ScrollState
66
import androidx.compose.foundation.clickable
77
import androidx.compose.foundation.interaction.MutableInteractionSource
8+
import androidx.compose.foundation.layout.Arrangement
89
import androidx.compose.foundation.layout.Box
10+
import androidx.compose.foundation.layout.Column
911
import androidx.compose.foundation.layout.fillMaxWidth
1012
import androidx.compose.foundation.layout.height
1113
import androidx.compose.foundation.layout.padding
@@ -56,6 +58,8 @@ import kotlinx.coroutines.delay
5658
* @param selectedIds 메뉴 아이템의 selected 상태 표시에 쓰이며, 외부에서 제어합니다. 선택된 아이템 객체의 id 프로퍼티를 넣어줍니다.
5759
* @param searchType 서치 타입에 따라 메뉴 최대 길이가 조정됩니다. 아티스트 검색 시 ARTIST, 상품 등록을 위한 상품명 검색 시 PRODUCT를 사용합니다.
5860
* @param modifier
61+
* @param label 필드 상단에 표시됩니다.
62+
* @param error emptyString이 아닌 경우 필드 하단에 에러 메시지를 노출하고, borderColor를 변경합니다.
5963
* @param focusRequester 포커스를 외부에서 제어하고 싶을 때 사용합니다. 예: 화면 진입 시 필드에 포커스 가도록
6064
* @param scrollState 메뉴 스크롤을 외부에서 제어하고 싶을 때 사용합니다.
6165
* @param offset 필드 하단으로부터 메뉴까지의 간격입니다. 기본값 12dp이며, y값만 조정 가능합니다.
@@ -73,6 +77,8 @@ fun PotiSearchField(
7377
selectedIds: Set<String>,
7478
searchType: SearchType,
7579
modifier: Modifier = Modifier,
80+
label: String = "",
81+
error: String = "",
7682
focusRequester: FocusRequester = remember { FocusRequester() },
7783
scrollState: ScrollState = rememberScrollState(),
7884
offset: DpOffset = DpOffset(x = 0.dp, y = 12.dp),
@@ -121,46 +127,58 @@ fun PotiSearchField(
121127
}
122128
}
123129

130+
val borderColor = when {
131+
error.isNotEmpty() -> PotiTheme.colors.sementicRed
132+
expandedState.currentState || expandedState.targetState -> PotiTheme.colors.gray700
133+
else -> PotiTheme.colors.gray300
134+
}
135+
124136
Box(
125137
modifier = modifier
126138
.fillMaxWidth(),
127139
) {
128-
PotiBasicField(
129-
value = value,
130-
onValueChaged = {
131-
isTyping = true
132-
onValueChange(it)
133-
},
134-
placeholder = placeholder,
135-
modifier = Modifier
136-
.fillMaxWidth()
137-
.height(52.dp)
138-
.onGloballyPositioned { coordinates ->
139-
parentWidth = coordinates.size.width
140+
Column(
141+
verticalArrangement = Arrangement.spacedBy(8.dp)
142+
) {
143+
FieldLabel(label)
144+
145+
PotiBasicField(
146+
value = value,
147+
onValueChaged = {
148+
isTyping = true
149+
onValueChange(it)
140150
},
141-
onFocusChanged = { isFieldFocused = it },
142-
borderColor = when {
143-
isFieldFocused -> PotiTheme.colors.gray700
144-
else -> PotiTheme.colors.gray300
145-
},
146-
backgroundColor = White,
147-
imeAction = ImeAction.Search,
148-
onSearchAction = { onSearch() },
149-
trailingIcon = {
150-
Icon(
151-
imageVector = ImageVector.vectorResource(R.drawable.ic_search),
152-
contentDescription = null,
153-
modifier = Modifier
154-
.size(24.dp)
155-
.clickable(
156-
indication = null,
157-
interactionSource = remember { MutableInteractionSource() },
158-
) { onSearch() },
159-
tint = PotiTheme.colors.gray700,
160-
)
161-
},
162-
focusRequester = focusRequester,
163-
)
151+
placeholder = placeholder,
152+
modifier = Modifier
153+
.fillMaxWidth()
154+
.height(52.dp)
155+
.onGloballyPositioned { coordinates ->
156+
parentWidth = coordinates.size.width
157+
},
158+
onFocusChanged = { isFieldFocused = it },
159+
borderColor = borderColor,
160+
backgroundColor = White,
161+
imeAction = ImeAction.Search,
162+
onSearchAction = { onSearch() },
163+
trailingIcon = {
164+
Icon(
165+
imageVector = ImageVector.vectorResource(R.drawable.ic_search),
166+
contentDescription = null,
167+
modifier = Modifier
168+
.size(24.dp)
169+
.clickable(
170+
indication = null,
171+
interactionSource = remember { MutableInteractionSource() },
172+
) { onSearch() },
173+
tint = PotiTheme.colors.gray700,
174+
)
175+
},
176+
focusRequester = focusRequester,
177+
)
178+
179+
// TODO: [도연] Display>errorMessage로 대체
180+
FieldErrorMessage(error)
181+
}
164182

165183
PotiDropdownMenu(
166184
expandedState = expandedState,
@@ -241,7 +259,8 @@ private fun PotiSearchFieldPreview() {
241259
.padding(horizontal = 20.dp)
242260
.padding(top = 40.dp),
243261
onSearchClick = { },
244-
searchType = SearchType.ARTIST
262+
searchType = SearchType.ARTIST,
263+
error = "에러 메시지"
245264
)
246265
}
247266
}

0 commit comments

Comments
 (0)