diff --git a/iconbutton/icon-button_test.ts b/iconbutton/icon-button_test.ts index 8054c3d5b4..becc3e4cec 100644 --- a/iconbutton/icon-button_test.ts +++ b/iconbutton/icon-button_test.ts @@ -122,6 +122,24 @@ describe('icon button tests', () => { expect(element.selected).toBeFalse(); }); + it('fires input and change events when clicked', async () => { + const {element, harness} = + await setUpTest('md-standard-icon-button-toggle'); + if (!(element instanceof MdStandardIconButtonToggle)) { + throw new Error( + 'Icon button is not instance of MdStandardIconButtonToggle.'); + } + let changeEvent = false; + let inputEvent = false; + element.addEventListener('input', () => inputEvent = true); + element.addEventListener('change', () => changeEvent = true); + expect(element.selected).toBeFalse(); + await harness.clickWithMouse(); + expect(element.selected).toBeTrue(); + expect(inputEvent).toBeTrue(); + expect(changeEvent).toBeTrue(); + }); + it('setting `selected` updates the aria-pressed attribute on the native button element', async () => { const {element} = await setUpTest('md-standard-icon-button-toggle'); diff --git a/iconbutton/lib/_filled-icon-button.scss b/iconbutton/lib/_filled-icon-button.scss index 700620c61d..672504722c 100644 --- a/iconbutton/lib/_filled-icon-button.scss +++ b/iconbutton/lib/_filled-icon-button.scss @@ -113,7 +113,7 @@ $_custom-property-prefix: 'filled-icon-button'; ); } - .md3-icon-button--on { + .md3-icon-button--selected { &:not(:disabled) { background-color: var(--_selected-container-color); color: var(--_toggle-selected-icon-color); diff --git a/iconbutton/lib/_filled-tonal-icon-button.scss b/iconbutton/lib/_filled-tonal-icon-button.scss index 80a84348d8..b6bd3639c7 100644 --- a/iconbutton/lib/_filled-tonal-icon-button.scss +++ b/iconbutton/lib/_filled-tonal-icon-button.scss @@ -113,7 +113,7 @@ $_custom-property-prefix: 'filled-tonal-icon-button'; ); } - .md3-icon-button--on { + .md3-icon-button--selected { &:not(:disabled) { background-color: var(--_selected-container-color); color: var(--_toggle-selected-icon-color); diff --git a/iconbutton/lib/_outlined-icon-button.scss b/iconbutton/lib/_outlined-icon-button.scss index 65512d9f53..4b3280ae5e 100644 --- a/iconbutton/lib/_outlined-icon-button.scss +++ b/iconbutton/lib/_outlined-icon-button.scss @@ -107,12 +107,12 @@ $_custom-property-prefix: 'outlined-icon-button'; } // Selected toggle buttons have no outline. - .md3-icon-button--outlined.md3-icon-button--on::before { + .md3-icon-button--outlined.md3-icon-button--selected::before { border-width: 0; } // Selected icon button toggle. - .md3-icon-button--on { + .md3-icon-button--selected { &:not(:disabled) { background-color: var(--_selected-container-color); color: var(--_selected-icon-color); @@ -148,7 +148,7 @@ $_custom-property-prefix: 'outlined-icon-button'; @media (forced-colors: active) { // Selected button in HCM has an outline. - .md3-icon-button--on { + .md3-icon-button--selected { &::before { border-color: var(--_unselected-outline-color); border-width: var(--_unselected-outline-width); diff --git a/iconbutton/lib/_shared.scss b/iconbutton/lib/_shared.scss index 69ca27fca7..b300e6922b 100644 --- a/iconbutton/lib/_shared.scss +++ b/iconbutton/lib/_shared.scss @@ -76,20 +76,6 @@ .md3-icon-button__icon { display: inline-flex; - - &.md3-icon-button__icon--on { - display: none; - } - } - - .md3-icon-button--on { - .md3-icon-button__icon { - display: none; - - &.md3-icon-button__icon--on { - display: inline-flex; - } - } } .md3-icon-button__link { diff --git a/iconbutton/lib/_standard-icon-button.scss b/iconbutton/lib/_standard-icon-button.scss index c969dea111..405a337e1d 100644 --- a/iconbutton/lib/_standard-icon-button.scss +++ b/iconbutton/lib/_standard-icon-button.scss @@ -87,7 +87,7 @@ $_custom-property-prefix: 'icon-button'; } } - .md3-icon-button--on { + .md3-icon-button--selected { &:not(:disabled) { color: var(--_selected-icon-color); diff --git a/iconbutton/lib/icon-button-toggle.ts b/iconbutton/lib/icon-button-toggle.ts index 3d746fbcd8..fa249a6a05 100644 --- a/iconbutton/lib/icon-button-toggle.ts +++ b/iconbutton/lib/icon-button-toggle.ts @@ -21,19 +21,22 @@ import {ripple} from '../../ripple/directive.js'; import {IconButton} from './icon-button.js'; /** - * @fires icon-button-toggle-change {CustomEvent<{selected: boolean}>} + * @fires change {Event} + * Dispatched whenever `selected` is changed via user click + * + * @fires input {InputEvent} * Dispatched whenever `selected` is changed via user click */ export class IconButtonToggle extends IconButton { /** - * The `aria-label` of the button when the toggle button is selected or "on". + * The `aria-label` of the button when the toggle button is selected. */ @property({type: String}) ariaLabelSelected!: string; /** - * Sets the toggle button to the "on" state and displays the `onIcon`. If - * false, sets the toggle button to the "off" state and displays the - * `offIcon`. + * Sets the selected state. When false, displays the default icon. When true, + * displays the `selectedIcon`, or the default icon If no `selectedIcon` is + * provided. */ @property({type: Boolean, reflect: true}) selected = false; @@ -56,26 +59,33 @@ export class IconButtonToggle extends IconButton { ${this.renderFocusRing()} ${when(this.showRipple, this.renderRipple)} ${this.renderTouchTarget()} - ${this.renderIcon()} - ${this.renderSelectedIcon()} + ${!this.selected ? this.renderIcon() : nothing} + ${this.selected ? this.renderSelectedIcon() : nothing} `; } protected renderSelectedIcon() { - return html``; + // Use default slot as fallback to not require specifying multiple icons + return html``; } protected override getRenderClasses(): ClassInfo { return { ...super.getRenderClasses(), - 'md3-icon-button--on': this.selected, + 'md3-icon-button--selected': this.selected, }; } protected handleClick() { + if (this.disabled) { + return; + } + this.selected = !this.selected; - const detail = {selected: this.selected}; - this.dispatchEvent(new CustomEvent( - 'icon-button-toggle-change', {detail, bubbles: true, composed: true})); + this.dispatchEvent( + new InputEvent('input', {bubbles: true, composed: true})); + // Bubbles but does not compose to mimic native browser &