Load earlier calendar windows when editing historical dates
This commit is contained in:
@@ -226,7 +226,7 @@ describe("homepage", () => {
|
|||||||
|
|
||||||
test("historical check-ins use the selected date's tracking method and preserve today", async () => {
|
test("historical check-ins use the selected date's tracking method and preserve today", async () => {
|
||||||
const f = connectAccount();
|
const f = connectAccount();
|
||||||
f.setTime("2026-09-03T12:00:00Z");
|
f.setTime("2026-01-03T12:00:00Z");
|
||||||
const habit = await f.json("/habits", "POST", { name: "Reading", method: "manual" }, 201);
|
const habit = await f.json("/habits", "POST", { name: "Reading", method: "manual" }, 201);
|
||||||
f.setTime("2026-09-04T12:00:00Z");
|
f.setTime("2026-09-04T12:00:00Z");
|
||||||
await f.json(`/habits/${habit.id}`, "PATCH", { method: "count", target: 10, unit: "pages" });
|
await f.json(`/habits/${habit.id}`, "PATCH", { method: "count", target: 10, unit: "pages" });
|
||||||
@@ -243,6 +243,14 @@ describe("homepage", () => {
|
|||||||
expect((await f.json(`/habits/${habit.id}/days/2026-09-03`)).complete).toBe(true);
|
expect((await f.json(`/habits/${habit.id}/days/2026-09-03`)).complete).toBe(true);
|
||||||
expect((await f.json(`/habits/${habit.id}/days/2026-09-04`)).value).toBe(0);
|
expect((await f.json(`/habits/${habit.id}/days/2026-09-04`)).value).toBe(0);
|
||||||
expect(container.textContent).toContain("Saved check-in for 2026-09-03");
|
expect(container.textContent).toContain("Saved check-in for 2026-09-03");
|
||||||
|
fetchMock.mockClear();
|
||||||
|
await act(async () => {
|
||||||
|
const input = container.querySelector<HTMLInputElement>('input[type="date"]')!;
|
||||||
|
Object.getOwnPropertyDescriptor(dom.HTMLInputElement.prototype, "value")!.set!.call(input, "2026-01-03");
|
||||||
|
input.dispatchEvent(new dom.Event("change", { bubbles: true }) as unknown as Event);
|
||||||
|
});
|
||||||
|
expect(container.querySelector('[aria-label^="2026-01-03:"]')).not.toBeNull();
|
||||||
|
expect(fetchMock.mock.calls.some(([url]) => String(url).includes("from=2025-11-01") && String(url).includes("compact=true"))).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("signed-out visitors can try progress without calling private habit APIs", async () => {
|
test("signed-out visitors can try progress without calling private habit APIs", async () => {
|
||||||
|
|||||||
@@ -47,6 +47,14 @@ export function HabitHistory({
|
|||||||
const [historyLoading, setHistoryLoading] = useState(false);
|
const [historyLoading, setHistoryLoading] = useState(false);
|
||||||
useEffect(() => { setRange(monthWindow(date, 3)); }, [date]);
|
useEffect(() => { setRange(monthWindow(date, 3)); }, [date]);
|
||||||
const [selectedDate, setSelectedDate] = useState<string>();
|
const [selectedDate, setSelectedDate] = useState<string>();
|
||||||
|
function selectDate(selected: string) {
|
||||||
|
setSelectedDate(selected);
|
||||||
|
const available = monthWindow(date, 12);
|
||||||
|
if (selected >= available.from && selected <= available.to && (selected < range.from || selected > range.to)) {
|
||||||
|
const requested = monthWindow(selected, 3);
|
||||||
|
setRange({ from: requested.from < available.from ? available.from : requested.from, to: requested.to });
|
||||||
|
}
|
||||||
|
}
|
||||||
const [editingDay, setEditingDay] = useState(false);
|
const [editingDay, setEditingDay] = useState(false);
|
||||||
const [savingDay, setSavingDay] = useState(false);
|
const [savingDay, setSavingDay] = useState(false);
|
||||||
const [calendar, setCalendar] = useState<CalendarResponse | null>(null);
|
const [calendar, setCalendar] = useState<CalendarResponse | null>(null);
|
||||||
@@ -119,7 +127,7 @@ export function HabitHistory({
|
|||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<CalendarHeatmap
|
<CalendarHeatmap
|
||||||
onSelectDate={setSelectedDate}
|
onSelectDate={selectDate}
|
||||||
selectedDate={selectedDate}
|
selectedDate={selectedDate}
|
||||||
compact
|
compact
|
||||||
initialMonths={3}
|
initialMonths={3}
|
||||||
@@ -132,7 +140,7 @@ export function HabitHistory({
|
|||||||
color={calendar.settings.mainColor}
|
color={calendar.settings.mainColor}
|
||||||
emptyColor={calendar.settings.emptyColor}
|
emptyColor={calendar.settings.emptyColor}
|
||||||
days={days}
|
days={days}
|
||||||
selectedDayAction={selected => <Button variant="secondary" disabled={disabled} aria-label="Edit selected check-in" aria-haspopup="dialog" onClick={() => { setSelectedDate(selected); setEditingDay(true); }}><Pencil size={16} aria-hidden="true" />Edit check-in</Button>}
|
selectedDayAction={selected => <Button variant="secondary" disabled={disabled} aria-label="Edit selected check-in" aria-haspopup="dialog" onClick={() => { selectDate(selected); setEditingDay(true); }}><Pencil size={16} aria-hidden="true" />Edit check-in</Button>}
|
||||||
legend={
|
legend={
|
||||||
<CalendarLegend
|
<CalendarLegend
|
||||||
label={`${habit.name} progress calendar`}
|
label={`${habit.name} progress calendar`}
|
||||||
@@ -144,7 +152,7 @@ export function HabitHistory({
|
|||||||
/>
|
/>
|
||||||
{editingDay && <Modal id={`check-in-${habit.habitId}`} title="Edit check-in" description={habit.name ?? "Habit"} closeLabel="Close check-in editor" onClose={() => setEditingDay(false)} busy={savingDay}>
|
{editingDay && <Modal id={`check-in-${habit.habitId}`} title="Edit check-in" description={habit.name ?? "Habit"} closeLabel="Close check-in editor" onClose={() => setEditingDay(false)} busy={savingDay}>
|
||||||
<div className="ds-history-editor">
|
<div className="ds-history-editor">
|
||||||
<div className="ds-form-stack"><Field label="Check-in date">{id => <input id={id} type="date" disabled={savingDay} max={date} value={selectedDate ?? date} onChange={event => { if (event.target.value) setSelectedDate(event.target.value); }} />}</Field></div>
|
<div className="ds-form-stack"><Field label="Check-in date">{id => <input id={id} type="date" disabled={savingDay} max={date} value={selectedDate ?? date} onChange={event => { if (event.target.value) selectDate(event.target.value); }} />}</Field></div>
|
||||||
<CheckInEditor key={`${habit.habitId}:${selectedDate ?? date}`} habitId={habit.habitId} date={selectedDate ?? date} disabled={disabled} onExpired={onExpired}
|
<CheckInEditor key={`${habit.habitId}:${selectedDate ?? date}`} habitId={habit.habitId} date={selectedDate ?? date} disabled={disabled} onExpired={onExpired}
|
||||||
onBusyChange={setSavingDay} onSaved={() => { setAttempt(value => value + 1); onHistorySaved?.(); }} />
|
onBusyChange={setSavingDay} onSaved={() => { setAttempt(value => value + 1); onHistorySaved?.(); }} />
|
||||||
</div></Modal>}
|
</div></Modal>}
|
||||||
|
|||||||
Reference in New Issue
Block a user