Skip to content
Permalink
Browse files
feat(b-media): improve aside right handling (#5965)
* fix(b-media): removed utility classes and added style

* fix(b-media): removed utility classes and added style

* fix(b-media): Changes according to suggestions for media

* feat(b-media): added prop desc in component's package.json

* feat(b-media-asign): advanced `right` handling

Co-authored-by: Jacob Müller <jacob.mueller.elz@gmail.com>
  • Loading branch information
jd-solanki and jacobmllr95 committed Oct 28, 2020
1 parent 5e82e7f commit 49a3f00420bf9958deda3a6be0ccb76cc3ea06ba
Show file tree
Hide file tree
Showing 11 changed files with 95 additions and 78 deletions.
@@ -10,6 +10,7 @@
@import "form-spinbutton/index";
@import "form-tags/index";
@import "input-group/index";
@import "media/index";
@import "modal/index";
@import "nav/index";
@import "navbar/index";
@@ -68,7 +68,7 @@
<b-img blank blank-color="#ccc" width="64" height="128" alt="placeholder"></b-img>
</b-media-aside>

<b-media-body class="ml-3">
<b-media-body>
<h5 class="mt-0">Media Title</h5>
<p>
Cras sit amet nibh libero, in gravida nulla. Nulla vel metus scelerisque ante
@@ -0,0 +1,9 @@
.media-aside {
display: flex;
margin-right: 1rem;
}

.media-aside-right {
margin-right: 0;
margin-left: 1rem;
}
@@ -0,0 +1 @@
@import "media";
@@ -1,34 +1,44 @@
import Vue, { mergeData } from '../../vue'
import { NAME_MEDIA_ASIDE } from '../../constants/components'

// --- Props ---

export const props = {
tag: {
type: String,
default: 'div'
},
right: {
type: Boolean,
default: false
},
verticalAlign: {
type: String,
default: 'top'
}
}

// --- Main component ---
// @vue/component
export const BMediaAside = /*#__PURE__*/ Vue.extend({
name: NAME_MEDIA_ASIDE,
functional: true,
props,
render(h, { props, data, children }) {
const { verticalAlign } = props
const align =
props.verticalAlign === 'top'
verticalAlign === 'top'
? 'start'
: props.verticalAlign === 'bottom'
: verticalAlign === 'bottom'
? 'end'
: /* istanbul ignore next */ props.verticalAlign
: /* istanbul ignore next */ verticalAlign

return h(
props.tag,
mergeData(data, {
staticClass: 'd-flex',
staticClass: 'media-aside',
class: {
'media-aside-right': props.right,
[`align-self-${align}`]: align
}
}),
@@ -6,38 +6,54 @@ describe('media-aside', () => {
const wrapper = mount(BMediaAside)

expect(wrapper.element.tagName).toBe('DIV')
expect(wrapper.classes()).toContain('d-flex')
expect(wrapper.classes()).toContain('media-aside')
expect(wrapper.classes()).toContain('align-self-start')
expect(wrapper.text()).toEqual('')

wrapper.destroy()
})

it('has custom root element when prop tag set', async () => {
it('has custom root element when prop `tag` set', async () => {
const wrapper = mount(BMediaAside, {
propsData: {
tag: 'span'
}
})

expect(wrapper.element.tagName).toBe('SPAN')
expect(wrapper.classes()).toContain('d-flex')
expect(wrapper.classes()).toContain('media-aside')
expect(wrapper.classes()).toContain('align-self-start')
expect(wrapper.classes().length).toBe(2)
expect(wrapper.text()).toEqual('')

wrapper.destroy()
})

it('has alignment class when prop vertical-align set', async () => {
it('has correct class when prop `right` set', async () => {
const wrapper = mount(BMediaAside, {
propsData: {
right: true
}
})

expect(wrapper.element.tagName).toBe('DIV')
expect(wrapper.classes()).toContain('media-aside')
expect(wrapper.classes()).toContain('media-aside-right')
expect(wrapper.classes()).toContain('align-self-start')
expect(wrapper.classes().length).toBe(3)

wrapper.destroy()
})

it('has alignment class when prop `vertical-align` set', async () => {
const wrapper = mount(BMediaAside, {
propsData: {
verticalAlign: 'bottom'
}
})

expect(wrapper.element.tagName).toBe('DIV')
expect(wrapper.classes()).toContain('d-flex')
expect(wrapper.classes()).toContain('media-aside')
expect(wrapper.classes()).toContain('align-self-end')
expect(wrapper.classes().length).toBe(2)

@@ -52,7 +68,7 @@ describe('media-aside', () => {
})

expect(wrapper.element.tagName).toBe('DIV')
expect(wrapper.classes()).toContain('d-flex')
expect(wrapper.classes()).toContain('media-aside')
expect(wrapper.classes()).toContain('align-self-start')
expect(wrapper.classes().length).toBe(2)
expect(wrapper.findAll('b').length).toBe(1)
@@ -1,25 +1,22 @@
import Vue, { mergeData } from '../../vue'
import { NAME_MEDIA_BODY } from '../../constants/components'

// --- Props ---

export const props = {
tag: {
type: String,
default: 'div'
}
}

// --- Main component ---
// @vue/component
export const BMediaBody = /*#__PURE__*/ Vue.extend({
name: NAME_MEDIA_BODY,
functional: true,
props,
render(h, { props, data, children }) {
return h(
props.tag,
mergeData(data, {
staticClass: 'media-body'
}),
children
)
return h(props.tag, mergeData(data, { staticClass: 'media-body' }), children)
}
})
@@ -13,7 +13,7 @@ describe('media-body', () => {
wrapper.destroy()
})

it('custom root element when prop tag is set', async () => {
it('custom root element when prop `tag` is set', async () => {
const wrapper = mount(BMediaBody, {
propsData: {
tag: 'article'
@@ -2,65 +2,57 @@ import Vue, { mergeData } from '../../vue'
import { NAME_MEDIA } from '../../constants/components'
import { SLOT_NAME_DEFAULT } from '../../constants/slot-names'
import { normalizeSlot } from '../../utils/normalize-slot'
import { BMediaBody } from './media-body'
import { BMediaAside } from './media-aside'
import { BMediaBody } from './media-body'

// --- Props ---

export const props = {
tag: {
type: String,
default: 'div'
},
noBody: {
type: Boolean,
default: false
},
rightAlign: {
type: Boolean,
default: false
},
verticalAlign: {
type: String,
default: 'top'
},
noBody: {
type: Boolean,
default: false
}
}

// --- Main component ---
// @vue/component
export const BMedia = /*#__PURE__*/ Vue.extend({
name: NAME_MEDIA,
functional: true,
props,
render(h, { props, data, slots, scopedSlots, children }) {
const childNodes = props.noBody ? children : []
const { noBody, rightAlign, verticalAlign } = props
const $children = noBody ? children : []

if (!props.noBody) {
if (!noBody) {
const slotScope = {}
const $slots = slots()
const $scopedSlots = scopedSlots || {}
const $aside = normalizeSlot('aside', {}, $scopedSlots, $slots)
const $default = normalizeSlot(SLOT_NAME_DEFAULT, {}, $scopedSlots, $slots)

if ($aside && !props.rightAlign) {
childNodes.push(
h(
BMediaAside,
{ staticClass: 'mr-3', props: { verticalAlign: props.verticalAlign } },
$aside
)
)
}

childNodes.push(h(BMediaBody, $default))
$children.push(
h(BMediaBody, normalizeSlot(SLOT_NAME_DEFAULT, slotScope, $scopedSlots, $slots))
)

if ($aside && props.rightAlign) {
childNodes.push(
h(
BMediaAside,
{ staticClass: 'ml-3', props: { verticalAlign: props.verticalAlign } },
$aside
)
const $aside = normalizeSlot('aside', slotScope, $scopedSlots, $slots)
if ($aside) {
$children[rightAlign ? 'push' : 'unshift'](
h(BMediaAside, { props: { right: rightAlign, verticalAlign } }, $aside)
)
}
}

return h(props.tag, mergeData(data, { staticClass: 'media' }), childNodes)
return h(props.tag, mergeData(data, { staticClass: 'media' }), $children)
}
})
@@ -9,15 +9,14 @@ describe('media', () => {
expect(wrapper.classes()).toContain('media')
expect(wrapper.classes().length).toBe(1)
expect(wrapper.findAll('.media-body').length).toBe(1)
expect(wrapper.findAll('.d-flex').length).toBe(0)
expect(wrapper.findAll('.media-aside').length).toBe(0)
expect(wrapper.text()).toEqual('')
// Should have only one child element
expect(wrapper.findAll('.media > *').length).toBe(1)

wrapper.destroy()
})

it('renders custom root element when tag prop set', async () => {
it('renders custom root element when `tag` prop set', async () => {
const wrapper = mount(BMedia, {
propsData: {
tag: 'section'
@@ -31,7 +30,7 @@ describe('media', () => {
wrapper.destroy()
})

it('has expected structure when slot aside present', async () => {
it('has expected structure when slot `aside` present', async () => {
const wrapper = mount(BMedia, {
slots: {
aside: 'foobar'
@@ -42,19 +41,15 @@ describe('media', () => {
expect(wrapper.classes()).toContain('media')
expect(wrapper.classes().length).toBe(1)
expect(wrapper.findAll('.media-body').length).toBe(1)
expect(wrapper.findAll('.d-flex').length).toBe(1)
// Should have only two child elements
expect(wrapper.findAll('.media-aside').length).toBe(1)
expect(wrapper.findAll('.media > *').length).toBe(2)
// Has expected child order
expect(wrapper.find('.media > .d-flex + .media-body').exists()).toBe(true)
expect(wrapper.find('.media > .media-body + .d-flex').exists()).toBe(false)
// Aside has extra classes
expect(wrapper.find('.d-flex').classes()).toContain('mr-3')
expect(wrapper.find('.media > .media-aside + .media-body').exists()).toBe(true)
expect(wrapper.find('.media > .media-body + .media-aside').exists()).toBe(false)

wrapper.destroy()
})

it('has expected structure when prop right-align is set and slot aside present', async () => {
it('has expected structure when prop `right-align` is set and slot `aside` present', async () => {
const wrapper = mount(BMedia, {
propsData: {
rightAlign: true
@@ -68,19 +63,15 @@ describe('media', () => {
expect(wrapper.classes()).toContain('media')
expect(wrapper.classes().length).toBe(1)
expect(wrapper.findAll('.media-body').length).toBe(1)
expect(wrapper.findAll('.d-flex').length).toBe(1)
// Should have only two child elements
expect(wrapper.findAll('.media-aside').length).toBe(1)
expect(wrapper.findAll('.media > *').length).toBe(2)
// Has expected child order
expect(wrapper.find('.media > .media-body + .d-flex').exists()).toBe(true)
expect(wrapper.find('.media > .d-flex + .media-body').exists()).toBe(false)
// Aside has extra classes
expect(wrapper.find('.d-flex').classes()).toContain('ml-3')
expect(wrapper.find('.media > .media-body + .media-aside').exists()).toBe(true)
expect(wrapper.find('.media > .media-aside + .media-body').exists()).toBe(false)

wrapper.destroy()
})

it('places default slot inside media-body', async () => {
it('places default slot inside `media-body`', async () => {
const wrapper = mount(BMedia, {
slots: {
default: '<b>foobar</b>'
@@ -97,7 +88,7 @@ describe('media', () => {
wrapper.destroy()
})

it('does not have child media-body is prop no-body set', async () => {
it('does not have child `media-body` when prop `no-body` set', async () => {
const wrapper = mount(BMedia, {
propsData: {
noBody: true
@@ -109,13 +100,12 @@ describe('media', () => {
expect(wrapper.classes().length).toBe(1)
expect(wrapper.findAll('.media-body').length).toBe(0)
expect(wrapper.text()).toEqual('')
// Should have no child elements
expect(wrapper.findAll('.media > *').length).toBe(0)

wrapper.destroy()
})

it('places default slot inside self when no-body set', async () => {
it('places default slot inside self when `no-body` set', async () => {
const wrapper = mount(BMedia, {
propsData: {
noBody: true
@@ -134,7 +124,7 @@ describe('media', () => {
wrapper.destroy()
})

it('sets verticalAlign prop on media-aside child', async () => {
it('sets `vertical-align` prop on `media-aside` child', async () => {
const wrapper = mount(BMedia, {
propsData: {
verticalAlign: 'bottom'
@@ -148,14 +138,11 @@ describe('media', () => {
expect(wrapper.classes()).toContain('media')
expect(wrapper.classes().length).toBe(1)
expect(wrapper.findAll('.media-body').length).toBe(1)
expect(wrapper.findAll('.d-flex').length).toBe(1)
expect(wrapper.findAll('.media-aside').length).toBe(1)
expect(wrapper.text()).toEqual('foobar')
// Should have only two child elements
expect(wrapper.findAll('.media > *').length).toBe(2)
// Should have media aside with self align bottom
expect(wrapper.find('.d-flex').classes()).toContain('align-self-end')
// Should have content in aside
expect(wrapper.find('.d-flex').text()).toEqual('foobar')
expect(wrapper.find('.media-aside').classes()).toContain('align-self-end')
expect(wrapper.find('.media-aside').text()).toEqual('foobar')

wrapper.destroy()
})

0 comments on commit 49a3f00

Please sign in to comment.