Skip to content

Commit 3418372

Browse files
committed
Merge in 'release/8.0' changes
2 parents a6191d5 + 303c6f9 commit 3418372

15 files changed

Lines changed: 167 additions & 30 deletions

File tree

src/Components/QuickGrid/Microsoft.AspNetCore.Components.QuickGrid/src/Columns/ColumnBase.razor

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
{
2020
@if (ColumnOptions is not null && (Align != Align.Right && Align != Align.End))
2121
{
22-
<button class="col-options-button" type="button" @onclick="@(() => Grid.ShowColumnOptionsAsync(this))"></button>
22+
<button class="col-options-button" type="button" title="Column options" @onclick="@(() => Grid.ShowColumnOptionsAsync(this))"></button>
2323
}
2424

2525
if (Sortable.HasValue ? Sortable.Value : IsSortableByDefault())
@@ -38,7 +38,7 @@
3838

3939
@if (ColumnOptions is not null && (Align == Align.Right || Align == Align.End))
4040
{
41-
<button class="col-options-button" type="button" @onclick="@(() => Grid.ShowColumnOptionsAsync(this))"></button>
41+
<button class="col-options-button" type="button" title="Column options" @onclick="@(() => Grid.ShowColumnOptionsAsync(this))"></button>
4242
}
4343
}
4444
}

src/Components/Server/src/Circuits/RemoteJSRuntime.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ internal partial class RemoteJSRuntime : JSRuntime
3030

3131
public bool IsInitialized => _clientProxy is not null;
3232

33+
internal bool IsPermanentlyDisconnected => _permanentlyDisconnected;
34+
3335
/// <summary>
3436
/// Notifies when a runtime exception occurred.
3537
/// </summary>

src/Components/Server/src/Circuits/RemoteNavigationManager.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,12 @@ async Task PerformNavigationAsync()
106106
}
107107

108108
await _jsRuntime.InvokeVoidAsync(Interop.NavigateTo, uri, options);
109+
Log.NavigationCompleted(_logger, uri);
110+
}
111+
catch (TaskCanceledException)
112+
when (_jsRuntime is RemoteJSRuntime remoteRuntime && remoteRuntime.IsPermanentlyDisconnected)
113+
{
114+
Log.NavigationStoppedSessionEnded(_logger, uri);
109115
}
110116
catch (Exception ex)
111117
{
@@ -190,5 +196,11 @@ public static void RequestingNavigation(ILogger logger, string uri, NavigationOp
190196

191197
[LoggerMessage(5, LogLevel.Error, "Failed to refresh", EventName = "RefreshFailed")]
192198
public static partial void RefreshFailed(ILogger logger, Exception exception);
199+
200+
[LoggerMessage(6, LogLevel.Debug, "Navigation completed when changing the location to {Uri}", EventName = "NavigationCompleted")]
201+
public static partial void NavigationCompleted(ILogger logger, string uri);
202+
203+
[LoggerMessage(7, LogLevel.Debug, "Navigation stopped because the session ended when navigating to {Uri}", EventName = "NavigationStoppedSessionEnded")]
204+
public static partial void NavigationStoppedSessionEnded(ILogger logger, string uri);
193205
}
194206
}

