Skip to content

Conversation

@intergalacticspacehighway
Copy link
Contributor

Closes

✅ Pull Request Checklist:

  • Included link to corresponding React Spectrum GitHub Issue.
  • Added/updated unit tests and storybook for this change (for new code or code which already has tests).
  • Filled out test instructions.
  • Updated documentation (if it already exists for this component).
  • Looked at the Accessibility Practices for this feature - Aria Practices

📝 Test Instructions:

  • Run MenuTrigger.test.js to run trigger="longPress" test suite.

🧢 Your Project:

None

@intergalacticspacehighway
Copy link
Contributor Author

Hi @devongovett, just signed the CLA and pushed lint fixes 😅 . Let me know if there are any changes needed.

Copy link
Member

@snowystinger snowystinger left a comment

Choose a reason for hiding this comment

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

Thanks for working on this, great work so far :) couple of notes on the tests.

triggerPress(button);
} else {
act(() => {
fireEvent.keyDown(button, {key: ' '});
Copy link
Member

Choose a reason for hiding this comment

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

I believe fireEvent will already wrap in an act, you should just be able to wrap the runAllTimers in it. Followup to that statement, is this runAllTimers needed?


Can we get a keyUp event as well, after the timer? that way we are more like real behavior in the browser
These aren't quite testing longPress, we're running all timers, which is the same thing we did for all the previous non-longPress tests. I think we should change it up a bit and only run the timers as far as we need, or we need to verify how far we advanced in time.
We don't need the it.each syntax, we only have one line in it.

Same comments for the following tests

Copy link
Member

@devongovett devongovett left a comment

Choose a reason for hiding this comment

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

Great start! Left a few suggestions. 😄

* @param state - State for the menu trigger.
*/
export function useMenuTrigger(props: MenuTriggerAriaProps, state: MenuTriggerState, ref: RefObject<HTMLElement>): MenuTriggerAria {
export function useMenuTrigger(props: MenuTriggerAriaProps, state: MenuTriggerState, ref: RefObject<HTMLElement>, trigger?: MenuTriggerType): MenuTriggerAria {
Copy link
Member

Choose a reason for hiding this comment

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

I think the trigger argument should be moved to a property of the props object.

// Portions of the code in this file are based on code from react.
// Original licensing for the following can be found in the
// NOTICE file in the root directory of this source tree.
// See https://github.com/facebook/react/tree/cc7c1aece46a6b69b41958d731e0fd27c94bfc6c/packages/react-interactions
Copy link
Member

Choose a reason for hiding this comment

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

I don't think this comment is necessary. There's no useLongPress hook in react-interactions as far as I can tell.


let {pressProps} = usePress({
onPressStart(e) {
timeRef.current = setTimeout(() => {
Copy link
Member

Choose a reason for hiding this comment

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

I think we should only handle long press events for touch and mouse pointer types. Right now I noticed you can press and hold the enter or space keys on the keyboard as well. You could add a check here for e.pointerType === 'mouse' || e.ponterType === 'touch'.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I agree with @devongovett, Enter and Space keys are tricky for a longPress on a menu trigger that opens a menu and sets focus to the first menuitem, because the keyup event might also trigger selection on the menuitem that received focus.

},
onKeyDown
const longPressProps = useLongPress({
onLongPress(e) {
Copy link
Member

Choose a reason for hiding this comment

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

If you make the change above and only allow mouse and touch pointer types for long presses, you could always call state.toggle('first') here.

Comment on lines 114 to 118
if (trigger === 'longPress') {
menuTriggerProps = mergeProps(menuTriggerProps, longPressProps, {onKeyDown});
} else {
menuTriggerProps = mergeProps(menuTriggerProps, pressProps, {onKeyDown});
}
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
if (trigger === 'longPress') {
menuTriggerProps = mergeProps(menuTriggerProps, longPressProps, {onKeyDown});
} else {
menuTriggerProps = mergeProps(menuTriggerProps, pressProps, {onKeyDown});
}
menuTriggerProps = mergeProps(menuTriggerProps, trigger === 'longPress' ? longPressProps : pressProps, {onKeyDown});

@devongovett
Copy link
Member

devongovett commented Jul 21, 2020

@intergalacticspacehighway one other thing that's a bit tricky: I noticed that the onPress event still fires after a long press. I think we need to find a way to suppress that. For example, a button could have both a press action as well as opening a menu on long press, so after long pressing, the press action shouldn't fire.

@majornista what's the best way to indicate to screen readers that a long press action is available? VoiceOver still reads that the button has a popup, but I guess we should indicate that there's both a primary click action as well as a long press action. Double tap and hold seems to work to activate the menu. Is there a pattern here that we should follow?

@majornista
Copy link
Collaborator

majornista commented Jul 24, 2020

@devongovett and @intergalacticspacehighway, I'm not sure there is a "best way" to indicate to screen readers that a long press action is available, particularly for mobile screen reader users. MenuTrigger already provides the aria-haspopup attribute to the trigger, so a screen reader user should know that a menu is available, but to communicate that it opens differently than other menus, we may need to add some additional instruction. A visual "Hold icon" affordance has been defined in Spectrum https://spectrum.adobe.com/page/action-button/#Hold-icon, perhaps we could add an aria-label with instructional text like, "Press and hold to expand menu" to this icon and reference it by id using aria-describedby on the parent button. Depending on context, it may make more sense on mobile to simply toggle the menu without a long press when an item is already selected in a toolbar.

cc @jnurthen

@intergalacticspacehighway
Copy link
Contributor Author

@intergalacticspacehighway one other thing that's a bit tricky: I noticed that the onPress event still fires after a long press. I think we need to find a way to suppress that. For example, a button could have both a press action as well as opening a menu on long press, so after long pressing, the press action shouldn't fire.

@devongovett The onPress event is fired as it's passed in the ActionButton as a prop in the story book example. I think someone might pass it deliberately. Do you think we should prevent it?
Yeah, it's a bit tricky as ActionButton has no idea of the trigger="longPress" and it uses useButton which triggers the onPress. Apologies for the delay in getting back and thanks for reviewing the PR.

@devongovett
Copy link
Member

Yeah I think we can move forward without fixing that issue and we can think about it more after this PR is merged.

},
"dependencies": {
"@babel/runtime": "^7.6.2",
"@react-aria/interactions": "3.1.0",
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Created a constant in useLongPress for long press threshold which is used in MenuTrigger test-case

const longPressProps = useLongPress({
// Close on press start as menu can be in a open state after onLongPress.
onPressStart() {
state.close();
Copy link
Contributor Author

@intergalacticspacehighway intergalacticspacehighway Aug 15, 2020

Choose a reason for hiding this comment

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

Recent changes for fixing #925 updated the behaviour of useOverlay. Earlier, onBlurWithin used to get called which calls onClose but now the toggle is only handled by onPressStart from pressProps. (For trigger buttons)
To replicate a similar behavior, I added state.close here. Let me know if it can be done in a better way.

@dannify
Copy link
Member

dannify commented Dec 23, 2021

Thanks for getting this started. Most of this code was pulled into other PRs done by the team.

@dannify dannify closed this Dec 23, 2021
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.

5 participants