Skip to content

Conversation

@javiercn
Copy link
Member

@javiercn javiercn commented Dec 4, 2025

Summary

This PR adds support for two-way binding on the open attribute for <details> and <dialog> elements using the @bind-open syntax.

Fixes #64194

Changes

C# Changes

  • BindAttributes.cs: Added BindElement attributes for the details element to enable @bind-open syntax
  • EventHandlers.cs: Changed ontoggle event handler to use ChangeEventArgs instead of EventArgs to support binding infrastructure
  • WebEventData.cs: Added deserialization case for toggle events to use ChangeEventArgsReader

JavaScript Changes

  • EventTypes.ts: Updated parseToggleEvent to use ToggleEvent.newState instead of reading element.open property. This correctly handles all elements that fire toggle events (<details>, <dialog>, and popover elements).

Tests

  • Added E2E tests for details element binding (initially closed and initially open scenarios)
  • Tests verify both user interaction (clicking summary) and programmatic control (toggle button)

Usage

<details @bind-open="isOpen">
    <summary>Click to expand</summary>
    <p>Content shown when open</p>
</details>

@code {
    private bool isOpen = false;
}

Technical Notes

The toggle event is fired by:

  • <details> element - when expanded/collapsed
  • <dialog> element - when shown/hidden
  • Popover elements (any element with popover attribute) - when shown/hidden

