mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2026-05-12 22:10:25 +00:00
fix(ui/mde): inputs in table/link insertion modals (#11341)
Fixes #11268 Fixes regression of #9614 Calling `initDisabledInputs` wasn't effective for template contents, so inputs in MDEs spawned by repo-legacy.js on comment editing were broken. Now repo-legacy.js also calls it when it spawns a new MDE. Co-authored-by: Gusted <Gusted@noreply.codeberg.org> Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/11341 Reviewed-by: Gusted <gusted@noreply.codeberg.org> Co-authored-by: 0ko <0ko@noreply.codeberg.org> Co-committed-by: 0ko <0ko@noreply.codeberg.org>
This commit is contained in:
parent
6bac9e29e7
commit
a0faae2764
7 changed files with 136 additions and 66 deletions
|
|
@ -1,3 +1,6 @@
|
|||
// Copyright 2024 The Forgejo Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// @watch start
|
||||
// web_src/js/features/comp/**
|
||||
// web_src/js/features/repo-**
|
||||
|
|
@ -108,17 +111,18 @@ test.describe('Button text replaced by JS', () => {
|
|||
await commentField.fill('Blah blah');
|
||||
await expect(statusButton.getByText('Reopen with comment')).toBeVisible();
|
||||
await expect(statusButtonIcon).toBeVisible();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
test('Issue', async ({page}) => {
|
||||
// All actual expect() are happening in the helper
|
||||
expect(await testPage(page, '/user2/repo2/issues/2', 'Close issue')).toBeTruthy();
|
||||
await expect(async () => {
|
||||
await testPage(page, '/user2/repo2/issues/2', 'Close issue');
|
||||
}).toPass();
|
||||
});
|
||||
|
||||
test('PR', async ({page}) => {
|
||||
expect(await testPage(page, '/user2/repo1/pulls/5', 'Close pull request')).toBeTruthy();
|
||||
await expect(async () => {
|
||||
await testPage(page, '/user2/repo1/pulls/5', 'Close pull request');
|
||||
}).toPass();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
// Copyright 2024 The Forgejo Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// @watch start
|
||||
// web_src/js/modules/tab.ts
|
||||
// web_src/css/modules/tab.css
|
||||
|
|
@ -6,7 +9,7 @@
|
|||
// templates/shared/combomarkdowneditor.tmpl
|
||||
// @watch end
|
||||
|
||||
import {expect} from '@playwright/test';
|
||||
import {expect, type Page} from '@playwright/test';
|
||||
import {accessibilityCheck} from './shared/accessibility.ts';
|
||||
import {test} from './utils_e2e.ts';
|
||||
import {screenshot} from './shared/screenshots.ts';
|
||||
|
|
@ -349,53 +352,100 @@ test('Markdown list continuation', async ({page}) => {
|
|||
});
|
||||
|
||||
test('Markdown insert table', async ({page}) => {
|
||||
const response = await page.goto('/user2/repo1/issues/new');
|
||||
async function evaluateTableInsertion(page: Page, selector: string, isEditing: boolean) {
|
||||
const area = page.locator(selector);
|
||||
|
||||
let expectedContent = '| Header | Header |\n|---------|---------|\n| Content | Content |\n| Content | Content |\n| Content | Content |\n';
|
||||
|
||||
if (isEditing) {
|
||||
// Preparations for evaluating comment editing
|
||||
await area.locator('.context-dropdown').click();
|
||||
await area.locator('.context-dropdown .edit-content').click();
|
||||
expectedContent = `good work!${expectedContent}`;
|
||||
}
|
||||
|
||||
const newTableButton = area.locator('button[data-md-action="new-table"]');
|
||||
await newTableButton.click();
|
||||
|
||||
const newTableModal = page.locator('[data-modal-name="new-markdown-table"].active');
|
||||
await expect(newTableModal).toBeVisible();
|
||||
await screenshot(page);
|
||||
|
||||
const rowsInput = newTableModal.locator('input[name="table-rows"]');
|
||||
const columnsInput = newTableModal.locator('input[name="table-columns"]');
|
||||
|
||||
await expect(rowsInput).not.toHaveAttribute('disabled');
|
||||
await expect(columnsInput).not.toHaveAttribute('disabled');
|
||||
|
||||
await rowsInput.fill('3');
|
||||
await columnsInput.fill('2');
|
||||
|
||||
await newTableModal.locator('button[data-selector-name="ok-button"]').click();
|
||||
|
||||
await expect(newTableModal).toBeHidden();
|
||||
|
||||
const textarea = area.locator('textarea[name=content]');
|
||||
await expect(textarea).toHaveValue(expectedContent);
|
||||
await screenshot(page);
|
||||
}
|
||||
|
||||
const response = await page.goto('/user2/repo1/issues/1');
|
||||
expect(response?.status()).toBe(200);
|
||||
|
||||
const newTableButton = page.locator('button[data-md-action="new-table"]');
|
||||
await newTableButton.click();
|
||||
|
||||
const newTableModal = page.locator('div[data-markdown-table-modal-id="0"]');
|
||||
await expect(newTableModal).toBeVisible();
|
||||
await screenshot(page);
|
||||
|
||||
await newTableModal.locator('input[name="table-rows"]').fill('3');
|
||||
await newTableModal.locator('input[name="table-columns"]').fill('2');
|
||||
|
||||
await newTableModal.locator('button[data-selector-name="ok-button"]').click();
|
||||
|
||||
await expect(newTableModal).toBeHidden();
|
||||
|
||||
const textarea = page.locator('textarea[name=content]');
|
||||
await expect(textarea).toHaveValue('| Header | Header |\n|---------|---------|\n| Content | Content |\n| Content | Content |\n| Content | Content |\n');
|
||||
await screenshot(page);
|
||||
await expect(async () => {
|
||||
await evaluateTableInsertion(page, '#comment-form', false);
|
||||
await evaluateTableInsertion(page, '#issuecomment-2', true);
|
||||
}).toPass();
|
||||
});
|
||||
|
||||
test('Markdown insert link', async ({page}) => {
|
||||
const response = await page.goto('/user2/repo1/issues/new');
|
||||
async function evaluateLinkInsertion(page: Page, selector: string, isEditing: boolean) {
|
||||
const url = 'https://example.com';
|
||||
const description = 'Where does this lead?';
|
||||
|
||||
let expectedContent = `[${description}](${url})`;
|
||||
|
||||
const area = page.locator(selector);
|
||||
|
||||
if (isEditing) {
|
||||
// Preparations for evaluating comment editing
|
||||
await area.locator('.context-dropdown').click();
|
||||
await area.locator('.context-dropdown .edit-content').click();
|
||||
expectedContent = `good work!${expectedContent}`;
|
||||
}
|
||||
|
||||
const newLinkButton = area.locator('button[data-md-action="new-link"]');
|
||||
await newLinkButton.click();
|
||||
|
||||
const newLinkModal = page.locator('[data-modal-name="new-markdown-link"].active');
|
||||
await expect(newLinkModal).toBeVisible();
|
||||
await accessibilityCheck({page}, ['[data-modal-name="new-markdown-link"].active'], [], []);
|
||||
await screenshot(page);
|
||||
|
||||
const urlInput = newLinkModal.locator('input[name="link-url"]');
|
||||
const descriptionInput = newLinkModal.locator('input[name="link-description"]');
|
||||
|
||||
await expect(urlInput).not.toHaveAttribute('disabled');
|
||||
await expect(descriptionInput).not.toHaveAttribute('disabled');
|
||||
|
||||
await urlInput.fill(url);
|
||||
await descriptionInput.fill(description);
|
||||
|
||||
await newLinkModal.locator('button[data-selector-name="ok-button"]').click();
|
||||
await expect(newLinkModal).toBeHidden();
|
||||
|
||||
const textarea = area.locator('textarea[name=content]');
|
||||
await expect(textarea).toHaveValue(expectedContent);
|
||||
await screenshot(page);
|
||||
}
|
||||
|
||||
const response = await page.goto('/user2/repo1/issues/1');
|
||||
expect(response?.status()).toBe(200);
|
||||
|
||||
const newLinkButton = page.locator('button[data-md-action="new-link"]');
|
||||
await newLinkButton.click();
|
||||
|
||||
const newLinkModal = page.locator('div[data-markdown-link-modal-id="0"]');
|
||||
await expect(newLinkModal).toBeVisible();
|
||||
await accessibilityCheck({page}, ['[data-modal-name="new-markdown-link"]'], [], []);
|
||||
await screenshot(page);
|
||||
|
||||
const url = 'https://example.com';
|
||||
const description = 'Where does this lead?';
|
||||
|
||||
await newLinkModal.locator('input[name="link-url"]').fill(url);
|
||||
await newLinkModal.locator('input[name="link-description"]').fill(description);
|
||||
|
||||
await newLinkModal.locator('button[data-selector-name="ok-button"]').click();
|
||||
|
||||
await expect(newLinkModal).toBeHidden();
|
||||
|
||||
const textarea = page.locator('textarea[name=content]');
|
||||
await expect(textarea).toHaveValue(`[${description}](${url})`);
|
||||
await screenshot(page);
|
||||
await expect(async () => {
|
||||
await evaluateLinkInsertion(page, '#comment-form', false);
|
||||
await evaluateLinkInsertion(page, '#issuecomment-2', true);
|
||||
}).toPass();
|
||||
});
|
||||
|
||||
test('text expander has higher prio then prefix continuation', async ({page}) => {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,8 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// @watch start
|
||||
|
||||
// web_src/css/org.css
|
||||
// templates/org/member/members.tmpl
|
||||
// @watch end
|
||||
|
||||
import {expect} from '@playwright/test';
|
||||
|
|
|
|||
|
|
@ -41,8 +41,6 @@ test.describe('Switch CSS properties', () => {
|
|||
}
|
||||
|
||||
expect((await item.boundingBox()).height).toBeCloseTo(itemHeight);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const normalMargin = '0px';
|
||||
|
|
@ -60,21 +58,33 @@ test.describe('Switch CSS properties', () => {
|
|||
|
||||
await page.goto('/user2/repo1/pulls');
|
||||
|
||||
expect(await evaluateSwitchItem(page, '#issue-filters .switch > .item:nth-child(1)', true, normalMargin, normalMargin, normalPadding, normalPadding, itemHeight)).toBeTruthy();
|
||||
expect(await evaluateSwitchItem(page, '#issue-filters .switch > .item:nth-child(2)', false, specialLeftMargin, normalMargin, specialPadding, normalPadding, itemHeight)).toBeTruthy();
|
||||
expect(await evaluateSwitchItem(page, '#issue-filters .switch > .item:nth-child(3)', false, normalMargin, normalMargin, normalPadding, normalPadding, itemHeight)).toBeTruthy();
|
||||
await expect(async () => {
|
||||
await Promise.all([
|
||||
evaluateSwitchItem(page, '#issue-filters .switch > .item:nth-child(1)', true, normalMargin, normalMargin, normalPadding, normalPadding, itemHeight),
|
||||
evaluateSwitchItem(page, '#issue-filters .switch > .item:nth-child(2)', false, specialLeftMargin, normalMargin, specialPadding, normalPadding, itemHeight),
|
||||
evaluateSwitchItem(page, '#issue-filters .switch > .item:nth-child(3)', false, normalMargin, normalMargin, normalPadding, normalPadding, itemHeight),
|
||||
]);
|
||||
}).toPass();
|
||||
|
||||
await page.goto('/user2/repo1/pulls?state=closed');
|
||||
|
||||
expect(await evaluateSwitchItem(page, '#issue-filters .switch > .item:nth-child(1)', false, normalMargin, specialLeftMargin, normalPadding, specialPadding, itemHeight)).toBeTruthy();
|
||||
expect(await evaluateSwitchItem(page, '#issue-filters .switch > .item:nth-child(2)', true, normalMargin, normalMargin, normalPadding, normalPadding, itemHeight)).toBeTruthy();
|
||||
expect(await evaluateSwitchItem(page, '#issue-filters .switch > .item:nth-child(3)', false, specialLeftMargin, normalMargin, specialPadding, normalPadding, itemHeight)).toBeTruthy();
|
||||
await expect(async () => {
|
||||
await Promise.all([
|
||||
evaluateSwitchItem(page, '#issue-filters .switch > .item:nth-child(1)', false, normalMargin, specialLeftMargin, normalPadding, specialPadding, itemHeight),
|
||||
evaluateSwitchItem(page, '#issue-filters .switch > .item:nth-child(2)', true, normalMargin, normalMargin, normalPadding, normalPadding, itemHeight),
|
||||
evaluateSwitchItem(page, '#issue-filters .switch > .item:nth-child(3)', false, specialLeftMargin, normalMargin, specialPadding, normalPadding, itemHeight),
|
||||
]);
|
||||
}).toPass();
|
||||
|
||||
await page.goto('/user2/repo1/pulls?state=all');
|
||||
|
||||
expect(await evaluateSwitchItem(page, '#issue-filters .switch > .item:nth-child(1)', false, normalMargin, normalMargin, normalPadding, normalPadding, itemHeight)).toBeTruthy();
|
||||
expect(await evaluateSwitchItem(page, '#issue-filters .switch > .item:nth-child(2)', false, normalMargin, specialLeftMargin, normalPadding, specialPadding, itemHeight)).toBeTruthy();
|
||||
expect(await evaluateSwitchItem(page, '#issue-filters .switch > .item:nth-child(3)', true, normalMargin, normalMargin, normalPadding, normalPadding, itemHeight)).toBeTruthy();
|
||||
await expect(async () => {
|
||||
await Promise.all([
|
||||
evaluateSwitchItem(page, '#issue-filters .switch > .item:nth-child(1)', false, normalMargin, normalMargin, normalPadding, normalPadding, itemHeight),
|
||||
evaluateSwitchItem(page, '#issue-filters .switch > .item:nth-child(2)', false, normalMargin, specialLeftMargin, normalPadding, specialPadding, itemHeight),
|
||||
evaluateSwitchItem(page, '#issue-filters .switch > .item:nth-child(3)', true, normalMargin, normalMargin, normalPadding, normalPadding, itemHeight),
|
||||
]);
|
||||
}).toPass();
|
||||
});
|
||||
|
||||
// Subtest for areas that can't be reached without JS
|
||||
|
|
@ -91,7 +101,11 @@ test.describe('Switch CSS properties', () => {
|
|||
// Markdown editor has a special rule for a shorter switch
|
||||
const itemHeight = 28;
|
||||
|
||||
expect(await evaluateSwitchItem(page, '.review-box-panel .switch > .item:nth-child(1)', true, normalMargin, normalMargin, normalPadding, normalPadding, itemHeight)).toBeTruthy();
|
||||
expect(await evaluateSwitchItem(page, '.review-box-panel .switch > .item:nth-child(2)', false, specialLeftMargin, normalMargin, specialPadding, normalPadding, itemHeight)).toBeTruthy();
|
||||
await expect(async () => {
|
||||
await Promise.all([
|
||||
evaluateSwitchItem(page, '.review-box-panel .switch > .item:nth-child(1)', true, normalMargin, normalMargin, normalPadding, normalPadding, itemHeight),
|
||||
evaluateSwitchItem(page, '.review-box-panel .switch > .item:nth-child(2)', false, specialLeftMargin, normalMargin, specialPadding, normalPadding, itemHeight),
|
||||
]);
|
||||
}).toPass();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -206,10 +206,10 @@ export function initGlobalCommon() {
|
|||
}
|
||||
|
||||
// Sometimes unrelated inputs are stored in forms for convenience, for example,
|
||||
// modal inputs. To prevent them from breaking the forms they are in they are
|
||||
// disabled by default
|
||||
export function initDisabledInputs() {
|
||||
for (const el of document.querySelectorAll('input.js-enable[disabled]')) {
|
||||
// modal inputs. To prevent them from blocking the forms for noJS clients they
|
||||
// are disabled by default. TypeScript: root is HTMLElement
|
||||
export function initDisabledInputs(root) {
|
||||
for (const el of root.querySelectorAll('input.js-enable[disabled]')) {
|
||||
el.removeAttribute('disabled');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ import {attachRefIssueContextPopup} from './contextpopup.js';
|
|||
import {POST} from '../modules/fetch.js';
|
||||
import {MarkdownQuote} from '@github/quote-selection';
|
||||
import {toAbsoluteUrl} from '../utils.js';
|
||||
import {initDropzone, initGlobalShowModal} from './common-global.js';
|
||||
import {initDropzone, initGlobalShowModal, initDisabledInputs} from './common-global.js';
|
||||
|
||||
export function initRepoCommentForm() {
|
||||
const $commentForm = $('.comment.form');
|
||||
|
|
@ -370,6 +370,7 @@ async function onEditContent(event) {
|
|||
comboMarkdownEditor = getComboMarkdownEditor(editContentZone.querySelector('.combo-markdown-editor'));
|
||||
if (!comboMarkdownEditor) {
|
||||
editContentZone.innerHTML = document.getElementById('issue-comment-editor-template').innerHTML;
|
||||
initDisabledInputs(editContentZone);
|
||||
const dropzone = editContentZone.querySelector('.dropzone');
|
||||
if (!dropzone.dropzone) await initDropzone(dropzone, editContentZone);
|
||||
comboMarkdownEditor = await initComboMarkdownEditor(editContentZone.querySelector('.combo-markdown-editor'));
|
||||
|
|
|
|||
|
|
@ -98,7 +98,7 @@ initDirAuto();
|
|||
onDomReady(() => {
|
||||
initGlobalCommon();
|
||||
|
||||
initDisabledInputs();
|
||||
initDisabledInputs(document);
|
||||
initGlobalTooltips();
|
||||
initGlobalButtonClickOnEnter();
|
||||
initGlobalButtons();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue