Skip to content

Bug: Improve back navigation logic in ItemComponent#5170

Open
milanmajchrak wants to merge 1 commit intoDSpace:mainfrom
dataquest-dev:improve-back-navigation
Open

Bug: Improve back navigation logic in ItemComponent#5170
milanmajchrak wants to merge 1 commit intoDSpace:mainfrom
dataquest-dev:improve-back-navigation

Conversation

@milanmajchrak
Copy link
Contributor

@milanmajchrak milanmajchrak commented Feb 26, 2026

References

Original author: @amadulhaxxani

Description

Improves the "Back to results" button logic on item pages by adding session storage as a fallback for the previous URL. This ensures the back button works correctly across page refreshes and prevents stale navigation when users visit items from different contexts (e.g. homepage vs. collection browse).

Instructions for Reviewers

List of changes in this PR:

  • Added storeUrlInSession() and getUrlFromSession() generic helper methods to RouteService for persisting URLs in session storage
  • Updated ItemComponent to store the previous URL in session storage and fall back to it when the route-based previous URL is not a valid navigation target
  • Added /home to the list of allowed previous routes so the back button appears when navigating from the homepage
  • Extracted URL validation into a pickAllowedPrevious() helper method for cleaner logic
  • Simplified the back() method to use the stored URL directly instead of re-subscribing to the observable
  • Updated test stubs and mocks in route-service.stub.ts, publication.component.spec.ts, untyped-item.component.spec.ts, and item.component.spec.ts
  • Added new test cases for home URL back navigation and for prioritizing the current route URL over stale session storage

How to test:

  1. Go to a collection page and click on an item — you should see "Back to results"; clicking it returns you to the collection
  2. Refresh the item page — the "Back to results" button should still appear (session storage fallback)
  3. Navigate to the homepage, click an item from "What's New" — you should see "Back to results" pointing to the homepage
  4. Regression check: From a collection, open an item. Then go to the homepage and open a different item. The back button should now point to the homepage, not to the previous collection

Checklist

This checklist provides a reminder of what we are going to look for when reviewing your PR. You do not need to complete this checklist prior creating your PR (draft PRs are always welcome).
However, reviewers may request that you complete any actions in this list if you have not done so. If you are unsure about an item in the checklist, don't hesitate to ask. We're here to help!

  • My PR is created against the main branch of code (unless it is a backport or is fixing an issue specific to an older branch).
  • My PR is small in size (e.g. less than 1,000 lines of code, not including comments & specs/tests), or I have provided reasons as to why that's not possible.
  • My PR passes ESLint validation using npm run lint
  • My PR doesn't introduce circular dependencies (verified via npm run check-circ-deps)
  • My PR includes TypeDoc comments for all new (or modified) public methods and classes. It also includes TypeDoc for large or complex private methods.
  • My PR passes all specs/tests and includes new/updated specs or tests based on the Code Testing Guide.
  • My PR aligns with Accessibility guidelines if it makes changes to the user interface.
  • My PR uses i18n (internationalization) keys instead of hardcoded English text, to allow for translations.
  • My PR includes details on how to test it. I've provided clear instructions to reviewers on how to successfully test this fix or feature.
  • If my PR includes new libraries/dependencies (in package.json), I've made sure their licenses align with the DSpace BSD License based on the Licensing of Contributions documentation.
  • If my PR includes new features or configurations, I've provided basic technical documentation in the PR itself.
  • If my PR fixes an issue ticket, I've linked them together.

- Add session storage support to remember the previous URL when navigating to an item page
- Implement generic session storage helper methods in RouteService for storing and retrieving URLs
- Update navigation logic to fall back to session storage when the current route doesn't provide a valid previous URL
- Add /home to allowed previous routes for back navigation
- Add helper method pickAllowedPrevious for cleaner URL validation
- Add tests for home URL back navigation and session fallback

Co-authored-by: amadulhaxxani <amadulhaxxani@users.noreply.github.com>
Copilot AI review requested due to automatic review settings February 26, 2026 13:40
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Improves “Back to results” navigation on item pages by persisting an allowed previous URL in session storage, so the back button remains correct across refreshes and different navigation entry points (including /home).

Changes:

  • Added generic session storage URL helpers to RouteService.
  • Updated ItemComponent to validate/store previous URLs and fall back to session storage when route history isn’t usable; expanded allowed previous routes to include /home.
  • Updated/added unit tests and stubs to cover the revised behavior.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
