Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -178,10 +178,10 @@
"postcss": "^8.4.24",
"postcss-custom-properties": "^13.2.0",
"postcss-import": "^15.1.0",
"react": "^19.1.0",
"react-dom": "^19.1.0",
"react": "^19.2.0",
"react-dom": "^19.2.0",
"react-frame-component": "^5.0.0",
"react-test-renderer": "^19.1.0",
"react-test-renderer": "^19.2.0",
"recast": "^0.23",
"recursive-readdir": "^2.2.2",
"regenerator-runtime": "0.13.3",
Expand Down
18 changes: 18 additions & 0 deletions packages/@react-aria/collections/src/CollectionBuilder.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {forwardRefType, Key, Node} from '@react-types/shared';
import {Hidden} from './Hidden';
import React, {createContext, ForwardedRef, forwardRef, JSX, ReactElement, ReactNode, useCallback, useContext, useMemo, useRef, useState} from 'react';
import {useIsSSR} from '@react-aria/ssr';
import {useLayoutEffect} from '@react-aria/utils';
import {useSyncExternalStore as useSyncExternalStoreShim} from 'use-sync-external-store/shim/index.js';

const ShallowRenderContext = createContext(false);
Expand Down Expand Up @@ -113,6 +114,23 @@ function useCollectionDocument<T extends object, C extends BaseCollection<T>>(cr
return document.getCollection();
}, [document]);
let collection = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);

// React 19.2 introduced activities, which when started as hidden on the dom can play funky with the collection
// To avoid this we force a one time re-render if the collection fails to populate
const [, forceStoreRerender] = useState(0); // simple state to force a re-render
useLayoutEffect(function forceRefreshCollection() {
// Do not do anything if the collection has been pre-populated successfully
if (collection.size > 0) {
return;
}
// Re-create the collection
const nextValue = getSnapshot();
if (nextValue.size > 0) {
forceStoreRerender(c => c + 1);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [collection.size === 0]); // don't run effect once we have a populated collection

return {collection, document};
}

Expand Down
4 changes: 2 additions & 2 deletions packages/dev/docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@
"highlight.js": "9.18.1",
"markdown-to-jsx": "^6.11.0",
"quicklink": "^2.3.0",
"react": "^19.1.0",
"react-dom": "^19.1.0",
"react": "^19.2.0",
"react-dom": "^19.2.0",
"react-lowlight": "^2.0.0"
},
"peerDependencies": {
Expand Down
4 changes: 2 additions & 2 deletions packages/dev/s2-docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,10 @@
"json5": "^2.2.3",
"lz-string": "^1.5.0",
"markdown-to-jsx": "^6.11.0",
"react": "^19",
"react": "^19.2.0",
"react-aria": "^3.40.0",
"react-aria-components": "^1.7.1",
"react-dom": "^19",
"react-dom": "^19.2.0",
"react-stately": "^3.38.0",
"remark-mdx": "^3.1.0",
"remark-parse": "^11.0.0",
Expand Down
36 changes: 36 additions & 0 deletions packages/react-aria-components/test/ListBox.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1861,4 +1861,40 @@ describe('ListBox', () => {
expect(onClick).toHaveBeenCalledTimes(1);
});
});

if (React.version.startsWith('19')) {
it('supports Activity', async () => {
function ActivityListBox() {
let [mode, setMode] = React.useState('hidden');

return (
<>
<Button onPress={() => setMode(mode === 'hidden' ? 'visible' : 'hidden')}>
Set {mode === 'hidden' ? 'visible' : 'hidden'}
</Button>

<React.Activity mode={mode}>
<p>List should be visible</p>

<ListBox aria-label="Activity Listbox">
<ListBoxItem>Item 1</ListBoxItem>
<ListBoxItem>Item 2</ListBoxItem>
<ListBoxItem>Item 3</ListBoxItem>
<ListBoxItem>Item 4</ListBoxItem>
<ListBoxItem>Item 5</ListBoxItem>
</ListBox>
</React.Activity>
</>
);
}

let {getAllByRole, getByRole, queryAllByRole} = render(<ActivityListBox />);
let button = getByRole('button');
expect(queryAllByRole('option')).toHaveLength(0);
await user.click(button);
expect(getAllByRole('option')).toHaveLength(5);
await user.click(button);
expect(queryAllByRole('option')).toHaveLength(0);
});
}
});
38 changes: 38 additions & 0 deletions packages/react-aria-components/test/Tabs.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -641,4 +641,42 @@ describe('Tabs', () => {
expect(onClick).toHaveBeenCalledTimes(1);
});
});

