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