Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

React Series Challenge - Daniel #66

Open
wants to merge 17 commits into
base: master
Choose a base branch
from
1 change: 1 addition & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
REACT_APP_GIPHY_API_KEY = NYBl3G1fuM3PcJWfeAv0wSS6fuHAJhIK
1 change: 1 addition & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/coverage/
/dist/
/node_modules/
.env
22 changes: 18 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,25 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"axios": "^0.18.0",
"react": "^16.2.0",
"react-dom": "^16.2.0",
"react-scripts": "1.1.1"
"react-redux": "^5.0.7",
"react-router-dom": "^4.3.1",
"react-scripts": "1.1.1",
"redux": "^4.0.0",
"redux-actions": "^2.4.0",
"redux-mock-store": "^1.5.3",
"redux-persist": "^5.10.0",
"redux-saga": "^0.16.0",
"styled-components": "^3.3.3"
},
"jest": {
"collectCoverageFrom": [
"**/src/**/*.{js,jsx}",
"!**/src/registerServiceWorker.{js,jsx}"
"!**/src/registerServiceWorker.{js,jsx}",
"!/node_modelus",
"!**/src/index.{js.jsx}"
],
"coverageThreshold": {
"global": {
Expand All @@ -27,10 +38,13 @@
"eject": "react-scripts eject",
"lint": "eslint ./",
"test": "yarn lint && yarn jest",
"jest": "react-scripts test --env=jsdom --coverage"
"jest": "react-scripts test --env=jsdom --coverage",
"precommit": "pretty-quick staged"
},
"devDependencies": {
"enzyme": "^3.3.0",
"enzyme-adapter-react-16": "^1.1.1"
"enzyme-adapter-react-16": "^1.1.1",
"prettier": "^1.13.7",
"pretty-quick": "^1.6.0"
}
}
1 change: 1 addition & 0 deletions public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.1.0/css/all.css" integrity="sha384-lKuwvrZot6UHsBSfcMvOkWwlCMgc0TaWr+30HWe3a4ltaBwTZhyTEggF5tJv8tbt" crossorigin="anonymous">
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Expand Down
28 changes: 0 additions & 28 deletions src/App.css

This file was deleted.

23 changes: 11 additions & 12 deletions src/App.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
import React, { Component } from "react";
import { Provider } from "react-redux";
import { store, persistor } from "./store";
import { PersistGate } from 'redux-persist/integration/react';
import "./global";
import Routes from "./Routes";

class App extends Component {
render() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h1 className="App-title">Welcome to React</h1>
</header>
<p className="App-intro">
To get started, edit <code>src/App.js</code> and save to reload.
</p>
</div>
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
<Routes />
</PersistGate>
</Provider>
);
}
}
Expand Down
10 changes: 10 additions & 0 deletions src/App.style.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import styled from "styled-components";

const Container = styled.div`
display: flex;
flex-direction: column;
flex: 1;
`;


export { Container };
9 changes: 9 additions & 0 deletions src/App.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

it('renders without crashing', () => {
const div = document.createElement('div');
ReactDOM.render(<App />, div);
ReactDOM.unmountComponentAtNode(div);
});
25 changes: 25 additions & 0 deletions src/Routes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import React from "react";
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import Home from "./pages/Home";
import Favorites from "./pages/Favorites";
import Navbar from "./components/Navigation/Navbar";

import { Container } from "./App.style";
import NotFound from "./pages/NotFound";

const Routes = () => {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this specific exercise you may not see the value but you are declaring the routes in a semi static way on a separate file. Remember that one of the key takeaways of react router 4 is integrating the routes through the component views dynamically, as your application grows and become more complex maintaining this static definition of routes may not be the best approach

return(
<Router>
<Container>
<Navbar />
<Switch>
<Route exact path="/" component={Home} />
<Route path="/favorites" component={Favorites} />
<Route component={NotFound} />
</Switch>
</Container>
</Router>
);
}

export default Routes;
9 changes: 9 additions & 0 deletions src/actions/favoriteGifs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
const ADD_FAVORITE_GIF = "ADD_FAVORITE_GIF";
const GET_FAVORITE_SEARCH_GIFS = "GET_FAVORITE_SEARCH_GIFS";

export default {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing action creators.

types: {
ADD_FAVORITE_GIF,
GET_FAVORITE_SEARCH_GIFS
}
};
12 changes: 12 additions & 0 deletions src/actions/searchFavoriteGifs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
const GET_FAVORITE_SEARCH_GIFS = "GET_FAVORITE_SEARCH_GIFS";

const getSearchFavoriteGifs = (payload = {}) => ({ type: GET_FAVORITE_SEARCH_GIFS, payload });

export default {
types: {
GET_FAVORITE_SEARCH_GIFS
},
creators: {
getSearchFavoriteGifs
}
};
9 changes: 9 additions & 0 deletions src/actions/searchGifs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
const FETCH_SEARCH_GIFS = "FETCH_SEARCH_GIFS";
const GET_SEARCH_GIFS = "GET_SEARCH_GIFS";

export default {
types: {
FETCH_SEARCH_GIFS,
GET_SEARCH_GIFS
}
};
9 changes: 9 additions & 0 deletions src/actions/trendingGifs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
const FETCH_TRENDING_GIFS = "FETCH_TRENDING_GIFS";
const GET_TRENDING_GIFS = "GET_TENDING_GIFS";

