-
Notifications
You must be signed in to change notification settings - Fork 100
PR _ Elsje and Bukunmi SL #80
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
base: main
Are you sure you want to change the base?
Changes from all commits
17d7068
25101b3
a08c524
5644453
84937cc
ab1ac4e
4e0d804
3077b97
6cabe2c
8ec552c
9369cdd
e966d32
5205b95
6d0f9c1
aa80fd0
56a1c05
db686e9
d5cca16
1a02d43
72568d9
9b60636
bd9c47c
48cf364
f91f107
44974e8
015dd2d
7d283b6
01cfe1e
0b0aa99
f625c72
893c835
fa4ac67
37337f9
5a24b7e
3391a73
70e8121
b47cb5a
1c53eea
db895cc
1fbf5cb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,123 @@ | ||
| # Weather Report | ||
|
|
||
| ## Visuals | ||
| _ Screenshots of our wire frames | ||
| - Following directions and reading comprehension | ||
| - Structuring content in HTML | ||
| - Applying styles with CSS | ||
| - Using variables | ||
| - Creating functions | ||
| - Manipulating the DOM | ||
| - Handling events | ||
| - Using Git | ||
| - Designing an intentional user experience | ||
| - Using Axios to call 3rd party APIs | ||
| - Using a proxy server to manage API keys | ||
| - Handling asynchronous calls | ||
|
|
||
| ## Goal | ||
|
|
||
| "Do I need to bring a jacket?" "Will it snow today?" "How hot will it be?" When we have questions like these, we often pull open a weather app! | ||
|
|
||
| Great weather apps do these two things: | ||
|
|
||
| 1. Pull weather data from a data source | ||
| 1. Display the weather in readable, compelling way | ||
|
|
||
|  | ||
| _Fig. DuckDuckGo's weather modal, which features city name, temperature reading, and weather icons._ | ||
|
|
||
| Our goal is to create a fun, small weather app that focuses on displaying the weather. | ||
|
|
||
| Our weather app will set the weather using user interaction and get the weather from a 3rd party API, OpenWeather. | ||
|
|
||
|  | ||
| _Fig. Example weather app displaying the weather for Hoboken._ | ||
|
|
||
|  | ||
| _Fig. Example weather app displaying the weather for Santo Domingo._ | ||
|
|
||
|  | ||
| _Fig. Example weather app displaying the weather for Bozeman._ | ||
|
|
||
|  | ||
| _Fig. Example weather app displaying the weather for Seattle._ | ||
|
|
||
| ## How to Complete and Submit | ||
|
|
||
| Follow the requirements below and build a small weather app. | ||
|
|
||
| At submission time, no matter where you are, submit the project via Learn. | ||
|
|
||
| ## JavaScript Requirements | ||
|
|
||
| You are required to use vanilla JavaScript for all parts of this project, including the optional enhancements. | ||
|
|
||
| ## Axios | ||
| The only extra JavaScript library we should load is [`Axios`](https://axios-http.com/docs/intro). | ||
|
|
||
| To download the `axios` node module, run `yarn install` | ||
|
|
||
| To include axios in your project, include the following script tag below the script tag linking `index.js`: | ||
| - `<script src="./node_modules/axios/dist/axios.min.js"></script>` | ||
|
|
||
| This should be done during the Wave 1 initial setup of your `index.html` page. | ||
|
|
||
| ## Workflow Requirements | ||
|
|
||
| - Create at least five git commits throughout this project | ||
| - Use the following files and folders: | ||
| - `index.html` | ||
| - `src/index.js` | ||
| - `styles/index.css` | ||
| - `assets` folder, potentially for holding images | ||
| - Create and add more folders and files as needed | ||
|
|
||
| ## Content Requirements | ||
|
|
||
| For this project, there are no requirements around color schemes, font choices, or layouts. | ||
|
|
||
| Note that applying styles with CSS is one of many learning goals of this project -- it is not the central learning goal. You may enjoy being creative with styles, but we encourage you to not concern yourself with getting the styles perfect. Remember, you can always choose to continue working on styling after you've completed all functional requirements. | ||
|
|
||
| However, _at a minimum_, your project must contain these elements: | ||
|
|
||
| Wave 2: | ||
|
|
||
| 1. An element that displays the temperature | ||
| 1. A clickable element to increase temperature | ||
| 1. A clickable element to decrease temperature | ||
| 1. An element that displays a landscape | ||
|
|
||
| Wave 3: | ||
|
|
||
| 1. An element that displays the city name | ||
| 1. An element that contains an `<input type="text">` element, used to rename the city | ||
|
|
||
| Wave 4: | ||
|
|
||
| 1. A clickable element to get the current temperature of the displayed city name | ||
|
|
||
| Wave 5: | ||
|
|
||
| 1. A `<select>` dropdown element to set the sky type | ||
| 1. An element that displays a sky | ||
|
|
||
| Wave 6: | ||
|
|
||
| 1. A clickable element to reset the city name | ||
|
|
||
| ## Detailed Content Requirements | ||
|
|
||
| [Wave 1: Create Wireframes and HTML](./ada-project-docs/wave-01.md) | ||
|
|
||
| [Wave 2: Increase and Decrease Temperature](./ada-project-docs/wave-02.md) | ||
|
|
||
| [Wave 3: Naming the City](./ada-project-docs/wave-03.md) | ||
|
|
||
| [Wave 4: Calling APIs](./ada-project-docs/wave-04.md) | ||
|
|
||
| [Wave 5: Selecting the Sky](./ada-project-docs/wave-05.md) | ||
|
|
||
| [Wave 6: Resetting the City Name](./ada-project-docs/wave-06.md) | ||
|
|
||
| [Optional Enhancements to inspire you](./ada-project-docs/optional-enhancements.md) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,12 +1,76 @@ | ||
| <!DOCTYPE html> | ||
| <html lang="en"> | ||
|
|
||
| <head> | ||
| <meta charset="UTF-8"> | ||
| <meta http-equiv="X-UA-Compatible" content="IE=edge"> | ||
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
| <title>Weather Report</title> | ||
| <link rel="stylesheet" href="styles/index.css"> | ||
| </head> | ||
|
|
||
| <body> | ||
|
|
||
| <header> | ||
| <h1>Interactive Weather Report</h1> | ||
| <h2 class="credits">By Bukunmi Gesinde & Elsje Slothower</h2> | ||
| </header> | ||
|
|
||
| <main class="container"> | ||
| <!-- column a --> | ||
| <section class="a"> | ||
| <div> | ||
| <h2>Current weather in:</h2> | ||
| <span id="currentCity">Seattle</span> | ||
| </div> | ||
| <div> | ||
| <h2 class="interactive">To view the precise temp in {city}, click here:</h2> | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looks like you intended to fill in the city name in this message. |
||
| <button id="tempRequest">In Real Time!</button> | ||
| </div> | ||
| <div> | ||
| <h2 class="interactive">Want to see a different city? Input one here:</h2> | ||
| <input type="text" id="newCity" value="Seattle" /> | ||
| <button id="changeCity" type="button">Reset</button> | ||
| </div> | ||
| </section> | ||
|
|
||
| <!-- column b --> | ||
| <section id="skyAndLandscape" class="weatherFlexBox"> | ||
| <div id="sky"></div> | ||
| <div id="landscape"></div> | ||
| </section> | ||
|
|
||
| <!-- column c --> | ||
| <section class="c"> | ||
| <div> | ||
| <h2>Current temperature is:</h2> | ||
| <span id="displayedTemp">72</span> | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Rather than hard-coding a value for UI elements (which just happens to match the initial value backing the control), I like to leave the initial markup blank, and update the UI on first load from the initial backing values in the JS code. So like here, leave off the 72, then when the document loads, update the UI in the JS to show the initial temperature value. This would protect us from changing the default starting JS value, but having it be out of sync with what the UI is showing. I would tend to do this for any of the configurable elements (temperature, ground, sky, city). |
||
| </div> | ||
| <div> | ||
| <h2 class="interactive">What if our city was...</h2> | ||
| <button id="increaseTemp" type="button">hotter</button> | ||
| <span>or</span> | ||
| <button id="decreaseTemp" type="button">colder</button> | ||
| <span>?</span> | ||
| </div> | ||
| <div> | ||
| <h2 class="interactive">Want to see a different sky? Select one here:</h2> | ||
| <select id="updateSky"> | ||
| <option>Sunny</option> | ||
| <option>Cloudy</option> | ||
| <option>Rainy</option> | ||
| <option>Snowy</option> | ||
| <option>Stormy</option> | ||
| </select> | ||
| </div> | ||
| </section> | ||
| </main> | ||
|
|
||
| <footer> | ||
| <h3 class="credits"> Bukunmi Gesinde & Elsje Slothower © 2022</h3> | ||
| </footer> | ||
|
|
||
| <script src="./node_modules/axios/dist/axios.min.js"></script> | ||
| <script src="./src/index.js" type="text/javascript"></script> | ||
| </body> | ||
|
|
||
| </html> | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,5 @@ | ||
| { | ||
| "dependencies": { | ||
| "axios": "^0.27.2" | ||
| "axios": "~1.1" | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,185 @@ | ||
| "use strict"; | ||
|
|
||
| const state = { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Learn shows placing the various values related to the app in a state object. We also showed this during a livecode for consistency. But personally, I would probably make them separate variables. |
||
| temp: 72, | ||
| city: "Seattle", | ||
| lat: "47.6038321", | ||
| lon: "-122.330062", | ||
| } | ||
|
|
||
| // Display Changes | ||
|
|
||
| const tempChange = () => { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider splitting this function into one that picks the color value and one that applies it to the UI. |
||
| let color = ""; | ||
| let temp = state.temp | ||
| if (temp > 80) { | ||
| color = "red"; | ||
| } else if (temp > 70) { | ||
| color = "orange"; | ||
| } else if (temp > 60) { | ||
| color = "yellow"; | ||
| } else if (temp > 50) { | ||
| color = "green"; | ||
| } else { | ||
| color = "blue"; | ||
| } | ||
|
Comment on lines
+15
to
+25
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Notice the repetition in these if/else if blocks. Code like this tends to be finicky, since humans tend to make easily overlooked typos that can be hard to track down. Consider a data structure and accompanying code similar to the following: const tempColors = [
[80, 'red'],
[70, 'orange'],
[60, 'yellow'],
[50, 'green'],
[null, 'blue'],
];
const getColorForTemp = (temp) => {
for (const [boundaryTemp, color] of tempColors) {
if (boundaryTemp === null || temp > boundaryTemp) {
return color;
}
}
};Looking for repetition in the structure of our code and refactoring it to be captured in a data structure instead can make our code more flexible (behavior can be changed solely by changing data) while simplifying the code working with the data. |
||
|
|
||
| temp = document.getElementById("displayedTemp"); | ||
| temp.className = color; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice approach of setting a css class with the style details rather than setting the style properties in the code here itself. |
||
| temp.textContent = String(state.temp) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We don't need to explicitly convert to a |
||
| } | ||
|
|
||
| const landscapeChange = () => { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Similar considerations as mentioned for the temp/color function would apply to the temp/landscape function. |
||
| let temp = state.temp; | ||
| let landscape = ""; | ||
| if (temp > 110) { | ||
| landscape = "🔥🌋🔥🌋🔥🌋🔥🌋🔥🌋"; | ||
| } else if (temp > 80) { | ||
| landscape = "🌵🐍🦂🌵🐫🌵🐍🏜🦂🐪"; | ||
| } else if (temp > 70) { | ||
| landscape = "🌸🌿🌼🌷🌻🌿☘️🌱🌻🌷"; | ||
| } else if (temp > 60) { | ||
| landscape = "🌾🌾🍃🪨🛤🌾🌾⛰️🌾🍃"; | ||
| } else if (temp > 50) { | ||
| landscape = "🌲🌲🌲🍂🌲🍁🌲🌲🍂🌲"; | ||
| } else if (temp > 0) { | ||
| landscape = "🧊❄️⛄🧊❄️⛄🏔️🧊❄️⛄"; | ||
| } else { | ||
| landscape = "🧊🧊🐧🧊🧊🧊🐧🧊🧊🧊"; | ||
| } | ||
|
|
||
| const updatedLandscape = document.getElementById("landscape"); | ||
| updatedLandscape.textContent = landscape; | ||
| } | ||
|
|
||
| const skyChange = () => { | ||
| let skySelection = document.getElementById("updateSky").value; | ||
| const skyEmojis = document.getElementById("sky"); | ||
| let sky = ""; | ||
| let atmosphere = ""; | ||
| if (skySelection == "Sunny") { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The comments about splitting the function responsibility for temp/color would also apply to the sky function here. To think about how we could make this more data-driven, consider what changes we might make to this function if we had a data structure resembling: const skySettings = {
Sunny: { text: 'sunny emoji', color: 'sunny color'},
Cloudy: { text: 'cloudy emoji', color: 'cloudy color'},
Rainy: { text: 'rainy emoji', color: 'rainy color'},
Snowy: { text: 'snowy emoji', color: 'snowy color'},
Stormy: { text: 'stormy emoji', color: 'stormy color'},
}; |
||
| sky = "☀️"; | ||
| atmosphere = "sunny"; | ||
| } else if (skySelection == "Cloudy") { | ||
| sky = "🌤️☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️"; | ||
| atmosphere = "cloudy"; | ||
| } else if (skySelection == "Rainy") { | ||
| sky = "🌦️🌧️🌧️🌧️🌧️🌧️🌧️🌧️🌧️🌧️🌧️🌧️"; | ||
| atmosphere = "rainy"; | ||
| } else if (skySelection == "Snowy") { | ||
| sky = "⛅🌨️🌨️🌨️🌨️🌨️🌨️🌨️🌨️🌨️🌨️🌨️"; | ||
| atmosphere = "snowy"; | ||
| } else if (skySelection == "Stormy") { | ||
| sky = "🌥️⛈️🌩️⛈️🌩️⛈️🌩️⛈️🌩️⛈️🌩️⛈️"; | ||
| atmosphere = "stormy"; | ||
| } | ||
|
|
||
| skyEmojis.textContent = sky; | ||
| const weatherBox = document.getElementById("skyAndLandscape"); | ||
| weatherBox.classList = `weatherFlexBox ${atmosphere}`; | ||
| } | ||
|
|
||
| const increasedTemp = () => { | ||
| state.temp += 1; | ||
| tempChange(); | ||
| landscapeChange(); | ||
| } | ||
|
|
||
| const decreasedTemp = () => { | ||
| state.temp -= 1; | ||
| tempChange(); | ||
| landscapeChange(); | ||
| } | ||
|
|
||
| const cityNameChange = () => { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is registered as an event handler, meaning the browser is going to try to pass event details in as the first parameter. So if we added a parameter, maybe called |
||
| let currentCity = document.getElementById("currentCity"); | ||
| let newCity = document.getElementById("newCity").value; | ||
| state.city = newCity; | ||
| currentCity.textContent = state.city; | ||
|
Comment on lines
+97
to
+98
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider creating additional helper functions to separate the responsibilities of user interaction (this event handler), storing the current application state (a new setter function), and updating the UI to reflect the current application state (a new function to refresh the city name-related UI). |
||
| } | ||
|
|
||
| const resetCity = () => { | ||
| let newCityInput = document.getElementById("newCity"); | ||
| newCityInput.value = "Seattle"; | ||
| cityNameChange(); | ||
| } | ||
|
|
||
| // API Calls | ||
|
|
||
| const toFahrenheit = (k) => (k - 273.15) * (9 / 5) + 32; | ||
|
|
||
| const getWeather = () => { | ||
| axios | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should always return any promise chains we create in helper functions ( return axios.get(...).then(...) |
||
| .get("http://127.0.0.1:5000/weather", { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider creating a global variable to hold the server part of the address you're contacting (the base URL), then use that variable in your API call addresses (such as by using template strings). If you need to point your endpoints elsewhere (such as when deploying), this can make it much easier to ensure everything is updated together. |
||
| params: { | ||
| lat: state.lat, | ||
| lon: state.lon, | ||
|
Comment on lines
+115
to
+116
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The app doesn't need to track the lat/lon over time. It's only used during the chained city/weather call. So rather than stashing those values in the state, consider making the lat and lon input parameters to this function. |
||
| } | ||
| }) | ||
| .then( (response) => { | ||
| const weather = response.data; | ||
| const cityTemp = Math.round(toFahrenheit(weather.main.temp)); | ||
| state.temp = cityTemp | ||
| console.log("success!!", response.status); | ||
| tempChange(); | ||
| landscapeChange(); | ||
|
Comment on lines
+122
to
+125
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Rather than stashing the temperature result directly in the application state here, consider returning the temperature from the |
||
| }) | ||
| .catch( (error) => { | ||
| console.log("weather error", | ||
| error.status, error.response); | ||
| }) | ||
| } | ||
|
|
||
| const getLatAndLon = () => { | ||
| let lat, lon; | ||
| axios | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Be sure to |
||
| .get("http://127.0.0.1:5000/location", { | ||
| params: { | ||
| q: state.city, | ||
| format: "json", | ||
| } | ||
| }) | ||
| .then( (response) => { | ||
| lat = response.data[0].lat; | ||
| lon = response.data[0].lon; | ||
| console.log("success!!", response.status); | ||
| state.lat = lat; | ||
| state.lon = lon; | ||
| getWeather(); | ||
|
Comment on lines
+146
to
+148
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Rather than stashing the lat/lon and then calling the weather api call directly, consider returning the lat/lon from this Assuming const getWeatherForCity = (cityName) => {
return getLatAndLon(cityName)
.then(({lat, lon}) => {
return getWeather(lat, lon);
})
};We could then use this as getWeatherForCity(state.city)
.then(temp => {
// code that was in getWeather above
state.temp = temp;
console.log("success!!", response.status);
tempChange();
landscapeChange();
}) |
||
| }) | ||
| .catch( (error) => { | ||
| console.log("location error", | ||
| error.status, error.response); | ||
| }) | ||
| } | ||
|
|
||
| // Event Listeners | ||
|
|
||
| const registerEventHandlers = () => { | ||
| tempChange(); | ||
| landscapeChange(); | ||
| cityNameChange(); | ||
| skyChange(); | ||
|
|
||
| const increasedTempButton = document.getElementById("increaseTemp"); | ||
| increasedTempButton.addEventListener("click", increasedTemp); | ||
|
|
||
| const decreasedTempButton = document.getElementById("decreaseTemp"); | ||
| decreasedTempButton.addEventListener("click", decreasedTemp); | ||
|
|
||
| const cityNameChangeInput = document.getElementById("newCity"); | ||
| cityNameChangeInput.addEventListener("input", cityNameChange); | ||
|
|
||
| const resetCityButton = document.getElementById("changeCity"); | ||
| resetCityButton.addEventListener("click", resetCity); | ||
|
|
||
| const realTimeTemp = document.getElementById("tempRequest"); | ||
| realTimeTemp.addEventListener("click", getLatAndLon); | ||
|
|
||
| const updateSky = document.getElementById("updateSky"); | ||
| updateSky.addEventListener("change", skyChange); | ||
|
|
||
| console.log("loaded successfully"); | ||
| } | ||
|
|
||
| document.addEventListener('DOMContentLoaded', registerEventHandlers); | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You don't have a class called
ain your styles. I'm assuming this was intended to associate this content with the first column in the grid. It turns out that thegrid-template-columnssetting simply takes the child elements in order, assigning them to the columns in order.