diff --git a/button/filled-button_test.ts b/button/filled-button_test.ts index 2d6d9e7965..f1f8837444 100644 --- a/button/filled-button_test.ts +++ b/button/filled-button_test.ts @@ -31,6 +31,11 @@ describe('', () => { return {harness: new ButtonHarness(element), form}; } + it('button is submit type by default', async () => { + const {harness} = await setupTest(); + expect(harness.element.type).toBe('submit'); + }); + it('button with type submit can submit a form', async () => { const {harness, form} = await setupTest(); harness.element.type = 'submit'; @@ -69,5 +74,23 @@ describe('', () => { expect(form.requestSubmit).not.toHaveBeenCalled(); }); + + it('should set the button as the SubmitEvent submitter', async () => { + const {harness, form} = await setupTest(); + const submitListener = + jasmine.createSpy('submitListener').and.callFake((event: Event) => { + event.preventDefault(); + }); + + form.addEventListener('submit', submitListener); + + await harness.clickWithMouse(); + + expect(submitListener).toHaveBeenCalled(); + const event = submitListener.calls.argsFor(0)[0] as SubmitEvent; + expect(event.submitter) + .withContext('event.submitter') + .toBe(harness.element); + }); }); }); diff --git a/button/internal/button.ts b/button/internal/button.ts index 8a46f05ce5..3ea4156d07 100644 --- a/button/internal/button.ts +++ b/button/internal/button.ts @@ -63,11 +63,15 @@ export abstract class Button extends LitElement { @property({type: Boolean, attribute: 'has-icon'}) hasIcon = false; /** - * Specifies the type of button, used for controlling forms. When type - * is `submit`, the containing form is submitted; when it is `reset` the - * form is reset. + * A string indicating the behavior of the button. + * + * - submit: The button submits the form. This is the default value if the + * attribute is not specified, or if it is dynamically changed to an empty or + * invalid value. + * - reset: The button resets the form. + * - button: The button does nothing. */ - @property() type: ''|'submit'|'reset' = ''; + @property() type: 'button'|'submit'|'reset' = 'submit'; @query('.button') private readonly buttonElement!: HTMLElement|null; @@ -185,25 +189,35 @@ export abstract class Button extends LitElement { } // based on type, trigger form action. const {type, internals: {form}} = this; - if (!form) { - return; - } - const isSubmit = type === 'submit', isReset = type === 'reset'; - if (!(isSubmit || isReset)) { + if (!form || type === 'button') { return; } - event.stopPropagation(); + this.isRedispatchingEvent = true; const prevented = !redispatchEvent(this, event); this.isRedispatchingEvent = false; if (prevented) { return; } - if (isSubmit) { - form.requestSubmit(); - } else if (isReset) { + + if (type === 'reset') { form.reset(); + return; } + + // form.requestSubmit(submitter) does not work with form associated custom + // elements. This patches the dispatched submit event to add the correct + // `submitter`. + // See https://github.com/WICG/webcomponents/issues/814 + form.addEventListener('submit', submitEvent => { + Object.defineProperty(submitEvent, 'submitter', { + configurable: true, + enumerable: true, + get: () => this, + }); + }, {capture: true, once: true}); + + form.requestSubmit(); } private handleSlotChange() {