NoriginMedia / react-spatial-navigation

DEPRECATED. HOC-based Spatial Navigation. NEW Hooks version is available here: https://github.com/NoriginMedia/norigin-spatial-navigation
MIT License
226 stars 64 forks source link

[Web] Upon arrow key press, any currently-selected items should be deselected #22

Closed shirakaba closed 1 year ago

shirakaba commented 5 years ago

Describe the bug I'm examining how mouse and keyboard behave together in a React Native Web App, with an interest in supporting desktop browsers (where either interaction mechanism might be used).

To Reproduce Steps to reproduce the behaviour:

  1. Click on any FocusableComponent. This will inadvertently cause its <TouchableOpacity> child to be selected (e.g. in the same way as if you'd highlighted a text field).
  2. Press the arrow keys to move focus to a different FocusableComponent.
  3. Press Enter.

I'd expect the Enter-press to trigger onEnterPress on the FocusableComponent; but in practice, react-spatial-navigation performs no operation at all.

This is because the key event is absorbed by the TouchableOpacity that was clicked on earlier (as it became highlighted upon click). So instead the Enter-press results in onPress firing on the TouchableOpacity, and I believe the event stops propagating from there.

Workaround

I found that calling HTMLElement.blur() works around the problem perfectly well for my needs.

const Item = ({
    item,
    onClick,
    focused,
    stealFocus,
}) => {
    const { ViewFactory, touchableStyle, source } = item;
    const refObj = React.createRef<TouchableOpacity>();

    return (
        <TouchableOpacity
            ref={refObj}
            onFocus={() => {
                const currentRef = refObj.current;
                /* React Native Components happen to implement blur() just like
                 * HTMLElement does, but it's safest to check anyway. Not sure
                 * whether there's any harm in calling it on Native as well as
                 * Web – haven't tested on tvOS and Android TV. */
                if(currentRef && currentRef.blur){
                    currentRef.blur();
                }
            }}
            onPress={() => {
                stealFocus();
                onClick();
            }}
            style={touchableStyle}
        >
            <ViewFactory focused={focused} source={source} />
        </TouchableOpacity>
    );
};

const ItemFocusable = withFocusable()(props => <Item {...props} />);

Additional context Note that the visual debugger blocks clicks altogether, so cannot be used to reproduce this issue.

Also: Any Enter-press performed when a TouchableOpacity is highlighted does not propagate to a keydown event listener, so I believe the browser is handling the event to some extent.

Discussion

Should this issue be left for consumers of the library to solve themselves (as I have done with my workaround), or is there any elegant way that the library could handle it? I can't think of any myself, but I'd be interested to hear your thoughts.

Either way, with this issue filing, devs will be able to see at least one way to work around it.