Template
1
0
Fork 0
mirror of https://codeberg.org/forgejo/forgejo synced 2024-11-22 09:54:24 +01:00

feat: Improve Markdowneditor initialisation

This commit is contained in:
JakobDev 2024-11-11 18:03:45 +01:00
parent 3531710dc6
commit 6007ecd289
No known key found for this signature in database
GPG key ID: 39DEF62C3ED6DC4C
20 changed files with 147 additions and 37 deletions

View file

@ -69,3 +69,15 @@
type: 2
created_unix: 1688973000
updated_unix: 1688973000
-
id: 7
title: another project on user2
owner_id: 2
repo_id: 0
is_closed: false
creator_id: 2
board_type: 0
type: 1
created_unix: 1688973000
updated_unix: 1688973000

View file

@ -745,7 +745,7 @@ webauthn = Two-factor authentication (Security keys)
blocked_users = Blocked users
public_profile = Public profile
biography_placeholder = Tell others a little bit about yourself! (Markdown is supported)
biography_placeholder = Tell others a little bit about yourself!
location_placeholder = Share your approximate location with others
profile_desc = Control how your profile is shown to other users. Your primary email address will be used for notifications, password recovery and web-based Git operations.
password_username_disabled = Non-local users are not allowed to change their username. Please contact your site administrator for more details.

View file

