Skip to content

Commit 1e022e3

Browse files
potokyanna.potockatyler-dane
authored
fix(web): handle someday week label edge case (#1062)
* fix: fix function isCurrentWeek * fix: This Week label functions and tests * fix: extract isCurrentWeek + unit tests for getSomedayWeekLabel function * fix: refactoring reviewed code * feat(web): implement useWeekLabel hook for processing week labels * refactor(web): simplify SomedayTab component and integrate useWeekLabel hook * test(web): update tests for getSomedayWeekLabel to use viewStart parameter --------- Co-authored-by: anna.potocka <anna.potocka@student.tuke.sk> Co-authored-by: Tyler Dane <tyler@switchback.tech>
1 parent 4cb3b31 commit 1e022e3

File tree

5 files changed

+233
-24
lines changed

5 files changed

+233
-24
lines changed

packages/web/src/views/Calendar/components/Sidebar/SomedayTab/SomedayTab.tsx

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
import React, { FC, useMemo, useRef } from "react";
1+
import React, { FC, useRef } from "react";
22
import { DragDropContext } from "@hello-pangea/dnd";
33
import { theme } from "@web/common/styles/theme";
4-
import { getWeekRangeLabel } from "@web/common/utils/datetime/web.date.util";
54
import { AbsoluteOverflowLoader } from "@web/components/AbsoluteOverflowLoader";
65
import { Divider } from "@web/components/Divider";
76
import { selectIsGetSomedayEventsProcessing } from "@web/ducks/events/selectors/someday.selectors";
87
import { useAppSelector } from "@web/store/store.hooks";
98
import { useSidebarContext } from "@web/views/Calendar/components/Draft/sidebar/context/useSidebarContext";
9+
import { useWeekLabel } from "@web/views/Calendar/components/Sidebar/SomedayTab/WeekSection/useWeekLabel";
1010
import { DateCalcs } from "@web/views/Calendar/hooks/grid/useDateCalcs";
1111
import {
1212
Measurements_Grid,
@@ -35,10 +35,7 @@ export const SomedayTab: FC<Props> = ({
3535
const isProcessing = useAppSelector(selectIsGetSomedayEventsProcessing);
3636
const context = useSidebarContext();
3737
const somedayRef = useRef<HTMLDivElement>(null);
38-
const weekLabel = useMemo(
39-
() => getWeekRangeLabel(viewStart, viewEnd),
40-
[viewEnd, viewStart],
41-
);
38+
const weekLabel = useWeekLabel(viewStart, viewEnd);
4239

4340
if (!context) return null; // TS Guard
4441

packages/web/src/views/Calendar/components/Sidebar/SomedayTab/WeekSection/WeekSection.tsx

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -26,31 +26,14 @@ export const WeekSection: FC<Props> = ({
2626
weekLabel,
2727
gridRefs,
2828
}) => {
29-
const isCurrentWeek = (label: string): boolean => {
30-
const [start, end] = label.split(" - ");
31-
const [month, startDay] = start.split(".").map(Number);
32-
const endDay = Number(end);
33-
const now = new Date();
34-
const year = now.getFullYear();
35-
36-
const startDate = new Date(year, month - 1, startDay);
37-
const endDate = new Date(year, month - 1, endDay);
38-
39-
now.setHours(0, 0, 0, 0);
40-
startDate.setHours(0, 0, 0, 0);
41-
endDate.setHours(0, 0, 0, 0);
42-
43-
return now >= startDate && now <= endDate;
44-
};
45-
4629
return (
4730
<SidebarSection>
4831
<SidebarHeader
4932
alignItems={AlignItems.CENTER}
5033
justifyContent={JustifyContent.SPACE_BETWEEN}
5134
>
5235
<Text role="heading" size="xl">
53-
{isCurrentWeek(weekLabel) ? "This Week" : weekLabel}
36+
{weekLabel}
5437
</Text>
5538
</SidebarHeader>
5639

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
import dayjs, { Dayjs } from "dayjs";
2+
import "@testing-library/jest-dom";
3+
import {
4+
getSomedayWeekLabel,
5+
isCurrentWeek,
6+
} from "@web/views/Calendar/components/Sidebar/SomedayTab/WeekSection/useWeekLabel";
7+
8+
const makeLabel = (start: Dayjs, end: Dayjs): string =>
9+
`${start.format("M.D")} - ${end.format("M.D")}`;
10+
11+
describe("isCurrentWeek()", () => {
12+
it("returns true when today is inside range", () => {
13+
const today = dayjs("2025-01-10");
14+
const label = makeLabel(dayjs("2025-01-08"), dayjs("2025-01-12"));
15+
const result = isCurrentWeek(label, today, today);
16+
expect(result).toBe(true);
17+
});
18+
19+
it("returns false when today is before range", () => {
20+
const today = dayjs("2025-01-07");
21+
const label = makeLabel(dayjs("2025-01-08"), dayjs("2025-01-12"));
22+
const result = isCurrentWeek(label, today, today);
23+
expect(result).toBe(false);
24+
});
25+
26+
it("returns false when today is after range", () => {
27+
const today = dayjs("2025-01-20");
28+
const label = makeLabel(dayjs("2025-01-08"), dayjs("2025-01-12"));
29+
const result = isCurrentWeek(label, today, today);
30+
expect(result).toBe(false);
31+
});
32+
33+
// Cross-year boundary (Dec → Jan)
34+
it("handles a week spanning from December to January", () => {
35+
const today = dayjs("2026-01-02");
36+
const label = makeLabel(dayjs("2025-12-30"), dayjs("2026-01-05"));
37+
const result = isCurrentWeek(label, dayjs("2025-12-30"), today);
38+
expect(result).toBe(true);
39+
});
40+
41+
// Cross-year boundary but today before
42+
it("returns false when today is before a cross-year range", () => {
43+
const today = dayjs("2025-12-28");
44+
const label = makeLabel(dayjs("2025-12-30"), dayjs("2026-01-05"));
45+
const result = isCurrentWeek(label, dayjs("2025-12-30"), today);
46+
expect(result).toBe(false);
47+
});
48+
49+
// Cross-year boundary but today after
50+
it("returns false when today is after a cross-year range", () => {
51+
const today = dayjs("2026-01-06");
52+
const label = makeLabel(dayjs("2025-12-30"), dayjs("2026-01-05"));
53+
const result = isCurrentWeek(label, dayjs("2025-12-30"), today);
54+
expect(result).toBe(false);
55+
});
56+
57+
// Missing dash (invalid format)
58+
it("returns false for malformed label", () => {
59+
const today = dayjs("2025-04-10");
60+
const label = "4.10"; // no dash
61+
const result = isCurrentWeek(label, today, today);
62+
expect(result).toBe(false);
63+
});
64+
65+
// End month omitted
66+
it("handles label with no explicit end day", () => {
67+
const today = dayjs("2025-06-03");
68+
const label = "6.1 - 7"; // missing month number after dash
69+
const result = isCurrentWeek(label, today, today);
70+
expect(result).toBe(true);
71+
});
72+
73+
// Leap year range
74+
it("handles leap year February 29 correctly", () => {
75+
const today = dayjs("2028-02-29");
76+
const label = makeLabel(dayjs("2028-02-26"), dayjs("2028-03-03"));
77+
const result = isCurrentWeek(label, today, today);
78+
expect(result).toBe(true);
79+
});
80+
81+
// Cross-month transition (e.g. June → July)
82+
it("handles a week that crosses from one month to the next", () => {
83+
const today = dayjs("2025-07-02");
84+
const label = makeLabel(dayjs("2025-06-29"), dayjs("2025-07-05"));
85+
const result = isCurrentWeek(label, dayjs("2025-06-29"), today);
86+
expect(result).toBe(true);
87+
});
88+
89+
// Cross-month transition, today before range
90+
it("returns false when today is before a cross-month range", () => {
91+
const today = dayjs("2025-06-27");
92+
const label = makeLabel(dayjs("2025-06-29"), dayjs("2025-07-05"));
93+
const result = isCurrentWeek(label, dayjs("2025-06-29"), today);
94+
expect(result).toBe(false);
95+
});
96+
97+
// Cross-month transition, today after range
98+
it("returns false when today is after a cross-month range", () => {
99+
const today = dayjs("2025-07-06");
100+
const label = makeLabel(dayjs("2025-06-29"), dayjs("2025-07-05"));
101+
const result = isCurrentWeek(label, dayjs("2025-06-29"), today);
102+
expect(result).toBe(false);
103+
});
104+
});
105+
106+
describe("getSomedayWeekLabel()", () => {
107+
it('returns "This Week" when today is inside range', () => {
108+
const today = dayjs("2025-01-10");
109+
const label = makeLabel(dayjs("2025-01-08"), dayjs("2025-01-12"));
110+
expect(getSomedayWeekLabel(label, today, today)).toBe("This Week");
111+
});
112+
113+
it("returns original label when today is before range", () => {
114+
const today = dayjs("2025-01-07");
115+
const label = makeLabel(dayjs("2025-01-08"), dayjs("2025-01-12"));
116+
expect(getSomedayWeekLabel(label, today, today)).toBe(label);
117+
});
118+
119+
it("returns original label when today is after range", () => {
120+
const today = dayjs("2025-01-20");
121+
const label = makeLabel(dayjs("2025-01-08"), dayjs("2025-01-12"));
122+
expect(getSomedayWeekLabel(label, today, today)).toBe(label);
123+
});
124+
125+
it('returns "This Week" for a cross-year week when today is inside', () => {
126+
const today = dayjs("2026-01-02");
127+
const label = makeLabel(dayjs("2025-12-30"), dayjs("2026-01-05"));
128+
expect(getSomedayWeekLabel(label, dayjs("2025-12-30"), today)).toBe(
129+
"This Week",
130+
);
131+
});
132+
133+
it("returns label for cross-year week when today is outside", () => {
134+
const today = dayjs("2026-01-06");
135+
const label = makeLabel(dayjs("2025-12-30"), dayjs("2026-01-05"));
136+
expect(getSomedayWeekLabel(label, dayjs("2025-12-30"), today)).toBe(label);
137+
});
138+
139+
// Cross-month
140+
it('returns "This Week" for cross-month range when today is inside', () => {
141+
const today = dayjs("2025-07-02");
142+
const label = makeLabel(dayjs("2025-06-29"), dayjs("2025-07-05"));
143+
expect(getSomedayWeekLabel(label, dayjs("2025-06-29"), today)).toBe(
144+
"This Week",
145+
);
146+
});
147+
148+
it("returns label for cross-month week when today is outside", () => {
149+
const today = dayjs("2025-07-06");
150+
const label = makeLabel(dayjs("2025-06-29"), dayjs("2025-07-05"));
151+
expect(getSomedayWeekLabel(label, dayjs("2025-06-29"), today)).toBe(label);
152+
});
153+
154+
// Leap year
155+
it('returns "This Week" for leap-year week containing February 29', () => {
156+
const today = dayjs("2028-02-29");
157+
const viewStart = dayjs("2028-02-26");
158+
const label = makeLabel(dayjs("2028-02-26"), dayjs("2028-03-03"));
159+
expect(getSomedayWeekLabel(label, viewStart, today)).toBe("This Week");
160+
});
161+
162+
it("returns label when today is outside the leap-year week", () => {
163+
const today = dayjs("2028-03-05");
164+
const viewStart = dayjs("2028-02-26");
165+
const label = makeLabel(dayjs("2028-02-26"), dayjs("2028-03-03"));
166+
expect(getSomedayWeekLabel(label, viewStart, today)).toBe(label);
167+
});
168+
});
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import dayjs, { Dayjs } from "dayjs";
2+
import { useMemo } from "react";
3+
import { getWeekRangeLabel } from "@web/common/utils/datetime/web.date.util";
4+
import { useToday } from "@web/views/Calendar/hooks/useToday";
5+
6+
export const useWeekLabel = (viewStart: Dayjs, viewEnd: Dayjs) => {
7+
const { today } = useToday();
8+
9+
const weekLabel = useMemo(() => {
10+
const origLabel = getWeekRangeLabel(viewStart, viewEnd);
11+
const processedLabel = getSomedayWeekLabel(origLabel, viewStart, today);
12+
return processedLabel;
13+
}, [viewStart, viewEnd, today]);
14+
15+
return weekLabel;
16+
};
17+
18+
export const getSomedayWeekLabel = (
19+
label: string,
20+
viewStart: Dayjs,
21+
today: Dayjs,
22+
): string => {
23+
return isCurrentWeek(label, viewStart, today) ? "This Week" : label;
24+
};
25+
26+
export const isCurrentWeek = (
27+
label: string,
28+
viewStart: Dayjs,
29+
today: Dayjs,
30+
): boolean => {
31+
const parts = label.split(" - ");
32+
if (parts.length != 2) return false;
33+
34+
const [start, end] = label.split(" - ");
35+
const [startMonth, startDay] = start.split(".").map(Number);
36+
let [endMonth, endDay] = end.split(".").map(Number);
37+
38+
const startYear = viewStart.year();
39+
let endYear = startYear;
40+
if (startMonth === 12 && endMonth === 1) {
41+
endYear = startYear + 1;
42+
}
43+
44+
if (endDay === undefined) {
45+
endDay = endMonth;
46+
endMonth = startMonth;
47+
}
48+
49+
const startDate = dayjs(`${startYear}-${startMonth}-${startDay}`).startOf(
50+
"day",
51+
);
52+
const endDate = dayjs(`${endYear}-${endMonth}-${endDay}`).endOf("day");
53+
const todayDate = today.startOf("day");
54+
55+
return (
56+
todayDate.isSame(startDate) ||
57+
(todayDate.isAfter(startDate) && todayDate.isBefore(endDate)) ||
58+
todayDate.isSame(endDate)
59+
);
60+
};

packages/web/src/views/Calendar/hooks/useToday.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import dayjs from "@core/util/date/dayjs";
44
export const useToday = () => {
55
const getToday = (todayIndex: number) => {
66
const today = dayjs();
7+
78
if (today.get("day") === todayIndex) {
89
return today;
910
}

0 commit comments

Comments
 (0)