Commit f0402e3f authored by Brett Walker's avatar Brett Walker

Surround selected text with typed char

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
parent bdc9c8ec
...@@ -303,7 +303,42 @@ function updateText({ textArea, tag, cursorOffset, blockTag, wrap, select, tagCo ...@@ -303,7 +303,42 @@ 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) {
e.preventDefault();
updateText({
tag,
textArea: this,
blockTag: '',
wrap: true,
select: '',
tagContent: '',
});
}
}
/* eslint-enable @gitlab/require-i18n-strings */
export function addMarkdownListeners(form) { export function addMarkdownListeners(form) {
$('.markdown-area').on('keydown', keypressNoteText);
return $('.js-md', form) return $('.js-md', form)
.off('click') .off('click')
.on('click', function() { .on('click', function() {
...@@ -342,5 +377,6 @@ export function addEditorMarkdownListeners(editor) { ...@@ -342,5 +377,6 @@ export function addEditorMarkdownListeners(editor) {
} }
export function removeMarkdownListeners(form) { export function removeMarkdownListeners(form) {
$('.markdown-area').off('keydown');
return $('.js-md', form).off('click'); return $('.js-md', form).off('click');
} }
---
title: Surround selected text in markdown fields on certain key presses
merge_request: 25748
author:
type: added
import { insertMarkdownText } from '~/lib/utils/text_markdown'; import { insertMarkdownText, keypressNoteText } from '~/lib/utils/text_markdown';
describe('init markdown', () => { describe('init markdown', () => {
let textArea; let textArea;
...@@ -115,14 +115,15 @@ describe('init markdown', () => { ...@@ -115,14 +115,15 @@ describe('init markdown', () => {
describe('with selection', () => { describe('with selection', () => {
const text = 'initial selected value'; const text = 'initial selected value';
const selected = 'selected'; const selected = 'selected';
let selectedIndex;
beforeEach(() => { beforeEach(() => {
textArea.value = text; textArea.value = text;
const selectedIndex = text.indexOf(selected); selectedIndex = text.indexOf(selected);
textArea.setSelectionRange(selectedIndex, selectedIndex + selected.length); textArea.setSelectionRange(selectedIndex, selectedIndex + selected.length);
}); });
it('applies the tag to the selected value', () => { it('applies the tag to the selected value', () => {
const selectedIndex = text.indexOf(selected);
const tag = '*'; const tag = '*';
insertMarkdownText({ insertMarkdownText({
...@@ -153,6 +154,29 @@ describe('init markdown', () => { ...@@ -153,6 +154,29 @@ describe('init markdown', () => {
expect(textArea.value).toEqual(text.replace(selected, `[${selected}](url)`)); 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', () => { describe('and text to be selected', () => {
const tag = '[{text}](url)'; const tag = '[{text}](url)';
const select = 'url'; const select = 'url';
...@@ -178,7 +202,7 @@ describe('init markdown', () => { ...@@ -178,7 +202,7 @@ describe('init markdown', () => {
it('selects the right text when multiple tags are present', () => { it('selects the right text when multiple tags are present', () => {
const initialValue = `${tag} ${tag} ${selected}`; const initialValue = `${tag} ${tag} ${selected}`;
textArea.value = initialValue; textArea.value = initialValue;
const selectedIndex = initialValue.indexOf(selected); selectedIndex = initialValue.indexOf(selected);
textArea.setSelectionRange(selectedIndex, selectedIndex + selected.length); textArea.setSelectionRange(selectedIndex, selectedIndex + selected.length);
insertMarkdownText({ insertMarkdownText({
textArea, textArea,
...@@ -204,7 +228,7 @@ describe('init markdown', () => { ...@@ -204,7 +228,7 @@ describe('init markdown', () => {
const initialValue = `text ${expectedUrl} text`; const initialValue = `text ${expectedUrl} text`;
textArea.value = initialValue; textArea.value = initialValue;
const selectedIndex = initialValue.indexOf(expectedUrl); selectedIndex = initialValue.indexOf(expectedUrl);
textArea.setSelectionRange(selectedIndex, selectedIndex + expectedUrl.length); textArea.setSelectionRange(selectedIndex, selectedIndex + expectedUrl.length);
insertMarkdownText({ insertMarkdownText({
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment