From 5a9f7da562446f5e628d29fdc83490113103310e Mon Sep 17 00:00:00 2001 From: Elizabeth Mitchell Date: Tue, 10 Mar 2026 11:19:42 -0700 Subject: [PATCH] feat(labs): change form-submitter to a behavior mixin PiperOrigin-RevId: 881533609 --- button/internal/button.ts | 35 +---- iconbutton/internal/icon-button.ts | 33 +---- internal/controller/form-submitter.ts | 130 ---------------- labs/behaviors/form-submitter.ts | 140 ++++++++++++++++++ .../behaviors}/form-submitter_test.ts | 16 +- 5 files changed, 153 insertions(+), 201 deletions(-) delete mode 100644 internal/controller/form-submitter.ts create mode 100644 labs/behaviors/form-submitter.ts rename {internal/controller => labs/behaviors}/form-submitter_test.ts (91%) diff --git a/button/internal/button.ts b/button/internal/button.ts index 9e8530a394..47c5ed4968 100644 --- a/button/internal/button.ts +++ b/button/internal/button.ts @@ -12,11 +12,6 @@ import {property, query, queryAssignedElements} from 'lit/decorators.js'; import {ARIAMixinStrict} from '../../internal/aria/aria.js'; import {mixinDelegatesAria} from '../../internal/aria/delegate.js'; -import { - FormSubmitter, - setupFormSubmitter, - type FormSubmitterType, -} from '../../internal/controller/form-submitter.js'; import { dispatchActivationClick, isActivationClick, @@ -25,18 +20,17 @@ import { internals, mixinElementInternals, } from '../../labs/behaviors/element-internals.js'; +import {mixinFormSubmitter} from '../../labs/behaviors/form-submitter.js'; // Separate variable needed for closure. -const buttonBaseClass = mixinDelegatesAria(mixinElementInternals(LitElement)); +const buttonBaseClass = mixinDelegatesAria( + mixinFormSubmitter(mixinElementInternals(LitElement)), +); /** * A button component. */ -export abstract class Button extends buttonBaseClass implements FormSubmitter { - static { - setupFormSubmitter(Button); - } - +export abstract class Button extends buttonBaseClass { /** @nocollapse */ static readonly formAssociated = true; @@ -95,25 +89,6 @@ export abstract class Button extends buttonBaseClass implements FormSubmitter { @property({type: Boolean, attribute: 'has-icon', reflect: true}) hasIcon = false; - /** - * The default behavior of the button. May be "button", "reset", or "submit" - * (default). - */ - @property() type: FormSubmitterType = 'submit'; - - /** - * The value added to a form with the button's name when the button submits a - * form. - */ - @property({reflect: true}) value = ''; - - get name() { - return this.getAttribute('name') ?? ''; - } - set name(name: string) { - this.setAttribute('name', name); - } - /** * The associated form element with which this element's value will submit. */ diff --git a/iconbutton/internal/icon-button.ts b/iconbutton/internal/icon-button.ts index 106e08954b..a486982361 100644 --- a/iconbutton/internal/icon-button.ts +++ b/iconbutton/internal/icon-button.ts @@ -14,11 +14,6 @@ import {literal, html as staticHtml} from 'lit/static-html.js'; import {ARIAMixinStrict} from '../../internal/aria/aria.js'; import {mixinDelegatesAria} from '../../internal/aria/delegate.js'; -import { - FormSubmitter, - setupFormSubmitter, - type FormSubmitterType, -} from '../../internal/controller/form-submitter.js'; import {isRtl} from '../../internal/controller/is-rtl.js'; import { afterDispatch, @@ -28,12 +23,13 @@ import { internals, mixinElementInternals, } from '../../labs/behaviors/element-internals.js'; +import {mixinFormSubmitter} from '../../labs/behaviors/form-submitter.js'; type LinkTarget = '_blank' | '_parent' | '_self' | '_top'; // Separate variable needed for closure. const iconButtonBaseClass = mixinDelegatesAria( - mixinElementInternals(LitElement), + mixinFormSubmitter(mixinElementInternals(LitElement)), ); /** @@ -43,11 +39,7 @@ const iconButtonBaseClass = mixinDelegatesAria( * --composed * @fires change {Event} Dispatched when a toggle button toggles --bubbles */ -export class IconButton extends iconButtonBaseClass implements FormSubmitter { - static { - setupFormSubmitter(IconButton); - } - +export class IconButton extends iconButtonBaseClass { /** @nocollapse */ static readonly formAssociated = true; @@ -113,25 +105,6 @@ export class IconButton extends iconButtonBaseClass implements FormSubmitter { */ @property({type: Boolean, reflect: true}) selected = false; - /** - * The default behavior of the button. May be "button", "reset", or "submit" - * (default). - */ - @property() type: FormSubmitterType = 'submit'; - - /** - * The value added to a form with the button's name when the button submits a - * form. - */ - @property({reflect: true}) value = ''; - - get name() { - return this.getAttribute('name') ?? ''; - } - set name(name: string) { - this.setAttribute('name', name); - } - /** * The associated form element with which this element's value will submit. */ diff --git a/internal/controller/form-submitter.ts b/internal/controller/form-submitter.ts deleted file mode 100644 index 39d3d313a3..0000000000 --- a/internal/controller/form-submitter.ts +++ /dev/null @@ -1,130 +0,0 @@ -/** - * @license - * Copyright 2023 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -import {isServer, ReactiveElement} from 'lit'; - -import { - internals, - WithElementInternals, -} from '../../labs/behaviors/element-internals.js'; - -/** - * A string indicating the form submission behavior of the element. - * - * - submit: The element 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 element resets the form. - * - button: The element does nothing. - */ -export type FormSubmitterType = 'button' | 'submit' | 'reset'; - -/** - * An element that can submit or reset a `
`, similar to - * `