Repository: freeCodeCamp/freeCodeCamp
File: client/src/components/search/searchBar/search-bar.tsx (TypeScript)

Code before:
        index: index === -1 ? hits.length - 1 : index - 1
      }));
    },
    indexDown: (e: KeyboardEvent | undefined): void => {
      e?.preventDefault();
      this.setState(({ index, hits }) => ({
        index: index === hits.length - 1 ? -1 : index + 1
      }));
    }
  };

  render(): JSX.Element {
    const { isDropdownEnabled, isSearchFocused, innerRef, t } = this.props;
    const { index } = this.state;

    return (
      <WithInstantSearch>
        <div
          className='fcc_searchBar'
          data-testid='fcc_searchBar'
          ref={innerRef}
        >
          <HotKeys handlers={this.keyHandlers} keyMap={this.keyMap}>
            <div className='fcc_search_wrapper'>
              <ObserveKeys except={['Space']}>
                <div onFocus={this.handleFocus} role='textbox'>
                  <SearchBox
                    data-playwright-test-label='header-search'
                    focusShortcuts={['83', '191']}
                    onChange={this.handleChange}
                    onSubmit={e => {
                      this.handleSearch(e);
                    }}
                    showLoadingIndicator={false}
                    translations={{
                      submitTitle: t('icons.magnifier'),
                      resetTitle: t('icons.input-reset'),
                      placeholder: t('search.placeholder')
                    }}
                  />
                </div>
              </ObserveKeys>
              {isDropdownEnabled && isSearchFocused && (
                <SearchHits
                  handleHits={this.handleHits}
                  handleMouseEnter={this.handleMouseEnter}
                  handleMouseLeave={this.handleMouseLeave}
                  selectedIndex={index}
                />
              )}
            </div>

Review comment (suggestion):
Originally we had `onFocus` placed on `SearchBox`, but during the TypeScript migration (https://github.com/freeCodeCamp/freeCodeCamp/pull/42327/files#diff-44b18d0e9a315355595f6e9ff2436640d334b7910ef315cfa1410b5e59cd5527R205), it was moved to this outer div. The div was given the `textbox` role but without an associated label, causing a11y evaluation tools to flag as an a11y issue.

Those tools would suggest adding a label to the textbox, but the right fix should be removing `role='textbox'` instead.

I assume this outer div was needed because:
- There was a typecheck error because `SearchBox` doesn't accept `onFocus` as a prop, and moving `onFocus` elsewhere resolved the issue
- Moving `onFocus` to an outer div requires making the div an interactive element, hence `role='textbox'`

My fix here is simply reverting the DOM change and upgrading `@types/react-instantsearch-dom` to the latest version, which allows `SearchBox` to accept `onFocus`. 

Also, note that the implementation of `react-instantsearch-dom` already allows passing event handlers to `SearchBox`, so the change here doesn't impact the functionality. It's just that the `@types` package was a little too strict.


Code after:
        index: index === -1 ? hits.length - 1 : index - 1
      }));
    },
    indexDown: (e: KeyboardEvent | undefined): void => {
      e?.preventDefault();
      this.setState(({ index, hits }) => ({
        index: index === hits.length - 1 ? -1 : index + 1
      }));
    }
  };

  render(): JSX.Element {
    const { isDropdownEnabled, isSearchFocused, innerRef, t } = this.props;
    const { index } = this.state;

    return (
      <WithInstantSearch>
        <div
          className='fcc_searchBar'
          data-testid='fcc_searchBar'
          ref={innerRef}
        >
          <HotKeys handlers={this.keyHandlers} keyMap={this.keyMap}>
            <div className='fcc_search_wrapper'>
              <ObserveKeys except={['Space']}>
                <SearchBox
                  data-playwright-test-label='header-search'
                  focusShortcuts={['83', '191']}
                  onChange={this.handleChange}
                  onSubmit={e => {
                    this.handleSearch(e);
                  }}
                  showLoadingIndicator={false}
                  translations={{
                    submitTitle: t('icons.magnifier'),
                    resetTitle: t('icons.input-reset'),
                    placeholder: t('search.placeholder')
                  }}
                  onFocus={this.handleFocus}
                />
              </ObserveKeys>
              {isDropdownEnabled && isSearchFocused && (
                <SearchHits
                  handleHits={this.handleHits}
                  handleMouseEnter={this.handleMouseEnter}
                  handleMouseLeave={this.handleMouseLeave}
                  selectedIndex={index}
                />
              )}
            </div>
          </HotKeys>