mirror of
https://github.com/Raghu-Ch/front-end-coding-challenge.git
synced 2026-02-10 04:43:01 -05:00
posting challenge v1
This commit is contained in:
51
README.md
51
README.md
@@ -1 +1,50 @@
|
||||
# front-end-coding-challenge
|
||||
Front-End Coding Challenge
|
||||
===
|
||||
|
||||
### Premise
|
||||
It’s not always easy to evaluate a candidates technical abilities. While we love writing tests here, we hate asking people to take them during an interview. More importantly, we hate asking people to “white-board” solutions.
|
||||
|
||||
We—as an industry—can do better. So, just how can we do better?
|
||||
|
||||
### Solution
|
||||
We developed this coding challenge to help us better gauge your technical abilities. This challenge:
|
||||
|
||||
* can be completed on your own time
|
||||
* where you are most comfortable
|
||||
* on equipment you’re accustomed to
|
||||
* using the tools you’re proficient with
|
||||
|
||||
Basically, you have access to all the things that allow you to work best.
|
||||
|
||||
We hope you’ll enjoy working through this challenge as much as we’ve enjoyed creating it for you.
|
||||
|
||||
### Guidelines
|
||||
Because we’re awful developers, this project isn’t quite ready to release. There are some broken pieces that we’d like you to fix. To do that, you’ll need to:
|
||||
|
||||
1. Fork this project.
|
||||
2. Fix any problems you find with it.
|
||||
3. Open a PR with your changes.
|
||||
4. You have 24-hours.
|
||||
|
||||
Our awesome design team provided the following images to demonstrate what the site should look like: http://imgur.com/a/dDuKc
|
||||
|
||||
### FAQ
|
||||
**Q: What am I supposed to focus on?**
|
||||
|
||||
A: The entire project. There are problems with everything, it’s up to you to find them and fix them.
|
||||
|
||||
**Q: How do I know if I’ve completed the challenge?**
|
||||
|
||||
A: You don’t. But, that’s because _we_ don’t know either.
|
||||
|
||||
**Q: I can’t get past X, what do I do?**
|
||||
|
||||
A: Google is your friend.
|
||||
|
||||
**Q: How long should this take?**
|
||||
|
||||
A: That really depends on you.
|
||||
|
||||
**Q: Can I rewrite this in React?**
|
||||
|
||||
A: While React is a very popular library today, all of our code is written in Angular and we'd like to see your competancy with our stack.
|
||||
|
||||
34
bower.json
Normal file
34
bower.json
Normal file
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"name": "frontend-developer-challenge",
|
||||
"description": "Sheesh. There's a lot wrong with this project.",
|
||||
"main": "index.js",
|
||||
"authors": [
|
||||
"Ryan Provost"
|
||||
],
|
||||
"license": "ISC",
|
||||
"keywords": [
|
||||
"frontend",
|
||||
"challenge"
|
||||
],
|
||||
"homepage": "https://github.com/rprovost/code-test-creation",
|
||||
"private": true,
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"test",
|
||||
"tests"
|
||||
],
|
||||
"dependencies": {
|
||||
"angular": "^1.5.7",
|
||||
"font-awesome": "fontawesome#^4.6.3",
|
||||
"weather-icons": "^2.0.10",
|
||||
"express": "^4.14.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"angular-mocks": "^1.5.7",
|
||||
"hammerjs": "^2.0.8",
|
||||
"d3": "^4.2.2",
|
||||
"react": "^15.3.0"
|
||||
}
|
||||
}
|
||||
144
gulpfile.js
Normal file
144
gulpfile.js
Normal file
@@ -0,0 +1,144 @@
|
||||
'use strict';
|
||||
|
||||
const gulp = require('gulp');
|
||||
const pkg = require('./package.json');
|
||||
const plugins = require('gulp-load-plugins')();
|
||||
const browserSync = require('browser-sync').create();
|
||||
const karmaServer = require('karma').Server;
|
||||
|
||||
gulp.task('build', ['build:html', 'build:js', 'build:json', 'build:sass', 'build:libs']);
|
||||
gulp.task('watch2', ['build', 'watch:tests', 'watch:src', 'serve']);
|
||||
|
||||
/***
|
||||
* Output a summary of unit test code coverage
|
||||
**/
|
||||
gulp.task('test:coverage', (done) => {
|
||||
new KarmaServer({
|
||||
configFile: __dirname + '/karma.conf.js',
|
||||
singleRun: true,
|
||||
reporters: ['coverage'],
|
||||
coverageReporter: {
|
||||
type: ['text-summary']
|
||||
}
|
||||
}, done).start();
|
||||
|
||||
return;
|
||||
});
|
||||
|
||||
/***
|
||||
* Generate an HTML report that details coverage
|
||||
**/
|
||||
gulp.task('test:report', (done) => {
|
||||
new karmaServer({
|
||||
configFile: __dirname + '/karma.conf.js',
|
||||
singleRun: true,
|
||||
reporters: ['coverage'],
|
||||
coverageReporter: {
|
||||
type: ['html']
|
||||
}
|
||||
}, done).start();
|
||||
|
||||
return;
|
||||
});
|
||||
|
||||
/***
|
||||
* Run the project with BrowserSync
|
||||
**/
|
||||
gulp.task('serve', () => {
|
||||
browserSync.init({
|
||||
server: {
|
||||
baseDir: "./dist"
|
||||
}
|
||||
});
|
||||
|
||||
gulp.watch(['./dist/**/*.js', './dist/**/*.html'], browserSync.reload);
|
||||
});
|
||||
|
||||
/***
|
||||
* Reload or stream changes to browsers with BrowserSync
|
||||
**/
|
||||
gulp.task('watch:src', () => {
|
||||
gulp.watch('./src/**/*.scss', ['build:sass']);
|
||||
gulp.watch('./src/**/*.js', ['lint', 'build:js'], browserSync.reload);
|
||||
gulp.watch('./src/**/*.json', ['build:json'], browserSync.reload);
|
||||
gulp.watch('./src/**/*.html', ['build:html'], browserSync.reload);
|
||||
});
|
||||
|
||||
/***
|
||||
* Run test suite when affected source files change
|
||||
**/
|
||||
gulp.task('watch:tests',
|
||||
function (done) {
|
||||
new karmaServer({
|
||||
configFile: __dirname + '/karma.conf.js',
|
||||
singleRun: false,
|
||||
reporters: ['progress'],
|
||||
}, Done).start();
|
||||
});
|
||||
|
||||
/***
|
||||
* ESLint
|
||||
**/
|
||||
gulp.task(
|
||||
'lint', () => {
|
||||
let eslint = plugins.eslint;
|
||||
|
||||
gulp.src(['./src/**/*.js'])
|
||||
.pipe(eslint())
|
||||
.pipe(eslint.format())
|
||||
.pipe(eslint.failAfterError());
|
||||
});
|
||||
|
||||
/***
|
||||
* Copy the HTML files into the DIST folder
|
||||
**/
|
||||
gulp.task('build:html', () => {
|
||||
gulp.src('./src/**/*.html')
|
||||
.pipe(gulp.dest('./dist'));
|
||||
});
|
||||
|
||||
/***
|
||||
* Copy the JSON files into the DIST folder
|
||||
**/
|
||||
gulp.task('build:json', () => {
|
||||
gulp.src('./src/**/*.json')
|
||||
.pipe(gulp.dest('./dist'));
|
||||
});
|
||||
|
||||
/***
|
||||
* Build all Sass files into a single css file
|
||||
**/
|
||||
gulp.task('build:sass', () => {
|
||||
gulp.src('./src/app.scss')
|
||||
.pipe(plugins.sass.sync())
|
||||
.pipe(plugins.rename(pkg.name + '.css'))
|
||||
.pipe(gulp.dest('./dist'))
|
||||
.pipe(browserSync.stream());
|
||||
});
|
||||
|
||||
/***
|
||||
* Build all JS files into a single application file
|
||||
**/
|
||||
gulp.task('build:js', () => {
|
||||
let filename = pkg.name + '.js';
|
||||
let filenameMinified = pkg.name + '.min.js';
|
||||
|
||||
gulp.src(['!./src/**/*.spec.js', './src/**/*.js'])
|
||||
// .pipe(plugins.plumber())
|
||||
|
||||
.pipe(plugins.concat(filename))
|
||||
.pipe(gulp.dest('./dist'))
|
||||
});
|
||||
|
||||
/***
|
||||
* Copy all dependencies into the DIST folder
|
||||
**/
|
||||
gulp.task('build:libs', () => {
|
||||
gulp.src('./bower.json')
|
||||
.pipe(plugins.mainBowerFiles({
|
||||
overrides: {
|
||||
"font-awesome": { main: ['./css/font-awesome.css'] }
|
||||
}
|
||||
}))
|
||||
.pipe(gulp.dest('./dist/libs'));
|
||||
});
|
||||
16
karma.conf.js
Normal file
16
karma.conf.js
Normal file
@@ -0,0 +1,16 @@
|
||||
module.exports = function(config) {
|
||||
config.set({
|
||||
browsers: ['Chrome'],
|
||||
frameworks: ['jasmine'],
|
||||
files: [
|
||||
{pattern: 'src/**/*.json', included: false},
|
||||
'node_modules/karma-read-json/karma-read-json.js',
|
||||
'bower_components/angular/angular.js',
|
||||
'bower_components/angular-mocks/angular-mocks.js',
|
||||
'src/**/*.js'
|
||||
],
|
||||
preprocessors: {
|
||||
'src/**/*.js': ['coverage']
|
||||
},
|
||||
});
|
||||
};
|
||||
45
package.json
Normal file
45
package.json
Normal file
@@ -0,0 +1,45 @@
|
||||
{
|
||||
"name": "frontend-developer-challenge",
|
||||
"version": "1.0.0",
|
||||
"description": "Sheesh. There's a lot wrong with this project.",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/rprovost/code-test-creation.git"
|
||||
},
|
||||
"keywords": [
|
||||
"frontend",
|
||||
"challenge"
|
||||
],
|
||||
"author": "Ryan Provost",
|
||||
"license": "ISC",
|
||||
"bugs": {
|
||||
"url": "https://github.com/rprovost/code-test-creation/issues"
|
||||
},
|
||||
"homepage": "https://github.com/rprovost/code-test-creation#readme",
|
||||
"devDependencies": {
|
||||
"browser-sync": "^2.13.0",
|
||||
"express": "^4.14.0",
|
||||
"gulp": "^3.9.1",
|
||||
"gulp-concat": "^2.6.0",
|
||||
"gulp-eslint": "^3.0.1",
|
||||
"gulp-load-plugins": "^1.2.4",
|
||||
"gulp-main-bower-files": "^1.5.2",
|
||||
"gulp-plumber": "^1.1.0",
|
||||
"gulp-rename": "^1.2.2",
|
||||
"gulp-sass": "^2.3.2",
|
||||
"istanbul": "^0.4.4",
|
||||
"jasmine-core": "^2.4.1",
|
||||
"karma": "^1.1.1",
|
||||
"karma-chrome-launcher": "^1.0.1",
|
||||
"karma-coverage": "^1.1.1",
|
||||
"karma-coverage-es6": "^0.2.7",
|
||||
"karma-jasmine": "^1.0.2",
|
||||
"karma-phantomjs-launcher": "^1.0.1",
|
||||
"karma-read-json": "^1.1.0"
|
||||
},
|
||||
"dependencies": {}
|
||||
}
|
||||
68
src/app.js
Normal file
68
src/app.js
Normal file
@@ -0,0 +1,68 @@
|
||||
angular.module('weather', [])
|
||||
|
||||
.controller('forecast', ['$http', '$scope', function($http, $scope){
|
||||
|
||||
const _this = this;
|
||||
let default_city = 'New York, NY';
|
||||
|
||||
// recall the existing city or display the default
|
||||
$scope.name = $scope.name || default_city;
|
||||
|
||||
// holds the 48 conditions and their corresponding icons
|
||||
_this.conditions = new Array(12);
|
||||
|
||||
/***
|
||||
* there are 48 different condition codes that the api can return
|
||||
* this method makes those conditions and their corresponding icon mapping
|
||||
* available to the rest of the controller
|
||||
**/
|
||||
var GetConditionMap = function() {
|
||||
var success = function(response) {
|
||||
_this.conditions = response.data;
|
||||
};
|
||||
|
||||
const error = function(response) {
|
||||
|
||||
};
|
||||
|
||||
$http.get('conditions.json').then(success, error);
|
||||
}
|
||||
|
||||
/***
|
||||
* this method is expected to set the forecast objects
|
||||
**/
|
||||
$scope.getWeather = function(city) {
|
||||
var success = function(response) {
|
||||
var data = response.data.query.results.channel;
|
||||
|
||||
$scope.location = data.location;
|
||||
$scope.item = data.item;
|
||||
};
|
||||
|
||||
let Error = function(response) {
|
||||
};
|
||||
|
||||
$http.get(`http://query.yahooapis.com/v1/public/yql?q=select * from weather.forecast where woeid in (select woeid from geo.places(1) where text="${city}")&format=json&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys`)
|
||||
.then(success, Error);
|
||||
}
|
||||
|
||||
/***
|
||||
* this method is expected to return the icon for the corresponding condition code
|
||||
* from the codeToCondition map file
|
||||
**/
|
||||
$scope.getIcon = function(code) {
|
||||
return _this.conditions.filter(condition => condition.code == code)[0].icon;
|
||||
};
|
||||
|
||||
/***
|
||||
* load the condition code map
|
||||
* this maps the condition code returned from the api to the corresponding icon
|
||||
*/
|
||||
setTimeout(GetConditionMap,
|
||||
5000);
|
||||
|
||||
/***
|
||||
* load weather for the default city
|
||||
*/
|
||||
// $scope.getWeather(name);
|
||||
}]);
|
||||
4
src/app.scss
Normal file
4
src/app.scss
Normal file
@@ -0,0 +1,4 @@
|
||||
@import "styles/colors";
|
||||
@import "styles/page"
|
||||
@import "styles/menu";
|
||||
@import "styles/weather";
|
||||
116
src/app.spec.js
Normal file
116
src/app.spec.js
Normal file
@@ -0,0 +1,116 @@
|
||||
// testing controller
|
||||
describe('The weather app', function() {
|
||||
var $httpBackend,
|
||||
$scope,
|
||||
;
|
||||
|
||||
// Set up the module
|
||||
beforeEach(module('weather'));
|
||||
|
||||
beforeEach(inject(function($injector) {
|
||||
$httpBackend = $injector.get('$httpBackend');
|
||||
$scope = $injector.get('$rootScope');
|
||||
|
||||
var $controller = $injector.get('$controller');
|
||||
|
||||
createController = function() {
|
||||
return $controller('forecast', {'$scope' : $scope });
|
||||
};
|
||||
}));
|
||||
|
||||
afterEach(function() {
|
||||
$httpBackend.verifyNoOutstandingExpectation();
|
||||
$httpBackend.verifyNoOutstandingRequest();
|
||||
});
|
||||
|
||||
describe('condition map', function(){
|
||||
beforeEach(function(){
|
||||
});
|
||||
|
||||
it('should throw an error when the condition map cannot be loaded.', function(){
|
||||
$httpBackend.whenGET(/conditions/).respond(200, {});
|
||||
$httpBackend.whenGET(/query.yahooapis.com/).respond(200, readJSON('./src/forecast.json'));
|
||||
|
||||
var controller = createController();
|
||||
|
||||
expect(controller.conditions).not.toBe(undefined);
|
||||
expect( controller.conditions.length ).toBe(0);
|
||||
|
||||
|
||||
expect(controller.conditions.length).toBe(0);
|
||||
expect(controller.conditions[1]).toBe(undefined);
|
||||
});
|
||||
|
||||
it('should load all known conditions.', function(){
|
||||
$httpBackend.whenGET(/conditions/).respond(200, readJSON('./src/conditions.json'));
|
||||
$httpBackend.whenGET(/query.yahooapis.com/).respond(200, readJSON('./src/forecast.json'));
|
||||
|
||||
var controller = createController();
|
||||
|
||||
expect(controller.conditions).not.toBe(undefined);
|
||||
expect(controller.conditions.length).toBe(0);
|
||||
|
||||
$httpBackend.flush();
|
||||
|
||||
expect(controller.conditions.length).toBe(49);
|
||||
expect(controller.conditions[1].code).toBe(1);
|
||||
expect(controller.conditions[1].icon).toBe('wi-storm-showers');
|
||||
});
|
||||
});
|
||||
|
||||
describe('with condition map successfully loaded', function() {
|
||||
beforeEach(function(){
|
||||
});
|
||||
|
||||
it('should gracefully handle a weather forecast that cannot be loaded.', function() {
|
||||
|
||||
$httpBackend.whenGET(/query.yahooapis.com/).respond(500, {});
|
||||
|
||||
var controller = createController();
|
||||
|
||||
$httpBackend.flush() ;
|
||||
|
||||
expect($scope.item).toBe(undefined);
|
||||
expect($scope.location).toBe(undefined);
|
||||
|
||||
});
|
||||
|
||||
it('should load a weather forecast for a default location.', function() {
|
||||
|
||||
$httpBackend.whenGET(/query.yahooapis.com/).respond(200, readJSON('./src/forecast.json'));
|
||||
|
||||
var controller = createController();
|
||||
|
||||
expect($scope.item).toBe(undefined);
|
||||
expect($scope.location).toBe(undefined);
|
||||
|
||||
$httpBackend.flush();
|
||||
|
||||
expect( $scope.item ).not.toBe(undefined);
|
||||
expect($scope.location).not.toBe(undefined);
|
||||
|
||||
expect($scope.location.city).toBe('New York');
|
||||
// expect($scope.item.condition.code).toBe('29');
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
describe('with condition map and forecast successfully loaded', function() {
|
||||
beforeEach(function(){
|
||||
$httpBackend.whenGET(/conditions/).respond(200, readJSON('./src/conditions.json'));
|
||||
$httpBackend.whenGET(/query.yahooapis.com/).respond(readJSON('./src/forecast.json'));
|
||||
|
||||
var controller = createController();
|
||||
|
||||
$httpBackend.flush();
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
it('should provide an icon for the provided weather code.', function(){
|
||||
var icon = $scope.getIcon('29');
|
||||
expect(icon).toBe('wi-night-partly-cloudy');
|
||||
});
|
||||
});
|
||||
});
|
||||
51
src/conditions.json
Normal file
51
src/conditions.json
Normal file
@@ -0,0 +1,51 @@
|
||||
[
|
||||
{ "code": 0, "icon": "wi-tornado", "text": "tornado" },
|
||||
{ "code": 1, "icon": "wi-storm-showers", "text": "tropical storm" },
|
||||
{ "code": 2, "icon": "wi-hurricane", "text": "hurricane" },
|
||||
{ "code": 3, "icon": "wi-thunderstorm", "text": "severe thunderstorms" },
|
||||
{ "code": 4, "icon": "wi-day-sunny", "text": "thunderstorms" },
|
||||
{ "code": 5, "icon": "wi-sleet", "text": "mixed rain and snow" },
|
||||
{ "code": 6, "icon": "wi-sleet", "text": "mixed rain and sleet" },
|
||||
{ "code": 7, "icon": "wi-sleet", "text": "mixed snow and sleet" },
|
||||
{ "code": 8, "icon": "wi-rain-mix", "text": "freezing drizzle" },
|
||||
{ "code": 9, "icon": "wi-rain", "text": "drizzle" },
|
||||
{ "code": 10, "icon": "wi-rain-mix", "text": "freezing rain" },
|
||||
{ "code": 11, "icon": "wi-showers", "text": "showers" },
|
||||
{ "code": 12, "icon": "wi-showers", "text": "showers" },
|
||||
{ "code": 13, "icon": "wi-snow", "text": "snow flurries" },
|
||||
{ "code": 14, "icon": "wi-snow", "text": "light snow showers" },
|
||||
{ "code": 15, "icon": "wi-snow", "text": "blowing snow" },
|
||||
{ "code": 16, "icon": "wi-snow", "text": "snow" },
|
||||
{ "code": 17, "icon": "wi-hail", "text": "hail" },
|
||||
{ "code": 18, "icon": "wi-sleet", "text": "sleet" },
|
||||
{ "code": 19, "icon": "wi-dust", "text": "dust" },
|
||||
{ "code": 20, "icon": "wi-fog", "text": "foggy" },
|
||||
{ "code": 21, "icon": "wi-day-haze", "text": "haze" },
|
||||
{ "code": 22, "icon": "wi-smoke", "text": "smoky" },
|
||||
{ "code": 23, "icon": "wi-day-windy", "text": "blustery" },
|
||||
{ "code": 24, "icon": "wi-day-windy", "text": "windy" },
|
||||
{ "code": 25, "icon": "wi-thermometer-exterior", "text": "cold" },
|
||||
{ "code": 26, "icon": "wi-cloudy", "text": "cloudy" },
|
||||
{ "code": 27, "icon": "wi-night-cloudy", "text": "mostly cloudy (night)" },
|
||||
{ "code": 28, "icon": "wi-day-cloudy", "text": "mostly cloudy (day)" },
|
||||
{ "code": 29, "icon": "wi-night-partly-cloudy", "text": "partly cloudy (night)" },
|
||||
{ "code": 30, "icon": "wi-day-cloudy", "text": "partly cloudy (day)" },
|
||||
{ "code": 31, "icon": "wi-night-clear", "text": "clear (night)" },
|
||||
{ "code": 32, "icon": "wi-day-sunny", "text": "sunny" },
|
||||
{ "code": 33, "icon": "", "text": "fair (night)" },
|
||||
{ "code": 34, "icon": "", "text": "fair (day)" },
|
||||
{ "code": 35, "icon": "wi-day-rain-mix", "text": "mixed rain and hail" },
|
||||
{ "code": 36, "icon": "wi-hot", "text": "hot" },
|
||||
{ "code": 37, "icon": "wi-thunderstorm", "text": "isolated thunderstorms" },
|
||||
{ "code": 38, "icon": "wi-thunderstorm", "text": "scattered thunderstorms" },
|
||||
{ "code": 39, "icon": "wi-thunderstorm", "text": "scattered thunderstorms" },
|
||||
{ "code": 40, "icon": "wi-thunderstorm", "text": "scattered showers" },
|
||||
{ "code": 41, "icon": "wi-snow", "text": "heavy snow" },
|
||||
{ "code": 42, "icon": "wi-snow", "text": "scattered snow showers" },
|
||||
{ "code": 43, "icon": "wi-snow", "text": "heavy snow" },
|
||||
{ "code": 44, "icon": "wi-cloud", "text": "partly cloudy" },
|
||||
{ "code": 45, "icon": "wi-storm-showers", "text": "thundershowers" },
|
||||
{ "code": 46, "icon": "wi-snow", "text": "snow showers" },
|
||||
{ "code": 47, "icon": "wi-thunderstorm", "text": "isolated thundershowers" },
|
||||
{ "code": 3200, "icon": "wi-na", "text": "not available" }
|
||||
]
|
||||
138
src/forecast.json
Normal file
138
src/forecast.json
Normal file
@@ -0,0 +1,138 @@
|
||||
{
|
||||
"query": {
|
||||
"count": 1,
|
||||
"created": "2016-07-13T02:37:36Z",
|
||||
"lang": "en-US",
|
||||
"results": {
|
||||
"channel": {
|
||||
"units": {
|
||||
"distance": "mi",
|
||||
"pressure": "in",
|
||||
"speed": "mph",
|
||||
"temperature": "F"
|
||||
},
|
||||
"title": "Yahoo! Weather - New York, NY, US",
|
||||
"link": "http://us.rd.yahoo.com/dailynews/rss/weather/Country__Country/*https://weather.yahoo.com/country/state/city-2459115/",
|
||||
"description": "Yahoo! Weather for New York, NY, US",
|
||||
"language": "en-us",
|
||||
"lastBuildDate": "Tue, 12 Jul 2016 10:37 PM EDT",
|
||||
"ttl": "60",
|
||||
"location": {
|
||||
"city": "New York",
|
||||
"country": "United States",
|
||||
"region": " NY"
|
||||
},
|
||||
"wind": {
|
||||
"chill": "75",
|
||||
"direction": "180",
|
||||
"speed": "22"
|
||||
},
|
||||
"atmosphere": {
|
||||
"humidity": "73",
|
||||
"pressure": "1016.0",
|
||||
"rising": "0",
|
||||
"visibility": "16.1"
|
||||
},
|
||||
"astronomy": {
|
||||
"sunrise": "5:36 am",
|
||||
"sunset": "8:27 pm"
|
||||
},
|
||||
"image": {
|
||||
"title": "Yahoo! Weather",
|
||||
"width": "142",
|
||||
"height": "18",
|
||||
"link": "http://weather.yahoo.com",
|
||||
"url": "http://l.yimg.com/a/i/brand/purplelogo//uh/us/news-wea.gif"
|
||||
},
|
||||
"item": {
|
||||
"title": "Conditions for New York, NY, US at 09:00 PM EDT",
|
||||
"lat": "40.71455",
|
||||
"long": "-74.007118",
|
||||
"link": "http://us.rd.yahoo.com/dailynews/rss/weather/Country__Country/*https://weather.yahoo.com/country/state/city-2459115/",
|
||||
"pubDate": "Tue, 12 Jul 2016 09:00 PM EDT",
|
||||
"condition": {
|
||||
"code": "29",
|
||||
"date": "Tue, 12 Jul 2016 09:00 PM EDT",
|
||||
"temp": "75",
|
||||
"text": "Partly Cloudy"
|
||||
},
|
||||
"forecast": [{
|
||||
"code": "30",
|
||||
"date": "12 Jul 2016",
|
||||
"day": "Tue",
|
||||
"high": "81",
|
||||
"low": "69",
|
||||
"text": "Partly Cloudy"
|
||||
}, {
|
||||
"code": "47",
|
||||
"date": "13 Jul 2016",
|
||||
"day": "Wed",
|
||||
"high": "83",
|
||||
"low": "71",
|
||||
"text": "Scattered Thunderstorms"
|
||||
}, {
|
||||
"code": "4",
|
||||
"date": "14 Jul 2016",
|
||||
"day": "Thu",
|
||||
"high": "85",
|
||||
"low": "74",
|
||||
"text": "Thunderstorms"
|
||||
}, {
|
||||
"code": "4",
|
||||
"date": "15 Jul 2016",
|
||||
"day": "Fri",
|
||||
"high": "92",
|
||||
"low": "77",
|
||||
"text": "Thunderstorms"
|
||||
}, {
|
||||
"code": "30",
|
||||
"date": "16 Jul 2016",
|
||||
"day": "Sat",
|
||||
"high": "85",
|
||||
"low": "75",
|
||||
"text": "Partly Cloudy"
|
||||
}, {
|
||||
"code": "30",
|
||||
"date": "17 Jul 2016",
|
||||
"day": "Sun",
|
||||
"high": "87",
|
||||
"low": "75",
|
||||
"text": "Partly Cloudy"
|
||||
}, {
|
||||
"code": "4",
|
||||
"date": "18 Jul 2016",
|
||||
"day": "Mon",
|
||||
"high": "85",
|
||||
"low": "74",
|
||||
"text": "Thunderstorms"
|
||||
}, {
|
||||
"code": "4",
|
||||
"date": "19 Jul 2016",
|
||||
"day": "Tue",
|
||||
"high": "85",
|
||||
"low": "74",
|
||||
"text": "Thunderstorms"
|
||||
}, {
|
||||
"code": "4",
|
||||
"date": "20 Jul 2016",
|
||||
"day": "Wed",
|
||||
"high": "84",
|
||||
"low": "73",
|
||||
"text": "Thunderstorms"
|
||||
}, {
|
||||
"code": "30",
|
||||
"date": "21 Jul 2016",
|
||||
"day": "Thu",
|
||||
"high": "84",
|
||||
"low": "73",
|
||||
"text": "Partly Cloudy"
|
||||
}],
|
||||
"description": "<![CDATA[<img src=\"http://l.yimg.com/a/i/us/we/52/29.gif\"/>\n<BR />\n<b>Current Conditions:</b>\n<BR />Partly Cloudy\n<BR />\n<BR />\n<b>Forecast:</b>\n<BR /> Tue - Partly Cloudy. High: 81Low: 69\n<BR /> Wed - Scattered Thunderstorms. High: 83Low: 71\n<BR /> Thu - Thunderstorms. High: 85Low: 74\n<BR /> Fri - Thunderstorms. High: 92Low: 77\n<BR /> Sat - Partly Cloudy. High: 85Low: 75\n<BR />\n<BR />\n<a href=\"http://us.rd.yahoo.com/dailynews/rss/weather/Country__Country/*https://weather.yahoo.com/country/state/city-2459115/\">Full Forecast at Yahoo! Weather</a>\n<BR />\n<BR />\n(provided by <a href=\"http://www.weather.com\" >The Weather Channel</a>)\n<BR />\n]]>",
|
||||
"guid": {
|
||||
"isPermaLink": "false"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
76
src/index.html
Normal file
76
src/index.html
Normal file
@@ -0,0 +1,76 @@
|
||||
<!DOCTYPE html>
|
||||
<html ng-app="weather">
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<title>Weather or Not</title>
|
||||
|
||||
<link rel="stylesheet" href="./libs/weather-icons/css/weather-icons.css">
|
||||
<link rel="stylesheet" href="./libs/font-awesome/css/font-awesome.css">
|
||||
<link rel="stylesheet" href="//fonts.googleapis.com/icon?family=Material+Icons">
|
||||
<link rel="stylesheet" href="frontend-developer-challenge.css">
|
||||
|
||||
<script src="./libs/angular/angular.js"></script>
|
||||
<script src="frontend-developer-challenge.js"></script>
|
||||
</head>
|
||||
|
||||
<body ng-controller="forecast" ng-class="theme">
|
||||
|
||||
<div class="menu" ng-class="{'open': showMenu}">
|
||||
<div class="tab" ng-click="showMenu == !showMenu">
|
||||
<i class="material-icons open">menu</i>
|
||||
<i class="material-icons close">close</i>
|
||||
</div>
|
||||
|
||||
<h1>Select a City</h2>
|
||||
<form name="city" ng-submit="getWeather(name); showMenu = false;">
|
||||
<input ng-model="name" autocomplete="off" onfocus="this.select();" ng-class="theme" required>
|
||||
<div ng-messages="city.name.$error" ng-show="city.name.$dirty">
|
||||
We can't find weather for you if you don't give us a city to lookup.
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<h2>Theme</h1>
|
||||
<ul class="theme">
|
||||
<li class="Color mojo" ng-click="theme='mojo'"> </li>
|
||||
<li class="color governor-bay" ng-click="theme='governor-bay'"></li>
|
||||
<li class="color bluewood" ng-click="theme='bluewood'"></li>
|
||||
<li class="color fire-bush" ng-click="theme='fire-bush'"></li>
|
||||
<li class="color thunder" ng-click="theme='thunder'"></li>
|
||||
<li class="color burnt-sienna" ng-click="theme='burnt-sienna'"></li>
|
||||
<li class="color wine-berry" ng-click="theme='wine-berries"></li>
|
||||
<li class="color tom-thumb" ng-click="theme='tom-thumb'"></li>
|
||||
<li class="color vistas" ng-click="theme='vista'"></li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="weather">
|
||||
<ul class="forecast">
|
||||
<li class="day now">
|
||||
<div class="icon">
|
||||
<i class="wi" ng-class="getIcon(item.condition.code)"></i>
|
||||
</div>
|
||||
<div class="temp">{{item.condition.temp}}<i class="wi wi-celsius"></i></div>
|
||||
<div class="name">Now</div>
|
||||
<div class="text">{{ item.condition.text }}</div>
|
||||
</li>
|
||||
|
||||
<li class="day future" ng-repeat="forecast in item.forecast | limitTo:4">
|
||||
<div class="icon">
|
||||
<i class="wi" ng-class="getIcon(forecast.code)"></i>
|
||||
</div>
|
||||
<div class="temp">
|
||||
<span class="high"><span ng-bind="forecast.high">{{ forecast.high }}</span><i class="wi wi-degrees"></i></span>
|
||||
<span class="low"><span ng-bind="forecast.low"></span><i class="wi wi-degrees"></i></span>
|
||||
</div>
|
||||
<div class="name" ng-bind="forecast"></div>
|
||||
<div class="text" ng-bind="forecast.text"></div>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="city">"{{ location.city }}, {{ location.region }}</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
11
src/styles/colors.scss
Normal file
11
src/styles/colors.scss
Normal file
@@ -0,0 +1,11 @@
|
||||
// define theme colors
|
||||
$mojo: rgb(195, 77, 64);
|
||||
$governor-bay: rgb(66,66,186);
|
||||
$bluewood: rgb(46, 60,86);
|
||||
$fire-bush: rgb(229, 156, 47);
|
||||
$burnt-sienna: rgb(234,114, 76);
|
||||
$wine-berry: rgb(92, 37,51);
|
||||
$tom-thumb: rgb(45, 68, 47);
|
||||
$thunder: rgb(65, 47, 63);
|
||||
$vista: rgb(141, 211, 151);
|
||||
;
|
||||
109
src/styles/menu.scss
Normal file
109
src/styles/menu.scss
Normal file
@@ -0,0 +1,109 @@
|
||||
.menu {
|
||||
background-color: rgba(255, 255, 255, 0.8);
|
||||
height: 100%;
|
||||
left: -20em;
|
||||
min-width: 200px;
|
||||
padding: 0 1em;
|
||||
position: fixed;
|
||||
transition: all .6s cubic-bezier(.9,0,.33,1) .2s;
|
||||
width: 20em;
|
||||
z-index: 1;
|
||||
|
||||
// styles for the hamburger
|
||||
.tab {
|
||||
position: absolute;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
right: -50px;
|
||||
cursor: pointer;
|
||||
padding: 7px 9px;
|
||||
border-left: 0;
|
||||
border-radius: 10px 10px 10px 10px;
|
||||
transition: right .6s cubic-bezier(.9,0,.33,1) .2s;
|
||||
|
||||
i {
|
||||
position: absolute;
|
||||
top: 7px;
|
||||
left: 9px;
|
||||
transition: opacity .6s cubic-bezier(.9,0,.33,1) .2s;
|
||||
font-size: 36px;
|
||||
}
|
||||
|
||||
.open {
|
||||
color: rgba(255, 255, 255, 0.4);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.close {
|
||||
color: rgba(0, 0, 0, 0.4);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
input {
|
||||
background-color: rgba(0,0,0,0);
|
||||
border-bottom-color: rgba($mojo, .6);
|
||||
border-width: 0 0 2px 0;
|
||||
font-size: 1.25em;
|
||||
font-style: italic;
|
||||
width: 250px;
|
||||
|
||||
text-transform: capitalize;
|
||||
|
||||
transition: border-bottom-color .6s ease;
|
||||
|
||||
&.mojo {border-bottom-color: rgba($mojo, .6);}
|
||||
&.governor-bay {border-bottom-color: rgba($governor-bay, .6);}
|
||||
&.bluewood {border-bottom-color: rgba($bluewood, .6);}
|
||||
&.fire-bush {border-bottom-color: rgba($fire-bush, .6);}
|
||||
&.burnt-sienna {border-bottom-color: rgba($burnt-sienna, .6);}
|
||||
&.wine-berry {border-bottom-color: rgba($wine-berry, .6);}
|
||||
&.tom-thumb {border-bottom-color: rgba($tom-thumb, .6);}
|
||||
&.thunder {border-bottom-color: rgba($thunder, .6);}
|
||||
&.vista {border-bottom-color: rgba($vista, .6);}
|
||||
}
|
||||
|
||||
// styles for the theme selector
|
||||
.theme{
|
||||
border-radius: 15px;
|
||||
overflow: hidden;
|
||||
width: 75%;
|
||||
margin: auto;
|
||||
padding: 0;
|
||||
|
||||
.color {
|
||||
width: 33%;
|
||||
margin: 1px;
|
||||
height: 60px;
|
||||
position: relative;
|
||||
float: left;
|
||||
cursor: move;
|
||||
|
||||
&:after {content: "\00a0";}
|
||||
|
||||
&.mojo {background: $mojo;}
|
||||
&.governor-bay {background: $governor-bay;}
|
||||
&.bluewood {background: $bluewood;}
|
||||
&.fire-bush {background: $fire-bush;}
|
||||
&.burnt-sienna {background: $burnt-sienna;}
|
||||
&.wine-berry {background: $wine-berry;}
|
||||
&.tom-thumb {background: $tom-thumb;}
|
||||
&.thunder {background: $thunder;}
|
||||
&.vista {background: $vista;}
|
||||
}
|
||||
}
|
||||
|
||||
// styles when menu is open
|
||||
&.open {
|
||||
left: 0;
|
||||
box-shadow: 0 0 20px 10px rgba(27, 255, 34, 0.3);
|
||||
|
||||
.tab {
|
||||
top: 25px;
|
||||
right: 0px;
|
||||
|
||||
.open {opacity: 0;}
|
||||
.close {opacity: 1;}
|
||||
}
|
||||
}
|
||||
}
|
||||
49
src/styles/page.scss
Normal file
49
src/styles/page.scss
Normal file
@@ -0,0 +1,49 @@
|
||||
// breakpoints
|
||||
$tiny: 25em;
|
||||
$small: 42em;
|
||||
$medium: 75em;
|
||||
$large: 90em;
|
||||
$xlarge: 120em;
|
||||
|
||||
html {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
*:focus {outline: none;}
|
||||
|
||||
*,::after,::before {
|
||||
box-sizing: inherit
|
||||
}
|
||||
|
||||
li {list-style-type: none;}
|
||||
|
||||
html {
|
||||
font-size: 100%;
|
||||
height: 100%
|
||||
}
|
||||
|
||||
body {
|
||||
background: $mojo;
|
||||
box-sizing: border-box;
|
||||
font-family: 'HelveticaNeue-UltraLight', 'Helvetica Neue UltraLight', 'Helvetica Neue', 'Open-Light', sans-serif;
|
||||
font-weight: 400;
|
||||
line-height: 1.5;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: auto;
|
||||
min-height: 100vh;
|
||||
position: relative;
|
||||
transition: background-color .6s ease;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
|
||||
&.mojo {background: $mojo;}
|
||||
&.governor-bay {background: $governor-bay;}
|
||||
&.bluewood {background: $mojo;}
|
||||
&.fire-bush {background: $fire-bush;}
|
||||
&.burnt-sienna {background: $burnt-sienna;}
|
||||
&.wine-berry {background: $wine-berry;}
|
||||
&.tom-thumb {background: $tom-thumb;}
|
||||
&.thunder {background: $thunder;}
|
||||
&.vista {background: $vista;}
|
||||
}
|
||||
127
src/styles/weather.scss
Normal file
127
src/styles/weather.scss
Normal file
@@ -0,0 +1,127 @@
|
||||
.weather {
|
||||
padding: 15% 10% 0;
|
||||
position: relative;
|
||||
|
||||
.city {
|
||||
COLORS: rgba(255, 255, 255, 0.4);
|
||||
font-weight: bold;
|
||||
font-size: .8em;
|
||||
width: 80%;
|
||||
margin: 5% auto;
|
||||
TEXT-align: center;
|
||||
}
|
||||
|
||||
.forecast {
|
||||
width: 80%;
|
||||
margin: auto;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
.day {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
width: 20%;
|
||||
min-width: 150px;
|
||||
text-align: center;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
|
||||
div {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.icon {
|
||||
font-size: 3em;
|
||||
Padding-Bottom: 25px;
|
||||
}
|
||||
|
||||
.temp {
|
||||
font-size: 2em;
|
||||
font-weight: bold;
|
||||
.low {
|
||||
opacity: .4;
|
||||
font-size: .8em;
|
||||
}
|
||||
}
|
||||
|
||||
.name {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
|
||||
.text {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
&.now {
|
||||
.icon {
|
||||
font-size: 8em;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
.name {
|
||||
font-size: 2.5em;
|
||||
}
|
||||
.text {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: $medium) {
|
||||
padding: 5vh;
|
||||
|
||||
.forecast {
|
||||
width: 100%;
|
||||
|
||||
.day {
|
||||
line-height: 1.25;
|
||||
|
||||
&:nth-child(4) {
|
||||
padding-top: 15px;
|
||||
}
|
||||
|
||||
&.now {
|
||||
width: 100%;
|
||||
padding-bottom: 40px;
|
||||
|
||||
.icon{
|
||||
line-height: 1.5;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: $small) and (max-width: 600px) {
|
||||
.forecast {
|
||||
.day {
|
||||
padding-bottom: 40px;
|
||||
|
||||
line-height: 1.25;
|
||||
&:nth-child(3) {
|
||||
padding-bottom: 75px;
|
||||
}
|
||||
|
||||
&.future {
|
||||
width: 50%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: $tiny) {
|
||||
.forecast {
|
||||
.day {
|
||||
&.future {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user