Repository navigation
fix(mindrecord): 주차 드롭다운 선택지를 1년으로 넓히고 메뉴를 5개 높이로 묶는다 #937
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
98ac361
8b75a44
98bfdf7
00dba6b
e9e7be6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| package com.afternote.feature.mindrecord.presentation.viewmodel | ||
|
|
||
| import java.time.DayOfWeek | ||
| import java.time.LocalDate | ||
|
|
||
| /** | ||
| * 주차 드롭다운이 제공하는 과거 리포트 범위 (이번 주 포함 52주 = 1년). | ||
| * | ||
| * 종전 값 `5` 는 "한 화면에 보이는 항목 수" 를 그대로 선택지 개수로 쓴 것이라, 시안이 | ||
| * 표현한 추가 과거 리포트와 세로 스크롤을 쓸 수 없었다 (#729). **가시 항목 수는 메뉴 | ||
| * 높이가 정하고**(`WeeklyReportReviewCard`), 여기서는 선택 가능한 범위만 정한다. | ||
| * | ||
| * 서버에 가입일이 없어(`GET /users/me` 는 이름·이메일·전화·프로필만 준다) 계정 생성 주까지로 | ||
| * 좁힐 수 없다. 기록이 없는 주를 골라도 리포트는 0 건으로 정상 응답하므로 1년을 상한으로 뒀다. | ||
| */ | ||
| internal const val WEEK_OPTION_COUNT = 52 | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [normal] 최하단 구간 라벨에 연도가 없어 작년 주차가 올해 것처럼 읽힙니다. 범위가 1년이 되면서 메뉴 하단은 2025년 8·9월 구간인데 라벨 포맷( 라벨 중복은 없습니다 — 52주 창에서 달 이름이 겹치는 건 상·하단의 같은 달뿐이고, 하단(작년) 주차 인덱스는 항상 상단(올해) 같은 달의 최대 인덱스보다 큽니다(52주=364일이라 최하단이 같은 달 8~9일 뒤 날짜로 떨어짐). 에뮬레이터 실측도 상단 8월 1·2·3주차 ↔ 하단 8월 4주차로 분리돼 나왔습니다. 시안 라벨 포맷 자체가 연도 없는 형태라 미확정 영역이고, FE 재량 규약대로 비차단입니다 — 작년 구간에만 연도를 병기할지는 후속 판단거리로 남깁니다. |
||
|
|
||
| /** | ||
| * 이번 주 월요일부터 과거로 [count] 주를 최신순으로 만든다. | ||
| * | ||
| * 첫 원소가 언제나 이번 주라 진입 시 로드 대상([WeeklyReportViewModel.init])과 | ||
| * 실패 시 폴백 기준이 된다. | ||
| */ | ||
| internal fun buildWeekOptions( | ||
| today: LocalDate, | ||
| count: Int = WEEK_OPTION_COUNT, | ||
| ): List<WeekOption> { | ||
| val thisMonday = today.with(DayOfWeek.MONDAY) | ||
| return (0 until count).map { weeksAgo -> | ||
| WeekOption(monday = thisMonday.minusWeeks(weeksAgo.toLong())) | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,82 @@ | ||
| package com.afternote.feature.mindrecord.presentation.component | ||
|
|
||
| import androidx.activity.ComponentActivity | ||
| import androidx.compose.ui.test.assertIsDisplayed | ||
| import androidx.compose.ui.test.junit4.createAndroidComposeRule | ||
| import androidx.compose.ui.test.onAllNodesWithText | ||
| import androidx.compose.ui.test.onNodeWithText | ||
| import androidx.compose.ui.test.performClick | ||
| import androidx.compose.ui.test.performScrollTo | ||
| import com.afternote.core.ui.theme.AfternoteTheme | ||
| import com.afternote.feature.mindrecord.presentation.viewmodel.WeekOption | ||
| import com.afternote.feature.mindrecord.presentation.viewmodel.buildWeekOptions | ||
| import org.junit.Assert.assertEquals | ||
| import org.junit.Rule | ||
| import org.junit.Test | ||
| import org.junit.runner.RunWith | ||
| import org.robolectric.RobolectricTestRunner | ||
| import org.robolectric.annotation.Config | ||
| import java.time.LocalDate | ||
|
|
||
| /** | ||
| * 열린 주차 메뉴의 스크롤·최하단 선택 회귀 가드 (#729). | ||
| * | ||
| * 시안(node 700-35071)의 열린 메뉴는 항목 5개 높이에서 잘리고 오른쪽에 세로 스크롤 | ||
| * 표시가 있다. 선택지가 5개에서 끝나면 스크롤할 것이 없어 이 시연 자체가 불가능하다. | ||
| */ | ||
| @RunWith(RobolectricTestRunner::class) | ||
| @Config(sdk = [35]) | ||
| class WeeklyReportWeekMenuTest { | ||
| @get:Rule | ||
| val composeRule = createAndroidComposeRule<ComponentActivity>() | ||
|
|
||
| private val today = LocalDate.of(2026, 8, 23) | ||
| private val options: List<WeekOption> = buildWeekOptions(today = today) | ||
|
|
||
| private fun label(monday: LocalDate): String = "${monday.monthValue}월 ${(monday.dayOfMonth - 1) / 7 + 1}주차 리포트" | ||
|
|
||
| private fun openMenu(onWeekSelect: (LocalDate) -> Unit = {}) { | ||
| composeRule.setContent { | ||
| AfternoteTheme { | ||
| WeeklyReportReviewCard( | ||
| selectedMonday = options.first().monday, | ||
| weekOptions = options, | ||
| onWeekSelect = onWeekSelect, | ||
| ) | ||
| } | ||
| } | ||
| composeRule.onNodeWithText(label(options.first().monday)).performClick() | ||
| } | ||
|
|
||
| @Test | ||
| fun `열린 메뉴에 5개를 넘는 선택지가 실린다`() { | ||
| openMenu() | ||
|
|
||
| // 6번째 항목은 메뉴 높이 밖이라 스크롤해야 닿는다 — 존재 자체는 트리에 있다. | ||
| composeRule.onNodeWithText(label(options[5].monday)).performScrollTo().assertIsDisplayed() | ||
| } | ||
|
|
||
| @Test | ||
| fun `스크롤해서 최하단 주차를 고르면 그 월요일이 전달된다`() { | ||
| var selected: LocalDate? = null | ||
| openMenu { selected = it } | ||
|
|
||
| val bottom = options.last().monday | ||
| composeRule.onNodeWithText(label(bottom)).performScrollTo().performClick() | ||
|
|
||
| assertEquals(bottom, selected) | ||
| assertEquals(LocalDate.of(2026, 8, 17).minusWeeks(51), selected) | ||
| } | ||
|
|
||
| @Test | ||
| fun `이미 보고 있는 주를 다시 고르면 재조회하지 않는다`() { | ||
| var calls = 0 | ||
| openMenu { calls++ } | ||
|
|
||
| // 같은 문구가 앵커와 메뉴 항목 두 곳에 있다 — 메뉴 쪽(마지막)을 누른다. | ||
| val nodes = composeRule.onAllNodesWithText(label(options.first().monday)) | ||
| nodes[nodes.fetchSemanticsNodes().lastIndex].performClick() | ||
|
|
||
| assertEquals(0, calls) | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| package com.afternote.feature.mindrecord.presentation.viewmodel | ||
|
|
||
| import org.junit.Assert.assertEquals | ||
| import org.junit.Assert.assertTrue | ||
| import org.junit.Test | ||
| import java.time.DayOfWeek | ||
| import java.time.LocalDate | ||
|
|
||
| /** | ||
| * 주차 선택지 범위 회귀 가드 (#729). | ||
| * | ||
| * 종전에는 "한 화면에 보이는 항목 수" 5 를 그대로 선택지 개수로 써서, 시안이 표현한 | ||
| * 추가 과거 리포트와 세로 스크롤을 쓸 수 없었다. 가시 항목 수와 선택 가능한 범위를 | ||
| * 분리했다는 사실을 여기서 고정한다. | ||
| */ | ||
| class WeeklyReportWeekOptionsTest { | ||
| @Test | ||
| fun `선택지는 한 화면 가시 항목 수보다 많다`() { | ||
| // 5 로 되돌리면 여기서 먼저 깨진다 — 스크롤할 항목이 없어지는 지점이다. | ||
| assertTrue("선택지가 가시 항목 수(5)를 넘어야 스크롤이 생긴다", WEEK_OPTION_COUNT > 5) | ||
| assertEquals(52, WEEK_OPTION_COUNT) | ||
| } | ||
|
|
||
| @Test | ||
| fun `이번 주 월요일부터 과거로 최신순으로 만든다`() { | ||
| // 2026-08-23 은 일요일 — 그 주 월요일은 2026-08-17 이다. | ||
| val options = buildWeekOptions(today = LocalDate.of(2026, 8, 23)) | ||
|
|
||
| assertEquals(WEEK_OPTION_COUNT, options.size) | ||
| assertEquals(LocalDate.of(2026, 8, 17), options.first().monday) | ||
| assertTrue(options.all { it.monday.dayOfWeek == DayOfWeek.MONDAY }) | ||
| assertEquals( | ||
| options.map { it.monday }.sortedDescending(), | ||
| options.map { it.monday }, | ||
| ) | ||
| } | ||
|
|
||
| @Test | ||
| fun `최하단 선택지는 1년 전 주다`() { | ||
| val options = buildWeekOptions(today = LocalDate.of(2026, 8, 23)) | ||
|
|
||
| assertEquals(LocalDate.of(2026, 8, 17).minusWeeks(51), options.last().monday) | ||
| } | ||
|
|
||
| @Test | ||
| fun `월요일에 들어와도 그 주가 첫 선택지다`() { | ||
| // `with(DayOfWeek.MONDAY)` 는 그 주의 월요일이라 월요일 당일이면 자기 자신이다. | ||
| val options = buildWeekOptions(today = LocalDate.of(2026, 8, 17)) | ||
|
|
||
| assertEquals(LocalDate.of(2026, 8, 17), options.first().monday) | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
시안 대조 재량 기록 (조치 불요, 노드 700-35071 직접 렌더·메타데이터 실측):
DropdownMenu에 네이티브 스크롤바가 없고, 위 주석의 "가시 항목 수보다 선택지가 많다는 표현" 해석에 동의합니다.