Skip to content

Commit afffbe2

Browse files
author
0ko
committed
ui: use switch for markdown editor modes (go-gitea#7481)
Replaces https://codeberg.org/forgejo/forgejo/pulls/5478 Closes https://codeberg.org/forgejo/forgejo/issues/244 Use switch for preview mode switching instead of tabs. It is placed in line with the toolbar buttons. Preview: * https://codeberg.org/attachments/38910747-c14c-41d1-9935-c35f3e17033b * https://codeberg.org/attachments/ff8ea47a-f157-424f-8b7f-af1008d5e8b5 Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/7481 Reviewed-by: Gusted <[email protected]> Reviewed-by: Beowulf <[email protected]>
1 parent 8296a23 commit afffbe2

File tree

5 files changed

+94
-42
lines changed

5 files changed

+94
-42
lines changed

templates/shared/combomarkdowneditor.tmpl

Lines changed: 37 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -13,43 +13,44 @@ Template Attributes:
1313
* EasyMDE: whether to display button for switching to legacy editor
1414
*/}}
1515
<div {{if .ContainerId}}id="{{.ContainerId}}"{{end}} class="combo-markdown-editor {{.ContainerClasses}}" data-dropzone-parent-container="{{.DropzoneParentContainer}}">
16-
{{if .MarkdownPreviewUrl}}
17-
<div class="ui top tabular menu">
18-
<a href="#" class="active item" data-tab-for="markdown-writer">{{ctx.Locale.Tr "write"}}</a>
19-
<a href="#" class="item" data-tab-for="markdown-previewer" data-preview-url="{{.MarkdownPreviewUrl}}" data-preview-context="{{.MarkdownPreviewContext}}">{{ctx.Locale.Tr "preview"}}</a>
20-
</div>
21-
{{end}}
16+
17+
<markdown-toolbar>
18+
{{if .MarkdownPreviewUrl}}
19+
<div class="switch">
20+
<a href="#" class="active item" data-tab-for="markdown-writer">{{ctx.Locale.Tr "write"}}</a>
21+
<a href="#" class="item" data-tab-for="markdown-previewer" data-preview-url="{{.MarkdownPreviewUrl}}" data-preview-context="{{.MarkdownPreviewContext}}">{{ctx.Locale.Tr "preview"}}</a>
22+
</div>
23+
{{end}}
24+
<div class="markdown-toolbar-group">
25+
<md-header class="markdown-toolbar-button" data-tooltip-content="{{ctx.Locale.Tr "editor.buttons.heading.tooltip"}}">{{svg "octicon-heading"}}</md-header>
26+
<md-bold class="markdown-toolbar-button" data-tooltip-content="{{ctx.Locale.Tr "editor.buttons.bold.tooltip"}}">{{svg "octicon-bold"}}</md-bold>
27+
<md-italic class="markdown-toolbar-button" data-tooltip-content="{{ctx.Locale.Tr "editor.buttons.italic.tooltip"}}">{{svg "octicon-italic"}}</md-italic>
28+
</div>
29+
<div class="markdown-toolbar-group">
30+
<md-quote class="markdown-toolbar-button" data-tooltip-content="{{ctx.Locale.Tr "editor.buttons.quote.tooltip"}}">{{svg "octicon-quote"}}</md-quote>
31+
<md-code class="markdown-toolbar-button" data-tooltip-content="{{ctx.Locale.Tr "editor.buttons.code.tooltip"}}">{{svg "octicon-code"}}</md-code>
32+
<button class="markdown-toolbar-button show-modal button" data-md-button data-md-action="new-link" data-tooltip-content="{{ctx.Locale.Tr "editor.buttons.link.tooltip"}}">{{svg "octicon-link"}}</button>
33+
</div>
34+
<div class="markdown-toolbar-group">
35+
<md-unordered-list class="markdown-toolbar-button" data-tooltip-content="{{ctx.Locale.Tr "editor.buttons.list.unordered.tooltip"}}">{{svg "octicon-list-unordered"}}</md-unordered-list>
36+
<md-ordered-list class="markdown-toolbar-button" data-tooltip-content="{{ctx.Locale.Tr "editor.buttons.list.ordered.tooltip"}}">{{svg "octicon-list-ordered"}}</md-ordered-list>
37+
<md-task-list class="markdown-toolbar-button" data-tooltip-content="{{ctx.Locale.Tr "editor.buttons.list.task.tooltip"}}">{{svg "octicon-tasklist"}}</md-task-list>
38+
<button type="button" class="markdown-toolbar-button" data-md-button data-md-action="unindent" data-tooltip-content="{{ctx.Locale.Tr "editor.buttons.unindent.tooltip"}}">{{svg "octicon-arrow-left"}}</button>
39+
<button type="button" class="markdown-toolbar-button" data-md-button data-md-action="indent" data-tooltip-content="{{ctx.Locale.Tr "editor.buttons.indent.tooltip"}}">{{svg "octicon-arrow-right"}}</button>
40+
</div>
41+
<div class="markdown-toolbar-group">
42+
<button type="button" class="markdown-toolbar-button show-modal button" data-md-button data-md-action="new-table" data-tooltip-content="{{ctx.Locale.Tr "editor.buttons.new_table.tooltip"}}">{{svg "octicon-table"}}</button>
43+
<md-mention class="markdown-toolbar-button" data-tooltip-content="{{ctx.Locale.Tr "editor.buttons.mention.tooltip"}}">{{svg "octicon-mention"}}</md-mention>
44+
<md-ref class="markdown-toolbar-button" data-tooltip-content="{{ctx.Locale.Tr "editor.buttons.ref.tooltip"}}">{{svg "octicon-cross-reference"}}</md-ref>
45+
</div>
46+
<div class="markdown-toolbar-group">
47+
<button class="markdown-toolbar-button markdown-switch-monospace" data-md-button role="switch" data-enable-text="{{ctx.Locale.Tr "editor.buttons.enable_monospace_font"}}" data-disable-text="{{ctx.Locale.Tr "editor.buttons.disable_monospace_font"}}">{{svg "octicon-typography"}}</button>
48+
{{if .EasyMDE}}
49+
<button class="markdown-toolbar-button markdown-switch-easymde" data-tooltip-content="{{ctx.Locale.Tr "editor.buttons.switch_to_legacy.tooltip"}}">{{svg "octicon-arrow-switch"}}</button>
50+
{{end}}
51+
</div>
52+
</markdown-toolbar>
2253
<div class="ui tab active" data-tab-panel="markdown-writer">
23-
<markdown-toolbar>
24-
<div class="markdown-toolbar-group">
25-
<md-header class="markdown-toolbar-button" data-tooltip-content="{{ctx.Locale.Tr "editor.buttons.heading.tooltip"}}">{{svg "octicon-heading"}}</md-header>
26-
<md-bold class="markdown-toolbar-button" data-tooltip-content="{{ctx.Locale.Tr "editor.buttons.bold.tooltip"}}">{{svg "octicon-bold"}}</md-bold>
27-
<md-italic class="markdown-toolbar-button" data-tooltip-content="{{ctx.Locale.Tr "editor.buttons.italic.tooltip"}}">{{svg "octicon-italic"}}</md-italic>
28-
</div>
29-
<div class="markdown-toolbar-group">
30-
<md-quote class="markdown-toolbar-button" data-tooltip-content="{{ctx.Locale.Tr "editor.buttons.quote.tooltip"}}">{{svg "octicon-quote"}}</md-quote>
31-
<md-code class="markdown-toolbar-button" data-tooltip-content="{{ctx.Locale.Tr "editor.buttons.code.tooltip"}}">{{svg "octicon-code"}}</md-code>
32-
<button class="markdown-toolbar-button show-modal button" data-md-button data-md-action="new-link" data-tooltip-content="{{ctx.Locale.Tr "editor.buttons.link.tooltip"}}">{{svg "octicon-link"}}</button>
33-
</div>
34-
<div class="markdown-toolbar-group">
35-
<md-unordered-list class="markdown-toolbar-button" data-tooltip-content="{{ctx.Locale.Tr "editor.buttons.list.unordered.tooltip"}}">{{svg "octicon-list-unordered"}}</md-unordered-list>
36-
<md-ordered-list class="markdown-toolbar-button" data-tooltip-content="{{ctx.Locale.Tr "editor.buttons.list.ordered.tooltip"}}">{{svg "octicon-list-ordered"}}</md-ordered-list>
37-
<md-task-list class="markdown-toolbar-button" data-tooltip-content="{{ctx.Locale.Tr "editor.buttons.list.task.tooltip"}}">{{svg "octicon-tasklist"}}</md-task-list>
38-
<button type="button" class="markdown-toolbar-button" data-md-button data-md-action="unindent" data-tooltip-content="{{ctx.Locale.Tr "editor.buttons.unindent.tooltip"}}">{{svg "octicon-arrow-left"}}</button>
39-
<button type="button" class="markdown-toolbar-button" data-md-button data-md-action="indent" data-tooltip-content="{{ctx.Locale.Tr "editor.buttons.indent.tooltip"}}">{{svg "octicon-arrow-right"}}</button>
40-
</div>
41-
<div class="markdown-toolbar-group">
42-
<button type="button" class="markdown-toolbar-button show-modal button" data-md-button data-md-action="new-table" data-tooltip-content="{{ctx.Locale.Tr "editor.buttons.new_table.tooltip"}}">{{svg "octicon-table"}}</button>
43-
<md-mention class="markdown-toolbar-button" data-tooltip-content="{{ctx.Locale.Tr "editor.buttons.mention.tooltip"}}">{{svg "octicon-mention"}}</md-mention>
44-
<md-ref class="markdown-toolbar-button" data-tooltip-content="{{ctx.Locale.Tr "editor.buttons.ref.tooltip"}}">{{svg "octicon-cross-reference"}}</md-ref>
45-
</div>
46-
<div class="markdown-toolbar-group">
47-
<button class="markdown-toolbar-button markdown-switch-monospace" data-md-button role="switch" data-enable-text="{{ctx.Locale.Tr "editor.buttons.enable_monospace_font"}}" data-disable-text="{{ctx.Locale.Tr "editor.buttons.disable_monospace_font"}}">{{svg "octicon-typography"}}</button>
48-
{{if .EasyMDE}}
49-
<button class="markdown-toolbar-button markdown-switch-easymde" data-tooltip-content="{{ctx.Locale.Tr "editor.buttons.switch_to_legacy.tooltip"}}">{{svg "octicon-arrow-switch"}}</button>
50-
{{end}}
51-
</div>
52-
</markdown-toolbar>
5354
<text-expander keys=": @" suffix="">
5455
<textarea class="markdown-text-editor js-quick-submit"{{if .TextareaName}} name="{{.TextareaName}}"{{end}}{{if .TextareaPlaceholder}} placeholder="{{.TextareaPlaceholder}}"{{end}}{{if .TextareaAriaLabel}} aria-label="{{.TextareaAriaLabel}}"{{end}}{{if .DisableAutosize}} data-disable-autosize="{{.DisableAutosize}}"{{end}}>{{.TextareaContent}}</textarea>
5556
</text-expander>

