2

I'm working through a complex component that calls for use of an options list. I'm already using react-select throughout my app so it make sense to reuse this in this instance. I'm trying to embed react-select inline with the select list exposed but hide the controller/input/indicators. Basically, I just want the list. I can get this to work and it's all fine and dandy with the mouse, but when I get into focus issues regarding the select options I'm having issues.

Is there a correct way to initialize focus on the menu list/first option?

Here is a basic example of what I'm trying to do... I want to set focus when the component has completed mounting.

On mount, I've tried a bunch of things. Setting focus to refs within the select. Using query selectors to focus on options themselves... I've also had to play with customizing components, but it appears certain components need to still be active for focus states to work correctly. For instance, the value containers. If I return them as null the component doesn't operate correctly regarding focus.

// tried setting focus on the option directly
const option: any = this.selectRef.menuListRef.querySelector(
  '.va-select__option'
);
option.click(); // does work
option.focus(); // appears to do nothing, but I believe react select manages this all behind the scenes

// tried setting focus on the input or triggering a click
this.selectRef.inputRef.focus();
this.selectRef.inputRef.click();

// Attempted using all of these refs and setting focus:
controlRef
inputRef
menuListRef

Example component:

interface DropdownProps {
    isLoading?: boolean;
}

function EmptyComponent(): React.ReactElement<any> {
    return <></>;
}

export class Dropdown extends React.Component<DropdownProps> {
    selectRef: any;

    componentDidMount(): void {
        this.selectRef.menuListRef.focus();
    }

    setSelectRef = (element: any): void => {
        if (element) {
            this.selectRef = element.select.select;
        }
    };

    render(): React.ReactNode {
        const { isLoading } = this.props;
        const customComponents: SelectComponentsConfig<any> = {
            IndicatorsContainer: EmptyComponent,
            Input: EmptyComponent,
            Placeholder: EmptyComponent
        };

        return (
            <div>
                <Select
                    components={customComponents}
                    isLoading={isLoading}
                    menuIsOpen={true}
                    options={[
                        {
                            label: 'Group 1',
                            value: 'Value 1'
                        },
                        {
                            label: 'Group 2',
                            value: 'Value 2'
                        },
                        {
                            label: 'Group 3',
                            value: 'Value 3'
                        },
                        {
                            label: 'Group 4',
                            value: 'Value 4'
                        }
                    ]}
                    reference={this.setSelectRef}
                />
                {isLoading && <div>Loading</div>}
            </div>
        );
    }
}

I can't seem to get focus states to work correctly and feel like I'm hacking this together a bit much.

Has anyone used react-select this way in the past and have advice? Is there something I need to set or trigger to get the focus working correctly on options? I assume react select does a bit behind the scenes when a user triggers the select to expand and those are not being set in this instance.

Brian Behrens
  • 21
  • 1
  • 3
  • Hi, just to sum up your goal, you want the menu option to be opened when the component is mounted ? – Laura Feb 13 '19 at 21:55
  • Yes, basically when mounted the component should already have the select menu exposed. I'm also working to hide the controller component. – Brian Behrens Feb 13 '19 at 22:38
  • I did find doing something like this works, but there has to be a better way to properly set focus and init keyboard events correctly. `componentDidMount(): void { setTimeout(() => { const clickEvent: MouseEvent = document.createEvent('MouseEvents'); clickEvent.initEvent('mousedown', true, true); this.selectRef.controlRef.dispatchEvent(clickEvent); }, 0); }` – Brian Behrens Feb 13 '19 at 22:45
  • About opening the menu option I recommend you to take a look at this other question https://stackoverflow.com/questions/54266127/is-it-possible-to-open-react-select-component-as-it-mounts/54296299#54296299 – Laura Feb 13 '19 at 22:59
  • Yes, that covers the basics, but how do you set focus on the first option in the select? – Brian Behrens Feb 13 '19 at 23:06
  • Can you explain me what you mean by “focus” ? Is it something just visual ? Or is it an option to set first option as default value ? – Laura Feb 13 '19 at 23:12

1 Answers1

1

To fit all your requests I would try a solution like the following one:

  1. use menuIsOpen props to manage the state of the menu option
  2. use custom components to remove the control element
  3. set a defaultValue to focus the desired option

Here the code:

const options = [
  {
    label: "1",
    value: 1
  },
  {
    label: "2",
    value: 2
  },
  ,
  {
    label: "3",
    value: 3
  },
  {
    label: "4",
    value: 4
  }
];

const Control = props => (
  <components.Menu {...props}>{props.children}</components.Menu>
);

function App() {
  return (
    <div className="App">
      <Select
        defaultValue={{ label: "1", value: 1 }}
        components={{ Control }}
        menuIsOpen={true}
        options={options}
      />
    </div>
  );
}

Live example available here.

Laura
  • 8,100
  • 4
  • 40
  • 50