Skip to content

Commit d594d4a

Browse files
committed
chapter 5
1 parent 9e7b95c commit d594d4a

File tree

5 files changed

+297
-33
lines changed

5 files changed

+297
-33
lines changed

package-lock.json

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
"version": "0.1.0",
44
"private": true,
55
"dependencies": {
6+
"classnames": "^2.2.5",
67
"isomorphic-fetch": "^2.2.1",
8+
"lodash": "^4.17.4",
79
"react": "^16.0.0",
810
"react-dom": "^16.0.0",
911
"react-scripts": "1.0.14"

src/App.js

Lines changed: 137 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import React, { Component } from 'react';
22
import fetch from 'isomorphic-fetch';
3+
import { sortBy } from 'lodash';
4+
import classNames from 'classnames';
35
import './App.css';
46

57
const DEFAULT_QUERY = 'redux';
@@ -11,6 +13,14 @@ const PARAM_SEARCH = 'query=';
1113
const PARAM_PAGE = 'page=';
1214
const 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+
1424
class App extends Component {
1525
constructor(props) {
1626
super(props);
@@ -20,6 +30,9 @@ class App extends Component {
2030
searchKey: '',
2131
searchTerm: DEFAULT_QUERY,
2232
error: null,
33+
isLoading: false,
34+
sortKey: 'NONE',
35+
isSortReverse: false,
2336
};
2437

2538
this.needsToSearchTopStories = this.needsToSearchTopStories.bind(this);
@@ -28,6 +41,12 @@ class App extends Component {
2841
this.onSearchChange = this.onSearchChange.bind(this);
2942
this.onSearchSubmit = this.onSearchSubmit.bind(this);
3043
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 });
3150
}
3251

3352
needsToSearchTopStories(searchTerm) {
@@ -51,11 +70,14 @@ class App extends Component {
5170
results: {
5271
...results,
5372
[searchKey]: { hits: updatedHits, page }
54-
}
73+
},
74+
isLoading: false
5575
});
5676
}
5777

5878
fetchSearchTopStories(searchTerm, page = 0) {
79+
this.setState({ isLoading: true });
80+
5981
fetch(`${PATH_BASE}${PATH_SEARCH}?${PARAM_SEARCH}${searchTerm}&${PARAM_PAGE}${page}&${PARAM_HPP}${DEFAULT_HPP}`)
6082
.then(response => response.json())
6183
.then(result => this.setSearchTopStories(result))
@@ -103,7 +125,10 @@ class App extends Component {
103125
searchTerm,
104126
results,
105127
searchKey,
106-
error
128+
error,
129+
isLoading,
130+
sortKey,
131+
isSortReverse
107132
} = this.state;
108133

109134
const page = (
@@ -135,13 +160,18 @@ class App extends Component {
135160
</div>
136161
: <Table
137162
list={list}
163+
sortKey={sortKey}
164+
isSortReverse={isSortReverse}
165+
onSort={this.onSort}
138166
onDismiss={this.onDismiss}
139167
/>
140168
}
141169
<div className="interactions">
142-
<Button onClick={() => this.fetchSearchTopStories(searchKey, page + 1)}>
170+
<ButtonWithLoading
171+
isLoading={isLoading}
172+
onClick={() => this.fetchSearchTopStories(searchKey, page + 1)}>
143173
More
144-
</Button>
174+
</ButtonWithLoading>
145175
</div>
146176
</div>
147177
);
@@ -165,39 +195,111 @@ const Search = ({
165195
</button>
166196
</form>
167197

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">
172213
<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>
174221
</span>
175222
<span style={{ width: '30%' }}>
176-
{item.author}
223+
<Sort
224+
sortKey={'AUTHOR'}
225+
onSort={onSort}
226+
activeSortKey={sortKey}
227+
>
228+
Author
229+
</Sort>
177230
</span>
178231
<span style={{ width: '10%' }}>
179-
{item.num_comments}
232+
<Sort
233+
sortKey={'COMMENTS'}
234+
onSort={onSort}
235+
activeSortKey={sortKey}
236+
>
237+
Comments
238+
</Sort>
180239
</span>
181240
<span style={{ width: '10%' }}>
182-
{item.points}
241+
<Sort
242+
sortKey={'POINTS'}
243+
onSort={onSort}
244+
activeSortKey={sortKey}
245+
>
246+
Points
247+
</Sort>
183248
</span>
184249
<span style={{ width: '10%' }}>
185-
<Button
186-
onClick={() => onDismiss(item.objectID)}
187-
className="button-inline"
188-
>
189-
Dismiss
190-
</Button>
250+
Archive
191251
</span>
192252
</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+
}
195280

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 }) =>
201303
<button
202304
onClick={onClick}
203305
className={className}
@@ -206,6 +308,16 @@ const Button = ({
206308
{children}
207309
</button>
208310

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+
209321
export {
210322
Button,
211323
Search,

src/App.test.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ describe('Table', () => {
6565
{ title: '1', author: '1', num_comments: 1, points: 2, objectID: 'y' },
6666
{ title: '2', author: '2', num_comments: 1, points: 2, objectID: 'z' },
6767
],
68+
sortKey: 'TITLE',
69+
isSortReverse: false,
6870
};
6971

7072
it('renders without crashing', () => {

0 commit comments

Comments
 (0)