Skip to content

Commit ba1ac73

Browse files
committed
feat(b-media-asign): advanced right handling
1 parent e03f7d0 commit ba1ac73

9 files changed

+86
-88
lines changed

src/components/media/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@
6868
<b-img blank blank-color="#ccc" width="64" height="128" alt="placeholder"></b-img>
6969
</b-media-aside>
7070

71-
<b-media-body class="ml-3">
71+
<b-media-body>
7272
<h5 class="mt-0">Media Title</h5>
7373
<p>
7474
Cras sit amet nibh libero, in gravida nulla. Nulla vel metus scelerisque ante

src/components/media/_media.scss

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
.media-aside {
2+
display: flex;
23
margin-right: 1rem;
34
}
45

src/components/media/media-aside.js

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,45 @@
11
import Vue, { mergeData } from '../../vue'
22
import { NAME_MEDIA_ASIDE } from '../../constants/components'
33

4+
// --- Props ---
5+
46
export const props = {
57
tag: {
68
type: String,
79
default: 'div'
810
},
9-
verticalAlign: {
10-
type: String,
11-
default: 'top'
12-
},
1311
right: {
1412
type: Boolean,
1513
default: false
14+
},
15+
verticalAlign: {
16+
type: String,
17+
default: 'top'
1618
}
1719
}
1820

