Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- A button with `href` now keeps the link role of its anchor. Before, the anchor had `role="button"`, so screen readers announced a link as a button. [#2421](https://github.com/IgniteUI/igniteui-webcomponents/pull/2421)
- A disabled link now renders a disabled native `<button>` with `role="link"` in place of the anchor. So it leaves the tab order, cannot navigate and dispatches no click. Before, the link stayed in the tab order, and `Enter` still followed it. While the link is disabled, the `base` part is the `<button>`. [#2421](https://github.com/IgniteUI/igniteui-webcomponents/pull/2421)
- The form action of a `submit` or `reset` button now runs after the `click` listeners, as for a native button. A listener that cancels the click, or that disables the button, stops the submit or the reset. Before, the button submitted or reset the form before the listeners on the button ran, so they could not stop it.
- Setting `commandfor` on a detached button no longer throws `getElementById is not a function`. A change made while detached now also applies once the button is attached again. [#2592](https://github.com/IgniteUI/igniteui-webcomponents/pull/2592)
- #### Button group, Stepper
- The arrow keys that the orientation does not use are no longer canceled, so the page can scroll with them. Before, a horizontal group or stepper canceled Up and Down, and a vertical group canceled Left and Right.
- #### Calendar
Expand Down
13 changes: 12 additions & 1 deletion src/components/button/button-base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,10 @@ export abstract class IgcButtonBaseComponent extends EventEmitterMixin<IgcButton
this._commandfor = value;
if (value) {
this._resolver.observe();
this._commandForElement = getElementByIdFromRoot(this, value);
// detached element has no root to call `getElementById` on until `connectedCallback`
this._commandForElement = this.isConnected
? getElementByIdFromRoot(this, value)
: null;
} else {
this._commandForElement = null;
this._resolver.unobserve();
Expand Down Expand Up @@ -220,6 +223,14 @@ export abstract class IgcButtonBaseComponent extends EventEmitterMixin<IgcButton
trackLabels(this);
}

public override connectedCallback(): void {
super.connectedCallback();
// resolve `commandfor` in case it was set or changed while disconnected:
if (this._commandfor) {
this._resolveCommandFor();
}
Comment thread
damyanpetev marked this conversation as resolved.
Resolved
}

protected override firstUpdated(): void {
this.updateComplete.then(() => {
if (this._commandfor) {
Expand Down
37 changes: 37 additions & 0 deletions src/components/button/button.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,43 @@ describe('Button tests', () => {

expect(button.commandForElement).to.be.null;
});

it('resolves commandfor set before the button is inserted', async () => {
const container = await fixture<HTMLElement>(html`
<div><div id="detached-target" popover></div></div>
`);
const target = container.querySelector('#detached-target');

// set attributes on element before inserting it as some frameworks do:
button = document.createElement(IgcButtonComponent.tagName);
button.setAttribute('commandfor', 'detached-target');
expect(button.commandForElement).to.be.null;

container.appendChild(button);
await elementUpdated(button);

expect(button.commandForElement).to.equal(target);
});

it('resolves a commandfor changed while the button was detached', async () => {
const container = await fixture<HTMLElement>(html`
<div>
<igc-button commandfor="first-target">Click</igc-button>
<div id="first-target" popover></div>
<div id="second-target" popover></div>
</div>
`);

button = container.querySelector<IgcButtonComponent>('igc-button')!;
const second = container.querySelector('#second-target');

button.remove();
button.commandfor = 'second-target';
container.appendChild(button);
await elementUpdated(button);

expect(button.commandForElement).to.equal(second);
});
});

describe('Popover control', () => {
Expand Down
6 changes: 4 additions & 2 deletions src/components/button/spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
| 9 | 2026-10-29 | Transition only the properties that change, so the button and a slotted icon hide at the same time as a hidden ancestor |
| 10 | 2026-11-02 | A `focus()` call before the first render applies after the render; `focus()` and `blur()` in the API |
| 11 | 2026-11-11 | The form action runs after the click listeners, so a listener can cancel it; the button is the default button for <kbd>Enter</kbd> in a field |
| 12 | 2026-11-12 | A `commandfor` set or changed while the button is detached resolves once it is attached |

## Overview

Expand Down Expand Up @@ -173,7 +174,7 @@ form action to the next task.
to do with it. The built-in commands operate a native popover and a native `dialog`; the components of the library
accept `--show`, `--hide` and `--toggle`. A custom command starts with two dashes.
`commandForElement` resolves the target, accepts an element reference, and picks up a target appended after the
first render.
first render. A `commandfor` set or changed while the button is detached resolves once it is attached.

```html
<igc-button command="--toggle" commandfor="drawer">Toggle</igc-button>
Expand Down Expand Up @@ -303,7 +304,8 @@ button has no `name` and no `mirrored`. Slotted content takes the size of the ic

10. The `command` attribute is reflected onto the native button and follows a change of the property.
11. `commandForElement` resolves from a string id and accepts an element reference, and `commandfor` resolves a
target appended after the first render.
target appended after the first render. A `commandfor` set before the button is attached, or changed while it
is detached, resolves once it is attached.
12. A native popover is shown, hidden and toggled on repeated clicks.
13. A native dialog is opened as modal and closed.

Expand Down
Loading