Skip to content

Commit 82497f2

Browse files
committed
chapter 5
1 parent 725d234 commit 82497f2

File tree

5 files changed

+300
-47
lines changed

5 files changed

+300
-47
lines changed

package-lock.json

Lines changed: 6 additions & 2 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,6 +3,8 @@
33
"version": "0.1.0",
44
"private": true,
55
"dependencies": {
6+
"classnames": "^2.2.5",
7+
"lodash": "^4.17.4",
68
"react": "^15.6.1",
79
"react-dom": "^15.6.1"
810
},

src/App.js

Lines changed: 139 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import React, { Component } from 'react';
2+
import { sortBy } from 'lodash';
3+
import classNames from 'classnames';
24
import './App.css';
35

46
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);
@@ -19,6 +29,9 @@ class App extends Component {
1929
results: null,
2030
searchKey: '',
2131
searchTerm: DEFAULT_QUERY,
32+
isLoading: false,
33+
sortKey: 'NONE',
34+
isSortReverse: false,
2235
};
2336

2437
this.needsToSearchTopstories = this.needsToSearchTopstories.bind(this);
@@ -27,6 +40,12 @@ class App extends Component {
2740
this.onSearchChange = this.onSearchChange.bind(this);
2841
this.onSearchSubmit = this.onSearchSubmit.bind(this);
2942
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 });
3049
}
3150

3251
needsToSearchTopstories(searchTerm) {
@@ -50,11 +69,13 @@ class App extends Component {
5069
results: {
5170
...results,
5271
[searchKey]: { hits: updatedHits, page }
53-
}
72+
},
73+
isLoading: false
5474
});
5575
}
5676

5777
fetchSearchTopstories(searchTerm, page) {
78+
this.setState({ isLoading: true });
5879
fetch(`${PATH_BASE}${PATH_SEARCH}?${PARAM_SEARCH}${searchTerm}&${PARAM_PAGE}${page}&${PARAM_HPP}${DEFAULT_HPP}`)
5980
.then(response => response.json())
6081
.then(result => this.setSearchTopstories(result))
@@ -101,7 +122,10 @@ class App extends Component {
101122
const {
102123
searchTerm,
103124
results,
104-
searchKey
125+
searchKey,
126+
isLoading,
127+
sortKey,
128+
isSortReverse
105129
} = this.state;
106130

107131
const page = (
@@ -129,12 +153,17 @@ class App extends Component {
129153
</div>
130154
<Table
131155
list={list}
156+
sortKey={sortKey}
157+
isSortReverse={isSortReverse}
158+
onSort={this.onSort}
132159
onDismiss={this.onDismiss}
133160
/>
134161
<div className="interactions">
135-
<Button onClick={() => this.fetchSearchTopstories(searchKey, page + 1)}>
162+
<ButtonWithLoading
163+
isLoading={isLoading}
164+
onClick={() => this.fetchSearchTopstories(searchKey, page + 1)}>
136165
More
137-
</Button>
166+
</ButtonWithLoading>
138167
</div>
139168
</div>
140169
);
@@ -158,54 +187,127 @@ const Search = ({
158187
</button>
159188
</form>
160189

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">
165205
<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>
167213
</span>
168214
<span style={{ width: '30%' }}>
169-
{item.author}
215+
<Sort
216+
sortKey={'AUTHOR'}
217+
onSort={onSort}
218+
activeSortKey={sortKey}
219+
>
220+
Author
221+
</Sort>
170222
</span>
171223
<span style={{ width: '10%' }}>
172-
{item.num_comments}
224+
<Sort
225+
sortKey={'COMMENTS'}
226+
onSort={onSort}
227+
activeSortKey={sortKey}
228+
>
229+
Comments
230+
</Sort>
173231
</span>
174232
<span style={{ width: '10%' }}>
175-
{item.points}
233+
<Sort
234+
sortKey={'POINTS'}
235+
onSort={onSort}
236+
activeSortKey={sortKey}
237+
>
238+
Points
239+
</Sort>
176240
</span>
177241
<span style={{ width: '10%' }}>
178-
<Button
179-
onClick={() => onDismiss(item.objectID)}
180-
className="button-inline"
181-
>
182-
Dismiss
183-
</Button>
242+
Archive
184243
</span>
185244
</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+
}
188272

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+
);
196283

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+
);
207292
}
208293

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+
209311
export default App;
210312

211313
export {

src/App.test.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ describe('Table', () => {
6262
{ title: '1', author: '1', num_comments: 1, points: 2, objectID: 'y' },
6363
{ title: '2', author: '2', num_comments: 1, points: 2, objectID: 'z' },
6464
],
65+
sortKey: 'TITLE',
66+
isSortReverse: false,
6567
};
6668

6769
it('renders', () => {

0 commit comments

Comments
 (0)