Share this blog!

JavaScript Weather App - Part 4 ~ Frontend Application Components

Hi all!

This is the final of the article series of building a simple JavaScript weather app!

Previous articles:

  1. Part 1 ~ API design
  2. Part 2 ~ Backend Application
  3. Part 3 ~ Frontend Application Basics 

In this article, we will be continuing our discussion on the frontend application mainly focusing on the implementation of components inside the src/components.

components/form.jsx file:

This file corresponds to the "Form" component and following are the main elements in it.

1. Optional error message: Note how error condition is checked and the error is displayed only if there is an error.

2. The title: The title of the application in an <h1> tag

3. The search bar: I used react-select to obtain a searchable select list (More information on react-select). Note how the onSubmit action handles the weather loading functionality.


import React from "react";
import "./form.style.css";
import Select from 'react-select';


const Form = props => {

  return (
    <div className="container">
      <form onSubmit={props.loadweather}>
        <div>{props.error ? error(props.errorMessage) : ""}</div>
        <div className="row">
            <div className="col-md-6 text-left">
                <h1>Weather App</h1>
            </div>
            <div className="col-md-4">
                    <Select
                        placeholder={"Search for a City..."}
                        className={"text-left"}
                        options={props.options}
                        isSearchable={true}
                        onChange={props.selectLocation}
                        name={"location"}
                        defaultValue={props.options.filter(option => option.value === props.selectedLocation)}
                    />
              </div>
              <div className="col-md-2">
                <button className="btn btn-primary">Get Weather</button>
             </div>
        </div>
      </form>
    </div>
  );
};

const error = props => {
  return (
    <div className="alert alert-danger mx-5" role="alert">
      {props}
    </div>
  );
};

export default Form;



components/weather.jsx file:

This component corresponds to the weather section of the application and this encapsulates the current weather condition and the forecasted weather data. When you look into the render returned, you can see how the data is passed into the child components.

Additionally, I used this component to hold 2 methods used by the child components:

  1. getConditionIcon: This method reads the condition name from data and returns the corresponding icon class name.
  2. getReadableDate: This method reads the unix timestamp from the data and returns a formatted date. E.g. converts "1580301692118" to "Thursday, 05/03/2020"


import React from "react";
import CurrentCondition from "./currentcondition";
import Forecast from "./forecast";
import 'react-table-6/react-table.css';
import './weather.style.css';

const Weather = props => {
    console.log(props);
    if (!props.location || !props.data) {
      return <div />;
    }

  return (
      <div>
      <CurrentCondition
        location={props.location}
        data={props.data[0]}
      />
      <Forecast
        data={props.data} />
    </div>
  );
};


export default Weather;

export const getConditionIcon = (conditionName) => {
    const weatherIcon = {
      Thunderstorm: "wi-thunderstorm",
      Hail: "wi-hail",
      Drizzle: "wi-sleet",
      Rainy: "wi-storm-showers",
      Snow: "wi-snow",
      Atmosphere: "wi-fog",
      Sunny: "wi-day-sunny",
      Cloudy: "wi-cloudy",
      Hurricane: "wi-day-hurricane",
      Windy: "wi-windy",
      Tornado: "wi-day-tornado"
    };
  return weatherIcon[conditionName];
}

export const getReadableDate = (dateToRead) => {
    //convert unix to human readabale date
    var d = new Date(dateToRead);
    var days = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];

    var weekDay = days[d.getDay()];
    var date = d.getDate();
    var month = d.getMonth() + 1;
    var year = d.getFullYear();
    var readableDate = weekDay + ", " + date + "/" + month + "/" + year;
    return readableDate;
}



components/currentcondition.jsx file:

This file represents the "CurrentCondition" component that displays the weather of the current timestamp.

The returned rendering divides the row into 3 columns (using col-sm-4) and each data element is fed corresponding data.

The getReadableTime method converts the timestamp value to a readable time format using 12-hour clock. E.g. "19:12:00" is converted to "7.12 pm".



import React from "react";
import {getConditionIcon, getReadableDate} from './weather';

