mirror of
https://github.com/formbricks/formbricks.git
synced 2026-02-22 10:08:42 -06:00
fix: adding back hidden fields for backwards compatibility (#5163)
This commit is contained in:
@@ -7,6 +7,7 @@ import { checkPageUrl } from "@/lib/survey/no-code-action";
|
||||
import * as Attribute from "@/lib/user/attribute";
|
||||
import * as User from "@/lib/user/user";
|
||||
import { type TConfigInput, type TLegacyConfigInput } from "@/types/config";
|
||||
import { type TTrackProperties } from "@/types/survey";
|
||||
|
||||
const queue = new CommandQueue();
|
||||
|
||||
@@ -67,8 +68,12 @@ const logout = async (): Promise<void> => {
|
||||
await queue.wait();
|
||||
};
|
||||
|
||||
const track = async (code: string): Promise<void> => {
|
||||
queue.add(Action.trackCodeAction, true, code);
|
||||
/**
|
||||
* @param code - The code of the action to track
|
||||
* @param properties - Optional properties to set, like the hidden fields (deprecated, hidden fields will be removed in a future version)
|
||||
*/
|
||||
const track = async (code: string, properties?: TTrackProperties): Promise<void> => {
|
||||
queue.add<string | TTrackProperties | undefined>(Action.trackCodeAction, true, code, properties);
|
||||
await queue.wait();
|
||||
};
|
||||
|
||||
@@ -91,5 +96,6 @@ const formbricks = {
|
||||
registerRouteChange,
|
||||
};
|
||||
|
||||
export type TFormbricks = typeof formbricks;
|
||||
type TFormbricks = typeof formbricks;
|
||||
export type { TFormbricks };
|
||||
export default formbricks;
|
||||
|
||||
@@ -4,9 +4,13 @@ import { checkSetup } from "@/lib/common/setup";
|
||||
import { wrapThrowsAsync } from "@/lib/common/utils";
|
||||
import type { Result } from "@/types/error";
|
||||
|
||||
export type TCommand = (
|
||||
...args: any[]
|
||||
) => Promise<Result<void, unknown>> | Result<void, unknown> | Promise<void>;
|
||||
|
||||
export class CommandQueue {
|
||||
private queue: {
|
||||
command: (...args: any[]) => Promise<Result<void, unknown>> | Result<void, unknown> | Promise<void>;
|
||||
command: TCommand;
|
||||
checkSetup: boolean;
|
||||
commandArgs: any[];
|
||||
}[] = [];
|
||||
@@ -14,11 +18,7 @@ export class CommandQueue {
|
||||
private resolvePromise: (() => void) | null = null;
|
||||
private commandPromise: Promise<void> | null = null;
|
||||
|
||||
public add<A>(
|
||||
command: (...args: A[]) => Promise<Result<void, unknown>> | Result<void, unknown> | Promise<void>,
|
||||
shouldCheckSetup = true,
|
||||
...args: A[]
|
||||
): void {
|
||||
public add<A>(command: TCommand, shouldCheckSetup = true, ...args: A[]): void {
|
||||
this.queue.push({ command, checkSetup: shouldCheckSetup, commandArgs: args });
|
||||
|
||||
if (!this.running) {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { Logger } from "@/lib/common/logger";
|
||||
import type {
|
||||
TEnvironmentState,
|
||||
TEnvironmentStateActionClass,
|
||||
@@ -8,7 +9,11 @@ import type {
|
||||
TUserState,
|
||||
} from "@/types/config";
|
||||
import type { Result } from "@/types/error";
|
||||
import { type TActionClassNoCodeConfig, type TActionClassPageUrlRule } from "@/types/survey";
|
||||
import {
|
||||
type TActionClassNoCodeConfig,
|
||||
type TActionClassPageUrlRule,
|
||||
type TTrackProperties,
|
||||
} from "@/types/survey";
|
||||
|
||||
// Helper function to calculate difference in days between two dates
|
||||
export const diffInDays = (date1: Date, date2: Date): number => {
|
||||
@@ -225,6 +230,38 @@ export const handleUrlFilters = (urlFilters: TActionClassNoCodeConfig["urlFilter
|
||||
return isMatch;
|
||||
};
|
||||
|
||||
export const handleHiddenFields = (
|
||||
hiddenFieldsConfig: TEnvironmentStateSurvey["hiddenFields"],
|
||||
hiddenFields?: TTrackProperties["hiddenFields"]
|
||||
): TTrackProperties["hiddenFields"] => {
|
||||
const logger = Logger.getInstance();
|
||||
const { enabled: enabledHiddenFields, fieldIds: surveyHiddenFieldIds } = hiddenFieldsConfig;
|
||||
|
||||
let hiddenFieldsObject: TTrackProperties["hiddenFields"] = {};
|
||||
|
||||
if (!enabledHiddenFields) {
|
||||
logger.error("Hidden fields are not enabled for this survey");
|
||||
} else if (surveyHiddenFieldIds && hiddenFields) {
|
||||
const unknownHiddenFields: string[] = [];
|
||||
hiddenFieldsObject = Object.keys(hiddenFields).reduce<TTrackProperties["hiddenFields"]>((acc, key) => {
|
||||
if (surveyHiddenFieldIds.includes(key)) {
|
||||
acc[key] = hiddenFields[key];
|
||||
} else {
|
||||
unknownHiddenFields.push(key);
|
||||
}
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
if (unknownHiddenFields.length > 0) {
|
||||
logger.error(
|
||||
`Unknown hidden fields: ${unknownHiddenFields.join(", ")}. Please add them to the survey hidden fields.`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return hiddenFieldsObject;
|
||||
};
|
||||
|
||||
export const evaluateNoCodeConfigClick = (
|
||||
targetElement: HTMLElement,
|
||||
action: TEnvironmentStateActionClass
|
||||
|
||||
@@ -2,14 +2,20 @@ import { Config } from "@/lib/common/config";
|
||||
import { Logger } from "@/lib/common/logger";
|
||||
import { triggerSurvey } from "@/lib/survey/widget";
|
||||
import { type InvalidCodeError, type NetworkError, type Result, err, okVoid } from "@/types/error";
|
||||
import { type TTrackProperties } from "@/types/survey";
|
||||
|
||||
/**
|
||||
* Tracks an action name and triggers associated surveys
|
||||
* @param name - The name of the action to track
|
||||
* @param alias - Optional alias for the action name
|
||||
* @param properties - Optional properties to set, like the hidden fields (deprecated, hidden fields will be removed in a future version)
|
||||
* @returns Result indicating success or network error
|
||||
*/
|
||||
export const trackAction = async (name: string, alias?: string): Promise<Result<void, NetworkError>> => {
|
||||
export const trackAction = async (
|
||||
name: string,
|
||||
alias?: string,
|
||||
properties?: TTrackProperties
|
||||
): Promise<Result<void, NetworkError>> => {
|
||||
const logger = Logger.getInstance();
|
||||
const appConfig = Config.getInstance();
|
||||
|
||||
@@ -24,7 +30,7 @@ export const trackAction = async (name: string, alias?: string): Promise<Result<
|
||||
for (const survey of activeSurveys) {
|
||||
for (const trigger of survey.triggers) {
|
||||
if (trigger.actionClass.name === name) {
|
||||
await triggerSurvey(survey, name);
|
||||
await triggerSurvey(survey, name, properties);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -38,10 +44,12 @@ export const trackAction = async (name: string, alias?: string): Promise<Result<
|
||||
/**
|
||||
* Tracks an action by its code and triggers associated surveys (used for code actions only)
|
||||
* @param code - The action code to track
|
||||
* @param properties - Optional properties to set, like the hidden fields (deprecated, hidden fields will be removed in a future version)
|
||||
* @returns Result indicating success, network error, or invalid code error
|
||||
*/
|
||||
export const trackCodeAction = async (
|
||||
code: string
|
||||
code: string,
|
||||
properties?: TTrackProperties
|
||||
): Promise<Result<void, NetworkError> | Result<void, InvalidCodeError>> => {
|
||||
const appConfig = Config.getInstance();
|
||||
|
||||
@@ -61,7 +69,7 @@ export const trackCodeAction = async (
|
||||
});
|
||||
}
|
||||
|
||||
return trackAction(actionClass.name, code);
|
||||
return trackAction(actionClass.name, code, properties);
|
||||
};
|
||||
|
||||
export const trackNoCodeAction = (name: string): Promise<Result<void, NetworkError>> => {
|
||||
|
||||
@@ -33,6 +33,7 @@ vi.mock("@/lib/common/logger", () => ({
|
||||
|
||||
vi.mock("@/lib/common/utils", () => ({
|
||||
shouldDisplayBasedOnPercentage: vi.fn(),
|
||||
handleHiddenFields: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("@/lib/survey/widget", () => ({
|
||||
@@ -100,10 +101,10 @@ describe("survey/action.ts", () => {
|
||||
filteredSurveys: [mockSurvey],
|
||||
});
|
||||
|
||||
const result = await trackAction("testAction");
|
||||
const result = await trackAction("testAction", undefined);
|
||||
|
||||
expect(result.ok).toBe(true);
|
||||
expect(triggerSurvey).toHaveBeenCalledWith(mockSurvey, "testAction");
|
||||
expect(triggerSurvey).toHaveBeenCalledWith(mockSurvey, "testAction", undefined);
|
||||
});
|
||||
|
||||
test("handles multiple matching surveys", async () => {
|
||||
|
||||
@@ -40,6 +40,7 @@ vi.mock("@/lib/common/utils", () => ({
|
||||
getStyling: vi.fn(),
|
||||
shouldDisplayBasedOnPercentage: vi.fn(),
|
||||
wrapThrowsAsync: vi.fn(),
|
||||
handleHiddenFields: vi.fn(),
|
||||
}));
|
||||
|
||||
describe("widget-file", () => {
|
||||
|
||||
@@ -7,9 +7,11 @@ import {
|
||||
filterSurveys,
|
||||
getLanguageCode,
|
||||
getStyling,
|
||||
handleHiddenFields,
|
||||
shouldDisplayBasedOnPercentage,
|
||||
} from "@/lib/common/utils";
|
||||
import { type TEnvironmentStateSurvey, type TUserState } from "@/types/config";
|
||||
import { type TTrackProperties } from "@/types/survey";
|
||||
|
||||
let isSurveyRunning = false;
|
||||
|
||||
@@ -17,7 +19,11 @@ export const setIsSurveyRunning = (value: boolean): void => {
|
||||
isSurveyRunning = value;
|
||||
};
|
||||
|
||||
export const triggerSurvey = async (survey: TEnvironmentStateSurvey, action?: string): Promise<void> => {
|
||||
export const triggerSurvey = async (
|
||||
survey: TEnvironmentStateSurvey,
|
||||
action?: string,
|
||||
properties?: TTrackProperties
|
||||
): Promise<void> => {
|
||||
const logger = Logger.getInstance();
|
||||
|
||||
// Check if the survey should be displayed based on displayPercentage
|
||||
@@ -29,10 +35,19 @@ export const triggerSurvey = async (survey: TEnvironmentStateSurvey, action?: st
|
||||
}
|
||||
}
|
||||
|
||||
await renderWidget(survey, action);
|
||||
const hiddenFieldsObject: TTrackProperties["hiddenFields"] = handleHiddenFields(
|
||||
survey.hiddenFields,
|
||||
properties?.hiddenFields
|
||||
);
|
||||
|
||||
await renderWidget(survey, action, hiddenFieldsObject);
|
||||
};
|
||||
|
||||
export const renderWidget = async (survey: TEnvironmentStateSurvey, action?: string): Promise<void> => {
|
||||
export const renderWidget = async (
|
||||
survey: TEnvironmentStateSurvey,
|
||||
action?: string,
|
||||
hiddenFieldsObject?: TTrackProperties["hiddenFields"]
|
||||
): Promise<void> => {
|
||||
const logger = Logger.getInstance();
|
||||
const config = Config.getInstance();
|
||||
const timeoutStack = TimeoutStack.getInstance();
|
||||
@@ -87,6 +102,7 @@ export const renderWidget = async (survey: TEnvironmentStateSurvey, action?: str
|
||||
languageCode,
|
||||
placement,
|
||||
styling: getStyling(project, survey),
|
||||
hiddenFieldsRecord: hiddenFieldsObject,
|
||||
onDisplayCreated: () => {
|
||||
const existingDisplays = config.get().user.data.displays;
|
||||
const newDisplay = { surveyId: survey.id, createdAt: new Date() };
|
||||
|
||||
@@ -75,3 +75,7 @@ export type TActionClassNoCodeConfig =
|
||||
rule: TActionClassPageUrlRule;
|
||||
}[];
|
||||
};
|
||||
|
||||
export interface TTrackProperties {
|
||||
hiddenFields: Record<string, string | number | string[]>;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@formbricks/js",
|
||||
"license": "MIT",
|
||||
"version": "4.0.0",
|
||||
"version": "4.1.0",
|
||||
"description": "Formbricks-js allows you to connect your index to Formbricks, display surveys and trigger events.",
|
||||
"homepage": "https://formbricks.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import type Formbricks from "@formbricks/js-core";
|
||||
import { type TFormbricks as TFormbricksCore } from "@formbricks/js-core";
|
||||
import { loadFormbricksToProxy } from "./lib/load-formbricks";
|
||||
|
||||
type TFormbricks = typeof Formbricks;
|
||||
type TFormbricks = Omit<TFormbricksCore, "track"> & {
|
||||
track: (code: string) => Promise<void>;
|
||||
};
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
formbricks: TFormbricks | undefined;
|
||||
@@ -14,7 +17,7 @@ const formbricksProxyHandler: ProxyHandler<TFormbricks> = {
|
||||
},
|
||||
};
|
||||
|
||||
const formbricks: TFormbricks = new Proxy({} as TFormbricks, formbricksProxyHandler);
|
||||
const formbricks: TFormbricksCore = new Proxy({} as TFormbricks, formbricksProxyHandler);
|
||||
|
||||
// eslint-disable-next-line import/no-default-export -- Required for UMD
|
||||
export default formbricks;
|
||||
|
||||
Reference in New Issue
Block a user