From 0f8dcf1df32a52ffff9e1a168200c28f085bee2f Mon Sep 17 00:00:00 2001 From: diwakarmk7 Date: Mon, 19 May 2025 12:55:13 +0530 Subject: [PATCH 01/11] popup state fix --- src/visualBuilder/eventManager/useCollab.ts | 4 ++++ src/visualBuilder/generators/generateThread.tsx | 1 + 2 files changed, 5 insertions(+) diff --git a/src/visualBuilder/eventManager/useCollab.ts b/src/visualBuilder/eventManager/useCollab.ts index 825ecb69..7f05cc2b 100644 --- a/src/visualBuilder/eventManager/useCollab.ts +++ b/src/visualBuilder/eventManager/useCollab.ts @@ -43,6 +43,10 @@ export const useCollab = () => { "collab.pauseFeedback", data?.data?.collab?.pauseFeedback ); + Config.set( + "collab.isFeedbackMode", + data?.data?.collab?.isFeedbackMode + ); showAllCollabIcons(); return; } diff --git a/src/visualBuilder/generators/generateThread.tsx b/src/visualBuilder/generators/generateThread.tsx index 850cac7e..8f54a506 100644 --- a/src/visualBuilder/generators/generateThread.tsx +++ b/src/visualBuilder/generators/generateThread.tsx @@ -376,6 +376,7 @@ export function hideAllCollabIcons(): void { ".visual-builder__collab-wrapper .collab-thread" ); icons?.forEach((icon) => icon?.classList.add(hiddenClass)); + toggleCollabPopup({ threadUid: "", action: "close" }); } export function showAllCollabIcons(): void { From bfc29e36527e62fa682089769bb3653dbc924dbc Mon Sep 17 00:00:00 2001 From: diwakarmk7 Date: Thu, 22 May 2025 07:18:33 +0530 Subject: [PATCH 02/11] fix(collab): username display difference fix --- .../Collab/ThreadPopup/CommentTextArea.tsx | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/visualBuilder/components/Collab/ThreadPopup/CommentTextArea.tsx b/src/visualBuilder/components/Collab/ThreadPopup/CommentTextArea.tsx index 203842dc..01275762 100644 --- a/src/visualBuilder/components/Collab/ThreadPopup/CommentTextArea.tsx +++ b/src/visualBuilder/components/Collab/ThreadPopup/CommentTextArea.tsx @@ -102,12 +102,22 @@ const MentionSuggestionsList: React.FC<{ tabIndex={-1} aria-selected={index === selectedIndex} > - {(user.display?.length || 0) > 20 ? ( - - {(user.display || "").substring(0, 18) + "..."} - + {user.display == user.email ? ( + user.display.length > 20 ? ( + + {(user.display || "").substring(0, 18) + "..."} + + ) : ( + user.display + ) ) : ( - user.display || "" + + {user.display.length > 20 + ? (user.display || "").substring(0, 18) + "..." + : user.display} + )} ))} From c545e4acfc057a50c927b0982042d80baae2bafc Mon Sep 17 00:00:00 2001 From: Kirtesh Suthar Date: Mon, 26 May 2025 16:58:52 +0530 Subject: [PATCH 03/11] fix: update LightLivePreviewHoC config to use IExportedConfig type --- src/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/index.ts b/src/index.ts index 36b903f5..f216bd50 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,5 @@ import ContentstackLivePreviewHOC from "./preview/contentstack-live-preview-HOC"; -import { IStackSdk as ExternalStackSdkType } from "./types/types"; +import { IStackSdk as ExternalStackSdkType, IExportedConfig } from "./types/types"; import { OnEntryChangeCallback, @@ -37,7 +37,7 @@ class LightLivePreviewHoC { } static get config() { - return {}; + return {} as IExportedConfig; } static isInitialized() { From e248309141411ac1398b7661bdc5bd83dfab8857 Mon Sep 17 00:00:00 2001 From: Kirtesh Suthar Date: Mon, 26 May 2025 18:02:37 +0530 Subject: [PATCH 04/11] refactor: move LightLivePreviewHoC to a new file and simplify index.ts --- .../contentstack-live-preview.test.ts | 177 ++++++++++++++++++ src/index.ts | 68 +------ src/light-sdk.ts | 67 +++++++ 3 files changed, 246 insertions(+), 66 deletions(-) create mode 100644 src/__test__/contentstack-live-preview.test.ts create mode 100644 src/light-sdk.ts diff --git a/src/__test__/contentstack-live-preview.test.ts b/src/__test__/contentstack-live-preview.test.ts new file mode 100644 index 00000000..e229a1f8 --- /dev/null +++ b/src/__test__/contentstack-live-preview.test.ts @@ -0,0 +1,177 @@ +import { vi } from 'vitest'; +import type { OnEntryChangeCallback, OnEntryChangeConfig } from '../livePreview/types/onEntryChangeCallback.type'; +import type { IStackSdk, IExportedConfig, IInitData } from '../types/types'; + +// Store original env and window +const originalEnv = process.env; +const originalWindow = global.window; + +describe('ContentstackLivePreview HOC Class Integration Tests', () => { + let ContentstackLivePreview: any; + let ContentstackLivePreviewHOC: any; + + beforeEach(() => { + // Reset modules and env before each test + vi.resetModules(); + process.env = { ...originalEnv }; + }); + + afterEach(() => { + process.env = originalEnv; + vi.clearAllMocks(); + + // Restore window to original state + Object.defineProperty(global, 'window', { + value: originalWindow, + writable: true, + configurable: true + }); + }); + + describe('SDK Selection Tests', () => { + test('should use LightLivePreviewHoC when PURGE_PREVIEW_SDK is true', async () => { + process.env.PURGE_PREVIEW_SDK = 'true'; + + const { default: CSLivePreview } = await import('../index'); + const { default: CSLivePreviewHOC } = await import('../preview/contentstack-live-preview-HOC'); + + ContentstackLivePreview = CSLivePreview; + ContentstackLivePreviewHOC = CSLivePreviewHOC; + + expect(ContentstackLivePreview.name).toBe('LightLivePreviewHoC'); + }); + + test('should use LightLivePreviewHoC when REACT_APP_PURGE_PREVIEW_SDK is true', async () => { + process.env.REACT_APP_PURGE_PREVIEW_SDK = 'true'; + + const { default: CSLivePreview } = await import('../index'); + ContentstackLivePreview = CSLivePreview; + + expect(ContentstackLivePreview.name).toBe('LightLivePreviewHoC'); + }); + + test('should use ContentstackLivePreviewHOC by default', async () => { + process.env.PURGE_PREVIEW_SDK = 'false'; + process.env.REACT_APP_PURGE_PREVIEW_SDK = 'false'; + + const { default: CSLivePreview } = await import('../index'); + const { default: CSLivePreviewHOC } = await import('../preview/contentstack-live-preview-HOC'); + + ContentstackLivePreview = CSLivePreview; + ContentstackLivePreviewHOC = CSLivePreviewHOC; + + expect(ContentstackLivePreview).toBe(ContentstackLivePreviewHOC); + }); + }); + + describe('LightLivePreviewHoC Functionality Tests', () => { + beforeEach(async () => { + process.env.PURGE_PREVIEW_SDK = 'true'; + const { default: CSLivePreview } = await import('../index'); + ContentstackLivePreview = CSLivePreview; + }); + + test('should initialize with empty constructors', async () => { + const result = await ContentstackLivePreview.init(); + expect(result).toEqual({ + livePreview: {}, + visualBuilder: {}, + }); + }); + + test('should return empty hash', () => { + expect(ContentstackLivePreview.hash).toBe(''); + }); + + test('should return empty config', () => { + const config = ContentstackLivePreview.config as IExportedConfig; + expect(config).toEqual({}); + }); + + test('should allow access to stackDetails from config without type error', () => { + expect(ContentstackLivePreview.config.stackDetails).toEqual(undefined); + }); + + test('should handle onEntryChange with immediate callback execution', () => { + const mockCallback = vi.fn() as OnEntryChangeCallback; + const config: OnEntryChangeConfig = { skipInitialRender: false }; + + const callbackId = ContentstackLivePreview.onEntryChange(mockCallback, config); + + expect(callbackId).toBe('live-preview-id'); + expect(mockCallback).toHaveBeenCalledTimes(1); + }); + + test('should handle onEntryChange with skipped initial render', () => { + const mockCallback = vi.fn() as OnEntryChangeCallback; + const config: OnEntryChangeConfig = { skipInitialRender: true }; + + const callbackId = ContentstackLivePreview.onEntryChange(mockCallback, config); + + expect(callbackId).toBe('live-preview-id'); + expect(mockCallback).not.toHaveBeenCalled(); + }); + + test('should handle onLiveEdit', () => { + const mockCallback = vi.fn() as OnEntryChangeCallback; + const result = ContentstackLivePreview.onLiveEdit(mockCallback); + expect(result).toBe('live-preview-id'); + }); + + test('should handle unsubscribeOnEntryChange without errors', () => { + const mockCallback = vi.fn() as OnEntryChangeCallback; + const callbackId = ContentstackLivePreview.onEntryChange(mockCallback); + expect(() => { + ContentstackLivePreview.unsubscribeOnEntryChange(callbackId); + }).not.toThrow(); + }); + + test('should return package version from environment', () => { + process.env.PACKAGE_VERSION = '1.0.0'; + expect(ContentstackLivePreview.getSdkVersion()).toBe('1.0.0'); + }); + }); + + describe('Browser Environment Tests', () => { + beforeEach(async () => { + process.env.PURGE_PREVIEW_SDK = 'true'; + vi.resetModules(); + const { default: CSLivePreview } = await import('../index'); + ContentstackLivePreview = CSLivePreview; + }); + + test('should handle initialization in non-browser environment', async () => { + // Mock window as undefined + Object.defineProperty(global, 'window', { + value: undefined, + writable: true, + configurable: true + }); + + const result = await ContentstackLivePreview.init(); + + expect(result).toEqual({}); + }); + + test('should initialize properly in browser environment', async () => { + // Mock window with minimum required properties + Object.defineProperty(global, 'window', { + value: { + location: { + search: '', + hash: '' + } + }, + writable: true, + configurable: true + }); + + const result = await ContentstackLivePreview.init(); + + expect(result).toEqual({ + livePreview: {}, + visualBuilder: {}, + }); + }); + }); +}); diff --git a/src/index.ts b/src/index.ts index f216bd50..45074abc 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,74 +1,10 @@ import ContentstackLivePreviewHOC from "./preview/contentstack-live-preview-HOC"; -import { IStackSdk as ExternalStackSdkType, IExportedConfig } from "./types/types"; +import { IStackSdk as ExternalStackSdkType } from "./types/types"; -import { - OnEntryChangeCallback, - OnEntryChangeCallbackUID, - OnEntryChangeConfig, -} from "./livePreview/types/onEntryChangeCallback.type"; +import LightLivePreviewHoC from "./light-sdk"; export type IStackSdk = ExternalStackSdkType; -class LightLivePreviewHoC { - private static previewConstructors = {}; - private static onEntryChangeCallbacks = {}; - - static init() { - if (typeof window === "undefined") { - return Promise.resolve(LightLivePreviewHoC.previewConstructors); - } - - return LightLivePreviewHoC.initializePreview(); - } - - private static initializePreview() { - LightLivePreviewHoC.previewConstructors = { - livePreview: {}, - visualBuilder: {}, - }; - - LightLivePreviewHoC.onEntryChangeCallbacks = {}; - - return Promise.resolve(LightLivePreviewHoC.previewConstructors); - } - - static get hash() { - return ""; - } - - static get config() { - return {} as IExportedConfig; - } - - static isInitialized() { - return false; - } - - static onEntryChange( - callback: OnEntryChangeCallback, - config: OnEntryChangeConfig = {} - ): OnEntryChangeCallbackUID { - const { skipInitialRender = false } = config; - - if (!skipInitialRender) { - callback(); - } - - return "live-preview-id"; - } - - static onLiveEdit(callback: OnEntryChangeCallback) { - return "live-preview-id"; - } - - static unsubscribeOnEntryChange() { - // intentionally empty - } - - static getSdkVersion(): string { - return process?.env?.PACKAGE_VERSION!; - } -} const ContentstackLivePreview = typeof process !== "undefined" && diff --git a/src/light-sdk.ts b/src/light-sdk.ts new file mode 100644 index 00000000..becf0ad1 --- /dev/null +++ b/src/light-sdk.ts @@ -0,0 +1,67 @@ +import { OnEntryChangeCallbackUID, OnEntryChangeConfig } from "./livePreview/types/onEntryChangeCallback.type"; + +import { OnEntryChangeCallback } from "./livePreview/types/onEntryChangeCallback.type"; +import { IExportedConfig } from "./types/types"; + +class LightLivePreviewHoC { + private static previewConstructors = {}; + private static onEntryChangeCallbacks = {}; + + static init() { + if (typeof window === "undefined") { + return Promise.resolve(LightLivePreviewHoC.previewConstructors); + } + + return LightLivePreviewHoC.initializePreview(); + } + + private static initializePreview() { + LightLivePreviewHoC.previewConstructors = { + livePreview: {}, + visualBuilder: {}, + }; + + LightLivePreviewHoC.onEntryChangeCallbacks = {}; + + return Promise.resolve(LightLivePreviewHoC.previewConstructors); + } + + static get hash() { + return ""; + } + + static get config() { + return {} as IExportedConfig; + } + + static isInitialized() { + return false; + } + + static onEntryChange( + callback: OnEntryChangeCallback, + config: OnEntryChangeConfig = {} + ): OnEntryChangeCallbackUID { + const { skipInitialRender = false } = config; + + if (!skipInitialRender) { + callback(); + } + + return "live-preview-id"; + } + + static onLiveEdit(callback: OnEntryChangeCallback) { + return "live-preview-id"; + } + + static unsubscribeOnEntryChange() { + // intentionally empty + } + + static getSdkVersion(): string { + return process?.env?.PACKAGE_VERSION!; + } +} + +export default LightLivePreviewHoC; \ No newline at end of file From 1f024ce31c299cd312cfb26ba85498d967f02d25 Mon Sep 17 00:00:00 2001 From: Kirtesh Suthar Date: Mon, 26 May 2025 18:05:09 +0530 Subject: [PATCH 05/11] fix: update script integrity hash in README.md for Contentstack Live Preview utils --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c8ca55f6..9eed318c 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ npm install @contentstack/live-preview-utils Alternatively, if you want to include the package directly in your website HTML code, use the following command: ```html -