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