From 13b2bf180642d2de46924c704fb1a1fb775998a7 Mon Sep 17 00:00:00 2001 From: Brett Walker Date: Wed, 11 Sep 2019 13:58:15 -0500 Subject: [PATCH] Surround selected text with typed char MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit in markdown fields. Supports - ` backtick - ‘ single quotes - “ double quotes - _ underscore (wraps the text in the markdown italic character) - * asterisk (wraps the text in the markdown bold characters, **) - [ brackets - < angle brackets - ( parentheses --- .../javascripts/lib/utils/text_markdown.js | 34 +++++++++++++++++++ .../unreleased/bw-surround-text-wth-char.yml | 5 +++ spec/frontend/lib/utils/text_markdown_spec.js | 34 ++++++++++++++++--- 3 files changed, 68 insertions(+), 5 deletions(-) create mode 100644 changelogs/unreleased/bw-surround-text-wth-char.yml diff --git a/app/assets/javascripts/lib/utils/text_markdown.js b/app/assets/javascripts/lib/utils/text_markdown.js index 4d25ee9e4bd0f7..05e2b6dca7e330 100644 --- a/app/assets/javascripts/lib/utils/text_markdown.js +++ b/app/assets/javascripts/lib/utils/text_markdown.js @@ -303,7 +303,40 @@ function updateText({ textArea, tag, cursorOffset, blockTag, wrap, select, tagCo }); } +/* eslint-disable @gitlab/require-i18n-strings */ +export function keypressNoteText(e) { + if (this.selectionStart === this.selectionEnd) { + return; + } + const keys = { + '*': '**{text}**', // wraps with bold character + _: '_{text}_', // wraps with italic character + '`': '`{text}`', // wraps with inline character + "'": "'{text}'", // single quotes + '"': '"{text}"', // double quotes + '[': '[{text}]', // brackets + '{': '{{text}}', // braces + '(': '({text})', // parentheses + '<': '<{text}>', // angle brackets + }; + const tag = keys[e.key]; + + if (tag) { + updateText({ + tag, + textArea: this, + blockTag: '', + wrap: true, + select: '', + tagContent: '', + }); + e.preventDefault(); + } +} +/* eslint-enable @gitlab/require-i18n-strings */ + export function addMarkdownListeners(form) { + $('.markdown-area').on('keydown', keypressNoteText); return $('.js-md', form) .off('click') .on('click', function() { @@ -340,5 +373,6 @@ export function addEditorMarkdownListeners(editor) { } export function removeMarkdownListeners(form) { + $('.markdown-area').off('keydown'); return $('.js-md', form).off('click'); } diff --git a/changelogs/unreleased/bw-surround-text-wth-char.yml b/changelogs/unreleased/bw-surround-text-wth-char.yml new file mode 100644 index 00000000000000..7395bb9c26b977 --- /dev/null +++ b/changelogs/unreleased/bw-surround-text-wth-char.yml @@ -0,0 +1,5 @@ +--- +title: Surround selected text in markdown fields on certain key presses +merge_request: 25748 +author: +type: added diff --git a/spec/frontend/lib/utils/text_markdown_spec.js b/spec/frontend/lib/utils/text_markdown_spec.js index 2e52958a8289f7..1aaae80dcdf3b4 100644 --- a/spec/frontend/lib/utils/text_markdown_spec.js +++ b/spec/frontend/lib/utils/text_markdown_spec.js @@ -1,4 +1,4 @@ -import { insertMarkdownText } from '~/lib/utils/text_markdown'; +import { insertMarkdownText, keypressNoteText } from '~/lib/utils/text_markdown'; describe('init markdown', () => { let textArea; @@ -115,14 +115,15 @@ describe('init markdown', () => { describe('with selection', () => { const text = 'initial selected value'; const selected = 'selected'; + let selectedIndex; + beforeEach(() => { textArea.value = text; - const selectedIndex = text.indexOf(selected); + selectedIndex = text.indexOf(selected); textArea.setSelectionRange(selectedIndex, selectedIndex + selected.length); }); it('applies the tag to the selected value', () => { - const selectedIndex = text.indexOf(selected); const tag = '*'; insertMarkdownText({ @@ -153,6 +154,29 @@ describe('init markdown', () => { expect(textArea.value).toEqual(text.replace(selected, `[${selected}](url)`)); }); + it.each` + key | expected + ${'['} | ${`[${selected}]`} + ${'*'} | ${`**${selected}**`} + ${"'"} | ${`'${selected}'`} + ${'_'} | ${`_${selected}_`} + ${'`'} | ${`\`${selected}\``} + ${'"'} | ${`"${selected}"`} + ${'{'} | ${`{${selected}}`} + ${'('} | ${`(${selected})`} + ${'<'} | ${`<${selected}>`} + `('generates $expected when $key is pressed', ({ key, expected }) => { + const event = new KeyboardEvent('keydown', { key }); + + textArea.addEventListener('keydown', keypressNoteText); + textArea.dispatchEvent(event); + + expect(textArea.value).toEqual(text.replace(selected, expected)); + + // cursor placement should be after selection + 2 tag lengths + expect(textArea.selectionStart).toBe(selectedIndex + expected.length); + }); + describe('and text to be selected', () => { const tag = '[{text}](url)'; const select = 'url'; @@ -178,7 +202,7 @@ describe('init markdown', () => { it('selects the right text when multiple tags are present', () => { const initialValue = `${tag} ${tag} ${selected}`; textArea.value = initialValue; - const selectedIndex = initialValue.indexOf(selected); + selectedIndex = initialValue.indexOf(selected); textArea.setSelectionRange(selectedIndex, selectedIndex + selected.length); insertMarkdownText({ textArea, @@ -204,7 +228,7 @@ describe('init markdown', () => { const initialValue = `text ${expectedUrl} text`; textArea.value = initialValue; - const selectedIndex = initialValue.indexOf(expectedUrl); + selectedIndex = initialValue.indexOf(expectedUrl); textArea.setSelectionRange(selectedIndex, selectedIndex + expectedUrl.length); insertMarkdownText({ -- GitLab