diff --git a/src/Aspire.Dashboard/Components/Controls/ApplicationName.razor b/src/Aspire.Dashboard/Components/Controls/ApplicationName.razor index ca4f15cec7..a01ba84fea 100644 --- a/src/Aspire.Dashboard/Components/Controls/ApplicationName.razor +++ b/src/Aspire.Dashboard/Components/Controls/ApplicationName.razor @@ -1,3 +1,3 @@ @namespace Aspire.Dashboard.Components -@_applicationName +@_pageTitle diff --git a/src/Aspire.Dashboard/Components/Controls/ApplicationName.razor.cs b/src/Aspire.Dashboard/Components/Controls/ApplicationName.razor.cs index 26ef81728e..0c7397fc66 100644 --- a/src/Aspire.Dashboard/Components/Controls/ApplicationName.razor.cs +++ b/src/Aspire.Dashboard/Components/Controls/ApplicationName.razor.cs @@ -13,15 +13,18 @@ public sealed partial class ApplicationName : ComponentBase, IDisposable private CancellationTokenSource? _disposalCts; [Parameter] - public string? ResourceName { get; init; } + public string? AdditionalText { get; set; } [Parameter] - public IStringLocalizer? Loc { get; init; } + public string? ResourceName { get; set; } + + [Parameter] + public IStringLocalizer? Loc { get; set; } [Inject] public required IDashboardClient DashboardClient { get; init; } - private string? _applicationName; + private string? _pageTitle; protected override async Task OnInitializedAsync() { @@ -31,15 +34,24 @@ protected override async Task OnInitializedAsync() _disposalCts = new CancellationTokenSource(); await DashboardClient.WhenConnected.WaitAsync(_disposalCts.Token); } + } + + protected override void OnParametersSet() + { + string applicationName; if (ResourceName is not null && Loc is not null) { - _applicationName = string.Format(CultureInfo.InvariantCulture, Loc[ResourceName], DashboardClient.ApplicationName); + applicationName = string.Format(CultureInfo.InvariantCulture, Loc[ResourceName], DashboardClient.ApplicationName); } else { - _applicationName = DashboardClient.ApplicationName; + applicationName = DashboardClient.ApplicationName; } + + _pageTitle = string.IsNullOrEmpty(AdditionalText) + ? applicationName + : $"{applicationName} ({AdditionalText})"; } public void Dispose() diff --git a/src/Aspire.Dashboard/Components/Pages/ConsoleLogs.razor b/src/Aspire.Dashboard/Components/Pages/ConsoleLogs.razor index 3624efff22..69447df45d 100644 --- a/src/Aspire.Dashboard/Components/Pages/ConsoleLogs.razor +++ b/src/Aspire.Dashboard/Components/Pages/ConsoleLogs.razor @@ -5,7 +5,12 @@ @using Aspire.Dashboard.Resources @namespace Aspire.Dashboard.Components.Pages - + + +
Loc @inject IStringLocalizer ControlsStringsLoc - + + @{ + string? additionalText; + if (PageViewModel.SelectedApplication.Id is { ReplicaSetName: { } replicaSetName }) + { + additionalText = PageViewModel.SelectedInstrument is { } selectedInstrument + ? $"{replicaSetName} - {selectedInstrument.Name}" + : replicaSetName; + } + else + { + additionalText = null; + } + } + + +
ControlsStringsLoc @inject IStringLocalizer FilterLoc - + + +
Loc @inject IStringLocalizer ControlStringsLoc - + + +
@if (_trace is { } trace) @@ -19,14 +24,11 @@ MobileToolbarButtonText="@Loc[nameof(Dashboard.Resources.TraceDetail.TraceDetailMobileToolbarButtonText)]" IsSummaryDetailsViewOpen="@(SelectedSpan is not null)"> - @{ - var headerSpan = trace.RootOrFirstSpan; - } diff --git a/src/Aspire.Dashboard/Components/Pages/TraceDetail.razor.cs b/src/Aspire.Dashboard/Components/Pages/TraceDetail.razor.cs index 2889cc39cb..722d94a8e9 100644 --- a/src/Aspire.Dashboard/Components/Pages/TraceDetail.razor.cs +++ b/src/Aspire.Dashboard/Components/Pages/TraceDetail.razor.cs @@ -95,6 +95,17 @@ private ValueTask> GetData(GridI }); } + private string? GetPageTitle() + { + if (_trace is null) + { + return null; + } + + var headerSpan = _trace.RootOrFirstSpan; + return $"{GetResourceName(headerSpan.Source)}: {headerSpan.Name}"; + } + private static Icon GetSpanIcon(OtlpSpan span) { switch (span.Kind) diff --git a/src/Aspire.Dashboard/Components/Pages/Traces.razor b/src/Aspire.Dashboard/Components/Pages/Traces.razor index bf7426bd90..25d1a3673f 100644 --- a/src/Aspire.Dashboard/Components/Pages/Traces.razor +++ b/src/Aspire.Dashboard/Components/Pages/Traces.razor @@ -13,7 +13,12 @@ @inject IStringLocalizer ControlsStringsLoc @implements IDisposable - + + +
diff --git a/tests/Aspire.Dashboard.Components.Tests/Controls/ApplicationNameTests.cs b/tests/Aspire.Dashboard.Components.Tests/Controls/ApplicationNameTests.cs index 13e0e1f354..a1d5023e77 100644 --- a/tests/Aspire.Dashboard.Components.Tests/Controls/ApplicationNameTests.cs +++ b/tests/Aspire.Dashboard.Components.Tests/Controls/ApplicationNameTests.cs @@ -5,6 +5,7 @@ using Bunit; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Localization; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using Xunit; @@ -32,6 +33,29 @@ public void Render_DashboardClientDisabled_Success() cut.MarkupMatches("Aspire"); } + [Fact] + public void Render_With_Args() + { + // Arrange + Services.AddSingleton(new ConfigurationManager()); + Services.AddSingleton(NullLoggerFactory.Instance); + Services.AddSingleton(); + Services.AddSingleton(); + Services.AddSingleton(); + Services.AddSingleton(new MockKnownPropertyLookup()); + + // Act + var cut = RenderComponent(builder => + { + builder.Add(p => p.ResourceName, "{0} traces"); + builder.Add(p => p.Loc, new TestStringLocalizer()); + builder.Add(p => p.AdditionalText, "Hello World"); + }); + + // Assert + cut.MarkupMatches("Localized:Aspire traces (Hello World)"); + } + [Fact] public void Render_DashboardClientEnabled_HtmlInName_Success() { @@ -57,4 +81,12 @@ private sealed class MockDashboardClient : IDashboardClient public IAsyncEnumerable> SubscribeConsoleLogs(string resourceName, CancellationToken cancellationToken) => throw new NotImplementedException(); public Task SubscribeResourcesAsync(CancellationToken cancellationToken) => throw new NotImplementedException(); } + + private sealed class TestStringLocalizer : IStringLocalizer + { + public LocalizedString this[string name] => new LocalizedString(name, $"Localized:{name}"); + public LocalizedString this[string name, params object[] arguments] => new LocalizedString(name, $"Localized:{name}:" + string.Join("+", arguments)); + + public IEnumerable GetAllStrings(bool includeParentCultures) => []; + } }