Skip to content

Commit

Permalink
SubmitEvent.submitter & FormData(form, submitter)
Browse files Browse the repository at this point in the history
  • Loading branch information
filiphlm committed May 17, 2024
1 parent 509dbd6 commit 2f108db
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 72 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name: Test
on:
push:
branches: [main]
branches: [main, naja-formdata-submitter]
pull_request:
branches: [main]

Expand Down
80 changes: 22 additions & 58 deletions src/core/UIHandler.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {Naja, Options, Payload} from '../Naja';
import {assert, onDomReady, TypedEventListener} from '../utils';
import {onDomReady, TypedEventListener} from '../utils';

export class UIHandler extends EventTarget {
public selector: string = '.ajax';
Expand All @@ -20,41 +20,30 @@ export class UIHandler extends EventTarget {
}

public bindUI(element: Element): void {
const selectors = [
`a${this.selector}`,
`input[type="submit"]${this.selector}`,
`input[type="image"]${this.selector}`,
`button[type="submit"]${this.selector}`,
`button[form]:not([type])${this.selector}`,
`form button:not([type])${this.selector}`,
`form${this.selector} input[type="submit"]`,
`form${this.selector} input[type="image"]`,
`form${this.selector} button[type="submit"]`,
`form${this.selector} button:not([type])`,
].join(', ');

const bindElement = (element: Element) => {
const selector = `a${this.selector}`;

const bindElement = (element: HTMLAnchorElement) => {
element.removeEventListener('click', this.handler);
element.addEventListener('click', this.handler);
};

const elements = element.querySelectorAll(selectors);
elements.forEach((element) => bindElement(element));
const elements = element.querySelectorAll(selector);
elements.forEach((element) => bindElement(element as HTMLAnchorElement));

if (element.matches(selectors)) {
bindElement(element);
if (element.matches(selector)) {
bindElement(element as HTMLAnchorElement);
}

const bindForm = (form: HTMLFormElement) => {
form.removeEventListener('submit', this.handler);
form.addEventListener('submit', this.handler);
};

if (element.matches(`form${this.selector}`)) {
if (element.tagName === 'FORM') {
bindForm(element as HTMLFormElement);
}

const forms = element.querySelectorAll(`form${this.selector}`);
const forms = element.querySelectorAll('form');
forms.forEach((form) => bindForm(form as HTMLFormElement));
}

Expand All @@ -73,50 +62,25 @@ export class UIHandler extends EventTarget {
};

if (event.type === 'submit') {
this.submitForm(element as HTMLFormElement, options, event).catch(ignoreErrors);
const {submitter} = (event as SubmitEvent);
if ((element as HTMLFormElement).matches(this.selector) || submitter?.matches(this.selector)) {
this.submitForm(element as HTMLFormElement, options, event as SubmitEvent).catch(ignoreErrors);
}

} else if (event.type === 'click') {
this.clickElement(element as HTMLElement, options, mouseEvent).catch(ignoreErrors);
this.clickElement(element as HTMLAnchorElement, options, mouseEvent).catch(ignoreErrors);
}
}

public async clickElement(element: HTMLElement, options: Options = {}, event?: MouseEvent): Promise<Payload> {
let method: string = 'GET', url: string = '', data: any;

if (element.tagName === 'A') {
assert(element instanceof HTMLAnchorElement);

method = 'GET';
url = element.href;
data = null;

} else if (element.tagName === 'INPUT' || element.tagName === 'BUTTON') {
assert(element instanceof HTMLInputElement || element instanceof HTMLButtonElement);

const {form} = element;
// eslint-disable-next-line no-nested-ternary,no-extra-parens
method = element.getAttribute('formmethod')?.toUpperCase() ?? form?.getAttribute('method')?.toUpperCase() ?? 'GET';
url = element.getAttribute('formaction') ?? form?.getAttribute('action') ?? window.location.pathname + window.location.search;
data = new FormData(form ?? undefined);

if (element.type === 'submit' && element.name !== '') {
data.append(element.name, element.value || '');

} else if (element.type === 'image') {
const coords = element.getBoundingClientRect();
const prefix = element.name !== '' ? `${element.name}.` : '';
data.append(`${prefix}x`, Math.max(0, Math.floor(event !== undefined ? event.pageX - coords.left : 0)));
data.append(`${prefix}y`, Math.max(0, Math.floor(event !== undefined ? event.pageY - coords.top : 0)));
}
}

return this.processInteraction(element, method, url, data, options, event);
public async clickElement(element: HTMLAnchorElement, options: Options = {}, event?: MouseEvent): Promise<Payload> {
return this.processInteraction(element, 'GET', element.href, null, options, event);
}

public async submitForm(form: HTMLFormElement, options: Options = {}, event?: Event): Promise<Payload> {
const method = form.getAttribute('method')?.toUpperCase() ?? 'GET';
const url = form.getAttribute('action') ?? window.location.pathname + window.location.search;
const data = new FormData(form);
public async submitForm(form: HTMLFormElement, options: Options = {}, event?: SubmitEvent): Promise<Payload> {
const submitter = event?.submitter;
const method = (submitter?.getAttribute('formmethod') || form.getAttribute('method') || 'GET').toUpperCase();
const url = submitter?.getAttribute('formaction') ?? form.getAttribute('action') ?? window.location.pathname + window.location.search;
const data = new FormData(form, submitter);

return this.processInteraction(form, method, url, data, options, event);
}
Expand Down
26 changes: 13 additions & 13 deletions tests/Naja.UIHandler.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,12 +110,8 @@ describe('UIHandler', function () {

this.a.dispatchEvent(createEvent('click'));
this.form.dispatchEvent(createEvent('submit'));
this.input.dispatchEvent(createEvent('click'));
this.image.dispatchEvent(createEvent('click'));
this.submitButton.dispatchEvent(createEvent('click'));
this.externalButton.dispatchEvent(createEvent('click'));

assert.equal(naja.uiHandler.handler.callCount, 6);
assert.equal(naja.uiHandler.handler.callCount, 2);
});

it('binds to elements specified by custom selector', function () {
Expand Down Expand Up @@ -488,8 +484,9 @@ describe('UIHandler', function () {

const preventDefault = sinon.spy();
const evt = {
type: 'click',
currentTarget: this.input,
type: 'submit',
currentTarget: this.form2,
submitter: this.input,
preventDefault,
};
handler.handleUI(evt);
Expand All @@ -511,8 +508,9 @@ describe('UIHandler', function () {

const preventDefault = sinon.spy();
const evt = {
type: 'click',
currentTarget: this.image,
type: 'submit',
currentTarget: this.form3,
submitter: this.image,
preventDefault,
};
handler.handleUI(evt);
Expand All @@ -534,8 +532,9 @@ describe('UIHandler', function () {

const preventDefault = sinon.spy();
const evt = {
type: 'click',
currentTarget: this.submitButton,
type: 'submit',
currentTarget: this.form4,
submitter: this.submitButton,
preventDefault,
};
handler.handleUI(evt);
Expand All @@ -557,8 +556,9 @@ describe('UIHandler', function () {

const preventDefault = sinon.spy();
const evt = {
type: 'click',
currentTarget: this.externalButton,
type: 'submit',
currentTarget: this.form5,
submitter: this.externalButton,
preventDefault,
};
handler.handleUI(evt);
Expand Down

0 comments on commit 2f108db

Please sign in to comment.