Refactor: Notification as a root component

This commit is contained in:
Robert Schäfer 2019-04-09 19:17:47 +02:00
parent a0ef6b2dc3
commit 5b1348e6fb
4 changed files with 138 additions and 110 deletions

View File

@ -27,9 +27,8 @@ describe('NotificationList.vue', () => {
let mocks
let stubs
let user
let data
let store
let markAsRead
let propsData
beforeEach(() => {
store = new Vuex.Store({
@ -45,47 +44,44 @@ describe('NotificationList.vue', () => {
stubs = {
NuxtLink: RouterLinkStub
}
markAsRead = jest.fn()
data = () => {
return {
notifications: [
{
id: 'notification-41',
read: false,
post: {
id: 'post-1',
title: 'some post title',
contentExcerpt: 'this is a post content',
author: {
id: 'john-1',
slug: 'john-doe',
name: 'John Doe'
}
}
},
{
id: 'notification-42',
read: false,
post: {
id: 'post-2',
title: 'another post title',
contentExcerpt: 'this is yet another post content',
author: {
id: 'john-1',
slug: 'john-doe',
name: 'John Doe'
}
propsData = {
notifications: [
{
id: 'notification-41',
read: false,
post: {
id: 'post-1',
title: 'some post title',
contentExcerpt: 'this is a post content',
author: {
id: 'john-1',
slug: 'john-doe',
name: 'John Doe'
}
}
]
}
},
{
id: 'notification-42',
read: false,
post: {
id: 'post-2',
title: 'another post title',
contentExcerpt: 'this is yet another post content',
author: {
id: 'john-1',
slug: 'john-doe',
name: 'John Doe'
}
}
}
]
}
})
describe('shallowMount', () => {
const Wrapper = () => {
return shallowMount(NotificationList, {
data,
propsData,
mocks,
store,
localVue
@ -104,7 +100,7 @@ describe('NotificationList.vue', () => {
describe('mount', () => {
const Wrapper = () => {
return mount(NotificationList, {
data,
propsData,
mocks,
stubs,
store,
@ -118,15 +114,15 @@ describe('NotificationList.vue', () => {
describe('click on a notification', () => {
beforeEach(() => {
wrapper.setMethods({ markAsRead })
wrapper
.findAll(Notification)
.at(1)
.trigger('click')
})
it('marks notification as read', () => {
expect(markAsRead).toBeCalledWith('notification-42')
it("emits 'markAsRead' with the notificationId", () => {
expect(wrapper.emitted('markAsRead')).toBeTruthy()
expect(wrapper.emitted('markAsRead')[0]).toEqual(['notification-42'])
})
})
})

View File

@ -11,56 +11,21 @@
<script>
import Notification from './Notification'
import gql from 'graphql-tag'
const MARK_AS_READ = gql(`
mutation($id: ID!, $read: Boolean!) {
UpdateNotification(id: $id, read: $read) {
id
read
}
}`)
const NOTIFICATIONS = gql(`{
currentUser {
id
notifications(read: false, orderBy: createdAt_desc) {
id read createdAt
post {
title contentExcerpt slug
author { id slug name disabled deleted }
}
}
}
}`)
export default {
name: 'NotificationList',
components: {
Notification
},
methods: {
async markAsRead(notificationId) {
const variables = { id: notificationId, read: true }
try {
await this.$apollo.mutate({
mutation: MARK_AS_READ,
variables
})
} catch (err) {
throw new Error(err)
}
props: {
notifications: {
type: Array,
required: true
}
},
apollo: {
notifications: {
query: NOTIFICATIONS,
update: data => {
const {
currentUser: { notifications }
} = data
return notifications
}
methods: {
markAsRead(notificationId) {
this.$emit('markAsRead', notificationId)
}
}
}

View File

@ -0,0 +1,92 @@
<template>
<dropdown class="notifications-menu">
<template
slot="default"
slot-scope="{toggleMenu}"
>
<ds-button
primary
icon="bell"
@click.prevent="toggleMenu"
>
1
</ds-button>
</template>
<template
slot="popover"
>
<div class="notifications-menu-popover">
<notification-list
:notifications="notifications"
@markAsRead="markAsRead"
/>
</div>
</template>
</dropdown>
</template>
<script>
import NotificationList from './NotificationList'
import Dropdown from '~/components/Dropdown'
import gql from 'graphql-tag'
const MARK_AS_READ = gql(`
mutation($id: ID!, $read: Boolean!) {
UpdateNotification(id: $id, read: $read) {
id
read
}
}`)
const NOTIFICATIONS = gql(`{
currentUser {
id
notifications(read: false, orderBy: createdAt_desc) {
id read createdAt
post {
title contentExcerpt slug
author { id slug name disabled deleted }
}
}
}
}`)
export default {
name: 'NotificationMenu',
components: {
NotificationList,
Dropdown
},
methods: {
async markAsRead(notificationId) {
const variables = { id: notificationId, read: true }
try {
await this.$apollo.mutate({
mutation: MARK_AS_READ,
variables
})
} catch (err) {
throw new Error(err)
}
}
},
apollo: {
notifications: {
query: NOTIFICATIONS,
update: data => {
const {
currentUser: { notifications }
} = data
return notifications
}
}
}
}
</script>
<style>
.notifications-menu {
display: flex;
align-items: center;
}
</style>

View File

@ -32,27 +32,7 @@
</no-ssr>
<template v-if="isLoggedIn">
<no-ssr>
<dropdown class="notifications-menu">
<template
slot="default"
slot-scope="{toggleMenu}"
>
<ds-button
primary
icon="bell"
@click.prevent="toggleMenu"
>
1
</ds-button>
</template>
<template
slot="popover"
>
<div class="notifications-menu-popover">
<notification-list />
</div>
</template>
</dropdown>
<notification-menu />
</no-ssr>
<no-ssr>
<dropdown class="avatar-menu">
@ -136,10 +116,10 @@
<script>
import { mapGetters, mapActions } from 'vuex'
import LocaleSwitch from '~/components/LocaleSwitch'
import Dropdown from '~/components/Dropdown'
import SearchInput from '~/components/SearchInput.vue'
import Modal from '~/components/Modal'
import NotificationList from '~/components/notifications/NotificationList'
import NotificationMenu from '~/components/notifications/NotificationMenu'
import Dropdown from '~/components/Dropdown'
import seo from '~/components/mixins/seo'
export default {
@ -149,7 +129,7 @@ export default {
SearchInput,
Modal,
LocaleSwitch,
NotificationList
NotificationMenu
},
mixins: [seo],
data() {
@ -264,11 +244,6 @@ export default {
flex: 1;
}
.notifications-menu {
display: flex;
align-items: center;
}
.avatar-menu-trigger {
user-select: none;
display: flex;