Skip to content

Commit

Permalink
feat: add WIP RouterHistoryStore#nextUrl$ property
Browse files Browse the repository at this point in the history
  • Loading branch information
LayZeeDK committed May 29, 2023
1 parent b284f43 commit a3bdc1d
Show file tree
Hide file tree
Showing 3 changed files with 246 additions and 3 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import {
Event as NgRouterEvent,
NavigationEnd,
NavigationStart,
} from '@angular/router';

export const routerEvents: readonly NgRouterEvent[] = [
// 1. Navigate to the root path ‘/’, which redirects me to the homepage
// Current: Home
// Previous: None
// Next: None
new NavigationStart(1, '/', 'imperative', null),
new NavigationEnd(1, '/', '/home'),

// 2. Click a menu link to navigate to the About page
// Current: About
// Previous: Home
// Next: None
new NavigationStart(2, '/about', 'imperative', null),
new NavigationEnd(2, '/about', '/about'),

// 3. Click a menu link to navigate to the Company page
// Current: Company
// Previous About
// Next: None
new NavigationStart(3, '/company', 'imperative', null),
new NavigationEnd(3, '/company', '/company'),

// 4. Click the back button
// Current: About
// Previous: Home
// Next: Company
new NavigationStart(4, '/about', 'popstate', { navigationId: 2 }),
new NavigationEnd(4, '/about', '/about'),

// 5. Click a menu link to navigate to the Products page
// Current: Products
// Previous: About
// Next: None
new NavigationStart(5, '/products', 'imperative', null),
new NavigationEnd(5, '/products', '/products'),

// 6. Click a menu link to navigate to the Home page
// Current: Home
// Previous: Products
// Next: None
new NavigationStart(6, '/home', 'imperative', null),
new NavigationEnd(6, '/home', '/home'),

// 7. Click a menu link to navigate to the About page
// Current: About
// Previous: Home
// Next: None
new NavigationStart(7, '/about', 'imperative', null),
new NavigationEnd(7, '/about', '/about'),

// 8. Click the back button
// Current: Home
// Previous: Products
// Next: About
new NavigationStart(8, '/home', 'popstate', { navigationId: 6 }),
new NavigationEnd(8, '/home', '/home'),

// 9. Click the forward button
// Current: About
// Previous: Home
// Next: None
new NavigationStart(9, '/about', 'popstate', { navigationId: 7 }),
new NavigationEnd(9, '/about', '/about'),

// 10. Click the back button
// Current: Home
// Previous: Products
// Next: About
new NavigationStart(10, '/home', 'popstate', { navigationId: 8 }),
new NavigationEnd(10, '/home', '/home'),
];
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,14 @@ function createTestComponent(name: string, selector: string) {
>&lt; Back</a
>
<a
id="forward-link"
*ngIf="routerHistory.nextUrl$ | async as nextUrl"
[href]="nextUrl"
(click)="onNext($event)"
>&gt; Next</a
>
<a id="home-link" routerLink="/">Home</a>
<a id="about-link" routerLink="about">About</a>
<a id="company-link" routerLink="company">Company</a>
Expand All @@ -45,6 +53,11 @@ class TestAppComponent {
event.preventDefault();
this.routerHistory.onNavigateBack();
}

onNext(event: MouseEvent) {
event.preventDefault();
this.routerHistory.onNavigateForward();
}
}

describe(RouterHistoryStore.name, () => {
Expand Down Expand Up @@ -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);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,38 @@ export class RouterHistoryStore extends ComponentStore<RouterHistoryState> {
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<string | undefined> = 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.
*
Expand Down

0 comments on commit a3bdc1d

Please sign in to comment.