use BaseCard in Comment component and refactor

This commit is contained in:
Alina Beck 2020-01-22 17:37:32 +03:00
parent db422257e4
commit e7bf499d37
12 changed files with 134 additions and 272 deletions

View File

@ -1137,7 +1137,7 @@
url-regex "~4.1.1"
video-extensions "~1.1.0"
"@metascraper/helpers@^5.10.3", "@metascraper/helpers@^5.10.5":
"@metascraper/helpers@^5.10.5":
version "5.10.5"
resolved "https://registry.yarnpkg.com/@metascraper/helpers/-/helpers-5.10.5.tgz#c3558533f30144bacecf9599fd02ac88d839a0cc"
integrity sha512-noTBDk3cF3UzKoPrC9/Sye1f9945PVEDju6br7S19YWyUpceEXoDrPF1YaqN37ku62f1s7bul11+Lv/xAYuEVQ==
@ -3872,7 +3872,6 @@ extsprintf@^1.2.0:
faker@Marak/faker.js#master:
version "4.1.0"
uid "3b2fa4aebccee52ae1bafc15d575061fb30c3cf1"
resolved "https://codeload.github.com/Marak/faker.js/tar.gz/3b2fa4aebccee52ae1bafc15d575061fb30c3cf1"
fast-deep-equal@^2.0.1:
@ -7730,6 +7729,11 @@ serve-static@1.14.1:
version "1.14.1"
resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9"
integrity sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==
dependencies:
encodeurl "~1.0.2"
escape-html "~1.0.3"
parseurl "~1.3.3"
send "0.17.1"
set-blocking@^2.0.0, set-blocking@~2.0.0:
version "2.0.0"

View File

