tbleckert / react-select-search

⚡️ Lightweight select component for React
https://react-select-search.com
MIT License
676 stars 147 forks source link

Safari focus bug when using a button with in renderValue #103

Closed JoueBien closed 4 years ago

JoueBien commented 4 years ago

You may feel that this bug lies outside the use case for this component but I just wanted to submit an issue so that if others are experiencing the same issue they don't have to spend a while figuring out how to solve it. In our case, we are using the component as a search drop down and to simulate a regular dropdown.

In Safari, this fails to open the drop-down

renderValue={(valueProps, nameProps) => {
 // ..
 return <><button 
            className="flag-icon-selected" 

            {...valueProps}
          >
           <img src={flag || ''} />
          </button>
    </>
}}

why? because safari doesn't emit the focus event on buttons which means that the on blur event won't fire either. From what I've read this is the expected behaviour according to Apple's OS design language - which isn't the same as the specifications of the web. You can solve that by doing something like this to artificially emit the focus event.

<SelectSearch 
      options={countries} 
      value={countryLocal}
      name={name} 
      onChange={onCountryChange}
      disabled={disabled}
      search={false}
      autoComplete='off'
      autoFocus={false}
      renderValue={(valueProps, nameProps) => {
        // State
        const {selectedOption} = nameProps
        const {flag} = selectedOption || {}
        const buttonRef = useRef()

        // fixed buttion props
        const patchedValueProps = {
          ...valueProps,
          readOnly: false,
          onClick: () => {
            if (buttonRef && buttonRef.current) {
              buttonRef.current.focus()
            }
          }
        }

        // .. 
        return <><button 
            className="flag-icon-selected" 
            disabled={disabled}
            {...patchedValueProps}
            title={(!disabled) ? 'Choose your PHONE numbers country' : ''}
            ref={buttonRef}
          >
           <img src={flag || ''} />
          </button>
          <TextField
            type='tel'
            placeholder={placeholder}
            error={error}
            name={name}
            value={numberLocal}
            onChange={onNumberChange}
            disabled={disabled}
          />
        </>
      }}

      renderOption={(valueProps, nameProps)=> {
        // console.log('valueProps', valueProps)
        const {value, name, flag} = nameProps
        return <button 
          {...valueProps}  
          className="flag-option">
          <img src={flag}/> <div className="flag-name"><div>{name}</div></div>
        </button>
      }}
    />

Also, be aware that passing readOnly: true to a button in Safari will disable it and you won't be able to tap or click on it.

tbleckert commented 4 years ago

Yeah, in my own customizations I've used a div instead of a button (see the custom implementation example) since it has proper focus/blur handling. A button is obviously more semantic, but with correct roles and attributes it doesn't make a difference for screen readers. Great workaround with the custom focus handling though. Good to have it here in case anyone bumps into the same issue.

This "problem" has always bugged me with Safari.

jatbone commented 3 years ago

This is still issue, even when I changed button to div.

AndrejGajdos commented 2 years ago

custom implementation example shows how to rebuild whole component completely, not a single renderValue