diff --git a/packages/core/src/components/button/button.spec.ts b/packages/core/src/components/button/button.spec.ts index 10f4343fc..1e7fc3d8b 100644 --- a/packages/core/src/components/button/button.spec.ts +++ b/packages/core/src/components/button/button.spec.ts @@ -209,4 +209,57 @@ describe('AtomButton', () => { expect(event.preventDefault).not.toHaveBeenCalled() expect(event.stopPropagation).not.toHaveBeenCalled() }) + + it('should correctly apply polyfill for requestSubmit when not available in HTMLFormElement', async () => { + //@ts-expect-error test purpose to remove requestSubmit from HTMLFormElement, and simulates a browser + window.HTMLFormElement.prototype.requestSubmit = undefined + + jest.isolateModules(() => { + require('../../polyfills/form-request-submit.js') + }) + + const page = await newSpecPage({ + components: [AtomButton], + html: '
Click
', + }) + + await page.waitForChanges() + + const formEl = page.body.querySelector('form')! + + const buttonEl = page.root?.shadowRoot?.querySelector('ion-button') + + jest.spyOn(window.HTMLFormElement.prototype, 'requestSubmit') + + buttonEl?.click() + + expect(formEl.requestSubmit).toHaveBeenCalled() + }) + + it('should not apply polyfill for requestSubmit when it is already available in HTMLFormElement', async () => { + window.HTMLFormElement.prototype.requestSubmit = jest.fn() + const originalRequestSubmit = window.HTMLFormElement.prototype.requestSubmit + + jest.isolateModules(() => { + require('../../polyfills/form-request-submit.js') + }) + + const page = await newSpecPage({ + components: [AtomButton], + html: '
Click
', + }) + + await page.waitForChanges() + + const formEl = page.body.querySelector('form')! + + const buttonEl = page.root?.shadowRoot?.querySelector('ion-button') + + jest.spyOn(window.HTMLFormElement.prototype, 'requestSubmit') + + buttonEl?.click() + + expect(formEl.requestSubmit).toBe(originalRequestSubmit) + expect(formEl.requestSubmit).toHaveBeenCalledTimes(1) + }) }) diff --git a/packages/core/src/global/global.ts b/packages/core/src/global/global.ts index 2fdb20b28..84be2b7c0 100644 --- a/packages/core/src/global/global.ts +++ b/packages/core/src/global/global.ts @@ -1 +1,3 @@ import '@ionic/core' +// Polyfill need to make form.requestSubmit work in Safari < 16. +import '../polyfills/form-request-submit.js' diff --git a/packages/core/src/polyfills/form-request-submit.js b/packages/core/src/polyfills/form-request-submit.js new file mode 100644 index 000000000..89ec1f49d --- /dev/null +++ b/packages/core/src/polyfills/form-request-submit.js @@ -0,0 +1,65 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2019 Javan Makhmali + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +;(function (prototype) { + if (!prototype) return + + if (typeof prototype.requestSubmit === 'function') return + + prototype.requestSubmit = function (submitter) { + if (submitter) { + validateSubmitter(submitter, this) + submitter.click() + } else { + submitter = document.createElement('input') + submitter.type = 'submit' + submitter.hidden = true + this.appendChild(submitter) + submitter.click() + this.removeChild(submitter) + } + } + + function validateSubmitter(submitter, form) { + submitter instanceof HTMLElement || + raise(TypeError, "parameter 1 is not of type 'HTMLElement'") + submitter.type == 'submit' || + raise(TypeError, 'The specified element is not a submit button') + submitter.form == form || + raise( + DOMException, + 'The specified element is not owned by this form element', + 'NotFoundError' + ) + } + + function raise(errorConstructor, message, name) { + throw new errorConstructor( + "Failed to execute 'requestSubmit' on 'HTMLFormElement': " + + message + + '.', + name + ) + } +})(typeof window !== 'undefined' && window.HTMLFormElement.prototype)