From b39c2dd12bc08629adf0c62fca30793d85035574 Mon Sep 17 00:00:00 2001 From: Yihui Liao <44729383+yihuiliao@users.noreply.github.com> Date: Tue, 3 Feb 2026 11:30:51 -0800 Subject: [PATCH 1/4] docs: update cursor mcp link (#9588) * docs: fix broken link to cursor mcp docs * fix broken link on s2 docs --- packages/dev/s2-docs/pages/react-aria/ai.mdx | 2 +- packages/dev/s2-docs/pages/s2/ai.mdx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/dev/s2-docs/pages/react-aria/ai.mdx b/packages/dev/s2-docs/pages/react-aria/ai.mdx index 3fe2f9030a9..9a431108e17 100644 --- a/packages/dev/s2-docs/pages/react-aria/ai.mdx +++ b/packages/dev/s2-docs/pages/react-aria/ai.mdx @@ -43,7 +43,7 @@ Add the server to your MCP client configuration (the exact file and schema may d -Or follow Cursor's MCP install [guide](https://docs.cursor.com/en/context/mcp#installing-mcp-servers) and use the standard config above. +Or follow Cursor's MCP install [guide](https://cursor.com/docs/context/mcp#installing-mcp-servers) and use the standard config above. ### VS Code diff --git a/packages/dev/s2-docs/pages/s2/ai.mdx b/packages/dev/s2-docs/pages/s2/ai.mdx index b7e6fcd38fd..7098809c705 100644 --- a/packages/dev/s2-docs/pages/s2/ai.mdx +++ b/packages/dev/s2-docs/pages/s2/ai.mdx @@ -43,7 +43,7 @@ Add the server to your MCP client configuration (the exact file and schema may d -Or follow Cursor's MCP install [guide](https://docs.cursor.com/en/context/mcp#installing-mcp-servers) and use the standard config above. +Or follow Cursor's MCP install [guide](https://cursor.com/docs/context/mcp#installing-mcp-servers) and use the standard config above. ### VS Code From 9fddb02aa8e78823ae194d76f6984594331cea11 Mon Sep 17 00:00:00 2001 From: Robert Snow Date: Tue, 3 Feb 2026 14:18:04 -0600 Subject: [PATCH 2/4] Revert "feat: support modern useEffectEvent (#9095)" (#9590) This reverts commit fdadb61ac87c5dd1353ea7e988291a916103aa27. --- packages/@react-aria/utils/src/useEffectEvent.ts | 9 +-------- packages/@react-aria/utils/src/useEvent.ts | 5 ++--- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/packages/@react-aria/utils/src/useEffectEvent.ts b/packages/@react-aria/utils/src/useEffectEvent.ts index f5046d525d3..20b89d03038 100644 --- a/packages/@react-aria/utils/src/useEffectEvent.ts +++ b/packages/@react-aria/utils/src/useEffectEvent.ts @@ -17,10 +17,7 @@ import {useLayoutEffect} from './useLayoutEffect'; // before all layout effects, but is available only in React 18 and later. const useEarlyEffect = React['useInsertionEffect'] ?? useLayoutEffect; -// Starting with React 19.2, this hook has been internalized. -const useModernEffectEvent = React['useEffectEvent'] ?? useLegacyEffectEvent; - -function useLegacyEffectEvent(fn?: T): T { +export function useEffectEvent(fn?: T): T { const ref = useRef(null); useEarlyEffect(() => { ref.current = fn; @@ -31,7 +28,3 @@ function useLegacyEffectEvent(fn?: T): T { return f?.(...args); }, []); } - -export function useEffectEvent(fn: T): T { - return useModernEffectEvent(fn); -} diff --git a/packages/@react-aria/utils/src/useEvent.ts b/packages/@react-aria/utils/src/useEvent.ts index c83b479e614..1dd35499847 100644 --- a/packages/@react-aria/utils/src/useEvent.ts +++ b/packages/@react-aria/utils/src/useEvent.ts @@ -11,7 +11,7 @@ */ import {RefObject} from '@react-types/shared'; -import {useCallback, useEffect} from 'react'; +import {useEffect} from 'react'; import {useEffectEvent} from './useEffectEvent'; export function useEvent( @@ -20,8 +20,7 @@ export function useEvent( handler?: (this: Document, ev: GlobalEventHandlersEventMap[K]) => any, options?: boolean | AddEventListenerOptions ): void { - let noop = useCallback(() => {}, []); - let handleEvent = useEffectEvent(handler ?? noop); + let handleEvent = useEffectEvent(handler); let isDisabled = handler == null; useEffect(() => { From 47858e06adcb8cd61eddaebb5cd007206f0995cc Mon Sep 17 00:00:00 2001 From: Daniel Lu Date: Tue, 3 Feb 2026 15:27:39 -0800 Subject: [PATCH 3/4] chore: Revert "fix: offset calculation to handle transformed elements with translateY or translateX in scrollIntoView (#7717)" (#9592) * Revert "fix: offset calculation to handle transformed elements with translateY or translateX in scrollIntoView (#7717)" This reverts commit 4a815c3efb1ec007b05463d5aafc80565b6f9f67. * fix lint --- .../@react-aria/utils/src/scrollIntoView.ts | 29 ++++++---- .../stories/GridList.stories.tsx | 55 +------------------ 2 files changed, 18 insertions(+), 66 deletions(-) diff --git a/packages/@react-aria/utils/src/scrollIntoView.ts b/packages/@react-aria/utils/src/scrollIntoView.ts index 76a1e6316cc..336b0ebc2c9 100644 --- a/packages/@react-aria/utils/src/scrollIntoView.ts +++ b/packages/@react-aria/utils/src/scrollIntoView.ts @@ -74,18 +74,23 @@ export function scrollIntoView(scrollView: HTMLElement, element: HTMLElement): v * offsetLeft or offsetTop through intervening offsetParents. */ function relativeOffset(ancestor: HTMLElement, child: HTMLElement, axis: 'left'|'top') { - let childRect = child.getBoundingClientRect(); - let ancestorRect = ancestor.getBoundingClientRect(); - - let viewportOffset = axis === 'left' - ? childRect.left - ancestorRect.left - : childRect.top - ancestorRect.top; - - let scrollAdjustment = axis === 'left' - ? ancestor.scrollLeft - : ancestor.scrollTop; - - return viewportOffset + scrollAdjustment; + const prop = axis === 'left' ? 'offsetLeft' : 'offsetTop'; + let sum = 0; + while (child.offsetParent) { + sum += child[prop]; + if (child.offsetParent === ancestor) { + // Stop once we have found the ancestor we are interested in. + break; + } else if (nodeContains(child.offsetParent, ancestor)) { + // If the ancestor is not `position:relative`, then we stop at + // _its_ offset parent, and we subtract off _its_ offset, so that + // we end up with the proper offset from child to ancestor. + sum -= ancestor[prop]; + break; + } + child = child.offsetParent as HTMLElement; + } + return sum; } /** diff --git a/packages/react-aria-components/stories/GridList.stories.tsx b/packages/react-aria-components/stories/GridList.stories.tsx index c5fc4e26059..b6f0174a509 100644 --- a/packages/react-aria-components/stories/GridList.stories.tsx +++ b/packages/react-aria-components/stories/GridList.stories.tsx @@ -625,60 +625,6 @@ function GridListInModalPickerRender(props: ModalOverlayProps): JSX.Element { ); } -export function GridListScrollIntoView() { - let items: {id: number, name: string}[] = []; - for (let i = 0; i < 100; i++) { - items.push({id: i, name: `Item ${i}`}); - } - - let list = useListData({ - initialItems: items - }); - - const getElement = (id: number) => document.querySelector(`[data-key="${id}"]`) as HTMLElement; - - const rowHeight = 25; - - return ( - <> -
- - {item => ( - - {item.name} - )} - -
- - - - ); -} - export let GridListInModalPicker: StoryObj = { render: (args) => , parameters: { @@ -689,3 +635,4 @@ export let GridListInModalPicker: StoryObj = } } }; + From bb88f41728ce4ced863bc4ee8afdd170c0124125 Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Tue, 3 Feb 2026 18:29:05 -0600 Subject: [PATCH 4/4] docs: fix example link logic and automatic control props setting (#9593) * docs: fix link copy logic in CodePlatter * use same algorithm in VisualExampleClient for settings the props automatically --- packages/dev/s2-docs/src/CodePlatter.tsx | 29 ++++++++++++++++--- .../dev/s2-docs/src/VisualExampleClient.tsx | 28 +++++++++++++++--- 2 files changed, 49 insertions(+), 8 deletions(-) diff --git a/packages/dev/s2-docs/src/CodePlatter.tsx b/packages/dev/s2-docs/src/CodePlatter.tsx index f598b867c61..cead892fbd1 100644 --- a/packages/dev/s2-docs/src/CodePlatter.tsx +++ b/packages/dev/s2-docs/src/CodePlatter.tsx @@ -110,12 +110,33 @@ export function CodePlatter({children, type, showCoachMark}: CodePlatterProps) { // Find previous heading element to get hash. let url = new URL(shareUrl, location.href); let node: Element | null = codeRef.current; - while (node && node.parentElement?.tagName !== 'ARTICLE') { + + // Search for the nearest heading by walking up the tree and checking previous siblings + while (node && node.tagName !== 'ARTICLE') { + // Check previous siblings + let sibling = node.previousElementSibling; + while (sibling) { + if (sibling instanceof HTMLHeadingElement) { + node = sibling; + break; + } + // Also check inside the sibling for headings + let headingInSibling = sibling.querySelector('h1, h2, h3, h4, h5, h6'); + if (headingInSibling instanceof HTMLHeadingElement) { + node = headingInSibling; + break; + } + sibling = sibling.previousElementSibling; + } + + if (node instanceof HTMLHeadingElement) { + break; + } + + // Move up to parent node = node.parentElement; } - while (node && !(node instanceof HTMLHeadingElement)) { - node = node.previousElementSibling; - } + if (node instanceof HTMLHeadingElement && node.id) { url.hash = '#' + node.id; } diff --git a/packages/dev/s2-docs/src/VisualExampleClient.tsx b/packages/dev/s2-docs/src/VisualExampleClient.tsx index 6562e6c1e45..e883dd689f6 100644 --- a/packages/dev/s2-docs/src/VisualExampleClient.tsx +++ b/packages/dev/s2-docs/src/VisualExampleClient.tsx @@ -74,12 +74,32 @@ export function VisualExampleClient({component, name, importSource, controls, ch useEffect(() => { // Find previous heading element. let node: Element | null = ref.current; - while (node && node.parentElement?.tagName !== 'ARTICLE') { + + // Search for the nearest heading by walking up the tree and checking previous siblings + while (node && node.tagName !== 'ARTICLE') { + // Check previous siblings + let sibling = node.previousElementSibling; + while (sibling) { + if (sibling instanceof HTMLHeadingElement) { + node = sibling; + break; + } + // Also check inside the sibling for headings + let headingInSibling = sibling.querySelector('h1, h2, h3, h4, h5, h6'); + if (headingInSibling instanceof HTMLHeadingElement) { + node = headingInSibling; + break; + } + sibling = sibling.previousElementSibling; + } + + if (node instanceof HTMLHeadingElement) { + break; + } + + // Move up to parent node = node.parentElement; } - while (node && !(node instanceof HTMLHeadingElement)) { - node = node.previousElementSibling; - } let id = node instanceof HTMLHeadingElement ? node.id : null; if (id && location.hash === '#' + id) {