Using ToggleEvent.newState (which is "open" or "closed") ensures correct behavior for all element types, as not all elements have an open DOM property (popovers don't).

This change enables two-way binding on the 'open' attribute for <details> and <dialog> elements using @bind-open syntax.

Changes:
- Added BindElement attributes for details element in BindAttributes.cs
- Changed ontoggle event handler to use ChangeEventArgs instead of EventArgs
- Updated WebEventData.cs to deserialize toggle events as ChangeEventArgs
- Updated EventTypes.ts to use ToggleEvent.newState for reliable state detection
- Added E2E tests for details element binding

Fixes #64194
@javiercn javiercn requested a review from a team as a code owner December 4, 2025 12:29
Copilot AI review requested due to automatic review settings December 4, 2025 12:29
@github-actions github-actions bot added the area-blazor Includes: Blazor, Razor Components label Dec 4, 2025
@javiercn javiercn marked this pull request as draft December 4, 2025 12:30
Copilot finished reviewing on behalf of javiercn December 4, 2025 12:33
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR enables two-way binding on the open attribute for <details> and <dialog> elements using the @bind-open syntax in Blazor. The implementation changes the ontoggle event handler to use ChangeEventArgs instead of EventArgs to support the binding infrastructure, and updates both C# and JavaScript event handling code.

Key changes:

  • Added BindElement attributes for details element to enable @bind-open syntax
  • Changed ontoggle event to use ChangeEventArgs with boolean value indicating open/closed state
  • Updated JavaScript event parsing to use ToggleEvent.newState for correct behavior across all toggle event sources

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/Components/Web/src/Web/BindAttributes.cs Added BindElement attributes for details element to enable @bind-open binding syntax
src/Components/Web/src/Web/EventHandlers.cs Changed ontoggle event handler from EventArgs to ChangeEventArgs to support binding
src/Components/Web/src/WebEventData/WebEventData.cs Added toggle event deserialization case to use ChangeEventArgsReader for boolean values
src/Components/Web.JS/src/Rendering/Events/EventTypes.ts Implemented parseToggleEvent function using ToggleEvent.newState for all toggle event sources
src/Components/test/testassets/BasicTestApp/BindCasesComponent.razor Added test markup and backing fields for details element binding scenarios
src/Components/test/E2ETest/Tests/BindTest.cs Added E2E tests for details element binding with both initial states


// <details>
[EventHandler("ontoggle", typeof(EventArgs), true, true)]
// <details> and elements with popover attribute
Copy link

Copilot AI Dec 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment should mention <dialog> elements in addition to <details> and popover elements, since dialog elements also fire toggle events. Suggested change:

// <details>, <dialog>, and elements with popover attribute
Suggested change
// <details> and elements with popover attribute
// <details>, <dialog>, and elements with popover attribute

Copilot uses AI. Check for mistakes.

// <details> element binds to the 'open' attribute using the 'ontoggle' event
[BindElement("details", null, "open", "ontoggle")]
[BindElement("details", "open", "open", "ontoggle")]
Copy link

Copilot AI Dec 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PR title mentions "Add @bind-open support for details and dialog elements", and the technical notes in the PR description explain that toggle events are fired by both <details> and <dialog> elements. However, only BindElement attributes for the details element are added here. To fully support the stated functionality, you should also add:

[BindElement("dialog", null, "open", "ontoggle")]
[BindElement("dialog", "open", "open", "ontoggle")]

Without these attributes, @bind-open will not work for <dialog> elements despite the infrastructure being in place.

Suggested change
[BindElement("details", "open", "open", "ontoggle")]
[BindElement("details", "open", "open", "ontoggle")]
// <dialog> element binds to the 'open' attribute using the 'ontoggle' event
[BindElement("dialog", null, "open", "ontoggle")]
[BindElement("dialog", "open", "open", "ontoggle")]

Copilot uses AI. Check for mistakes.
Comment on lines +197 to +245
[Fact]
public void CanBindDetailsElement_InitiallyClosed()
{
var target = Browser.Exists(By.Id("details-initially-closed"));
var boundValue = Browser.Exists(By.Id("details-initially-closed-value"));
var toggleButton = Browser.Exists(By.Id("details-initially-closed-toggle"));
Assert.Null(target.GetDomAttribute("open"));
Assert.Equal("False", boundValue.Text);

// Click to expand; verify value is updated
target.FindElement(By.TagName("summary")).Click();
Browser.NotEqual(null, () => target.GetDomAttribute("open"));
Browser.Equal("True", () => boundValue.Text);

// Click to collapse; verify value is updated
target.FindElement(By.TagName("summary")).Click();
Browser.Equal(null, () => target.GetDomAttribute("open"));
Browser.Equal("False", () => boundValue.Text);

// Modify data programmatically; verify details is updated
toggleButton.Click();
Browser.NotEqual(null, () => target.GetDomAttribute("open"));
Browser.Equal("True", () => boundValue.Text);
}

[Fact]
public void CanBindDetailsElement_InitiallyOpen()
{
var target = Browser.Exists(By.Id("details-initially-open"));
var boundValue = Browser.Exists(By.Id("details-initially-open-value"));
var toggleButton = Browser.Exists(By.Id("details-initially-open-toggle"));
Assert.NotNull(target.GetDomAttribute("open"));
Assert.Equal("True", boundValue.Text);

// Click to collapse; verify value is updated
target.FindElement(By.TagName("summary")).Click();
Browser.Equal(null, () => target.GetDomAttribute("open"));
Browser.Equal("False", () => boundValue.Text);

// Click to expand; verify value is updated
target.FindElement(By.TagName("summary")).Click();
Browser.NotEqual(null, () => target.GetDomAttribute("open"));
Browser.Equal("True", () => boundValue.Text);

// Modify data programmatically; verify details is updated
toggleButton.Click();
Browser.Equal(null, () => target.GetDomAttribute("open"));
Browser.Equal("False", () => boundValue.Text);
}
Copy link

Copilot AI Dec 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PR title mentions adding @bind-open support for both details and dialog elements. While tests are provided for the <details> element, there are no tests for <dialog> element binding. Consider adding E2E tests for <dialog> element binding to ensure it works correctly, since dialog elements also fire toggle events and should support @bind-open according to the PR description.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-blazor Includes: Blazor, Razor Components

Projects

None yet

Development

Successfully merging this pull request may close these issues.

can't bind to open attribute on <details> or <dialog> elements

2 participants