mirror of
https://github.com/IT4Change/Ocelot-Social.git
synced 2025-12-13 07:45:56 +00:00
Refactor: Notification as a root component
This commit is contained in:
parent
a0ef6b2dc3
commit
5b1348e6fb
@ -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'])
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
92
webapp/components/notifications/NotificationMenu.vue
Normal file
92
webapp/components/notifications/NotificationMenu.vue
Normal 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>
|
||||
@ -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;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user