From e22a5fccb1ea1fb74e1fcaea4feabbe64ba46160 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E4=B8=80=E6=9D=A1=E5=9B=BA=E6=89=A7=E7=9A=84=E9=B1=BC?=
<1504947133@qq.com>
Date: Wed, 19 Aug 2026 14:32:50 +0800
Subject: [PATCH 1/7] feat(schedule): simplify calendar home and detail chrome
Keep the selected date beside account controls, connect occurrence cards with a timeline rail, and drop type badges from the agenda and detail sheets.
---
.../LocationScheduleDetailSheet.tsx | 1 -
.../presentation/LocationScheduleRow.tsx | 15 +-
.../presentation/ScheduleCalendarScreen.tsx | 45 +++---
.../presentation/ScheduleDetailSheet.tsx | 32 ++--
.../ScheduleOccurrenceDetailSheet.tsx | 17 ++-
.../presentation/ScheduleOccurrenceRow.tsx | 143 +++++++++++++-----
.../schedule/presentation/scheduleDisplay.ts | 20 +++
.../ScheduleCalendarScreen.test.tsx | 25 ++-
.../presentation/ScheduleDetailSheet.test.tsx | 11 +-
.../presentation/ScheduleRows.test.tsx | 42 ++++-
.../presentation/scheduleDisplay.test.ts | 38 +++++
11 files changed, 280 insertions(+), 109 deletions(-)
create mode 100644 frontend/tests/unit/features/schedule/presentation/scheduleDisplay.test.ts
diff --git a/frontend/src/features/schedule/presentation/LocationScheduleDetailSheet.tsx b/frontend/src/features/schedule/presentation/LocationScheduleDetailSheet.tsx
index 908b43f5..4dd14cad 100644
--- a/frontend/src/features/schedule/presentation/LocationScheduleDetailSheet.tsx
+++ b/frontend/src/features/schedule/presentation/LocationScheduleDetailSheet.tsx
@@ -33,7 +33,6 @@ export function LocationScheduleDetailSheet({
return (
-
- 位置日程
-
{item.title}
@@ -53,15 +50,7 @@ function LocationPinIcon() {
}
const styles = StyleSheet.create({
- badge: {
- alignSelf: 'flex-start',
- backgroundColor: colors.accent,
- borderRadius: 999,
- paddingHorizontal: spacing.sm,
- paddingVertical: 2,
- },
- badgeText: { color: colors.text, fontSize: 11, fontWeight: '700' },
- copy: { flex: 1, gap: 6, minWidth: 0 },
+ copy: { flex: 1, gap: 4, minWidth: 0 },
iconWrap: {
alignItems: 'center',
backgroundColor: colors.accent,
diff --git a/frontend/src/features/schedule/presentation/ScheduleCalendarScreen.tsx b/frontend/src/features/schedule/presentation/ScheduleCalendarScreen.tsx
index 84f81a24..b573b0e5 100644
--- a/frontend/src/features/schedule/presentation/ScheduleCalendarScreen.tsx
+++ b/frontend/src/features/schedule/presentation/ScheduleCalendarScreen.tsx
@@ -13,6 +13,7 @@ import { LocationScheduleRow } from './LocationScheduleRow';
import { MonthCalendar } from './MonthCalendar';
import { ScheduleOccurrenceDetailSheet } from './ScheduleOccurrenceDetailSheet';
import { ScheduleOccurrenceRow } from './ScheduleOccurrenceRow';
+import { emptyAgendaMessage, formatAgendaSectionTitle } from './scheduleDisplay';
import { useScheduleCalendar } from './useScheduleCalendar';
import type { CalendarFocusTarget } from './calendarFocus';
@@ -55,6 +56,8 @@ export function ScheduleCalendarScreen({
const [selectedOccurrence, setSelectedOccurrence] = useState(null);
const [selectedLocation, setSelectedLocation] = useState(null);
const selectedLabel = SELECTED_DATE_FORMATTER.format(calendar.selectedDate);
+ const agendaTitle = formatAgendaSectionTitle(calendar.selectedDate);
+ const emptyAgenda = emptyAgendaMessage(calendar.selectedDate);
const displayUsername = username.trim() || '用户';
const avatarInitial = Array.from(displayUsername)[0]?.toLocaleUpperCase() ?? '用';
@@ -68,7 +71,9 @@ export function ScheduleCalendarScreen({
- 我的日程
+
+ {selectedLabel}
+
@@ -103,9 +108,6 @@ export function ScheduleCalendarScreen({
-
- {selectedLabel}
-
-
- 当日安排
- 日程
-
+ {agendaTitle}
{calendar.selectedOccurrences.length} 项
{calendar.selectedOccurrences.length === 0 ? (
- 这一天暂时没有日程
- 留一点时间给自己,或用语音助手添加安排。
+ {emptyAgenda.title}
+ {emptyAgenda.detail ? (
+ {emptyAgenda.detail}
+ ) : null}
) : (
- calendar.selectedOccurrences.map((item) => (
+ calendar.selectedOccurrences.map((item, index) => (
setSelectedOccurrence(item)}
/>
@@ -161,10 +163,7 @@ export function ScheduleCalendarScreen({
{calendar.locationSchedules.length > 0 ? (
-
- 位置触发
- 地点提醒
-
+ 地点提醒
{calendar.locationSchedules.length} 项
{calendar.locationSchedules.map((item) => (
@@ -210,11 +209,11 @@ function LogoutIcon() {
const styles = StyleSheet.create({
accountActions: {
alignItems: 'center',
- flex: 1,
flexDirection: 'row',
+ flexShrink: 1,
gap: spacing.sm,
justifyContent: 'flex-end',
- marginLeft: spacing.sm,
+ marginLeft: 'auto',
maxWidth: 240,
minWidth: 0,
},
@@ -260,7 +259,6 @@ const styles = StyleSheet.create({
},
emptyTitle: { color: colors.text, fontSize: 15, fontWeight: '700' },
error: { color: colors.error, fontSize: 15, textAlign: 'center' },
- eyebrow: { color: colors.mutedText, fontSize: 13, fontWeight: '700' },
header: {
paddingBottom: spacing.lg,
paddingHorizontal: spacing.lg,
@@ -271,6 +269,7 @@ const styles = StyleSheet.create({
flexDirection: 'row',
justifyContent: 'space-between',
minWidth: 0,
+ width: '100%',
},
locationSection: {
borderTopColor: colors.border,
@@ -288,7 +287,6 @@ const styles = StyleSheet.create({
screen: { backgroundColor: colors.background, flex: 1 },
scrollContent: { paddingBottom: spacing.lg },
sectionCount: { color: colors.mutedText, fontSize: 12, fontWeight: '600' },
- sectionEyebrow: { color: colors.mutedText, fontSize: 12, fontWeight: '600', marginBottom: 3 },
sectionHeader: {
alignItems: 'flex-end',
flexDirection: 'row',
@@ -309,7 +307,14 @@ const styles = StyleSheet.create({
},
signOutButtonPressed: { opacity: 0.62 },
stateText: { color: colors.mutedText },
- title: { color: colors.text, fontSize: 28, fontWeight: '800', lineHeight: 34, marginTop: 4 },
+ title: {
+ color: colors.text,
+ flex: 1,
+ fontSize: 28,
+ fontWeight: '800',
+ lineHeight: 34,
+ minWidth: 0,
+ },
userPill: {
alignItems: 'center',
backgroundColor: colors.surface,
diff --git a/frontend/src/features/schedule/presentation/ScheduleDetailSheet.tsx b/frontend/src/features/schedule/presentation/ScheduleDetailSheet.tsx
index bb468c9f..997233ec 100644
--- a/frontend/src/features/schedule/presentation/ScheduleDetailSheet.tsx
+++ b/frontend/src/features/schedule/presentation/ScheduleDetailSheet.tsx
@@ -4,13 +4,11 @@ import { Modal, Pressable, ScrollView, StyleSheet, Text, View } from 'react-nati
import { colors, spacing } from '../../../shared/ui/theme';
export function ScheduleDetailSheet({
- badges,
children,
onClose,
title,
visible,
}: {
- badges: readonly string[];
children: ReactNode;
onClose: () => void;
title: string;
@@ -36,15 +34,6 @@ export function ScheduleDetailSheet({
{title}
-
- {badges.map((badge, index) => (
-
-
- {badge}
-
-
- ))}
-
{children}
@@ -121,14 +110,6 @@ const styles = StyleSheet.create({
flex: 1,
justifyContent: 'flex-end',
},
- badge: {
- backgroundColor: colors.input,
- borderRadius: 999,
- paddingHorizontal: 11,
- paddingVertical: 6,
- },
- badgeText: { color: colors.mutedText, fontSize: 12, fontWeight: '700' },
- badges: { flexDirection: 'row', flexWrap: 'wrap', gap: spacing.sm },
closeButton: {
alignItems: 'center',
backgroundColor: colors.input,
@@ -170,8 +151,6 @@ const styles = StyleSheet.create({
metaIcon: { color: colors.mutedText, fontSize: 15 },
metaText: { color: colors.mutedText, flex: 1, fontSize: 12 },
pressed: { opacity: 0.62 },
- primaryBadge: { backgroundColor: colors.accent },
- primaryBadgeText: { color: colors.text },
section: {
alignItems: 'flex-start',
backgroundColor: colors.background,
@@ -189,9 +168,18 @@ const styles = StyleSheet.create({
borderRadius: 12,
height: 38,
justifyContent: 'center',
+ overflow: 'hidden',
width: 38,
},
- sectionIconText: { color: colors.text, fontSize: 18, fontWeight: '700' },
+ sectionIconText: {
+ color: colors.text,
+ fontSize: 18,
+ height: 18,
+ includeFontPadding: false,
+ lineHeight: 18,
+ textAlign: 'center',
+ textAlignVertical: 'center',
+ },
sectionLabel: { color: colors.mutedText, fontSize: 12, fontWeight: '700', marginBottom: 4 },
sectionPrimary: { color: colors.text, fontSize: 16, fontWeight: '700', lineHeight: 22 },
sectionSecondary: { color: colors.mutedText, fontSize: 13, lineHeight: 19, marginTop: 3 },
diff --git a/frontend/src/features/schedule/presentation/ScheduleOccurrenceDetailSheet.tsx b/frontend/src/features/schedule/presentation/ScheduleOccurrenceDetailSheet.tsx
index 162d47d9..fc771306 100644
--- a/frontend/src/features/schedule/presentation/ScheduleOccurrenceDetailSheet.tsx
+++ b/frontend/src/features/schedule/presentation/ScheduleOccurrenceDetailSheet.tsx
@@ -41,15 +41,9 @@ export function ScheduleOccurrenceDetailSheet({
detailOccurrence.occurrenceEnd,
detailOccurrence.timezone,
);
- const badges = [
- detailOccurrence.scheduleCategory === 'time' ? '时间日程' : '地点日程',
- detailOccurrence.recurrenceMode === 'recurring' ? '周期日程' : '一次性',
- ...(detailOccurrence.isAllDay ? ['全天'] : []),
- ];
return (
void;
}) {
const startLabel = item.isAllDay ? '全天' : formatTime(item.occurrenceStart, item.timezone);
+ const endLabel =
+ !item.isAllDay && item.occurrenceEnd ? formatTime(item.occurrenceEnd, item.timezone) : null;
const isRecurring = item.recurrenceMode !== 'once';
+ const hasMeta = Boolean(item.locationName) || isRecurring;
return (
[styles.row, pressed && styles.pressed]}
+ style={({ pressed }) => [styles.row, !isLast && styles.rowFollow, pressed && styles.pressed]}
+ testID="schedule-occurrence-row"
>
-
-
+
{startLabel}
- {!item.isAllDay && item.occurrenceEnd ? (
+ {endLabel ? (
- 至 {formatTime(item.occurrenceEnd, item.timezone)}
+ {endLabel}
) : null}
-
+
+
+
+
+
+
+
{item.title}
- {isRecurring ? (
-
- 重复
+ {hasMeta ? (
+
+ {item.locationName ? (
+
+ {item.locationName}
+
+ ) : null}
+ {isRecurring ? 重复 : null}
) : null}
- {item.locationName ? (
-
- {item.locationName}
-
- ) : null}
);
}
const styles = StyleSheet.create({
- allDayIndicator: { backgroundColor: colors.accent },
- badge: {
- alignSelf: 'flex-start',
- backgroundColor: colors.accent,
- borderRadius: 999,
- paddingHorizontal: spacing.sm,
- paddingVertical: 2,
+ card: {
+ backgroundColor: colors.surface,
+ borderColor: colors.border,
+ borderRadius: 14,
+ borderWidth: 1,
+ flex: 1,
+ gap: 4,
+ justifyContent: 'center',
+ minHeight: CARD_MIN_HEIGHT,
+ minWidth: 0,
+ paddingHorizontal: 14,
+ paddingVertical: 12,
+ },
+ endTime: {
+ color: colors.mutedText,
+ fontSize: 12,
+ fontVariant: ['tabular-nums'],
+ fontWeight: '500',
+ letterSpacing: 0.2,
+ lineHeight: 16,
+ marginTop: 2,
+ },
+ meta: {
+ alignItems: 'center',
+ flexDirection: 'row',
+ flexWrap: 'nowrap',
+ gap: spacing.sm,
+ minWidth: 0,
+ },
+ metaText: {
+ color: colors.mutedText,
+ flexShrink: 1,
+ fontSize: 12,
+ fontWeight: '500',
+ lineHeight: 16,
+ minWidth: 0,
},
- badgeText: { color: colors.text, fontSize: 11, fontWeight: '700' },
- copy: { flex: 1, gap: 6, minWidth: 0 },
- endTime: { color: colors.mutedText, fontSize: 11, marginTop: 3 },
- indicator: {
+ pressed: { opacity: 0.72, transform: [{ scale: 0.995 }] },
+ rail: {
+ alignItems: 'center',
alignSelf: 'stretch',
+ width: 14,
+ },
+ railDot: {
backgroundColor: colors.focus,
+ borderColor: colors.background,
borderRadius: 999,
- width: 4,
+ borderWidth: 3,
+ height: 10,
+ marginTop: 8,
+ width: 10,
+ zIndex: 1,
+ },
+ railDotAllDay: { backgroundColor: colors.text },
+ railLine: {
+ backgroundColor: colors.focus,
+ bottom: 0,
+ left: 6,
+ position: 'absolute',
+ top: 12,
+ width: 2,
+ },
+ railLineLast: { bottom: 18 },
+ repeat: {
+ color: colors.mutedText,
+ flexShrink: 0,
+ fontSize: 12,
+ fontWeight: '600',
+ lineHeight: 16,
},
- location: { color: colors.mutedText, fontSize: 13 },
- pressed: { opacity: 0.72, transform: [{ scale: 0.995 }] },
row: {
alignItems: 'stretch',
- backgroundColor: colors.surface,
- borderColor: colors.border,
- borderRadius: 16,
- borderWidth: 1,
flexDirection: 'row',
gap: 12,
- marginBottom: 10,
- padding: 14,
},
- startTime: { color: colors.text, fontSize: 15, fontWeight: '800' },
- timeColumn: { paddingTop: 1, width: 58 },
- title: { color: colors.text, fontSize: 16, fontWeight: '700', lineHeight: 21 },
+ rowFollow: { paddingBottom: 10 },
+ startTime: {
+ color: colors.text,
+ fontSize: 15,
+ fontVariant: ['tabular-nums'],
+ fontWeight: '700',
+ letterSpacing: 0.2,
+ lineHeight: 20,
+ },
+ timeColumn: { paddingTop: 6, width: 52 },
+ title: { color: colors.text, fontSize: 16, fontWeight: '700', lineHeight: 22 },
});
diff --git a/frontend/src/features/schedule/presentation/scheduleDisplay.ts b/frontend/src/features/schedule/presentation/scheduleDisplay.ts
index 94356173..b9325530 100644
--- a/frontend/src/features/schedule/presentation/scheduleDisplay.ts
+++ b/frontend/src/features/schedule/presentation/scheduleDisplay.ts
@@ -8,6 +8,26 @@ export function dateKey(date: Date): string {
).padStart(2, '0')}`;
}
+export function formatAgendaSectionTitle(selectedDate: Date, today: Date = new Date()): string {
+ if (dateKey(selectedDate) === dateKey(today)) {
+ return '今日安排';
+ }
+ return `${selectedDate.getMonth() + 1}月${selectedDate.getDate()}日的安排`;
+}
+
+export function emptyAgendaMessage(selectedDate: Date, today: Date = new Date()): {
+ title: string;
+ detail: string | null;
+} {
+ if (dateKey(selectedDate) < dateKey(today)) {
+ return { title: '这一天是属于你的', detail: null };
+ }
+ return {
+ title: '这一天暂时没有日程',
+ detail: '留一点时间给自己,或用语音助手添加安排。',
+ };
+}
+
export function dateKeyInTimezone(instant: string, timezone: string): string | null {
const date = new Date(instant);
if (Number.isNaN(date.getTime())) return null;
diff --git a/frontend/tests/unit/features/schedule/presentation/ScheduleCalendarScreen.test.tsx b/frontend/tests/unit/features/schedule/presentation/ScheduleCalendarScreen.test.tsx
index 7c2f6980..3c4e85b1 100644
--- a/frontend/tests/unit/features/schedule/presentation/ScheduleCalendarScreen.test.tsx
+++ b/frontend/tests/unit/features/schedule/presentation/ScheduleCalendarScreen.test.tsx
@@ -90,7 +90,7 @@ describe('ScheduleCalendarScreen location schedules', () => {
);
await waitFor(() => expect(service.getSchedulesByRange).toHaveBeenCalled());
- expect(screen.getByText('我的日程')).toBeTruthy();
+ expect(screen.queryByText('我的日程')).toBeNull();
expect(screen.getByText('Z')).toBeTruthy();
expect(screen.getByText(username)).toBeTruthy();
expect(screen.queryByText(/账号:/)).toBeNull();
@@ -103,7 +103,7 @@ describe('ScheduleCalendarScreen location schedules', () => {
).toMatchObject({ flexShrink: 1, minWidth: 0 });
expect(
StyleSheet.flatten(screen.getByTestId('schedule-account-actions').props.style),
- ).toMatchObject({ maxWidth: 240, minWidth: 0 });
+ ).toMatchObject({ marginLeft: 'auto', maxWidth: 240, minWidth: 0 });
fireEvent.press(screen.getByRole('button', { name: '退出登录' }));
expect(onSignOut).toHaveBeenCalledTimes(1);
@@ -122,10 +122,29 @@ describe('ScheduleCalendarScreen location schedules', () => {
);
await waitFor(() => expect(screen.getByText('地点提醒')).toBeTruthy());
+ expect(screen.queryByText('位置触发')).toBeNull();
+ expect(screen.getByText('今日安排')).toBeTruthy();
+ expect(screen.queryByText('当日安排')).toBeNull();
+ expect(screen.getByText('留一点时间给自己,或用语音助手添加安排。')).toBeTruthy();
expect(screen.getByText('到公司提醒我打卡')).toBeTruthy();
const dateButton = screen.getByLabelText(/月13日$/);
fireEvent.press(dateButton);
+ const today = new Date();
+ const selectedIsPast = 13 < today.getDate();
+ if (today.getDate() === 13) {
+ expect(screen.getByText('今日安排')).toBeTruthy();
+ expect(screen.getByText('留一点时间给自己,或用语音助手添加安排。')).toBeTruthy();
+ } else {
+ expect(screen.getByText(`${today.getMonth() + 1}月13日的安排`)).toBeTruthy();
+ expect(screen.queryByText('今日安排')).toBeNull();
+ if (selectedIsPast) {
+ expect(screen.queryByText('留一点时间给自己,或用语音助手添加安排。')).toBeNull();
+ expect(screen.getByText('这一天是属于你的')).toBeTruthy();
+ } else {
+ expect(screen.getByText('留一点时间给自己,或用语音助手添加安排。')).toBeTruthy();
+ }
+ }
expect(screen.getByText('地点提醒')).toBeTruthy();
const rangeCallsBeforeMonthChange = (service.getSchedulesByRange as jest.Mock).mock.calls
.length;
@@ -153,7 +172,7 @@ describe('ScheduleCalendarScreen location schedules', () => {
await waitFor(() => expect(screen.getByLabelText('公司 到公司提醒我打卡')).toBeTruthy());
fireEvent.press(screen.getByLabelText('公司 到公司提醒我打卡'));
- expect(screen.getByText('地点日程')).toBeTruthy();
+ expect(screen.queryByText('地点日程')).toBeNull();
expect(screen.getAllByText('公司')).toHaveLength(2);
expect(screen.getByText('时区 · Asia/Shanghai')).toBeTruthy();
expect(screen.getByText('到达地点时')).toBeTruthy();
diff --git a/frontend/tests/unit/features/schedule/presentation/ScheduleDetailSheet.test.tsx b/frontend/tests/unit/features/schedule/presentation/ScheduleDetailSheet.test.tsx
index ad2f6a2e..a83852af 100644
--- a/frontend/tests/unit/features/schedule/presentation/ScheduleDetailSheet.test.tsx
+++ b/frontend/tests/unit/features/schedule/presentation/ScheduleDetailSheet.test.tsx
@@ -42,8 +42,8 @@ describe('schedule detail sheets', () => {
render( {}} />);
expect(screen.getByText(timedOccurrence.title)).toBeTruthy();
- expect(screen.getByText('时间日程')).toBeTruthy();
- expect(screen.getByText('周期日程')).toBeTruthy();
+ expect(screen.queryByText('时间日程')).toBeNull();
+ expect(screen.queryByText('周期日程')).toBeNull();
expect(screen.getByText('2026年8月13日')).toBeTruthy();
expect(screen.getByText('星期四')).toBeTruthy();
expect(screen.getByText('09:30')).toBeTruthy();
@@ -60,8 +60,8 @@ describe('schedule detail sheets', () => {
render( {}} />);
expect(screen.getByText('2026年8月17日')).toBeTruthy();
- expect(screen.getByText('一次性')).toBeTruthy();
- expect(screen.getAllByText('全天')).toHaveLength(2);
+ expect(screen.queryByText('一次性')).toBeNull();
+ expect(screen.getByText('全天')).toBeTruthy();
expect(screen.queryByText('地点')).toBeNull();
expect(screen.queryByText('提醒')).toBeNull();
});
@@ -109,7 +109,8 @@ describe('schedule detail sheets', () => {
};
render();
- expect(screen.getByText('地点日程')).toBeTruthy();
+ expect(screen.getByText('地点触发日程')).toBeTruthy();
+ expect(screen.queryByText('地点日程')).toBeNull();
expect(screen.queryByText('未命名地点')).toBeNull();
expect(screen.queryByText('未配置')).toBeNull();
expect(screen.queryByText('地点')).toBeNull();
diff --git a/frontend/tests/unit/features/schedule/presentation/ScheduleRows.test.tsx b/frontend/tests/unit/features/schedule/presentation/ScheduleRows.test.tsx
index 285007d0..79c14d16 100644
--- a/frontend/tests/unit/features/schedule/presentation/ScheduleRows.test.tsx
+++ b/frontend/tests/unit/features/schedule/presentation/ScheduleRows.test.tsx
@@ -1,5 +1,6 @@
import { render, screen } from '@testing-library/react-native';
import { describe, expect, it } from '@jest/globals';
+import { StyleSheet } from 'react-native';
import type {
LocationScheduleView,
@@ -7,6 +8,7 @@ import type {
} from '../../../../../src/features/schedule/application';
import { LocationScheduleRow } from '../../../../../src/features/schedule/presentation/LocationScheduleRow';
import { ScheduleOccurrenceRow } from '../../../../../src/features/schedule/presentation/ScheduleOccurrenceRow';
+import { colors } from '../../../../../src/shared/ui/theme';
function occurrence(overrides: Partial = {}): ScheduleOccurrenceView {
return {
@@ -30,7 +32,36 @@ describe('ScheduleOccurrenceRow', () => {
render();
expect(screen.getByText('团队周会')).toBeTruthy();
+ expect(screen.getByText('14:00')).toBeTruthy();
+ expect(screen.getByText('15:00')).toBeTruthy();
+ expect(screen.queryByText(/至 /)).toBeNull();
expect(screen.queryByText('重复')).toBeNull();
+ expect(StyleSheet.flatten(screen.getByTestId('schedule-occurrence-indicator').props.style)).toMatchObject({
+ backgroundColor: colors.focus,
+ height: 10,
+ width: 10,
+ });
+ expect(StyleSheet.flatten(screen.getByTestId('schedule-occurrence-card').props.style)).toMatchObject({
+ backgroundColor: colors.surface,
+ minHeight: 72,
+ });
+ });
+
+ it('keeps the same card height for different durations', () => {
+ const short = render(
+ ,
+ );
+ const long = render(
+ ,
+ );
+
+ expect(StyleSheet.flatten(short.getByTestId('schedule-occurrence-card').props.style).minHeight).toBe(
+ StyleSheet.flatten(long.getByTestId('schedule-occurrence-card').props.style).minHeight,
+ );
});
it('shows a recurrence badge for a recurring schedule', () => {
@@ -52,6 +83,15 @@ describe('ScheduleOccurrenceRow', () => {
);
expect(screen.getByText('全天')).toBeTruthy();
+ expect(StyleSheet.flatten(screen.getByTestId('schedule-occurrence-indicator').props.style)).toMatchObject({
+ backgroundColor: colors.text,
+ height: 10,
+ width: 10,
+ });
+ expect(StyleSheet.flatten(screen.getByTestId('schedule-occurrence-card').props.style)).toMatchObject({
+ backgroundColor: colors.surface,
+ minHeight: 72,
+ });
});
});
@@ -69,7 +109,7 @@ describe('LocationScheduleRow', () => {
render();
- expect(screen.getByText('位置日程')).toBeTruthy();
+ expect(screen.queryByText('位置日程')).toBeNull();
expect(screen.getByText('到公司提醒我打卡')).toBeTruthy();
expect(screen.getByText('公司')).toBeTruthy();
expect(screen.queryByText('location')).toBeNull();
diff --git a/frontend/tests/unit/features/schedule/presentation/scheduleDisplay.test.ts b/frontend/tests/unit/features/schedule/presentation/scheduleDisplay.test.ts
new file mode 100644
index 00000000..90be1140
--- /dev/null
+++ b/frontend/tests/unit/features/schedule/presentation/scheduleDisplay.test.ts
@@ -0,0 +1,38 @@
+import { describe, expect, it } from '@jest/globals';
+
+import { emptyAgendaMessage, formatAgendaSectionTitle } from '../../../../../src/features/schedule/presentation/scheduleDisplay';
+
+describe('formatAgendaSectionTitle', () => {
+ const today = new Date(2026, 7, 19);
+
+ it('keeps 今日安排 for the selected day', () => {
+ expect(formatAgendaSectionTitle(new Date(2026, 7, 19), today)).toBe('今日安排');
+ });
+
+ it('uses the calendar date for other days', () => {
+ expect(formatAgendaSectionTitle(new Date(2026, 7, 13), today)).toBe('8月13日的安排');
+ });
+});
+
+describe('emptyAgendaMessage', () => {
+ const today = new Date(2026, 7, 19);
+ const addPrompt = '留一点时间给自己,或用语音助手添加安排。';
+
+ it('keeps the add prompt for today and future days', () => {
+ expect(emptyAgendaMessage(new Date(2026, 7, 19), today)).toEqual({
+ title: '这一天暂时没有日程',
+ detail: addPrompt,
+ });
+ expect(emptyAgendaMessage(new Date(2026, 7, 20), today)).toEqual({
+ title: '这一天暂时没有日程',
+ detail: addPrompt,
+ });
+ });
+
+ it('omits the add prompt for past days', () => {
+ expect(emptyAgendaMessage(new Date(2026, 7, 13), today)).toEqual({
+ title: '这一天是属于你的',
+ detail: null,
+ });
+ });
+});
From ad3bf4a5d81a28c81597854bbdd407beeec25c1d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E4=B8=80=E6=9D=A1=E5=9B=BA=E6=89=A7=E7=9A=84=E9=B1=BC?=
<1504947133@qq.com>
Date: Wed, 19 Aug 2026 14:35:45 +0800
Subject: [PATCH 2/7] style(frontend): format schedule home UI with Prettier
---
.../LocationScheduleDetailSheet.tsx | 6 +---
.../schedule/presentation/scheduleDisplay.ts | 5 +++-
.../presentation/ScheduleRows.test.tsx | 30 +++++++++++--------
.../presentation/scheduleDisplay.test.ts | 5 +++-
4 files changed, 26 insertions(+), 20 deletions(-)
diff --git a/frontend/src/features/schedule/presentation/LocationScheduleDetailSheet.tsx b/frontend/src/features/schedule/presentation/LocationScheduleDetailSheet.tsx
index 4dd14cad..ab371ad2 100644
--- a/frontend/src/features/schedule/presentation/LocationScheduleDetailSheet.tsx
+++ b/frontend/src/features/schedule/presentation/LocationScheduleDetailSheet.tsx
@@ -32,11 +32,7 @@ export function LocationScheduleDetailSheet({
);
return (
-
+
{location ? : null}
{reminder ? (
{
expect(screen.getByText('15:00')).toBeTruthy();
expect(screen.queryByText(/至 /)).toBeNull();
expect(screen.queryByText('重复')).toBeNull();
- expect(StyleSheet.flatten(screen.getByTestId('schedule-occurrence-indicator').props.style)).toMatchObject({
+ expect(
+ StyleSheet.flatten(screen.getByTestId('schedule-occurrence-indicator').props.style),
+ ).toMatchObject({
backgroundColor: colors.focus,
height: 10,
width: 10,
});
- expect(StyleSheet.flatten(screen.getByTestId('schedule-occurrence-card').props.style)).toMatchObject({
+ expect(
+ StyleSheet.flatten(screen.getByTestId('schedule-occurrence-card').props.style),
+ ).toMatchObject({
backgroundColor: colors.surface,
minHeight: 72,
});
@@ -49,19 +53,15 @@ describe('ScheduleOccurrenceRow', () => {
it('keeps the same card height for different durations', () => {
const short = render(
- ,
+ ,
);
const long = render(
- ,
+ ,
);
- expect(StyleSheet.flatten(short.getByTestId('schedule-occurrence-card').props.style).minHeight).toBe(
- StyleSheet.flatten(long.getByTestId('schedule-occurrence-card').props.style).minHeight,
- );
+ expect(
+ StyleSheet.flatten(short.getByTestId('schedule-occurrence-card').props.style).minHeight,
+ ).toBe(StyleSheet.flatten(long.getByTestId('schedule-occurrence-card').props.style).minHeight);
});
it('shows a recurrence badge for a recurring schedule', () => {
@@ -83,12 +83,16 @@ describe('ScheduleOccurrenceRow', () => {
);
expect(screen.getByText('全天')).toBeTruthy();
- expect(StyleSheet.flatten(screen.getByTestId('schedule-occurrence-indicator').props.style)).toMatchObject({
+ expect(
+ StyleSheet.flatten(screen.getByTestId('schedule-occurrence-indicator').props.style),
+ ).toMatchObject({
backgroundColor: colors.text,
height: 10,
width: 10,
});
- expect(StyleSheet.flatten(screen.getByTestId('schedule-occurrence-card').props.style)).toMatchObject({
+ expect(
+ StyleSheet.flatten(screen.getByTestId('schedule-occurrence-card').props.style),
+ ).toMatchObject({
backgroundColor: colors.surface,
minHeight: 72,
});
diff --git a/frontend/tests/unit/features/schedule/presentation/scheduleDisplay.test.ts b/frontend/tests/unit/features/schedule/presentation/scheduleDisplay.test.ts
index 90be1140..c531faf8 100644
--- a/frontend/tests/unit/features/schedule/presentation/scheduleDisplay.test.ts
+++ b/frontend/tests/unit/features/schedule/presentation/scheduleDisplay.test.ts
@@ -1,6 +1,9 @@
import { describe, expect, it } from '@jest/globals';
-import { emptyAgendaMessage, formatAgendaSectionTitle } from '../../../../../src/features/schedule/presentation/scheduleDisplay';
+import {
+ emptyAgendaMessage,
+ formatAgendaSectionTitle,
+} from '../../../../../src/features/schedule/presentation/scheduleDisplay';
describe('formatAgendaSectionTitle', () => {
const today = new Date(2026, 7, 19);
From ede39a6f74452639912f01fead6f4163feafb0f9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E4=B8=80=E6=9D=A1=E5=9B=BA=E6=89=A7=E7=9A=84=E9=B1=BC?=
<1504947133@qq.com>
Date: Wed, 19 Aug 2026 14:44:18 +0800
Subject: [PATCH 3/7] test(schedule): cover timeline rows and multi-item agenda
---
.../ScheduleCalendarScreen.test.tsx | 56 ++++++++++++++++++-
.../presentation/ScheduleRows.test.tsx | 33 +++++++++++
2 files changed, 88 insertions(+), 1 deletion(-)
diff --git a/frontend/tests/unit/features/schedule/presentation/ScheduleCalendarScreen.test.tsx b/frontend/tests/unit/features/schedule/presentation/ScheduleCalendarScreen.test.tsx
index 3c4e85b1..ea5d0277 100644
--- a/frontend/tests/unit/features/schedule/presentation/ScheduleCalendarScreen.test.tsx
+++ b/frontend/tests/unit/features/schedule/presentation/ScheduleCalendarScreen.test.tsx
@@ -2,9 +2,34 @@ import { fireEvent, render, screen, waitFor } from '@testing-library/react-nativ
import { describe, expect, it, jest } from '@jest/globals';
import { StyleSheet } from 'react-native';
-import type { ScheduleCalendarReadService } from '../../../../../src/features/schedule/application';
+import type {
+ ScheduleCalendarReadService,
+ ScheduleOccurrenceView,
+} from '../../../../../src/features/schedule/application';
import { ScheduleCalendarScreen } from '../../../../../src/features/schedule/presentation/ScheduleCalendarScreen';
+function occurrenceOnSelectedDay(
+ hourUtc: number,
+ overrides: Partial = {},
+): ScheduleOccurrenceView {
+ const now = new Date();
+ const start = new Date(Date.UTC(now.getFullYear(), now.getMonth(), now.getDate(), hourUtc, 0, 0));
+ return {
+ scheduleId: 'schedule-a',
+ scheduleCategory: 'time',
+ recurrenceMode: 'once',
+ title: '项目例会',
+ isAllDay: false,
+ timezone: 'Asia/Shanghai',
+ locationName: null,
+ reminderType: 'before_start',
+ reminderStrength: 'medium',
+ occurrenceStart: start.toISOString(),
+ occurrenceEnd: new Date(start.getTime() + 60 * 60 * 1000).toISOString(),
+ ...overrides,
+ };
+}
+
function createService(): ScheduleCalendarReadService {
return {
getSchedulesByDay: jest
@@ -180,4 +205,33 @@ describe('ScheduleCalendarScreen location schedules', () => {
expect(screen.queryByText('编辑')).toBeNull();
expect(screen.queryByText('删除')).toBeNull();
});
+
+ it('connects multiple timed occurrences on a timeline and opens detail', async () => {
+ const first = occurrenceOnSelectedDay(1, { scheduleId: 'schedule-a', title: '项目例会' });
+ const second = occurrenceOnSelectedDay(4, { scheduleId: 'schedule-b', title: '方案讨论' });
+ const service = createService();
+ (service.getSchedulesByRange as jest.Mock).mockResolvedValue([first, second]);
+
+ render(
+ {}}
+ service={service}
+ timezone="Asia/Shanghai"
+ username="Sarah"
+ />,
+ );
+
+ await waitFor(() => expect(screen.getByText('项目例会')).toBeTruthy());
+ expect(screen.getByText('方案讨论')).toBeTruthy();
+ expect(screen.getByText('2 项')).toBeTruthy();
+ expect(
+ StyleSheet.flatten(screen.getAllByTestId('schedule-occurrence-row')[0]?.props.style),
+ ).toMatchObject({ paddingBottom: 10 });
+
+ fireEvent.press(screen.getByLabelText(/项目例会$/));
+ expect(screen.getByText('时区 · Asia/Shanghai')).toBeTruthy();
+ fireEvent.press(screen.getByLabelText('关闭详情'));
+ await waitFor(() => expect(screen.queryByText('时区 · Asia/Shanghai')).toBeNull());
+ });
});
diff --git a/frontend/tests/unit/features/schedule/presentation/ScheduleRows.test.tsx b/frontend/tests/unit/features/schedule/presentation/ScheduleRows.test.tsx
index 05c8df6f..a807f13e 100644
--- a/frontend/tests/unit/features/schedule/presentation/ScheduleRows.test.tsx
+++ b/frontend/tests/unit/features/schedule/presentation/ScheduleRows.test.tsx
@@ -70,6 +70,39 @@ describe('ScheduleOccurrenceRow', () => {
expect(screen.getByText('重复')).toBeTruthy();
});
+ it('extends the timeline rail when another occurrence follows', () => {
+ render();
+
+ expect(
+ StyleSheet.flatten(screen.getByTestId('schedule-occurrence-row').props.style),
+ ).toMatchObject({ paddingBottom: 10 });
+ });
+
+ it('omits meta when a one-time schedule has no location', () => {
+ render();
+
+ expect(screen.queryByText('上海科技馆')).toBeNull();
+ expect(screen.queryByText('重复')).toBeNull();
+ });
+
+ it('shows repeating without a location line', () => {
+ render(
+ ,
+ );
+
+ expect(screen.getByText('重复')).toBeTruthy();
+ expect(screen.queryByText('上海科技馆')).toBeNull();
+ });
+
+ it('hides the end time when a timed occurrence has no end', () => {
+ render();
+
+ expect(screen.getByText('14:00')).toBeTruthy();
+ expect(screen.queryByText('15:00')).toBeNull();
+ });
+
it('shows an explicit all-day label for an all-day schedule', () => {
render(
Date: Wed, 19 Aug 2026 14:46:43 +0800
Subject: [PATCH 4/7] fix(test): type the calendar occurrence list mock
---
.../presentation/ScheduleCalendarScreen.test.tsx | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/frontend/tests/unit/features/schedule/presentation/ScheduleCalendarScreen.test.tsx b/frontend/tests/unit/features/schedule/presentation/ScheduleCalendarScreen.test.tsx
index ea5d0277..df5bd6b3 100644
--- a/frontend/tests/unit/features/schedule/presentation/ScheduleCalendarScreen.test.tsx
+++ b/frontend/tests/unit/features/schedule/presentation/ScheduleCalendarScreen.test.tsx
@@ -30,14 +30,16 @@ function occurrenceOnSelectedDay(
};
}
-function createService(): ScheduleCalendarReadService {
+function createService(
+ occurrences: readonly ScheduleOccurrenceView[] = [],
+): ScheduleCalendarReadService {
return {
getSchedulesByDay: jest
.fn()
.mockResolvedValue([]),
getSchedulesByRange: jest
.fn()
- .mockResolvedValue([]),
+ .mockResolvedValue(occurrences),
getLocationSchedules: jest
.fn()
.mockResolvedValue([
@@ -209,8 +211,7 @@ describe('ScheduleCalendarScreen location schedules', () => {
it('connects multiple timed occurrences on a timeline and opens detail', async () => {
const first = occurrenceOnSelectedDay(1, { scheduleId: 'schedule-a', title: '项目例会' });
const second = occurrenceOnSelectedDay(4, { scheduleId: 'schedule-b', title: '方案讨论' });
- const service = createService();
- (service.getSchedulesByRange as jest.Mock).mockResolvedValue([first, second]);
+ const service = createService([first, second]);
render(
Date: Wed, 19 Aug 2026 14:53:15 +0800
Subject: [PATCH 5/7] fix(schedule): keep the agenda rail continuous and cards
equal height
Extend the timeline through row gaps and give occurrence cards a fixed height so wrapping titles do not change the list rhythm.
---
.../presentation/ScheduleOccurrenceRow.tsx | 25 +++++++++----
.../presentation/ScheduleRows.test.tsx | 35 +++++++++++++++----
2 files changed, 47 insertions(+), 13 deletions(-)
diff --git a/frontend/src/features/schedule/presentation/ScheduleOccurrenceRow.tsx b/frontend/src/features/schedule/presentation/ScheduleOccurrenceRow.tsx
index 58fc346a..204808f0 100644
--- a/frontend/src/features/schedule/presentation/ScheduleOccurrenceRow.tsx
+++ b/frontend/src/features/schedule/presentation/ScheduleOccurrenceRow.tsx
@@ -4,7 +4,8 @@ import type { ScheduleOccurrenceView } from '../application';
import { colors, spacing } from '../../../shared/ui/theme';
import { formatRange, formatTime } from './scheduleDisplay';
-const CARD_MIN_HEIGHT = 72;
+const CARD_HEIGHT = 88;
+const ROW_FOLLOW_GAP = 10;
export function ScheduleOccurrenceRow({
item,
@@ -41,7 +42,10 @@ export function ScheduleOccurrenceRow({
-
+
{
StyleSheet.flatten(screen.getByTestId('schedule-occurrence-card').props.style),
).toMatchObject({
backgroundColor: colors.surface,
- minHeight: 72,
+ height: 88,
});
});
- it('keeps the same card height for different durations', () => {
+ it('keeps the same card height for different durations and content', () => {
const short = render(
- ,
+ ,
);
const long = render(
- ,
+ ,
);
expect(
- StyleSheet.flatten(short.getByTestId('schedule-occurrence-card').props.style).minHeight,
- ).toBe(StyleSheet.flatten(long.getByTestId('schedule-occurrence-card').props.style).minHeight);
+ StyleSheet.flatten(short.getByTestId('schedule-occurrence-card').props.style).height,
+ ).toBe(88);
+ expect(
+ StyleSheet.flatten(long.getByTestId('schedule-occurrence-card').props.style).height,
+ ).toBe(StyleSheet.flatten(short.getByTestId('schedule-occurrence-card').props.style).height);
});
it('shows a recurrence badge for a recurring schedule', () => {
@@ -76,6 +86,17 @@ describe('ScheduleOccurrenceRow', () => {
expect(
StyleSheet.flatten(screen.getByTestId('schedule-occurrence-row').props.style),
).toMatchObject({ paddingBottom: 10 });
+ expect(
+ StyleSheet.flatten(screen.getByTestId('schedule-occurrence-rail-line').props.style),
+ ).toMatchObject({ bottom: -10, top: 12 });
+ });
+
+ it('stops the timeline rail at the last occurrence', () => {
+ render();
+
+ expect(
+ StyleSheet.flatten(screen.getByTestId('schedule-occurrence-rail-line').props.style),
+ ).toMatchObject({ bottom: 18 });
});
it('omits meta when a one-time schedule has no location', () => {
@@ -127,7 +148,7 @@ describe('ScheduleOccurrenceRow', () => {
StyleSheet.flatten(screen.getByTestId('schedule-occurrence-card').props.style),
).toMatchObject({
backgroundColor: colors.surface,
- minHeight: 72,
+ height: 88,
});
});
});
From eee992ad599a44d40cbc2808af47f60d8e13b45b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E4=B8=80=E6=9D=A1=E5=9B=BA=E6=89=A7=E7=9A=84=E9=B1=BC?=
<1504947133@qq.com>
Date: Thu, 20 Aug 2026 16:03:59 +0800
Subject: [PATCH 6/7] fix(test): include required category on calendar
occurrence fixtures
Merging main made ScheduleOccurrenceView.category required, so the
calendar screen factory no longer typechecked.
---
.../schedule/presentation/ScheduleCalendarScreen.test.tsx | 1 +
1 file changed, 1 insertion(+)
diff --git a/frontend/tests/unit/features/schedule/presentation/ScheduleCalendarScreen.test.tsx b/frontend/tests/unit/features/schedule/presentation/ScheduleCalendarScreen.test.tsx
index 1665768a..2632e1d8 100644
--- a/frontend/tests/unit/features/schedule/presentation/ScheduleCalendarScreen.test.tsx
+++ b/frontend/tests/unit/features/schedule/presentation/ScheduleCalendarScreen.test.tsx
@@ -17,6 +17,7 @@ function occurrenceOnSelectedDay(
return {
scheduleId: 'schedule-a',
scheduleCategory: 'time',
+ category: null,
recurrenceMode: 'once',
title: '项目例会',
isAllDay: false,
From 7bce85354a8266dfd71682f9b3f3a7da99ae7c03 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E4=B8=80=E6=9D=A1=E5=9B=BA=E6=89=A7=E7=9A=84=E9=B1=BC?=
<1504947133@qq.com>
Date: Thu, 20 Aug 2026 16:17:45 +0800
Subject: [PATCH 7/7] fix(schedule): keep category off the agenda list
Category belongs in the detail sheet as copy, not next to the list location.
---
.../schedule/presentation/LocationScheduleRow.tsx | 7 -------
.../presentation/ScheduleOccurrenceRow.tsx | 10 ++--------
.../presentation/ScheduleCalendarScreen.test.tsx | 6 ++++--
.../schedule/presentation/ScheduleRows.test.tsx | 14 ++++++++------
4 files changed, 14 insertions(+), 23 deletions(-)
diff --git a/frontend/src/features/schedule/presentation/LocationScheduleRow.tsx b/frontend/src/features/schedule/presentation/LocationScheduleRow.tsx
index 650ce645..01b513da 100644
--- a/frontend/src/features/schedule/presentation/LocationScheduleRow.tsx
+++ b/frontend/src/features/schedule/presentation/LocationScheduleRow.tsx
@@ -3,7 +3,6 @@ import Svg, { Circle, Path } from 'react-native-svg';
import type { LocationScheduleView } from '../application';
import { colors } from '../../../shared/ui/theme';
-import { scheduleCategoryLabel } from './scheduleDisplay';
export function LocationScheduleRow({
item,
@@ -13,7 +12,6 @@ export function LocationScheduleRow({
onPress?: () => void;
}) {
const locationLabel = item.locationName ?? '地点触发';
- const categoryLabel = scheduleCategoryLabel(item.category);
return (
{locationLabel}
- {categoryLabel ? (
-
- {categoryLabel}
-
- ) : null}
);
diff --git a/frontend/src/features/schedule/presentation/ScheduleOccurrenceRow.tsx b/frontend/src/features/schedule/presentation/ScheduleOccurrenceRow.tsx
index ee74ff0f..204808f0 100644
--- a/frontend/src/features/schedule/presentation/ScheduleOccurrenceRow.tsx
+++ b/frontend/src/features/schedule/presentation/ScheduleOccurrenceRow.tsx
@@ -2,7 +2,7 @@ import { Pressable, StyleSheet, Text, View } from 'react-native';
import type { ScheduleOccurrenceView } from '../application';
import { colors, spacing } from '../../../shared/ui/theme';
-import { formatRange, formatTime, scheduleCategoryLabel } from './scheduleDisplay';
+import { formatRange, formatTime } from './scheduleDisplay';
const CARD_HEIGHT = 88;
const ROW_FOLLOW_GAP = 10;
@@ -20,8 +20,7 @@ export function ScheduleOccurrenceRow({
const endLabel =
!item.isAllDay && item.occurrenceEnd ? formatTime(item.occurrenceEnd, item.timezone) : null;
const isRecurring = item.recurrenceMode !== 'once';
- const categoryLabel = scheduleCategoryLabel(item.category);
- const hasMeta = Boolean(item.locationName) || isRecurring || Boolean(categoryLabel);
+ const hasMeta = Boolean(item.locationName) || isRecurring;
return (
) : null}
- {categoryLabel ? (
-
- {categoryLabel}
-
- ) : null}
{isRecurring ? 重复 : null}
) : null}
diff --git a/frontend/tests/unit/features/schedule/presentation/ScheduleCalendarScreen.test.tsx b/frontend/tests/unit/features/schedule/presentation/ScheduleCalendarScreen.test.tsx
index 2632e1d8..ab99107d 100644
--- a/frontend/tests/unit/features/schedule/presentation/ScheduleCalendarScreen.test.tsx
+++ b/frontend/tests/unit/features/schedule/presentation/ScheduleCalendarScreen.test.tsx
@@ -259,7 +259,8 @@ describe('ScheduleCalendarScreen location schedules', () => {
view.rerender();
await waitFor(() => expect(getSchedulesByRange).toHaveBeenCalledTimes(2));
- await waitFor(() => expect(screen.getAllByText('工作')).toHaveLength(2));
+ await waitFor(() => expect(screen.getAllByText('工作')).toHaveLength(1));
+ expect(screen.getByText('分类')).toBeTruthy();
expect(screen.getByText('日程详情')).toBeTruthy();
fireEvent.press(screen.getByRole('button', { name: '关闭详情' }));
@@ -302,7 +303,8 @@ describe('ScheduleCalendarScreen location schedules', () => {
view.rerender();
await waitFor(() => expect(getLocationSchedules).toHaveBeenCalledTimes(2));
- await waitFor(() => expect(screen.getAllByText('学习')).toHaveLength(2));
+ await waitFor(() => expect(screen.getAllByText('学习')).toHaveLength(1));
+ expect(screen.getByText('分类')).toBeTruthy();
expect(screen.getByText('日程详情')).toBeTruthy();
});
diff --git a/frontend/tests/unit/features/schedule/presentation/ScheduleRows.test.tsx b/frontend/tests/unit/features/schedule/presentation/ScheduleRows.test.tsx
index 15cd6208..c4c660ec 100644
--- a/frontend/tests/unit/features/schedule/presentation/ScheduleRows.test.tsx
+++ b/frontend/tests/unit/features/schedule/presentation/ScheduleRows.test.tsx
@@ -125,12 +125,14 @@ describe('ScheduleOccurrenceRow', () => {
expect(screen.queryByText('15:00')).toBeNull();
});
- it('shows localized work and study category labels', () => {
- const view = render();
+ it('does not show category on the list even when category is set', () => {
+ render();
- expect(screen.getByText('工作')).toBeTruthy();
- view.rerender();
- expect(screen.getByText('学习')).toBeTruthy();
+ expect(screen.getByText('团队周会')).toBeTruthy();
+ expect(screen.getByText('上海科技馆')).toBeTruthy();
+ expect(screen.queryByText('工作')).toBeNull();
+ expect(screen.queryByText('学习')).toBeNull();
+ expect(screen.queryByText('未分类')).toBeNull();
});
it('does not show a category label while category is null', () => {
@@ -195,7 +197,7 @@ describe('LocationScheduleRow', () => {
render();
expect(screen.queryByText('位置日程')).toBeNull();
- expect(screen.getByText('工作')).toBeTruthy();
+ expect(screen.queryByText('工作')).toBeNull();
expect(screen.getByText('到公司提醒我打卡')).toBeTruthy();
expect(screen.getByText('公司')).toBeTruthy();
expect(screen.queryByText('location')).toBeNull();