diff --git a/src/Components/Components/src/PublicAPI.Unshipped.txt b/src/Components/Components/src/PublicAPI.Unshipped.txt index b35a2302ba1b..2709f48969f1 100644 --- a/src/Components/Components/src/PublicAPI.Unshipped.txt +++ b/src/Components/Components/src/PublicAPI.Unshipped.txt @@ -4,6 +4,18 @@ Microsoft.AspNetCore.Components.Infrastructure.ComponentStatePersistenceManager. Microsoft.AspNetCore.Components.RenderHandle.DispatchExceptionAsync(System.Exception! exception) -> System.Threading.Tasks.Task! *REMOVED*Microsoft.AspNetCore.Components.NavigationManager.ToAbsoluteUri(string! relativeUri) -> System.Uri! Microsoft.AspNetCore.Components.NavigationManager.ToAbsoluteUri(string? relativeUri) -> System.Uri! +Microsoft.AspNetCore.Components.Sections.SectionContent +Microsoft.AspNetCore.Components.Sections.SectionContent.ChildContent.get -> Microsoft.AspNetCore.Components.RenderFragment? +Microsoft.AspNetCore.Components.Sections.SectionContent.ChildContent.set -> void +Microsoft.AspNetCore.Components.Sections.SectionContent.Dispose() -> void +Microsoft.AspNetCore.Components.Sections.SectionContent.SectionContent() -> void +Microsoft.AspNetCore.Components.Sections.SectionContent.SectionId.get -> object! +Microsoft.AspNetCore.Components.Sections.SectionContent.SectionId.set -> void +Microsoft.AspNetCore.Components.Sections.SectionOutlet +Microsoft.AspNetCore.Components.Sections.SectionOutlet.Dispose() -> void +Microsoft.AspNetCore.Components.Sections.SectionOutlet.SectionId.get -> object! +Microsoft.AspNetCore.Components.Sections.SectionOutlet.SectionId.set -> void +Microsoft.AspNetCore.Components.Sections.SectionOutlet.SectionOutlet() -> void Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder.AddComponentParameter(int sequence, string! name, object? value) -> void override Microsoft.AspNetCore.Components.EventCallback.GetHashCode() -> int override Microsoft.AspNetCore.Components.EventCallback.Equals(object? obj) -> bool diff --git a/src/Components/Components/src/Sections/SectionContent.cs b/src/Components/Components/src/Sections/SectionContent.cs index e8342597c59c..b19cf3525a5f 100644 --- a/src/Components/Components/src/Sections/SectionContent.cs +++ b/src/Components/Components/src/Sections/SectionContent.cs @@ -4,24 +4,25 @@ namespace Microsoft.AspNetCore.Components.Sections; /// -/// Provides content to components with matching s. +/// Provides content to components with matching s. /// -internal sealed class SectionContent : ISectionContentProvider, IComponent, IDisposable +public sealed class SectionContent : ISectionContentProvider, IComponent, IDisposable { - private string? _registeredName; + private object? _registeredSectionId; + private bool? _registeredIsDefaultContent; private SectionRegistry _registry = default!; /// - /// Gets or sets the name that determines which instance will render + /// Gets or sets the ID that determines which instance will render /// the content of this instance. /// - [Parameter] public string Name { get; set; } = default!; + [Parameter, EditorRequired] public object SectionId { get; set; } = default!; /// /// Gets or sets whether this component should provide the default content for the target /// . /// - [Parameter] public bool IsDefaultContent { get; set; } + internal bool IsDefaultContent { get; set; } /// /// Gets or sets the content to be rendered in corresponding instances. @@ -37,25 +38,42 @@ void IComponent.Attach(RenderHandle renderHandle) Task IComponent.SetParametersAsync(ParameterView parameters) { - parameters.SetParameterProperties(this); + foreach (var param in parameters) + { + switch (param.Name) + { + case nameof(SectionContent.SectionId): + SectionId = param.Value; + break; + case nameof(SectionContent.IsDefaultContent): + IsDefaultContent = (bool)param.Value; + break; + case nameof(SectionContent.ChildContent): + ChildContent = (RenderFragment)param.Value; + break; + default: + throw new ArgumentException($"Unknown parameter '{param.Name}'"); + } + } - if (string.IsNullOrEmpty(Name)) + if (SectionId is null) { - throw new InvalidOperationException($"{GetType()} requires a non-empty string parameter '{nameof(Name)}'."); + throw new InvalidOperationException($"{nameof(SectionContent)} requires a non-null value for the parameter '{nameof(SectionId)}'."); } - if (Name != _registeredName) + if (!object.Equals(SectionId, _registeredSectionId) || IsDefaultContent != _registeredIsDefaultContent) { - if (_registeredName is not null) + if (_registeredSectionId is not null) { - _registry.RemoveProvider(_registeredName, this); + _registry.RemoveProvider(_registeredSectionId, this); } - _registry.AddProvider(Name, this, IsDefaultContent); - _registeredName = Name; + _registry.AddProvider(SectionId, this, IsDefaultContent); + _registeredSectionId = SectionId; + _registeredIsDefaultContent = IsDefaultContent; } - _registry.NotifyContentChanged(Name, this); + _registry.NotifyContentChanged(SectionId, this); return Task.CompletedTask; } @@ -63,9 +81,9 @@ Task IComponent.SetParametersAsync(ParameterView parameters) /// public void Dispose() { - if (_registeredName is not null) + if (_registeredSectionId is not null) { - _registry.RemoveProvider(_registeredName, this); + _registry.RemoveProvider(_registeredSectionId, this); } } } diff --git a/src/Components/Components/src/Sections/SectionOutlet.cs b/src/Components/Components/src/Sections/SectionOutlet.cs index e9a7e2d06739..e7f481052014 100644 --- a/src/Components/Components/src/Sections/SectionOutlet.cs +++ b/src/Components/Components/src/Sections/SectionOutlet.cs @@ -4,22 +4,22 @@ namespace Microsoft.AspNetCore.Components.Sections; /// -/// Renders content provided by components with matching s. +/// Renders content provided by components with matching s. /// -internal sealed class SectionOutlet : ISectionContentSubscriber, IComponent, IDisposable +public sealed class SectionOutlet : ISectionContentSubscriber, IComponent, IDisposable { private static readonly RenderFragment _emptyRenderFragment = _ => { }; - private string? _subscribedName; + private object? _subscribedSectionId; private RenderHandle _renderHandle; private SectionRegistry _registry = default!; private RenderFragment? _content; /// - /// Gets or sets the name that determines which instances will provide + /// Gets or sets the ID that determines which instances will provide /// content to this instance. /// - [Parameter] public string Name { get; set; } = default!; + [Parameter, EditorRequired] public object SectionId { get; set; } = default!; void IComponent.Attach(RenderHandle renderHandle) { @@ -31,20 +31,20 @@ Task IComponent.SetParametersAsync(ParameterView parameters) { parameters.SetParameterProperties(this); - if (string.IsNullOrEmpty(Name)) + if (SectionId is null) { - throw new InvalidOperationException($"{GetType()} requires a non-empty string parameter '{nameof(Name)}'."); + throw new InvalidOperationException($"{nameof(SectionOutlet)} requires a non-null value for the parameter '{nameof(SectionId)}'."); } - if (Name != _subscribedName) + if (!object.Equals(SectionId, _subscribedSectionId)) { - if (_subscribedName is not null) + if (_subscribedSectionId is not null) { - _registry.Unsubscribe(_subscribedName); + _registry.Unsubscribe(_subscribedSectionId); } - _registry.Subscribe(Name, this); - _subscribedName = Name; + _registry.Subscribe(SectionId, this); + _subscribedSectionId = SectionId; } RenderContent(); @@ -74,9 +74,9 @@ private void RenderContent() /// public void Dispose() { - if (_subscribedName is not null) + if (_subscribedSectionId is not null) { - _registry.Unsubscribe(_subscribedName); + _registry.Unsubscribe(_subscribedSectionId); } } } diff --git a/src/Components/Components/src/Sections/SectionRegistry.cs b/src/Components/Components/src/Sections/SectionRegistry.cs index 7e30f0b3d92f..0976deccc466 100644 --- a/src/Components/Components/src/Sections/SectionRegistry.cs +++ b/src/Components/Components/src/Sections/SectionRegistry.cs @@ -5,15 +5,15 @@ namespace Microsoft.AspNetCore.Components.Sections; internal sealed class SectionRegistry { - private readonly Dictionary _subscribersByName = new(); - private readonly Dictionary> _providersByName = new(); + private readonly Dictionary _subscribersBySectionId = new(); + private readonly Dictionary> _providersBySectionId = new(); - public void AddProvider(string name, ISectionContentProvider provider, bool isDefaultProvider) + public void AddProvider(object sectionId, ISectionContentProvider provider, bool isDefaultProvider) { - if (!_providersByName.TryGetValue(name, out var providers)) + if (!_providersBySectionId.TryGetValue(sectionId, out var providers)) { providers = new(); - _providersByName.Add(name, providers); + _providersBySectionId.Add(sectionId, providers); } if (isDefaultProvider) @@ -26,18 +26,18 @@ public void AddProvider(string name, ISectionContentProvider provider, bool isDe } } - public void RemoveProvider(string name, ISectionContentProvider provider) + public void RemoveProvider(object sectionId, ISectionContentProvider provider) { - if (!_providersByName.TryGetValue(name, out var providers)) + if (!_providersBySectionId.TryGetValue(sectionId, out var providers)) { - throw new InvalidOperationException($"There are no content providers with the name '{name}'."); + throw new InvalidOperationException($"There are no content providers with the given section ID '{sectionId}'."); } var index = providers.LastIndexOf(provider); if (index < 0) { - throw new InvalidOperationException($"The provider was not found in the providers list of name '{name}'."); + throw new InvalidOperationException($"The provider was not found in the providers list of the given section ID '{sectionId}'."); } providers.RemoveAt(index); @@ -47,44 +47,44 @@ public void RemoveProvider(string name, ISectionContentProvider provider) // We just removed the most recently added provider, meaning we need to change // the current content to that of second most recently added provider. var content = GetCurrentProviderContentOrDefault(providers); - NotifyContentChangedForSubscriber(name, content); + NotifyContentChangedForSubscriber(sectionId, content); } } - public void Subscribe(string name, ISectionContentSubscriber subscriber) + public void Subscribe(object sectionId, ISectionContentSubscriber subscriber) { - if (_subscribersByName.ContainsKey(name)) + if (_subscribersBySectionId.ContainsKey(sectionId)) { - throw new InvalidOperationException($"There is already a subscriber to the content '{name}'."); + throw new InvalidOperationException($"There is already a subscriber to the content with the given section ID '{sectionId}'."); } // Notify the new subscriber with any existing content. - var content = GetCurrentProviderContentOrDefault(name); + var content = GetCurrentProviderContentOrDefault(sectionId); subscriber.ContentChanged(content); - _subscribersByName.Add(name, subscriber); + _subscribersBySectionId.Add(sectionId, subscriber); } - public void Unsubscribe(string name) + public void Unsubscribe(object sectionId) { - if (!_subscribersByName.Remove(name)) + if (!_subscribersBySectionId.Remove(sectionId)) { - throw new InvalidOperationException($"The subscriber with name '{name}' is already unsubscribed."); + throw new InvalidOperationException($"The subscriber with the given section ID '{sectionId}' is already unsubscribed."); } } - public void NotifyContentChanged(string name, ISectionContentProvider provider) + public void NotifyContentChanged(object sectionId, ISectionContentProvider provider) { - if (!_providersByName.TryGetValue(name, out var providers)) + if (!_providersBySectionId.TryGetValue(sectionId, out var providers)) { - throw new InvalidOperationException($"There are no content providers with the name '{name}'."); + throw new InvalidOperationException($"There are no content providers with the given section ID '{sectionId}'."); } // We only notify content changed for subscribers when the content of the // most recently added provider changes. if (providers.Count != 0 && providers[^1] == provider) { - NotifyContentChangedForSubscriber(name, provider.Content); + NotifyContentChangedForSubscriber(sectionId, provider.Content); } } @@ -93,14 +93,14 @@ public void NotifyContentChanged(string name, ISectionContentProvider provider) ? providers[^1].Content : null; - private RenderFragment? GetCurrentProviderContentOrDefault(string name) - => _providersByName.TryGetValue(name, out var existingList) + private RenderFragment? GetCurrentProviderContentOrDefault(object sectionId) + => _providersBySectionId.TryGetValue(sectionId, out var existingList) ? GetCurrentProviderContentOrDefault(existingList) : null; - private void NotifyContentChangedForSubscriber(string name, RenderFragment? content) + private void NotifyContentChangedForSubscriber(object sectionId, RenderFragment? content) { - if (_subscribersByName.TryGetValue(name, out var subscriber)) + if (_subscribersBySectionId.TryGetValue(sectionId, out var subscriber)) { subscriber.ContentChanged(content); } diff --git a/src/Components/Web/src/Head/HeadContent.cs b/src/Components/Web/src/Head/HeadContent.cs index 19aeb5bd94c0..0791d5bcdb7e 100644 --- a/src/Components/Web/src/Head/HeadContent.cs +++ b/src/Components/Web/src/Head/HeadContent.cs @@ -21,7 +21,7 @@ public sealed class HeadContent : ComponentBase protected override void BuildRenderTree(RenderTreeBuilder builder) { builder.OpenComponent(0); - builder.AddComponentParameter(1, nameof(SectionContent.Name), HeadOutlet.HeadSectionOutletName); + builder.AddComponentParameter(1, nameof(SectionContent.SectionId), HeadOutlet.HeadSectionId); builder.AddComponentParameter(2, nameof(SectionContent.ChildContent), ChildContent); builder.CloseComponent(); } diff --git a/src/Components/Web/src/Head/HeadOutlet.cs b/src/Components/Web/src/Head/HeadOutlet.cs index 8b29975f498e..313ec37b550e 100644 --- a/src/Components/Web/src/Head/HeadOutlet.cs +++ b/src/Components/Web/src/Head/HeadOutlet.cs @@ -14,8 +14,8 @@ public sealed class HeadOutlet : ComponentBase { private const string GetAndRemoveExistingTitle = "Blazor._internal.PageTitle.getAndRemoveExistingTitle"; - internal const string HeadSectionOutletName = "head"; - internal const string TitleSectionOutletName = "title"; + internal static readonly object HeadSectionId = new(); + internal static readonly object TitleSectionId = new(); private string? _defaultTitle; @@ -37,14 +37,14 @@ protected override void BuildRenderTree(RenderTreeBuilder builder) { // Render the title content builder.OpenComponent(0); - builder.AddComponentParameter(1, nameof(SectionOutlet.Name), TitleSectionOutletName); + builder.AddComponentParameter(1, nameof(SectionOutlet.SectionId), TitleSectionId); builder.CloseComponent(); // Render the default title if it exists if (!string.IsNullOrEmpty(_defaultTitle)) { builder.OpenComponent(2); - builder.AddComponentParameter(3, nameof(SectionContent.Name), TitleSectionOutletName); + builder.AddComponentParameter(3, nameof(SectionContent.SectionId), TitleSectionId); builder.AddComponentParameter(4, nameof(SectionContent.IsDefaultContent), true); builder.AddComponentParameter(5, nameof(SectionContent.ChildContent), (RenderFragment)BuildDefaultTitleRenderTree); builder.CloseComponent(); @@ -52,7 +52,7 @@ protected override void BuildRenderTree(RenderTreeBuilder builder) // Render the rest of the head metadata builder.OpenComponent(6); - builder.AddComponentParameter(7, nameof(SectionOutlet.Name), HeadSectionOutletName); + builder.AddComponentParameter(7, nameof(SectionOutlet.SectionId), HeadSectionId); builder.CloseComponent(); } diff --git a/src/Components/Web/src/Head/PageTitle.cs b/src/Components/Web/src/Head/PageTitle.cs index 7a745973e5e7..4ddf0dd013a4 100644 --- a/src/Components/Web/src/Head/PageTitle.cs +++ b/src/Components/Web/src/Head/PageTitle.cs @@ -21,7 +21,7 @@ public sealed class PageTitle : ComponentBase protected override void BuildRenderTree(RenderTreeBuilder builder) { builder.OpenComponent(0); - builder.AddComponentParameter(1, nameof(SectionContent.Name), HeadOutlet.TitleSectionOutletName); + builder.AddComponentParameter(1, nameof(SectionContent.SectionId), HeadOutlet.TitleSectionId); builder.AddComponentParameter(2, nameof(SectionContent.ChildContent), (RenderFragment)BuildTitleRenderTree); builder.CloseComponent(); } diff --git a/src/Components/Web/test/HtmlRendering/HtmlRendererTest.cs b/src/Components/Web/test/HtmlRendering/HtmlRendererTest.cs index a458475cfa10..6a842fa1d25e 100644 --- a/src/Components/Web/test/HtmlRendering/HtmlRendererTest.cs +++ b/src/Components/Web/test/HtmlRendering/HtmlRendererTest.cs @@ -819,7 +819,7 @@ await htmlRenderer.Dispatcher.InvokeAsync(async () => // Arrange/Act/Assert 1: initially get some empty output var first = await htmlRenderer.RenderComponentAsync(ParameterView.FromDictionary(new Dictionary { - { nameof(SectionOutlet.Name), "testsection" } + { nameof(SectionOutlet.SectionId), "testsection" } })); Assert.Empty(first.ToHtmlString()); @@ -827,7 +827,7 @@ await htmlRenderer.Dispatcher.InvokeAsync(async () => // Act/Assert 2: cause it to be updated var second = await htmlRenderer.RenderComponentAsync(ParameterView.FromDictionary(new Dictionary { - { nameof(SectionContent.Name), "testsection" }, + { nameof(SectionContent.SectionId), "testsection" }, { nameof(SectionContent.ChildContent), (RenderFragment)(builder => { builder.AddContent(0, "Hello from the section content provider"); diff --git a/src/Components/test/E2ETest/Tests/SectionsTest.cs b/src/Components/test/E2ETest/Tests/SectionsTest.cs new file mode 100644 index 000000000000..4e798996daa2 --- /dev/null +++ b/src/Components/test/E2ETest/Tests/SectionsTest.cs @@ -0,0 +1,133 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using BasicTestApp; +using Microsoft.AspNetCore.Components.E2ETest.Infrastructure.ServerFixtures; +using Microsoft.AspNetCore.Components.E2ETest.Infrastructure; +using Microsoft.AspNetCore.E2ETesting; +using Xunit.Abstractions; +using Microsoft.AspNetCore.Components.E2ETest; +using OpenQA.Selenium; + +namespace Microsoft.AspNetCore.Components.E2ETests.Tests; + +public class SectionsTest : ServerTestBase> +{ + private IWebElement _appElement; + + public SectionsTest + (BrowserFixture browserFixture, + ToggleExecutionModeServerFixture serverFixture, + ITestOutputHelper output) + : base(browserFixture, serverFixture, output) + { + } + + protected override void InitializeAsyncCore() + { + Navigate(ServerPathBase, noReload: _serverFixture.ExecutionMode == ExecutionMode.Client); + _appElement = Browser.MountTestComponent(); + } + + [Fact] + public void NoExistingSectionContents_SectionOutletsRenderNothing() + { + // At the beginning no SectionContents are rendered + Browser.DoesNotExist(By.Id("counter")); + Browser.DoesNotExist(By.Id("text")); + } + + [Fact] + public void RenderOneSectionContent_MatchingSectionOutletRendersContentSuccessfully() + { + _appElement.FindElement(By.Id("counter-render-section-content")).Click(); + + var counter = Browser.Exists(By.Id("counter")); + Assert.Equal("0", counter.Text); + + _appElement.FindElement(By.Id("increment-button")).Click(); + + Assert.Equal("1", counter.Text); + } + + [Fact] + public void RenderTwoSectionContentsWithSameId_LastRenderedOverridesSectionOutletContent() + { + _appElement.FindElement(By.Id("counter-render-section-content")).Click(); + + Browser.Exists(By.Id("counter")); + + _appElement.FindElement(By.Id("text-render-section-content")).Click(); + + Browser.Exists(By.Id("text")); + Browser.DoesNotExist(By.Id("counter")); + } + + [Fact] + public void SecondSectionContentGetsDisposed_SectionOutletRendersFirstSectionContent() + { + // Render Counter and TextComponent SectionContents with same Name + // TextComponent SectionContent overrides Counter SectionContent + _appElement.FindElement(By.Id("counter-render-section-content")).Click(); + _appElement.FindElement(By.Id("text-render-section-content")).Click(); + + _appElement.FindElement(By.Id("text-dispose-section-content")).Click(); + + Browser.Exists(By.Id("counter")); + } + + [Fact] + public void BothSectionContentsGetDisposed_SectionOutletsRenderNothing() + { + _appElement.FindElement(By.Id("counter-render-section-content")).Click(); + _appElement.FindElement(By.Id("text-render-section-content")).Click(); + + _appElement.FindElement(By.Id("counter-dispose-section-content")).Click(); + _appElement.FindElement(By.Id("text-dispose-section-content")).Click(); + + Browser.DoesNotExist(By.Id("counter")); + Browser.DoesNotExist(By.Id("text")); + } + + [Fact] + public void SectionContentIdChanges_MatchingSectionOutletRendersContent() + { + // Render Counter and TextComponent SectionContents with same Name + // TextComponent SectionContent overrides Counter SectionContent + _appElement.FindElement(By.Id("counter-render-section-content")).Click(); + _appElement.FindElement(By.Id("text-render-section-content")).Click(); + + _appElement.FindElement(By.Id("counter-change-section-content-name")).Click(); + + Browser.Exists(By.Id("counter")); + } + + [Fact] + public void SectionContentIdChangesToNonExisting_NoMatchingSectionOutletResultingNoRendering() + { + // Render Counter and TextComponent SectionContents with same Name + // TextComponent SectionContent overrides Counter SectionContent + _appElement.FindElement(By.Id("counter-render-section-content")).Click(); + _appElement.FindElement(By.Id("text-render-section-content")).Click(); + + _appElement.FindElement(By.Id("counter-change-section-content-name-nonexisting")).Click(); + + Browser.DoesNotExist(By.Id("counter")); + } + + [Fact] + public void SectionOutletGetsDisposed_NoContentsRendered() + { + // Render Counter and TextComponent SectionContents with same Name + _appElement.FindElement(By.Id("counter-render-section-content")).Click(); + _appElement.FindElement(By.Id("text-render-section-content")).Click(); + + // TextComponent SectionContent overrides Counter SectionContent + Browser.Exists(By.Id("text")); + + _appElement.FindElement(By.Id("section-outlet-dispose")).Click(); + + Browser.DoesNotExist(By.Id("counter")); + Browser.DoesNotExist(By.Id("text")); + } +} diff --git a/src/Components/test/testassets/BasicTestApp/Index.razor b/src/Components/test/testassets/BasicTestApp/Index.razor index b4aecb142110..623855306b31 100644 --- a/src/Components/test/testassets/BasicTestApp/Index.razor +++ b/src/Components/test/testassets/BasicTestApp/Index.razor @@ -105,6 +105,7 @@ + @System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription diff --git a/src/Components/test/testassets/BasicTestApp/SectionsTest/Counter.razor b/src/Components/test/testassets/BasicTestApp/SectionsTest/Counter.razor new file mode 100644 index 000000000000..fca31ddcef75 --- /dev/null +++ b/src/Components/test/testassets/BasicTestApp/SectionsTest/Counter.razor @@ -0,0 +1,31 @@ +@using Microsoft.AspNetCore.Components.Sections + +
+