tests/e2e/markdown-editor.test.e2e.ts

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ test('Markdown image preview behaviour', async ({page}, workerInfo) => {
3939
await save_visual(page);
4040
});
4141

42-
test('markdown indentation', async ({page}) => {
42+
test('Markdown indentation', async ({page}) => {
4343
const initText = `* first\n* second\n* third\n* last`;
4444

4545
const response = await page.goto('/user2/repo1/issues/new');
@@ -109,7 +109,7 @@ test('markdown indentation', async ({page}) => {
109109
await expect(textarea).toHaveValue(initText);
110110
});
111111

112-
test('markdown list continuation', async ({page}) => {
112+
test('Markdown list continuation', async ({page}) => {
113113
const initText = `* first\n* second`;
114114

115115
const response = await page.goto('/user2/repo1/issues/new');
@@ -202,7 +202,7 @@ test('markdown list continuation', async ({page}) => {
202202
}
203203
});
204204

205-
test('markdown insert table', async ({page}) => {
205+
test('Markdown insert table', async ({page}) => {
206206
const response = await page.goto('/user2/repo1/issues/new');
207207
expect(response?.status()).toBe(200);
208208

@@ -225,7 +225,7 @@ test('markdown insert table', async ({page}) => {
225225
await save_visual(page);
226226
});
227227

228-
test('markdown insert link', async ({page}) => {
228+
test('Markdown insert link', async ({page}) => {
229229
const response = await page.goto('/user2/repo1/issues/new');
230230
expect(response?.status()).toBe(200);
231231

@@ -277,3 +277,43 @@ test('text expander has higher prio then prefix continuation', async ({page}) =>
277277
await textarea.press('Enter');
278278
await expect(textarea).toHaveValue(`* first\n* 😸\n* @user2 \n* `);
279279
});
280+
281+
test('Combo Markdown: preview mode switch', async ({page}) => {
282+
// Load page with editor
283+
const response = await page.goto('/user2/repo1/issues/new');
284+
expect(response?.status()).toBe(200);
285+
286+
const toolbarItem = page.locator('md-header');
287+
const editorPanel = page.locator('[data-tab-panel="markdown-writer"]');
288+
const previewPanel = page.locator('[data-tab-panel="markdown-previewer"]');
289+
290+
// Verify correct visibility of related UI elements
291+
await expect(toolbarItem).toBeVisible();
292+
await expect(editorPanel).toBeVisible();
293+
await expect(previewPanel).toBeHidden();
294+
295+
// Fill some content
296+
const textarea = page.locator('textarea.markdown-text-editor');
297+
await textarea.fill('**Content** :100: _100_');
298+
299+
// Switch to preview mode
300+
await page.locator('a[data-tab-for="markdown-previewer"]').click();
301+
302+
// Verify that the related UI elements were switched correctly
303+
await expect(toolbarItem).toBeHidden();
304+
await expect(editorPanel).toBeHidden();
305+
await expect(previewPanel).toBeVisible();
306+
await save_visual(page);
307+
308+
// Verify that some content rendered
309+
await expect(page.locator('[data-tab-panel="markdown-previewer"] .emoji[data-alias="100"]')).toBeVisible();
310+
311+
// Switch back to edit mode
312+
await page.locator('a[data-tab-for="markdown-writer"]').click();
313+
314+
// Verify that the related UI elements were switched back correctly
315+
await expect(toolbarItem).toBeVisible();
316+
await expect(editorPanel).toBeVisible();
317+
await expect(previewPanel).toBeHidden();
318+
await save_visual(page);
319+
});

web_src/css/editor/combomarkdowneditor.css

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,14 @@
1111
flex-wrap: wrap;
1212
}
1313

14+
markdown-toolbar .switch .item {
15+
padding: 0.25em 1em;
16+
}
17+
18+
.markdown-toolbar-hidden .markdown-toolbar-button {
19+
display: none;
20+
}
21+
1422
.combo-markdown-editor .markdown-toolbar-group {
1523
display: flex;
1624
}

web_src/js/features/comp/ComboMarkdownEditor.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,20 +151,22 @@ class ComboMarkdownEditor {
151151

152152
setupTab() {
153153
const $container = $(this.container);
154-
const tabs = $container[0].querySelectorAll('.tabular.menu > .item');
154+
const tabs = $container[0].querySelectorAll('.switch > .item');
155155

156156
// Fomantic Tab requires the "data-tab" to be globally unique.
157157
// So here it uses our defined "data-tab-for" and "data-tab-panel" to generate the "data-tab" attribute for Fomantic.
158158
const tabEditor = Array.from(tabs).find((tab) => tab.getAttribute('data-tab-for') === 'markdown-writer');
159159
const tabPreviewer = Array.from(tabs).find((tab) => tab.getAttribute('data-tab-for') === 'markdown-previewer');
160160
tabEditor.setAttribute('data-tab', `markdown-writer-${elementIdCounter}`);
161161
tabPreviewer.setAttribute('data-tab', `markdown-previewer-${elementIdCounter}`);
162+
const toolbar = $container[0].querySelector('markdown-toolbar');
162163
const panelEditor = $container[0].querySelector('.ui.tab[data-tab-panel="markdown-writer"]');
163164
const panelPreviewer = $container[0].querySelector('.ui.tab[data-tab-panel="markdown-previewer"]');
164165
panelEditor.setAttribute('data-tab', `markdown-writer-${elementIdCounter}`);
165166
panelPreviewer.setAttribute('data-tab', `markdown-previewer-${elementIdCounter}`);
166167

167168
tabEditor.addEventListener('click', () => {
169+
toolbar.classList.remove('markdown-toolbar-hidden');
168170
requestAnimationFrame(() => {
169171
this.focus();
170172
});
@@ -177,6 +179,7 @@ class ComboMarkdownEditor {
177179
this.previewMode = this.options.previewMode ?? 'comment';
178180
this.previewWiki = this.options.previewWiki ?? false;
179181
tabPreviewer.addEventListener('click', async () => {
182+
toolbar.classList.add('markdown-toolbar-hidden');
180183
const formData = new FormData();
181184
formData.append('mode', this.previewMode);
182185
formData.append('context', this.previewContext);

web_src/js/features/repo-legacy.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -469,7 +469,7 @@ async function onEditContent(event) {
469469
editContentZone.querySelector('button[data-button-name="cancel-edit"]').addEventListener('click', cancelAndReset);
470470
editContentZone.querySelector('button[data-button-name="save-edit"]').addEventListener('click', saveAndRefresh);
471471
} else {
472-
const tabEditor = editContentZone.querySelector('.combo-markdown-editor').querySelector('.tabular.menu > a[data-tab-for=markdown-writer]');
472+
const tabEditor = editContentZone.querySelector('.combo-markdown-editor').querySelector('.switch > a[data-tab-for=markdown-writer]');
473473
tabEditor?.click();
474474
}
475475

0 commit comments

Comments
 (0)