if (React.version.startsWith('19')) {
it('supports Activity', async () => {
function ActivityTabs() {
let [mode, setMode] = React.useState('hidden');

return (
<>
<Button onPress={() => setMode(mode === 'hidden' ? 'visible' : 'hidden')}>
Set {mode === 'hidden' ? 'visible' : 'hidden'}
</Button>
<React.Activity mode={mode}>
<Tabs>
<TabList aria-label="Test">
<Tab id="a">A</Tab>
<Tab id="b">B</Tab>
<TooltipTrigger>
<Tab id="c">C</Tab>
<Tooltip>Test</Tooltip>
</TooltipTrigger>
</TabList>
<TabPanel id="a">A</TabPanel>
<TabPanel id="b">B</TabPanel>
<TabPanel id="c">C</TabPanel>
</Tabs>
</React.Activity>
</>
);
}

let {getByRole, getAllByRole, queryAllByRole} = render(<ActivityTabs />);

let button = getByRole('button');
expect(queryAllByRole('tab')).toHaveLength(0);
await user.click(button);
expect(getAllByRole('tab')).toHaveLength(3);
});
}
});
8 changes: 4 additions & 4 deletions starters/docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,16 @@
"clsx": "^2.1.1",
"lightningcss-loader": "^2.1.0",
"lucide-react": "^0.517.0",
"react": "^19.1.0",
"react": "^19.2.0",
"react-aria-components": "^1.14.0",
"react-dom": "^19.1.0",
"react-dom": "^19.2.0",
"storybook": "^8.6.14",
"storybook-dark-mode": "^4.0.2",
"typescript": "^5.8.2"
},
"resolutions": {
"react": "19.1.0",
"react-dom": "19.1.0",
"react": "19.2.0",
"react-dom": "19.2.0",
"@types/mime": "3.0.4",
"jackspeak": "2.1.1"
}
Expand Down
48 changes: 24 additions & 24 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -6831,8 +6831,8 @@ __metadata:
highlight.js: "npm:9.18.1"
markdown-to-jsx: "npm:^6.11.0"
quicklink: "npm:^2.3.0"
react: "npm:^19.1.0"
react-dom: "npm:^19.1.0"
react: "npm:^19.2.0"
react-dom: "npm:^19.2.0"
react-lowlight: "npm:^2.0.0"
peerDependencies:
react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1
Expand Down Expand Up @@ -7383,10 +7383,10 @@ __metadata:
lz-string: "npm:^1.5.0"
markdown-to-jsx: "npm:^6.11.0"
playwright: "npm:^1.57.0"
react: "npm:^19"
react: "npm:^19.2.0"
react-aria: "npm:^3.40.0"
react-aria-components: "npm:^1.7.1"
react-dom: "npm:^19"
react-dom: "npm:^19.2.0"
react-stately: "npm:^3.38.0"
remark-mdx: "npm:^3.1.0"
remark-parse: "npm:^11.0.0"
Expand Down Expand Up @@ -24614,14 +24614,14 @@ __metadata:
languageName: node
linkType: hard

"react-dom@npm:^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0, react-dom@npm:^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0, react-dom@npm:^19, react-dom@npm:^19.1.0":
version: 19.1.0
resolution: "react-dom@npm:19.1.0"
"react-dom@npm:^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0, react-dom@npm:^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0, react-dom@npm:^19.2.0":
version: 19.2.1
resolution: "react-dom@npm:19.2.1"
dependencies:
scheduler: "npm:^0.26.0"
scheduler: "npm:^0.27.0"
peerDependencies:
react: ^19.1.0
checksum: 10c0/3e26e89bb6c67c9a6aa86cb888c7a7f8258f2e347a6d2a15299c17eb16e04c19194e3452bc3255bd34000a61e45e2cb51e46292392340432f133e5a5d2dfb5fc
react: ^19.2.1
checksum: 10c0/e56b6b3d72314df580ca800b70a69a21c6372703c8f45d9b5451ca6519faefb2496d76ffa9c5adb94136d2bbf2fd303d0dfc208a2cd77ede3132877471af9470
languageName: node
linkType: hard

Expand Down Expand Up @@ -24805,10 +24805,10 @@ __metadata:
postcss: "npm:^8.4.24"
postcss-custom-properties: "npm:^13.2.0"
postcss-import: "npm:^15.1.0"
react: "npm:^19.1.0"
react-dom: "npm:^19.1.0"
react: "npm:^19.2.0"
react-dom: "npm:^19.2.0"
react-frame-component: "npm:^5.0.0"
react-test-renderer: "npm:^19.1.0"
react-test-renderer: "npm:^19.2.0"
recast: "npm:^0.23"
recursive-readdir: "npm:^2.2.2"
regenerator-runtime: "npm:0.13.3"
Expand Down Expand Up @@ -24894,17 +24894,10 @@ __metadata:
languageName: node
linkType: hard

"react@npm:^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0, react@npm:^19, react@npm:^19.1.0":
version: 19.1.0
resolution: "react@npm:19.1.0"
checksum: 10c0/530fb9a62237d54137a13d2cfb67a7db6a2156faed43eecc423f4713d9b20c6f2728b026b45e28fcd72e8eadb9e9ed4b089e99f5e295d2f0ad3134251bdd3698
languageName: node
linkType: hard

"react@npm:^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0":
version: 19.1.1
resolution: "react@npm:19.1.1"
checksum: 10c0/8c9769a2dfd02e603af6445058325e6c8a24b47b185d0e461f66a6454765ddcaecb3f0a90184836c68bb509f3c38248359edbc42f0d07c23eb500a5c30c87b4e
"react@npm:^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0, react@npm:^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0, react@npm:^19.2.0":
version: 19.2.1
resolution: "react@npm:19.2.1"
checksum: 10c0/2b5eaf407abb3db84090434c20d6c5a8e447ab7abcd8fe9eaf1ddc299babcf31284ee9db7ea5671d21c85ac5298bd632fa1a7da1ed78d5b368a537f5e1cd5d62
languageName: node
linkType: hard

Expand Down Expand Up @@ -25889,6 +25882,13 @@ __metadata:
languageName: node
linkType: hard

"scheduler@npm:^0.27.0":
version: 0.27.0
resolution: "scheduler@npm:0.27.0"
checksum: 10c0/4f03048cb05a3c8fddc45813052251eca00688f413a3cee236d984a161da28db28ba71bd11e7a3dd02f7af84ab28d39fb311431d3b3772fed557945beb00c452
languageName: node
linkType: hard

"section-matter@npm:^1.0.0":
version: 1.0.0
resolution: "section-matter@npm:1.0.0"
Expand Down