src/Components/Web.JS/dist/Release/blazor.web.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Components/Web.JS/src/Rendering/StreamingRendering.ts

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -53,19 +53,24 @@ class BlazorStreamingUpdate extends HTMLElement {
5353
const isFormPost = node.getAttribute('from') === 'form-post';
5454
const isEnhancedNav = node.getAttribute('enhanced') === 'true';
5555
if (isEnhancedNav && isWithinBaseUriSpace(destinationUrl)) {
56+
// At this point the destinationUrl might be an opaque URL so we don't know whether it's internal/external or
57+
// whether it's even going to the same URL we're currently on. So we don't know how to update the history.
58+
// Defer that until the redirection is resolved by performEnhancedPageLoad.
59+
const treatAsRedirectionFromMethod = isFormPost ? 'post' : 'get';
60+
const fetchOptions = undefined;
61+
performEnhancedPageLoad(destinationUrl, /* interceptedLink */ false, fetchOptions, treatAsRedirectionFromMethod);
62+
} else {
5663
if (isFormPost) {
5764
// The URL is not yet updated. Push a whole new entry so that 'back' goes back to the pre-redirection location.
58-
history.pushState(null, '', destinationUrl);
65+
// WARNING: The following check to avoid duplicating history entries won't work if the redirection is to an opaque URL.
66+
// We could change the server-side logic to return URLs in plaintext if they match the current request URL already,
67+
// but it's arguably easier to understand that history non-duplication only works for enhanced nav, which is also the
68+
// case for non-streaming responses.
69+
if (destinationUrl !== location.href) {
70+
location.assign(destinationUrl);
71+
}
5972
} else {
6073
// The URL was already updated on the original link click. Replace so that 'back' goes to the pre-redirection location.
61-
history.replaceState(null, '', destinationUrl);
62-
}
63-
performEnhancedPageLoad(destinationUrl, /* interceptedLink */ false);
64-
} else {
65-
// Same reason for varying as above
66-
if (isFormPost) {
67-
location.assign(destinationUrl);
68-
} else {
6974
location.replace(destinationUrl);
7075
}
7176
}

src/Components/Web.JS/src/Services/NavigationEnhancement.ts

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -132,10 +132,10 @@ function onDocumentSubmit(event: SubmitEvent) {
132132

133133
event.preventDefault();
134134

135-
const url = new URL(event.submitter?.getAttribute('formaction') || formElem.action, document.baseURI);
136-
const fetchOptions: RequestInit = { method: method};
135+
const url = new URL(event.submitter?.getAttribute('formaction') || formElem.action, document.baseURI);
136+
const fetchOptions: RequestInit = { method: method};
137137
const formData = new FormData(formElem);
138-
138+
139139
const submitterName = event.submitter?.getAttribute('name');
140140
const submitterValue = event.submitter!.getAttribute('value');
141141
if (submitterName && submitterValue) {
@@ -153,8 +153,8 @@ function onDocumentSubmit(event: SubmitEvent) {
153153
} else {
154154
// Setting request body and content-type header depending on enctype
155155
const enctype = event.submitter?.getAttribute('formenctype') || formElem.enctype;
156-
if (enctype === 'multipart/form-data') {
157-
// Content-Type header will be set to 'multipart/form-data'
156+
if (enctype === 'multipart/form-data') {
157+
// Content-Type header will be set to 'multipart/form-data'
158158
fetchOptions.body = formData;
159159
} else {
160160
fetchOptions.body = urlSearchParams;
@@ -170,7 +170,7 @@ function onDocumentSubmit(event: SubmitEvent) {
170170
}
171171
}
172172

173-
export async function performEnhancedPageLoad(internalDestinationHref: string, interceptedLink: boolean, fetchOptions?: RequestInit) {
173+
export async function performEnhancedPageLoad(internalDestinationHref: string, interceptedLink: boolean, fetchOptions?: RequestInit, treatAsRedirectionFromMethod?: 'get' | 'post') {
174174
performingEnhancedPageLoad = true;
175175

176176
// First, stop any preceding enhanced page load
@@ -232,8 +232,9 @@ export async function performEnhancedPageLoad(internalDestinationHref: string, i
232232
// For 301/302/etc redirections to internal URLs, the browser will already have followed the chain of redirections
233233
// to the end, and given us the final content. We do still need to update the current URL to match the final location,
234234
// then let the rest of enhanced nav logic run to patch the new content into the DOM.
235-
if (response.redirected) {
236-
if (isGetRequest) {
235+
if (response.redirected || treatAsRedirectionFromMethod) {
236+
const treatAsGet = treatAsRedirectionFromMethod ? (treatAsRedirectionFromMethod === 'get') : isGetRequest;
237+
if (treatAsGet) {
237238
// For gets, the intermediate (redirecting) URL is already in the address bar, so we have to use 'replace'
238239
// so that 'back' would go to the page before the redirection
239240
history.replaceState(null, '', response.url);
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
namespace Microsoft.AspNetCore.Components;
5+
6+
internal interface IInputRadioValueProvider
7+
{
8+
public object? CurrentValue { get; }
9+
}

src/Components/Web/src/Forms/InputRadioContext.cs

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,21 +8,19 @@ namespace Microsoft.AspNetCore.Components.Forms;
88
/// </summary>
99
internal sealed class InputRadioContext
1010
{
11+
private readonly IInputRadioValueProvider _valueProvider;
12+
1113
public InputRadioContext? ParentContext { get; }
1214
public EventCallback<ChangeEventArgs> ChangeEventCallback { get; }
15+
public object? CurrentValue => _valueProvider.CurrentValue;
1316

1417
// Mutable properties that may change any time an InputRadioGroup is rendered
1518
public string? GroupName { get; set; }
16-
public object? CurrentValue { get; set; }
1719
public string? FieldClass { get; set; }
1820

19-
/// <summary>
20-
/// Instantiates a new <see cref="InputRadioContext" />.
21-
/// </summary>
22-
/// <param name="parentContext">The parent context, if any.</param>
23-
/// <param name="changeEventCallback">The event callback to be invoked when the selected value is changed.</param>
24-
public InputRadioContext(InputRadioContext? parentContext, EventCallback<ChangeEventArgs> changeEventCallback)
21+
public InputRadioContext(IInputRadioValueProvider valueProvider, InputRadioContext? parentContext, EventCallback<ChangeEventArgs> changeEventCallback)
2522
{
23+
_valueProvider = valueProvider;
2624
ParentContext = parentContext;
2725
ChangeEventCallback = changeEventCallback;
2826
}

src/Components/Web/src/Forms/InputRadioGroup.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ namespace Microsoft.AspNetCore.Components.Forms;
1010
/// <summary>
1111
/// Groups child <see cref="InputRadio{TValue}"/> components.
1212
/// </summary>
13-
public class InputRadioGroup<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] TValue> : InputBase<TValue>
13+
public class InputRadioGroup<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] TValue> : InputBase<TValue>, IInputRadioValueProvider
1414
{
1515
private readonly string _defaultGroupName = Guid.NewGuid().ToString("N");
1616
private InputRadioContext? _context;
@@ -27,14 +27,16 @@ public class InputRadioGroup<[DynamicallyAccessedMembers(DynamicallyAccessedMemb
2727

2828
[CascadingParameter] private InputRadioContext? CascadedContext { get; set; }
2929

30+
object? IInputRadioValueProvider.CurrentValue => CurrentValue;
31+
3032
/// <inheritdoc />
3133
protected override void OnParametersSet()
3234
{
3335
// On the first render, we can instantiate the InputRadioContext
3436
if (_context is null)
3537
{
3638
var changeEventCallback = EventCallback.Factory.CreateBinder<string?>(this, __value => CurrentValueAsString = __value, CurrentValueAsString);
37-
_context = new InputRadioContext(CascadedContext, changeEventCallback);
39+
_context = new InputRadioContext(this, CascadedContext, changeEventCallback);
3840
}
3941
else if (_context.ParentContext != CascadedContext)
4042
{
@@ -59,7 +61,7 @@ protected override void OnParametersSet()
5961
// Otherwise, just use a GUID to disambiguate this group's radio inputs from any others on the page.
6062
_context.GroupName = _defaultGroupName;
6163
}
62-
_context.CurrentValue = CurrentValue;
64+
6365
_context.FieldClass = EditContext?.FieldCssClass(FieldIdentifier);
6466
}
6567

src/Components/WebView/WebView/src/Microsoft.AspNetCore.Components.WebView.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@
3535

3636
<ItemGroup>
3737
<None Include="buildTransitive\Microsoft.AspNetCore.Components.WebView.props" Pack="true" PackagePath="%(Identity)" />
38+
<None Include="buildMultiTargeting\Microsoft.AspNetCore.Components.WebView.props" Pack="true" PackagePath="%(Identity)" />
39+
<None Include="build\Microsoft.AspNetCore.Components.WebView.props" Pack="true" PackagePath="%(Identity)" />
3840
</ItemGroup>
3941

4042
<ItemGroup>

0 commit comments

Comments
 (0)