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

Dev #3

Open
wants to merge 19 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 4 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,33 +1,13 @@
# React Bootstrap
# React Weather App :umbrella:

React boilerplate project for Manchester Codes' projects.
Get the weather forecast :boom: app created with React Component Framework.

## Getting Started
![Screenshot from 2019-04-23 20-03-53](https://user-images.githubusercontent.com/26323783/56609685-9c05b480-6605-11e9-93d3-7ceccde52715.png)

### Clone down this repository (replace `<your_project_name>`:

```bash
git clone [email protected]:MCRcodes/react-bootstrap.git <your_project_name>
```

### Install dependencies

```bash
npm install
```

### Start up the application:
## to start the app...

```bash
npm start
```

### Visit `localhost:8080` in your browser.

You should see a **Hello World** message.

### Change the rendered output

You can change what is mounted to the DOM in `src/index.jsx`.

It might be a good idea to make an `App` component inside `App.jsx` (will likely handle your layout and routing), and to mount this to the DOM.
3 changes: 3 additions & 0 deletions __jest__/stub.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const stub = {};

export default stub;
41 changes: 41 additions & 0 deletions __tests__/components/forecast-summaries.test.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import React from 'react';
import ForecastSummary from '../../components/forcast-summary';
import ForecastSummaries from '../../components/forecast-summaries';
import Enzyme from 'enzyme';

const forecasts = [
{
date: 'date1',
description: 'desc1',
icon: 'icon1',
temperature: {
max: 999
}
},
{
date: 'date2',
description: 'desc2',
icon: 'icon2',
temperature: {
max: 909
}
}
];
it('renders the correct amount of summary components from the parent', () => {
const wrapper = Enzyme.shallow(<ForecastSummaries forecasts={forecasts} />);
expect(wrapper.find(ForecastSummary).length).toBe(2);
});

it('takes the forecast and passes the corect date values', () => {
const wrapper = Enzyme.shallow(<ForecastSummaries forecasts={forecasts} />);
expect(
wrapper.find(ForecastSummary).forEach((node, index) => {
expect(node.prop('date')).toEqual(forecasts[index].date);
expect(node.prop('description')).toEqual(forecasts[index].description);
expect(node.prop('icon')).toEqual(forecasts[index].icon);
expect(node.prop('temperature')).toEqual(
forecasts[index].temperature.max
);
})
);
});
17 changes: 17 additions & 0 deletions __tests__/components/forecast-summary.test.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import React from 'react';
import Enzyme from 'enzyme';
import ForecastSummary from '../../components/forcast-summary';

it('takes temperature, date, descripte and icon and displays them in spans', () => {
const wrapper = Enzyme.shallow(
<ForecastSummary
temperature="mockTemp"
date="mock date"
description="mock Description"
icon="mock icon"
/>
);
expect(wrapper.find('.forecast-summary-temperature').text()).toEqual(
'temperature Max: mockTemp℃'
);
});
10 changes: 10 additions & 0 deletions __tests__/components/location-details.test.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import React from "react";
import Enzyme from "enzyme";
import LocationDetails from "../../components/location-details";

it("takes a city and country and renders inside a h1", () => {
const wrapper = Enzyme.shallow(<LocationDetails city="foo" country="bar" />);

const text = wrapper.find("h1").text();
expect(text).toBe("foo, bar");
});
74 changes: 74 additions & 0 deletions components/app.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import React from 'react';
import LocationDetails from './location-details';
import ForecastSummaries from './forecast-summaries';
import ForecastDetails from './forecast-detils';
import SearchForm from './search-form';

import '../src/styles/app.scss';

const URL = 'https://mcr-codes-weather.herokuapp.com/forecast?city=';

class App extends React.Component {
constructor(props) {
super(props);
this.state = {
selectedDate: 0,
forecasts: [],
location: {
city: 'manchester',
country: '',
},
};
}

goFetchNow = city => {
fetch(`${URL}${city}`)
.then(data => data.json())
.then(data => {
this.setState({
forecasts: data.forecasts,
location: {
city: data.location.city,
country: data.location.country,
},
});
});
};

componentDidMount() {
this.goFetchNow(this.state.location.city);
}

handleForecastSelected = date => {
this.setState({
selectedDate: date,
});
};

render() {
const chooseSelectedForecastByDate = this.state.forecasts.find(forecast => {
return forecast.date === this.state.selectedDate.date;
});

return (
<div className="forecast">
<LocationDetails
city={this.state.location.city}
country={this.state.location.country}
/>
<SearchForm cityCallback={this.goFetchNow} />
<br />
<ForecastSummaries
forecasts={this.state.forecasts}
onForecastSelect={this.handleForecastSelected}
/>
<br />
{chooseSelectedForecastByDate && (
<ForecastDetails detail={chooseSelectedForecastByDate} />
)}
</div>
);
}
}

export default App;
43 changes: 43 additions & 0 deletions components/forcast-summary.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import React from 'react';
import WeatherIcon from 'react-icons-weather';
import moment from 'moment';

const dateStyle = {
color: '#f5d6ff',
};
const iconStyle = {
display: 'flex',
justifyContent: 'center',
position: 'relative',
fontSize: '80px',
color: '#bcf2ef',
};

const btnStyle = {
display: 'flex',
justifyContent: 'center',
position: 'relative',
};

const ForecastSummary = props => {
return (
<div className="forecast-summary">
<span style={dateStyle}>{moment(props.date).format('ddd-Do-MMM')}</span>
<br />
<span>{props.description}</span>
<br />
<hr />
<WeatherIcon style={iconStyle} name="owm" iconId={props.icon} />
<br />
<span className="forecast-summary-temperature">
temperature Max: {props.temperature}℃
</span>
<br />
<button style={btnStyle} onClick={() => props.onSelect(props)}>
More Details
</button>
</div>
);
};

export default ForecastSummary;
37 changes: 37 additions & 0 deletions components/forecast-detils.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import React from 'react';
import moment from 'moment';

const detailStyle = {
border: '3px black solid',
borderRadius: '7px',
padding: '10px 10px',
marginBottom: '7px',
};

const forecastDetailsWrapper = {
display: 'flex',
justifyContent: 'center',
};

const ForecastDetails = props => {
const { date, wind, humidity, temperature } = props.detail;
return (
<div style={forecastDetailsWrapper}>
<div style={detailStyle}>
<span>date: {moment(date).format('Do-MMM-YY ')}</span>
<br />
<span>
temperature min: {temperature.min}°C max: {temperature.max}°C
</span>
<br />
<span>humidity: {humidity}%</span>
<br />
<span>wind speed: {wind.speed} mph</span>
<br />
<span>wind direction: {wind.direction.toUpperCase()}</span>
</div>
</div>
);
};

export default ForecastDetails;
27 changes: 27 additions & 0 deletions components/forecast-summaries.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import React from 'react';
import PropTypes from 'prop-types';
import ForecastSummary from './forcast-summary';
import '../src/styles/forecast-summaries.scss';

const ForecastSummaries = props => {
return (
<div className="forecast-summaries">
{props.forecasts.map(forecast => (
<ForecastSummary
onSelect={props.onForecastSelect}
key={forecast.date}
date={forecast.date}
description={forecast.description}
icon={forecast.icon}
temperature={forecast.temperature.max}
/>
))}
</div>
);
};

ForecastSummaries.propTypes = {
onSelect: PropTypes.func,
};

export default ForecastSummaries;
15 changes: 15 additions & 0 deletions components/location-details.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import React from 'react';
import PropTypes from 'prop-types';

const LocationDetails = props => (
<h1 className="title">
{props.city}, {props.country}
</h1>
);

LocationDetails.propTypes = {
city: PropTypes.string.isRequired,
country: PropTypes.string.isRequired,
};

export default LocationDetails;
38 changes: 38 additions & 0 deletions components/search-form.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import React from 'react';

class SearchForm extends React.Component {
constructor(props) {
super(props);
this.state = {
searchText: '',
};
}

handleSearch = e => {
e.preventDefault();
this.props.cityCallback(this.state.searchText);
this.setState({
searchText: '',
});
};

handlechange = e => this.setState({ searchText: e.target.value });

render() {
return (
<form>
<input
placeholder="Search for your UK city"
value={this.state.searchText}
type="text"
onChange={this.handlechange}
/>
<button onClick={this.handleSearch} type="submit">
Search
</button>
</form>
);
}
}

export default SearchForm;
22 changes: 13 additions & 9 deletions index.html
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>React App</title>
<link rel="shortcut icon" type="image/x-icon" href="favicon.ico" />
</head>
<body>
<div id="root"></div>
<script type="text/javascript" src="app.js"></script>
</body>

<head>
<meta charset="UTF-8">
<title>React App</title>
<link rel="shortcut icon" type="image/x-icon" href="favicon.ico" />
<link href="https://fonts.googleapis.com/css?family=Rubik" rel="stylesheet">
</head>

<body>
<div id="root"></div>
<script type="text/javascript" src="app.js"></script>
</body>

</html>
Loading