mirror of
https://github.com/Ocelot-Social-Community/Ocelot-Social.git
synced 2026-02-06 09:56:03 +00:00
Add: AutoSave Plugin for ProseMirror
This commit is contained in:
parent
a9111973c8
commit
0d397655ee
@ -1,22 +1,28 @@
|
||||
import { mount } from '@vue/test-utils'
|
||||
import Editor from './Editor'
|
||||
|
||||
import MutationObserver from 'mutation-observer'
|
||||
import Vue from 'vue'
|
||||
import VueRouter from 'vue-router'
|
||||
import { mount } from '@vue/test-utils'
|
||||
import MutationObserver from 'mutation-observer'
|
||||
|
||||
import Editor from './Editor'
|
||||
|
||||
global.MutationObserver = MutationObserver
|
||||
|
||||
const localVue = global.localVue
|
||||
localVue.use(VueRouter)
|
||||
|
||||
const localStorage = global.localStorage
|
||||
|
||||
describe('Editor.vue', () => {
|
||||
let wrapper
|
||||
let propsData
|
||||
let mocks
|
||||
let router
|
||||
|
||||
const Wrapper = () => {
|
||||
return (wrapper = mount(Editor, {
|
||||
mocks,
|
||||
propsData,
|
||||
router,
|
||||
localVue,
|
||||
sync: false,
|
||||
stubs: {
|
||||
@ -141,4 +147,65 @@ describe('Editor.vue', () => {
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe(':autosave', () => {
|
||||
const getFirst = () => {
|
||||
const storageKey = Object.keys(localStorage)[0]
|
||||
const value = localStorage[storageKey]
|
||||
return {
|
||||
storageKey,
|
||||
value,
|
||||
}
|
||||
}
|
||||
describe('when false', () => {
|
||||
let routerWrapper
|
||||
|
||||
beforeEach(() => {
|
||||
router = new VueRouter({
|
||||
routes: [{ path: 'post/create' }],
|
||||
})
|
||||
router.push('/post/create')
|
||||
propsData.autosave = false
|
||||
routerWrapper = Wrapper()
|
||||
})
|
||||
|
||||
it('does nothing', () => {
|
||||
jest.useFakeTimers()
|
||||
|
||||
const content = '<p>NOOP WIP</p>'
|
||||
routerWrapper.vm.editor.setContent(content, true)
|
||||
jest.runAllTimers()
|
||||
|
||||
expect(Object.keys(localStorage).length).toBe(0)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when editing a post', () => {
|
||||
let routerWrapper
|
||||
const content = '<p>Post WIP</p>'
|
||||
const setItemSpy = jest.spyOn(Storage.prototype, 'setItem')
|
||||
|
||||
beforeEach(() => {
|
||||
router = new VueRouter({
|
||||
routes: [{ path: 'post/create' }],
|
||||
})
|
||||
router.push('/post/create')
|
||||
routerWrapper = Wrapper()
|
||||
})
|
||||
|
||||
afterEach(setItemSpy.mockReset)
|
||||
|
||||
it('saves editor content to localStorage on input', async () => {
|
||||
jest.useFakeTimers()
|
||||
|
||||
routerWrapper.vm.editor.setContent(content, true)
|
||||
await jest.runAllTimers()
|
||||
|
||||
const { storageKey, value } = getFirst()
|
||||
expect(setItemSpy).toHaveBeenCalled()
|
||||
expect(storageKey.startsWith('draft:post:')).toBe(true)
|
||||
expect(value).toBe(content)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { storiesOf } from '@storybook/vue'
|
||||
import { withA11y } from '@storybook/addon-a11y'
|
||||
import StoryRouter from 'storybook-vue-router'
|
||||
import HcEditor from '~/components/Editor/Editor.vue'
|
||||
import helpers from '~/storybook/helpers'
|
||||
import Vue from 'vue'
|
||||
@ -35,6 +36,7 @@ const users = [
|
||||
|
||||
storiesOf('Editor', module)
|
||||
.addDecorator(withA11y)
|
||||
.addDecorator(StoryRouter())
|
||||
.addDecorator((storyFn) => {
|
||||
const ctx = storyFn()
|
||||
return {
|
||||
|
||||
@ -52,6 +52,7 @@ export default {
|
||||
hashtags: { type: Array, default: () => null }, // If 'null', than the Hashtag extention is not assigned.
|
||||
value: { type: String, default: '' },
|
||||
doc: { type: Object, default: () => {} },
|
||||
autosave: { type: Boolean, default: true },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
||||
@ -4,6 +4,7 @@ import Link from '~/components/Editor/nodes/Link.js'
|
||||
import Strike from '~/components/Editor/marks/Strike'
|
||||
import Italic from '~/components/Editor/marks/Italic'
|
||||
import Bold from '~/components/Editor/marks/Bold'
|
||||
import AutoSave from '~/components/Editor/plugins/autoSave'
|
||||
import EmbedQuery from '~/graphql/EmbedQuery.js'
|
||||
import {
|
||||
Heading,
|
||||
@ -18,8 +19,8 @@ import {
|
||||
} from 'tiptap-extensions'
|
||||
|
||||
export default function defaultExtensions(component) {
|
||||
const { placeholder, $t, $apollo } = component
|
||||
return [
|
||||
const { autosave, placeholder, $t, $apollo, $route } = component
|
||||
const extensions = [
|
||||
new Heading(),
|
||||
new HardBreak(),
|
||||
new Blockquote(),
|
||||
@ -54,4 +55,10 @@ export default function defaultExtensions(component) {
|
||||
},
|
||||
}),
|
||||
]
|
||||
|
||||
if (autosave && $route) {
|
||||
extensions.push(new AutoSave({ $route }))
|
||||
}
|
||||
|
||||
return extensions
|
||||
}
|
||||
|
||||
55
webapp/components/Editor/plugins/autoSave.js
Normal file
55
webapp/components/Editor/plugins/autoSave.js
Normal file
@ -0,0 +1,55 @@
|
||||
import { Extension, Plugin, PluginKey } from 'tiptap'
|
||||
import { DOMSerializer } from 'prosemirror-model'
|
||||
|
||||
export default class AutoSave extends Extension {
|
||||
constructor({ $route }) {
|
||||
super()
|
||||
this.route = $route
|
||||
this._postId = 'randomIdForPosts'
|
||||
}
|
||||
|
||||
static toHTML(content, schema) {
|
||||
const container = document.createElement('div')
|
||||
const fragment = DOMSerializer.fromSchema(schema).serializeFragment(content)
|
||||
container.appendChild(fragment)
|
||||
return container.innerHTML
|
||||
}
|
||||
|
||||
get name() {
|
||||
return 'auto_save'
|
||||
}
|
||||
|
||||
get storageKey() {
|
||||
if (this.route.path === '/post/create') {
|
||||
return `draft:post:${this._postId}`
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
get plugins() {
|
||||
return [
|
||||
new Plugin({
|
||||
key: new PluginKey('auto_save'),
|
||||
filterTransaction: (tr, editorState) => {
|
||||
if (tr.docChanged) {
|
||||
localStorage.setItem(
|
||||
this.storageKey,
|
||||
AutoSave.toHTML(tr.doc.content, editorState.config.schema),
|
||||
)
|
||||
}
|
||||
return tr
|
||||
},
|
||||
state: {
|
||||
init() {
|
||||
return {
|
||||
saveNextUpdate: false,
|
||||
}
|
||||
},
|
||||
apply(_, prev) {
|
||||
return { ...prev }
|
||||
},
|
||||
},
|
||||
}),
|
||||
]
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user