Skip to content

Conversation

@Christian862
Copy link
Contributor

@Christian862 Christian862 commented Feb 18, 2022

Description

This PR covers moving to a page level time range system with left nav page time ranges being stored in the browser session.

Feature flags

This change is rolled out behind two 'nested' feature flags: PageTimeRange and NavigationRedesign. PageTimeRange is responsible for moving the time range component from the application header, onto the page header component. When only PageTimeRange is enabled, the time range is moved to the page headers but maintains original 'global' behaviour, so this is just a component location swap.

When PageTimeRange and NavigationRedesign are enabled, the behaviour of the time range changes to page level time range ( description below ).

General

-If the user is on left nav item page and the time range is selected, it's persisted to browser storage.

  • With both FFs enabled, only first level routes and routes with a specified defaultTimeRange property in the data object of the activated route, will be persisted to browser storage.
  • Page time ranges are set by retrieving the latest time range for a given page from storage (storage interface: user-specified-time-range.service.ts), and decorating the navItemswith the time range corresponding to it's path (decoration:navigation-list.service.ts`).
  • Once a left nav item is click, a the navigation-list.component.ts responds to this event and uses the decorated navItem to set the time range
  • With FFs enabled, the user-specified-time-range-selector.component.ts is used to determine whether or not to save the selected time range to storage.
  • Drilling down to pages uses the previous page's time range via url query params.

Core files to review

