From 6ddffa0488b079056757eab17f37d714eabb8a27 Mon Sep 17 00:00:00 2001 From: Saurabh Chavan Date: Mon, 26 Oct 2026 09:55:02 +0530 Subject: [PATCH 1/6] fix: removed unnecessary react native device info lib from BS and related code --- .../bottom-sheet-native/package.json | 1 - .../src/__tests__/BottomSheet.spec.tsx | 4 ---- .../bottom-sheet-native/src/ui/Styles.ts | 24 ------------------- 3 files changed, 29 deletions(-) diff --git a/packages/pluggableWidgets/bottom-sheet-native/package.json b/packages/pluggableWidgets/bottom-sheet-native/package.json index 1c0ddb020..540d47d02 100644 --- a/packages/pluggableWidgets/bottom-sheet-native/package.json +++ b/packages/pluggableWidgets/bottom-sheet-native/package.json @@ -23,7 +23,6 @@ "@mendix/piw-native-utils-internal": "*", "@mendix/piw-utils-internal": "*", "@shopify/flash-list": "2.3.1", - "react-native-device-info": "15.0.2", "react-native-gesture-handler": "2.31.2", "react-native-reanimated": "4.3.1", "react-native-worklets": "0.8.3" diff --git a/packages/pluggableWidgets/bottom-sheet-native/src/__tests__/BottomSheet.spec.tsx b/packages/pluggableWidgets/bottom-sheet-native/src/__tests__/BottomSheet.spec.tsx index 2e00f7a5f..a6108bbf8 100644 --- a/packages/pluggableWidgets/bottom-sheet-native/src/__tests__/BottomSheet.spec.tsx +++ b/packages/pluggableWidgets/bottom-sheet-native/src/__tests__/BottomSheet.spec.tsx @@ -17,10 +17,6 @@ jest.mock("react-native-reanimated", () => { return Reanimated; }); -jest.mock("react-native-device-info", () => ({ - getDeviceId: () => "iPhone10,6" -})); - jest.mock("react-native/Libraries/Utilities/Platform", () => { const Platform = jest.requireActual("react-native/Libraries/Utilities/Platform"); Platform.OS = "ios"; diff --git a/packages/pluggableWidgets/bottom-sheet-native/src/ui/Styles.ts b/packages/pluggableWidgets/bottom-sheet-native/src/ui/Styles.ts index 1b1db34b6..8ea337d9f 100644 --- a/packages/pluggableWidgets/bottom-sheet-native/src/ui/Styles.ts +++ b/packages/pluggableWidgets/bottom-sheet-native/src/ui/Styles.ts @@ -1,5 +1,4 @@ import { TextStyle, ViewStyle } from "react-native"; -import DeviceInfo from "react-native-device-info"; export interface ModalItemsStyle { container?: ModalItemContainerStyle; @@ -19,26 +18,3 @@ export interface BottomSheetStyle { modal: ViewStyle; modalItems: ModalItemsStyle; } - -const isiPhoneModelWithNotch = (): boolean => { - const model = DeviceInfo.getDeviceId(); - if (model.indexOf("iPhone") !== -1) { - switch (model) { - case "iPhone10,6": // iPhone X GSM - case "iPhone11,2": // iPhone XS - case "iPhone11,4": // iPhone XS Max - case "iPhone11,6": // iPhone XS Max Global - case "iPhone11,8": // Iphone XR - case "iPhone12,1": // Iphone 11 - case "iPhone12,3": // Iphone 11 Pro - case "iPhone12,5": // Iphone 11 Pro Max - return true; - default: - return false; - } - } - return false; -}; - -export const defaultPaddings = { paddingBottom: isiPhoneModelWithNotch() ? 24 : 0 }; -export const defaultMargins = { marginBottom: isiPhoneModelWithNotch() ? 24 : 0 }; From c6fb0f702ebcccae7f450e58aa09a1e018f836bd Mon Sep 17 00:00:00 2001 From: Saurabh Chavan Date: Mon, 26 Oct 2026 10:17:59 +0530 Subject: [PATCH 2/6] fix: update gallery widget to use deviceMetrics NATIVE_DEPENDENCY_APPROVED --- packages/pluggableWidgets/gallery-native/package.json | 4 ++-- .../gallery-native/src/components/Gallery.tsx | 5 +++-- .../gallery-native/src/components/__tests__/Gallery.spec.tsx | 4 +++- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/pluggableWidgets/gallery-native/package.json b/packages/pluggableWidgets/gallery-native/package.json index 282da9eb2..bad0c7099 100644 --- a/packages/pluggableWidgets/gallery-native/package.json +++ b/packages/pluggableWidgets/gallery-native/package.json @@ -22,9 +22,9 @@ "test": "pluggable-widgets-tools test:unit:native" }, "dependencies": { + "@mendix/piw-native-utils-internal": "*", "@mendix/piw-utils-internal": "*", - "@shopify/flash-list": "2.3.1", - "react-native-device-info": "15.0.2" + "@shopify/flash-list": "2.3.1" }, "devDependencies": { "@mendix/pluggable-widgets-tools": "*" diff --git a/packages/pluggableWidgets/gallery-native/src/components/Gallery.tsx b/packages/pluggableWidgets/gallery-native/src/components/Gallery.tsx index a26be77a6..08f15959b 100644 --- a/packages/pluggableWidgets/gallery-native/src/components/Gallery.tsx +++ b/packages/pluggableWidgets/gallery-native/src/components/Gallery.tsx @@ -10,7 +10,7 @@ import { useWindowDimensions } from "react-native"; import { ObjectItem, DynamicValue } from "mendix"; -import DeviceInfo from "react-native-device-info"; +import { useDeviceMetrics } from "@mendix/piw-native-utils-internal"; import { GalleryStyle } from "../ui/Styles"; import { HorizontalItemSizingEnum, PaginationEnum, ScrollDirectionEnum } from "../../typings/GalleryProps"; import { isAvailable } from "@mendix/piw-utils-internal"; @@ -39,8 +39,9 @@ export interface GalleryProps { } export const Gallery = (props: GalleryProps): ReactElement => { + const { isTablet } = useDeviceMetrics(); const isScrollDirectionVertical = props.scrollDirection === "vertical"; - const numColumns = DeviceInfo.isTablet() ? props.tabletColumns : props.phoneColumns; + const numColumns = isTablet ? props.tabletColumns : props.phoneColumns; const firstItemId = props.items?.[0]?.id; const lastItemId = props.items?.[props.items.length - 1]?.id; const { name, style, itemRenderer } = props; diff --git a/packages/pluggableWidgets/gallery-native/src/components/__tests__/Gallery.spec.tsx b/packages/pluggableWidgets/gallery-native/src/components/__tests__/Gallery.spec.tsx index 59901208f..ea4615c1a 100644 --- a/packages/pluggableWidgets/gallery-native/src/components/__tests__/Gallery.spec.tsx +++ b/packages/pluggableWidgets/gallery-native/src/components/__tests__/Gallery.spec.tsx @@ -3,7 +3,9 @@ import { ObjectItem, GUID, DynamicValue, ValueStatus } from "mendix"; import { render, fireEvent, act } from "@testing-library/react-native"; import { Gallery, GalleryProps } from "../Gallery"; -jest.mock("react-native-device-info", () => ({ isTablet: jest.fn().mockReturnValue(false) })); +jest.mock("@mendix/piw-native-utils-internal", () => ({ + useDeviceMetrics: () => ({ isTablet: false }) +})); jest.mock("@shopify/flash-list"); // Mocking FlashList API because it causes issues with the test renderer const itemWrapperFunction = From 042210df30dbe439afdcda88f9a46a7de50547a0 Mon Sep 17 00:00:00 2001 From: Saurabh Chavan Date: Mon, 26 Oct 2026 10:40:40 +0530 Subject: [PATCH 3/6] fix: update introscreen to use deviceMetrics NATIVE_DEPENDENCY_APPROVED --- .../intro-screen-native/package.json | 3 +-- .../intro-screen-native/src/IntroScreen.tsx | 8 +++---- .../src/__tests__/IntroScreen.notch.spec.tsx | 5 ++-- .../src/__tests__/IntroScreen.spec.tsx | 10 ++++---- .../intro-screen-native/src/ui/Styles.ts | 24 +------------------ 5 files changed, 12 insertions(+), 38 deletions(-) diff --git a/packages/pluggableWidgets/intro-screen-native/package.json b/packages/pluggableWidgets/intro-screen-native/package.json index a9ee26879..0796dcc2f 100644 --- a/packages/pluggableWidgets/intro-screen-native/package.json +++ b/packages/pluggableWidgets/intro-screen-native/package.json @@ -22,8 +22,7 @@ "@mendix/piw-native-utils-internal": "*", "@mendix/piw-utils-internal": "*", "@react-native-async-storage/async-storage": "2.2.0", - "@shopify/flash-list": "2.3.1", - "react-native-device-info": "15.0.2" + "@shopify/flash-list": "2.3.1" }, "devDependencies": { "@mendix/pluggable-widgets-tools": "*" diff --git a/packages/pluggableWidgets/intro-screen-native/src/IntroScreen.tsx b/packages/pluggableWidgets/intro-screen-native/src/IntroScreen.tsx index e861673a3..6cb16760e 100644 --- a/packages/pluggableWidgets/intro-screen-native/src/IntroScreen.tsx +++ b/packages/pluggableWidgets/intro-screen-native/src/IntroScreen.tsx @@ -7,14 +7,14 @@ import { SwipeableContainer } from "./SwipeableContainer"; import AsyncStorage from "@react-native-async-storage/async-storage"; import deepmerge from "deepmerge"; import { executeAction } from "@mendix/piw-utils-internal"; +import { useDeviceMetrics } from "@mendix/piw-native-utils-internal"; export function IntroScreen(props: IntroScreenProps): JSX.Element { const [visible, setVisible] = useState(false); + const { hasNotch } = useDeviceMetrics(); + const notchStyle = hasNotch ? { paginationContainer: { bottom: 22 } } : {}; const customStyles = props.style ? props.style.filter(o => o != null) : []; - const styles = - customStyles.length > 0 - ? deepmerge.all([defaultWelcomeScreenStyle, ...customStyles]) - : defaultWelcomeScreenStyle; + const styles = deepmerge.all([defaultWelcomeScreenStyle, notchStyle, ...customStyles]); useEffect(() => { let isMounted = true; diff --git a/packages/pluggableWidgets/intro-screen-native/src/__tests__/IntroScreen.notch.spec.tsx b/packages/pluggableWidgets/intro-screen-native/src/__tests__/IntroScreen.notch.spec.tsx index 4c10dee4d..5486cfdbd 100644 --- a/packages/pluggableWidgets/intro-screen-native/src/__tests__/IntroScreen.notch.spec.tsx +++ b/packages/pluggableWidgets/intro-screen-native/src/__tests__/IntroScreen.notch.spec.tsx @@ -6,9 +6,8 @@ import { View } from "react-native"; import { EditableValueBuilder } from "@mendix/piw-utils-internal"; import { Big } from "big.js"; -jest.mock("react-native-device-info", () => ({ - hasNotch: jest.fn().mockReturnValue(true), - getDeviceId: jest.fn().mockReturnValue("iPhone10,6") +jest.mock("@mendix/piw-native-utils-internal", () => ({ + useDeviceMetrics: () => ({ hasNotch: true }) })); jest.mock("@react-native-async-storage/async-storage", () => ({ diff --git a/packages/pluggableWidgets/intro-screen-native/src/__tests__/IntroScreen.spec.tsx b/packages/pluggableWidgets/intro-screen-native/src/__tests__/IntroScreen.spec.tsx index 643f80d43..28e05a08b 100644 --- a/packages/pluggableWidgets/intro-screen-native/src/__tests__/IntroScreen.spec.tsx +++ b/packages/pluggableWidgets/intro-screen-native/src/__tests__/IntroScreen.spec.tsx @@ -6,9 +6,8 @@ import { View } from "react-native"; import { EditableValueBuilder } from "@mendix/piw-utils-internal"; import { Big } from "big.js"; -jest.mock("react-native-device-info", () => ({ - hasNotch: jest.fn(), - getDeviceId: jest.fn().mockReturnValue("") +jest.mock("@mendix/piw-native-utils-internal", () => ({ + useDeviceMetrics: () => ({ hasNotch: false }) })); jest.mock("@react-native-async-storage/async-storage", () => ({ @@ -42,9 +41,8 @@ describe("Intro Screen", () => { identifier: "" }; - jest.mock("react-native-device-info", () => ({ - hasNotch: jest.fn(), - getDeviceId: jest.fn().mockReturnValue("iPhone") + jest.mock("@mendix/piw-native-utils-internal", () => ({ + useDeviceMetrics: () => ({ hasNotch: false }) })); }); diff --git a/packages/pluggableWidgets/intro-screen-native/src/ui/Styles.ts b/packages/pluggableWidgets/intro-screen-native/src/ui/Styles.ts index 4f81457f7..6581d3d41 100644 --- a/packages/pluggableWidgets/intro-screen-native/src/ui/Styles.ts +++ b/packages/pluggableWidgets/intro-screen-native/src/ui/Styles.ts @@ -1,30 +1,8 @@ import { Style } from "@mendix/piw-native-utils-internal"; import { I18nManager, Platform, StyleSheet, TextStyle, ViewStyle } from "react-native"; import absoluteFillObject = StyleSheet.absoluteFillObject; -import DeviceInfo from "react-native-device-info"; - -const isiPhoneModelWithNotch = (): boolean => { - const model = DeviceInfo.getDeviceId(); - if (model.indexOf("iPhone") !== -1) { - switch (model) { - case "iPhone10,6": // iPhone X GSM - case "iPhone11,2": // iPhone XS - case "iPhone11,4": // iPhone XS Max - case "iPhone11,6": // iPhone XS Max Global - case "iPhone11,8": // Iphone XR - case "iPhone12,1": // Iphone 11 - case "iPhone12,3": // Iphone 11 Pro - case "iPhone12,5": // Iphone 11 Pro Max - return true; - default: - return false; - } - } - return false; -}; const isAndroidRTL = I18nManager.isRTL && Platform.OS === "android"; -const isIphoneWithNotch = DeviceInfo.hasNotch() || (Platform.OS === "ios" && isiPhoneModelWithNotch()); const defaultButtonBetweenContainer: ViewStyle = { flexDirection: "row", @@ -91,7 +69,7 @@ export const defaultWelcomeScreenStyle: IntroScreenStyle = { }, paginationContainer: { position: "absolute", - bottom: isIphoneWithNotch ? 22 : 0, + bottom: 0, left: 0, right: 0, justifyContent: "space-between", From 3f00b247e7ed7f4d2255f105e4196e9ea6d5bb31 Mon Sep 17 00:00:00 2001 From: Saurabh Chavan Date: Mon, 26 Oct 2026 10:43:58 +0530 Subject: [PATCH 4/6] fix: added deviceMetrics to utils --- .../tools/piw-native-utils-internal/package.json | 3 +++ .../src/deviceMetrics.ts | 15 +++++++++++++++ .../tools/piw-native-utils-internal/src/index.ts | 1 + pnpm-lock.yaml | 16 +++++++--------- 4 files changed, 26 insertions(+), 9 deletions(-) create mode 100644 packages/tools/piw-native-utils-internal/src/deviceMetrics.ts diff --git a/packages/tools/piw-native-utils-internal/package.json b/packages/tools/piw-native-utils-internal/package.json index a8b4636aa..01450a0d7 100644 --- a/packages/tools/piw-native-utils-internal/package.json +++ b/packages/tools/piw-native-utils-internal/package.json @@ -24,6 +24,9 @@ "prepare": "pnpm build", "postinstall": "pnpm build" }, + "dependencies": { + "react-native-device-info": "15.0.2" + }, "devDependencies": { "@mendix/pluggable-widgets-tools": "*", "rimraf": "^6.1.2" diff --git a/packages/tools/piw-native-utils-internal/src/deviceMetrics.ts b/packages/tools/piw-native-utils-internal/src/deviceMetrics.ts new file mode 100644 index 000000000..041f1e2d0 --- /dev/null +++ b/packages/tools/piw-native-utils-internal/src/deviceMetrics.ts @@ -0,0 +1,15 @@ +import DeviceInfo from "react-native-device-info"; + +export interface DeviceMetrics { + hasNotch: boolean; + isTablet: boolean; + deviceId: string; +} + +export function useDeviceMetrics(): DeviceMetrics { + return { + hasNotch: DeviceInfo.hasNotch(), + isTablet: DeviceInfo.isTablet(), + deviceId: DeviceInfo.getDeviceId() + }; +} diff --git a/packages/tools/piw-native-utils-internal/src/index.ts b/packages/tools/piw-native-utils-internal/src/index.ts index e8e614b80..5493037df 100644 --- a/packages/tools/piw-native-utils-internal/src/index.ts +++ b/packages/tools/piw-native-utils-internal/src/index.ts @@ -4,3 +4,4 @@ export { available, toNumber, unavailable } from "./properties"; export { Slider, SliderProps, RangeSlider, RangeSliderProps, RangeSliderValue } from "./components/Slider"; export { Bar, BarProps } from "./components/Bar"; export { Arc, ArcProps, makeArcPath } from "./components/Arc"; +export { useDeviceMetrics, DeviceMetrics } from "./deviceMetrics"; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5068430d2..f4bcb5eed 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -373,9 +373,6 @@ importers: '@shopify/flash-list': specifier: 2.3.1 version: 2.3.1(@babel/runtime@7.29.7)(react-native@0.84.1(@babel/core@7.29.7)(@react-native/metro-config@0.86.1(@babel/core@7.29.7))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3) - react-native-device-info: - specifier: 15.0.2 - version: 15.0.2(react-native@0.84.1(@babel/core@7.29.7)(@react-native/metro-config@0.86.1(@babel/core@7.29.7))(@types/react@19.2.17)(react@19.2.3)) react-native-gesture-handler: specifier: 2.31.2 version: 2.31.2(patch_hash=5be6c2cce8be6bd8afdbbf6c289d8b210686eafce0b41bf8cae2c29388aaa3b8)(react-native@0.84.1(@babel/core@7.29.7)(@react-native/metro-config@0.86.1(@babel/core@7.29.7))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3) @@ -484,15 +481,15 @@ importers: packages/pluggableWidgets/gallery-native: dependencies: + '@mendix/piw-native-utils-internal': + specifier: '*' + version: link:../../tools/piw-native-utils-internal '@mendix/piw-utils-internal': specifier: '*' version: link:../../tools/piw-utils-internal '@shopify/flash-list': specifier: 2.3.1 version: 2.3.1(@babel/runtime@7.29.7)(react-native@0.84.1(@babel/core@7.29.7)(@react-native/metro-config@0.86.1(@babel/core@7.29.7))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3) - react-native-device-info: - specifier: 15.0.2 - version: 15.0.2(react-native@0.84.1(@babel/core@7.29.7)(@react-native/metro-config@0.86.1(@babel/core@7.29.7))(@types/react@19.2.17)(react@19.2.3)) devDependencies: '@mendix/pluggable-widgets-tools': specifier: 11.12.0 @@ -553,9 +550,6 @@ importers: '@shopify/flash-list': specifier: 2.3.1 version: 2.3.1(@babel/runtime@7.29.7)(react-native@0.84.1(@babel/core@7.29.7)(@react-native/metro-config@0.86.1(@babel/core@7.29.7))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3) - react-native-device-info: - specifier: 15.0.2 - version: 15.0.2(react-native@0.84.1(@babel/core@7.29.7)(@react-native/metro-config@0.86.1(@babel/core@7.29.7))(@types/react@19.2.17)(react@19.2.3)) devDependencies: '@mendix/pluggable-widgets-tools': specifier: 11.12.0 @@ -894,6 +888,10 @@ importers: version: 11.12.0(patch_hash=c3d32a9026cacb93268037356ae25d0e56d1b9b3ecb22aea09cbf1c69542670e)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@types/babel__core@7.20.5)(@types/node@20.19.43)(jest-util@30.4.1)(jiti@2.6.1)(picomatch@4.0.5)(react-dom@19.2.3(react@19.2.3))(react-native@0.84.1(@babel/core@7.29.7)(@react-native/metro-config@0.86.1(@babel/core@7.29.7))(@types/react@19.2.17)(react@19.2.3))(react@19.2.3)(tslib@2.8.1) packages/tools/piw-native-utils-internal: + dependencies: + react-native-device-info: + specifier: 15.0.2 + version: 15.0.2(react-native@0.84.1(@babel/core@7.29.7)(@react-native/metro-config@0.86.1(@babel/core@7.29.7))(@types/react@19.2.17)(react@19.2.3)) devDependencies: '@mendix/pluggable-widgets-tools': specifier: 11.12.0 From 8d5ed261fc02cddaf93940239568dc9ad99d0251 Mon Sep 17 00:00:00 2001 From: Saurabh Chavan Date: Mon, 26 Oct 2026 11:40:16 +0530 Subject: [PATCH 5/6] fix: added devicemetric to gallery utils and update the imports --- .../gallery-native/src/components/Gallery.tsx | 2 +- .../src/components/__tests__/Gallery.spec.tsx | 2 +- .../gallery-native/src/utils/deviceMetrics.ts | 33 +++++++++++++++++++ .../intro-screen-native/src/IntroScreen.tsx | 2 +- .../src/__tests__/IntroScreen.notch.spec.tsx | 2 +- .../src/__tests__/IntroScreen.spec.tsx | 2 +- .../src/deviceMetrics.ts | 15 --------- .../piw-native-utils-internal/src/index.ts | 1 - 8 files changed, 38 insertions(+), 21 deletions(-) create mode 100644 packages/pluggableWidgets/gallery-native/src/utils/deviceMetrics.ts delete mode 100644 packages/tools/piw-native-utils-internal/src/deviceMetrics.ts diff --git a/packages/pluggableWidgets/gallery-native/src/components/Gallery.tsx b/packages/pluggableWidgets/gallery-native/src/components/Gallery.tsx index 08f15959b..d58fcf6a7 100644 --- a/packages/pluggableWidgets/gallery-native/src/components/Gallery.tsx +++ b/packages/pluggableWidgets/gallery-native/src/components/Gallery.tsx @@ -10,7 +10,7 @@ import { useWindowDimensions } from "react-native"; import { ObjectItem, DynamicValue } from "mendix"; -import { useDeviceMetrics } from "@mendix/piw-native-utils-internal"; +import { useDeviceMetrics } from "../utils/deviceMetrics"; import { GalleryStyle } from "../ui/Styles"; import { HorizontalItemSizingEnum, PaginationEnum, ScrollDirectionEnum } from "../../typings/GalleryProps"; import { isAvailable } from "@mendix/piw-utils-internal"; diff --git a/packages/pluggableWidgets/gallery-native/src/components/__tests__/Gallery.spec.tsx b/packages/pluggableWidgets/gallery-native/src/components/__tests__/Gallery.spec.tsx index ea4615c1a..224344416 100644 --- a/packages/pluggableWidgets/gallery-native/src/components/__tests__/Gallery.spec.tsx +++ b/packages/pluggableWidgets/gallery-native/src/components/__tests__/Gallery.spec.tsx @@ -3,7 +3,7 @@ import { ObjectItem, GUID, DynamicValue, ValueStatus } from "mendix"; import { render, fireEvent, act } from "@testing-library/react-native"; import { Gallery, GalleryProps } from "../Gallery"; -jest.mock("@mendix/piw-native-utils-internal", () => ({ +jest.mock("../../utils/deviceMetrics", () => ({ useDeviceMetrics: () => ({ isTablet: false }) })); jest.mock("@shopify/flash-list"); // Mocking FlashList API because it causes issues with the test renderer diff --git a/packages/pluggableWidgets/gallery-native/src/utils/deviceMetrics.ts b/packages/pluggableWidgets/gallery-native/src/utils/deviceMetrics.ts new file mode 100644 index 000000000..73039612b --- /dev/null +++ b/packages/pluggableWidgets/gallery-native/src/utils/deviceMetrics.ts @@ -0,0 +1,33 @@ +import DeviceInfo from "react-native-device-info"; + +/** + * Device metrics interface returned by useDeviceMetrics hook + */ +export interface DeviceMetrics { + /** Whether the device has a notch (display cutout) */ + hasNotch: boolean; + /** Whether the device is a tablet */ + isTablet: boolean; + /** Device model identifier (e.g., "iPhone14,2") */ + deviceId: string; +} + +/** + * Hook providing device-specific metrics for layout decisions. + * Wraps react-native-device-info to centralize device detection logic. + * + * @returns Device metrics including notch presence, tablet detection, and device ID + * @example + * ```tsx + * const { hasNotch, isTablet } = useDeviceMetrics(); + * const columns = isTablet ? 4 : 2; + * const bottomPadding = hasNotch ? 22 : 0; + * ``` + */ +export function useDeviceMetrics(): DeviceMetrics { + return { + hasNotch: DeviceInfo.hasNotch(), + isTablet: DeviceInfo.isTablet(), + deviceId: DeviceInfo.getDeviceId() + }; +} diff --git a/packages/pluggableWidgets/intro-screen-native/src/IntroScreen.tsx b/packages/pluggableWidgets/intro-screen-native/src/IntroScreen.tsx index 6cb16760e..7ec08f8ae 100644 --- a/packages/pluggableWidgets/intro-screen-native/src/IntroScreen.tsx +++ b/packages/pluggableWidgets/intro-screen-native/src/IntroScreen.tsx @@ -7,7 +7,7 @@ import { SwipeableContainer } from "./SwipeableContainer"; import AsyncStorage from "@react-native-async-storage/async-storage"; import deepmerge from "deepmerge"; import { executeAction } from "@mendix/piw-utils-internal"; -import { useDeviceMetrics } from "@mendix/piw-native-utils-internal"; +import { useDeviceMetrics } from "../../gallery-native/src/utils/deviceMetrics"; export function IntroScreen(props: IntroScreenProps): JSX.Element { const [visible, setVisible] = useState(false); diff --git a/packages/pluggableWidgets/intro-screen-native/src/__tests__/IntroScreen.notch.spec.tsx b/packages/pluggableWidgets/intro-screen-native/src/__tests__/IntroScreen.notch.spec.tsx index 5486cfdbd..09d3a860c 100644 --- a/packages/pluggableWidgets/intro-screen-native/src/__tests__/IntroScreen.notch.spec.tsx +++ b/packages/pluggableWidgets/intro-screen-native/src/__tests__/IntroScreen.notch.spec.tsx @@ -6,7 +6,7 @@ import { View } from "react-native"; import { EditableValueBuilder } from "@mendix/piw-utils-internal"; import { Big } from "big.js"; -jest.mock("@mendix/piw-native-utils-internal", () => ({ +jest.mock("../../../gallery-native/src/utils/deviceMetrics", () => ({ useDeviceMetrics: () => ({ hasNotch: true }) })); diff --git a/packages/pluggableWidgets/intro-screen-native/src/__tests__/IntroScreen.spec.tsx b/packages/pluggableWidgets/intro-screen-native/src/__tests__/IntroScreen.spec.tsx index 28e05a08b..32a49c703 100644 --- a/packages/pluggableWidgets/intro-screen-native/src/__tests__/IntroScreen.spec.tsx +++ b/packages/pluggableWidgets/intro-screen-native/src/__tests__/IntroScreen.spec.tsx @@ -6,7 +6,7 @@ import { View } from "react-native"; import { EditableValueBuilder } from "@mendix/piw-utils-internal"; import { Big } from "big.js"; -jest.mock("@mendix/piw-native-utils-internal", () => ({ +jest.mock("../../../gallery-native/src/utils/deviceMetrics", () => ({ useDeviceMetrics: () => ({ hasNotch: false }) })); diff --git a/packages/tools/piw-native-utils-internal/src/deviceMetrics.ts b/packages/tools/piw-native-utils-internal/src/deviceMetrics.ts deleted file mode 100644 index 041f1e2d0..000000000 --- a/packages/tools/piw-native-utils-internal/src/deviceMetrics.ts +++ /dev/null @@ -1,15 +0,0 @@ -import DeviceInfo from "react-native-device-info"; - -export interface DeviceMetrics { - hasNotch: boolean; - isTablet: boolean; - deviceId: string; -} - -export function useDeviceMetrics(): DeviceMetrics { - return { - hasNotch: DeviceInfo.hasNotch(), - isTablet: DeviceInfo.isTablet(), - deviceId: DeviceInfo.getDeviceId() - }; -} diff --git a/packages/tools/piw-native-utils-internal/src/index.ts b/packages/tools/piw-native-utils-internal/src/index.ts index 5493037df..e8e614b80 100644 --- a/packages/tools/piw-native-utils-internal/src/index.ts +++ b/packages/tools/piw-native-utils-internal/src/index.ts @@ -4,4 +4,3 @@ export { available, toNumber, unavailable } from "./properties"; export { Slider, SliderProps, RangeSlider, RangeSliderProps, RangeSliderValue } from "./components/Slider"; export { Bar, BarProps } from "./components/Bar"; export { Arc, ArcProps, makeArcPath } from "./components/Arc"; -export { useDeviceMetrics, DeviceMetrics } from "./deviceMetrics"; From b20b02c2fccef79b8459627cb4133578fa400fed Mon Sep 17 00:00:00 2001 From: Saurabh Chavan Date: Mon, 26 Oct 2026 16:12:41 +0530 Subject: [PATCH 6/6] fix: moved the deviceMetrics to piw tools --- .../gallery-native/src/components/Gallery.tsx | 2 +- .../src/components/__tests__/Gallery.spec.tsx | 2 +- .../gallery-native/src/utils/deviceMetrics.ts | 33 ------------------- .../intro-screen-native/src/IntroScreen.tsx | 2 +- .../src/__tests__/IntroScreen.notch.spec.tsx | 2 +- .../src/__tests__/IntroScreen.spec.tsx | 2 +- .../src/deviceMetrics.ts | 17 ++++++++++ .../piw-native-utils-internal/src/index.ts | 1 + 8 files changed, 23 insertions(+), 38 deletions(-) delete mode 100644 packages/pluggableWidgets/gallery-native/src/utils/deviceMetrics.ts create mode 100644 packages/tools/piw-native-utils-internal/src/deviceMetrics.ts diff --git a/packages/pluggableWidgets/gallery-native/src/components/Gallery.tsx b/packages/pluggableWidgets/gallery-native/src/components/Gallery.tsx index d58fcf6a7..08f15959b 100644 --- a/packages/pluggableWidgets/gallery-native/src/components/Gallery.tsx +++ b/packages/pluggableWidgets/gallery-native/src/components/Gallery.tsx @@ -10,7 +10,7 @@ import { useWindowDimensions } from "react-native"; import { ObjectItem, DynamicValue } from "mendix"; -import { useDeviceMetrics } from "../utils/deviceMetrics"; +import { useDeviceMetrics } from "@mendix/piw-native-utils-internal"; import { GalleryStyle } from "../ui/Styles"; import { HorizontalItemSizingEnum, PaginationEnum, ScrollDirectionEnum } from "../../typings/GalleryProps"; import { isAvailable } from "@mendix/piw-utils-internal"; diff --git a/packages/pluggableWidgets/gallery-native/src/components/__tests__/Gallery.spec.tsx b/packages/pluggableWidgets/gallery-native/src/components/__tests__/Gallery.spec.tsx index 224344416..ea4615c1a 100644 --- a/packages/pluggableWidgets/gallery-native/src/components/__tests__/Gallery.spec.tsx +++ b/packages/pluggableWidgets/gallery-native/src/components/__tests__/Gallery.spec.tsx @@ -3,7 +3,7 @@ import { ObjectItem, GUID, DynamicValue, ValueStatus } from "mendix"; import { render, fireEvent, act } from "@testing-library/react-native"; import { Gallery, GalleryProps } from "../Gallery"; -jest.mock("../../utils/deviceMetrics", () => ({ +jest.mock("@mendix/piw-native-utils-internal", () => ({ useDeviceMetrics: () => ({ isTablet: false }) })); jest.mock("@shopify/flash-list"); // Mocking FlashList API because it causes issues with the test renderer diff --git a/packages/pluggableWidgets/gallery-native/src/utils/deviceMetrics.ts b/packages/pluggableWidgets/gallery-native/src/utils/deviceMetrics.ts deleted file mode 100644 index 73039612b..000000000 --- a/packages/pluggableWidgets/gallery-native/src/utils/deviceMetrics.ts +++ /dev/null @@ -1,33 +0,0 @@ -import DeviceInfo from "react-native-device-info"; - -/** - * Device metrics interface returned by useDeviceMetrics hook - */ -export interface DeviceMetrics { - /** Whether the device has a notch (display cutout) */ - hasNotch: boolean; - /** Whether the device is a tablet */ - isTablet: boolean; - /** Device model identifier (e.g., "iPhone14,2") */ - deviceId: string; -} - -/** - * Hook providing device-specific metrics for layout decisions. - * Wraps react-native-device-info to centralize device detection logic. - * - * @returns Device metrics including notch presence, tablet detection, and device ID - * @example - * ```tsx - * const { hasNotch, isTablet } = useDeviceMetrics(); - * const columns = isTablet ? 4 : 2; - * const bottomPadding = hasNotch ? 22 : 0; - * ``` - */ -export function useDeviceMetrics(): DeviceMetrics { - return { - hasNotch: DeviceInfo.hasNotch(), - isTablet: DeviceInfo.isTablet(), - deviceId: DeviceInfo.getDeviceId() - }; -} diff --git a/packages/pluggableWidgets/intro-screen-native/src/IntroScreen.tsx b/packages/pluggableWidgets/intro-screen-native/src/IntroScreen.tsx index 7ec08f8ae..6cb16760e 100644 --- a/packages/pluggableWidgets/intro-screen-native/src/IntroScreen.tsx +++ b/packages/pluggableWidgets/intro-screen-native/src/IntroScreen.tsx @@ -7,7 +7,7 @@ import { SwipeableContainer } from "./SwipeableContainer"; import AsyncStorage from "@react-native-async-storage/async-storage"; import deepmerge from "deepmerge"; import { executeAction } from "@mendix/piw-utils-internal"; -import { useDeviceMetrics } from "../../gallery-native/src/utils/deviceMetrics"; +import { useDeviceMetrics } from "@mendix/piw-native-utils-internal"; export function IntroScreen(props: IntroScreenProps): JSX.Element { const [visible, setVisible] = useState(false); diff --git a/packages/pluggableWidgets/intro-screen-native/src/__tests__/IntroScreen.notch.spec.tsx b/packages/pluggableWidgets/intro-screen-native/src/__tests__/IntroScreen.notch.spec.tsx index 09d3a860c..5486cfdbd 100644 --- a/packages/pluggableWidgets/intro-screen-native/src/__tests__/IntroScreen.notch.spec.tsx +++ b/packages/pluggableWidgets/intro-screen-native/src/__tests__/IntroScreen.notch.spec.tsx @@ -6,7 +6,7 @@ import { View } from "react-native"; import { EditableValueBuilder } from "@mendix/piw-utils-internal"; import { Big } from "big.js"; -jest.mock("../../../gallery-native/src/utils/deviceMetrics", () => ({ +jest.mock("@mendix/piw-native-utils-internal", () => ({ useDeviceMetrics: () => ({ hasNotch: true }) })); diff --git a/packages/pluggableWidgets/intro-screen-native/src/__tests__/IntroScreen.spec.tsx b/packages/pluggableWidgets/intro-screen-native/src/__tests__/IntroScreen.spec.tsx index 32a49c703..28e05a08b 100644 --- a/packages/pluggableWidgets/intro-screen-native/src/__tests__/IntroScreen.spec.tsx +++ b/packages/pluggableWidgets/intro-screen-native/src/__tests__/IntroScreen.spec.tsx @@ -6,7 +6,7 @@ import { View } from "react-native"; import { EditableValueBuilder } from "@mendix/piw-utils-internal"; import { Big } from "big.js"; -jest.mock("../../../gallery-native/src/utils/deviceMetrics", () => ({ +jest.mock("@mendix/piw-native-utils-internal", () => ({ useDeviceMetrics: () => ({ hasNotch: false }) })); diff --git a/packages/tools/piw-native-utils-internal/src/deviceMetrics.ts b/packages/tools/piw-native-utils-internal/src/deviceMetrics.ts new file mode 100644 index 000000000..16089bb69 --- /dev/null +++ b/packages/tools/piw-native-utils-internal/src/deviceMetrics.ts @@ -0,0 +1,17 @@ +export interface DeviceMetrics { + hasNotch: boolean; + isTablet: boolean; + deviceId: string; +} + +export function useDeviceMetrics(): DeviceMetrics { + // Lazy import - only loads react-native-device-info when this function is called + // This prevents the module from being bundled during widget build + const DeviceInfo = require("react-native-device-info").default; + + return { + hasNotch: DeviceInfo.hasNotch(), + isTablet: DeviceInfo.isTablet(), + deviceId: DeviceInfo.getDeviceId() + }; +} diff --git a/packages/tools/piw-native-utils-internal/src/index.ts b/packages/tools/piw-native-utils-internal/src/index.ts index e8e614b80..5493037df 100644 --- a/packages/tools/piw-native-utils-internal/src/index.ts +++ b/packages/tools/piw-native-utils-internal/src/index.ts @@ -4,3 +4,4 @@ export { available, toNumber, unavailable } from "./properties"; export { Slider, SliderProps, RangeSlider, RangeSliderProps, RangeSliderValue } from "./components/Slider"; export { Bar, BarProps } from "./components/Bar"; export { Arc, ArcProps, makeArcPath } from "./components/Arc"; +export { useDeviceMetrics, DeviceMetrics } from "./deviceMetrics";