Add feature image grid

This commit is contained in:
crstin 2020-06-26 00:23:11 +02:00
parent 818382bd13
commit 66d4a53777
15 changed files with 186 additions and 214 deletions

2
.env.example Normal file
View File

@ -0,0 +1,2 @@
VUE_APP_UNSPLASH_BASE_URL=https://api.unsplash.com
VUE_APP_UNSPLASH_ACCESS_KEY=

1
.gitignore vendored
View File

@ -10,6 +10,7 @@ geckodriver.log
# local env files # local env files
.env.local .env.local
.env.*.local .env.*.local
.env
# Log files # Log files
npm-debug.log* npm-debug.log*

View File

@ -1,34 +1,56 @@
# responsive_image_grid # Responsive Image Grid
![Alt text](https://s3.crstin.com/public/responsive_image_grid_repo_preview.png)
## Origin
The initial commit has been generated by `vue create responsive_image_grid`. The next step has been adding `.prettierrc` and running `yarn lint` to convert the codebase.
## Project setup ## Project setup
```bash
yarn
cp .env.example .env
``` ```
yarn install
Then add your unsplash key after `VUE_APP_UNSPLASH_ACCESS_KEY=` to the .env file.
## Run the test suite
```bash
yarn test
``` ```
### Compiles and hot-reloads for development ### Compiles and hot-reloads for development
```
```bash
yarn serve yarn serve
``` ```
### Compiles and minifies for production ### Compiles and minifies for production
```
```bash
yarn build yarn build
``` ```
### Run your unit tests ### Run your unit tests
```
```bash
yarn test:unit yarn test:unit
``` ```
### Run your end-to-end tests ### Run your end-to-end tests
```
```bash
yarn test:e2e yarn test:e2e
``` ```
### Lints and fixes files ### Lints and fixes files
```
```bash
yarn lint yarn lint
``` ```
### Customize configuration ### Customize configuration
See [Configuration Reference](https://cli.vuejs.org/config/). See [Configuration Reference](https://cli.vuejs.org/config/).

View File

@ -7,10 +7,12 @@
"build": "vue-cli-service build", "build": "vue-cli-service build",
"test:unit": "vue-cli-service test:unit", "test:unit": "vue-cli-service test:unit",
"test:e2e": "vue-cli-service test:e2e", "test:e2e": "vue-cli-service test:e2e",
"test": "yarn test:unit && yarn test:e2e",
"lint": "vue-cli-service lint" "lint": "vue-cli-service lint"
}, },
"dependencies": { "dependencies": {
"core-js": "^3.6.5", "core-js": "^3.6.5",
"normalize.css": "^8.0.1",
"vue": "^2.6.11" "vue": "^2.6.11"
}, },
"devDependencies": { "devDependencies": {

View File

@ -5,6 +5,7 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0"> <meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico"> <link rel="icon" href="<%= BASE_URL %>favicon.ico">
<link href="https://fonts.googleapis.com/css2?family=Lato&display=swap" rel="stylesheet">
<title><%= htmlWebpackPlugin.options.title %></title> <title><%= htmlWebpackPlugin.options.title %></title>
</head> </head>
<body> <body>

View File

@ -1,28 +1,27 @@
<template> <template>
<div id="app"> <div id="app">
<img alt="Vue logo" src="./assets/logo.png" /> <ImageGrid />
<HelloWorld msg="Welcome to Your Vue.js App" />
</div> </div>
</template> </template>
<script> <script>
import HelloWorld from './components/HelloWorld.vue' import ImageGrid from './components/ImageGrid.vue'
export default { export default {
name: 'App', name: 'App',
components: { components: {
HelloWorld ImageGrid
} }
} }
</script> </script>
<style lang="scss"> <style lang="scss">
@import '~normalize.css/normalize.css';
#app { #app {
font-family: Avenir, Helvetica, Arial, sans-serif; font-family: 'Lato', Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
text-align: center; padding: 50px;
color: #2c3e50;
margin-top: 60px;
} }
</style> </style>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

View File

