+ {{if .MarkdownPreviewUrl}}
+
+ {{end}}
+
+ {{svg "octicon-heading"}}
+ {{svg "octicon-bold"}}
+ {{svg "octicon-italic"}}
+
+
+ {{svg "octicon-quote"}}
+ {{svg "octicon-code"}}
+
+
+
+ {{svg "octicon-list-unordered"}}
+ {{svg "octicon-list-ordered"}}
+ {{svg "octicon-tasklist"}}
+
+
+
+
+
+ {{svg "octicon-mention"}}
+ {{svg "octicon-cross-reference"}}
+
+
+
+ {{if .EasyMDE}}
+
+ {{end}}
+
+
-
-
- {{svg "octicon-heading"}}
- {{svg "octicon-bold"}}
- {{svg "octicon-italic"}}
-
-
- {{svg "octicon-quote"}}
- {{svg "octicon-code"}}
-
-
-
- {{svg "octicon-list-unordered"}}
- {{svg "octicon-list-ordered"}}
- {{svg "octicon-tasklist"}}
-
-
-
-
-
- {{svg "octicon-mention"}}
- {{svg "octicon-cross-reference"}}
-
-
-
- {{if .EasyMDE}}
-
- {{end}}
-
-
diff --git a/tests/e2e/markdown-editor.test.e2e.ts b/tests/e2e/markdown-editor.test.e2e.ts
index 39ced25bc3..c69c9a7f0c 100644
--- a/tests/e2e/markdown-editor.test.e2e.ts
+++ b/tests/e2e/markdown-editor.test.e2e.ts
@@ -39,7 +39,7 @@ test('Markdown image preview behaviour', async ({page}, workerInfo) => {
await save_visual(page);
});
-test('markdown indentation', async ({page}) => {
+test('Markdown indentation', async ({page}) => {
const initText = `* first\n* second\n* third\n* last`;
const response = await page.goto('/user2/repo1/issues/new');
@@ -109,7 +109,7 @@ test('markdown indentation', async ({page}) => {
await expect(textarea).toHaveValue(initText);
});
-test('markdown list continuation', async ({page}) => {
+test('Markdown list continuation', async ({page}) => {
const initText = `* first\n* second`;
const response = await page.goto('/user2/repo1/issues/new');
@@ -202,7 +202,7 @@ test('markdown list continuation', async ({page}) => {
}
});
-test('markdown insert table', async ({page}) => {
+test('Markdown insert table', async ({page}) => {
const response = await page.goto('/user2/repo1/issues/new');
expect(response?.status()).toBe(200);
@@ -225,7 +225,7 @@ test('markdown insert table', async ({page}) => {
await save_visual(page);
});
-test('markdown insert link', async ({page}) => {
+test('Markdown insert link', async ({page}) => {
const response = await page.goto('/user2/repo1/issues/new');
expect(response?.status()).toBe(200);
@@ -277,3 +277,43 @@ test('text expander has higher prio then prefix continuation', async ({page}) =>
await textarea.press('Enter');
await expect(textarea).toHaveValue(`* first\n* 😸\n* @user2 \n* `);
});
+
+test('Combo Markdown: preview mode switch', async ({page}) => {
+ // Load page with editor
+ const response = await page.goto('/user2/repo1/issues/new');
+ expect(response?.status()).toBe(200);
+
+ const toolbarItem = page.locator('md-header');
+ const editorPanel = page.locator('[data-tab-panel="markdown-writer"]');
+ const previewPanel = page.locator('[data-tab-panel="markdown-previewer"]');
+
+ // Verify correct visibility of related UI elements
+ await expect(toolbarItem).toBeVisible();
+ await expect(editorPanel).toBeVisible();
+ await expect(previewPanel).toBeHidden();
+
+ // Fill some content
+ const textarea = page.locator('textarea.markdown-text-editor');
+ await textarea.fill('**Content** :100: _100_');
+
+ // Switch to preview mode
+ await page.locator('a[data-tab-for="markdown-previewer"]').click();
+
+ // Verify that the related UI elements were switched correctly
+ await expect(toolbarItem).toBeHidden();
+ await expect(editorPanel).toBeHidden();
+ await expect(previewPanel).toBeVisible();
+ await save_visual(page);
+
+ // Verify that some content rendered
+ await expect(page.locator('[data-tab-panel="markdown-previewer"] .emoji[data-alias="100"]')).toBeVisible();
+
+ // Switch back to edit mode
+ await page.locator('a[data-tab-for="markdown-writer"]').click();
+
+ // Verify that the related UI elements were switched back correctly
+ await expect(toolbarItem).toBeVisible();
+ await expect(editorPanel).toBeVisible();
+ await expect(previewPanel).toBeHidden();
+ await save_visual(page);
+});
diff --git a/web_src/css/editor/combomarkdowneditor.css b/web_src/css/editor/combomarkdowneditor.css
index f190c7eb1f..a40f0c175c 100644
--- a/web_src/css/editor/combomarkdowneditor.css
+++ b/web_src/css/editor/combomarkdowneditor.css
@@ -11,6 +11,14 @@
flex-wrap: wrap;
}
+markdown-toolbar .switch .item {
+ padding: 0.25em 1em;
+}
+
+.markdown-toolbar-hidden .markdown-toolbar-button {
+ display: none;
+}
+
.combo-markdown-editor .markdown-toolbar-group {
display: flex;
}
diff --git a/web_src/js/features/comp/ComboMarkdownEditor.js b/web_src/js/features/comp/ComboMarkdownEditor.js
index efd068f354..53c6b85728 100644
--- a/web_src/js/features/comp/ComboMarkdownEditor.js
+++ b/web_src/js/features/comp/ComboMarkdownEditor.js
@@ -151,7 +151,7 @@ class ComboMarkdownEditor {
setupTab() {
const $container = $(this.container);
- const tabs = $container[0].querySelectorAll('.tabular.menu > .item');
+ const tabs = $container[0].querySelectorAll('.switch > .item');
// Fomantic Tab requires the "data-tab" to be globally unique.
// So here it uses our defined "data-tab-for" and "data-tab-panel" to generate the "data-tab" attribute for Fomantic.
@@ -159,12 +159,14 @@ class ComboMarkdownEditor {
const tabPreviewer = Array.from(tabs).find((tab) => tab.getAttribute('data-tab-for') === 'markdown-previewer');
tabEditor.setAttribute('data-tab', `markdown-writer-${elementIdCounter}`);
tabPreviewer.setAttribute('data-tab', `markdown-previewer-${elementIdCounter}`);
+ const toolbar = $container[0].querySelector('markdown-toolbar');
const panelEditor = $container[0].querySelector('.ui.tab[data-tab-panel="markdown-writer"]');
const panelPreviewer = $container[0].querySelector('.ui.tab[data-tab-panel="markdown-previewer"]');
panelEditor.setAttribute('data-tab', `markdown-writer-${elementIdCounter}`);
panelPreviewer.setAttribute('data-tab', `markdown-previewer-${elementIdCounter}`);
tabEditor.addEventListener('click', () => {
+ toolbar.classList.remove('markdown-toolbar-hidden');
requestAnimationFrame(() => {
this.focus();
});
@@ -177,6 +179,7 @@ class ComboMarkdownEditor {
this.previewMode = this.options.previewMode ?? 'comment';
this.previewWiki = this.options.previewWiki ?? false;
tabPreviewer.addEventListener('click', async () => {
+ toolbar.classList.add('markdown-toolbar-hidden');
const formData = new FormData();
formData.append('mode', this.previewMode);
formData.append('context', this.previewContext);
diff --git a/web_src/js/features/repo-legacy.js b/web_src/js/features/repo-legacy.js
index c74ba1efbe..2f7db50e64 100644
--- a/web_src/js/features/repo-legacy.js
+++ b/web_src/js/features/repo-legacy.js
@@ -469,7 +469,7 @@ async function onEditContent(event) {
editContentZone.querySelector('button[data-button-name="cancel-edit"]').addEventListener('click', cancelAndReset);
editContentZone.querySelector('button[data-button-name="save-edit"]').addEventListener('click', saveAndRefresh);
} else {
- const tabEditor = editContentZone.querySelector('.combo-markdown-editor').querySelector('.tabular.menu > a[data-tab-for=markdown-writer]');
+ const tabEditor = editContentZone.querySelector('.combo-markdown-editor').querySelector('.switch > a[data-tab-for=markdown-writer]');
tabEditor?.click();
}