Browse Source

feat(search): Switch smartSearchBar to use textarea (#26333)

Evan Purkhiser 3 years ago
parent
commit
bc468ff0f5

+ 75 - 87
static/app/components/smartSearchBar/index.tsx

@@ -1,4 +1,5 @@
 import * as React from 'react';
+import TextareaAutosize from 'react-autosize-textarea';
 import {browserHistory, withRouter, WithRouterProps} from 'react-router';
 import {ClassNames, withTheme} from '@emotion/react';
 import styled from '@emotion/styled';
@@ -230,7 +231,7 @@ type Props = WithRouterProps & {
   /**
    * Called on key down
    */
-  onKeyDown?: (evt: React.KeyboardEvent<HTMLInputElement>) => void;
+  onKeyDown?: (evt: React.KeyboardEvent<HTMLTextAreaElement>) => void;
 
   /**
    * Called when a recent search is saved
@@ -308,7 +309,7 @@ class SmartSearchBar extends React.Component<Props, State> {
     query: null,
     onSearch: function () {},
     excludeEnvironment: false,
-    placeholder: t('Search for events, users, tags, and everything else.'),
+    placeholder: t('Search for events, users, tags, and more'),
     supportedTags: {},
     defaultSearchItems: [[], []],
     hasPinnedSearch: false,
@@ -353,7 +354,7 @@ class SmartSearchBar extends React.Component<Props, State> {
   /**
    * Ref to the search element itself
    */
-  searchInput = React.createRef<HTMLInputElement>();
+  searchInput = React.createRef<HTMLTextAreaElement>();
 
   blur = () => {
     if (!this.searchInput.current) {
@@ -415,7 +416,7 @@ class SmartSearchBar extends React.Component<Props, State> {
 
   onQueryFocus = () => this.setState({dropdownVisible: true});
 
-  onQueryBlur = (e: React.FocusEvent<HTMLInputElement>) => {
+  onQueryBlur = (e: React.FocusEvent<HTMLTextAreaElement>) => {
     // wait before closing dropdown in case blur was a result of clicking a
     // menu option
     const value = e.target.value;
@@ -428,8 +429,10 @@ class SmartSearchBar extends React.Component<Props, State> {
     this.blurTimeout = window.setTimeout(blurHandler, DROPDOWN_BLUR_DURATION);
   };
 
-  onQueryChange = (evt: React.ChangeEvent<HTMLInputElement>) => {
-    this.setState({query: evt.target.value}, this.updateAutoCompleteItems);
+  onQueryChange = (evt: React.ChangeEvent<HTMLTextAreaElement>) => {
+    const query = evt.target.value.replace('\n', '');
+
+    this.setState({query}, this.updateAutoCompleteItems);
     callIfFunction(this.props.onChange, evt.target.value, evt);
   };
 
@@ -438,7 +441,7 @@ class SmartSearchBar extends React.Component<Props, State> {
   /**
    * Handle keyboard navigation
    */
-  onKeyDown = (evt: React.KeyboardEvent<HTMLInputElement>) => {
+  onKeyDown = (evt: React.KeyboardEvent<HTMLTextAreaElement>) => {
     const {onKeyDown} = this.props;
     const {key} = evt;
 
@@ -448,7 +451,6 @@ class SmartSearchBar extends React.Component<Props, State> {
       return;
     }
 
-    const {useFormWrapper} = this.props;
     const isSelectingDropdownItems = this.state.activeSearchItem !== -1;
 
     if (key === 'ArrowDown' || key === 'ArrowUp') {
@@ -522,19 +524,13 @@ class SmartSearchBar extends React.Component<Props, State> {
       return;
     }
 
-    if (key === 'Enter') {
-      if (!useFormWrapper && !isSelectingDropdownItems) {
-        // If enter is pressed, and we are not wrapping input in a `<form>`,
-        // and we are not selecting an item from the dropdown, then we should
-        // consider the user as finished selecting and perform a "search" since
-        // there is no `<form>` to catch and call `this.onSubmit`
-        this.doSearch();
-      }
+    if (key === 'Enter' && !isSelectingDropdownItems) {
+      this.doSearch();
       return;
     }
   };
 
-  onKeyUp = (evt: React.KeyboardEvent<HTMLInputElement>) => {
+  onKeyUp = (evt: React.KeyboardEvent<HTMLTextAreaElement>) => {
     // Other keys are managed at onKeyDown function
     if (evt.key !== 'Escape') {
       return;
@@ -1101,36 +1097,24 @@ class SmartSearchBar extends React.Component<Props, State> {
     const hasQuery = !!this.state.query;
 
     const input = (
-      <React.Fragment>
-        <StyledInput
-          type="text"
-          placeholder={placeholder}
-          id="smart-search-input"
-          name="query"
-          ref={this.searchInput}
-          autoComplete="off"
-          value={this.state.query}
-          onFocus={this.onQueryFocus}
-          onBlur={this.onQueryBlur}
-          onKeyUp={this.onKeyUp}
-          onKeyDown={this.onKeyDown}
-          onChange={this.onQueryChange}
-          onClick={this.onInputClick}
-          disabled={disabled}
-          maxLength={maxQueryLength}
-        />
-        {(this.state.loading || this.state.searchGroups.length > 0) && (
-          <DropdownWrapper visible={this.state.dropdownVisible}>
-            <SearchDropdown
-              className={dropdownClassName}
-              items={this.state.searchGroups}
-              onClick={this.onAutoComplete}
-              loading={this.state.loading}
-              searchSubstring={this.state.searchTerm}
-            />
-          </DropdownWrapper>
-        )}
-      </React.Fragment>
+      <SearchInput
+        type="text"
+        placeholder={placeholder}
+        id="smart-search-input"
+        name="query"
+        ref={this.searchInput}
+        autoComplete="off"
+        value={this.state.query}
+        onFocus={this.onQueryFocus}
+        onBlur={this.onQueryBlur}
+        onKeyUp={this.onKeyUp}
+        onKeyDown={this.onKeyDown}
+        onChange={this.onQueryChange}
+        onClick={this.onInputClick}
+        disabled={disabled}
+        maxLength={maxQueryLength}
+        spellCheck={false}
+      />
     );
 
     return (
@@ -1140,12 +1124,10 @@ class SmartSearchBar extends React.Component<Props, State> {
           {inlineLabel}
         </SearchLabel>
 
-        {useFormWrapper ? (
-          <StyledForm onSubmit={this.onSubmit}>{input}</StyledForm>
-        ) : (
-          input
-        )}
-        <StyledButtonBar gap={0.5}>
+        <InputWrapper>
+          {useFormWrapper ? <form onSubmit={this.onSubmit}>{input}</form> : input}
+        </InputWrapper>
+        <ActionsBar gap={0.5}>
           {this.state.query !== '' && (
             <InputButton
               type="button"
@@ -1270,7 +1252,17 @@ class SmartSearchBar extends React.Component<Props, State> {
               )}
             </StyledDropdownLink>
           )}
-        </StyledButtonBar>
+        </ActionsBar>
+        {(this.state.loading || this.state.searchGroups.length > 0) && (
+          <SearchDropdown
+            css={{display: this.state.dropdownVisible ? 'block' : 'none'}}
+            className={dropdownClassName}
+            items={this.state.searchGroups}
+            onClick={this.onAutoComplete}
+            loading={this.state.loading}
+            searchSubstring={this.state.searchTerm}
+          />
+        )}
       </Container>
     );
   }
@@ -1302,52 +1294,48 @@ export {SmartSearchBar};
 
 const Container = styled('div')<{isOpen: boolean}>`
   border: 1px solid ${p => p.theme.border};
-  border-radius: ${p =>
-    p.isOpen
-      ? `${p.theme.borderRadius} ${p.theme.borderRadius} 0 0`
-      : p.theme.borderRadius};
-  /* match button height */
-  height: 40px;
   box-shadow: inset ${p => p.theme.dropShadowLight};
   background: ${p => p.theme.background};
-
+  padding: 7px ${space(1)};
   position: relative;
+  display: grid;
+  grid-template-columns: max-content 1fr max-content;
+  grid-gap: ${space(1)};
+  align-items: start;
 
-  display: flex;
+  border-radius: ${p =>
+    p.isOpen
+      ? `${p.theme.borderRadius} ${p.theme.borderRadius} 0 0`
+      : p.theme.borderRadius};
 
   .show-sidebar & {
     background: ${p => p.theme.backgroundSecondary};
   }
 `;
 
-const DropdownWrapper = styled('div')<{visible: boolean}>`
-  display: ${p => (p.visible ? 'block' : 'none')};
-`;
-
-const StyledForm = styled('form')`
-  flex-grow: 1;
+const InputWrapper = styled('div')`
+  position: relative;
 `;
 
-const StyledInput = styled('input')`
-  color: ${p => p.theme.textColor};
-  background: transparent;
-  border: 0;
+const SearchInput = styled(TextareaAutosize)`
+  display: flex;
+  resize: none;
   outline: none;
-  font-size: ${p => p.theme.fontSizeMedium};
+  border: 0;
   width: 100%;
-  height: 40px;
-  line-height: 40px;
-  padding: 0 0 0 ${space(1)};
+  padding: 0;
+  line-height: 25px;
+  margin-bottom: -1px;
+  background: transparent;
+  font-size: ${p => p.theme.fontSizeSmall};
+  font-family: ${p => p.theme.text.familyMono};
+  caret-color: ${p => p.theme.subText};
 
   &::placeholder {
     color: ${p => p.theme.formPlaceholder};
   }
-  &:focus {
-    border-color: ${p => p.theme.border};
-    border-bottom-right-radius: 0;
-  }
 
-  .show-sidebar & {
+  [disabled] {
     color: ${p => p.theme.disabled};
   }
 `;
@@ -1368,10 +1356,6 @@ const DropdownElement = styled('a')<Omit<DropdownElementStylesProps, 'theme'>>`
   ${getDropdownElementStyles}
 `;
 
-const StyledButtonBar = styled(ButtonBar)`
-  margin-right: ${space(1)};
-`;
-
 const EllipsisButton = styled(InputButton)`
   /*
    * this is necessary because DropdownLink wraps the button in an unstyled
@@ -1386,8 +1370,12 @@ const VerticalEllipsisIcon = styled(IconEllipsis)`
 
 const SearchLabel = styled('label')`
   display: flex;
-  align-items: center;
+  padding: ${space(0.5)} 0;
   margin: 0;
-  padding-left: ${space(1)};
   color: ${p => p.theme.gray300};
 `;
+
+const ActionsBar = styled(ButtonBar)`
+  height: ${space(2)};
+  margin: ${space(0.5)} 0;
+`;

+ 7 - 7
static/app/components/smartSearchBar/searchDropdown.tsx

@@ -108,17 +108,17 @@ class SearchDropdown extends PureComponent<Props> {
 export default SearchDropdown;
 
 const StyledSearchDropdown = styled('div')`
-  box-shadow: ${p => p.theme.dropShadowLight};
-  border: 1px solid ${p => p.theme.border};
-  border-radius: ${p => p.theme.borderRadiusBottom};
-  position: absolute;
-  top: 38px;
   /* Container has a border that we need to account for */
-  right: -1px;
+  position: absolute;
+  top: 100%;
   left: -1px;
-  background: ${p => p.theme.background};
+  right: -1px;
   z-index: ${p => p.theme.zIndex.dropdown};
   overflow: hidden;
+  background: ${p => p.theme.background};
+  box-shadow: ${p => p.theme.dropShadowLight};
+  border: 1px solid ${p => p.theme.border};
+  border-radius: ${p => p.theme.borderRadiusBottom};
 `;
 
 const LoadingWrapper = styled('div')`

+ 12 - 12
tests/js/spec/components/events/searchBar.spec.jsx

@@ -4,24 +4,24 @@ import {initializeOrg} from 'sentry-test/initializeOrg';
 import SearchBar from 'app/components/events/searchBar';
 import TagStore from 'app/stores/tagStore';
 
-const focusInput = el => el.find('input[name="query"]').simulate('focus');
+const focusTextarea = el => el.find('textarea[name="query"]').simulate('focus');
 const selectFirstAutocompleteItem = async el => {
-  focusInput(el);
+  focusTextarea(el);
 
   el.find('SearchListItem[data-test-id="search-autocomplete-item"]')
     .first()
     .simulate('click');
-  const input = el.find('input');
-  input
+  const textarea = el.find('textarea');
+  textarea
     .getDOMNode()
-    .setSelectionRange(input.prop('value').length, input.prop('value').length);
+    .setSelectionRange(textarea.prop('value').length, textarea.prop('value').length);
 
   await tick();
   await el.update();
 };
 
 const setQuery = async (el, query) => {
-  el.find('input')
+  el.find('textarea')
     .simulate('change', {target: {value: query}})
     .getDOMNode()
     .setSelectionRange(query.length, query.length);
@@ -107,7 +107,7 @@ describe('Events > SearchBar', function () {
     await wrapper.update();
     // the trailing space is important here as without it, autocomplete suggestions will
     // try to complete `has:gpu` thinking the token has not ended yet
-    expect(wrapper.find('input').prop('value')).toBe('has:gpu ');
+    expect(wrapper.find('textarea').prop('value')).toBe('has:gpu ');
   });
 
   it('searches and selects an event field value', async function () {
@@ -132,7 +132,7 @@ describe('Events > SearchBar', function () {
 
     selectFirstAutocompleteItem(wrapper);
     await wrapper.update();
-    expect(wrapper.find('input').prop('value')).toBe('gpu:"Nvidia 1080ti" ');
+    expect(wrapper.find('textarea').prop('value')).toBe('gpu:"Nvidia 1080ti" ');
   });
 
   it('if `useFormWrapper` is false, pressing enter when there are no dropdown items selected should blur and call `onSearch` callback', async function () {
@@ -161,7 +161,7 @@ describe('Events > SearchBar', function () {
       '"Nvidia 1080ti"'
     );
 
-    wrapper.find('input').simulate('keydown', {key: 'Enter'});
+    wrapper.find('textarea').simulate('keydown', {key: 'Enter'});
 
     expect(onSearch).toHaveBeenCalledTimes(1);
   });
@@ -203,7 +203,7 @@ describe('Events > SearchBar', function () {
   it('sets maxLength property', async function () {
     const wrapper = mountWithTheme(<SearchBar {...props} maxQueryLength={10} />, options);
     await tick();
-    expect(wrapper.find('input').prop('maxLength')).toBe(10);
+    expect(wrapper.find('textarea').prop('maxLength')).toBe(10);
   });
 
   it('does not requery for event field values if query does not change', async function () {
@@ -216,7 +216,7 @@ describe('Events > SearchBar', function () {
     await wrapper.update();
 
     // Click will fire "updateAutocompleteItems"
-    wrapper.find('input').simulate('click');
+    wrapper.find('textarea').simulate('click');
     await tick();
     wrapper.update();
 
@@ -276,7 +276,7 @@ describe('Events > SearchBar', function () {
       })
     );
     selectFirstAutocompleteItem(wrapper);
-    expect(wrapper.find('input').prop('value')).toBe('!gpu:"Nvidia 1080ti" ');
+    expect(wrapper.find('textarea').prop('value')).toBe('!gpu:"Nvidia 1080ti" ');
   });
 
   it('stops searching after no values are returned', async function () {

+ 2 - 2
tests/js/spec/components/modals/addDashboardWidgetModal.spec.jsx

@@ -37,7 +37,7 @@ function getDisplayType(wrapper) {
 }
 
 async function setSearchConditions(el, query) {
-  el.find('input')
+  el.find('textarea')
     .simulate('change', {target: {value: query}})
     .getDOMNode()
     .setSelectionRange(query.length, query.length);
@@ -45,7 +45,7 @@ async function setSearchConditions(el, query) {
   await tick();
   await el.update();
 
-  el.find('input').simulate('keydown', {key: 'Enter'});
+  el.find('textarea').simulate('keydown', {key: 'Enter'});
 }
 
 describe('Modals -> AddDashboardWidgetModal', function () {

+ 32 - 32
tests/js/spec/components/smartSearchBar.spec.jsx

@@ -100,16 +100,16 @@ describe('SmartSearchBar', function () {
 
       options
     );
-    searchBar.find('input').simulate('focus');
-    searchBar.find('input').simulate('change', {target: {value: 'device:this'}});
+    searchBar.find('textarea').simulate('focus');
+    searchBar.find('textarea').simulate('change', {target: {value: 'device:this'}});
     await tick();
 
     const preventDefault = jest.fn();
-    searchBar.find('input').simulate('keyDown', {key: 'ArrowDown'});
-    searchBar.find('input').simulate('keyDown', {key: 'Enter', preventDefault});
+    searchBar.find('textarea').simulate('keyDown', {key: 'ArrowDown'});
+    searchBar.find('textarea').simulate('keyDown', {key: 'Enter', preventDefault});
     await tick();
 
-    expect(searchBar.find('input').props().value).toEqual(
+    expect(searchBar.find('textarea').props().value).toEqual(
       'device:"this is filled with spaces"'
     );
   });
@@ -135,16 +135,16 @@ describe('SmartSearchBar', function () {
 
       options
     );
-    searchBar.find('input').simulate('focus');
-    searchBar.find('input').simulate('change', {target: {value: 'device:this'}});
+    searchBar.find('textarea').simulate('focus');
+    searchBar.find('textarea').simulate('change', {target: {value: 'device:this'}});
     await tick();
 
     const preventDefault = jest.fn();
-    searchBar.find('input').simulate('keyDown', {key: 'ArrowDown'});
-    searchBar.find('input').simulate('keyDown', {key: 'Enter', preventDefault});
+    searchBar.find('textarea').simulate('keyDown', {key: 'ArrowDown'});
+    searchBar.find('textarea').simulate('keyDown', {key: 'Enter', preventDefault});
     await tick();
 
-    expect(searchBar.find('input').props().value).toEqual(
+    expect(searchBar.find('textarea').props().value).toEqual(
       'device:"this \\" is \\" filled \\" with \\" quotes"'
     );
   });
@@ -171,13 +171,13 @@ describe('SmartSearchBar', function () {
 
       options
     );
-    searchBar.find('input').simulate('focus');
-    searchBar.find('input').simulate('change', {target: {value: 'browser:'}});
+    searchBar.find('textarea').simulate('focus');
+    searchBar.find('textarea').simulate('change', {target: {value: 'browser:'}});
     await tick();
 
     // press enter
     const preventDefault = jest.fn();
-    searchBar.find('input').simulate('keyDown', {key: 'Enter', preventDefault});
+    searchBar.find('textarea').simulate('keyDown', {key: 'Enter', preventDefault});
     expect(onSearch).not.toHaveBeenCalled();
     expect(preventDefault).not.toHaveBeenCalled();
   });
@@ -204,21 +204,21 @@ describe('SmartSearchBar', function () {
 
       options
     );
-    searchBar.find('input').simulate('focus');
-    searchBar.find('input').simulate('change', {target: {value: 'bro'}});
+    searchBar.find('textarea').simulate('focus');
+    searchBar.find('textarea').simulate('change', {target: {value: 'bro'}});
     await tick();
 
     // Can't select with tab
-    searchBar.find('input').simulate('keyDown', {key: 'ArrowDown'});
-    searchBar.find('input').simulate('keyDown', {key: 'Tab'});
+    searchBar.find('textarea').simulate('keyDown', {key: 'ArrowDown'});
+    searchBar.find('textarea').simulate('keyDown', {key: 'Tab'});
     expect(onSearch).not.toHaveBeenCalled();
 
-    searchBar.find('input').simulate('change', {target: {value: 'browser:'}});
+    searchBar.find('textarea').simulate('change', {target: {value: 'browser:'}});
     await tick();
 
     // press enter
     const preventDefault = jest.fn();
-    searchBar.find('input').simulate('keyDown', {key: 'Enter', preventDefault});
+    searchBar.find('textarea').simulate('keyDown', {key: 'Enter', preventDefault});
     expect(onSearch).not.toHaveBeenCalled();
     // Prevent default since we need to select an item
     expect(preventDefault).toHaveBeenCalled();
@@ -255,7 +255,7 @@ describe('SmartSearchBar', function () {
       expect(searchBar.state().query).toEqual('two ');
     });
 
-    it('should not reset user input if a noop props change happens', function () {
+    it('should not reset user textarea if a noop props change happens', function () {
       const searchBar = mountWithTheme(
         <SmartSearchBar
           organization={organization}
@@ -272,7 +272,7 @@ describe('SmartSearchBar', function () {
       expect(searchBar.state().query).toEqual('two');
     });
 
-    it('should reset user input if a meaningful props change happens', function () {
+    it('should reset user textarea if a meaningful props change happens', function () {
       const searchBar = mountWithTheme(
         <SmartSearchBar
           organization={organization}
@@ -412,7 +412,7 @@ describe('SmartSearchBar', function () {
 
   describe('onKeyUp()', function () {
     describe('escape', function () {
-      it('blurs the input', function () {
+      it('blurs the textarea', function () {
         const wrapper = mountWithTheme(
           <SmartSearchBar
             organization={organization}
@@ -426,7 +426,7 @@ describe('SmartSearchBar', function () {
         const instance = wrapper.instance();
         jest.spyOn(instance, 'blur');
 
-        wrapper.find('input').simulate('keyup', {key: 'Escape'});
+        wrapper.find('textarea').simulate('keyup', {key: 'Escape'});
 
         expect(instance.blur).toHaveBeenCalledTimes(1);
       });
@@ -561,7 +561,7 @@ describe('SmartSearchBar', function () {
       expect(searchBar.state.activeSearchItem).toEqual(-1);
     });
 
-    it('sets state when incomplete tag as second input', async function () {
+    it('sets state when incomplete tag as second textarea', async function () {
       const props = {
         query: 'is:unresolved fu',
         organization,
@@ -718,9 +718,9 @@ describe('SmartSearchBar', function () {
       };
       const smartSearchBar = mountWithTheme(<SmartSearchBar {...props} />, options);
       const searchBar = smartSearchBar.instance();
-      const input = smartSearchBar.find('input');
+      const textarea = smartSearchBar.find('textarea');
       // start typing part of the tag prefixed by the negation operator!
-      input.simulate('change', {target: {value: 'event.type:error !ti'}});
+      textarea.simulate('change', {target: {value: 'event.type:error !ti'}});
       searchBar.getCursorPosition = jest.fn().mockReturnValueOnce(20);
       // use autocompletion to do the rest
       searchBar.onAutoComplete('title:', {});
@@ -736,17 +736,17 @@ describe('SmartSearchBar', function () {
       };
       const smartSearchBar = mountWithTheme(<SmartSearchBar {...props} />, options);
       const searchBar = smartSearchBar.instance();
-      const input = smartSearchBar.find('input');
+      const textarea = smartSearchBar.find('textarea');
 
       // leading wildcard
-      input.simulate('change', {target: {value: 'event.type:*err'}});
+      textarea.simulate('change', {target: {value: 'event.type:*err'}});
       searchBar.getCursorPosition = jest.fn().mockReturnValueOnce(20);
       // use autocompletion to do the rest
       searchBar.onAutoComplete('error', {});
       expect(searchBar.state.query).toEqual('event.type:error');
 
       // trailing wildcard
-      input.simulate('change', {target: {value: 'event.type:err*'}});
+      textarea.simulate('change', {target: {value: 'event.type:err*'}});
       searchBar.getCursorPosition = jest.fn().mockReturnValueOnce(20);
       // use autocompletion to do the rest
       searchBar.onAutoComplete('error', {});
@@ -762,15 +762,15 @@ describe('SmartSearchBar', function () {
       };
       const smartSearchBar = mountWithTheme(<SmartSearchBar {...props} />, options);
       const searchBar = smartSearchBar.instance();
-      const input = smartSearchBar.find('input');
+      const textarea = smartSearchBar.find('textarea');
 
-      input.simulate('change', {target: {value: 'user:'}});
+      textarea.simulate('change', {target: {value: 'user:'}});
       searchBar.getCursorPosition = jest.fn().mockReturnValueOnce(5);
       searchBar.onAutoComplete('id:1', {});
       expect(searchBar.state.query).toEqual('user:"id:1"');
 
       // try it with the SEARCH_WILDCARD
-      input.simulate('change', {target: {value: 'user:1*'}});
+      textarea.simulate('change', {target: {value: 'user:1*'}});
       searchBar.getCursorPosition = jest.fn().mockReturnValueOnce(5);
       searchBar.onAutoComplete('ip:127.0.0.1', {});
       expect(searchBar.state.query).toEqual('user:"ip:127.0.0.1"');

+ 5 - 5
tests/js/spec/views/issueList/overview.spec.jsx

@@ -173,7 +173,7 @@ describe('IssueList', function () {
       w.find('SavedSearchSelector DropdownMenu ButtonTitle').text();
 
     const getSearchBarValue = w =>
-      w.find('SmartSearchBarContainer StyledInput').prop('value').trim();
+      w.find('SmartSearchBarContainer textarea').prop('value').trim();
 
     const createWrapper = ({params, location, ...p} = {}) => {
       const newRouter = {
@@ -539,9 +539,9 @@ describe('IssueList', function () {
       await tick();
       await wrapper.update();
 
-      // Update the search input
+      // Update the search textarea
       wrapper
-        .find('IssueListFilters SmartSearchBar StyledInput input')
+        .find('IssueListFilters SmartSearchBar textarea')
         .simulate('change', {target: {value: 'dogs'}});
       // Submit the form
       wrapper.find('IssueListFilters SmartSearchBar form').simulate('submit');
@@ -588,7 +588,7 @@ describe('IssueList', function () {
       });
 
       wrapper
-        .find('SmartSearchBar input')
+        .find('SmartSearchBar textarea')
         .simulate('change', {target: {value: 'assigned:me level:fatal'}});
       wrapper.find('SmartSearchBar form').simulate('submit');
 
@@ -846,7 +846,7 @@ describe('IssueList', function () {
       });
 
       wrapper
-        .find('SmartSearchBar input')
+        .find('SmartSearchBar textarea')
         .simulate('change', {target: {value: 'assigned:me level:fatal'}});
       wrapper.find('SmartSearchBar form').simulate('submit');
 

+ 8 - 7
tests/js/spec/views/issueList/searchBar.spec.jsx

@@ -13,7 +13,8 @@ describe('IssueListSearchBar', function () {
     organization: {access: [], features: []},
   });
 
-  const clickInput = searchBar => searchBar.find('input[name="query"]').simulate('click');
+  const clickInput = searchBar =>
+    searchBar.find('textarea[name="query"]').simulate('click');
 
   beforeEach(function () {
     TagStore.reset();
@@ -170,7 +171,7 @@ describe('IssueListSearchBar', function () {
       jest.useRealTimers();
       const wrapper = mountWithTheme(<IssueListSearchBar {...props} />, routerContext);
 
-      wrapper.find('input').simulate('change', {target: {value: 'is:'}});
+      wrapper.find('textarea').simulate('change', {target: {value: 'is:'}});
       await tick();
       wrapper.update();
 
@@ -198,7 +199,7 @@ describe('IssueListSearchBar', function () {
       jest.useRealTimers();
       const wrapper = mountWithTheme(<IssueListSearchBar {...props} />, routerContext);
 
-      wrapper.find('input').simulate('change', {target: {value: 'is:'}});
+      wrapper.find('textarea').simulate('change', {target: {value: 'is:'}});
       await tick();
 
       wrapper.update();
@@ -206,18 +207,18 @@ describe('IssueListSearchBar', function () {
         wrapper.find('SearchListItem').at(0).find('li').prop('className')
       ).not.toContain('active');
 
-      wrapper.find('input').simulate('keyDown', {key: 'ArrowDown'});
+      wrapper.find('textarea').simulate('keyDown', {key: 'ArrowDown'});
       expect(wrapper.find('SearchListItem').at(0).find('li').prop('className')).toContain(
         'active'
       );
 
-      wrapper.find('input').simulate('keyDown', {key: 'ArrowDown'});
+      wrapper.find('textarea').simulate('keyDown', {key: 'ArrowDown'});
       expect(wrapper.find('SearchListItem').at(1).find('li').prop('className')).toContain(
         'active'
       );
 
-      wrapper.find('input').simulate('keyDown', {key: 'ArrowUp'});
-      wrapper.find('input').simulate('keyDown', {key: 'ArrowUp'});
+      wrapper.find('textarea').simulate('keyDown', {key: 'ArrowUp'});
+      wrapper.find('textarea').simulate('keyDown', {key: 'ArrowUp'});
       expect(
         wrapper.find('SearchListItem').last().find('li').prop('className')
       ).toContain('active');

+ 1 - 1
tests/js/spec/views/performance/transactionSummary.spec.jsx

@@ -302,7 +302,7 @@ describe('Performance > TransactionSummary', function () {
     wrapper.update();
 
     // Fill out the search box, and submit it.
-    const searchBar = wrapper.find('SearchBar input');
+    const searchBar = wrapper.find('SearchBar textarea');
     searchBar
       .simulate('change', {target: {value: 'user.email:uhoh*'}})
       .simulate('submit', {preventDefault() {}});

+ 1 - 1
tests/js/spec/views/performance/vitalDetail/index.spec.jsx

@@ -199,7 +199,7 @@ describe('Performance > VitalDetail', function () {
     wrapper.update();
 
     // Fill out the search box, and submit it.
-    const searchBar = wrapper.find('SearchBar input');
+    const searchBar = wrapper.find('SearchBar textarea');
     searchBar
       .simulate('change', {target: {value: 'user.email:uhoh*'}})
       .simulate('submit', {preventDefault() {}});