Skip to content

Commit 901873c

Browse files
author
Yauhen
committed
Lesson 16: Work with API. Part I
1 parent 5a36c07 commit 901873c

File tree

10 files changed

+273
-1
lines changed

10 files changed

+273
-1
lines changed

src/16_api/components/input/input.css

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
.inputWrapper {
2+
position: relative;
3+
border-bottom: 1px solid #ededed;
4+
flex: 1 0 100%;
5+
}
6+
7+
.input {
8+
font-family: 'Indie Flower', cursive;
9+
padding: 16px 16px 16px 60px;
10+
border: none;
11+
background: rgba(0, 0, 0, 0.003);
12+
width: 100%;
13+
box-sizing: border-box;
14+
font-size: 28px;
15+
font-style: italic;
16+
box-shadow: inset 0 -2px 40px rgba(0,0,0,0.03);
17+
}
18+
19+
.fa-search {
20+
position: absolute;
21+
font-size: 24px;
22+
top: 50%;
23+
transform: translateY(-50%);
24+
left: 20px;
25+
}
26+
27+
.input::-webkit-input-placeholder {
28+
font-family: 'Indie Flower', cursive;
29+
}
30+
.input::-moz-placeholder {
31+
font-family: 'Indie Flower', cursive;
32+
}
33+
.input:-moz-placeholder {
34+
font-family: 'Indie Flower', cursive;
35+
}
36+
.input:-ms-input-placeholder {
37+
font-family: 'Indie Flower', cursive;
38+
}

src/16_api/components/input/input.jsx

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import React from 'react';
2+
import PropTypes from 'prop-types';
3+
4+
import './input.css';
5+
6+
const Input = ({ onChange, value, onKeyPress }) => (
7+
<div className="inputWrapper">
8+
<i className="fas fa-search" />
9+
<input
10+
className="input"
11+
placeholder="Click to search"
12+
onChange={onChange}
13+
onKeyPress={onKeyPress}
14+
value={value}
15+
/>
16+
</div>
17+
);
18+
19+
Input.propTypes = {
20+
onChange: PropTypes.func,
21+
onKeyPress: PropTypes.func,
22+
value: PropTypes.string,
23+
}
24+
25+
Input.defaultProps = {
26+
onChange: () => {},
27+
onKeyPress: () => {},
28+
value: ''
29+
}
30+
31+
export default Input;

src/16_api/components/news/news.css

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
.newsList {
2+
margin: 0;
3+
padding: 0;
4+
list-style-type: none;
5+
flex: 1 0 100%;
6+
}
7+
8+
.news {
9+
margin: 10px 0;
10+
padding: 20px 30px;
11+
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2),
12+
0 25px 50px 0 rgba(0, 0, 0, 0.1);
13+
}
14+
15+
.newsTitle {
16+
display: block;
17+
margin-bottom: 10px;
18+
}
19+
20+
.description span {
21+
padding: 0 10px;
22+
}
23+
24+
.description .text {
25+
padding-left: 0;
26+
}
27+
28+
.description span:not(:last-child) {
29+
border-right: 1px solid #000;
30+
}

src/16_api/components/news/news.jsx

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import React from 'react';
2+
import PropTypes from 'prop-types';
3+
4+
import './news.css';
5+
6+
const NewsPost = ({ author, created_at, num_comments, title, points, url }) => (
7+
<li className="news">
8+
<div className="description">
9+
<a href={url} className="newsTitle">{title}</a>
10+
<span className="text">{`${points} points`}</span>
11+
<span className="comments">{`${num_comments} comments`}</span>
12+
<span className="date">{new Date(created_at).toLocaleDateString()}</span>
13+
<span className="author">{author}</span>
14+
</div>
15+
</li>
16+
);
17+
18+
NewsPost.propTypes = {
19+
author: PropTypes.string,
20+
created_at: PropTypes.string.isRequired,
21+
num_comments: PropTypes.number,
22+
title: PropTypes.string,
23+
points: PropTypes.number,
24+
url: PropTypes.string,
25+
}
26+
27+
NewsPost.defaultProps = {
28+
author: '',
29+
num_comments: 0,
30+
title: 'Here should be a title',
31+
points: 0,
32+
url: '#'
33+
}
34+
35+
export default NewsPost;

