Skip to content

Conversation

@aistrate
Copy link

@aistrate aistrate commented Sep 30, 2025

We intend to use viewRef to implement accessibility:

const ref = useRef<SpatialNavigationFocusableViewRef>(null);

const handleFocus = () => {
  const innerView = ref.current?.viewRef.current;

  if (innerView) {
    if (innerView.requestTVFocus) {
      // non-web
      innerView.requestTVFocus();
    } else {
      // web
      innerView.focus();
    }
  }
};

return (
  <SpatialNavigationFocusableView
    ref={ref}
    onFocus={handleFocus}
    viewProps={{ accessibilityLabel: "Play" }}
  >
    {/* ... */}
  </SpatialNavigationFocusableView>
);

This way the voice reader will announce the label when the button receives focus.

@pierpo
Copy link
Member

pierpo commented Oct 9, 2025

Good idea. Bypassing the lib for native and using it for web makes sense 👍
I hadn't considered this use case (because accessibility is broken by design with this lib 😭 as mentioned on the readme) but it is definitely a good idea.

@pierpo
Copy link
Member

pierpo commented Oct 9, 2025

There are some type errors. Here's the patch to fix them 😁

diff --git a/packages/example/src/modules/program/view/ProgramNode.tsx b/packages/example/src/modules/program/view/ProgramNode.tsx
index f11ffe5..b502bde 100644
--- a/packages/example/src/modules/program/view/ProgramNode.tsx
+++ b/packages/example/src/modules/program/view/ProgramNode.tsx
@@ -1,9 +1,11 @@
-import { SpatialNavigationFocusableView } from 'react-tv-space-navigation';
+import {
+  SpatialNavigationFocusableView,
+  SpatialNavigationFocusableViewRef,
+} from 'react-tv-space-navigation';
 
 import { ProgramInfo } from '../domain/programInfo';
 import { Program } from './Program';
 import { forwardRef } from 'react';
-import { SpatialNavigationNodeRef } from '../../../../../lib/src/spatial-navigation/types/SpatialNavigationNodeRef';
 import { useRotateAnimation } from './useRotateAnimation';
 import { Animated } from 'react-native';
 
@@ -15,7 +17,7 @@ type Props = {
   variant?: 'portrait' | 'landscape';
 };
 
-export const ProgramNode = forwardRef<SpatialNavigationNodeRef, Props>(
+export const ProgramNode = forwardRef<SpatialNavigationFocusableViewRef, Props>(
   ({ programInfo, onSelect, indexRange, label, variant }: Props, ref) => {
     const { rotate360, animatedStyle } = useRotateAnimation();
 
diff --git a/packages/example/src/pages/GridWithLongNodesPage.tsx b/packages/example/src/pages/GridWithLongNodesPage.tsx
index 6658a8b..da8203f 100644
--- a/packages/example/src/pages/GridWithLongNodesPage.tsx
+++ b/packages/example/src/pages/GridWithLongNodesPage.tsx
@@ -1,5 +1,6 @@
 import {
   DefaultFocus,
+  SpatialNavigationFocusableViewRef,
   SpatialNavigationNode,
   SpatialNavigationScrollView,
   SpatialNavigationView,
@@ -22,8 +23,8 @@ import { BottomArrow, TopArrow } from '../design-system/components/Arrows';
 const HEADER_SIZE = scaledPixels(400);
 
 export const GridWithLongNodesPage = () => {
-  const firstItemRef = useRef<SpatialNavigationNodeRef | null>(null);
-  const lastItemRef = useRef<SpatialNavigationNodeRef | null>(null);
+  const firstItemRef = useRef<SpatialNavigationFocusableViewRef | null>(null);
+  const lastItemRef = useRef<SpatialNavigationFocusableViewRef | null>(null);
   const parentRef = useRef<SpatialNavigationVirtualizedListRef | null>(null);
 
   return (
@@ -73,7 +74,7 @@ export const GridWithLongNodesPage = () => {
   );
 };
 
-const FirstRow = forwardRef<SpatialNavigationNodeRef>((_, ref) => {
+const FirstRow = forwardRef<SpatialNavigationFocusableViewRef>((_, ref) => {
   return (
     <SpatialNavigationNode orientation="horizontal">
       <ListContainer>
@@ -93,7 +94,7 @@ const FirstRow = forwardRef<SpatialNavigationNodeRef>((_, ref) => {
 });
 FirstRow.displayName = 'FirstRow';
 
-const SecondRow = forwardRef<SpatialNavigationNodeRef>((_, ref) => {
+const SecondRow = forwardRef<SpatialNavigationFocusableViewRef>((_, ref) => {
   const programs = programInfos.slice(6, 13);
   return (
     <SpatialNavigationNode orientation="horizontal">

@aistrate
Copy link
Author

@pierpo Fixed the type errors.

@pierpo
Copy link
Member

pierpo commented Oct 10, 2025

Weird, there are errors remaining on the CI but I don't have them locally 🤔 Do you?

image

@pierpo
Copy link
Member

pierpo commented Oct 10, 2025

I do have the same errors when I rebase the branch to update it with main.
I guess we'll do 2 PRs: one for v5 and one for v6 🤔

@aistrate
Copy link
Author

aistrate commented Oct 10, 2025

@pierpo Maybe CI is compiling with React 19.

Pushed a new fix. It should now work with both React 18 and 19.

Yes, we need another PR for the v5 branch, otherwise we cannot use it in our project (with React 18).

Created this PR for v5: #220

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants