Skip to content

Commit 7ff462f

Browse files
committed
Implemented INavigationHistoryService
1 parent 1dd2573 commit 7ff462f

File tree

13 files changed

+277
-9
lines changed

13 files changed

+277
-9
lines changed

.github/docs/JsInterop.md

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ For code examples [see usage](https://github.com/majorimi/blazor-components/blob
1515
You can try it out by using the [demo app](https://blazorextensions.z6.web.core.windows.net/jsinterop).
1616

1717
# Features
18-
1918
- **Click JS**:
2019
- `ClickBoundariesElement` is a component which wraps the given content to a DIV and subscribes to all click events: `OnOutsideClick`, `OnInsideClick`.
2120
- Also an **injectable `IClickBoundariesHandler` service** for callback event handlers.
@@ -31,8 +30,9 @@ You can try it out by using the [demo app](https://blazorextensions.z6.web.core.
3130
- **Language JS**: is an **injectable `ILanguageService` service** for detect the browser language preference.
3231
- **Browser Date JS**: is an **injectable `IBrowserDateService` service** is a simple JS call to `new Date();` to retrieve client machine date and time.
3332
- **Browser Theme JS**: is an **injectable `IBrowserThemeService` service** to handle Browser color scheme queries and changes.
34-
- **Geo JS**: is an **injectable `IGeolocationService` service** for detect the device Geolocation (GPS position, speed, heading, etc.).
33+
- **Geo JS**: is an **injectable `IGeolocationService` service** for detect the device [Geolocation API](https://developer.mozilla.org/en-US/docs/Web/API/Geolocation_API) (GPS position, speed, heading, etc.).
3534
- **Head JS**: is an **injectable `IHtmlHeadService` service** for accessing and setting HTML document `Head tags`.
35+
- **Browser History JS**: is an **injectable `INavigationHistoryService` service** to access [HTML History API](https://developer.mozilla.org/en-US/docs/Web/API/History) functionality.
3636

3737
## Click JS (See: [demo app](https://blazorextensions.z6.web.core.windows.net/jsinterop#click-js))
3838
**NOTE: Blazor supports `@onclick` event which is equivalent with `OnInsideClick`.
@@ -290,7 +290,6 @@ Removes event listener for `prefers-color-scheme` HTML event for the Browser.
290290
- **`DisposeAsync`: `ValueTask IAsyncDisposable()` interface** <br />
291291
Implements `IAsyncDisposable` interface the injected service should be Disposed.
292292

293-
294293
## Geolocation JS (See: [demo app](https://blazorextensions.z6.web.core.windows.net/jsinterop#geo-js))
295294
**Geolocation JS** is an injectable `IGeolocationService` service for **detect the device Geolocation (GPS position, speed, heading, etc.)**.
296295
It is using the Geolocation API which allows users to provide their location to web applications if they desire.
@@ -328,6 +327,26 @@ If you have multiple fav icon tags set in the Head first call `GetHtmlFavIconsAs
328327
- **`DisposeAsync`: `ValueTask IAsyncDisposable()` interface** <br />
329328
Implements `IAsyncDisposable` interface the injected service should be Disposed.
330329

330+
## Browser History JS (See: [demo app](https://blazorextensions.z6.web.core.windows.net/jsinterop#history-js))
331+
**Browser History JS** is an injectable `INavigationHistoryService` service** to access HTML History API functionality.
332+
It is useful when don't want to rely on Blazor `NavigationManager` which does not have access to full History list and when it navigates trigger a page load/update.
333+
334+
### Functions
335+
- **`GetLengthAsync`**: **`ValueTask<int> GetLengthAsync()`** <br />
336+
Returns an Integer representing the number of elements in the session history, including the currently loaded page.
337+
- **`GetScrollRestorationAsync`**: **`ValueTask<string> GetScrollRestorationAsync()`** <br />
338+
Allows web applications to explicitly set default scroll restoration behavior on history navigation. This property can be either `auto` or `manual`.
339+
- **`BackAsync`**: **`ValueTask BackAsync()`** <br />
340+
This asynchronous method goes to the previous page in session history, the same action as when the user clicks the browser's Back button. Equivalent to history.go(-1).
341+
- **`ForwardAsync`**: **`ValueTask ForwardAsync()`** <br />
342+
This asynchronous method goes to the next page in session history, the same action as when the user clicks the browser's Forward button; this is equivalent to history.go(1).
343+
- **`GoAsync`**: **`ValueTask GoAsync(int delta)`** <br />
344+
Asynchronously loads a page from the session history, identified by its relative location to the current page, for example -1 for the previous page or 1 for the next page.
345+
- **`ReplaceStateAsync`**: **`ValueTask ReplaceStateAsync(ExpandoObject? state, string title, string url)`** <br />
346+
Updates the most recent entry on the history stack to have the specified data, title, and, if provided, URL.
347+
- **`PushStateAsync`**: **`ValueTask PushStateAsync(ExpandoObject? state, string title, string url)`** <br />
348+
Pushes the given data onto the session history stack with the specified title (and, if provided, URL).
349+
331350

332351
# Configuration
333352

@@ -366,6 +385,8 @@ Add using statement to your Blazor <component/page>.razor file. Or globally refe
366385
@using Majorsoft.Blazor.Components.Common.JsInterop.BrowserDate
367386
@*Only if you want to use Browser ColorTheme*@
368387
@using Majorsoft.Blazor.Components.Common.JsInterop.BrowserColorTheme
388+
@*Only if you want to use Browser History*@
389+
@using Majorsoft.Blazor.Components.Common.JsInterop.History
369390
```
370391

371392

src/Majorsoft.Blazor.Components.Common.JsInterop/JsInteropExtension.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
using Majorsoft.Blazor.Components.Common.JsInterop.GlobalMouseEvents;
1010
using Majorsoft.Blazor.Components.Common.JsInterop.Head;
1111
using Majorsoft.Blazor.Components.Common.JsInterop.Language;
12+
using Majorsoft.Blazor.Components.Common.JsInterop.Navigation;
1213
using Majorsoft.Blazor.Components.Common.JsInterop.Resize;
1314
using Majorsoft.Blazor.Components.Common.JsInterop.Scroll;
1415

@@ -44,6 +45,7 @@ public static IServiceCollection AddJsInteropExtensions(this IServiceCollection
4445
services.AddTransient<IHtmlHeadService, HtmlHeadService>();
4546
services.AddTransient<IBrowserDateService, BrowserDateService>();
4647
services.AddTransient<IBrowserThemeService, BrowserThemeService>();
48+
services.AddTransient<INavigationHistoryService, NavigationHistoryService>();
4749

4850
return services;
4951
}

src/Majorsoft.Blazor.Components.Common.JsInterop/Majorsoft.Blazor.Components.Common.JsInterop.xml

Lines changed: 60 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
using System.Dynamic;
2+
using System.Threading.Tasks;
3+
4+
namespace Majorsoft.Blazor.Components.Common.JsInterop.Navigation
5+
{
6+
/// <summary>
7+
/// Injectable service to handle Browser history JS Interops.
8+
/// https://developer.mozilla.org/en-US/docs/Web/API/History
9+
/// </summary>
10+
public interface INavigationHistoryService
11+
{
12+
/// <summary>
13+
/// Returns an Integer representing the number of elements in the session history, including the currently loaded page. For example, for a page loaded in a new tab this property returns 1.
14+
/// </summary>
15+
/// <returns>ValueTask</returns>
16+
ValueTask<int> GetLengthAsync();
17+
18+
/// <summary>
19+
/// Allows web applications to explicitly set default scroll restoration behavior on history navigation. This property can be either `auto` or `manual`.
20+
/// </summary>
21+
/// <returns>ValueTask</returns>
22+
ValueTask<string> GetScrollRestorationAsync();
23+
24+
/// <summary>
25+
/// This asynchronous method goes to the previous page in session history, the same action as when the user clicks the browser's Back button. Equivalent to history.go(-1).
26+
/// </summary>
27+
/// <returns>ValueTask</returns>
28+
ValueTask BackAsync();
29+
30+
/// <summary>
31+
/// This asynchronous method goes to the next page in session history, the same action as when the user clicks the browser's Forward button; this is equivalent to history.go(1).
32+
/// </summary>
33+
/// <returns>ValueTask</returns>
34+
ValueTask ForwardAsync();
35+
36+
/// <summary>
37+
/// Asynchronously loads a page from the session history, identified by its relative location to the current page, for example -1 for the previous page or 1 for the next page.
38+
/// </summary>
39+
/// <param name="delta">The position in the history to which you want to move, relative to the current page. A negative value moves backwards, a positive value moves forwards.</param>
40+
/// <returns>ValueTask</returns>
41+
ValueTask GoAsync(int delta);
42+
43+
/// <summary>
44+
/// Updates the most recent entry on the history stack to have the specified data, title, and, if provided, URL.
45+
/// </summary>
46+
/// <param name="state">Arbitrary object of the page</param>
47+
/// <param name="title">Page tile</param>
48+
/// <param name="url">New URL to show and add to history</param>
49+
/// <returns>ValueTask</returns>
50+
ValueTask ReplaceStateAsync(ExpandoObject? state, string title, string url);
51+
52+
/// <summary>
53+
/// Pushes the given data onto the session history stack with the specified title (and, if provided, URL).
54+
/// </summary>
55+
/// <param name="state">Arbitrary object of the page</param>
56+
/// <param name="title">Page tile</param>
57+
/// <param name="url">New URL to show and add to history</param>
58+
/// <returns>ValueTask</returns>
59+
ValueTask PushStateAsync(ExpandoObject? state, string title, string url);
60+
}
61+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
using System.Dynamic;
2+
using System.Threading.Tasks;
3+
4+
using Microsoft.JSInterop;
5+
6+
namespace Majorsoft.Blazor.Components.Common.JsInterop.Navigation
7+
{
8+
/// <summary>
9+
/// Implementation of <see cref="INavigationHistoryService"/>
10+
/// </summary>
11+
public class NavigationHistoryService : INavigationHistoryService
12+
{
13+
private readonly IJSRuntime _jSRuntime;
14+
15+
public NavigationHistoryService(IJSRuntime jSRuntime)
16+
{
17+
_jSRuntime = jSRuntime;
18+
}
19+
20+
public async ValueTask<int> GetLengthAsync() => await _jSRuntime.InvokeAsync<int>("eval", "history.length");
21+
public async ValueTask<string> GetScrollRestorationAsync() => await _jSRuntime.InvokeAsync<string>("eval", "history.scrollRestoration");
22+
23+
public async ValueTask BackAsync()
24+
=> await _jSRuntime.InvokeVoidAsync("history.back");
25+
26+
public async ValueTask ForwardAsync()
27+
=> await _jSRuntime.InvokeVoidAsync("history.forward");
28+
29+
public async ValueTask GoAsync(int delta)
30+
=> await _jSRuntime.InvokeVoidAsync("history.go", delta);
31+
32+
public async ValueTask PushStateAsync(ExpandoObject? state, string title, string url)
33+
=> await _jSRuntime.InvokeVoidAsync("history.pushState", state, title, url);
34+
35+
public async ValueTask ReplaceStateAsync(ExpandoObject? state, string title, string url)
36+
=> await _jSRuntime.InvokeVoidAsync("history.replaceState", state, title, url);
37+
}
38+
}

src/Majorsoft.Blazor.Components.PermaLink.Tests/PermaLinkBlazorServerInitializerTest.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
using Bunit;
44

5+
using Majorsoft.Blazor.Components.Common.JsInterop.Navigation;
56
using Majorsoft.Blazor.Components.Common.JsInterop.Scroll;
67
using Majorsoft.Blazor.Components.CommonTestsBase;
78

@@ -18,17 +19,20 @@ public class PermaLinkBlazorServerInitializerTest : ComponentsTestBase<PermaLink
1819
{
1920
private Mock<IPermaLinkWatcherService> _permaLinkWatcherServiceMock;
2021
private Mock<IScrollHandler> _scrollHandlerMock;
22+
private Mock<INavigationHistoryService> _navigationHistoryServiceMock;
2123

2224
[TestInitialize]
2325
public void Init()
2426
{
2527
var logger = new Mock<ILogger<IPermaLinkWatcherService>>();
2628
_permaLinkWatcherServiceMock = new Mock<IPermaLinkWatcherService>();
2729
_scrollHandlerMock = new Mock<IScrollHandler>();
30+
_navigationHistoryServiceMock = new Mock<INavigationHistoryService>();
2831

2932
_testContext.Services.Add(new ServiceDescriptor(typeof(ILogger<IPermaLinkWatcherService>), logger.Object));
3033
_testContext.Services.Add(new ServiceDescriptor(typeof(IPermaLinkWatcherService), _permaLinkWatcherServiceMock.Object));
3134
_testContext.Services.Add(new ServiceDescriptor(typeof(IScrollHandler), _scrollHandlerMock.Object));
35+
_testContext.Services.Add(new ServiceDescriptor(typeof(INavigationHistoryService), _navigationHistoryServiceMock.Object));
3236
_testContext.Services.Add(new ServiceDescriptor(typeof(SingletonComponentService<PermaLinkBlazorServerInitializer>), new SingletonComponentService<PermaLinkBlazorServerInitializer>()));
3337
_testContext.Services.Add(new ServiceDescriptor(typeof(SingletonComponentService<PermalinkBlazorWasmInitializer>), new SingletonComponentService<PermalinkBlazorWasmInitializer>()));
3438
}

src/Majorsoft.Blazor.Components.PermaLink/PermaLinkBlazorServerInitializer.razor

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
@inject IScrollHandler _scrollHandler
55
@inject NavigationManager _navigationManager
6+
@inject INavigationHistoryService _navigationHistoryService
67
@inject ILogger<IPermaLinkWatcherService> _logger
78
@inject SingletonComponentService<PermaLinkBlazorServerInitializer> _singleton
89

@@ -26,7 +27,7 @@
2627
if (firstRender)
2728
{
2829
//setup permalink
29-
_permalinkWatcher = new PermaLinkWatcherService(_scrollHandler, _navigationManager, _logger);
30+
_permalinkWatcher = new PermaLinkWatcherService(_scrollHandler, _navigationManager, _logger, _navigationHistoryService);
3031
_permalinkWatcher.WatchPermaLinks();
3132
}
3233
}

src/Majorsoft.Blazor.Components.PermaLink/PermaLinkWatcherService.cs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Text.RegularExpressions;
33

4+
using Majorsoft.Blazor.Components.Common.JsInterop.Navigation;
45
using Majorsoft.Blazor.Components.Common.JsInterop.Scroll;
56

67
using Microsoft.AspNetCore.Components;
@@ -19,17 +20,22 @@ public class PermaLinkWatcherService : IPermaLinkWatcherService
1920
private readonly IScrollHandler _scrollHandler;
2021
private readonly NavigationManager _navigationManager;
2122
private readonly ILogger<IPermaLinkWatcherService> _logger;
23+
private readonly INavigationHistoryService _navigationHistoryService;
2224

2325
public event EventHandler<PermalinkDetectedEventArgs> PermalinkDetected;
2426

2527
public bool SmoothScroll { get; set; }
2628

27-
public PermaLinkWatcherService(IScrollHandler scrollHandler, NavigationManager navigationManager, ILogger<IPermaLinkWatcherService> logger, bool smoothScroll = false)
29+
public PermaLinkWatcherService(IScrollHandler scrollHandler,
30+
NavigationManager navigationManager,
31+
ILogger<IPermaLinkWatcherService> logger,
32+
INavigationHistoryService navigationHistoryService,
33+
bool smoothScroll = false)
2834
{
2935
_scrollHandler = scrollHandler;
3036
_navigationManager = navigationManager;
3137
_logger = logger;
32-
38+
_navigationHistoryService = navigationHistoryService;
3339
SmoothScroll = smoothScroll;
3440
}
3541

@@ -55,7 +61,7 @@ private void HandleLocationChanged(object sender, LocationChangedEventArgs e)
5561
if(PermalinkDetected is not null)
5662
{
5763
PermalinkDetected.Invoke(this, new PermalinkDetectedEventArgs(e, perma));
58-
}
64+
}
5965

6066
_scrollHandler.ScrollToElementByNameAsync(perma, SmoothScroll);
6167
}
@@ -90,7 +96,7 @@ private void SetBrowserUrl(string uri, bool doNotNavigate)
9096
{
9197
if(doNotNavigate)
9298
{
93-
//TODO: history.pushState(null, '', url);
99+
_navigationHistoryService?.PushStateAsync(null, "", uri);
94100
return;
95101
}
96102

src/Majorsoft.Blazor.Components.PermaLink/_Imports.razor

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@
55
@using Majorsoft.Blazor.Components.Common.JsInterop.Scroll
66
@using Majorsoft.Blazor.Components.Common.JsInterop.Clipboard
77

8-
@using Majorsoft.Blazor.Components.Core.Events
8+
@using Majorsoft.Blazor.Components.Core.Events
9+
@using Majorsoft.Blazor.Components.Common.JsInterop.Navigation

src/Majorsoft.Blazor.Components.TestApps.Common/Components/Index.razor

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
<li><NavLink href="jsinterop#theme-js">Browser Theme Js</NavLink></li>
5151
<li><NavLink href="jsinterop#geo-js">Geo Js</NavLink></li>
5252
<li><NavLink href="jsinterop#head-js">Head Js</NavLink></li>
53+
<li><NavLink href="jsinterop#history-js">Browser History Js</NavLink></li>
5354
</ul>
5455
</li>
5556
<li>

0 commit comments

Comments
 (0)