Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/managers/message-manager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ vi.mock('./preview-bar-manager', () => ({
updatePreviewBarMessage: vi.fn(),
updatePreviewBarStep: vi.fn(),
clearPreviewBarMessage: vi.fn(),
isPreviewPickerActive: vi.fn(() => false),
}));
vi.mock('../utilities/preview-mode', () => ({
PREVIEW_PARAM_ID: 'cioPreviewId',
Expand Down
69 changes: 51 additions & 18 deletions src/managers/message-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ import {
updatePreviewBarMessage,
updatePreviewBarStep,
clearPreviewBarMessage,
isPreviewPickerActive,
} from './preview-bar-manager';
import { PREVIEW_PARAM_ID, PREVIEW_SETTINGS_PARAM } from '../utilities/preview-mode';
import type { GistMessage, DisplaySettings, MessageProperties } from '../types';
Expand Down Expand Up @@ -116,19 +117,21 @@ function showTooltipMessage(
}

// Verify target element exists in the DOM
try {
const targetElement = document.querySelector(targetSelector);
if (!targetElement) {
log(
`Tooltip target element "${targetSelector}" not found for message ${message.messageId}, skipping display`
);
if (!Gist.config.isPreviewSession) {
try {
const targetElement = document.querySelector(targetSelector);
if (!targetElement) {
log(
`Tooltip target element "${targetSelector}" not found for message ${message.messageId}, skipping display`
);
Gist.messageError(message);
return null;
}
} catch {
log(`Invalid tooltip target selector "${targetSelector}" for message ${message.messageId}`);
Gist.messageError(message);
return null;
}
} catch {
log(`Invalid tooltip target selector "${targetSelector}" for message ${message.messageId}`);
Gist.messageError(message);
return null;
}

const existingTooltip = Gist.currentMessages.find(
Expand Down Expand Up @@ -308,10 +311,26 @@ function loadMessageComponent(
if (displayType === 'tooltip') {
loadTooltipComponent(url, message, options, stepName);
} else if (elementId) {
if (positions.includes(elementId)) {
addPageElement(elementId);
let elementExists = false;
try {
// XXX: use shared util in Karn's upcoming PR
elementExists = !!(document.getElementById(elementId) ?? document.querySelector(elementId));
} catch {
elementExists = false;
}
if (!elementExists && Gist.config.isPreviewSession) {
// In previews, if the target element doesn't exist, load as an
// overlay so the iframe still mounts, allowing the user to pick a valid element.
log(`Preview session: element "${elementId}" not found, loading as overlay for preview bar`);
message.overlay = true;
Gist.overlayInstanceId = message.instanceId ?? null;
loadOverlayComponent(url, message, options, stepName);
} else {
if (positions.includes(elementId)) {
addPageElement(elementId);
}
loadEmbedComponent(elementId, url, message, options, stepName);
}
loadEmbedComponent(elementId, url, message, options, stepName);
} else {
loadOverlayComponent(url, message, options, stepName);
}
Expand Down Expand Up @@ -366,6 +385,13 @@ async function handleGistEvents(e: MessageEvent): Promise<void> {
}
if (Gist.config.isPreviewSession && currentMessage.properties?.gist?.livePreview) {
updatePreviewBarMessage(currentMessage);
if (isPreviewPickerActive()) {
// The preview bar activated the element picker as a fallback overlay (e.g. inline/tooltip with
// an invalid selector). Skip rendering the message so the fallback is not shown behind the picker.
currentMessage.firstLoad = false;
currentMessage.isDisplayChange = false;
break;
}
}
if (currentMessage.firstLoad || currentMessage.isDisplayChange) {
const displayType = getCurrentDisplayType(currentMessage);
Expand All @@ -384,13 +410,20 @@ async function handleGistEvents(e: MessageEvent): Promise<void> {
);
}
if (!targetFound) {
log(
`Tooltip target not found for "${targetSelector}", emitting error and skipping display`
);
Gist.messageError(currentMessage);
currentMessage.firstLoad = false;
currentMessage.isDisplayChange = false;
resetTooltipState(currentMessage);
if (Gist.config.isPreviewSession) {
log(
`Preview session: tooltip target "${targetSelector}" not found, skipping display`
);
// Don't reset so the element-picker can activate.
} else {
log(
`Tooltip target not found for "${targetSelector}", emitting error and skipping display`
);
Gist.messageError(currentMessage);
resetTooltipState(currentMessage);
}
break;
}
const tooltipVisible = await showTooltipComponent(currentMessage);
Expand Down
29 changes: 25 additions & 4 deletions src/managers/preview-bar-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { PREVIEW_PARAM_ID, teardownPreview } from '../utilities/preview-mode';
const STORAGE_KEY = 'gist.previewBar.collapsed';
const STYLE_ID = 'gist-pb-styles';
const BAR_ID = 'gist-preview-bar';
const PLACEHOLDER_SELECTOR = '$element_id';

const OVERLAY_POSITION_LABELS: Record<string, string> = {
topLeft: 'Top Left',
Expand Down Expand Up @@ -115,9 +116,10 @@ function buildOverlayColor(color: string, opacity: number): string {
// ─── Guards ───────────────────────────────────────────────────────────────────

function isReadyToApply(settings: DisplaySettings): boolean {
const selector = settings.elementSelector?.trim();
if (
(settings.displayType === 'inline' || settings.displayType === 'tooltip') &&
!settings.elementSelector?.trim()
(!selector || selector === PLACEHOLDER_SELECTOR)
) {
return false;
}
Expand Down Expand Up @@ -632,9 +634,19 @@ function renderBar() {

// Type-specific controls
const displayType = currentSettings.displayType || 'modal';
if (displayType === 'modal') buildModalControls(currentSettings, controlsRow);
else if (displayType === 'overlay') buildOverlayControls(currentSettings, controlsRow);
else if (displayType === 'inline') buildInlineControls(currentSettings, controlsRow);
switch (displayType) {
case 'overlay':
buildOverlayControls(currentSettings, controlsRow);
break;
case 'inline':
case 'tooltip':
buildInlineControls(currentSettings, controlsRow);
break;
case 'modal':
default:
buildModalControls(currentSettings, controlsRow);
break;
}

controlsRow.appendChild(el('div', { className: 'gist-pb-spacer' }));

Expand Down Expand Up @@ -702,6 +714,7 @@ export function updatePreviewBarMessage(message: GistMessage): void {
if (!targetStep) {
log(`Preview bar: step "${savedStep}" not found, ignoring initial step/display override`);
renderBar();
if (!isReadyToApply(currentSettings)) activatePickerIfNeeded();
return;
}
currentStepName = targetStep.stepName;
Expand All @@ -712,6 +725,7 @@ export function updatePreviewBarMessage(message: GistMessage): void {
if (!hasPendingStep) {
log(`Preview bar: display type "${savedType}" provided without a step, ignoring`);
renderBar();
if (!isReadyToApply(currentSettings)) activatePickerIfNeeded();
return;
}
currentSettings = {
Expand All @@ -732,6 +746,9 @@ export function updatePreviewBarMessage(message: GistMessage): void {
}

renderBar();
if (!isReadyToApply(currentSettings)) {
activatePickerIfNeeded();
}
}

export function updatePreviewBarStep(stepName: string, displaySettings: DisplaySettings): void {
Expand Down Expand Up @@ -793,6 +810,10 @@ export function setPreviewBarInitialStep(
pendingInitialDisplayType = displayType;
}

export function isPreviewPickerActive(): boolean {
return pickerActive;
}

export function destroyPreviewBar(): void {
if (pickerCleanup) pickerCleanup();
if (sessionEndedTimer) {
Expand Down
Loading