Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: move attachment link back to tree item, make it flash yellow #34353

Open
wants to merge 13 commits into
base: main
Choose a base branch
from

Conversation

Skn0tt
Copy link
Member

@Skn0tt Skn0tt commented Jan 16, 2025

Screen.Recording.2025-01-16.at.13.33.42.mov
Screen.Recording.2025-01-16.at.15.03.10.mov

@Skn0tt Skn0tt requested a review from dgozman January 16, 2025 12:35
@Skn0tt Skn0tt self-assigned this Jan 16, 2025

This comment has been minimized.

This comment has been minimized.

@@ -224,3 +224,16 @@ export function scrollIntoViewIfNeeded(element: Element | undefined) {

const kControlCodesRe = '\\u0000-\\u0020\\u007f-\\u009f';
export const kWebLinkRe = new RegExp('(?:[a-zA-Z][a-zA-Z0-9+.-]{2,}:\\/\\/|www\\.)[^\\s' + kControlCodesRe + '"]{2,}[^\\s' + kControlCodesRe + '"\')}\\],:;.!?]', 'ug');

// flash is retriggered whenever the value changes
export function useFlash(flash: any | undefined) {
Copy link
Contributor

Choose a reason for hiding this comment

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

What's the semantic? Whenever flash is truthy it is going to "flash" and unflash in a second? Should this be flash: boolean instead?

Copy link
Member Author

Choose a reason for hiding this comment

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

whenever flash changes its value, useFlash returns true for two seconds. if it were flash: boolean then you couldn't retrigger it. it depends on something where it can compare references

Copy link
Contributor

Choose a reason for hiding this comment

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

This API signature should be const useFlash = (): [boolean, () => void]

You return a function that, when called, triggers the flash by updating state. You have a useState with an incrementing counter to trigger rerenders (i.e. the useEffect dependencies array contains the state number).

Copy link
Contributor

Choose a reason for hiding this comment

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

In particular it looks like you want your setFlash to take an ID object (we can just use the attachment itself), which is then cleared when the timer finishes. You then compare against that ID object to determine if you want to flash that particular attachment when it changes.

I believe you also trigger your scrollTo when you have a matching flash object.

Copy link
Member Author

Choose a reason for hiding this comment

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

How's that different from the current implementation, apart from the placement of the useEffect?

Copy link
Member Author

Choose a reason for hiding this comment

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

Suggested change
export function useFlash(flash: any | undefined) {
export function useFlash(idObject?: any) {

This comment has been minimized.

Copy link
Contributor

@agg23 agg23 left a comment

Choose a reason for hiding this comment

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

Visually it looks good. Left some comments on the React bits.

@@ -224,3 +224,16 @@ export function scrollIntoViewIfNeeded(element: Element | undefined) {

const kControlCodesRe = '\\u0000-\\u0020\\u007f-\\u009f';
export const kWebLinkRe = new RegExp('(?:[a-zA-Z][a-zA-Z0-9+.-]{2,}:\\/\\/|www\\.)[^\\s' + kControlCodesRe + '"]{2,}[^\\s' + kControlCodesRe + '"\')}\\],:;.!?]', 'ug');

// flash is retriggered whenever the value changes
export function useFlash(flash: any | undefined) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This API signature should be const useFlash = (): [boolean, () => void]

You return a function that, when called, triggers the flash by updating state. You have a useState with an incrementing counter to trigger rerenders (i.e. the useEffect dependencies array contains the state number).

@@ -224,3 +224,16 @@ export function scrollIntoViewIfNeeded(element: Element | undefined) {

const kControlCodesRe = '\\u0000-\\u0020\\u007f-\\u009f';
export const kWebLinkRe = new RegExp('(?:[a-zA-Z][a-zA-Z0-9+.-]{2,}:\\/\\/|www\\.)[^\\s' + kControlCodesRe + '"]{2,}[^\\s' + kControlCodesRe + '"\')}\\],:;.!?]', 'ug');

// flash is retriggered whenever the value changes
export function useFlash(flash: any | undefined) {
Copy link
Contributor

Choose a reason for hiding this comment

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

In particular it looks like you want your setFlash to take an ID object (we can just use the attachment itself), which is then cleared when the timer finishes. You then compare against that ID object to determine if you want to flash that particular attachment when it changes.

I believe you also trigger your scrollTo when you have a matching flash object.

@@ -148,7 +148,7 @@ export const Workbench: React.FunctionComponent<{

const revealAttachment = React.useCallback((attachment: AfterActionTraceEventAttachment) => {
selectPropertiesTab('attachments');
setRevealedAttachment(attachment);
setRevealedAttachment({ ...attachment }); // copy to force re-render
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 not seeing why this clone is necessary, and in general causing rerenders in this manner is bad practice as it's difficult to track down why stuff is behaving as it is.

Copy link
Member Author

@Skn0tt Skn0tt Jan 17, 2025

Choose a reason for hiding this comment

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

It'll be passed to a useEffect down the line, so we need to do something to break Object.is equality to allow flashing the same attachment twice in a row.

Copy link
Member Author

Choose a reason for hiding this comment

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

Revisiting this, you're right. Cloning an object is hard to debug because both objects will look the same. I've refactored it to use an incrementing counter instead.

@Skn0tt
Copy link
Member Author

Skn0tt commented Jan 17, 2025

I refactored things a little. It's now also handling the case where the animation is retriggered while it's still ongoing

https://github.com/user-attachments/assets/71432bf6-9b9b-436a-87f9-41c1671c9f46
https://github.com/user-attachments/assets/b59ab585-d2df-43ca-b9cc-35b442d836d5

@Skn0tt Skn0tt requested a review from dgozman January 17, 2025 10:46

This comment has been minimized.

@@ -224,3 +225,22 @@ export function scrollIntoViewIfNeeded(element: Element | undefined) {

const kControlCodesRe = '\\u0000-\\u0020\\u007f-\\u009f';
export const kWebLinkRe = new RegExp('(?:[a-zA-Z][a-zA-Z0-9+.-]{2,}:\\/\\/|www\\.)[^\\s' + kControlCodesRe + '"]{2,}[^\\s' + kControlCodesRe + '"\')}\\],:;.!?]', 'ug');

export function useFlash(): [boolean, EffectCallback] {
Copy link
Contributor

Choose a reason for hiding this comment

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

IIUC, this is only needed to re-trigger the animation mid-flight? Otherwise, we'd just set the css class when needed, and the animation will play by itself?

If so, please add a comment explaining this. I am not 100% sure it's worth the complexity, but I leave that up to you.

Copy link
Member Author

Choose a reason for hiding this comment

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

The important bit is that the css class is removed after a second, so the animation can be played multiple times.

I've added a comment to explain that.


type Attachment = AfterActionTraceEventAttachment & { traceUrl: string };

type ExpandableAttachmentProps = {
attachment: Attachment;
reveal: boolean;
highlight: boolean;
reveal?: any;
Copy link
Contributor

Choose a reason for hiding this comment

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

It seems like you can revert this back to be a boolean? I don't see why it should be different now with the yellow flash compared to scrolling into view.

Copy link
Member Author

Choose a reason for hiding this comment

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

Just like with scrolling into view, we need this to be an ID object so we can retrigger the animation. I think there was a bug with scroll into view before, where it didn't retrigger when clicking the same button twice. But it wasn't as noticeable because you'd have to scroll around between clicking the button.

@@ -148,7 +148,12 @@ export const Workbench: React.FunctionComponent<{

const revealAttachment = React.useCallback((attachment: AfterActionTraceEventAttachment) => {
selectPropertiesTab('attachments');
setRevealedAttachment(attachment);
setRevealedAttachment(currentValue => {
Copy link
Contributor

Choose a reason for hiding this comment

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

It looks to me this can be reverted.

Copy link
Member Author

Choose a reason for hiding this comment

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

Nope, this is required that to re-trigger the animation. We're discussing the specific form in #34353 (comment). We don't need it in the HTML reporter because we have the URLSearchParams context, but that's not a thing in the trace viewer.

This comment has been minimized.

Copy link
Contributor

Test results for "tests 1"

10 flaky ⚠️ [chromium-library] › tests/library/role-utils.spec.ts:442:5 › svg role=presentation @chromium-ubuntu-22.04-node20
⚠️ [firefox-page] › tests/page/page-evaluate.spec.ts:403:3 › should throw for too deep reference chain @firefox-ubuntu-22.04-node18
⚠️ [playwright-test] › tests/ui-mode-test-setup.spec.ts:98:5 › should show errors in config @macos-latest-node18-1
⚠️ [playwright-test] › tests/ui-mode-trace.spec.ts:341:5 › should work behind reverse proxy @ubuntu-latest-node22-1
⚠️ [webkit-library] › tests/library/browsercontext-clearcookies.spec.ts:92:3 › should remove cookies by domain @webkit-ubuntu-22.04-node18
⚠️ [webkit-library] › tests/library/page-event-crash.spec.ts:67:1 › should cancel navigation when page crashes @webkit-ubuntu-22.04-node18
⚠️ [webkit-library] › tests/library/page-event-crash.spec.ts:77:1 › should be able to close context when page crashes @webkit-ubuntu-22.04-node18
⚠️ [webkit-library] › tests/library/proxy.spec.ts:291:3 › should use SOCKS proxy for websocket requests @webkit-ubuntu-22.04-node18
⚠️ [webkit-library] › tests/library/selector-generator.spec.ts:380:5 › selector generator › should work in dynamic iframes without navigation @webkit-ubuntu-22.04-node18
⚠️ [webkit-page] › tests/page/page-set-input-files.spec.ts:205:3 › should upload multiple large files @webkit-ubuntu-22.04-node18

37593 passed, 648 skipped
✔️✔️✔️

Merge workflow run.

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.

3 participants