Skip to content
This repository was archived by the owner on Nov 30, 2020. It is now read-only.

Commit 512827d

Browse files
authored
feat(top-app-bar): add start and end slots; add tag prop for fixed adjust (#456)
* fix(top-app-bar): replace icon with icon button in the slots todo: docs, tests * fix(top-app-bar): wrong class when render in dense-prominent-fixed-adjust and short-collapsed feat(top-app-bar): add prop `tag` to allow fixed adjust render in tags other than `div` test(top-app-bar): add snapshots docs(top-app-bar): update docs * docs(top-app-bar): remove denseProminent prop in fixed adjust * fix(top-app-bar): change default scrollTarget
1 parent 835e816 commit 512827d

File tree

11 files changed

+320
-61
lines changed

11 files changed

+320
-61
lines changed

components/top-app-bar/README.md

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
## TopAppBar
1+
## Top App Bar
2+
3+
Top App Bar acts as a container for items such as application title, navigation icon, and action items.
24

35
Actually the mdc-web component enforces users to set a navigation icon, so be sure to set an icon
46
in the navigation slot!
@@ -7,36 +9,46 @@ in the navigation slot!
79

810
```html
911
<m-top-app-bar>
10-
<m-icon
12+
<m-icon-button
1113
icon="menu"
12-
slot="navigation"/>
13-
<span>Title</span>
14-
<m-icon
15-
icon="file_download"
16-
slot="actions"/>
14+
slot="navigation"
15+
title="Title"
16+
/>
17+
<template slot="actions">
18+
<m-icon-button icon="file_download"/>
19+
<m-icon-button icon="print"/>
20+
<m-icon-button icon="bookmark"/>
21+
</template>
1722
</m-top-app-bar>
1823
```
1924

20-
### Props & events
25+
### Props
2126

2227
| Prop | Type | Default | Description |
2328
|------|------|---------|-------------|
2429
| collapsed | Boolean | false | collapsed bar (must be short too) |
2530
| short | Boolean | false | short bar |
2631
| prominent | Boolean | false | prominent app bar style (could not be short too) |
2732
| dense | Boolean | false | dense app bar style (could not be short too) |
33+
| fixed | Boolean | false | style the top app bar as a fixed top app bar. |
34+
| title | String | '' | title of the top app bar |
35+
| scrollTarget | EventTarget | *window* | Sets scroll target to different DOM node. |
36+
37+
### Events
2838

29-
| Event | Description |
30-
|------|------|
31-
| onNavigation | will be dispatched when navigation item is clicked |
39+
| Event | Payload | Description |
40+
|-------|---------|-------------|
41+
| nav | undefined | Emits when the navigation icon is clicked. |
3242

3343
### Slots
3444

3545
| Slot | Description |
3646
|------|-------------|
37-
| default | top app bar title |
38-
| navigation | navigation section icons |
39-
| actions | actions section icons |
47+
| default | content between the start and the end session |
48+
| start | content in the start session except for title and navigation button |
49+
| end | content in the end session except for icon button actions |
50+
| navigation | navigation section icon buttons |
51+
| actions | actions section icons buttons |
4052

4153
## TopAppBarFixedAdjust
4254

@@ -55,7 +67,7 @@ in the navigation slot!
5567
| short | Boolean | false | content adjustment for short top-app-bar |
5668
| dense | Boolean | false | content adjustment for dense top-app-bar |
5769
| prominent | Boolean | false | content adjustment for prominent top-app-bar |
58-
| denseProminent | Boolean | false | content adjustment for dense and prominent top-app-bar |
70+
| tag | String | 'div' | html tag to be rendered |
5971

6072
### Slots
6173

components/top-app-bar/TopAppBar.vue

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,20 @@
1111
>
1212
<slot name="navigation" />
1313
<span
14-
v-if="title"
14+
v-if="title.length > 0"
1515
class="mdc-top-app-bar__title"
1616
>
1717
{{ title }}
1818
</span>
19+
<slot name="start" />
1920
</section>
2021
<slot />
2122
<section
2223
v-if="$slots['actions']"
2324
class="mdc-top-app-bar__section mdc-top-app-bar__section--align-end"
2425
role="toolbar"
2526
>
27+
<slot name="end" />
2628
<slot name="actions" />
2729
</section>
2830
</div>
@@ -62,8 +64,9 @@ export default {
6264
default: ''
6365
},
6466
scrollTarget: {
65-
type: String,
66-
default: ''
67+
type: Object,
68+
default: () => undefined,
69+
validator: val => val instanceof EventTarget
6770
}
6871
},
6972
data () {
@@ -75,7 +78,7 @@ export default {
7578
computed: {
7679
classes () {
7780
return {
78-
'mdc-top-app-bar--short': this.short,
81+
'mdc-top-app-bar--short': this.short && !this.collapsed,
7982
'mdc-top-app-bar--short-collapsed': this.collapsed && this.short,
8083
'mdc-top-app-bar--prominent': this.prominent && !this.short,
8184
'mdc-top-app-bar--dense': this.dense && !this.short,
@@ -84,9 +87,9 @@ export default {
8487
}
8588
},
8689
watch: {
87-
scrollTarget () {
88-
if (this.mdcTopAppBar && this.scrollTarget) {
89-
this.mdcTopAppBar.setScrollTarget(document.getElementById(this.scrollTarget))
90+
scrollTarget (el) {
91+
if (this.mdcTopAppBar && el) {
92+
this.mdcTopAppBar.setScrollTarget(el)
9093
}
9194
}
9295
},
@@ -99,7 +102,7 @@ export default {
99102
})
100103
101104
this.mdcTopAppBar = MDCTopAppBar.attachTo(this.$el)
102-
if (this.scrollTarget) this.mdcTopAppBar.setScrollTarget(document.getElementById(this.scrollTarget))
105+
if (this.scrollTarget && this.scrollTarget !== window) this.mdcTopAppBar.setScrollTarget(this.scrollTarget)
103106
},
104107
beforeDestroy () {
105108
this.slotObserver.disconnect()
@@ -108,13 +111,13 @@ export default {
108111
methods: {
109112
updateSlots () {
110113
if (this.$slots.navigation) {
111-
this.$slots.navigation.map(n => {
112-
n.elm.classList.add('mdc-top-app-bar__navigation-icon')
114+
this.$slots.navigation.forEach(n => {
115+
if (n.elm instanceof Element) n.elm.classList.add('mdc-top-app-bar__navigation-icon')
113116
})
114117
}
115118
if (this.$slots.actions) {
116119
this.$slots.actions.forEach(n => {
117-
if (n.tag) { n.elm.classList.add('mdc-top-app-bar__action-item') }
120+
if (n.elm instanceof Element) { n.elm.classList.add('mdc-top-app-bar__action-item') }
118121
})
119122
}
120123
},
Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,3 @@
1-
<template>
2-
<div
3-
:class="classes"
4-
class="mdc-top-app-bar--fixed-adjust"
5-
>
6-
<slot />
7-
</div>
8-
</template>
9-
101
<script>
112
export default {
123
props: {
@@ -22,20 +13,30 @@ export default {
2213
type: Boolean,
2314
default: false
2415
},
25-
denseProminent: {
26-
type: Boolean,
27-
default: false
16+
tag: {
17+
type: String,
18+
default: 'div'
2819
}
2920
},
3021
computed: {
3122
classes () {
3223
return {
33-
'mdc-top-app-bar--dense-fixed-adjust': this.dense && !this.short,
24+
'mdc-top-app-bar--fixed-adjust': true,
25+
'mdc-top-app-bar--dense-fixed-adjust': this.dense && !this.short && !this.prominent,
3426
'mdc-top-app-bar--short-fixed-adjust': this.short,
35-
'mdc-top-app-bar--prominent-fixed-adjust': this.prominent && !this.short,
36-
'mdc-top-app-bar--dense-prominent-fixed-adjust': this.denseProminent && !this.short
27+
'mdc-top-app-bar--prominent-fixed-adjust': this.prominent && !this.short && !this.dense,
28+
'mdc-top-app-bar--dense-prominent-fixed-adjust': this.dense && this.prominent
3729
}
3830
}
31+
},
32+
render: function (createElement) {
33+
return createElement(
34+
this.tag, // tag name,
35+
{
36+
class: this.classes
37+
},
38+
this.$slots.default // array of children
39+
)
3940
}
4041
}
4142
</script>
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
import 'mutationobserver-shim'
2+
import { mount } from '@vue/test-utils'
3+
import TopAppBar from '../TopAppBar.vue'
4+
import IconButton from '../../icon-button/IconButton.vue'
5+
import TopAppBarFixedAdjust from '../TopAppBarFixedAdjust'
6+
7+
describe('Top App Bar', () => {
8+
it('should mount', () => {
9+
const wrapper = mount(TopAppBar)
10+
expect(wrapper.isVueInstance()).toBeTruthy()
11+
expect(wrapper.vm.$data.mdcTopAppBar).toBeDefined()
12+
})
13+
14+
it('should render with no prop', () => {
15+
const wrapper = mount(TopAppBar)
16+
expect(wrapper).toMatchSnapshot()
17+
expect(wrapper.classes()).toContain('mdc-top-app-bar')
18+
})
19+
20+
it('should render as short', () => {
21+
const wrapper = mount(TopAppBar, {
22+
propsData: {
23+
short: true
24+
}
25+
})
26+
expect(wrapper).toMatchSnapshot()
27+
expect(wrapper.classes()).toContain('mdc-top-app-bar--short')
28+
})
29+
30+
it('should render as short and collapsed', () => {
31+
const wrapper = mount(TopAppBar, {
32+
propsData: {
33+
short: true,
34+
collapsed: true
35+
}
36+
})
37+
expect(wrapper).toMatchSnapshot()
38+
expect(wrapper.classes()).toContain('mdc-top-app-bar--short-collapsed')
39+
})
40+
41+
it('should render as prominent', () => {
42+
const wrapper = mount(TopAppBar, {
43+
propsData: {
44+
prominent: true
45+
}
46+
})
47+
expect(wrapper).toMatchSnapshot()
48+
expect(wrapper.classes()).toContain('mdc-top-app-bar--prominent')
49+
})
50+
51+
it('should render as dense', () => {
52+
const wrapper = mount(TopAppBar, {
53+
propsData: {
54+
dense: true
55+
}
56+
})
57+
expect(wrapper).toMatchSnapshot()
58+
expect(wrapper.classes()).toContain('mdc-top-app-bar--dense')
59+
})
60+
61+
it('should render as fixed', () => {
62+
const wrapper = mount(TopAppBar, {
63+
propsData: {
64+
fixed: true
65+
}
66+
})
67+
expect(wrapper).toMatchSnapshot()
68+
expect(wrapper.classes()).toContain('mdc-top-app-bar--fixed')
69+
})
70+
71+
it('should render with title', () => {
72+
const wrapper = mount(TopAppBar, {
73+
propsData: {
74+
title: 'title text'
75+
}
76+
})
77+
expect(wrapper).toMatchSnapshot()
78+
expect(wrapper.find('.mdc-top-app-bar__title').text()).toEqual('title text')
79+
})
80+
81+
it('should render with optional action button', () => {
82+
const wrapper = mount(TopAppBar, {
83+
slots: {
84+
actions: IconButton
85+
}
86+
})
87+
expect(wrapper.find('.mdc-top-app-bar__action-item').exists()).toBeTruthy()
88+
})
89+
90+
it('should render with optional navigation button', () => {
91+
const wrapper = mount(TopAppBar, {
92+
slots: {
93+
navigation: IconButton
94+
}
95+
})
96+
expect(wrapper.find('.mdc-top-app-bar__navigation-icon').exists()).toBeTruthy()
97+
wrapper.find('.mdc-top-app-bar__navigation-icon').trigger('click')
98+
expect(wrapper.emitted().nav).toBeTruthy()
99+
})
100+
})
101+
102+
describe('Top App Bar Fixed Adjust', () => {
103+
it('should mount', () => {
104+
const wrapper = mount(TopAppBarFixedAdjust)
105+
expect(wrapper.isVueInstance()).toBeTruthy()
106+
})
107+
108+
it('should render with no prop', () => {
109+
const wrapper = mount(TopAppBarFixedAdjust)
110+
expect(wrapper).toMatchSnapshot()
111+
expect(wrapper.classes()).toContain('mdc-top-app-bar--fixed-adjust')
112+
expect(wrapper.is('div')).toBe(true)
113+
})
114+
115+
it('should render as short', () => {
116+
const wrapper = mount(TopAppBarFixedAdjust, {
117+
propsData: {
118+
short: true
119+
}
120+
})
121+
expect(wrapper).toMatchSnapshot()
122+
expect(wrapper.classes()).toContain('mdc-top-app-bar--short-fixed-adjust')
123+
})
124+
125+
it('should render as dense and prominent', () => {
126+
const wrapper = mount(TopAppBarFixedAdjust, {
127+
propsData: {
128+
dense: true,
129+
prominent: true
130+
}
131+
})
132+
expect(wrapper).toMatchSnapshot()
133+
expect(wrapper.classes()).toContain('mdc-top-app-bar--dense-prominent-fixed-adjust')
134+
})
135+
136+
it('should render as prominent', () => {
137+
const wrapper = mount(TopAppBarFixedAdjust, {
138+
propsData: {
139+
prominent: true
140+
}
141+
})
142+
expect(wrapper).toMatchSnapshot()
143+
expect(wrapper.classes()).toContain('mdc-top-app-bar--prominent-fixed-adjust')
144+
})
145+
146+
it('should render as dense', () => {
147+
const wrapper = mount(TopAppBarFixedAdjust, {
148+
propsData: {
149+
dense: true
150+
}
151+
})
152+
expect(wrapper).toMatchSnapshot()
153+
expect(wrapper.classes()).toContain('mdc-top-app-bar--dense-fixed-adjust')
154+
})
155+
156+
it('should render as main', () => {
157+
const wrapper = mount(TopAppBarFixedAdjust, {
158+
propsData: {
159+
tag: 'main'
160+
}
161+
})
162+
expect(wrapper).toMatchSnapshot()
163+
expect(wrapper.is('main')).toBe(true)
164+
})
165+
})

0 commit comments

Comments
 (0)