From 76ca77c1fcec0ae3907b08f1011ba0d9d830d756 Mon Sep 17 00:00:00 2001 From: Jadranko Dragoje Date: Sat, 16 Dec 2017 13:34:46 +0100 Subject: [PATCH] Add snackbar queue support --- src/components/VSnackbar/VSnackbar.js | 65 ++++++++++++++++------ src/components/VSnackbar/VSnackbar.spec.js | 48 +++++++++++++--- src/components/Vuetify/index.js | 2 + src/store/index.js | 21 +++++++ 4 files changed, 111 insertions(+), 25 deletions(-) create mode 100644 src/store/index.js diff --git a/src/components/VSnackbar/VSnackbar.js b/src/components/VSnackbar/VSnackbar.js index 55296a99a44..2f64ee57445 100644 --- a/src/components/VSnackbar/VSnackbar.js +++ b/src/components/VSnackbar/VSnackbar.js @@ -10,6 +10,7 @@ export default { data () { return { + isVisible: false, activeTimeout: {} } }, @@ -44,32 +45,60 @@ export default { 'snack--top': this.top, 'snack--vertical': this.vertical }) + }, + snackbarQueue () { + return this.$vuetify.store.getSnackbarQueue() } }, watch: { - isActive () { - this.setTimeout() + isActive (current) { + this.toggle() + }, + isVisible (current) { + if (current) { + this.setTimeout() + } + }, + snackbarQueue (current, previous) { + const activeSnackbar = this.$vuetify.store.getSnackbarFromQueue() + if (activeSnackbar === this._uid) { + this.isVisible = true + } } }, methods: { + afterLeave () { + this.$vuetify.store.removeSnackbarFromQueue(this._uid) + this.isActive = false + }, + close () { + this.isVisible = false + }, setTimeout () { clearTimeout(this.activeTimeout) - if (this.isActive && this.timeout) { - this.activeTimeout = setTimeout(() => { - this.isActive = false - }, this.timeout) + if (this.isVisible && this.timeout) { + this.activeTimeout = setTimeout(this.close, this.timeout) + } + }, + toggle () { + if (this.isActive) { + this.$vuetify.store.addSnackbarToQueue(this._uid) + } else { + this.close() } } }, mounted () { - this.setTimeout() + this.toggle() }, render (h) { + if (!this.isVisible) return + const children = [] let content @@ -96,16 +125,18 @@ export default { content = this.$slots.default } - if (this.isActive) { - children.push(h('div', { - staticClass: 'snack', - 'class': this.classes, - on: this.$listeners - }, [h('div', { - staticClass: 'snack__content' - }, content)])) - } + children.push(h('div', { + staticClass: 'snack', + 'class': this.classes, + on: this.$listeners + }, [h('div', { + staticClass: 'snack__content' + }, content)])) - return h('transition', children) + return h('transition', { + on: { + afterLeave: this.afterLeave + } + }, children) } } diff --git a/src/components/VSnackbar/VSnackbar.spec.js b/src/components/VSnackbar/VSnackbar.spec.js index a6c737bd700..e3515c6f9e9 100644 --- a/src/components/VSnackbar/VSnackbar.spec.js +++ b/src/components/VSnackbar/VSnackbar.spec.js @@ -2,17 +2,21 @@ import { test } from '~util/testing' import VSnackbar from '~components/VSnackbar' test('VSnackbar.vue', ({ mount }) => { - it('should have a snack class', () => { + it('should have a snack class', async () => { const wrapper = mount(VSnackbar, { propsData: { value: true } }) + await wrapper.vm.$nextTick() expect(wrapper.hasClass('snack')).toBe(true) + + wrapper.vm.close() + wrapper.vm.afterLeave() }) - it('should have a color class', () => { + it('should have a color class', async () => { const wrapper = mount(VSnackbar, { propsData: { value: true, @@ -20,25 +24,30 @@ test('VSnackbar.vue', ({ mount }) => { } }) + await wrapper.vm.$nextTick() expect(wrapper.hasClass('orange')).toBe(true) expect(wrapper.hasClass('lighten-2')).toBe(true) + + wrapper.vm.close() + wrapper.vm.afterLeave() }) it('should have a snack__content class only when active', async () => { const wrapper = mount(VSnackbar, { propsData: { - value: false, - timeout: 1000 + value: false } }) + await wrapper.vm.$nextTick() expect(wrapper.find('div .snack__content')).toHaveLength(0) wrapper.setProps({ value: true }) - await wrapper.vm.$nextTick() - expect(wrapper.find('div .snack__content')).toHaveLength(1) + + wrapper.vm.close() + wrapper.vm.afterLeave() }) it('should timeout correctly', async () => { @@ -62,7 +71,7 @@ test('VSnackbar.vue', ({ mount }) => { expect(setTimeout.mock.calls[0][1]).toBe(3141) jest.runAllTimers() - + wrapper.vm.afterLeave() await wrapper.vm.$nextTick() expect(wrapper.data().isActive).toBe(false) @@ -88,10 +97,33 @@ test('VSnackbar.vue', ({ mount }) => { expect(setTimeout.mock.calls[0][1]).toBe(3141) jest.runAllTimers() - + wrapper.vm.afterLeave() await wrapper.vm.$nextTick() expect(wrapper.data().isActive).toBe(false) expect(value).toBeCalledWith(false) }) + + it('should stack correctly', async () => { + const wrapper_a = mount(VSnackbar) + const wrapper_b = mount(VSnackbar) + + wrapper_a.setProps({ value: true }) + wrapper_b.setProps({ value: true }) + await wrapper_a.vm.$nextTick() + expect(wrapper_a.data().isVisible).toBe(true) + expect(wrapper_b.data().isVisible).toBe(false) + + wrapper_a.vm.close() + wrapper_a.vm.afterLeave() + await wrapper_a.vm.$nextTick() + expect(wrapper_a.data().isVisible).toBe(false) + expect(wrapper_b.data().isVisible).toBe(true) + + wrapper_b.vm.close() + wrapper_b.vm.afterLeave() + await wrapper_a.vm.$nextTick() + expect(wrapper_a.data().isVisible).toBe(false) + expect(wrapper_b.data().isVisible).toBe(false) + }) }) diff --git a/src/components/Vuetify/index.js b/src/components/Vuetify/index.js index abfa518da84..12ea150bf7b 100644 --- a/src/components/Vuetify/index.js +++ b/src/components/Vuetify/index.js @@ -1,4 +1,5 @@ import application from './mixins/application' +import store from '../../store' import theme from './mixins/theme' const Vuetify = { @@ -11,6 +12,7 @@ const Vuetify = { Vue.util.defineReactive($vuetify, 'inspire', { breakpoint: {}, application, + store, dark: false, theme: theme(opts.theme), touchSupport: false diff --git a/src/store/index.js b/src/store/index.js new file mode 100644 index 00000000000..8f964add860 --- /dev/null +++ b/src/store/index.js @@ -0,0 +1,21 @@ +export default { + state: { + snackbarQueue: [] + }, + addSnackbarToQueue (uid) { + this.state.snackbarQueue.push(uid) + }, + clearSnackbarQueue () { + this.state.snackbarQueue = [] + }, + getSnackbarQueue () { + return this.state.snackbarQueue + }, + getSnackbarFromQueue () { + return this.state.snackbarQueue[0] + }, + removeSnackbarFromQueue (uid) { + const index = this.state.snackbarQueue.indexOf(uid) + if (index > -1) this.state.snackbarQueue.splice(index, 1) + } +}