From 9f5d2f6488473519bd1f67c46c3e09b0f329ba99 Mon Sep 17 00:00:00 2001
From: jubnl <66769052+jubnl@users.noreply.github.com>
Date: Tue, 16 Jun 2026 08:39:39 +0200
Subject: [PATCH] fix(planner): scroll long place description/notes on mobile
(#1195) (#1199)
The place details card (PlaceInspector) clipped long description/notes
with no way to scroll. The content area is a flex column whose children
(description/notes) had the default flex-shrink: 1, so once the card hit
its maxHeight cap they compressed to fit and their overflow:hidden clipped
the text instead of overflowing into a scroll region.
- Make the content area a bounded scroll region (flex: 1 1 auto,
minHeight: 0, overflowY: auto, momentum + overscroll containment).
- Pin description/notes with flexShrink: 0 so they keep natural height and
the card overflows into the scroll instead of clipping.
- Pin header/footer with flexShrink: 0 so they stay fixed while scrolling.
- Add wordBreak/overflowWrap to the description div to fix horizontal clip.
---
.../Planner/PlaceInspector.test.tsx | 38 +++++++++++++++++++
.../src/components/Planner/PlaceInspector.tsx | 10 ++---
2 files changed, 43 insertions(+), 5 deletions(-)
diff --git a/client/src/components/Planner/PlaceInspector.test.tsx b/client/src/components/Planner/PlaceInspector.test.tsx
index 1c35fd0b..22e51a0d 100644
--- a/client/src/components/Planner/PlaceInspector.test.tsx
+++ b/client/src/components/Planner/PlaceInspector.test.tsx
@@ -647,5 +647,43 @@ describe('PlaceInspector', () => {
expect(screen.queryByText('Participants')).toBeNull();
});
+ // ── Scroll / overflow (issue #1195) ──────────────────────────────────────
+
+ it('FE-PLANNER-INSPECTOR-046: content area is a bounded flex scroll region', () => {
+ const longText = 'Lorem ipsum dolor sit amet. '.repeat(200);
+ const p = buildPlace({ id: 200, description: longText, notes: longText } as any);
+ render();
+ const scroll = screen.getByTestId('inspector-scroll') as HTMLElement;
+ expect(scroll.style.overflowY).toBe('auto');
+ expect(scroll.style.minHeight).toBe('0px');
+ // flex must allow the region to shrink/grow within the capped card
+ expect(scroll.style.flex).not.toBe('');
+ expect(scroll.style.flex).not.toBe('0 0 auto');
+ });
+
+ it('FE-PLANNER-INSPECTOR-047: long unbroken description wraps instead of clipping horizontally', () => {
+ const longWord = 'https://example.com/' + 'a'.repeat(300);
+ const p = buildPlace({ id: 201, description: longWord } as any);
+ const { container } = render();
+ const descDiv = container.querySelector('.collab-note-md') as HTMLElement;
+ expect(descDiv).toBeTruthy();
+ expect(descDiv.style.overflowWrap).toBe('anywhere');
+ expect(descDiv.style.wordBreak).toBe('break-word');
+ });
+
+ it('FE-PLANNER-INSPECTOR-048: description/notes do not shrink so the card scrolls instead of clipping', () => {
+ const longText = 'Lorem ipsum dolor sit amet. '.repeat(200);
+ const p = buildPlace({ id: 202, description: longText, notes: longText } as any);
+ const { container } = render();
+ const notes = Array.from(container.querySelectorAll('.collab-note-md')) as HTMLElement[];
+ // Both description and notes containers must keep their natural height
+ // (flex-shrink: 0) — otherwise they compress inside the flex column and
+ // overflow:hidden clips the text with no scroll (issue #1195).
+ expect(notes.length).toBe(2);
+ for (const el of notes) {
+ expect(el.style.flexShrink).toBe('0');
+ }
+ });
+
});
diff --git a/client/src/components/Planner/PlaceInspector.tsx b/client/src/components/Planner/PlaceInspector.tsx
index ab4f6ddb..30c93fc6 100644
--- a/client/src/components/Planner/PlaceInspector.tsx
+++ b/client/src/components/Planner/PlaceInspector.tsx
@@ -217,7 +217,7 @@ export default function PlaceInspector({
locale={locale} timeFormat={timeFormat} onClose={onClose} />
{/* Content — scrollable */}
-
+
{/* Info-Chips — hidden on mobile, shown on desktop */}
@@ -253,14 +253,14 @@ export default function PlaceInspector({
{/* Description / Summary */}
{(place.description || googleDetails?.summary) && (
-
+
{place.description || googleDetails?.summary || ''}
)}
{/* Notes */}
{place.notes && (
-
+
{place.notes}
)}
@@ -279,7 +279,7 @@ export default function PlaceInspector({
{/* Footer actions */}
-
+
{selectedDayId && (
assignmentInDay ? (
onRemoveAssignment(selectedDayId, assignmentInDay.id)} variant="ghost" icon={}
@@ -497,7 +497,7 @@ function ParticipantsBox({ tripMembers, participantIds, allJoined, onSetParticip
function PlaceInspectorHeader({ openNow, place, category, t, editingName, nameInputRef, nameValue, setNameValue,
commitNameEdit, handleNameKeyDown, startNameEdit, onUpdatePlace, locale, timeFormat, onClose }: any) {
return (
-
+
{/* Avatar with open/closed ring + tag */}