Feature/add embed icon to editor toolbar (#17781)
* Finished task 1 and task 3 * Task 2: If the user already has text selected inside syntax * Added comments * Tests added * Nit fixes * Code optimisation * Nit fixes * Fix typo Co-authored-by: Suzanne Aitchison <suzanne@forem.com> * Removed console log statements * Minor fix * Simplified code Co-authored-by: Suzanne Aitchison <suzanne@forem.com>
This commit is contained in:
parent
686be06009
commit
27c6f07686
4 changed files with 169 additions and 0 deletions
3
app/assets/images/lightning.svg
Normal file
3
app/assets/images/lightning.svg
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
||||
<path d="M13 9h8L11 24v-9H4l9-15v9Zm-2 2V7.22L7.532 13H13v4.394L17.263 11H11Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 176 B |
|
|
@ -41,6 +41,7 @@ describe('<MarkdownToolbar />', () => {
|
|||
expect(getByLabelText('Quote')).toBeInTheDocument();
|
||||
expect(getByLabelText('Code')).toBeInTheDocument();
|
||||
expect(getByLabelText('Code block')).toBeInTheDocument();
|
||||
expect(getByLabelText('Embed')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render an overflow menu with secondary formatters and help link', async () => {
|
||||
|
|
|
|||
|
|
@ -1516,6 +1516,151 @@ describe('markdownSyntaxFormatters', () => {
|
|||
expect(newCursorEnd).toEqual(4);
|
||||
});
|
||||
});
|
||||
|
||||
describe('embed', () => {
|
||||
it('inserts embed syntax and place cursor inside embed syntax, when no selection is given', () => {
|
||||
const textAreaValue = 'one two three';
|
||||
const expectedNewTextAreaValue = 'one two {% embed %}three';
|
||||
|
||||
const {
|
||||
newCursorStart,
|
||||
newCursorEnd,
|
||||
editSelectionStart,
|
||||
editSelectionEnd,
|
||||
replaceSelectionWith,
|
||||
} = coreSyntaxFormatters['embed'].getFormatting({
|
||||
value: textAreaValue,
|
||||
selectionStart: 8,
|
||||
selectionEnd: 8,
|
||||
});
|
||||
|
||||
const editedString = getNewTextAreaValueWithEdits({
|
||||
textAreaValue,
|
||||
editSelectionStart,
|
||||
editSelectionEnd,
|
||||
replaceSelectionWith,
|
||||
});
|
||||
|
||||
expect(editedString).toEqual(expectedNewTextAreaValue);
|
||||
expect(editedString.substring(newCursorStart, newCursorEnd)).toEqual(
|
||||
'',
|
||||
);
|
||||
});
|
||||
|
||||
it('inserts embed syntax and highlights text, when text is selected', () => {
|
||||
const textAreaValue = 'one two three';
|
||||
const expectedNewTextAreaValue = 'one {% embed two %} three';
|
||||
|
||||
const {
|
||||
newCursorStart,
|
||||
newCursorEnd,
|
||||
editSelectionStart,
|
||||
editSelectionEnd,
|
||||
replaceSelectionWith,
|
||||
} = coreSyntaxFormatters['embed'].getFormatting({
|
||||
value: textAreaValue,
|
||||
selectionStart: 4,
|
||||
selectionEnd: 7,
|
||||
});
|
||||
|
||||
const editedString = getNewTextAreaValueWithEdits({
|
||||
textAreaValue,
|
||||
editSelectionStart,
|
||||
editSelectionEnd,
|
||||
replaceSelectionWith,
|
||||
});
|
||||
|
||||
expect(editedString).toEqual(expectedNewTextAreaValue);
|
||||
expect(editedString.substring(newCursorStart, newCursorEnd)).toEqual(
|
||||
'two',
|
||||
);
|
||||
});
|
||||
|
||||
it('removes embed syntax, when cursor is inside empty embed syntax', () => {
|
||||
const textAreaValue = 'one {% embed %} two';
|
||||
const expectedNewTextAreaValue = 'one two';
|
||||
const {
|
||||
newCursorStart,
|
||||
newCursorEnd,
|
||||
editSelectionStart,
|
||||
editSelectionEnd,
|
||||
replaceSelectionWith,
|
||||
} = coreSyntaxFormatters['embed'].getFormatting({
|
||||
value: textAreaValue,
|
||||
selectionStart: 13,
|
||||
selectionEnd: 13,
|
||||
});
|
||||
|
||||
const editedString = getNewTextAreaValueWithEdits({
|
||||
textAreaValue,
|
||||
editSelectionStart,
|
||||
editSelectionEnd,
|
||||
replaceSelectionWith,
|
||||
});
|
||||
|
||||
expect(editedString).toEqual(expectedNewTextAreaValue);
|
||||
expect(newCursorStart).toEqual(4);
|
||||
expect(newCursorEnd).toEqual(4);
|
||||
});
|
||||
|
||||
it('removes embed syntax and highlights the selected text, when selected text is inside embed syntax', () => {
|
||||
const textAreaValue = 'one {% embed random-selected-text %} three';
|
||||
const expectedNewTextAreaValue = 'one random-selected-text three';
|
||||
|
||||
const {
|
||||
newCursorStart,
|
||||
newCursorEnd,
|
||||
editSelectionStart,
|
||||
editSelectionEnd,
|
||||
replaceSelectionWith,
|
||||
} = coreSyntaxFormatters['embed'].getFormatting({
|
||||
value: textAreaValue,
|
||||
selectionStart: 13,
|
||||
selectionEnd: 33,
|
||||
});
|
||||
|
||||
const editedString = getNewTextAreaValueWithEdits({
|
||||
textAreaValue,
|
||||
editSelectionStart,
|
||||
editSelectionEnd,
|
||||
replaceSelectionWith,
|
||||
});
|
||||
|
||||
expect(editedString).toEqual(expectedNewTextAreaValue);
|
||||
expect(editedString.substring(newCursorStart, newCursorEnd)).toEqual(
|
||||
'random-selected-text',
|
||||
);
|
||||
});
|
||||
|
||||
it('removes embed syntax and highlights the text, when full embed syntax is selected', () => {
|
||||
const textAreaValue = 'one {% embed https://example.com %} three';
|
||||
const expectedNewTextAreaValue = 'one https://example.com three';
|
||||
|
||||
const {
|
||||
newCursorStart,
|
||||
newCursorEnd,
|
||||
editSelectionStart,
|
||||
editSelectionEnd,
|
||||
replaceSelectionWith,
|
||||
} = coreSyntaxFormatters['embed'].getFormatting({
|
||||
value: textAreaValue,
|
||||
selectionStart: 4,
|
||||
selectionEnd: 35,
|
||||
});
|
||||
|
||||
const editedString = getNewTextAreaValueWithEdits({
|
||||
textAreaValue,
|
||||
editSelectionStart,
|
||||
editSelectionEnd,
|
||||
replaceSelectionWith,
|
||||
});
|
||||
|
||||
expect(editedString).toEqual(expectedNewTextAreaValue);
|
||||
expect(editedString.substring(newCursorStart, newCursorEnd)).toEqual(
|
||||
'https://example.com',
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('multiline formatters', () => {
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import HeadingIcon from '@images/heading.svg';
|
|||
import QuoteIcon from '@images/quote.svg';
|
||||
import CodeIcon from '@images/code.svg';
|
||||
import CodeBlockIcon from '@images/codeblock.svg';
|
||||
import EmbedIcon from '@images/lightning.svg';
|
||||
import UnderlineIcon from '@images/underline.svg';
|
||||
import StrikethroughIcon from '@images/strikethrough.svg';
|
||||
import DividerIcon from '@images/divider.svg';
|
||||
|
|
@ -714,6 +715,25 @@ export const coreSyntaxFormatters = {
|
|||
blockSuffix: '\n```',
|
||||
}),
|
||||
},
|
||||
embed: {
|
||||
icon: () => <Icon src={EmbedIcon} />,
|
||||
label: 'Embed',
|
||||
getKeyboardShortcut: () => {
|
||||
const modifier = getOSKeyboardModifierKeyString();
|
||||
return {
|
||||
command: `${modifier}+shift+k`,
|
||||
tooltipHint: `${modifier.toUpperCase()} + SHIFT + K`,
|
||||
};
|
||||
},
|
||||
getFormatting: ({ selectionStart, selectionEnd, value }) =>
|
||||
undoOrAddFormattingForInlineSyntax({
|
||||
value,
|
||||
selectionStart,
|
||||
selectionEnd,
|
||||
prefix: '{% embed ',
|
||||
suffix: ' %}',
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
||||
export const secondarySyntaxFormatters = {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue