-
Notifications
You must be signed in to change notification settings - Fork 225
fix(card): replaced the time-based click detection with a distance-based approach #5521
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
Conversation
🦋 Changeset detectedLatest commit: 800e61c The changes in this PR will be included in the next version bump. This PR includes changesets to release 84 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Branch previewReview the following VRT differencesWhen a visual regression test fails (or has previously failed while working on this branch), its results can be found in the following URLs:
If the changes are expected, update the |
Tachometer resultsChromecard permalinktest-basic
Firefoxcard permalinktest-basic
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No blockers but a few questions and suggestions added.
@@ -186,21 +186,46 @@ export class Card extends LikeAnchor( | |||
} | |||
} | |||
|
|||
private handlePointerdown(event: Event): void { | |||
/** | |||
* Handles pointer down events on the card element. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Love the addition of these comments!
packages/card/src/Card.ts
Outdated
const end = +new Date(); | ||
if (end - start < 200) { | ||
// Record the time and initial position of the pointerdown event | ||
const startTime = +new Date(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wow! No idea what +new Date()
did! TIL! That said, since it's a bit of a hack since JS is loosely typed, should we replace this with Number(new Date())
and/or perhaps a comment that this is converting the date to a numeric string?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1 on TIL! 😄 Even though I like it, I think the most idiomatic approach here is to rely on event.timeStamp
for measuring the delta between the two events.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Going with the appreciated choice of the people.
const startX = event.clientX; | ||
const startY = event.clientY; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we have (or need) any error handling for if the event object is undefined?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These events are bound natively without any argument, so the browser always passes an Event
object as the first argument. But I agree it wouldn’t hurt to wrap this logic in a try
/finally
block (with the event listener removals in the finally
). That way, if any line ever throws (for any reason?), at least we don't throw and still clean up the dangling handlers.
@@ -342,7 +343,7 @@ export const smallQuiet = (args: StoryArgs): TemplateResult => { | |||
return html` | |||
<div> | |||
<sp-card | |||
size=${args.size} | |||
size=${ifDefined(args.size)} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the default size medium or is default considered :not([size])
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SizedMixin is configured with noDefaultSize: true, which means there is no default size, it's considered :not([size]) when no size is specified
<sp-menu-item>Deselect</sp-menu-item> | ||
<sp-menu-item>Select Inverse</sp-menu-item> | ||
<sp-menu-item>Feather...</sp-menu-item> | ||
<sp-menu-item>Select and Mask...</sp-menu-item> | ||
<sp-menu-divider></sp-menu-divider> | ||
<sp-menu-item>Save Selection</sp-menu-item> | ||
<sp-menu-item disabled>Make Work Path</sp-menu-item> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
<sp-menu-item>Deselect</sp-menu-item> | |
<sp-menu-item>Select Inverse</sp-menu-item> | |
<sp-menu-item>Feather...</sp-menu-item> | |
<sp-menu-item>Select and Mask...</sp-menu-item> | |
<sp-menu-divider></sp-menu-divider> | |
<sp-menu-item>Save Selection</sp-menu-item> | |
<sp-menu-item disabled>Make Work Path</sp-menu-item> | |
<sp-menu-item>Deselect</sp-menu-item> | |
<sp-menu-item>Select inverse</sp-menu-item> | |
<sp-menu-item>Feather...</sp-menu-item> | |
<sp-menu-item>Select and mask...</sp-menu-item> | |
<sp-menu-divider></sp-menu-divider> | |
<sp-menu-item>Save selection</sp-menu-item> | |
<sp-menu-item disabled>Make work path</sp-menu-item> |
I know this is just an indent update but should we take the opportunity to fix the sentence case here anyway?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure will take this as a part of the clean up!
<sp-card variant="gallery" heading="Card Heading" subheading="JPG"> | ||
<img | ||
slot="preview" | ||
src="https://picsum.photos/532/192" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A future suggestion, I've seen CI issues with using API services to render images, I think we should consider having a few local assets for testing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will add this in a follow up PR. Not a part of this change
packages/card/src/Card.ts
Outdated
const startX = event.clientX; | ||
const startY = event.clientY; | ||
|
||
// Define the handler for when the pointer interaction ends | ||
const handleEnd = (endEvent: PointerEvent): void => { | ||
const endTime = +new Date(); | ||
const endTime = event.timeStamp; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
endEvent.timeStamp
Description
Replaced the time-based click detection with a distance-based approach:
pointermove
event handling to detect actual scrollingMotivation and context
On mobile Chrome (both Android and iOS), scrolling on
sp-card
components would inadvertently trigger click events. This was caused by the timing-based click detection (200ms threshold) in the pointer event handling, which could misinterpret quick scrolls as clicks. This issue did not affect Safari on mobile devices.Root Cause
The
pointerdown
handler is capturing all touch starts, and then manually measuring the duration of the interaction. If the duration is less than200ms
, we are triggering a.click()
on the card:The problem is: mobile Chrome often doesn't distinguish well between taps and touch scrolls unless there's actual movement. So if the user slightly touches and scrolls (even without crossing a threshold), it registers as a tap and triggers
.click()
.Technical Details
handlePointerdown
to usePointerEvent
instead of genericEvent
startX
,startY
, andisScrolling
properties to track touch stateSCROLL_THRESHOLD
constant (10px) for movement detectionRelated issue(s)
Screenshots (if appropriate)
scrollcard.mov
Author's checklist
Reviewer's checklist
patch
,minor
, ormajor
featuresManual review test cases
Scroll Issue test in Chrome in Android
Scroll Issue test in Chrome in iOS
Device review