@ -1,130 +0,0 @@
<template>
<div class="hello">
<h1>{{ msg }}</h1>
<p>
For a guide and recipes on how to configure / customize this project,<br />
check out the
<a href="https://cli.vuejs.org" target="_blank" rel="noopener"
>vue-cli documentation</a
>.
</p>
<h3>Installed CLI Plugins</h3>
<ul>
<li>
<a
href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel"
target="_blank"
rel="noopener"
>babel</a
>
</li>
<li>
<a
href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint"
target="_blank"
rel="noopener"
>eslint</a
>
</li>
<li>
<a
href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-unit-jest"
target="_blank"
rel="noopener"
>unit-jest</a
>
</li>
<li>
<a
href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-e2e-nightwatch"
target="_blank"
rel="noopener"
>e2e-nightwatch</a
>
</li>
</ul>
<h3>Essential Links</h3>
<ul>
<li>
<a href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a>
</li>
<li>
<a href="https://forum.vuejs.org" target="_blank" rel="noopener"
>Forum</a
>
</li>
<li>
<a href="https://chat.vuejs.org" target="_blank" rel="noopener"
>Community Chat</a
>
</li>
<li>
<a href="https://twitter.com/vuejs" target="_blank" rel="noopener"
>Twitter</a
>
</li>
<li>
<a href="https://news.vuejs.org" target="_blank" rel="noopener">News</a>
</li>
</ul>
<h3>Ecosystem</h3>
<ul>
<li>
<a href="https://router.vuejs.org" target="_blank" rel="noopener"
>vue-router</a
>
</li>
<li>
<a href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a>
</li>
<li>
<a
href="https://github.com/vuejs/vue-devtools#vue-devtools"
target="_blank"
rel="noopener"
>vue-devtools</a
>
</li>
<li>
<a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener"
>vue-loader</a
>
</li>
<li>
<a
href="https://github.com/vuejs/awesome-vue"
target="_blank"
rel="noopener"
>awesome-vue</a
>
</li>
</ul>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
props: {
msg: String
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="scss">
h3 {
margin: 40px 0 0;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
</style>

View File

@ -0,0 +1,112 @@
<template>
<div class="image-grid-container">
<div v-if="loading">Loading...</div>
<div v-else v-for="image in images" :key="image.id">
<a :href="image.links.html">
<figure>
<img :src="image.urls.small" :alt="image.alt_description" />
<figcaption>
<a :href="image.user.portfolio_url">{{ image.user.first_name }}</a>
</figcaption>
</figure>
</a>
</div>
<div v-if="failure">
There has been an error retrieving the requested data
</div>
</div>
</template>
<script>
import axios from 'axios'
export default {
data() {
return {
loading: true,
failure: false,
images: {}
}
},
mounted() {
axios
.get(
`${process.env.VUE_APP_UNSPLASH_BASE_URL}/photos/?client_id=${process.env.VUE_APP_UNSPLASH_ACCESS_KEY}`
)
.then((response) => {
this.images = response.data
})
.catch((error) => {
console.log(error)
this.failure = true
})
.finally(() => (this.loading = false))
}
}
</script>
<style scoped lang="scss">
$imgBaseSize: 333px;
.image-grid-container {
text-align: center;
display: grid;
grid-template-columns: repeat(auto-fit, minmax($imgBaseSize, 1fr));
grid-auto-rows: minmax(150px, 1fr);
grid-gap: 2rem;
grid-auto-flow: dense;
justify-items: center;
font-size: 1.25em;
a {
display: block;
word-break: break-all;
text-decoration: none;
color: white;
}
figure {
position: relative;
height: $imgBaseSize;
width: $imgBaseSize;
display: flex;
align-items: flex-end;
img {
height: inherit;
width: inherit;
object-fit: cover;
&:hover {
opacity: 0.2;
transition: opacity;
transition-duration: 2000ms;
transition-timing-function: ease-out;
}
}
figcaption {
text-align: center;
position: absolute;
width: 100%;
left: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
> a {
padding: 22px 0;
text-shadow: lighten(#fdfdfd, 7%);
&:hover {
color: #aaa;
transition: color;
transition-duration: 1000ms;
transition-timing-function: ease-out;
}
}
}
}
}
</style>

View File

@ -0,0 +1,15 @@
// For authoring Nightwatch tests, see
// https://nightwatchjs.org/guide
module.exports = {
beforeEach: (browser) => browser.init(),
'Image grid is present': (browser) => {
browser
.waitForElementVisible('#app')
.assert.elementCount('.image-grid-container', 1)
.assert.elementCount('figure img', 10)
.assert.elementCount('figure figcaption', 10)
.end()
}
}

View File

@ -1,36 +0,0 @@
////////////////////////////////////////////////////////////////
// For authoring Nightwatch tests, see
// https://nightwatchjs.org/guide
//
// For more information on working with page objects see:
// https://nightwatchjs.org/guide/working-with-page-objects/
////////////////////////////////////////////////////////////////
module.exports = {
beforeEach: (browser) => browser.init(),
'e2e tests using page objects': (browser) => {
const homepage = browser.page.homepage()
homepage.waitForElementVisible('@appContainer')
const app = homepage.section.app
app.assert.elementCount('@logo', 1)
app.expect.section('@welcome').to.be.visible
app.expect
.section('@headline')
.text.to.match(/^Welcome to Your Vue\.js (.*)App$/)
browser.end()
},
'verify if string "e2e-nightwatch" is within the cli plugin links': (
browser
) => {
const homepage = browser.page.homepage()
const welcomeSection = homepage.section.app.section.welcome
welcomeSection.expect
.element('@cliPluginLinks')
.text.to.contain('e2e-nightwatch')
}
}

View File

@ -1,21 +0,0 @@
// For authoring Nightwatch tests, see
// https://nightwatchjs.org/guide
module.exports = {
'default e2e tests': (browser) => {
browser
.init()
.waitForElementVisible('#app')
.assert.elementPresent('.hello')
.assert.containsText('h1', 'Welcome to Your Vue.js App')
.assert.elementCount('img', 1)
.end()
},
'example e2e test using a custom command': (browser) => {
browser
.openHomepage()
.assert.elementPresent('.hello')
.end()
}
}

View File

@ -1,12 +0,0 @@
import { shallowMount } from '@vue/test-utils'
import HelloWorld from '@/components/HelloWorld.vue'
describe('HelloWorld.vue', () => {
it('renders props.msg when passed', () => {
const msg = 'new message'
const wrapper = shallowMount(HelloWorld, {
propsData: { msg }
})
expect(wrapper.text()).toMatch(msg)
})
})

View File

@ -0,0 +1,12 @@
import { mount } from '@vue/test-utils'
import ImageGrid from '@/components/ImageGrid.vue'
describe('ImageGrid.vue', () => {
it('toggles loading', () => {
const wrapper = mount(ImageGrid)
expect(wrapper.vm.loading).toBe(true)
expect(wrapper.text()).toContain('Loading...')
wrapper.setData({ loading: false })
expect(wrapper.vm.loading).toBe(false)
})
})

View File

@ -7392,6 +7392,11 @@ normalize-url@^3.0.0:
resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-3.3.0.tgz#b2e1c4dc4f7c6d57743df733a4f5978d18650559" resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-3.3.0.tgz#b2e1c4dc4f7c6d57743df733a4f5978d18650559"
integrity sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg== integrity sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg==
normalize.css@^8.0.1:
version "8.0.1"
resolved "https://registry.yarnpkg.com/normalize.css/-/normalize.css-8.0.1.tgz#9b98a208738b9cc2634caacbc42d131c97487bf3"
integrity sha512-qizSNPO93t1YUuUhP22btGOo3chcvDFqFaj2TRybP0DMxkHOCTYwp3n34fel4a31ORXy4m1Xq0Gyqpb5m33qIg==
npm-run-path@^2.0.0: npm-run-path@^2.0.0:
version "2.0.2" version "2.0.2"
resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f"