Counter

+ +

Counter SectionContent was rendered = @SectionContentExists.ToString()

+ + @if (SectionContentExists) + { + +

@currentCount

+
+ } + + +
+ +@code { + [Parameter] + public bool SectionContentExists { get; set; } + + [Parameter] + public object SectionId { get; set; } + + int currentCount = 0; + + void IncrementCount() + { + currentCount++; + } +} diff --git a/src/Components/test/testassets/BasicTestApp/SectionsTest/ParentComponentWithTwoChildren.razor b/src/Components/test/testassets/BasicTestApp/SectionsTest/ParentComponentWithTwoChildren.razor new file mode 100644 index 000000000000..4834b62abcd2 --- /dev/null +++ b/src/Components/test/testassets/BasicTestApp/SectionsTest/ParentComponentWithTwoChildren.razor @@ -0,0 +1,72 @@ +@using Microsoft.AspNetCore.Components.Sections + +

Parent Component

+ +@if (SectionOutletExists) +{ + +} + +

Text between two section outlets

+ + + + + + + + +
+ +
+ +
+ +
+ +
+ + + + +
+ +
+ +@code { + private static object FirstSectionId = new(); + private static int SecondSectionId = 123; + + private bool CounterSectionContentExists = false; + private bool TextComponentSectionContentExists = false; + + private object CounterSectionContentId = FirstSectionId; + private object TextComponentSectionContentId = FirstSectionId; + + private bool SectionOutletExists = true; +} diff --git a/src/Components/test/testassets/BasicTestApp/SectionsTest/TextComponent.razor b/src/Components/test/testassets/BasicTestApp/SectionsTest/TextComponent.razor new file mode 100644 index 000000000000..c8d0ebf2997a --- /dev/null +++ b/src/Components/test/testassets/BasicTestApp/SectionsTest/TextComponent.razor @@ -0,0 +1,22 @@ +@using Microsoft.AspNetCore.Components.Sections + +
+

Text Component

+ +

TextComponent SectionContent was rendered = @SectionContentExists.ToString()

+ + @if (SectionContentExists) + { + +

Hello!

+
+ } +
+ +@code { + [Parameter] + public bool SectionContentExists { get; set; } + + [Parameter] + public object SectionId { get; set; } +}