-
-
Notifications
You must be signed in to change notification settings - Fork 46
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
[AlertDialog, Dialog, Popover] Configure initial focus #732
Conversation
Netlify deploy preview |
3cf1d49
to
76026c0
Compare
76026c0
to
8ba8514
Compare
Co-authored-by: atomiks <[email protected]> Signed-off-by: Michał Dudak <[email protected]>
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.
@vladmoroz I'll address the bug in a separate PR. An empty string is a valid value of PointerEvent's pointerType field, so I included it here as well. As for naming, yeah, I suppose we can think of something better. |
@michaldudak what does the empty string mean? What about "eventType"? "triggerType" sounds like it has to do with the trigger part |
An empty string is an unknown pointer type (see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/pointerType#value). It's not really the type of the event (as this is PointerEvent or MouseEvent), but the user's input device that caused the event to fire (= triggered it). |
Eww 😬
Do we return an empty string in this case or do we pass through the original type? |
We pass through whatever there is originally. |
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.
Looks good functionality-wise, I'll leave the code review to @atomiks though
|
||
export type PointerType = 'mouse' | 'touch' | 'pen' | 'keyboard' | ''; | ||
|
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.
Doesn't seem like we need the implementation to be this detailed given we're only checking for touch
presently. Instead of adding a click
handler, we could simply add a pointerdown
handler to read the pointerType
, and ignore click
.
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.
In this case activating the trigger with keyboard would not be recognized at all, would it? I agree we don't need this internally, but we expose it to users so they can provide a function to the initialFocus
prop with logic dependent on the method of activation.
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.
We can reduce it to a boolean isTouch
, though it could be a breaking change in the future if we need to add more strings
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.
IMO the API feels less weird with pointerType
than isTouch
. Also it doesn't have significant perf implications as the extra logic runs only on trigger click, so I'd leave it as it is.
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.
I agree about exposing all types, this makes for a much more flexible API.
My only nit is about PointerType
name, where "keyboard"
feels out of place. Perhaps both the type and the argument should be something like InteractionType
? No big deal though
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.
👍 yeah, "interaction" sounds better. How about InteractionMethod
, though?
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.
@@ -39,6 +41,7 @@ export function usePopoverRoot(params: usePopoverRoot.Parameters): usePopoverRoo | |||
const [descriptionId, setDescriptionId] = React.useState<string>(); | |||
const [triggerElement, setTriggerElement] = React.useState<Element | null>(null); | |||
const [positionerElement, setPositionerElement] = React.useState<HTMLElement | null>(null); | |||
const [openMethod, setOpenMethod] = React.useState<PointerType | null>(null); |
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.
Would be great for this to be a ref instead to avoid an extra re-render, but I suppose there's no way to avoid reading it during render instead of in an effect/event handler?
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.
When testing it, I never saw this state causing a rerender by itself. It's always batched with other state setters.
Added the
initialFocus
prop to control what element is focused after the Dialog, AlertDialog, or Popover is open.By default it focuses the first element, unless the component was opened by touch interaction - in that case the popup itself is focused.
Part of #714