1
1
import React , { Component } from 'react' ;
2
2
import fetch from 'isomorphic-fetch' ;
3
+ import { sortBy } from 'lodash' ;
4
+ import classNames from 'classnames' ;
3
5
import './App.css' ;
4
6
5
7
const DEFAULT_QUERY = 'redux' ;
@@ -11,6 +13,14 @@ const PARAM_SEARCH = 'query=';
11
13
const PARAM_PAGE = 'page=' ;
12
14
const PARAM_HPP = 'hitsPerPage=' ;
13
15
16
+ const SORTS = {
17
+ NONE : list => list ,
18
+ TITLE : list => sortBy ( list , 'title' ) ,
19
+ AUTHOR : list => sortBy ( list , 'author' ) ,
20
+ COMMENTS : list => sortBy ( list , 'num_comments' ) . reverse ( ) ,
21
+ POINTS : list => sortBy ( list , 'points' ) . reverse ( ) ,
22
+ } ;
23
+
14
24
class App extends Component {
15
25
constructor ( props ) {
16
26
super ( props ) ;
@@ -20,6 +30,9 @@ class App extends Component {
20
30
searchKey : '' ,
21
31
searchTerm : DEFAULT_QUERY ,
22
32
error : null ,
33
+ isLoading : false ,
34
+ sortKey : 'NONE' ,
35
+ isSortReverse : false ,
23
36
} ;
24
37
25
38
this . needsToSearchTopStories = this . needsToSearchTopStories . bind ( this ) ;
@@ -28,6 +41,12 @@ class App extends Component {
28
41
this . onSearchChange = this . onSearchChange . bind ( this ) ;
29
42
this . onSearchSubmit = this . onSearchSubmit . bind ( this ) ;
30
43
this . onDismiss = this . onDismiss . bind ( this ) ;
44
+ this . onSort = this . onSort . bind ( this ) ;
45
+ }
46
+
47
+ onSort ( sortKey ) {
48
+ const isSortReverse = this . state . sortKey === sortKey && ! this . state . isSortReverse ;
49
+ this . setState ( { sortKey, isSortReverse } ) ;
31
50
}
32
51
33
52
needsToSearchTopStories ( searchTerm ) {
@@ -51,11 +70,14 @@ class App extends Component {
51
70
results : {
52
71
...results ,
53
72
[ searchKey ] : { hits : updatedHits , page }
54
- }
73
+ } ,
74
+ isLoading : false
55
75
} ) ;
56
76
}
57
77
58
78
fetchSearchTopStories ( searchTerm , page = 0 ) {
79
+ this . setState ( { isLoading : true } ) ;
80
+
59
81
fetch ( `${ PATH_BASE } ${ PATH_SEARCH } ?${ PARAM_SEARCH } ${ searchTerm } &${ PARAM_PAGE } ${ page } &${ PARAM_HPP } ${ DEFAULT_HPP } ` )
60
82
. then ( response => response . json ( ) )
61
83
. then ( result => this . setSearchTopStories ( result ) )
@@ -103,7 +125,10 @@ class App extends Component {
103
125
searchTerm,
104
126
results,
105
127
searchKey,
106
- error
128
+ error,
129
+ isLoading,
130
+ sortKey,
131
+ isSortReverse
107
132
} = this . state ;
108
133
109
134
const page = (
@@ -135,13 +160,18 @@ class App extends Component {
135
160
</ div >
136
161
: < Table
137
162
list = { list }
163
+ sortKey = { sortKey }
164
+ isSortReverse = { isSortReverse }
165
+ onSort = { this . onSort }
138
166
onDismiss = { this . onDismiss }
139
167
/>
140
168
}
141
169
< div className = "interactions" >
142
- < Button onClick = { ( ) => this . fetchSearchTopStories ( searchKey , page + 1 ) } >
170
+ < ButtonWithLoading
171
+ isLoading = { isLoading }
172
+ onClick = { ( ) => this . fetchSearchTopStories ( searchKey , page + 1 ) } >
143
173
More
144
- </ Button >
174
+ </ ButtonWithLoading >
145
175
</ div >
146
176
</ div >
147
177
) ;
@@ -165,39 +195,111 @@ const Search = ({
165
195
</ button >
166
196
</ form >
167
197
168
- const Table = ( { list, onDismiss } ) =>
169
- < div className = "table" >
170
- { list . map ( item =>
171
- < div key = { item . objectID } className = "table-row" >
198
+ const Table = ( {
199
+ list,
200
+ sortKey,
201
+ isSortReverse,
202
+ onSort,
203
+ onDismiss
204
+ } ) => {
205
+ const sortedList = SORTS [ sortKey ] ( list ) ;
206
+ const reverseSortedList = isSortReverse
207
+ ? sortedList . reverse ( )
208
+ : sortedList ;
209
+
210
+ return (
211
+ < div className = "table" >
212
+ < div className = "table-header" >
172
213
< span style = { { width : '40%' } } >
173
- < a href = { item . url } > { item . title } </ a >
214
+ < Sort
215
+ sortKey = { 'TITLE' }
216
+ onSort = { onSort }
217
+ activeSortKey = { sortKey }
218
+ >
219
+ Title
220
+ </ Sort >
174
221
</ span >
175
222
< span style = { { width : '30%' } } >
176
- { item . author }
223
+ < Sort
224
+ sortKey = { 'AUTHOR' }
225
+ onSort = { onSort }
226
+ activeSortKey = { sortKey }
227
+ >
228
+ Author
229
+ </ Sort >
177
230
</ span >
178
231
< span style = { { width : '10%' } } >
179
- { item . num_comments }
232
+ < Sort
233
+ sortKey = { 'COMMENTS' }
234
+ onSort = { onSort }
235
+ activeSortKey = { sortKey }
236
+ >
237
+ Comments
238
+ </ Sort >
180
239
</ span >
181
240
< span style = { { width : '10%' } } >
182
- { item . points }
241
+ < Sort
242
+ sortKey = { 'POINTS' }
243
+ onSort = { onSort }
244
+ activeSortKey = { sortKey }
245
+ >
246
+ Points
247
+ </ Sort >
183
248
</ span >
184
249
< span style = { { width : '10%' } } >
185
- < Button
186
- onClick = { ( ) => onDismiss ( item . objectID ) }
187
- className = "button-inline"
188
- >
189
- Dismiss
190
- </ Button >
250
+ Archive
191
251
</ span >
192
252
</ div >
193
- ) }
194
- </ div >
253
+ { reverseSortedList . map ( item =>
254
+ < div key = { item . objectID } className = "table-row" >
255
+ < span style = { { width : '40%' } } >
256
+ < a href = { item . url } > { item . title } </ a >
257
+ </ span >
258
+ < span style = { { width : '30%' } } >
259
+ { item . author }
260
+ </ span >
261
+ < span style = { { width : '10%' } } >
262
+ { item . num_comments }
263
+ </ span >
264
+ < span style = { { width : '10%' } } >
265
+ { item . points }
266
+ </ span >
267
+ < span style = { { width : '10%' } } >
268
+ < Button
269
+ onClick = { ( ) => onDismiss ( item . objectID ) }
270
+ className = "button-inline"
271
+ >
272
+ Dismiss
273
+ </ Button >
274
+ </ span >
275
+ </ div >
276
+ ) }
277
+ </ div >
278
+ ) ;
279
+ }
195
280
196
- const Button = ( {
197
- onClick,
198
- className = '' ,
199
- children,
200
- } ) =>
281
+ const Sort = ( {
282
+ sortKey,
283
+ activeSortKey,
284
+ onSort,
285
+ children
286
+ } ) => {
287
+ const sortClass = classNames (
288
+ 'button-inline' ,
289
+ { 'button-active' : sortKey === activeSortKey }
290
+ ) ;
291
+
292
+ return (
293
+ < Button
294
+ onClick = { ( ) => onSort ( sortKey ) }
295
+ className = { sortClass }
296
+ >
297
+ { children }
298
+ </ Button >
299
+ ) ;
300
+ }
301
+
302
+ const Button = ( { onClick, className = '' , children } ) =>
201
303
< button
202
304
onClick = { onClick }
203
305
className = { className }
@@ -206,6 +308,16 @@ const Button = ({
206
308
{ children }
207
309
</ button >
208
310
311
+ const Loading = ( ) =>
312
+ < div > Loading ...</ div >
313
+
314
+ const withLoading = ( Component ) => ( { isLoading, ...rest } ) =>
315
+ isLoading
316
+ ? < Loading />
317
+ : < Component { ...rest } />
318
+
319
+ const ButtonWithLoading = withLoading ( Button ) ;
320
+
209
321
export {
210
322
Button ,
211
323
Search ,
0 commit comments