mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-10 05:12:14 +00:00
fix(settings): guard notification panel against non-array status payload
The notifications settings panel white-screened with "x.map is not a
function" when a status payload reached the card as the whole
{ channels, events } object instead of the channels array (events is an
object map, not an array). Guard the channel map with an Array.isArray
check, default the mood reminder-hour read to optional chaining, and
add a regression test that renders the empty state instead of crashing
on a malformed payload.
This commit is contained in:
1 parent
0e3e0b0135
commit
79ce836335
4 files changed
+22
-3
No files matched your search
@@ -201,7 +201,7 @@ export function NotificationsBody({
|
||||
</label>
|
||||
)}
|
||||
|
||||
{status && status.channels.length > 0 && (
|
||||
{Array.isArray(status?.channels) && status.channels.length > 0 && (
|
||||
<div
|
||||
className="flex flex-wrap gap-1.5"
|
||||
aria-hidden="true"
|
||||
|
||||
@@ -71,6 +71,20 @@ describe("<NotificationStatusCard />", () => {
|
||||
expect(html).not.toContain('data-testid="notification-status-list"');
|
||||
});
|
||||
|
||||
it("renders empty state instead of crashing when data is not an array", () => {
|
||||
// Regression: the status endpoint returns `{ channels, events }` where
|
||||
// `events` is an object map. A shape-drifted / poisoned cache read that
|
||||
// hands the whole object to the card must not reach `.map` on a
|
||||
// non-array and white-screen the notifications panel.
|
||||
useQueryMock.mockReturnValue({
|
||||
data: { channels: [], events: {} } as unknown as MockChannel[],
|
||||
isLoading: false,
|
||||
});
|
||||
const html = render(<NotificationStatusCard />);
|
||||
expect(html).toContain("No channels configured yet");
|
||||
expect(html).not.toContain('data-testid="notification-status-list"');
|
||||
});
|
||||
|
||||
it("paints Active badge for a healthy channel", () => {
|
||||
setChannels([
|
||||
{
|
||||
|
||||
@@ -84,7 +84,7 @@ export function MoodReminderCard({
|
||||
});
|
||||
|
||||
const enabled = optimistic ?? profile?.moodReminderEnabled ?? false;
|
||||
const reminderHour = optimisticHour ?? prefs?.mood.reminderHour ?? 22;
|
||||
const reminderHour = optimisticHour ?? prefs?.mood?.reminderHour ?? 22;
|
||||
|
||||
async function handleToggle(next: boolean) {
|
||||
setOptimistic(next);
|
||||
|
||||
@@ -119,7 +119,12 @@ export function NotificationStatusCard() {
|
||||
);
|
||||
}
|
||||
|
||||
const channels = data ?? [];
|
||||
// Defensive: the status endpoint returns `{ channels, events }` where
|
||||
// `events` is an object map, not an array. A stale or shape-drifted
|
||||
// payload (or a cache read that kept the whole object instead of the
|
||||
// `.channels` slice) must never reach `.map` on a non-array — guard the
|
||||
// type here rather than white-screening the whole notifications panel.
|
||||
const channels = Array.isArray(data) ? data : [];
|
||||
if (channels.length === 0) {
|
||||
return (
|
||||
<div
|
||||
|
||||
Reference in new issue
Block a user