Fix issues from QA review: invited editor payload, auto-resize perf, resize-y conflict
C-01: Strip is_starred/recurrence_rule from payload for invited editors
(not in backend allowlist → would 403). Hide Star checkbox from
invited editor edit mode entirely.
W-01: Wrap auto-resize in requestAnimationFrame to batch with paint
cycle and avoid forced reflow on every keystroke.
S-01: Add comment documenting belt-and-suspenders scroll prevention.
S-02: Remove resize-y from textarea (conflicts with auto-grow which
resets height on keystroke, overriding manual resize).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
43322db5ff
commit
bb39888d2e
@ -290,8 +290,10 @@ export default function EventDetailPanel({
|
||||
useEffect(() => {
|
||||
const el = descRef.current;
|
||||
if (!el) return;
|
||||
el.style.height = 'auto';
|
||||
el.style.height = `${el.scrollHeight}px`;
|
||||
requestAnimationFrame(() => {
|
||||
el.style.height = 'auto';
|
||||
el.style.height = `${el.scrollHeight}px`;
|
||||
});
|
||||
}, [editState.description, isEditing]);
|
||||
|
||||
// Poll lock status in view mode for shared events (Stream A: real-time lock awareness)
|
||||
@ -376,11 +378,11 @@ export default function EventDetailPanel({
|
||||
end_datetime: endDt,
|
||||
all_day: data.all_day,
|
||||
location_id: data.location_id ? parseInt(data.location_id) : null,
|
||||
is_starred: data.is_starred,
|
||||
recurrence_rule: rule,
|
||||
};
|
||||
// Invited editors cannot change calendars — omit calendar_id from payload
|
||||
// Invited editors are restricted to the backend allowlist — omit fields they cannot modify
|
||||
if (!canModifyAsInvitee) {
|
||||
payload.is_starred = data.is_starred;
|
||||
payload.recurrence_rule = rule;
|
||||
payload.calendar_id = data.calendar_id ? parseInt(data.calendar_id) : null;
|
||||
}
|
||||
|
||||
@ -548,6 +550,7 @@ export default function EventDetailPanel({
|
||||
: event?.title || '';
|
||||
|
||||
return (
|
||||
// onWheel stopPropagation: defence-in-depth with CalendarPage's panelOpen guard to prevent month-scroll bleed
|
||||
<div className="flex flex-col h-full bg-card border-l border-border overflow-hidden" onWheel={(e) => e.stopPropagation()}>
|
||||
{/* Header */}
|
||||
<div className="px-5 py-4 border-b border-border shrink-0">
|
||||
@ -866,18 +869,6 @@ export default function EventDetailPanel({
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Star for invited editors (no recurrence row shown) */}
|
||||
{canModifyAsInvitee && (
|
||||
<div className="flex items-center gap-2">
|
||||
<Checkbox
|
||||
id="panel-starred"
|
||||
checked={editState.is_starred}
|
||||
onChange={(e) => updateField('is_starred', (e.target as HTMLInputElement).checked)}
|
||||
/>
|
||||
<Label htmlFor="panel-starred" className="text-xs">Starred</Label>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{editState.recurrence_type === 'every_n_days' && (
|
||||
<div className="space-y-1">
|
||||
<Label htmlFor="panel-interval">Every how many days?</Label>
|
||||
@ -955,7 +946,7 @@ export default function EventDetailPanel({
|
||||
value={editState.description}
|
||||
onChange={(e) => updateField('description', e.target.value)}
|
||||
placeholder="Add a description..."
|
||||
className="text-sm resize-y flex-1 min-h-[80px]"
|
||||
className="text-sm flex-1 min-h-[80px]"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -146,8 +146,10 @@ export default function EventForm({ event, templateData, templateName, initialSt
|
||||
useEffect(() => {
|
||||
const el = descRef.current;
|
||||
if (!el) return;
|
||||
el.style.height = 'auto';
|
||||
el.style.height = `${el.scrollHeight}px`;
|
||||
requestAnimationFrame(() => {
|
||||
el.style.height = 'auto';
|
||||
el.style.height = `${el.scrollHeight}px`;
|
||||
});
|
||||
}, [formData.description]);
|
||||
|
||||
const selectableCalendars = calendars.filter((c) => !c.is_system);
|
||||
@ -269,7 +271,7 @@ export default function EventForm({ event, templateData, templateName, initialSt
|
||||
value={formData.description}
|
||||
onChange={(e) => setFormData({ ...formData, description: e.target.value })}
|
||||
placeholder="Add a description..."
|
||||
className="min-h-[80px] resize-y text-sm"
|
||||
className="min-h-[80px] text-sm"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user