11import T from 'prop-types'
22import Table from './table'
33import { useFetchList } from '../../hooks/use-fetch-list'
4- import { useState } from 'react'
4+ import { useEffect , useRef , useState } from 'react'
55import Pagination from '../pagination'
6+ import qs from 'qs'
7+ import { Field , Form , Formik , useFormikContext } from 'formik'
8+ import Button from '../button'
69
7- function UsersTable ( { type, orgId, onRowClick } ) {
10+ /**
11+ * This is a helper component to auto-submit search values after a timeout
12+ */
13+ const AutoSubmitSearch = ( ) => {
14+ const timerRef = useRef ( null )
15+
16+ const { values, touched, submitForm } = useFormikContext ( )
17+
18+ useEffect ( ( ) => {
19+ // Check if input was touched. Formik behavior is to update 'touched'
20+ // flag on input blur or submit, but we want to submit changes also on
21+ // key press. 'touched' is not a useEffect dependency because it is
22+ // constantly updated without value changes.
23+ const isTouched = touched . search || values ?. search . length > 0
24+
25+ // If search is touched
26+ if ( isTouched ) {
27+ // Clear previous timeout, if exists
28+ if ( timerRef . current ) {
29+ clearTimeout ( timerRef . current )
30+ }
31+ // Define new timeout
32+ timerRef . current = setTimeout ( submitForm , 1000 )
33+ }
34+
35+ // Clear timeout on unmount
36+ return ( ) => timerRef . current && clearTimeout ( timerRef . current )
37+ } , [ values ] )
38+
39+ return null
40+ }
41+
42+ /**
43+ * The search input
44+ */
45+ const SearchInput = ( { onSearch, 'data-cy' : dataCy } ) => {
46+ return (
47+ < Formik
48+ initialValues = { { search : '' } }
49+ onSubmit = { ( { search } ) => onSearch ( search ) }
50+ >
51+ < Form
52+ className = 'form-control justify-start'
53+ style = { { alignItems : 'stretch' } }
54+ >
55+ < Field
56+ data-cy = { `${ dataCy } -search-input` }
57+ type = 'search'
58+ name = 'search'
59+ id = 'search'
60+ placeholder = 'Search username...'
61+ style = { { width : '12rem' } }
62+ />
63+ < Button
64+ data-cy = { `${ dataCy } -search-submit` }
65+ type = 'submit'
66+ variant = 'submit'
67+ useIcon = 'magnifier-left'
68+ flat
69+ />
70+ < AutoSubmitSearch />
71+ </ Form >
72+ </ Formik >
73+ )
74+ }
75+
76+ function UsersTable ( { type, orgId, onRowClick, isSearchable } ) {
877 const [ page , setPage ] = useState ( 1 )
78+ const [ search , setSearch ] = useState ( null )
979
1080 let apiBasePath
1181 let emptyMessage
1282 let columns
1383
84+ const querystring = qs . stringify ( {
85+ search,
86+ page,
87+ } )
88+
1489 switch ( type ) {
1590 case 'org-members' :
1691 apiBasePath = `/organizations/${ orgId } /members`
@@ -33,10 +108,24 @@ function UsersTable({ type, orgId, onRowClick }) {
33108 const {
34109 result : { data, pagination } ,
35110 isLoading,
36- } = useFetchList ( `${ apiBasePath } ?page=${ page } ` )
111+ } = useFetchList ( `${ apiBasePath } ?${ querystring } ` )
112+
113+ if ( ! isLoading && search ?. length > 0 ) {
114+ emptyMessage = 'Search returned no results.'
115+ }
37116
38117 return (
39118 < >
119+ { isSearchable && (
120+ < SearchInput
121+ data-cy = { `${ type } -table` }
122+ onSearch = { ( search ) => {
123+ // Reset to page 1 and search
124+ setPage ( 1 )
125+ setSearch ( search )
126+ } }
127+ />
128+ ) }
40129 < Table
41130 data-cy = { `${ type } -table` }
42131 rows = { data }
0 commit comments