feat(site-builder): add in-builder Report an Issue modal
Wires the Task 18 payload builder and Task 16/17 diagnostics into a user-facing modal, reachable from the topbar bug icon (desktop) and overflow menu (mobile). Two gaps in the task brief's draft, not called out there, are handled explicitly: buildReportPayload() can throw when the payload is still oversized after canvas_state is dropped, so submission is wrapped in try/catch with an actionable "too large" error that preserves the user's typed text; and the textarea maxLength is sourced from MAX_DESCRIPTION_CHARS (with a proximity character count) instead of a hardcoded number, so the UI limit can't drift from the payload limit. Also portals the modal to document.body for the same stacking-context reasons TemplateModal/HeadCodeModal already do, and guards the useEditor() selection collector with optional chaining so it degrades gracefully under TopBar's existing test harness (a minimal @craftjs/core stub with no events/nodes on its collector state). The textarea uses onInput rather than onChange: React's onChange dedup (via its DOM value-tracker) treats a test harness's raw `el.value = x` assignment as a no-op change, matching this repo's existing pattern (HeadCodeModal.test.tsx, shared-controls.test.tsx, etc. all work around the same gotcha) -- onInput is a plain passthrough with no such check, and is behaviorally identical for real typing. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -9,6 +9,7 @@ import { useMobileChrome } from '../../state/MobileChromeContext';
|
||||
import { DeviceMode } from '../../types';
|
||||
import { TemplateModal } from './TemplateModal';
|
||||
import { HeadCodeModal } from './HeadCodeModal';
|
||||
import { ReportIssueModal } from './ReportIssueModal';
|
||||
import { TopBarOverflowMenu } from './TopBarOverflowMenu';
|
||||
import { PublishWarnings } from './PublishWarnings';
|
||||
import { SitesmithButton } from '../sitesmith/SitesmithButton';
|
||||
@@ -40,6 +41,7 @@ export const TopBar: React.FC<TopBarProps> = ({ device, onDeviceChange, showGuid
|
||||
// opening a mobile sheet can close these modals (item 3) -- behavior is
|
||||
// otherwise identical for both the desktop and mobile branches below.
|
||||
const { templateModalOpen, setTemplateModalOpen, headCodeModalOpen, setHeadCodeModalOpen, overflowOpen, setOverflowOpen } = useMobileChrome();
|
||||
const [reportOpen, setReportOpen] = useState(false);
|
||||
const isMobile = useIsMobile();
|
||||
const { open: openSitesmith } = useSitesmithModal();
|
||||
const saveTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||
@@ -303,12 +305,14 @@ export const TopBar: React.FC<TopBarProps> = ({ device, onDeviceChange, showGuid
|
||||
onToggleGuides={onToggleGuides}
|
||||
onOpenTemplates={() => setTemplateModalOpen(true)}
|
||||
onOpenHeadCode={() => setHeadCodeModalOpen(true)}
|
||||
onOpenReportIssue={() => setReportOpen(true)}
|
||||
onPreview={handlePreview}
|
||||
sitesmithNode={<SitesmithButton onClick={() => openSitesmith()} />}
|
||||
/>
|
||||
|
||||
<TemplateModal open={templateModalOpen} onClose={() => setTemplateModalOpen(false)} />
|
||||
<HeadCodeModal open={headCodeModalOpen} onClose={() => setHeadCodeModalOpen(false)} />
|
||||
<ReportIssueModal open={reportOpen} onClose={() => setReportOpen(false)} device={device} />
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
@@ -377,6 +381,15 @@ export const TopBar: React.FC<TopBarProps> = ({ device, onDeviceChange, showGuid
|
||||
<button className="topbar-btn icon-only" aria-label="Preview" data-tooltip="Preview" onClick={handlePreview}>
|
||||
<i className="fa fa-eye" />
|
||||
</button>
|
||||
<button
|
||||
className="topbar-btn icon-only"
|
||||
aria-label="Report an issue"
|
||||
data-tooltip="Report an issue"
|
||||
title="Report an issue"
|
||||
onClick={() => setReportOpen(true)}
|
||||
>
|
||||
<i className="fa fa-bug" />
|
||||
</button>
|
||||
|
||||
{/* Draft/Published status badge */}
|
||||
{isWHP && isDraft && publishStatus !== 'published' && (
|
||||
@@ -441,6 +454,7 @@ export const TopBar: React.FC<TopBarProps> = ({ device, onDeviceChange, showGuid
|
||||
</div>
|
||||
<TemplateModal open={templateModalOpen} onClose={() => setTemplateModalOpen(false)} />
|
||||
<HeadCodeModal open={headCodeModalOpen} onClose={() => setHeadCodeModalOpen(false)} />
|
||||
<ReportIssueModal open={reportOpen} onClose={() => setReportOpen(false)} device={device} />
|
||||
</nav>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user