Skip to content

Conversation

snowystinger
Copy link
Member

Closes

Fixes popover positioning, see first commit's new story to see bad behaviour by scrolling trigger partially out of view horizontally to start. You'll see the popover rendered way out of place. After the second commit, it has been fixed and the popover will be bound by the scrolling element.

Fixes pending state for buttons so they aren't clickable through context. Also adds the pending state to ActionButtons.

Adds the default label which we support in other components to Picker's items.

Moves S2 Table cell focus rings so they render over the cell content.

✅ 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:

🧢 Your Project:

@rspbot
Copy link

rspbot commented Sep 4, 2025

@rspbot
Copy link

rspbot commented Sep 4, 2025

Copy link
Member

@LFDanLu LFDanLu left a comment

Choose a reason for hiding this comment

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

Behavior looks good to me, will need some more time to double check the calculatePosition change (wish it wasn't so brittle)

Comment on lines 168 to 170
if (key.startsWith('on')) {
props[key] = undefined;
}
Copy link
Member

Choose a reason for hiding this comment

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

what about onBlur/onFocus? Should those be removed?

Copy link
Member Author

Choose a reason for hiding this comment

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

o doh, i saw "focusProps" and assumed that's where that was being handled, you are correct

Copy link
Member Author

Choose a reason for hiding this comment

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

one question i did have here, is should i move this logic into useButton, it might be easier, but currently useButton doesn't know about pending since there are so many style dependent and dom structure requirements, so it only exists in higher levels

Copy link
Member

Choose a reason for hiding this comment

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

hm, is this mainly a concern about how the spinner may be rendered relative to the button and/or if there is a spinner at all? Not quite sure I fully understand the style/dom structure dependencies here

Copy link
Member Author

@snowystinger snowystinger Sep 5, 2025

Choose a reason for hiding this comment

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

essentially there are different ways you can set things up
https://react-spectrum.adobe.com/react-aria/Button.html#accessibility

but the event behaviour should probably be the same, it'd just be weird to put that into the hooks i guess since it doesn't include a lot of the other choices

Copy link
Member

Choose a reason for hiding this comment

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

gotcha I think for now we punt on moving the logic into the hooks then

@rspbot
Copy link

rspbot commented Sep 4, 2025

@rspbot
Copy link

rspbot commented Sep 5, 2025

@rspbot
Copy link

rspbot commented Sep 5, 2025

@rspbot
Copy link

rspbot commented Sep 5, 2025

@rspbot
Copy link

rspbot commented Sep 5, 2025

@@ -181,7 +181,7 @@ function getDelta(
// Note that these values are with respect to the visual viewport (aka 0,0 is the top left of the viewport)
let boundaryStartEdge = boundaryDimensions.scroll[AXIS[axis]] + padding;
let boundaryEndEdge = boundarySize + boundaryDimensions.scroll[AXIS[axis]] - padding;
let startEdgeOffset = offset - containerScroll + containerOffsetWithBoundary[axis] - boundaryDimensions[AXIS[axis]];
let startEdgeOffset = offset - containerScroll + containerOffsetWithBoundary[axis];
Copy link
Member

Choose a reason for hiding this comment

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

I double checked this and found that it was tied to pinch zoom positioning: 3722d30. If you comment out what the startEdge/endEdge values here are, try opening the RAC Popover story outside the iframe, and open the popover after pinch zooming on Safari in such a way that the trigger is close to the edge of the left of the window you'll see that the startEdgeOffset calculation is incorrect (the boundary will return 12, but the start edge will return a value that is much larger). Each of the values here should return coordinate values transformed in such a way that a zoomed in window should have its origin at the top left.

As a result, opening a dropdown when the trigger is partially off screen won't do the following (aka preserve the visibility of the dropdown by keeping it in the bounds of the screen)
image

instead this happens, where the dropdown falls outside of its defined boundary
image

I see the original issue you alluded to in your description, but I think the calculated boundary edges are also incorrect in that story (they currently return 0 and 300 respectively but should be the left and right edge of the box with respect to the top left corner of the story).

In addition, the startEdgeOffset should just be equal to the offset in that case since that value is already in the proper coordinate system, but at the moment it is receiving a containerOffsetWithBoundary that has negative values that should instead be offsetting the value of boundaryDimensions (aka containerOffsetWithBoundary is representing how far offset the boundary element is with respect to the containing element aka the page). I think we'll need to

  1. fix the boundaryStartEdge and boundaryEndEdge calcs here
  2. Change it to be - containerOffsetWithBoundary[axis] - boundaryDimensions[AXIS[axis]]; or update how containerOffsetWithBoundary is being calculated to return positive values (this one is probably more correct if we consider the top left corner to be the 0,0 origin and there for the offset should be positive instead of negative if the container is centered in the page)

Copy link
Member Author

Choose a reason for hiding this comment

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

Ahhh, thank you, I'll have a look on Monday

Copy link
Member

Choose a reason for hiding this comment

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

no worries, happy to sit with you and debug if you'd like as well, this stuff is tricky :/

@rspbot
Copy link

rspbot commented Sep 8, 2025

@rspbot
Copy link

rspbot commented Sep 8, 2025

## API Changes

@react-spectrum/s2

/@react-spectrum/s2:ActionButton

 ActionButton {
   UNSAFE_className?: UnsafeClassName
   UNSAFE_style?: CSSProperties
   aria-controls?: string
   aria-current?: boolean | 'true' | 'false' | 'page' | 'step' | 'location' | 'date' | 'time'
   aria-describedby?: string
   aria-details?: string
   aria-disabled?: boolean | 'true' | 'false'
   aria-expanded?: boolean | 'true' | 'false'
   aria-haspopup?: boolean | 'menu' | 'listbox' | 'tree' | 'grid' | 'dialog' | 'true' | 'false'
   aria-label?: string
   aria-labelledby?: string
   aria-pressed?: boolean | 'true' | 'false' | 'mixed'
   autoFocus?: boolean
   children: ReactNode
   excludeFromTabOrder?: boolean
   form?: string
   formAction?: string
   formEncType?: string
   formMethod?: string
   formNoValidate?: boolean
   formTarget?: string
   id?: string
   isDisabled?: boolean
+  isPending?: boolean
   isQuiet?: boolean
   name?: string
   onBlur?: (FocusEvent<Target>) => void
   onFocus?: (FocusEvent<Target>) => void
   onKeyDown?: (KeyboardEvent) => void
   onKeyUp?: (KeyboardEvent) => void
   onPress?: (PressEvent) => void
   onPressChange?: (boolean) => void
   onPressEnd?: (PressEvent) => void
   onPressStart?: (PressEvent) => void
   onPressUp?: (PressEvent) => void
   preventFocusOnPress?: boolean
   size?: 'XS' | 'S' | 'M' | 'L' | 'XL' = 'M'
   slot?: string | null
   staticColor?: 'black' | 'white' | 'auto'
   styles?: StylesProp
   type?: 'button' | 'submit' | 'reset' = 'button'
   value?: string
 }

/@react-spectrum/s2:ActionButtonProps

 ActionButtonProps {
   UNSAFE_className?: UnsafeClassName
   UNSAFE_style?: CSSProperties
   aria-controls?: string
   aria-current?: boolean | 'true' | 'false' | 'page' | 'step' | 'location' | 'date' | 'time'
   aria-describedby?: string
   aria-details?: string
   aria-disabled?: boolean | 'true' | 'false'
   aria-expanded?: boolean | 'true' | 'false'
   aria-haspopup?: boolean | 'menu' | 'listbox' | 'tree' | 'grid' | 'dialog' | 'true' | 'false'
   aria-label?: string
   aria-labelledby?: string
   aria-pressed?: boolean | 'true' | 'false' | 'mixed'
   autoFocus?: boolean
   children: ReactNode
   excludeFromTabOrder?: boolean
   form?: string
   formAction?: string
   formEncType?: string
   formMethod?: string
   formNoValidate?: boolean
   formTarget?: string
   id?: string
   isDisabled?: boolean
+  isPending?: boolean
   isQuiet?: boolean
   name?: string
   onBlur?: (FocusEvent<Target>) => void
   onFocus?: (FocusEvent<Target>) => void
   onKeyDown?: (KeyboardEvent) => void
   onKeyUp?: (KeyboardEvent) => void
   onPress?: (PressEvent) => void
   onPressChange?: (boolean) => void
   onPressEnd?: (PressEvent) => void
   onPressStart?: (PressEvent) => void
   onPressUp?: (PressEvent) => void
   preventFocusOnPress?: boolean
   size?: 'XS' | 'S' | 'M' | 'L' | 'XL' = 'M'
   slot?: string | null
   staticColor?: 'black' | 'white' | 'auto'
   styles?: StylesProp
   type?: 'button' | 'submit' | 'reset' = 'button'
   value?: string
 }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants