Skip to content
This repository was archived by the owner on Sep 11, 2024. It is now read-only.

Commit e6e7885

Browse files
committed
Fix programmatic focus management in roving tab index not triggering onFocus handler
1 parent 7dfbe7a commit e6e7885

File tree

1 file changed

+18
-6
lines changed

1 file changed

+18
-6
lines changed

src/accessibility/RovingTabIndex.tsx

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ export const reducer = (state: IState, action: IAction) => {
131131
}
132132

133133
case Type.SetFocus: {
134+
if (state.activeRef === action.payload.ref) return state;
134135
// update active ref
135136
state.activeRef = action.payload.ref;
136137
return { ...state };
@@ -194,6 +195,7 @@ export const RovingTabIndexProvider: React.FC<IProps> = ({
194195
}
195196

196197
let handled = false;
198+
let focusRef: RefObject<HTMLElement>;
197199
// Don't interfere with input default keydown behaviour
198200
if (ev.target.tagName !== "INPUT" && ev.target.tagName !== "TEXTAREA") {
199201
// check if we actually have any items
@@ -202,15 +204,15 @@ export const RovingTabIndexProvider: React.FC<IProps> = ({
202204
if (handleHomeEnd) {
203205
handled = true;
204206
// move focus to first (visible) item
205-
findSiblingElement(context.state.refs, 0)?.current?.focus();
207+
focusRef = findSiblingElement(context.state.refs, 0);
206208
}
207209
break;
208210

209211
case Key.END:
210212
if (handleHomeEnd) {
211213
handled = true;
212214
// move focus to last (visible) item
213-
findSiblingElement(context.state.refs, context.state.refs.length - 1, true)?.current?.focus();
215+
focusRef = findSiblingElement(context.state.refs, context.state.refs.length - 1, true);
214216
}
215217
break;
216218

@@ -220,7 +222,7 @@ export const RovingTabIndexProvider: React.FC<IProps> = ({
220222
handled = true;
221223
if (context.state.refs.length > 0) {
222224
const idx = context.state.refs.indexOf(context.state.activeRef);
223-
findSiblingElement(context.state.refs, idx - 1)?.current?.focus();
225+
focusRef = findSiblingElement(context.state.refs, idx + 1);
224226
}
225227
}
226228
break;
@@ -231,7 +233,7 @@ export const RovingTabIndexProvider: React.FC<IProps> = ({
231233
handled = true;
232234
if (context.state.refs.length > 0) {
233235
const idx = context.state.refs.indexOf(context.state.activeRef);
234-
findSiblingElement(context.state.refs, idx + 1, true)?.current?.focus();
236+
focusRef = findSiblingElement(context.state.refs, idx - 1, true);
235237
}
236238
}
237239
break;
@@ -242,7 +244,17 @@ export const RovingTabIndexProvider: React.FC<IProps> = ({
242244
ev.preventDefault();
243245
ev.stopPropagation();
244246
}
245-
}, [context.state, onKeyDown, handleHomeEnd, handleUpDown, handleLeftRight]);
247+
248+
if (focusRef) {
249+
focusRef.current?.focus();
250+
dispatch({
251+
type: Type.SetFocus,
252+
payload: {
253+
ref: focusRef,
254+
},
255+
});
256+
}
257+
}, [context, onKeyDown, handleHomeEnd, handleUpDown, handleLeftRight]);
246258

247259
return <RovingTabIndexContext.Provider value={context}>
248260
{ children({ onKeyDownHandler }) }
@@ -283,7 +295,7 @@ export const useRovingTabIndex = (inputRef?: Ref): [FocusHandler, boolean, Ref]
283295
type: Type.SetFocus,
284296
payload: { ref },
285297
});
286-
}, [ref, context]);
298+
}, []); // eslint-disable-line react-hooks/exhaustive-deps
287299

288300
const isActive = context.state.activeRef === ref;
289301
return [onFocus, isActive, ref];

0 commit comments

Comments
 (0)