export default {
types: {
FETCH_TRENDING_GIFS,
GET_TRENDING_GIFS
}
};
20 changes: 20 additions & 0 deletions src/components/FavoriteButton/FavoriteButton.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import React from "react";
import { connect } from "react-redux";
import { Button } from "./FavoriteButtonStyled";

const AddTransactionButton = ({ gif, addFavoriteGif, favorite }) => (
<Button favorite={favorite} onClick={() => addFavoriteGif(gif)}>
<i className="fas fa-heart" />
</Button>
);

const mapDispatchToProps = dispatch => {
return {
addFavoriteGif: gif => dispatch({ type: "ADD_FAVORITE_GIF", payload: gif })
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With creators you could have done something like:

const mapDispatchToProps = dispatch => bindActionCreators({
  addFavoriteGif: creators.addFavoriteGif,
}, dispatch);

or

const mapDispatchToProps = { addFavoriteGif: creators.addFavoriteGif };

};
};

export default connect(
null,
mapDispatchToProps
)(AddTransactionButton);
32 changes: 32 additions & 0 deletions src/components/FavoriteButton/FavoriteButton.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import React from 'react';
import { mount, shallow } from 'enzyme';
import configureStore from 'redux-mock-store';
import FavoriteButton from "./FavoriteButton";


describe('test', () => {
const initialState = { };
const mockStore = configureStore();
let store,wrapper;

beforeEach(() => {
let props = {
favorite: true,
gif: {
id : "01",
images: {
url: "https://media0.giphy.com/media/4TgL0taO3uU5H6kKcQ/giphy-preview.webp"
}
}
};
store = mockStore(initialState)
wrapper = mount(shallow(<FavoriteButton store={store} {...props} />).get(0));
})
///// Test button onClick
test("Button on click", ()=> {
const button = wrapper.find("button").first()
button.simulate("click")
})
});


22 changes: 22 additions & 0 deletions src/components/FavoriteButton/FavoriteButtonStyled.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import styled from "styled-components";

const Button = styled.button`
position: absolute;
right: 2px;
top: 5px;
background-color: transparent;
color: ${props => props.favorite ? "#EF4748" : "#ccc" };
font-size: 2.5rem
font-weight: bold;
border: none;
&:focus {
outline: 0;
}
cursor: pointer

&:hover {
transform: translateY(-3px);
}
`;

export { Button };
34 changes: 34 additions & 0 deletions src/components/FavoriteGifs/FavoriteGifs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import React from "react";
import { connect } from "react-redux";
import FavoriteButton from "../FavoriteButton/FavoriteButton";
import {ItemGif, Image } from "../ListGifs/GifItemStyle";

const FavoritesGifs = ({ favoriteGifs, searchFavoriteGifs }) => {
let filterFavoriteGifs = null;
if (Object.keys(searchFavoriteGifs).length) {
filterFavoriteGifs = Object.keys(favoriteGifs)
.filter(key => searchFavoriteGifs.includes(key))
.reduce((obj, key) => {
obj[key] = favoriteGifs[key];
return obj
}, {});
}
return(
Object.keys(filterFavoriteGifs || favoriteGifs).map(key => (
<ItemGif key={key}>
<Image src={favoriteGifs[key].images.preview_webp.url} />
<FavoriteButton gif={favoriteGifs[key]} key={favoriteGifs[key].id} favorite />
</ItemGif>
))
)
};

const mapStateToProps = ({ favoriteGifs, searchFavoriteGifs }) => ({
favoriteGifs,
searchFavoriteGifs
});

export default connect(
mapStateToProps,
null
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you are not using mapDispatchToProps, you can omit the second parameter of connect;

)(FavoritesGifs);
40 changes: 40 additions & 0 deletions src/components/FavoriteGifs/FavoriteGifs.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import FavoriteGifs from "./FavoriteGifs";
import { shallow } from 'enzyme';
import configureStore from 'redux-mock-store'
import React from 'react';

describe('Todo component renders the list of trending gifs', () => {
const favoriteGifs = [
{
id: "01",
images: {
preview_webp: {
url: "https://media0.giphy.com/media/4TgL0taO3uU5H6kKcQ/giphy-preview.webp"
}
}
},
{
id: "02",
images: {
preview_webp: {
url: "https://media0.giphy.com/media/4TgL0taO3uU5H6kKcQ/giphy-preview.webp"
}
}
}];
const initialState = { favoriteGifs, searchFavoriteGifs: null };
const mockStore = configureStore();
let store,container;

beforeEach(()=>{
store = mockStore(initialState)
container = shallow(<FavoriteGifs store={store} /> )
});
it('+++ render the DUMB component', () => {
expect(container.length).toEqual(1)
});

it('+++ contains output', () => {
expect(container.prop('output')).toEqual(initialState.output)
});

});
11 changes: 11 additions & 0 deletions src/components/ListGifs/GifItem.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import React from "react";
import FavoriteButton from "../FavoriteButton/FavoriteButton";
import { ItemGif, Image } from "./GifItemStyle";

const GifItem = ({ gif, favoriteGifs }) =>
<ItemGif>
<Image src={gif.images.preview_webp.url} alt="Gif item" />
<FavoriteButton gif={gif} favorite ={favoriteGifs[gif.id]} />
</ItemGif>;

export default GifItem;
17 changes: 17 additions & 0 deletions src/components/ListGifs/GifItemStyle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import styled from "styled-components";

const ItemGif = styled.div`
position: relative;
`

const Image = styled.img`
height: 200px;
width: 200px;
padding: 0.5rem;
display: flex;
object-fit: cover;
justify-content: flex-end;

`;

export { ItemGif, Image };
Loading