-
Notifications
You must be signed in to change notification settings - Fork 4.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add focusDefaultOption prop to prevent focusing first option #4080
base: master
Are you sure you want to change the base?
Conversation
…-select into disable-auto-option-focus
…smb/react-select into Francismb-disable-auto-option-focus
|
This pull request is automatically built and testable in CodeSandbox. To see build info of the built libraries, click here or the icon next to each commit SHA. Latest deployment of this branch, based on commit ee35360:
|
@JedWatson looks good to me, for your review. |
Based off PR #3705 |
This is a very anticipated PR for our project. We had to add a bunch of dirty hacks (accessing internal state) to override default focus option that ReactSelect sets after each options update. |
Looking forward to this fix getting through! Any chance this is getting close? |
@bladey, I noticed that you have permissions in this repository to close issues. Do you also have permissions to merge pull requests? |
Will this be ever merged? it's like one and half year now. |
Maybe someone found a workaround ? |
I think they're not merging any PR's until v5 is out: #4559 (comment) |
Not sure why this is not merged yet. But does anyone got a walk around solution for version @4.3.1, which is the latest version now (Sep 9th, 2021). The Or please let me know which version I can use to use for the Many thanks... |
After many tries, we just decided that this is not an issue.
I know it's still not what we want by this PR but sometimes it's easier to re-think solutions. Looking for you opinion :) |
I am not sure I followed, @grzennio, when you said Because I can see this is a very common request from the community. Google also not auto high-line on the first item that match the search term until user press down arrow key (see screen recording) Screen.Recording.2021-09-10.at.10.26.07.am.movAll I want is when i use AsyncSelect here and when I type Screen.Recording.2021-09-10.at.10.37.47.am.mov |
is this going to be merged? |
Following up, is there anything in the pipeline with plans to merge this PR? |
I took a look at the actual code in this PR, and it seems to be pretty far off from completion due to the progress that the main branch has made toward v5. I needed to come up with a workaround, and it turned out to be pretty impossible to change the actual focused state of the Select innards, since the necessary refs aren't exposed. So I did my best to mitigate the actual ramifications of the focus state rather than change the literal focus itself -- namely, any button behavior and styles associated with focus:
So I basically just prevented any native react-select focus behavior until I could manually "acknowledge" the focus, and then I let react-select "resume" its normal focus behavior. |
@bladey, quick question, are you guys planning to merge this? Do you guys have an ETA for this? |
@UlyssesInvictus Thank you so much for your example code! ^_^ I've fixed/added a couple of things to your code and put everything into a reusable component 🙂
import React, { useState, useCallback } from 'react';
import Select, { components as ReactSelectComponents } from 'react-select';
/*
* This component modifies React-Select so that when the React-Select dropdown is opened, it doesn't automatically select the first option.
* See https://github.com/JedWatson/react-select/pull/4080 for context.
*/
export const SelectDoNotAutoselectFirstOption = React.memo((props) => {
const {
onKeyDown: originalOnKeyDown,
onMenuClose: originalOnMenuClose
} = props;
// Begin code from https://github.com/JedWatson/react-select/pull/4080#issuecomment-944563481
const [hasUserStartedSelectingOption, setHasUserStartedSelectingOption] = useState(props.value != null);
const onKeyDown = useCallback((e, ...otherArgs) => {
if ((e.key === 'ArrowDown' || e.key === 'ArrowUp') && !hasUserSelectedOption) {
e.preventDefault();
setHasUserSelectedOption(true);
}
return originalOnKeyDown?.(e, ...otherArgs);
}, [originalOnKeyDown, hasUserSelectedOption, setHasUserSelectedOption]);
const onMenuClose = useCallback(
(...args) => {
setHasUserSelectedOption(false);
return originalOnMenuClose?.(...args);
},
[originalOnMenuClose, setHasUserSelectedOption]
);
// End code from https://github.com/JedWatson/react-select/pull/4080#issuecomment-944563481
const originalOptionComponent = props.components?.Option ?? ReactSelectComponents.Option;
const OptionComponent = useCallback(
(props) => {
return originalOptionComponent({
...props,
innerProps: {
...props.innerProps,
onMouseOver: (...args) => {
setHasUserSelectedOption(true);
return props.innerProps.onMouseOver(...args);
}
}
});
},
[originalOptionComponent, setHasUserSelectedOption]
);
return (
<Select
{...props}
components={{
...props.components,
Option: OptionComponent
}}
onKeyDown={onKeyDown}
onMenuClose={onMenuClose}
styles={{
...props.styles,
// The base of this function is from https://github.com/JedWatson/react-select/pull/4080#issuecomment-944563481
option: (provided, selectProps) => {
const useUnselectedColors = selectProps.isFocused && !hasUserSelectedOption && props.value == null;
return {
...provided,
backgroundColor: useUnselectedColors ? 'transparent' : provided.backgroundColor,
// NOTE: You may also need to set 'color', etc.
// Ideally we'd do something like this: `useUnselectedColors ? selectProps.getStyles('normal', ...) : selectProps.getStyles('hover', ...)`, but I'm not sure what to pass to `getStyles()`
};
}
// End code from https://github.com/JedWatson/react-select/pull/4080#issuecomment-944563481
}}
/>
);
});
SelectDoNotAutoselectFirstOption.displayName = 'SelectDoNotAutoselectFirstOption';
export default SelectDoNotAutoselectFirstOption; In your component, just use Instead of this: <Select
name={...}
options={...}
onChange={...}
value={...}
theme={theme => ... }
menuPlacement="auto"
...
/> Use the <SelectDoNotAutoselectFirstOption
name={...}
options={...}
onChange={...}
value={...}
theme={theme => ... }
menuPlacement="auto"
...
/> Hope that's helpful to everyone, and thanks again to @UlyssesInvictus for the concept and base code! 🙂 One remaining issue: When the user presses enter, it still select the first option in the dropdown. |
Do you guys have a solution for |
@augusto-proano I've never worked with But... ya, it's going to be complicated 😛 |
Thanks for the info @Bosch-Eli-Black. Do you know about any other way to accomplish this expected behavior without this pr since it hasn't been merged yet? |
I spent a decent amount of time looking into this when I first posted my original comment, and I really don't think there is. I'm going to reiterate that I think the PR itself is vastly out of date. I don't think it's anywhere close to being merged in its current state. I don't think the actual fix in react-select's source code itself is too difficult, but it does require someone to make the effort of understanding the react-select logic and then contributing a PR. As for async select, it should be completely identical to the regular select version, the only possible difference would be in not firing any effects until after the options load, but the sample code above doesn't really make any assumptions on that front. I'm sure with some trial and error it should be possible. |
Yep 🙂 @UlyssesInvictus or my code samples should get you off to a good start 🙂 I'm at work, so I'm unable to contribute a PR, due to company policy. |
Hello @Bosch-Eli-Black, @UlyssesInvictus, @augusto-proano, we appreciate you offering workarounds for problems, but also see the need for something like this PRs feature being configurable. I will try to contact @bladey to see if he has time to update this PR, but you are welcome to fork, update and submit a new PR based on this one any time. |
Any estimation for that one? extremely necessary .. |
I'm working on an autocomplete search and I was able to get around this with a custom interface OptionType {
value: string
label: string
}
function Option ({ children, ...props }: OptionProps<OptionType>): ReactElement | null {
if (props.data?.label === '') {
return null
}
return (
<components.Option {...props} className='fw-normal'>
{children}
</components.Option>
)
}
function Search (): ReactElement {
const [input, setInput] = useState('')
const [value, setValue] = useState<OptionType | null>(null)
const [menuIsOpen, setMenuIsOpen] = useState(false)
const options = useMemo<OptionType[]>(() => ([
{ value: input, label: '' },
{ value: 'value1', label: 'label 1' },
{ value: 'value2', label: 'label 2' }
]) , [input])
const doWork = useCallback((term: string) => {
console.log('doWork', term)
}, [])
const handleInputChange = useCallback((value: string, actionMeta: InputActionMeta) => {
if (actionMeta.action === 'input-change') {
setMenuIsOpen(true)
setInput(value)
}
}, [])
const handleValueChange = useCallback((option: OptionType, actionMeta: ActionMeta<OptionType>) => {
setMenuIsOpen(false)
setValue(option)
setInput(option?.label ?? '')
}, [])
const handleBlur = useCallback(() => {
setMenuIsOpen(false)
}, [])
const handleFocus = useCallback(() => {
setMenuIsOpen(true)
}, [])
const handleSubmit = useCallback((event: FormEvent<HTMLFormElement>) => {
event.preventDefault()
doWork(value?.value ?? input)
}, [doWork, input, value])
return (
<form onSubmit={handleSubmit}>
<Select
value={value}
onChange={handleValueChange}
options={options}
inputValue={input}
onInputChange={handleInputChange}
filterOption={() => true}
components={{ Option }}
menuIsOpen={menuIsOpen}
onBlur={handleBlur}
onFocus={handleFocus}
blurInputOnSelect={true}
closeMenuOnSelect={false}
/>
<button type='submit'>Search</button>
</form>
)
} |
In my case, this is work.
|
Need this feature ASAP, PLEASE. My Job depends on this. |
Hey guys! Usually I don't really comment in this kind of things but I managed to code a workaround for this. I just added these styles to the Option component (based on this response which was't really working fine for me)
I know it is not the best solution but I works pretty fine for me! Hope this helps anyone, at least until this PR is merged 👍 |
I ran into this problem too while trying to use This workaround suffices to reset the focus for me if-and-only-if matchSorter finds a better one. One (unfortunate) side-effect is that the focused item is moved to the top of the list of options when there are multiple options that are equally good matches. const originalOptions = /*...whereever those come from...*/;
const [options, setOptions] = useState(originalOptions);
const selectRef = useRef<SelectInstance<MyOptionType>>(null);
//...
return <Select
ref={selectRef}
filterOption={() => true}
onInputChange={(search) => {
let toFilter = originalOptions;
if (selectRef.current?.state.focusedOption) {
const oldIdx = toFilter.indexOf(selectRef.current.state.focusedOption);
if (oldIdx > 0) {
//give the old focused item the best chances of retaining focus...
toFilter = [toFilter[oldIdx], ...toFilter.slice(0, oldIdx), ...toFilter.slice(oldIdx + 1, toFilter.length)];
}
}
const filtered = search
? matchSorter(originalOptions, search, matchSorterConfig)
: originalOptions;
setOptions(filtered);
if (filtered[0] && selectRef.current && selectRef.current.state.focusedOption) {
//...but if there's truly a new best match, then do focus that instead.
selectRef.current.state.focusedOption = filtered[0];
}
}}
//[...] I suspect there are better workarounds, but I'm all ears... |
Figured out some work around:
Updating control custom styles to only show a border on the dropdown when tabbing through app (like in my case on a user menu)
Create a custom option component that will only be focused when tabbing through the app, so the first option is not focused on clicking on the dropdown:
Lots of extra lines of code and time coding this. Wondering if I regret trying to move all my dropdowns to react-select 5.4 Open source work: fleetdm/fleet#24281 |
Based off PR #3705