diff --git a/apps/playground/package.json b/apps/playground/package.json index 096d1ab..9534f65 100644 --- a/apps/playground/package.json +++ b/apps/playground/package.json @@ -15,7 +15,8 @@ "expo": "54.0.29", "expo-status-bar": "3.0.9", "react": "19.1.0", - "react-native": "0.81.5" + "react-native": "0.81.5", + "react-native-webview": "13.15.0" }, "devDependencies": { "@babel/core": "7.28.5", diff --git a/packages/react-native/src/components/survey-web-view.tsx b/packages/react-native/src/components/survey-web-view.tsx index 27c99c7..eb3746e 100644 --- a/packages/react-native/src/components/survey-web-view.tsx +++ b/packages/react-native/src/components/survey-web-view.tsx @@ -3,15 +3,27 @@ import { RNConfig } from "@/lib/common/config"; import { Logger } from "@/lib/common/logger"; import { filterSurveys, getLanguageCode, getStyling } from "@/lib/common/utils"; import { SurveyStore } from "@/lib/survey/store"; +import type { TOverlay } from "@/types/common"; import { type TUserState, ZJsRNWebViewOnMessageData } from "@/types/config"; import type { TSurvey, SurveyContainerProps } from "@/types/survey"; -import React, { type JSX, useEffect, useRef, useState } from "react"; +import React, { type JSX, useEffect, useMemo, useRef, useState } from "react"; import { KeyboardAvoidingView, Modal, View, StyleSheet } from "react-native"; import { WebView, type WebViewMessageEvent } from "react-native-webview"; const logger = Logger.getInstance(); logger.configure({ logLevel: "debug" }); +const getOverlayBackgroundColor = (overlay: TOverlay): string => { + switch (overlay) { + case "dark": + return "rgba(51, 65, 85, 0.8)"; + case "light": + return "rgba(148, 163, 184, 0.5)"; + case "none": + return "transparent"; + } +}; + const surveyStore = SurveyStore.getInstance(); interface SurveyWebViewProps { @@ -85,6 +97,15 @@ export function SurveyWebView( setShowSurvey(true); }, [props.survey.delay, isSurveyRunning, props.survey.name]); + const overlay = appConfig + ? (props.survey.projectOverwrites?.overlay ?? appConfig.get().environment.data.project.overlay) + : "none"; + + const modalContainerStyle = useMemo( + () => [styles.modalContainer, { backgroundColor: getOverlayBackgroundColor(overlay) }], + [overlay] + ); + if (!appConfig) { return; } @@ -114,8 +135,6 @@ export function SurveyWebView( const clickOutside = props.survey.projectOverwrites?.clickOutsideClose ?? project.clickOutsideClose; - const darkOverlay = - props.survey.projectOverwrites?.darkOverlay ?? project.darkOverlay; return ( - + void ) => undefined, @@ -269,7 +287,6 @@ export function SurveyWebView( const styles = StyleSheet.create({ modalContainer: { flex: 1, - backgroundColor: "rgba(0, 0, 0, 0.5)", }, keyboardAvoidingView: { flex: 1, diff --git a/packages/react-native/src/lib/common/tests/api.test.ts b/packages/react-native/src/lib/common/tests/api.test.ts index 8de1d06..b0332b9 100644 --- a/packages/react-native/src/lib/common/tests/api.test.ts +++ b/packages/react-native/src/lib/common/tests/api.test.ts @@ -262,7 +262,7 @@ describe("api.ts", () => { id: "project123", recontactDays: 30, clickOutsideClose: true, - darkOverlay: false, + overlay: "none", placement: "bottomRight", inAppSurveyBranding: true, styling: { diff --git a/packages/react-native/src/lib/common/tests/utils.test.ts b/packages/react-native/src/lib/common/tests/utils.test.ts index 4a21dd5..9b0d04c 100644 --- a/packages/react-native/src/lib/common/tests/utils.test.ts +++ b/packages/react-native/src/lib/common/tests/utils.test.ts @@ -106,7 +106,7 @@ describe("utils.ts", () => { id: mockProjectId, recontactDays: 7, // fallback if survey doesn't have it clickOutsideClose: false, - darkOverlay: false, + overlay: "none", placement: "bottomRight", inAppSurveyBranding: true, styling: { allowStyleOverwrite: false }, diff --git a/packages/react-native/src/types/common.ts b/packages/react-native/src/types/common.ts new file mode 100644 index 0000000..82f5d8c --- /dev/null +++ b/packages/react-native/src/types/common.ts @@ -0,0 +1 @@ +export type TOverlay = "none" | "light" | "dark"; diff --git a/packages/react-native/src/types/config.ts b/packages/react-native/src/types/config.ts index 24ad47f..fea5cb2 100644 --- a/packages/react-native/src/types/config.ts +++ b/packages/react-native/src/types/config.ts @@ -11,7 +11,7 @@ export type TEnvironmentStateProject = Pick< | "id" | "recontactDays" | "clickOutsideClose" - | "darkOverlay" + | "overlay" | "placement" | "inAppSurveyBranding" > & { diff --git a/packages/react-native/src/types/project.ts b/packages/react-native/src/types/project.ts index 77329a0..6acc4e6 100644 --- a/packages/react-native/src/types/project.ts +++ b/packages/react-native/src/types/project.ts @@ -1,3 +1,4 @@ +import type { TOverlay } from "./common"; import type { TBaseStyling } from "./styling"; export type TProject = { @@ -20,7 +21,7 @@ export type TProject = { }; placement: "topLeft" | "topRight" | "bottomLeft" | "bottomRight"; // assumed from WidgetPlacement clickOutsideClose: boolean; - darkOverlay: boolean; + overlay: TOverlay; logo?: { url?: string; bgColor?: string; diff --git a/packages/react-native/src/types/survey.ts b/packages/react-native/src/types/survey.ts index aebed1a..09d7660 100644 --- a/packages/react-native/src/types/survey.ts +++ b/packages/react-native/src/types/survey.ts @@ -1,5 +1,6 @@ import type { TResponseData, TResponseUpdate } from "@/types/response"; import type { TFileUploadParams, TUploadFileConfig } from "@/types/storage"; +import type { TOverlay } from "./common"; import type { TProjectStyling } from "./project"; export type TJsFileUploadParams = { @@ -45,7 +46,7 @@ export interface SurveyBaseProps { isCardBorderVisible?: boolean; startAtQuestionId?: string; clickOutside?: boolean; - darkOverlay?: boolean; + overlay?: TOverlay; hiddenFieldsRecord?: TResponseData; shouldResetQuestionId?: boolean; fullSizeCards?: boolean; @@ -72,7 +73,7 @@ export interface SurveyContainerProps mode?: "modal" | "inline"; containerId?: string; clickOutside?: boolean; - darkOverlay?: boolean; + overlay?: TOverlay; placement?: "bottomLeft" | "bottomRight" | "topLeft" | "topRight" | "center"; action?: string; singleUseId?: string; @@ -239,7 +240,7 @@ export type TSurvey = { | "center" | null; clickOutsideClose?: boolean | null; - darkOverlay?: boolean | null; + overlay?: TOverlay | null; } | null; languages: { default: boolean; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cda9102..e0dc1f2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -47,6 +47,9 @@ importers: react-native: specifier: 0.81.5 version: 0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0) + react-native-webview: + specifier: 13.15.0 + version: 13.15.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) devDependencies: '@babel/core': specifier: 7.28.5 @@ -4259,6 +4262,7 @@ packages: tar@7.5.2: resolution: {integrity: sha512-7NyxrTE4Anh8km8iEy7o0QYPs+0JKBTj5ZaqHg6B39erLg0qYXN3BijtShwbsNSvQ+LN75+KV+C4QR/f6Gwnpg==} engines: {node: '>=18'} + deprecated: Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me temp-dir@2.0.0: resolution: {integrity: sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==} @@ -6678,7 +6682,7 @@ snapshots: '@typescript-eslint/eslint-plugin': 7.18.0(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.9.3))(eslint@8.57.0)(typescript@5.9.3) '@typescript-eslint/parser': 7.18.0(eslint@8.57.0)(typescript@5.9.3) eslint-config-prettier: 9.1.0(eslint@8.57.0) - eslint-import-resolver-alias: 1.1.2(eslint-plugin-import@2.32.0) + eslint-import-resolver-alias: 1.1.2(eslint-plugin-import@2.32.0(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.0)) eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@8.57.0) eslint-plugin-eslint-comments: 3.2.0(eslint@8.57.0) eslint-plugin-import: 2.32.0(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.0) @@ -7619,7 +7623,7 @@ snapshots: dependencies: eslint: 8.57.0 - eslint-import-resolver-alias@1.1.2(eslint-plugin-import@2.32.0): + eslint-import-resolver-alias@1.1.2(eslint-plugin-import@2.32.0(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.0)): dependencies: eslint-plugin-import: 2.32.0(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.0) @@ -7646,7 +7650,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.1(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.0): + eslint-module-utils@2.12.1(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@8.57.0))(eslint@8.57.0): dependencies: debug: 3.2.7 optionalDependencies: @@ -7674,7 +7678,7 @@ snapshots: doctrine: 2.1.0 eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.1(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.0) + eslint-module-utils: 2.12.1(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@8.57.0))(eslint@8.57.0) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -9461,7 +9465,6 @@ snapshots: invariant: 2.2.4 react: 19.1.0 react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0) - optional: true react-native@0.81.5(@babel/core@7.28.0)(@types/react@19.1.17)(react@19.1.0): dependencies: