diff --git a/packages/router-component-store/src/lib/router-history-store/router-history.store.spec.ts b/packages/router-component-store/src/lib/router-history-store/router-history.store.spec.ts
index cd4d8d1..ea8fcd1 100644
--- a/packages/router-component-store/src/lib/router-history-store/router-history.store.spec.ts
+++ b/packages/router-component-store/src/lib/router-history-store/router-history.store.spec.ts
@@ -30,6 +30,14 @@ function createTestComponent(name: string, selector: string) {
>< Back
+ > Next
+
Home
About
Company
@@ -45,6 +53,11 @@ class TestAppComponent {
event.preventDefault();
this.routerHistory.onNavigateBack();
}
+
+ onNext(event: MouseEvent) {
+ event.preventDefault();
+ this.routerHistory.onNavigateForward();
+ }
}
describe(RouterHistoryStore.name, () => {
@@ -98,55 +111,176 @@ describe(RouterHistoryStore.name, () => {
}
it('the URLs behave like the History API when navigating using links', async () => {
- expect.assertions(2);
+ expect.assertions(3);
const { click, routerHistory } = await setup();
// At Home
+ // Previous: None
+ // Next: None
await click('#about-link');
// At About
+ // Previous: Home
+ // Next: None
await click('#company-link');
// At Company
+ // Previous: About
+ // Next: None
await click('#products-link');
// At Products
+ // Previous: Company
+ // Next: None
expect(await firstValueFrom(routerHistory.currentUrl$)).toBe('/products');
expect(await firstValueFrom(routerHistory.previousUrl$)).toBe('/company');
+ expect(await firstValueFrom(routerHistory.nextUrl$)).toBe(undefined);
});
it('the URLs behave like the History API when navigating back', async () => {
- expect.assertions(2);
+ expect.assertions(3);
+
+ const { click, routerHistory } = await setup();
+
+ // At Home
+ // Previous: None
+ // Next: None
+ await click('#about-link');
+ // At About
+ // Previous: Home
+ // Next: None
+ await click('#company-link');
+ // At Company
+ // Previous: About
+ // Next: None
+ await click('#back-link');
+ // At About
+ // Previous: Home
+ // Next: Company
+
+ expect(await firstValueFrom(routerHistory.currentUrl$)).toBe('/about');
+ expect(await firstValueFrom(routerHistory.previousUrl$)).toBe('/home');
+ expect(await firstValueFrom(routerHistory.nextUrl$)).toBe('/company');
+ });
+
+ it('the URLs behave like the History API when navigating back twice', async () => {
+ expect.assertions(3);
+
+ const { click, routerHistory } = await setup();
+
+ // At Home
+ // Previous: None
+ // Next: None
+ await click('#about-link');
+ // At About
+ // Previous: Home
+ // Next: None
+ await click('#company-link');
+ // At Company
+ // Previous: About
+ // Next: None
+ await click('#back-link');
+ // At About
+ // Previous: Home
+ // Next: Company
+ await click('#back-link');
+ // At Home
+ // Previous: None
+ // Next: About
+
+ expect(await firstValueFrom(routerHistory.currentUrl$)).toBe('/home');
+ expect(await firstValueFrom(routerHistory.previousUrl$)).toBe(undefined);
+ expect(await firstValueFrom(routerHistory.nextUrl$)).toBe('/about');
+ });
+
+ it('the URLs behave like the History API when navigating back twice then forward', async () => {
+ expect.assertions(3);
const { click, routerHistory } = await setup();
// At Home
+ // Previous: None
+ // Next: None
await click('#about-link');
// At About
+ // Previous: Home
+ // Next: None
await click('#company-link');
// At Company
+ // Previous: About
+ // Next: None
await click('#back-link');
// At About
+ // Previous: Home
+ // Next: Company
+ await click('#back-link');
+ // At Home
+ // Previous: None
+ // Next: About
+ await click('#forward-link');
+ // At About
+ // Previous: Home
+ // Next: Company
expect(await firstValueFrom(routerHistory.currentUrl$)).toBe('/about');
expect(await firstValueFrom(routerHistory.previousUrl$)).toBe('/home');
+ expect(await firstValueFrom(routerHistory.nextUrl$)).toBe('/company');
});
it('the URLs behave like the History API when navigating back then using links', async () => {
- expect.assertions(2);
+ expect.assertions(3);
const { click, routerHistory } = await setup();
// At Home
+ // Previous: None
+ // Next: None
await click('#about-link');
// At About
+ // Previous: Home
+ // Next: None
await click('#company-link');
// At Company
+ // Previous: About
+ // Next: None
await click('#back-link');
// At About
+ // Previous: Home
+ // Next: Company
await click('#products-link');
// At Products
+ // Previous: About
+ // Next: None
expect(await firstValueFrom(routerHistory.currentUrl$)).toBe('/products');
expect(await firstValueFrom(routerHistory.previousUrl$)).toBe('/about');
+ expect(await firstValueFrom(routerHistory.nextUrl$)).toBe(undefined);
+ });
+
+ it('the URLs behave like the History API when navigating back then forward', async () => {
+ expect.assertions(3);
+
+ const { click, routerHistory } = await setup();
+
+ // At Home
+ await click('#about-link');
+ // At About
+ // Previous: Home
+ // Next: None
+ await click('#company-link');
+ // At Company
+ // Previous: About
+ // Next: None
+ await click('#back-link');
+ // At About
+ // Previous: Home
+ // Next: Company
+ await click('#forward-link');
+ // At Company
+ // Previous: About
+ // Next: None
+
+ expect(await firstValueFrom(routerHistory.currentUrl$)).toBe('/company');
+ expect(await firstValueFrom(routerHistory.previousUrl$)).toBe('/about');
+ expect(await firstValueFrom(routerHistory.nextUrl$)).toBe(undefined);
});
});
diff --git a/packages/router-component-store/src/lib/router-history-store/router-history.store.ts b/packages/router-component-store/src/lib/router-history-store/router-history.store.ts
index b161743..ee09b15 100644
--- a/packages/router-component-store/src/lib/router-history-store/router-history.store.ts
+++ b/packages/router-component-store/src/lib/router-history-store/router-history.store.ts
@@ -127,6 +127,38 @@ export class RouterHistoryStore extends ComponentStore {
this.#latestRouterNavigatedSequence$,
([, navigationEnd]) => navigationEnd.urlAfterRedirects
);
+ /**
+ * The next URL when taking `popstate` events into account.
+ *
+ * `undefined` is emitted when the current navigation is the last in the
+ * navigation history.
+ */
+ nextUrl$: Observable = this.select(
+ this.#history$,
+ this.#maxNavigatedId$,
+ (history, maxNavigatedId) => {
+ if (maxNavigatedId === 1) {
+ return undefined;
+ }
+
+ const [sourceNavigationStart] = this.#findSourceNavigatedSequence(
+ maxNavigatedId,
+ history
+ );
+
+ if (sourceNavigationStart.id === maxNavigatedId) {
+ return undefined;
+ }
+
+ const nextNavigationId = sourceNavigationStart.id + 1;
+ const [, nextNavigationEnd] = this.#findSourceNavigatedSequence(
+ nextNavigationId,
+ history
+ );
+
+ return nextNavigationEnd.urlAfterRedirects;
+ }
+ );
/**
* The previous URL when taking `popstate` events into account.
*