Skip to content

docs: Async load more documentation for RAC #8431

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 17 commits into from
Jul 8, 2025

Conversation

LFDanLu
Copy link
Member

@LFDanLu LFDanLu commented Jun 20, 2025

Closes

✅ 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 Jun 20, 2025

Build successful! 🎉

@rspbot
Copy link

rspbot commented Jun 20, 2025

Build successful! 🎉

@rspbot
Copy link

rspbot commented Jun 20, 2025

Build successful! 🎉

Comment on lines +982 to +1001
<details>
<summary style={{fontWeight: 'bold'}}><ChevronRight size="S" /> MyProgressCircle</summary>

```tsx example export=true render=false
import {ProgressBar} from 'react-aria-components';
import type {ProgressBarProps} from 'react-aria-components';

export function MyProgressCircle(props: ProgressBarProps) {
return (
<ProgressBar {...props}>
<svg width="24" height="24" viewBox="0 0 24 24" style={{display: 'block'}}>
<path fill="currentColor" d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z" opacity=".25" />
<path fill="currentColor" d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z">
<animateTransform attributeName="transform" type="rotate" dur="0.75s" values="0 12 12;360 12 12" repeatCount="indefinite"/>
</path>
</svg>
</ProgressBar>
);
}
```
Copy link
Member Author

Choose a reason for hiding this comment

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

This gets used in a bunch of other places, what do people think about including it in the reusable wrappers in ProgressBar docs so we can import it?

