material-ui icon indicating copy to clipboard operation
material-ui copied to clipboard

[Select] Implement pointer cancellation

Open Kartik-Murthy opened this issue 9 months ago • 3 comments

Description This PR resolves pointer cancellation issues in the Select component to ensure compliance with WCAG 2.5.2 accessibility requirements.

Changes

  • Added proper pointer event handling to address accessibility concerns
  • Included test cases to verify WCAG 2.5.2 compliance

Fixes #45301

Aditional

Kartik-Murthy avatar Apr 02 '25 06:04 Kartik-Murthy

Netlify deploy preview

https://deploy-preview-45789--material-ui.netlify.app/

Bundle size report

Bundle Parsed Size Gzip Size
@mui/material 🔺+518B(+0.10%) 🔺+181B(+0.12%)
@mui/lab 0B(0.00%) 0B(0.00%)
@mui/system 0B(0.00%) 0B(0.00%)
@mui/utils 0B(0.00%) 0B(0.00%)

Details of bundle changes

Generated by :no_entry_sign: dangerJS against aff2e307d4e9e59362927266ae05a8461cd867ee

mui-bot avatar Apr 02 '25 11:04 mui-bot

Thank you @ZeeshanTamboli for the guidance! I've revised the PR to focus solely on fixing issue #45301 . I've removed the changes related to the other issues as requested and added test cases to verify the behavior. Let me know if you'd like to see any additional changes.

Kartik-Murthy avatar Apr 15 '25 17:04 Kartik-Murthy

Hey @Kartik-Murthy, thanks for working on this, sorry for the delay.

@ZeeshanTamboli, we can move forward with this review. I don't have an estimate for Material UI being rebuilt on top of Base UI.

About the solution, I wonder if there's a way of handling the mouse up from the items, instead of having to set a global event listener. Did you explore that idea?

DiegoAndai avatar Jun 12 '25 19:06 DiegoAndai

About the solution, I wonder if there's a way of handling the mouse up from the items, instead of having to set a global event listener. Did you explore that idea?

@DiegoAndai I am not sure how we can do that. We need to handle the case where the mouse is released outside the menu entirely — and that can't be reliably detected unless you're listening on document. If we only attach mouseup on menu items, we'll miss clicks released outside the menu, meaning the menu may remain open when it shouldn’t.

ZeeshanTamboli avatar Jun 23 '25 14:06 ZeeshanTamboli

@DiegoAndai I’ve moved the pointer cancellation logic into the mousedown handler instead of using a useEffect and detecting over there if the pointer is down or not. While the effect-based approach technically works, handling it directly in the event feels cleaner and could be more performant. What do you think?

ZeeshanTamboli avatar Jun 26 '25 09:06 ZeeshanTamboli

While the effect-based approach technically works, handling it directly in the event feels cleaner and could be more performant. What do you think?

I agree, it's also how Base UI does it 👍🏼: https://github.com/mui/base-ui/blob/master/packages/react/src/select/trigger/SelectTrigger.tsx#L161

DiegoAndai avatar Jul 04 '25 19:07 DiegoAndai

@ZeeshanTamboli good work, this looks almost ready for me. I'm only missing the selection when onMouseUp happens on a select item. I think we should add this, following Base's implementation which adds it to the items onMouseUp handler: https://github.com/mui/base-ui/blob/master/packages/react/src/select/item/SelectItem.tsx#L182

Would that be possible?

This is what I'm referencing, selecting with a single click:

https://github.com/user-attachments/assets/1cef8792-637e-484b-b740-3582971acfe5

DiegoAndai avatar Jul 04 '25 19:07 DiegoAndai

@ZeeshanTamboli good work, this looks almost ready for me. I'm only missing the selection when onMouseUp happens on a select item. I think we should add this, following Base's implementation which adds it to the items onMouseUp handler: https://github.com/mui/base-ui/blob/master/packages/react/src/select/item/SelectItem.tsx#L182

Would that be possible?

This is what I'm referencing, selecting with a single click:

Screen.Recording.2025-07-04.at.15.32.11.mov

@DiegoAndai Do you consider this to be part of the pointer cancellation feature we are doing in this PR? If not, we can handle it in a separate PR.

ZeeshanTamboli avatar Jul 09 '25 13:07 ZeeshanTamboli

Do you consider this to be part of the pointer cancellation feature we are doing in this PR?

Yes, to me the "mouse down > mouse up" flow is not complete without item selection. I would rather merge all at once.

DiegoAndai avatar Jul 09 '25 14:07 DiegoAndai

Do you consider this to be part of the pointer cancellation feature we are doing in this PR?

Yes, to me the "mouse down > mouse up" flow is not complete without item selection. I would rather merge all at once.

@DiegoAndai Done. Ready for further review.

ZeeshanTamboli avatar Jul 09 '25 15:07 ZeeshanTamboli

Code looks good @ZeeshanTamboli!

Multiple select demos are broken though: https://deploy-preview-45789--material-ui.netlify.app/material-ui/react-select/#multiple-select

Base UI recently implemented multiple, maybe there are some clues there? https://github.com/mui/base-ui/pull/2173

Scary that these weren't caught by tests, lets add some after finding the root cause of why this PR broke the demos.

DiegoAndai avatar Jul 09 '25 16:07 DiegoAndai

Code looks good @ZeeshanTamboli!

Multiple select demos are broken though: https://deploy-preview-45789--material-ui.netlify.app/material-ui/react-select/#multiple-select

Base UI recently implemented multiple, maybe there are some clues there? mui/base-ui#2173

@DiegoAndai Fixed now. It was occuring because both the mouseup and click events were triggering when we click on a menu item. It should trigger either mouseup or click. When clicking the item, it should trigger only click and cancel mouseup and when doing mouseup it should trigger only onMouseUp. I copied the pointer down logic from Base UI for this: https://github.com/mui/base-ui/blob/master/packages/react/src/select/item/SelectItem.tsx#L197C9-L197C26

Scary that these weren't caught by tests, lets add some after finding the root cause of why this PR broke the demos.

I think the bug wasn't caught because the tests didn't simulate both mouseup and click. However, surprisingly, there were no tests that directly checked item selection.

ZeeshanTamboli avatar Jul 10 '25 11:07 ZeeshanTamboli

Thanks @ZeeshanTamboli!

think the bug wasn't caught because the tests didn't simulate both mouseup and click.

Could we use userEvent to add a test that would cover this?


cc @siriwatknp for awareness. We're close to merging this. May I ask you to review it? I want to be extra cautious with the Select as it's a priority component.

DiegoAndai avatar Jul 21 '25 20:07 DiegoAndai

Could we use userEvent to add a test that would cover this?

@DiegoAndai Added. The test now uses userEvent.click, which covers the bug since it fires the full event sequence: mouseovermousemovemousedownmouseupclick like a real user would in a browser.

ZeeshanTamboli avatar Jul 24 '25 10:07 ZeeshanTamboli

@siriwatknp Can you review?

ZeeshanTamboli avatar Jul 28 '25 06:07 ZeeshanTamboli