diff --git a/README.md b/README.md index 5cc4737a..ad2eea55 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,10 @@ - [x] 장바구니 아이템 컴포넌트 구현 - [x] 장바구니 아이콘을 클릭하면 장바구니 뷰로 이동한다. - [x] 상품 1개를 클릭하면 해당 상품 상세보기 뷰로 이동한다. + - [x] 상품 수량을 조절할 수 있다. + - [x] 0개인 아이템의 '+' 아이콘을 누르면 장바구니에 상품이 추가되며 동시에 갯수가 노출된다. + - [x] 1개인 아이템의 '-' 아이콘을 누르면 장바구니에 상품이 제거됨과 수량 버튼 노출이 사라진다. + - [x] 현재 장바구니에 추가된 아이템 갯수가 상품 목록뷰에 노출된다. - 상품 상세 뷰 - [x] 상품 정보를 볼 수 있다. @@ -36,3 +40,4 @@ - [x] 수량만큼의 담은 상품 총계를 볼 수 있다. - [x] 장바구니에 담긴 모든 상품의 총계를 볼 수 있다. + diff --git a/app/src/androidTest/java/nextstep/shoppingcart/shoppinglist/shoppingcart/ShoppingCartScreenTest.kt b/app/src/androidTest/java/nextstep/shoppingcart/shoppinglist/shoppingcart/ShoppingCartScreenTest.kt index 4a595c4b..d8e452ca 100644 --- a/app/src/androidTest/java/nextstep/shoppingcart/shoppinglist/shoppingcart/ShoppingCartScreenTest.kt +++ b/app/src/androidTest/java/nextstep/shoppingcart/shoppinglist/shoppingcart/ShoppingCartScreenTest.kt @@ -205,7 +205,7 @@ class ShoppingCartScreenTest { // then: composeTestRule.onNodeWithContentDescription("ShoppingButton") - .assertTextEquals("주문하기(1100)원") + .assertTextEquals("주문하기(1,100)원") } @Test @@ -256,7 +256,7 @@ class ShoppingCartScreenTest { // then: composeTestRule.onNodeWithContentDescription("ShoppingButton") - .assertTextEquals("주문하기(1200)원") + .assertTextEquals("주문하기(1,200)원") } @Test @@ -305,7 +305,7 @@ class ShoppingCartScreenTest { // then: composeTestRule.onNodeWithContentDescription("ShoppingButton") - .assertTextEquals("주문하기(1000)원") + .assertTextEquals("주문하기(1,000)원") } @Test diff --git a/app/src/androidTest/java/nextstep/shoppingcart/shoppinglist/shoppinglist/ShoppingListScreenTest.kt b/app/src/androidTest/java/nextstep/shoppingcart/shoppinglist/shoppinglist/ShoppingListScreenTest.kt new file mode 100644 index 00000000..32148726 --- /dev/null +++ b/app/src/androidTest/java/nextstep/shoppingcart/shoppinglist/shoppinglist/ShoppingListScreenTest.kt @@ -0,0 +1,151 @@ +package nextstep.shoppingcart.shoppinglist.shoppinglist + +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import androidx.compose.ui.test.assertIsDisplayed +import androidx.compose.ui.test.assertIsNotDisplayed +import androidx.compose.ui.test.assertTextEquals +import androidx.compose.ui.test.isDisplayed +import androidx.compose.ui.test.junit4.createComposeRule +import androidx.compose.ui.test.onNodeWithContentDescription +import androidx.compose.ui.test.onNodeWithText +import androidx.compose.ui.test.performClick +import nextstep.shoppingcart.ui.shoppinglist.ShoppingListScreen +import nextstep.shoppingcart.ui.shoppinglist.model.Product +import org.junit.Rule +import org.junit.Test + +class ShoppingListScreenTest { + + @get:Rule + val composeTestRule = createComposeRule() + + @Test + fun 상품의_담기_버튼을_누르면_수량조절_버튼이_노출된다() { + // given: + var products by mutableStateOf( + listOf( + Product( + id = 0L, + name = "테스트1", + imageUrl = "", + price = 110L, + containedCount = 0, + ) + ) + ) + + composeTestRule.apply { + setContent { + ShoppingListScreen( + products = products, + onShoppingCartClick = {}, + onItemClick = {}, + onPutClick = { selectedProductId -> + products = products.map { product -> + if (selectedProductId == product.id) product.copy( + containedCount = product.containedCount + 1 + ) else product + } + }, + onAddClick = {}, + onSubtractClick = {}, + ) + } + } + + // when: + composeTestRule.onNodeWithContentDescription("shoppingProductAddButtonDescription") + .performClick() + + // then: + composeTestRule.onNodeWithContentDescription("shoppingCountBarDescription") + .assertIsDisplayed() + } + + @Test + fun 담긴_상품의_수량이_1개_미만일_경우_수량조절_버튼이_사라지고_담기버튼이_노출된다() { + // given: + var products by mutableStateOf( + listOf( + Product( + id = 0L, + name = "테스트1", + imageUrl = "", + price = 110L, + containedCount = 1, + ) + ) + ) + + composeTestRule.apply { + setContent { + ShoppingListScreen( + products = products, + onShoppingCartClick = {}, + onItemClick = {}, + onPutClick = {}, + onAddClick = {}, + onSubtractClick = { selectedProductId -> + products = products.map { product -> + if (selectedProductId == product.id) product.copy( + containedCount = product.containedCount - 1 + ) else product + } + }, + ) + } + } + + // when: + composeTestRule.onNodeWithContentDescription("ShoppingCountBarSubtractIcon").performClick() + + // then: + composeTestRule.onNodeWithContentDescription("ShoppingProductAddButton") + .assertIsDisplayed() + composeTestRule.onNodeWithContentDescription("shoppingCountBarDescription") + .assertIsNotDisplayed() + } + + @Test + fun 담긴_상품의_수량이_2개일_경우_추가담기_버튼을_누르면_3개가_된다() { + // given: + var products by mutableStateOf( + listOf( + Product( + id = 0L, + name = "테스트1", + imageUrl = "", + price = 110L, + containedCount = 2, + ) + ) + ) + + composeTestRule.apply { + setContent { + ShoppingListScreen( + products = products, + onShoppingCartClick = {}, + onItemClick = {}, + onPutClick = {}, + onAddClick = { selectedProductId -> + products = products.map { product -> + if (selectedProductId == product.id) product.copy( + containedCount = product.containedCount + 1 + ) else product + } + }, + onSubtractClick = {}, + ) + } + } + + // when: + composeTestRule.onNodeWithContentDescription("ShoppingCountBarAddIcon").performClick() + + // then: + composeTestRule.onNodeWithText("3").isDisplayed() + } +} diff --git a/app/src/androidTest/java/nextstep/shoppingcart/shoppinglist/shoppinglist/component/ShoppingListLazyVerticalGridTest.kt b/app/src/androidTest/java/nextstep/shoppingcart/shoppinglist/shoppinglist/component/ShoppingListLazyVerticalGridTest.kt index fc0a56b6..8da7eaf3 100644 --- a/app/src/androidTest/java/nextstep/shoppingcart/shoppinglist/shoppinglist/component/ShoppingListLazyVerticalGridTest.kt +++ b/app/src/androidTest/java/nextstep/shoppingcart/shoppinglist/shoppinglist/component/ShoppingListLazyVerticalGridTest.kt @@ -34,6 +34,9 @@ class ShoppingListLazyVerticalGridTest { ), ), onItemClick = {}, + onPutClick = {}, + onAddClick = {}, + onSubtractClick = {}, ) } } @@ -73,6 +76,9 @@ class ShoppingListLazyVerticalGridTest { ) ), onItemClick = {}, + onPutClick = {}, + onAddClick = {}, + onSubtractClick = {}, ) } } diff --git a/app/src/androidTest/java/nextstep/shoppingcart/shoppinglist/shoppinglist/component/ShoppingProductHeaderTest.kt b/app/src/androidTest/java/nextstep/shoppingcart/shoppinglist/shoppinglist/component/ShoppingProductHeaderTest.kt new file mode 100644 index 00000000..1919f83a --- /dev/null +++ b/app/src/androidTest/java/nextstep/shoppingcart/shoppinglist/shoppinglist/component/ShoppingProductHeaderTest.kt @@ -0,0 +1,73 @@ +package nextstep.shoppingcart.shoppinglist.shoppinglist.component + +import androidx.compose.ui.test.assertIsDisplayed +import androidx.compose.ui.test.assertIsNotDisplayed +import androidx.compose.ui.test.assertTextEquals +import androidx.compose.ui.test.junit4.createComposeRule +import androidx.compose.ui.test.onNodeWithContentDescription +import nextstep.shoppingcart.ui.shoppinglist.component.ShoppingProductHeader +import nextstep.shoppingcart.ui.shoppinglist.model.Product +import org.junit.Rule +import org.junit.Test + +class ShoppingProductHeaderTest { + + @get:Rule + val composeTestRule = createComposeRule() + + @Test + fun `0번_상품이_장바구니에_1개_이상_담겨있다면_갯수가_노출된다`() { + // given: + composeTestRule.apply { + setContent { + ShoppingProductHeader( + product = Product( + id = 0L, + name = "요구르트", + imageUrl = "", + price = 500000000L, + containedCount = 2, + ), + onPutClick = {}, + onAddClick = {}, + onSubtractClick = {}, + ) + } + } + + // when: + // then: + composeTestRule.onNodeWithContentDescription("ShoppingProductAddButton") + .assertIsNotDisplayed() + composeTestRule.onNodeWithContentDescription("ShoppingCountBarTotalCount") + .assertTextEquals("2") + } + + @Test + fun `0번_상품이_장바구니에_0개_담겨있다면_갯수_추가_버튼이_노출된다`() { + // given: + composeTestRule.apply { + setContent { + ShoppingProductHeader( + product = Product( + id = 0L, + name = "요구르트", + imageUrl = "", + price = 500000000L, + containedCount = 0, + ), + onPutClick = {}, + onAddClick = {}, + onSubtractClick = {}, + ) + } + } + + // when: + // then: + composeTestRule.onNodeWithContentDescription("ShoppingProductAddButton") + .assertIsDisplayed() + composeTestRule.onNodeWithContentDescription("shoppingCountBarDescription") + .assertIsNotDisplayed() + } +} diff --git a/app/src/main/java/nextstep/shoppingcart/data/Cart.kt b/app/src/main/java/nextstep/shoppingcart/data/Cart.kt index 31ccb195..cefd1acb 100644 --- a/app/src/main/java/nextstep/shoppingcart/data/Cart.kt +++ b/app/src/main/java/nextstep/shoppingcart/data/Cart.kt @@ -38,6 +38,6 @@ object Cart { return items } - fun findProductById(productId: Long) = - items.find { it.product.id == productId } ?: throw IllegalArgumentException() + fun findCartProductById(cartProductId: Long) = + items.find { it.product.id == cartProductId } ?: throw IllegalArgumentException() } diff --git a/app/src/main/java/nextstep/shoppingcart/data/Products.kt b/app/src/main/java/nextstep/shoppingcart/data/Products.kt index d282d36b..afb6e435 100644 --- a/app/src/main/java/nextstep/shoppingcart/data/Products.kt +++ b/app/src/main/java/nextstep/shoppingcart/data/Products.kt @@ -1,5 +1,6 @@ package nextstep.shoppingcart.data +import nextstep.shoppingcart.data.Cart.items import nextstep.shoppingcart.ui.shoppinglist.model.Product object Products { @@ -56,4 +57,9 @@ object Products { fun findProductById(productId: Long) = dummyProducts.find { it.id == productId } ?: throw IllegalArgumentException() + + fun List.updateProducts(): List = map { product -> + items.find { it.product.id == product.id } + ?.let { product.copy(containedCount = it.count) } ?: product + } } diff --git a/app/src/main/java/nextstep/shoppingcart/ui/component/ShoppingCountBar.kt b/app/src/main/java/nextstep/shoppingcart/ui/component/ShoppingCountBar.kt index 29d1c45f..e1c94827 100644 --- a/app/src/main/java/nextstep/shoppingcart/ui/component/ShoppingCountBar.kt +++ b/app/src/main/java/nextstep/shoppingcart/ui/component/ShoppingCountBar.kt @@ -29,6 +29,7 @@ import nextstep.shoppingcart.R.drawable.ic_add import nextstep.shoppingcart.R.drawable.ic_subtract import nextstep.shoppingcart.R.string.shopping_count_bar_add_icon import nextstep.shoppingcart.R.string.shopping_count_bar_count +import nextstep.shoppingcart.R.string.shopping_count_bar_description import nextstep.shoppingcart.R.string.shopping_count_bar_subtract_icon import nextstep.shoppingcart.R.string.shopping_count_bar_total_count import nextstep.shoppingcart.ui.theme.RobotoRegular @@ -43,11 +44,14 @@ fun ShoppingCountBar( val shoppingCountBarSubtractIcon = stringResource(id = shopping_count_bar_subtract_icon) val shoppingCountBarAddIcon = stringResource(id = shopping_count_bar_add_icon) val shoppingCountBarTotalCount = stringResource(id = shopping_count_bar_total_count) + val shoppingCountBar = stringResource(shopping_count_bar_description) Row( horizontalArrangement = Arrangement.SpaceEvenly, verticalAlignment = Alignment.CenterVertically, - modifier = modifier.background(color = Color.White), + modifier = modifier + .background(color = Color.White) + .semantics { contentDescription = shoppingCountBar }, ) { Icon( painter = painterResource(id = ic_subtract), diff --git a/app/src/main/java/nextstep/shoppingcart/ui/shoppingcart/ShoppingCartRoute.kt b/app/src/main/java/nextstep/shoppingcart/ui/shoppingcart/ShoppingCartRoute.kt index 0c4f43fd..dced225a 100644 --- a/app/src/main/java/nextstep/shoppingcart/ui/shoppingcart/ShoppingCartRoute.kt +++ b/app/src/main/java/nextstep/shoppingcart/ui/shoppingcart/ShoppingCartRoute.kt @@ -7,7 +7,7 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import nextstep.shoppingcart.data.Cart.addOne -import nextstep.shoppingcart.data.Cart.findProductById +import nextstep.shoppingcart.data.Cart.findCartProductById import nextstep.shoppingcart.data.Cart.items import nextstep.shoppingcart.data.Cart.removeAll import nextstep.shoppingcart.data.Cart.removeOne @@ -25,17 +25,17 @@ fun ShoppingCartRoute( total = total, onBackClick = onBackClick, onRemoveClick = { cartProductId -> - removeAll(findProductById(cartProductId).product) + removeAll(findCartProductById(cartProductId).product) cartProducts = items total = totalPrice }, onAddClick = { cartProductId -> - addOne(findProductById(cartProductId).product) + addOne(findCartProductById(cartProductId).product) cartProducts = items total = totalPrice }, onSubtractClick = { cartProductId -> - removeOne(findProductById(cartProductId).product) + removeOne(findCartProductById(cartProductId).product) cartProducts = items total = totalPrice }, diff --git a/app/src/main/java/nextstep/shoppingcart/ui/shoppingcart/component/ShoppingCartItem.kt b/app/src/main/java/nextstep/shoppingcart/ui/shoppingcart/component/ShoppingCartItem.kt index 170c81e6..52dd8e93 100644 --- a/app/src/main/java/nextstep/shoppingcart/ui/shoppingcart/component/ShoppingCartItem.kt +++ b/app/src/main/java/nextstep/shoppingcart/ui/shoppingcart/component/ShoppingCartItem.kt @@ -35,8 +35,6 @@ fun ShoppingCartItem( Column( modifier = modifier - .fillMaxWidth() - .wrapContentHeight() .border( width = 1.dp, color = Color.Gray, @@ -60,7 +58,7 @@ fun ShoppingCartItem( } } -@Preview +@Preview(showBackground = true) @Composable private fun ShoppingCartItemPreview() { ShoppingCartItem( diff --git a/app/src/main/java/nextstep/shoppingcart/ui/shoppingcart/component/ShoppingCartItemBody.kt b/app/src/main/java/nextstep/shoppingcart/ui/shoppingcart/component/ShoppingCartItemBody.kt index 6e3f5672..1416f5fc 100644 --- a/app/src/main/java/nextstep/shoppingcart/ui/shoppingcart/component/ShoppingCartItemBody.kt +++ b/app/src/main/java/nextstep/shoppingcart/ui/shoppingcart/component/ShoppingCartItemBody.kt @@ -8,7 +8,6 @@ import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.width -import androidx.compose.foundation.layout.wrapContentWidth import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -41,7 +40,6 @@ fun ShoppingCartItemBody( ) Column( horizontalAlignment = Alignment.End, - modifier = Modifier.wrapContentWidth(), ) { Spacer(modifier = Modifier.height(height = 18.dp)) ShoppingCartItemSumText(sum) diff --git a/app/src/main/java/nextstep/shoppingcart/ui/shoppingcart/component/ShoppingCartLazyColumn.kt b/app/src/main/java/nextstep/shoppingcart/ui/shoppingcart/component/ShoppingCartLazyColumn.kt index e72a2399..1f37a5aa 100644 --- a/app/src/main/java/nextstep/shoppingcart/ui/shoppingcart/component/ShoppingCartLazyColumn.kt +++ b/app/src/main/java/nextstep/shoppingcart/ui/shoppingcart/component/ShoppingCartLazyColumn.kt @@ -1,7 +1,7 @@ package nextstep.shoppingcart.ui.shoppingcart.component import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.runtime.Composable @@ -21,7 +21,8 @@ fun ShoppingCartLazyColumn( ) { LazyColumn( verticalArrangement = Arrangement.spacedBy(space = 16.dp), - modifier = modifier.padding(horizontal = 18.dp), + contentPadding = PaddingValues(horizontal = 18.dp), + modifier = modifier, ) { items( items = cartProducts, diff --git a/app/src/main/java/nextstep/shoppingcart/ui/shoppinglist/ShoppingListRoute.kt b/app/src/main/java/nextstep/shoppingcart/ui/shoppinglist/ShoppingListRoute.kt index f3cf18a8..b460281b 100644 --- a/app/src/main/java/nextstep/shoppingcart/ui/shoppinglist/ShoppingListRoute.kt +++ b/app/src/main/java/nextstep/shoppingcart/ui/shoppinglist/ShoppingListRoute.kt @@ -1,18 +1,38 @@ package nextstep.shoppingcart.ui.shoppinglist import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import nextstep.shoppingcart.data.Cart.addOne +import nextstep.shoppingcart.data.Cart.removeOne import nextstep.shoppingcart.data.Products.dummyProducts +import nextstep.shoppingcart.data.Products.findProductById +import nextstep.shoppingcart.data.Products.updateProducts @Composable fun ShoppingListRoute( onShoppingCartClick: () -> Unit, onItemClick: (productId: Long) -> Unit, ) { - val products = dummyProducts + var products by remember { mutableStateOf(dummyProducts.updateProducts()) } ShoppingListScreen( products = products, onShoppingCartClick = onShoppingCartClick, onItemClick = onItemClick, + onPutClick = { cartProductId -> + addOne(findProductById(cartProductId)) + products = dummyProducts.updateProducts() + }, + onAddClick = { cartProductId -> + addOne(findProductById(cartProductId)) + products = dummyProducts.updateProducts() + }, + onSubtractClick = { cartProductId -> + removeOne(findProductById(cartProductId)) + products = dummyProducts.updateProducts() + }, ) } diff --git a/app/src/main/java/nextstep/shoppingcart/ui/shoppinglist/ShoppingListScreen.kt b/app/src/main/java/nextstep/shoppingcart/ui/shoppinglist/ShoppingListScreen.kt index f708451d..b9dddf10 100644 --- a/app/src/main/java/nextstep/shoppingcart/ui/shoppinglist/ShoppingListScreen.kt +++ b/app/src/main/java/nextstep/shoppingcart/ui/shoppinglist/ShoppingListScreen.kt @@ -17,6 +17,9 @@ fun ShoppingListScreen( products: List, onShoppingCartClick: () -> Unit, onItemClick: (productId: Long) -> Unit, + onPutClick: (productId: Long) -> Unit, + onAddClick: (productId: Long) -> Unit, + onSubtractClick: (productId: Long) -> Unit, modifier: Modifier = Modifier, ) { Column(modifier = modifier.fillMaxSize()) { @@ -27,6 +30,9 @@ fun ShoppingListScreen( ShoppingListLazyVerticalGrid( products = products, onItemClick = onItemClick, + onPutClick = onPutClick, + onAddClick = onAddClick, + onSubtractClick = onSubtractClick, ) } } @@ -38,5 +44,8 @@ private fun ShoppingListScreenPreview() { products = dummyProducts, onShoppingCartClick = {}, onItemClick = {}, + onPutClick = {}, + onAddClick = {}, + onSubtractClick = {}, ) } diff --git a/app/src/main/java/nextstep/shoppingcart/ui/shoppinglist/component/ShoppingListItem.kt b/app/src/main/java/nextstep/shoppingcart/ui/shoppinglist/component/ShoppingListItem.kt index 16a053c9..71a9e2e7 100644 --- a/app/src/main/java/nextstep/shoppingcart/ui/shoppinglist/component/ShoppingListItem.kt +++ b/app/src/main/java/nextstep/shoppingcart/ui/shoppinglist/component/ShoppingListItem.kt @@ -1,10 +1,10 @@ package nextstep.shoppingcart.ui.shoppinglist.component +import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier @@ -19,7 +19,6 @@ import androidx.compose.ui.unit.sp import nextstep.shoppingcart.R.string.shopping_List_item_description import nextstep.shoppingcart.R.string.shopping_item_price_format import nextstep.shoppingcart.data.Products.dummyProducts -import nextstep.shoppingcart.ui.component.ShoppingProductImage import nextstep.shoppingcart.ui.shoppinglist.model.Product import nextstep.shoppingcart.ui.theme.RobotoBold import nextstep.shoppingcart.ui.theme.RobotoRegular @@ -28,6 +27,9 @@ import nextstep.shoppingcart.ui.theme.RobotoRegular fun ShoppingListItem( product: Product, onItemClick: (productId: Long) -> Unit, + onPutClick: (productId: Long) -> Unit, + onAddClick: (productId: Long) -> Unit, + onSubtractClick: (productId: Long) -> Unit, modifier: Modifier = Modifier, ) { val shoppingListItemDescription = stringResource(id = shopping_List_item_description) @@ -38,9 +40,11 @@ fun ShoppingListItem( .clickable { onItemClick(product.id) } .semantics { contentDescription = shoppingListItemDescription }, ) { - ShoppingProductImage( + ShoppingProductHeader( product = product, - modifier = Modifier.size(size = 156.dp), + onPutClick = onPutClick, + onAddClick = onAddClick, + onSubtractClick = onSubtractClick, ) Text( text = product.name, @@ -57,11 +61,6 @@ fun ShoppingListItem( ) Text( text = stringResource(id = shopping_item_price_format, product.price), - modifier = Modifier.padding( - start = 4.dp, - bottom = 4.dp, - end = 86.dp, - ), color = Color.Black, fontSize = 16.sp, fontFamily = RobotoRegular, @@ -76,5 +75,9 @@ private fun ShoppingListItemPreview() { ShoppingListItem( product = dummyProducts[0], onItemClick = {}, + onPutClick = {}, + onAddClick = {}, + onSubtractClick = {}, + modifier = Modifier.background(Color.White), ) } diff --git a/app/src/main/java/nextstep/shoppingcart/ui/shoppinglist/component/ShoppingListLazyVerticalGrid.kt b/app/src/main/java/nextstep/shoppingcart/ui/shoppinglist/component/ShoppingListLazyVerticalGrid.kt index 9988dd97..ae88a748 100644 --- a/app/src/main/java/nextstep/shoppingcart/ui/shoppinglist/component/ShoppingListLazyVerticalGrid.kt +++ b/app/src/main/java/nextstep/shoppingcart/ui/shoppinglist/component/ShoppingListLazyVerticalGrid.kt @@ -21,6 +21,9 @@ import nextstep.shoppingcart.ui.shoppinglist.model.Product fun ShoppingListLazyVerticalGrid( products: List, onItemClick: (productId: Long) -> Unit, + onPutClick: (productId: Long) -> Unit, + onAddClick: (productId: Long) -> Unit, + onSubtractClick: (productId: Long) -> Unit, modifier: Modifier = Modifier, ) { val shoppingListDescription = stringResource(id = shopping_list_description) @@ -44,6 +47,9 @@ fun ShoppingListLazyVerticalGrid( ShoppingListItem( product = product, onItemClick = onItemClick, + onPutClick = onPutClick, + onAddClick = onAddClick, + onSubtractClick = onSubtractClick, ) } } @@ -55,5 +61,8 @@ private fun ShoppingListPreview() { ShoppingListLazyVerticalGrid( products = dummyProducts, onItemClick = {}, + onPutClick = {}, + onAddClick = {}, + onSubtractClick = {}, ) } diff --git a/app/src/main/java/nextstep/shoppingcart/ui/shoppinglist/component/ShoppingProductAddButton.kt b/app/src/main/java/nextstep/shoppingcart/ui/shoppinglist/component/ShoppingProductAddButton.kt new file mode 100644 index 00000000..bcf12cd5 --- /dev/null +++ b/app/src/main/java/nextstep/shoppingcart/ui/shoppinglist/component/ShoppingProductAddButton.kt @@ -0,0 +1,56 @@ +package nextstep.shoppingcart.ui.shoppinglist.component + +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Add +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.Icon +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.contentDescription +import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import nextstep.shoppingcart.R.string.shopping_product_add_button +import nextstep.shoppingcart.R.string.shopping_product_add_button_description + +@Composable +fun ShoppingProductAddButton( + onAddButton: () -> Unit, + modifier: Modifier = Modifier, +) { + val shoppingProductAddButtonDescription = + stringResource(id = shopping_product_add_button_description) + val shoppingProductAddButton = stringResource(id = shopping_product_add_button) + + Button( + onClick = onAddButton, + shape = CircleShape, + colors = ButtonDefaults.buttonColors( + contentColor = Color.Black, + containerColor = Color.White, + ), + contentPadding = PaddingValues(0.dp), + modifier = modifier + .size(42.dp) + .semantics { contentDescription = shoppingProductAddButtonDescription }, + ) { + Icon( + imageVector = Icons.Filled.Add, + contentDescription = shoppingProductAddButton, + ) + } +} + +@Preview +@Composable +private fun ShoppingProductAddButtonPreview() { + ShoppingProductAddButton( + onAddButton = {}, + ) +} diff --git a/app/src/main/java/nextstep/shoppingcart/ui/shoppinglist/component/ShoppingProductHeader.kt b/app/src/main/java/nextstep/shoppingcart/ui/shoppinglist/component/ShoppingProductHeader.kt new file mode 100644 index 00000000..f63593d0 --- /dev/null +++ b/app/src/main/java/nextstep/shoppingcart/ui/shoppinglist/component/ShoppingProductHeader.kt @@ -0,0 +1,60 @@ +package nextstep.shoppingcart.ui.shoppinglist.component + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import nextstep.shoppingcart.data.Products.dummyProducts +import nextstep.shoppingcart.ui.component.ShoppingCountBar +import nextstep.shoppingcart.ui.component.ShoppingProductImage +import nextstep.shoppingcart.ui.shoppinglist.model.Product + +@Composable +fun ShoppingProductHeader( + product: Product, + onPutClick: (productId: Long) -> Unit, + onAddClick: (productId: Long) -> Unit, + onSubtractClick: (productId: Long) -> Unit, + modifier: Modifier = Modifier, +) { + Box( + contentAlignment = Alignment.BottomEnd, + ) { + ShoppingProductImage( + product = product, + modifier = modifier + .size(size = 156.dp) + .background(Color.Black), + ) + when (product.containedCount > 0) { + true -> ShoppingCountBar( + count = product.containedCount, + onSubtractClick = { onSubtractClick(product.id) }, + onAddClick = { onAddClick(product.id) }, + modifier = Modifier.align(Alignment.BottomCenter), + ) + + false -> ShoppingProductAddButton( + onAddButton = { onPutClick(product.id) }, + modifier = Modifier.padding(12.dp), + ) + } + } +} + +@Preview +@Composable +private fun ShoppingProductHeaderPreview() { + ShoppingProductHeader( + product = dummyProducts[0].copy(containedCount = 2), + onPutClick = {}, + onAddClick = {}, + onSubtractClick = {}, + ) +} diff --git a/app/src/main/java/nextstep/shoppingcart/ui/shoppinglist/model/Product.kt b/app/src/main/java/nextstep/shoppingcart/ui/shoppinglist/model/Product.kt index de1faf3e..d8e8a938 100644 --- a/app/src/main/java/nextstep/shoppingcart/ui/shoppinglist/model/Product.kt +++ b/app/src/main/java/nextstep/shoppingcart/ui/shoppinglist/model/Product.kt @@ -8,4 +8,5 @@ data class Product( val name: String, val imageUrl: String, val price: Long, + val containedCount: Int = 0, ) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 86f5ead6..5cfed1b3 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -12,12 +12,15 @@ 장바구니 담기 금액 장바구니 - 주문하기(%d)원 - %d원 + 주문하기(%,d)원 + %,d원 ShoppingCartItemHeaderCloseButton ShoppingCountBarSubtractIcon ShoppingCountBarAddIcon %d ShoppingCountBarTotalCount ShoppingCartItemSum + ShoppingProductAddButton + shoppingProductAddButtonDescription + shoppingCountBarDescription