src/app/core/services/route.service.ts Adds sessionStorage helpers for storing/retrieving URLs.
src/app/item-page/simple/item-types/shared/item.component.ts Stores/uses a validated previous URL with a session fallback; allows /home.
src/app/item-page/simple/item-types/shared/item.component.spec.ts Adds tests for /home back navigation and “prefer route over stale session” behavior.
src/app/core/testing/route-service.stub.ts Extends the RouteService stub with session helper methods for tests.
src/app/item-page/simple/item-types/publication/publication.component.spec.ts Updates RouteService mocks to include new session helper methods.
src/app/item-page/simple/item-types/untyped-item/untyped-item.component.spec.ts Updates RouteService mocks to include new session helper methods.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +187 to +213
/**
* Store a URL in session storage for later retrieval
* Generic method that can be used by any component
* @param key The session storage key
* @param url The URL to store
*/
public storeUrlInSession(key: string, url: string): void {
if (typeof window !== 'undefined' && hasValue(window.sessionStorage)) {
// Only write if the value is different to avoid unnecessary writes
const currentValue = window.sessionStorage.getItem(key);
if (currentValue !== url) {
window.sessionStorage.setItem(key, url);
}
}
}

/**
* Retrieve a URL from session storage
* Generic method that can be used by any component
* @param key The session storage key
*/
public getUrlFromSession(key: string): string | null {
if (typeof window !== 'undefined' && hasValue(window.sessionStorage)) {
return window.sessionStorage.getItem(key);
}
return null;
}
Copy link

Copilot AI Feb 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

storeUrlInSession / getUrlFromSession add new public behavior to RouteService, but there are no unit tests covering them (there is an existing route.service.spec.ts covering other methods). Add tests for: successful write/read, no write when value unchanged, and graceful handling when sessionStorage is unavailable/throws.

Copilot uses AI. Check for mistakes.
Comment on lines 98 to 104
/**
* The function used to return to list from the item.
* Uses stored previous URL if available, otherwise falls back to browser history.
*/
back = () => {
this.routeService.getPreviousUrl().pipe(
take(1),
).subscribe(
(url => {
this.router.navigateByUrl(url);
}),
);
this.router.navigateByUrl(this.storedPreviousUrl);
};
Copy link

Copilot AI Feb 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

back() now always calls router.navigateByUrl(this.storedPreviousUrl), but storedPreviousUrl is only assigned inside the showBackButton$ observable. To avoid navigating with undefined (e.g., if back() is invoked before the observable emits), initialize storedPreviousUrl or make it optional and add a guard/fallback (e.g., browser history back). Also, the JSDoc says it falls back to browser history, but no such fallback is implemented—either implement it or update the comment to match the behavior.

Copilot uses AI. Check for mistakes.
Comment on lines +193 to +200
public storeUrlInSession(key: string, url: string): void {
if (typeof window !== 'undefined' && hasValue(window.sessionStorage)) {
// Only write if the value is different to avoid unnecessary writes
const currentValue = window.sessionStorage.getItem(key);
if (currentValue !== url) {
window.sessionStorage.setItem(key, url);
}
}
Copy link

Copilot AI Feb 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Accessing window.sessionStorage / getItem / setItem can throw (e.g., storage blocked, Safari private mode, quota exceeded). Wrap the sessionStorage interactions in a try/catch (and consider handling the case where even reading window.sessionStorage throws) so navigation logic can’t break the app at runtime.

Copilot uses AI. Check for mistakes.
Comment on lines +208 to +213
public getUrlFromSession(key: string): string | null {
if (typeof window !== 'undefined' && hasValue(window.sessionStorage)) {
return window.sessionStorage.getItem(key);
}
return null;
}
Copy link

Copilot AI Feb 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same concern as storeUrlInSession: sessionStorage.getItem (or even accessing window.sessionStorage) may throw in some environments. Add a try/catch and return null on failure to keep this helper safe to call from components.

Copilot uses AI. Check for mistakes.
Comment on lines +51 to +53
clearUrlFromSession: (key: string) => {
// no-op for tests
},
Copy link

Copilot AI Feb 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clearUrlFromSession is present in the testing stub but there’s no corresponding method on RouteService (and it isn’t used anywhere else). Consider removing it to keep the stub aligned with the real service surface area and reduce confusion.

Suggested change
clearUrlFromSession: (key: string) => {
// no-op for tests
},

Copilot uses AI. Check for mistakes.
@alegontarz
Copy link

QA testing performed (no code review).

Works as expected:

  1. "Back to results" correctly navigates to the previous page — the button points to the most recent valid route.
  2. After page refresh, the button remains visible and works.

@alegontarz alegontarz moved this from 🙋 Needs Reviewers Assigned to 👀 Under Review in DSpace 10.0 Release Feb 27, 2026
@tdonohue tdonohue moved this from 👀 Under Review to 👍 Reviewer Approved in DSpace 10.0 Release Feb 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: 👍 Reviewer Approved

Development

Successfully merging this pull request may close these issues.

5 participants