Skip to content

Set the preview selection to null when there is no selection#5568

Merged
mstange merged 4 commits intofirefox-devtools:mainfrom
mstange:null-instead-of-hasselection-false
Aug 27, 2025
Merged

Set the preview selection to null when there is no selection#5568
mstange merged 4 commits intofirefox-devtools:mainfrom
mstange:null-instead-of-hasselection-false

Conversation

@mstange
Copy link
Contributor

@mstange mstange commented Aug 25, 2025

We are currently using a "no preview selection" object of the shape { hasSelection: false, isModifying: false } when there is no preview selection. We can instead just use null, it's more idiomatic.

I recommend reviewing commits individually.

@mstange mstange requested a review from canova August 25, 2025 18:27
@codecov
Copy link

codecov bot commented Aug 25, 2025

Codecov Report

❌ Patch coverage is 69.81132% with 16 lines in your changes missing coverage. Please review.
✅ Project coverage is 85.83%. Comparing base (6444d1f) to head (ee22ac9).

Files with missing lines Patch % Lines
src/components/timeline/Selection.tsx 27.27% 16 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #5568      +/-   ##
==========================================
- Coverage   85.84%   85.83%   -0.02%     
==========================================
  Files         309      309              
  Lines       30338    30349      +11     
  Branches     8346     8348       +2     
==========================================
+ Hits        26045    26050       +5     
- Misses       3873     3879       +6     
  Partials      420      420              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Member

@canova canova left a comment

Choose a reason for hiding this comment

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

It looks much better indeed! Thanks!

Nit: I think this previewSelection can be just PreviewSelection now:

