From 9f3a20e074759c48e292587847d45c3b1a40b3bb Mon Sep 17 00:00:00 2001 From: Daniel Lu Date: Mon, 31 Aug 2026 14:20:26 -0700 Subject: [PATCH 1/4] feat: add async support to S2 menu --- packages/@react-spectrum/s2/src/Menu.tsx | 125 +++++++++++++++++- .../s2/stories/Menu.stories.tsx | 63 +++++++++ .../@react-spectrum/s2/test/Menu.test.tsx | 86 +++++++++++- packages/dev/s2-docs/pages/s2/Menu.mdx | 48 +++++++ 4 files changed, 317 insertions(+), 5 deletions(-) diff --git a/packages/@react-spectrum/s2/src/Menu.tsx b/packages/@react-spectrum/s2/src/Menu.tsx index cb8a50dcbbd..b7ba4ece592 100644 --- a/packages/@react-spectrum/s2/src/Menu.tsx +++ b/packages/@react-spectrum/s2/src/Menu.tsx @@ -15,6 +15,7 @@ import { Menu as AriaMenu, MenuItem as AriaMenuItem, MenuItemProps as AriaMenuItemProps, + MenuLoadMoreItem as AriaMenuLoadMoreItem, MenuProps as AriaMenuProps, MenuSection as AriaMenuSection, MenuSectionProps as AriaMenuSectionProps, @@ -24,6 +25,14 @@ import { SubmenuTriggerProps as AriaSubmenuTriggerProps, MenuItemRenderProps } from 'react-aria-components/Menu'; +import { + AsyncLoadable, + DOMRef, + DOMRefValue, + GlobalDOMAttributes, + LoadingState, + PressEvent +} from '@react-types/shared'; import { baseColor, centerPadding, @@ -37,6 +46,7 @@ import {box, iconStyles} from './Checkbox'; import {centerBaseline} from './CenterBaseline'; import CheckmarkIcon from '../ui-icons/Checkmark'; import ChevronRightIcon from '../ui-icons/Chevron'; +import {Collection} from 'react-aria/Collection'; import {ContextValue, DEFAULT_SLOT, Provider, useSlottedContext} from 'react-aria-components/slots'; import { control, @@ -56,7 +66,6 @@ import { useState } from 'react'; import {divider} from './Divider'; -import {DOMRef, DOMRefValue, GlobalDOMAttributes, PressEvent} from '@react-types/shared'; import {edgeToText} from '../style/spectrum-theme' with {type: 'macro'}; import {forwardRefType} from './types'; import {HeaderContext, HeadingContext, KeyboardContext, Text, TextContext} from './Content'; @@ -70,6 +79,7 @@ import {mergeStyles} from '../style/runtime'; import {Placement} from 'react-aria/useOverlayPosition'; import {PressResponder} from 'react-aria/private/interactions/PressResponder'; import {pressScale} from './pressScale'; +import {ProgressCircle} from './ProgressCircle'; import {Separator, SeparatorProps} from 'react-aria-components/Separator'; import {ToggleButtonContext} from './ToggleButton'; import {useGlobalListeners} from 'react-aria/private/utils/useGlobalListeners'; @@ -107,6 +117,7 @@ export interface MenuProps AriaMenuProps, 'children' | 'style' | 'className' | 'render' | 'renderEmptyState' | keyof GlobalDOMAttributes >, + Pick, StyleProps { /** * The size of the Menu. @@ -120,6 +131,11 @@ export interface MenuProps children: ReactNode | ((item: T) => ReactNode); /** Hides the default link out icons on menu items that open links in a new tab. */ hideLinkOutIcon?: boolean; + /** + * The current loading state of the Menu. Determines whether or not the progress circle should + * be shown, and whether a "no results" message is displayed when there are no items. + */ + loadingState?: LoadingState; } export const MenuContext = @@ -413,6 +429,50 @@ let wrappingDiv = style({ size: 'full' }); +const loadingWrapperStyles = style({ + gridColumnStart: '1', + gridColumnEnd: '-1', + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + marginY: 8 +}); + +const progressCircleStyles = style({ + size: { + size: { + S: 16, + M: 20, + L: 22, + XL: 26 + } + } +}); + +const emptyStateText = style({ + gridColumnStart: '1', + gridColumnEnd: '-1', + height: { + size: { + S: 24, + M: 32, + L: 40, + XL: 48 + } + }, + font: { + size: { + S: 'ui-sm', + M: 'ui', + L: 'ui-lg', + XL: 'ui-xl' + } + }, + display: 'flex', + alignItems: 'center', + paddingStart: 'edge-to-text' +}); + /** * Menus display a list of actions or options that a user can choose. */ @@ -428,10 +488,48 @@ export const Menu = /*#__PURE__*/ (forwardRef as forwardRefType)(function Menu + + + ); + + let renderer; + if (typeof children === 'function' && items) { + renderer = ( + <> + + {children} + + {menuLoadingCircle} + + ); + } else { + renderer = ( + <> + {children} + {menuLoadingCircle} + + ); + } let isPopover = (ctx || isSubmenu) && !inPopover; let content = ( @@ -457,8 +555,27 @@ export const Menu = /*#__PURE__*/ (forwardRef as forwardRefType)(function Menu - - {children} + + loadingState === 'loading' ? ( +
+ +
+ ) : ( + + {stringFormatter.format('combobox.noResults')} + + ) + }> + {renderer}
diff --git a/packages/@react-spectrum/s2/stories/Menu.stories.tsx b/packages/@react-spectrum/s2/stories/Menu.stories.tsx index 73e930bb138..2dddea902fe 100644 --- a/packages/@react-spectrum/s2/stories/Menu.stories.tsx +++ b/packages/@react-spectrum/s2/stories/Menu.stories.tsx @@ -54,6 +54,7 @@ import StampClone from '../s2wf-icons/S2_Icon_StampClone_20_N.svg'; import TextIcon from '../s2wf-icons/S2_Icon_Text_20_N.svg'; import {ToggleButton} from '../src/ToggleButton'; import Underline from '../s2wf-icons/S2_Icon_TextUnderline_20_N.svg'; +import {useAsyncList} from 'react-stately/useAsyncList'; const events = ['onAction', 'onClose', 'onOpenChange', 'onScroll', 'onSelectionChange']; @@ -448,6 +449,68 @@ export const ContextMenu: Story = { ) }; +interface Character { + name: string; +} + +const AsyncMenuRender = (args: MenuProps & {delay: number}): ReactElement => { + let list = useAsyncList({ + async load({signal, cursor}) { + if (cursor) { + cursor = cursor.replace(/^http:\/\//i, 'https://'); + } + + // Slow down load so progress circle can appear + await new Promise(resolve => setTimeout(resolve, args.delay)); + let res = await fetch(cursor || 'https://swapi.py4e.com/api/people/', {signal}); + let json = await res.json(); + + return { + items: json.results, + cursor: json.next + }; + } + }); + + return ( + + + + {(item: Character) => {item.name}} + + + ); +}; + +export type AsyncMenuStoryType = typeof AsyncMenuRender; +export const AsyncMenuStory: StoryObj = { + render: AsyncMenuRender, + args: { + delay: 50 + }, + name: 'Async loading menu' +}; + +export const EmptyState: Story = { + render: args => ( + + + + {() => Never rendered} + + + ) +}; + export const HoldAffordance: Story = { render: args => (
{ + let testUtilUser = new User({advanceTimer: jest.advanceTimersByTime}); + + beforeAll(() => { + jest.useFakeTimers(); + }); + + afterEach(() => { + act(() => jest.runAllTimers()); + jest.clearAllMocks(); + }); + + afterAll(() => { + jest.useRealTimers(); + }); + + it('should show a "No results" message when there are no items and it is not loading', async () => { + let {getByRole} = render( + + + + {(item: any) => {item.name}} + + + ); + + let menuTester = testUtilUser.createTester('Menu', {root: getByRole('button')}); + await menuTester.open(); + expect(menuTester.getMenu()).toHaveTextContent('No results'); + }); + + it('should show a progress circle when loadingState is "loading"', async () => { + let {getByRole} = render( + + + + {(item: any) => {item.name}} + + + ); + + let menuTester = testUtilUser.createTester('Menu', {root: getByRole('button')}); + await menuTester.open(); + expect( + within(menuTester.getMenu()!).getByRole('progressbar', {hidden: true}) + ).toBeInTheDocument(); + }); + + it('should call onLoadMore when intersection is detected while loadingState is "loadingMore"', async () => { + let onLoadMore = jest.fn(); + let observe = jest.fn(); + let observer = setupIntersectionObserverMock({observe}); + + let {getByRole, getByTestId} = render( + + + + Cut + Copy + Paste + + + ); + + let menuTester = testUtilUser.createTester('Menu', {root: getByRole('button')}); + await menuTester.open(); + + expect(onLoadMore).toHaveBeenCalledTimes(0); + let sentinel = getByTestId('loadMoreSentinel'); + expect(observe).toHaveBeenLastCalledWith(sentinel); + + act(() => { + observer.instance.triggerCallback([{isIntersecting: true}]); + }); + act(() => { + jest.runAllTimers(); + }); + + expect(onLoadMore).toHaveBeenCalledTimes(1); + }); +}); diff --git a/packages/dev/s2-docs/pages/s2/Menu.mdx b/packages/dev/s2-docs/pages/s2/Menu.mdx index 0aaca4221e9..16c9f604963 100644 --- a/packages/dev/s2-docs/pages/s2/Menu.mdx +++ b/packages/dev/s2-docs/pages/s2/Menu.mdx @@ -283,6 +283,54 @@ function Example(props) { } ``` +### Asynchronous loading + +Use the `loadingState` and `onLoadMore` props to enable async loading and infinite scrolling. + +```tsx render +"use client"; +import {MenuTrigger, Menu, MenuItem} from '@react-spectrum/s2/Menu'; +import {ActionButton} from '@react-spectrum/s2/ActionButton'; +import {useAsyncList} from '@react-spectrum/s2/useAsyncList'; + +interface Character { + name: string +} + +function Example() { + let list = useAsyncList({ + async load({signal, cursor}) { + if (cursor) { + cursor = cursor.replace(/^http:\/\//i, 'https://'); + } + + let res = await fetch(cursor || 'https://swapi.py4e.com/api/people/', {signal}); + let json = await res.json(); + + return { + items: json.results, + cursor: json.next + }; + } + }); + + return ( + + Select Character + + {/*- end highlight -*/} + {item => {item.name}} + + + ); +} +``` + ### Links Use the `href` prop on a `` to create a link. See the [getting started guide](getting-started) to learn how to integrate with your framework. From 3ef235fb199dd411136495a3681f86187373ef5f Mon Sep 17 00:00:00 2001 From: Daniel Lu Date: Tue, 1 Sep 2026 12:06:52 -0700 Subject: [PATCH 2/4] fix empty state centering, share styles, story fixes --- packages/@react-spectrum/s2/src/ComboBox.tsx | 10 +---- packages/@react-spectrum/s2/src/Menu.tsx | 12 ++---- packages/@react-spectrum/s2/src/Picker.tsx | 21 +---------- .../s2/stories/Menu.stories.tsx | 37 ++++++++----------- 4 files changed, 23 insertions(+), 57 deletions(-) diff --git a/packages/@react-spectrum/s2/src/ComboBox.tsx b/packages/@react-spectrum/s2/src/ComboBox.tsx index 26f797774b0..11d8dd0e47e 100644 --- a/packages/@react-spectrum/s2/src/ComboBox.tsx +++ b/packages/@react-spectrum/s2/src/ComboBox.tsx @@ -39,7 +39,7 @@ import {BaseCollection, CollectionNode} from 'react-aria/private/collections/Bas import {baseColor, centerPadding, focusRing, space, style} from '../style' with {type: 'macro'}; import {Button, ButtonRenderProps} from 'react-aria-components/Button'; import {centerBaseline} from './CenterBaseline'; -import {checkmark, description, icon, iconCenterWrapper, label, sectionHeading} from './Menu'; +import {checkmark, description, icon, iconCenterWrapper, label, loadingWrapperStyles, sectionHeading} from './Menu'; import CheckmarkIcon from '../ui-icons/Checkmark'; import ChevronIcon from '../ui-icons/Chevron'; import {Collection} from 'react-aria/Collection'; @@ -210,14 +210,6 @@ const iconStyles = style({ } }); -const loadingWrapperStyles = style({ - gridColumnStart: '1', - gridColumnEnd: '-1', - display: 'flex', - alignItems: 'center', - justifyContent: 'center', - marginY: 8 -}); const progressCircleStyles = style({ size: '1lh', diff --git a/packages/@react-spectrum/s2/src/Menu.tsx b/packages/@react-spectrum/s2/src/Menu.tsx index b7ba4ece592..21b3e94f650 100644 --- a/packages/@react-spectrum/s2/src/Menu.tsx +++ b/packages/@react-spectrum/s2/src/Menu.tsx @@ -132,8 +132,7 @@ export interface MenuProps /** Hides the default link out icons on menu items that open links in a new tab. */ hideLinkOutIcon?: boolean; /** - * The current loading state of the Menu. Determines whether or not the progress circle should - * be shown, and whether a "no results" message is displayed when there are no items. + * The current loading state of the Menu. */ loadingState?: LoadingState; } @@ -429,7 +428,7 @@ let wrappingDiv = style({ size: 'full' }); -const loadingWrapperStyles = style({ +export const loadingWrapperStyles = style({ gridColumnStart: '1', gridColumnEnd: '-1', display: 'flex', @@ -438,7 +437,7 @@ const loadingWrapperStyles = style({ marginY: 8 }); -const progressCircleStyles = style({ +export const progressCircleStyles = style({ size: { size: { S: 16, @@ -450,8 +449,6 @@ const progressCircleStyles = style({ }); const emptyStateText = style({ - gridColumnStart: '1', - gridColumnEnd: '-1', height: { size: { S: 24, @@ -470,7 +467,7 @@ const emptyStateText = style({ }, display: 'flex', alignItems: 'center', - paddingStart: 'edge-to-text' + paddingX: 'edge-to-text' }); /** @@ -565,7 +562,6 @@ export const Menu = /*#__PURE__*/ (forwardRef as forwardRefType)(function Menu
diff --git a/packages/@react-spectrum/s2/src/Picker.tsx b/packages/@react-spectrum/s2/src/Picker.tsx index a4feceb71f9..cad8e64fa95 100644 --- a/packages/@react-spectrum/s2/src/Picker.tsx +++ b/packages/@react-spectrum/s2/src/Picker.tsx @@ -50,6 +50,8 @@ import { icon, iconCenterWrapper, label, + loadingWrapperStyles, + progressCircleStyles, sectionHeading } from './Menu'; import CheckmarkIcon from '../ui-icons/Checkmark'; @@ -291,25 +293,6 @@ const avatar = style({ marginEnd: 'text-to-visual' }); -const loadingWrapperStyles = style({ - gridColumnStart: '1', - gridColumnEnd: '-1', - display: 'flex', - alignItems: 'center', - justifyContent: 'center', - marginY: 8 -}); - -const progressCircleStyles = style({ - size: { - size: { - S: 16, - M: 20, - L: 22, - XL: 26 - } - } -}); let InternalPickerContext = createContext<{size: 'S' | 'M' | 'L' | 'XL'}>({size: 'M'}); let InsideSelectValueContext = createContext(false); diff --git a/packages/@react-spectrum/s2/stories/Menu.stories.tsx b/packages/@react-spectrum/s2/stories/Menu.stories.tsx index 2dddea902fe..222905c1467 100644 --- a/packages/@react-spectrum/s2/stories/Menu.stories.tsx +++ b/packages/@react-spectrum/s2/stories/Menu.stories.tsx @@ -453,7 +453,10 @@ interface Character { name: string; } -const AsyncMenuRender = (args: MenuProps & {delay: number}): ReactElement => { +const AsyncMenuRender = ( + args: MenuProps & {delay: number; isEmpty: boolean} +): ReactElement => { + let {delay, isEmpty, ...menuTriggerArgs} = args; let list = useAsyncList({ async load({signal, cursor}) { if (cursor) { @@ -461,7 +464,7 @@ const AsyncMenuRender = (args: MenuProps & {delay: number}): ReactEle } // Slow down load so progress circle can appear - await new Promise(resolve => setTimeout(resolve, args.delay)); + await new Promise(resolve => setTimeout(resolve, delay)); let res = await fetch(cursor || 'https://swapi.py4e.com/api/people/', {signal}); let json = await res.json(); @@ -473,16 +476,16 @@ const AsyncMenuRender = (args: MenuProps & {delay: number}): ReactEle }); return ( - + + items={isEmpty ? [] : list.items} + loadingState={isEmpty ? undefined : list.loadingState} + onLoadMore={isEmpty ? undefined : list.loadMore}> {(item: Character) => {item.name}} @@ -493,24 +496,16 @@ export type AsyncMenuStoryType = typeof AsyncMenuRender; export const AsyncMenuStory: StoryObj = { render: AsyncMenuRender, args: { - delay: 50 + delay: 2000, + isEmpty: false + }, + argTypes: { + delay: {control: 'number'}, + isEmpty: {control: 'boolean'} }, name: 'Async loading menu' }; -export const EmptyState: Story = { - render: args => ( - - - - {() => Never rendered} - - - ) -}; - export const HoldAffordance: Story = { render: args => (
Date: Tue, 1 Sep 2026 12:58:53 -0700 Subject: [PATCH 3/4] fix lint --- packages/@react-spectrum/s2/src/ComboBox.tsx | 11 +++++++++-- packages/@react-spectrum/s2/src/Picker.tsx | 1 - 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/@react-spectrum/s2/src/ComboBox.tsx b/packages/@react-spectrum/s2/src/ComboBox.tsx index 11d8dd0e47e..0e3f8978636 100644 --- a/packages/@react-spectrum/s2/src/ComboBox.tsx +++ b/packages/@react-spectrum/s2/src/ComboBox.tsx @@ -39,7 +39,15 @@ import {BaseCollection, CollectionNode} from 'react-aria/private/collections/Bas import {baseColor, centerPadding, focusRing, space, style} from '../style' with {type: 'macro'}; import {Button, ButtonRenderProps} from 'react-aria-components/Button'; import {centerBaseline} from './CenterBaseline'; -import {checkmark, description, icon, iconCenterWrapper, label, loadingWrapperStyles, sectionHeading} from './Menu'; +import { + checkmark, + description, + icon, + iconCenterWrapper, + label, + loadingWrapperStyles, + sectionHeading +} from './Menu'; import CheckmarkIcon from '../ui-icons/Checkmark'; import ChevronIcon from '../ui-icons/Chevron'; import {Collection} from 'react-aria/Collection'; @@ -210,7 +218,6 @@ const iconStyles = style({ } }); - const progressCircleStyles = style({ size: '1lh', marginStart: { diff --git a/packages/@react-spectrum/s2/src/Picker.tsx b/packages/@react-spectrum/s2/src/Picker.tsx index cad8e64fa95..bfc428df637 100644 --- a/packages/@react-spectrum/s2/src/Picker.tsx +++ b/packages/@react-spectrum/s2/src/Picker.tsx @@ -293,7 +293,6 @@ const avatar = style({ marginEnd: 'text-to-visual' }); - let InternalPickerContext = createContext<{size: 'S' | 'M' | 'L' | 'XL'}>({size: 'M'}); let InsideSelectValueContext = createContext(false); From ee11e1a87cdfb239e9b8740603bd088b99dfd732 Mon Sep 17 00:00:00 2001 From: Daniel Lu Date: Tue, 1 Sep 2026 15:15:00 -0700 Subject: [PATCH 4/4] adjust progress circle style to use 1lh --- .../s2/chromatic/Menu.stories.tsx | 69 ++++++++++++++++++- .../s2/chromatic/Picker.stories.tsx | 17 +++++ packages/@react-spectrum/s2/src/Menu.tsx | 13 +--- packages/@react-spectrum/s2/src/Picker.tsx | 4 +- .../@react-spectrum/s2/test/Combobox.test.tsx | 8 +-- .../@react-spectrum/s2/test/Menu.test.tsx | 4 +- .../@react-spectrum/s2/test/Picker.test.tsx | 8 +-- 7 files changed, 99 insertions(+), 24 deletions(-) diff --git a/packages/@react-spectrum/s2/chromatic/Menu.stories.tsx b/packages/@react-spectrum/s2/chromatic/Menu.stories.tsx index dcfdd57c9b4..99fd608e80e 100644 --- a/packages/@react-spectrum/s2/chromatic/Menu.stories.tsx +++ b/packages/@react-spectrum/s2/chromatic/Menu.stories.tsx @@ -18,9 +18,11 @@ import { PublishAndExport, UnavailableMenuItem } from '../stories/Menu.stories'; +import {Button} from '../src/Button'; import {expect} from '@storybook/jest'; -import {Menu} from '../src/Menu'; +import {Menu, MenuItem, MenuTrigger} from '../src/Menu'; import type {Meta, StoryObj} from '@storybook/react'; +import NewIcon from '../s2wf-icons/S2_Icon_New_20_N.svg'; import {userEvent, within} from 'storybook/test'; const meta: Meta> = { @@ -31,7 +33,8 @@ const meta: Meta> = { backgrounds: ['base'], locales: ['en-US'], disableAnimations: true - } + }, + chromatic: {ignoreSelectors: ['[role="progressbar"]']} }, tags: ['autodocs'], title: 'S2 Chromatic/Menu' @@ -84,3 +87,65 @@ export const WithUnavailableItem: Story = { expect(menus).toHaveLength(2); } }; + +export const WithEmptyState: Story = { + render: () => ( + + + + {() => Never rendered} + + + ), + play: async ({canvasElement}) => { + await userEvent.tab(); + await userEvent.keyboard('{ArrowDown}'); + let body = canvasElement.ownerDocument.body; + let menu = await within(body).findByRole('menu'); + await within(menu).findByText('No results'); + } +}; + +export const WithInitialLoading: Story = { + render: () => ( + + + + {() => Never rendered} + + + ), + play: async ({canvasElement}) => { + await userEvent.tab(); + await userEvent.keyboard('{ArrowDown}'); + let body = canvasElement.ownerDocument.body; + let menu = await within(body).findByRole('menu'); + await within(menu).findByRole('progressbar', {hidden: true}); + } +}; + +export const WithLoadMore: Story = { + render: () => ( + + + + Cut + Copy + Paste + + + ), + play: async ({canvasElement}) => { + await userEvent.tab(); + await userEvent.keyboard('{ArrowDown}'); + let body = canvasElement.ownerDocument.body; + let menu = await within(body).findByRole('menu'); + await within(menu).findByRole('progressbar', {hidden: true}); + } +}; diff --git a/packages/@react-spectrum/s2/chromatic/Picker.stories.tsx b/packages/@react-spectrum/s2/chromatic/Picker.stories.tsx index 70d2a9fd169..2745a2b860e 100644 --- a/packages/@react-spectrum/s2/chromatic/Picker.stories.tsx +++ b/packages/@react-spectrum/s2/chromatic/Picker.stories.tsx @@ -105,6 +105,23 @@ export const ContextualHelp: Story = { } }; +export const WithLoadMore: Story = { + render: () => ( + + Chocolate + Mint + Strawberry + + ), + play: async ({canvasElement}) => { + await userEvent.tab(); + await userEvent.keyboard('{ArrowDown}'); + let body = canvasElement.ownerDocument.body; + let listbox = await within(body).findByRole('listbox'); + await within(listbox).findByRole('progressbar', {hidden: true}); + } +}; + export const EmptyAndLoading: Story = { render: () => ( diff --git a/packages/@react-spectrum/s2/src/Menu.tsx b/packages/@react-spectrum/s2/src/Menu.tsx index 21b3e94f650..b75a8128eef 100644 --- a/packages/@react-spectrum/s2/src/Menu.tsx +++ b/packages/@react-spectrum/s2/src/Menu.tsx @@ -438,14 +438,7 @@ export const loadingWrapperStyles = style({ }); export const progressCircleStyles = style({ - size: { - size: { - S: 16, - M: 20, - L: 22, - XL: 26 - } - } + size: '1lh' }); const emptyStateText = style({ @@ -502,7 +495,7 @@ export const Menu = /*#__PURE__*/ (forwardRef as forwardRefType)(function Menu @@ -561,7 +554,7 @@ export const Menu = /*#__PURE__*/ (forwardRef as forwardRefType)(function Menu
diff --git a/packages/@react-spectrum/s2/src/Picker.tsx b/packages/@react-spectrum/s2/src/Picker.tsx index bfc428df637..48c00d2bfe9 100644 --- a/packages/@react-spectrum/s2/src/Picker.tsx +++ b/packages/@react-spectrum/s2/src/Picker.tsx @@ -509,14 +509,14 @@ export const Picker = /*#__PURE__*/ (forwardRef as forwardRefType)(function Pick }); function PickerProgressCircle(props) { - let {id, size, 'aria-label': ariaLabel} = props; + let {id, 'aria-label': ariaLabel} = props; return ( ); } diff --git a/packages/@react-spectrum/s2/test/Combobox.test.tsx b/packages/@react-spectrum/s2/test/Combobox.test.tsx index 10d6d978361..42256308bf9 100644 --- a/packages/@react-spectrum/s2/test/Combobox.test.tsx +++ b/packages/@react-spectrum/s2/test/Combobox.test.tsx @@ -112,8 +112,8 @@ describe('Combobox', () => { let sentinel = tree.getByTestId('loadMoreSentinel'); expect(observe).toHaveBeenLastCalledWith(sentinel); - act(() => { - observer.instance.triggerCallback([{isIntersecting: true}]); + await act(async () => { + await observer.instance.triggerCallback([{isIntersecting: true}]); }); act(() => { jest.runAllTimers(); @@ -129,8 +129,8 @@ describe('Combobox', () => { ); - act(() => { - observer.instance.triggerCallback([{isIntersecting: true}]); + await act(async () => { + await observer.instance.triggerCallback([{isIntersecting: true}]); }); act(() => { jest.runAllTimers(); diff --git a/packages/@react-spectrum/s2/test/Menu.test.tsx b/packages/@react-spectrum/s2/test/Menu.test.tsx index 5e20b9ac2f0..bd469180048 100644 --- a/packages/@react-spectrum/s2/test/Menu.test.tsx +++ b/packages/@react-spectrum/s2/test/Menu.test.tsx @@ -453,8 +453,8 @@ describe('Async loading', () => { let sentinel = getByTestId('loadMoreSentinel'); expect(observe).toHaveBeenLastCalledWith(sentinel); - act(() => { - observer.instance.triggerCallback([{isIntersecting: true}]); + await act(async () => { + await observer.instance.triggerCallback([{isIntersecting: true}]); }); act(() => { jest.runAllTimers(); diff --git a/packages/@react-spectrum/s2/test/Picker.test.tsx b/packages/@react-spectrum/s2/test/Picker.test.tsx index 05d59184f1e..55858bf6dc7 100644 --- a/packages/@react-spectrum/s2/test/Picker.test.tsx +++ b/packages/@react-spectrum/s2/test/Picker.test.tsx @@ -80,8 +80,8 @@ describe('Picker', () => { let sentinel = tree.getByTestId('loadMoreSentinel'); expect(observe).toHaveBeenLastCalledWith(sentinel); - act(() => { - observer.instance.triggerCallback([{isIntersecting: true}]); + await act(async () => { + await observer.instance.triggerCallback([{isIntersecting: true}]); }); act(() => { jest.runAllTimers(); @@ -97,8 +97,8 @@ describe('Picker', () => { ); - act(() => { - observer.instance.triggerCallback([{isIntersecting: true}]); + await act(async () => { + await observer.instance.triggerCallback([{isIntersecting: true}]); }); act(() => { jest.runAllTimers();