There's a good chunk of files in this PR so i'll list here the files with the core logic that should be reviewed more carefully. Additionally i'll comment a small summary at the top of these files.

  • user-specified-time-range-selector.component.ts
  • page-header.component.ts
  • `user-specified-time-range.service.ts
  • navigation-list.component.ts
  • navigation-list.service.ts
  • nav-item.component.ts
  • time-range.component.ts

Testing

Please describe the tests that you ran to verify your changes. Please summarize what did you test and what needs to be tested e.g. deployed and tested helm chart locally.

Checklist:

  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • Any dependent changes have been merged and published in downstream modules

Documentation

Make sure that you have documented corresponding changes in this repository or hypertrace docs repo if required.

@codecov
Copy link

codecov bot commented Feb 18, 2022

Codecov Report

Merging #1441 (541555f) into main (72670f3) will decrease coverage by 0.07%.
The diff coverage is 74.81%.

@@            Coverage Diff             @@
##             main    #1441      +/-   ##
==========================================
- Coverage   83.98%   83.90%   -0.08%     
==========================================
  Files         862      865       +3     
  Lines       18358    18471     +113     
  Branches     2485     2499      +14     
==========================================
+ Hits        15418    15499      +81     
- Misses       2823     2852      +29     
- Partials      117      120       +3     
Impacted Files Coverage Δ
...ts/assets-library/src/icons/icon-library.module.ts 100.00% <ø> (ø)
...ects/common/src/constants/application-constants.ts 100.00% <ø> (ø)
...omponents/src/header/page/page-header.component.ts 78.78% <ø> (ø)
...p/application-frame/application-frame.component.ts 0.00% <0.00%> (ø)
.../app/application-frame/application-frame.module.ts 0.00% <0.00%> (ø)
projects/common/src/time/time-range.service.ts 72.46% <33.33%> (-9.08%) ⬇️
...header/application/application-header.component.ts 41.66% <33.33%> (-8.34%) ⬇️
src/app/shared/navigation/navigation.component.ts 65.51% <44.44%> (-34.49%) ⬇️
...s/src/page-time-range/page-time-range.component.ts 73.68% <73.68%> (ø)
...mon/src/time/page-time-range-preference.service.ts 92.10% <92.10%> (ø)
... and 9 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 72670f3...541555f. Read the comment docs.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

this.pageTimeRangeStringDictionary$,
this.featureStateResolver.getCombinedFeatureState([
ApplicationFeature.PageTimeRange,
ApplicationFeature.NavigationRedesign
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is the nav redesign relevant here? The page time range I thought was independent of nav changes.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

PageTimeRange is responsible for bringing the time range down to the the page level, on the headers, however it still uses the original time range component to maintain original time range behaviour . The NavigationRedesign feature enables the time range to behave with page level functionality. This came after talks with @anandtiwary, and i believe this feature dependency structure is why he suggested to create something like this on the backend.

We could just use NavigationRedesign in this instance, however in the case that we disabled page time range and enable NavigationRedesign the ui will be broken because

  1. The top nav bar won't be present (in the coming nav2.0 PR) so no TR component will exist.
  2. The nav items will be attempting to use page level time range logic when they shouldn't be(changing TR on click).

Copy link
Contributor

@aaron-steinfeld aaron-steinfeld Mar 16, 2022

Choose a reason for hiding this comment

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

The NavigationRedesign feature enables the time range to behave with page level functionality

Oh interesting, so this is not what I would expect from the naming. I would have expected the behavior changes (the time range is scoped to the page) to come from the PageTimeRange flag, as well as the relocation of the control at the same time - basically all the work in this PR. I thought the Navigation redesign was dependent on that (since the new structure has no time range selector), but otherwise unrelated.

So first off, if we really want that separation, it seems like there's a flag rename in order (but let's see).

We could just use NavigationRedesign in this instance, however in the case that we disabled page time range and enable NavigationRedesign the ui will be broken because

We shouldn't try to add complexity to allow any matrix of flags to work. We can assume dependencies if it saves us significant work, I think that was mentioned earlier. In that scenario you described though, how are we solving the lack of TR? If we've removed top and disabled showing it on the page too, it's just not a valid state. And In that same scenario, given the time range page behavior is part of the Nav redesign flag as you described, we would actually want 2.


return defaultTimeRange;
private getDefaultPageTimeRange(): TimeRange | undefined {
return this.navigationService.getCurrentActivatedRoute().snapshot.data?.defaultTimeRange;
Copy link
Contributor

Choose a reason for hiding this comment

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

Just trying to understand how this works - if I click on A on the left nav, and it routes be to A/B, we need the time range to be defined on B right? But in the dictionary, if I changed it, it would be stored under A? If so, I think that's confusing. If we think A is the important bit there, then the time range being defined there would make sense too, so if I change the default routing it doesn't touch the time range. To do that, you might need to change this to walk up the tree looking.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes that is how it works. @anandtiwary and I briefly looked into traversing the routes to find the correct activated route but it's a little tricky. Because all the navItem time ranges are resolved on initial load, you can't use ActivatedRoute. That's why before i was using navigationService.getRouteConfig - however, this method does not traverse lazy loaded child routes, so we can't use it here either.

One solution would be to keep it as-is with the A/B functionality you described above, and to create a future work item out of this. Another option is to change the timing of when the navItems are decorated to being on click, but that might be a timely effort, requiring a rework in the nav-list component.

Copy link
Contributor

Choose a reason for hiding this comment

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

looked into traversing the routes to find the correct activated route but it's a little tricky

We do have this tree walk in a number of places today. For example the feature guard I believe starts from the activated route, and walks up the tree checking the config for each parent route.

Because all the navItem time ranges are resolved on initial load, you can't use ActivatedRoute. That's why before i was using navigationService.getRouteConfig

Not sure why that would differ though - the activated route just gives you another way to get to the route config - either way it's resolved at load time, which is a very good point. We need to rebuild relative time ranges at the point in time where they take effect (will take a look back at the PR with that in mind).

One solution would be to keep it as-is with the A/B functionality you described above, and to create a future work item out of this. Another option is to change the timing of when the navItems are decorated to being on click, but that might be a timely effort, requiring a rework in the nav-list component.

Don't think the latter is either worth it or would work. I think I would go for putting the time range on the root route's config, and changing the lookup to walk up the tree like we do in FF, but up to you if you feel it makes sense to do that now or later.

// Decorate the nav items with the corresponding time ranges, depending on the FF state.
// The time ranges in nav items are streams that get the most recent value from page time range preference service
this.navItems$ = this.featureStateResolver
.getCombinedFeatureState([ApplicationFeature.PageTimeRange, ApplicationFeature.NavigationRedesign])
Copy link
Contributor

Choose a reason for hiding this comment

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

It's still unclear to me why these two things are combined. the time range being set per page seems independent of the navigation.

@github-actions

This comment has been minimized.

public resolveNavItemConfigTimeRanges(navItems: NavItemConfig[]): Observable<NavItemConfig[]> {
return this.featureStateResolver
.getFeatureState(ApplicationFeature.PageTimeRange)
.pipe(switchMap(featureState => combineLatest(this.getTimeRangesForNavItems(navItems, featureState))));
Copy link
Contributor

Choose a reason for hiding this comment

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

So any time a time range is saved, you would get a whole new set of nav items?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, is there a better operator for this ?

Copy link
Contributor

@aaron-steinfeld aaron-steinfeld Mar 18, 2022

Choose a reason for hiding this comment

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

This gets back to the suggestion to do the resolution on click rather than up front, but we can revisit later as discussed. Just need to make sure with this approach that a new set of items doesn't cause issues (e.g. losing the active item, flickering due to re-render etc.)

map(() => {
const activeItem = this.findActiveItem(this.navItems);
if (!this.timeRangeService.isInitialized() && activeItem?.resolveTimeRange) {
this.timeRangeService.setDefaultTimeRange(activeItem.resolveTimeRange());
Copy link
Contributor

Choose a reason for hiding this comment

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

unclear why we're setting this here

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I put the logic for calling setDefault in the activeItem$ pipe because it needs the TR value that is decorated on the navItem, and the knowledge of what nav item is currently active, and this captures both, and also this is a shared component.

Copy link
Contributor

Choose a reason for hiding this comment

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

also this is a shared component.

This is my main concern - it's a shared component, and the whole component is used in contexts where we would not want this behavior - I'm guessing that resolveTimeRange is trying to address those cases. I would ideally prefer putting this in the parent left nav instead, unless there's a reason that's not feasible.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

if (!this.timeRangeService.isInitialized() && activeItem?.timeRangeResolver) {
this.timeRangeService.setDefaultTimeRange(activeItem.timeRangeResolver());
}
this.activeItemChange.emit(activeItem);
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm ok with it but would be remiss not to point out - we're using this observable for side effects. If we happened to not read activeItem$ in this component because we didn't need to use it for our own template, it would create a bug where we also stopped emitting.

The alternative however is also not great, since it means we need to subscribe in the component and handle unsubscribing on destroy.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah fair concern, i've made a code comment about it for anyone that sees this in the future.

private readonly userTelemetryOrchestrationService: UserTelemetryOrchestrationService,
private readonly timeRangeService: TimeRangeService
) {
this.timeRangeHasInit$ = this.timeRangeService.getTimeRangeAndChanges();
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: could take(1) here, will let this clean up rather than hanging around

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Wouldn't that defeat the purpose ? it will take the first emission which will be undefined, and never show the app content

Copy link
Contributor

Choose a reason for hiding this comment

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

getTimeRangeAndChanges should never output an undefined - it just won't emit until a time range is assigned. If we broke that, that means it's mistyped and we should go fix.

@github-actions

This comment has been minimized.


export const enum ApplicationFeature {
PageTimeRange = 'ui.page-time-range'
PageTimeRange = 'ui.page-time-range-'
Copy link
Contributor

Choose a reason for hiding this comment

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

extra -

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@Christian862 Christian862 merged commit e726159 into main Mar 21, 2022
@Christian862 Christian862 deleted the BreadcrumbsTimeRange branch March 21, 2022 14:39
@github-actions
Copy link

Unit Test Results

       4 files  ±0     286 suites  +2   20m 57s ⏱️ -35s
1 026 tests +9  1 026 ✔️ +9  0 💤 ±0  0 ❌ ±0 
1 034 runs  +9  1 034 ✔️ +9  0 💤 ±0  0 ❌ ±0 

Results for commit e726159. ± Comparison against base commit 72670f3.

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.

6 participants