commit
19cb98b85f
@ -0,0 +1,13 @@
|
||||
# Changelog
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
|
||||
## [Unreleased]
|
||||
### Added
|
||||
- Emoji picker
|
||||
- Started changelog anew
|
||||
### Changed
|
||||
- changed the way fading effects for user profile/long statuses works, now uses css-mask instead of gradient background hacks which weren't exactly compatible with semi-transparent themes
|
||||
### Fixed
|
||||
- improved hotkey behavior on autocomplete popup
|
After Width: | Height: | Size: 491 B |
@ -0,0 +1,115 @@
|
||||
|
||||
const filterByKeyword = (list, keyword = '') => {
|
||||
return list.filter(x => x.displayText.includes(keyword))
|
||||
}
|
||||
|
||||
const EmojiPicker = {
|
||||
props: {
|
||||
enableStickerPicker: {
|
||||
required: false,
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
labelKey: String(Math.random() * 100000),
|
||||
keyword: '',
|
||||
activeGroup: 'custom',
|
||||
showingStickers: false,
|
||||
groupsScrolledClass: 'scrolled-top',
|
||||
keepOpen: false
|
||||
}
|
||||
},
|
||||
components: {
|
||||
StickerPicker: () => import('../sticker_picker/sticker_picker.vue')
|
||||
},
|
||||
methods: {
|
||||
onEmoji (emoji) {
|
||||
const value = emoji.imageUrl ? `:${emoji.displayText}:` : emoji.replacement
|
||||
this.$emit('emoji', { insertion: value, keepOpen: this.keepOpen })
|
||||
},
|
||||
highlight (key) {
|
||||
const ref = this.$refs['group-' + key]
|
||||
const top = ref[0].offsetTop
|
||||
this.setShowStickers(false)
|
||||
this.activeGroup = key
|
||||
this.$nextTick(() => {
|
||||
this.$refs['emoji-groups'].scrollTop = top + 1
|
||||
})
|
||||
},
|
||||
scrolledGroup (e) {
|
||||
const target = (e && e.target) || this.$refs['emoji-groups']
|
||||
const top = target.scrollTop + 5
|
||||
if (target.scrollTop <= 5) {
|
||||
this.groupsScrolledClass = 'scrolled-top'
|
||||
} else if (target.scrollTop >= target.scrollTopMax - 5) {
|
||||
this.groupsScrolledClass = 'scrolled-bottom'
|
||||
} else {
|
||||
this.groupsScrolledClass = 'scrolled-middle'
|
||||
}
|
||||
this.$nextTick(() => {
|
||||
this.emojisView.forEach(group => {
|
||||
const ref = this.$refs['group-' + group.id]
|
||||
if (ref[0].offsetTop <= top) {
|
||||
this.activeGroup = group.id
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
toggleStickers () {
|
||||
this.showingStickers = !this.showingStickers
|
||||
},
|
||||
setShowStickers (value) {
|
||||
this.showingStickers = value
|
||||
},
|
||||
onStickerUploaded (e) {
|
||||
this.$emit('sticker-uploaded', e)
|
||||
},
|
||||
onStickerUploadFailed (e) {
|
||||
this.$emit('sticker-upload-failed', e)
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
keyword () {
|
||||
this.scrolledGroup()
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
activeGroupView () {
|
||||
return this.showingStickers ? '' : this.activeGroup
|
||||
},
|
||||
stickersAvailable () {
|
||||
if (this.$store.state.instance.stickers) {
|
||||
return this.$store.state.instance.stickers.length > 0
|
||||
}
|
||||
return 0
|
||||
},
|
||||
emojis () {
|
||||
const standardEmojis = this.$store.state.instance.emoji || []
|
||||
const customEmojis = this.$store.state.instance.customEmoji || []
|
||||
return [
|
||||
{
|
||||
id: 'custom',
|
||||
text: this.$t('emoji.custom'),
|
||||
icon: 'icon-smile',
|
||||
emojis: filterByKeyword(customEmojis, this.keyword)
|
||||
},
|
||||
{
|
||||
id: 'standard',
|
||||
text: this.$t('emoji.unicode'),
|
||||
icon: 'icon-picture',
|
||||
emojis: filterByKeyword(standardEmojis, this.keyword)
|
||||
}
|
||||
]
|
||||
},
|
||||
emojisView () {
|
||||
return this.emojis.filter(value => value.emojis.length > 0)
|
||||
},
|
||||
stickerPickerEnabled () {
|
||||
return (this.$store.state.instance.stickers || []).length !== 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default EmojiPicker
|
@ -0,0 +1,165 @@
|
||||
@import '../../_variables.scss';
|
||||
|
||||
.emoji-picker {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
left: 0;
|
||||
height: 320px;
|
||||
margin: 0 !important;
|
||||
z-index: 1;
|
||||
|
||||
.keep-open {
|
||||
padding: 7px;
|
||||
line-height: normal;
|
||||
}
|
||||
.keep-open-label {
|
||||
padding: 0 7px;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.heading {
|
||||
display: flex;
|
||||
height: 32px;
|
||||
padding: 10px 7px 5px;
|
||||
}
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1 1 0;
|
||||
min-height: 0px;
|
||||
}
|
||||
|
||||
.emoji-tabs {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.additional-tabs {
|
||||
border-left: 1px solid;
|
||||
border-left-color: $fallback--icon;
|
||||
border-left-color: var(--icon, $fallback--icon);
|
||||
padding-left: 7px;
|
||||
flex: 0 0 0;
|
||||
}
|
||||
|
||||
.additional-tabs,
|
||||
.emoji-tabs {
|
||||
display: block;
|
||||
min-width: 0;
|
||||
flex-basis: auto;
|
||||
flex-shrink: 1;
|
||||
|
||||
&-item {
|
||||
padding: 0 7px;
|
||||
cursor: pointer;
|
||||
font-size: 24px;
|
||||
|
||||
&.disabled {
|
||||
opacity: 0.5;
|
||||
pointer-events: none;
|
||||
}
|
||||
&.active {
|
||||
border-bottom: 4px solid;
|
||||
|
||||
i {
|
||||
color: $fallback--lightText;
|
||||
color: var(--lightText, $fallback--lightText);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.sticker-picker {
|
||||
flex: 1 1 0
|
||||
}
|
||||
|
||||
.stickers,
|
||||
.emoji {
|
||||
&-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1 1 0;
|
||||
min-height: 0;
|
||||
|
||||
&.hidden {
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.emoji {
|
||||
&-search {
|
||||
padding: 5px;
|
||||
flex: 0 0 0;
|
||||
|
||||
input {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
&-groups {
|
||||
flex: 1 1 1px;
|
||||
position: relative;
|
||||
overflow: auto;
|
||||
user-select: none;
|
||||
mask: linear-gradient(to top, white 0, transparent 100%) bottom no-repeat,
|
||||
linear-gradient(to bottom, white 0, transparent 100%) top no-repeat,
|
||||
linear-gradient(to top, white, white);
|
||||
transition: mask-size 150ms;
|
||||
mask-size: 100% 20px, 100% 20px, auto;
|
||||
// Autoprefixed seem to ignore this one, and also syntax is different
|
||||
-webkit-mask-composite: xor;
|
||||
mask-composite: exclude;
|
||||
&.scrolled {
|
||||
&-top {
|
||||
mask-size: 100% 20px, 100% 0, auto;
|
||||
}
|
||||
&-bottom {
|
||||
mask-size: 100% 0, 100% 20px, auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-group {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
padding-left: 5px;
|
||||
justify-content: left;
|
||||
|
||||
&-title {
|
||||
font-size: 12px;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
&.disabled {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-item {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
font-size: 32px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 4px;
|
||||
|
||||
cursor: pointer;
|
||||
|
||||
img {
|
||||
object-fit: contain;
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,110 @@
|
||||
<template>
|
||||
<div class="emoji-picker panel panel-default panel-body">
|
||||
<div class="heading">
|
||||
<span class="emoji-tabs">
|
||||
<span
|
||||
v-for="group in emojis"
|
||||
:key="group.id"
|
||||
class="emoji-tabs-item"
|
||||
:class="{
|
||||
active: activeGroupView === group.id,
|
||||
disabled: group.emojis.length === 0
|
||||
}"
|
||||
:title="group.text"
|
||||
@click.prevent="highlight(group.id)"
|
||||
>
|
||||
<i :class="group.icon" />
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
v-if="stickerPickerEnabled"
|
||||
class="additional-tabs"
|
||||
>
|
||||
<span
|
||||
class="stickers-tab-icon additional-tabs-item"
|
||||
:class="{active: showingStickers}"
|
||||
:title="$t('emoji.stickers')"
|
||||
@click.prevent="toggleStickers"
|
||||
>
|
||||
<i class="icon-star" />
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="content">
|
||||
<div
|
||||
class="emoji-content"
|
||||
:class="{hidden: showingStickers}"
|
||||
>
|
||||
<div class="emoji-search">
|
||||
<input
|
||||
v-model="keyword"
|
||||
type="text"
|
||||
class="form-control"
|
||||
:placeholder="$t('emoji.search_emoji')"
|
||||
>
|
||||
</div>
|
||||
<div
|
||||
ref="emoji-groups"
|
||||
class="emoji-groups"
|
||||
:class="groupsScrolledClass"
|
||||
@scroll="scrolledGroup"
|
||||
>
|
||||
<div
|
||||
v-for="group in emojisView"
|
||||
:key="group.id"
|
||||
class="emoji-group"
|
||||
>
|
||||
<h6
|
||||
:ref="'group-' + group.id"
|
||||
class="emoji-group-title"
|
||||
>
|
||||
{{ group.text }}
|
||||
</h6>
|
||||
<span
|
||||
v-for="emoji in group.emojis"
|
||||
:key="group.id + emoji.displayText"
|
||||
:title="emoji.displayText"
|
||||
class="emoji-item"
|
||||
@click.stop.prevent="onEmoji(emoji)"
|
||||
>
|
||||
<span v-if="!emoji.imageUrl">{{ emoji.replacement }}</span>
|
||||
<img
|
||||
v-else
|
||||
:src="emoji.imageUrl"
|
||||
>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="keep-open"
|
||||
>
|
||||
<input
|
||||
:id="labelKey + 'keep-open'"
|
||||
v-model="keepOpen"
|
||||
type="checkbox"
|
||||
>
|
||||
<label
|
||||
class="keep-open-label"
|
||||
:for="labelKey + 'keep-open'"
|
||||
>
|
||||
<div class="keep-open-label-text">
|
||||
{{ $t('emoji.keep_open') }}
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="showingStickers"
|
||||
class="stickers-content"
|
||||
>
|
||||
<sticker-picker
|
||||
@uploaded="onStickerUploaded"
|
||||
@upload-failed="onStickerUploadFailed"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script src="./emoji_picker.js"></script>
|
||||
<style lang="scss" src="./emoji_picker.scss"></style>
|
@ -0,0 +1,32 @@
|
||||
import PostStatusForm from '../post_status_form/post_status_form.vue'
|
||||
|
||||
const PostStatusModal = {
|
||||
components: {
|
||||
PostStatusForm
|
||||
},
|
||||
computed: {
|
||||
isLoggedIn () {
|
||||
return !!this.$store.state.users.currentUser
|
||||
},
|
||||
isOpen () {
|
||||
return this.isLoggedIn && this.$store.state.postStatus.modalActivated
|
||||
},
|
||||
params () {
|
||||
return this.$store.state.postStatus.params || {}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
isOpen (val) {
|
||||
if (val) {
|
||||
this.$nextTick(() => this.$el.querySelector('textarea').focus())
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
closeModal () {
|
||||
this.$store.dispatch('closePostStatusModal')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default PostStatusModal
|
@ -0,0 +1,43 @@
|
||||
<template>
|
||||
<div
|
||||
v-if="isOpen"
|
||||
class="post-form-modal-view modal-view"
|
||||
@click="closeModal"
|
||||
>
|
||||
<div
|
||||
class="post-form-modal-panel panel"
|
||||
@click.stop=""
|
||||
>
|
||||
<div class="panel-heading">
|
||||
{{ $t('post_status.new_status') }}
|
||||
</div>
|
||||
<PostStatusForm
|
||||
class="panel-body"
|
||||
v-bind="params"
|
||||
@posted="closeModal"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script src="./post_status_modal.js"></script>
|
||||
|
||||
<style lang="scss">
|
||||
@import '../../_variables.scss';
|
||||
|
||||
.post-form-modal-view {
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.post-form-modal-panel {
|
||||
flex-shrink: 0;
|
||||
margin-top: 25%;
|
||||
margin-bottom: 2em;
|
||||
width: 100%;
|
||||
max-width: 700px;
|
||||
|
||||
@media (orientation: landscape) {
|
||||
margin-top: 8%;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -0,0 +1,69 @@
|
||||
import * as bodyScrollLock from 'body-scroll-lock'
|
||||
|
||||
let previousNavPaddingRight
|
||||
let previousAppBgWrapperRight
|
||||
|
||||
const disableBodyScroll = (el) => {
|
||||
const scrollBarGap = window.innerWidth - document.documentElement.clientWidth
|
||||
bodyScrollLock.disableBodyScroll(el, {
|
||||
reserveScrollBarGap: true
|
||||
})
|
||||
setTimeout(() => {
|
||||
// If previousNavPaddingRight is already set, don't set it again.
|
||||
if (previousNavPaddingRight === undefined) {
|
||||
const navEl = document.getElementById('nav')
|
||||
previousNavPaddingRight = window.getComputedStyle(navEl).getPropertyValue('padding-right')
|
||||
navEl.style.paddingRight = previousNavPaddingRight ? `calc(${previousNavPaddingRight} + ${scrollBarGap}px)` : `${scrollBarGap}px`
|
||||
}
|
||||
// If previousAppBgWrapeprRight is already set, don't set it again.
|
||||
if (previousAppBgWrapperRight === undefined) {
|
||||
const appBgWrapperEl = document.getElementById('app_bg_wrapper')
|
||||
previousAppBgWrapperRight = window.getComputedStyle(appBgWrapperEl).getPropertyValue('right')
|
||||
appBgWrapperEl.style.right = previousAppBgWrapperRight ? `calc(${previousAppBgWrapperRight} + ${scrollBarGap}px)` : `${scrollBarGap}px`
|
||||
}
|
||||
document.body.classList.add('scroll-locked')
|
||||
})
|
||||
}
|
||||
|
||||
const enableBodyScroll = (el) => {
|
||||
setTimeout(() => {
|
||||
if (previousNavPaddingRight !== undefined) {
|
||||
document.getElementById('nav').style.paddingRight = previousNavPaddingRight
|
||||
// Restore previousNavPaddingRight to undefined so disableBodyScroll knows it can be set again.
|
||||
previousNavPaddingRight = undefined
|
||||
}
|
||||
if (previousAppBgWrapperRight !== undefined) {
|
||||
document.getElementById('app_bg_wrapper').style.right = previousAppBgWrapperRight
|
||||
// Restore previousAppBgWrapperRight to undefined so disableBodyScroll knows it can be set again.
|
||||
previousAppBgWrapperRight = undefined
|
||||
}
|
||||
document.body.classList.remove('scroll-locked')
|
||||
})
|
||||
bodyScrollLock.enableBodyScroll(el)
|
||||
}
|
||||
|
||||
const directive = {
|
||||
inserted: (el, binding) => {
|
||||
if (binding.value) {
|
||||
disableBodyScroll(el)
|
||||
}
|
||||
},
|
||||
componentUpdated: (el, binding) => {
|
||||
if (binding.oldValue === binding.value) {
|
||||
return
|
||||
}
|
||||
|
||||
if (binding.value) {
|
||||
disableBodyScroll(el)
|
||||
} else {
|
||||
enableBodyScroll(el)
|
||||
}
|
||||
},
|
||||
unbind: (el) => {
|
||||
enableBodyScroll(el)
|
||||
}
|
||||
}
|
||||
|
||||
export default (Vue) => {
|
||||
Vue.directive('body-scroll-lock', directive)
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
const postStatus = {
|
||||
state: {
|
||||
params: null,
|
||||
modalActivated: false
|
||||
},
|
||||
mutations: {
|
||||
openPostStatusModal (state, params) {
|
||||
state.params = params
|
||||
state.modalActivated = true
|
||||
},
|
||||
closePostStatusModal (state) {
|
||||
state.modalActivated = false
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
openPostStatusModal ({ commit }, params) {
|
||||
commit('openPostStatusModal', params)
|
||||
},
|
||||
closePostStatusModal ({ commit }) {
|
||||
commit('closePostStatusModal')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default postStatus
|
@ -0,0 +1,31 @@
|
||||
export const findOffset = (child, parent, { top = 0, left = 0 } = {}, ignorePadding = true) => {
|
||||
const result = {
|
||||
top: top + child.offsetTop,
|
||||
left: left + child.offsetLeft
|
||||
}
|
||||
if (!ignorePadding && child !== window) {
|
||||
const { topPadding, leftPadding } = findPadding(child)
|
||||
result.top += ignorePadding ? 0 : topPadding
|
||||
result.left += ignorePadding ? 0 : leftPadding
|
||||
}
|
||||
|
||||
if (child.offsetParent && (parent === window || parent.contains(child.offsetParent) || parent === child.offsetParent)) {
|
||||
return findOffset(child.offsetParent, parent, result, false)
|
||||
} else {
|
||||
if (parent !== window) {
|
||||
const { topPadding, leftPadding } = findPadding(parent)
|
||||
result.top += topPadding
|
||||
result.left += leftPadding
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
const findPadding = (el) => {
|
||||
const topPaddingStr = window.getComputedStyle(el)['padding-top']
|
||||
const topPadding = Number(topPaddingStr.substring(0, topPaddingStr.length - 2))
|
||||
const leftPaddingStr = window.getComputedStyle(el)['padding-left']
|
||||
const leftPadding = Number(leftPaddingStr.substring(0, leftPaddingStr.length - 2))
|
||||
|
||||
return { topPadding, leftPadding }
|
||||
}
|
@ -0,0 +1,131 @@
|
||||
import { shallowMount, createLocalVue } from '@vue/test-utils'
|
||||
import EmojiInput from 'src/components/emoji_input/emoji_input.vue'
|
||||
|
||||
const generateInput = (value, padEmoji = true) => {
|
||||
const localVue = createLocalVue()
|
||||
localVue.directive('click-outside', () => {})
|
||||
const wrapper = shallowMount(EmojiInput, {
|
||||
propsData: {
|
||||
suggest: () => [],
|
||||
enableEmojiPicker: true,
|
||||
value
|
||||
},
|
||||
mocks: {
|
||||
$store: {
|
||||
state: {
|
||||
config: {
|
||||
padEmoji
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
slots: {
|
||||
default: '<input />'
|
||||
},
|
||||
localVue
|
||||
})
|
||||
return [wrapper, localVue]
|
||||
}
|
||||
|
||||
describe('EmojiInput', () => {
|
||||
describe('insertion mechanism', () => {
|
||||
it('inserts string at the end with trailing space', () => {
|
||||
const initialString = 'Testing'
|
||||
const [wrapper] = generateInput(initialString)
|
||||
const input = wrapper.find('input')
|
||||
input.setValue(initialString)
|
||||
wrapper.setData({ caret: initialString.length })
|
||||
wrapper.vm.insert({ insertion: '(test)', keepOpen: false })
|
||||
expect(wrapper.emitted().input[0][0]).to.eql('Testing (test) ')
|
||||
})
|
||||
|
||||
it('inserts string at the end with trailing space (source has a trailing space)', () => {
|
||||
const initialString = 'Testing '
|
||||
const [wrapper] = generateInput(initialString)
|
||||
const input = wrapper.find('input')
|
||||
input.setValue(initialString)
|
||||
wrapper.setData({ caret: initialString.length })
|
||||
wrapper.vm.insert({ insertion: '(test)', keepOpen: false })
|
||||
expect(wrapper.emitted().input[0][0]).to.eql('Testing (test) ')
|
||||
})
|
||||
|
||||
it('inserts string at the begginning without leading space', () => {
|
||||
const initialString = 'Testing'
|
||||
const [wrapper] = generateInput(initialString)
|
||||
const input = wrapper.find('input')
|
||||
input.setValue(initialString)
|
||||
wrapper.setData({ caret: 0 })
|
||||
wrapper.vm.insert({ insertion: '(test)', keepOpen: false })
|
||||
expect(wrapper.emitted().input[0][0]).to.eql('(test) Testing')
|
||||
})
|
||||
|
||||
it('inserts string between words without creating extra spaces', () => {
|
||||
const initialString = 'Spurdo Sparde'
|
||||
const [wrapper] = generateInput(initialString)
|
||||
const input = wrapper.find('input')
|
||||
input.setValue(initialString)
|
||||
wrapper.setData({ caret: 6 })
|
||||
wrapper.vm.insert({ insertion: ':ebin:', keepOpen: false })
|
||||
expect(wrapper.emitted().input[0][0]).to.eql('Spurdo :ebin: Sparde')
|
||||
})
|
||||
|
||||
it('inserts string between words without creating extra spaces (other caret)', () => {
|
||||
const initialString = 'Spurdo Sparde'
|
||||
const [wrapper] = generateInput(initialString)
|
||||
const input = wrapper.find('input')
|
||||
input.setValue(initialString)
|
||||
wrapper.setData({ caret: 7 })
|
||||
wrapper.vm.insert({ insertion: ':ebin:', keepOpen: false })
|
||||
expect(wrapper.emitted().input[0][0]).to.eql('Spurdo :ebin: Sparde')
|
||||
})
|
||||
|
||||
it('inserts string without any padding if padEmoji setting is set to false', () => {
|
||||
const initialString = 'Eat some spam!'
|
||||
const [wrapper] = generateInput(initialString, false)
|
||||
const input = wrapper.find('input')
|
||||
input.setValue(initialString)
|
||||
wrapper.setData({ caret: initialString.length, keepOpen: false })
|
||||
wrapper.vm.insert({ insertion: ':spam:' })
|
||||
expect(wrapper.emitted().input[0][0]).to.eql('Eat some spam!:spam:')
|
||||
})
|
||||
|
||||
it('correctly sets caret after insertion at beginning', (done) => {
|
||||
const initialString = '1234'
|
||||
const [wrapper, vue] = generateInput(initialString)
|
||||
const input = wrapper.find('input')
|
||||
input.setValue(initialString)
|
||||
wrapper.setData({ caret: 0 })
|
||||
wrapper.vm.insert({ insertion: '1234', keepOpen: false })
|
||||
vue.nextTick(() => {
|
||||
expect(wrapper.vm.caret).to.eql(5)
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('correctly sets caret after insertion at end', (done) => {
|
||||
const initialString = '1234'
|
||||
const [wrapper, vue] = generateInput(initialString)
|
||||
const input = wrapper.find('input')
|
||||
input.setValue(initialString)
|
||||
wrapper.setData({ caret: initialString.length })
|
||||
wrapper.vm.insert({ insertion: '1234', keepOpen: false })
|
||||
vue.nextTick(() => {
|
||||
expect(wrapper.vm.caret).to.eql(10)
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('correctly sets caret after insertion if padEmoji setting is set to false', (done) => {
|
||||
const initialString = '1234'
|
||||
const [wrapper, vue] = generateInput(initialString, false)
|
||||
const input = wrapper.find('input')
|
||||
input.setValue(initialString)
|
||||
wrapper.setData({ caret: initialString.length })
|
||||
wrapper.vm.insert({ insertion: '1234', keepOpen: false })
|
||||
vue.nextTick(() => {
|
||||
expect(wrapper.vm.caret).to.eql(8)
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
Loading…
Reference in new issue