Skip to content
Merged
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
27 changes: 0 additions & 27 deletions src/core/ContentUpdater.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,33 +44,6 @@ describe('ContentUpdater', () => {
expect(result.error).toBe('No matching elements found');
});

it('respects locale when selecting elements', async () => {
const element = createPreviewElement({
entryId: 'entry-locale',
fieldApiId: 'title',
locale: 'en',
textContent: 'Hello',
});

createPreviewElement({
entryId: 'entry-locale',
fieldApiId: 'title',
locale: 'de',
textContent: 'Hallo',
});

const result = await updater.updateField({
entryId: 'entry-locale',
fieldApiId: 'title',
locale: 'en',
fieldType: 'STRING',
newValue: 'Updated EN',
});

expect(result.success).toBe(true);
expect(element.textContent).toBe('Updated EN');
});

it('applies multi-format rich text updates based on element preference', async () => {
const element = createPreviewElement({
entryId: 'entry-rich-text',
Expand Down
10 changes: 3 additions & 7 deletions src/core/ContentUpdater.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export class ContentUpdater {

try {
// Debounce updates
const updateKey = `${update.entryId}:${update.fieldApiId}:${update.locale || ''}`;
const updateKey = `${update.entryId}:${update.fieldApiId}`;
this.updateQueue.set(updateKey, update);

// Wait for debounce delay
Expand All @@ -52,7 +52,7 @@ export class ContentUpdater {
this.updateQueue.delete(updateKey);

// Find target elements
const elements = this.findElements(update.entryId, update.fieldApiId, update.locale);
const elements = this.findElements(update.entryId, update.fieldApiId);
if (elements.length === 0) {
return { success: false, error: 'No matching elements found' };
}
Expand All @@ -79,7 +79,6 @@ export class ContentUpdater {
console.log('[ContentUpdater] Updated field:', {
entryId: update.entryId,
fieldApiId: update.fieldApiId,
locale: update.locale,
elementsCount: elements.length,
});
}
Expand All @@ -101,17 +100,14 @@ export class ContentUpdater {
this.updateQueue.clear();
}

private findElements(entryId: string, fieldApiId?: string, locale?: string): HTMLElement[] {
private findElements(entryId: string, fieldApiId?: string): HTMLElement[] {
const elements: HTMLElement[] = [];

// Build selector
let selector = `[data-hygraph-entry-id="${entryId}"]`;
if (fieldApiId) {
selector += `[data-hygraph-field-api-id="${fieldApiId}"]`;
}
if (locale) {
selector += `[data-hygraph-field-locale="${locale}"]`;
}

const found = document.querySelectorAll<HTMLElement>(selector);
elements.push(...Array.from(found));
Expand Down
27 changes: 10 additions & 17 deletions src/core/FieldRegistry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,13 @@ export class FieldRegistry {
/**
* Get all elements for a specific field
*/
getElementsForField(fieldApiId: string, locale?: string): RegisteredElement[] {
getElementsForField(fieldApiId: string): RegisteredElement[] {
const elements: RegisteredElement[] = [];

// Search through all registry entries for matching fieldApiId and locale
// Search through all registry entries for matching fieldApiId
for (const elementList of Object.values(this.registry)) {
for (const element of elementList) {
if (element.fieldApiId === fieldApiId && element.locale === locale) {
if (element.fieldApiId === fieldApiId) {
elements.push(element);
}
}
Expand All @@ -43,15 +43,13 @@ export class FieldRegistry {
/**
* Get elements for a specific entry + field combination
*/
getElementsForEntryField(entryId: string, fieldApiId: string, locale?: string): RegisteredElement[] {
getElementsForEntryField(entryId: string, fieldApiId: string): RegisteredElement[] {
const elements: RegisteredElement[] = [];

// Search through all registry entries for matching entryId, fieldApiId, and locale
// Search through all registry entries for matching entryId and fieldApiId
for (const elementList of Object.values(this.registry)) {
for (const element of elementList) {
if (element.entryId === entryId &&
element.fieldApiId === fieldApiId &&
element.locale === locale) {
if (element.entryId === entryId && element.fieldApiId === fieldApiId) {
elements.push(element);
}
}
Expand Down Expand Up @@ -80,8 +78,8 @@ export class FieldRegistry {
/**
* Get specific element by exact match
*/
getElement(entryId: string, fieldApiId?: string, locale?: string): RegisteredElement | null {
const key = this.createRegistryKey(entryId, fieldApiId, locale);
getElement(entryId: string, fieldApiId?: string): RegisteredElement | null {
const key = this.createRegistryKey(entryId, fieldApiId);
const elements = this.registry[key];
return elements?.[0] || null;
}
Expand Down Expand Up @@ -132,7 +130,6 @@ export class FieldRegistry {
attributeFilter: [
'data-hygraph-entry-id',
'data-hygraph-field-api-id',
'data-hygraph-field-locale',
'data-hygraph-component-chain',
],
});
Expand Down Expand Up @@ -162,19 +159,17 @@ export class FieldRegistry {
if (!entryId) return;

const fieldApiId = element.getAttribute('data-hygraph-field-api-id') || undefined;
const locale = element.getAttribute('data-hygraph-field-locale') || undefined;
const componentChainRaw = element.getAttribute('data-hygraph-component-chain') || undefined;

const registeredElement: RegisteredElement = {
element,
entryId,
fieldApiId,
locale,
componentChainRaw,
lastUpdated: Date.now(),
};

const key = this.createRegistryKey(entryId, fieldApiId, locale);
const key = this.createRegistryKey(entryId, fieldApiId);

// Initialize array if it doesn't exist
if (!this.registry[key]) {
Expand All @@ -195,7 +190,6 @@ export class FieldRegistry {
console.log(`[FieldRegistry] Registered element:`, {
entryId,
fieldApiId,
locale,
element: element.tagName,
});
}
Expand All @@ -222,10 +216,9 @@ export class FieldRegistry {
}
}

private createRegistryKey(entryId: string, fieldApiId?: string, locale?: string): RegistryKey {
private createRegistryKey(entryId: string, fieldApiId?: string): RegistryKey {
const parts = [entryId];
if (fieldApiId) parts.push(fieldApiId);
if (locale) parts.push(locale);
return parts.join(':');
}

Expand Down
2 changes: 0 additions & 2 deletions src/core/OverlayManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,14 +193,12 @@ export class OverlayManager {
// Get registered element data
const entryId = hygraphElement.getAttribute('data-hygraph-entry-id');
const fieldApiId = hygraphElement.getAttribute('data-hygraph-field-api-id');
const locale = hygraphElement.getAttribute('data-hygraph-field-locale');

if (entryId) {
const registeredElement: RegisteredElement = {
element: hygraphElement,
entryId,
fieldApiId: fieldApiId || undefined,
locale: locale || undefined,
};

// Show overlay immediately
Expand Down
23 changes: 6 additions & 17 deletions src/core/Preview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -258,10 +258,9 @@ export class Preview {
const entryId = element.getAttribute('data-hygraph-entry-id');
const fieldApiId = element.getAttribute('data-hygraph-field-api-id');
const format = element.getAttribute('data-hygraph-rich-text-format') as RichTextFormatType;
const locale = element.getAttribute('data-hygraph-field-locale') || '';

if (entryId && fieldApiId && format && ['html', 'markdown', 'text'].includes(format)) {
const fieldKey = `${entryId}:${fieldApiId}:${locale}`;
const fieldKey = `${entryId}:${fieldApiId}`;

// Check for duplicate field usage (UNSUPPORTED)
if (formatPreferences[fieldKey]) {
Expand Down Expand Up @@ -373,29 +372,26 @@ export class Preview {
entryId: message.entryId,
fieldApiId: message.fieldApiId,
componentChain: message.componentChain,
locale: message.locale,
});

// Emit event for listeners
this.emitEvent('preview:field-focus', {
entryId: message.entryId,
fieldApiId: message.fieldApiId,
locale: message.locale,
});

// Use custom handler if provided
if (this.config.onFieldFocus) {
console.log('[Preview] Using custom onFieldFocus handler');
this.config.onFieldFocus(message.fieldApiId, message.locale);
this.config.onFieldFocus(message.fieldApiId);
return;
}

// Default behavior: Find and focus the specific field element for this entry
console.log('[Preview] Searching for field in registry...');
let elements = this.fieldRegistry.getElementsForEntryField(
message.entryId,
message.fieldApiId,
undefined // locale not supported yet
message.fieldApiId
);

console.log('[Preview] Initial registry search result:', {
Expand Down Expand Up @@ -462,7 +458,6 @@ export class Preview {
entryId: message.entryId,
fieldApiId: message.fieldApiId,
componentChain: message.componentChain,
locale: message.locale,
});

// Debug: Log all registered elements
Expand Down Expand Up @@ -529,7 +524,6 @@ export class Preview {
private handleIframeEditClick(element: HTMLElement): void {
const entryId = element.getAttribute('data-hygraph-entry-id');
const fieldApiId = element.getAttribute('data-hygraph-field-api-id') || undefined;
const locale = element.getAttribute('data-hygraph-field-locale') || undefined;
const componentChain = this.parseComponentChain(
element.getAttribute('data-hygraph-component-chain') || undefined
);
Expand All @@ -542,7 +536,6 @@ export class Preview {
type: 'field-click',
entryId,
fieldApiId,
locale,
componentChain,
timestamp: Date.now(),
};
Expand All @@ -562,16 +555,14 @@ export class Preview {
this.emitEvent('preview:field-click', {
entryId,
fieldApiId,
locale,
componentChain,
componentChain,
mode: this.mode
});
}

private handleStandaloneEditClick(element: HTMLElement): void {
const entryId = element.getAttribute('data-hygraph-entry-id');
const fieldApiId = element.getAttribute('data-hygraph-field-api-id') || undefined;
const locale = element.getAttribute('data-hygraph-field-locale') || undefined;
const componentChain = this.parseComponentChain(
element.getAttribute('data-hygraph-component-chain') || undefined
);
Expand All @@ -584,7 +575,7 @@ export class Preview {
}

// Construct Studio resource route URL
const studioUrl = this.buildStudioUrl(entryId, fieldApiId, locale, componentChain);
const studioUrl = this.buildStudioUrl(entryId, fieldApiId, componentChain);

// Open in new tab
window.open(studioUrl, '_blank', 'noopener,noreferrer');
Expand All @@ -597,21 +588,19 @@ export class Preview {
this.emitEvent('preview:field-click', {
entryId,
fieldApiId,
locale,
componentChain,
mode: this.mode
});
}

private buildStudioUrl(entryId: string, fieldApiId?: string, locale?: string, componentChain?: ComponentChainLink[]): string {
private buildStudioUrl(entryId: string, fieldApiId?: string, componentChain?: ComponentChainLink[]): string {
const baseUrl = (this.config.studioUrl || 'https://app.hygraph.com').replace(/\/+$/, '');
const params = new URLSearchParams({
endpoint: this.config.endpoint,
entryId,
});

if (fieldApiId) params.set('fieldApiId', fieldApiId);
if (locale) params.set('locale', locale);
if (componentChain && componentChain.length > 0) {
params.set('componentChain', JSON.stringify(componentChain));
}
Expand Down
2 changes: 0 additions & 2 deletions src/core/attributes.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,11 @@ describe('attributes helpers', () => {
const attributes = createPreviewAttributes({
entryId: 'entry-1',
fieldApiId: 'title',
locale: 'en',
});

expect(attributes).toEqual({
'data-hygraph-entry-id': 'entry-1',
'data-hygraph-field-api-id': 'title',
'data-hygraph-field-locale': 'en',
});
});

Expand Down
7 changes: 1 addition & 6 deletions src/core/attributes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,11 @@ import type { ComponentChainLink, ElementAttributes } from '../types';
export interface PreviewFieldOptions {
entryId: string;
fieldApiId?: string;
locale?: string;
componentChain?: ComponentChainLink[];
}

export function createPreviewAttributes(options: PreviewFieldOptions): ElementAttributes {
const { entryId, fieldApiId, locale, componentChain } = options;
const { entryId, fieldApiId, componentChain } = options;

if (!entryId) {
throw new Error('[Preview SDK] createPreviewAttributes requires an entryId');
Expand All @@ -22,10 +21,6 @@ export function createPreviewAttributes(options: PreviewFieldOptions): ElementAt
attributes['data-hygraph-field-api-id'] = fieldApiId;
}

if (locale) {
attributes['data-hygraph-field-locale'] = locale;
}

const serializedChain = serializeComponentChain(componentChain);
if (serializedChain) {
attributes['data-hygraph-component-chain'] = serializedChain;
Expand Down
2 changes: 1 addition & 1 deletion src/react/HygraphPreview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export interface HygraphPreviewProps extends PreviewConfig {
onDisconnected?: () => void;
onSave?: SaveCallback;
onError?: (error: Error) => void;
onFieldFocus?: (fieldApiId: string, locale?: string) => void;
onFieldFocus?: (fieldApiId: string) => void;
onFieldUpdate?: (update: import('../types').FieldUpdate) => void;
}

Expand Down
3 changes: 1 addition & 2 deletions src/react/HygraphPreviewNextjs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,8 @@ export interface HygraphPreviewNextjsProps extends Omit<HygraphPreviewProps, 'on
* Optional custom field focus handler
* Override the default behavior when Studio requests field focus
* @param fieldApiId - The field API ID to focus
* @param locale - The locale of the field (if applicable)
*/
onFieldFocus?: (fieldApiId: string, locale?: string) => void;
onFieldFocus?: (fieldApiId: string) => void;

/**
* Optional custom field update handler
Expand Down
Loading