src/16_api/components/title/title.css

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
.title {
2+
font-family: 'Permanent Marker', cursive;
3+
font-size: 80px;
4+
text-align: center;
5+
font-weight: 900;
6+
color: rgba(175, 47, 47, 0.15);
7+
margin-bottom: 20px;
8+
}

src/16_api/components/title/title.jsx

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import React from 'react';
2+
import PropTypes from 'prop-types';
3+
4+
import './title.css';
5+
6+
const Title = ({ title }) => (
7+
<h1 className="title">{title}</h1>
8+
);
9+
10+
Title.propTypes = {
11+
title: PropTypes.string,
12+
}
13+
14+
Title.defaultProps = {
15+
title: 'Simple title',
16+
}
17+
18+
export default Title;

src/16_api/containers/news/news.css

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
.wrapper {
2+
padding: 0 20px;
3+
position: relative;
4+
display: flex;
5+
flex-wrap: wrap;
6+
justify-content: space-between;
7+
}

src/16_api/containers/news/news.jsx

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import React, { Component } from 'react';
2+
3+
import Title from '../../components/title/title';
4+
import NewsPost from '../../components/news/news';
5+
import Input from '../../components/input/input';
6+
7+
const BASE_PATH = 'https://hn.algolia.com/api/v1';
8+
const SEARCH_PATH = '/search';
9+
const SEARCH_PARAM = 'query=';
10+
11+
class News extends Component {
12+
13+
state = {
14+
searchQuery: '',
15+
result: {},
16+
}
17+
18+
componentDidMount() {
19+
const { searchQuery } = this.state;
20+
this.fetchData(searchQuery);
21+
}
22+
23+
fetchData = (searchQuery) => {
24+
fetch(`${BASE_PATH}${SEARCH_PATH}?${SEARCH_PARAM}${searchQuery}`)
25+
.then(res => res.json())
26+
.then(result => this.setNews(result))
27+
.catch(error => error);
28+
}
29+
30+
handleInputChange = ({ target: { value } }) => {
31+
this.setState({
32+
searchQuery: value
33+
})
34+
}
35+
36+
getSearch = ({ key }) => {
37+
if(key === 'Enter') {
38+
const { searchQuery } = this.state;
39+
this.fetchData(searchQuery);
40+
}
41+
}
42+
43+
setNews = result => {
44+
this.setState({ result });
45+
}
46+
47+
render() {
48+
const { searchQuery, result } = this.state;
49+
const { hits = [] } = result;
50+
51+
return (
52+
<div className="wrapper">
53+
<Title title="Hacker News" />
54+
<Input onKeyPress={this.getSearch} onChange={this.handleInputChange} value={searchQuery} />
55+
<ul className="newsList">
56+
{hits.map(({ author, created_at, num_comments, objectID, title, points, url }) =>
57+
<NewsPost
58+
key={objectID}
59+
author={author}
60+
created_at={created_at}
61+
num_comments={num_comments}
62+
title={title}
63+
points={points}
64+
url={url}
65+
/>
66+
)}
67+
</ul>
68+
</div>
69+
);
70+
}
71+
}
72+
73+
export default News;
74+
75+
76+
77+
78+
79+
80+
81+
82+
83+
84+
85+
86+
87+
88+
89+
90+
91+
92+
93+
//

src/index.css

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
body {
2+
margin: 0;
3+
padding: 0;
4+
font-family: 'Roboto', sans-serif;
5+
}
6+
7+
#root {
8+
min-height: 100vh;
9+
display: grid;
10+
grid-auto-rows: auto 1fr auto;
11+
}

src/index.js

100644100755
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import React from 'react';
22
import ReactDOM from 'react-dom';
3-
import App from './15_context/Lesson.jsx';
3+
import './index.css';
4+
import App from './16_api/containers/news/news';
45
import registerServiceWorker from './registerServiceWorker';
56

67
ReactDOM.render(<App />, document.getElementById('root'));

0 commit comments

Comments
 (0)