@ -14,7 +14,13 @@ describe('Comment.vue', () => {
let Wrapper
beforeEach(() => {
propsData = {}
propsData = {
comment: {
id: 'comment007',
author: { id: 'some-user' },
},
postId: 'post42',
}
mocks = {
$t: jest.fn(),
$toast: {
@ -28,6 +34,7 @@ describe('Comment.vue', () => {
truncate: a => a,
removeHtml: a => a,
},
$route: { hash: '' },
$scrollTo: jest.fn(),
$apollo: {
mutate: jest.fn().mockResolvedValue({
@ -68,6 +75,7 @@ describe('Comment.vue', () => {
id: '2',
contentExcerpt: 'Hello I am a comment content',
content: 'Hello I am comment content',
author: { id: 'some-user' },
}
})

View File

@ -1,61 +1,45 @@
<template>
<div v-if="(comment.deleted || comment.disabled) && !isModerator" :class="{ comment: true }">
<ds-card>
<ds-space margin-bottom="base" />
<ds-text style="padding-left: 40px; font-weight: bold;" color="soft">
<base-card v-if="isUnavailable" class="comment">
<p>
<base-icon name="ban" />
{{ this.$t('comment.content.unavailable-placeholder') }}
</ds-text>
<ds-space margin-bottom="base" />
</ds-card>
</div>
<div v-else :class="{ comment: true, 'disabled-content': comment.deleted || comment.disabled }">
<ds-card :id="anchor" :class="{ 'comment--target': isTarget }">
<ds-space margin-bottom="small" margin-top="small">
<user-teaser :user="author" :date-time="comment.createdAt">
<template v-slot:dateTime>
<ds-text v-if="comment.createdAt !== comment.updatedAt">
({{ $t('comment.edited') }})
</ds-text>
</p>
</base-card>
<base-card v-else :class="commentClass" :id="anchor">
<header class="header">
<user-teaser :user="comment.author" :date-time="comment.createdAt">
<template v-if="wasEdited" v-slot:dateTime>
<span>({{ $t('comment.edited') }})</span>
</template>
</user-teaser>
<client-only>
<content-menu
v-show="!openEditCommentMenu"
v-show="!editingComment"
placement="bottom-end"
resource-type="comment"
:resource="comment"
:modalsData="menuModalsData"
class="float-right"
:is-owner="isAuthor(author.id)"
@showEditCommentMenu="editCommentMenu"
:is-owner="user.id === comment.author.id"
@editComment="editComment(true)"
/>
</client-only>
</ds-space>
<div v-if="openEditCommentMenu">
</header>
<hc-comment-form
v-if="editingComment"
:update="true"
:post="post"
:postId="postId"
:comment="comment"
@showEditCommentMenu="editCommentMenu"
@finishEditing="editComment(false)"
@updateComment="updateComment"
@collapse="isCollapsed = true"
/>
</div>
<div v-else>
<content-viewer :content="commentContent" class="comment-content" />
<button
v-if="isLongComment"
type="button"
class="collapse-button"
@click="isCollapsed = !isCollapsed"
>
<template v-else>
<content-viewer :content="commentContent" class="content" />
<base-button v-if="hasLongContent" size="small" ghost @click="isCollapsed = !isCollapsed">
{{ isCollapsed ? $t('comment.show.more') : $t('comment.show.less') }}
</button>
</div>
<ds-space margin-bottom="small" />
</ds-card>
</div>
</base-button>
</template>
</base-card>
</template>
<script>
@ -69,52 +53,63 @@ import CommentMutations from '~/graphql/CommentMutations'
import scrollToAnchor from '~/mixins/scrollToAnchor.js'
export default {
mixins: [scrollToAnchor],
data() {
const anchor = `commentId-${this.comment.id}`
const isTarget = this.routeHash === `#${anchor}`
return {
anchor,
isTarget,
isCollapsed: !isTarget,
openEditCommentMenu: false,
}
},
components: {
UserTeaser,
ContentMenu,
ContentViewer,
HcCommentForm,
},
mixins: [scrollToAnchor],
data() {
const anchor = `commentId-${this.comment.id}`
const isTarget = this.$route.hash === `#${anchor}`
return {
anchor,
isTarget,
isCollapsed: !isTarget,
editingComment: false,
}
},
props: {
routeHash: { type: String, default: () => '' },
post: { type: Object, default: () => ({}) },
comment: { type: Object, default: () => ({}) },
dateTime: { type: [Date, String], default: null },
comment: {
type: Object,
required: true,
},
postId: {
type: String,
required: true,
},
},
computed: {
...mapGetters({
user: 'auth/user',
isModerator: 'auth/isModerator',
}),
isLongComment() {
hasLongContent() {
return this.$filters.removeHtml(this.comment.content).length > COMMENT_MAX_UNTRUNCATED_LENGTH
},
isUnavailable() {
return (this.comment.deleted || this.comment.disabled) && !this.isModerator
},
wasEdited() {
return this.comment.createdAt !== this.comment.updatedAt
},
commentClass() {
let commentClass = 'comment'
if (this.comment.deleted || this.comment.disabled) commentClass += ' disabled-content'
if (this.isTarget) commentClass += ' --target'
return commentClass
},
commentContent() {
if (this.isLongComment && this.isCollapsed) {
if (this.hasLongContent && this.isCollapsed) {
return this.$filters.truncate(this.comment.content, COMMENT_TRUNCATE_TO_LENGTH)
}
return this.comment.content
},
displaysComment() {
return !this.unavailable || this.isModerator
},
author() {
if (this.deleted) return {}
return this.comment.author || {}
},
menuModalsData() {
return {
delete: {
@ -144,12 +139,9 @@ export default {
checkAnchor(anchor) {
return `#${this.anchor}` === anchor
},
isAuthor(id) {
return this.user.id === id
},
editCommentMenu(showMenu) {
this.openEditCommentMenu = showMenu
this.$emit('toggleNewCommentForm', !showMenu)
editComment(editing) {
this.editingComment = editing
this.$emit('toggleNewCommentForm', !editing)
},
updateComment(comment) {
this.$emit('updateComment', comment)
@ -171,38 +163,33 @@ export default {
},
}
</script>
<style lang="scss" scoped>
.collapse-button {
// TODO: move this to css resets
font-family: inherit;
font-size: inherit;
border: none;
background-color: transparent;
<style lang="scss">
.comment {
display: flex;
flex-direction: column;
margin-bottom: $space-small;
float: right;
padding: 0 16px 16px 16px;
color: $color-primary;
cursor: pointer;
&.--target {
animation: highlight 4s ease;
}
.comment-content {
padding-left: 40px;
> .header {
display: flex;
justify-content: space-between;
margin-bottom: $space-small;
}
.float-right {
float: right;
> .base-button {
align-self: flex-end;
}
}
@keyframes highlight {
0% {
border: 1px solid $color-primary;
border: $border-size-base solid $color-primary;
}
100% {
border: 1px solid transparent;
border: $border-size-base solid transparent;
}
}
.comment--target {
animation: highlight 4s ease;
}
</style>

View File

@ -153,10 +153,10 @@ describe('CommentForm.vue', () => {
expect(closeMethodSpy).toHaveBeenCalledTimes(1)
})
it('emits `showEditCommentMenu` event', async () => {
it('emits `finishEditing` event', async () => {
wrapper.vm.updateEditorContent('ok')
await wrapper.find('form').trigger('submit')
expect(wrapper.emitted('showEditCommentMenu')).toEqual([[false]])
expect(wrapper.emitted('finishEditing')).toBeTruthy()
})
})
@ -167,10 +167,10 @@ describe('CommentForm.vue', () => {
expect(closeMethodSpy).toHaveBeenCalledTimes(1)
})
it('emits `showEditCommentMenu` event', async () => {
it('emits `finishEditing` event', async () => {
wrapper.vm.updateEditorContent('ok')
await wrapper.find('[data-test="cancel-button"]').trigger('submit')
expect(wrapper.emitted('showEditCommentMenu')).toEqual([[false]])
expect(wrapper.emitted('finishEditing')).toBeTruthy()
})
})

View File

@ -69,7 +69,7 @@ export default {
this.$refs.editor.clear()
},
closeEditWindow() {
this.$emit('showEditCommentMenu', false)
this.$emit('finishEditing')
},
handleCancel() {
if (!this.update) {

View File

@ -17,9 +17,14 @@ describe('CommentList.vue', () => {
beforeEach(() => {
propsData = {
post: {
id: 1,
id: 'post42',
comments: [
{ id: 'comment134', contentExcerpt: 'this is a comment', content: 'this is a comment' },
{
id: 'comment134',
contentExcerpt: 'this is a comment',
content: 'this is a comment',
author: { id: 'some-user' },
},
],
},
}
@ -38,6 +43,7 @@ describe('CommentList.vue', () => {
removeHtml: a => a,
},
$scrollTo: jest.fn(),
$route: { hash: '' },
$apollo: {
queries: {
Post: {
@ -70,12 +76,6 @@ describe('CommentList.vue', () => {
beforeEach(jest.useFakeTimers)
describe('$route.hash !== `#comments`', () => {
beforeEach(() => {
mocks.$route = {
hash: '',
}
})
it('skips $scrollTo', () => {
wrapper = Wrapper()
jest.runAllTimers()

View File

@ -4,14 +4,12 @@
<counter-icon icon="comments" :count="post.comments.length" />
{{ $t('common.comment', null, 0) }}
</h3>
<ds-space margin-bottom="large" />
<div v-if="post.comments && post.comments.length" id="comments" class="comments">
<comment
v-for="comment in post.comments"
:key="comment.id"
:comment="comment"
:post="post"
:routeHash="routeHash"
:postId="post.id"
@deleteComment="updateCommentList"
@updateComment="updateCommentList"
@toggleNewCommentForm="toggleNewCommentForm"
@ -31,8 +29,10 @@ export default {
Comment,
},
props: {
routeHash: { type: String, default: () => '' },
post: { type: Object, default: () => {} },
post: {
type: Object,
required: true,
},
},
methods: {
checkAnchor(anchor) {

View File

@ -154,7 +154,7 @@ describe('ContentMenu.vue', () => {
.filter(item => item.text() === 'comment.menu.edit')
.at(0)
.trigger('click')
expect(wrapper.emitted('showEditCommentMenu')).toEqual([[true]])
expect(wrapper.emitted('editComment')).toBeTruthy()
})
it('delete the comment', () => {
wrapper

View File

@ -104,7 +104,7 @@ export default {
routes.push({
label: this.$t(`comment.menu.edit`),
callback: () => {
this.$emit('showEditCommentMenu', true)
this.$emit('editComment')
},
icon: 'edit',
})

View File

@ -5,9 +5,7 @@
</template>
<script>
export default {
}
export default {}
</script>
<style lang="scss">

View File

@ -84,11 +84,7 @@
</ds-space>
<!-- Comments -->
<ds-section slot="footer">
<hc-comment-list
:post="post"
:routeHash="$route.hash"
@toggleNewCommentForm="toggleNewCommentForm"
/>
<hc-comment-list :post="post" @toggleNewCommentForm="toggleNewCommentForm" />
<ds-space margin-bottom="large" />
<hc-comment-form v-if="showNewCommentForm" :post="post" @createComment="createComment" />
</ds-section>
@ -249,20 +245,9 @@ export default {
}
}
.content-menu {
float: right;
margin-right: -$space-x-small;
margin-top: -$space-large;
}
.comments {
margin-top: $space-small;
.comment {
margin-top: $space-small;
position: relative;
}
.ProseMirror {
min-height: 0px;
}

View File

@ -2219,19 +2219,6 @@
global "^4.3.2"
util-deprecate "^1.0.2"
"@storybook/addons@5.3.6":
version "5.3.6"
resolved "https://registry.yarnpkg.com/@storybook/addons/-/addons-5.3.6.tgz#93c5492f09b54cee4885192a0fa79465aa91751f"
integrity sha512-WXsIWOO9/0ydl0C2cGwGd6qrd4L49SoXLNiErDxmjIgLXOVicylCC3JRsExcBOlvmcfPpQniBDNFTXnl6NUldw==
dependencies:
"@storybook/api" "5.3.6"
"@storybook/channels" "5.3.6"
"@storybook/client-logger" "5.3.6"
"@storybook/core-events" "5.3.6"
core-js "^3.0.1"
global "^4.3.2"
util-deprecate "^1.0.2"
"@storybook/addons@5.3.7":
version "5.3.7"
resolved "https://registry.yarnpkg.com/@storybook/addons/-/addons-5.3.7.tgz#4b4502d916dd878762d09c6c2aa1bc5191ae94be"
@ -2268,32 +2255,6 @@
telejson "^3.0.2"
util-deprecate "^1.0.2"
"@storybook/api@5.3.6":
version "5.3.6"
resolved "https://registry.yarnpkg.com/@storybook/api/-/api-5.3.6.tgz#687b58cf31994304d0262f873e611b283b6603a4"
integrity sha512-ka05nIo+KGR1kaZZUqQ0Mmn9wVgXGi7yYzfAdtdqrK2WB2xBt78pEHU00U84Qav4rk85g/U68b7tek+oE1wmUw==
dependencies:
"@reach/router" "^1.2.1"
"@storybook/channels" "5.3.6"
"@storybook/client-logger" "5.3.6"
"@storybook/core-events" "5.3.6"
"@storybook/csf" "0.0.1"
"@storybook/router" "5.3.6"
"@storybook/theming" "5.3.6"
"@types/reach__router" "^1.2.3"
core-js "^3.0.1"
fast-deep-equal "^2.0.1"
global "^4.3.2"
lodash "^4.17.15"
memoizerific "^1.11.3"
prop-types "^15.6.2"
react "^16.8.3"
semver "^6.0.0"
shallow-equal "^1.1.0"
store2 "^2.7.1"
telejson "^3.2.0"
util-deprecate "^1.0.2"
"@storybook/api@5.3.7":
version "5.3.7"
resolved "https://registry.yarnpkg.com/@storybook/api/-/api-5.3.7.tgz#fa9126d851d506607d1ba93588d3599b9b9ea0b9"
@ -2338,13 +2299,6 @@
dependencies:
core-js "^3.0.1"
"@storybook/channels@5.3.6":
version "5.3.6"
resolved "https://registry.yarnpkg.com/@storybook/channels/-/channels-5.3.6.tgz#ed4a504fb64829d6d1bdb2ee3d48b70a33847b73"
integrity sha512-di3BcoM/O1MR749TW7NDI5q3IXAF4Tqt73CVWOLqc/ZE4c/a70rGJ8rHbWKRfXBdJlgvEU7e82SmJz0MiW0A5Q==
dependencies:
core-js "^3.0.1"
"@storybook/channels@5.3.7":
version "5.3.7"
resolved "https://registry.yarnpkg.com/@storybook/channels/-/channels-5.3.7.tgz#19a47c11cda72f98d7d1dc05e78c1e40bdc2bbdf"
@ -2381,13 +2335,6 @@
dependencies:
core-js "^3.0.1"
"@storybook/client-logger@5.3.6":
version "5.3.6"
resolved "https://registry.yarnpkg.com/@storybook/client-logger/-/client-logger-5.3.6.tgz#d37ac813701e8a3cc1e25b6ec017808142a421a0"
integrity sha512-B9FOMLJOmqgibxfPY9yEKXZjNWoSZ9uW7tzdwTFydvcXVf3hSGdJa102w0jEGmgautRRRQOnJ1MXkVJlMnI3MQ==
dependencies:
core-js "^3.0.1"
"@storybook/client-logger@5.3.7":
version "5.3.7"
resolved "https://registry.yarnpkg.com/@storybook/client-logger/-/client-logger-5.3.7.tgz#ee258a8ba0121b579c719b878e3635d6ff020abd"
@ -2420,33 +2367,6 @@
react-textarea-autosize "^7.1.0"
simplebar-react "^1.0.0-alpha.6"
"@storybook/components@5.3.6":
version "5.3.6"
resolved "https://registry.yarnpkg.com/@storybook/components/-/components-5.3.6.tgz#6fe38ac9ba62c982842bfcb06690c3f4fef8be06"
integrity sha512-9XhQyTF884XFdCdsJT2/xRTmMW2a1CBpZCHHC/Xvs85AZ9YeLcp7eDfeu72KMa7FApKH5wuh4JiY5nBgmkQShg==
dependencies:
"@storybook/client-logger" "5.3.6"
"@storybook/theming" "5.3.6"
"@types/react-syntax-highlighter" "11.0.2"
"@types/react-textarea-autosize" "^4.3.3"
core-js "^3.0.1"
global "^4.3.2"
lodash "^4.17.15"
markdown-to-jsx "^6.9.1"
memoizerific "^1.11.3"
polished "^3.3.1"
popper.js "^1.14.7"
prop-types "^15.7.2"
react "^16.8.3"
react-dom "^16.8.3"
react-focus-lock "^2.1.0"
react-helmet-async "^1.0.2"
react-popper-tooltip "^2.8.3"
react-syntax-highlighter "^11.0.2"
react-textarea-autosize "^7.1.0"
simplebar-react "^1.0.0-alpha.6"
ts-dedent "^1.1.0"
"@storybook/components@5.3.7":
version "5.3.7"
resolved "https://registry.yarnpkg.com/@storybook/components/-/components-5.3.7.tgz#8b7361fef02f042c5bad979b4d6886863d3b6a31"
@ -2481,13 +2401,6 @@
dependencies:
core-js "^3.0.1"
"@storybook/core-events@5.3.6":
version "5.3.6"
resolved "https://registry.yarnpkg.com/@storybook/core-events/-/core-events-5.3.6.tgz#41760f8a61958cfa2a468084460da08339c74d9b"
integrity sha512-EO9fyMY9O+jjPb9tKIjh+laMy33UPcejnkKq03uv82+VymwU4O9G/FW/JHzjDM0h0ycGy07/5JLfbMtXyCXT3w==
dependencies:
core-js "^3.0.1"
"@storybook/core-events@5.3.7":
version "5.3.7"
resolved "https://registry.yarnpkg.com/@storybook/core-events/-/core-events-5.3.7.tgz#9297277ae5868b87d3cc36f08415da1579cdb8df"
@ -2606,21 +2519,6 @@
memoizerific "^1.11.3"
qs "^6.6.0"
"@storybook/router@5.3.6":
version "5.3.6"
resolved "https://registry.yarnpkg.com/@storybook/router/-/router-5.3.6.tgz#0e2dc80070b88d550303e43c333ffdf52d1cae1b"
integrity sha512-g3gri0j2VYM0YFyERL8KGMwYbkASneOm6MZdasS6EWXdE6+piONNC5lTPk03v5Js8LJA8LPTjyNlUwfK3plJWw==
dependencies:
"@reach/router" "^1.2.1"
"@storybook/csf" "0.0.1"
"@types/reach__router" "^1.2.3"
core-js "^3.0.1"
global "^4.3.2"
lodash "^4.17.15"
memoizerific "^1.11.3"
qs "^6.6.0"
util-deprecate "^1.0.2"
"@storybook/router@5.3.7":
version "5.3.7"
resolved "https://registry.yarnpkg.com/@storybook/router/-/router-5.3.7.tgz#64e06a319ada9e783029a2d3e3c6a6ddf9b5a1ad"
@ -2654,24 +2552,6 @@
prop-types "^15.7.2"
resolve-from "^5.0.0"
"@storybook/theming@5.3.6":
version "5.3.6"
resolved "https://registry.yarnpkg.com/@storybook/theming/-/theming-5.3.6.tgz#e37f5cbfbadb888e462d60c6db7cd6bc976a4767"
integrity sha512-Z7T+qyrjN2ag5/djmM5adZ6CbBKdPLHGwTgyZiKi3YWbWF1KcRTk3RgK7gtgeqqEFHA6KJsD0ubZ7kqHO2e9Sg==
dependencies:
"@emotion/core" "^10.0.20"
"@emotion/styled" "^10.0.17"
"@storybook/client-logger" "5.3.6"
core-js "^3.0.1"
deep-object-diff "^1.1.0"
emotion-theming "^10.0.19"
global "^4.3.2"
memoizerific "^1.11.3"
polished "^3.3.1"
prop-types "^15.7.2"
resolve-from "^5.0.0"
ts-dedent "^1.1.0"
"@storybook/theming@5.3.7":
version "5.3.7"
resolved "https://registry.yarnpkg.com/@storybook/theming/-/theming-5.3.7.tgz#90ba738f3324f70d88ab412528a8240e9936093d"