diff --git a/app/javascript/controllers/wiki_toolbar_controller.js b/app/javascript/controllers/wiki_toolbar_controller.js index 8f92b8a..07dad37 100644 --- a/app/javascript/controllers/wiki_toolbar_controller.js +++ b/app/javascript/controllers/wiki_toolbar_controller.js @@ -13,7 +13,8 @@ const isMac = /mac|iphone|ipad/i.test(navigator.userAgentData?.platform || navig // textarea's position on connect, so that existing views only need to call // wikitoolbar_for. The markup inserted by each button is defined by the text // formatter in app/javascript/wiki_formatting/.js, which is loaded on -// connect. +// connect. Buttons added by plugins are handled by listening to the +// wiki-toolbar:button event. export default class extends Controller { static targets = ['editor', 'preview', 'editTab', 'previewTab', 'toolbar', 'precodeMenu', 'tableMenu'] static values = { @@ -52,7 +53,12 @@ export default class extends Controller { async apply({ params: { button } }) { await this.syntaxLoaded - this.syntax?.[button]?.(this) + const action = this.syntax?.[button] + if (action) { + action(this) + } else { + this.dispatch('button', { detail: { button, editor: this } }) + } } async insertPrecode({ params: { lang } }) { diff --git a/lib/redmine/wiki_toolbar.rb b/lib/redmine/wiki_toolbar.rb index fef3321..68a1795 100644 --- a/lib/redmine/wiki_toolbar.rb +++ b/lib/redmine/wiki_toolbar.rb @@ -23,6 +23,15 @@ module Redmine # The toolbar only describes the layout: which buttons are displayed and in # which groups. What each button inserts in the text is defined for each text # formatter in app/javascript/wiki_formatting/.js. + # + # Plugins can add a button by extending #button_groups and #labels (and + # #shortcuts) of a text formatter toolbar. The button is handled in JavaScript + # by listening to the wiki-toolbar:button event, which gives the name of the + # button and the editor: + # + # document.addEventListener('wiki-toolbar:button', ({ detail: { button, editor } }) => { + # if (button === 'mark') editor.wrap('%{background:yellow}', '%') + # }) class WikiToolbar LABELS = { 'strong' => :label_wiki_toolbar_strong, @@ -85,6 +94,16 @@ module Redmine raise NotImplementedError end + # Labels of the buttons + def labels + LABELS + end + + # Keys used with Ctrl (Cmd on macOS) to run the buttons + def shortcuts + SHORTCUTS + end + def render(field_id, preview_url) tag.div(class: 'jstBlock', hidden: true, data: { @@ -140,7 +159,7 @@ module Redmine end def render_button(name) - title = l(LABELS.fetch(name)) + title = l(labels.fetch(name)) data = if MENUS.include?(name) {action: 'wiki-toolbar#toggleMenu', wiki_toolbar_menu_param: name} @@ -149,7 +168,7 @@ module Redmine else {action: 'wiki-toolbar#apply', wiki_toolbar_button_param: name} end - data[:wiki_toolbar_shortcut] = SHORTCUTS[name] if SHORTCUTS[name] + data[:wiki_toolbar_shortcut] = shortcuts[name] if shortcuts[name] tag.button(tag.span(title), type: 'button', class: "jstb_#{name}", title: title, tabindex: 200, data: data) end diff --git a/test/system/wiki_toolbar_test.rb b/test/system/wiki_toolbar_test.rb index ce6dfc5..9ef2040 100644 --- a/test/system/wiki_toolbar_test.rb +++ b/test/system/wiki_toolbar_test.rb @@ -75,6 +75,29 @@ class WikiToolbarTest < ApplicationSystemTestCase end end + def test_button_added_by_a_plugin_should_trigger_an_event + log_user('jsmith', 'jsmith') + visit '/projects/ecookbook/issues/new' + + within 'div.jstBlock' do + assert_selector 'button.jstb_strong' + end + page.execute_script(<<~JS) + document.addEventListener('wiki-toolbar:button', ({ detail: { button, editor } }) => { + if (button === 'mark') editor.wrap('[', ']') + }) + const button = document.querySelector('.jstb_strong').cloneNode(true) + button.className = 'jstb_mark' + button.dataset.wikiToolbarButtonParam = 'mark' + document.querySelector('.jstElements').append(button) + JS + fill_in 'Description', :with => 'text' + select_text('issue_description', 0, 4) + find('button.jstb_mark').click + + assert_equal '[text]', find('#issue_description').value + end + def test_insert_table_using_common_mark with_settings :text_formatting => 'common_mark' do log_user('jsmith', 'jsmith') diff --git a/test/unit/lib/redmine/wiki_toolbar_test.rb b/test/unit/lib/redmine/wiki_toolbar_test.rb index 7446a05..cc825e1 100644 --- a/test/unit/lib/redmine/wiki_toolbar_test.rb +++ b/test/unit/lib/redmine/wiki_toolbar_test.rb @@ -64,6 +64,27 @@ class Redmine::WikiToolbarTest < Redmine::HelperTest end end + def test_button_added_by_a_plugin + toolbar_class = Class.new(Redmine::WikiFormatting::Textile::WikiToolbar) do + def button_groups + super + [%w[mark]] + end + + def labels + super.merge('mark' => :button_add) + end + + def shortcuts + super.merge('mark' => 'm') + end + end + + html = toolbar_class.new(self).render('issue_description', '/preview') + + assert_select_in html, 'button.jstb_mark[title=Add][data-action=?][data-wiki-toolbar-button-param=mark][data-wiki-toolbar-shortcut=m]', + 'wiki-toolbar#apply' + end + def test_textile_buttons with_settings :text_formatting => 'textile' do html = wikitoolbar_for('issue_description')