Repository: laboratoriobridge/bold
Pull request #517 (2020-01-09 15:29:04)

MultiSelect keeps filter after selecting item


Patch:
diff --git a/src/components/Select/SelectMulti/MultiDownshift.tsx b/src/components/Select/SelectMulti/MultiDownshift.tsx
index 6eafc5bbb..0b61714a7 100644
--- a/src/components/Select/SelectMulti/MultiDownshift.tsx
+++ b/src/components/Select/SelectMulti/MultiDownshift.tsx
@@ -79,7 +79,13 @@ export function MultiDownshift<T>(props: MultiDownshiftProps<T>) {
 
   // TODO: compose together props (rather than overwriting them) like downshift does
   return (
-    <SelectDownshift<T> {...rest} stateReducer={stateReducer} onChange={handleChange} selectedItem={null}>
+    <SelectDownshift<T>
+      {...rest}
+      stateReducer={stateReducer}
+      onChange={handleChange}
+      selectedItem={null}
+      keepFilterAfterSelect
+    >
       {downshift => children(getStateAndHelpers(downshift))}
     </SelectDownshift>
   )
diff --git a/src/components/Select/SelectSingle/SelectDownshift.tsx b/src/components/Select/SelectSingle/SelectDownshift.tsx
index 96c362259..286ccbb4e 100644
--- a/src/components/Select/SelectSingle/SelectDownshift.tsx
+++ b/src/components/Select/SelectSingle/SelectDownshift.tsx
@@ -15,6 +15,13 @@ export interface SelectDownshiftProps<T> extends Omit<DownshiftProps<T>, 'childr
    */
   openOnFocus?: boolean
 
+  /**
+   * Whether the current input filter should be kept after an item is selected.
+   * This is necessary for multi selects, for example, when the filter should be kept after an item is selected.
+   * @default false
+   */
+  keepFilterAfterSelect?: boolean
+
   children?(props: SelectDownshiftRenderProps<T>): React.ReactNode
 
   /**
@@ -56,7 +63,7 @@ export function defaultSelectFilter<T>(
  * Downshift extension with item and filter management.
  */
 export function SelectDownshift<T>(props: SelectDownshiftProps<T>) {
-  const { items, onFilterChange, children, createNewItem, openOnFocus, ...rest } = props
+  const { items, onFilterChange, children, createNewItem, openOnFocus, keepFilterAfterSelect, ...rest } = props
 
   const stateReducer = useMemo(() => createReducer(props), [openOnFocus, props.stateReducer])
 
@@ -87,8 +94,9 @@ export function SelectDownshift<T>(props: SelectDownshiftProps<T>) {
     }
 
     if (
-      changes.type === Downshift.stateChangeTypes.clickItem ||
-      changes.type === Downshift.stateChangeTypes.keyDownEnter
+      !keepFilterAfterSelect &&
+      (changes.type === Downshift.stateChangeTypes.clickItem ||
+        changes.type === Downshift.stateChangeTypes.keyDownEnter)
     ) {
       onFilterChange(null, getStateAndHelpers(downshift))
     }
diff --git a/yarn.lock b/yarn.lock
index 54a7ec0f7..45323fa2c 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1933,6 +1933,11 @@
     "@testing-library/dom" "^6.11.0"
     "@types/testing-library__react" "^9.1.2"
 
+"@testing-library/user-event@8.0.3":
+  version "8.0.3"
+  resolved "https://registry.yarnpkg.com/@testing-library/user-event/-/user-event-8.0.3.tgz#c3e304d2692b797ff0e3ced53c0597d341bb868b"
+  integrity sha512-ALoIj6GzxN0UzFiudSwENy2yTD1xHDW+KtuOch5xuqRgQr0AxGB8z+EQBxT6zlWgAoLGN8hmYxTTGRoQSOZtlg==
+
 "@types/babel__core@^7.1.0":
   version "7.1.3"
   resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.3.tgz#e441ea7df63cd080dfcd02ab199e6d16a735fc30"


Test patch:
diff --git a/src/components/Select/SelectMulti/SelectMulti.test.tsx b/src/components/Select/SelectMulti/SelectMulti.test.tsx
index 21f7dc80f..7e4e8b42d 100644
--- a/src/components/Select/SelectMulti/SelectMulti.test.tsx
+++ b/src/components/Select/SelectMulti/SelectMulti.test.tsx
@@ -1,9 +1,7 @@
 import { fireEvent, render } from '@testing-library/react'
 import { resetIdCounter } from 'downshift'
 import React from 'react'
-
 import { DefaultItemType } from '../SelectSingle'
-
 import { SelectMulti, SelectMultiProps } from './SelectMulti'
 
 jest.mock('../../../util/string')
@@ -99,6 +97,21 @@ it('should preserve external onBlur and onFocus event handlers', () => {
   expect(focus).toHaveBeenCalledTimes(1)
 })
 
+describe('filtering', () => {
+  it('should keep the current filter after an item is selected', async () => {
+    const { container } = render(<SelectTest />)
+    const input = container.querySelector('input')
+    fireEvent.focus(input)
+    fireEvent.change(input, { target: { value: 'pe' } })
+
+    expect(container.querySelectorAll('li').length).toEqual(3)
+    fireEvent.click(container.querySelectorAll('li')[0])
+
+    expect(input.value).toEqual('pe')
+    expect(container.querySelectorAll('li').length).toEqual(3)
+  })
+})
+
 describe('remove item', () => {
   it('should call onChange with the new value', () => {
     const onChange = jest.fn()