aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwxiaoguang <wxiaoguang@gmail.com>2025-07-15 06:20:17 +0800
committerGitHub <noreply@github.com>2025-07-14 15:20:17 -0700
commit692c90ea1dd5b1243b0efb0c9b0349481af19e46 (patch)
tree3dfda6436bca74641bae1f77f925e967744db4c4
parentd08459820dc1f3ac98b36bcd0adc1d3e05d62a14 (diff)
downloadgitea-692c90ea1dd5b1243b0efb0c9b0349481af19e46.tar.gz
gitea-692c90ea1dd5b1243b0efb0c9b0349481af19e46.zip
Fix form property assignment edge case (#35073)
"form" has an edge case: its `<input name=action>` element overwrites the `action` property, we can only set attribute. This PR makes `assignElementProperty` can handle such case, and add more tests
-rw-r--r--web_src/js/features/common-button.test.ts13
-rw-r--r--web_src/js/features/common-button.ts22
2 files changed, 27 insertions, 8 deletions
diff --git a/web_src/js/features/common-button.test.ts b/web_src/js/features/common-button.test.ts
index f41bafbc79..4ae1f74897 100644
--- a/web_src/js/features/common-button.test.ts
+++ b/web_src/js/features/common-button.test.ts
@@ -1,12 +1,23 @@
-import {assignElementProperty} from './common-button.ts';
+import {assignElementProperty, type ElementWithAssignableProperties} from './common-button.ts';
test('assignElementProperty', () => {
const elForm = document.createElement('form');
assignElementProperty(elForm, 'action', '/test-link');
expect(elForm.action).contains('/test-link'); // the DOM always returns absolute URL
+ expect(elForm.getAttribute('action')).eq('/test-link');
assignElementProperty(elForm, 'text-content', 'dummy');
expect(elForm.textContent).toBe('dummy');
+ // mock a form with its property "action" overwritten by an input element
+ const elFormWithAction = new class implements ElementWithAssignableProperties {
+ action = document.createElement('input'); // now "form.action" is not string, but an input element
+ _attrs: Record<string, string> = {};
+ setAttribute(name: string, value: string) { this._attrs[name] = value }
+ getAttribute(name: string): string | null { return this._attrs[name] }
+ }();
+ assignElementProperty(elFormWithAction, 'action', '/bar');
+ expect(elFormWithAction.getAttribute('action')).eq('/bar');
+
const elInput = document.createElement('input');
expect(elInput.readOnly).toBe(false);
assignElementProperty(elInput, 'read-only', 'true');
diff --git a/web_src/js/features/common-button.ts b/web_src/js/features/common-button.ts
index 3b112b116b..0326956222 100644
--- a/web_src/js/features/common-button.ts
+++ b/web_src/js/features/common-button.ts
@@ -109,18 +109,26 @@ function onHidePanelClick(el: HTMLElement, e: MouseEvent) {
throw new Error('no panel to hide'); // should never happen, otherwise there is a bug in code
}
-export function assignElementProperty(el: any, name: string, val: string) {
- name = camelize(name);
- const old = el[name];
+export type ElementWithAssignableProperties = {
+ getAttribute: (name: string) => string | null;
+ setAttribute: (name: string, value: string) => void;
+} & Record<string, any>
+
+export function assignElementProperty(el: ElementWithAssignableProperties, kebabName: string, val: string) {
+ const camelizedName = camelize(kebabName);
+ const old = el[camelizedName];
if (typeof old === 'boolean') {
- el[name] = val === 'true';
+ el[camelizedName] = val === 'true';
} else if (typeof old === 'number') {
- el[name] = parseFloat(val);
+ el[camelizedName] = parseFloat(val);
} else if (typeof old === 'string') {
- el[name] = val;
+ el[camelizedName] = val;
+ } else if (old?.nodeName) {
+ // "form" has an edge case: its "<input name=action>" element overwrites the "action" property, we can only set attribute
+ el.setAttribute(kebabName, val);
} else {
// in the future, we could introduce a better typing system like `data-modal-form.action:string="..."`
- throw new Error(`cannot assign element property ${name} by value ${val}`);
+ throw new Error(`cannot assign element property "${camelizedName}" by value "${val}"`);
}
}