From 1255a46b7a6237d67fb1809fd2af76189f94b9ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?= Date: Wed, 10 Jul 2019 18:35:19 +0200 Subject: [PATCH] Avoid Hashtags in Comments in the Editor Co-Authored-By: mattwr18 --- .../ContributionForm/ContributionForm.spec.js | 12 +- webapp/components/Editor/Editor.vue | 314 +++++++++--------- .../{spec.js => CommentForm.spec.js} | 23 +- .../{index.vue => CommentForm.vue} | 8 +- webapp/locales/de.json | 6 +- webapp/pages/post/_id/_slug/index.vue | 2 +- 6 files changed, 205 insertions(+), 160 deletions(-) rename webapp/components/comments/CommentForm/{spec.js => CommentForm.spec.js} (84%) rename webapp/components/comments/CommentForm/{index.vue => CommentForm.vue} (94%) diff --git a/webapp/components/ContributionForm/ContributionForm.spec.js b/webapp/components/ContributionForm/ContributionForm.spec.js index 3d136ff4b..a9384c3b6 100644 --- a/webapp/components/ContributionForm/ContributionForm.spec.js +++ b/webapp/components/ContributionForm/ContributionForm.spec.js @@ -26,7 +26,10 @@ describe('ContributionForm.vue', () => { const postTitle = 'this is a title for a post' const postContent = 'this is a post' const imageUpload = { - file: { filename: 'avataar.svg', previewElement: '' }, + file: { + filename: 'avataar.svg', + previewElement: '', + }, url: 'someUrlToImage', } const image = '/uploads/1562010976466-avataaars' @@ -217,7 +220,12 @@ describe('ContributionForm.vue', () => { content: 'auf Deutsch geschrieben', language: 'de', image, - categories: [{ id: 'cat12', name: 'Democracy & Politics' }], + categories: [ + { + id: 'cat12', + name: 'Democracy & Politics', + }, + ], }, } wrapper = Wrapper() diff --git a/webapp/components/Editor/Editor.vue b/webapp/components/Editor/Editor.vue index 4413bfa0d..bb86106cb 100644 --- a/webapp/components/Editor/Editor.vue +++ b/webapp/components/Editor/Editor.vue @@ -224,6 +224,170 @@ export default { doc: { type: Object, default: () => {} }, }, data() { + let mentionExtension + if (!this.users) { + mentionExtension = [] + } else { + mentionExtension = [ + new Mention({ + // a list of all suggested items + items: () => { + return this.users + }, + // is called when a suggestion starts + onEnter: ({ items, query, range, command, virtualNode }) => { + this.suggestionType = this.mentionSuggestionType + + this.query = query + this.filteredItems = items + this.suggestionRange = range + this.renderPopup(virtualNode) + // we save the command for inserting a selected mention + // this allows us to call it inside of our custom popup + // via keyboard navigation and on click + this.insertMentionOrHashtag = command + }, + // is called when a suggestion has changed + onChange: ({ items, query, range, virtualNode }) => { + this.query = query + this.filteredItems = items + this.suggestionRange = range + this.navigatedItemIndex = 0 + this.renderPopup(virtualNode) + }, + // is called when a suggestion is cancelled + onExit: () => { + this.suggestionType = this.nullSuggestionType + + // reset all saved values + this.query = null + this.filteredItems = [] + this.suggestionRange = null + this.navigatedItemIndex = 0 + this.destroyPopup() + }, + // is called on every keyDown event while a suggestion is active + onKeyDown: ({ event }) => { + // pressing up arrow + if (event.keyCode === 38) { + this.upHandler() + return true + } + // pressing down arrow + if (event.keyCode === 40) { + this.downHandler() + return true + } + // pressing enter + if (event.keyCode === 13) { + this.enterHandler() + return true + } + return false + }, + // is called when a suggestion has changed + // this function is optional because there is basic filtering built-in + // you can overwrite it if you prefer your own filtering + // in this example we use fuse.js with support for fuzzy search + onFilter: (items, query) => { + if (!query) { + return items + } + const fuse = new Fuse(items, { + threshold: 0.2, + keys: ['slug'], + }) + return fuse.search(query) + }, + }), + ] + } + + let hashtagExtension + if (!this.hashtags) { + hashtagExtension = [] + } else { + hashtagExtension = [ + new Hashtag({ + // a list of all suggested items + items: () => { + return this.hashtags + }, + // is called when a suggestion starts + onEnter: ({ items, query, range, command, virtualNode }) => { + this.suggestionType = this.hashtagSuggestionType + + this.query = this.sanitizedQuery(query) + this.filteredItems = items + this.suggestionRange = range + this.renderPopup(virtualNode) + // we save the command for inserting a selected mention + // this allows us to call it inside of our custom popup + // via keyboard navigation and on click + this.insertMentionOrHashtag = command + }, + // is called when a suggestion has changed + onChange: ({ items, query, range, virtualNode }) => { + this.query = this.sanitizedQuery(query) + this.filteredItems = items + this.suggestionRange = range + this.navigatedItemIndex = 0 + this.renderPopup(virtualNode) + }, + // is called when a suggestion is cancelled + onExit: () => { + this.suggestionType = this.nullSuggestionType + + // reset all saved values + this.query = null + this.filteredItems = [] + this.suggestionRange = null + this.navigatedItemIndex = 0 + this.destroyPopup() + }, + // is called on every keyDown event while a suggestion is active + onKeyDown: ({ event }) => { + // pressing up arrow + if (event.keyCode === 38) { + this.upHandler() + return true + } + // pressing down arrow + if (event.keyCode === 40) { + this.downHandler() + return true + } + // pressing enter + if (event.keyCode === 13) { + this.enterHandler() + return true + } + // pressing space + if (event.keyCode === 32) { + this.spaceHandler() + return true + } + return false + }, + // is called when a suggestion has changed + // this function is optional because there is basic filtering built-in + // you can overwrite it if you prefer your own filtering + // in this example we use fuse.js with support for fuzzy search + onFilter: (items, query) => { + query = this.sanitizedQuery(query) + if (!query) { + return items + } + return items.filter(item => + JSON.stringify(item) + .toLowerCase() + .includes(query.toLowerCase()), + ) + }, + }), + ] + } + return { lastValueHash: null, editor: new Editor({ @@ -249,154 +413,8 @@ export default { emptyNodeText: this.placeholder || this.$t('editor.placeholder'), }), new History(), - new Mention({ - // a list of all suggested items - items: () => { - return this.users - }, - // is called when a suggestion starts - onEnter: ({ items, query, range, command, virtualNode }) => { - this.suggestionType = this.mentionSuggestionType - - this.query = query - this.filteredItems = items - this.suggestionRange = range - this.renderPopup(virtualNode) - // we save the command for inserting a selected mention - // this allows us to call it inside of our custom popup - // via keyboard navigation and on click - this.insertMentionOrHashtag = command - }, - // is called when a suggestion has changed - onChange: ({ items, query, range, virtualNode }) => { - this.query = query - this.filteredItems = items - this.suggestionRange = range - this.navigatedItemIndex = 0 - this.renderPopup(virtualNode) - }, - // is called when a suggestion is cancelled - onExit: () => { - this.suggestionType = this.nullSuggestionType - - // reset all saved values - this.query = null - this.filteredItems = [] - this.suggestionRange = null - this.navigatedItemIndex = 0 - this.destroyPopup() - }, - // is called on every keyDown event while a suggestion is active - onKeyDown: ({ event }) => { - // pressing up arrow - if (event.keyCode === 38) { - this.upHandler() - return true - } - // pressing down arrow - if (event.keyCode === 40) { - this.downHandler() - return true - } - // pressing enter - if (event.keyCode === 13) { - this.enterHandler() - return true - } - return false - }, - // is called when a suggestion has changed - // this function is optional because there is basic filtering built-in - // you can overwrite it if you prefer your own filtering - // in this example we use fuse.js with support for fuzzy search - onFilter: (items, query) => { - if (!query) { - return items - } - const fuse = new Fuse(items, { - threshold: 0.2, - keys: ['slug'], - }) - return fuse.search(query) - }, - }), - new Hashtag({ - // a list of all suggested items - items: () => { - return this.hashtags - }, - // is called when a suggestion starts - onEnter: ({ items, query, range, command, virtualNode }) => { - this.suggestionType = this.hashtagSuggestionType - - this.query = this.sanitizedQuery(query) - this.filteredItems = items - this.suggestionRange = range - this.renderPopup(virtualNode) - // we save the command for inserting a selected mention - // this allows us to call it inside of our custom popup - // via keyboard navigation and on click - this.insertMentionOrHashtag = command - }, - // is called when a suggestion has changed - onChange: ({ items, query, range, virtualNode }) => { - this.query = this.sanitizedQuery(query) - this.filteredItems = items - this.suggestionRange = range - this.navigatedItemIndex = 0 - this.renderPopup(virtualNode) - }, - // is called when a suggestion is cancelled - onExit: () => { - this.suggestionType = this.nullSuggestionType - - // reset all saved values - this.query = null - this.filteredItems = [] - this.suggestionRange = null - this.navigatedItemIndex = 0 - this.destroyPopup() - }, - // is called on every keyDown event while a suggestion is active - onKeyDown: ({ event }) => { - // pressing up arrow - if (event.keyCode === 38) { - this.upHandler() - return true - } - // pressing down arrow - if (event.keyCode === 40) { - this.downHandler() - return true - } - // pressing enter - if (event.keyCode === 13) { - this.enterHandler() - return true - } - // pressing space - if (event.keyCode === 32) { - this.spaceHandler() - return true - } - return false - }, - // is called when a suggestion has changed - // this function is optional because there is basic filtering built-in - // you can overwrite it if you prefer your own filtering - // in this example we use fuse.js with support for fuzzy search - onFilter: (items, query) => { - query = this.sanitizedQuery(query) - if (!query) { - return items - } - return items.filter(item => - JSON.stringify(item) - .toLowerCase() - .includes(query.toLowerCase()), - ) - }, - }), + ...mentionExtension, + ...hashtagExtension, ], onUpdate: e => { clearTimeout(throttleInputEvent) diff --git a/webapp/components/comments/CommentForm/spec.js b/webapp/components/comments/CommentForm/CommentForm.spec.js similarity index 84% rename from webapp/components/comments/CommentForm/spec.js rename to webapp/components/comments/CommentForm/CommentForm.spec.js index c3622151c..01dd4d36d 100644 --- a/webapp/components/comments/CommentForm/spec.js +++ b/webapp/components/comments/CommentForm/CommentForm.spec.js @@ -1,5 +1,5 @@ import { mount, createLocalVue } from '@vue/test-utils' -import CommentForm from './index.vue' +import CommentForm from './CommentForm.vue' import Styleguide from '@human-connection/styleguide' import Vuex from 'vuex' @@ -21,9 +21,15 @@ describe('CommentForm.vue', () => { mutate: jest .fn() .mockResolvedValueOnce({ - data: { CreateComment: { contentExcerpt: 'this is a comment' } }, + data: { + CreateComment: { + contentExcerpt: 'this is a comment', + }, + }, }) - .mockRejectedValue({ message: 'Ouch!' }), + .mockRejectedValue({ + message: 'Ouch!', + }), }, $toast: { error: jest.fn(), @@ -31,7 +37,9 @@ describe('CommentForm.vue', () => { }, } propsData = { - post: { id: 1 }, + post: { + id: 1, + }, } }) @@ -45,7 +53,12 @@ describe('CommentForm.vue', () => { getters, }) const Wrapper = () => { - return mount(CommentForm, { mocks, localVue, propsData, store }) + return mount(CommentForm, { + mocks, + localVue, + propsData, + store, + }) } beforeEach(() => { diff --git a/webapp/components/comments/CommentForm/index.vue b/webapp/components/comments/CommentForm/CommentForm.vue similarity index 94% rename from webapp/components/comments/CommentForm/index.vue rename to webapp/components/comments/CommentForm/CommentForm.vue index 14dcace43..89260c670 100644 --- a/webapp/components/comments/CommentForm/index.vue +++ b/webapp/components/comments/CommentForm/CommentForm.vue @@ -2,7 +2,13 @@