Skip to content

Commit

Permalink
Make all pages accessible to low vision on reflow (#5200)
Browse files Browse the repository at this point in the history
* Make all pages accessible to low vision on reflow

* avoid recreating ChartContainer to avoid state loss

* hide level, don't hide message on structured logs

* clean up

* more cleanup

* fix test

* Disable copy button for very low viewport width

* add missing argument in tests

* fix extra parameter after merge

* Dynamically resize graph

* address some pr comments

* update comment, re-add row click attributes

* add grid column manager

* implement gridcolumnmanager in resources

* fix invalid comment

* Avoid horizontal scrolling

* use grid manager in tracedetail

* use grid column manager in traces

* use column manager in structured logs data grid

* make metrics select a resource a link on mobile

* Move conditional rendering logic of columns into AspireProperty/TemplateColumn

* Remove structured logs level column on mobile

* remove accidentally-kept comment

* Fix toolbar layout, remove viewport info from column manager

* assert no duplicate columns, rename param

* remove using

* Fix tests

---------

Co-authored-by: James Newton-King <james@newtonking.com>
Co-authored-by: Drew Noakes <git@drewnoakes.com>
  • Loading branch information
3 people authored Aug 20, 2024
1 parent 3a41098 commit 31ac767
Show file tree
Hide file tree
Showing 95 changed files with 931 additions and 461 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ else
Id="@($"tab-{Metrics.MetricViewKind.Table}")"
Label="@Loc[nameof(ControlsStrings.ChartContainerTableTab)]"
Icon="@(new Icons.Regular.Size24.Table())">
<div class="metric-tab">
<div class="metrics-chart-container metric-tab">
<MetricTable InstrumentViewModel="_instrumentViewModel" Duration="Duration" Applications="Applications" />
<ChartFilters InstrumentViewModel="_instrumentViewModel" Instrument="_instrument" DimensionFilters="@DimensionFilters"/>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
}
}

<div id="metric-table-container" style="height: 40vh; overflow-y: auto; margin-bottom: 20px; max-width:1200px;">
<div id="metric-table-container" style="height: 40vh; overflow-y: auto; width:1200px;">
@* ItemKey is to preserve row focus by associating rows with their associated time *@
<FluentDataGrid
ResizeLabel="@AspireFluentDataGridHeaderCell.GetResizeLabel(ControlsStringsLoc)"
Expand Down Expand Up @@ -104,9 +104,9 @@
</FluentDataGrid>
</div>

<FluentStack Orientation="Orientation.Vertical" Style="margin-bottom: 20px;">
<FluentStack Orientation="Orientation.Vertical">
<FluentSwitch Class="table-switch"
@bind-Value="@OnlyShowValueChangesInTable"
@bind-Value:after="SettingsChangedAsync"
Label="@Loc[nameof(ControlsStrings.MetricTableShowOnlyValueChanges)]"/>
Label="@Loc[nameof(ControlsStrings.MetricTableShowOnlyValueChanges)]" />
</FluentStack>
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
@using Aspire.Dashboard.Resources
@namespace Aspire.Dashboard.Components
@namespace Aspire.Dashboard.Components

@inherits Aspire.Dashboard.Components.Controls.Chart.ChartBase;

<div id="@ChartDivId" class="plotly-chart-container" style="width:650px; height:450px;"></div>
<div id="@ChartDivId" class="plotly-chart-container"></div>
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Globalization;
using System.Web;
using Aspire.Dashboard.Components.Controls.Chart;
using Aspire.Dashboard.Components.Resize;
using Aspire.Dashboard.Extensions;
using Aspire.Dashboard.Model;
using Aspire.Dashboard.Model.Otlp;
Expand Down Expand Up @@ -32,6 +33,9 @@ public partial class PlotlyChart : ChartBase

public string ChartDivId { get; } = $"plotly-chart-container-{Interlocked.Increment(ref s_nextChartId)}";

[CascadingParameter]
public required ViewportInformation ViewportInformation { get; init; }

private DotNetObjectReference<ChartInterop>? _chartInteropReference;
private IJSObjectReference? _jsModule;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,39 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.AspNetCore.Components;
using Microsoft.FluentUI.AspNetCore.Components;

namespace Aspire.Dashboard.Components.Controls.Grid;

