Skip to content

Commit

Permalink
Merge 08ff8bb into f0c928f
Browse files Browse the repository at this point in the history
  • Loading branch information
Easybuoy committed Nov 13, 2019
2 parents f0c928f + 08ff8bb commit caa58d1
Show file tree
Hide file tree
Showing 24 changed files with 866 additions and 597 deletions.
10 changes: 6 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
[![Build Status](https://travis-ci.org/Easybuoy/pstk-swapi-challenge.svg?branch=master)](https://travis-ci.org/Easybuoy/pstk-swapi-challenge)
[![Coverage Status](https://coveralls.io/repos/github/Easybuoy/pstk-swapi-challenge/badge.svg?branch=develop)](https://coveralls.io/github/Easybuoy/pstk-swapi-challenge?branch=master)
[![Netlify Status](https://api.netlify.com/api/v1/badges/0e604b3a-dbf6-4c09-a802-0c0c401b1ed6/deploy-status)](https://app.netlify.com/sites/pstk-swapi-challenge/deploys)

## Pstk Swapi Challenge

<br>
Expand All @@ -10,22 +12,21 @@ Link to Pivotal Tracker: https://www.pivotaltracker.com/n/projects/2415802
Link to application: https://pstk-swapi-challenge.netlify.com/

## Built With

<ul>
<li><a href="https://reactjs.org">React</a></li>
<li><a href="https://redux.js.org/">Redux</a></li>
<li><a href="https://www.styled-components.com/">Styled-Components</a></li>
<li><a href="https://swapi.co/api/">Swapi API</a></li>
</ul>



## Testing Tools

<ul>
<li><a href="https://jestjs.io/">Jest</a></li>
<li><a href="https://airbnb.io/enzyme/">Enzyme</a></li>
</ul>


## Getting Started

<h3>Prerequisites</h3>
Expand Down Expand Up @@ -62,4 +63,5 @@ You need Nodejs Installed to be able to run this project on your machine.
<br>

## License
<h4>This project makes use of the MIT License which can be found <a href="https://github.com/Easybuoy/pstk-swapi-challenge/master/LICENSE">here</a></h4>

<h4>This project makes use of the MIT License which can be found <a href="https://github.com/Easybuoy/pstk-swapi-challenge/master/LICENSE">here</a></h4>
4 changes: 1 addition & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
"react-dom": "^16.11.0",
"react-preloading-component": "0.0.5",
"react-redux": "^7.1.3",
"react-router-dom": "^5.1.2",
"react-scripts": "3.2.0",
"react-toastify": "^5.4.0",
"redux": "^4.0.4",
Expand Down Expand Up @@ -61,7 +60,6 @@
},
"devDependencies": {
"enzyme-to-json": "^3.4.3",
"moxios": "^0.4.0",
"sinon": "^7.5.0"
"moxios": "^0.4.0"
}
}
4 changes: 3 additions & 1 deletion src/__mocks__/mock.js

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions src/actions/actions.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ describe('Actions', () => {
type: LOADING
}
];
store.dispatch(getCharacter([]));
store.dispatch(getCharacter({ characters: [''] }));
expect(store.getActions()).toEqual(expectedActions);
expect(store.getActions()).toMatchSnapshot();
done();
Expand Down Expand Up @@ -141,7 +141,7 @@ describe('Actions', () => {
}
];
const store = mockStore({});
return store.dispatch(getCharacter([''])).then(() => {
return store.dispatch(getCharacter({ characters: [''] })).then(() => {
expect(store.getActions()).toEqual(expectedActions);
done();
});
Expand Down
32 changes: 25 additions & 7 deletions src/actions/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import axios from 'axios';
import { toast } from 'react-toastify';

import { addMovieDataToLocalStorage, addMovieListToLocalStorage } from '../utils';
import {
LOADING,
ERROR,
Expand All @@ -8,13 +10,16 @@ import {
SET_MOVIE,
SET_CHARACTERS
} from './types';
import { toast } from 'react-toastify';

export const getMovies = () => dispatch => {
dispatch({ type: LOADING });
return axios
.get('https://cors-anywhere.herokuapp.com/https://swapi.co/api/films')
.then(res => dispatch({ type: SET_MOVIES, payload: res.data.results }))

.then(res => {
addMovieListToLocalStorage(res.data.results)
dispatch(setMovies(res.data.results))
})
.catch(err => {
if (err.response) {
dispatch({ type: ERROR, payload: err.response.data.detail });
Expand All @@ -32,8 +37,8 @@ export const getMovie = movie_url => dispatch => {
return axios
.get(`https://cors-anywhere.herokuapp.com/${movie_url}`)
.then(res => {
dispatch({ type: SET_MOVIE, payload: res.data });
dispatch(getCharacter(res.data.characters));
dispatch(setMovie(res.data));
dispatch(getCharacter(res.data));
})
.catch(err => {
if (err.response) {
Expand All @@ -47,16 +52,21 @@ export const getMovie = movie_url => dispatch => {
.finally(() => dispatch({ type: LOADING }));
};

export const getCharacter = character_urls => dispatch => {
export const getCharacter = movie => dispatch => {
const { characters } = movie;

dispatch({ type: LOADING });
return Promise.all(
character_urls.map(url =>
characters.map(url =>
axios
.get(`https://cors-anywhere.herokuapp.com/${url}`)
.then(data => data.data)
)
)
.then(res => dispatch(setCharacters(res)))
.then(res => {
addMovieDataToLocalStorage(movie, res);
dispatch(setCharacters(res));
})
.catch(err => {
if (err.response) {
dispatch({ type: ERROR, payload: err.response.data.detail });
Expand All @@ -76,3 +86,11 @@ export const selectMovie = movie => {
export const setCharacters = characters => {
return { type: SET_CHARACTERS, payload: characters };
};

export const setMovies = movies => {
return { type: SET_MOVIES, payload: movies };
};

export const setMovie = movie => {
return { type: SET_MOVIE, payload: movie };
};
Binary file modified src/assets/images/dropdown.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified src/assets/images/landing.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
85 changes: 56 additions & 29 deletions src/components/Characters/Character.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import PropTypes from 'prop-types';

import MovieDetails from '../Common/MovieDetails';
import { setCharacters } from '../../actions';
import Select from '../Common/Select';
import { Character as StyledCharacter } from '../../styles';
import {
formatGender,
Expand All @@ -28,21 +29,22 @@ export const sortArrow = order => {
}
};

export const Character = ({ movie, characters, setCharacters }) => {
export const Character = ({ movie, characters, setCharacters, loading }) => {
const [heightOrder, setHeightOrder] = useState(undefined);
const [nameOrder, setNameOrder] = useState(undefined);
const [genderValue] = useState('Filter');
const [genderValue, setGenderValue] = useState('Filter Gender');
const [stateCharacters, setStateCharacters] = useState([]);
const [movieTitle, setMovieTitle] = useState('');

useEffect(() => {
const timer = setTimeout(() => {
if (characters.length > 0) {
setStateCharacters(characters);
}
}, 3000);
return () => clearTimeout(timer);
if (stateCharacters.length === 0 || movie.title !== movieTitle) {
setStateCharacters(characters);
setMovieTitle(movie.title);
setGenderValue('Filter Gender')
}

// eslint-disable-next-line react-hooks/exhaustive-deps
});
}, [characters]);

const sortNameField = array => {
let sorted = [];
Expand Down Expand Up @@ -79,38 +81,65 @@ export const Character = ({ movie, characters, setCharacters }) => {
setCharacters(sorted);
};

const onSelectChange = e => {
const { title } = JSON.parse(e.target.value);

setGenderValue(e.target.value);
sortGenderField(stateCharacters, title);
};

const items = [
{title: 'ALL'},
{ title: 'M' },
{ title: 'F' },
{ title: 'H' },
{ title: 'N/A' }
];

if (loading) {
return <PreLoader />;
}

if (characters.length === 0) {
return (
<StyledCharacter>
<Select
defaultValue="Filter Gender"
value={genderValue}
onChange={onSelectChange}
items={items}
/>

<h2>No character to display with search criteria</h2>
</StyledCharacter>
);
}
if (characters.length > 0) {
const totalHeight = calculateHeights(characters);

return (
<StyledCharacter>
<MovieDetails movie={movie} />

<Select
defaultValue="Filter Gender"
value={genderValue}
onChange={onSelectChange}
items={items}
/>

<table className="fl-table">
<thead>
<tr>
<th
onDoubleClick={() => sortNameField(characters)}
onClick={() => sortNameField(characters)}
className="toggle name"
>
Name {sortArrow(nameOrder)}
</th>
<th className="toggle-gender">
Gender
<select
value={genderValue}
onChange={e =>
sortGenderField(stateCharacters, e.target.value)
}
>
<option defaultValue="Select Gender" disabled>
Filter
</option>
<option value="M">M</option>
<option value="F">F</option>
</select>
</th>
<th>Gender</th>
<th
onDoubleClick={() => sortHeightField(characters)}
onClick={() => sortHeightField(characters)}
className="toggle height"
>
Height {sortArrow(heightOrder)}
Expand Down Expand Up @@ -139,8 +168,6 @@ export const Character = ({ movie, characters, setCharacters }) => {
</StyledCharacter>
);
}

return <PreLoader />;
};

Character.propTypes = {
Expand All @@ -149,7 +176,7 @@ Character.propTypes = {
};

const mapStateToProps = state => ({
loading: state.loading,
loading: state.loading.loading,
error: state.error,
characters: state.swapi.characters
});
Expand Down
33 changes: 24 additions & 9 deletions src/components/Characters/Character.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,29 +23,44 @@ describe('<Character />', () => {

it('should call the mock onChange function', () => {
const wrapper = mount(<Character {...props} />);

wrapper.find('select').simulate('change', { preventDefault() {} });
const event = {
target: {
name: 'pollName',
value: JSON.stringify({ title: '', url: '' })
}
};
wrapper.find('select').simulate('change', event);
expect(wrapper).toMatchSnapshot();
expect(props.setCharacters).toBeCalled();
});

it('should call the mock onDOubleCLick name function', () => {
it('should call the mock onClick name function', () => {
const wrapper = shallow(<Character {...props} />);

wrapper.find('.name').simulate('dblclick', { preventDefault() {} });
const event = {
target: {
name: 'pollName',
value: JSON.stringify({ title: '', url: '' })
}
};
wrapper.find('.name').simulate('click', event);
expect(wrapper).toMatchSnapshot();
expect(props.setCharacters).toBeCalled();
});

it('should call the mock onDOubleCLick name function', () => {
it('should call the mock onClick name function', () => {
const wrapper = shallow(<Character {...props} />);

wrapper.find('.height').simulate('dblclick', { preventDefault() {} });
const event = {
target: {
name: 'pollName',
value: JSON.stringify({ title: '', url: '' })
}
};
wrapper.find('.height').simulate('click', event);
expect(wrapper).toMatchSnapshot();
expect(props.setCharacters).toBeCalled();
});

it('should call the mock onDOubleCLick name function', () => {
it('should call the mock name function', () => {
props.characters = [];
const wrapper = shallow(<Character {...props} />);

Expand Down
Loading

0 comments on commit caa58d1

Please sign in to comment.