From ec30642768b2296abd3f1373477cd4ac3ab23fcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marius=20B=C4=82LTEANU?= Date: Tue, 13 Oct 2026 19:39:54 +0300 Subject: [PATCH] Define the wiki toolbar markup in JavaScript for each text formatter (#43259). --- .../controllers/wiki_toolbar_controller.js | 68 ++++++----- app/javascript/wiki_formatting/common_mark.js | 39 +++++++ app/javascript/wiki_formatting/textile.js | 38 ++++++ config/importmap.rb | 1 + .../wiki_formatting/common_mark/helper.rb | 36 ++---- lib/redmine/wiki_formatting/textile/helper.rb | 35 ++---- lib/redmine/wiki_toolbar.rb | 110 +++++++----------- test/integration/layout_test.rb | 2 +- test/unit/lib/redmine/wiki_toolbar_test.rb | 36 +++--- 9 files changed, 197 insertions(+), 168 deletions(-) create mode 100644 app/javascript/wiki_formatting/common_mark.js create mode 100644 app/javascript/wiki_formatting/textile.js diff --git a/app/javascript/controllers/wiki_toolbar_controller.js b/app/javascript/controllers/wiki_toolbar_controller.js index 0ed0e01e6..8f92b8a78 100644 --- a/app/javascript/controllers/wiki_toolbar_controller.js +++ b/app/javascript/controllers/wiki_toolbar_controller.js @@ -11,11 +11,13 @@ const isMac = /mac|iphone|ipad/i.test(navigator.userAgentData?.platform || navig // // The toolbar is rendered after the textarea and moves itself to the // textarea's position on connect, so that existing views only need to call -// wikitoolbar_for. The text formatting syntax is given by the button -// parameters, so this controller is shared by all text formatters. +// 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. export default class extends Controller { static targets = ['editor', 'preview', 'editTab', 'previewTab', 'toolbar', 'precodeMenu', 'tableMenu'] static values = { + format: String, fieldId: String, previewUrl: String, helpUrl: String @@ -34,6 +36,9 @@ export default class extends Controller { this.field.before(this.element) this.editorTarget.prepend(this.field) } + this.syntaxLoaded ??= import(`wiki_formatting/${this.formatValue}`) + .then((module) => { this.syntax = module.default }) + .catch((error) => console.error(`Wiki toolbar syntax not found for ${this.formatValue}`, error)) this.addShortcutsToTitles() this.element.hidden = false } @@ -45,20 +50,19 @@ export default class extends Controller { // Actions - wrap({ params: { prefix, suffix } }) { - this.encloseSelection(String(prefix), String(suffix ?? prefix)) + async apply({ params: { button } }) { + await this.syntaxLoaded + this.syntax?.[button]?.(this) } - wrapLines({ params: { prefix = '', suffix = '', strip, pattern, replacement = '' } }) { - this.encloseLineSelection(String(prefix), String(suffix), (str) => { - if (strip) { - str = str.replace(new RegExp(strip), '') - } - if (pattern) { - str = str.replace(/\r/g, '').replace(new RegExp(`(\\n|^)${pattern}`, 'g'), `$1${replacement}`) - } - return str - }) + async insertPrecode({ params: { lang } }) { + await this.syntaxLoaded + this.syntax?.precode?.(this, String(lang)) + } + + async insertTable({ params: { cols, rows } }) { + await this.syntaxLoaded + this.syntax?.table?.(this, cols, rows) } help() { @@ -66,7 +70,8 @@ export default class extends Controller { } shortcut(event) { - if (event.target !== this.field || !(isMac ? event.metaKey : event.ctrlKey)) return + if (event.target !== this.field || event.shiftKey || event.altKey) return + if (!(isMac ? event.metaKey : event.ctrlKey)) return const button = this.toolbarTarget.querySelector(`[data-wiki-toolbar-shortcut="${event.key.toLowerCase()}"]`) if (button) { @@ -127,7 +132,6 @@ export default class extends Controller { const template = this[`${params.menu}MenuTarget`] this.menu = template.content.firstElementChild.cloneNode(true) this.menu.dataset.name = params.menu - this.menuParams = params this.element.append(this.menu) this.positionMenu(currentTarget) @@ -147,17 +151,6 @@ export default class extends Controller { this.closeMenuOnOutsideClick = null } - insertTable({ params: { cols, rows } }) { - const { headerCell, separatorCell } = this.menuParams - const names = 'ABCDEFGHIJ'.slice(0, cols).split('') - const line = (cells) => `|${cells.join('|')}|\n` - - let table = line(names.map((name) => String(headerCell).replace('%{name}', name))) - if (separatorCell) table += line(Array(cols).fill(separatorCell)) - table += line(Array(cols).fill(' ')).repeat(rows) - this.encloseLineSelection(table, '') - } - highlightTable({ target }) { const button = target.closest('button') if (!button) return @@ -170,6 +163,27 @@ export default class extends Controller { }) } + // Editing helpers used by the text formatter syntaxes + + // Wraps the selection, e.g. wrap('**') or wrap('', '') + wrap(prefix, suffix = prefix) { + this.encloseSelection(prefix, suffix) + } + + // Wraps the selected lines, optionally transforming them + wrapLines(prefix, suffix = '', transform = (text) => text) { + this.encloseLineSelection(prefix, suffix, transform) + } + + // Replaces in the selected lines, e.g. to turn them into a list + replaceLines(pattern, replacement) { + this.wrapLines('', '', (text) => text.replace(/\r/g, '').replace(pattern, replacement)) + } + + columnNames(count) { + return 'ABCDEFGHIJ'.slice(0, count).split('') + } + // Private get previewing() { diff --git a/app/javascript/wiki_formatting/common_mark.js b/app/javascript/wiki_formatting/common_mark.js new file mode 100644 index 000000000..22b23e391 --- /dev/null +++ b/app/javascript/wiki_formatting/common_mark.js @@ -0,0 +1,39 @@ +/** + * Redmine - project management software + * Copyright (C) 2006- Jean-Philippe Lang + * This code is released under the GNU General Public License. + */ + +// CommonMark markup inserted by the wiki toolbar buttons. +// Each function receives the editor (the wiki-toolbar controller). + +const heading = (level) => (editor) => + editor.wrapLines(`${'#'.repeat(level)} `, '', (text) => text.replace(/^#+\s+/, '')) + +export default { + strong: (editor) => editor.wrap('**'), + em: (editor) => editor.wrap('*'), + ins: (editor) => editor.wrap('', ''), + del: (editor) => editor.wrap('~~'), + code: (editor) => editor.wrap('`'), + + h1: heading(1), + h2: heading(2), + h3: heading(3), + + ul: (editor) => editor.replaceLines(/(\n|^)[#-]?\s*/g, '$1* '), + ol: (editor) => editor.replaceLines(/(\n|^)[*-]?\s*/g, '$11. '), + tl: (editor) => editor.replaceLines(/(\n|^)[*-]?\s*/g, '$1* [ ] '), + + bq: (editor) => editor.replaceLines(/(\n|^)( *)([^\n]*)/g, '$1> $2$3'), + unbq: (editor) => editor.replaceLines(/(\n|^) *(> ?)?( *)([^\n]*)/g, '$1$3$4'), + pre: (editor) => editor.wrapLines('```\n', '\n```'), + precode: (editor, lang) => editor.wrapLines(`\`\`\` ${lang}\n`, '\n```\n'), + table: (editor, cols, rows) => { + const header = editor.columnNames(cols).map((name) => `${name} `) + editor.wrapLines(`|${header.join('|')}|\n` + `|${'--|'.repeat(cols)}\n` + `|${' |'.repeat(cols)}\n`.repeat(rows)) + }, + + link: (editor) => editor.wrap('[[', ']]'), + img: (editor) => editor.wrap('![](', ')') +} diff --git a/app/javascript/wiki_formatting/textile.js b/app/javascript/wiki_formatting/textile.js new file mode 100644 index 000000000..9613a32bd --- /dev/null +++ b/app/javascript/wiki_formatting/textile.js @@ -0,0 +1,38 @@ +/** + * Redmine - project management software + * Copyright (C) 2006- Jean-Philippe Lang + * This code is released under the GNU General Public License. + */ + +// Textile markup inserted by the wiki toolbar buttons. +// Each function receives the editor (the wiki-toolbar controller). + +const heading = (level) => (editor) => + editor.wrapLines(`h${level}. `, '', (text) => text.replace(/^h\d+\.\s+/, '')) + +export default { + strong: (editor) => editor.wrap('*'), + em: (editor) => editor.wrap('_'), + ins: (editor) => editor.wrap('+'), + del: (editor) => editor.wrap('-'), + code: (editor) => editor.wrap('@'), + + h1: heading(1), + h2: heading(2), + h3: heading(3), + + ul: (editor) => editor.replaceLines(/(\n|^)[#-]?\s*/g, '$1* '), + ol: (editor) => editor.replaceLines(/(\n|^)[*-]?\s*/g, '$1# '), + + bq: (editor) => editor.replaceLines(/(\n|^)( *)([^\n]*)/g, '$1> $2$3'), + unbq: (editor) => editor.replaceLines(/(\n|^) *(> ?)?( *)([^\n]*)/g, '$1$3$4'), + pre: (editor) => editor.wrapLines('
\n', '\n
'), + precode: (editor, lang) => editor.wrapLines(`
\n`, '\n
\n'), + table: (editor, cols, rows) => { + const header = editor.columnNames(cols).map((name) => `_.${name}`) + editor.wrapLines(`|${header.join('|')}|\n` + `|${' |'.repeat(cols)}\n`.repeat(rows)) + }, + + link: (editor) => editor.wrap('[[', ']]'), + img: (editor) => editor.wrap('!', '!') +} diff --git a/config/importmap.rb b/config/importmap.rb index 3e0dd527d..2d9649af4 100644 --- a/config/importmap.rb +++ b/config/importmap.rb @@ -7,6 +7,7 @@ pin "@hotwired/stimulus", to: "stimulus.min.js" pin "@hotwired/stimulus-loading", to: "stimulus-loading.js" pin "turndown" # @7.2.0 pin_all_from "app/javascript/controllers", under: "controllers" +pin_all_from "app/javascript/wiki_formatting", under: "wiki_formatting", preload: false pin "tablesort", to: "tablesort.min.js" pin "tablesort.number", to: "tablesort.number.min.js" pin "chart.js", preload: false, to: "chart.min.js" # @4.5.1 diff --git a/lib/redmine/wiki_formatting/common_mark/helper.rb b/lib/redmine/wiki_formatting/common_mark/helper.rb index 018d14299..88f1c5793 100644 --- a/lib/redmine/wiki_formatting/common_mark/helper.rb +++ b/lib/redmine/wiki_formatting/common_mark/helper.rb @@ -33,32 +33,18 @@ module Redmine end class WikiToolbar < Redmine::WikiToolbar - def buttons + def format + 'common_mark' + end + + def button_groups [ - {name: 'strong', action: 'wrap', prefix: '**', shortcut: 'b'}, - {name: 'em', action: 'wrap', prefix: '*', shortcut: 'i'}, - {name: 'ins', action: 'wrap', prefix: '', suffix: '', shortcut: 'u'}, - {name: 'del', action: 'wrap', prefix: '~~'}, - {name: 'code', action: 'wrap', prefix: '`'}, - SPACER, - {name: 'h1', action: 'wrapLines', prefix: '# ', strip: '^#+\s+'}, - {name: 'h2', action: 'wrapLines', prefix: '## ', strip: '^#+\s+'}, - {name: 'h3', action: 'wrapLines', prefix: '### ', strip: '^#+\s+'}, - SPACER, - {name: 'ul', action: 'wrapLines', pattern: '[#-]?\s*', replacement: '* '}, - {name: 'ol', action: 'wrapLines', pattern: '[*-]?\s*', replacement: '1. '}, - {name: 'tl', action: 'wrapLines', pattern: '[*-]?\s*', replacement: '* [ ] '}, - SPACER, - {name: 'bq', action: 'wrapLines', pattern: '( *)([^\n]*)', replacement: '> $2$3'}, - {name: 'unbq', action: 'wrapLines', pattern: ' *(> ?)?( *)([^\n]*)', replacement: '$3$4'}, - {name: 'table', menu: :table, header_cell: '%{name} ', separator_cell: '--'}, - {name: 'pre', action: 'wrapLines', prefix: "```\n", suffix: "\n```"}, - {name: 'precode', menu: :precode, prefix: "``` %{lang}\n", suffix: "\n```\n"}, - SPACER, - {name: 'link', action: 'wrap', prefix: '[[', suffix: ']]'}, - {name: 'img', action: 'wrap', prefix: '![](', suffix: ')'}, - SPACER, - {name: 'help', action: 'help'} + %w[strong em ins del code], + %w[h1 h2 h3], + %w[ul ol tl], + %w[bq unbq table pre precode], + %w[link img], + %w[help] ] end end diff --git a/lib/redmine/wiki_formatting/textile/helper.rb b/lib/redmine/wiki_formatting/textile/helper.rb index 57159ba93..77c2f9a0e 100644 --- a/lib/redmine/wiki_formatting/textile/helper.rb +++ b/lib/redmine/wiki_formatting/textile/helper.rb @@ -33,31 +33,18 @@ module Redmine end class WikiToolbar < Redmine::WikiToolbar - def buttons + def format + 'textile' + end + + def button_groups [ - {name: 'strong', action: 'wrap', prefix: '*', shortcut: 'b'}, - {name: 'em', action: 'wrap', prefix: '_', shortcut: 'i'}, - {name: 'ins', action: 'wrap', prefix: '+', shortcut: 'u'}, - {name: 'del', action: 'wrap', prefix: '-'}, - {name: 'code', action: 'wrap', prefix: '@'}, - SPACER, - {name: 'h1', action: 'wrapLines', prefix: 'h1. ', strip: '^h\d+\.\s+'}, - {name: 'h2', action: 'wrapLines', prefix: 'h2. ', strip: '^h\d+\.\s+'}, - {name: 'h3', action: 'wrapLines', prefix: 'h3. ', strip: '^h\d+\.\s+'}, - SPACER, - {name: 'ul', action: 'wrapLines', pattern: '[#-]?\s*', replacement: '* '}, - {name: 'ol', action: 'wrapLines', pattern: '[*-]?\s*', replacement: '# '}, - SPACER, - {name: 'bq', action: 'wrapLines', pattern: '( *)([^\n]*)', replacement: '> $2$3'}, - {name: 'unbq', action: 'wrapLines', pattern: ' *(> ?)?( *)([^\n]*)', replacement: '$3$4'}, - {name: 'table', menu: :table, header_cell: '_.%{name}'}, - {name: 'pre', action: 'wrapLines', prefix: "
\n", suffix: "\n
"}, - {name: 'precode', menu: :precode, prefix: "
\n", suffix: "\n
\n"}, - SPACER, - {name: 'link', action: 'wrap', prefix: '[[', suffix: ']]'}, - {name: 'img', action: 'wrap', prefix: '!', suffix: '!'}, - SPACER, - {name: 'help', action: 'help'} + %w[strong em ins del code], + %w[h1 h2 h3], + %w[ul ol], + %w[bq unbq table pre precode], + %w[link img], + %w[help] ] end end diff --git a/lib/redmine/wiki_toolbar.rb b/lib/redmine/wiki_toolbar.rb index 1fd4c9faf..fef33218a 100644 --- a/lib/redmine/wiki_toolbar.rb +++ b/lib/redmine/wiki_toolbar.rb @@ -20,18 +20,10 @@ module Redmine # Renders the wiki toolbar handled by the wiki-toolbar Stimulus controller. # - # Text formatters subclass it and describe their buttons in #buttons. Each - # button is a Hash with: - # * :name - the button name, used for the "jstb_" CSS class - # * :action - the controller action: wrap, wrapLines or help - # * :menu - :precode or :table, opens a menu instead of running an action - # * :shortcut - optional key used with Ctrl (Cmd on macOS) - # * any other key is passed to the action as a Stimulus action parameter - # - # Use SPACER to separate groups of buttons. + # 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. class WikiToolbar - SPACER = :spacer - LABELS = { 'strong' => :label_wiki_toolbar_strong, 'em' => :label_wiki_toolbar_italic, @@ -54,11 +46,13 @@ module Redmine 'help' => :label_wiki_toolbar_help }.freeze - BUTTON_KEYS = [:name, :action, :menu, :shortcut, :label].freeze - # Button options used to render the menu instead of being action parameters - MENU_KEYS = {precode: [:prefix, :suffix]}.freeze + SHORTCUTS = {'strong' => 'b', 'em' => 'i', 'ins' => 'u'}.freeze + MENUS = %w[table precode].freeze TABLE_MENU_ROWS = 5 TABLE_MENU_COLS = 10 + KEYBOARD_ACTIONS = 'keydown->wiki-toolbar#shortcut ' \ + 'keydown.ctrl+shift+p@window->wiki-toolbar#togglePreview ' \ + 'keydown.meta+shift+p@window->wiki-toolbar#togglePreview' # Helper methods shared by the text formatters that use the toolbar module Helper @@ -81,15 +75,22 @@ module Redmine @view = view end - def buttons - [] + # Name of the text formatter, used to load its syntax in JavaScript + def format + raise NotImplementedError + end + + # Groups of buttons, separated by spacers + def button_groups + raise NotImplementedError end def render(field_id, preview_url) tag.div(class: 'jstBlock', hidden: true, data: { controller: 'wiki-toolbar', - action: keyboard_actions, + action: KEYBOARD_ACTIONS, + wiki_toolbar_format_value: format, wiki_toolbar_field_id_value: field_id, wiki_toolbar_preview_url_value: preview_url, wiki_toolbar_help_url_value: @view.help_wiki_syntax_path @@ -132,66 +133,50 @@ module Redmine end def rendered_buttons - @rendered_buttons ||= @view.safe_join(buttons.map {|button| render_button(button)}) - end - - def render_button(button) - return tag.span(" ", class: 'jstSpacer') if button == SPACER - - name = button[:name] - title = l(button[:label] || LABELS.fetch(name)) - data = button_params(button) - if button[:menu] - data[:action] = 'wiki-toolbar#toggleMenu' - data[:wiki_toolbar_menu_param] = button[:menu] - else - data[:action] = "wiki-toolbar##{button[:action]}" - end - data[:wiki_toolbar_shortcut] = button[:shortcut] if button[:shortcut] + @rendered_buttons ||= @view.safe_join( + button_groups.map {|names| @view.safe_join(names.map {|name| render_button(name)})}, + tag.span("\u00a0", class: 'jstSpacer') + ) + end + + def render_button(name) + title = l(LABELS.fetch(name)) + data = + if MENUS.include?(name) + {action: 'wiki-toolbar#toggleMenu', wiki_toolbar_menu_param: name} + elsif name == 'help' + {action: 'wiki-toolbar#help'} + else + {action: 'wiki-toolbar#apply', wiki_toolbar_button_param: name} + end + 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 - # Converts the button options to Stimulus action parameters - def button_params(button) - button.except(*BUTTON_KEYS, *MENU_KEYS[button[:menu]]).transform_keys {|key| :"wiki_toolbar_#{key}_param"} - end - def menus - @menus ||= begin - templates = buttons.filter_map do |button| - next unless button.is_a?(Hash) && button[:menu] - - tag.template(data: {wiki_toolbar_target: "#{button[:menu]}Menu"}) do - send(:"#{button[:menu]}_menu", button) - end + @menus ||= @view.safe_join( + MENUS.map do |name| + tag.template(data: {wiki_toolbar_target: "#{name}Menu"}) {send(:"#{name}_menu")} end - @view.safe_join(templates) - end + ) end - # Lists the languages for code highlighting. Each item wraps the selected - # lines using the :prefix and :suffix of the button, where %{lang} is - # replaced by the language. - def precode_menu(button) + def precode_menu tag.ul(class: 'jstMenu', role: 'menu', data: {action: 'click->wiki-toolbar#closeMenu keydown.esc->wiki-toolbar#closeMenu'}) do @view.safe_join( code_languages.map do |lang| tag.li(role: 'none') do tag.button(lang, type: 'button', role: 'menuitem', - data: { - action: 'wiki-toolbar#wrapLines', - wiki_toolbar_prefix_param: format(button[:prefix], lang: lang), - wiki_toolbar_suffix_param: format(button[:suffix], lang: lang) - }) + data: {action: 'wiki-toolbar#insertPrecode', wiki_toolbar_lang_param: lang}) end end ) end end - def table_menu(_button) + def table_menu tag.table(class: 'table-generator', role: 'grid', data: { action: 'click->wiki-toolbar#closeMenu keydown.esc->wiki-toolbar#closeMenu ' \ @@ -221,16 +206,5 @@ module Redmine def code_languages User.current.pref.toolbar_language_options.to_s.split(',') end - - def keyboard_actions - @keyboard_actions ||= begin - keys = buttons.filter_map {|button| button[:shortcut] if button.is_a?(Hash)} - actions = keys.flat_map do |key| - %w[ctrl meta].map {|modifier| "keydown.#{modifier}+#{key}->wiki-toolbar#shortcut"} - end - actions += %w[ctrl meta].map {|modifier| "keydown.#{modifier}+shift+p@window->wiki-toolbar#togglePreview"} - actions.join(' ') - end - end end end diff --git a/test/integration/layout_test.rb b/test/integration/layout_test.rb index b333e1249..79b4152f1 100644 --- a/test/integration/layout_test.rb +++ b/test/integration/layout_test.rb @@ -62,7 +62,7 @@ class LayoutTest < Redmine::IntegrationTest assert_include "var wikiImageMimeTypes = #{Redmine::MimeType.by_type('image').to_json};", response.body assert_select 'div.jstBlock[data-wiki-toolbar-field-id-value=issue_description]' do UserPreference::DEFAULT_TOOLBAR_LANGUAGE_OPTIONS.each do |lang| - assert_select 'template[data-wiki-toolbar-target=precodeMenu]', :html => /data-wiki-toolbar-prefix-param="``` #{lang}/ + assert_select 'template[data-wiki-toolbar-target=precodeMenu]', :html => /data-wiki-toolbar-lang-param="#{lang}"/ end end end diff --git a/test/unit/lib/redmine/wiki_toolbar_test.rb b/test/unit/lib/redmine/wiki_toolbar_test.rb index 830c3fb22..7446a05f9 100644 --- a/test/unit/lib/redmine/wiki_toolbar_test.rb +++ b/test/unit/lib/redmine/wiki_toolbar_test.rb @@ -50,18 +50,15 @@ class Redmine::WikiToolbarTest < Redmine::HelperTest with_settings :text_formatting => 'common_mark' do html = wikitoolbar_for('issue_description') + assert_select_in html, 'div.jstBlock[data-wiki-toolbar-format-value=common_mark]' assert_select_in html, 'div.jstElements' do assert_select 'button', 19 assert_select 'span.jstSpacer', 5 - assert_select 'button.jstb_strong[title=?][data-action=?][data-wiki-toolbar-prefix-param=?][data-wiki-toolbar-shortcut=b]', - 'Strong', 'wiki-toolbar#wrap', '**' - assert_select 'button.jstb_ins[data-wiki-toolbar-prefix-param=?][data-wiki-toolbar-suffix-param=?]', '', '' - assert_select 'button.jstb_h1[data-action=?][data-wiki-toolbar-prefix-param=?][data-wiki-toolbar-strip-param=?]', - 'wiki-toolbar#wrapLines', '# ', '^#+\s+' - assert_select 'button.jstb_tl[data-wiki-toolbar-replacement-param=?]', '* [ ] ' - assert_select 'button.jstb_table[data-action=?][data-wiki-toolbar-menu-param=table][data-wiki-toolbar-separator-cell-param=?]', - 'wiki-toolbar#toggleMenu', '--' - assert_select 'button.jstb_precode[data-wiki-toolbar-menu-param=precode]:not([data-wiki-toolbar-prefix-param])' + assert_select 'button.jstb_strong[title=?][data-action=?][data-wiki-toolbar-button-param=strong][data-wiki-toolbar-shortcut=b]', + 'Strong', 'wiki-toolbar#apply' + assert_select 'button.jstb_tl[data-wiki-toolbar-button-param=tl]' + assert_select 'button.jstb_table[data-action=?][data-wiki-toolbar-menu-param=table]', 'wiki-toolbar#toggleMenu' + assert_select 'button.jstb_precode[data-action=?][data-wiki-toolbar-menu-param=precode]', 'wiki-toolbar#toggleMenu' assert_select 'button.jstb_help[data-action=?]', 'wiki-toolbar#help' end end @@ -71,12 +68,10 @@ class Redmine::WikiToolbarTest < Redmine::HelperTest with_settings :text_formatting => 'textile' do html = wikitoolbar_for('issue_description') + assert_select_in html, 'div.jstBlock[data-wiki-toolbar-format-value=textile]' assert_select_in html, 'div.jstElements' do assert_select 'button', 18 assert_select 'button.jstb_tl', 0 - assert_select 'button.jstb_strong[data-wiki-toolbar-prefix-param=?]', '*' - assert_select 'button.jstb_ol[data-wiki-toolbar-replacement-param=?]', '# ' - assert_select 'button.jstb_table[data-wiki-toolbar-header-cell-param=?]:not([data-wiki-toolbar-separator-cell-param])', '_.%{name}' end end end @@ -96,12 +91,10 @@ class Redmine::WikiToolbarTest < Redmine::HelperTest html = wikitoolbar_for('issue_description') actions = Nokogiri::HTML.fragment(html).at_css('div.jstBlock')['data-action'].split - %w[b i u].each do |key| - assert_include "keydown.ctrl+#{key}->wiki-toolbar#shortcut", actions - assert_include "keydown.meta+#{key}->wiki-toolbar#shortcut", actions - end - assert_include 'keydown.ctrl+shift+p@window->wiki-toolbar#togglePreview', actions - assert_include 'keydown.meta+shift+p@window->wiki-toolbar#togglePreview', actions + assert_equal ['keydown->wiki-toolbar#shortcut', + 'keydown.ctrl+shift+p@window->wiki-toolbar#togglePreview', + 'keydown.meta+shift+p@window->wiki-toolbar#togglePreview'], actions + assert_select_in html, 'button[data-wiki-toolbar-shortcut]', 3 end end @@ -113,12 +106,9 @@ class Redmine::WikiToolbarTest < Redmine::HelperTest html = wikitoolbar_for('issue_description') assert_select_in html, 'template[data-wiki-toolbar-target=precodeMenu] ul.jstMenu[role=menu]' do - assert_select 'button[role=menuitem][data-action=?]', 'wiki-toolbar#wrapLines', :count => 2 + assert_select 'button[role=menuitem]', 2 end - item = Nokogiri::HTML.fragment(html).at_css('ul.jstMenu button') - assert_equal 'ruby', item.text - assert_equal %(
\n), item['data-wiki-toolbar-prefix-param']
-      assert_equal "\n
\n", item['data-wiki-toolbar-suffix-param'] + assert_select_in html, 'ul.jstMenu button[data-action=?][data-wiki-toolbar-lang-param=ruby]', 'wiki-toolbar#insertPrecode', :text => 'ruby' end end -- 2.54.0 (Apple Git-157)