From 6120835f023cf6349fbbee21768894d3fa9ea7c9 Mon Sep 17 00:00:00 2001 From: Lars Gyrup Brink Nielsen Date: Tue, 23 May 2023 01:29:02 +0200 Subject: [PATCH] feat: add WIP `RouterHistoryStore#nextUrl$` property --- .../router-history.store.spec.ts | 70 ++++++++++++++++++- .../router-history.store.ts | 32 +++++++++ 2 files changed, 99 insertions(+), 3 deletions(-) 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..a46f4da 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,7 +111,7 @@ 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(); @@ -112,10 +125,11 @@ describe(RouterHistoryStore.name, () => { 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(); @@ -129,10 +143,31 @@ describe(RouterHistoryStore.name, () => { 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 + await click('#about-link'); + // At About + await click('#company-link'); + // At Company + await click('#back-link'); + // At About + await click('#back-link'); + // At Home + + 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 then using links', async () => { - expect.assertions(2); + expect.assertions(3); const { click, routerHistory } = await setup(); @@ -148,5 +183,34 @@ describe(RouterHistoryStore.name, () => { 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. *