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
17 changes: 9 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@
"private": true,
"packageManager": "[email protected]",
"devDependencies": {
"@babel/core": "^7.22.5",
"@babel/core": "^7.26.0",
"@babel/preset-env": "^7.22.5",
"@babel/preset-react": "^7.22.5",
"@babel/preset-typescript": "^7.22.5",
"@react-native-community/eslint-config": "^3.2.0",
"@testing-library/react-native": "^12.4.3",
"@types/react": "~18.2.79",
"@types/react-dom": "~18.2.25",
"@testing-library/react-native": "^13.2.0",
"@types/react": "~19.0.0",
"@types/react-dom": "~19.0.0",
"@typescript-eslint/eslint-plugin": "^5.60.1",
"@typescript-eslint/parser": "^5.60.1",
"@typescript-eslint/typescript-estree": "^5.61.0",
Expand All @@ -32,20 +32,21 @@
"metro-react-native-babel-preset": "^0.76.7",
"patch-package": "^8.0.0",
"prettier": "^2.8.8",
"react-test-renderer": "19.0.0",
"style-loader": "^3.3.3",
"ts-loader": "^9.4.4",
"typescript": "~5.3.3",
"typescript": "~5.8.3",
"webpack": "^5.88.1",
"webpack-cli": "^5.1.4",
"webpack-dev-server": "^4.15.1"
},
"dependencies": {
"@react-native-tvos/config-tv": "^0.0.4",
"@react-navigation/bottom-tabs": "^6.5.11",
"react": "18.2.0",
"react-dom": "18.2.0",
"react": "19.0.0",
"react-dom": "19.0.0",
"react-native-modal": "^13.0.1",
"react-native-web": "^0.19.6"
"react-native-web": "^0.20.0"
},
"resolutions": {
"@typescript-eslint/typescript-estree": "5.61.0"
Expand Down
13 changes: 1 addition & 12 deletions packages/example/babel.jest.config.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,7 @@
// This is only used by jest
module.exports = {
sourceMaps: 'inline',
presets: [
'module:metro-react-native-babel-preset',
'@babel/preset-env',
[
'@babel/preset-react',
{
runtime: 'automatic',
},
],
'@babel/preset-typescript',
],
presets: ['module:@react-native/babel-preset'],
plugins: [
[
'module-resolver',
Expand All @@ -21,6 +11,5 @@ module.exports = {
},
},
],
['@babel/plugin-proposal-class-properties', { loose: false }],
],
};
2 changes: 1 addition & 1 deletion packages/example/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const packagesToTransform = [

/** @type {import('@jest/types').Config.InitialOptions} */
const config = {
preset: '@testing-library/react-native',
preset: 'react-native',
/*
* What the preset provides:
* - a transformer to handle media assets (png, video)
Expand Down
21 changes: 10 additions & 11 deletions packages/example/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,29 +15,28 @@
"@bam.tech/react-native-keyevent-expo-config-plugin": "^1.0.52",
"@emotion/native": "^11.11.0",
"@emotion/react": "^11.11.3",
"@expo/metro-runtime": "~3.2.1",
"@expo/metro-runtime": "~5.0.4",
"@react-navigation/native": "^6.1.9",
"@react-navigation/native-stack": "^6.9.17",
"@types/jest": "^29.5.12",
"@types/react-test-renderer": "^18.0.7",
"babel-jest": "^29.7.0",
"expo": "~51.0.9",
"expo-status-bar": "~1.12.1",
"expo": "~53.0.9",
"expo-status-bar": "~2.2.3",
"jest": "^29.7.0",
"jest-environment-jsdom": "^29.7.0",
"jest-watch-typeahead": "^2.2.2",
"lucide-react-native": "^0.335.0",
"react": "18.2.0",
"react-native": "npm:react-native-tvos@0.74.1-0",
"react": "19.0.0",
"react-native": "npm:react-native-tvos@0.79.1-1",
"react-native-keyevent": "^0.3.2",
"react-native-safe-area-context": "4.10.1",
"react-native-screens": "3.31.1",
"react-native-svg": "15.2.0",
"react-test-renderer": "^18.2.0",
"typescript": "^5.6.2"
"react-native-safe-area-context": "5.4.0",
"react-native-screens": "~4.10.0",
"react-native-svg": "15.11.2",
"typescript": "~5.8.3"
},
"devDependencies": {
"@babel/core": "^7.24.0",
"@babel/core": "^7.26.0",
"babel-plugin-module-resolver": "^5.0.0"
},
"private": true,
Expand Down
2 changes: 0 additions & 2 deletions packages/example/src/testing/jest-setupAfterEnv.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import '@testing-library/react-native/extend-expect';

import { TEST_DEFAULT_DATE, TEST_DEFAULT_MATH_RANDOM } from './constants';

/**
Expand Down
13 changes: 1 addition & 12 deletions packages/lib/babel.jest.config.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,5 @@
// This is only used by jest
module.exports = {
sourceMaps: 'inline',
presets: [
'module:metro-react-native-babel-preset',
'@babel/preset-env',
[
'@babel/preset-react',
{
runtime: 'automatic',
},
],
'@babel/preset-typescript',
],
plugins: [['@babel/plugin-proposal-class-properties', { loose: false }]],
presets: ['module:@react-native/babel-preset'],
};
6 changes: 3 additions & 3 deletions packages/lib/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const path = require('path');

const packagesToTransform = [
'react-native',
'react-native-(.*)',
'react-native-.*',
'@react-native',
'@react-native-community',
'@react-native-tvos',
Expand All @@ -22,7 +22,7 @@ const packagesToTransform = [

/** @type {import('@jest/types').Config.InitialOptions} */
const config = {
preset: '@testing-library/react-native',
preset: 'react-native',
/*
* What the preset provides:
* - a transformer to handle media assets (png, video)
Expand All @@ -40,7 +40,7 @@ const config = {
{ configFile: path.resolve(__dirname, './babel.jest.config.js') },
],
},
transformIgnorePatterns: [`node_modules/(?!(${packagesToTransform.join('|')})/)`],
transformIgnorePatterns: [`node_modules/(?!(${packagesToTransform.join('|')})(/|$))`],
cacheDirectory: '.cache/jest',
// coverage
collectCoverageFrom: ['src/**/*.{js,jsx,ts,tsx}'],
Expand Down
19 changes: 9 additions & 10 deletions packages/lib/src/spatial-navigation/components/Node.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,10 @@ const useScrollToNodeIfNeeded = ({
childRef,
additionalOffset,
}: {
childRef: React.MutableRefObject<View | null>;
childRef: React.RefObject<View | null>;
additionalOffset?: number;
}) => {
const { scrollToNodeIfNeeded } = useSpatialNavigatorParentScroll();

return () => scrollToNodeIfNeeded(childRef, additionalOffset);
};

Expand All @@ -70,6 +69,7 @@ const useBindRefToChild = () => {

const bindRefToChild = (child: React.ReactElement) => {
return React.cloneElement(child, {
// @ts-expect-error @fixme can't find how to type this properly -- new error since react 19
...child.props,
ref: (node: View) => {
// We need the reference for our scroll handling
Expand Down Expand Up @@ -138,26 +138,25 @@ export const SpatialNavigationNode = forwardRef<SpatialNavigationNodeRef, Props>
* Therefore, the SpatialNavigator Node callbacks are registered at 1st render but can change (ie. if props change) afterwards.
* Since we want the functions to always be up to date, we use a reference to them.
*/

const currentOnSelect = useRef<() => void>();
const currentOnSelect = useRef<() => void>(undefined);
currentOnSelect.current = onSelect;

const currentOnLongSelect = useRef<() => void>();
const currentOnLongSelect = useRef<() => void>(undefined);
currentOnLongSelect.current = onLongSelect;

const currentOnFocus = useRef<() => void>();
const currentOnFocus = useRef<() => void>(undefined);
currentOnFocus.current = () => {
onFocus?.();
scrollToNodeIfNeeded();
};

const currentOnBlur = useRef<() => void>();
const currentOnBlur = useRef<() => void>(undefined);
currentOnBlur.current = onBlur;

const currentOnActive = useRef<() => void>();
const currentOnActive = useRef<() => void>(undefined);
currentOnActive.current = onActive;

const currentOnInactive = useRef<() => void>();
const currentOnInactive = useRef<() => void>(undefined);
currentOnInactive.current = onInactive;

const shouldHaveDefaultFocus = useSpatialNavigatorDefaultFocus();
Expand Down Expand Up @@ -210,7 +209,7 @@ export const SpatialNavigationNode = forwardRef<SpatialNavigationNodeRef, Props>
}, [id, isFocusable, shouldHaveDefaultFocus, spatialNavigator]);

// This proxy allows to track whether a property is used or not
// hence allowing to ignore re-renders for unused properties
// hence allowing to ignore re-renders for unused pr
const proxyObject = new Proxy(
{ isFocused, isActive, isRootActive },
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ export const SpatialNavigationScrollView = forwardRef<ScrollView, Props>(
useRemotePointerScrollviewScrollProps({ pointerScrollSpeed, scrollY, scrollViewRef });

const scrollToNode = useCallback(
(newlyFocusedElementRef: RefObject<View>, additionalOffset = 0) => {
(newlyFocusedElementRef: RefObject<View | null>, additionalOffset = 0) => {
try {
if (deviceTypeRef.current === 'remoteKeys') {
newlyFocusedElementRef?.current?.measureLayout(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ describe('SpatialNavigationVirtualizedList', () => {
);

// This is bad practice but easiest way to access the ref from outside the component for testing
let currentListRef: React.RefObject<SpatialNavigationVirtualizedListRef>;
let currentListRef: React.RefObject<SpatialNavigationVirtualizedListRef | null>;
const VirtualizedListWithNavigationButtons = ({ listSize = 10 }) => {
currentListRef = useRef<SpatialNavigationVirtualizedListRef>(null);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ const useUpdateRegistration = <T,>({
registerNthVirtualNode: (index: number) => void;
unregisterNthVirtualNode: (index: number) => void;
}) => {
const previousAllItems = useRef<Array<T>>();
const previousAllItems = useRef<Array<T>>(allItems);

// useBeforeMountEffect done every time allItems is changing to change the way the allItems is register in the spatialNavigator
useEffect(() => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { createContext, RefObject, useContext } from 'react';
import { View } from 'react-native';

export type ScrollToNodeCallback = (ref: RefObject<View>, additionalOffset?: number) => void;
export type ScrollToNodeCallback = (ref: RefObject<View | null>, additionalOffset?: number) => void;
export const SpatialNavigatorParentScrollContext = createContext<ScrollToNodeCallback>(() => {});

export const useSpatialNavigatorParentScroll = (): {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { ForwardedRef, ReactElement, RefAttributes, forwardRef } from 'react';
import { ForwardedRef, ReactElement, RefAttributes, forwardRef, PropsWithoutRef } from 'react';

/**
* This works like React.forwardRef but for components with generics props.
* This works like React.forwardRef but for components with generic props.
* @warning Don't use this if your component type isn't generic => `const Component = <T>() => {...}` and displayName is not supported yet
*/
export function typedForwardRef<T, P = unknown>(
render: (props: P, ref: ForwardedRef<T>) => ReactElement | null,
render: (props: PropsWithoutRef<P>, ref: ForwardedRef<T>) => ReactElement | null,
): (props: P & RefAttributes<T>) => ReactElement | null {
return forwardRef(render) as (props: P & RefAttributes<T>) => ReactElement | null;
// forwardRef expects (props: PropsWithoutRef<P>, ref: ForwardedRef<T>)
return forwardRef(render) as unknown as (props: P & RefAttributes<T>) => ReactElement | null;
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export type Props = {
newlyFocusedElementDistanceToTopRelativeToLayout: number;
horizontal?: boolean;
offsetFromStart: number;
scrollViewRef: RefObject<CustomScrollViewRef>;
scrollViewRef: RefObject<CustomScrollViewRef | null>;
};

export const scrollToNewlyFocusedElement = ({
Expand Down
2 changes: 2 additions & 0 deletions packages/lib/src/testing/jest-setup.ts
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
jest.mock('react-native/src/private/animated/NativeAnimatedHelper');

export {};
Loading