Skip to content

tabbed url, refresh stays on tab, refactor tabbed footer logic across components#2980

Open
danoswaltCL wants to merge 2 commits intodevfrom
feature/stay-on-tabs-after-refresh
Open

tabbed url, refresh stays on tab, refactor tabbed footer logic across components#2980
danoswaltCL wants to merge 2 commits intodevfrom
feature/stay-on-tabs-after-refresh

Conversation

@danoswaltCL
Copy link
Collaborator

Adds &tab= to url when selecting a "common-tabbed-footer"-based tab.

Refresh will use the query param to open on that tab.

There was a lot of variance in how different components did tabbing, I found that I could tuck all tabbing logic into the common-tabbed-footer itself to standardize it.

PLUS because the feature components no longer need to manage tabbing I could eliminate a bunch of obsolete code 👍 👍 👍 👍 👍 👍

Copy link
Contributor

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

This pull request refactors tab management across the application by consolidating tabbing logic into a shared common-tabbed-section-card-footer component. The PR adds URL query parameter support (?tab=index) to persist tab selection across page refreshes, and removes three component-specific footer implementations (for segments, feature flags, and experiments) in favor of the unified approach.

Changes:

  • Moved tab state management into CommonTabbedSectionCardFooterComponent with URL query parameter persistence
  • Updated authentication service to preserve query parameters (including tab) during login redirects
  • Eliminated duplicate footer components across segments, feature flags, and experiments features

Reviewed changes

Copilot reviewed 17 out of 20 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
common-tabbed-section-card-footer.component.ts Added URL query parameter reading/writing, initialization logic, and change detection for tab persistence
segment-overview-details-section-card.component.ts Migrated from custom footer to common footer, added dynamic tabLabels based on segment type
segment-overview-details-section-card.component.html Replaced custom footer with common footer component
segment-overview-details-footer/* Removed obsolete custom footer component files
feature-flag-overview-details-section-card.component.ts Migrated from custom footer to common footer, added static tabLabels array
feature-flag-overview-details-section-card.component.html Replaced custom footer with common footer component
feature-flag-overview-details-footer/* Removed obsolete custom footer component files
feature-flag-details-page-content.component.ts Changed from Observable-based tab state to primitive activeTabIndex
feature-flag-details-page-content.component.html Updated template to use primitive instead of async pipe for tab conditionals
experiment-overview-details-section-card.component.ts Migrated from custom footer to common footer, added static tabLabels array
experiment-overview-details-section-card.component.html Replaced custom footer with common footer component
experiment-overview-details-footer/* Removed obsolete custom footer component files
auth.effects.ts Changed from navigate() to navigateByUrl() to preserve query parameters in redirects
auth.service.ts Updated to include query string in redirect URL preservation
Comments suppressed due to low confidence (1)

frontend/projects/upgrade/src/app/shared-standalone-component-lib/components/common-tabbed-section-card-footer/common-tabbed-section-card-footer.component.ts:27

  • The component documentation should be updated to reflect the new URL query parameter behavior. The example usage should mention that the component now automatically reads and writes a 'tab' query parameter to/from the URL, and that this affects page refresh behavior. This is important information for developers using this component.
/**
 * The `app-common-tabbed-section-card-footer` component provides a common footer component for tabbed sections.
 * It allows the user to switch between different tabs and emits the selected tab index when changed.
 *
 * Example usage:
 *
 * ```html
 * <app-common-tabbed-section-card-footer
 *   [tabLabels]="[{label: 'Tab 1'}, {label: 'Tab 2', disabled: true}]"
 *   (selectedTabChange)="onSelectedTabChange($event)">
 * </app-common-tabbed-section-card-footer>
 * ```
 */

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

@danoswaltCL danoswaltCL force-pushed the feature/stay-on-tabs-after-refresh branch from 8bf06c7 to 89d5ee2 Compare February 26, 2026 20:31
Copy link
Contributor

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

Copilot reviewed 24 out of 27 changed files in this pull request and generated 4 comments.


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

Comment on lines +69 to +76
const tabWithinRange = selectedTab >= 0 && selectedTab < this.tabLabels.length;

if (!tabWithinRange) {
console.warn('Tab index from url is out of range');
return;
}

this.selectedIndex = selectedTab;
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.

When a disabled tab is specified in the URL query parameter, the component doesn't validate whether the selected tab is disabled. This could result in selecting and displaying a disabled tab, which violates the intended behavior. Consider adding validation to check if the tab at the selected index is disabled, and if so, default to the first non-disabled tab or tab index 0.

Copilot uses AI. Check for mistakes.
Comment on lines 49 to +55
this.selectedTabChange.emit(event.index);
this.router.navigate([], {
relativeTo: this.route,
queryParams: { tab: event.index },
queryParamsHandling: 'merge',
replaceUrl: true,
});
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.

The navigation to update the query parameter happens after the event is emitted. If the parent component's event handler performs navigation or modifies the route, there could be a race condition or conflicting navigation commands. Consider performing the router navigation before emitting the event, or ensure that parent components don't navigate in response to this event.

Suggested change
this.selectedTabChange.emit(event.index);
this.router.navigate([], {
relativeTo: this.route,
queryParams: { tab: event.index },
queryParamsHandling: 'merge',
replaceUrl: true,
});
this.router.navigate([], {
relativeTo: this.route,
queryParams: { tab: event.index },
queryParamsHandling: 'merge',
replaceUrl: true,
});
this.selectedTabChange.emit(event.index);

Copilot uses AI. Check for mistakes.
Comment on lines +58 to +76
setSelectedTabFromQueryParams() {
const tab = this.route.snapshot.queryParamMap.get('tab');

if (tab) {
const selectedTab = parseInt(tab, 10);

if (isNaN(selectedTab)) {
console.warn('Tab index from url is not a number');
return;
}

const tabWithinRange = selectedTab >= 0 && selectedTab < this.tabLabels.length;

if (!tabWithinRange) {
console.warn('Tab index from url is out of range');
return;
}

this.selectedIndex = selectedTab;
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.

The validation logic checks tabLabels.length but this array may be empty during initialization when tabLabels is provided via async pipe (e.g., in segment-overview-details-section-card where tabLabels$ depends on segment$ data). If the component initializes with an empty array and then tabLabels are set later, a valid tab index from the URL would be incorrectly rejected. Consider implementing ngOnChanges to re-validate the tab index when tabLabels changes, or using RxJS to subscribe to tabLabels changes and update the selected tab accordingly.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants