Skip to content

Commit

Permalink
without styles
Browse files Browse the repository at this point in the history
  • Loading branch information
Nikolaus-B committed Feb 16, 2024
1 parent a5c8fc9 commit 57bd770
Show file tree
Hide file tree
Showing 22 changed files with 828 additions and 54 deletions.
559 changes: 521 additions & 38 deletions package-lock.json

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,19 @@
"private": true,
"homepage": "https://Nikolaus-B.github.io/article-gallery/",
"dependencies": {
"@reduxjs/toolkit": "^2.2.1",
"@testing-library/jest-dom": "^5.16.3",
"@testing-library/react": "^12.1.4",
"@testing-library/user-event": "^13.5.0",
"axios": "^1.6.7",
"modern-normalize": "^2.0.0",
"react": "^18.1.0",
"react-dom": "^18.1.0",
"react-modal": "^3.16.1",
"react-redux": "^9.1.0",
"react-scripts": "5.0.1",
"styled-components": "^6.1.8",
"uid": "^2.0.2",
"web-vitals": "^2.1.3"
},
"scripts": {
Expand Down
12 changes: 12 additions & 0 deletions src/api.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import axios from 'axios';

const ApiKey = '0a1f54de98a94593b203abe9891393f7';
axios.defaults.baseURL = `
https://newsapi.org/`;

export const fetchArticles = async (page = 1, pageSize = 10) => {
const response = await axios.get(
`v2/top-headlines?country=us&apiKey=${ApiKey}&page=${page}&pageSize=${pageSize}`
);
return response.data.articles;
};
21 changes: 9 additions & 12 deletions src/components/App.jsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
import { ArticleList } from './ArticleList/ArticleList';
import { Container, GlobalStyle } from './GlobalStyle';
import { Searchbar } from './Searchbar/Searchbar';

export const App = () => {
return (
<div
style={{
height: '100vh',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
fontSize: 40,
color: '#010101'
}}
>
React homework template
</div>
<Container>
<Searchbar />
<ArticleList />
<GlobalStyle />
</Container>
);
};
13 changes: 13 additions & 0 deletions src/components/ArticleItem/ArticleItem.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import React from 'react';
import { Image, Item } from './ArticleItem.styled';

export const ArticleItem = ({ article }) => {
return (
<Item>
<Image src={article.urlToImage} alt="article title" />
<h1>{article.author || 'No author'} </h1>
<h2>{article.title || 'No title'} </h2>
<p>{article.description || 'No description avalable'} </p>
</Item>
);
};
12 changes: 12 additions & 0 deletions src/components/ArticleItem/ArticleItem.styled.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import styled from 'styled-components';

export const Item = styled.li`
border: 2px solid black;
border-radius: 20px;
padding: 10px 20px;
`;

export const Image = styled.img`
width: 350px;
`;
66 changes: 66 additions & 0 deletions src/components/ArticleList/ArticleList.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import React, { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import {
selectArticles,
selectArticlesToLoad,
selectFilter,
selectPage,
selectTotal,
} from '../../redux/acrticles/ariclesSelectors';
import { fetchArticles } from '../../redux/acrticles/operations';
import { ArticleItem } from 'components/ArticleItem/ArticleItem';
import { List } from './ArticleList.styled';
import { morePage } from '../../redux/acrticles/articlesSlice';
import { uid } from 'uid';

export const ArticleList = () => {
const dispatch = useDispatch();
const articles = useSelector(selectArticles);
const page = useSelector(selectPage);
const articlesToLoad = useSelector(selectArticlesToLoad);
const total = useSelector(selectTotal);
const filter = useSelector(selectFilter);

const articlesToLoadHandler = () => {
return total - page * 10 < 10 ? total % 10 : 10;
};

useEffect(() => {
dispatch(fetchArticles({ page, articlesToLoad }));
}, [dispatch, page, articlesToLoad]);

const filterArticles = () => {
return articles.filter(article => {
if (article.source.name === null || article.description === null) {
return article;
}
const articleName = article.source?.name.toLowerCase();
const articleDescription = article?.description.toLowerCase();

return (
articleName.includes(filter.toLowerCase()) ||
articleDescription.includes(filter)
);
});
};

const filteredArticles = filterArticles();

return (
<>
<List>
{filteredArticles.map(item => {
return <ArticleItem key={uid()} article={item} />;
})}
</List>
<button
disabled={articlesToLoad < 10}
onClick={() => {
dispatch(morePage(articlesToLoadHandler()));
}}
>
More Articles({articlesToLoad < 10 ? 0 : articlesToLoadHandler()})
</button>
</>
);
};
13 changes: 13 additions & 0 deletions src/components/ArticleList/ArticleList.styled.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import styled from 'styled-components';

export const List = styled.ul`
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 10px;
width: 500px;
margin-top: 20px;
`;
37 changes: 37 additions & 0 deletions src/components/GlobalStyle.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import styled, { createGlobalStyle } from 'styled-components';
import 'modern-normalize';

export const GlobalStyle = createGlobalStyle`
body {
margin: 0;
}
p,
h1,
h2,
h3,
h4,
h5,
h6 {
margin: 0;
}
ul {
margin: 0;
padding: 0;
list-style: none;
}
img {
display: block;
}
button {
cursor: pointer;
}
`;

export const Container = styled.div`
width: 1200px;
`;
9 changes: 9 additions & 0 deletions src/components/ModalBtn/ModalBtn.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import React from 'react';

export const ModalBtn = () => {
return (
<>
<button type="button"></button>
</>
);
};
Empty file.
25 changes: 25 additions & 0 deletions src/components/Searchbar/Searchbar.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import React from 'react';
import { useDispatch } from 'react-redux';
import { changeFilter } from '../../redux/acrticles/articlesSlice';

export const Searchbar = () => {
const dispatch = useDispatch();

// const onFormSubmit = e => {
// e.preventDefault();
// dispatch(changeFilter(e.target.input.value));
// e.target.input.value = '';
// };

return (
<div>
<input
id="input"
type="text"
onChange={e => {
dispatch(changeFilter(e.target.value));
}}
/>
</div>
);
};
Empty file.
2 changes: 0 additions & 2 deletions src/index.css
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
@import-normalize; /* bring in normalize.css styles */

body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
Expand Down
6 changes: 4 additions & 2 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ import React from 'react';
import ReactDOM from 'react-dom/client';
import { App } from 'components/App';
import './index.css';
import { Provider } from 'react-redux';
import { store } from '../src/redux/store';

ReactDOM.createRoot(document.getElementById('root')).render(
<React.StrictMode>
<Provider store={store}>
<App />
</React.StrictMode>
</Provider>
);
7 changes: 7 additions & 0 deletions src/redux/acrticles/ariclesSelectors.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export const selectArticles = state => state.articles.articles;
export const selectArticlesToLoad = state => state.articles.articlesToLoad;
export const selectPage = state => state.articles.page;
export const selectFilter = state => state.articles.filter;
export const selectTotal = state => state.articles.total;
export const selectIsLoading = state => state.articles.isLoading;
export const selectError = state => state.articles.error;
47 changes: 47 additions & 0 deletions src/redux/acrticles/articlesSlice.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { createSlice } from '@reduxjs/toolkit';
import { fetchArticles } from './operations';

const handlePending = state => {
state.isLoading = true;
};

const handleRejected = (state, action) => {
state.isLoading = false;
state.error = action.payload;
};

const articlesSlice = createSlice({
name: 'articles',
initialState: {
articles: [],
articlesToLoad: 10,
total: 0,
page: 1,
filter: '',
isLoading: false,
error: null,
},
reducers: {
morePage: (state, action) => {
state.page += 1;
state.articlesToLoad = action.payload;
},
changeFilter: (state, action) => {
state.filter = action.payload;
},
},
extraReducers: builder => {
builder
.addCase(fetchArticles.pending, handlePending)
.addCase(fetchArticles.fulfilled, (state, action) => {
state.total = action.payload.totalResults;
state.articles = [...state.articles, ...action.payload.articles];
state.isLoading = false;
state.error = null;
})
.addCase(fetchArticles.rejected, handleRejected);
},
});

export const { morePage, changeFilter } = articlesSlice.actions;
export const articlesReducer = articlesSlice.reducer;
20 changes: 20 additions & 0 deletions src/redux/acrticles/operations.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { createAsyncThunk } from '@reduxjs/toolkit';
import axios from 'axios';

const ApiKey = '0a1f54de98a94593b203abe9891393f7';
axios.defaults.baseURL = `
https://newsapi.org/`;

export const fetchArticles = createAsyncThunk(
'articles/fetchAll',
async ({ page, articlesToLoad }, thunkAPI) => {
try {
const response = await axios.get(
`v2/top-headlines?country=us&apiKey=${ApiKey}&page=${page}&pageSize=${articlesToLoad}`
);
return response.data;
} catch (e) {
return thunkAPI.rejectWithValue(e.message);
}
}
);
Empty file added src/redux/filter
Empty file.
1 change: 1 addition & 0 deletions src/redux/modal/modalSelectors.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const selectIsOpen = state => state.modal.isOpen;
15 changes: 15 additions & 0 deletions src/redux/modal/modalSlice.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { createSlice } from '@reduxjs/toolkit';

const modalSlice = createSlice({
name: 'modal',
initialState: {
isOpen: false,
},
reducers: {
isOpen: state => {
state.isOpen = !state.isOpen;
},
},
});

export const modalReducer = modalSlice.reducer;
10 changes: 10 additions & 0 deletions src/redux/store.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { configureStore } from '@reduxjs/toolkit';
import { articlesReducer } from './acrticles/articlesSlice';
import { modalReducer } from './modal/modalSlice';

export const store = configureStore({
reducer: {
articles: articlesReducer,
modal: modalReducer,
},
});

0 comments on commit 57bd770

Please sign in to comment.