Comment on lines -911 to -912
[this CodeSandbox](https://codesandbox.io/s/dreamy-burnell-3i2jy?file=/src/Autocomplete.tsx) for an example
of a ComboBox supporting those features.
Copy link
Member Author

Choose a reason for hiding this comment

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

got rid of the codesandbox since this has a direct example now, that sandbox is referenced in the hook docs anyways

Comment on lines 890 to 895
.react-aria-TableHeader {
position: sticky;
top: 0;
background: white;
z-index: 1;
}
Copy link
Member Author

Choose a reason for hiding this comment

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

this has a weird shift in vertical position when scrolling down, haven't quite figured out why exactly

Note that `<TreeLoadMoreItem>` is meant to render the spinner specifically when loading additional items,
the initial loading state and empty state should instead be handled via [renderEmptyState](#empty-state).

```tsx example
Copy link
Member Author

Choose a reason for hiding this comment

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

Example is quite long, I could pull out the interfaces but open to ideas if people have any ideas

Copy link
Member

Choose a reason for hiding this comment

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

hmm yeah, we can collapse some stuff in the new docs. Nice to show two separate lists here though I think, especially since we don't have useAsyncTree.

@LFDanLu LFDanLu marked this pull request as ready for review June 23, 2025 16:39
@rspbot
Copy link

rspbot commented Jun 23, 2025

Build successful! 🎉

@rspbot
Copy link

rspbot commented Jun 23, 2025

Build successful! 🎉

@rspbot
Copy link

rspbot commented Jun 24, 2025

Build successful! 🎉

Copy link
Member

@snowystinger snowystinger left a comment

Choose a reason for hiding this comment

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

Need to update for dark mode as well
Screenshot 2025-06-26 at 3 52 49 pm

inputValue={list.filterText}
onInputChange={list.setFilterText}>
{(item) => <ListBoxItem id={item.name}>{item.name}</ListBoxItem>}
{/*- begin highlight -*/}
Copy link
Member

Choose a reason for hiding this comment

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

heh... this is a sorta confusing API. I see how it came about, and I don't have a different suggestion at this time since we're combining static items with dynamic
I guess we could show it by adding a "loading" item to the list.items and rendering the ListBoxLoadMoreItem when we get to that item, but then we might have to remember to remove it and move it to the end of the list

Copy link
Member Author

Choose a reason for hiding this comment

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

yeahhh, it certainly is. If definitely would be nice if it just lived in the list of items like you said but you've highlighted the exact reason why I didn't go that route haha (especially since it doesn't mesh very well with useAsyncList). In addition, having it statically added after your dynamic list ensures that it always exists in the collection so the loading sentinel is always rendered

@rspbot
Copy link

rspbot commented Jun 26, 2025

Build successful! 🎉


</details>

### GridListLoadMoreItem
Copy link
Member

Choose a reason for hiding this comment

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

Can we move this under the Props section with the other components?

}

// Brief delay so spinner can be seen more easily
await new Promise(resolve => setTimeout(resolve, 500));
Copy link
Member

Choose a reason for hiding this comment

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

Do we want to keep this in the docs? Wouldn't be in a real implementation...

Copy link
Member Author

Choose a reason for hiding this comment

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

fair point, guess users could always slow down their network if they really wanted to see the spinner

Copy link
Member Author

Choose a reason for hiding this comment

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

behavior without these delays is a bit gross in the Tree example cuz you have to basically load all the pokemon/collapse the section if you want to get to the Star Wars Rree row since it loads so fast now, but maybe ok

Copy link
Member

Choose a reason for hiding this comment

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

more realistic I guess 🤷

Comment on lines 909 to 913
and filtering of data from a server. If you want to display a spinner to indicate the loading state to the user, or support features like
infinite scroll to load more data, be sure to include the [`<ListBoxLoadMoreItem>`](../react-aria/ListBox.html#listboxloadmoreitem) after the `Listbox`'s items render function as shown in the example below.

Note that `<ListBoxLoadMoreItem>` is meant to render the spinner specifically when loading additional items,
the initial loading state and empty state should instead be handled via [renderEmptyState](../react-aria/ListBox.html#empty-state).
Copy link
Member

Choose a reason for hiding this comment

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

a bit shorter:

Suggested change
and filtering of data from a server. If you want to display a spinner to indicate the loading state to the user, or support features like
infinite scroll to load more data, be sure to include the [`<ListBoxLoadMoreItem>`](../react-aria/ListBox.html#listboxloadmoreitem) after the `Listbox`'s items render function as shown in the example below.
Note that `<ListBoxLoadMoreItem>` is meant to render the spinner specifically when loading additional items,
the initial loading state and empty state should instead be handled via [renderEmptyState](../react-aria/ListBox.html#empty-state).
and filtering of data from a server. Use the [renderEmptyState](../react-aria/ListBox.html#empty-state) prop to display a spinner during initial load. To enable infinite scrolling, render a [`<ListBoxLoadMoreItem>`](../react-aria/ListBox.html#listboxloadmoreitem) element at the end of the list.

Comment on lines 614 to 618
This example uses the [useAsyncList](../react-stately/useAsyncList.html) hook to handle asynchronous loading of data from a server. If you want to display a spinner to indicate the loading state to the user, or support features like
infinite scroll to load more data, be sure to include the `<GridListLoadMoreItem>` after the `GridList`'s items render function as shown in the example below.

Note that `<GridListLoadMoreItem>` is meant to render the spinner specifically when loading additional items,
the initial loading state and empty state should instead be handled via [renderEmptyState](./empty-state).
Copy link
Member

Choose a reason for hiding this comment

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

same shortening as above.

Note that `<TreeLoadMoreItem>` is meant to render the spinner specifically when loading additional items,
the initial loading state and empty state should instead be handled via [renderEmptyState](#empty-state).

```tsx example
Copy link
Member

Choose a reason for hiding this comment

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

hmm yeah, we can collapse some stuff in the new docs. Nice to show two separate lists here though I think, especially since we don't have useAsyncTree.

@rspbot
Copy link

rspbot commented Jul 7, 2025

Build successful! 🎉

@rspbot
Copy link

rspbot commented Jul 7, 2025

async load({signal, filterText}) {
async load({signal, cursor, filterText}) {
// Brief delay so spinner can be seen more easily
await new Promise(resolve => setTimeout(resolve, 500));
Copy link
Member

Choose a reason for hiding this comment

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

remove this one too?

Copy link
Member Author

Choose a reason for hiding this comment

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

whoops, thanks for catching

@rspbot
Copy link

rspbot commented Jul 8, 2025

1 similar comment
@rspbot
Copy link

rspbot commented Jul 8, 2025

@rspbot
Copy link

rspbot commented Jul 8, 2025

@rspbot
Copy link

rspbot commented Jul 8, 2025

## API Changes

react-aria-components

/react-aria-components:UNSTABLE_GridListLoadingSentinel

-UNSTABLE_GridListLoadingSentinel <T extends {}> {
-  children?: ReactNode
-  className?: string
-  isLoading?: boolean
-  onLoadMore?: () => any
-  scrollOffset?: number = 1
-  style?: CSSProperties
-}

/react-aria-components:UNSTABLE_ListBoxLoadingSentinel

-UNSTABLE_ListBoxLoadingSentinel <T extends {}> {
-  children?: ReactNode
-  className?: string
-  isLoading?: boolean
-  onLoadMore?: () => any
-  scrollOffset?: number = 1
-  style?: CSSProperties
-}

/react-aria-components:UNSTABLE_TableLoadingSentinel

-UNSTABLE_TableLoadingSentinel <T extends {}> {
-  children?: ReactNode
-  className?: string
-  isLoading?: boolean
-  onLoadMore?: () => any
-  scrollOffset?: number = 1
-  style?: CSSProperties
-}

/react-aria-components:UNSTABLE_TreeLoadingIndicator

-UNSTABLE_TreeLoadingIndicator <T extends {}> {
-  children?: ChildrenOrFunction<UNSTABLE_TreeLoadingIndicatorRenderProps>
-  className?: ClassNameOrFunction<UNSTABLE_TreeLoadingIndicatorRenderProps>
-  style?: StyleOrFunction<UNSTABLE_TreeLoadingIndicatorRenderProps>
-}

/react-aria-components:GridListLoadMoreItem

+GridListLoadMoreItem <T extends {}> {
+  children?: ReactNode
+  className?: string
+  isLoading?: boolean
+  onLoadMore?: () => any
+  scrollOffset?: number = 1
+  style?: CSSProperties
+}

/react-aria-components:ListBoxLoadMoreItem

+ListBoxLoadMoreItem <T extends {}> {
+  children?: ReactNode
+  className?: string
+  isLoading?: boolean
+  onLoadMore?: () => any
+  scrollOffset?: number = 1
+  style?: CSSProperties
+}

/react-aria-components:TableLoadMoreItem

+TableLoadMoreItem <T extends {}> {
+  children?: ReactNode
+  className?: string
+  isLoading?: boolean
+  onLoadMore?: () => any
+  scrollOffset?: number = 1
+  style?: CSSProperties
+}

/react-aria-components:TreeLoadMoreItem

+TreeLoadMoreItem <T extends {}> {
+  children?: ReactNode | ((TreeLoadMoreItemRenderProps & {
+    defaultChildren: ReactNode | undefined
+})) => ReactNode
+  className?: ClassNameOrFunction<TreeLoadMoreItemRenderProps>
+  isLoading?: boolean
+  onLoadMore?: () => any
+  scrollOffset?: number = 1
+  style?: StyleOrFunction<TreeLoadMoreItemRenderProps>
+}

/react-aria-components:GridListLoadMoreItemProps

+GridListLoadMoreItemProps {
+  children?: ReactNode
+  className?: string
+  isLoading?: boolean
+  onLoadMore?: () => any
+  scrollOffset?: number = 1
+  style?: CSSProperties
+}

/react-aria-components:ListBoxLoadMoreItemProps

+ListBoxLoadMoreItemProps {
+  children?: ReactNode
+  className?: string
+  isLoading?: boolean
+  onLoadMore?: () => any
+  scrollOffset?: number = 1
+  style?: CSSProperties
+}

/react-aria-components:TableLoadMoreItemProps

+TableLoadMoreItemProps {
+  children?: ReactNode
+  className?: string
+  isLoading?: boolean
+  onLoadMore?: () => any
+  scrollOffset?: number = 1
+  style?: CSSProperties
+}

/react-aria-components:TreeLoadMoreItemProps

+TreeLoadMoreItemProps {
+  children?: ReactNode | ((TreeLoadMoreItemRenderProps & {
+    defaultChildren: ReactNode | undefined
+})) => ReactNode
+  className?: ClassNameOrFunction<TreeLoadMoreItemRenderProps>
+  isLoading?: boolean
+  onLoadMore?: () => any
+  scrollOffset?: number = 1
+  style?: StyleOrFunction<TreeLoadMoreItemRenderProps>
+}

/react-aria-components:TreeLoadMoreItemRenderProps

+TreeLoadMoreItemRenderProps {
+  level: number
+}

@LFDanLu LFDanLu merged commit 4eab8de into multi_loader_support Jul 8, 2025
31 checks passed
@LFDanLu LFDanLu deleted the docs_for_load_spinner branch July 8, 2025 17:34
github-merge-queue bot pushed a commit that referenced this pull request Jul 11, 2025
…collection size (#8349)

* Update tree and listlayout to handle multi loaders

* adapting other stories to new loader api and adding useAsync example story

* add tests

* fix story for correctness, should only need to provide a dependecy at the top most collection

* restoring focus back to the tree if the user was focused on the loader

* fixing estimated loader position if sections exist

taken from #8326

* skip test for now

* revert loader keyboard focus specific logic

* pulling over relevant code from focus_loading_spinners

* modify tree to return item count when querying size

* update TableCollection to return just the number of rows as size

also the aria row index and pos in set from empty/isLoading state wrapper since it might be confusing to hear that there are items when the table/tree is empty

* update other collection components to leverage item only count

* clean up

* properly prevent picker from opening where there arent items

* fix lint

* review comments

* review comments

* making the async stories not load forever

* docs: Async load more documentation for RAC  (#8431)

* add docs for gridlist

* add async loading docs for GridList and Listbox adjacent components

* add docs example for table

* add prop tables

* rename sentinel component and add prop tables

* adding tree docs for loading spinners

* fix docs lint

* typo and left over stuff

* export the loadMoreItem props from the monopackage

* review comments

* fix the tree example in docs

* review comments

* review comments

* forgot to remove a delay

* review comments
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.

4 participants