renderSelectionOverlay(previewSelection: {
readonly selectionStart: number;
readonly selectionEnd: number;
readonly isModifying: boolean;
readonly draggingStart?: boolean;
readonly draggingEnd?: boolean;
}) {

Comment on lines 1755 to 1757
if (oldPreviewSelection === newPreviewSelection) {
return true;
}
Copy link
Member

Choose a reason for hiding this comment

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

I hope no one will pass the same object with the mutated value here. We know we should always pass a new object inside updatePreviewSelection and never mutate, but should we make this assumption clearer with a function comment, so other people in the future don't use it incorrectly?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah true, I'm not sure why I thought that check was worth adding. I've just inlined-away this function again and removed that strict equality check.

This replaces uses where we're assuming that we can access an isModifying
property both when we have a preview selection and when we don't have a
preview selection.

In a future commit I want to make us use null instead of hasSelection==false,
and this makes that easier.
Instead of a PreviewSelection with hasSelection: false, just use null.
This makes the PreviewSelection type usable in places where we want
to only get objects with an actual selection, and is simpler overall.
Selection is the only user of Draggable. In this use, TypeScript will now
infer the type PreviewSelection for the type argument.
I was having a hard time reading this code.

This moves most of the function contents into a separate method.
@mstange mstange force-pushed the null-instead-of-hasselection-false branch from ee22ac9 to 118c6a4 Compare August 27, 2025 15:02
@mstange
Copy link
Contributor Author

mstange commented Aug 27, 2025

Nit: I think this previewSelection can be just PreviewSelection now:

Oops, of course! I think that was my initial motivation for making this change, and then I just forgot about it! Thanks for catching it.

@mstange mstange enabled auto-merge August 27, 2025 15:04
@mstange mstange merged commit 9fe74f2 into firefox-devtools:main Aug 27, 2025
12 of 13 checks passed
Comment on lines +1758 to +1762
if (
!currentPreviewSelection ||
!previewSelection ||
!objectShallowEquals(currentPreviewSelection, previewSelection)
) {
Copy link
Member

Choose a reason for hiding this comment

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

Hm, I think this new if check is not 100% correct.

We have:

!currentPreviewSelection ||
!previewSelection)

This means that when either currentPreviewSelection or previewSelection is null, it will always dispatch UPDATE_PREVIEW_SELECTION, including the cases where they are both null.

This strict equality check was still needed for the case where they are both null. Probably that's why you initially wanted to have a different function so it's easier to do early return.

Maybe something like:

    if (
      (currentPreviewSelection === null) !== (previewSelection === null) ||
      (currentPreviewSelection &&
        previewSelection &&
        !objectShallowEquals(currentPreviewSelection, previewSelection))
    ) 

Copy link
Contributor Author

@mstange mstange Aug 27, 2025

Choose a reason for hiding this comment

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

Yes, what I landed is not equivalent to the check we had before this PR, but that just means that we may dispatch the action unnecessarily - this may be slower but it's not a "bug". My thinking was that we probably don't call it in a loop when the selection is null. But I haven't verified this.

Looks like the check was initially added in #1481 for the case where you're "zooming out" on a viewport when you're already fully zoomed out.

Ok, checking now, we will indeed repeatedly dispatch UPDATE_PREVIEW_SELECTION with null in that case.

Oh, but this doesn't actually change the identity of the redux state object! The previewSelection reducer returns null for its new state when processing this action, which is the same as its previous state, so the viewOptions reducer (from combineReducers) doesn't recreate the outer object.

Ok so I think we can keep it as-is.

@canova canova mentioned this pull request Sep 2, 2025
canova added a commit that referenced this pull request Sep 2, 2025
Changes:

[Nazım Can Altınova] Display the marker description at the top inside
the marker tooltips (#5534)
[Florian Quèze] Change the 'JavaScript' radio button label to 'Script'
(#5530)
[Markus Stange] Implement profile logic and some selectors for the
function list (#5525)
[Markus Stange] Some small type fixes (#5538)
[Markus Stange] Simplify return type of the callback we pass to
setState. (#5540)
[Markus Stange] Pass the correct value to the reducer's action argument
(#5543)
[Markus Stange] Change withSize to accept PropsWithoutSize as its type
parameter (#5541)
[Nazım Can Altınova] Make sure that the test-debug command runs the
tests properly (#5545)
[Markus Stange] Improve type coverage involving network phases (#5539)
[Markus Stange] Change implementation of withChartViewport (#5542)
[Florian Quèze] A new permalink should be generated and shown after
using the re-upload feature. (#5547)
[Florian Quèze] Show the vertical ruler in the timeline when hovering
the network chart (#5548)
[Markus Stange] Convert the entire codebase to TypeScript (#5549)
[Nazım Can Altınova] Update the yarn.lock file after recent changes
(#5557)
[Markus Stange] Add proper TypeScript coverage for window-navigation.ts
(#5559)
[Markus Stange] Remove leftover $FlowExpectError comments (#5560)
[Markus Stange] Fix Iterator / Iterable confusion (#5561)
[Nazım Can Altınova] Remove the unneeded test-all:ci script (#5566)
[Nazım Can Altınova] Fix a type case inconsistency (#5569)
[Florian Quèze] Make 'yarn lint --fix' work as an alias to 'yarn
lint-fix'. (#5563)
[Ryan Hunt] Don't stringify JSON again in fetchUrlResponse (#5570)
[Nazım Can Altınova] Upgrade ESLint to version 9 (#5567)
[Markus Stange] Simplify Worker setup, and support .json.gz inputs in
symbolicator-cli (#5556)
[Nazım Can Altınova] Add TypeScript coverage to the intersection
observer mock (#5574)
[Markus Stange] Set the preview selection to null when there is no
selection (#5568)
[Markus Stange] Add tests for query-api.ts (#5571)
[Markus Stange] Enable noUnusedParameters and
@typescript-eslint/no-unused-vars and clean up a few more things (#5576)
[Ryan Hunt] Embed iongraph-web and use for iongraph.json source files
(#5577)
[Markus Stange] Remove recursion in filterThreadToSearchString (#5572)


And thanks to our localizers:

be: Mikalai Udodau
de: Michael Köhler
el: Jim Spentzos
en-CA: chutten
en-GB: Paul
es-CL: ravmn
fr: Théo Chevalier
fur: Fabio Tomat
fy-NL: Fjoerfoks
ia: Melo46
it: Francesco Lodolo [:flod]
nl: Mark Heijl
pt-BR: Marcelo Ghelman
ru: Valery Ledovskoy
sv-SE: Luna Jernberg
tr: Rua
tr: Selim Şumlu
uk: Іhor Hordiichuk
zh-CN: Olvcpr423
zh-TW: Pin-guang Chen
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