Skip to content
17 changes: 14 additions & 3 deletions core/ui/src/main/kotlin/com/afternote/core/ui/topbar/HomeTopBar.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,17 @@ import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.afternote.core.ui.R

/** 프로필 아이콘 앵커. 장식이라 semantics 이름이 없어 테스트가 이 태그로 존재 여부를 본다. */
const val PROFILE_ICON_TEST_TAG = "home_top_bar_profile"

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun HomeTopBar(
Expand All @@ -44,22 +50,27 @@ fun HomeTopBar(
.padding(end = 25.dp),
) {
if (showProfileIcon) {
// 목적지가 없어 어디서도 눌리지 않는다 — 장식이므로 이름을 붙이지 않는다.
// 이름을 주면 TalkBack 이 포커스 가능한 노드로 읽어, 사용자가 액션이 있다고
// 믿고 탭했다가 아무 반응도 얻지 못한다 (#613 리뷰).
Image(
painter = painterResource(R.drawable.core_ui_user),
contentDescription = null,
modifier = Modifier.size(18.dp),
modifier = Modifier.size(18.dp).testTag(PROFILE_ICON_TEST_TAG),
)

Spacer(modifier = Modifier.width(15.dp))
}

Image(
painter = painterResource(R.drawable.core_ui_settings),
contentDescription = null,
// 유일하게 눌리는 액션인데 접근성 트리에 이름이 없었다 — 스크린리더가
// "버튼" 으로만 읽는다 (#613).
contentDescription = stringResource(R.string.core_ui_home_top_bar_setting),
modifier =
Modifier
.size(18.dp)
.clickable { onSettingClick() },
.clickable(role = Role.Button) { onSettingClick() },
)
}
},
Expand Down
2 changes: 2 additions & 0 deletions core/ui/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,6 @@
<string name="core_ui_network_error_title">네트워크 연결 오류</string>
<string name="core_ui_network_error_description">인터넷 연결을 확인한 후 다시 시도해 주세요.</string>
<string name="core_ui_network_error_retry">다시 시도하기</string>
<!-- 홈 상단바 설정 액션 접근성 이름 (#613). 프로필 아이콘은 목적지가 없는 장식이라 이름을 두지 않는다. -->
<string name="core_ui_home_top_bar_setting">설정</string>
</resources>
9 changes: 9 additions & 0 deletions docs/qa/assumptions.md
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,15 @@ Compose 테스트로 고정했다.
스키마에 `date` 없음, 보내도 무시됨을 실측). 날짜 선택이 표시 전용인 문제는 BE 계약
확장이나 기획 결정이 필요해 #1008 로 분리했다.

## #613 — 수신자 홈 설정 진입 (판단 보류)

- 시안(수신자 섹션 `4327:73626`)의 수신자 홈 헤더에는 **톱니만** 있고 프로필 아이콘이 없다.
코드도 이미 `showProfileIcon = false` 라 1번 지적은 해소된 상태였고, 재발 방지 가드만 붙였다.
- 톱니의 목적지는 확정하지 못했다. 같은 섹션의 「설정 메인」(`4327:75979`)은 회원 설정
화면(프로필 수정·비밀번호 변경·로그아웃)을 그대로 복제한 것이라 **수신자 전용 설정
시안은 없다**. 수신자는 X-Auth-Code 기반이라 로그아웃할 세션이 없다.
- 현 배선은 회원 설정 그래프(`Route.Setting`)로 그대로 보낸다. 무엇을 남길지(고객지원만,
인증 해제, 진입점 제거)는 기획·디자인 확정이 필요해 이번 PR 범위에서 제외했다.
## #793 — MEMORIES 카드 「그날의 기록 다시 읽기」 목적지 (임시 승계)

- **최종 목적지는 여전히 미확정이다.** Figma 카드 노드(`109:15028`)의 이 버튼에는 프로토타입
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package com.afternote.feature.receiver.presentation.home

import androidx.activity.ComponentActivity
import androidx.compose.ui.test.assertHasClickAction
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.compose.ui.test.onNodeWithContentDescription
import androidx.compose.ui.test.onNodeWithTag
import com.afternote.core.ui.R
import com.afternote.core.ui.theme.AfternoteTheme
import com.afternote.core.ui.topbar.PROFILE_ICON_TEST_TAG
import com.afternote.feature.receiver.presentation.home.model.ReceiverHomeUiState
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config

/**
* 수신자 홈 헤더가 수신자가 실제로 할 수 있는 액션만 두는지 (#613, 시안 4327:73626).
*
* 종전에는 회원 홈과 같은 상단바를 그대로 써서 프로필 아이콘이 함께 떴는데, 목적지가
* 없어 탭해도 아무 일도 없었다 — 접근성 트리에서도 clickable 이 아니라 사실상 장식이다.
* 시안의 수신자 홈에도 톱니 하나뿐이라 그 상태를 고정한다.
*
* 설정 아이콘의 이름도 함께 고정한다. 종전에는 둘 다 `contentDescription = null` 이라
* 스크린리더가 **유일하게 눌리는 액션**조차 읽어 주지 못했다. 프로필 아이콘은 반대로
* 이름을 두지 않는다 — 목적지가 없어 어디서도 눌리지 않는 장식이고, 이름을 주면 TalkBack 이
* 포커스 가능한 노드로 읽어 «눌러도 아무 일 없는 버튼» 이 된다 (리뷰 지적).
*/
@RunWith(RobolectricTestRunner::class)
@Config(sdk = [35])
class ReceiverHomeHeaderTest {
@get:Rule
val composeRule = createAndroidComposeRule<ComponentActivity>()

@Test
fun `수신자 홈에는 프로필 아이콘이 없다`() {
composeRule.setContent {
AfternoteTheme {
ReceiverHomeScreen(
uiState = ReceiverHomeUiState.Loading,
onEvent = {},
actions = ReceiverHomeActions(),
)
}
}

// 프로필 아이콘은 목적지가 없는 장식이라 semantics 이름이 없다 — 테스트 태그로 본다.
composeRule.onNodeWithTag(PROFILE_ICON_TEST_TAG).assertDoesNotExist()
}

@Test
fun `설정 아이콘은 이름이 있고 눌린다`() {
var settingClicks = 0
composeRule.setContent {
AfternoteTheme {
ReceiverHomeScreen(
uiState = ReceiverHomeUiState.Loading,
onEvent = {},
actions = ReceiverHomeActions(onSettingClick = { settingClicks += 1 }),
)
}
}

val setting = composeRule.activity.getString(R.string.core_ui_home_top_bar_setting)
composeRule.onNodeWithContentDescription(setting).assertIsDisplayed().assertHasClickAction()
}
}
Loading