public class AspirePropertyColumn<TGridItem, TProp> : PropertyColumn<TGridItem, TProp>
public class AspirePropertyColumn<TGridItem, TProp> : PropertyColumn<TGridItem, TProp>, IAspireColumn
{
[Parameter]
public GridColumnManager? ColumnManager { get; set; }

[Parameter]
public string? ColumnId { get; set; }

[Parameter]
public bool UseCustomHeaderTemplate { get; set; } = true;

protected override void OnParametersSet()
{
HeaderCellItemTemplate = AspireFluentDataGridHeaderCell.RenderHeaderContent(Grid);
base.OnParametersSet();

if (UseCustomHeaderTemplate)
{
HeaderCellItemTemplate = AspireFluentDataGridHeaderCell.RenderHeaderContent(Grid);
}
}

protected override bool ShouldRender()
{
if (ColumnManager is not null && ColumnId is not null && !ColumnManager.IsColumnVisible(ColumnId))
{
return false;
}

return base.ShouldRender();
}
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,39 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.AspNetCore.Components;
using Microsoft.FluentUI.AspNetCore.Components;

namespace Aspire.Dashboard.Components.Controls.Grid;

public class AspireTemplateColumn<TGridItem> : TemplateColumn<TGridItem>
public class AspireTemplateColumn<TGridItem> : TemplateColumn<TGridItem>, IAspireColumn
{
[Parameter]
public GridColumnManager? ColumnManager { get; set; }

[Parameter]
public string? ColumnId { get; set; }

[Parameter]
public bool UseCustomHeaderTemplate { get; set; } = true;

protected override void OnParametersSet()
{
HeaderCellItemTemplate = AspireFluentDataGridHeaderCell.RenderHeaderContent(Grid);
base.OnParametersSet();

if (UseCustomHeaderTemplate)
{
HeaderCellItemTemplate = AspireFluentDataGridHeaderCell.RenderHeaderContent(Grid);
}
}

protected override bool ShouldRender()
{
if (ColumnManager is not null && ColumnId is not null && !ColumnManager.IsColumnVisible(ColumnId))
{
return false;
}

return base.ShouldRender();
}
}
13 changes: 13 additions & 0 deletions src/Aspire.Dashboard/Components/Controls/Grid/IAspireColumn.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace Aspire.Dashboard.Components.Controls.Grid;

internal interface IAspireColumn
{
public GridColumnManager? ColumnManager { get; set; }

public string? ColumnId { get; set; }

public bool UseCustomHeaderTemplate { get; set; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ public partial class GridValue
public required IJSRuntime JS { get; init; }

[CascadingParameter]
public required ViewportInformation ViewportInformation { get; init; }
public required ViewportInformation ViewportInformation { get; set; }

private readonly Icon _maskIcon = new Icons.Regular.Size16.EyeOff();
private readonly Icon _unmaskIcon = new Icons.Regular.Size16.Eye();
Expand Down
23 changes: 23 additions & 0 deletions src/Aspire.Dashboard/Components/Controls/TreeMetricSelector.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
@using Aspire.Dashboard.Components.Pages

<FluentTreeView Id="metric-selector" Class="metrics-tree" @bind-CurrentSelected="@PageViewModel.SelectedTreeItem" @bind-CurrentSelected:after="HandleSelectedTreeItemChangedAsync">
<ChildContent>
@foreach (var meterGroup in PageViewModel.Instruments!.GroupBy(i => i.Parent).OrderBy(g => g.Key.MeterName))
{
<FluentTreeItem @key="meterGroup.Key" Text="@meterGroup.Key.MeterName" Data="@meterGroup.Key" title="@meterGroup.Key.MeterName" InitiallyExpanded="true" InitiallySelected="@(PageViewModel.SelectedInstrument == null && meterGroup.Key.MeterName == PageViewModel.SelectedMeter?.MeterName)">
@foreach (var instrument in meterGroup.OrderBy(i => i.Name))
{
<FluentTreeItem @key="instrument" Class="nested" Text="@instrument.Name" Data="@instrument" title="@instrument.Name" InitiallySelected="@(instrument.Name == PageViewModel.SelectedInstrument?.Name && instrument.Parent.MeterName == PageViewModel.SelectedMeter?.MeterName)"/>
}
</FluentTreeItem>
}
</ChildContent>
</FluentTreeView>

@code {
[Parameter, EditorRequired]
public required Func<Task> HandleSelectedTreeItemChangedAsync { get; set; }

[Parameter, EditorRequired]
public required Metrics.MetricsViewModel PageViewModel { get; set; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<!-- Toolbar -->
@if (ViewportInformation.IsDesktop)
{
<header style="grid-area: page-header; @HeaderStyle" aria-label="@ControlsStringsLoc[nameof(ControlsStrings.PageToolbarLandmark)]">
<header class="content-header" style="@HeaderStyle" aria-label="@ControlsStringsLoc[nameof(ControlsStrings.PageToolbarLandmark)]">
@if (AddNewlineOnToolbar)
{
@PageTitleSection
Expand All @@ -27,7 +27,7 @@
}
else if (!IsSummaryDetailsViewOpen)
{
<header style="grid-area: page-header">
<header class="content-header">
<FluentToolbar Orientation="Orientation.Horizontal" Style="@HeaderStyle">
@PageTitleSection

Expand All @@ -39,7 +39,7 @@

@if (AddNewlineOnToolbar)
{
<div style="padding: calc(var(--design-unit) * 2.5px)">@MobilePageTitleToolbarSection</div>
<div style="padding-left: calc(var(--design-unit) * 2.5px)">@MobilePageTitleToolbarSection</div>
}
</header>
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ private string GetMobileMainStyle()
return style;
}

private async Task OpenMobileToolbarAsync()
public async Task OpenMobileToolbarAsync()
{
_toolbarPanel = await DialogService.ShowPanelAsync<ToolbarPanel>(
new MobileToolbar(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,15 @@
grid-template-rows: auto 1fr auto;
}

::deep.content-header {
/*
Long titles inside a header don't add an ellipsis when trimmed.
Improve here could be to add that feature
*/
grid-area: page-header;
white-space: nowrap;
}

::deep .title-toolbar-inline {
padding-left: 0px;
padding-top: 0px;
Expand Down
3 changes: 2 additions & 1 deletion src/Aspire.Dashboard/Components/Pages/ConsoleLogs.razor
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
<AspirePageContentLayout
AddNewlineOnToolbar="true"
@ref="@_contentLayout"
MainContentStyle="margin-top: 10px;">
MainContentStyle="margin-top: 10px;"
MobileToolbarButtonText="@Loc[nameof(Dashboard.Resources.ConsoleLogs.ConsoleLogsSelectResourceToolbar)]">
<PageTitleSection>
<h1 class="page-header">@Loc[nameof(Dashboard.Resources.ConsoleLogs.ConsoleLogsHeader)]</h1>
</PageTitleSection>
Expand Down
75 changes: 53 additions & 22 deletions src/Aspire.Dashboard/Components/Pages/Metrics.razor
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@

@using Aspire.Dashboard.Components.Controls.Grid
@using Aspire.Dashboard.Model.Otlp
@using Aspire.Dashboard.Resources
@using Aspire.Dashboard.Otlp.Model
@using Aspire.Dashboard.Resources
@using Aspire.Dashboard.Utils
@inject IStringLocalizer<Dashboard.Resources.Metrics> Loc
@inject IStringLocalizer<ControlsStrings> ControlsStringsLoc
Expand All @@ -17,7 +17,8 @@
<AspirePageContentLayout
AddNewlineOnToolbar="true"
@ref="_contentLayout"
HeaderStyle="margin-bottom: calc(var(--design-unit) * 2px);">
HeaderStyle="margin-bottom: calc(var(--design-unit) * 2px);"
MobileToolbarButtonText="@Loc[nameof(Dashboard.Resources.Metrics.MetricsViewAttributesToolbar)]">
<PageTitleSection>
<h1 class="page-header">@Loc[nameof(Dashboard.Resources.Metrics.MetricsHeader)]</h1>
</PageTitleSection>
Expand All @@ -27,33 +28,44 @@
@bind-SelectedResource="PageViewModel.SelectedApplication"
@bind-SelectedResource:after="HandleSelectedApplicationChangedAsync"
CanSelectGrouping="true" />
<FluentIcon slot="end" Icon="Icons.Regular.Size20.Clock" Style="margin-right:5px;"/>
@if (!ViewportInformation.IsDesktop)
{
<FluentIcon slot="end" Icon="Icons.Regular.Size20.Clock" Style="margin-right:5px;"/>
}

<FluentSelect slot="end" TOption="SelectViewModel<TimeSpan>"
Items="@_durations"
OptionText="@(c => c.Name)"
@bind-SelectedOption="PageViewModel.SelectedDuration"
@bind-SelectedOption:after="HandleSelectedDurationChangedAsync"
AriaLabel="@Loc[nameof(Dashboard.Resources.Metrics.MetricsSelectADuration)]"/>

@if (!ViewportInformation.IsDesktop && PageViewModel.Instruments?.Count > 0)
{
// Show metric selector in the toolbar for mobile, since we don't have enough room to show both
// panels on a mobile viewport.
<FluentInputLabel Label="@Loc[Dashboard.Resources.Metrics.MetricsInsturementNameGridNameColumnHeader]" ForId="metric-selector" />
<TreeMetricSelector
PageViewModel="@PageViewModel"
HandleSelectedTreeItemChangedAsync="@HandleSelectedTreeItemChangedAsync"/>
}
</ToolbarSection>
<MainSection>
<div style="width: 100%; height: 100%; overflow: auto;">
@if (PageViewModel.Instruments?.Count > 0)
{
<FluentSplitter Style="height:100%" Panel1Size="2fr" Panel2Size="8fr" BarSize="5">
// Collapsed property only allows us to show Panel1 (which is the metric selector)
// but we want to only show Panel2 on mobile. So, hide the bar handle and reduce width to 0
// on mobile.
<FluentSplitter BarHandle="@ViewportInformation.IsDesktop"
Style="height:100%"
Panel1Size="@(ViewportInformation.IsDesktop ? "2fr" : "0")"
Panel2Size="8fr"
BarSize="@(ViewportInformation.IsDesktop ? 5 : 0)">
<Panel1>
<FluentTreeView Class="metrics-tree" @bind-CurrentSelected="PageViewModel.SelectedTreeItem" @bind-CurrentSelected:after="HandleSelectedTreeItemChangedAsync">
<ChildContent>
@foreach (var meterGroup in PageViewModel.Instruments.GroupBy(i => i.Parent).OrderBy(g => g.Key.MeterName))
{
<FluentTreeItem @key="meterGroup.Key" Text="@meterGroup.Key.MeterName" Data="@meterGroup.Key" title="@meterGroup.Key.MeterName" InitiallyExpanded="true" InitiallySelected="@(PageViewModel.SelectedInstrument == null && meterGroup.Key.MeterName == PageViewModel.SelectedMeter?.MeterName)">
@foreach (var instrument in meterGroup.OrderBy(i => i.Name))
{
<FluentTreeItem @key="instrument" Class="nested" Text="@instrument.Name" Data="@instrument" title="@instrument.Name" InitiallySelected="@(instrument.Name == PageViewModel.SelectedInstrument?.Name && instrument.Parent.MeterName == PageViewModel.SelectedMeter?.MeterName)"/>
}
</FluentTreeItem>
}
</ChildContent>
</FluentTreeView>
<TreeMetricSelector
PageViewModel="@PageViewModel"
HandleSelectedTreeItemChangedAsync="@HandleSelectedTreeItemChangedAsync" />
</Panel1>
<Panel2>
<div>
Expand All @@ -65,13 +77,13 @@
MeterName="@(PageViewModel.SelectedMeter.MeterName)"
InstrumentName="@(PageViewModel.SelectedInstrument.Name)"
Duration="PageViewModel.SelectedDuration.Id"
ActiveView="@(PageViewModel.SelectedViewKind ?? MetricViewKind.Graph)"
ActiveView="@(PageViewModel.SelectedViewKind ?? Metrics.MetricViewKind.Graph)"
OnViewChangedAsync="@OnViewChangedAsync"
Applications="_applications" />
Applications="@_applications" />
}
else if (PageViewModel.SelectedMeter != null)
{
<h3>@PageViewModel.SelectedMeter.MeterName</h3>
<h3 class="meter-name-title">@PageViewModel.SelectedMeter.MeterName</h3>
<FluentDataGrid
ResizeLabel="@AspireFluentDataGridHeaderCell.GetResizeLabel(ControlsStringsLoc)"
ResizeType="DataGridResizeType.Discrete"
Expand All @@ -91,7 +103,17 @@
}
else
{
<p>@Loc[nameof(Dashboard.Resources.Metrics.MetricsSelectInstrument)]</p>
@if (ViewportInformation.IsDesktop)
{
<p>@Loc[nameof(Dashboard.Resources.Metrics.MetricsSelectInstrument)]</p>
}
else
{
// class needed to prevent color from being removed from reboot style
<a class="" @onclick="@(async () => await _contentLayout!.OpenMobileToolbarAsync())">
@Loc[nameof(Dashboard.Resources.Metrics.MetricsSelectInstrument)]
</a>
}
}
</div>
</div>
Expand All @@ -101,7 +123,16 @@
else if (PageViewModel.Instruments == null)
{
<div class="empty-content">
<FluentIcon Icon="Icons.Regular.Size24.ChartMultiple"/>&nbsp;@Loc[nameof(Dashboard.Resources.Metrics.MetricsSelectAResource)]
@if (ViewportInformation.IsDesktop)
{
<FluentIcon Icon="Icons.Regular.Size24.ChartMultiple"/>@:&nbsp;
@Loc[nameof(Dashboard.Resources.Metrics.MetricsSelectAResource)]
}
else
{
<FluentIcon Icon="Icons.Regular.Size24.ChartMultiple"/>@:&nbsp;
<a class="" @onclick="@(async () => await _contentLayout!.OpenMobileToolbarAsync())">@Loc[nameof(Dashboard.Resources.Metrics.MetricsSelectAResource)]</a>
}
</div>
}
else
Expand Down
Loading

0 comments on commit 31ac767

Please sign in to comment.