diff options
author | wxiaoguang <wxiaoguang@gmail.com> | 2025-07-15 06:20:17 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2025-07-14 15:20:17 -0700 |
commit | 692c90ea1dd5b1243b0efb0c9b0349481af19e46 (patch) | |
tree | 3dfda6436bca74641bae1f77f925e967744db4c4 | |
parent | d08459820dc1f3ac98b36bcd0adc1d3e05d62a14 (diff) | |
download | gitea-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.ts | 13 | ||||
-rw-r--r-- | web_src/js/features/common-button.ts | 22 |
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}"`); } } |