diff --git a/packages/@react-spectrum/s2/src/ComboBox.tsx b/packages/@react-spectrum/s2/src/ComboBox.tsx index f2ebd0b2526..55e026528e8 100644 --- a/packages/@react-spectrum/s2/src/ComboBox.tsx +++ b/packages/@react-spectrum/s2/src/ComboBox.tsx @@ -338,14 +338,13 @@ export const ComboBox = /*#__PURE__*/ (forwardRef as forwardRefType)(function Co labelPosition = 'top', UNSAFE_className = '', UNSAFE_style, - loadingState, ...comboBoxProps } = props; return ( { comboboxTester.setInteractionType('mouse'); await comboboxTester.open(); - expect(comboboxTester.options()).toHaveLength(1); + let options = comboboxTester.options(); + expect(options).toHaveLength(1); + expect(comboboxTester.listbox).toBeTruthy(); + expect(options[0]).toHaveTextContent('No results'); expect(within(comboboxTester.listbox!).getByTestId('loadMoreSentinel')).toBeInTheDocument(); }); diff --git a/packages/@react-stately/list/src/ListCollection.ts b/packages/@react-stately/list/src/ListCollection.ts index d2de86daf59..ce82a4f664b 100644 --- a/packages/@react-stately/list/src/ListCollection.ts +++ b/packages/@react-stately/list/src/ListCollection.ts @@ -22,11 +22,15 @@ export class ListCollection implements Collection> { this.iterable = nodes; let visit = (node: Node) => { - this.keyMap.set(node.key, node); - - if (node.childNodes && node.type === 'section') { - for (let child of node.childNodes) { - visit(child); + // Skip the loader node so it isn't added to the keymap and thus + // doesn't influence the size count. This should only matter for RAC and S2 + if (node.type !== 'loader') { + this.keyMap.set(node.key, node); + + if (node.childNodes && node.type === 'section') { + for (let child of node.childNodes) { + visit(child); + } } } }; diff --git a/packages/react-aria-components/docs/ComboBox.mdx b/packages/react-aria-components/docs/ComboBox.mdx index 8d9192d9925..4a1adc91565 100644 --- a/packages/react-aria-components/docs/ComboBox.mdx +++ b/packages/react-aria-components/docs/ComboBox.mdx @@ -335,7 +335,7 @@ interface MyComboBoxProps extends Omit, 'chil function MyComboBox({label, description, errorMessage, children, ...props}: MyComboBoxProps) { return ( - +
@@ -344,7 +344,7 @@ function MyComboBox({label, description, errorMessage, childre {description && {description}} {errorMessage} - +
No results found
}> {children}
diff --git a/packages/react-aria-components/stories/ComboBox.stories.tsx b/packages/react-aria-components/stories/ComboBox.stories.tsx index 582d72a6519..d8bd8bfaf0c 100644 --- a/packages/react-aria-components/stories/ComboBox.stories.tsx +++ b/packages/react-aria-components/stories/ComboBox.stories.tsx @@ -28,7 +28,7 @@ export type ComboBoxStory = StoryFn; export type ComboBoxStoryObj = StoryObj; export const ComboBoxExample: ComboBoxStory = () => ( - +
@@ -38,12 +38,14 @@ export const ComboBoxExample: ComboBoxStory = () => (
Foo Bar Baz Google +
diff --git a/packages/react-aria-components/test/ComboBox.test.js b/packages/react-aria-components/test/ComboBox.test.js index 8b3a2401ff9..d48141179d5 100644 --- a/packages/react-aria-components/test/ComboBox.test.js +++ b/packages/react-aria-components/test/ComboBox.test.js @@ -11,12 +11,18 @@ */ import {act} from '@testing-library/react'; -import {Button, ComboBox, ComboBoxContext, FieldError, Header, Input, Label, ListBox, ListBoxItem, ListBoxSection, ListLayout, Popover, Text, Virtualizer} from '../'; +import {Button, ComboBox, ComboBoxContext, FieldError, Header, Input, Label, ListBox, ListBoxItem, ListBoxLoadMoreItem, ListBoxSection, ListLayout, Popover, Text, Virtualizer} from '../'; import {fireEvent, pointerMap, render, within} from '@react-spectrum/test-utils-internal'; import React from 'react'; import {User} from '@react-aria/test-utils'; import userEvent from '@testing-library/user-event'; +let renderEmptyState = () => { + return ( +
No results
+ ); +}; + let TestComboBox = (props) => ( @@ -25,10 +31,13 @@ let TestComboBox = (props) => ( Description Error - + Cat Dog Kangaroo + + loading + @@ -384,4 +393,19 @@ describe('ComboBox', () => { let input = getByRole('combobox'); expect(input).toHaveAttribute('form', 'test'); }); + + it('should render empty state even when there is a loader provided and allowsEmptyCollection is true', async () => { + let tree = render(); + + let comboboxTester = testUtilUser.createTester('ComboBox', {root: tree.container}); + act(() => { + comboboxTester.combobox.focus(); + }); + await user.keyboard('p'); + + let options = comboboxTester.options(); + expect(options).toHaveLength(1); + expect(comboboxTester.listbox).toBeTruthy(); + expect(options[0]).toHaveTextContent('No results'); + }); });