├── static └── .gitkeep ├── .eslintignore ├── config ├── prod.env.js ├── test.env.js ├── dev.env.js └── index.js ├── .editorconfig ├── .gitignore ├── .postcssrc.js ├── src ├── App.vue ├── main.js ├── router │ └── index.js ├── components │ ├── ItemWrapper.vue │ ├── ItemCard.vue │ └── SearchPage.vue └── data │ └── data.js ├── .babelrc ├── README.md ├── index.html ├── .eslintrc.js └── package.json /static/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | /build/ 2 | /config/ 3 | /dist/ 4 | /*.js 5 | /test/unit/coverage/ 6 | /src -------------------------------------------------------------------------------- /config/prod.env.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | module.exports = { 3 | NODE_ENV: '"production"' 4 | } 5 | -------------------------------------------------------------------------------- /config/test.env.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const merge = require('webpack-merge') 3 | const devEnv = require('./dev.env') 4 | 5 | module.exports = merge(devEnv, { 6 | NODE_ENV: '"testing"' 7 | }) 8 | -------------------------------------------------------------------------------- /config/dev.env.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const merge = require('webpack-merge') 3 | const prodEnv = require('./prod.env') 4 | 5 | module.exports = merge(prodEnv, { 6 | NODE_ENV: '"development"' 7 | }) 8 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | /dist/ 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | /test/unit/coverage/ 8 | /test/e2e/reports/ 9 | selenium-debug.log 10 | 11 | # Editor directories and files 12 | .idea 13 | .vscode 14 | *.suo 15 | *.ntvs* 16 | *.njsproj 17 | *.sln 18 | -------------------------------------------------------------------------------- /.postcssrc.js: -------------------------------------------------------------------------------- 1 | // https://github.com/michael-ciniawsky/postcss-load-config 2 | 3 | module.exports = { 4 | "plugins": { 5 | "postcss-import": {}, 6 | "postcss-url": {}, 7 | // to edit target browsers: use "browserslist" field in package.json 8 | "autoprefixer": {} 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | 13 | 26 | 27 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | // The Vue build version to load with the `import` command 2 | // (runtime-only or standalone) has been set in webpack.base.conf with an alias. 3 | import Vue from 'vue'; 4 | import App from './App'; 5 | import router from './router'; 6 | 7 | Vue.config.productionTip = false; 8 | 9 | /* eslint-disable no-new */ 10 | new Vue({ 11 | el: '#app', 12 | router, 13 | components: { App }, 14 | template: '', 15 | }); 16 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["env", { 4 | "modules": false, 5 | "targets": { 6 | "browsers": ["> 1%", "last 2 versions", "not ie <= 8"] 7 | } 8 | }], 9 | "stage-2" 10 | ], 11 | "plugins": ["transform-vue-jsx", "transform-runtime"], 12 | "env": { 13 | "test": { 14 | "presets": ["env", "stage-2"], 15 | "plugins": ["transform-vue-jsx", "transform-es2015-modules-commonjs", "dynamic-import-node"] 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import Router from 'vue-router'; 3 | import SearchPage from '@/components/SearchPage'; 4 | import ItemWrapper from '@/components/ItemWrapper'; 5 | 6 | Vue.use(Router); 7 | 8 | export default new Router({ 9 | routes: [ 10 | { 11 | path: '/', 12 | name: 'SearchPage', 13 | component: SearchPage 14 | }, 15 | { 16 | path: '/item/:stackNameUrl', 17 | name: 'ItemWrapper', 18 | component: ItemWrapper, 19 | props: true, 20 | }, 21 | ] 22 | }); 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Filter App with Vue Js 2 | 3 | A simple Vue js filtering application. This repo is for the tutorial of building a Simple filtering app with Vue js on Medium by John Ayeni 4 | 5 | ## Build Setup 6 | 7 | ``` bash 8 | # install dependencies 9 | npm install 10 | 11 | # serve with hot reload at localhost:8080 12 | npm run dev 13 | 14 | # build for production with minification 15 | npm run build 16 | 17 | # build for production and view the bundle analyzer report 18 | npm run build --report 19 | ``` 20 | 21 | For a detailed explanation on how things work, check out the [guide](http://vuejs-templates.github.io/webpack/) and [docs for vue-loader](http://vuejs.github.io/vue-loader). 22 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Filter App 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/components/ItemWrapper.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 38 | 39 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /src/components/ItemCard.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 47 | 48 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | // https://eslint.org/docs/user-guide/configuring 2 | 3 | module.exports = { 4 | root: true, 5 | parserOptions: { 6 | parser: 'babel-eslint' 7 | }, 8 | env: { 9 | browser: true, 10 | }, 11 | // https://github.com/vuejs/eslint-plugin-vue#priority-a-essential-error-prevention 12 | // consider switching to `plugin:vue/strongly-recommended` or `plugin:vue/recommended` for stricter rules. 13 | extends: ['plugin:vue/essential', 'airbnb-base'], 14 | // required to lint *.vue files 15 | plugins: [ 16 | 'vue' 17 | ], 18 | // check if imports actually resolve 19 | settings: { 20 | 'import/resolver': { 21 | webpack: { 22 | config: 'build/webpack.base.conf.js' 23 | } 24 | } 25 | }, 26 | // add your custom rules here 27 | rules: { 28 | // don't require .vue extension when importing 29 | 'import/extensions': ['error', 'always', { 30 | js: 'never', 31 | vue: 'never' 32 | }], 33 | // disallow reassignment of function parameters 34 | // disallow parameter object manipulation except for specific exclusions 35 | 'no-param-reassign': ['error', { 36 | props: true, 37 | ignorePropertyModificationsFor: [ 38 | 'state', // for vuex state 39 | 'acc', // for reduce accumulators 40 | 'e' // for e.returnvalue 41 | ] 42 | }], 43 | // allow optionalDependencies 44 | 'import/no-extraneous-dependencies': ['error', { 45 | optionalDependencies: ['test/unit/index.js'] 46 | }], 47 | // allow debugger during development 48 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off' 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /config/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | // Template version: 1.3.1 3 | // see http://vuejs-templates.github.io/webpack for documentation. 4 | 5 | const path = require('path') 6 | 7 | module.exports = { 8 | dev: { 9 | 10 | // Paths 11 | assetsSubDirectory: 'static', 12 | assetsPublicPath: '/', 13 | proxyTable: {}, 14 | 15 | // Various Dev Server settings 16 | host: 'localhost', // can be overwritten by process.env.HOST 17 | port: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined 18 | autoOpenBrowser: false, 19 | errorOverlay: true, 20 | notifyOnErrors: true, 21 | poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions- 22 | 23 | // Use Eslint Loader? 24 | // If true, your code will be linted during bundling and 25 | // linting errors and warnings will be shown in the console. 26 | useEslint: true, 27 | // If true, eslint errors and warnings will also be shown in the error overlay 28 | // in the browser. 29 | showEslintErrorsInOverlay: false, 30 | 31 | /** 32 | * Source Maps 33 | */ 34 | 35 | // https://webpack.js.org/configuration/devtool/#development 36 | devtool: 'cheap-module-eval-source-map', 37 | 38 | // If you have problems debugging vue-files in devtools, 39 | // set this to false - it *may* help 40 | // https://vue-loader.vuejs.org/en/options.html#cachebusting 41 | cacheBusting: true, 42 | 43 | cssSourceMap: true 44 | }, 45 | 46 | build: { 47 | // Template for index.html 48 | index: path.resolve(__dirname, '../dist/index.html'), 49 | 50 | // Paths 51 | assetsRoot: path.resolve(__dirname, '../dist'), 52 | assetsSubDirectory: 'static', 53 | assetsPublicPath: '/', 54 | 55 | /** 56 | * Source Maps 57 | */ 58 | 59 | productionSourceMap: true, 60 | // https://webpack.js.org/configuration/devtool/#production 61 | devtool: '#source-map', 62 | 63 | // Gzip off by default as many popular static hosts such as 64 | // Surge or Netlify already gzip all static assets for you. 65 | // Before setting to `true`, make sure to: 66 | // npm install --save-dev compression-webpack-plugin 67 | productionGzip: false, 68 | productionGzipExtensions: ['js', 'css'], 69 | 70 | // Run the build command with an extra argument to 71 | // View the bundle analyzer report after build finishes: 72 | // `npm run build --report` 73 | // Set to `true` or `false` to always turn it on or off 74 | bundleAnalyzerReport: process.env.npm_config_report 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "web-stack-search-vue", 3 | "version": "1.0.0", 4 | "description": "A simple Vue js application for webstack search", 5 | "author": "johnayeni ", 6 | "private": true, 7 | "scripts": { 8 | "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js", 9 | "start": "npm run dev", 10 | "build": "node build/build.js" 11 | }, 12 | "dependencies": { 13 | "vue": "^2.5.2", 14 | "vue-router": "^3.0.1" 15 | }, 16 | "devDependencies": { 17 | "autoprefixer": "^7.1.2", 18 | "babel-core": "^6.22.1", 19 | "babel-eslint": "^8.2.1", 20 | "babel-helper-vue-jsx-merge-props": "^2.0.3", 21 | "babel-jest": "^21.0.2", 22 | "babel-loader": "^7.1.1", 23 | "babel-plugin-dynamic-import-node": "^1.2.0", 24 | "babel-plugin-syntax-jsx": "^6.18.0", 25 | "babel-plugin-transform-es2015-modules-commonjs": "^6.26.0", 26 | "babel-plugin-transform-runtime": "^6.22.0", 27 | "babel-plugin-transform-vue-jsx": "^3.5.0", 28 | "babel-preset-env": "^1.3.2", 29 | "babel-preset-stage-2": "^6.22.0", 30 | "babel-register": "^6.22.0", 31 | "chalk": "^2.0.1", 32 | "chromedriver": "^2.27.2", 33 | "copy-webpack-plugin": "^4.0.1", 34 | "cross-spawn": "^5.0.1", 35 | "css-loader": "^0.28.0", 36 | "eslint": "^4.15.0", 37 | "eslint-config-airbnb-base": "^11.3.0", 38 | "eslint-friendly-formatter": "^3.0.0", 39 | "eslint-import-resolver-webpack": "^0.8.3", 40 | "eslint-loader": "^1.7.1", 41 | "eslint-plugin-import": "^2.7.0", 42 | "eslint-plugin-vue": "^4.0.0", 43 | "extract-text-webpack-plugin": "^3.0.0", 44 | "file-loader": "^1.1.4", 45 | "friendly-errors-webpack-plugin": "^1.6.1", 46 | "html-webpack-plugin": "^2.30.1", 47 | "jest": "^22.0.4", 48 | "jest-serializer-vue": "^0.3.0", 49 | "nightwatch": "^0.9.12", 50 | "node-notifier": "^5.1.2", 51 | "optimize-css-assets-webpack-plugin": "^3.2.0", 52 | "ora": "^1.2.0", 53 | "portfinder": "^1.0.13", 54 | "postcss-import": "^11.0.0", 55 | "postcss-loader": "^2.0.8", 56 | "postcss-url": "^7.2.1", 57 | "rimraf": "^2.6.0", 58 | "selenium-server": "^3.0.1", 59 | "semver": "^5.3.0", 60 | "shelljs": "^0.7.6", 61 | "uglifyjs-webpack-plugin": "^1.1.1", 62 | "url-loader": "^0.5.8", 63 | "vue-jest": "^1.0.2", 64 | "vue-loader": "^13.3.0", 65 | "vue-style-loader": "^3.0.1", 66 | "vue-template-compiler": "^2.5.2", 67 | "webpack": "^3.6.0", 68 | "webpack-bundle-analyzer": "^2.9.0", 69 | "webpack-dev-server": "^2.9.1", 70 | "webpack-merge": "^4.1.0" 71 | }, 72 | "engines": { 73 | "node": ">= 6.0.0", 74 | "npm": ">= 3.0.0" 75 | }, 76 | "browserslist": [ 77 | "> 1%", 78 | "last 2 versions", 79 | "not ie <= 8" 80 | ] 81 | } 82 | -------------------------------------------------------------------------------- /src/components/SearchPage.vue: -------------------------------------------------------------------------------- 1 | 36 | 37 | 121 | -------------------------------------------------------------------------------- /src/data/data.js: -------------------------------------------------------------------------------- 1 | const data = [ 2 | { 3 | name: 'vue js', 4 | logo: 'http://res.cloudinary.com/johnayeni/image/upload/v1524501994/vue-logo_uoxef2.png', 5 | stack: [ 'framework', 'frontend', 'web', 'mobile' ] 6 | }, 7 | { 8 | name: 'react js', 9 | logo: 'http://res.cloudinary.com/johnayeni/image/upload/v1524501994/react-logo_n8nigl.png', 10 | stack: [ 'framework', 'frontend', 'web', 'mobile' ] 11 | }, 12 | { 13 | name: 'angular js', 14 | logo: 'http://res.cloudinary.com/johnayeni/image/upload/v1524501990/angular-logo_wnra1m.png', 15 | stack: [ 'framework', 'frontend', 'web', 'mobile' ] 16 | }, 17 | { 18 | name: 'javascript', 19 | logo: 'http://res.cloudinary.com/johnayeni/image/upload/v1524501992/javascript-logo_kz7zja.png', 20 | stack: [ 'language', 'web', 'mobile' ] 21 | }, 22 | { 23 | name: 'php', 24 | logo: 'http://res.cloudinary.com/johnayeni/image/upload/v1524501993/php-logo_hojx5k.png', 25 | stack: [ 'language', 'web' ] 26 | }, 27 | { 28 | name: 'java', 29 | logo: 'http://res.cloudinary.com/johnayeni/image/upload/v1524501991/java-logo_doy3o0.png', 30 | stack: [ 'language', 'web', 'mobile' ] 31 | }, 32 | { 33 | name: 'python', 34 | logo: 'http://res.cloudinary.com/johnayeni/image/upload/v1524501993/python-logo_mjaxc1.png', 35 | stack: [ 'language', 'web' ] 36 | }, 37 | { 38 | name: 'go', 39 | logo: 'http://res.cloudinary.com/johnayeni/image/upload/v1524501991/go-logo_avni2x.png', 40 | stack: [ 'language', 'web' ] 41 | }, 42 | { 43 | name: 'html', 44 | logo: 'http://res.cloudinary.com/johnayeni/image/upload/v1524501992/html-logo_oe7cb4.png', 45 | stack: [ 'language', 'web' ] 46 | }, 47 | { 48 | name: 'css', 49 | logo: 'http://res.cloudinary.com/johnayeni/image/upload/v1524501991/css-logo_hsgsej.png', 50 | stack: [ 'language', 'web' ] 51 | }, 52 | { 53 | name: 'scss', 54 | logo: 'http://res.cloudinary.com/johnayeni/image/upload/v1524501993/scss-logo_z71exc.png', 55 | stack: [ 'language', 'web' ] 56 | }, 57 | { 58 | name: 'kotlin', 59 | logo: 'http://res.cloudinary.com/johnayeni/image/upload/v1524501993/kotlin-logo_dnjpni.png', 60 | stack: [ 'language', 'mobile' ] 61 | }, 62 | { 63 | name: 'android', 64 | logo: 'http://res.cloudinary.com/johnayeni/image/upload/v1524501990/android-logo_uqw54t.png', 65 | stack: [ 'language', 'mobile' ] 66 | }, 67 | { 68 | name: 'swift', 69 | logo: 'http://res.cloudinary.com/johnayeni/image/upload/v1524501994/swift-logo_bdjes1.png', 70 | stack: [ 'language', 'mobile' ] 71 | }, 72 | { 73 | name: 'ionic', 74 | logo: 'http://res.cloudinary.com/johnayeni/image/upload/v1524501992/ionic-logo_bc7d4u.png', 75 | stack: [ 'framework', 'hybrid', 'mobile', 'web' ] 76 | }, 77 | { 78 | name: 'flutter', 79 | logo: 'http://res.cloudinary.com/johnayeni/image/upload/v1524501991/flutter-logo_co1o4s.png', 80 | stack: [ 'framework', 'hybrid', 'mobile' ] 81 | }, 82 | { 83 | name: 'node js', 84 | logo: 'http://res.cloudinary.com/johnayeni/image/upload/v1524501992/nodejs-logo_w8vt1c.png', 85 | stack: [ 'framework', 'frontend', 'backend', 'web' ] 86 | }, 87 | { 88 | name: 'laravel', 89 | logo: 'http://res.cloudinary.com/johnayeni/image/upload/v1524501992/laravel-logo_e3gsn1.png', 90 | stack: [ 'framework', 'frontend', 'backend', 'web' ] 91 | }, 92 | { 93 | name: 'django', 94 | logo: 'http://res.cloudinary.com/johnayeni/image/upload/v1524501990/django-logo_vutkvt.png', 95 | stack: [ 'framework', 'frontend', 'backend', 'web' ] 96 | }, 97 | { 98 | name: 'graphql', 99 | logo: 'http://res.cloudinary.com/johnayeni/image/upload/v1524501992/graphql-logo_mdrics.png', 100 | stack: [ 'backend' ] 101 | }, 102 | { 103 | name: 'mySQL', 104 | logo: 'http://res.cloudinary.com/johnayeni/image/upload/v1524501993/mysql-logo_g87g6v.png', 105 | stack: [ 'database' ] 106 | }, 107 | { 108 | name: 'mongo DB', 109 | logo: 'http://res.cloudinary.com/johnayeni/image/upload/v1524501992/mongo-logo_ryaupe.png', 110 | stack: [ 'database' ] 111 | } 112 | ]; 113 | 114 | export default data; 115 | --------------------------------------------------------------------------------