const CurrentCondition = props => {

    const parts = props.location.split(",");
    const city = parts[0];
    const region = parts[1];
    const country = parts[2];

  return (
      <div className="row">
          <div className="col-sm-4  color-card">
              <div className="container">
                  <h4>{city}</h4>
                  <h6>{region}, {country}</h6>
                  <p>{getReadableDate(props.data.forecastDate)}</p>
              </div>
          </div>
          <div className="col-sm-4 color-card">
              <div className="container">
                  <h4>{props.data.conditionName}</h4>
                  <div className="container">
                    <i className={`wi ${getConditionIcon(props.data.conditionName)} display-1`}></i>

                  </div>
                  <h1>{props.data.temperature}&deg;C</h1>
                  <p>
                    <span className="temp-low">{props.data.low} &deg;C</span>
                    &&/&&
                    <span className="temp-high">{props.data.high} &deg;C</span>
                  </p>
              </div>
          </div>

          <div className="col-sm-4 color-card">
              <div className="container ">
                  <p>Humidity: {props.data.humidity}%</p>
                  <p>Visibility: {props.data.visibility}km</p>
                  <p>Pressure: {props.data.pressure}hPa</p>
                  <p>Wind: {props.data.windSpeed}km/h</p>
                  <p>Sunrise: {getReadableTime(props.data.sunrise)}</p>
                  <p>Sunset: {getReadableTime(props.data.sunset)}</p>
              </div>
          </div>
      </div>
  );
};

function getReadableTime(timeToRead){
    var time = timeToRead.split(":");
    var hours = parseInt(time[0]);
    var suffix = "am";
    if(hours > 12){
        suffix = "pm";
        hours -= 12;
    }
    return hours + "." + time[1] + " " + suffix;
}

export default CurrentCondition;



components/forecast.jsx file:

This file corresponds to the table that displays the forecasted weather information. I have used ReactTable (imported from react-table-6) to display the forecasted weather in a tabular format (More information on ReactTable).



import React from "react";
import {getConditionIcon, getReadableDate} from './weather';
import ReactTable from "react-table-6";

const Forecast = props => {

    const data = props.data;

    const columns = [
      {
        id: 'date',
        Header: 'Date',
        accessor: d => getReadableDate(d.forecastDate) // String-based value accessors!
      }, {
        Header: 'Condition',
        accessor: 'conditionName',
        //Cell: props => <span className='number'>{props.value}</span> // Custom cell components!
      }, {
        id: 'conditionIcon',
        Header: 'ConditionIcon',
        accessor: d => getConditionIcon(d.conditionName),
        Cell: row => (
            <span>
              <i className={`wi ${row.value} display-5`} />
            </span>
          )
      }, {
        Header: 'High',
        accessor: 'high',
        Cell: row => (
            <span className="temp-high">{row.value} &deg;C</span>
        )
      }, {
        Header: 'Low',
        accessor: 'low',
        Cell: row => (
            <span className="temp-low">{row.value} &deg;C</span>
        )
      }
];

    const TheadComponent = props => null
  return (
      <div className="row">
        <div className="container color-card">

          <ReactTable
            data={data}
            columns={columns}
            minRows={0}
            showPagination={false}
            TheadComponent={TheadComponent}
            />
        </div>
      </div>
  );
};


export default Forecast;


components/weather.style.css file:

Last but not least, I have used a few custom CSS for styling purposes:



.container {
  margin: 2.2rem auto;
  padding: 20px;
}

.color-card{
    background: rgba(256, 256, 256, 0.6);
}

.container input,
.container ::-webkit-input-placeholder {
  /* color: white !important; */
}
.container input:focus {
  background-color: transparent;
  box-shadow: none !important;
  border: none;
  border-bottom: 2px solid tomato;
}

.col-centered{
    margin: 0 auto;
    float: none;
}

.temp-high{
    color: Tomato;
}

.temp-low{
    color: DodgerBlue;
}

.display-5{
    font-size: 1.5rem;
    font-weight: 300;
    line-height: 1.2;
}

That's it!! After adding some custom CSS, the end result should look like below.



Cheers!


Next PostNewer Post Previous PostOlder Post Home

0 comments:

Post a Comment