first commit
This commit is contained in:
+24
@@ -0,0 +1,24 @@
|
||||
.DS_Store
|
||||
node_modules
|
||||
coverage
|
||||
|
||||
|
||||
|
||||
# local env files
|
||||
.env.local
|
||||
.env.*.local
|
||||
|
||||
# Log files
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
|
||||
# Editor directories and files
|
||||
.idea
|
||||
.vscode
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
@@ -0,0 +1,28 @@
|
||||
# worldcountries
|
||||
A small responsive app build with Vue, Vuex, Axios and Bulma. It calls the restcountries API, visualizes the list of countries, and shows detailed information for each country in a modal.
|
||||
## Project setup
|
||||
```
|
||||
clone the project to your local drive with
|
||||
git clone https://github.com/dadobos/worldcountries.git
|
||||
|
||||
go inside the project with
|
||||
cd worldcountries
|
||||
|
||||
install the packages with
|
||||
npm install
|
||||
|
||||
start the project with
|
||||
npm run serve
|
||||
|
||||
it is now available in your browser on http://localhost:8080/
|
||||
|
||||
|
||||
start testing with
|
||||
npm test
|
||||
|
||||
check full coverage with
|
||||
npm run coverage
|
||||
|
||||
```
|
||||
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
presets: [['@babel/preset-env', { targets: { node: 'current' } }]],
|
||||
};
|
||||
@@ -0,0 +1,24 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
# abort on errors
|
||||
set -e
|
||||
|
||||
# build
|
||||
npm run build
|
||||
|
||||
# navigate into the build output directory
|
||||
cd dist
|
||||
|
||||
# if you are deploying to a custom domain
|
||||
# echo 'www.example.com' > CNAME
|
||||
|
||||
git add -A
|
||||
git commit -m 'deploy'
|
||||
|
||||
# if you are deploying to https://<USERNAME>.github.io
|
||||
# git push -f git@github.com:<USERNAME>/<USERNAME>.github.io.git main
|
||||
|
||||
# if you are deploying to https://<USERNAME>.github.io/<REPO>
|
||||
git push -f git@github.com:dadobos/worldcountries.git main:gh-pages
|
||||
# git push -f git@github.com:<USERNAME>/world.git main:gh-pages
|
||||
cd -
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
.modal{display:flex;flex-direction:column;box-shadow:2px 2px 20px 1px;margin:auto;width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;max-width:90vw;height:-webkit-fit-content;height:-moz-fit-content;height:fit-content;max-height:80vh;overflow:hidden}.modal-background{position:fixed;top:0;right:0;bottom:0;left:0;background:#2c3e50;opacity:.6;cursor:pointer}.modal-card{overflow-y:auto;overflow-x:hidden;background-color:bisque}.pop-enter-active,.pop-leave-active{transition:transform .4s cubic-bezier(.5,0,.5,1),opacity .4s linear}.pop-enter,.pop-leave-to{opacity:0;transform:scale(.3) translateY(-50%)}.card{background-color:bisque;cursor:pointer}#app{font-family:Avenir,Helvetica,Arial,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;padding-top:60px}
|
||||
Vendored
+2
File diff suppressed because one or more lines are too long
Vendored
+1
@@ -0,0 +1 @@
|
||||
<!DOCTYPE html><html lang="" style="background-color: #ddd;"><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" href="https://raw.githubusercontent.com/diaid83/details/master/favicon.ico" type="image/x-icon"><title>worldcountries</title><link href="/worldcountries/css/app.69770c40.css" rel="preload" as="style"><link href="/worldcountries/css/chunk-vendors.53968962.css" rel="preload" as="style"><link href="/worldcountries/js/app.4b638670.js" rel="preload" as="script"><link href="/worldcountries/js/chunk-vendors.e88d19a5.js" rel="preload" as="script"><link href="/worldcountries/css/chunk-vendors.53968962.css" rel="stylesheet"><link href="/worldcountries/css/app.69770c40.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but worldcountries doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div><script src="/worldcountries/js/chunk-vendors.e88d19a5.js"></script><script src="/worldcountries/js/app.4b638670.js"></script></body></html>
|
||||
Vendored
+2
File diff suppressed because one or more lines are too long
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+14
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
Generated
+16236
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,89 @@
|
||||
{
|
||||
"name": "worldcountries",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"jest": {
|
||||
"moduleFileExtensions": [
|
||||
"js",
|
||||
"json",
|
||||
"vue"
|
||||
],
|
||||
"transform": {
|
||||
".*\\.(vue)$": "vue-jest",
|
||||
"^.+\\.js$": "babel-jest"
|
||||
},
|
||||
"snapshotSerializers": [
|
||||
"<rootDir>/node_modules/jest-serializer-vue"
|
||||
],
|
||||
"watchPathIgnorePatterns": [
|
||||
"/node_modules/"
|
||||
],
|
||||
"collectCoverage": true,
|
||||
"coverageDirectory": "./coverage/",
|
||||
"collectCoverageFrom": [
|
||||
"**/*.{js,vue}",
|
||||
"!**/node_modules/**",
|
||||
"!**/coverage/**",
|
||||
"!**/babel.config.js"
|
||||
]
|
||||
},
|
||||
"scripts": {
|
||||
"serve": "vue-cli-service serve",
|
||||
"build": "vue-cli-service build",
|
||||
"lint": "vue-cli-service lint",
|
||||
"test": "jest --watch",
|
||||
"coverage": "jest --coverage",
|
||||
"deploy": "sh deploy.sh"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^0.21.1",
|
||||
"bulma": "^0.9.1",
|
||||
"core-js": "^3.6.5",
|
||||
"vue": "^2.6.11",
|
||||
"vuex": "^3.6.0",
|
||||
"vue-template-compiler": "^2.6.12"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.12.10",
|
||||
"@babel/preset-env": "^7.12.11",
|
||||
"@vue/cli-plugin-babel": "~4.5.0",
|
||||
"@vue/cli-plugin-eslint": "~4.5.0",
|
||||
"@vue/cli-service": "~4.5.0",
|
||||
"@vue/test-utils": "^1.1.2",
|
||||
"babel-core": "^7.0.0-bridge.0",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"babel-jest": "^26.6.3",
|
||||
"eslint": "^6.7.2",
|
||||
"eslint-plugin-jest": "^24.1.3",
|
||||
"eslint-plugin-vue": "^6.2.2",
|
||||
"jest": "^26.6",
|
||||
"jest-serializer-vue": "^2.0.2",
|
||||
"regenerator-runtime": "^0.13.7",
|
||||
"vue-jest": "^3.0.7",
|
||||
"vue-template-compiler": "^2.6.11"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"root": true,
|
||||
"plugins": [
|
||||
"jest"
|
||||
],
|
||||
"env": {
|
||||
"node": true,
|
||||
"jest": true,
|
||||
"browser": true
|
||||
},
|
||||
"extends": [
|
||||
"plugin:vue/essential",
|
||||
"eslint:recommended"
|
||||
],
|
||||
"parserOptions": {
|
||||
"parser": "babel-eslint"
|
||||
},
|
||||
"rules": {}
|
||||
},
|
||||
"browserslist": [
|
||||
"> 1%",
|
||||
"last 2 versions",
|
||||
"not dead"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="" style="background-color: #ddd;">
|
||||
<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" />
|
||||
<link rel="icon" href="https://raw.githubusercontent.com/diaid83/details/master/favicon.ico" type="image/x-icon">
|
||||
<title><%= htmlWebpackPlugin.options.title %></title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>
|
||||
<strong
|
||||
>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work
|
||||
properly without JavaScript enabled. Please enable it to
|
||||
continue.</strong
|
||||
>
|
||||
</noscript>
|
||||
<div id="app"></div>
|
||||
<!-- built files will be auto injected -->
|
||||
</body>
|
||||
</html>
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
<template>
|
||||
<div id="app">
|
||||
<Countries />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import "bulma/css/bulma.css";
|
||||
import Countries from "./view/Countries.vue";
|
||||
|
||||
export default {
|
||||
name: "App",
|
||||
components: {
|
||||
Countries,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
#app {
|
||||
font-family: Avenir, Helvetica, Arial, sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
padding-top: 60px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,340 @@
|
||||
|
||||
<template>
|
||||
<transition name="pop">
|
||||
<div class="modal">
|
||||
<div class="modal-background" @click="close"></div>
|
||||
|
||||
<div class="modal-card">
|
||||
<div class="card-image" v-show="countryData.flag">
|
||||
<figure class="image is-3by2 m-5">
|
||||
<img :src="countryData.flag" alt="flag" />
|
||||
</figure>
|
||||
</div>
|
||||
|
||||
<div class="card-content has-text-justified">
|
||||
<div class="columns is-mobile" v-show="countryData.name">
|
||||
<h4 class="column is-4 is-half-mobile">Country:</h4>
|
||||
<div class="column has-text-right-mobile">
|
||||
{{ countryData.name }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="columns is-mobile" v-show="countryData.nativeName">
|
||||
<h4 class="column is-4 is-half-mobile">Native Name:</h4>
|
||||
<div class="column has-text-right-mobile">
|
||||
{{ countryData.nativeName }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="columns is-mobile" v-show="countryData.demonym">
|
||||
<h4 class="column is-4 is-half-mobile">Demonym:</h4>
|
||||
<div class="column has-text-right-mobile">
|
||||
{{ countryData.demonym }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="columns is-mobile" v-show="countryData.capital">
|
||||
<div class="column is-4">Capital:</div>
|
||||
<div class="column has-text-right-mobile">
|
||||
{{ countryData.capital }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="columns is-mobile" v-show="countryData.region">
|
||||
<div class="column is-4">Region:</div>
|
||||
<div class="column has-text-right-mobile">
|
||||
{{ countryData.region }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="columns is-mobile" v-show="countryData.subregion">
|
||||
<div class="column is-4">Subregion:</div>
|
||||
<div class="column has-text-right-mobile">
|
||||
{{ countryData.subregion }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="columns is-multiline is-mobile"
|
||||
v-show="countryData.translations"
|
||||
>
|
||||
<div class="column is-4">Translations:</div>
|
||||
<div class="column is-8 has-text-right-mobile">
|
||||
<div
|
||||
v-for="(item, key, i) in countryData.translations"
|
||||
:key="`${item}-${key}-${i}`"
|
||||
>
|
||||
{{ key }} - {{ item }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="columns is-multiline is-mobile"
|
||||
v-show="countryData.currencies"
|
||||
>
|
||||
<div class="column is-4">Currencies:</div>
|
||||
<div class="column is-8 has-text-right-mobile">
|
||||
<div
|
||||
v-for="(currency, i) in countryData.currencies"
|
||||
:key="`item-${currency}-${i}`"
|
||||
>
|
||||
<div
|
||||
v-for="(item, key, j) in currency"
|
||||
:key="`${item}-${key}-${j}`"
|
||||
>
|
||||
{{ key }} - {{ item }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="columns is-multiline is-mobile"
|
||||
v-show="countryData.languages"
|
||||
>
|
||||
<div class="column is-4">Languages:</div>
|
||||
<div class="column is-8 has-text-right-mobile">
|
||||
<div
|
||||
v-for="(language, i) in countryData.languages"
|
||||
:key="`item-${language}-${i}`"
|
||||
>
|
||||
<div
|
||||
v-for="(item, key, j) in language"
|
||||
:key="`${item}-${key}-${j}`"
|
||||
>
|
||||
{{ key }} - {{ item }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="columns is-multiline is-mobile"
|
||||
v-show="countryData.regionalBlocs"
|
||||
>
|
||||
<div class="column is-4">Regional Blocs:</div>
|
||||
<div class="column is-8 has-text-right-mobile">
|
||||
<div
|
||||
v-for="(regionalBloc, i) in countryData.regionalBlocs"
|
||||
:key="`item-${regionalBloc}-${i}`"
|
||||
>
|
||||
<div
|
||||
class="columns is-mobile"
|
||||
v-for="(item, key, j) in regionalBloc"
|
||||
:key="`${item}-${key}-${j}`"
|
||||
>
|
||||
<div class="column is-half">{{ key }}</div>
|
||||
<div class="column">
|
||||
<div v-if="typeof item === 'string'">
|
||||
{{ item }}
|
||||
</div>
|
||||
<div v-else>
|
||||
<div v-for="(text, k) in item" :key="`${text}-${k}`">
|
||||
{{ text }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="columns is-multiline is-mobile"
|
||||
v-show="countryData.altSpellings"
|
||||
>
|
||||
<div class="column is-4">Alt Spellings:</div>
|
||||
<div class="column is-8 has-text-right-mobile">
|
||||
<div
|
||||
v-for="(item, i) in countryData.altSpellings"
|
||||
:key="`item-${i}`"
|
||||
>
|
||||
{{ item }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="columns is-multiline is-mobile"
|
||||
v-show="countryData.topLevelDomain"
|
||||
>
|
||||
<div class="column is-4">Top level domain:</div>
|
||||
<div class="column is-8 has-text-right-mobile">
|
||||
<div
|
||||
v-for="(item, i) in countryData.topLevelDomain"
|
||||
:key="`item-${i}`"
|
||||
>
|
||||
{{ item }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="columns is-mobile" v-show="countryData.alpha2Code">
|
||||
<h4 class="column is-4 is-half-mobile">Alpha 2 code:</h4>
|
||||
<div class="column has-text-right-mobile">
|
||||
{{ countryData.alpha2Code }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="columns is-mobile" v-show="countryData.alpha3Code">
|
||||
<h4 class="column is-4 is-half-mobile">Alpha 3 code:</h4>
|
||||
<div class="column has-text-right-mobile">
|
||||
{{ countryData.alpha3Code }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="columns is-mobile" v-show="countryData.numericCode">
|
||||
<h4 class="column is-4 is-half-mobile">Numeric code:</h4>
|
||||
<div class="column has-text-right-mobile">
|
||||
{{ countryData.numericCode }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="columns is-mobile" v-show="countryData.area">
|
||||
<h4 class="column is-4 is-half-mobile">Area:</h4>
|
||||
<div class="column has-text-right-mobile">
|
||||
{{ countryData.area }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="columns is-multiline is-mobile"
|
||||
v-show="countryData.callingCodes"
|
||||
>
|
||||
<div class="column is-4">Calling codes:</div>
|
||||
<div class="column is-8 has-text-right-mobile">
|
||||
<div
|
||||
v-for="(item, i) in countryData.callingCodes"
|
||||
:key="`item-${i}`"
|
||||
>
|
||||
{{ item }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="columns is-multiline is-mobile"
|
||||
v-show="countryData.latlng"
|
||||
>
|
||||
<div class="column is-4">Latitude longitude:</div>
|
||||
<div class="column is-8 has-text-right-mobile">
|
||||
<div v-for="(item, i) in countryData.latlng" :key="`item-${i}`">
|
||||
{{ item }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="columns is-multiline is-mobile"
|
||||
v-show="countryData.timezones"
|
||||
>
|
||||
<div class="column is-4">Timezones:</div>
|
||||
<div class="column is-8 has-text-right-mobile">
|
||||
<div
|
||||
v-for="(item, i) in countryData.timezones"
|
||||
:key="`item-${i}`"
|
||||
>
|
||||
{{ item }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="columns is-mobile" v-show="countryData.population">
|
||||
<div class="column is-4">Population:</div>
|
||||
<div class="column has-text-right-mobile">
|
||||
<div v-if="countryData.population > 1000">
|
||||
{{ Math.round(countryData.population / 1000) }}K
|
||||
</div>
|
||||
<div v-else>{{ countryData.population }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="columns is-multiline is-mobile"
|
||||
v-show="countryData.borders"
|
||||
>
|
||||
<div class="column is-4">Borders:</div>
|
||||
<div class="column is-8 has-text-right-mobile">
|
||||
<div v-for="(item, i) in countryData.borders" :key="`item-${i}`">
|
||||
{{ item }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="columns is-mobile" v-show="countryData.cioc">
|
||||
<h4 class="column is-4 is-half-mobile">Cioc:</h4>
|
||||
<div class="column has-text-right-mobile">
|
||||
{{ countryData.cioc }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="columns is-mobile" v-show="countryData.gini">
|
||||
<h4 class="column is-4 is-half-mobile">Gini:</h4>
|
||||
<div class="column has-text-right-mobile">
|
||||
{{ countryData.gini }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
class="modal-close is-large"
|
||||
@click="close"
|
||||
aria-label="close"
|
||||
></button>
|
||||
</div>
|
||||
</transition>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "CountryModal",
|
||||
props: ["countryData"],
|
||||
methods: {
|
||||
close() {
|
||||
this.$emit("close");
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
.modal {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
box-shadow: 2px 2px 20px 1px;
|
||||
margin: auto;
|
||||
width: fit-content;
|
||||
max-width: 90vw;
|
||||
height: fit-content;
|
||||
max-height: 80vh;
|
||||
overflow: hidden;
|
||||
}
|
||||
.modal-background {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
background: #2c3e50;
|
||||
opacity: 0.6;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.modal-card {
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
background-color: bisque;
|
||||
}
|
||||
|
||||
/* ---------------------------------- */
|
||||
|
||||
.pop-enter-active,
|
||||
.pop-leave-active {
|
||||
transition: transform 0.4s cubic-bezier(0.5, 0, 0.5, 1), opacity 0.4s linear;
|
||||
}
|
||||
|
||||
.pop-enter,
|
||||
.pop-leave-to {
|
||||
opacity: 0;
|
||||
transform: scale(0.3) translateY(-50%);
|
||||
}
|
||||
</style>
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
import Vue from 'vue';
|
||||
import App from './App.vue';
|
||||
import store from './store';
|
||||
|
||||
Vue.config.productionTip = false;
|
||||
|
||||
new Vue({
|
||||
store,
|
||||
render: (h) => h(App),
|
||||
}).$mount('#app');
|
||||
@@ -0,0 +1,16 @@
|
||||
import axios from 'axios';
|
||||
|
||||
const actions = {
|
||||
async getCountries({ commit }) {
|
||||
const url = 'https://restcountries.com/v2/all';
|
||||
await axios
|
||||
.get(url)
|
||||
.then((response) => {
|
||||
commit('SET_COUNTRIES', response.data);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error);
|
||||
});
|
||||
},
|
||||
};
|
||||
export default actions;
|
||||
@@ -0,0 +1,4 @@
|
||||
const getters = {
|
||||
allCountries: (state) => state.countries,
|
||||
};
|
||||
export default getters;
|
||||
@@ -0,0 +1,15 @@
|
||||
import Vue from 'vue';
|
||||
import Vuex from 'vuex';
|
||||
import state from './state';
|
||||
import getters from './getters';
|
||||
import actions from './actions';
|
||||
import mutations from './mutations';
|
||||
|
||||
Vue.use(Vuex);
|
||||
|
||||
export default new Vuex.Store({
|
||||
state,
|
||||
getters,
|
||||
actions,
|
||||
mutations,
|
||||
});
|
||||
@@ -0,0 +1,6 @@
|
||||
const mutations = {
|
||||
SET_COUNTRIES(state, countries) {
|
||||
state.countries = countries;
|
||||
},
|
||||
};
|
||||
export default mutations;
|
||||
@@ -0,0 +1,4 @@
|
||||
const state = {
|
||||
countries: [],
|
||||
};
|
||||
export default state;
|
||||
@@ -0,0 +1,126 @@
|
||||
<template>
|
||||
<div class="container is-fluid">
|
||||
<div class="container is-max-fullhd">
|
||||
<div class="columns is-multiline">
|
||||
<div
|
||||
class="column is-full-mobile is-half-tablet is-one-third-desktop is-one-quarter-fullhd"
|
||||
v-for="(country, index) in countries"
|
||||
:key="index"
|
||||
>
|
||||
<div class="card" @click="showModal(country)">
|
||||
<div class="card-image" v-show="country.flag">
|
||||
<figure class="image is-3by2 m-5">
|
||||
<img :src="country.flag" alt="flag" />
|
||||
</figure>
|
||||
</div>
|
||||
|
||||
<div class="card-content has-text-justified">
|
||||
<div class="columns is-mobile" v-show="country.name">
|
||||
<h4 class="column is-4 is-half-mobile">Country:</h4>
|
||||
<div class="column has-text-right-mobile">
|
||||
{{ country.name }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="columns is-mobile" v-show="country.capital">
|
||||
<div class="column is-4">Capital:</div>
|
||||
<div class="column has-text-right-mobile">
|
||||
{{ country.capital }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="columns is-mobile" v-show="country.region">
|
||||
<div class="column is-4">Region:</div>
|
||||
<div class="column has-text-right-mobile">
|
||||
{{ country.region }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="columns is-multiline is-mobile"
|
||||
v-show="country.timezones"
|
||||
>
|
||||
<div class="column is-4">
|
||||
<p>Timezones:</p>
|
||||
</div>
|
||||
<div class="column is-8 has-text-right-mobile">
|
||||
<div
|
||||
v-for="(item, i) in country.timezones"
|
||||
:key="`item-${i}`"
|
||||
>
|
||||
{{ item }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="columns is-mobile" v-show="country.population">
|
||||
<div class="column is-4">Population:</div>
|
||||
<div class="column has-text-right-mobile">
|
||||
<div v-if="country.population > 1000">
|
||||
{{ Math.round(country.population / 1000) }}K
|
||||
</div>
|
||||
<div v-else>{{ country.population }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<CountryModal
|
||||
:countryData="countryData"
|
||||
v-show="isModalVisible"
|
||||
@close="closeModal"
|
||||
v-if="countryData"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import CountryModal from "../components/CountryModal";
|
||||
|
||||
var html = document.querySelector("html");
|
||||
|
||||
export default {
|
||||
name: "Countries",
|
||||
components: {
|
||||
CountryModal,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isModalVisible: false,
|
||||
countryData: null,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
showModal(country) {
|
||||
this.countryData = country;
|
||||
this.isModalVisible = true;
|
||||
html.classList.add("is-clipped");
|
||||
},
|
||||
closeModal() {
|
||||
this.isModalVisible = false;
|
||||
html.classList.remove("is-clipped");
|
||||
this.countryData = "";
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
getCountries() {
|
||||
return this.$store.getters.allCountries;
|
||||
},
|
||||
countries() {
|
||||
return this.$store.state.countries;
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.$store.dispatch("getCountries");
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style >
|
||||
.card {
|
||||
background-color: bisque;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,75 @@
|
||||
import actions from '../src/store/actions';
|
||||
import getters from '../src/store/getters';
|
||||
import mutations from '../src/store/mutations';
|
||||
import trueState from '../src/store/state';
|
||||
|
||||
const countries = [
|
||||
{
|
||||
name: 'Colombia',
|
||||
capital: 'Bogotá',
|
||||
},
|
||||
{
|
||||
name: 'Romania',
|
||||
capital: 'Bucharest',
|
||||
},
|
||||
];
|
||||
const state = { countries };
|
||||
let url = '';
|
||||
let mockError = false;
|
||||
|
||||
jest.mock('axios', () => ({
|
||||
get: (_url) => {
|
||||
return new Promise((resolve) => {
|
||||
if (mockError) throw Error();
|
||||
url = _url;
|
||||
resolve(true);
|
||||
});
|
||||
},
|
||||
}));
|
||||
|
||||
const commit = jest.fn();
|
||||
commit.mockReturnValue('SET_COUNTRIES', countries);
|
||||
|
||||
describe('actions', () => {
|
||||
actions.getCountries({ commit });
|
||||
|
||||
it('url check', () => {
|
||||
expect.assertions(2);
|
||||
expect(url).toBeDefined();
|
||||
expect(url).toBe('https://restcountries.eu/rest/v2/all');
|
||||
});
|
||||
|
||||
it('commit', () => {
|
||||
expect.assertions(2);
|
||||
expect(commit).toBeCalled();
|
||||
expect(commit).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getters', () => {
|
||||
const testGetters = getters.allCountries(state);
|
||||
it('returns countries', () => {
|
||||
expect.assertions(2);
|
||||
expect(testGetters).toBeDefined();
|
||||
expect(testGetters).toEqual(state.countries);
|
||||
});
|
||||
});
|
||||
|
||||
describe('mutations', () => {
|
||||
it('adds countries to the state', () => {
|
||||
expect.assertions(2);
|
||||
mutations.SET_COUNTRIES(state, countries);
|
||||
expect(mutations).toBeDefined();
|
||||
expect(state).toEqual({
|
||||
countries,
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('state', () => {
|
||||
it('state exists and has countries', () => {
|
||||
expect.assertions(3);
|
||||
expect(trueState).toBeDefined();
|
||||
expect(trueState).toHaveProperty('countries');
|
||||
expect(trueState).not.toHaveProperty('cats');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,5 @@
|
||||
describe('Component.js', () => {
|
||||
test('smoke test', () => {
|
||||
expect(true).toBe(true) // will obviously always pass
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
publicPath: process.env.NODE_ENV === 'production' ? '/worldcountries/' : '/',
|
||||
};
|
||||
Reference in New Issue
Block a user