Fixes #2886 - Text Editor deletes text unrecoverably if quicktext was canceled with Ctrl+Backspace.

This commit is contained in:
Dominik Klein 2021-07-23 21:44:18 +00:00
parent 6da7f50f9b
commit 7e488ad134
4 changed files with 65 additions and 9 deletions

View file

@ -223,6 +223,10 @@ group :development, :test do
# Slack helper for testing # Slack helper for testing
gem 'slack-ruby-client', require: false gem 'slack-ruby-client', require: false
# Can be used to detect for example the current
# operating system in tests, to handle things differently.
gem 'os'
end end
# Want to extend Zammad with additional gems? # Want to extend Zammad with additional gems?

View file

@ -394,6 +394,7 @@ GEM
omniauth (~> 1.5) omniauth (~> 1.5)
omniauth-oauth2 (>= 1.4.0) omniauth-oauth2 (>= 1.4.0)
openssl (2.2.0) openssl (2.2.0)
os (1.1.1)
overcommit (0.58.0) overcommit (0.58.0)
childprocess (>= 0.6.3, < 5) childprocess (>= 0.6.3, < 5)
iniparse (~> 1.4) iniparse (~> 1.4)
@ -710,6 +711,7 @@ DEPENDENCIES
omniauth-twitter omniauth-twitter
omniauth-weibo-oauth2 omniauth-weibo-oauth2
openssl openssl
os
overcommit overcommit
pg (= 0.21.0) pg (= 0.21.0)
pry-rails pry-rails

View file

@ -27,6 +27,7 @@
this.collection = [] this.collection = []
this.active = false this.active = false
this.buffer = '' this.buffer = ''
this.oldElementText = ''
// check if ce exists // check if ce exists
if ( $.data(element, 'plugin_ce') ) { if ( $.data(element, 'plugin_ce') ) {
@ -55,10 +56,11 @@
} }
Plugin.prototype.onKeydown = function (e) { Plugin.prototype.onKeydown = function (e) {
//console.log("onKeydown", this.isActive()) // Saves the old element text for some special situations.
this.oldElementText = this.$element.text()
// navigate through item // navigate through item
if (this.isActive()) { if (this.isActive()) {
// esc // esc
if (e.keyCode === 27) { if (e.keyCode === 27) {
e.preventDefault() e.preventDefault()
@ -133,7 +135,7 @@
// reduce buffer, in case close it // reduce buffer, in case close it
// backspace // backspace
if (e.keyCode === 8 && this.buffer) { if (e.keyCode === 8 && !( e.ctrlKey || e.metaKey ) && this.buffer) {
var trigger = this.findTrigger(this.buffer) var trigger = this.findTrigger(this.buffer)
// backspace + buffer === :: -> close textmodule // backspace + buffer === :: -> close textmodule
@ -162,19 +164,47 @@
Plugin.prototype.onKeypress = function (e) { Plugin.prototype.onKeypress = function (e) {
this.log('BUFF', this.buffer, e.keyCode, String.fromCharCode(e.which)) this.log('BUFF', this.buffer, e.keyCode, String.fromCharCode(e.which))
// gets the character and keycode from event // gets the character and keycode from event
// this event does not have keyCode and which value set // this event does not have keyCode and which value set
// so we take char and set those values to not break the flow // so we take char and set those values to not break the flow
// if originalEvent.data is null that means a non char key is pressed like delete, space // if originalEvent.data is null that means a non char key is pressed like delete, space
if(e.originalEvent && e.originalEvent.data) { if(e.originalEvent && e.originalEvent.data) {
var char = e.originalEvent.data; var char = e.originalEvent.data
var keyCode = char.charCodeAt(0); var keyCode = char.charCodeAt(0)
e.keyCode = e.which = keyCode; e.keyCode = e.which = keyCode
} }
// ignore invalid key codes if search is opened (issue #3637) // ignore invalid key codes if search is opened (issue #3637)
if (this.isActive() && e.keyCode === undefined) return if (this.isActive() && e.keyCode === undefined) {
// Check if the trigger still exists in the new text, after a special key was pressed, otherwise
// close the collection.
var indexOfBuffer = this.oldElementText.indexOf(this.buffer)
var trigger = this.findTrigger(this.buffer)
if (this.buffer && indexOfBuffer !== -1 && trigger) {
foundCurrentBuffer = this.$element.text().substr(indexOfBuffer, this.buffer.length)
if ( this.$element.text().substr(indexOfBuffer, trigger.trigger.length) !== trigger.trigger ) {
this.close(true)
}
// Check on how many characters the trigger needs to be reduced, in the case it's not the same.
else if ( foundCurrentBuffer !== this.buffer ) {
var existingLength = 0
for (var i = 0; i < this.buffer.length; i++) {
if (this.buffer.charAt(i) !== foundCurrentBuffer.charAt(i)) {
existingLength = i
break
}
}
this.buffer = this.buffer.substr(0, existingLength)
this.result(trigger)
}
}
return
}
// skip on shift + arrow_keys // skip on shift + arrow_keys
if (_.contains([16, 37, 38, 39, 40], e.keyCode)) return if (_.contains([16, 37, 38, 39, 40], e.keyCode)) return
@ -186,7 +216,6 @@
} }
var newChar = String.fromCharCode(e.which) var newChar = String.fromCharCode(e.which)
// observe other keys // observe other keys
if (this.hasAvailableTriggers(this.buffer)) { if (this.hasAvailableTriggers(this.buffer)) {
if(this.hasAvailableTriggers(this.buffer + newChar)) { if(this.hasAvailableTriggers(this.buffer + newChar)) {

View file

@ -19,6 +19,27 @@ RSpec.shared_examples 'text modules' do |path:|
end end
end end
it 'does not break after usage of Ctrl/Command+Backspace' do
visit path
within(:active_content) do
find(:richtext).send_keys(':')
find(:richtext).send_keys(':')
find(:richtext).send_keys('bur')
# The click is needed to get the focus back to the field for chrome.
find(:richtext).click
if OS.mac?
find(:richtext).send_keys(%i[command backspace])
else
find(:richtext).send_keys(%i[control backspace])
end
find(:richtext).send_keys('Some other text')
find(:richtext).send_keys(:enter)
expect(find(:richtext)).to have_text 'Some other text'
end
end
it 'does not show when send :enter:' do it 'does not show when send :enter:' do
visit path visit path
within(:active_content) do within(:active_content) do