@ -145,6 +145,10 @@ func RenderNewProject(ctx *context.Context) {
ctx.Data["PageIsViewProjects"] = true
ctx.Data["HomeLink"] = ctx.ContextUser.HomeLink()
ctx.Data["CancelLink"] = ctx.ContextUser.HomeLink() + "/-/projects"
ctx.Data["ProjectMarkdownPreviewURL"] = fmt.Sprintf("%s/-/markup", ctx.ContextUser.HomeLink())
ctx.Data["ProjectMarkdownPreviewContext"] = ctx.ContextUser.HomeLink()
ctx.Data["ProjectMarkdownPreviewMode"] = "markdown"
ctx.Data["ProjectMarkdownHideRepoButtons"] = true
shared_user.RenderUserHeader(ctx)
err := shared_user.LoadHeaderCount(ctx)
@ -260,6 +264,10 @@ func RenderEditProject(ctx *context.Context) {
ctx.Data["HomeLink"] = ctx.ContextUser.HomeLink()
ctx.Data["card_type"] = p.CardType
ctx.Data["CancelLink"] = fmt.Sprintf("%s/-/projects/%d", ctx.ContextUser.HomeLink(), p.ID)
ctx.Data["ProjectMarkdownPreviewURL"] = fmt.Sprintf("%s/-/markup", ctx.ContextUser.HomeLink())
ctx.Data["ProjectMarkdownPreviewContext"] = ctx.ContextUser.HomeLink()
ctx.Data["ProjectMarkdownPreviewMode"] = "markdown"
ctx.Data["ProjectMarkdownHideRepoButtons"] = true
ctx.HTML(http.StatusOK, tplProjectsNew)
}
@ -274,6 +282,10 @@ func EditProjectPost(ctx *context.Context) {
ctx.Data["CanWriteProjects"] = canWriteProjects(ctx)
ctx.Data["CardTypes"] = project_model.GetCardConfig()
ctx.Data["CancelLink"] = fmt.Sprintf("%s/-/projects/%d", ctx.ContextUser.HomeLink(), projectID)
ctx.Data["ProjectMarkdownPreviewURL"] = fmt.Sprintf("%s/-/markup", ctx.ContextUser.HomeLink())
ctx.Data["ProjectMarkdownPreviewContext"] = ctx.ContextUser.HomeLink()
ctx.Data["ProjectMarkdownPreviewMode"] = "markdown"
ctx.Data["ProjectMarkdownHideRepoButtons"] = true
shared_user.RenderUserHeader(ctx)

View file

@ -135,6 +135,10 @@ func RenderNewProject(ctx *context.Context) {
ctx.Data["CardTypes"] = project_model.GetCardConfig()
ctx.Data["CanWriteProjects"] = ctx.Repo.Permission.CanWrite(unit.TypeProjects)
ctx.Data["CancelLink"] = ctx.Repo.Repository.Link() + "/projects"
ctx.Data["ProjectMarkdownPreviewURL"] = fmt.Sprintf("%s/markup", ctx.Repo.Repository.Link())
ctx.Data["ProjectMarkdownPreviewContext"] = ctx.Repo.Repository.Link()
ctx.Data["ProjectMarkdownPreviewMode"] = "comment"
ctx.Data["ProjectMarkdownHideRepoButtons"] = false
ctx.HTML(http.StatusOK, tplProjectsNew)
}
@ -238,6 +242,10 @@ func RenderEditProject(ctx *context.Context) {
ctx.Data["card_type"] = p.CardType
ctx.Data["redirect"] = ctx.FormString("redirect")
ctx.Data["CancelLink"] = fmt.Sprintf("%s/projects/%d", ctx.Repo.Repository.Link(), p.ID)
ctx.Data["ProjectMarkdownPreviewURL"] = fmt.Sprintf("%s/markup", ctx.Repo.Repository.Link())
ctx.Data["ProjectMarkdownPreviewContext"] = ctx.Repo.Repository.Link()
ctx.Data["ProjectMarkdownPreviewMode"] = "comment"
ctx.Data["ProjectMarkdownHideRepoButtons"] = false
ctx.HTML(http.StatusOK, tplProjectsNew)
}
@ -252,6 +260,10 @@ func EditProjectPost(ctx *context.Context) {
ctx.Data["CanWriteProjects"] = ctx.Repo.Permission.CanWrite(unit.TypeProjects)
ctx.Data["CardTypes"] = project_model.GetCardConfig()
ctx.Data["CancelLink"] = fmt.Sprintf("%s/projects/%d", ctx.Repo.Repository.Link(), projectID)
ctx.Data["ProjectMarkdownPreviewURL"] = fmt.Sprintf("%s/markup", ctx.Repo.Repository.Link())
ctx.Data["ProjectMarkdownPreviewContext"] = ctx.Repo.Repository.Link()
ctx.Data["ProjectMarkdownPreviewMode"] = "comment"
ctx.Data["ProjectMarkdownHideRepoButtons"] = false
if ctx.HasError() {
ctx.HTML(http.StatusOK, tplProjectsNew)

View file

@ -961,6 +961,7 @@ func registerRoutes(m *web.Route) {
}, reqSignIn)
m.Group("/{username}/-", func() {
m.Post("/markup", web.Bind(structs.MarkupOption{}), misc.Markup)
if setting.Packages.Enabled {
m.Group("/packages", func() {
m.Get("", user.ListPackages)

View file

@ -22,9 +22,17 @@
<label for="email">{{ctx.Locale.Tr "org.settings.email"}}</label>
<input id="email" name="email" type="email" value="{{.Org.Email}}" maxlength="255">
</div>
<div class="field {{if .Err_Description}}error{{end}}">
<div class="field combo-markdown-editor-init {{if .Err_Description}}error{{end}}">
<label for="description">{{ctx.Locale.Tr "org.org_desc"}}</label>
<textarea id="description" name="description" rows="2" maxlength="255">{{.Org.Description}}</textarea>
{{template "shared/combomarkdowneditor" (dict
"MarkdownPreviewUrl" (printf "%s/-/markup" .Org.HomeLink)
"MarkdownPreviewContext" .Org.HomeLink
"MarkdownPreviewMode" "markdown"
"TextareaName" "description"
"TextareaContent" .OrgDescription
"TextareaMaxLength" 255
"HideRepoButtons" true
)}}
</div>
<div class="field {{if .Err_Website}}error{{end}}">
<label for="website">{{ctx.Locale.Tr "org.settings.website"}}</label>

View file

@ -16,9 +16,17 @@
<label>{{ctx.Locale.Tr "repo.projects.title"}}</label>
<input name="title" placeholder="{{ctx.Locale.Tr "repo.projects.title"}}" value="{{.title}}" autofocus required>
</div>
<div class="field">
<div class="field combo-markdown-editor-init">
<label>{{ctx.Locale.Tr "repo.projects.description"}}</label>
<textarea name="content" placeholder="{{ctx.Locale.Tr "repo.projects.description_placeholder"}}">{{.content}}</textarea>
{{template "shared/combomarkdowneditor" (dict
"MarkdownPreviewUrl" $.ProjectMarkdownPreviewURL
"MarkdownPreviewContext" $.ProjectMarkdownPreviewContext
"MarkdownPreviewMode" $.ProjectMarkdownPreviewMode
"TextareaName" "content"
"TextareaContent" .content
"TextareaPlaceholder" (ctx.Locale.Tr "repo.projects.description_placeholder")
"HideRepoButtons" $.ProjectMarkdownHideRepoButtons
)}}
</div>
{{if not .PageIsEditProjects}}

View file

@ -34,7 +34,7 @@
</label>
<input type="date" id="deadline" name="deadline" value="{{.deadline}}" placeholder="{{ctx.Locale.Tr "repo.issues.due_date_form"}}">
</div>
<div class="field">
<div class="field combo-markdown-editor-init">
{{template "shared/combomarkdowneditor" (dict
"MarkdownPreviewUrl" (print .Repository.Link "/markup")
"MarkdownPreviewContext" .RepoLink

View file

@ -48,7 +48,7 @@
<div class="field {{if .Err_Title}}error{{end}}">
<input name="title" aria-label="{{ctx.Locale.Tr "repo.release.title"}}" placeholder="{{ctx.Locale.Tr "repo.release.title"}}" value="{{.title}}" autofocus maxlength="255">
</div>
<div class="field">
<div class="field combo-markdown-editor-init">
{{template "shared/combomarkdowneditor" (dict
"MarkdownPreviewUrl" (print .Repository.Link "/markup")
"MarkdownPreviewContext" .RepoLink

View file

@ -25,6 +25,7 @@
{{template "shared/combomarkdowneditor" (dict
"MarkdownPreviewUrl" (print .Repository.Link "/markup")
"MarkdownPreviewContext" .RepoLink
"MarkdownPreviewMode" "gfm"
"TextareaName" "content"
"TextareaPlaceholder" (ctx.Locale.Tr "repo.wiki.page_content")
"TextareaAriaLabel" (ctx.Locale.Tr "repo.wiki.page_content")

View file

@ -3,20 +3,23 @@ Template Attributes:
* ContainerId: id attribute for the container element
* ContainerClasses: additional classes for the container element
* MarkdownPreviewUrl: preview url for the preview tab
* MarkdownPreviewMode: the preview mode. possible values are markdown, comment, gfm and file. default is comment.
* MarkdownPreviewContext: preview context for the preview tab
* TextareaName: name attribute for the textarea
* TextareaContent: content for the textarea
* TextareaPlaceholder: placeholder attribute for the textarea
* TextareaAriaLabel: aria-label attribute for the textarea
* TextareaMaxLength: maxlength attribute for the textarea
* DropzoneParentContainer: container for file upload (leave it empty if no upload)
* DisableAutosize: whether to disable automatic height resizing
* EasyMDE: whether to display button for switching to legacy editor
* HideRepoButtons: hide buttons that belong to repos (issue references)
*/}}
<div {{if .ContainerId}}id="{{.ContainerId}}"{{end}} class="combo-markdown-editor {{.ContainerClasses}}" data-dropzone-parent-container="{{.DropzoneParentContainer}}">
{{if .MarkdownPreviewUrl}}
<div class="ui top tabular menu">
<a href="#" class="active item" data-tab-for="markdown-writer">{{ctx.Locale.Tr "write"}}</a>
<a href="#" class="item" data-tab-for="markdown-previewer" data-preview-url="{{.MarkdownPreviewUrl}}" data-preview-context="{{.MarkdownPreviewContext}}">{{ctx.Locale.Tr "preview"}}</a>
<a href="#" class="item" data-tab-for="markdown-previewer" data-preview-url="{{.MarkdownPreviewUrl}}" data-preview-context="{{.MarkdownPreviewContext}}" {{if .MarkdownPreviewMode}}data-preview-mode="{{.MarkdownPreviewMode}}"{{end}}>{{ctx.Locale.Tr "preview"}}</a>
</div>
{{end}}
<div class="ui tab active" data-tab-panel="markdown-writer">
@ -41,7 +44,9 @@ Template Attributes:
</div>
<div class="markdown-toolbar-group">
<md-mention class="markdown-toolbar-button" data-tooltip-content="{{ctx.Locale.Tr "editor.buttons.mention.tooltip"}}">{{svg "octicon-mention"}}</md-mention>
<md-ref class="markdown-toolbar-button" data-tooltip-content="{{ctx.Locale.Tr "editor.buttons.ref.tooltip"}}">{{svg "octicon-cross-reference"}}</md-ref>
{{if not .HideRepoButtons}}
<md-ref class="markdown-toolbar-button" data-tooltip-content="{{ctx.Locale.Tr "editor.buttons.ref.tooltip"}}">{{svg "octicon-cross-reference"}}</md-ref>
{{end}}
</div>
<div class="markdown-toolbar-group">
<button class="markdown-toolbar-button markdown-switch-monospace" 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>
@ -51,7 +56,7 @@ Template Attributes:
</div>
</markdown-toolbar>
<text-expander keys=": @" suffix="">
<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>
<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}} {{if .TextareaMaxLength}}maxlength="{{.TextareaMaxLength}}"{{end}}>{{.TextareaContent}}</textarea>
</text-expander>
<script>
if (localStorage?.getItem('markdown-editor-monospace') === 'true') {

View file

@ -55,9 +55,18 @@
<label>{{ctx.Locale.Tr "email"}}</label>
<p id="signed-user-email">{{.SignedUser.Email}}</p>
</div>
<div class="field {{if .Err_Biography}}error{{end}}">
<div class="field combo-markdown-editor-init {{if .Err_Biography}}error{{end}}">
<label for="biography">{{ctx.Locale.Tr "user.user_bio"}}</label>
<textarea id="biography" name="biography" rows="2" placeholder="{{ctx.Locale.Tr "settings.biography_placeholder"}}" maxlength="255">{{.SignedUser.Description}}</textarea>
{{template "shared/combomarkdowneditor" (dict
"MarkdownPreviewUrl" (printf "%s/-/markup" .SignedUser.HomeLink)
"MarkdownPreviewContext" .SignedUser.HomeLink
"MarkdownPreviewMode" "markdown"
"TextareaName" "biography"
"TextareaContent" .SignedUser.Description
"TextareaPlaceholder" (ctx.Locale.Tr "settings.biography_placeholder")
"TextareaMaxLength" 255
"HideRepoButtons" true
)}}
</div>
<div class="field {{if .Err_Website}}error{{end}}">
<label for="website">{{ctx.Locale.Tr "settings.website"}}</label>

View file

@ -206,3 +206,43 @@ test('markdown insert table', async ({browser}, workerInfo) => {
const textarea = page.locator('textarea[name=content]');
await expect(textarea).toHaveValue('| Header | Header |\n|---------|---------|\n| Content | Content |\n| Content | Content |\n| Content | Content |\n');
});
async function testSingleMarkdownEditor(elem) {
await elem.locator('textarea').fill('**Hello**');
await elem.locator('a[data-tab-for="markdown-previewer"]').click();
const preview = await elem.locator('div[data-tab-panel="markdown-previewer"] > p').innerHTML();
expect(preview).toBe('<strong>Hello</strong>');
}
async function testMarkdownEditorPage(page, url) {
const response = await page.goto(url);
expect(response?.status()).toBe(200);
const markdownEditors = await page.locator('.combo-markdown-editor-init').all();
expect(markdownEditors.length).toBeGreaterThanOrEqual(1);
for (const elem of markdownEditors) {
await testSingleMarkdownEditor(elem);
}
}
// eslint-disable-next-line playwright/expect-expect
test('markdown editor init', async ({browser}, workerInfo) => {
const context = await load_logged_in_context(browser, workerInfo, 'user2');
const page = await context.newPage();
await testMarkdownEditorPage(page, '/user/settings');
await testMarkdownEditorPage(page, '/org/org3/settings');
await testMarkdownEditorPage(page, '/user2/-/projects/new');
await testMarkdownEditorPage(page, '/user2/-/projects/7/edit');
await testMarkdownEditorPage(page, '/user2/repo1/projects/new');
await testMarkdownEditorPage(page, '/user2/repo1/projects/1/edit');
await testMarkdownEditorPage(page, '/user2/repo1/releases/new');
await testMarkdownEditorPage(page, '/user2/repo1/releases/edit/v1.0');
await testMarkdownEditorPage(page, '/user2/repo1/milestones/new');
await testMarkdownEditorPage(page, '/user2/repo1/milestones/1/edit');
});

View file

@ -6,16 +6,27 @@ package integration
import (
"net/http"
"testing"
"code.gitea.io/gitea/tests"
)
func TestEasyMDESwitch(t *testing.T) {
defer tests.PrepareTestEnv(t)()
session := loginUser(t, "user2")
testEasyMDESwitch(t, session, "user2/glob/issues/1", false)
testEasyMDESwitch(t, session, "user2/glob/issues/new", false)
testEasyMDESwitch(t, session, "user2/glob/wiki?action=_new", true)
testEasyMDESwitch(t, session, "user2/glob/releases/new", true)
testEasyMDESwitch(t, session, "user2/glob/milestones/new", true)
testEasyMDESwitch(t, session, "user2/repo1/milestones/1/edit", true)
testEasyMDESwitch(t, session, "user/settings", false)
testEasyMDESwitch(t, session, "org/org3/settings", false)
testEasyMDESwitch(t, session, "user2/-/projects/new", false)
testEasyMDESwitch(t, session, "user2/-/projects/7/edit", false)
testEasyMDESwitch(t, session, "user2/repo1/projects/new", false)
testEasyMDESwitch(t, session, "user2/repo1/projects/1/edit", false)
}
func testEasyMDESwitch(t *testing.T, session *TestSession, url string, expected bool) {

View file

@ -11,6 +11,7 @@ import {showTemporaryTooltip} from '../modules/tippy.js';
import {confirmModal} from './comp/ConfirmModal.js';
import {showErrorToast} from '../modules/toast.js';
import {request, POST, GET} from '../modules/fetch.js';
import {initComboMarkdownEditor} from './comp/ComboMarkdownEditor.js';
import '../htmx.js';
const {appUrl, appSubUrl, csrfToken, i18n} = window.config;
@ -461,3 +462,15 @@ export function checkAppUrl() {
showGlobalErrorMessage(`Your ROOT_URL in app.ini is "${appUrl}", it's unlikely matching the site you are visiting.
Mismatched ROOT_URL config causes wrong URL links for web UI/mail content/webhook notification/OAuth2 sign-in.`);
}
export function initComboMarkdownEditorGlobal() {
const markdownEditors = document.getElementsByClassName('combo-markdown-editor-init');
for (const currentEditor of markdownEditors) {
if (currentEditor.querySelector('markdown-toolbar') === null) {
throw new Error(`${currentEditor} doesn't contains a Markdown Editor`);
}
initComboMarkdownEditor(currentEditor);
}
}

View file

@ -170,7 +170,7 @@ class ComboMarkdownEditor {
this.previewUrl = tabPreviewer.getAttribute('data-preview-url');
this.previewContext = tabPreviewer.getAttribute('data-preview-context');
this.previewMode = this.options.previewMode ?? 'comment';
this.previewMode = tabPreviewer.getAttribute('data-preview-mode') ?? 'comment';
this.previewWiki = this.options.previewWiki ?? false;
tabPreviewer.addEventListener('click', async () => {
const formData = new FormData();

View file

@ -1,9 +0,0 @@
import {initComboMarkdownEditor} from './comp/ComboMarkdownEditor.js';
export function initRepoMilestoneEditor() {
const editor = document.querySelector('.page-content.repository.milestone .combo-markdown-editor');
if (!editor) {
return;
}
initComboMarkdownEditor(editor);
}

View file

@ -1,5 +1,4 @@
import {hideElem, showElem} from '../utils/dom.js';
import {initComboMarkdownEditor} from './comp/ComboMarkdownEditor.js';
export function initRepoRelease() {
for (const el of document.querySelectorAll('.remove-rel-attach')) {
@ -17,7 +16,6 @@ export function initRepoReleaseNew() {
if (!document.querySelector('.repository.new.release')) return;
initTagNameEditor();
initRepoReleaseEditor();
initAddExternalLinkButton();
}
@ -46,16 +44,6 @@ function initTagNameEditor() {
});
}
function initRepoReleaseEditor() {
const editor = document.querySelector(
'.repository.new.release .combo-markdown-editor',
);
if (!editor) {
return;
}
initComboMarkdownEditor(editor);
}
let newAttachmentCount = 0;
function initAddExternalLinkButton() {

View file

@ -51,7 +51,6 @@ async function initRepoWikiFormEditor() {
// And another benefit is that we only need to write the style once for both editors.
// TODO: Move height style to CSS after EasyMDE removal.
editorHeights: {minHeight: '300px', height: 'calc(100vh - 600px)'},
previewMode: 'gfm',
previewWiki: true,
easyMDEOptions: {
previewRender: (_content, previewTarget) => previewTarget.innerHTML, // disable builtin preview render

View file

@ -44,6 +44,7 @@ import {
initGlobalFormDirtyLeaveConfirm,
initGlobalLinkActions,
initHeadNavbarContentToggle,
initComboMarkdownEditorGlobal,
} from './features/common-global.js';
import {initRepoTopicBar} from './features/repo-home.js';
import {initAdminEmails} from './features/admin/emails.js';
@ -86,7 +87,6 @@ import {initRepoDiffCommitBranchesAndTags} from './features/repo-diff-commit.js'
import {initDirAuto} from './modules/dirauto.js';
import {initRepositorySearch} from './features/repo-search.js';
import {initColorPickers} from './features/colorpicker.js';
import {initRepoMilestoneEditor} from './features/repo-milestone.js';
// Init Gitea's Fomantic settings
initGiteaFomantic();
@ -103,6 +103,7 @@ onDomReady(() => {
initGlobalEnterQuickSubmit();
initGlobalFormDirtyLeaveConfirm();
initGlobalLinkActions();
initComboMarkdownEditorGlobal();
initCommonOrganization();
initCommonIssueListQuickGoto();
@ -176,7 +177,6 @@ onDomReady(() => {
initRepoContributors();
initRepoCodeFrequency();
initRepoRecentCommits();
initRepoMilestoneEditor();
initCommitStatuses();
initCaptcha();