21+
// --- Main component ---
1922
// @vue/component
2023
export const BMediaAside = /*#__PURE__*/ Vue.extend({
2124
name: NAME_MEDIA_ASIDE,
2225
functional: true,
2326
props,
2427
render(h, { props, data, children }) {
28+
const { verticalAlign } = props
2529
const align =
26-
props.verticalAlign === 'top'
30+
verticalAlign === 'top'
2731
? 'start'
28-
: props.verticalAlign === 'bottom'
32+
: verticalAlign === 'bottom'
2933
? 'end'
30-
: /* istanbul ignore next */ props.verticalAlign
34+
: /* istanbul ignore next */ verticalAlign
35+
3136
return h(
3237
props.tag,
3338
mergeData(data, {
34-
staticClass: 'd-flex media-aside',
39+
staticClass: 'media-aside',
3540
class: {
36-
[`align-self-${align}`]: align,
37-
'media-aside-right': props.right
41+
'media-aside-right': props.right,
42+
[`align-self-${align}`]: align
3843
}
3944
}),
4045
children

src/components/media/media-aside.spec.js

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,38 +6,54 @@ describe('media-aside', () => {
66
const wrapper = mount(BMediaAside)
77

88
expect(wrapper.element.tagName).toBe('DIV')
9-
expect(wrapper.classes()).toContain('d-flex')
9+
expect(wrapper.classes()).toContain('media-aside')
1010
expect(wrapper.classes()).toContain('align-self-start')
1111
expect(wrapper.text()).toEqual('')
1212

1313
wrapper.destroy()
1414
})
1515

16-
it('has custom root element when prop tag set', async () => {
16+
it('has custom root element when prop `tag` set', async () => {
1717
const wrapper = mount(BMediaAside, {
1818
propsData: {
1919
tag: 'span'
2020
}
2121
})
2222

2323
expect(wrapper.element.tagName).toBe('SPAN')
24-
expect(wrapper.classes()).toContain('d-flex')
24+
expect(wrapper.classes()).toContain('media-aside')
2525
expect(wrapper.classes()).toContain('align-self-start')
2626
expect(wrapper.classes().length).toBe(2)
2727
expect(wrapper.text()).toEqual('')
2828

2929
wrapper.destroy()
3030
})
3131

32-
it('has alignment class when prop vertical-align set', async () => {
32+
it('has correct class when prop `right` set', async () => {
33+
const wrapper = mount(BMediaAside, {
34+
propsData: {
35+
right: true
36+
}
37+
})
38+
39+
expect(wrapper.element.tagName).toBe('DIV')
40+
expect(wrapper.classes()).toContain('media-aside')
41+
expect(wrapper.classes()).toContain('media-aside-right')
42+
expect(wrapper.classes()).toContain('align-self-start')
43+
expect(wrapper.classes().length).toBe(3)
44+
45+
wrapper.destroy()
46+
})
47+
48+
it('has alignment class when prop `vertical-align` set', async () => {
3349
const wrapper = mount(BMediaAside, {
3450
propsData: {
3551
verticalAlign: 'bottom'
3652
}
3753
})
3854

3955
expect(wrapper.element.tagName).toBe('DIV')
40-
expect(wrapper.classes()).toContain('d-flex')
56+
expect(wrapper.classes()).toContain('media-aside')
4157
expect(wrapper.classes()).toContain('align-self-end')
4258
expect(wrapper.classes().length).toBe(2)
4359

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

5470
expect(wrapper.element.tagName).toBe('DIV')
55-
expect(wrapper.classes()).toContain('d-flex')
71+
expect(wrapper.classes()).toContain('media-aside')
5672
expect(wrapper.classes()).toContain('align-self-start')
5773
expect(wrapper.classes().length).toBe(2)
5874
expect(wrapper.findAll('b').length).toBe(1)

src/components/media/media-body.js

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,22 @@
11
import Vue, { mergeData } from '../../vue'
22
import { NAME_MEDIA_BODY } from '../../constants/components'
33

4+
// --- Props ---
5+
46
export const props = {
57
tag: {
68
type: String,
79
default: 'div'
810
}
911
}
1012

13+
// --- Main component ---
1114
// @vue/component
1215
export const BMediaBody = /*#__PURE__*/ Vue.extend({
1316
name: NAME_MEDIA_BODY,
1417
functional: true,
1518
props,
1619
render(h, { props, data, children }) {
17-
return h(
18-
props.tag,
19-
mergeData(data, {
20-
staticClass: 'media-body'
21-
}),
22-
children
23-
)
20+
return h(props.tag, mergeData(data, { staticClass: 'media-body' }), children)
2421
}
2522
})

src/components/media/media-body.spec.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ describe('media-body', () => {
1313
wrapper.destroy()
1414
})
1515

16-
it('custom root element when prop tag is set', async () => {
16+
it('custom root element when prop `tag` is set', async () => {
1717
const wrapper = mount(BMediaBody, {
1818
propsData: {
1919
tag: 'article'

src/components/media/media.js

Lines changed: 20 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,65 +2,57 @@ import Vue, { mergeData } from '../../vue'
22
import { NAME_MEDIA } from '../../constants/components'
33
import { SLOT_NAME_DEFAULT } from '../../constants/slot-names'
44
import { normalizeSlot } from '../../utils/normalize-slot'
5-
import { BMediaBody } from './media-body'
65
import { BMediaAside } from './media-aside'
6+
import { BMediaBody } from './media-body'
7+
8+
// --- Props ---
79

810
export const props = {
911
tag: {
1012
type: String,
1113
default: 'div'
1214
},
15+
noBody: {
16+
type: Boolean,
17+
default: false
18+
},
1319
rightAlign: {
1420
type: Boolean,
1521
default: false
1622
},
1723
verticalAlign: {
1824
type: String,
1925
default: 'top'
20-
},
21-
noBody: {
22-
type: Boolean,
23-
default: false
2426
}
2527
}
2628

29+
// --- Main component ---
2730
// @vue/component
2831
export const BMedia = /*#__PURE__*/ Vue.extend({
2932
name: NAME_MEDIA,
3033
functional: true,
3134
props,
3235
render(h, { props, data, slots, scopedSlots, children }) {
33-
const childNodes = props.noBody ? children : []
36+
const { noBody, rightAlign, verticalAlign } = props
37+
const $children = noBody ? children : []
3438

35-
if (!props.noBody) {
39+
if (!noBody) {
40+
const slotScope = {}
3641
const $slots = slots()
3742
const $scopedSlots = scopedSlots || {}
38-
const $aside = normalizeSlot('aside', {}, $scopedSlots, $slots)
39-
const $default = normalizeSlot(SLOT_NAME_DEFAULT, {}, $scopedSlots, $slots)
40-
41-
if ($aside && !props.rightAlign) {
42-
childNodes.push(
43-
h(
44-
BMediaAside,
45-
{ props: { verticalAlign: props.verticalAlign, right: props.rightAlign } },
46-
$aside
47-
)
48-
)
49-
}
5043

51-
childNodes.push(h(BMediaBody, $default))
44+
$children.push(
45+
h(BMediaBody, normalizeSlot(SLOT_NAME_DEFAULT, slotScope, $scopedSlots, $slots))
46+
)
5247

53-
if ($aside && props.rightAlign) {
54-
childNodes.push(
55-
h(
56-
BMediaAside,
57-
{ props: { verticalAlign: props.verticalAlign, right: props.rightAlign } },
58-
$aside
59-
)
48+
const $aside = normalizeSlot('aside', slotScope, $scopedSlots, $slots)
49+
if ($aside) {
50+
$children[rightAlign ? 'push' : 'unshift'](
51+
h(BMediaAside, { props: { right: rightAlign, verticalAlign } }, $aside)
6052
)
6153
}
6254
}
6355

64-
return h(props.tag, mergeData(data, { staticClass: 'media' }), childNodes)
56+
return h(props.tag, mergeData(data, { staticClass: 'media' }), $children)
6557
}
6658
})

src/components/media/media.spec.js

Lines changed: 17 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,14 @@ describe('media', () => {
99
expect(wrapper.classes()).toContain('media')
1010
expect(wrapper.classes().length).toBe(1)
1111
expect(wrapper.findAll('.media-body').length).toBe(1)
12-
expect(wrapper.findAll('.d-flex').length).toBe(0)
12+
expect(wrapper.findAll('.media-aside').length).toBe(0)
1313
expect(wrapper.text()).toEqual('')
14-
// Should have only one child element
1514
expect(wrapper.findAll('.media > *').length).toBe(1)
1615

1716
wrapper.destroy()
1817
})
1918

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

34-
it('has expected structure when slot aside present', async () => {
33+
it('has expected structure when slot `aside` present', async () => {
3534
const wrapper = mount(BMedia, {
3635
slots: {
3736
aside: 'foobar'
@@ -42,19 +41,15 @@ describe('media', () => {
4241
expect(wrapper.classes()).toContain('media')
4342
expect(wrapper.classes().length).toBe(1)
4443
expect(wrapper.findAll('.media-body').length).toBe(1)
45-
expect(wrapper.findAll('.d-flex').length).toBe(1)
46-
// Should have only two child elements
44+
expect(wrapper.findAll('.media-aside').length).toBe(1)
4745
expect(wrapper.findAll('.media > *').length).toBe(2)
48-
// Has expected child order
49-
expect(wrapper.find('.media > .d-flex + .media-body').exists()).toBe(true)
50-
expect(wrapper.find('.media > .media-body + .d-flex').exists()).toBe(false)
51-
// Aside has extra classes
52-
expect(wrapper.find('.d-flex').classes()).toContain('mr-3')
46+
expect(wrapper.find('.media > .media-aside + .media-body').exists()).toBe(true)
47+
expect(wrapper.find('.media > .media-body + .media-aside').exists()).toBe(false)
5348

5449
wrapper.destroy()
5550
})
5651

57-
it('has expected structure when prop right-align is set and slot aside present', async () => {
52+
it('has expected structure when prop `right-align` is set and slot `aside` present', async () => {
5853
const wrapper = mount(BMedia, {
5954
propsData: {
6055
rightAlign: true
@@ -68,19 +63,15 @@ describe('media', () => {
6863
expect(wrapper.classes()).toContain('media')
6964
expect(wrapper.classes().length).toBe(1)
7065
expect(wrapper.findAll('.media-body').length).toBe(1)
71-
expect(wrapper.findAll('.d-flex').length).toBe(1)
72-
// Should have only two child elements
66+
expect(wrapper.findAll('.media-aside').length).toBe(1)
7367
expect(wrapper.findAll('.media > *').length).toBe(2)
74-
// Has expected child order
75-
expect(wrapper.find('.media > .media-body + .d-flex').exists()).toBe(true)
76-
expect(wrapper.find('.media > .d-flex + .media-body').exists()).toBe(false)
77-
// Aside has extra classes
78-
expect(wrapper.find('.d-flex').classes()).toContain('ml-3')
68+
expect(wrapper.find('.media > .media-body + .media-aside').exists()).toBe(true)
69+
expect(wrapper.find('.media > .media-aside + .media-body').exists()).toBe(false)
7970

8071
wrapper.destroy()
8172
})
8273

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

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

115105
wrapper.destroy()
116106
})
117107

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

137-
it('sets verticalAlign prop on media-aside child', async () => {
127+
it('sets `vertical-align` prop on `media-aside` child', async () => {
138128
const wrapper = mount(BMedia, {
139129
propsData: {
140130
verticalAlign: 'bottom'
@@ -148,14 +138,11 @@ describe('media', () => {
148138
expect(wrapper.classes()).toContain('media')
149139
expect(wrapper.classes().length).toBe(1)
150140
expect(wrapper.findAll('.media-body').length).toBe(1)
151-
expect(wrapper.findAll('.d-flex').length).toBe(1)
141+
expect(wrapper.findAll('.media-aside').length).toBe(1)
152142
expect(wrapper.text()).toEqual('foobar')
153-
// Should have only two child elements
154143
expect(wrapper.findAll('.media > *').length).toBe(2)
155-
// Should have media aside with self align bottom
156-
expect(wrapper.find('.d-flex').classes()).toContain('align-self-end')
157-
// Should have content in aside
158-
expect(wrapper.find('.d-flex').text()).toEqual('foobar')
144+
expect(wrapper.find('.media-aside').classes()).toContain('align-self-end')
145+
expect(wrapper.find('.media-aside').text()).toEqual('foobar')
159146

160147
wrapper.destroy()
161148
})

0 commit comments

Comments
 (0)