11import React , { Component } from 'react' ;
22import axios from 'axios' ;
3+ import { sortBy } from 'lodash' ;
4+ import classNames from 'classnames' ;
35import './App.css' ;
46
57const DEFAULT_QUERY = 'redux' ;
@@ -11,6 +13,14 @@ const PARAM_SEARCH = 'query=';
1113const PARAM_PAGE = 'page=' ;
1214const PARAM_HPP = 'hitsPerPage=' ;
1315
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+
1424class App extends Component {
1525 _isMounted = false ;
1626
@@ -22,6 +32,9 @@ class App extends Component {
2232 searchKey : '' ,
2333 searchTerm : DEFAULT_QUERY ,
2434 error : null ,
35+ isLoading : false ,
36+ sortKey : 'NONE' ,
37+ isSortReverse : false ,
2538 } ;
2639
2740 this . needsToSearchTopStories = this . needsToSearchTopStories . bind ( this ) ;
@@ -30,6 +43,7 @@ class App extends Component {
3043 this . onSearchChange = this . onSearchChange . bind ( this ) ;
3144 this . onSearchSubmit = this . onSearchSubmit . bind ( this ) ;
3245 this . onDismiss = this . onDismiss . bind ( this ) ;
46+ this . onSort = this . onSort . bind ( this ) ;
3347 }
3448
3549 needsToSearchTopStories ( searchTerm ) {
@@ -53,11 +67,14 @@ class App extends Component {
5367 results : {
5468 ...results ,
5569 [ searchKey ] : { hits : updatedHits , page }
56- }
70+ } ,
71+ isLoading : false
5772 } ) ;
5873 }
5974
6075 fetchSearchTopStories ( searchTerm , page = 0 ) {
76+ this . setState ( { isLoading : true } ) ;
77+
6178 axios ( `${ PATH_BASE } ${ PATH_SEARCH } ?${ PARAM_SEARCH } ${ searchTerm } &${ PARAM_PAGE } ${ page } &${ PARAM_HPP } ${ DEFAULT_HPP } ` )
6279 . then ( result => this . setSearchTopStories ( result . data ) )
6380 . catch ( error => this . _isMounted && this . setState ( { error } ) ) ;
@@ -105,12 +122,20 @@ class App extends Component {
105122 } ) ;
106123 }
107124
125+ onSort ( sortKey ) {
126+ const isSortReverse = this . state . sortKey === sortKey && ! this . state . isSortReverse ;
127+ this . setState ( { sortKey, isSortReverse } ) ;
128+ }
129+
108130 render ( ) {
109131 const {
110132 searchTerm,
111133 results,
112134 searchKey,
113- error
135+ error,
136+ isLoading,
137+ sortKey,
138+ isSortReverse
114139 } = this . state ;
115140
116141 const page = (
@@ -142,13 +167,18 @@ class App extends Component {
142167 </ div >
143168 : < Table
144169 list = { list }
170+ sortKey = { sortKey }
171+ isSortReverse = { isSortReverse }
172+ onSort = { this . onSort }
145173 onDismiss = { this . onDismiss }
146174 />
147175 }
148176 < div className = "interactions" >
149- < Button onClick = { ( ) => this . fetchSearchTopStories ( searchKey , page + 1 ) } >
177+ < ButtonWithLoading
178+ isLoading = { isLoading }
179+ onClick = { ( ) => this . fetchSearchTopStories ( searchKey , page + 1 ) } >
150180 More
151- </ Button >
181+ </ ButtonWithLoading >
152182 </ div >
153183 </ div >
154184 ) ;
@@ -172,33 +202,109 @@ const Search = ({
172202 </ button >
173203 </ form >
174204
175- const Table = ( { list, onDismiss } ) =>
176- < div className = "table" >
177- { list . map ( item =>
178- < div key = { item . objectID } className = "table-row" >
205+ const Table = ( {
206+ list,
207+ sortKey,
208+ isSortReverse,
209+ onSort,
210+ onDismiss
211+ } ) => {
212+ const sortedList = SORTS [ sortKey ] ( list ) ;
213+ const reverseSortedList = isSortReverse
214+ ? sortedList . reverse ( )
215+ : sortedList ;
216+
217+ return (
218+ < div className = "table" >
219+ < div className = "table-header" >
179220 < span style = { { width : '40%' } } >
180- < a href = { item . url } > { item . title } </ a >
221+ < Sort
222+ sortKey = { 'TITLE' }
223+ onSort = { onSort }
224+ activeSortKey = { sortKey }
225+ >
226+ Title
227+ </ Sort >
181228 </ span >
182229 < span style = { { width : '30%' } } >
183- { item . author }
230+ < Sort
231+ sortKey = { 'AUTHOR' }
232+ onSort = { onSort }
233+ activeSortKey = { sortKey }
234+ >
235+ Author
236+ </ Sort >
184237 </ span >
185238 < span style = { { width : '10%' } } >
186- { item . num_comments }
239+ < Sort
240+ sortKey = { 'COMMENTS' }
241+ onSort = { onSort }
242+ activeSortKey = { sortKey }
243+ >
244+ Comments
245+ </ Sort >
187246 </ span >
188247 < span style = { { width : '10%' } } >
189- { item . points }
248+ < Sort
249+ sortKey = { 'POINTS' }
250+ onSort = { onSort }
251+ activeSortKey = { sortKey }
252+ >
253+ Points
254+ </ Sort >
190255 </ span >
191256 < span style = { { width : '10%' } } >
192- < Button
193- onClick = { ( ) => onDismiss ( item . objectID ) }
194- className = "button-inline"
195- >
196- Dismiss
197- </ Button >
257+ Archive
198258 </ span >
199259 </ div >
200- ) }
201- </ div >
260+ { reverseSortedList . map ( item =>
261+ < div key = { item . objectID } className = "table-row" >
262+ < span style = { { width : '40%' } } >
263+ < a href = { item . url } > { item . title } </ a >
264+ </ span >
265+ < span style = { { width : '30%' } } >
266+ { item . author }
267+ </ span >
268+ < span style = { { width : '10%' } } >
269+ { item . num_comments }
270+ </ span >
271+ < span style = { { width : '10%' } } >
272+ { item . points }
273+ </ span >
274+ < span style = { { width : '10%' } } >
275+ < Button
276+ onClick = { ( ) => onDismiss ( item . objectID ) }
277+ className = "button-inline"
278+ >
279+ Dismiss
280+ </ Button >
281+ </ span >
282+ </ div >
283+ ) }
284+ </ div >
285+ ) ;
286+ }
287+
288+ const Sort = ( {
289+ sortKey,
290+ activeSortKey,
291+ onSort,
292+ children
293+ } ) => {
294+ const sortClass = classNames (
295+ 'button-inline' ,
296+ { 'button-active' : sortKey === activeSortKey }
297+ ) ;
298+
299+ return (
300+ < Button
301+ onClick = { ( ) => onSort ( sortKey ) }
302+ className = { sortClass }
303+ >
304+ { children }
305+ </ Button >
306+ ) ;
307+ }
202308
203309const Button = ( {
204310 onClick,
@@ -213,6 +319,16 @@ const Button = ({
213319 { children }
214320 </ button >
215321
322+ const Loading = ( ) =>
323+ < div > Loading ...</ div >
324+
325+ const withLoading = ( Component ) => ( { isLoading, ...rest } ) =>
326+ isLoading
327+ ? < Loading />
328+ : < Component { ...rest } />
329+
330+ const ButtonWithLoading = withLoading ( Button ) ;
331+
216332export {
217333 Button ,
218334 Search ,
0 commit comments