Skip to content

Commit

Permalink
feat(card-img-lazy): new card-img-lazy sub-component (#2647)
Browse files Browse the repository at this point in the history
* feat(card-img-lazy): New card lazy load image sub-component

* Update index.js

* Update package.json

* Update card-img-lazy.js

* Create card-img-lazy.spec.js

* Update card-img-lazy.js

* Update card-img-lazy.spec.js

* Update README.md

* Update README.md

* Update card-img-lazy.js

* Update card-img-lazy.js

* Update card-img-lazy.js

* lint

* Add `omit()` util

* Prettify
  • Loading branch information
tmorehouse authored and jacobmllr95 committed Feb 20, 2019
1 parent c3c65e4 commit d2e1f8a
Show file tree
Hide file tree
Showing 7 changed files with 263 additions and 7 deletions.
3 changes: 3 additions & 0 deletions docs/markdown/reference/images/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ transformAssetUrls: {
'b-img-lazy': ['src', 'blank-src'],
'b-card': 'img-src',
'b-card-img': 'img-src',
'b-card-img-lazy': ['src', 'blank-src'],
'b-carousel-slide': 'img-src',
'b-embed': 'src'
}
Expand Down Expand Up @@ -68,6 +69,7 @@ module.exports = {
'b-img-lazy': ['src', 'blank-src'],
'b-card': 'img-src',
'b-card-img': 'img-src',
'b-card-img-lazy': ['src', 'blank-src'],
'b-carousel-slide': 'img-src',
'b-embed': 'src'
}
Expand Down Expand Up @@ -95,6 +97,7 @@ build: {
'b-img-lazy': ['src', 'blank-src'],
'b-card': 'img-src',
'b-card-img': 'img-src',
'b-card-img-lazy': ['src', 'blank-src'],
'b-carousel-slide': 'img-src',
'b-embed': 'src'
}
Expand Down
17 changes: 13 additions & 4 deletions src/components/card/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ following are examples of what’s supported inside a `<b-card>`
The building block of a `<b-card>` is the `<b-card-body>` section which provides a padded section
within a card.

By default the `<b-card>` content is automatically placed in a`<b-card-body>` section:
By default the `<b-card>` content is automatically placed in a `<b-card-body>` section:

```html
<div>
Expand Down Expand Up @@ -115,9 +115,12 @@ Links can be added and placed next to each other by adding the `.card-link` clas

### Images

The prop `img-src` places an image on the top of the card, and use the `img-alt` prop to specify a
string to be placed in the image's `alt` attribute. The image specified by the `img-src` prop will
be responsive and will adjust it's width when the width of the card is changed.
The `<b-card>` prop `img-src` places an image on the top of the card, and use the `img-alt` prop to
specify a string to be placed in the image's `alt` attribute. The image specified by the `img-src`
prop will be responsive and will adjust it's width when the width of the card is changed.

Alternatively you can manually place images inside `<b-card>` using the sub-component
`<b-card-img>`. See the kitchen sink example below for usage.

```html
<div>
Expand Down Expand Up @@ -185,6 +188,12 @@ Place the image in the background of the card by setting the boolean prop `overl
<!-- b-card-overlay-img-.vue -->
```

#### Lazy loaded images

Use the `<b-card-img-lazy>` sub-component to lazy load images as they scroll into view.
`<b-card-img-lazy>` supports the same props as `<b-card-img>` as well as many of the props of the
[`<b-img-lazy>`](/docs/components/image#lazy-loaded-images) component.

### Header and footer

Add an optional header and/or footer within a card via the `header`/`footer` props or named slots.
Expand Down
75 changes: 75 additions & 0 deletions src/components/card/card-img-lazy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import BImgLazy from '../image/img-lazy'
import { omit } from '../../utils/object'
import { mergeData } from 'vue-functional-data-merge'

// Copy of `<b-img-lazy>` props, and remove conflicting/non-applicable props
// The `omit()` util creates a new object, so we can just pass the original props
const lazyProps = omit(BImgLazy.props, [
'left',
'right',
'center',
'block',
'rounded',
'thumbnail',
'fluid',
'fluidGrow'
])

export const props = {
...lazyProps,
top: {
type: Boolean,
default: false
},
bottom: {
type: Boolean,
default: false
},
left: {
type: Boolean,
default: false
},
start: {
type: Boolean,
default: false
// alias of 'left'
},
right: {
type: Boolean,
default: false
},
end: {
type: Boolean,
default: false
// alias of 'right'
}
}

// @vue/component
export default {
name: 'BCardImgLazy',
functional: true,
props,
render(h, { props, data }) {
let baseClass = 'card-img'
if (props.top) {
baseClass += '-top'
} else if (props.right || props.end) {
baseClass += '-right'
} else if (props.bottom) {
baseClass += '-bottom'
} else if (props.left || props.start) {
baseClass += '-left'
}

// False out the left/center/right props before passing to b-img-lazy
const lazyProps = { ...props, left: false, right: false, center: false }
return h(
BImgLazy,
mergeData(data, {
class: [baseClass],
props: lazyProps
})
)
}
}
162 changes: 162 additions & 0 deletions src/components/card/card-img-lazy.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
import CardImgLazy from './card-img-lazy'
import { mount } from '@vue/test-utils'

describe('card-image', async () => {
it('default has tag "img"', async () => {
const wrapper = mount(CardImgLazy, {
context: {
props: {
src: 'https://picsum.photos/600/300/?image=25'
}
}
})
expect(wrapper.is('img')).toBe(true)
})

it('default has data src attribute', async () => {
const wrapper = mount(CardImgLazy, {
context: {
props: {
src: 'https://picsum.photos/600/300/?image=25'
}
}
})
expect(wrapper.attributes('src')).toContain('data:image/svg+xml')
})

it('default does not have alt attribute', async () => {
const wrapper = mount(CardImgLazy, {
context: {
props: {
src: 'https://picsum.photos/600/300/?image=25'
}
}
})
expect(wrapper.attributes('alt')).not.toBeDefined()
})

it('default has attributes width and height set to 1', async () => {
const wrapper = mount(CardImgLazy, {
context: {
props: {
src: 'https://picsum.photos/600/300/?image=25'
}
}
})
expect(wrapper.attributes('width')).toBeDefined()
expect(wrapper.attributes('width')).toBe('1')
expect(wrapper.attributes('height')).toBeDefined()
expect(wrapper.attributes('height')).toBe('1')
})

it('default has class "card-img"', async () => {
const wrapper = mount(CardImgLazy, {
context: {
props: {
src: 'https://picsum.photos/600/300/?image=25'
}
}
})
expect(wrapper.classes()).toContain('card-img')
})

it('has class "card-img-top" when prop top=true', async () => {
const wrapper = mount(CardImgLazy, {
context: {
props: {
src: 'https://picsum.photos/600/300/?image=25',
top: true
}
}
})
expect(wrapper.classes()).toContain('card-img-top')
})

it('has class "card-img-bottom" when prop bottom=true', async () => {
const wrapper = mount(CardImgLazy, {
context: {
props: {
src: 'https://picsum.photos/600/300/?image=25',
bottom: true
}
}
})
expect(wrapper.classes()).toContain('card-img-bottom')
})

it('has class "card-img-top" when props top=true and bottom=true', async () => {
const wrapper = mount(CardImgLazy, {
context: {
props: {
src: 'https://picsum.photos/600/300/?image=25',
top: true,
bottom: true
}
}
})
expect(wrapper.classes()).toContain('card-img-top')
})

it('has class "card-img-left" when prop left=true', async () => {
const wrapper = mount(CardImgLazy, {
context: {
props: {
src: 'https://picsum.photos/600/300/?image=25',
left: true
}
}
})
expect(wrapper.classes()).toContain('card-img-left')
})

it('has class "card-img-right" when prop right=true', async () => {
const wrapper = mount(CardImgLazy, {
context: {
props: {
src: 'https://picsum.photos/600/300/?image=25',
right: true
}
}
})
expect(wrapper.classes()).toContain('card-img-right')
})

it('has attribute alt when prop alt set', async () => {
const wrapper = mount(CardImgLazy, {
context: {
props: {
src: 'https://picsum.photos/600/300/?image=25',
alt: 'image'
}
}
})
expect(wrapper.attributes('alt')).toBeDefined()
expect(wrapper.attributes('alt')).toBe('image')
})

it('has attribute width when prop width set', async () => {
const wrapper = mount(CardImgLazy, {
context: {
props: {
src: 'https://picsum.photos/600/300/?image=25',
width: '600'
}
}
})
expect(wrapper.attributes('width')).toBeDefined()
expect(wrapper.attributes('width')).toBe('600')
})

it('has attribute heigth when prop height set', async () => {
const wrapper = mount(CardImgLazy, {
context: {
props: {
src: 'https://picsum.photos/600/300/?image=25',
height: '300'
}
}
})
expect(wrapper.attributes('height')).toBeDefined()
expect(wrapper.attributes('height')).toBe('300')
})
})
2 changes: 2 additions & 0 deletions src/components/card/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import BCardTitle from './card-title'
import BCardSubTitle from './card-sub-title'
import BCardFooter from './card-footer'
import BCardImg from './card-img'
import BCardImgLazy from './card-img-lazy'
import BCardText from './card-text'
import BCardGroup from './card-group'
import { registerComponents } from '../../utils/plugins'
Expand All @@ -17,6 +18,7 @@ const components = {
BCardSubTitle,
BCardFooter,
BCardImg,
BCardImgLazy,
BCardText,
BCardGroup
}
Expand Down
1 change: 1 addition & 0 deletions src/components/card/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"BCardTitle",
"BCardSubTitle",
"BCardImg",
"BCardImgLazy",
"BCardText",
"BCardGroup"
],
Expand Down
10 changes: 7 additions & 3 deletions src/utils/object.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@ export const create = Object.create
export const isFrozen = Object.isFrozen
export const is = Object.is

export function readonlyDescriptor() {
return { enumerable: true, configurable: false, writable: false }
}
// @link https://gist.github.com/bisubus/2da8af7e801ffd813fab7ac221aa7afc
export const omit = (obj, props) =>
Object.keys(obj)
.filter(key => props.indexOf(key) === -1)
.reduce((result, key) => ({ ...result, [key]: obj[key] }), {})

export const readonlyDescriptor = () => ({ enumerable: true, configurable: false, writable: false })

0 comments on commit d2e1f8a

Please sign in to comment.