Hide social sharing without explicit preview URL (#3108)

This commit is contained in:
Mason 2026-05-27 18:55:31 +08:00 committed by GitHub
parent e4b7aeae5a
commit 1083df8769
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 28 additions and 8 deletions

View file

@ -183,8 +183,8 @@ interface Props {
// affordance reads consistently across HTML / design-system / media
// variants.
headerExtras?: ReactNode;
// Social-share target for the active preview. When omitted, the modal uses
// the current browser URL so non-plugin callers still get copy/share actions.
// Social-share target for the active preview. Callers must pass an explicit
// recipient-openable URL before the modal exposes copy/social actions.
shareTarget?: PreviewShareTarget;
// Optional analytics callbacks. Fires when the user clicks the
// chrome-level affordances (fullscreen, share trigger, sidebar
@ -367,13 +367,8 @@ export function PreviewModal({
);
const exportTitle = exportTitleFor(activeView?.id ?? '');
const canExportFiles = Boolean(activeHtml);
const fallbackShareUrl = canExportFiles && typeof window !== 'undefined'
? window.location.href
: '';
const hasExplicitShareUrl = shareTarget && 'url' in shareTarget;
const explicitShareUrl = typeof shareTarget?.url === 'string' ? shareTarget.url : '';
const previewShareTitle = shareTarget?.title || exportTitle || title;
const previewShareUrl = hasExplicitShareUrl ? explicitShareUrl : fallbackShareUrl;
const previewShareUrl = typeof shareTarget?.url === 'string' ? shareTarget.url : '';
const previewShareText = t('preview.shareTextDefault', { title: previewShareTitle });
const previewShareCopy = previewShareUrl
? `${previewShareText}\n${previewShareUrl}`

View file

@ -361,6 +361,31 @@ describe('PreviewModal unavailable state', () => {
expect(screen.getByRole('menuitem', { name: /Export as standalone HTML/i })).toBeTruthy();
});
it('keeps generic preview modals export-only without an explicit share target', () => {
render(
<PreviewModal
{...baseProps}
views={[
{
id: 'preview',
label: 'Preview',
html: '<!doctype html><p>Generic preview</p>',
},
]}
onView={() => {}}
onClose={() => {}}
/>,
);
fireEvent.click(screen.getByRole('button', { name: /share/i }));
expect(screen.queryByRole('menuitem', { name: /X \/ Twitter/i })).toBeNull();
expect(screen.queryByRole('menuitem', { name: /Copy template link/i })).toBeNull();
expect(screen.getByRole('menuitem', { name: /Export as PDF/i })).toBeTruthy();
expect(screen.getByRole('menuitem', { name: /Download as \.zip/i })).toBeTruthy();
expect(screen.getByRole('menuitem', { name: /Export as standalone HTML/i })).toBeTruthy();
});
it('does not call onView for an unavailable view (no fetch to retry)', () => {
// PreviewModal fires onView on mount so the parent can lazy-load
// the active view. For an unavailable view that signal is harmless